summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/drm_edid.c121
-rw-r--r--include/drm/drm_crtc.h3
2 files changed, 95 insertions, 29 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index a839a28d8ee6..fab2bdf9c423 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -550,11 +550,20 @@ static int add_detailed_info(struct drm_connector *connector,
}
#define DDC_ADDR 0x50
-
-unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter)
+/**
+ * Get EDID information via I2C.
+ *
+ * \param adapter : i2c device adaptor
+ * \param buf : EDID data buffer to be filled
+ * \param len : EDID data buffer length
+ * \return 0 on success or -1 on failure.
+ *
+ * Try to fetch EDID information by calling i2c driver function.
+ */
+int drm_do_probe_ddc_edid(struct i2c_adapter *adapter,
+ unsigned char *buf, int len)
{
unsigned char start = 0x0;
- unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
struct i2c_msg msgs[] = {
{
.addr = DDC_ADDR,
@@ -564,31 +573,36 @@ unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter)
}, {
.addr = DDC_ADDR,
.flags = I2C_M_RD,
- .len = EDID_LENGTH,
+ .len = len,
.buf = buf,
}
};
- if (!buf) {
- dev_warn(&adapter->dev, "unable to allocate memory for EDID "
- "block.\n");
- return NULL;
- }
-
if (i2c_transfer(adapter, msgs, 2) == 2)
- return buf;
+ return 0;
dev_info(&adapter->dev, "unable to read EDID block.\n");
- kfree(buf);
- return NULL;
+ return -1;
}
EXPORT_SYMBOL(drm_do_probe_ddc_edid);
-static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
+/**
+ * Get EDID information.
+ *
+ * \param adapter : i2c device adaptor.
+ * \param buf : EDID data buffer to be filled
+ * \param len : EDID data buffer length
+ * \return 0 on success or -1 on failure.
+ *
+ * Initialize DDC, then fetch EDID information
+ * by calling drm_do_probe_ddc_edid function.
+ */
+static int drm_ddc_read(struct i2c_adapter *adapter,
+ unsigned char *buf, int len)
{
struct i2c_algo_bit_data *algo_data = adapter->algo_data;
- unsigned char *edid = NULL;
int i, j;
+ int ret = -1;
algo_data->setscl(algo_data->data, 1);
@@ -616,7 +630,7 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
msleep(15);
/* Do the real work */
- edid = drm_do_probe_ddc_edid(adapter);
+ ret = drm_do_probe_ddc_edid(adapter, buf, len);
algo_data->setsda(algo_data->data, 0);
algo_data->setscl(algo_data->data, 0);
msleep(15);
@@ -632,7 +646,7 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
msleep(15);
algo_data->setscl(algo_data->data, 0);
algo_data->setsda(algo_data->data, 0);
- if (edid)
+ if (ret == 0)
break;
}
/* Release the DDC lines when done or the Apple Cinema HD display
@@ -641,9 +655,31 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
algo_data->setsda(algo_data->data, 1);
algo_data->setscl(algo_data->data, 1);
- return edid;
+ return ret;
+}
+
+static int drm_ddc_read_edid(struct drm_connector *connector,
+ struct i2c_adapter *adapter,
+ char *buf, int len)
+{
+ int ret;
+
+ ret = drm_ddc_read(adapter, buf, len);
+ if (ret != 0) {
+ dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n",
+ drm_get_connector_name(connector));
+ goto end;
+ }
+ if (!edid_is_valid((struct edid *)buf)) {
+ dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
+ drm_get_connector_name(connector));
+ ret = -1;
+ }
+end:
+ return ret;
}
+#define MAX_EDID_EXT_NUM 4
/**
* drm_get_edid - get EDID data, if available
* @connector: connector we're probing
@@ -656,24 +692,53 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
struct edid *drm_get_edid(struct drm_connector *connector,
struct i2c_adapter *adapter)
{
+ int ret;
struct edid *edid;
- edid = (struct edid *)drm_ddc_read(adapter);
- if (!edid) {
- dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n",
- drm_get_connector_name(connector));
- return NULL;
+ edid = kmalloc(EDID_LENGTH * (MAX_EDID_EXT_NUM + 1),
+ GFP_KERNEL);
+ if (edid == NULL) {
+ dev_warn(&connector->dev->pdev->dev,
+ "Failed to allocate EDID\n");
+ goto end;
}
- if (!edid_is_valid(edid)) {
- dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
- drm_get_connector_name(connector));
- kfree(edid);
- return NULL;
+
+ /* Read first EDID block */
+ ret = drm_ddc_read_edid(connector, adapter,
+ (unsigned char *)edid, EDID_LENGTH);
+ if (ret != 0)
+ goto clean_up;
+
+ /* There are EDID extensions to be read */
+ if (edid->extensions != 0) {
+ int edid_ext_num = edid->extensions;
+
+ if (edid_ext_num > MAX_EDID_EXT_NUM) {
+ dev_warn(&connector->dev->pdev->dev,
+ "The number of extension(%d) is "
+ "over max (%d), actually read number (%d)\n",
+ edid_ext_num, MAX_EDID_EXT_NUM,
+ MAX_EDID_EXT_NUM);
+ /* Reset EDID extension number to be read */
+ edid_ext_num = MAX_EDID_EXT_NUM;
+ }
+ /* Read EDID including extensions too */
+ ret = drm_ddc_read_edid(connector, adapter, (char *)edid,
+ EDID_LENGTH * (edid_ext_num + 1));
+ if (ret != 0)
+ goto clean_up;
+
}
connector->display_info.raw_edid = (char *)edid;
+ goto end;
+clean_up:
+ kfree(edid);
+ edid = NULL;
+end:
return edid;
+
}
EXPORT_SYMBOL(drm_get_edid);
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 33ae98ced80e..9022b2468182 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -613,7 +613,8 @@ extern void drm_fb_release(struct drm_file *file_priv);
extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
extern struct edid *drm_get_edid(struct drm_connector *connector,
struct i2c_adapter *adapter);
-extern unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter);
+extern int drm_do_probe_ddc_edid(struct i2c_adapter *adapter,
+ unsigned char *buf, int len);
extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode);
extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode);
OpenPOWER on IntegriCloud