2 * Copyright (C) 2011-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
23 #include "EGLNativeTypeRaspberryPI.h"
24 #include "utils/log.h"
25 #include "guilib/gui3d.h"
26 #include "linux/DllBCM.h"
27 #include "utils/StringUtils.h"
28 #include "settings/Settings.h"
30 #ifndef __VIDEOCORE4__
31 #define __VIDEOCORE4__
34 #define __VCCOREVER__ 0x04000000
36 #define IS_WIDESCREEN(m) ( m == 3 || m == 7 || m == 9 || \
37 m == 11 || m == 13 || m == 15 || m == 18 || m == 22 || \
38 m == 24 || m == 26 || m == 28 || m == 30 || m == 36 || \
39 m == 38 || m == 43 || m == 45 || m == 49 || m == 51 || \
40 m == 53 || m == 55 || m == 57 || m == 59)
42 #define MAKEFLAGS(group, mode, interlace) \
43 ( ( (mode)<<24 ) | ( (group)<<16 ) | \
44 ( (interlace) != 0 ? D3DPRESENTFLAG_INTERLACED : D3DPRESENTFLAG_PROGRESSIVE) | \
45 ( ((group) == HDMI_RES_GROUP_CEA && IS_WIDESCREEN(mode) ) ? D3DPRESENTFLAG_WIDESCREEN : 0) )
47 #define GETFLAGS_GROUP(f) ( (HDMI_RES_GROUP_T)( ((f) >> 16) & 0xff ))
48 #define GETFLAGS_MODE(f) ( ( (f) >>24 ) & 0xff )
51 //#define DEBUG_PRINT 1
54 #if defined(DEBUG_PRINT)
55 # define DLOG(fmt, args...) printf(fmt, ##args)
57 # define DLOG(fmt, args...)
61 CEGLNativeTypeRaspberryPI::CEGLNativeTypeRaspberryPI()
63 #if defined(TARGET_RASPBERRY_PI)
65 m_nativeWindow = NULL;
69 CEGLNativeTypeRaspberryPI::~CEGLNativeTypeRaspberryPI()
71 #if defined(TARGET_RASPBERRY_PI)
78 bool CEGLNativeTypeRaspberryPI::CheckCompatibility()
80 #if defined(TARGET_RASPBERRY_PI)
81 DLOG("CEGLNativeTypeRaspberryPI::CheckCompatibility\n");
88 void CEGLNativeTypeRaspberryPI::Initialize()
90 #if defined(TARGET_RASPBERRY_PI)
92 m_dispman_element = DISPMANX_NO_HANDLE;
93 m_dispman_display = DISPMANX_NO_HANDLE;
97 m_initDesktopRes = true;
99 m_DllBcmHost = new DllBcmHost;
100 m_DllBcmHost->Load();
104 void CEGLNativeTypeRaspberryPI::Destroy()
106 #if defined(TARGET_RASPBERRY_PI)
107 if(m_DllBcmHost && m_DllBcmHost->IsLoaded())
108 m_DllBcmHost->Unload();
114 bool CEGLNativeTypeRaspberryPI::CreateNativeDisplay()
116 m_nativeDisplay = EGL_DEFAULT_DISPLAY;
120 bool CEGLNativeTypeRaspberryPI::CreateNativeWindow()
122 #if defined(TARGET_RASPBERRY_PI)
124 m_nativeWindow = (EGLNativeWindowType) calloc(1,sizeof( EGL_DISPMANX_WINDOW_T));
125 DLOG("CEGLNativeTypeRaspberryPI::CEGLNativeTypeRaspberryPI\n");
132 bool CEGLNativeTypeRaspberryPI::GetNativeDisplay(XBNativeDisplayType **nativeDisplay) const
136 *nativeDisplay = (XBNativeDisplayType*) &m_nativeDisplay;
140 bool CEGLNativeTypeRaspberryPI::GetNativeWindow(XBNativeDisplayType **nativeWindow) const
142 DLOG("CEGLNativeTypeRaspberryPI::GetNativeWindow\n");
145 *nativeWindow = (XBNativeWindowType*) &m_nativeWindow;
149 bool CEGLNativeTypeRaspberryPI::DestroyNativeDisplay()
151 DLOG("CEGLNativeTypeRaspberryPI::DestroyNativeDisplay\n");
155 bool CEGLNativeTypeRaspberryPI::DestroyNativeWindow()
157 #if defined(TARGET_RASPBERRY_PI)
158 DestroyDispmaxWindow();
159 free(m_nativeWindow);
160 m_nativeWindow = NULL;
161 DLOG("CEGLNativeTypeRaspberryPI::DestroyNativeWindow\n");
168 bool CEGLNativeTypeRaspberryPI::GetNativeResolution(RESOLUTION_INFO *res) const
170 #if defined(TARGET_RASPBERRY_PI)
173 DLOG("CEGLNativeTypeRaspberryPI::GetNativeResolution %s\n", res->strMode.c_str());
180 #if defined(TARGET_RASPBERRY_PI)
181 int CEGLNativeTypeRaspberryPI::FindMatchingResolution(const RESOLUTION_INFO &res, const std::vector<RESOLUTION_INFO> &resolutions)
183 for (int i = 0; i < (int)resolutions.size(); i++)
185 if(resolutions[i].iScreenWidth == res.iScreenWidth && resolutions[i].iScreenHeight == res.iScreenHeight && resolutions[i].fRefreshRate == res.fRefreshRate &&
186 (resolutions[i].dwFlags & (D3DPRESENTFLAG_MODE3DSBS|D3DPRESENTFLAG_MODE3DTB)) == (res.dwFlags & (D3DPRESENTFLAG_MODE3DSBS|D3DPRESENTFLAG_MODE3DTB)))
195 #if defined(TARGET_RASPBERRY_PI)
196 int CEGLNativeTypeRaspberryPI::AddUniqueResolution(const RESOLUTION_INFO &res, std::vector<RESOLUTION_INFO> &resolutions)
198 int i = FindMatchingResolution(res, resolutions);
200 { // don't replace a progressive resolution with an interlaced one of same resolution
201 if (!(res.dwFlags & D3DPRESENTFLAG_INTERLACED))
202 resolutions[i] = res;
206 resolutions.push_back(res);
212 bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
214 #if defined(TARGET_RASPBERRY_PI)
215 if(!m_DllBcmHost || !m_nativeWindow)
218 DestroyDispmaxWindow();
220 if(!m_fixedMode && GETFLAGS_GROUP(res.dwFlags) && GETFLAGS_MODE(res.dwFlags))
222 sem_init(&m_tv_synced, 0, 0);
223 m_DllBcmHost->vc_tv_register_callback(CallbackTvServiceCallback, this);
225 if (res.dwFlags & (D3DPRESENTFLAG_MODE3DSBS|D3DPRESENTFLAG_MODE3DTB))
227 /* inform TV of any 3D settings. Note this property just applies to next hdmi mode change, so no need to call for 2D modes */
228 HDMI_PROPERTY_PARAM_T property;
229 property.property = HDMI_PROPERTY_3D_STRUCTURE;
230 if (res.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
231 property.param1 = HDMI_3D_FORMAT_SBS_HALF;
232 else if (res.dwFlags & D3DPRESENTFLAG_MODE3DTB)
233 property.param1 = HDMI_3D_FORMAT_TB_HALF;
235 property.param1 = HDMI_3D_FORMAT_NONE;
237 vc_tv_hdmi_set_property(&property);
239 int success = m_DllBcmHost->vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags));
243 CLog::Log(LOGDEBUG, "EGL set HDMI mode (%d,%d)=%d%s%s\n",
244 GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags), success,
245 (res.dwFlags & D3DPRESENTFLAG_MODE3DSBS) ? " SBS":"",
246 (res.dwFlags & D3DPRESENTFLAG_MODE3DTB) ? " TB":"");
248 sem_wait(&m_tv_synced);
252 CLog::Log(LOGERROR, "EGL failed to set HDMI mode (%d,%d)=%d%s%s\n",
253 GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags), success,
254 (res.dwFlags & D3DPRESENTFLAG_MODE3DSBS) ? " SBS":"",
255 (res.dwFlags & D3DPRESENTFLAG_MODE3DTB) ? " TB":"");
257 m_DllBcmHost->vc_tv_unregister_callback(CallbackTvServiceCallback);
258 sem_destroy(&m_tv_synced);
263 m_dispman_display = m_DllBcmHost->vc_dispmanx_display_open(0);
265 m_width = res.iWidth;
266 m_height = res.iHeight;
273 dst_rect.width = res.iScreenWidth;
274 dst_rect.height = res.iScreenHeight;
278 src_rect.width = m_width << 16;
279 src_rect.height = m_height << 16;
281 VC_DISPMANX_ALPHA_T alpha;
282 memset(&alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T));
283 alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE;
285 DISPMANX_CLAMP_T clamp;
286 memset(&clamp, 0x0, sizeof(DISPMANX_CLAMP_T));
288 DISPMANX_TRANSFORM_T transform = DISPMANX_NO_ROTATE;
289 DISPMANX_UPDATE_HANDLE_T dispman_update = m_DllBcmHost->vc_dispmanx_update_start(0);
291 CLog::Log(LOGDEBUG, "EGL set resolution %dx%d -> %dx%d @ %.2f fps (%d,%d) flags:%x aspect:%.2f\n",
292 m_width, m_height, dst_rect.width, dst_rect.height, res.fRefreshRate, GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags), (int)res.dwFlags, res.fPixelRatio);
294 m_dispman_element = m_DllBcmHost->vc_dispmanx_element_add(dispman_update,
298 (DISPMANX_RESOURCE_HANDLE_T)0, // src
300 DISPMANX_PROTECTION_NONE,
303 transform); // transform
305 assert(m_dispman_element != DISPMANX_NO_HANDLE);
306 assert(m_dispman_element != (unsigned)DISPMANX_INVALID);
308 memset(m_nativeWindow, 0, sizeof(EGL_DISPMANX_WINDOW_T));
310 EGL_DISPMANX_WINDOW_T *nativeWindow = (EGL_DISPMANX_WINDOW_T *)m_nativeWindow;
312 nativeWindow->element = m_dispman_element;
313 nativeWindow->width = m_width;
314 nativeWindow->height = m_height;
316 m_DllBcmHost->vc_dispmanx_display_set_background(dispman_update, m_dispman_display, 0x00, 0x00, 0x00);
317 m_DllBcmHost->vc_dispmanx_update_submit_sync(dispman_update);
319 DLOG("CEGLNativeTypeRaspberryPI::SetNativeResolution\n");
327 #if defined(TARGET_RASPBERRY_PI)
328 static float get_display_aspect_ratio(HDMI_ASPECT_T aspect)
330 float display_aspect;
332 case HDMI_ASPECT_4_3: display_aspect = 4.0/3.0; break;
333 case HDMI_ASPECT_14_9: display_aspect = 14.0/9.0; break;
334 case HDMI_ASPECT_16_9: display_aspect = 16.0/9.0; break;
335 case HDMI_ASPECT_5_4: display_aspect = 5.0/4.0; break;
336 case HDMI_ASPECT_16_10: display_aspect = 16.0/10.0; break;
337 case HDMI_ASPECT_15_9: display_aspect = 15.0/9.0; break;
338 case HDMI_ASPECT_64_27: display_aspect = 64.0/27.0; break;
339 default: display_aspect = 16.0/9.0; break;
341 return display_aspect;
344 static float get_display_aspect_ratio(SDTV_ASPECT_T aspect)
346 float display_aspect;
348 case SDTV_ASPECT_4_3: display_aspect = 4.0/3.0; break;
349 case SDTV_ASPECT_14_9: display_aspect = 14.0/9.0; break;
350 case SDTV_ASPECT_16_9: display_aspect = 16.0/9.0; break;
351 default: display_aspect = 4.0/3.0; break;
353 return display_aspect;
356 static bool ClampToGUIDisplayLimits(int &width, int &height)
358 float max_height = (float)CSettings::Get().GetInt("videoscreen.limitgui");
359 float default_ar = 16.0f/9.0f;
360 if (max_height < 540.0f || max_height > 1080.0f)
361 max_height = 1080.0f;
363 float ar = (float)width/(float)height;
364 float max_width = max_height * default_ar;
365 // bigger than maximum, so need to clamp
366 if (width > max_width || height > max_height) {
367 // wider than max, so clamp width first
371 height = max_width / ar + 0.5f;
372 // taller than max, so clamp height first
375 width = max_height * ar + 0.5f;
383 static void SetResolutionString(RESOLUTION_INFO &res)
385 int gui_width = res.iScreenWidth;
386 int gui_height = res.iScreenHeight;
388 ClampToGUIDisplayLimits(gui_width, gui_height);
390 res.iWidth = gui_width;
391 res.iHeight = gui_height;
393 res.strMode = StringUtils::Format("%dx%d (%dx%d) @ %.2f%s%s%s - Full Screen", res.iScreenWidth, res.iScreenHeight, res.iWidth, res.iHeight, res.fRefreshRate,
394 res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "",
395 res.dwFlags & D3DPRESENTFLAG_MODE3DTB ? " 3DTB" : "",
396 res.dwFlags & D3DPRESENTFLAG_MODE3DSBS ? " 3DSBS" : "");
400 bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &resolutions)
402 #if defined(TARGET_RASPBERRY_PI)
410 /* read initial desktop resolution before probe resolutions.
411 * probing will replace the desktop resolution when it finds the same one.
412 * we raplace it because probing will generate more detailed
413 * resolution flags we don't get with vc_tv_get_state.
418 TV_DISPLAY_STATE_T tv_state;
420 // get current display settings state
421 memset(&tv_state, 0, sizeof(TV_DISPLAY_STATE_T));
422 m_DllBcmHost->vc_tv_get_display_state(&tv_state);
424 if ((tv_state.state & ( VC_HDMI_HDMI | VC_HDMI_DVI )) != 0) // hdtv
426 m_desktopRes.iScreen = 0;
427 m_desktopRes.bFullScreen = true;
428 m_desktopRes.iWidth = tv_state.display.hdmi.width;
429 m_desktopRes.iHeight = tv_state.display.hdmi.height;
430 m_desktopRes.iScreenWidth = tv_state.display.hdmi.width;
431 m_desktopRes.iScreenHeight= tv_state.display.hdmi.height;
432 m_desktopRes.dwFlags = MAKEFLAGS(tv_state.display.hdmi.group, tv_state.display.hdmi.mode, tv_state.display.hdmi.scan_mode);
433 m_desktopRes.fPixelRatio = get_display_aspect_ratio((HDMI_ASPECT_T)tv_state.display.hdmi.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
435 if (tv_state.display.hdmi.format_3d == HDMI_3D_FORMAT_SBS_HALF)
437 m_desktopRes.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
438 m_desktopRes.fPixelRatio *= 2.0;
440 else if (tv_state.display.hdmi.format_3d == HDMI_3D_FORMAT_TB_HALF)
442 m_desktopRes.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
443 m_desktopRes.fPixelRatio *= 0.5;
445 m_desktopRes.fRefreshRate = (float)tv_state.display.hdmi.frame_rate;
449 m_desktopRes.iScreen = 0;
450 m_desktopRes.bFullScreen = true;
451 m_desktopRes.iWidth = tv_state.display.sdtv.width;
452 m_desktopRes.iHeight = tv_state.display.sdtv.height;
453 m_desktopRes.iScreenWidth = tv_state.display.sdtv.width;
454 m_desktopRes.iScreenHeight= tv_state.display.sdtv.height;
455 m_desktopRes.dwFlags = D3DPRESENTFLAG_INTERLACED;
456 m_desktopRes.fRefreshRate = (float)tv_state.display.sdtv.frame_rate;
457 m_desktopRes.fPixelRatio = get_display_aspect_ratio((SDTV_ASPECT_T)tv_state.display.sdtv.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
460 m_desktopRes.strMode = StringUtils::Format("%dx%d", m_desktopRes.iScreenWidth, m_desktopRes.iScreenHeight);
462 if((int)m_desktopRes.fRefreshRate > 1)
463 SetResolutionString(m_desktopRes);
465 m_initDesktopRes = false;
467 m_desktopRes.iSubtitles = (int)(0.965 * m_desktopRes.iHeight);
469 CLog::Log(LOGDEBUG, "EGL initial desktop resolution %s (%.2f)\n", m_desktopRes.strMode.c_str(), m_desktopRes.fPixelRatio);
472 GetSupportedModes(HDMI_RES_GROUP_CEA, resolutions);
473 GetSupportedModes(HDMI_RES_GROUP_DMT, resolutions);
475 if(resolutions.size() == 0)
478 CLog::Log(LOGDEBUG, "EGL probe resolution %s:%x\n", m_desktopRes.strMode.c_str(), m_desktopRes.dwFlags);
480 AddUniqueResolution(m_desktopRes, resolutions);
483 if(resolutions.size() < 2)
486 DLOG("CEGLNativeTypeRaspberryPI::ProbeResolutions\n");
493 bool CEGLNativeTypeRaspberryPI::GetPreferredResolution(RESOLUTION_INFO *res) const
495 DLOG("CEGLNativeTypeRaspberryPI::GetPreferredResolution\n");
499 bool CEGLNativeTypeRaspberryPI::ShowWindow(bool show)
501 DLOG("CEGLNativeTypeRaspberryPI::ShowWindow\n");
505 #if defined(TARGET_RASPBERRY_PI)
506 void CEGLNativeTypeRaspberryPI::DestroyDispmaxWindow()
511 DISPMANX_UPDATE_HANDLE_T dispman_update = m_DllBcmHost->vc_dispmanx_update_start(0);
513 if (m_dispman_element != DISPMANX_NO_HANDLE)
515 m_DllBcmHost->vc_dispmanx_element_remove(dispman_update, m_dispman_element);
516 m_dispman_element = DISPMANX_NO_HANDLE;
518 m_DllBcmHost->vc_dispmanx_update_submit_sync(dispman_update);
520 if (m_dispman_display != DISPMANX_NO_HANDLE)
522 m_DllBcmHost->vc_dispmanx_display_close(m_dispman_display);
523 m_dispman_display = DISPMANX_NO_HANDLE;
525 DLOG("CEGLNativeTypeRaspberryPI::DestroyDispmaxWindow\n");
528 void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::vector<RESOLUTION_INFO> &resolutions)
533 //Supported HDMI CEA/DMT resolutions, preferred resolution will be returned
534 int32_t num_modes = 0;
535 HDMI_RES_GROUP_T prefer_group;
536 uint32_t prefer_mode;
538 TV_SUPPORTED_MODE_NEW_T *supported_modes = NULL;
539 // query the number of modes first
540 int max_supported_modes = m_DllBcmHost->vc_tv_hdmi_get_supported_modes_new(group, NULL, 0, &prefer_group, &prefer_mode);
542 if (max_supported_modes > 0)
543 supported_modes = new TV_SUPPORTED_MODE_NEW_T[max_supported_modes];
547 num_modes = m_DllBcmHost->vc_tv_hdmi_get_supported_modes_new(group,
548 supported_modes, max_supported_modes, &prefer_group, &prefer_mode);
550 CLog::Log(LOGDEBUG, "EGL get supported modes (%d) = %d, prefer_group=%x, prefer_mode=%x\n",
551 group, num_modes, prefer_group, prefer_mode);
554 if (num_modes > 0 && prefer_group != HDMI_RES_GROUP_INVALID)
556 TV_SUPPORTED_MODE_NEW_T *tv = supported_modes;
557 for (i=0; i < num_modes; i++, tv++)
562 res.bFullScreen = true;
563 res.dwFlags = MAKEFLAGS(group, tv->code, tv->scan_mode);
564 res.fRefreshRate = (float)tv->frame_rate;
565 res.iWidth = tv->width;
566 res.iHeight = tv->height;
567 res.iScreenWidth = tv->width;
568 res.iScreenHeight = tv->height;
569 res.fPixelRatio = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res.iScreenWidth / (float)res.iScreenHeight);
571 SetResolutionString(res);
573 CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f) %s%s:%x\n", i, res.strMode.c_str(), res.fPixelRatio,
574 tv->native ? "N" : "", tv->scan_mode ? "I" : "", tv->code);
576 res.iSubtitles = (int)(0.965 * res.iHeight);
578 AddUniqueResolution(res, resolutions);
580 // Also add 3D versions of modes
581 if (tv->struct_3d_mask & HDMI_3D_STRUCT_SIDE_BY_SIDE_HALF_HORIZONTAL)
583 RESOLUTION_INFO res2 = res;
584 res2.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
585 res2.fPixelRatio = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
586 res2.fPixelRatio *= 2.0f;
587 SetResolutionString(res2);
588 CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f)\n", i, res2.strMode.c_str(), res2.fPixelRatio);
590 res2.iSubtitles = (int)(0.965 * res2.iHeight);
592 AddUniqueResolution(res2, resolutions);
594 if (tv->struct_3d_mask & HDMI_3D_STRUCT_TOP_AND_BOTTOM)
596 RESOLUTION_INFO res2 = res;
597 res2.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
598 res2.fPixelRatio = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
599 res2.fPixelRatio *= 0.5f;
600 SetResolutionString(res2);
601 CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f)\n", i, res2.strMode.c_str(), res2.fPixelRatio);
603 res2.iSubtitles = (int)(0.965 * res2.iHeight);
605 AddUniqueResolution(res2, resolutions);
610 delete [] supported_modes;
613 void CEGLNativeTypeRaspberryPI::TvServiceCallback(uint32_t reason, uint32_t param1, uint32_t param2)
615 CLog::Log(LOGDEBUG, "EGL tv_service_callback (%d,%d,%d)\n", reason, param1, param2);
618 case VC_HDMI_UNPLUGGED:
620 case VC_HDMI_STANDBY:
626 //Signal we are ready now
627 sem_post(&m_tv_synced);
634 void CEGLNativeTypeRaspberryPI::CallbackTvServiceCallback(void *userdata, uint32_t reason, uint32_t param1, uint32_t param2)
636 CEGLNativeTypeRaspberryPI *callback = static_cast<CEGLNativeTypeRaspberryPI*>(userdata);
637 callback->TvServiceCallback(reason, param1, param2);