static int get_status( void ) { TV_DISPLAY_STATE_T tvstate; if( vc_tv_get_display_state( &tvstate ) == 0) { //The width/height parameters are in the same position in the union //for HDMI and SDTV HDMI_PROPERTY_PARAM_T property; property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE; vc_tv_hdmi_get_property(&property); float frame_rate = property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC ? tvstate.display.hdmi.frame_rate * (1000.0f/1001.0f) : tvstate.display.hdmi.frame_rate; if(tvstate.display.hdmi.width && tvstate.display.hdmi.height) { LOG_STD( "state 0x%x [%s], %ux%u @ %.2fHz, %s", tvstate.state, status_mode(&tvstate), tvstate.display.hdmi.width, tvstate.display.hdmi.height, frame_rate, tvstate.display.hdmi.scan_mode ? "interlaced" : "progressive" ); } else { LOG_STD( "state 0x%x [%s]", tvstate.state, status_mode(&tvstate)); } } else { LOG_STD( "Error getting current display state"); } return 0; }
static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt) { vout_display_sys_t *sys = vd->sys; TV_DISPLAY_STATE_T display_state; TV_SUPPORTED_MODE_NEW_T supported_modes[VC_TV_MAX_MODE_IDS]; char response[20]; /* answer is hvs_update_fields=%1d */ int num_modes; double frame_rate = (double)fmt->i_frame_rate / fmt->i_frame_rate_base; int best_id = -1; double best_score, score; int i; vc_tv_get_display_state(&display_state); if(display_state.display.hdmi.mode != HDMI_MODE_OFF) { num_modes = vc_tv_hdmi_get_supported_modes_new(display_state.display.hdmi.group, supported_modes, VC_TV_MAX_MODE_IDS, NULL, NULL); for (i = 0; i < num_modes; ++i) { TV_SUPPORTED_MODE_NEW_T *mode = &supported_modes[i]; if (!sys->native_interlaced) { if (mode->width != display_state.display.hdmi.width || mode->height != display_state.display.hdmi.height || mode->scan_mode == HDMI_INTERLACED) continue; } else { if (mode->width != vd->fmt.i_visible_width || mode->height != vd->fmt.i_visible_height) continue; if (mode->scan_mode != sys->b_progressive ? HDMI_NONINTERLACED : HDMI_INTERLACED) continue; } score = fmod(supported_modes[i].frame_rate, frame_rate); if((best_id < 0) || (score < best_score)) { best_id = i; best_score = score; } } if((best_id >= 0) && (display_state.display.hdmi.mode != supported_modes[best_id].code)) { msg_Info(vd, "Setting HDMI refresh rate to %"PRIu32, supported_modes[best_id].frame_rate); vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, supported_modes[best_id].group, supported_modes[best_id].code); } if (sys->native_interlaced && supported_modes[best_id].scan_mode == HDMI_INTERLACED) { char hvs_mode = sys->b_top_field_first ? '1' : '2'; if (vc_gencmd(response, sizeof(response), "hvs_update_fields %c", hvs_mode) != 0 || response[18] != hvs_mode) msg_Warn(vd, "Could not set hvs field mode"); else msg_Info(vd, "Configured hvs field mode for interlaced %s playback", sys->b_top_field_first ? "tff" : "bff"); } } }
static int get_status( void ) { TV_DISPLAY_STATE_T tvstate; if( vc_tv_get_display_state( &tvstate ) == 0) { //The width/height parameters are in the same position in the union //for HDMI and SDTV if(tvstate.display.hdmi.width && tvstate.display.hdmi.height) { LOG_STD( "state 0x%x [%s], %ux%u @ %uHz, %s", tvstate.state, status_mode(&tvstate), tvstate.display.hdmi.width, tvstate.display.hdmi.height, tvstate.display.hdmi.frame_rate, tvstate.display.hdmi.scan_mode ? "interlaced" : "progressive" ); } else { LOG_STD( "state 0x%x [%s]", tvstate.state, status_mode(&tvstate)); } } else { LOG_STD( "Error getting current display state"); } return 0; }
int graphics_setup(void) { #ifdef PICASSO96 picasso_InitResolutions(); InitPicasso96(); #endif VCHI_INSTANCE_T vchi_instance; VCHI_CONNECTION_T *vchi_connection; TV_DISPLAY_STATE_T tvstate; if(vchi_initialise(&vchi_instance) == 0) { if(vchi_connect(NULL, 0, vchi_instance) == 0) { vc_vchi_tv_init(vchi_instance, &vchi_connection, 1); if(vc_tv_get_display_state(&tvstate) == 0) { HDMI_PROPERTY_PARAM_T property; property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE; vc_tv_hdmi_get_property(&property); float frame_rate = property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC ? tvstate.display.hdmi.frame_rate * (1000.0f/1001.0f) : tvstate.display.hdmi.frame_rate; host_hz = (int)frame_rate; time_per_host_frame = time_for_host_hz_frames / host_hz; } vc_vchi_tv_stop(); vchi_disconnect(vchi_instance); } } if(display_pipe == 0) { display_pipe = xmalloc (smp_comm_pipe, 1); init_comm_pipe(display_pipe, 20, 1); } if(display_sem == 0) { uae_sem_init (&display_sem, 0, 0); } if(display_tid == 0 && display_pipe != 0 && display_sem != 0) { uae_start_thread(_T("render"), display_thread, NULL, &display_tid); } write_comm_pipe_u32(display_pipe, DISPLAY_SIGNAL_SETUP, 1); return 1; }
static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height) { TV_DISPLAY_STATE_T display_state; int ret = 0; if (vc_tv_get_display_state(&display_state) == 0) { if (display_state.state & 0xFF) { *width = display_state.display.hdmi.width; *height = display_state.display.hdmi.height; } else if (display_state.state & 0xFF00) { *width = display_state.display.sdtv.width; *height = display_state.display.sdtv.height; } else { msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state); ret = -1; } } else { msg_Warn(vd, "Failed to query display resolution"); ret = -1; } return ret; }
/** * Return old mode so we can reset */ int rpi_set_display_framerate(float fps, int width, int height) { TV_DISPLAY_STATE_T state = {}; if(vc_tv_get_display_state(&state)) { TRACE(TRACE_DEBUG, "TV", "Failed to get TV state"); return -1; } TRACE(TRACE_DEBUG, "TV", "Searching for mode for %d x %d @ %f fps. Current mode=%d", width, height, fps, state.display.hdmi.mode); HDMI_RES_GROUP_T prefer_group; uint32_t prefer_mode; int native_deinterlace = 0; int num_modes = vc_tv_hdmi_get_supported_modes_new(HDMI_RES_GROUP_CEA, NULL, 0, &prefer_group, &prefer_mode); if(num_modes <= 0) return -1; TV_SUPPORTED_MODE_NEW_T *modes = alloca(sizeof(TV_SUPPORTED_MODE_NEW_T) * num_modes); num_modes = vc_tv_hdmi_get_supported_modes_new(HDMI_RES_GROUP_CEA, modes, num_modes, &prefer_group, &prefer_mode); TV_SUPPORTED_MODE_NEW_T *best_mode = NULL; uint32_t best_score = 1<<30; for(int i = 0; i < num_modes; i++) { TV_SUPPORTED_MODE_NEW_T *tv = modes + i; uint32_t score = 0; uint32_t w = tv->width; uint32_t h = tv->height; uint32_t r = tv->frame_rate; /* Check if frame rate match (equal or exact multiple) */ if(fabs(r - 1.0f*fps) / fps < 0.002f) score += 0; else if(fabs(r - 2.0f*fps) / fps < 0.002f) score += 1<<8; else score += (1<<16) + (1<<20)/r; // bad - but prefer higher framerate /* Check size too, only choose, bigger resolutions */ if(width && height) { /* cost of too small a resolution is high */ score += MAX((int)(width -w), 0) * (1<<16); score += MAX((int)(height-h), 0) * (1<<16); /* cost of too high a resolution is lower */ score += MAX((int)(w-width ), 0) * (1<<4); score += MAX((int)(h-height), 0) * (1<<4); } // native is good if (!tv->native) score += 1<<16; #if 0 // wanting 3D but not getting it is a negative if (is3d == CONF_FLAGS_FORMAT_SBS && !(tv->struct_3d_mask & HDMI_3D_STRUCT_SIDE_BY_SIDE_HALF_HORIZONTAL)) score += 1<<18; if (is3d == CONF_FLAGS_FORMAT_TB && !(tv->struct_3d_mask & HDMI_3D_STRUCT_TOP_AND_BOTTOM)) score += 1<<18; #endif // prefer square pixels modes float par = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio)*(float)tv->height/(float)tv->width; score += fabs(par - 1.0f) * (1<<12); if(score < best_score) { best_mode = tv; best_score = score; } } if(best_mode == NULL) return -1; char response[80]; TRACE(TRACE_DEBUG, "TV", "Output mode %d: %dx%d@%d %s%s:%x\n", best_mode->code, best_mode->width, best_mode->height, best_mode->frame_rate, best_mode->native ? "N":"", best_mode->scan_mode?"I":"", best_mode->code); if (native_deinterlace && best_mode->scan_mode) vc_gencmd(response, sizeof response, "hvs_update_fields %d", 1); // if we are closer to ntsc version of framerate, let gpu know int ifps = (int)(fps+0.5f); int ntsc_freq = fabs(fps*1001.0f/1000.0f - ifps) < fabs(fps-ifps); vc_gencmd(response, sizeof response, "hdmi_ntsc_freqs %d", ntsc_freq); /* Inform TV of any 3D settings. Note this property just applies to next hdmi mode change, so no need to call for 2D modes */ HDMI_PROPERTY_PARAM_T property; property.property = HDMI_PROPERTY_3D_STRUCTURE; property.param1 = HDMI_3D_FORMAT_NONE; property.param2 = 0; #if 0 if (is3d != CONF_FLAGS_FORMAT_NONE) { if (is3d == CONF_FLAGS_FORMAT_SBS && best_mode->struct_3d_mask & HDMI_3D_STRUCT_SIDE_BY_SIDE_HALF_HORIZONTAL) property.param1 = HDMI_3D_FORMAT_SBS_HALF; else if (is3d == CONF_FLAGS_FORMAT_TB && best_mode->struct_3d_mask & HDMI_3D_STRUCT_TOP_AND_BOTTOM) property.param1 = HDMI_3D_FORMAT_TB_HALF; m_BcmHost.vc_tv_hdmi_set_property(&property); } #endif TRACE(TRACE_DEBUG, "TV", "ntsc_freq:%d %s%s\n", ntsc_freq, property.param1 == HDMI_3D_FORMAT_SBS_HALF ? "3DSBS":"", property.param1 == HDMI_3D_FORMAT_TB_HALF ? "3DTB":""); int err = vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, HDMI_RES_GROUP_CEA, best_mode->code); if(err) { TRACE(TRACE_DEBUG, "TV", "Failed to set mode %d", best_mode->code); return -1; } return state.display.hdmi.mode; }
static bool recreate_dispmanx(struct ra_ctx *ctx) { struct priv *p = ctx->priv; int display_nr = 0; int layer = 0; MP_VERBOSE(ctx, "Recreating DISPMANX state...\n"); destroy_dispmanx(ctx); p->display = vc_dispmanx_display_open(display_nr); p->update = vc_dispmanx_update_start(0); if (!p->display || !p->update) { MP_FATAL(ctx, "Could not get DISPMANX objects.\n"); goto fail; } uint32_t dispw, disph; if (graphics_get_display_size(0, &dispw, &disph) < 0) { MP_FATAL(ctx, "Could not get display size.\n"); goto fail; } p->w = dispw; p->h = disph; if (ctx->vo->opts->fullscreen) { p->x = p->y = 0; } else { struct vo_win_geometry geo; struct mp_rect screenrc = {0, 0, p->w, p->h}; vo_calc_window_geometry(ctx->vo, &screenrc, &geo); mp_rect_intersection(&geo.win, &screenrc); p->x = geo.win.x0; p->y = geo.win.y0; p->w = geo.win.x1 - geo.win.x0; p->h = geo.win.y1 - geo.win.y0; } // dispmanx is like a neanderthal version of Wayland - you can add an // overlay any place on the screen. VC_RECT_T dst = {.x = p->x, .y = p->y, .width = p->w, .height = p->h}; VC_RECT_T src = {.width = p->w << 16, .height = p->h << 16}; VC_DISPMANX_ALPHA_T alpha = { .flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE, .opacity = 0xFF, }; p->window = vc_dispmanx_element_add(p->update, p->display, layer, &dst, 0, &src, DISPMANX_PROTECTION_NONE, &alpha, 0, 0); if (!p->window) { MP_FATAL(ctx, "Could not add DISPMANX element.\n"); goto fail; } vc_dispmanx_update_submit_sync(p->update); p->update = vc_dispmanx_update_start(0); p->egl_window = (EGL_DISPMANX_WINDOW_T){ .element = p->window, .width = p->w, .height = p->h, }; p->egl_surface = eglCreateWindowSurface(p->egl_display, p->egl_config, &p->egl_window, NULL); if (p->egl_surface == EGL_NO_SURFACE) { MP_FATAL(ctx, "Could not create EGL surface!\n"); goto fail; } if (!eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface, p->egl_context)) { MP_FATAL(ctx, "Failed to set context!\n"); goto fail; } p->display_fps = 0; TV_GET_STATE_RESP_T tvstate; TV_DISPLAY_STATE_T tvstate_disp; if (!vc_tv_get_state(&tvstate) && !vc_tv_get_display_state(&tvstate_disp)) { if (tvstate_disp.state & (VC_HDMI_HDMI | VC_HDMI_DVI)) { p->display_fps = tvstate_disp.display.hdmi.frame_rate; HDMI_PROPERTY_PARAM_T param = { .property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE, }; if (!vc_tv_hdmi_get_property(¶m) && param.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC) p->display_fps = p->display_fps / 1.001; } else { p->display_fps = tvstate_disp.display.sdtv.frame_rate; } } p->win_params[0] = display_nr; p->win_params[1] = layer; p->win_params[2] = p->x; p->win_params[3] = p->y; ctx->vo->dwidth = p->w; ctx->vo->dheight = p->h; ra_gl_ctx_resize(ctx->swapchain, p->w, p->h, 0); ctx->vo->want_redraw = true; vo_event(ctx->vo, VO_EVENT_WIN_STATE); return true; fail: destroy_dispmanx(ctx); return false; } static void rpi_swap_buffers(struct ra_ctx *ctx) { struct priv *p = ctx->priv; eglSwapBuffers(p->egl_display, p->egl_surface); } static bool rpi_init(struct ra_ctx *ctx) { struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); bcm_host_init(); vc_tv_register_callback(tv_callback, ctx); p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (!eglInitialize(p->egl_display, NULL, NULL)) { MP_FATAL(ctx, "EGL failed to initialize.\n"); goto fail; } if (!mpegl_create_context(ctx, p->egl_display, &p->egl_context, &p->egl_config)) goto fail; if (recreate_dispmanx(ctx) < 0) goto fail; mpegl_load_functions(&p->gl, ctx->log); struct ra_gl_ctx_params params = { .swap_buffers = rpi_swap_buffers, .native_display_type = "MPV_RPI_WINDOW", .native_display = p->win_params, }; if (!ra_gl_ctx_init(ctx, &p->gl, params)) goto fail; return true; fail: rpi_uninit(ctx); return false; } static bool rpi_reconfig(struct ra_ctx *ctx) { return recreate_dispmanx(ctx); } static struct mp_image *take_screenshot(struct ra_ctx *ctx) { struct priv *p = ctx->priv; if (!p->display) return NULL; struct mp_image *img = mp_image_alloc(IMGFMT_BGR0, p->w, p->h); if (!img) return NULL; DISPMANX_RESOURCE_HANDLE_T resource = vc_dispmanx_resource_create(VC_IMAGE_ARGB8888, img->w | ((img->w * 4) << 16), img->h, &(int32_t){0}); if (!resource) goto fail; if (vc_dispmanx_snapshot(p->display, resource, 0)) goto fail; VC_RECT_T rc = {.width = img->w, .height = img->h}; if (vc_dispmanx_resource_read_data(resource, &rc, img->planes[0], img->stride[0])) goto fail; vc_dispmanx_resource_delete(resource); return img; fail: vc_dispmanx_resource_delete(resource); talloc_free(img); return NULL; } static int rpi_control(struct ra_ctx *ctx, int *events, int request, void *arg) { struct priv *p = ctx->priv; switch (request) { case VOCTRL_SCREENSHOT_WIN: *(struct mp_image **)arg = take_screenshot(ctx); return VO_TRUE; case VOCTRL_FULLSCREEN: recreate_dispmanx(ctx); return VO_TRUE; case VOCTRL_CHECK_EVENTS: if (atomic_fetch_and(&p->reload_display, 0)) { MP_WARN(ctx, "Recovering from display mode switch...\n"); recreate_dispmanx(ctx); } return VO_TRUE; case VOCTRL_GET_DISPLAY_FPS: *(double *)arg = p->display_fps; return VO_TRUE; } return VO_NOTIMPL; } const struct ra_ctx_fns ra_ctx_rpi = { .type = "opengl", .name = "rpi", .reconfig = rpi_reconfig, .control = rpi_control, .init = rpi_init, .uninit = rpi_uninit, };