Commit b17e52ef authored by Adam Jackson's avatar Adam Jackson Committed by Dave Airlie

drm/edid: Extend range-based mode addition for EDID 1.4

1.4 adds better pixel clock precision, explicit reduced blanking
awareness, and extended sync ranges.  It's almost like a real spec.
Signed-off-by: default avatarAdam Jackson <ajax@redhat.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent d1ff6409
...@@ -1066,36 +1066,89 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid ...@@ -1066,36 +1066,89 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid
return modes; return modes;
} }
/*
* XXX fix this for:
* - GTF secondary curve formula
* - EDID 1.4 range offsets
* - CVT extended bits
*/
static bool static bool
mode_in_range(struct drm_display_mode *mode, struct detailed_timing *timing) mode_is_rb(struct drm_display_mode *mode)
{ {
struct detailed_data_monitor_range *range; return (mode->htotal - mode->hdisplay == 160) &&
int hsync, vrefresh; (mode->hsync_end - mode->hdisplay == 80) &&
(mode->hsync_end - mode->hsync_start == 32) &&
range = &timing->data.other_data.data.range; (mode->vsync_start - mode->vdisplay == 3);
}
static bool
mode_in_hsync_range(struct drm_display_mode *mode, struct edid *edid, u8 *t)
{
int hsync, hmin, hmax;
hmin = t[7];
if (edid->revision >= 4)
hmin += ((t[4] & 0x04) ? 255 : 0);
hmax = t[8];
if (edid->revision >= 4)
hmax += ((t[4] & 0x08) ? 255 : 0);
hsync = drm_mode_hsync(mode); hsync = drm_mode_hsync(mode);
vrefresh = drm_mode_vrefresh(mode);
if (hsync < range->min_hfreq_khz || hsync > range->max_hfreq_khz) return (hsync <= hmax && hsync >= hmin);
}
static bool
mode_in_vsync_range(struct drm_display_mode *mode, struct edid *edid, u8 *t)
{
int vsync, vmin, vmax;
vmin = t[5];
if (edid->revision >= 4)
vmin += ((t[4] & 0x01) ? 255 : 0);
vmax = t[6];
if (edid->revision >= 4)
vmax += ((t[4] & 0x02) ? 255 : 0);
vsync = drm_mode_vrefresh(mode);
return (vsync <= vmax && vsync >= vmin);
}
static u32
range_pixel_clock(struct edid *edid, u8 *t)
{
/* unspecified */
if (t[9] == 0 || t[9] == 255)
return 0;
/* 1.4 with CVT support gives us real precision, yay */
if (edid->revision >= 4 && t[10] == 0x04)
return (t[9] * 10000) - ((t[12] >> 2) * 250);
/* 1.3 is pathetic, so fuzz up a bit */
return t[9] * 10000 + 5001;
}
/*
* XXX fix this for GTF secondary curve formula
*/
static bool
mode_in_range(struct drm_display_mode *mode, struct edid *edid,
struct detailed_timing *timing)
{
u32 max_clock;
u8 *t = (u8 *)timing;
if (!mode_in_hsync_range(mode, edid, t))
return false; return false;
if (vrefresh < range->min_vfreq || vrefresh > range->max_vfreq) if (!mode_in_vsync_range(mode, edid, t))
return false; return false;
if (range->pixel_clock_mhz && range->pixel_clock_mhz != 0xff) { if ((max_clock = range_pixel_clock(edid, t)))
/* be forgiving since it's in units of 10MHz */
int max_clock = range->pixel_clock_mhz * 10 + 9;
max_clock *= 1000;
if (mode->clock > max_clock) if (mode->clock > max_clock)
return false; return false;
}
/* 1.4 max horizontal check */
if (edid->revision >= 4 && t[10] == 0x04)
if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3))))
return false;
if (mode_is_rb(mode) && !drm_monitor_supports_rb(edid))
return false;
return true; return true;
} }
...@@ -1104,15 +1157,16 @@ mode_in_range(struct drm_display_mode *mode, struct detailed_timing *timing) ...@@ -1104,15 +1157,16 @@ mode_in_range(struct drm_display_mode *mode, struct detailed_timing *timing)
* XXX If drm_dmt_modes ever regrows the CVT-R modes (and it will) this will * XXX If drm_dmt_modes ever regrows the CVT-R modes (and it will) this will
* need to account for them. * need to account for them.
*/ */
static int drm_gtf_modes_for_range(struct drm_connector *connector, static int
struct detailed_timing *timing) drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
struct detailed_timing *timing)
{ {
int i, modes = 0; int i, modes = 0;
struct drm_display_mode *newmode; struct drm_display_mode *newmode;
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
for (i = 0; i < drm_num_dmt_modes; i++) { for (i = 0; i < drm_num_dmt_modes; i++) {
if (mode_in_range(drm_dmt_modes + i, timing)) { if (mode_in_range(drm_dmt_modes + i, edid, timing)) {
newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]); newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]);
if (newmode) { if (newmode) {
drm_mode_probed_add(connector, newmode); drm_mode_probed_add(connector, newmode);
...@@ -1288,7 +1342,8 @@ static int add_detailed_modes(struct drm_connector *connector, ...@@ -1288,7 +1342,8 @@ static int add_detailed_modes(struct drm_connector *connector,
switch (data->type) { switch (data->type) {
case EDID_DETAIL_MONITOR_RANGE: case EDID_DETAIL_MONITOR_RANGE:
if (gtf) if (gtf)
modes += drm_gtf_modes_for_range(connector, timing); modes += drm_gtf_modes_for_range(connector, edid,
timing);
break; break;
case EDID_DETAIL_STD_MODES: case EDID_DETAIL_STD_MODES:
/* Six modes per detailed section */ /* Six modes per detailed section */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment