Hi3531D调试手记(六):IT6801实现HDMI转码BT1120输入VI

  Hi3531D 的 MPP 系统中 VI 模块接收视频流就是基于 BT656/BT1120 协议的,开发板上做出的 HDMI 输入则是利用 IT6801 将 HDMI 硬件转码为 BT1120 实现的。虽说自制的板子上同时留了两种预案的接口,BT1120 接口也能把图像数据送进 VI,但是因为其他的一些原因,还是得把 HDMI 输入也实装 Orz…


零、私货(吐槽)

  • 编程手册上展示的芯片工作流程倒是很简单,但是调试起来莫名地痛苦。
  • 自己做的板子上跑不了 IT6801 的 DEMO,能否正常输出暂且不论,单单连跑完 HDMI 寄存器初始化都做不到,嗯,无法理解。
  • 网上关于 IT6801 驱动开发的讨论简直少得离谱,又有种以前捣鼓 UWB 的感觉,不过这次的更离谱,官网都不给资料下载。合着这玩意儿的驱动算商业机密?
  • IT6801 Programming Guide 是真滴难找,大部分资料包里塞的都是 IT6802 的编程手册,据说用起来效果是一样的,在入手 IT6801 的编程手册之前咱也确实是在参考 IT6802 的手册来配置 IT6801。但是!可能的话还是建议用 IT6801 Programming Guide 来进行开发。

一、概念

1.1 HDMI 传输原理

  HDM(High-Definition Multimedia Interface,高清晰多媒体接口)是一种采用 TMDS (Time Minimized Differential Signal,最小化传输差分信号)传输技术的数字音频视频接口。TMDS 是一种微分信号机制,采用差分传动方式,利用 2 个引脚间电压差来传送信号的技术,其传输数据的数值(“0” 或者 “1”)由两脚间电压正负极性和大小决定。
  每一个标准的 HDMI 连接,都包含了 3 个用于传输数据的 TMDS 传输通道,还有 1 个独立的 TMDS 时钟通道,以保证传输时所需的统一时序。在一个时钟周期内,每个 TMDS 通道都能
传送 10bit 的数据流。而这 10bit 数据,可以由若干种不同的编码格式构成。
在这里插入图片描述

  • HDMI 把视频信号分为 R、G、B、H、V 五种信号用 TMDS 技术编码;
  • TMDS 的三个通道分别传输 R、G、B 三原色, H、V 编码在 B 信号通道里面传输(嵌入式传输),R、G、B 的多余位置可以用来传输音频信号;
  • DDC(Display Data Channel,显示数据通道)用来传输接收端支持的视频配置信息和数据格式信息,输出端会读取这些 E-EDID,并以此来决定输出的视频格式;
  • CEC 即消费电子控制通道,通过该通道可以控制视听设备的工作。

1.2 HPD 与 EDID

  之前已经有人做过 IT6801 的调试并记录了心得 —— HDMI接收芯片 IT6801fn 输入调试,这算是咱在网络上找到的为数不多的有用信息之一了,这里对其做一些细化补充。

  • 首先介绍 EDID。EDID 的全称是 Extended Display Identification Data(扩展显示标识数据),分为主块和扩展块两个部分,长度均为 128 字节。主块包含必要的信息因此必须存在,而扩展块的内容主要和音频属性相关。DVI 和 VGA 没有音频,HDMI 自带音频,所以 VGA、DVI 的 EDID 由主块 128 字节组成,HDMI 的 EDID 则会在主块的基础上再增加扩展块,也就是会有 256 字节的 EDID。HDMI EDID 扩展块数据规范按照 CEA-861x 标准定义。EDID 中包含了有关显示器及其性能的参数,包括供应商信息、最大图像大小、颜色设置、厂商预设置、频率范围的限制以及显示器名和序列号的字符串等等。形象地说,EDID 就是显示器的身份证、户口本、技能证书等证件的集合,目的就是告诉别人我是谁,我从哪来,我能干什么。
  • HPD 信号是标准 HDMI 接口使用的重要信号之一,用于设备的热插拔检测。这个信号是主机系统用来判断是否需要对 HDMI/DVI 接口是否发送 TMDS 信号的依据。具体的判断方法为:当主机系统检测到 HDMI 接口上的 HPD 信号从低电平被拉高到高电平时,就认为有显示器连接到 HDMI 口,此时就会作出响应,请求读取显示器端的 EDID 信息,以获取显示器支持的显示时序。如果发现自己能够输出使显示器正常显示的视频信号,主机系统就会开始 TMDS 信号传输,将视频流送出 HDMI 接口。

  因此,要想 IT6801 能够正常被 PC 识别,就需要在完成初始化工作(包括对 EDID 的设置)之后主动拉高 HPD才行。另外, IT6801 是有内部 EDID RAM,有初始 EDID 的,只要使用自带的 EDID RAM(具体使用方法后面介绍),就可以不另外设置 EDID 以及外接 EDID。

1.3 IT6801 编程相关引脚

  数据手册里对所有的引脚都有进行描述,然而这里重点关注的只有 Programming Pins(硬件设计部分不归咱管)。
在这里插入图片描述  虽说硬件不归咱管,但是 CDSENSE 这个引脚注意一下,虽然描述是用来检测 MHL 的连接,但是如果确定使用 HDMI 而不使用 MHL 请务必接地,务必接地,务必接地!不要问为什么 QAQ

PS:即便确定了只使用 HDMI 的 Video 部分而无需 Audio 部分,也请务必保证 Audio 相关的供电与接地引脚连接到位
在这里插入图片描述

1.4 Linux I2C 驱动框架

  在裸机开发中编写某个使用 I2C 来通信的设备驱动时,一般会有两个部分的驱动:

  • I2C 主机驱动,这是处理器的 I2C 接口驱动,一旦编写完成就不需要再做修改,其他的 I2C 设备直接调用主机驱动提供的 API 函数完成读写操作即可;
  • I2C 设备驱动。也就是直接适配对应设备的驱动,一个萝卜一个坑,不能混用。

  Linux 内核根据驱动分离与分层的思想,也将 I2C 驱动分为了类似的两部分:

  • I2C 总线驱动,就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动;
  • I2C 设备驱动,与裸机开发的 I2C 设备驱动类似,就是针对具体的 I2C 设备而编写的驱动。

  Linux 内核代码文件 i2c-dev.c(位置在源码目录 drivers/i2c/i2c-dev.c) 中实现了 I2C 适配器设备文件的功能,针对每个适配器生成一个主设备号为 89 的设备节点(定义在源码目录 include/linux/i2c-dev.h 中,次设备号为 0-255),i2c-dev.c 提供了通用的 read()、write() 和 ioctl() 等文件操作接口,因此在用户空间的应用层就可以借用这些接口访问挂接在适配器上的 I2C 设备的存储空间或寄存器,并控制 I2C 设备的工作方式。
在这里插入图片描述
  I2C 适配器的设备节点是 /dev/i2c-x,其中 x 是数字。由于适配器编号是动态分配的(和注册次序有关),所以想了解哪一个适配器对应什么编号,可以查看 /sys/class/i2c-dev/ 目录下的文件内容。
在这里插入图片描述

二、IT6801驱动实现

2.1 I2C 适配器驱动配置

  虽说可以在 dts 文件下检查默认的引脚复用配置,但为了保险起见,还是根据引脚复用 Excel 表,在 pinmux.sh(引脚复用的寄存器统一配置脚本)中增加了相关复用信息:

#pinmux.sh 增加内容
#i2c0
himm 0x120f01cc 1
himm 0x120f01d0 1

在这里插入图片描述

2.2 HDMI 寄存器初始化配置

  首先是打开 I2C 适配器并完成基础设置:

/*
 * 打开 I2C 适配器
 * iic_name : i2c 适配器设备节点 /dev/i2c-x
 * flag : 操作权限 O_RDWR
 */
static int open_iic(const char *iic_name, int flag)
{
	int fd;

	fd = open(iic_name, flag);

	return fd;
}

/*
 * 设置 I2C 适配器
 * reg_width : 寄存器和数据的宽度   0:8位  1:16位
 * iic_addr : iic器件的地址
 */
static void set_iic_mode(int regwidth, int iic_addr)
{
	int ret;

	ret = ioctl(fd_i2c, I2C_SLAVE_FORCE, iic_addr);		// 强制设置从机地址
	if (ret < 0) 
		printf("set iic_addr failed!\n");

	ret = ioctl(fd_i2c, I2C_16BIT_REG, regwidth);
	if (ret < 0) 
		printf("set reg_width error!\n");

	ret = ioctl(fd_i2c, I2C_16BIT_DATA, regwidth);
	if (ret < 0) 
		printf("set data_width error!\n");
}
  • 根据 IT6801 数据手册中的编程相关引脚描述,IT6801 的器件地址取决于 PCADR 引脚是上拉(0x92)还是下拉(0x90),这里是下拉,因此 iic_addr 的值为 0x90:
    在这里插入图片描述
  • 寄存器位宽和数据位宽经由手册可以判断出应为 8 位,因此 regwidth 的值为 0:
    在这里插入图片描述
  • I2C_SLAVE_FORCE、I2C_16BIT_REG、I2C_16BIT_DATA 均为定义在内核源码目录的 include/uapi/linux/i2c-dev.h 中的 IOCTL 宏:
    在这里插入图片描述
      接下来就是根据编程手册里的推荐顺序来初始化寄存器了(总表长度占了一整页,这里只截取了一部分):
    在这里插入图片描述
unsigned char IT6801_HDMI_INIT_TABLE[][3] = 
{
	{0x0f, 0x03, 0x00},	// Change Bank 0
	{0x10, 0xff, 0x08},		// default:0x00,[3]1:Register reset
	{0x10, 0xff, 0x17},		// default:0x00,[4]1:Auto Video Reset [2]1:Interrupt Reset [1]1:Audio Reset [0]1:Video Reset
	{0x11, 0xff, 0x1f},		// default:0x00,Port0 [4]1:EQ Reset [3]1:CLKD5 Reset [2]1:CDR Reset [1]1:HDCP Reset [0]1:All logic Reset
	{0x18, 0xff, 0x1f},		// default:0x00,Port1 相关
	{0x12, 0xff, 0xf8},		// default:0x00,MHL 相关
	{0x10, 0xff, 0x10},		// default:0x00,[4]1:Auto Video Reset
	{0x11, 0xff, 0xa0},		// default:0x00,MHL 相关
	{0x18, 0xff, 0xa0},		// default:0x00,Port1 相关
	{0x12, 0xff, 0x00},		// default:0x00,MHL 相关
	{0x0f, 0x03, 0x01},	// Change Bank 1
	{0xc0, 0x80, 0x00},		// default:0x80,[7]0:手册中无描述信息,意义不明
	{0x0f, 0x03, 0x00},	// Change Bank 0
	{0x17, 0xc0, 0x80},		// default:0xC0,Port0 [7]1:Inverse Port 0 input HCLK
	{0x1e, 0xc0, 0x00},		// default:0xC0,Port1 相关
	{0x0e, 0xff, 0xff},		// default:0xFE,[0]1:Enable RCLK for CEC
	{0x86, 0xff, 0xc9},		/* SW programmable I2C Slave Address of CEC block:0xC8 */
	{0x16, 0x08, 0x08},		// default:0x80,Port0 [3]1: Enable CLKD5 auto power down
	{0x1d, 0x08, 0x08},		// default:0x00,Port1 相关
	{0x2b, 0x07, 0x07},		// default:0x00,Port0 FixTek3D 相关(3D没有基础,看不明白手册里的描述)
	{0x31, 0xff, 0x2c},		// default:0x39
	{0x34, 0xff, 0xe1},		/* SW programmable I2C Slave Address of MHL block:0xE0 */
	{0x35, 0x0c, 0x01},		// default:0x03
	{0x54, 0x0c, 0x09},		// default:0x10,[1:0]01:RCLK Frequency select(掩码是 0x0c 属实没看懂 0x09 也没看懂,手册上 [3:2] 明明是 Reserved)
	{0x6a, 0xff, 0x81},		// default:0x83,Decide which kind of packet on Gene
	{0x74, 0xff, 0xa0},		// default:0x20,[7]1:Enable i2s and SPDIFoutput [5]1:Disable false DE output
	{0x50, 0x1f, 0x12},		// default 0xbf,[4]1:Invert output DCLK and DCLK DELAY 2 Step
	{0x65, 0x0c, 0x58},		// default:0x00,[6]1:embeded sync [5:4]01:YUV422 [3:2]10:12bits
	{0x7a, 0x80, 0x80},		// default:0xD0,[7]1:enable audio B Frame Swap Interupt
	{0x85, 0x02, 0x02},		// default:0x0C,[1]1: gating avmute in video detect module
	{0xc0, 0x03, 0x00},		// default:0x07
	{0x87, 0xff, 0xa9},		/* SW programmable I2C address of EDID RAM:0xA8 */
	{0x71, 0x08, 0x00},		// default:0x08,[3]0:must clear to 0
	{0x37, 0xff, 0x88},		// default:0x80,Port0 [7:0]0x88:must set to 0xA6(但是手册里给的初始化表设置的却是0x88)
	{0x4d, 0xff, 0x88},		// default:0x80,Port1 相关
	{0x67, 0x80, 0x00},		// default:0x80,[7]0:disable HW CSCSel
	{0x7a, 0x70, 0x70},		// default:0xD0
	{0x7e, 0x40, 0x00},		// default:0x00
	{0x52, 0x20, 0x20},		// default:0x20,[5]1:for disable Auto video MUTE
	{0x53, 0xc0, 0x32},		// default:0x00,QE16-QE23、 QE28-QE35 有效
	{0x58, 0xff, 0xab},		// Video output driving strength
	{0x59, 0xff, 0xaa},		// Audio output driving strength
	{0x0f, 0x03, 0x01},	// Change bank 1
	{0xbc, 0xff, 0x06},		// 仅出现在初始化表里的寄存器,没有在手册的其他位置出现,意义不明
	{0xb5, 0x03, 0x03},		// 同上
	{0xb6, 0x07, 0x00},		// 同上
	{0xb1, 0xff, 0x20},		// default:0x80,[5]1:software overwrite IPLL
	{0xb2, 0xff, 0x01},		// default:0x04,[1]1:increase filter resistance
	{0x0f, 0x03, 0x00},	// Change bank 0
	{0x25, 0xff, 0x1f},		// Default EQ Value
	{0x3d, 0xff, 0x1f},		// Default EQ Value
	{0x27, 0xff, 0x1f},		// Default EQ Value
	{0x28, 0xff, 0x1f},		// Default EQ Value
	{0x29, 0xff, 0x1f},		// Default EQ Value
	{0x3f, 0xff, 0x1f},		// Default EQ Value
	{0x40, 0xff, 0x1f},		// Default EQ Value
	{0x41, 0xff, 0x1f},		// Default EQ Value
	{0x22, 0xff, 0x00},		// default:0x00
	{0x26, 0xff, 0x00},		// default:0x00
	{0x3a, 0xff, 0x00},		// default:0x00,Port1 相关
	{0x3e, 0xff, 0x00},		// default:0x00,Port1 相关
	{0x20, 0x7f, 0x3f},		// default:0x00,[5:4]11:R_CS 初始化 [3:2]11:G_CS 初始化 [1:0]11:B_CS 初始化
	{0x38, 0x7f, 0x3f},		// default:0x00,Port1 相关
	{0xff, 0xff, 0xff}
};

/*
 * HDMI 读寄存器
 * RegAddr : 寄存器地址
 * 返回 : 寄存器存储的值
 */
static unsigned char hdmirxrd(unsigned char RegAddr)
{
	unsigned char recv_buf[4] = {0};

	recv_buf[0] = RegAddr;
	printf("read_reg_addr is %x, ", recv_buf[0]);
	read(fd_i2c, recv_buf, 1);
	printf("read_reg_value is %x\n", recv_buf[0]);

	return recv_buf[0];
}

/*
 * HDMI 设置寄存器
 * RegAddr : 寄存器地址
 * mask : 位清除掩码
 * ucdata : 位更新值
 */
static void hdmirxset(unsigned char RegAddr, unsigned char mask, unsigned char ucdata)
{
	unsigned char send_buf[4] = {0};

	send_buf[0] = RegAddr;
	send_buf[1] = hdmirxrd(RegAddr);
					// 		保留不用清除的位			  更新清除的位
	send_buf[1] = (send_buf[1] & ((~mask) & 0xFF)) + (mask & ucdata);
	printf("send_reg_addr is %x, send_reg_value is %x\n", send_buf[0], send_buf[1]);

	write(fd_i2c, send_buf, 2);
}

/*
 * HDMI 写寄存器
 * RegAddr : 寄存器地址
 * ucdata : 要写入的值
 */
static void hdmirxwr(unsigned char RegAddr, unsigned char ucdata)
{
	unsigned char send_buf[4] = {0};

	send_buf[0] = RegAddr;
	send_buf[1] = ucdata;
	printf("send_reg_addr is %x, send_reg_value is %x\n", send_buf[0], send_buf[1]);

	write(fd_i2c, send_buf, 2);
}

/*
 * HDMI 寄存器初始化
 * (*init_table)[3] : 用来初始化 HDMI 寄存器的配置列表
 */
static void hdimrx_write_init(unsigned char (*init_table)[3])
{
	int count = 0;

	while(init_table[count][0] != 0xff)
	{
		hdmirxset(init_table[count][0], init_table[count][1], init_table[count][2]);
		count ++;
	}
	printf("count is %d\n", count);
}

void it6801_init()
{
	fd_i2c = open_iic("/dev/i2c-0", O_RDWR);
	if (fd_i2c < 0)
		printf("Can't Open /dev/i2c-0!\n");
	else
	{
		set_iic_mode(0, HDMI_I2C_ADDR);

		usleep(100);										// 上电延时 100us
		
		printf("---------------------------------------------------------------------\n");
		hdimrx_write_init(IT6801_HDMI_INIT_TABLE);			// HDMI 寄存器初始化
		printf("---------------------------------------------------------------------\n");
	}
}

  就结果来看,是能正常配置 IT6801 的寄存器的,而此时还没有做其他的配置,PC 并未识别出 IT6801 芯片:
在这里插入图片描述
在这里插入图片描述

2.3 EDID 配置

2.3.1 启用内部 EDID

在这里插入图片描述
  根据编程手册的说法,如果要启用内部 EDID,就需要断开与外部 EDID ROM DDC 总线的连接,同时在 reg87 使能 EDID Block 的访问权限并设置好 EDID 的 I2C 从机地址。在初始化代码中增加如下部分:

	......
	else
	{
		set_iic_mode(0, HDMI_I2C_ADDR);

		HPDCtrl(0);											// 清除 HPD 信号V
		usleep(100);										// 上电延时 100us

		printf("---------------------------------------------------------------------\n");
		hdimrx_write_init(IT6801_HDMI_INIT_TABLE);			// HDMI 寄存器初始化
		printf("---------------------------------------------------------------------\n");
		
		hdmirxwr(0xc0, 0x44);								// 选用内部 EDID

		// printf("*********************************************************************\n");
		// EDIDRAMInitial(Edid_Block);						// EDID RAM 初始化
		// printf("*********************************************************************\n");

		hdmirxset(0x51, 0x01, 0x00); 						// 设置 port 0 为 main port

		HPDCtrl(1);											// 设置 HPD 信号V
	}

  有了关于 EDID 和 HPD 的设置,现在已经能被 PC 识别到:
在这里插入图片描述

2.3.2 更新 EDID(自定义 EDID)

在这里插入图片描述
在这里插入图片描述

  IT6801 内部自带的 EDID 已经直接将 1080p 设置为了推荐分辨率,如无特殊要求可以直接使用(个人的需求是 4k,改肯定是要改的,但是本文就以 1080p 接着往下调了)。如果需要修改,就需要根据之前设置好的 EDID RAM 的 I2C 从机地址修改 I2C 接口设置,以查看和修改 EDID RAM 内的数据。注意,RAM 已经足够说明这里的修改只是本次运行的设置修改,掉电就会失效。EDID RAM 的配置列表可以直接从 DEMO 里扒出来,在熟悉 EDID 结构的基础上可以自行修改配置,没必要把 DEMO 里一堆看似智能实际冗余的自动计算部分也搬出来。

unsigned char Edid_Block[256] = {
// IT6802 with 640x480p , 720x480p , 1280x720p , 1920x1080p
// EDID 主块(Block 0): 0x00 ~ 0x7F
/*----------------------------------------------- Header -----------------------------------------------*/
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,   				// 0x00 ~ 0x07:EDID开始标志
/*----------------------------------- Vendor / Product Identification ----------------------------------*/ 
0x26, 0x85, 								      				// 0x08 ~ 0x09:制造商名称
0x02, 0x68, 									  				// 0x0A ~ 0x0B:产品代码
0x01, 0x68, 0x00, 0x00,   						  				// 0x0C ~ 0x0F:产品序列号
0x00, 0x17, 									  				// 0x10 , 0x11:制造周(0无效),制造年份(1990年为0)
/*---------------------------------- EDID Structure Version / Revision ---------------------------------*/
0x01, 0x03,										  				// 0x12 , 0x13:版本号,修改号(v1.3)
/*---------------------------------- Basic Display Parameters / Features -------------------------------*/
0x80, 											  				// 0x14:视频信号定义(bit7=1,数字信号)
0x73, 0x41,										  				// 0x15 , 0x16:最大水平图像尺寸(cm),最大垂直图像尺寸(cm)
0x78,   						  				  				// 0x17:显示传输特性(Gamma * 100 - 100)范围[1,3.55]
0x2A, 											  				// 0x18:电源管理标准 DPMS(bit5=1,支持Off Mode功能;bit[4:3]=01,RGB颜色显示;bit1=1,推荐分辨率模式,即推荐分辨率为第一个描述的时序 DTD)
/*---------------------------------------- Color Characteristics ---------------------------------------*/
0x7C, 0x11, 0x9E, 0x59, 0x47, 0x9B, 0x27, 0x10, 0x50, 0x54,  	// 0x19 ~ 0x22:红绿、蓝白场的 xy 坐标信息
/*----------------------------------------- Established Timings ----------------------------------------*/
0x00, 0x00, 0x00,  												// 0x23 ~ 0x25:提供基本固定的一些输出时序,未使用时设为 0x00
/*----------------------------------- Standard Timing Identification -----------------------------------*/
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,		// 0x26 ~ 0x35:提供最多 8 种分辨率的识别,两两一组,未使用时设为 0x01
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
/*---------------------------------- Detailed Timing Descriptions(DTD) ---------------------------------*/
// 推荐分辨率(单个 DTD 占 18 字节,共可记录 4 个 DTD,因此这一部分共占 72 字节)
0x02, 0x3A,   													// 0x36 , 0x37:十进制像素时钟/10000,先存储低8位,这里存储的时钟频率就是 148.5MHz(0x3A02)
0x80, 0x18, 0x71, 												// 0x38 ~ 0x3A:分别对应 H_Active 低8位、H_Blanking 低8位、{H_Active 高4位,H_Blanking 高4位},这里的 H_Active 为 1920(0x780) pixels,H_Blanking 为 280(0x118) pixels
0x38, 0x2D, 0x40, 												// 0x3B ~ 0x3D:V 参数信息,与 H 类似,这里的 V_Active 为 1080(0x438) lines,V_Blanking 为 45(0x2D) lines
0x58, 0x2C,   													// 0x3E , 0x3F:H_Sync Offset 低8位,H_Sync Pulse Width 低8位
0x45, 															// 0x40:{V_Sync Offset 低4位,V_Sync Pulse Width 低4位}
0x00, 															// 0x41:{H_Sync Offset 高2位,H_Sync Pulse Width 高2位,V_Sync Offset 高2位,V_Sync Pulse Width 高2位}
0x10, 0x09, 													// 0x42 , 0x43:H_Image Size(mm) 低8位,V_Image Size(mm) 低8位											
0x00,															// 0x44:{H_Image Size 高4位,V_Image Size 高4位} 
0x00, 0x00, 													// 0x45 , 0x46:H_Border,V_Border
0x1E,   														// 0x47:bit7=0,不交错;bit[6:5]=11,正常显示无立体;bit[4:3]=11,数字分离信号
// 第二个 DTD
0x8C, 0x0A, 0xD0, 0x8A, 0x20, 0xE0, 0x2D, 0x10, 0x10, 0x3E,		// 0x48 ~ 0x59
0x96, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x18, 
// 第三个 DTD
0x00, 0x00, 0x00, 0xFC, 0x00, 0x49, 0x54, 0x45, 0x36, 0x38,   	// 0x5A ~ 0x6B
0x30, 0x32, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
// 第四个 DTD
0x00, 0x00, 0x00, 0xFD, 0x00, 0x30, 0x7A, 0x0F, 0x50, 0x10, 	// 0x6C ~ 0x7D
0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*-------------------------------------------- Extension Flag ------------------------------------------*/
0x01,    														// 0x7E:后面有 EDID 扩展块(Block 1)
0xF3,															// 0x7F:主块(Block 0)的 Checksum,使前127字节加上此字节的总和为0

// EDID 扩展块(Block 1): 0x80 ~ 0xFF
/*---------------------------------------- CEA Extension Header ----------------------------------------*/
0x02, 															// 0x80:CEA Extension Tag,固定为 0x02
0x03, 															// 0x81:修订号,目前一般为 0x03
0x19, 															// 0x82:DTD 开始地址,这里表示从 0x19(+0x80) 开始描述 DTD
0x72,															// 0x83:bit6=1,支持 basic audio;bit5=1,支持 YUV444;bit4=1,支持 YUV422;bit[3:0]=0010,DTD 个数为2 
/*------------------------------------------ Vedio Data Block ------------------------------------------*/
0x46, 															// 0x84:bit[7:5]=010,对应 Tag 为 Vedio Data Block(0x02);bit[4:0]=00110,该字节后还有 6 个字节(SVD)属于这个 VDB
0x90, 0x04, 0x13, 0x01, 0x02, 0x03, 							// 0x85 ~ 0x8A:每个字节表示支持的一种分辨率,代号与分辨率的关系由 HDMI 标准直接定义
/*------------------------------------------ Audio Data Block ------------------------------------------*/
0x23, 															// 0x8B:bit[7:5]=001,对应 Tag 为 Audio Data Block(0x01);bit[4:0]=00011,该字节后还有 3 个字节属于这个 ADB
0x09, 0x07, 0x07, 												// 0x8C ~ 0x8E:三个字节为一组,因此这里只有一组声音相关的描述信息
																//				byte1(0x09)的bit[6:3]=0001,音频格式为 Linear PCM;bit[2:0]=001,Max Number of channels 为 001+1=2;
																//				byte2(0x07)的bit[6:0]=0000111,支持频率 48KHz、44.1KHz、32KHz
																//				byte3(0x07)的bit[2:0]=111,支持位宽 24bit、20bit、16bit
/*------------------------------------ Speaker Allocation Data Block -----------------------------------*/
0x83,   														// 0x8F:bit[7:5]=100,对应 Tag 为 Speaker Allocation Data Block;bit[4:0]=00011,该字节后还有 3 个字节属于这个 SADB
0x01, 0x00, 0x00, 												// 0x90 ~ 0x92:三个字节为一组,因此这里只有一组关于喇叭位置的描述信息
/*-------------------------------------- Vendor Specific Data Block ------------------------------------*/
0x65, 															// 0x93:bit[7:5]=011,对应 Tag 为 Vendor Specific Data Block(VSDB);bit[4:0]=00101,该字节后还有 5 个字节属于这个 VSDB
0x03, 0x0C, 0x00, 												// 0x94 ~ 0x96:H14b VSDB 固定标识,0x000C03
0x10, 0x00, 													// 0x97 ~ 0x98:CEC物理地址,此处为 1.0.0.0,同时也对应 AB(0x97)、CD(0x98) 的值
/*---------------------------------- Detailed Timing Descriptions(DTD) ---------------------------------*/
0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20, 0x6E, 0x28, 	// 0x99 ~ 0xAA:补充的第一组 DTD
0x55, 0x00, 0x10, 0x09, 0x00, 0x00, 0x00, 0x1E, 
0xD6, 0x09, 0x80, 0xA0, 0x20, 0xE0, 0x2D, 0x10, 0x10, 0x60,		// 0xAB ~ 0xBC:补充的第二组 DTD
0xA2, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x18,
0x8C, 0x0A, 0xD0, 0x8A, 0x20, 0xE0, 0x2D, 0x10, 0x10, 0x3E,		// 0xBC ~ 0xCE:补充的第三组 DTD
0x96, 0x00, 0x10, 0x09, 0x00, 0x00, 0x00, 0x18,
/*----------------------------------------------- Reserved ---------------------------------------------*/
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	0x00, 0x00,		// 0xCF:从该字节开始,之后没有用到的位置均用 0 填充
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*-------------------------------------------- Extension Flag ------------------------------------------*/
0x7B  	 														// 0xFF:扩展块(Block 1)的 Checksum,使前127字节加上此字节的总和为0
}

/*
 * EDID RAM 初始化
 * *pIT6801EDID : 用来初始化 EDID RAM 的配置列表
 */
static void EDIDRAMInitial(unsigned char *pIT6801EDID)
{
	int i;
	unsigned char send_buf[4] = {0};
	unsigned char recv_buf[4] = {0};

	hdmirxset(0xc0, 0x01, 0x01);				// HDMI RegC0[1:0]=11 for disable HDMI DDC bus to access EDID RAM

	set_iic_mode(0, EDID_I2C_ADDR);
	printf("Read EDID Table:\n");
	for(i = 0; i < 256; i++)
	{
		recv_buf[0] = (unsigned char)i;
		read(fd_i2c, recv_buf, 1);

		printf("%02x ",recv_buf[0]);
		if((i % 16) == 15)
			printf("\r\n");
	}

	printf("Set EDID Table:\n");
	for(i = 0; i < 256; i++)
	{
		send_buf[0] = (unsigned char)i;
		send_buf[1] = *(pIT6801EDID+i);
		write(fd_i2c, send_buf, 2);

		printf("%02x ",send_buf[1]);
		if((i % 16) == 15)
			printf("\r\n");
	}

	// printf("Check EDID Table:\n");
	// for(i = 0; i < 256; i++)
	// {
	// 	recv_buf[0] = (unsigned char)i;
	// 	read(fd_i2c, recv_buf, 1);

	// 	printf("%02x ",recv_buf[0]);
	// 	if((i % 16) == 15)
	// 		printf("\r\n");
	// }

	set_iic_mode(0, HDMI_I2C_ADDR);
	hdmirxset(0xc1, 0xff, 0x97);				// VSDB 地址(0x93+4)
	hdmirxset(0xc2, 0xff, 0x10);				// AB 的值
	hdmirxset(0xc3, 0xff, 0x00);				// CD 的值
	hdmirxset(0xc4, 0xff, pIT6801EDID[127]);
	hdmirxset(0xc5, 0xff, pIT6801EDID[255]);

	hdmirxset(0xc0, 0x01, 0x00);				// HDMI RegC0[1:0]=00 for enable HDMI DDC bus to access EDID RAM
}

  虽然和初始的 EDID 推荐显示模式配置相同,但是从显示器名还是能看出区别来的:
在这里插入图片描述

2.4 中断处理

在这里插入图片描述
  数据手册里已经指明了软件中断处理的必要性。因此,简单处理一下就是完成初始化以后就进入轮询的方式,持续检测几个中断相关的寄存器,并根据中断事件做出相应的处理(DEMO 里后续的视频输出配置全部由中断状态决定,当然也可以从一开始就设置好固定的输出格式)。这也是可以从 DEMO 里挑着扒下来的。

void it6801_InterruptHandler()
{
	unsigned char Reg05h;
	......

	chgbank(0);

	Reg05h = hdmirxrd(0x05);
	......
	
	hdmirxwr(0x05, Reg05h);
	......

	if(Reg05h) 
	{
		printf("Reg05 = 0x%02X \r\n", Reg05h);

		if(Reg05h & 0x80) 
			printf("#### Port 0 HDCP Off Detected ####\r\n");
		......
	}
	......
}

void it6801_main(void)
{
	it6801_init();

	while(1)
	{
		usleep(150000);
		it6801_InterruptHandler();
	}
}

2.5 视频输出配置

在这里插入图片描述

2.5.1 视频输入状态监测

在这里插入图片描述
  做完以上部分的工作,就可以通过寄存器 reg0A 持续监测 IT6801 的 input port 状态了:没有数据输入时读出来的值就是 0x11,有数据输入时就是 0xaf 或是 0xbf(如果出现了诸如 0x3f 等其他奇怪的值,则有可能是 硬件上的连接问题 QAQ 或是 信号的质量问题 导致的),至此同时实现了 4 步中的前 2 步:
在这里插入图片描述

2.5.2 输出格式配置

在这里插入图片描述
  之后的色彩空间配置就可以根据两张基本表自行调整了:
在这里插入图片描述
在这里插入图片描述

PS:YUV 601 和 YUV 709 对应两种不同的标准,601 是 BT601,SDTV 数据结构,BT656 是 SDTV 的接口定义;709 是 BT709,HDTV 数据结构,BT1120 是 HDTV 的接口定义。除此以外,YUV 还有 BT2020 标准(并不是 2020 年出的标准),不同标准下 YUV 和 RGB 之间的转换参数矩阵也不相同,这就是转换公式存在不同版本的原因。

  最后是设置输出引脚。这一部分跟着编程手册走也没有问题:
在这里插入图片描述
在这里插入图片描述

static void VideoOutputConfigure()
{
	// 设置输出为 YUV422 内同步
	hdmirxwr(0x51, 0x40);
	hdmirxwr(0x65, 0x52);

	// Color Space Matrix Set
	chgbank(1);
	hdmirxwr(0x70, 0x10);		// YUV601:0x10(0~255)	0x00(16~235)		YUV709:0x10(0~255)	0x00(16~235)
	hdmirxwr(0x71, 0x80);		// YUV601:0x80(0~255)	0x80(16~235)		YUV709:0x80(0~255)	0x80(16~235)
	hdmirxwr(0x72, 0x10);		// YUV601:0x10(0~255)	0x10(16~235)		YUV709:0x10(0~255)	0x10(16~235)
	hdmirxwr(0x73, 0xe4);		// YUV601:0x09(0~255)	0xb2(16~235)		YUV709:0xe4(0~255)	0xb8(16~235)
	hdmirxwr(0x74, 0x04);		// YUV601:0x04(0~255)	0x04(16~235)		YUV709:0x04(0~255)	0x05(16~235)
	hdmirxwr(0x75, 0x77);		// YUV601:0x0e(0~255)	0x65(16~235)		YUV709:0x77(0~255)	0xb4(16~235)
	hdmirxwr(0x76, 0x01);		// YUV601:0x02(0~255)	0x02(16~235)		YUV709:0x01(0~255)	0x01(16~235)
	hdmirxwr(0x77, 0x7f);		// YUV601:0xc9(0~255)	0xe9(16~235)		YUV709:0x7f(0~255)	0x94(16~235)
	hdmirxwr(0x78, 0x00);		// YUV601:0x00(0~255)	0x00(16~235)		YUV709:0x00(0~255)	0x00(16~235)
	hdmirxwr(0x79, 0xd0);		// YUV601:0x0f(0~255)	0x93(16~235)		YUV709:0xd0(0~255)	0x4a(16~235)
	hdmirxwr(0x7a, 0x3c);		// YUV601:0x3d(0~255)	0x3c(16~235)		YUV709:0x3c(0~255)	0x3c(16~235)
	hdmirxwr(0x7b, 0x83);		// YUV601:0x84(0~255)	0x18(16~235)		YUV709:0x83(0~255)	0x17(16~235)
	hdmirxwr(0x7c, 0x03);		// YUV601:0x03(0~255)	0x04(16~235)		YUV709:0x03(0~255)	0x04(16~235)
	hdmirxwr(0x7d, 0xad);		// YUV601:0x6d(0~255)	0x55(16~235)		YUV709:0xad(0~255)	0x9f(16~235)
	hdmirxwr(0x7e, 0x3f);		// YUV601:0x3f(0~255)	0x3f(16~235)		YUV709:0x3f(0~255)	0x3f(16~235)
	hdmirxwr(0x7f, 0x48);		// YUV601:0xab(0~255)	0x49(16~235)		YUV709:0x48(0~255)	0xd9(16~235)
	hdmirxwr(0x80, 0x3d);		// YUV601:0x3d(0~255)	0x3d(16~235)		YUV709:0x3d(0~255)	0x3c(16~235)
	hdmirxwr(0x81, 0x32);		// YUV601:0xd1(0~255)	0x9f(16~235)		YUV709:0x32(0~255)	0x10(16~235)
	hdmirxwr(0x82, 0x3f);		// YUV601:0x3e(0~255)	0x3e(16~235)		YUV709:0x3f(0~255)	0x3f(16~235)
	hdmirxwr(0x83, 0x84);		// YUV601:0x84(0~255)	0x18(16~235)		YUV709:0x84(0~255)	0x17(16~235)
	hdmirxwr(0x84, 0x03);		// YUV601:0x03(0~255)	0x04(16~235)		YUV709:0x03(0~255)	0x04(16~235)
	chgbank(0);

	// 复位
	hdmirxwr(0x64, 0x01);
	hdmirxwr(0x64, 0x00);

	// QE16-QE23、QE28-QE35 有效
	hdmirxwr(0x53, 0x32);
}

2.5.3 色偏矫正(咕~)

  在配置好 IT6801 后与 Hi3531D 的 VI 对接,出现了下图的显示效果:
在这里插入图片描述
  随后将掩码 1 清零,掩码 0 依次设置为 0xFF000000 和 0xFF0000测试效果如下:

  • 0xFF000000,0x0。这个乍一看不知道该怎么解释 Orz:
    在这里插入图片描述
  • 0xFF0000,0x0。这个看起来倒是靠谱很多,像是 Y 分量正常而 UV=0 的样子:
    在这里插入图片描述

  之后在易百纳社区里找到了类似的问题帖:[hi3531/hi3535/hi3536] 求助,Hi3531DV100 接BT1120图像颜色异常,现象高度相似,硬件连接基本一致,因此大致可以确定 Hi3531D 输入处存在 Y/C 接反的问题。然而咱使用的 SDK 貌似不是新版的,没找到软件设置翻转的接口,设置分量掩码也改不过来。硬件修改的话,又不是有很多时间能花在这个比较鸡肋的 HDMI 输入方案(其实软件也一样)上,就着 Y 分量的显示效果往下调 4k 似乎勉强也行(以调通为优先,效果次之),所以本文记录到此为止。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值