Commit 9cf00977 authored by Adam Jackson's avatar Adam Jackson Committed by Dave Airlie
Browse files

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: default avatarAdam Jackson <ajax@redhat.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 9632b41f
Loading
Loading
Loading
Loading
+61 −102
Original line number Diff line number Diff line
@@ -838,53 +838,41 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid
	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.
 */
static int add_detailed_info(struct drm_connector *connector,
			     struct edid *edid, u32 quirks)
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;
	int i, j, modes = 0;
	int timing_level;

	timing_level = standard_timing_level(edid);
	if (timing->pixel_clock) {
		newmode = drm_mode_detailed(dev, edid, timing, quirks);
		if (!newmode)
			return 0;

	for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
		struct detailed_timing *timing = &edid->detailed_timings[i];
		struct detailed_non_pixel *data = &timing->data.other_data;
		struct drm_display_mode *newmode;
		if (preferred)
			newmode->type |= DRM_MODE_TYPE_PREFERRED;

		/* X server check is version 1.1 or higher */
		if (edid->version == 1 && edid->revision >= 1 &&
		    !timing->pixel_clock) {
			/* Other timing or info */
		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 */
		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);
@@ -895,22 +883,40 @@ static int add_detailed_info(struct drm_connector *connector,
	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;
}

/**
 * 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;

		modes += add_detailed_modes(connector, timing, edid, quirks,
					    preferred);
	}

	return modes;
}

/**
 * add_detailed_mode_eedid - get detailed mode info from addtional timing
 * 			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,
			     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;
@@ -980,51 +983,7 @@ static int add_detailed_info_eedid(struct drm_connector *connector,
	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;
		}

		/* 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;
		}
		modes += add_detailed_modes(connector, timing, edid, quirks, 0);
	}

	return modes;