[PATCH v4 6/9] drm: Decouple EDID parsing from I2C adapter
Daniel Vetter
daniel at ffwll.ch
Tue Nov 25 23:28:34 PST 2014
On Wed, Nov 26, 2014 at 01:47:27AM +0200, Laurent Pinchart wrote:
> From: Lars-Peter Clausen <lars at metafoo.de>
>
> The drm_get_edid() function performs direct I2C accesses to read EDID
> blocks, assuming that the monitor DDC interface is directly connected to
> the I2C bus. It can't thus be used with HDMI encoders that control the
> DDC bus and expose EDID blocks through a different interface.
>
> Refactor drm_do_get_edid() to take a block read callback function
> instead of an I2C adapter, and export it for direct use by drivers.
>
> As in the general case the DDC bus is accessible by the kernel at the
> I2C level, drivers must make all reasonable efforts to expose it as an
> I2C adapter and use drm_get_edid() instead of abusing this function.
>
> Signed-off-by: Lars-Peter Clausen <lars at metafoo.de>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas at ideasonboard.com>
Reviewed-by: Daniel Vetter <daniel.vetter at ffwll.ch>
> ---
> drivers/gpu/drm/drm_edid.c | 43 ++++++++++++++++++++++++++++++-------------
> include/drm/drm_edid.h | 5 +++++
> 2 files changed, 35 insertions(+), 13 deletions(-)
>
> Daniel, could you please review and hopefully ack this ? If this new version is
> acceptable I'd like to send an updated pull request for R-Car DU HDMI support
> for v3.19, so time is running short.
>
> Changes since v3:
>
> - Add kerneldoc for the new exported drm_do_get_edid function
>
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 3bf999134bcc..1a77a49d2695 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -1125,9 +1125,9 @@ EXPORT_SYMBOL(drm_edid_is_valid);
> * Return: 0 on success or -1 on failure.
> */
> static int
> -drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
> - int block, int len)
> +drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
> {
> + struct i2c_adapter *adapter = data;
> unsigned char start = block * EDID_LENGTH;
> unsigned char segment = block >> 1;
> unsigned char xfers = segment ? 3 : 2;
> @@ -1184,8 +1184,26 @@ static bool drm_edid_is_zero(u8 *in_edid, int length)
> return true;
> }
>
> -static u8 *
> -drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
> +/**
> + * drm_do_get_edid - get EDID data using a custom EDID block read function
> + * @connector: connector we're probing
> + * @get_edid_block: EDID block read function
> + * @data: private data passed to the block read function
> + *
> + * When the I2C adapter connected to the DDC bus is hidden behind a device that
> + * exposes a different interface to read EDID blocks this function can be used
> + * to get EDID data using a custom block read function.
> + *
> + * As in the general case the DDC bus is accessible by the kernel at the I2C
> + * level, drivers must make all reasonable efforts to expose it as an I2C
> + * adapter and use drm_get_edid() instead of abusing this function.
> + *
> + * Return: Pointer to valid EDID or NULL if we couldn't find any.
> + */
> +struct edid *drm_do_get_edid(struct drm_connector *connector,
> + int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
> + size_t len),
> + void *data)
> {
> int i, j = 0, valid_extensions = 0;
> u8 *block, *new;
> @@ -1196,7 +1214,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
>
> /* base block fetch */
> for (i = 0; i < 4; i++) {
> - if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH))
> + if (get_edid_block(data, block, 0, EDID_LENGTH))
> goto out;
> if (drm_edid_block_valid(block, 0, print_bad_edid))
> break;
> @@ -1210,7 +1228,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
>
> /* if there's no extensions, we're done */
> if (block[0x7e] == 0)
> - return block;
> + return (struct edid *)block;
>
> new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL);
> if (!new)
> @@ -1219,7 +1237,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
>
> for (j = 1; j <= block[0x7e]; j++) {
> for (i = 0; i < 4; i++) {
> - if (drm_do_probe_ddc_edid(adapter,
> + if (get_edid_block(data,
> block + (valid_extensions + 1) * EDID_LENGTH,
> j, EDID_LENGTH))
> goto out;
> @@ -1247,7 +1265,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
> block = new;
> }
>
> - return block;
> + return (struct edid *)block;
>
> carp:
> if (print_bad_edid) {
> @@ -1260,6 +1278,7 @@ out:
> kfree(block);
> return NULL;
> }
> +EXPORT_SYMBOL_GPL(drm_do_get_edid);
>
> /**
> * drm_probe_ddc() - probe DDC presence
> @@ -1289,12 +1308,10 @@ EXPORT_SYMBOL(drm_probe_ddc);
> struct edid *drm_get_edid(struct drm_connector *connector,
> struct i2c_adapter *adapter)
> {
> - struct edid *edid = NULL;
> -
> - if (drm_probe_ddc(adapter))
> - edid = (struct edid *)drm_do_get_edid(connector, adapter);
> + if (!drm_probe_ddc(adapter))
> + return NULL;
>
> - return edid;
> + return drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter);
> }
> EXPORT_SYMBOL(drm_get_edid);
>
> diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
> index c2f1bfa22010..d59240ffb1f7 100644
> --- a/include/drm/drm_edid.h
> +++ b/include/drm/drm_edid.h
> @@ -381,4 +381,9 @@ static inline int drm_eld_size(const uint8_t *eld)
> return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4;
> }
>
> +struct edid *drm_do_get_edid(struct drm_connector *connector,
> + int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
> + size_t len),
> + void *data);
> +
> #endif /* __DRM_EDID_H__ */
> --
> 2.0.4
>
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
More information about the dri-devel
mailing list