Skip to content

Commit

Permalink
drm/edid: Extend range-based mode addition for EDID 1.4
Browse files Browse the repository at this point in the history
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: Adam Jackson <ajax@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
  • Loading branch information
Adam Jackson authored and Dave Airlie committed Apr 6, 2010
1 parent d1ff640 commit b17e52e
Showing 1 changed file with 78 additions and 23 deletions.
101 changes: 78 additions & 23 deletions drivers/gpu/drm/drm_edid.c
Original file line number Diff line number Diff line change
Expand Up @@ -1066,36 +1066,89 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid
return modes;
}

/*
* XXX fix this for:
* - GTF secondary curve formula
* - EDID 1.4 range offsets
* - CVT extended bits
*/
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;
int hsync, vrefresh;

range = &timing->data.other_data.data.range;
return (mode->htotal - mode->hdisplay == 160) &&
(mode->hsync_end - mode->hdisplay == 80) &&
(mode->hsync_end - mode->hsync_start == 32) &&
(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);
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;

if (vrefresh < range->min_vfreq || vrefresh > range->max_vfreq)
if (!mode_in_vsync_range(mode, edid, t))
return false;

if (range->pixel_clock_mhz && range->pixel_clock_mhz != 0xff) {
/* be forgiving since it's in units of 10MHz */
int max_clock = range->pixel_clock_mhz * 10 + 9;
max_clock *= 1000;
if ((max_clock = range_pixel_clock(edid, t)))
if (mode->clock > max_clock)
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;
}
Expand All @@ -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
* need to account for them.
*/
static int drm_gtf_modes_for_range(struct drm_connector *connector,
struct detailed_timing *timing)
static int
drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
struct detailed_timing *timing)
{
int i, modes = 0;
struct drm_display_mode *newmode;
struct drm_device *dev = connector->dev;

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]);
if (newmode) {
drm_mode_probed_add(connector, newmode);
Expand Down Expand Up @@ -1288,7 +1342,8 @@ static int add_detailed_modes(struct drm_connector *connector,
switch (data->type) {
case EDID_DETAIL_MONITOR_RANGE:
if (gtf)
modes += drm_gtf_modes_for_range(connector, timing);
modes += drm_gtf_modes_for_range(connector, edid,
timing);
break;
case EDID_DETAIL_STD_MODES:
/* Six modes per detailed section */
Expand Down

0 comments on commit b17e52e

Please sign in to comment.