Commit 18df89fe authored by Lars-Peter Clausen's avatar Lars-Peter Clausen Committed by Laurent Pinchart

drm: Decouple EDID parsing from I2C adapter

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: default avatarLars-Peter Clausen <lars@metafoo.de>
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: default avatarRob Clark <robdclark@gmail.com>
Reviewed-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent 637e6194
...@@ -1125,9 +1125,9 @@ EXPORT_SYMBOL(drm_edid_is_valid); ...@@ -1125,9 +1125,9 @@ EXPORT_SYMBOL(drm_edid_is_valid);
* Return: 0 on success or -1 on failure. * Return: 0 on success or -1 on failure.
*/ */
static int static int
drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
int block, int len)
{ {
struct i2c_adapter *adapter = data;
unsigned char start = block * EDID_LENGTH; unsigned char start = block * EDID_LENGTH;
unsigned char segment = block >> 1; unsigned char segment = block >> 1;
unsigned char xfers = segment ? 3 : 2; unsigned char xfers = segment ? 3 : 2;
...@@ -1184,8 +1184,26 @@ static bool drm_edid_is_zero(u8 *in_edid, int length) ...@@ -1184,8 +1184,26 @@ static bool drm_edid_is_zero(u8 *in_edid, int length)
return true; 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; int i, j = 0, valid_extensions = 0;
u8 *block, *new; u8 *block, *new;
...@@ -1196,7 +1214,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) ...@@ -1196,7 +1214,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
/* base block fetch */ /* base block fetch */
for (i = 0; i < 4; i++) { 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; goto out;
if (drm_edid_block_valid(block, 0, print_bad_edid)) if (drm_edid_block_valid(block, 0, print_bad_edid))
break; break;
...@@ -1210,7 +1228,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) ...@@ -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 there's no extensions, we're done */
if (block[0x7e] == 0) if (block[0x7e] == 0)
return block; return (struct edid *)block;
new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL); new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL);
if (!new) if (!new)
...@@ -1219,7 +1237,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) ...@@ -1219,7 +1237,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
for (j = 1; j <= block[0x7e]; j++) { for (j = 1; j <= block[0x7e]; j++) {
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
if (drm_do_probe_ddc_edid(adapter, if (get_edid_block(data,
block + (valid_extensions + 1) * EDID_LENGTH, block + (valid_extensions + 1) * EDID_LENGTH,
j, EDID_LENGTH)) j, EDID_LENGTH))
goto out; goto out;
...@@ -1247,7 +1265,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) ...@@ -1247,7 +1265,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
block = new; block = new;
} }
return block; return (struct edid *)block;
carp: carp:
if (print_bad_edid) { if (print_bad_edid) {
...@@ -1260,6 +1278,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) ...@@ -1260,6 +1278,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
kfree(block); kfree(block);
return NULL; return NULL;
} }
EXPORT_SYMBOL_GPL(drm_do_get_edid);
/** /**
* drm_probe_ddc() - probe DDC presence * drm_probe_ddc() - probe DDC presence
...@@ -1289,12 +1308,10 @@ EXPORT_SYMBOL(drm_probe_ddc); ...@@ -1289,12 +1308,10 @@ EXPORT_SYMBOL(drm_probe_ddc);
struct edid *drm_get_edid(struct drm_connector *connector, struct edid *drm_get_edid(struct drm_connector *connector,
struct i2c_adapter *adapter) struct i2c_adapter *adapter)
{ {
struct edid *edid = NULL; if (!drm_probe_ddc(adapter))
return NULL;
if (drm_probe_ddc(adapter))
edid = (struct edid *)drm_do_get_edid(connector, adapter);
return edid; return drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter);
} }
EXPORT_SYMBOL(drm_get_edid); EXPORT_SYMBOL(drm_get_edid);
......
...@@ -381,4 +381,9 @@ static inline int drm_eld_size(const uint8_t *eld) ...@@ -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; 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__ */ #endif /* __DRM_EDID_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment