123,18 → 123,20 |
*/ |
static bool edid_is_valid(struct edid *edid) |
{ |
int i; |
int i, score = 0; |
u8 csum = 0; |
u8 *raw_edid = (u8 *)edid; |
|
if (memcmp(edid->header, edid_header, sizeof(edid_header))) |
for (i = 0; i < sizeof(edid_header); i++) |
if (raw_edid[i] == edid_header[i]) |
score++; |
|
if (score == 8) ; |
else if (score >= 6) { |
DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); |
memcpy(raw_edid, edid_header, sizeof(edid_header)); |
} else |
goto bad; |
if (edid->version != 1) { |
DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); |
goto bad; |
} |
if (edid->revision > 4) |
DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); |
|
for (i = 0; i < EDID_LENGTH; i++) |
csum += raw_edid[i]; |
143,6 → 145,14 |
goto bad; |
} |
|
if (edid->version != 1) { |
DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); |
goto bad; |
} |
|
if (edid->revision > 4) |
DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); |
|
return 1; |
|
bad: |
481,16 → 491,17 |
3048, 3536, 0, 1600, 1603, 1609, 1682, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
}; |
static const int drm_num_dmt_modes = |
sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); |
|
static struct drm_display_mode *drm_find_dmt(struct drm_device *dev, |
int hsize, int vsize, int fresh) |
{ |
int i, count; |
int i; |
struct drm_display_mode *ptr, *mode; |
|
count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); |
mode = NULL; |
for (i = 0; i < count; i++) { |
for (i = 0; i < drm_num_dmt_modes; i++) { |
ptr = &drm_dmt_modes[i]; |
if (hsize == ptr->hdisplay && |
vsize == ptr->vdisplay && |
834,53 → 845,146 |
return modes; |
} |
|
/** |
* add_detailed_modes - get detailed mode info from EDID data |
* @connector: attached connector |
* @edid: EDID block to scan |
* @quirks: quirks to apply |
* |
* Some of the detailed timing sections may contain mode information. Grab |
* it and add it to the list. |
/* |
* XXX fix this for: |
* - GTF secondary curve formula |
* - EDID 1.4 range offsets |
* - CVT extended bits |
*/ |
static int add_detailed_info(struct drm_connector *connector, |
struct edid *edid, u32 quirks) |
static bool |
mode_in_range(struct drm_display_mode *mode, struct detailed_timing *timing) |
{ |
struct detailed_data_monitor_range *range; |
int hsync, vrefresh; |
|
range = &timing->data.other_data.data.range; |
|
hsync = drm_mode_hsync(mode); |
vrefresh = drm_mode_vrefresh(mode); |
|
if (hsync < range->min_hfreq_khz || hsync > range->max_hfreq_khz) |
return false; |
|
if (vrefresh < range->min_vfreq || vrefresh > range->max_vfreq) |
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 (mode->clock > max_clock) |
return false; |
} |
|
return true; |
} |
|
/* |
* 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) |
{ |
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)) { |
newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]); |
if (newmode) { |
drm_mode_probed_add(connector, newmode); |
modes++; |
} |
} |
} |
|
return modes; |
} |
|
static int drm_cvt_modes(struct drm_connector *connector, |
struct detailed_timing *timing) |
{ |
int i, j, modes = 0; |
int timing_level; |
struct drm_display_mode *newmode; |
struct drm_device *dev = connector->dev; |
struct cvt_timing *cvt; |
const int rates[] = { 60, 85, 75, 60, 50 }; |
|
timing_level = standard_timing_level(edid); |
for (i = 0; i < 4; i++) { |
int width, height; |
cvt = &(timing->data.other_data.data.cvt[i]); |
|
for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { |
struct detailed_timing *timing = &edid->detailed_timings[i]; |
height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 8) + 1) * 2; |
switch (cvt->code[1] & 0xc0) { |
case 0x00: |
width = height * 4 / 3; |
break; |
case 0x40: |
width = height * 16 / 9; |
break; |
case 0x80: |
width = height * 16 / 10; |
break; |
case 0xc0: |
width = height * 15 / 9; |
break; |
} |
|
for (j = 1; j < 5; j++) { |
if (cvt->code[2] & (1 << j)) { |
newmode = drm_cvt_mode(dev, width, height, |
rates[j], j == 0, |
false, false); |
if (newmode) { |
drm_mode_probed_add(connector, newmode); |
modes++; |
} |
} |
} |
} |
|
return modes; |
} |
|
static int add_detailed_modes(struct drm_connector *connector, |
struct detailed_timing *timing, |
struct edid *edid, u32 quirks, int preferred) |
{ |
int i, modes = 0; |
struct detailed_non_pixel *data = &timing->data.other_data; |
int timing_level = standard_timing_level(edid); |
int gtf = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF); |
struct drm_display_mode *newmode; |
struct drm_device *dev = connector->dev; |
|
/* X server check is version 1.1 or higher */ |
if (edid->version == 1 && edid->revision >= 1 && |
!timing->pixel_clock) { |
/* Other timing or info */ |
if (timing->pixel_clock) { |
newmode = drm_mode_detailed(dev, edid, timing, quirks); |
if (!newmode) |
return 0; |
|
if (preferred) |
newmode->type |= DRM_MODE_TYPE_PREFERRED; |
|
drm_mode_probed_add(connector, newmode); |
return 1; |
} |
|
/* other timing types */ |
switch (data->type) { |
case EDID_DETAIL_MONITOR_SERIAL: |
break; |
case EDID_DETAIL_MONITOR_STRING: |
break; |
case EDID_DETAIL_MONITOR_RANGE: |
/* Get monitor range data */ |
if (gtf) |
modes += drm_gtf_modes_for_range(connector, timing); |
break; |
case EDID_DETAIL_MONITOR_NAME: |
break; |
case EDID_DETAIL_MONITOR_CPDATA: |
break; |
case EDID_DETAIL_STD_MODES: |
for (j = 0; j < 6; i++) { |
/* Six modes per detailed section */ |
for (i = 0; i < 6; i++) { |
struct std_timing *std; |
struct drm_display_mode *newmode; |
|
std = &data->data.timings[j]; |
newmode = drm_mode_std(dev, std, |
edid->revision, |
std = &data->data.timings[i]; |
newmode = drm_mode_std(dev, std, edid->revision, |
timing_level); |
if (newmode) { |
drm_mode_probed_add(connector, newmode); |
888,25 → 992,46 |
} |
} |
break; |
case EDID_DETAIL_CVT_3BYTE: |
modes += drm_cvt_modes(connector, timing); |
break; |
default: |
break; |
} |
} else { |
newmode = drm_mode_detailed(dev, edid, timing, quirks); |
if (!newmode) |
|
return modes; |
} |
|
/** |
* add_detailed_info - get detailed mode info from EDID data |
* @connector: attached connector |
* @edid: EDID block to scan |
* @quirks: quirks to apply |
* |
* Some of the detailed timing sections may contain mode information. Grab |
* it and add it to the list. |
*/ |
static int add_detailed_info(struct drm_connector *connector, |
struct edid *edid, u32 quirks) |
{ |
int i, modes = 0; |
|
for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { |
struct detailed_timing *timing = &edid->detailed_timings[i]; |
int preferred = (i == 0) && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); |
|
/* In 1.0, only timings are allowed */ |
if (!timing->pixel_clock && edid->version == 1 && |
edid->revision == 0) |
continue; |
|
/* First detailed mode is preferred */ |
if (i == 0 && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING)) |
newmode->type |= DRM_MODE_TYPE_PREFERRED; |
drm_mode_probed_add(connector, newmode); |
|
modes++; |
modes += add_detailed_modes(connector, timing, edid, quirks, |
preferred); |
} |
} |
|
return modes; |
} |
|
/** |
* add_detailed_mode_eedid - get detailed mode info from addtional timing |
* EDID block |
920,12 → 1045,9 |
static int add_detailed_info_eedid(struct drm_connector *connector, |
struct edid *edid, u32 quirks) |
{ |
struct drm_device *dev = connector->dev; |
int i, j, modes = 0; |
int i, modes = 0; |
char *edid_ext = NULL; |
struct detailed_timing *timing; |
struct detailed_non_pixel *data; |
struct drm_display_mode *newmode; |
int edid_ext_num; |
int start_offset, end_offset; |
int timing_level; |
976,53 → 1098,9 |
for (i = start_offset; i < end_offset; |
i += sizeof(struct detailed_timing)) { |
timing = (struct detailed_timing *)(edid_ext + i); |
data = &timing->data.other_data; |
/* Detailed mode timing */ |
if (timing->pixel_clock) { |
newmode = drm_mode_detailed(dev, edid, timing, quirks); |
if (!newmode) |
continue; |
|
drm_mode_probed_add(connector, newmode); |
|
modes++; |
continue; |
modes += add_detailed_modes(connector, timing, edid, quirks, 0); |
} |
|
/* Other timing or info */ |
switch (data->type) { |
case EDID_DETAIL_MONITOR_SERIAL: |
break; |
case EDID_DETAIL_MONITOR_STRING: |
break; |
case EDID_DETAIL_MONITOR_RANGE: |
/* Get monitor range data */ |
break; |
case EDID_DETAIL_MONITOR_NAME: |
break; |
case EDID_DETAIL_MONITOR_CPDATA: |
break; |
case EDID_DETAIL_STD_MODES: |
/* Five modes per detailed section */ |
for (j = 0; j < 5; i++) { |
struct std_timing *std; |
struct drm_display_mode *newmode; |
|
std = &data->data.timings[j]; |
newmode = drm_mode_std(dev, std, |
edid->revision, |
timing_level); |
if (newmode) { |
drm_mode_probed_add(connector, newmode); |
modes++; |
} |
} |
break; |
default: |
break; |
} |
} |
|
return modes; |
} |
|
1066,20 → 1144,20 |
struct i2c_adapter *adapter, |
char *buf, int len) |
{ |
int ret; |
int i; |
|
ret = drm_do_probe_ddc_edid(adapter, buf, len); |
if (ret != 0) { |
goto end; |
for (i = 0; i < 4; i++) { |
if (drm_do_probe_ddc_edid(adapter, buf, len)) |
return -1; |
if (edid_is_valid((struct edid *)buf)) |
return 0; |
} |
if (!edid_is_valid((struct edid *)buf)) { |
|
/* repeated checksum failures; warn, but carry on */ |
dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", |
drm_get_connector_name(connector)); |
ret = -1; |
return -1; |
} |
end: |
return ret; |
} |
|
/** |
* drm_get_edid - get EDID data, if available |
1296,6 → 1374,8 |
ptr->vdisplay > vdisplay) |
continue; |
} |
if (drm_mode_vrefresh(ptr) > 61) |
continue; |
mode = drm_mode_duplicate(dev, ptr); |
if (mode) { |
drm_mode_probed_add(connector, mode); |