|
@@ -550,11 +550,20 @@ static int add_detailed_info(struct drm_connector *connector,
|
|
}
|
|
}
|
|
|
|
|
|
#define DDC_ADDR 0x50
|
|
#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 start = 0x0;
|
|
- unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
|
|
|
|
struct i2c_msg msgs[] = {
|
|
struct i2c_msg msgs[] = {
|
|
{
|
|
{
|
|
.addr = DDC_ADDR,
|
|
.addr = DDC_ADDR,
|
|
@@ -564,31 +573,36 @@ unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter)
|
|
}, {
|
|
}, {
|
|
.addr = DDC_ADDR,
|
|
.addr = DDC_ADDR,
|
|
.flags = I2C_M_RD,
|
|
.flags = I2C_M_RD,
|
|
- .len = EDID_LENGTH,
|
|
|
|
|
|
+ .len = len,
|
|
.buf = buf,
|
|
.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)
|
|
if (i2c_transfer(adapter, msgs, 2) == 2)
|
|
- return buf;
|
|
|
|
|
|
+ return 0;
|
|
|
|
|
|
dev_info(&adapter->dev, "unable to read EDID block.\n");
|
|
dev_info(&adapter->dev, "unable to read EDID block.\n");
|
|
- kfree(buf);
|
|
|
|
- return NULL;
|
|
|
|
|
|
+ return -1;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(drm_do_probe_ddc_edid);
|
|
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;
|
|
struct i2c_algo_bit_data *algo_data = adapter->algo_data;
|
|
- unsigned char *edid = NULL;
|
|
|
|
int i, j;
|
|
int i, j;
|
|
|
|
+ int ret = -1;
|
|
|
|
|
|
algo_data->setscl(algo_data->data, 1);
|
|
algo_data->setscl(algo_data->data, 1);
|
|
|
|
|
|
@@ -616,7 +630,7 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
|
|
msleep(15);
|
|
msleep(15);
|
|
|
|
|
|
/* Do the real work */
|
|
/* 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->setsda(algo_data->data, 0);
|
|
algo_data->setscl(algo_data->data, 0);
|
|
algo_data->setscl(algo_data->data, 0);
|
|
msleep(15);
|
|
msleep(15);
|
|
@@ -632,7 +646,7 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
|
|
msleep(15);
|
|
msleep(15);
|
|
algo_data->setscl(algo_data->data, 0);
|
|
algo_data->setscl(algo_data->data, 0);
|
|
algo_data->setsda(algo_data->data, 0);
|
|
algo_data->setsda(algo_data->data, 0);
|
|
- if (edid)
|
|
|
|
|
|
+ if (ret == 0)
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
/* Release the DDC lines when done or the Apple Cinema HD display
|
|
/* 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->setsda(algo_data->data, 1);
|
|
algo_data->setscl(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
|
|
* drm_get_edid - get EDID data, if available
|
|
* @connector: connector we're probing
|
|
* @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 edid *drm_get_edid(struct drm_connector *connector,
|
|
struct i2c_adapter *adapter)
|
|
struct i2c_adapter *adapter)
|
|
{
|
|
{
|
|
|
|
+ int ret;
|
|
struct edid *edid;
|
|
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;
|
|
connector->display_info.raw_edid = (char *)edid;
|
|
|
|
+ goto end;
|
|
|
|
|
|
|
|
+clean_up:
|
|
|
|
+ kfree(edid);
|
|
|
|
+ edid = NULL;
|
|
|
|
+end:
|
|
return edid;
|
|
return edid;
|
|
|
|
+
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(drm_get_edid);
|
|
EXPORT_SYMBOL(drm_get_edid);
|
|
|
|
|