(KMS/DRM) add support for custom modes from hdmi timings

This commit is contained in:
cpasjuste 2020-11-18 11:34:44 +01:00
parent bcc7dde90d
commit 0082806620
3 changed files with 98 additions and 18 deletions

View File

@ -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;

View File

@ -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

View File

@ -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;
}
}
}
}