Skip to content

Commit

Permalink
drm: read EDID extensions from monitor
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ma Ling authored and Dave Airlie committed Mar 29, 2009
1 parent 955a23e commit 167f3a0
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 29 deletions.
121 changes: 93 additions & 28 deletions drivers/gpu/drm/drm_edid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion include/drm/drm_crtc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 167f3a0

Please sign in to comment.