From 008280662058d92c6014063704038f970336c186 Mon Sep 17 00:00:00 2001 From: cpasjuste Date: Wed, 18 Nov 2020 11:34:44 +0100 Subject: [PATCH 1/3] (KMS/DRM) add support for custom modes from hdmi timings --- configuration.c | 1 + configuration.h | 2 + gfx/drivers_context/drm_ctx.c | 113 ++++++++++++++++++++++++++++------ 3 files changed, 98 insertions(+), 18 deletions(-) diff --git a/configuration.c b/configuration.c index 4e723cea76..ef5087cbdb 100644 --- a/configuration.c +++ b/configuration.c @@ -1235,6 +1235,7 @@ static struct config_array_setting *populate_settings_array(settings_t *settings SETTING_ARRAY("twitch_stream_key", settings->arrays.twitch_stream_key, true, NULL, true); SETTING_ARRAY("discord_app_id", settings->arrays.discord_app_id, true, DEFAULT_DISCORD_APP_ID, true); SETTING_ARRAY("ai_service_url", settings->arrays.ai_service_url, true, DEFAULT_AI_SERVICE_URL, true); + SETTING_ARRAY("crt_switch_timings", settings->arrays.crt_switch_timings, false, NULL, true); *size = count; diff --git a/configuration.h b/configuration.h index 3b9e627e75..0119153fcc 100644 --- a/configuration.h +++ b/configuration.h @@ -367,6 +367,8 @@ typedef struct settings char twitch_stream_key[PATH_MAX_LENGTH]; char discord_app_id[PATH_MAX_LENGTH]; char ai_service_url[PATH_MAX_LENGTH]; + + char crt_switch_timings[255]; } arrays; struct diff --git a/gfx/drivers_context/drm_ctx.c b/gfx/drivers_context/drm_ctx.c index 7c055a5e5a..2036d8b594 100644 --- a/gfx/drivers_context/drm_ctx.c +++ b/gfx/drivers_context/drm_ctx.c @@ -90,7 +90,73 @@ struct drm_fb uint32_t fb_id; }; +// https://www.raspberrypi.org/documentation/configuration/config-txt/video.md +typedef struct hdmi_timings +{ + int h_active_pixels; // horizontal pixels (width) + int h_sync_polarity; // invert hsync polarity + int h_front_porch; // horizontal forward padding from DE acitve edge + int h_sync_pulse; // hsync pulse width in pixel clocks + int h_back_porch; // vertical back padding from DE active edge + int v_active_lines; // vertical pixels height (lines) + int v_sync_polarity; // invert vsync polarity + int v_front_porch; // vertical forward padding from DE active edge + int v_sync_pulse; // vsync pulse width in pixel clocks + int v_back_porch; // vertical back padding from DE active edge + int v_sync_offset_a; // leave at zero + int v_sync_offset_b; // leave at zero + int pixel_rep; // leave at zero + int frame_rate; // screen refresh rate in Hz + int interlaced; // leave at zero + int pixel_freq; // clock frequency (width*height*framerate) + int aspect_ratio; // * +} hdmi_timings_t; + static enum gfx_ctx_api drm_api = GFX_CTX_NONE; +static drmModeModeInfo crt_switch_mode; + +static bool gfx_ctx_drm_timings_to_mode(const char *timings_str, drmModeModeInfoPtr modeInfo) +{ + int ret; + hdmi_timings_t timings; + + if(timings_str != NULL && modeInfo != NULL) { + ret = sscanf(timings_str, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", + &timings.h_active_pixels, &timings.h_sync_polarity, &timings.h_front_porch, + &timings.h_sync_pulse, &timings.h_back_porch, + &timings.v_active_lines, &timings.v_sync_polarity, &timings.v_front_porch, + &timings.v_sync_pulse, &timings.v_back_porch, + &timings.v_sync_offset_a, &timings.v_sync_offset_b, &timings.pixel_rep, &timings.frame_rate, + &timings.interlaced, &timings.pixel_freq, &timings.aspect_ratio); + if (ret != 17) { + RARCH_ERR("[KMS/EGL]: malformed custom mode: %s\n", timings_str); + return false; + } + + memset(modeInfo, 0, sizeof(drmModeModeInfo)); + modeInfo->clock = timings.pixel_freq / 1000; + modeInfo->hdisplay = timings.h_active_pixels; + modeInfo->hsync_start = modeInfo->hdisplay + timings.h_front_porch; + modeInfo->hsync_end = modeInfo->hsync_start + timings.h_sync_pulse; + modeInfo->htotal = modeInfo->hsync_end + timings.h_back_porch; + modeInfo->hskew = 0; // TODO: ?? + modeInfo->vdisplay = timings.v_active_lines; + modeInfo->vsync_start = modeInfo->vdisplay + (timings.v_front_porch * (timings.interlaced ? 2 : 1)); + modeInfo->vsync_end = modeInfo->vsync_start + (timings.v_sync_pulse * (timings.interlaced ? 2 : 1)); + modeInfo->vtotal = modeInfo->vsync_end + (timings.v_back_porch * (timings.interlaced ? 2 : 1)); + modeInfo->vscan = 0; // TODO: ?? + modeInfo->vrefresh = timings.frame_rate; + modeInfo->flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC + | (timings.interlaced ? DRM_MODE_FLAG_INTERLACE : 0); + modeInfo->type = 0; + snprintf(modeInfo->name, DRM_DISPLAY_MODE_LEN, "CRT_%ux%u_%u", + modeInfo->hdisplay, modeInfo->vdisplay, modeInfo->vrefresh); + + return true; + } + + return false; +} static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data) { @@ -611,6 +677,8 @@ error: } #endif + + static bool gfx_ctx_drm_set_video_mode(void *data, unsigned width, unsigned height, bool fullscreen) @@ -622,6 +690,7 @@ static bool gfx_ctx_drm_set_video_mode(void *data, settings_t *settings = config_get_ptr(); unsigned black_frame_insertion = settings->uints.video_black_frame_insertion; float video_refresh_rate = settings->floats.video_refresh_rate; + char *crt_switch_timings = settings->arrays.crt_switch_timings; if (!drm) return false; @@ -640,28 +709,36 @@ static bool gfx_ctx_drm_set_video_mode(void *data, g_drm_mode = &g_drm_connector->modes[0]; else { - /* Try to match refresh_rate as closely as possible. - * - * Lower resolutions tend to have multiple supported - * refresh rates as well. - */ - float minimum_fps_diff = 0.0f; - - /* Find best match. */ - for (i = 0; i < g_drm_connector->count_modes; i++) + // check for custom crt hdmi timings + if(!string_is_empty(crt_switch_timings) + && gfx_ctx_drm_timings_to_mode(crt_switch_timings, &crt_switch_mode)) { - float diff; - if (width != g_drm_connector->modes[i].hdisplay || + RARCH_LOG("[KMS/EGL]: custom mode requested: %s\n", crt_switch_mode.name); + g_drm_mode = &crt_switch_mode; + } + else + { + /* Try to match refresh_rate as closely as possible. + * + * Lower resolutions tend to have multiple supported + * refresh rates as well. + */ + float minimum_fps_diff = 0.0f; + + /* Find best match. */ + for (i = 0; i < g_drm_connector->count_modes; i++) { + float diff; + if (width != g_drm_connector->modes[i].hdisplay || height != g_drm_connector->modes[i].vdisplay) - continue; + continue; - diff = fabsf(refresh_mod * g_drm_connector->modes[i].vrefresh - - video_refresh_rate); + diff = fabsf(refresh_mod * g_drm_connector->modes[i].vrefresh + - video_refresh_rate); - if (!g_drm_mode || diff < minimum_fps_diff) - { - g_drm_mode = &g_drm_connector->modes[i]; - minimum_fps_diff = diff; + if (!g_drm_mode || diff < minimum_fps_diff) { + g_drm_mode = &g_drm_connector->modes[i]; + minimum_fps_diff = diff; + } } } } From 485513ba61628a8447ee14372bd27660eabf3230 Mon Sep 17 00:00:00 2001 From: cpasjuste Date: Wed, 18 Nov 2020 15:21:03 +0100 Subject: [PATCH 2/3] (KMS/DRM) correctly handle custom modes polarity, implement aspect ratio support from hdmi_timings --- gfx/drivers_context/drm_ctx.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/gfx/drivers_context/drm_ctx.c b/gfx/drivers_context/drm_ctx.c index 2036d8b594..878a5d8018 100644 --- a/gfx/drivers_context/drm_ctx.c +++ b/gfx/drivers_context/drm_ctx.c @@ -139,15 +139,31 @@ static bool gfx_ctx_drm_timings_to_mode(const char *timings_str, drmModeModeInfo modeInfo->hsync_start = modeInfo->hdisplay + timings.h_front_porch; modeInfo->hsync_end = modeInfo->hsync_start + timings.h_sync_pulse; modeInfo->htotal = modeInfo->hsync_end + timings.h_back_porch; - modeInfo->hskew = 0; // TODO: ?? + modeInfo->hskew = 0; modeInfo->vdisplay = timings.v_active_lines; modeInfo->vsync_start = modeInfo->vdisplay + (timings.v_front_porch * (timings.interlaced ? 2 : 1)); modeInfo->vsync_end = modeInfo->vsync_start + (timings.v_sync_pulse * (timings.interlaced ? 2 : 1)); modeInfo->vtotal = modeInfo->vsync_end + (timings.v_back_porch * (timings.interlaced ? 2 : 1)); modeInfo->vscan = 0; // TODO: ?? modeInfo->vrefresh = timings.frame_rate; - modeInfo->flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC - | (timings.interlaced ? DRM_MODE_FLAG_INTERLACE : 0); + modeInfo->flags = timings.interlaced ? DRM_MODE_FLAG_INTERLACE : 0; + modeInfo->flags |= timings.v_sync_polarity ? DRM_MODE_FLAG_NVSYNC : DRM_MODE_FLAG_PVSYNC; + modeInfo->flags |= timings.h_sync_polarity ? DRM_MODE_FLAG_NHSYNC : DRM_MODE_FLAG_PHSYNC; + switch(timings.aspect_ratio) + { + case 1: + modeInfo->flags |= DRM_MODE_FLAG_PIC_AR_4_3; + break; + case 3: + modeInfo->flags |= DRM_MODE_FLAG_PIC_AR_16_9; + break; + case 8: + modeInfo->flags |= DRM_MODE_FLAG_PIC_AR_64_27; + break; + default: + modeInfo->flags |= DRM_MODE_FLAG_PIC_AR_NONE; + break; + } modeInfo->type = 0; snprintf(modeInfo->name, DRM_DISPLAY_MODE_LEN, "CRT_%ux%u_%u", modeInfo->hdisplay, modeInfo->vdisplay, modeInfo->vrefresh); From e31182b005bd8f6929241aa81d0b0176647eff4b Mon Sep 17 00:00:00 2001 From: cpasjuste Date: Thu, 19 Nov 2020 10:21:18 +0100 Subject: [PATCH 3/3] (KMS/DRM) cleanup custom video mode loading (crt_switch_timings) --- gfx/drivers_context/drm_ctx.c | 55 +++++++++++++++-------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/gfx/drivers_context/drm_ctx.c b/gfx/drivers_context/drm_ctx.c index 878a5d8018..fdcb0d0129 100644 --- a/gfx/drivers_context/drm_ctx.c +++ b/gfx/drivers_context/drm_ctx.c @@ -90,7 +90,10 @@ struct drm_fb uint32_t fb_id; }; -// https://www.raspberrypi.org/documentation/configuration/config-txt/video.md +/* + * https://github.com/libretro/RetroArch/pull/11590 + * https://www.raspberrypi.org/documentation/configuration/config-txt/video.md + */ typedef struct hdmi_timings { int h_active_pixels; // horizontal pixels (width) @@ -113,15 +116,18 @@ typedef struct hdmi_timings } hdmi_timings_t; static enum gfx_ctx_api drm_api = GFX_CTX_NONE; -static drmModeModeInfo crt_switch_mode; +static drmModeModeInfo gfx_ctx_crt_switch_mode; -static bool gfx_ctx_drm_timings_to_mode(const char *timings_str, drmModeModeInfoPtr modeInfo) +/* Load custom hdmi timings from config */ +bool gfx_ctx_drm_load_mode(drmModeModeInfoPtr modeInfo) { int ret; hdmi_timings_t timings; + settings_t *settings = config_get_ptr(); + char *crt_switch_timings = settings->arrays.crt_switch_timings; - if(timings_str != NULL && modeInfo != NULL) { - ret = sscanf(timings_str, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", + if(modeInfo != NULL && !string_is_empty(crt_switch_timings)) { + ret = sscanf(crt_switch_timings, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", &timings.h_active_pixels, &timings.h_sync_polarity, &timings.h_front_porch, &timings.h_sync_pulse, &timings.h_back_porch, &timings.v_active_lines, &timings.v_sync_polarity, &timings.v_front_porch, @@ -129,7 +135,7 @@ static bool gfx_ctx_drm_timings_to_mode(const char *timings_str, drmModeModeInfo &timings.v_sync_offset_a, &timings.v_sync_offset_b, &timings.pixel_rep, &timings.frame_rate, &timings.interlaced, &timings.pixel_freq, &timings.aspect_ratio); if (ret != 17) { - RARCH_ERR("[KMS/EGL]: malformed custom mode: %s\n", timings_str); + RARCH_ERR("[DRM]: malformed mode requested: %s\n", crt_switch_timings); return false; } @@ -149,21 +155,6 @@ static bool gfx_ctx_drm_timings_to_mode(const char *timings_str, drmModeModeInfo modeInfo->flags = timings.interlaced ? DRM_MODE_FLAG_INTERLACE : 0; modeInfo->flags |= timings.v_sync_polarity ? DRM_MODE_FLAG_NVSYNC : DRM_MODE_FLAG_PVSYNC; modeInfo->flags |= timings.h_sync_polarity ? DRM_MODE_FLAG_NHSYNC : DRM_MODE_FLAG_PHSYNC; - switch(timings.aspect_ratio) - { - case 1: - modeInfo->flags |= DRM_MODE_FLAG_PIC_AR_4_3; - break; - case 3: - modeInfo->flags |= DRM_MODE_FLAG_PIC_AR_16_9; - break; - case 8: - modeInfo->flags |= DRM_MODE_FLAG_PIC_AR_64_27; - break; - default: - modeInfo->flags |= DRM_MODE_FLAG_PIC_AR_NONE; - break; - } modeInfo->type = 0; snprintf(modeInfo->name, DRM_DISPLAY_MODE_LEN, "CRT_%ux%u_%u", modeInfo->hdisplay, modeInfo->vdisplay, modeInfo->vrefresh); @@ -445,9 +436,15 @@ nextgpu: drm_setup(fd); /* Choose the optimal video mode for get_video_size(): - - the current video mode from the CRTC + - custom timings from configuration + - else the current video mode from the CRTC - otherwise pick first connector mode */ - if (g_orig_crtc->mode_valid) + if(gfx_ctx_drm_load_mode(&gfx_ctx_crt_switch_mode)) + { + drm->fb_width = gfx_ctx_crt_switch_mode.hdisplay; + drm->fb_height = gfx_ctx_crt_switch_mode.vdisplay; + } + else if (g_orig_crtc->mode_valid) { drm->fb_width = g_orig_crtc->mode.hdisplay; drm->fb_height = g_orig_crtc->mode.vdisplay; @@ -693,8 +690,6 @@ error: } #endif - - static bool gfx_ctx_drm_set_video_mode(void *data, unsigned width, unsigned height, bool fullscreen) @@ -706,7 +701,6 @@ static bool gfx_ctx_drm_set_video_mode(void *data, settings_t *settings = config_get_ptr(); unsigned black_frame_insertion = settings->uints.video_black_frame_insertion; float video_refresh_rate = settings->floats.video_refresh_rate; - char *crt_switch_timings = settings->arrays.crt_switch_timings; if (!drm) return false; @@ -725,12 +719,11 @@ static bool gfx_ctx_drm_set_video_mode(void *data, g_drm_mode = &g_drm_connector->modes[0]; else { - // check for custom crt hdmi timings - if(!string_is_empty(crt_switch_timings) - && gfx_ctx_drm_timings_to_mode(crt_switch_timings, &crt_switch_mode)) + /* check if custom hdmi timings were asked */ + if(gfx_ctx_crt_switch_mode.vdisplay > 0) { - RARCH_LOG("[KMS/EGL]: custom mode requested: %s\n", crt_switch_mode.name); - g_drm_mode = &crt_switch_mode; + RARCH_LOG("[DRM]: custom mode requested: %s\n", gfx_ctx_crt_switch_mode.name); + g_drm_mode = &gfx_ctx_crt_switch_mode; } else {