[PATCH/RFC 5/5] [HACK] drm/edid: Decouple EDID retrieval from connector

Laurent Pinchart laurent.pinchart at ideasonboard.com
Wed Aug 21 18:50:05 UTC 2019


This patch is a proof of concept showing how EDID retrieval could be
decoupled from drm_connector, in order to avoid passing the connector
pointer to the drm_bridge .get_edid() operation. The user of such
bridges (in most case the future drm_bridge_connector helper, see
"[PATCH v2 00/50] drm/omap: Replace custom display drivers with
drm_bridge and drm_panel") would need to duplicate the logic found in
drm_do_get_edid(), and bridges would use the __drm_do_get_edid()
function to retrieve and return a drm_edid structure.

Not-yet-signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
---
 drivers/gpu/drm/drm_edid.c | 157 ++++++++++++++++++++++---------------
 1 file changed, 95 insertions(+), 62 deletions(-)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 4b28635f1050..37b6a6de9f42 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -5119,34 +5119,6 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
 	return ret == xfers ? 0 : -1;
 }
 
-static void connector_bad_edid(struct drm_connector *connector,
-			       u8 *edid, int num_blocks)
-{
-	int i;
-
-	if (connector->bad_edid_counter++ && !(drm_debug & DRM_UT_KMS))
-		return;
-
-	dev_warn(connector->dev->dev,
-		 "%s: EDID is invalid:\n",
-		 connector->name);
-	for (i = 0; i < num_blocks; i++) {
-		u8 *block = edid + i * EDID_LENGTH;
-		char prefix[20];
-
-		if (drm_edid_is_zero(block, EDID_LENGTH))
-			sprintf(prefix, "\t[%02x] ZERO ", i);
-		else if (!drm_edid_block_valid(block, i, false, NULL))
-			sprintf(prefix, "\t[%02x] BAD  ", i);
-		else
-			sprintf(prefix, "\t[%02x] GOOD ", i);
-
-		print_hex_dump(KERN_WARNING,
-			       prefix, DUMP_PREFIX_NONE, 16, 1,
-			       block, EDID_LENGTH, false);
-	}
-}
-
 /* Get override or firmware EDID */
 static struct edid *drm_get_override_edid(struct drm_connector *connector)
 {
@@ -5201,31 +5173,67 @@ static bool __drm_probe_ddc(int (*get_edid_block)(void *data, u8 *buf,
 	return (get_edid_block(data, &out, 0, 1) == 0);
 }
 
-static struct edid *__drm_do_get_edid(struct drm_connector *connector,
+struct drm_edid {
+	u8 *data;
+	unsigned int num_blocks;
+
+	unsigned int null_counter;
+	unsigned int bad_counter;
+	bool corrupt;
+};
+
+static void connector_bad_edid(struct drm_device *dev, const char *name,
+			       struct drm_edid *edid)
+{
+	int i;
+
+	if (edid->bad_counter++ && !(drm_debug & DRM_UT_KMS))
+		return;
+
+	dev_warn(dev->dev, "%s: EDID is invalid:\n", name);
+
+	for (i = 0; i < edid->num_blocks; i++) {
+		u8 *block = edid->data + i * EDID_LENGTH;
+		char prefix[20];
+
+		if (drm_edid_is_zero(block, EDID_LENGTH))
+			sprintf(prefix, "\t[%02x] ZERO ", i);
+		else if (!drm_edid_block_valid(block, i, false, NULL))
+			sprintf(prefix, "\t[%02x] BAD  ", i);
+		else
+			sprintf(prefix, "\t[%02x] GOOD ", i);
+
+		print_hex_dump(KERN_WARNING,
+			       prefix, DUMP_PREFIX_NONE, 16, 1,
+			       block, EDID_LENGTH, false);
+	}
+}
+
+static int __drm_do_get_edid(struct drm_device *dev, const char *name,
+			     struct drm_edid *edid,
 	int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
 			      size_t len),
 	void *data)
 {
-	int i, j = 0, valid_extensions = 0;
-	u8 *edid, *new;
-	struct edid *override;
+	unsigned int valid_extensions;
+	unsigned int i, j;
+	u8 *new;
+	int ret;
 
-	override = drm_get_override_edid(connector);
-	if (override)
-		return override;
+	edid->data = kmalloc(EDID_LENGTH, GFP_KERNEL);
+	if (!edid->data)
+		return -ENOMEM;
 
-	if ((edid = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL)
-		return NULL;
+	edid->num_blocks = 1;
 
 	/* base block fetch */
 	for (i = 0; i < 4; i++) {
-		if (get_edid_block(data, edid, 0, EDID_LENGTH))
+		if (get_edid_block(data, edid->data, 0, EDID_LENGTH))
 			goto out;
-		if (drm_edid_block_valid(edid, 0, false,
-					 &connector->edid_corrupt))
+		if (drm_edid_block_valid(edid->data, 0, false, &edid->corrupt))
 			break;
-		if (i == 0 && drm_edid_is_zero(edid, EDID_LENGTH)) {
-			connector->null_edid_counter++;
+		if (i == 0 && drm_edid_is_zero(edid->data, EDID_LENGTH)) {
+			edid->null_counter++;
 			goto carp;
 		}
 	}
@@ -5233,17 +5241,19 @@ static struct edid *__drm_do_get_edid(struct drm_connector *connector,
 		goto carp;
 
 	/* if there's no extensions, we're done */
-	valid_extensions = edid[0x7e];
+	valid_extensions = edid->data[0x7e] + 1;
 	if (valid_extensions == 0)
-		return (struct edid *)edid;
+		return 0;
 
-	new = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL);
+	edid->num_blocks += valid_extensions;
+
+	new = krealloc(edid->data, edid->num_blocks * EDID_LENGTH, GFP_KERNEL);
 	if (!new)
 		goto out;
-	edid = new;
+	edid->data = new;
 
-	for (j = 1; j <= edid[0x7e]; j++) {
-		u8 *block = edid + j * EDID_LENGTH;
+	for (j = 1; j < edid->num_blocks; j++) {
+		u8 *block = edid->data + j * EDID_LENGTH;
 
 		for (i = 0; i < 4; i++) {
 			if (get_edid_block(data, block, j, EDID_LENGTH))
@@ -5256,22 +5266,25 @@ static struct edid *__drm_do_get_edid(struct drm_connector *connector,
 			valid_extensions--;
 	}
 
-	if (valid_extensions != edid[0x7e]) {
+	if (valid_extensions != edid->num_blocks - 1) {
 		u8 *base;
 
-		connector_bad_edid(connector, edid, edid[0x7e] + 1);
+		connector_bad_edid(dev, name, edid);
 
-		edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
-		edid[0x7e] = valid_extensions;
+		edid->data[EDID_LENGTH-1] += edid->data[0x7e] - valid_extensions;
+		edid->data[0x7e] = valid_extensions;
+		edid->num_blocks = valid_extensions + 1;
 
 		new = kmalloc_array(valid_extensions + 1, EDID_LENGTH,
 				    GFP_KERNEL);
-		if (!new)
+		if (!new) {
+			ret = -ENOMEM;
 			goto out;
+		}
 
 		base = new;
-		for (i = 0; i <= edid[0x7e]; i++) {
-			u8 *block = edid + i * EDID_LENGTH;
+		for (i = 0; i < edid->num_blocks; i++) {
+			u8 *block = edid->data + i * EDID_LENGTH;
 
 			if (!drm_edid_block_valid(block, i, false, NULL))
 				continue;
@@ -5280,17 +5293,19 @@ static struct edid *__drm_do_get_edid(struct drm_connector *connector,
 			base += EDID_LENGTH;
 		}
 
-		kfree(edid);
-		edid = new;
+		kfree(edid->data);
+		edid->data = new;
 	}
 
-	return (struct edid *)edid;
+	return 0;
 
 carp:
-	connector_bad_edid(connector, edid, 1);
+	connector_bad_edid(dev, name, edid);
+	ret = -EINVAL;
 out:
-	kfree(edid);
-	return NULL;
+	kfree(edid->data);
+	edid->data = NULL;
+	return ret;
 }
 
 /**
@@ -5318,6 +5333,9 @@ struct edid *drm_do_get_edid(struct drm_connector *connector,
 			      size_t len),
 	void *data)
 {
+	struct drm_edid edid;
+	struct edid *override;
+
 	if (connector->force == DRM_FORCE_OFF)
 		return NULL;
 
@@ -5325,7 +5343,22 @@ struct edid *drm_do_get_edid(struct drm_connector *connector,
 	    !__drm_probe_ddc(get_edid_block, data))
 		return NULL;
 
-	return __drm_do_get_edid(connector, get_edid_block, data);
+	override = drm_get_override_edid(connector);
+	if (override)
+		return override;
+
+	memset(&edid, 0, sizeof(edid));
+	edid.bad_counter = connector->bad_edid_counter;
+	edid.null_counter = connector->null_edid_counter;
+
+	__drm_do_get_edid(connector->dev, connector->name, &edid,
+			  get_edid_block, data);
+
+	connector->bad_edid_counter = edid.bad_counter;
+	connector->null_edid_counter = edid.null_counter;
+	connector->edid_corrupt = edid.corrupt;
+
+	return (struct edid *)edid.data;
 }
 EXPORT_SYMBOL_GPL(drm_do_get_edid);
 
-- 
Regards,

Laurent Pinchart



More information about the dri-devel mailing list