kernel图显系统中DDC的代码实现原理

DDC获取EDID的入口函数是drm_do_get_edid(),其调用DDC通讯接口drm_do_probe_ddc_edid()。

struct edid *drm_get_edid(struct drm_connector *connector,
			  struct i2c_adapter *adapter)
{
...
	edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter);
...
}

drm_do_probe_ddc_edid()作为参数传入drm_do_get_edid()中。

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)
{
...
	/* base block fetch */
	for (i = 0; i < 4; i++) {
		if (get_edid_block(data, edid, 0, EDID_LENGTH))
			goto out;
...
	}
...

DDC的消息传输机制
HDMI的EDID最多包含256Bytes,其余的如VGA、DVI是128Bytes。对于E-EDID来讲,由512Bytes组成,分2个segment。每次i2c传输128bytes,分4次传输完成。因此每个segment由两部分组成,这体现在DDC的word addr上。

下面是读取E-EDID的时序图。

|------- 1st-----|------2st-------|------3st-------|------4st-------|
| word addr wr 0 |word addr 0x80  |word addr wr 0  |word addr 0x80--|
|----128Bytes----|----128bytes----|----128bytes----|----128bytes----|
|------------segment0-------------|------------segment1-------------|
|------- segment addr wr 0 -------|------- segment addr wr 1 -------|

在描述了DDC消息机制后,继续。

DRM架构定义了3个i2c消息头,包括两次i2c write和一次i2c read。
i2c 消息头定义如下。

do {
		struct i2c_msg msgs[] = {
			{
				.addr	= DDC_SEGMENT_ADDR,
				.flags	= 0,
				.len	= 1,
				.buf	= &segment,
			}, {
				.addr	= DDC_ADDR,
				.flags	= 0,
				.len	= 1,
				.buf	= &start,
			}, {
				.addr	= DDC_ADDR,
				.flags	= I2C_M_RD,
				.len	= len,
				.buf	= buf,
			}
		};

kernel代码中也是规定了每次只能读取128Bytes的EDID信息。当获取到EDID之后,对EDID进行解析,再决定是否还要继续读取额外长度的信息。

#define EDID_LENGTH 128
#define DDC_ADDR 0x50
#define DDC_ADDR2 0x52

...
/* 获取EDID时共进行4次i2c传输,每次读取128Bytes data */
for (i = 0; i < 4; i++) {
	if (get_edid_block(data, edid, 0, EDID_LENGTH))
		goto out;
	if (drm_edid_block_valid(edid, 0, false,
				 &connector->edid_corrupt))
		break;
	if (i == 0 && drm_edid_is_zero(edid, EDID_LENGTH)) {
		connector->null_edid_counter++;
		goto carp;
	}
}

例如下面的ZTE HDMI驱动中,根据DRM定义的i2c头信息实现了DDC word_addr和segment_addr的配置。

static int zx_hdmi_i2c_write(struct zx_hdmi *hdmi, struct i2c_msg *msg)
{
...
	if (msg->addr == DDC_SEGMENT_ADDR)
		hdmi_writeb(hdmi, ZX_DDC_SEGM, msg->addr << 1);
	else if (msg->addr == DDC_ADDR)
		hdmi_writeb(hdmi, ZX_DDC_ADDR, msg->addr << 1);
...
}
上一篇:deepin上配置samba服务器


下一篇:Lab2: system calls 引导