drm/edid: Unify detailed block parsing between base and extension blocks
Also fix an embarassing bug in standard timing subblock parsing that would result in an infinite loop. Signed-off-by: Adam Jackson <ajax@redhat.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
Родитель
9632b41f00
Коммит
9cf00977da
|
@ -838,8 +838,57 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid
|
||||||
return 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);
|
||||||
|
struct drm_display_mode *newmode;
|
||||||
|
struct drm_device *dev = connector->dev;
|
||||||
|
|
||||||
|
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_RANGE:
|
||||||
|
/* Get monitor range data */
|
||||||
|
break;
|
||||||
|
case EDID_DETAIL_STD_MODES:
|
||||||
|
/* Six modes per detailed section */
|
||||||
|
for (i = 0; i < 6; i++) {
|
||||||
|
struct std_timing *std;
|
||||||
|
struct drm_display_mode *newmode;
|
||||||
|
|
||||||
|
std = &data->data.timings[i];
|
||||||
|
newmode = drm_mode_std(dev, std, edid->revision,
|
||||||
|
timing_level);
|
||||||
|
if (newmode) {
|
||||||
|
drm_mode_probed_add(connector, newmode);
|
||||||
|
modes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add_detailed_modes - get detailed mode info from EDID data
|
* add_detailed_info - get detailed mode info from EDID data
|
||||||
* @connector: attached connector
|
* @connector: attached connector
|
||||||
* @edid: EDID block to scan
|
* @edid: EDID block to scan
|
||||||
* @quirks: quirks to apply
|
* @quirks: quirks to apply
|
||||||
|
@ -850,67 +899,24 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid
|
||||||
static int add_detailed_info(struct drm_connector *connector,
|
static int add_detailed_info(struct drm_connector *connector,
|
||||||
struct edid *edid, u32 quirks)
|
struct edid *edid, u32 quirks)
|
||||||
{
|
{
|
||||||
struct drm_device *dev = connector->dev;
|
int i, modes = 0;
|
||||||
int i, j, modes = 0;
|
|
||||||
int timing_level;
|
|
||||||
|
|
||||||
timing_level = standard_timing_level(edid);
|
|
||||||
|
|
||||||
for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
|
for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
|
||||||
struct detailed_timing *timing = &edid->detailed_timings[i];
|
struct detailed_timing *timing = &edid->detailed_timings[i];
|
||||||
struct detailed_non_pixel *data = &timing->data.other_data;
|
int preferred = (i == 0) && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
|
||||||
struct drm_display_mode *newmode;
|
|
||||||
|
|
||||||
/* X server check is version 1.1 or higher */
|
/* In 1.0, only timings are allowed */
|
||||||
if (edid->version == 1 && edid->revision >= 1 &&
|
if (!timing->pixel_clock && edid->version == 1 &&
|
||||||
!timing->pixel_clock) {
|
edid->revision == 0)
|
||||||
/* Other timing or info */
|
continue;
|
||||||
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:
|
|
||||||
for (j = 0; j < 6; i++) {
|
|
||||||
struct std_timing *std;
|
|
||||||
struct drm_display_mode *newmode;
|
|
||||||
|
|
||||||
std = &data->data.timings[j];
|
modes += add_detailed_modes(connector, timing, edid, quirks,
|
||||||
newmode = drm_mode_std(dev, std,
|
preferred);
|
||||||
edid->revision,
|
|
||||||
timing_level);
|
|
||||||
if (newmode) {
|
|
||||||
drm_mode_probed_add(connector, newmode);
|
|
||||||
modes++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newmode = drm_mode_detailed(dev, edid, timing, quirks);
|
|
||||||
if (!newmode)
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return modes;
|
return modes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add_detailed_mode_eedid - get detailed mode info from addtional timing
|
* add_detailed_mode_eedid - get detailed mode info from addtional timing
|
||||||
* EDID block
|
* EDID block
|
||||||
|
@ -924,12 +930,9 @@ static int add_detailed_info(struct drm_connector *connector,
|
||||||
static int add_detailed_info_eedid(struct drm_connector *connector,
|
static int add_detailed_info_eedid(struct drm_connector *connector,
|
||||||
struct edid *edid, u32 quirks)
|
struct edid *edid, u32 quirks)
|
||||||
{
|
{
|
||||||
struct drm_device *dev = connector->dev;
|
int i, modes = 0;
|
||||||
int i, j, modes = 0;
|
|
||||||
char *edid_ext = NULL;
|
char *edid_ext = NULL;
|
||||||
struct detailed_timing *timing;
|
struct detailed_timing *timing;
|
||||||
struct detailed_non_pixel *data;
|
|
||||||
struct drm_display_mode *newmode;
|
|
||||||
int edid_ext_num;
|
int edid_ext_num;
|
||||||
int start_offset, end_offset;
|
int start_offset, end_offset;
|
||||||
int timing_level;
|
int timing_level;
|
||||||
|
@ -980,51 +983,7 @@ static int add_detailed_info_eedid(struct drm_connector *connector,
|
||||||
for (i = start_offset; i < end_offset;
|
for (i = start_offset; i < end_offset;
|
||||||
i += sizeof(struct detailed_timing)) {
|
i += sizeof(struct detailed_timing)) {
|
||||||
timing = (struct detailed_timing *)(edid_ext + i);
|
timing = (struct detailed_timing *)(edid_ext + i);
|
||||||
data = &timing->data.other_data;
|
modes += add_detailed_modes(connector, timing, edid, quirks, 0);
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
return modes;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче