[PATCH] drm/edid: Add both 60Hz and 59.94Hz CEA modes to connector's mode list
Daniel Vetter
daniel at ffwll.ch
Fri May 31 08:43:51 PDT 2013
On Fri, May 31, 2013 at 03:23:41PM +0300, ville.syrjala at linux.intel.com wrote:
> From: Ville Syrjälä <ville.syrjala at linux.intel.com>
>
> Having both modes can be beneficial for video playback cases. If you can
> match the video framerate exactly, and the audio and video clocks come
> from the same source, you should be able to avoid dropped/repeated
> frames without expensive operations such as resampling the audio to
> match video output rate.
>
> Rather than add both variants based on the CEA extension short video
> descriptors in do_cea_modes(), add only one variant there. Once all
> the EDID has been fully probed, do a loop over the entire probed mode
> list, during which we add the other variants for all modes that match
> CEA modes. This allows us to match modes that didn't come via the CEA
> short video descriptors. For example one Samsung TV here doesn't have
> the 640x480-60 mode as a SVD, but instead it's specified via a detailed
> timing descriptor.
>
> Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
> ---
> A few people requested this. Originally I was a bit opposed to it, but
> when I thought about it a bit more I figured if the audio and video
> clocks come from the same source (or happen to be close enough w/o
> significant drift), this could provide a better A/V sync w/o resampling
> tricks.
Yeah, I think this should be useful for a bunch of people. I've recently
chatted with a few xbmc folks on #irc and one thing they've requested is
mode fine-tuning. For DP we should have plenty of precision, but for HDMI
we'd need to (slightly) frob the vtotal ever so often to compensate. With
some runtime-tuning a la npt we should have perfect a/v sync without any
audio resampling.
Anyway, on the patch:
Reviewed-by: Daniel Vetter <daniel.vetter at ffwll.ch>
>
> drivers/gpu/drm/drm_edid.c | 102 ++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 88 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 9e62bbe..e8d17ee 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -2321,6 +2321,31 @@ u8 *drm_find_cea_extension(struct edid *edid)
> }
> EXPORT_SYMBOL(drm_find_cea_extension);
>
> +/*
> + * Calculate the alternate clock for the CEA mode
> + * (60Hz vs. 59.94Hz etc.)
> + */
> +static unsigned int
> +cea_mode_alternate_clock(const struct drm_display_mode *cea_mode)
> +{
> + unsigned int clock = cea_mode->clock;
> +
> + if (cea_mode->vrefresh % 6 != 0)
> + return clock;
> +
> + /*
> + * edid_cea_modes contains the 59.94Hz
> + * variant for 240 and 480 line modes,
> + * and the 60Hz variant otherwise.
> + */
> + if (cea_mode->vdisplay == 240 || cea_mode->vdisplay == 480)
> + clock = clock * 1001 / 1000;
> + else
> + clock = DIV_ROUND_UP(clock * 1000, 1001);
> +
> + return clock;
> +}
> +
> /**
> * drm_match_cea_mode - look for a CEA mode matching given mode
> * @to_match: display mode
> @@ -2339,21 +2364,9 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
> const struct drm_display_mode *cea_mode = &edid_cea_modes[mode];
> unsigned int clock1, clock2;
>
> - clock1 = clock2 = cea_mode->clock;
> -
> /* Check both 60Hz and 59.94Hz */
> - if (cea_mode->vrefresh % 6 == 0) {
> - /*
> - * edid_cea_modes contains the 59.94Hz
> - * variant for 240 and 480 line modes,
> - * and the 60Hz variant otherwise.
> - */
> - if (cea_mode->vdisplay == 240 ||
> - cea_mode->vdisplay == 480)
> - clock1 = clock1 * 1001 / 1000;
> - else
> - clock2 = DIV_ROUND_UP(clock2 * 1000, 1001);
> - }
> + clock1 = cea_mode->clock;
> + clock2 = cea_mode_alternate_clock(cea_mode);
>
> if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) ||
> KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) &&
> @@ -2364,6 +2377,66 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
> }
> EXPORT_SYMBOL(drm_match_cea_mode);
>
> +static int
> +add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid)
> +{
> + struct drm_device *dev = connector->dev;
> + struct drm_display_mode *mode, *tmp;
> + LIST_HEAD(list);
> + int modes = 0;
> +
> + /* Don't add CEA modes if the CEA extension block is missing */
> + if (!drm_find_cea_extension(edid))
> + return 0;
> +
> + /*
> + * Go through all probed modes and create a new mode
> + * with the alternate clock for certain CEA modes.
> + */
> + list_for_each_entry(mode, &connector->probed_modes, head) {
> + const struct drm_display_mode *cea_mode;
> + struct drm_display_mode *newmode;
> + u8 cea_mode_idx = drm_match_cea_mode(mode) - 1;
> + unsigned int clock1, clock2;
> +
> + if (cea_mode_idx >= ARRAY_SIZE(edid_cea_modes))
> + continue;
> +
> + cea_mode = &edid_cea_modes[cea_mode_idx];
> +
> + clock1 = cea_mode->clock;
> + clock2 = cea_mode_alternate_clock(cea_mode);
> +
> + if (clock1 == clock2)
> + continue;
> +
> + if (mode->clock != clock1 && mode->clock != clock2)
> + continue;
> +
> + newmode = drm_mode_duplicate(dev, cea_mode);
> + if (!newmode)
> + continue;
> +
> + /*
> + * The current mode could be either variant. Make
> + * sure to pick the "other" clock for the new mode.
> + */
> + if (mode->clock != clock1)
> + newmode->clock = clock1;
> + else
> + newmode->clock = clock2;
> +
> + list_add_tail(&newmode->head, &list);
> + }
> +
> + list_for_each_entry_safe(mode, tmp, &list, head) {
> + list_del(&mode->head);
> + drm_mode_probed_add(connector, mode);
> + modes++;
> + }
> +
> + return modes;
> +}
>
> static int
> do_cea_modes (struct drm_connector *connector, u8 *db, u8 len)
> @@ -2946,6 +3019,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
> if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
> num_modes += add_inferred_modes(connector, edid);
> num_modes += add_cea_modes(connector, edid);
> + num_modes += add_alternate_cea_modes(connector, edid);
>
> if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
> edid_fixup_preferred(connector, quirks);
> --
> 1.8.1.5
>
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
More information about the dri-devel
mailing list