[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