Commit fa3a7340 authored by Ville Syrjälä's avatar Ville Syrjälä Committed by Daniel Vetter
Browse files

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: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: default avatarAdam Jackson <ajax@redhat.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent affa0e03
Loading
Loading
Loading
Loading
+48 −0
Original line number Diff line number Diff line
@@ -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)
{