diff options
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 43 | ||||
-rw-r--r-- | include/drm/drm_edid.h | 5 |
2 files changed, 35 insertions, 13 deletions
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__ */ |