[PATCH 5/5] drm/edid: add displayid detailed 1 timings to the modelist.
Jani Nikula
jani.nikula at linux.intel.com
Wed May 4 08:10:53 UTC 2016
On Tue, 03 May 2016, Dave Airlie <airlied at gmail.com> wrote:
> From: Dave Airlie <airlied at redhat.com>
>
> The tiled 5K Dell monitor appears to be hiding it's tiled mode
> inside the displayid timings block, this patch parses this
> blocks and adds the modes to the modelist.
>
> Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=95207
> Signed-off-by: Dave Airlie <airlied at redhat.com>
> ---
> drivers/gpu/drm/drm_edid.c | 105 ++++++++++++++++++++++++++++++++++++++++++++
> include/drm/drm_displayid.h | 17 +++++++
> 2 files changed, 122 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index e85d828..aca9e25 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -3925,6 +3925,110 @@ static int validate_displayid(u8 *displayid, int length, int idx)
> return 0;
> }
>
> +static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev,
> + struct displayid_detailed_timings_1 *timings)
> +{
> + struct drm_display_mode *mode;
> + unsigned pixel_clock = (timings->pixel_clock[0] |
> + (timings->pixel_clock[1] << 8) |
> + (timings->pixel_clock[2] << 16));
> + unsigned hactive = (timings->hactive[0] | timings->hactive[1] << 8) + 1;
> + unsigned hblank = (timings->hblank[0] | timings->hblank[1] << 8) + 1;
> + unsigned hsync = (timings->hsync[0] | (timings->hsync[1] & 0x7f) << 8) + 1;
> + unsigned hsync_width = (timings->hsw[0] | timings->hsw[1] << 8) + 1;
> + unsigned vactive = (timings->vactive[0] | timings->vactive[1] << 8) + 1;
> + unsigned vblank = (timings->vblank[0] | timings->vblank[1] << 8) + 1;
> + unsigned vsync = (timings->vsync[0] | (timings->vsync[1] & 0x7f) << 8) + 1;
> + unsigned vsync_width = (timings->vsw[0] | timings->vsw[1] << 8) + 1;
> + bool hsync_positive = (timings->hsync[1] >> 7) & 0x1;
> + bool vsync_positive = (timings->vsync[1] >> 7) & 0x1;
> + mode = drm_mode_create(dev);
> + if (!mode)
> + return NULL;
> +
> + mode->clock = pixel_clock * 10;
> + mode->hdisplay = hactive;
> + mode->hsync_start = mode->hdisplay + hsync;
> + mode->hsync_end = mode->hsync_start + hsync_width;
> + mode->htotal = mode->hdisplay + hblank;
> +
> + mode->vdisplay = vactive;
> + mode->vsync_start = mode->vdisplay + vsync;
> + mode->vsync_end = mode->vsync_start + vsync_width;
> + mode->vtotal = mode->vdisplay + vblank;
> +
> + mode->flags = 0;
> + mode->flags |= hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
> + mode->flags |= vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
> + mode->type = DRM_MODE_TYPE_DRIVER;
> +
> + if (timings->flags & 0x80)
> + mode->type |= DRM_MODE_TYPE_PREFERRED;
> + mode->vrefresh = drm_mode_vrefresh(mode);
> + drm_mode_set_name(mode);
> +
> + return mode;
> +}
> +
> +static int add_displayid_detailed_1_modes(struct drm_connector *connector,
> + struct displayid_block *block)
> +{
> + struct displayid_detailed_timing_block *det = (struct displayid_detailed_timing_block *)block;
> + int i;
> + int num_timings;
> + struct drm_display_mode *newmode;
> + int num_modes = 0;
> + /* blocks must be multiple of 20 bytes length */
> + if (block->num_bytes % 20)
> + return 0;
> +
> + num_timings = block->num_bytes / 20;
> + for (i = 0; i < num_timings; i++) {
> + struct displayid_detailed_timings_1 *timings = &det->timings[i];
> +
> + newmode = drm_mode_displayid_detailed(connector->dev, timings);
> + if (!newmode)
> + continue;
> +
> + drm_mode_probed_add(connector, newmode);
> + num_modes++;
> + }
> + return num_modes;
> +}
> +
> +static int add_displayid_detailed_modes(struct drm_connector *connector,
> + struct edid *edid)
> +{
> + u8 *displayid;
> + int ret;
> + int idx = 1;
> + int length = EDID_LENGTH;
> + struct displayid_block *block;
> + int num_modes = 0;
> +
> + displayid = drm_find_displayid_extension(edid);
> + if (!displayid)
> + return 0;
> +
> + ret = validate_displayid(displayid, length, idx);
> + if (ret)
> + return 0;
> +
> + idx += sizeof(struct displayid_hdr);
> + while (block = (struct displayid_block *)&displayid[idx],
> + idx + sizeof(struct displayid_block) <= length &&
> + idx + sizeof(struct displayid_block) + block->num_bytes <= length &&
> + block->num_bytes > 0) {
> + idx += block->num_bytes + sizeof(struct displayid_block);
> + switch (block->tag) {
> + case DATA_BLOCK_TYPE_1_DETAILED_TIMING:
> + num_modes += add_displayid_detailed_1_modes(connector, block);
> + break;
> + }
> + }
> + return num_modes;
> +}
> +
> /**
> * drm_add_edid_modes - add modes from EDID data, if available
> * @connector: connector we're probing
> @@ -3970,6 +4074,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
> num_modes += add_established_modes(connector, edid);
> num_modes += add_cea_modes(connector, edid);
> num_modes += add_alternate_cea_modes(connector, edid);
> + num_modes += add_displayid_detailed_modes(connector, edid);
> if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
> num_modes += add_inferred_modes(connector, edid);
>
> diff --git a/include/drm/drm_displayid.h b/include/drm/drm_displayid.h
> index 042f9fc..edef51d 100644
> --- a/include/drm/drm_displayid.h
> +++ b/include/drm/drm_displayid.h
> @@ -75,4 +75,21 @@ struct displayid_tiled_block {
> u8 topology_id[8];
> } __packed;
>
> +struct displayid_detailed_timings_1 {
> + u8 pixel_clock[3];
> + u8 flags;
> + u8 hactive[2];
> + u8 hblank[2];
> + u8 hsync[2];
> + u8 hsw[2];
> + u8 vactive[2];
> + u8 vblank[2];
> + u8 vsync[2];
> + u8 vsw[2];
An alternative would be to declare these fields as __le16, and you could
read them in drm_mode_displayid_detailed() using le16_to_cpu().
Anyway, these structs should be __packed.
BR,
Jani.
> +};
> +
> +struct displayid_detailed_timing_block {
> + struct displayid_block base;
> + struct displayid_detailed_timings_1 timings[0];
> +};
> #endif
--
Jani Nikula, Intel Open Source Technology Center
More information about the dri-devel
mailing list