mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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:
		
							parent
							
								
									9632b41f00
								
							
						
					
					
						commit
						9cf00977da
					
				
					 1 changed files with 61 additions and 102 deletions
				
			
		| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue