From fa3a7340eaa174a36c12c31747dc6eccd08530b5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Oct 2015 11:43:32 +0300 Subject: [PATCH] drm/edid: Fix up clock for CEA/HDMI modes specified via detailed timings MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit EDID detailed timings have a resolution of 10kHz for the pixel clock, so they can't represent certain CEA/HDMI modes accurately. If we see a mode coming in via detailed timings which otherwise matches one of the CEA/HDMI modes except the clock is just a bit off, let's assume that the intention was for that mode to be one of the CEA/HDMI modes and go ahead and fix up the clock to match the CEA/HDMI spec exactly (well, as close as we can get with the 1 kHz resolution we use). This should help code that's looking for an exact clock match (eg. i915 audio N/CTS setup). Cc: Adam Jackson Cc: Clint Taylor Cc: Libin Yang Signed-off-by: Ville Syrjälä Reviewed-by: Adam Jackson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_edid.c | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index d895556be4f0..977915cf530a 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2418,6 +2418,8 @@ add_cvt_modes(struct drm_connector *connector, struct edid *edid) return closure.modes; } +static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode); + static void do_detailed_mode(struct detailed_timing *timing, void *c) { @@ -2434,6 +2436,13 @@ do_detailed_mode(struct detailed_timing *timing, void *c) if (closure->preferred) newmode->type |= DRM_MODE_TYPE_PREFERRED; + /* + * Detailed modes are limited to 10kHz pixel clock resolution, + * so fix up anything that looks like CEA/HDMI mode, but the clock + * is just slightly off. + */ + fixup_detailed_cea_mode_clock(newmode); + drm_mode_probed_add(closure->connector, newmode); closure->modes++; closure->preferred = 0; @@ -3103,6 +3112,45 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid) return modes; } +static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode) +{ + const struct drm_display_mode *cea_mode; + int clock1, clock2, clock; + u8 mode_idx; + const char *type; + + mode_idx = drm_match_cea_mode(mode) - 1; + if (mode_idx < ARRAY_SIZE(edid_cea_modes)) { + type = "CEA"; + cea_mode = &edid_cea_modes[mode_idx]; + clock1 = cea_mode->clock; + clock2 = cea_mode_alternate_clock(cea_mode); + } else { + mode_idx = drm_match_hdmi_mode(mode) - 1; + if (mode_idx < ARRAY_SIZE(edid_4k_modes)) { + type = "HDMI"; + cea_mode = &edid_4k_modes[mode_idx]; + clock1 = cea_mode->clock; + clock2 = hdmi_mode_alternate_clock(cea_mode); + } else { + return; + } + } + + /* pick whichever is closest */ + if (abs(mode->clock - clock1) < abs(mode->clock - clock2)) + clock = clock1; + else + clock = clock2; + + if (mode->clock == clock) + return; + + DRM_DEBUG("detailed mode matches %s VIC %d, adjusting clock %d -> %d\n", + type, mode_idx + 1, mode->clock, clock); + mode->clock = clock; +} + static void parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db) { -- 2.20.1