/*************************************************************************** * Copyright (c) 2012, Broadcom Corporation * All Rights Reserved * Confidential Property of Broadcom Corporation * * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. * * $brcm_Workfile: $ * $brcm_Revision: $ * $brcm_Date: $ * * Module Description: * * Revision History: * * $brcm_Log: $ * ***************************************************************************/ #include "bsettop_display.h" #include "gist.h" #include "nexus_platform.h" #include "nexus_display.h" #include "nexus_display_vbi.h" #include "nexus_video_window.h" #include "nexus_video_adj.h" #include "nexus_hdmi_output_hdcp.h" #include "nexus_hdmi_output_cec.h" #include "bsettop_display.h" #include "bsettop_hdmi.h" #include "bsettop_display_n_priv.h" #include "bkni_event_group.h" #include "bos_task_priorities.h" struct bdisplay { bool open; NEXUS_DisplayHandle handle[eBDISPLAY_ID_MAX]; NEXUS_VideoWindowHandle window[eBDISPLAY_ID_MAX]; NEXUS_VideoFormat format; bdisplay_settings settings; bsettop_hdcp_authentication_cb_t hdcp_authentication_cb; /* hdcp check callback */ }; #define HDMI_TASK_STACK_SIZE 1024 struct bsettop_hdmi { NEXUS_HdmiOutputHandle handle; NEXUS_DisplayHandle display; bool connected; bool hdcpStarted; bool native; BKNI_EventHandle hotplugEvent; BKNI_EventHandle hdcpEvent; BKNI_EventHandle cecEvent; BKNI_EventGroupHandle hdmiEvent; b_task_t task_h; unsigned int task_stack[HDMI_TASK_STACK_SIZE]; bsettop_hdcp_authentication_cb_t hdcp_authentication_cb; bool transmitEncrypted; bool pjCheckEnabled; bool checkHDCP; }; static struct bdisplay s_display = { false, /*open*/ {NULL, NULL}, /*display handle*/ {NULL, NULL}, /*window handle*/ NEXUS_VideoFormat_e720p, /*format*/ { bdisplay_format_auto, NULL, /* normaly used to notify HDMI of rate change */ NULL, /* passed to rate_change_cb */ NULL, /* RF output */ NULL, /* SPDIF output */ NULL, /* I2S output */ bdisplay_sd_output_options_auto, bdisplay_hd_output_options_auto, 0, /* sharpness */ false, /* force_change_format */ false, /* deinterlace */ false, /* shrink_width */ 0 /* deinterlacer_affinity */ }, NULL }; static struct bsettop_hdmi s_hdmi_output; bvideo_format nexus2dta_vformat(NEXUS_VideoFormat vformat); boutput_hdmi_hdcp_state nexus2dta_hdcp_state(NEXUS_HdmiOutputHdcpState hdcpState); extern bresult bgraphics_p_format_change(NEXUS_DisplayHandle display, int id); BDBG_MODULE(bdisplay); bdisplay_t bdisplay_open(int id) { NEXUS_PlatformConfiguration platformConfig; NEXUS_DisplaySettings displaySettings; int i; BSTD_UNUSED(id); NEXUS_Platform_GetConfiguration(&platformConfig); NEXUS_Display_GetDefaultSettings(&displaySettings); displaySettings.format = s_display.format; s_display.handle[eBDISPLAY_HDMI] = NEXUS_Display_Open(0, &displaySettings); NEXUS_Display_GetDefaultSettings(&displaySettings); displaySettings.format = NEXUS_VideoFormat_eNtsc; s_display.handle[eBDISPLAY_COMPOSITE] = NEXUS_Display_Open(1, &displaySettings); NEXUS_Display_AddOutput(s_display.handle[eBDISPLAY_COMPOSITE], NEXUS_CompositeOutput_GetConnector(platformConfig.outputs.composite[0])); #if NEXUS_NUM_RFM_OUTPUTS NEXUS_Display_AddOutput(s_display.handle[eBDISPLAY_COMPOSITE], NEXUS_Rfm_GetVideoConnector(platformConfig.outputs.rfm[0])); #endif for (i=0; iwindow[index]; } /* * Summary: * to set initial hdmi video format if not default 720p * Description: * to set initial hdmi video format if not default 720p **/ bresult bdisplay_set_init_format(bobject_t display_id, bsettop_display_format_t format) { NEXUS_VideoFormat fmt = NEXUS_VideoFormat_eUnknown; if (eBDISPLAY_HDMI != display_id) return berr_not_supported; if (bdisplay_format_1080i == format) fmt = NEXUS_VideoFormat_e1080i; else if (bdisplay_format_480i == format) fmt = NEXUS_VideoFormat_eNtsc; else if (bdisplay_format_480p == format) fmt = NEXUS_VideoFormat_e480p; if (fmt != NEXUS_VideoFormat_eUnknown) s_display.format = fmt; return b_ok; } NEXUS_DisplayHandle bdisplay_p_get_handle(bdisplay_t display, int id) { if (id >= eBDISPLAY_ID_MAX) return NULL; return display->handle[id]; } NEXUS_VideoFormat bdisplay_hdmi_format_auto(void) { return NEXUS_VideoFormat_e720p; } bresult bdisplay_set(bdisplay_t display, bdisplay_settings *settings) { NEXUS_VideoFormat vformat; NEXUS_DisplaySettings dsettings; NEXUS_DisplayAspectRatio aspect; NEXUS_Display_GetSettings(display->handle[eBDISPLAY_HDMI], &dsettings); switch (settings->format) { case bdisplay_format_480i: vformat = NEXUS_VideoFormat_eNtsc; break; case bdisplay_format_480p: vformat = NEXUS_VideoFormat_e480p; break; case bdisplay_format_720p: vformat = NEXUS_VideoFormat_e720p; break; case bdisplay_format_1080p24: vformat = NEXUS_VideoFormat_e1080p24hz; break; case bdisplay_format_1080p30: vformat = NEXUS_VideoFormat_e1080p30hz; break; case bdisplay_format_auto: vformat = bdisplay_hdmi_format_auto(); break; case bdisplay_format_1080i: default: vformat = NEXUS_VideoFormat_e1080i; break; } switch (settings->hd_options) { case bdisplay_hd_output_options_stretch: aspect = NEXUS_DisplayAspectRatio_e16x9; break; case bdisplay_hd_output_options_pillarbox: aspect = NEXUS_DisplayAspectRatio_e4x3; break; case bdisplay_hd_output_options_auto: default: aspect = NEXUS_DisplayAspectRatio_eAuto; break; } if (dsettings.format != vformat) { dsettings.format = vformat; dsettings.aspectRatio = aspect; display->format = vformat; NEXUS_Display_SetSettings(display->handle[eBDISPLAY_HDMI], &dsettings); bgraphics_p_format_change(display->handle[eBDISPLAY_HDMI], eBDISPLAY_HDMI); } else if (dsettings.aspectRatio != aspect) { dsettings.aspectRatio = aspect; NEXUS_Display_SetSettings(display->handle[eBDISPLAY_HDMI], &dsettings); } if ((settings->deinterlace != display->settings.deinterlace) || (settings->shrink_width != display->settings.shrink_width) || (settings->deinterlacer_affinity != display->settings.deinterlacer_affinity)) { display->settings.shrink_width = settings->shrink_width; bdisplay_set_deinterlacer(display, settings->deinterlacer_affinity, settings->deinterlace); } display->settings = *settings; return b_ok; } void bdisplay_get(bdisplay_t display, bdisplay_settings *settings) { BDBG_ASSERT(display); BDBG_ASSERT(settings); *settings = display->settings; } void bdisplay_get_default_settings(bdisplay_settings *psettings) { if (psettings) BKNI_Memcpy(psettings, &s_display.settings, sizeof(bdisplay_settings)); } void bdisplay_set_deinterlacer(bdisplay_t display, int id, bool enable) { int alt_id; NEXUS_VideoWindowMadSettings madSettings; alt_id = (id == eBDISPLAY_HDMI)?eBDISPLAY_COMPOSITE:eBDISPLAY_HDMI; NEXUS_VideoWindow_GetMadSettings(display->window[alt_id], &madSettings); madSettings.deinterlace = false; NEXUS_VideoWindow_SetMadSettings(display->window[alt_id], &madSettings); NEXUS_VideoWindow_GetMadSettings(display->window[id], &madSettings); #ifdef SHRINK_WIDTH if ((display->format != NEXUS_VideoFormat_e1080p24hz) && (display->format != NEXUS_VideoFormat_e1080p30hz)) madSettings.shrinkWidth = display->settings.shrink_width; #endif madSettings.deinterlace = enable; NEXUS_VideoWindow_SetMadSettings(display->window[id], &madSettings); } void bdisplay_set_coefficient_index(bdisplay_t display, bool horiz, int output, int coeff_idx) { BSTD_UNUSED(display); BSTD_UNUSED(horiz); BSTD_UNUSED(output); BSTD_UNUSED(coeff_idx); } bresult bdisplay_set_vbi_rating_info(bdisplay_t display, unsigned int ratings_tv, unsigned int ratings_movie, unsigned int content_v, unsigned int content_s, unsigned int content_l, unsigned int content_d) { NEXUS_ClosedCaptionData xds_data[3]; size_t written; BSTD_UNUSED(content_v); xds_data[0].field = 1; /* bottom field */ xds_data[0].data[0] = 0x01; xds_data[0].data[1] = 0x05; xds_data[1].field = 1; /* character 1 : x 1 D/a2 a1 a0 r2 r1 r0 * character 2 : x 1 (F)V S L/a3 g2 g1 g0 */ xds_data[1].data[0] = (ratings_movie|0x40) | 0x08 | (content_d<<5); xds_data[1].data[1] = (ratings_tv|0x40) | (content_l<<3) | (content_s<<4) | (content_s<<5); xds_data[2].field = 1; xds_data[2].data[0] = 0x0f; xds_data[2].data[1] = 0x1d; NEXUS_Display_WriteClosedCaption(display->handle[eBDISPLAY_COMPOSITE], xds_data, 3, &written); if (written != 3) { BDBG_WRN(("Rating info wasn't fully written on VBI (%d)", written)); } return BERR_SUCCESS; } /* --------------------------------------------------------------------------- * HDMI * --------------------------------------------------------------------------- */ /* following should match with bsettop_display_format_t */ static NEXUS_VideoFormat s_display_format_to_nexus[] = { NEXUS_VideoFormat_eMax, /* bdisplay_format_auto */ NEXUS_VideoFormat_e1080i, NEXUS_VideoFormat_e720p, NEXUS_VideoFormat_eNtsc, NEXUS_VideoFormat_e480p, NEXUS_VideoFormat_e1080p, NEXUS_VideoFormat_e1080p24hz, NEXUS_VideoFormat_e1080p30hz }; static int s_num_display_format_to_nexus = sizeof(s_display_format_to_nexus)/sizeof(NEXUS_VideoFormat); static void bsettop_hdmi_p_connect(bsettop_hdmi_t hdmi); static void bsettop_hdmi_p_disconnect(bsettop_hdmi_t hdmi); static void bsettop_hdmi_task(void *data); static void bsettop_hdmi_p_hotplug_cb(void *pparam, int iparam) { BSTD_UNUSED(iparam); BKNI_SetEvent((BKNI_EventHandle)pparam); } #ifdef HDCPLIB static void bsettop_hdmi_p_hdcp_state_cb(void *pparam, int iparam) { BSTD_UNUSED(iparam); BKNI_SetEvent((BKNI_EventHandle)pparam); } #endif static void bsettop_hdmi_p_cec_cb(void *pparam, int iparam) { BSTD_UNUSED(iparam); BKNI_SetEvent((BKNI_EventHandle)pparam); } bresult bsettop_hdmi_open(bsettop_hdmi_t *h_hdmi, bdisplay_t display, baudio_decode_t audio_decode, bsettop_hdcp_authentication_cb_t hdcp_authentication_cb) { NEXUS_PlatformConfiguration platformConfig; NEXUS_HdmiOutputSettings hdmiSettings; #ifdef HDCPLIB NEXUS_HdmiOutputHdcpSettings hdcpSettings; #endif #if BHDM_CEC_SUPPORT NEXUS_HdmiOutputCecSettings cecSettings; #endif b_task_params task_params; BSTD_UNUSED(audio_decode); BKNI_Memset(&s_hdmi_output, 0, sizeof(s_hdmi_output)); NEXUS_Platform_GetConfiguration(&platformConfig); s_hdmi_output.handle = platformConfig.outputs.hdmi[0]; s_hdmi_output.display = display->handle[eBDISPLAY_HDMI]; s_hdmi_output.hdcp_authentication_cb = hdcp_authentication_cb; BKNI_CreateEventGroup(&s_hdmi_output.hdmiEvent); BKNI_CreateEvent(&s_hdmi_output.hotplugEvent); BKNI_CreateEvent(&s_hdmi_output.hdcpEvent); BKNI_CreateEvent(&s_hdmi_output.cecEvent); BKNI_AddEventGroup(s_hdmi_output.hdmiEvent, s_hdmi_output.hotplugEvent); BKNI_AddEventGroup(s_hdmi_output.hdmiEvent, s_hdmi_output.hdcpEvent); BKNI_AddEventGroup(s_hdmi_output.hdmiEvent, s_hdmi_output.cecEvent); bsettop_hdmi_p_connect(&s_hdmi_output); NEXUS_HdmiOutput_GetSettings(s_hdmi_output.handle, &hdmiSettings); hdmiSettings.hotplugCallback.callback = bsettop_hdmi_p_hotplug_cb; hdmiSettings.hotplugCallback.context = s_hdmi_output.hotplugEvent; hdmiSettings.cecCallback.callback = bsettop_hdmi_p_cec_cb; hdmiSettings.cecCallback.context = &s_hdmi_output; NEXUS_HdmiOutput_SetSettings(s_hdmi_output.handle, &hdmiSettings); #ifdef HDCPLIB NEXUS_HdmiOutput_GetHdcpSettings(s_hdmi_output.handle, &hdcpSettings); hdcpSettings->stateChangedCallback.callback = bsettop_hdmi_p_hdcp_state_cb; hdcpSettings->stateChangedCallback.context = s_hdmi_output.hdcpEvent; hdcpSettings->successCallback.callback = bsettop_hdmi_p_hdcp_state_cb; hdcpSettings->successCallback.context = s_hdmi_output.hdcpEvent; hdcpSettings->failureCallback.callback = bsettop_hdmi_p_hdcp_state_cb; hdcpSettings->failureCallback.context = s_hdmi_output.hdcpEvent; NEXUS_HdmiOutput_SetHdcpSettings(s_hdmi_output.handle, &hdcpSettings); #endif #if BHDM_CEC_SUPPORT NEXUS_HdmiOutput_GetCecSettings(s_hdmi_output.handle, &cecSettings); cecSettings.enabled = true; NEXUS_HdmiOutput_SetCecSettingS(s_hdmi_output.handle, &cecSettings); #endif task_params.name = "HDMI"; task_params.priority = HDMI_PRIORITY; task_params.stack_size = HDMI_TASK_STACK_SIZE; task_params.stack = s_hdmi_output.task_stack; bos_start_task(&(s_hdmi_output.task_h), &task_params, bsettop_hdmi_task, &s_hdmi_output); s_hdmi_output.connected = true; *h_hdmi = &s_hdmi_output; return b_ok; } void bsettop_hdmi_close(bsettop_hdmi_t h_hdmi) { if (h_hdmi->task_h) bos_stop_task(h_hdmi->task_h); BKNI_RemoveEventGroup(h_hdmi->hdmiEvent, h_hdmi->hotplugEvent); BKNI_RemoveEventGroup(h_hdmi->hdmiEvent, h_hdmi->hdcpEvent); BKNI_RemoveEventGroup(h_hdmi->hdmiEvent, h_hdmi->cecEvent); BKNI_DestroyEvent(h_hdmi->hotplugEvent); BKNI_DestroyEvent(h_hdmi->hdcpEvent); BKNI_DestroyEvent(h_hdmi->cecEvent); BKNI_DestroyEventGroup(h_hdmi->hdmiEvent); } bool bsettop_hdmi_is_video_fmt_supported(bsettop_display_format_t format) { NEXUS_HdmiOutputStatus status; if (format == bdisplay_format_auto) return true; if (format >= bdisplay_format_max) { BDBG_ERR(("%s: Invalid format", __func__)); return false; } NEXUS_HdmiOutput_GetStatus(s_hdmi_output.handle, &status); if (status.videoFormatSupported[s_display_format_to_nexus[format]]) return true; else return false; } bool bsettop_hdmi_get_RGB_output(void) { NEXUS_HdmiOutputSettings settings; NEXUS_HdmiOutput_GetSettings(s_hdmi_output.handle, &settings); if (settings.colorSpace == NEXUS_ColorSpace_eRgb) return true; else return false; } bresult bsettop_hdmi_set_RGB_output(bool bRGB) { NEXUS_HdmiOutputSettings settings; NEXUS_HdmiOutput_GetSettings(s_hdmi_output.handle, &settings); if (bRGB) settings.colorSpace = NEXUS_ColorSpace_eRgb; else settings.colorSpace = NEXUS_ColorSpace_eYCbCr444; NEXUS_HdmiOutput_SetSettings(s_hdmi_output.handle, &settings); return b_ok; } /* TODO:: * does it require new NEXUs API? such as NEXUS_HdmiOutput_SetAudioParams_priv.. */ bool bsettop_hdmi_get_native_audio_mode(void) { return s_hdmi_output.native; } BERR_Code bsettop_hdmi_set_native_audio_mode(bool native) { s_hdmi_output.native = native; return b_ok; } /** * Summary: * Get the current HDMI status */ bresult boutput_hdmi_get_status(bsettop_hdmi_t h_hdmi, boutput_hdmi_status *status /* [out] */ ) { NEXUS_HdmiOutputStatus hdmiStatus; NEXUS_HdmiOutputHdcpStatus hdcpStatus; BDBG_ASSERT(h_hdmi); BDBG_ASSERT(status); NEXUS_HdmiOutput_GetStatus(h_hdmi->handle, &hdmiStatus); NEXUS_HdmiOutput_GetHdcpStatus(h_hdmi->handle, &hdcpStatus); status->connected = hdmiStatus.connected; status->is_hdmi = hdmiStatus.hdmiDevice; status->preferred = bvideo_format_count; switch (hdmiStatus.preferredVideoFormat) { case NEXUS_VideoFormat_eNtsc: status->preferred = bvideo_format_ntsc; break; case NEXUS_VideoFormat_e480p: status->preferred = bvideo_format_480p; break; case NEXUS_VideoFormat_e720p: status->preferred = bvideo_format_720p; break; case NEXUS_VideoFormat_e1080i: status->preferred = bvideo_format_1080i; break; default: break; } if (hdcpStatus.hdcpState == NEXUS_HdmiOutputHdcpState_eEncryptionEnabled) status->hdcp_state = boutput_hdmi_hdcp_state_enabled; else status->hdcp_state = boutput_hdmi_hdcp_state_internal_err; return b_ok; } static void bsettop_hdmi_p_disconnect(bsettop_hdmi_t hdmi) { NEXUS_DisplaySettings displaySettings; #ifdef HDCPLIB if (hdmi->hdcpStarted) { NEXUS_HdmiOutput_DisableHdcpAuthentication(hdmi->handle); hdmi->hdcpStarted = false; } #endif NEXUS_Display_GetSettings(hdmi->display, &displaySettings); NEXUS_Display_RemoveOutput(hdmi->display, NEXUS_HdmiOutput_GetVideoConnector(hdmi->handle)); NEXUS_Display_SetSettings(hdmi->display, &displaySettings); } static void bsettop_hdmi_p_connect(bsettop_hdmi_t hdmi) { NEXUS_DisplaySettings displaySettings; NEXUS_Display_AddOutput(hdmi->display, NEXUS_HdmiOutput_GetVideoConnector(hdmi->handle)); NEXUS_Display_GetSettings(hdmi->display, &displaySettings); NEXUS_Display_SetSettings(hdmi->display, &displaySettings); } static void bsettop_hdmi_task(void *data) { bsettop_hdmi_t hdmi = (bsettop_hdmi_t)data; BKNI_EventHandle events[3]; unsigned i, nevents; int j; NEXUS_HdmiOutputStatus status; NEXUS_HdmiOutputHandle handle = hdmi->handle; NEXUS_DisplaySettings displaySettings; #if HDCPLIB NEXUS_HdmiOutputHdcpStatus hdcpStatus; #endif NEXUS_HdmiOutputCecStatus cecStatus; #if BHDM_CEC_SUPPORT NEXUS_HdmiOutputCecMessageData cecMsg; #endif while (1) { BKNI_WaitForGroup(hdmi->hdmiEvent, -1, events, sizeof(events)/sizeof(*events), &nevents); for (i=0; ihotplugEvent) { /* process hot plug events */ NEXUS_HdmiOutput_GetStatus(handle, &status); if (hdmi->connected == status.connected) { BDBG_WRN(("hotplug state not changed : %d", hdmi->connected)); continue; } hdmi->connected = status.connected; if (hdmi->connected) { /* disconnected -> connected */ BDBG_WRN(("%s:%d HDMI connected", __func__, __LINE__)); NEXUS_Display_GetSettings(hdmi->display, &displaySettings); if (!status.videoFormatSupported[displaySettings.format]) { for (j = 1; j< s_num_display_format_to_nexus; j++) { if (status.videoFormatSupported[s_display_format_to_nexus[j]]) break; } if (j==s_num_display_format_to_nexus) { BDBG_WRN(("%s:%d device doesn't support valid format", __func__, __LINE__)); continue; } displaySettings.format = s_display_format_to_nexus[j]; NEXUS_Display_SetSettings(hdmi->display, &displaySettings); BDBG_WRN(("%s:%d video format not supported. use %d instead", displaySettings.format)); } bsettop_hdmi_p_connect(hdmi); } else { /* connected -> disconnected */ BDBG_WRN(("%s:%d HDMI disconnected", __func__, __LINE__)); bsettop_hdmi_p_disconnect(hdmi); } } #ifdef HDCPLIB /* TODO:: HDCP/CEC event handling */ else if (events[i] == hdmi->hdcpEvent) { /* process hdcp events */ } #endif else if (events[i] == hdmi->cecEvent) { /* process cec events */ NEXUS_HdmiOutput_GetCecStatus(hdmi->handle, &cecStatus); } } } } /* TODO:: platform will handle */ void bsettop_hdmi_standby(bsettop_hdmi_t h_hdmi, bool standby) { BSTD_UNUSED(h_hdmi); BSTD_UNUSED(standby); } bresult bdisplay_vbi_clear_cc(void) { NEXUS_ClosedCaptionData ccData[6]; size_t i, num; /* Force send EDM command to clear */ for (i=0; i<2; i++) { ccData[i].field = 0; ccData[i].data[0] = 0x14; ccData[i].data[1] = 0x2c; } /* Force send ENM command to clear */ for (i=2; i<4; i++) { ccData[i].field = 0; ccData[i].data[0] = 0x14; ccData[i].data[1] = 0x2e; } /* EOC command */ for (i=4; i<6; i++) { ccData[i].field = 0; ccData[i].data[0] = 0x14; ccData[i].data[1] = 0x2f; } NEXUS_Display_WriteClosedCaption(s_display.handle[eBDISPLAY_COMPOSITE], ccData, 6, &num); return b_ok; } bresult bdisplay_vbi_enable_amol_gs(bool enable) { BSTD_UNUSED(enable); return b_ok; } void bsettop_hdmi_mark_dsp_fmt_valid(bsettop_hdmi_t h_hdmi) { BSTD_UNUSED(h_hdmi); }