[Patch 1/3] edid override: infrastructure

Thorsten Schoel tschoel at web.de
Tue Apr 24 01:49:18 PDT 2012


From: Thorsten Schoel <tschoel at web.de>

Provides infrastructure for overriding edid information of individual
monitors.

Signed-off-by: Thorsten Schoel <tschoel at web.de>
---
diff -Nurp vanilla/drivers/gpu/drm/drm_edid.c infra/drivers/gpu/drm/drm_edid.c
--- vanilla/drivers/gpu/drm/drm_edid.c	2012-01-25 22:29:25.000000000 +0100
+++ infra/drivers/gpu/drm/drm_edid.c	2012-01-25 22:12:59.000000000 +0100
@@ -30,6 +30,8 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/list.h>
 #include <linux/export.h>
 #include "drmP.h"
 #include "drm_edid.h"
@@ -226,6 +228,158 @@ bool drm_edid_is_valid(struct edid *edid
 }
 EXPORT_SYMBOL(drm_edid_is_valid);
 
+struct drm_edid_override {
+	struct list_head list;
+	
+	char *connector;
+	struct edid *edid;
+	unsigned num_blocks;
+};
+LIST_HEAD(drm_edid_override_list);
+
+/*
+ * Free the memory associated with an EDID override.
+ */
+static void
+drm_edid_override_delete(struct drm_edid_override *entry)
+{
+	if (entry->edid)
+		kfree(entry->edid);
+	kfree(entry->connector);
+	kfree(entry);
+}
+
+/**
+ * Remove an existing override.
+ *
+ * \param connector : name of a drm_connector
+ */
+void
+drm_edid_override_remove(const char *connector)
+{
+	struct drm_edid_override *entry = NULL;
+	
+	list_for_each_entry (entry, &drm_edid_override_list, list) {
+		if (strcmp(entry->connector, connector))
+			continue;
+		list_del(&entry->list);
+		drm_edid_override_delete(entry);
+		break;
+	}
+}
+EXPORT_SYMBOL(drm_edid_override_remove);
+
+static int
+drm_edid_override_do_set(char *connector, struct edid *edid, unsigned num_blocks)
+{
+	struct drm_edid_override *entry = NULL;
+	int found = 0;
+	
+	/* replace previous entry if present */
+	list_for_each_entry (entry, &drm_edid_override_list, list) {
+		if (strcmp(connector, entry->connector))
+			continue;
+		found = 1;
+		break;
+	}
+
+	if (!found) {
+		entry = kmalloc(sizeof(struct drm_edid_override), GFP_KERNEL);
+		if (!entry) {
+			kfree(connector);
+			kfree(edid);
+			return -ENOMEM;
+		}
+		INIT_LIST_HEAD(&entry->list);
+	} else {
+		kfree(entry->connector);
+		if (entry->edid)
+			kfree(entry->edid);
+	}
+	
+	entry->connector = connector;
+	entry->edid = edid;
+	entry->num_blocks = num_blocks;
+	
+	if (!found)
+		list_add_tail(&entry->list, &drm_edid_override_list);
+	DRM_INFO("Set new EDID override for connector %s", connector);
+	
+	return 0;
+}
+
+/**
+ * Add a new override for connector if none has been set yet or replace the
+ * current one.
+ *
+ * \param connector : name of a drm_connector as retrieved through drm_get_connector_name
+ * \param edid      : binary EDID data
+ */
+int
+drm_edid_override_set(const char *connector, const struct edid *edid)
+{
+	char *connector_dup = NULL;
+	struct edid *edid_dup = NULL;
+	int num_blocks = 0;
+	unsigned edid_size = 0;
+	
+	connector_dup = kstrdup(connector, GFP_KERNEL);
+	if (!connector_dup)
+		return -ENOMEM;
+	
+	num_blocks = edid->extensions + 1;
+	edid_size = num_blocks * EDID_LENGTH;
+	edid_dup = kmalloc(edid_size, GFP_KERNEL);
+	if (!edid_dup) {
+		kfree(connector_dup);
+		return -ENOMEM;
+	}
+	memcpy(edid_dup, edid, edid_size);
+	
+	return drm_edid_override_do_set(connector_dup, edid_dup, num_blocks);
+}
+EXPORT_SYMBOL(drm_edid_override_set);
+
+/**
+ * Get EDID information from overrides list if available.
+ *
+ * \param connector : DRM connector to get EDID for
+ * \return a newly allocated edid structure filled with the EDID data from the
+ * 	   override or NULL if no override for @connector could be found.
+ */
+static struct edid *
+drm_edid_override_get (struct drm_connector *connector)
+{
+	struct list_head *pos = NULL;
+	struct drm_edid_override *entry = NULL;
+	char *connector_name = NULL;
+	struct edid *result = NULL;
+
+	connector_name = drm_get_connector_name(connector);
+    
+	list_for_each(pos, &drm_edid_override_list) {
+		entry = (struct drm_edid_override *)
+			list_entry(pos, struct drm_edid_override, list);
+		if(!strcmp(entry->connector, connector_name))
+			break;
+		else
+			entry = NULL;
+	}
+	
+	if (!entry)
+		return NULL;
+	
+	if (!entry->edid) {
+		DRM_ERROR("EDID override without EDID data for connector %s", connector_name);
+		return NULL;
+	}
+	
+	if ((result = kmalloc(entry->num_blocks * EDID_LENGTH, GFP_KERNEL)))
+		memcpy(result, entry->edid, entry->num_blocks * EDID_LENGTH);
+	
+	return result;
+}
+
 #define DDC_SEGMENT_ADDR 0x30
 /**
  * Get EDID information via I2C.
@@ -375,7 +529,8 @@ drm_probe_ddc(struct i2c_adapter *adapte
  * @adapter: i2c adapter to use for DDC
  *
  * Poke the given i2c channel to grab EDID data if possible.  If found,
- * attach it to the connector.
+ * attach it to the connector. If there is an override for connector, use that
+ * instead.
  *
  * Return edid data or NULL if we couldn't find any.
  */
@@ -383,14 +538,20 @@ struct edid *drm_get_edid(struct drm_con
 			  struct i2c_adapter *adapter)
 {
 	struct edid *edid = NULL;
-
-	if (drm_probe_ddc(adapter))
+	char *connector_name = NULL;
+	
+	connector_name = drm_get_connector_name(connector);
+	
+	if ((edid = drm_edid_override_get(connector))) {
+		DRM_DEBUG("Using EDID override for %s", connector_name);
+	} else if (drm_probe_ddc(adapter)) {
+		DRM_DEBUG("Getting EDID by probing i2c-bus for %s", connector_name);
 		edid = (struct edid *)drm_do_get_edid(connector, adapter);
+	}
 
 	connector->display_info.raw_edid = (char *)edid;
 
 	return edid;
-
 }
 EXPORT_SYMBOL(drm_get_edid);
 
diff -Nurp vanilla/include/drm/drm_crtc.h infra/include/drm/drm_crtc.h
--- vanilla/include/drm/drm_crtc.h	2012-01-12 20:42:45.000000000 +0100
+++ infra/include/drm/drm_crtc.h	2012-01-25 22:09:30.000000000 +0100
@@ -854,6 +854,8 @@ extern void drm_fb_release(struct drm_fi
 extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
 extern struct edid *drm_get_edid(struct drm_connector *connector,
 				 struct i2c_adapter *adapter);
+extern int drm_edid_override_set(const char *connector, const struct edid *edid);
+extern void drm_edid_override_remove(const char *connector);
 extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
 extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode);
 extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode);
---




More information about the dri-devel mailing list