aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2016-05-01 18:35:05 -0400
committerDave Airlie <airlied@redhat.com>2016-05-22 21:35:31 -0400
commita39ed680bddb1ead592e22ed812c7e47286bfc03 (patch)
tree510d7161ec444c8e787bbc5a50cf6e2fb2ba0ee3
parentc97291774c1b867b56c3d439ddaec9a965cf559e (diff)
drm/edid: add displayid detailed 1 timings to the modelist. (v1.1)
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. v1.1: add missing __packed. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=95207 Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r--drivers/gpu/drm/drm_edid.c108
-rw-r--r--include/drm/drm_displayid.h17
2 files changed, 125 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index fac4efc7dfe2..7df26d4b7ad8 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -3924,6 +3924,110 @@ static int validate_displayid(u8 *displayid, int length, int idx)
3924 return 0; 3924 return 0;
3925} 3925}
3926 3926
3927static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev,
3928 struct displayid_detailed_timings_1 *timings)
3929{
3930 struct drm_display_mode *mode;
3931 unsigned pixel_clock = (timings->pixel_clock[0] |
3932 (timings->pixel_clock[1] << 8) |
3933 (timings->pixel_clock[2] << 16));
3934 unsigned hactive = (timings->hactive[0] | timings->hactive[1] << 8) + 1;
3935 unsigned hblank = (timings->hblank[0] | timings->hblank[1] << 8) + 1;
3936 unsigned hsync = (timings->hsync[0] | (timings->hsync[1] & 0x7f) << 8) + 1;
3937 unsigned hsync_width = (timings->hsw[0] | timings->hsw[1] << 8) + 1;
3938 unsigned vactive = (timings->vactive[0] | timings->vactive[1] << 8) + 1;
3939 unsigned vblank = (timings->vblank[0] | timings->vblank[1] << 8) + 1;
3940 unsigned vsync = (timings->vsync[0] | (timings->vsync[1] & 0x7f) << 8) + 1;
3941 unsigned vsync_width = (timings->vsw[0] | timings->vsw[1] << 8) + 1;
3942 bool hsync_positive = (timings->hsync[1] >> 7) & 0x1;
3943 bool vsync_positive = (timings->vsync[1] >> 7) & 0x1;
3944 mode = drm_mode_create(dev);
3945 if (!mode)
3946 return NULL;
3947
3948 mode->clock = pixel_clock * 10;
3949 mode->hdisplay = hactive;
3950 mode->hsync_start = mode->hdisplay + hsync;
3951 mode->hsync_end = mode->hsync_start + hsync_width;
3952 mode->htotal = mode->hdisplay + hblank;
3953
3954 mode->vdisplay = vactive;
3955 mode->vsync_start = mode->vdisplay + vsync;
3956 mode->vsync_end = mode->vsync_start + vsync_width;
3957 mode->vtotal = mode->vdisplay + vblank;
3958
3959 mode->flags = 0;
3960 mode->flags |= hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
3961 mode->flags |= vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
3962 mode->type = DRM_MODE_TYPE_DRIVER;
3963
3964 if (timings->flags & 0x80)
3965 mode->type |= DRM_MODE_TYPE_PREFERRED;
3966 mode->vrefresh = drm_mode_vrefresh(mode);
3967 drm_mode_set_name(mode);
3968
3969 return mode;
3970}
3971
3972static int add_displayid_detailed_1_modes(struct drm_connector *connector,
3973 struct displayid_block *block)
3974{
3975 struct displayid_detailed_timing_block *det = (struct displayid_detailed_timing_block *)block;
3976 int i;
3977 int num_timings;
3978 struct drm_display_mode *newmode;
3979 int num_modes = 0;
3980 /* blocks must be multiple of 20 bytes length */
3981 if (block->num_bytes % 20)
3982 return 0;
3983
3984 num_timings = block->num_bytes / 20;
3985 for (i = 0; i < num_timings; i++) {
3986 struct displayid_detailed_timings_1 *timings = &det->timings[i];
3987
3988 newmode = drm_mode_displayid_detailed(connector->dev, timings);
3989 if (!newmode)
3990 continue;
3991
3992 drm_mode_probed_add(connector, newmode);
3993 num_modes++;
3994 }
3995 return num_modes;
3996}
3997
3998static int add_displayid_detailed_modes(struct drm_connector *connector,
3999 struct edid *edid)
4000{
4001 u8 *displayid;
4002 int ret;
4003 int idx = 1;
4004 int length = EDID_LENGTH;
4005 struct displayid_block *block;
4006 int num_modes = 0;
4007
4008 displayid = drm_find_displayid_extension(edid);
4009 if (!displayid)
4010 return 0;
4011
4012 ret = validate_displayid(displayid, length, idx);
4013 if (ret)
4014 return 0;
4015
4016 idx += sizeof(struct displayid_hdr);
4017 while (block = (struct displayid_block *)&displayid[idx],
4018 idx + sizeof(struct displayid_block) <= length &&
4019 idx + sizeof(struct displayid_block) + block->num_bytes <= length &&
4020 block->num_bytes > 0) {
4021 idx += block->num_bytes + sizeof(struct displayid_block);
4022 switch (block->tag) {
4023 case DATA_BLOCK_TYPE_1_DETAILED_TIMING:
4024 num_modes += add_displayid_detailed_1_modes(connector, block);
4025 break;
4026 }
4027 }
4028 return num_modes;
4029}
4030
3927/** 4031/**
3928 * drm_add_edid_modes - add modes from EDID data, if available 4032 * drm_add_edid_modes - add modes from EDID data, if available
3929 * @connector: connector we're probing 4033 * @connector: connector we're probing
@@ -3969,6 +4073,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
3969 num_modes += add_established_modes(connector, edid); 4073 num_modes += add_established_modes(connector, edid);
3970 num_modes += add_cea_modes(connector, edid); 4074 num_modes += add_cea_modes(connector, edid);
3971 num_modes += add_alternate_cea_modes(connector, edid); 4075 num_modes += add_alternate_cea_modes(connector, edid);
4076 num_modes += add_displayid_detailed_modes(connector, edid);
3972 if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) 4077 if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
3973 num_modes += add_inferred_modes(connector, edid); 4078 num_modes += add_inferred_modes(connector, edid);
3974 4079
@@ -4260,6 +4365,9 @@ static int drm_parse_display_id(struct drm_connector *connector,
4260 if (ret) 4365 if (ret)
4261 return ret; 4366 return ret;
4262 break; 4367 break;
4368 case DATA_BLOCK_TYPE_1_DETAILED_TIMING:
4369 /* handled in mode gathering code. */
4370 break;
4263 default: 4371 default:
4264 DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n", block->tag); 4372 DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n", block->tag);
4265 break; 4373 break;
diff --git a/include/drm/drm_displayid.h b/include/drm/drm_displayid.h
index 623b4e98e748..c0d4df6a606f 100644
--- a/include/drm/drm_displayid.h
+++ b/include/drm/drm_displayid.h
@@ -73,4 +73,21 @@ struct displayid_tiled_block {
73 u8 topology_id[8]; 73 u8 topology_id[8];
74} __packed; 74} __packed;
75 75
76struct displayid_detailed_timings_1 {
77 u8 pixel_clock[3];
78 u8 flags;
79 u8 hactive[2];
80 u8 hblank[2];
81 u8 hsync[2];
82 u8 hsw[2];
83 u8 vactive[2];
84 u8 vblank[2];
85 u8 vsync[2];
86 u8 vsw[2];
87} __packed;
88
89struct displayid_detailed_timing_block {
90 struct displayid_block base;
91 struct displayid_detailed_timings_1 timings[0];
92};
76#endif 93#endif