[PATCH 10/17] DRM/KMS/EDID: Cache EDID blobs with extensions.
Takashi Iwai
tiwai at suse.de
Tue Nov 20 00:29:47 PST 2012
At Mon, 19 Nov 2012 15:23:11 -0500,
"Egbert Eich " <"eich at novell.com> wrote:
>
> According the the VESA specs there can be up to 254 EEDID extension blocks.
> Since we may read the EDID (including extensions) in 10 second intervals to
> probe for display hotplugging (at least in cases where no hardware hotplug
> detection exists) and I2C transfer is rather slow we may end up consuming
> a considerable amount on CPU time for just that.
> This patch caches the EDID block if it contains at least one extension.
> To determine if the blocks match we only tranfer the base block, on a match
> we use the cached data.
>
> Signed-off-by: Egbert Eich <eich at suse.de>
> ---
> drivers/gpu/drm/drm_crtc.c | 1 +
> drivers/gpu/drm/drm_edid.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
> include/drm/drm_crtc.h | 1 +
> include/drm/drm_edid.h | 1 +
> 4 files changed, 45 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 3533609..e283355 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -598,6 +598,7 @@ void drm_connector_cleanup(struct drm_connector *connector)
> drm_mode_remove(connector, mode);
>
> mutex_lock(&dev->mode_config.mutex);
> + drm_cache_edid(connector, NULL);
> drm_mode_object_put(dev, &connector->base);
> list_del(&connector->head);
> dev->mode_config.num_connector--;
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 3e7df61..410a54d 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -431,6 +431,41 @@ fixup_edid(u8 **blockp, int valid_extensions)
> return (new ? valid_extensions : -ENOMEM);
> }
>
> +static bool
> +compare_get_edid_from_cache(struct drm_connector *connector, struct edid **edidp)
> +{
> + if (connector->edid_cache &&
> + connector->edid_cache->prod_code[0] == (*edidp)->prod_code[0] &&
> + connector->edid_cache->prod_code[1] == (*edidp)->prod_code[1] &&
> + connector->edid_cache->serial == (*edidp)->serial &&
> + connector->edid_cache->input == (*edidp)->input) {
> + int size = (connector->edid_cache->extensions + 1) * EDID_LENGTH;
> + struct edid *new = kmalloc(size, GFP_KERNEL);
> + if (!new)
> + return false;
> + DRM_DEBUG_KMS("Got EDID for %s from cache.\n",drm_get_connector_name(connector));
> + memcpy(new, connector->edid_cache, size);
> + kfree(*edidp);
You can use kmemdup().
> + *edidp = new;
> + return true;
> + }
> + return false;
> +}
> +
> +void
> +drm_cache_edid(struct drm_connector *connector, struct edid *edid)
> +{
> + struct edid *new = NULL;
> + kfree(connector->edid_cache);
> + if (edid) {
> + int size = (edid->extensions + 1) * EDID_LENGTH;
> + new = kmalloc(size, GFP_KERNEL);
> + if (new)
> + memcpy(new, edid, size);
Ditto.
Takashi
> + }
> + connector->edid_cache = new;
> +}
> +
> static u8 *
> drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
> {
> @@ -462,10 +497,14 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
> if (i == 4)
> goto carp;
>
> - /* if there's no extensions, we're done */
> + /* if there are no extensions, we're done - don't bother caching */
> if (block[EDID_EXTENSION_FLAG_OFFSET] == 0)
> return block;
>
> + /* see if EDID is in the cache - no need to read all extension blocks */
> + if (compare_get_edid_from_cache(connector, (struct edid **)&block))
> + return block;
> +
> new = krealloc(block, (block[EDID_EXTENSION_FLAG_OFFSET] + 1) * EDID_LENGTH, GFP_KERNEL);
> if (!new)
> goto out;
> @@ -505,6 +544,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
>
> no_more:
> fixup_edid(&block, valid_extensions);
> + drm_cache_edid(connector, (struct edid *)block);
>
> return block;
>
> @@ -513,6 +553,7 @@ carp:
> dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n",
> drm_get_connector_name(connector), j);
> }
> + drm_cache_edid(connector, NULL);
> connector->bad_edid_counter++;
>
> out:
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 49dd8c2..6a1054c 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -601,6 +601,7 @@ struct drm_connector {
> struct drm_encoder *encoder; /* currently active encoder */
>
> /* EDID bits */
> + struct edid *edid_cache;
> uint8_t eld[MAX_ELD_BYTES];
> bool dvi_dual;
> int max_tmds_clock; /* in MHz */
> diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
> index 53c619c..c880510 100644
> --- a/include/drm/drm_edid.h
> +++ b/include/drm/drm_edid.h
> @@ -256,5 +256,6 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
> struct edid *drm_load_edid_firmware(struct drm_connector *connector);
> #endif
> int drm_validate_edid_blob(struct drm_connector *connector, u8 **blockp, int len);
> +void drm_cache_edid(struct drm_connector *connector, struct edid *edid);
>
> #endif /* __DRM_EDID_H__ */
> --
> 1.7.7
>
More information about the dri-devel
mailing list