UPSTREAM: drm/edid: Make the detailed timing CEA/HDMI mode fixup accept up to 5kHz...
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Mon, 16 Nov 2015 19:05:12 +0000 (21:05 +0200)
committerZheng Yang <zhengyang@rock-chips.com>
Fri, 3 Mar 2017 03:39:23 +0000 (11:39 +0800)
Rather than using drm_match_cea_mode() to see if the EDID detailed
timings are supposed to represent one of the CEA/HDMI modes, add a
special version of that function that takes in an explicit clock
tolerance value (in kHz). When looking at the detailed timings specify
the tolerance as 5kHz due to the 10kHz clock resolution limit inherent
in detailed timings.

drm_match_cea_mode() uses the normal KHZ2PICOS() matching of clocks,
which only allows smaller errors for lower clocks (eg. for 25200 it
won't allow any error) and a bigger error for higher clocks (eg. for
297000 it actually matches 296913-297000). So it doesn't really match
what we want for the fixup. Using the explicit +-5kHz is much better
for this use case.

Not sure if we should change the normal mode matching to also use
something else besides KHZ2PICOS() since it allows a different
proportion of error depending on the clock. I believe VESA CVT
allows a maximum deviation of .5%, so using that for normal mode
matching might be a good idea?

Change-Id: I824ec50368ddf152c9daa747ba92aaba1ef50f4b
Cc: Adam Jackson <ajax@redhat.com>
Tested-by: nathan.d.ciobanu@linux.intel.com
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=92217
Fixes: fa3a7340eaa1 ("drm/edid: Fix up clock for CEA/HDMI modes specified via detailed timings")
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>
(cherry picked from commit 4c6bcf44549907cb50b67f98eb13717a4adc6b33)
Signed-off-by: Zheng Yang <zhengyang@rock-chips.com>
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_modes.c
include/drm/drm_modes.h

index 7e7e4446962bc80ced41bf8a1c5e5bd42aaa30e2..2d6932c9a351223b26aec2a0cc0edbb3dc2a7cfb 100644 (file)
@@ -2556,6 +2556,33 @@ cea_mode_alternate_clock(const struct drm_display_mode *cea_mode)
        return clock;
 }
 
+static u8 drm_match_cea_mode_clock_tolerance(const struct drm_display_mode *to_match,
+                                            unsigned int clock_tolerance)
+{
+       u8 mode;
+
+       if (!to_match->clock)
+               return 0;
+
+       for (mode = 0; mode < ARRAY_SIZE(edid_cea_modes); mode++) {
+               const struct drm_display_mode *cea_mode = &edid_cea_modes[mode];
+               unsigned int clock1, clock2;
+
+               /* Check both 60Hz and 59.94Hz */
+               clock1 = cea_mode->clock;
+               clock2 = cea_mode_alternate_clock(cea_mode);
+
+               if (abs(to_match->clock - clock1) > clock_tolerance &&
+                   abs(to_match->clock - clock2) > clock_tolerance)
+                       continue;
+
+               if (drm_mode_equal_no_clocks(to_match, cea_mode))
+                       return mode + 1;
+       }
+
+       return 0;
+}
+
 /**
  * drm_match_cea_mode - look for a CEA mode matching given mode
  * @to_match: display mode
@@ -2620,6 +2647,33 @@ hdmi_mode_alternate_clock(const struct drm_display_mode *hdmi_mode)
        return cea_mode_alternate_clock(hdmi_mode);
 }
 
+static u8 drm_match_hdmi_mode_clock_tolerance(const struct drm_display_mode *to_match,
+                                             unsigned int clock_tolerance)
+{
+       u8 mode;
+
+       if (!to_match->clock)
+               return 0;
+
+       for (mode = 0; mode < ARRAY_SIZE(edid_4k_modes); mode++) {
+               const struct drm_display_mode *hdmi_mode = &edid_4k_modes[mode];
+               unsigned int clock1, clock2;
+
+               /* Make sure to also match alternate clocks */
+               clock1 = hdmi_mode->clock;
+               clock2 = hdmi_mode_alternate_clock(hdmi_mode);
+
+               if (abs(to_match->clock - clock1) > clock_tolerance &&
+                   abs(to_match->clock - clock2) > clock_tolerance)
+                       continue;
+
+               if (drm_mode_equal_no_clocks(to_match, hdmi_mode))
+                       return mode + 1;
+       }
+
+       return 0;
+}
+
 /*
  * drm_match_hdmi_mode - look for a HDMI mode matching given mode
  * @to_match: display mode
@@ -3145,14 +3199,18 @@ static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode)
        u8 mode_idx;
        const char *type;
 
-       mode_idx = drm_match_cea_mode(mode) - 1;
+       /*
+        * allow 5kHz clock difference either way to account for
+        * the 10kHz clock resolution limit of detailed timings.
+        */
+       mode_idx = drm_match_cea_mode_clock_tolerance(mode, 5) - 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;
+               mode_idx = drm_match_hdmi_mode_clock_tolerance(mode, 5) - 1;
                if (mode_idx < ARRAY_SIZE(edid_4k_modes)) {
                        type = "HDMI";
                        cea_mode = &edid_4k_modes[mode_idx];
index 71a10f08522e4a8f67c914dd3a9307b4435f1c99..a773a7cdcb07891e3c5a6ecb598f44a259d53c1b 100644 (file)
@@ -917,13 +917,30 @@ bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_displ
        } else if (mode1->clock != mode2->clock)
                return false;
 
+       return drm_mode_equal_no_clocks(mode1, mode2);
+}
+EXPORT_SYMBOL(drm_mode_equal);
+
+/**
+ * drm_mode_equal_no_clocks - test modes for equality
+ * @mode1: first mode
+ * @mode2: second mode
+ *
+ * Check to see if @mode1 and @mode2 are equivalent, but
+ * don't check the pixel clocks.
+ *
+ * Returns:
+ * True if the modes are equal, false otherwise.
+ */
+bool drm_mode_equal_no_clocks(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2)
+{
        if ((mode1->flags & DRM_MODE_FLAG_3D_MASK) !=
            (mode2->flags & DRM_MODE_FLAG_3D_MASK))
                return false;
 
        return drm_mode_equal_no_clocks_no_stereo(mode1, mode2);
 }
-EXPORT_SYMBOL(drm_mode_equal);
+EXPORT_SYMBOL(drm_mode_equal_no_clocks);
 
 /**
  * drm_mode_equal_no_clocks_no_stereo - test modes for equality
index 08a8cac9e5554ea20b3184bee3a8bb31088e757c..f9115aee43f480d7c972eb72c34031cc27bfc3cd 100644 (file)
@@ -222,6 +222,8 @@ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
                                            const struct drm_display_mode *mode);
 bool drm_mode_equal(const struct drm_display_mode *mode1,
                    const struct drm_display_mode *mode2);
+bool drm_mode_equal_no_clocks(const struct drm_display_mode *mode1,
+                             const struct drm_display_mode *mode2);
 bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1,
                                        const struct drm_display_mode *mode2);