]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
drm: read EDID extensions from monitor
authorMa Ling <ling.ma@intel.com>
Fri, 20 Mar 2009 06:09:48 +0000 (14:09 +0800)
committerDave Airlie <airlied@redhat.com>
Sun, 29 Mar 2009 08:31:41 +0000 (18:31 +1000)
Usually drm read basic EDID, that is enough for us, but since igital display
were introduced i.e. HDMI monitor, sometime we need to interact with monitor by
EDID extension information,

EDID extensions include audio/video data block, speaker allocation and vendor specific data blocks.

This patch intends to read EDID extensions from digital monitor for users.

Signed-off-by: Ma Ling <ling.ma@intel.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/drm_edid.c
include/drm/drm_crtc.h

index a839a28d8ee606763ad213d9dc785bc4dde0dd6c..fab2bdf9c4235d5af98b09f2be27f75992c11f0e 100644 (file)
@@ -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);
 
index 33ae98ced80e9b68b8fb34f9e19c1575da2bb36b..9022b2468182124d3fdc3d5e49e72140f44ea65f 100644 (file)
@@ -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);