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@xxxxxxxxxxxxxxxx> --- 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 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel