[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