Browse Source

dtrm/edid: Allow comma separated edid binaries. (v3)

Allow comma separated filenames in the edid_firmware parameter.

For example:

edid_firmware=eDP-1:edid/1280x480.bin,DP-2:edid/1920x1080.bin

v2: Use strsep() to simplify parsing of comma seperated string. (Matt)
    Move initial bail before strdup. (Matt)
v3: Changed conditionals after while loop to make more readable (Jani)
    Updated kernel-parameters.txt to reflect changes (Jani)

Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Reviewed-by: Matt Roper <matthew.d.roper@intel.com>
Signed-off-by: Bob Paauwe <bob.j.paauwe@intel.com>
[danvet: Flatten else control flow and appease checkpatch.]
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Bob Paauwe 10 years ago
parent
commit
96206e2922
2 changed files with 42 additions and 14 deletions
  1. 9 6
      Documentation/kernel-parameters.txt
  2. 33 8
      drivers/gpu/drm/drm_edid_load.c

+ 9 - 6
Documentation/kernel-parameters.txt

@@ -927,11 +927,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 			The filter can be disabled or changed to another
 			The filter can be disabled or changed to another
 			driver later using sysfs.
 			driver later using sysfs.
 
 
-	drm_kms_helper.edid_firmware=[<connector>:]<file>
-			Broken monitors, graphic adapters and KVMs may
-			send no or incorrect EDID data sets. This parameter
-			allows to specify an EDID data set in the
-			/lib/firmware directory that is used instead.
+	drm_kms_helper.edid_firmware=[<connector>:]<file>[,[<connector>:]<file>]
+			Broken monitors, graphic adapters, KVMs and EDIDless
+			panels may send no or incorrect EDID data sets.
+			This parameter allows to specify an EDID data sets
+			in the /lib/firmware directory that are used instead.
 			Generic built-in EDID data sets are used, if one of
 			Generic built-in EDID data sets are used, if one of
 			edid/1024x768.bin, edid/1280x1024.bin,
 			edid/1024x768.bin, edid/1280x1024.bin,
 			edid/1680x1050.bin, or edid/1920x1080.bin is given
 			edid/1680x1050.bin, or edid/1920x1080.bin is given
@@ -940,7 +940,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 			available in Documentation/EDID/HOWTO.txt. An EDID
 			available in Documentation/EDID/HOWTO.txt. An EDID
 			data set will only be used for a particular connector,
 			data set will only be used for a particular connector,
 			if its name and a colon are prepended to the EDID
 			if its name and a colon are prepended to the EDID
-			name.
+			name. Each connector may use a unique EDID data
+			set by separating the files with a comma.  An EDID
+			data set with no connector name will be used for
+			any connectors not explicitly specified.
 
 
 	dscc4.setup=	[NET]
 	dscc4.setup=	[NET]
 
 

+ 33 - 8
drivers/gpu/drm/drm_edid_load.c

@@ -264,20 +264,43 @@ out:
 int drm_load_edid_firmware(struct drm_connector *connector)
 int drm_load_edid_firmware(struct drm_connector *connector)
 {
 {
 	const char *connector_name = connector->name;
 	const char *connector_name = connector->name;
-	char *edidname = edid_firmware, *last, *colon;
+	char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL;
 	int ret;
 	int ret;
 	struct edid *edid;
 	struct edid *edid;
 
 
-	if (*edidname == '\0')
+	if (edid_firmware[0] == '\0')
 		return 0;
 		return 0;
 
 
-	colon = strchr(edidname, ':');
-	if (colon != NULL) {
-		if (strncmp(connector_name, edidname, colon - edidname))
-			return 0;
-		edidname = colon + 1;
-		if (*edidname == '\0')
+	/*
+	 * If there are multiple edid files specified and separated
+	 * by commas, search through the list looking for one that
+	 * matches the connector.
+	 *
+	 * If there's one or more that don't't specify a connector, keep
+	 * the last one found one as a fallback.
+	 */
+	fwstr = kstrdup(edid_firmware, GFP_KERNEL);
+	edidstr = fwstr;
+
+	while ((edidname = strsep(&edidstr, ","))) {
+		colon = strchr(edidname, ':');
+		if (colon != NULL) {
+			if (strncmp(connector_name, edidname, colon - edidname))
+				continue;
+			edidname = colon + 1;
+			break;
+		}
+
+		if (*edidname != '\0') /* corner case: multiple ',' */
+			fallback = edidname;
+	}
+
+	if (!edidname) {
+		if (!fallback) {
+			kfree(fwstr);
 			return 0;
 			return 0;
+		}
+		edidname = fallback;
 	}
 	}
 
 
 	last = edidname + strlen(edidname) - 1;
 	last = edidname + strlen(edidname) - 1;
@@ -285,6 +308,8 @@ int drm_load_edid_firmware(struct drm_connector *connector)
 		*last = '\0';
 		*last = '\0';
 
 
 	edid = edid_load(connector, edidname, connector_name);
 	edid = edid_load(connector, edidname, connector_name);
+	kfree(fwstr);
+
 	if (IS_ERR_OR_NULL(edid))
 	if (IS_ERR_OR_NULL(edid))
 		return 0;
 		return 0;