1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#if !defined(__KERNEL__)
29#include <string.h>
30#endif
31
32#include "interface/vcos/vcos.h"
33
34#include "vchost_platform_config.h"
35#include "vchost.h"
36
37#include "interface/vchi/vchi.h"
38#include "interface/vchi/common/endian.h"
39#include "interface/vchi/message_drivers/message.h"
40#include "vc_tvservice.h"
41#include "vc_dispmanx_types.h"
42
43/******************************************************************************
44Local types and defines.
45******************************************************************************/
46#ifndef _min
47#define _min(x,y) (((x) <= (y))? (x) : (y))
48#endif
49#ifndef _max
50#define _max(x,y) (((x) >= (y))? (x) : (y))
51#endif
52
53// Pick a number way outside normal range
54#define INVALID_DISPLAY_ID 65536
55
56typedef struct
57{
58 TVSERVICE_CALLBACK_T notify_fn;
59 void *notify_data;
60} TVSERVICE_HOST_CALLBACK_T;
61
62typedef struct {
63 uint32_t is_valid;
64 uint32_t max_modes; //How big the table have we allocated
65 uint32_t num_modes; //How many valid entries are there
66 TV_SUPPORTED_MODE_NEW_T *modes;
67} TVSERVICE_MODE_CACHE_T;
68
69//TV service host side state (mostly the same as Videocore side - TVSERVICE_STATE_T)
70typedef struct {
71 //Generic service stuff
72 VCHI_SERVICE_HANDLE_T client_handle[VCHI_MAX_NUM_CONNECTIONS]; //To connect to server on VC
73 VCHI_SERVICE_HANDLE_T notify_handle[VCHI_MAX_NUM_CONNECTIONS]; //For incoming notification
74 uint32_t msg_flag[VCHI_MAX_NUM_CONNECTIONS];
75 char command_buffer[TVSERVICE_MSGFIFO_SIZE];
76 char response_buffer[TVSERVICE_MSGFIFO_SIZE];
77 uint32_t response_length;
78 uint32_t notify_buffer[TVSERVICE_MSGFIFO_SIZE/sizeof(uint32_t)];
79 uint32_t notify_length;
80 uint32_t num_connections;
81 VCOS_MUTEX_T lock;
82 TVSERVICE_HOST_CALLBACK_T callbacks[TVSERVICE_MAX_CALLBACKS];
83 int initialised;
84 int to_exit;
85
86 //TV stuff
87 uint32_t copy_protect;
88
89 //HDMI specific stuff
90 HDMI_RES_GROUP_T hdmi_current_group;
91 HDMI_MODE_T hdmi_current_mode;
92 HDMI_DISPLAY_OPTIONS_T hdmi_options;
93
94 //If client ever asks for supported modes, we store them for quick return
95 HDMI_RES_GROUP_T hdmi_preferred_group;
96 uint32_t hdmi_preferred_mode;
97 TVSERVICE_MODE_CACHE_T dmt_cache;
98 TVSERVICE_MODE_CACHE_T cea_cache;
99
100 //SDTV specific stuff
101 SDTV_COLOUR_T sdtv_current_colour;
102 SDTV_MODE_T sdtv_current_mode;
103 SDTV_OPTIONS_T sdtv_options;
104 SDTV_CP_MODE_T sdtv_current_cp_mode;
105} TVSERVICE_HOST_STATE_T;
106
107/******************************************************************************
108Static data.
109******************************************************************************/
110static TVSERVICE_HOST_STATE_T tvservice_client;
111static VCOS_EVENT_T tvservice_message_available_event;
112static VCOS_EVENT_T tvservice_notify_available_event;
113static VCOS_THREAD_T tvservice_notify_task;
114
115static int default_display_number;
116
117#define VCOS_LOG_CATEGORY (&tvservice_log_category)
118static VCOS_LOG_CAT_T tvservice_log_category;
119
120//Command strings - must match what's in vc_tvservice_defs.h
121static char* tvservice_command_strings[] = {
122 "get_state",
123 "hdmi_on_preferred",
124 "hdmi_on_best",
125 "hdmi_on_explicit",
126 "sdtv_on",
127 "off",
128 "query_supported_modes",
129 "query_mode_support",
130 "query_audio_support",
131 "enable_copy_protect",
132 "disable_copy_protect",
133 "show_info",
134 "get_av_latency",
135 "hdcp_set_key",
136 "hdcp_set_srm",
137 "set_spd",
138 "set_display_options",
139 "test_mode_start",
140 "test_mode_stop",
141 "ddc_read",
142 "set_attached",
143 "set_property",
144 "get_property",
145 "get_display_state",
146 "get_supported_modes",
147 "get_device_id",
148 "get_attached_devices",
149 "end_of_list"
150};
151
152static char *get_command_string(int command)
153{
154 if (command >= VC_TV_END_OF_LIST ||
155 command >= sizeof(tvservice_command_strings)/sizeof(tvservice_command_strings[0]))
156 return ("Unknown command");
157
158 return tvservice_command_strings[command];
159}
160
161/******************************************************************************
162Static functions.
163******************************************************************************/
164//Lock the host state
165static __inline int tvservice_lock_obtain (void) {
166 if(tvservice_client.initialised && vcos_mutex_lock(&tvservice_client.lock) == VCOS_SUCCESS) {
167 //Check again in case the service has been stopped
168 if (tvservice_client.initialised) {
169 vchi_service_use(tvservice_client.client_handle[0]);
170 return 0;
171 }
172 else
173 vcos_mutex_unlock(&tvservice_client.lock);
174 }
175
176 return -1;
177}
178
179//Unlock the host state
180static __inline void tvservice_lock_release (void) {
181 if (tvservice_client.initialised) {
182 vchi_service_release(tvservice_client.client_handle[0]);
183 }
184 vcos_mutex_unlock(&tvservice_client.lock);
185}
186
187//Forward declarations
188static void tvservice_client_callback( void *callback_param,
189 VCHI_CALLBACK_REASON_T reason,
190 void *msg_handle );
191
192static void tvservice_notify_callback( void *callback_param,
193 VCHI_CALLBACK_REASON_T reason,
194 void *msg_handle );
195
196static int32_t tvservice_wait_for_reply(void *response, uint32_t max_length, uint32_t *actual_length);
197
198static int32_t tvservice_wait_for_bulk_receive(void *buffer, uint32_t max_length);
199
200static int32_t tvservice_send_command( uint32_t command, uint32_t display_id, void *buffer, uint32_t length, uint32_t has_reply);
201
202static int32_t tvservice_send_command_reply( uint32_t command, uint32_t display_id, void *buffer, uint32_t length,
203 void *response, uint32_t max_length);
204
205static void *tvservice_notify_func(void *arg);
206
207
208/******************************************************************************
209TV service API
210******************************************************************************/
211/******************************************************************************
212NAME
213 vc_vchi_tv_init
214
215SYNOPSIS
216 int vc_vchi_tv_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections )
217
218FUNCTION
219 Initialise the TV service for use. A negative return value
220 indicates failure (which may mean it has not been started on VideoCore).
221
222RETURNS
223 int
224******************************************************************************/
225VCHPRE_ int VCHPOST_ vc_vchi_tv_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) {
226 int32_t success;
227 uint32_t i;
228 VCOS_STATUS_T status;
229 VCOS_THREAD_ATTR_T attrs;
230 static const HDMI_DISPLAY_OPTIONS_T hdmi_default_display_options =
231 {
232 HDMI_ASPECT_UNKNOWN,
233 VC_FALSE, 0, 0, // No vertical bar information.
234 VC_FALSE, 0, 0, // No horizontal bar information.
235 0 // No overscan flags.
236 };
237 TV_ATTACHED_DEVICES_T devices;
238
239 if (tvservice_client.initialised)
240 return -2;
241
242 vcos_log_set_level(VCOS_LOG_CATEGORY, VCOS_LOG_ERROR);
243 vcos_log_register("tvservice-client", VCOS_LOG_CATEGORY);
244
245 vcos_log_trace("[%s]", VCOS_FUNCTION);
246
247 // record the number of connections
248 memset( &tvservice_client, 0, sizeof(TVSERVICE_HOST_STATE_T) );
249 tvservice_client.num_connections = num_connections;
250
251 status = vcos_mutex_create(&tvservice_client.lock, "HTV");
252 vcos_assert(status == VCOS_SUCCESS);
253 status = vcos_event_create(&tvservice_message_available_event, "HTV");
254 vcos_assert(status == VCOS_SUCCESS);
255 status = vcos_event_create(&tvservice_notify_available_event, "HTV");
256 vcos_assert(status == VCOS_SUCCESS);
257
258 //Initialise any other non-zero bits of the TV service state here
259 tvservice_client.sdtv_current_mode = SDTV_MODE_OFF;
260 tvservice_client.sdtv_options.aspect = SDTV_ASPECT_4_3;
261 memcpy(&tvservice_client.hdmi_options, &hdmi_default_display_options, sizeof(HDMI_DISPLAY_OPTIONS_T));
262
263 for (i=0; i < tvservice_client.num_connections; i++) {
264
265 // Create a 'Client' service on the each of the connections
266 SERVICE_CREATION_T tvservice_parameters = { VCHI_VERSION(VC_TVSERVICE_VER),
267 TVSERVICE_CLIENT_NAME, // 4cc service code
268 connections[i], // passed in fn ptrs
269 0, // tx fifo size (unused)
270 0, // tx fifo size (unused)
271 &tvservice_client_callback, // service callback
272 &tvservice_message_available_event, // callback parameter
273 VC_TRUE, // want_unaligned_bulk_rx
274 VC_TRUE, // want_unaligned_bulk_tx
275 VC_FALSE, // want_crc
276 };
277
278 SERVICE_CREATION_T tvservice_parameters2 = { VCHI_VERSION(VC_TVSERVICE_VER),
279 TVSERVICE_NOTIFY_NAME, // 4cc service code
280 connections[i], // passed in fn ptrs
281 0, // tx fifo size (unused)
282 0, // tx fifo size (unused)
283 &tvservice_notify_callback,// service callback
284 &tvservice_notify_available_event, // callback parameter
285 VC_FALSE, // want_unaligned_bulk_rx
286 VC_FALSE, // want_unaligned_bulk_tx
287 VC_FALSE, // want_crc
288 };
289
290 //Create the client to normal TV service
291 success = vchi_service_open( initialise_instance, &tvservice_parameters, &tvservice_client.client_handle[i] );
292
293 //Create the client to the async TV service (any TV related notifications)
294 if (success == 0)
295 {
296 success = vchi_service_open( initialise_instance, &tvservice_parameters2, &tvservice_client.notify_handle[i] );
297 if (success == 0)
298 {
299 vchi_service_release(tvservice_client.client_handle[i]);
300 vchi_service_release(tvservice_client.notify_handle[i]);
301 }
302 else
303 {
304 vchi_service_close(tvservice_client.client_handle[i]);
305 vcos_log_error("Failed to connect to async TV service: %d", success);
306 }
307 } else {
308 vcos_log_error("Failed to connect to TV service: %d", success);
309 }
310
311 if (success != 0)
312 {
313 while (i > 0)
314 {
315 --i;
316 vchi_service_close(tvservice_client.client_handle[i]);
317 vchi_service_close(tvservice_client.notify_handle[i]);
318 }
319 return -1;
320 }
321 }
322
323 //Create the notifier task
324 vcos_thread_attr_init(&attrs);
325 vcos_thread_attr_setstacksize(&attrs, 4096);
326 vcos_thread_attr_settimeslice(&attrs, 1);
327
328 status = vcos_thread_create(&tvservice_notify_task, "HTV Notify", &attrs, tvservice_notify_func, &tvservice_client);
329 vcos_assert(status == VCOS_SUCCESS);
330
331 tvservice_client.initialised = 1;
332
333 // Now try and find a decent default display ID for backward compatibility
334 // Priority is Main LCD, Aux LCD, HDMI0, HDMI1
335 // Forcing to INVALID by default is for backward compatibility and is used to
336 // flag to the system that we should use old non display id commands even if a
337 // newer command is requested. ie new linux app on older firmware that does not
338 // support display ID based commands
339 default_display_number = INVALID_DISPLAY_ID;
340
341 if (vc_tv_get_attached_devices(&devices) != -1 && devices.num_attached > 0)
342 {
343 default_display_number = DISPMANX_ID_HDMI1;
344
345 // If only one, use it!
346 if (devices.num_attached == 1)
347 default_display_number = devices.display_number[0];
348 else
349 {
350 int i;
351 for (i=0;i<devices.num_attached;i++)
352 {
353 // If we find an LCD, use it straight away
354 if (devices.display_number[i] == DISPMANX_ID_MAIN_LCD)
355 {
356 default_display_number = DISPMANX_ID_MAIN_LCD;
357 break;
358 }
359 if (devices.display_number[i] == DISPMANX_ID_AUX_LCD)
360 {
361 default_display_number = DISPMANX_ID_AUX_LCD;
362 }
363 // If find HDMI0, if we haven't already selected an LCD, use it but keep searching in case main LCD turns up.
364 if (devices.display_number[i] == DISPMANX_ID_HDMI0 && default_display_number != DISPMANX_ID_AUX_LCD)
365 {
366 default_display_number = DISPMANX_ID_HDMI0;
367 }
368 }
369 }
370 }
371
372 vcos_log_trace("TV service initialised");
373
374 return 0;
375}
376
377/***********************************************************
378 * Name: vc_vchi_tv_stop
379 *
380 * Arguments:
381 * -
382 *
383 * Description: Stops the Host side part of TV service
384 *
385 * Returns: -
386 *
387 ***********************************************************/
388VCHPRE_ void VCHPOST_ vc_vchi_tv_stop( void ) {
389 // Wait for the current lock-holder to finish before zapping TV service
390 uint32_t i;
391
392 if (!tvservice_client.initialised)
393 return;
394
395 vcos_log_trace("[%s]", VCOS_FUNCTION);
396 if(tvservice_lock_obtain() == 0)
397 {
398 void *dummy;
399 vchi_service_release(tvservice_client.client_handle[0]); // to match the use in tvservice_lock_obtain()
400
401 for (i=0; i < tvservice_client.num_connections; i++) {
402 int32_t result;
403 vchi_service_use(tvservice_client.client_handle[i]);
404 vchi_service_use(tvservice_client.notify_handle[i]);
405 result = vchi_service_close(tvservice_client.client_handle[i]);
406 vcos_assert( result == 0 );
407 result = vchi_service_close(tvservice_client.notify_handle[i]);
408 vcos_assert( result == 0 );
409 }
410
411 // Unset the initialise flag so no other threads can obtain the lock
412 tvservice_client.initialised = 0;
413
414 tvservice_lock_release();
415 tvservice_client.to_exit = 1; //Signal to quit
416 vcos_event_signal(&tvservice_notify_available_event);
417 vcos_thread_join(&tvservice_notify_task, &dummy);
418
419 if(tvservice_client.cea_cache.modes)
420 vcos_free(tvservice_client.cea_cache.modes);
421
422 if(tvservice_client.dmt_cache.modes)
423 vcos_free(tvservice_client.dmt_cache.modes);
424
425 vcos_mutex_delete(&tvservice_client.lock);
426 vcos_event_delete(&tvservice_message_available_event);
427 vcos_event_delete(&tvservice_notify_available_event);
428 }
429}
430
431
432/***********************************************************
433 * Name: vc_tv_register_callback
434 *
435 * Arguments:
436 * callback function, context to be passed when function is called
437 *
438 * Description: Register a callback function for all TV notifications
439 *
440 * Returns: -
441 *
442 ***********************************************************/
443VCHPRE_ void VCHPOST_ vc_tv_register_callback(TVSERVICE_CALLBACK_T callback, void *callback_data) {
444
445 vcos_assert_msg(callback != NULL, "Use vc_tv_unregister_callback() to remove a callback");
446
447 vcos_log_trace("[%s]", VCOS_FUNCTION);
448 if(tvservice_lock_obtain() == 0)
449 {
450 uint32_t done = 0;
451 uint32_t i;
452 for(i = 0; (i < TVSERVICE_MAX_CALLBACKS) && !done; i++)
453 {
454 if(tvservice_client.callbacks[i].notify_fn == NULL)
455 {
456 tvservice_client.callbacks[i].notify_fn = callback;
457 tvservice_client.callbacks[i].notify_data = callback_data;
458 done = 1;
459 } // if
460 } // for
461 vcos_assert(done);
462 tvservice_lock_release();
463 }
464}
465
466/***********************************************************
467 * Name: vc_tv_unregister_callback
468 *
469 * Arguments:
470 * callback function
471 *
472 * Description: Unregister a previously-registered callback function for TV notifications
473 *
474 * Returns: -
475 *
476 ***********************************************************/
477VCHPRE_ void VCHPOST_ vc_tv_unregister_callback(TVSERVICE_CALLBACK_T callback)
478{
479 vcos_assert(callback != NULL);
480
481 vcos_log_trace("[%s]", VCOS_FUNCTION);
482 if(tvservice_lock_obtain() == 0)
483 {
484 uint32_t done = 0;
485 uint32_t i;
486 for(i = 0; (i < TVSERVICE_MAX_CALLBACKS) && !done; i++)
487 {
488 if(tvservice_client.callbacks[i].notify_fn == callback)
489 {
490 tvservice_client.callbacks[i].notify_fn = NULL;
491 tvservice_client.callbacks[i].notify_data = NULL;
492 done = 1;
493 } // if
494 } // for
495 vcos_assert(done);
496 tvservice_lock_release();
497 }
498}
499
500/***********************************************************
501 * Name: vc_tv_unregister_callback_full
502 *
503 * Arguments:
504 * callback function
505 * callback function context
506 *
507 * Description: Unregister a previously-registered callback function for TV notifications
508 *
509 * Returns: -
510 *
511 ***********************************************************/
512VCHPRE_ void VCHPOST_ vc_tv_unregister_callback_full(TVSERVICE_CALLBACK_T callback, void *callback_data)
513{
514 vcos_assert(callback != NULL);
515
516 vcos_log_trace("[%s]", VCOS_FUNCTION);
517 if(tvservice_lock_obtain() == 0)
518 {
519 uint32_t done = 0;
520 uint32_t i;
521 for(i = 0; (i < TVSERVICE_MAX_CALLBACKS) && !done; i++)
522 {
523 if(tvservice_client.callbacks[i].notify_fn == callback &&
524 tvservice_client.callbacks[i].notify_data == callback_data)
525 {
526 tvservice_client.callbacks[i].notify_fn = NULL;
527 tvservice_client.callbacks[i].notify_data = NULL;
528 done = 1;
529 } // if
530 } // for
531 vcos_assert(done);
532 tvservice_lock_release();
533 }
534}
535
536/*********************************************************************************
537 *
538 * Static functions definitions
539 *
540 *********************************************************************************/
541//TODO: Might need to handle multiple connections later
542/***********************************************************
543 * Name: tvservice_client_callback
544 *
545 * Arguments: semaphore, callback reason and message handle
546 *
547 * Description: Callback when a message is available for TV service
548 *
549 ***********************************************************/
550static void tvservice_client_callback( void *callback_param,
551 const VCHI_CALLBACK_REASON_T reason,
552 void *msg_handle ) {
553 VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param;
554
555 vcos_log_trace("[%s]", VCOS_FUNCTION);
556 (void)msg_handle;
557
558 if ( reason != VCHI_CALLBACK_MSG_AVAILABLE || event == NULL)
559 return;
560
561 vcos_event_signal(event);
562}
563
564/***********************************************************
565 * Name: tvservice_notify_callback
566 *
567 * Arguments: semaphore, callback reason and message handle
568 *
569 * Description: Callback when a message is available for TV notify service
570 *
571 ***********************************************************/
572static void tvservice_notify_callback( void *callback_param,
573 const VCHI_CALLBACK_REASON_T reason,
574 void *msg_handle ) {
575 VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param;
576
577 vcos_log_trace("[%s]", VCOS_FUNCTION);
578 (void)msg_handle;
579
580 if ( reason != VCHI_CALLBACK_MSG_AVAILABLE || event == NULL)
581 return;
582
583 vcos_event_signal(event);
584}
585
586/***********************************************************
587 * Name: tvservice_wait_for_reply
588 *
589 * Arguments: response buffer, buffer length
590 *
591 * Description: blocked until something is in the buffer
592 *
593 * Returns error code of vchi
594 *
595 ***********************************************************/
596static int32_t tvservice_wait_for_reply(void *response, uint32_t max_length, uint32_t *actual_length) {
597 int32_t success = 0;
598 uint32_t length_read = 0;
599 vcos_log_trace("[%s]", VCOS_FUNCTION);
600 do {
601 //TODO : we need to deal with messages coming through on more than one connections properly
602 //At the moment it will always try to read the first connection if there is something there
603 //Check if there is something in the queue, if so return immediately
604 //otherwise wait for the semaphore and read again
605 success = vchi_msg_dequeue( tvservice_client.client_handle[0], response, max_length, &length_read, VCHI_FLAGS_NONE );
606 } while( length_read == 0 && vcos_event_wait(&tvservice_message_available_event) == VCOS_SUCCESS);
607 if(length_read) {
608 vcos_log_trace("TV service got reply %d bytes", length_read);
609 } else {
610 vcos_log_warn("TV service wait for reply failed");
611 }
612
613 if (actual_length)
614 *actual_length = length_read;
615
616 return success;
617}
618
619/***********************************************************
620 * Name: tvservice_wait_for_bulk_receive
621 *
622 * Arguments: response buffer, buffer length
623 *
624 * Description: blocked until bulk receive
625 *
626 * Returns error code of vchi
627 *
628 ***********************************************************/
629static int32_t tvservice_wait_for_bulk_receive(void *buffer, uint32_t max_length) {
630 /*if(!vcos_verify(((uint32_t) buffer & 0xf) == 0)) //should be 16 byte aligned
631 return -1;*/
632 vcos_log_trace("[%s]", VCOS_FUNCTION);
633 if(!vcos_verify(buffer)) {
634 vcos_log_error("TV service: NULL buffer passed to wait_for_bulk_receive");
635 return -1;
636 }
637
638 return vchi_bulk_queue_receive( tvservice_client.client_handle[0],
639 buffer,
640 max_length,
641 VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE,
642 NULL );
643}
644
645/***********************************************************
646 * Name: tvservice_send_command
647 *
648 * Arguments: command, parameter buffer, parameter length, has reply? (non-zero means yes)
649 *
650 * Description: send a command and optionally wait for its single value response (TV_GENERAL_RESP_T)
651 *
652 * Returns: response.ret (currently only 1 int in the wrapped response), endian translated if necessary
653 *
654 ***********************************************************/
655
656static int32_t tvservice_send_command(uint32_t command, uint32_t display_id, void *buffer, uint32_t length, uint32_t has_reply) {
657
658 VCHI_MSG_VECTOR_T vector[3];
659 int vector_idx = 0;
660
661 vector[vector_idx].vec_base = &command;
662 vector[vector_idx].vec_len = sizeof(command);
663 vector_idx++;
664
665 if(vcos_verify(command < VC_TV_END_OF_LIST))
666 {
667 vcos_log_trace("[%s] command:%s param length %d %s", VCOS_FUNCTION,
668 get_command_string(command), length,
669 (has_reply)? "has reply" : " no reply");
670 }
671 else
672 {
673 vcos_log_error("[%s] not sending invalid command %d", VCOS_FUNCTION, command);
674 return -1;
675 }
676
677 if (display_id != INVALID_DISPLAY_ID)
678 {
679 vector[vector_idx].vec_base = &display_id;
680 vector[vector_idx].vec_len = sizeof(display_id);
681 vector_idx++;
682
683 command |= TVSERVICE_COMMAND_HAS_DISPLAY_ID;
684 }
685
686 vector[vector_idx].vec_base = buffer;
687 vector[vector_idx].vec_len = length;
688 vector_idx++;
689
690 int32_t success = 0;
691 TV_GENERAL_RESP_T response;
692 response.ret = -1;
693
694 if(tvservice_lock_obtain() == 0)
695 {
696 success = vchi_msg_queuev( tvservice_client.client_handle[0],
697 vector, vector_idx,
698 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL );
699 if(success == 0 && has_reply) {
700 //otherwise only wait for a reply if we ask for one
701 success = tvservice_wait_for_reply(&response, sizeof(response), NULL);
702 response.ret = VC_VTOH32(response.ret);
703 } else {
704 if(success != 0)
705 vcos_log_error("TV service failed to send command %s length %d, error code %d",
706 get_command_string(command), length, success);
707 //No reply expected or failed to send, send the success code back instead
708 response.ret = success;
709 }
710 tvservice_lock_release();
711 }
712 return response.ret;
713}
714
715/***********************************************************
716 * Name: tvservice_send_command_reply
717 *
718 * Arguments: command, parameter buffer, parameter length, reply buffer, buffer length
719 *
720 * Description: send a command and wait for its non-single value response (in a buffer)
721 *
722 * Returns: error code, host app is responsible to do endian translation
723 *
724 ***********************************************************/
725static int32_t tvservice_send_command_reply( uint32_t command, uint32_t display_id, void *buffer, uint32_t length,
726 void *response, uint32_t max_length)
727{
728 VCHI_MSG_VECTOR_T vector[3];
729 int vector_idx = 0;
730
731 vector[vector_idx].vec_base = &command;
732 vector[vector_idx].vec_len = sizeof(command);
733 vector_idx++;
734
735 if (display_id != INVALID_DISPLAY_ID)
736 {
737 vector[vector_idx].vec_base = &display_id;
738 vector[vector_idx].vec_len = sizeof(display_id);
739 vector_idx++;
740
741 command |= TVSERVICE_COMMAND_HAS_DISPLAY_ID;
742 }
743
744 vector[vector_idx].vec_base = buffer;
745 vector[vector_idx].vec_len = length;
746 vector_idx++;
747
748 int32_t success = 0;
749 uint32_t actual_length = 0;
750
751 vcos_log_trace("[%s] sending command (with reply) %s param length %d", VCOS_FUNCTION,
752 get_command_string(command), length);
753
754 if(tvservice_lock_obtain() == 0)
755 {
756 success = vchi_msg_queuev( tvservice_client.client_handle[0],
757 vector, vector_idx,
758 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL );
759 if(success == 0)
760 {
761 success = tvservice_wait_for_reply(response, max_length, &actual_length);
762
763 // Need to determine if we were returned an error code as a single response
764 // So, if we were expecting more than an int32_t but only got int32_t, AND
765 // its value is < 0 then assume its an error code and return that.
766 if (max_length != sizeof(int32_t) && actual_length == sizeof(int32_t))
767 {
768 if (*(int32_t*)response < 0)
769 success = *(int32_t*)response;
770 }
771 }
772 else
773 vcos_log_error("TV service failed to send command %s param length %d, error code %d",
774 get_command_string(command), length, success);
775
776 tvservice_lock_release();
777 }
778
779 return success;
780}
781
782/***********************************************************
783 * Name: tvservice_notify_func
784 *
785 * Arguments: TV service state
786 *
787 * Description: This is the notification task which receives all TV
788 * service notifications
789 *
790 * Returns: does not return
791 *
792 ***********************************************************/
793static void *tvservice_notify_func(void *arg) {
794 int32_t success;
795 TVSERVICE_HOST_STATE_T *state = (TVSERVICE_HOST_STATE_T *) arg;
796 TV_DISPLAY_STATE_T tvstate;
797
798 vcos_log_trace("TV service async thread started");
799 /* Check starting state, and put service in use if necessary */
800 // TODoO; Need to distinguish which display somehow...
801 success = tvservice_send_command_reply( VC_TV_GET_DISPLAY_STATE, default_display_number, NULL, 0, &tvstate, sizeof(TV_DISPLAY_STATE_T));
802 if (success != 0)
803 return 0;
804 if (tvstate.state & VC_HDMI_ATTACHED)
805 {
806 /* Connected */
807 if (tvstate.state & (VC_HDMI_HDMI | VC_HDMI_DVI))
808 {
809 /* Mode already selected */
810 vchi_service_use(state->notify_handle[0]);
811 }
812 }
813 // the state machine below only wants a single bit to be set
814 if (tvstate.state & (VC_HDMI_ATTACHED|VC_HDMI_UNPLUGGED))
815 tvstate.state &= ~(VC_HDMI_HDMI | VC_HDMI_DVI);
816
817 while(1) {
818 VCOS_STATUS_T status = vcos_event_wait(&tvservice_notify_available_event);
819 if(status != VCOS_SUCCESS || !state->initialised || state->to_exit)
820 break;
821
822 do {
823 uint32_t reason, param1, param2;
824 //Get all notifications in the queue
825 success = vchi_msg_dequeue( state->notify_handle[0], state->notify_buffer, sizeof(state->notify_buffer), &state->notify_length, VCHI_FLAGS_NONE );
826 if(success != 0 || state->notify_length < sizeof(uint32_t)*3 ) {
827 vcos_assert(state->notify_length == sizeof(uint32_t)*3);
828 break;
829 }
830
831 if(tvservice_lock_obtain() != 0)
832 break;
833
834 //Check what notification it is and update ourselves accordingly before notifying the host app
835 //All notifications are of format: reason, param1, param2 (all 32-bit unsigned int)
836 reason = VC_VTOH32(state->notify_buffer[0]), param1 = VC_VTOH32(state->notify_buffer[1]), param2 = VC_VTOH32(state->notify_buffer[2]);
837 vcos_log_trace("[%s] %s %d %d", VCOS_FUNCTION, vc_tv_notification_name(reason),
838 param1, param2);
839 switch(reason) {
840 case VC_HDMI_UNPLUGGED:
841 if(tvstate.state & (VC_HDMI_HDMI|VC_HDMI_DVI)) {
842 state->copy_protect = 0;
843 if((tvstate.state & VC_HDMI_ATTACHED) == 0) {
844 vchi_service_release(state->notify_handle[0]);
845 }
846 }
847 tvstate.state &= ~(VC_HDMI_HDMI|VC_HDMI_DVI|VC_HDMI_ATTACHED|VC_HDMI_HDCP_AUTH);
848 tvstate.state |= (VC_HDMI_UNPLUGGED | VC_HDMI_HDCP_UNAUTH);
849 vcos_log_trace("[%s] invalidating caches", VCOS_FUNCTION);
850 state->cea_cache.is_valid = state->cea_cache.num_modes = 0;
851 state->dmt_cache.is_valid = state->dmt_cache.num_modes = 0;
852 break;
853
854 case VC_HDMI_ATTACHED:
855 if(tvstate.state & (VC_HDMI_HDMI|VC_HDMI_DVI)) {
856 state->copy_protect = 0;
857 vchi_service_release(state->notify_handle[0]);
858 }
859 tvstate.state &= ~(VC_HDMI_HDMI|VC_HDMI_DVI|VC_HDMI_UNPLUGGED|VC_HDMI_HDCP_AUTH);
860 tvstate.state |= VC_HDMI_ATTACHED;
861 state->hdmi_preferred_group = (HDMI_RES_GROUP_T) param1;
862 state->hdmi_preferred_mode = param2;
863 break;
864
865 case VC_HDMI_DVI:
866 if(tvstate.state & (VC_HDMI_ATTACHED|VC_HDMI_UNPLUGGED)) {
867 vchi_service_use(state->notify_handle[0]);
868 }
869 tvstate.state &= ~(VC_HDMI_HDMI|VC_HDMI_ATTACHED|VC_HDMI_UNPLUGGED);
870 tvstate.state |= VC_HDMI_DVI;
871 state->hdmi_current_group = (HDMI_RES_GROUP_T) param1;
872 state->hdmi_current_mode = param2;
873 break;
874
875 case VC_HDMI_HDMI:
876 if(tvstate.state & (VC_HDMI_ATTACHED|VC_HDMI_UNPLUGGED)) {
877 vchi_service_use(state->notify_handle[0]);
878 }
879 tvstate.state &= ~(VC_HDMI_DVI|VC_HDMI_ATTACHED|VC_HDMI_UNPLUGGED);
880 tvstate.state |= VC_HDMI_HDMI;
881 state->hdmi_current_group = (HDMI_RES_GROUP_T) param1;
882 state->hdmi_current_mode = param2;
883 break;
884
885 case VC_HDMI_HDCP_UNAUTH:
886 tvstate.state &= ~VC_HDMI_HDCP_AUTH;
887 tvstate.state |= VC_HDMI_HDCP_UNAUTH;
888 state->copy_protect = 0;
889 //Do we care about the reason for HDCP unauth in param1?
890 break;
891
892 case VC_HDMI_HDCP_AUTH:
893 tvstate.state &= ~VC_HDMI_HDCP_UNAUTH;
894 tvstate.state |= VC_HDMI_HDCP_AUTH;
895 state->copy_protect = 1;
896 break;
897
898 case VC_HDMI_HDCP_KEY_DOWNLOAD:
899 case VC_HDMI_HDCP_SRM_DOWNLOAD:
900 //Nothing to do here, just tell the host app whether it is successful or not (in param1)
901 break;
902
903 case VC_SDTV_UNPLUGGED: //Currently we don't get this
904 if(tvstate.state & (VC_SDTV_PAL | VC_SDTV_NTSC)) {
905 state->copy_protect = 0;
906 }
907 tvstate.state &= ~(VC_SDTV_ATTACHED | VC_SDTV_PAL | VC_SDTV_NTSC);
908 tvstate.state |= (VC_SDTV_UNPLUGGED | VC_SDTV_CP_INACTIVE);
909 state->sdtv_current_mode = SDTV_MODE_OFF;
910 break;
911
912 case VC_SDTV_ATTACHED: //Currently we don't get this either
913 tvstate.state &= ~(VC_SDTV_UNPLUGGED | VC_SDTV_PAL | VC_SDTV_NTSC);
914 tvstate.state |= VC_SDTV_ATTACHED;
915 state->sdtv_current_mode = SDTV_MODE_OFF;
916 break;
917
918 case VC_SDTV_NTSC:
919 tvstate.state &= ~(VC_SDTV_UNPLUGGED | VC_SDTV_ATTACHED | VC_SDTV_PAL);
920 tvstate.state |= VC_SDTV_NTSC;
921 state->sdtv_current_mode = (SDTV_MODE_T) param1;
922 state->sdtv_options.aspect = (SDTV_ASPECT_T) param2;
923 if(param1 & SDTV_COLOUR_RGB) {
924 state->sdtv_current_colour = SDTV_COLOUR_RGB;
925 } else if(param1 & SDTV_COLOUR_YPRPB) {
926 state->sdtv_current_colour = SDTV_COLOUR_YPRPB;
927 } else {
928 state->sdtv_current_colour = SDTV_COLOUR_UNKNOWN;
929 }
930 break;
931
932 case VC_SDTV_PAL:
933 tvstate.state &= ~(VC_SDTV_UNPLUGGED | VC_SDTV_ATTACHED | VC_SDTV_NTSC);
934 tvstate.state |= VC_SDTV_PAL;
935 state->sdtv_current_mode = (SDTV_MODE_T) param1;
936 state->sdtv_options.aspect = (SDTV_ASPECT_T) param2;
937 if(param1 & SDTV_COLOUR_RGB) {
938 state->sdtv_current_colour = SDTV_COLOUR_RGB;
939 } else if(param1 & SDTV_COLOUR_YPRPB) {
940 state->sdtv_current_colour = SDTV_COLOUR_YPRPB;
941 } else {
942 state->sdtv_current_colour = SDTV_COLOUR_UNKNOWN;
943 }
944 break;
945
946 case VC_SDTV_CP_INACTIVE:
947 tvstate.state &= ~VC_SDTV_CP_ACTIVE;
948 tvstate.state |= VC_SDTV_CP_INACTIVE;
949 state->copy_protect = 0;
950 state->sdtv_current_cp_mode = SDTV_CP_NONE;
951 break;
952
953 case VC_SDTV_CP_ACTIVE:
954 tvstate.state &= ~VC_SDTV_CP_INACTIVE;
955 tvstate.state |= VC_SDTV_CP_ACTIVE;
956 state->copy_protect = 1;
957 state->sdtv_current_cp_mode = (SDTV_CP_MODE_T) param1;
958 break;
959 }
960
961 tvservice_lock_release();
962
963 //Now callback the host app(s)
964 uint32_t i, called = 0;
965 for(i = 0; i < TVSERVICE_MAX_CALLBACKS; i++)
966 {
967 if(state->callbacks[i].notify_fn != NULL)
968 {
969 called++;
970 state->callbacks[i].notify_fn
971 (state->callbacks[i].notify_data, reason, param1, param2);
972 } // if
973 } // for
974 if(called == 0) {
975 vcos_log_info("TV service: No callback handler specified, callback [%s] swallowed",
976 vc_tv_notification_name(reason));
977 }
978 } while(success == 0 && state->notify_length >= sizeof(uint32_t)*3); //read the next message if any
979 } //while (1)
980
981 if(state->to_exit)
982 vcos_log_trace("TV service async thread exiting");
983
984 return 0;
985}
986
987/***********************************************************
988 Actual TV service API starts here
989***********************************************************/
990
991/***********************************************************
992 * Name: vc_tv_get_state
993 *
994 * Arguments:
995 * Pointer to tvstate structure
996 *
997 * Description: Get the current TV state
998 *
999 * Returns: if the command is successful (zero) or not (non-zero)
1000 * If the command fails to be sent, passed in state is unchanged
1001 *
1002 ***********************************************************/
1003VCHPRE_ int VCHPOST_ vc_tv_get_state_id(uint32_t display_id, TV_GET_STATE_RESP_T *tvstate) {
1004 int success = -1;
1005
1006 vcos_log_trace("[%s]", VCOS_FUNCTION);
1007 if(vcos_verify(tvstate)) {
1008 success = tvservice_send_command_reply( VC_TV_GET_STATE, display_id, NULL, 0,
1009 tvstate, sizeof(*tvstate));
1010 if(success == 0) {
1011 tvstate->state = VC_VTOH32(tvstate->state);
1012 tvstate->width = VC_VTOH32(tvstate->width);
1013 tvstate->height = VC_VTOH32(tvstate->height);
1014 tvstate->frame_rate = VC_VTOH16(tvstate->frame_rate);
1015 tvstate->scan_mode = VC_VTOH16(tvstate->scan_mode);
1016 }
1017 }
1018 return success;
1019}
1020
1021VCHPRE_ int VCHPOST_ vc_tv_get_state(TV_GET_STATE_RESP_T *tvstate)
1022{
1023 return vc_tv_get_state_id(default_display_number, tvstate);
1024}
1025
1026/***********************************************************
1027 * Name: vc_tv_get_display_state
1028 *
1029 * Arguments:
1030 * Pointer to tvstate structure
1031 *
1032 * Description: Get the current TV display state.
1033 *
1034 * Returns: if the command is successful (zero) or not (non-zero)
1035 * If the command fails to be sent, passed in state is unchanged
1036 *
1037 ***********************************************************/
1038VCHPRE_ int VCHPOST_ vc_tv_get_display_state_id(uint32_t display_id, TV_DISPLAY_STATE_T *tvstate) {
1039 int success = -1;
1040
1041 vcos_log_trace("[%s]", VCOS_FUNCTION);
1042 if(vcos_verify(tvstate)) {
1043 success = tvservice_send_command_reply( VC_TV_GET_DISPLAY_STATE, display_id, NULL, 0,
1044 tvstate, sizeof(TV_DISPLAY_STATE_T));
1045 }
1046 return success;
1047}
1048
1049VCHPRE_ int VCHPOST_ vc_tv_get_display_state(TV_DISPLAY_STATE_T *tvstate) {
1050 return vc_tv_get_display_state_id(default_display_number, tvstate);
1051}
1052
1053/***********************************************************
1054 * Name: vc_tv_hdmi_power_on_preferred/vc_tv_hdmi_power_on_preferred_3d
1055 *
1056 * Arguments:
1057 * none
1058 *
1059 * Description: Power on HDMI at preferred resolution
1060 * Enter 3d if the _3d function was called
1061 *
1062 * Analogue TV will be powered down if on (same for the following
1063 * two HDMI power on functions below)
1064 *
1065 * Returns: single value interpreted as HDMI_RESULT_T (zero means success)
1066 * if successful, there will be a callback when the power on is complete
1067 *
1068 ***********************************************************/
1069static int vc_tv_hdmi_power_on_preferred_actual(uint32_t display_id, uint32_t in_3d) {
1070 TV_HDMI_ON_PREFERRED_PARAM_T param;
1071 int success;
1072
1073 vcos_log_trace("[%s]", VCOS_FUNCTION);
1074 param.in_3d = VC_HTOV32(in_3d);
1075
1076 success = tvservice_send_command( VC_TV_HDMI_ON_PREFERRED, display_id, &param, sizeof(param), 1);
1077 return success;
1078}
1079
1080VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_preferred_id(uint32_t display_id) {
1081 return vc_tv_hdmi_power_on_preferred_actual(display_id, 0);
1082}
1083
1084VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_preferred_3d_id(uint32_t display_id) {
1085 return vc_tv_hdmi_power_on_preferred_actual(display_id, 1);
1086}
1087
1088VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_preferred() {
1089 return vc_tv_hdmi_power_on_preferred_actual(default_display_number, 0);
1090}
1091
1092VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_preferred_3d() {
1093 return vc_tv_hdmi_power_on_preferred_actual(default_display_number, 1);
1094}
1095
1096/***********************************************************
1097 * Name: vc_tv_hdmi_power_on_best/vc_tv_hdmi_power_on_best_3d
1098 *
1099 * Arguments:
1100 * screen width, height, frame rate, scan_mode (HDMI_NONINTERLACED / HDMI_INTERLACED)
1101 * match flags
1102 *
1103 * Description: Power on HDMI at best matched resolution based on passed in parameters
1104 * Enter 3d mode if the _3d function was called
1105 *
1106 * Returns: single value interpreted as HDMI_RESULT_T (zero means success)
1107 * if successful, there will be a callback when the power on is complete
1108 *
1109 ***********************************************************/
1110static int vc_tv_hdmi_power_on_best_actual(uint32_t display_id,
1111 uint32_t width, uint32_t height, uint32_t frame_rate,
1112 HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags,
1113 uint32_t in_3d) {
1114 TV_HDMI_ON_BEST_PARAM_T param;
1115 int success;
1116
1117 vcos_log_trace("[%s]", VCOS_FUNCTION);
1118 param.width = VC_HTOV32(width);
1119 param.height = VC_HTOV32(height);
1120 param.frame_rate = VC_HTOV32(frame_rate);
1121 param.scan_mode = VC_HTOV32(scan_mode);
1122 param.match_flags = VC_HTOV32(match_flags);
1123 param.in_3d = VC_HTOV32(in_3d);
1124
1125 success = tvservice_send_command( VC_TV_HDMI_ON_BEST, display_id, &param, sizeof(TV_HDMI_ON_BEST_PARAM_T), 1);
1126 return success;
1127}
1128
1129VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_best_id(uint32_t display_id,
1130 uint32_t width, uint32_t height, uint32_t frame_rate,
1131 HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) {
1132 vcos_log_trace("[%s]", VCOS_FUNCTION);
1133 return vc_tv_hdmi_power_on_best_actual(display_id, width, height, frame_rate, scan_mode, match_flags, 0);
1134}
1135
1136VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_best_3d_id(uint32_t display_id,
1137 uint32_t width, uint32_t height, uint32_t frame_rate,
1138 HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) {
1139 vcos_log_trace("[%s]", VCOS_FUNCTION);
1140 return vc_tv_hdmi_power_on_best_actual(display_id, width, height, frame_rate, scan_mode, match_flags, 1);
1141}
1142
1143VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_best(uint32_t width, uint32_t height, uint32_t frame_rate,
1144 HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) {
1145 vcos_log_trace("[%s]", VCOS_FUNCTION);
1146 return vc_tv_hdmi_power_on_best_actual(default_display_number, width, height, frame_rate, scan_mode, match_flags, 0);
1147}
1148
1149VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_best_3d(uint32_t width, uint32_t height, uint32_t frame_rate,
1150 HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) {
1151 vcos_log_trace("[%s]", VCOS_FUNCTION);
1152 return vc_tv_hdmi_power_on_best_actual(default_display_number, width, height, frame_rate, scan_mode, match_flags, 1);
1153}
1154
1155/***********************************************************
1156 * Name: vc_tv_hdmi_power_on_explicit
1157 *
1158 * Arguments:
1159 * mode (HDMI_MODE_HDMI/HDMI_MODE_DVI),
1160 * group (HDMI_RES_GROUP_CEA/HDMI_RES_GROUP_DMT),
1161 * code
1162 *
1163 * Description: Power on HDMI at explicit mode
1164 * Enter 3d mode if supported by the TV and if mode was set to HDMI_MODE_3D
1165 * If Videocore has EDID, this will still be subject to EDID restriction,
1166 * otherwise HDMI will be powered on at the said mode
1167 *
1168 * Returns: single value interpreted as HDMI_RESULT_T (zero means success)
1169 * if successful, there will be a callback when the power on is complete
1170 *
1171 ***********************************************************/
1172VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_explicit_new_id(uint32_t display_id, HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code) {
1173 TV_HDMI_ON_EXPLICIT_PARAM_T param;
1174 int success;
1175
1176 vcos_log_trace("[%s] mode %d group %d code %d", VCOS_FUNCTION,
1177 mode, group, code);
1178 param.hdmi_mode = mode;
1179 param.group = group;
1180 param.mode = code;
1181
1182 success = tvservice_send_command( VC_TV_HDMI_ON_EXPLICIT, display_id, &param, sizeof(TV_HDMI_ON_EXPLICIT_PARAM_T), 1);
1183 return success;
1184}
1185
1186VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code) {
1187 return vc_tv_hdmi_power_on_explicit_new_id(default_display_number, mode, group, code);
1188}
1189
1190/***********************************************************
1191 * Name: vc_tv_sdtv_power_on
1192 *
1193 * Arguments:
1194 * SDTV mode, options (currently only aspect ratio)
1195 *
1196 * Description: Power on SDTV at required mode and aspect ratio (default 4:3)
1197 * HDMI will be powered down if currently on
1198 *
1199 * Returns: single value (zero means success)
1200 * if successful, there will be a callback when the power on is complete
1201 *
1202 ***********************************************************/
1203VCHPRE_ int VCHPOST_ vc_tv_sdtv_power_on_id(uint32_t display_id, SDTV_MODE_T mode, SDTV_OPTIONS_T *options) {
1204 TV_SDTV_ON_PARAM_T param;
1205 int success;
1206
1207 vcos_log_trace("[%s]", VCOS_FUNCTION);
1208 param.mode = VC_HTOV32(mode);
1209 param.aspect = (options)? VC_HTOV32(options->aspect) : VC_HTOV32(SDTV_ASPECT_4_3);
1210
1211 success = tvservice_send_command( VC_TV_SDTV_ON, display_id, &param, sizeof(TV_SDTV_ON_PARAM_T), 1);
1212 return success;
1213}
1214
1215VCHPRE_ int VCHPOST_ vc_tv_sdtv_power_on(SDTV_MODE_T mode, SDTV_OPTIONS_T *options) {
1216 return vc_tv_sdtv_power_on_id(default_display_number, mode, options);
1217}
1218
1219/***********************************************************
1220 * Name: vc_tv_power_off
1221 *
1222 * Arguments:
1223 * none
1224 *
1225 * Description: Power off whatever is on at the moment, no effect if nothing is on
1226 *
1227 * Returns: whether command is successfully sent (and callback for HDMI)
1228 *
1229 ***********************************************************/
1230VCHPRE_ int VCHPOST_ vc_tv_power_off_id(uint32_t display_id) {
1231 vcos_log_trace("[%s]", VCOS_FUNCTION);
1232 return tvservice_send_command( VC_TV_OFF, display_id, NULL, 0, 0);
1233}
1234
1235VCHPRE_ int VCHPOST_ vc_tv_power_off( void ) {
1236 return vc_tv_power_off_id(default_display_number);
1237}
1238
1239/***********************************************************
1240 * Name: vc_tv_hdmi_get_supported_modes
1241 *
1242 * Arguments:
1243 * group (HDMI_RES_GROUP_CEA/HDMI_RES_GROUP_DMT),
1244 * array of TV_SUPPORT_MODE_T structs, length of array, pointer to preferred group,
1245 * pointer to prefer mode code (the last two pointers can be NULL, if the caller
1246 * is not interested to learn what the preferred mode is)
1247 * If passed in a null supported_modes array, or the length of array
1248 * is zero, the number of supported modes in that particular group
1249 * will be returned instead
1250 *
1251 * Description: Get supported modes for a particular group,
1252 * the length of array limits no. of modes returned
1253 *
1254 * Returns: Returns the number of modes actually written (or the number
1255 * of supported modes if passed in a null array or length of array==0).
1256 * Returns < 0 for error.
1257 *
1258 ***********************************************************/
1259VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_supported_modes_new_id(uint32_t display_id,
1260 HDMI_RES_GROUP_T group,
1261 TV_SUPPORTED_MODE_NEW_T *supported_modes,
1262 uint32_t max_supported_modes,
1263 HDMI_RES_GROUP_T *preferred_group,
1264 uint32_t *preferred_mode) {
1265 uint32_t param[2] = {(uint32_t) group, 0};
1266 TV_QUERY_SUPPORTED_MODES_RESP_T response;
1267 TVSERVICE_MODE_CACHE_T *cache = NULL;
1268 int error = -1;
1269 int modes_copied = 0;
1270
1271 vcos_log_trace("[%s]", VCOS_FUNCTION);
1272
1273 switch(group) {
1274 case HDMI_RES_GROUP_DMT:
1275 cache = &tvservice_client.dmt_cache;
1276 break;
1277 case HDMI_RES_GROUP_CEA:
1278 cache = &tvservice_client.cea_cache;
1279 break;
1280 default:
1281 vcos_log_error("Invalid group %d in [%s]", group, VCOS_FUNCTION);
1282 return -1;
1283 }
1284 vcos_log_trace("[%s] group %d cache valid %d",
1285 VCOS_FUNCTION, group, cache->is_valid);
1286
1287 memset(&response, 0, sizeof(response));
1288 if(!cache->is_valid) {
1289 vchi_service_use(tvservice_client.client_handle[0]);
1290 if((error = tvservice_send_command_reply(VC_TV_QUERY_SUPPORTED_MODES, display_id, &param[0], sizeof(uint32_t),
1291 &response, sizeof(response))) == VC_HDMI_SUCCESS) {
1292 //First ask how many modes there are, if the current table is big enough to hold
1293 //all the modes, just copy over, otherwise allocate a new table
1294 if(response.num_supported_modes) {
1295 if(cache->max_modes < response.num_supported_modes) {
1296 cache->max_modes = response.num_supported_modes;
1297 if(cache->modes) {
1298 vcos_free(cache->modes);
1299 cache->modes = NULL;
1300 }
1301 } else {
1302 vcos_assert(cache->modes);
1303 memset(cache->modes, 0, cache->max_modes * sizeof(TV_SUPPORTED_MODE_NEW_T));
1304 }
1305 if(cache->modes == NULL) {
1306 cache->modes = vcos_calloc(cache->max_modes, sizeof(TV_SUPPORTED_MODE_NEW_T), "cached modes");
1307 }
1308 if(vcos_verify(cache->modes)) {
1309 //If we have successfully allocated the table, send
1310 //another request to actually download the modes from Videocore
1311 param[1] = response.num_supported_modes;
1312 if((error = tvservice_send_command_reply(VC_TV_QUERY_SUPPORTED_MODES_ACTUAL, display_id, param, sizeof(param),
1313 &response, sizeof(response))) == VC_HDMI_SUCCESS) {
1314 //The response comes back may indicate a different number of modes to param[1].
1315 //This happens if a new EDID was read in between the two requests (should be rare),
1316 //in which case we just download as many as VC says will be sent
1317 cache->num_modes = response.num_supported_modes;
1318 vcos_assert(response.num_supported_modes <= param[1]); //VC will not return more than what we have allocated
1319 if(cache->num_modes) {
1320 error = tvservice_wait_for_bulk_receive(cache->modes, cache->num_modes * sizeof(TV_SUPPORTED_MODE_NEW_T));
1321 if(error)
1322 vcos_log_error("Failed to download %s cache in [%s]", HDMI_RES_GROUP_NAME(group), VCOS_FUNCTION);
1323 } else {
1324 vcos_log_error("First query of supported modes indicated there are %d, but now there are none, has the TV been unplugged? [%s]",
1325 param[1], VCOS_FUNCTION);
1326 }
1327 } else {
1328 vcos_log_error("Failed to request %s cache in [%s]", HDMI_RES_GROUP_NAME(group), VCOS_FUNCTION);
1329 }
1330 } else {
1331 //If we failed to allocate memory, the request stops here
1332 vcos_log_error("Failed to allocate memory for %s cache in [%s]", HDMI_RES_GROUP_NAME(group), VCOS_FUNCTION);
1333 }
1334 } else {
1335 //The request also terminates if there are no supported modes reported
1336 vcos_log_trace("No supported modes returned for group %s in [%s]", HDMI_RES_GROUP_NAME(group), VCOS_FUNCTION);
1337 }
1338 } else {
1339 vcos_log_error("Failed to query supported modes for group %s in [%s]", HDMI_RES_GROUP_NAME(group), VCOS_FUNCTION);
1340 }
1341 vchi_service_release(tvservice_client.client_handle[0]);
1342
1343 if(!error) {
1344 cache->is_valid = 1;
1345 vcos_log_trace("[%s] cached %d %s resolutions", VCOS_FUNCTION, response.num_supported_modes, HDMI_RES_GROUP_NAME(group));
1346 tvservice_client.hdmi_preferred_group = response.preferred_group;
1347 tvservice_client.hdmi_preferred_mode = response.preferred_mode;
1348 }
1349 }
1350
1351 if(cache->is_valid) {
1352 if(supported_modes && max_supported_modes) {
1353 modes_copied = _min(max_supported_modes, cache->num_modes);
1354 memcpy(supported_modes, cache->modes, modes_copied*sizeof(TV_SUPPORTED_MODE_NEW_T));
1355 } else {
1356 //If we pass in a null pointer, return the size of table instead
1357 modes_copied = cache->num_modes;
1358 }
1359 }
1360
1361 if(preferred_group && preferred_mode) {
1362 *preferred_group = tvservice_client.hdmi_preferred_group;
1363 *preferred_mode = tvservice_client.hdmi_preferred_mode;
1364 }
1365
1366 return modes_copied; //If there was an error, this will be zero
1367}
1368
1369VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_supported_modes_new(HDMI_RES_GROUP_T group,
1370 TV_SUPPORTED_MODE_NEW_T *supported_modes,
1371 uint32_t max_supported_modes,
1372 HDMI_RES_GROUP_T *preferred_group,
1373 uint32_t *preferred_mode) {
1374 return vc_tv_hdmi_get_supported_modes_new_id(default_display_number,
1375 group,
1376 supported_modes,
1377 max_supported_modes,
1378 preferred_group,
1379 preferred_mode);
1380}
1381
1382/***********************************************************
1383 * Name: vc_tv_hdmi_mode_supported
1384 *
1385 * Arguments:
1386 * resolution standard (CEA/DMT), mode code
1387 *
1388 * Description: Query if a particular mode is supported
1389 *
1390 * Returns: single value return > 0 means supported, 0 means unsupported, < 0 means error
1391 *
1392 ***********************************************************/
1393VCHPRE_ int VCHPOST_ vc_tv_hdmi_mode_supported_id(uint32_t display_id, HDMI_RES_GROUP_T group,
1394 uint32_t mode) {
1395 TV_QUERY_MODE_SUPPORT_PARAM_T param = {group, mode};
1396 vcos_log_trace("[%s]", VCOS_FUNCTION);
1397
1398 return tvservice_send_command( VC_TV_QUERY_MODE_SUPPORT, display_id, &param, sizeof(TV_QUERY_MODE_SUPPORT_PARAM_T), 1);
1399}
1400
1401VCHPRE_ int VCHPOST_ vc_tv_hdmi_mode_supported(HDMI_RES_GROUP_T group,
1402 uint32_t mode) {
1403 return vc_tv_hdmi_mode_supported_id(default_display_number, group, mode);
1404}
1405
1406/***********************************************************
1407 * Name: vc_tv_hdmi_audio_supported
1408 *
1409 * Arguments:
1410 * audio format (EDID_AudioFormat + EDID_AudioCodingExtension),
1411 * no. of channels (1-8),
1412 * sample rate (EDID_AudioSampleRate except "refer to header"),
1413 * bit rate (or sample size if pcm)
1414 * use EDID_AudioSampleSize as sample size argument
1415 *
1416 * Description: Query if a particular audio format is supported
1417 *
1418 * Returns: single value return which will be flags in EDID_AUDIO_SUPPORT_FLAG_T
1419 * zero means everything is supported, < 0 means error
1420 *
1421 ***********************************************************/
1422VCHPRE_ int VCHPOST_ vc_tv_hdmi_audio_supported_id(uint32_t display_id,
1423 uint32_t audio_format, uint32_t num_channels,
1424 EDID_AudioSampleRate fs, uint32_t bitrate) {
1425 TV_QUERY_AUDIO_SUPPORT_PARAM_T param = { VC_HTOV32(audio_format),
1426 VC_HTOV32(num_channels),
1427 VC_HTOV32(fs),
1428 VC_HTOV32(bitrate) };
1429 vcos_log_trace("[%s]", VCOS_FUNCTION);
1430 if(!vcos_verify(num_channels > 0 && num_channels <= 8 && fs != EDID_AudioSampleRate_eReferToHeader))
1431 return -1;
1432
1433 return tvservice_send_command( VC_TV_QUERY_AUDIO_SUPPORT, display_id, &param, sizeof(TV_QUERY_AUDIO_SUPPORT_PARAM_T), 1);
1434}
1435
1436
1437VCHPRE_ int VCHPOST_ vc_tv_hdmi_audio_supported(uint32_t audio_format, uint32_t num_channels,
1438 EDID_AudioSampleRate fs, uint32_t bitrate) {
1439 return vc_tv_hdmi_audio_supported_id(default_display_number, audio_format, num_channels,
1440 fs, bitrate);
1441}
1442
1443/***********************************************************
1444 * Name: vc_tv_enable_copyprotect
1445 *
1446 * Arguments:
1447 * copy protect mode (only used for SDTV), time out in milliseconds
1448 *
1449 * Description: Enable copy protection (either HDMI or SDTV must be powered on)
1450 *
1451 * Returns: single value return 0 means success, additional result via callback
1452 *
1453 ***********************************************************/
1454VCHPRE_ int VCHPOST_ vc_tv_enable_copyprotect_id(uint32_t display_id, uint32_t cp_mode, uint32_t timeout) {
1455 TV_ENABLE_COPY_PROTECT_PARAM_T param = {VC_HTOV32(cp_mode), VC_HTOV32(timeout)};
1456 vcos_log_trace("[%s]", VCOS_FUNCTION);
1457 return tvservice_send_command( VC_TV_ENABLE_COPY_PROTECT, display_id, &param, sizeof(TV_ENABLE_COPY_PROTECT_PARAM_T), 1);
1458}
1459
1460VCHPRE_ int VCHPOST_ vc_tv_enable_copyprotect(uint32_t cp_mode, uint32_t timeout) {
1461 return vc_tv_enable_copyprotect_id(default_display_number, cp_mode, timeout);
1462}
1463
1464/***********************************************************
1465 * Name: vc_tv_disable_copyprotect
1466 *
1467 * Arguments:
1468 * none
1469 *
1470 * Description: Disable copy protection (either HDMI or SDTV must be powered on)
1471 *
1472 * Returns: single value return 0 means success, additional result via callback
1473 *
1474 ***********************************************************/
1475VCHPRE_ int VCHPOST_ vc_tv_disable_copyprotect_id(uint32_t display_id) {
1476 vcos_log_trace("[%s]", VCOS_FUNCTION);
1477 return tvservice_send_command( VC_TV_DISABLE_COPY_PROTECT, display_id, NULL, 0, 1);
1478}
1479
1480VCHPRE_ int VCHPOST_ vc_tv_disable_copyprotect( void ) {
1481 return vc_tv_disable_copyprotect_id(default_display_number);
1482}
1483
1484/***********************************************************
1485 * Name: vc_tv_show_info
1486 *
1487 * Arguments:
1488 * show (1) or hide (0) info screen
1489 *
1490 * Description: Show or hide info screen, only works in HDMI at the moment
1491 *
1492 * Returns: zero if command is successfully sent
1493 *
1494 ***********************************************************/
1495VCHPRE_ int VCHPOST_ vc_tv_show_info_id(uint32_t display_id, uint32_t show) {
1496 TV_SHOW_INFO_PARAM_T param = {VC_HTOV32(show)};
1497 vcos_log_trace("[%s]", VCOS_FUNCTION);
1498 return tvservice_send_command( VC_TV_SHOW_INFO, display_id, &param, sizeof(TV_SHOW_INFO_PARAM_T), 0);
1499}
1500
1501VCHPRE_ int VCHPOST_ vc_tv_show_info(uint32_t show) {
1502 return vc_tv_show_info_id(default_display_number, show);
1503}
1504
1505/***********************************************************
1506 * Name: vc_tv_hdmi_get_av_latency
1507 *
1508 * Arguments:
1509 * none
1510 *
1511 * Description: Get the AV latency (in ms) for HDMI (lipsync), only valid if
1512 * HDMI is currently powered on, otherwise you get zero
1513 *
1514 * Returns: latency (zero if error or latency is not defined), < 0 if failed to send command)
1515 *
1516 ***********************************************************/
1517VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_av_latency_id(uint32_t display_id) {
1518
1519 vcos_log_trace("[%s]", VCOS_FUNCTION);
1520 return tvservice_send_command( VC_TV_GET_AV_LATENCY, display_id, NULL, 0, 1);
1521}
1522
1523VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_av_latency( void ) {
1524 return vc_tv_hdmi_get_av_latency_id(default_display_number);
1525}
1526
1527/***********************************************************
1528 * Name: vc_tv_hdmi_set_hdcp_key
1529 *
1530 * Arguments:
1531 * key block, whether we wait (1) or not (0) for the key to download
1532 *
1533 * Description: Download HDCP key
1534 *
1535 * Returns: single value return indicating download status
1536 * (or queued status if we don't wait)
1537 * Callback indicates the validity of key
1538 *
1539 ***********************************************************/
1540VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_hdcp_key_id(uint32_t display_id, const uint8_t *key) {
1541 TV_HDCP_SET_KEY_PARAM_T param;
1542
1543 vcos_log_trace("[%s]", VCOS_FUNCTION);
1544 if(!vcos_verify(key))
1545 return -1;
1546 memcpy(param.key, key, HDCP_KEY_BLOCK_SIZE);
1547 return tvservice_send_command( VC_TV_HDCP_SET_KEY, display_id, &param, sizeof(param), 0);
1548}
1549
1550VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_hdcp_key(const uint8_t *key) {
1551 return vc_tv_hdmi_set_hdcp_key_id(default_display_number, key);
1552}
1553
1554/***********************************************************
1555 * Name: vc_tv_hdmi_set_hdcp_revoked_list
1556 *
1557 * Arguments:
1558 * list, size of list
1559 *
1560 * Description: Download HDCP revoked list
1561 *
1562 * Returns: single value return indicating download status
1563 * (or queued status if we don't wait)
1564 * Callback indicates the number of keys set (zero if failed, unless you are clearing the list)
1565 *
1566 ***********************************************************/
1567VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_hdcp_revoked_list_id(uint32_t display_id, const uint8_t *list, uint32_t num_keys) {
1568 TV_HDCP_SET_SRM_PARAM_T param = {VC_HTOV32(num_keys)};
1569 int success = tvservice_send_command( VC_TV_HDCP_SET_SRM, display_id, &param, sizeof(TV_HDCP_SET_SRM_PARAM_T), 0);
1570
1571 vcos_log_trace("[%s]", VCOS_FUNCTION);
1572 if(success == 0 && num_keys && list) { //Set num_keys to zero if we are clearing the list
1573 //Sent the command, now download the list
1574 if(tvservice_lock_obtain() == 0)
1575 {
1576 success = vchi_bulk_queue_transmit( tvservice_client.client_handle[0],
1577 list,
1578 num_keys * HDCP_KSV_LENGTH,
1579 VCHI_FLAGS_BLOCK_UNTIL_QUEUED,
1580 0 );
1581 tvservice_lock_release();
1582 }
1583 else
1584 success = -1;
1585 }
1586 return success;
1587}
1588
1589VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_hdcp_revoked_list(const uint8_t *list, uint32_t num_keys) {
1590 return vc_tv_hdmi_set_hdcp_revoked_list_id(default_display_number, list, num_keys);
1591}
1592
1593/***********************************************************
1594 * Name: vc_tv_hdmi_set_spd
1595 *
1596 * Arguments:
1597 * manufacturer, description, product type (HDMI_SPD_TYPE_CODE_T)
1598 *
1599 * Description: Set SPD
1600 *
1601 * Returns: whether command was sent successfully (zero means success)
1602 *
1603 ***********************************************************/
1604VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_spd_id(uint32_t display_id, const char *manufacturer, const char *description, HDMI_SPD_TYPE_CODE_T type) {
1605 TV_SET_SPD_PARAM_T param;
1606 vcos_log_trace("[%s]", VCOS_FUNCTION);
1607
1608 if(!vcos_verify(manufacturer && description))
1609 return -1;
1610
1611 memcpy(param.manufacturer, manufacturer, TV_SPD_NAME_LEN);
1612 memcpy(param.description, description, TV_SPD_DESC_LEN);
1613 param.type = VC_HTOV32(type);
1614 return tvservice_send_command( VC_TV_SET_SPD, display_id, &param, sizeof(TV_SET_SPD_PARAM_T), 0);
1615}
1616
1617VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_spd(const char *manufacturer, const char *description, HDMI_SPD_TYPE_CODE_T type) {
1618 return vc_tv_hdmi_set_spd_id(default_display_number, manufacturer, description, type);
1619}
1620
1621/***********************************************************
1622 * Name: vc_tv_hdmi_set_display_options
1623 *
1624 * Arguments:
1625 * aspect ratio (HDMI_ASPECT_T enum), left/right bar width, top/bottom bar height
1626 *
1627 * Description: Set active area for HDMI (bar width/height should be set to zero if absent)
1628 *
1629 * Returns: whether command was sent successfully (zero means success)
1630 *
1631 ***********************************************************/
1632VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_display_options_id(uint32_t display_id,
1633 HDMI_ASPECT_T aspect,
1634 uint32_t left_bar_width, uint32_t right_bar_width,
1635 uint32_t top_bar_height, uint32_t bottom_bar_height,
1636 uint32_t overscan_flags) {
1637 TV_SET_DISPLAY_OPTIONS_PARAM_T param;
1638 vcos_log_trace("[%s]", VCOS_FUNCTION);
1639
1640 param.aspect = VC_HTOV32(aspect);
1641 param.vertical_bar_present = VC_HTOV32((left_bar_width || right_bar_width)? VC_TRUE : VC_FALSE);
1642 param.left_bar_width = VC_HTOV32(left_bar_width);
1643 param.right_bar_width = VC_HTOV32(right_bar_width);
1644 param.horizontal_bar_present = VC_HTOV32((top_bar_height || bottom_bar_height)? VC_TRUE : VC_FALSE);
1645 param.top_bar_height = VC_HTOV32(top_bar_height);
1646 param.bottom_bar_height = VC_HTOV32(bottom_bar_height);
1647 param.overscan_flags = VC_HTOV32(overscan_flags);
1648 return tvservice_send_command( VC_TV_SET_DISPLAY_OPTIONS, display_id, &param, sizeof(TV_SET_DISPLAY_OPTIONS_PARAM_T), 0);
1649}
1650
1651VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_display_options(HDMI_ASPECT_T aspect,
1652 uint32_t left_bar_width, uint32_t right_bar_width,
1653 uint32_t top_bar_height, uint32_t bottom_bar_height,
1654 uint32_t overscan_flags) {
1655 return vc_tv_hdmi_set_display_options_id(default_display_number, aspect,
1656 left_bar_width, right_bar_width,
1657 top_bar_height, bottom_bar_height,
1658 overscan_flags);
1659}
1660
1661/***********************************************************
1662 * Name: vc_tv_test_mode_start
1663 *
1664 * Arguments:
1665 * 24-bit colour, test mode (TV_TEST_MODE_T enum)
1666 *
1667 * Description: Power on HDMI to test mode, HDMI must be off to start with
1668 *
1669 * Returns: whether command was sent successfully (zero means success)
1670 *
1671 ***********************************************************/
1672VCHPRE_ int VCHPOST_ vc_tv_test_mode_start_id(uint32_t display_id, uint32_t colour, TV_TEST_MODE_T test_mode) {
1673 TV_TEST_MODE_START_PARAM_T param = {VC_HTOV32(colour), VC_HTOV32(test_mode)};
1674
1675 vcos_log_trace("[%s]", VCOS_FUNCTION);
1676 return tvservice_send_command( VC_TV_TEST_MODE_START, display_id, &param, sizeof(TV_TEST_MODE_START_PARAM_T), 0);
1677}
1678
1679VCHPRE_ int VCHPOST_ vc_tv_test_mode_start(uint32_t colour, TV_TEST_MODE_T test_mode) {
1680 return vc_tv_test_mode_start_id(default_display_number, colour, test_mode);
1681}
1682
1683/***********************************************************
1684 * Name: vc_tv_test_mode_stop
1685 *
1686 * Arguments:
1687 * none
1688 *
1689 * Description: Stop test mode and power down HDMI
1690 *
1691 * Returns: whether command was sent successfully (zero means success)
1692 *
1693 ***********************************************************/
1694VCHPRE_ int VCHPOST_ vc_tv_test_mode_stop_id(uint32_t display_id) {
1695 vcos_log_trace("[%s]", VCOS_FUNCTION);
1696 return tvservice_send_command( VC_TV_TEST_MODE_STOP, display_id, NULL, 0, 0);
1697}
1698
1699VCHPRE_ int VCHPOST_ vc_tv_test_mode_stop( void ) {
1700 return vc_tv_test_mode_stop_id(default_display_number);
1701}
1702
1703/***********************************************************
1704 * Name: vc_tv_hdmi_ddc_read
1705 *
1706 * Arguments:
1707 * offset, length to read, pointer to buffer, must be 16 byte aligned
1708 *
1709 * Description: ddc read over i2c (HDMI only at the moment)
1710 *
1711 * Returns: length of data read (so zero means error) and the buffer will be filled
1712 * only if no error
1713 *
1714 ***********************************************************/
1715VCHPRE_ int VCHPOST_ vc_tv_hdmi_ddc_read_id(uint32_t display_id, uint32_t offset, uint32_t length, uint8_t *buffer) {
1716 int success;
1717 TV_DDC_READ_PARAM_T param = {VC_HTOV32(offset), VC_HTOV32(length)};
1718
1719 vcos_log_trace("[%s]", VCOS_FUNCTION);
1720
1721 /*if(!vcos_verify(buffer && (((uint32_t) buffer) % 16) == 0))
1722 return -1;*/
1723
1724 vchi_service_use(tvservice_client.client_handle[0]);
1725 success = tvservice_send_command( VC_TV_DDC_READ, display_id, &param, sizeof(TV_DDC_READ_PARAM_T), 1);
1726
1727 if(success == 0) {
1728 success = tvservice_wait_for_bulk_receive(buffer, length);
1729 }
1730 vchi_service_release(tvservice_client.client_handle[0]);
1731 return (success == 0)? length : 0; //Either return the whole block or nothing
1732}
1733
1734VCHPRE_ int VCHPOST_ vc_tv_hdmi_ddc_read(uint32_t offset, uint32_t length, uint8_t *buffer) {
1735 return vc_tv_hdmi_ddc_read_id(default_display_number, offset, length, buffer);
1736}
1737
1738/**
1739 * Sets whether the TV is attached or unplugged.
1740 * Required when hotplug interrupt is not handled by VideoCore.
1741 */
1742VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_attached_id(uint32_t display_id, uint32_t attached)
1743{
1744 vcos_log_trace("[%s] attached %d", VCOS_FUNCTION, attached);
1745 return tvservice_send_command(VC_TV_SET_ATTACHED, display_id, &attached, sizeof(uint32_t), 0);
1746}
1747
1748VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_attached(uint32_t attached) {
1749 return vc_tv_hdmi_set_attached_id(default_display_number, attached);
1750}
1751
1752/**
1753 * Sets a property in HDMI output
1754 */
1755VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_property_id(uint32_t display_id, const HDMI_PROPERTY_PARAM_T *property) {
1756 HDMI_PROPERTY_PARAM_T _property;
1757 if(vcos_verify(property)) {
1758 memcpy(&_property, property, sizeof(_property));
1759 vcos_log_trace("[%s] property:%d values:%d,%d", VCOS_FUNCTION, property->property, property->param1, property->param2);
1760 return tvservice_send_command(VC_TV_SET_PROP, display_id, &_property, sizeof(_property), 1);
1761 }
1762 return -1;
1763}
1764
1765VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_property(const HDMI_PROPERTY_PARAM_T *property) {
1766 return vc_tv_hdmi_set_property_id(default_display_number, property);
1767}
1768
1769/**
1770 * Gets a property from HDMI
1771 */
1772VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_property_id(uint32_t display_id, HDMI_PROPERTY_PARAM_T *property) {
1773 int ret = -1;
1774 if(vcos_verify(property)) {
1775 TV_GET_PROP_PARAM_T param = {0, {HDMI_PROPERTY_MAX, 0, 0}};
1776 uint32_t prop = (uint32_t) property->property;
1777 property->param1 = property->param2 = 0;
1778 vcos_log_trace("[%s] property:%d", VCOS_FUNCTION, property->property);
1779 if((ret = tvservice_send_command_reply( VC_TV_GET_PROP, display_id, &prop, sizeof(prop),
1780 &param, sizeof(param))) == VC_HDMI_SUCCESS) {
1781 property->param1 = param.property.param1;
1782 property->param2 = param.property.param2;
1783 }
1784 }
1785 return ret;
1786}
1787
1788VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_property(HDMI_PROPERTY_PARAM_T *property) {
1789 return vc_tv_hdmi_get_property_id(default_display_number, property);
1790}
1791
1792/**
1793 * Converts the notification reason to a string.
1794 *
1795 * @param reason is the notification reason
1796 * @return The notification reason as a string.
1797 */
1798VCHPRE_ const char* vc_tv_notification_name(VC_HDMI_NOTIFY_T reason)
1799{
1800 switch (reason)
1801 {
1802 case VC_HDMI_UNPLUGGED:
1803 return "VC_HDMI_UNPLUGGED";
1804 case VC_HDMI_ATTACHED:
1805 return "VC_HDMI_ATTACHED";
1806 case VC_HDMI_DVI:
1807 return "VC_HDMI_DVI";
1808 case VC_HDMI_HDMI:
1809 return "VC_HDMI_HDMI";
1810 case VC_HDMI_HDCP_UNAUTH:
1811 return "VC_HDMI_HDCP_UNAUTH";
1812 case VC_HDMI_HDCP_AUTH:
1813 return "VC_HDMI_HDCP_AUTH";
1814 case VC_HDMI_HDCP_KEY_DOWNLOAD:
1815 return "VC_HDMI_HDCP_KEY_DOWNLOAD";
1816 case VC_HDMI_HDCP_SRM_DOWNLOAD:
1817 return "VC_HDMI_HDCP_SRM_DOWNLOAD";
1818 case VC_HDMI_CHANGING_MODE:
1819 return "VC_HDMI_CHANGING_MODE";
1820 default:
1821 return "VC_HDMI_UNKNOWN";
1822 }
1823}
1824
1825// temporary: maintain backwards compatibility
1826VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_supported_modes_id(uint32_t display_id, HDMI_RES_GROUP_T group,
1827 TV_SUPPORTED_MODE_T *supported_modes_deprecated,
1828 uint32_t max_supported_modes,
1829 HDMI_RES_GROUP_T *preferred_group,
1830 uint32_t *preferred_mode) {
1831 TV_SUPPORTED_MODE_NEW_T *supported_modes_new = malloc(max_supported_modes * sizeof *supported_modes_new);
1832 int modes_copied = vc_tv_hdmi_get_supported_modes_new_id(display_id, group==3 ? HDMI_RES_GROUP_CEA:group, supported_modes_new, max_supported_modes, preferred_group, preferred_mode);
1833 int i, j=0;
1834
1835 for (i=0; i<modes_copied; i++) {
1836 TV_SUPPORTED_MODE_T *q = supported_modes_deprecated + j;
1837 TV_SUPPORTED_MODE_NEW_T *p = supported_modes_new + i;
1838 if (group != 3 || (p->struct_3d_mask & HDMI_3D_STRUCT_SIDE_BY_SIDE_HALF_HORIZONTAL)) {
1839 q->scan_mode = p->scan_mode;
1840 q->native = p->native;
1841 q->code = p->code;
1842 q->frame_rate = p->frame_rate;
1843 q->width = p->width;
1844 q->height = p->height;
1845 j++;
1846 }
1847 }
1848 free(supported_modes_new);
1849
1850 return 0;
1851}
1852
1853VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_supported_modes(HDMI_RES_GROUP_T group,
1854 TV_SUPPORTED_MODE_T *supported_modes_deprecated,
1855 uint32_t max_supported_modes,
1856 HDMI_RES_GROUP_T *preferred_group,
1857 uint32_t *preferred_mode) {
1858 return vc_tv_hdmi_get_supported_modes_id(default_display_number, group,
1859 supported_modes_deprecated,
1860 max_supported_modes,
1861 preferred_group,
1862 preferred_mode);
1863}
1864
1865/**
1866 * Get the unique device ID from the EDID
1867 * @param pointer to device ID struct
1868 * @return zero if successful, non-zero if failed.
1869 */
1870VCHPRE_ int VCHPOST_ vc_tv_get_device_id_id(uint32_t display_id, TV_DEVICE_ID_T *id) {
1871 int ret = -1;
1872 TV_DEVICE_ID_T param;
1873 memset(&param, 0, sizeof(TV_DEVICE_ID_T));
1874 if(vcos_verify(id)) {
1875 if((ret = tvservice_send_command_reply( VC_TV_GET_DEVICE_ID, display_id, NULL, 0,
1876 &param, sizeof(param))) == VC_HDMI_SUCCESS) {
1877 memcpy(id, &param, sizeof(TV_DEVICE_ID_T));
1878 } else {
1879 id->vendor[0] = '\0';
1880 id->monitor_name[0] = '\0';
1881 id->serial_num = 0;
1882 }
1883 }
1884 return ret;
1885}
1886
1887VCHPRE_ int VCHPOST_ vc_tv_get_device_id(TV_DEVICE_ID_T *id) {
1888 return vc_tv_get_device_id_id(default_display_number, id);
1889}
1890
1891// temporary: maintain backwards compatibility
1892VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_explicit_id(uint32_t display_id, HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code) {
1893 if (group == HDMI_RES_GROUP_CEA_3D) {
1894 HDMI_PROPERTY_PARAM_T property;
1895 property.property = HDMI_PROPERTY_3D_STRUCTURE;
1896 property.param1 = HDMI_RES_GROUP_CEA;
1897 property.param2 = 0;
1898 vc_tv_hdmi_set_property_id(display_id, &property);
1899 group = HDMI_RES_GROUP_CEA;
1900 }
1901 return vc_tv_hdmi_power_on_explicit_new_id(display_id, mode, group, code);
1902}
1903
1904VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_explicit(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code) {
1905 return vc_tv_hdmi_power_on_explicit_id(default_display_number, mode, group, code);
1906}
1907
1908VCHPRE_ int VCHPOST_ vc_tv_get_attached_devices(TV_ATTACHED_DEVICES_T *devices)
1909{
1910 memset(devices, 0, sizeof(*devices));
1911
1912 return tvservice_send_command_reply(VC_TV_GET_ATTACHED_DEVICES, INVALID_DISPLAY_ID, NULL, 0,
1913 devices, sizeof(*devices));
1914}
1915