summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_edid.c
diff options
context:
space:
mode:
authorVille Syrjälä <ville.syrjala@linux.intel.com>2015-10-08 11:43:32 +0300
committerDaniel Vetter <daniel.vetter@ffwll.ch>2015-10-20 12:01:28 +0200
commitfa3a7340eaa174a36c12c31747dc6eccd08530b5 (patch)
treedd7f5b7239b9fd7adad05a61466b2902bbbee0f7 /drivers/gpu/drm/drm_edid.c
parentaffa0e033b04996700434312c76df3c78f683870 (diff)
drm/edid: Fix up clock for CEA/HDMI modes specified via detailed timings
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 <ajax@redhat.com> Cc: Clint Taylor <clinton.a.taylor@intel.com> Cc: Libin Yang <libin.yang@intel.com> Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Reviewed-by: Adam Jackson <ajax@redhat.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
-rw-r--r--drivers/gpu/drm/drm_edid.c48
1 files changed, 48 insertions, 0 deletions
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)
{