Linux第105步_基于SiI9022A芯片的RGB转HDMI实验

SiI9022A是一款HDMI传输芯片,可以将“音视频接口”转换为HDMI或者DVI格式,是一个视频转换芯片。本实验基于linux的驱动程序设计。

SiI9022A支持输入视频格式有:xvYCC、BTA-T1004、ITU-R.656,内置DE发生器,支持SYNC格式(RGB 格式)。输出格式支持:HDMI、HDCP和DVI、最高支持1080P视频输出、支持HDMI A、HDMI C和Micro-D连接器。SiI9022A使用I2C接口进行配置。由于STM32MP157没有HDMI外设,只有RGB屏幕接口,因此,我们可以通过RGB转HDMI的芯片SiI9022A来实现HDMI连接。

1、了解SiI9022A的相关寄存器

#define SII902X_TPI_PIXEL_REPETITION  0x8

/*TPI输入总线和像素重复数据寄存器地址为0x08*/

#define SII902X_TPI_AVI_PIXEL_REP_BUS_24BIT     BIT(5)

/*输入总线选择:bit5=0半像素宽;bit5=1全像素宽*/

#define SII902X_TPI_AVI_PIXEL_REP_RISING_EDGE   BIT(4)

/*边沿选择:bit4=0下降沿;bit4=1上升沿*/

#define SII902X_TPI_AVI_PIXEL_REP_4X 3

/*像素重复因子:PR[3:0]=0011b,每个像素发送4次*/

#define SII902X_TPI_AVI_PIXEL_REP_2X 1

/*像素重复因子:PR[3:0]=0001b,每个像素发送2次*/

#define SII902X_TPI_AVI_PIXEL_REP_NONE 0

/*像素重复因子:PR[3:0]=0000b,每个像素发送1次,不用重复发送*/

#define SII902X_TPI_CLK_RATIO_HALF (0 << 6) /*bit7:6=00b,TCLK选择0.5倍的速度*/

#define SII902X_TPI_CLK_RATIO_1X (1 << 6) /*bit7:6=01b,TCLK选择1倍的速度*/

#define SII902X_TPI_CLK_RATIO_2X (2 << 6) /*bit7:6=10b,TCLK选择2倍的速度*/

#define SII902X_TPI_CLK_RATIO_4X (3 << 6) /*bit7:6=11b,TCLK选择4倍的速度*/

#define SII902X_TPI_AVI_IN_FORMAT 0x9

/*TPI AVI输入和输出格式数据配置寄存器地址为0x09*/

#define SII902X_TPI_AVI_INPUT_BITMODE_12BIT BIT(7)

#define SII902X_TPI_AVI_INPUT_DITHER BIT(6)

/*bit7:6=00b,输入颜色深度8位*/

/*bit7:6=01b,没有定义"输入颜色深度"*/

/*颜色抖动则是尝试用较低的颜色位深度来获得更为丰富的视觉效果,

比如用1位的位深度来尽可能得到8位的视觉效果*/

/*bit7:6=10b,输入颜色深度10/12位,在4:2:2模式中,无"颜色抖动"*/

/*bit7:6=11b,输入颜色深度10/12位,在4:2:2模式中,"颜色抖动"为8位的视觉效果*/

#define SII902X_TPI_AVI_INPUT_RANGE_LIMITED (2 << 2)

/*bit3:2=10b,关闭视频范围扩展*/

#define SII902X_TPI_AVI_INPUT_RANGE_FULL (1 << 2) /*bit3:2=01b,打开视频范围扩展*/

#define SII902X_TPI_AVI_INPUT_RANGE_AUTO (0 << 2)

/*bit3:2=00b,自动选择"视频范围扩展"*/

#define SII902X_TPI_AVI_INPUT_COLORSPACE_BLACK (3 << 0)

/*bit1:0=11b,输入色彩为Black模式*/

#define SII902X_TPI_AVI_INPUT_COLORSPACE_YUV422 (2 << 0)

/*bit1:0=10b,输入色彩为YCbCr 4:2:2模式*/

#define SII902X_TPI_AVI_INPUT_COLORSPACE_YUV444 (1 << 0)

/*bit1:0=01b,输入色彩为YCbCr 4:4:4模式*/

#define SII902X_TPI_AVI_INPUT_COLORSPACE_RGB (0 << 0)

/*bit1:0=01b,输入色彩为RGB模式*/

#define SII902X_TPI_AVI_INFOFRAME 0x0c

/*"AVI信息帧寄存器"起始地址为0x0C*/

/*用来存放"AVI信息帧校验和"*/

#define SII902X_SYS_CTRL_DATA 0x1a /*系统控制寄存器地址为0x1A*/

#define SII902X_SYS_CTRL_PWR_DWN BIT(4)

/*bit4=0b,TDMS输出控制激活;bit4=1b,TDMS输出控制关闭*/

#define SII902X_SYS_CTRL_AV_MUTE BIT(3)

/*bit3=0b,普通的音视频,指同时包含音频和视频内容的媒体形式;*/

/*bit3=1b,配置为音频和视频接收器*/

#define SII902X_SYS_CTRL_DDC_BUS_REQ BIT(2)

/*bit2=0b,主机无需请求使用DDC总线*/

/*bit2=1b,主机请求使用DDC总线*/

#define SII902X_SYS_CTRL_DDC_BUS_GRTD BIT(1)

/*bit1=0b,DDC总线不可用*/

/*bit1=1b,主机可以写DDC总线*/

#define SII902X_SYS_CTRL_OUTPUT_MODE BIT(0)

/*bit0=0b,输出模式选择为DVI,bit0=1b,输出模式选择为HDMI*/

#define SII902X_SYS_CTRL_OUTPUT_HDMI 1 /*bit0=1b,输出模式选择为HDMI*/

#define SII902X_SYS_CTRL_OUTPUT_DVI 0 /*bit0=0b,输出模式选择为DVI*/

#define SII902X_REG_CHIPID(n) (0x1b + (n))

/*地址范围0x1B~0x1D,保存的是TPI芯片的ID和版本*/

#define SII902X_PWR_STATE_CTRL 0x1e

/*TPI设备电源状态控制数据寄存器地址为0x1E*/

#define SII902X_AVI_POWER_STATE_MSK GENMASK(1, 0)  /*结果为0x03*/

#define SII902X_AVI_POWER_STATE_D(l) ((l) & SII902X_AVI_POWER_STATE_MSK)

/*执行SII902X_AVI_POWER_STATE_D(0);//结果为0x00*/

/*Power_state[1:0] = 00b,配置"全功率操作模式"*/

#define BITS_PER_LONG 32

#define GENMASK(h, l)  ( ( (~(u32)(0)) - ((u32)(1) << (l) ) + 1 ) & ( ~(u32)(0) >> (BITS_PER_LONG - 1 - (h)) ) )

#define SII902X_AVI_POWER_STATE_MSK GENMASK(1, 0)  /*结果为0x03*/

#define SII902X_AVI_POWER_STATE_D(l) ((l) & SII902X_AVI_POWER_STATE_MSK)

d=SII902X_AVI_POWER_STATE_D(0);/*结果为0x00*/

#define SII902X_TPI_I2S_ENABLE_MAPPING_REG 0x1f

/*Audio配置寄存器起始地址为0x1F*/

#define SII902X_TPI_I2S_CONFIG_FIFO0 (0 << 0) /*bit1:0=00b,FIFO使用通道0*/

#define SII902X_TPI_I2S_CONFIG_FIFO1 (1 << 0) /*bit1:0=01b,FIFO使用通道1*/

#define SII902X_TPI_I2S_CONFIG_FIFO2 (2 << 0) /*bit1:0=10b,FIFO使用通道2*/

#define SII902X_TPI_I2S_CONFIG_FIFO3 (3 << 0) /*bit1:0=11b,FIFO使用通道3*/

#define SII902X_TPI_I2S_LEFT_RIGHT_SWAP (1 << 2)

/*bit2=0b,I2S左右通道不用交换;*/

/*bit2=1b,I2S左右通道交换*/

#define SII902X_TPI_I2S_AUTO_DOWNSAMPLE (1 << 3)

/*bit3=0b,FIFO使用通道0不用自动下采样到"基本音频模式"*/

/*bit3=1b,FIFO使用通道0会自动下采样到"基本音频模式"*/

#define SII902X_TPI_I2S_SELECT_SD0 (0 << 4) /*bit5:4=00b,选择SD0引脚*/

#define SII902X_TPI_I2S_SELECT_SD1 (1 << 4) /*bit5:4=01b,选择SD1引脚*/

#define SII902X_TPI_I2S_SELECT_SD2 (2 << 4) /*bit5:4=10b,选择SD2引脚*/

#define SII902X_TPI_I2S_SELECT_SD3 (3 << 4) /*bit5:4=11b,选择SD3引脚*/

#define SII902X_TPI_I2S_FIFO_ENABLE (1 << 7)

/*bit7=0b,不使能"SD引脚"选择;*/

/*bit7=1b,使能"SD引脚"选择;*/

#define SII902X_TPI_I2S_INPUT_CONFIG_REG 0x20 /*I2S输入配置寄存器的地址为0x20*/

#define SII902X_TPI_I2S_FIRST_BIT_SHIFT_YES (0 << 0)

/*bit0=0b,将WS转换为SD,第1位要移位*/

#define SII902X_TPI_I2S_FIRST_BIT_SHIFT_NO (1 << 0)

/*bit0=1b,将WS转换为SD,第1位不用移位*/

#define SII902X_TPI_I2S_SD_DIRECTION_MSB_FIRST (0 << 1)

/*bit1=0b,字节移位第1位为最高位*/

#define SII902X_TPI_I2S_SD_DIRECTION_LSB_FIRST (1 << 1)

/*bit1=1b,字节移位第1位为最低位*/

#define SII902X_TPI_I2S_SD_JUSTIFY_LEFT (0 << 2) /*bit2=0b,数据左对齐*/

#define SII902X_TPI_I2S_SD_JUSTIFY_RIGHT (1 << 2) /*bit2=1b,数据右对齐*/

#define SII902X_TPI_I2S_WS_POLARITY_LOW (0 << 3) /*bit3=0b,WS极性为LOW*/

#define SII902X_TPI_I2S_WS_POLARITY_HIGH (1 << 3) /*bit3=1b,WS极性为HIGH*/

#define SII902X_TPI_I2S_MCLK_MULTIPLIER_128 (0 << 4) /*bit6:4=000b,MCLK乘数为128*/

#define SII902X_TPI_I2S_MCLK_MULTIPLIER_256 (1 << 4) /*bit6:4=001b,MCLK乘数为256*/

#define SII902X_TPI_I2S_MCLK_MULTIPLIER_384 (2 << 4) /*bit6:4=010b,MCLK乘数为384*/

#define SII902X_TPI_I2S_MCLK_MULTIPLIER_512 (3 << 4) /*bit6:4=011b,MCLK乘数为512*/

#define SII902X_TPI_I2S_MCLK_MULTIPLIER_768 (4 << 4) /*bit6:4=100b,MCLK乘数为768*/

#define SII902X_TPI_I2S_MCLK_MULTIPLIER_1024 (5 << 4)

/*bit6:4=101b,MCLK乘数为1024*/

#define SII902X_TPI_I2S_MCLK_MULTIPLIER_1152 (6 << 4)

/*bit6:4=110b,MCLK乘数为1152*/

#define SII902X_TPI_I2S_MCLK_MULTIPLIER_192 (7 << 4) /*bit6:4=111b,MCLK乘数为192*/

#define SII902X_TPI_I2S_SCK_EDGE_FALLING  (0 << 7) /*bit7=0b,SCK采样边沿为下降沿*/

#define SII902X_TPI_I2S_SCK_EDGE_RISING    (1 << 7) /*bit7=1b,SCK采样边沿为上升沿*/

#define SII902X_TPI_I2S_STRM_HDR_BASE 0x21 /*I2S通道状态寄存器起始地址为0x21*/

#define SII902X_TPI_I2S_STRM_HDR_SIZE 5   /*I2S通道状态寄存器数量为5*/

#define SII902X_TPI_AUDIO_CONFIG_BYTE2_REG 0x26

/*TPI Audio配置寄存器地址为0x26*/

#define SII902X_TPI_AUDIO_CODING_STREAM_HEADER (0 << 0)

/*CT[3:0]=0000b,音频配置类型参考流头*/

#define SII902X_TPI_AUDIO_CODING_PCM (1 << 0) /*CT[3:0]=0001b,音频配置类型为PCM*/

#define SII902X_TPI_AUDIO_CODING_AC3 (2 << 0) /*CT[3:0]=0010b,音频配置类型为AC-3*/

#define SII902X_TPI_AUDIO_CODING_MPEG1 (3 << 0)

/*CT[3:0]=0011b,音频配置类型为MPEG1*/

#define SII902X_TPI_AUDIO_CODING_MP3 (4 << 0) /*CT[3:0]=0100b,音频配置类型为MP3*/

#define SII902X_TPI_AUDIO_CODING_MPEG2 (5 << 0)

/*CT[3:0]=0101b,音频配置类型为MPED2*/

#define SII902X_TPI_AUDIO_CODING_AAC (6 << 0) /*CT[3:0]=0110b,音频配置类型为AAC*/

#define SII902X_TPI_AUDIO_CODING_DTS (7 << 0) /*CT[3:0]=0111b,音频配置类型为DTS*/

#define SII902X_TPI_AUDIO_CODING_ATRAC (8 << 0) /*CT[3:0]=1000b,音频配置类型为ATRAC*/

#define SII902X_TPI_AUDIO_MUTE_DISABLE (0 << 4) /*Mute=0b表示普通音*/

#define SII902X_TPI_AUDIO_MUTE_ENABLE  (1 << 4) /*Mute=1b表示使用弱音器*/

#define SII902X_TPI_AUDIO_LAYOUT_2_CHANNELS (0 << 5)

/*Layout=0b,表示音频包头布局指示器0*/

#define SII902X_TPI_AUDIO_LAYOUT_8_CHANNELS (1 << 5)

/*Layout=1b,表示音频包头布局指示器1*/

#define SII902X_TPI_AUDIO_INTERFACE_DISABLE (0 << 6) /*00b,表示不使用Audio接口*/

#define SII902X_TPI_AUDIO_INTERFACE_SPDIF (1 << 6) /*01b,表示Audio接口使用S/PDIF*/

#define SII902X_TPI_AUDIO_INTERFACE_I2S (2 << 6) /*10b,表示Audio接口使用I2S*/

#define SII902X_TPI_AUDIO_CONFIG_BYTE3_REG 0x27

/*TPI Audio配置寄存器地址为0x27*/

#define SII902X_TPI_AUDIO_FREQ_STREAM (0 << 3) /*SF[2:0]=000b,Audio采样频率参考流头;*/

#define SII902X_TPI_AUDIO_FREQ_32KHZ (1 << 3) /*SF[2:0]=001b,Audio采样频率为32KHz;*/

#define SII902X_TPI_AUDIO_FREQ_44KHZ (2 << 3)

/*SF[2:0]=010b,Audio采样频率为44.1KHz;*/

#define SII902X_TPI_AUDIO_FREQ_48KHZ (3 << 3)

/*SF[2:0]=011b,Audio采样频率为48KHz;*/

#define SII902X_TPI_AUDIO_FREQ_88KHZ (4 << 3)

/*SF[2:0]=100b,Audio采样频率为88.2KHz;*/

#define SII902X_TPI_AUDIO_FREQ_96KHZ (5 << 3) /*SF[2:0]=101b,Audio采样频率为96KHz;*/

#define SII902X_TPI_AUDIO_FREQ_176KHZ (6 << 3)

/*SF[2:0]=110b,Audio采样频率为176.4KHz;*/

#define SII902X_TPI_AUDIO_FREQ_192KHZ (7 << 3)

/*SF[2:0]=111b,Audio采样频率为192KHz;*/

#define SII902X_TPI_AUDIO_SAMPLE_SIZE_STREAM (0 << 6)

/*SS[1:0]=00,Audio采样位数参考流头;*/

#define SII902X_TPI_AUDIO_SAMPLE_SIZE_16 (1 << 6) /*SS[1:0]=01,Audio采样位数为16位;*/

#define SII902X_TPI_AUDIO_SAMPLE_SIZE_20 (2 << 6) /*SS[1:0]=10,Audio采样位数为20位;*/

#define SII902X_TPI_AUDIO_SAMPLE_SIZE_24 (3 << 6) /*SS[1:0]=1q,Audio采样位数为24位;*/

#define SII902X_TPI_AUDIO_CONFIG_BYTE4_REG 0x28

/*Audio配置寄存器结束地址为0x28,保留不用*/

#define SII902X_INT_ENABLE 0x3c  /*"中断使能寄存器"起始地址为0x3C*/

#define SII902X_HOTPLUG_EVENT BIT(0) /*"中断使能寄存器"bit0=1,即使能sii902x输出中断*/

#define SII902X_INT_STATUS 0x3d  /*"中断状态寄存器"结束地址为0x3D*/

#define SII902X_PLUGGED_STATUS BIT(2) /*"中断状态寄存器"bit2=1,表示产生中断*/

#define SII902X_REG_TPI_RQB 0xc7

/*复位和初始化寄存器地址为0xC7*/

/*复位芯片,接着向地址0xC7的寄存器写入0x00,使能TPI模式*/

/*间接内部寄存器访问,Indirect internal register access*/

#define SII902X_IND_SET_PAGE 0xbc /*页寄存器地址为0xBC*/

#define SII902X_IND_OFFSET 0xbd /*变址寄存器地址为0xBD*/

#define SII902X_IND_VALUE 0xbe

/*由页寄存器和变址寄存器选中的寄存器,即为当前寄存器(其固定地址为0xBE)*/

#define SII902X_TPI_MISC_INFOFRAME_BASE 0xbf

/*"TPI杂项信息帧数据寄存器"起始地址为0xBF*/

#define SII902X_TPI_MISC_INFOFRAME_END 0xde

/*"TPI杂项信息帧数据寄存器"结束地址为0xDE*/

#define SII902X_TPI_MISC_INFOFRAME_SIZE \

(SII902X_TPI_MISC_INFOFRAME_END - SII902X_TPI_MISC_INFOFRAME_BASE)

/*"其它信息帧寄存器"的数量,感觉这里少了1个???*/

2、SiI9022A的原理图

STM32MP157使用I2C2接口对SiI9022A进行配置,这里用到了PH4(I2C2_SCL)和PH5(I2C2_SDA)这两个引脚。另外还有一个HDMI_INT中断引脚连接到PH6,一个复位HDMI_RESET引脚连接到PA3。该实验主要是实现HDMI的显示功能,因此,不用去管音频接口。

3、修改设备树

3.1、打开设备树头文件“stm32mp15-pinctrl.dtsi”,找到“i2c2_pins_a”,内容如下:

i2c2_pins_a: i2c2-0 { /*在默认状态下使用*/

pins {

pinmux = <STM32_PINMUX('H', 4, AF4)>, /* I2C2_SCL */

 <STM32_PINMUX('H', 5, AF4)>; /* I2C2_SDA */

bias-disable;/*禁止使用内部偏置电压*/

drive-open-drain;/*开漏输出*/

slew-rate = <0>;/*引脚的速度,可设置:0~3,0最慢,3 最高*/

};

};

i2c2_pins_sleep_a: i2c2-1 { /*在睡眠状态下使用*/

pins {

pinmux = <STM32_PINMUX('H', 4, ANALOG)>, /* I2C2_SCL */

 <STM32_PINMUX('H', 5, ANALOG)>; /* I2C2_SDA */

};

};

3.2、SiI9022A需要一个 1.2V 电压,打开“stm32mp157d-atk.dts”,添加内容如下(注意:是在根节点“/”下添加):

v1v2_hdmi:regulator-v1v2-hdmi {

compatible ="requlator-fixed";

regulator-name ="v1v2_hdmi";

regulator-min-microvolt=<1200000>;

regulator-max-microvolt=<1200000>;

regulator-always-on;

regulator-boot-on;

};

3.3、打开“stm32mp157d-atk.dts”,添加内容如下(注意:不是在根节点“/”下添加):

&i2c2 {

pinctrl-names ="default", "sleep";

pinctrl-0=<&i2c2_pins_a>;/*pinctrl-0为default模式*/

pinctrl-1 =<&i2c2_pins_sleep_a>;/*pinctrl-1为sleep模式*/

/*设置了两个pinmux模式:pinctrl-0为default模式,pinctrl-1为sleep模式,

系统默认使用default模式。*/

status = "okay";

hdmi: hdmi-transmitter@39 {

/*向i2c2添加hdmi-transmitter子节点,“@”后面的“39”就是SiI9022A的I2C器件地址*/

compatible ="sil,sii9022";/*compatible属性值为"sil,sii9022"*/

reg = <0x39>;/*reg属性是设置SiI9022A的器件地址0x39*/

iovcc-supply = <&v3v3>;

cvcc12-supply = <&v1v2_hdmi>;

reset-gpios = <&gpioa 3 GPIO_ACTIVE_LOW>;/*设置复位引脚为PA3,低电压有效*/

interrupt-parent = <&gpioh>;/*指定父中断器为&gpioh*/

/*通过interrupt-parent属性指定SiI9022A的INT引脚的中断父节点为gpioh*/

interrupts = <6 IRQ_TYPE_EDGE_FALLING>;/*设置中断引脚为PH6,下降沿有效*/

        /*查看参考手册“Table 118”,EXTI[6]的事件输入号码为6*/

        /*中断类型和触发方式为下降沿触发*/

/*可以用interrupts-extended = <&gpioh 6 IRQ_TYPE_EDGE_FALLING>;替换上面两句*/

#sound-dai-cells = <1>;

status = "okay";

ports {

#address-cells = <1>;

#size-cells = <0>;

port@0 { /*port节点就是用来接收LTDC数据的接口*/

reg = <0>;

sii9022_in:endpoint {

remote-endpoint = <<dc_ep0_out>;

};

};

};

};

};

3.4、打开“stm32mp157d-atk.dts”,找到ltdc节点,内容如下:

<dc {

pinctrl-names = "default", "sleep";

pinctrl-0 = <<dc_pins_b>;/*pinctrl-0为default模式*/

pinctrl-1 = <<dc_pins_sleep_b>;/*pinctrl-1为sleep模式*/

/*设置了两个pinmux模式:pinctrl-0为default模式,pinctrl-1为sleep模式,

系统默认使用default模式。*/

status = "okay";

port {

#address-cells = <1>;

#size-cells = <0>;

ltdc_ep0_out: endpoint@0 { /*ltdc_ep0_out为port的子节点*/

reg = <0>;

remote-endpoint = <&rgb_panel_in>;

/*remote-endpoint属性告诉ltdc节点输出到rgb_panel_in接口*/

};

};

};

修改ltdc节点如下:

<dc {

pinctrl-names = "default", "sleep";

pinctrl-0 = <<dc_pins_b>;/*pinctrl-0为default模式*/

pinctrl-1 = <<dc_pins_sleep_b>;/*pinctrl-1为sleep模式*/

/*设置了两个pinmux模式:pinctrl-0为default模式,pinctrl-1为sleep模式,

系统默认使用default模式。*/

status = "okay";

port {

#address-cells = <1>;

#size-cells = <0>;

ltdc_ep0_out: endpoint@0 { /*ltdc_ep0_out为port的子节点*/

reg = <0>;

/*remote-endpoint = <&rgb_panel_in>;*/

/*remote-endpoint属性告诉ltdc节点输出到rgb_panel_in接口*/

remote-endpoint = <&sii9022_in>;

/*remote-endpoint属性告诉ltdc节点输出到sii9022_in接口*/

};

};

};

3.5、打开“stm32mp157d-atk.dts”,找到panel_rgb节点,内容如下:

panel_rgb: panel-rgb {

compatible = "zgq,lcd-rgb";

/*在 panel-simple.c 文件里的platform_of_match数组增加一个

of_device_id结构体,此结构体的compatible成员属性值为“zgq,lcd-rgb”。*/

backlight = <&backlight>;/*此属性值为引用背光节点*/

status = "okay";

port {

rgb_panel_in: endpoint {

remote-endpoint = <<dc_ep0_out>;

/*要从ltdc节点里获取显示数据*/

};

};

};

将“panel_rgb节点”屏蔽掉,如下:

/*

panel_rgb: panel-rgb {

compatible = "zgq,lcd-rgb";

//在 panel-simple.c 文件里的platform_of_match数组增加一个

//of_device_id结构体,此结构体的compatible成员属性值为“zgq,lcd-rgb”。

backlight = <&backlight>;//此属性值为引用背光节点

status = "okay";

port {

rgb_panel_in: endpoint {

remote-endpoint = <<dc_ep0_out>;

//要从ltdc节点里获取显示数据

};

};

};

*/

3.6、查看PH4,PH5,PH6PA3是否被使用

打开设备树头文件“stm32mp15-pinctrl.dtsi”,查看PA11和PA12是否被使用了。

①点击“编辑”,点击“查找”,输入“STM32_PINMUX('H', 4”,然后“回车”,没有发现PH4被复用;

②点击“编辑”,点击“查找”,输入“STM32_PINMUX('H',5”,然后“回车”,发现PH5被复用,屏蔽该语句,见下图:

③点击“编辑”,点击“查找”,输入“STM32_PINMUX('H', 6”,然后“回车”,发现PH6被复用,屏蔽该语句,见下图:

④点击“编辑”,点击“查找”,输入“STM32_PINMUX('A', 3”,然后“回车”,发现PA3被复用,屏蔽该语句,见下图:

4、通过“linux内核图形化配置界面”,使能内核自带的sii902x驱动

1)、打开终端。

2)、输入“cd linux/atk-mp1/linux/my_linux/linux-5.4.31/回车”,切换到“linux/atk-mp1/linux/my_linux/linux-5.4.31/”目录;

3)、输入“make menuconfig回车”,打开linux内核图形化配置界面移动向下光标键至“Device Drivers”,见下图

4)、按下回车键,移动向下光标键至“Graphics support”,见下图

5)、按下回车键,移动向下光标键至“Display Interface Bridges”,见下图

6)、按下回车键,移动向下光标键至“Silicon Image sii902x RGB/HDMI bridge”,见下图

7)、按“Y”

8)、先“保存”,按“TAB键”至“Save”,按下“回车”,得到下面的界面。

9)、输入“./arch/arm/configs/stm32mp1_atk_defconfig”,移动“向下光标键”至“Ok”,得到下图:

10)、按“回车”,保存完成。得到下面的界面。

11)、按“回车”,退出保存界面。然后按“ESC键”,直到得到下面的界面:

12)、输入“make stm32mp1_atk_defconfig回车”,注意:如果忘记执行,可能再次打开时会发现“.config”没有被更新,得到下图:

5、编译设备树

打开VSCode中的终端,输入“make uImage dtbs LOADADDR=0XC2000040 -j8回车”,执行编译“Image”和“dtbs”,并指定装载的起始地址为0XC2000040,j8表示指定采用8线程执行。make dtbs”,用来指定编译设备树。见下图:

②输入“ls arch/arm/boot/uImage -l

查看是否生成了新的“uImage”文件

③输入“ls arch/arm/boot/dts/stm32mp157d-atk.dtb -l

查看是否生成了新的“stm32mp157d-atk.dtb”文件

4)、拷贝输出的文件:

①输入“cp arch/arm/boot/uImage /home/zgq/linux/atk-mp1/linux/bootfs/ -f回车”,执行文件拷贝,准备烧录到EMMC;

②输入“cp arch/arm/boot/dts/stm32mp157d-atk.dtb /home/zgq/linux/atk-mp1/linux/bootfs/ -f回车”,执行文件拷贝,准备烧录到EMMC

③输入“cp arch/arm/boot/uImage /home/zgq/linux/tftpboot/ -f回车”,执行文件拷贝,准备从tftp下载;

④输入“cp arch/arm/boot/dts/stm32mp157d-atk.dtb /home/zgq/linux/tftpboot/ -f回车”,执行文件拷贝,准备从tftp下载;

⑤输入“ls -l /home/zgq/linux/atk-mp1/linux/bootfs/回车”,查看“/home/zgq/linux/atk-mp1/linux/bootfs/”目录下的所有文件和文件夹

⑥输入“ls -l /home/zgq/linux/tftpboot/回车”,查看“/home/zgq/linux/tftpboot/”目录下的所有文件和文件夹

⑦输入“chmod 777 /home/zgq/linux/tftpboot/stm32mp157d-atk.dtb回车

给“stm32mp157d-atk.dtb”文件赋予可执行权限

⑧输入“chmod 777 /home/zgq/linux/tftpboot/uImage回车 ,给“uImage”文件赋予可执行权限

⑨输入“ls /home/zgq/linux/tftpboot/ -l回车”,查看“/home/zgq/linux/tftpboot/”目录下的所有文件和文件夹

6、测试程序

#include <linux/gpio/consumer.h>
#include <linux/i2c-mux.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>#include <sound/hdmi-codec.h>#define SII902X_TPI_VIDEO_DATA			0x0#define SII902X_TPI_PIXEL_REPETITION		0x8 /*TPI输入总线和像素重复数据寄存器地址为0x08*/
#define SII902X_TPI_AVI_PIXEL_REP_BUS_24BIT     BIT(5) /*输入总线选择:bit5=0半像素宽;bit5=1全像素宽*/
#define SII902X_TPI_AVI_PIXEL_REP_RISING_EDGE   BIT(4) /*边沿选择:bit4=0下降沿;bit4=1上升沿*/
#define SII902X_TPI_AVI_PIXEL_REP_4X		3  /*像素重复因子:PR[3:0]=0011b,每个像素发送4次*/
#define SII902X_TPI_AVI_PIXEL_REP_2X		1  /*像素重复因子:PR[3:0]=0001b,每个像素发送2次*/
#define SII902X_TPI_AVI_PIXEL_REP_NONE		0  /*像素重复因子:PR[3:0]=0000b,每个像素发送1次,不用重复发送*/
#define SII902X_TPI_CLK_RATIO_HALF		(0 << 6) /*bit7:6=00b,TCLK选择0.5倍的速度*/
#define SII902X_TPI_CLK_RATIO_1X		(1 << 6) /*bit7:6=01b,TCLK选择1倍的速度*/
#define SII902X_TPI_CLK_RATIO_2X		(2 << 6) /*bit7:6=10b,TCLK选择2倍的速度*/
#define SII902X_TPI_CLK_RATIO_4X		(3 << 6) /*bit7:6=11b,TCLK选择4倍的速度*/#define SII902X_TPI_AVI_IN_FORMAT		0x9 /*TPI AVI输入和输出格式数据配置寄存器地址为0x09*/
#define SII902X_TPI_AVI_INPUT_BITMODE_12BIT	BIT(7)
#define SII902X_TPI_AVI_INPUT_DITHER		BIT(6)
/*bit7:6=00b,输入颜色深度8位*/
/*bit7:6=01b,没有定义"输入颜色深度"*/
/*颜色抖动则是尝试用较低的颜色位深度来获得更为丰富的视觉效果,
比如用1位的位深度来尽可能得到8位的视觉效果*/
/*bit7:6=10b,输入颜色深度10/12位,在4:2:2模式中,无"颜色抖动"*/
/*bit7:6=11b,输入颜色深度10/12位,在4:2:2模式中,"颜色抖动"为8位的视觉效果**/
#define SII902X_TPI_AVI_INPUT_RANGE_LIMITED	(2 << 2) /*bit3:2=10b,关闭视频范围扩展*/
#define SII902X_TPI_AVI_INPUT_RANGE_FULL	(1 << 2) /*bit3:2=01b,打开视频范围扩展*/
#define SII902X_TPI_AVI_INPUT_RANGE_AUTO	(0 << 2) /*bit3:2=00b,自动选择"视频范围扩展"*/
#define SII902X_TPI_AVI_INPUT_COLORSPACE_BLACK	(3 << 0) /*bit1:0=11b,输入色彩为Black模式*/
#define SII902X_TPI_AVI_INPUT_COLORSPACE_YUV422	(2 << 0) /*bit1:0=10b,输入色彩为YCbCr 4:2:2模式*/
#define SII902X_TPI_AVI_INPUT_COLORSPACE_YUV444	(1 << 0) /*bit1:0=01b,输入色彩为YCbCr 4:4:4模式*/
#define SII902X_TPI_AVI_INPUT_COLORSPACE_RGB	(0 << 0) /*bit1:0=01b,输入色彩为RGB模式*/#define SII902X_TPI_AVI_INFOFRAME		0x0c
/*"AVI信息帧寄存器"起始地址为0x0C*/
/*用来存放"AVI信息帧校验和"*/#define SII902X_SYS_CTRL_DATA			0x1a /*系统控制寄存器地址为0x1A*/
#define SII902X_SYS_CTRL_PWR_DWN		BIT(4)
/*bit4=0b,TDMS输出控制激活;bit4=1b,TDMS输出控制关闭*/
#define SII902X_SYS_CTRL_AV_MUTE		BIT(3)
/*bit3=0b,普通的音视频,指同时包含音频和视频内容的媒体形式;*/
/*bit3=1b,配置为音频和视频接收器*/
#define SII902X_SYS_CTRL_DDC_BUS_REQ		BIT(2)
/*bit2=0b,主机无需请求使用DDC总线*/
/*bit2=1b,主机请求使用DDC总线*/
#define SII902X_SYS_CTRL_DDC_BUS_GRTD		BIT(1)
/*bit1=0b,DDC总线不可用*/
/*bit1=1b,主机可以写DDC总线*/
#define SII902X_SYS_CTRL_OUTPUT_MODE		BIT(0)
/*bit0=0b,输出模式选择为DVI,bit0=1b,输出模式选择为HDMI*/
#define SII902X_SYS_CTRL_OUTPUT_HDMI	1 /*bit0=1b,输出模式选择为HDMI*/
#define SII902X_SYS_CTRL_OUTPUT_DVI		0 /*bit0=0b,输出模式选择为DVI*/#define SII902X_REG_CHIPID(n)			(0x1b + (n))
/*地址范围0x1B~0x1D,保存的是TPI芯片的ID和版本*/#define SII902X_PWR_STATE_CTRL			0x1e /*TPI设备电源状态控制数据寄存器地址为0x1E*/
#define SII902X_AVI_POWER_STATE_MSK		GENMASK(1, 0)  /*结果为0x03*/
#define SII902X_AVI_POWER_STATE_D(l)	((l) & SII902X_AVI_POWER_STATE_MSK)
/*执行SII902X_AVI_POWER_STATE_D(0);//结果为0x00*/
/*Power_state[1:0] = 00b,配置"全功率操作模式"*//* Audio  */
#define SII902X_TPI_I2S_ENABLE_MAPPING_REG	0x1f  /*Audio配置寄存器起始地址为0x1F*/
#define SII902X_TPI_I2S_CONFIG_FIFO0		(0 << 0) /*bit1:0=00b,FIFO使用通道0*/
#define SII902X_TPI_I2S_CONFIG_FIFO1		(1 << 0) /*bit1:0=01b,FIFO使用通道1*/
#define SII902X_TPI_I2S_CONFIG_FIFO2		(2 << 0) /*bit1:0=10b,FIFO使用通道2*/
#define SII902X_TPI_I2S_CONFIG_FIFO3		(3 << 0) /*bit1:0=11b,FIFO使用通道3*/
#define SII902X_TPI_I2S_LEFT_RIGHT_SWAP		(1 << 2)
/*bit2=0b,I2S左右通道不用交换;*/
/*bit2=1b,I2S左右通道交换*/
#define SII902X_TPI_I2S_AUTO_DOWNSAMPLE		(1 << 3)
/*bit3=0b,FIFO使用通道0不用自动下采样到"基本音频模式"*/
/*bit3=1b,FIFO使用通道0会自动下采样到"基本音频模式"*/
#define SII902X_TPI_I2S_SELECT_SD0			(0 << 4) /*bit5:4=00b,选择SD0引脚*/
#define SII902X_TPI_I2S_SELECT_SD1			(1 << 4) /*bit5:4=01b,选择SD1引脚*/
#define SII902X_TPI_I2S_SELECT_SD2			(2 << 4) /*bit5:4=10b,选择SD2引脚*/
#define SII902X_TPI_I2S_SELECT_SD3			(3 << 4) /*bit5:4=11b,选择SD3引脚*/
#define SII902X_TPI_I2S_FIFO_ENABLE			(1 << 7)
/*bit7=0b,不使能"SD引脚"选择;*/
/*bit7=1b,使能"SD引脚"选择;*/#define SII902X_TPI_I2S_INPUT_CONFIG_REG	0x20 /*I2S输入配置寄存器的地址为0x20*/
#define SII902X_TPI_I2S_FIRST_BIT_SHIFT_YES		(0 << 0) /*bit0=0b,将WS转换为SD,第1位要移位*/
#define SII902X_TPI_I2S_FIRST_BIT_SHIFT_NO		(1 << 0) /*bit0=1b,将WS转换为SD,第1位不用移位*/
#define SII902X_TPI_I2S_SD_DIRECTION_MSB_FIRST	(0 << 1) /*bit1=0b,字节移位第1位为最高位*/
#define SII902X_TPI_I2S_SD_DIRECTION_LSB_FIRST	(1 << 1) /*bit1=1b,字节移位第1位为最低位*/
#define SII902X_TPI_I2S_SD_JUSTIFY_LEFT			(0 << 2) /*bit2=0b,数据左对齐*/
#define SII902X_TPI_I2S_SD_JUSTIFY_RIGHT		(1 << 2) /*bit2=1b,数据右对齐*/
#define SII902X_TPI_I2S_WS_POLARITY_LOW			(0 << 3) /*bit3=0b,WS极性为LOW*/
#define SII902X_TPI_I2S_WS_POLARITY_HIGH		(1 << 3) /*bit3=1b,WS极性为HIGH*/
#define SII902X_TPI_I2S_MCLK_MULTIPLIER_128		(0 << 4) /*bit6:4=000b,MCLK乘数为128*/
#define SII902X_TPI_I2S_MCLK_MULTIPLIER_256		(1 << 4) /*bit6:4=001b,MCLK乘数为256*/
#define SII902X_TPI_I2S_MCLK_MULTIPLIER_384		(2 << 4) /*bit6:4=010b,MCLK乘数为384*/
#define SII902X_TPI_I2S_MCLK_MULTIPLIER_512		(3 << 4) /*bit6:4=011b,MCLK乘数为512*/
#define SII902X_TPI_I2S_MCLK_MULTIPLIER_768		(4 << 4) /*bit6:4=100b,MCLK乘数为768*/
#define SII902X_TPI_I2S_MCLK_MULTIPLIER_1024	(5 << 4) /*bit6:4=101b,MCLK乘数为1024*/
#define SII902X_TPI_I2S_MCLK_MULTIPLIER_1152	(6 << 4) /*bit6:4=110b,MCLK乘数为1152*/
#define SII902X_TPI_I2S_MCLK_MULTIPLIER_192		(7 << 4) /*bit6:4=111b,MCLK乘数为192*/
#define SII902X_TPI_I2S_SCK_EDGE_FALLING		(0 << 7) /*bit7=0b,SCK采样边沿为下降沿*/
#define SII902X_TPI_I2S_SCK_EDGE_RISING			(1 << 7) /*bit7=1b,SCK采样边沿为上升沿*/#define SII902X_TPI_I2S_STRM_HDR_BASE	0x21 /*I2S通道状态寄存器起始地址为0x21*/
#define SII902X_TPI_I2S_STRM_HDR_SIZE	5    /*I2S通道状态寄存器数量为5*/#define SII902X_TPI_AUDIO_CONFIG_BYTE2_REG	0x26 /*TPI Audio配置寄存器地址为0x26*/
#define SII902X_TPI_AUDIO_CODING_STREAM_HEADER	(0 << 0) /*CT[3:0]=0000b,音频配置类型参考流头*/
#define SII902X_TPI_AUDIO_CODING_PCM			(1 << 0) /*CT[3:0]=0001b,音频配置类型为PCM*/
#define SII902X_TPI_AUDIO_CODING_AC3			(2 << 0) /*CT[3:0]=0010b,音频配置类型为AC-3*/
#define SII902X_TPI_AUDIO_CODING_MPEG1			(3 << 0) /*CT[3:0]=0011b,音频配置类型为MPEG1*/
#define SII902X_TPI_AUDIO_CODING_MP3			(4 << 0) /*CT[3:0]=0100b,音频配置类型为MP3*/
#define SII902X_TPI_AUDIO_CODING_MPEG2			(5 << 0) /*CT[3:0]=0101b,音频配置类型为MPED2*/
#define SII902X_TPI_AUDIO_CODING_AAC			(6 << 0) /*CT[3:0]=0110b,音频配置类型为AAC*/
#define SII902X_TPI_AUDIO_CODING_DTS			(7 << 0) /*CT[3:0]=0111b,音频配置类型为DTS*/
#define SII902X_TPI_AUDIO_CODING_ATRAC			(8 << 0) /*CT[3:0]=1000b,音频配置类型为ATRAC*/
#define SII902X_TPI_AUDIO_MUTE_DISABLE			(0 << 4) /*Mute=0b表示普通音*/
#define SII902X_TPI_AUDIO_MUTE_ENABLE			(1 << 4) /*Mute=1b表示使用弱音器*/
#define SII902X_TPI_AUDIO_LAYOUT_2_CHANNELS		(0 << 5) /*Layout=0b,表示音频包头布局指示器0*/
#define SII902X_TPI_AUDIO_LAYOUT_8_CHANNELS		(1 << 5) /*Layout=1b,表示音频包头布局指示器1*/
#define SII902X_TPI_AUDIO_INTERFACE_DISABLE		(0 << 6) /*00b,表示不使用Audio接口*/
#define SII902X_TPI_AUDIO_INTERFACE_SPDIF		(1 << 6) /*01b,表示Audio接口使用S/PDIF*/
#define SII902X_TPI_AUDIO_INTERFACE_I2S			(2 << 6) /*10b,表示Audio接口使用I2S*/#define SII902X_TPI_AUDIO_CONFIG_BYTE3_REG	0x27 /*TPI Audio配置寄存器地址为0x27*/
#define SII902X_TPI_AUDIO_FREQ_STREAM	(0 << 3) /*SF[2:0]=000b,Audio采样频率参考流头;*/
#define SII902X_TPI_AUDIO_FREQ_32KHZ	(1 << 3) /*SF[2:0]=001b,Audio采样频率为32KHz;*/
#define SII902X_TPI_AUDIO_FREQ_44KHZ	(2 << 3) /*SF[2:0]=010b,Audio采样频率为44.1KHz;*/
#define SII902X_TPI_AUDIO_FREQ_48KHZ	(3 << 3) /*SF[2:0]=011b,Audio采样频率为48KHz;*/
#define SII902X_TPI_AUDIO_FREQ_88KHZ	(4 << 3) /*SF[2:0]=100b,Audio采样频率为88.2KHz;*/
#define SII902X_TPI_AUDIO_FREQ_96KHZ	(5 << 3) /*SF[2:0]=101b,Audio采样频率为96KHz;*/
#define SII902X_TPI_AUDIO_FREQ_176KHZ	(6 << 3) /*SF[2:0]=110b,Audio采样频率为176.4KHz;*/
#define SII902X_TPI_AUDIO_FREQ_192KHZ	(7 << 3) /*SF[2:0]=111b,Audio采样频率为192KHz;*/
#define SII902X_TPI_AUDIO_SAMPLE_SIZE_STREAM	(0 << 6) /*SS[1:0]=00,Audio采样位数参考流头;*/
#define SII902X_TPI_AUDIO_SAMPLE_SIZE_16		(1 << 6) /*SS[1:0]=01,Audio采样位数为16位;*/
#define SII902X_TPI_AUDIO_SAMPLE_SIZE_20		(2 << 6) /*SS[1:0]=10,Audio采样位数为20位;*/
#define SII902X_TPI_AUDIO_SAMPLE_SIZE_24		(3 << 6) /*SS[1:0]=1q,Audio采样位数为24位;*/#define SII902X_TPI_AUDIO_CONFIG_BYTE4_REG	0x28
/*Audio配置寄存器结束地址为0x28,保留不用*/#define SII902X_INT_ENABLE			0x3c  /*"中断使能寄存器"起始地址为0x3C*/
#define SII902X_INT_STATUS			0x3d  /*"中断状态寄存器"结束地址为0x3D*/
#define SII902X_HOTPLUG_EVENT		BIT(0) /*"中断使能寄存器"bit0=1,即使能sii902x输出中断*/
#define SII902X_PLUGGED_STATUS		BIT(2) /*"中断状态寄存器"bit2=1,表示产生中断*/#define SII902X_REG_TPI_RQB			0xc7
/*复位和初始化寄存器地址为0xC7*/
/*复位芯片,接着向地址0xC7的寄存器写入0x00,使能TPI模式*//*间接内部寄存器访问,Indirect internal register access*/
#define SII902X_IND_SET_PAGE	0xbc /*页寄存器地址为0xBC*/
#define SII902X_IND_OFFSET		0xbd /*变址寄存器地址为0xBD*/
#define SII902X_IND_VALUE		0xbe
/*由页寄存器和变址寄存器选中的寄存器,即为当前寄存器(其固定地址为0xBE)*/#define SII902X_TPI_MISC_INFOFRAME_BASE		0xbf
/*"TPI杂项信息帧数据寄存器"起始地址为0xBF*/
#define SII902X_TPI_MISC_INFOFRAME_END		0xde
/*"TPI杂项信息帧数据寄存器"结束地址为0xDE*/
#define SII902X_TPI_MISC_INFOFRAME_SIZE	\(SII902X_TPI_MISC_INFOFRAME_END - SII902X_TPI_MISC_INFOFRAME_BASE)
/*"其它信息帧寄存器"的数量,感觉这里少了1个???*/#define SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS	500#define SII902X_AUDIO_PORT_INDEX		3/* CEC device */
#define SII902X_CEC_I2C_ADDR	0x30#define SII902X_CEC_SETUP			0x8estruct sii902x {struct i2c_client *i2c;/*i2c设备*/struct regmap *regmap;struct drm_bridge bridge;struct drm_connector connector;struct gpio_desc *reset_gpio;struct i2c_mux_core *i2cmux;struct edid *edid;/** Mutex protects audio and video functions from interfering* each other, by keeping their i2c command sequences atomic.*/struct mutex mutex;/*声明互斥体mutex*/struct sii902x_audio {struct platform_device *pdev;struct clk *mclk;u32 i2s_fifo_sequence[4];} audio;struct regulator_bulk_data supplies[2];
};//函数功能:通过I2C读取地址为reg寄存器的值,返回值在首地址为val的存储区中
static int sii902x_read_unlocked(struct i2c_client *i2c, u8 reg, u8 *val)
{union i2c_smbus_data data;int ret;ret = __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags,I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, &data);/*在12C总线上传输数据,支持多种传输类型,如字节读写、块读写等。如果成功,则返回传输的字节数,否则返回一个负数。*/if (ret < 0)return ret;*val = data.byte;//保存“地址为reg寄存器的值”return 0;
}//函数功能:通过I2C,将val的值写入地址为reg寄存器中
static int sii902x_write_unlocked(struct i2c_client *i2c, u8 reg, u8 val)
{union i2c_smbus_data data;data.byte = val;return __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags,I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA,&data);/*在12C总线上传输数据,支持多种传输类型,如字节读写、块读写等。如果成功,则返回传输的字节数,否则返回一个负数。*/
}//函数功能:通过I2C,修改地址为reg寄存器中的mask位
static int sii902x_update_bits_unlocked(struct i2c_client *i2c, u8 reg, u8 mask,u8 val)
{int ret;u8 status;ret = sii902x_read_unlocked(i2c, reg, &status);/*通过I2C读取地址为reg寄存器的值,返回值在status中*/if (ret)return ret;status &= ~mask;status |= val & mask;return sii902x_write_unlocked(i2c, reg, status);/*通过I2C,将status的值写入地址为reg寄存器中*/
}static inline struct sii902x *bridge_to_sii902x(struct drm_bridge *bridge)
{return container_of(bridge, struct sii902x, bridge);
}static inline struct sii902x *connector_to_sii902x(struct drm_connector *con)
{return container_of(con, struct sii902x, connector);
}//函数功能:sii902复位
static void sii902x_reset(struct sii902x *sii902x)
{if (!sii902x->reset_gpio)return;gpiod_set_value(sii902x->reset_gpio, 1);/*1表示设置引脚输出高电平*//* The datasheet says treset-min = 100us. Make it 150us to be sure. */usleep_range(150, 200);/*睡大约一段时间150us~200us*/gpiod_set_value(sii902x->reset_gpio, 0);/*0表示设置引脚输出低电平*/
}//函数功能:读"中断状态寄存器"bit2
//返回值为1,表示HDMI已经连接;
//返回值为2,表示HDMI没有连接;
static enum drm_connector_status
sii902x_connector_detect(struct drm_connector *connector, bool force)
{struct sii902x *sii902x = connector_to_sii902x(connector);unsigned int status;mutex_lock(&sii902x->mutex);/*上锁*/regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status);/*读"中断状态寄存器"bit2,地址为0x3D,返回值保存到status中"*/mutex_unlock(&sii902x->mutex);/*解锁*/return (status & SII902X_PLUGGED_STATUS) ?connector_status_connected : connector_status_disconnected;/*"中断状态寄存器"bit2=1,表示产生中断*/
}static const struct drm_connector_funcs sii902x_connector_funcs = {.detect = sii902x_connector_detect,/*读"中断状态寄存器"bit2*/.fill_modes = drm_helper_probe_single_connector_modes,/*获得完整的显示模式*/.destroy = drm_connector_cleanup,/*清除初始化的连接器*/.reset = drm_atomic_helper_connector_reset,.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};static int sii902x_get_modes(struct drm_connector *connector)
{struct sii902x *sii902x = connector_to_sii902x(connector);u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;u8 output_mode = SII902X_SYS_CTRL_OUTPUT_DVI;struct edid *edid;int num = 0, ret;mutex_lock(&sii902x->mutex);/*上锁*/kfree(sii902x->edid);/*在Linux内核中,kfree是一个用于释放内存的函数,它用于释放通过kmalloc、kzalloc、vmalloc等函数分配的内存。这些分配函数在内核空间中动态地分配内存,而kfree则用于在不再需要这些内存时将其释放回系统。*/sii902x->edid = NULL;edid = drm_get_edid(connector, sii902x->i2cmux->adapter[0]);drm_connector_update_edid_property(connector, edid);if (edid) {if (drm_detect_hdmi_monitor(edid))output_mode = SII902X_SYS_CTRL_OUTPUT_HDMI;/*bit0=1b,输出模式选择为HDMI*/num = drm_add_edid_modes(connector, edid);sii902x->edid = edid;}ret = drm_display_info_set_bus_formats(&connector->display_info,&bus_format, 1);if (ret)goto error_out;ret = regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,SII902X_SYS_CTRL_OUTPUT_MODE, output_mode);/*系统控制寄存器(地址为0x1A),将bit0=1b,输出模式选择为HDMI*/if (ret)goto error_out;ret = num;error_out:mutex_unlock(&sii902x->mutex);/*解锁*/return ret;
}static enum drm_mode_status sii902x_mode_valid(struct drm_connector *connector,struct drm_display_mode *mode)
{/* TODO: check mode */return MODE_OK;
}static const struct drm_connector_helper_funcs sii902x_connector_helper_funcs = {.get_modes = sii902x_get_modes,.mode_valid = sii902x_mode_valid,
};//函数功能:关闭"TDMS输出控制"
static void sii902x_bridge_disable(struct drm_bridge *bridge)
{struct sii902x *sii902x = bridge_to_sii902x(bridge);mutex_lock(&sii902x->mutex);/*上锁*/regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,SII902X_SYS_CTRL_PWR_DWN,SII902X_SYS_CTRL_PWR_DWN);/*系统控制寄存器地址为0x1A,设置bit4=1b,TDMS输出控制关闭*/mutex_unlock(&sii902x->mutex);/*解锁*/
}static void sii902x_bridge_enable(struct drm_bridge *bridge)
{struct sii902x *sii902x = bridge_to_sii902x(bridge);u8 output_mode = SII902X_SYS_CTRL_OUTPUT_DVI;mutex_lock(&sii902x->mutex);/*上锁*/regmap_update_bits(sii902x->regmap, SII902X_PWR_STATE_CTRL,SII902X_AVI_POWER_STATE_MSK,SII902X_AVI_POWER_STATE_D(0));/*TPI设备电源状态控制数据寄存器地址为0x1E*//*Power_state[1:0] = 00b,配置"全功率操作模式"*/regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,SII902X_SYS_CTRL_PWR_DWN, 0);/*系统控制寄存器地址为0x1A,设置bit4=0b,打开TDMS输出控制*/if (sii902x->edid) {if (drm_detect_hdmi_monitor(sii902x->edid))output_mode = SII902X_SYS_CTRL_OUTPUT_HDMI;}regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,SII902X_SYS_CTRL_OUTPUT_MODE, output_mode);mutex_unlock(&sii902x->mutex);/*解锁*/
}static void sii902x_bridge_mode_set(struct drm_bridge *bridge,const struct drm_display_mode *mode,const struct drm_display_mode *adj)
{struct sii902x *sii902x = bridge_to_sii902x(bridge);struct regmap *regmap = sii902x->regmap;u8 buf[HDMI_INFOFRAME_SIZE(AVI)];struct hdmi_avi_infoframe frame;u16 pixel_clock_10kHz = adj->clock / 10;int ret;buf[0] = pixel_clock_10kHz & 0xff;buf[1] = pixel_clock_10kHz >> 8;buf[2] = adj->vrefresh;buf[3] = 0x00;buf[4] = adj->hdisplay;buf[5] = adj->hdisplay >> 8;buf[6] = adj->vdisplay;buf[7] = adj->vdisplay >> 8;buf[8] = SII902X_TPI_CLK_RATIO_1X | SII902X_TPI_AVI_PIXEL_REP_NONE | SII902X_TPI_AVI_PIXEL_REP_BUS_24BIT;/*buf[8]为"TPI输入总线和像素重复数据寄存器"中的配置数据,其寄存器地址为0x08*//*bit7:6=01b,TCLK选择1倍的速度;*//*像素重复因子:PR[3:0]=0000b,每个像素发送1次,不用重复发送*//*输入总线选择:bit5=1全像素宽*/buf[9] = SII902X_TPI_AVI_INPUT_RANGE_AUTO | SII902X_TPI_AVI_INPUT_COLORSPACE_RGB;/*buf[9]为TPI AVI输入和输出格式数据配置寄存器中的配置数据,其寄存器地址为0x09*//*bit3:2=00b,自动选择"视频范围扩展"*//*bit1:0=01b,输入色彩为RGB模式*/mutex_lock(&sii902x->mutex);/*上锁*/ret = regmap_bulk_write(regmap, SII902X_TPI_VIDEO_DATA, buf, 10);/*向设备写入多个寄存器*//*sii902x寄存器首地址为0x00*//*待数据的首地址为buf*//*写入的字节数量为10*/if (ret)goto out;ret = drm_hdmi_avi_infoframe_from_display_mode(&frame,&sii902x->connector, adj);if (ret < 0) {DRM_ERROR("couldn't fill AVI infoframe\n");goto out;}ret = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf));if (ret < 0) {DRM_ERROR("failed to pack AVI infoframe: %d\n", ret);goto out;}/* Do not send the infoframe header, but keep the CRC field. */regmap_bulk_write(regmap, SII902X_TPI_AVI_INFOFRAME,buf + HDMI_INFOFRAME_HEADER_SIZE - 1,HDMI_AVI_INFOFRAME_SIZE + 1);/*向设备写入多个寄存器*//*sii902x寄存器首地址为0x0C*//*待数据的首地址为(buf + HDMI_INFOFRAME_HEADER_SIZE - 1)*//*写入的字节数量为(HDMI_AVI_INFOFRAME_SIZE+1)*/out:mutex_unlock(&sii902x->mutex);/*解锁*/
}static int sii902x_bridge_attach(struct drm_bridge *bridge)
{struct sii902x *sii902x = bridge_to_sii902x(bridge);struct drm_device *drm = bridge->dev;int ret;drm_connector_helper_add(&sii902x->connector,&sii902x_connector_helper_funcs);/*为连接器设置helper虚值表*/if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {/*检查驱动程序特性标志*/dev_err(&sii902x->i2c->dev,"sii902x driver is only compatible with DRM devices supporting atomic updates\n");return -ENOTSUPP;}ret = drm_connector_init(drm, &sii902x->connector,&sii902x_connector_funcs,DRM_MODE_CONNECTOR_HDMIA);/*初始化一个预分配的连接器*/if (ret)return ret;if (sii902x->i2c->irq > 0)sii902x->connector.polled = DRM_CONNECTOR_POLL_HPD;elsesii902x->connector.polled = DRM_CONNECTOR_POLL_CONNECT;drm_connector_attach_encoder(&sii902x->connector, bridge->encoder);/*将连接器连接到编码器上*/return 0;
}static const struct drm_bridge_funcs sii902x_bridge_funcs = {.attach = sii902x_bridge_attach,.mode_set = sii902x_bridge_mode_set,.disable = sii902x_bridge_disable,.enable = sii902x_bridge_enable,
};//mute=1,修改"TPI Audio配置寄存器(地址为0x26)"的bit4为1,使能乐器声音为柔和的
//mute=0,修改"TPI Audio配置寄存器(地址为0x26)"的bit4为0,不使能乐器声音为柔和的
static int sii902x_mute(struct sii902x *sii902x, bool mute)
{struct device *dev = &sii902x->i2c->dev;unsigned int val = mute ? SII902X_TPI_AUDIO_MUTE_ENABLE :SII902X_TPI_AUDIO_MUTE_DISABLE;dev_dbg(dev, "%s: %s\n", __func__, mute ? "Muted" : "Unmuted");return regmap_update_bits(sii902x->regmap,SII902X_TPI_AUDIO_CONFIG_BYTE2_REG,SII902X_TPI_AUDIO_MUTE_ENABLE, val);/*mute=1,修改"TPI Audio配置寄存器(地址为0x26)"的bit4为1,使能乐器声音为柔和的*//*mute=0,修改"TPI Audio配置寄存器(地址为0x26)"的bit4为0,不使能乐器声音为柔和的*/
}static const int sii902x_mclk_div_table[] = {128, 256, 384, 512, 768, 1024, 1152, 192 };static int sii902x_select_mclk_div(u8 *i2s_config_reg, unsigned int rate,unsigned int mclk)
{int div = mclk / rate;int distance = 100000;u8 i, nearest = 0;for (i = 0; i < ARRAY_SIZE(sii902x_mclk_div_table); i++) {unsigned int d = abs(div - sii902x_mclk_div_table[i]);if (d >= distance)continue;nearest = i;distance = d;if (d == 0)break;}*i2s_config_reg |= nearest << 4;return sii902x_mclk_div_table[nearest];
}static const struct sii902x_sample_freq {u32 freq;u8 val;
} sii902x_sample_freq[] = {{ .freq = 32000,	.val = SII902X_TPI_AUDIO_FREQ_32KHZ },{ .freq = 44000,	.val = SII902X_TPI_AUDIO_FREQ_44KHZ },{ .freq = 48000,	.val = SII902X_TPI_AUDIO_FREQ_48KHZ },{ .freq = 88000,	.val = SII902X_TPI_AUDIO_FREQ_88KHZ },{ .freq = 96000,	.val = SII902X_TPI_AUDIO_FREQ_96KHZ },{ .freq = 176000,	.val = SII902X_TPI_AUDIO_FREQ_176KHZ },{ .freq = 192000,	.val = SII902X_TPI_AUDIO_FREQ_192KHZ },
};static int sii902x_audio_hw_params(struct device *dev, void *data,struct hdmi_codec_daifmt *daifmt,struct hdmi_codec_params *params)
{struct sii902x *sii902x = dev_get_drvdata(dev);u8 i2s_config_reg = SII902X_TPI_I2S_SD_DIRECTION_MSB_FIRST;u8 config_byte2_reg = (SII902X_TPI_AUDIO_INTERFACE_I2S |SII902X_TPI_AUDIO_MUTE_ENABLE |SII902X_TPI_AUDIO_CODING_PCM);u8 config_byte3_reg = 0;u8 infoframe_buf[HDMI_INFOFRAME_SIZE(AUDIO)];unsigned long mclk_rate;int i, ret;if (daifmt->bit_clk_master || daifmt->frame_clk_master) {dev_dbg(dev, "%s: I2S master mode not supported\n", __func__);return -EINVAL;}switch (daifmt->fmt) {case HDMI_I2S:i2s_config_reg |= SII902X_TPI_I2S_FIRST_BIT_SHIFT_YES |SII902X_TPI_I2S_SD_JUSTIFY_LEFT;break;case HDMI_RIGHT_J:i2s_config_reg |= SII902X_TPI_I2S_SD_JUSTIFY_RIGHT;break;case HDMI_LEFT_J:i2s_config_reg |= SII902X_TPI_I2S_SD_JUSTIFY_LEFT;break;default:dev_dbg(dev, "%s: Unsupported i2s format %u\n", __func__,daifmt->fmt);return -EINVAL;}if (daifmt->bit_clk_inv)i2s_config_reg |= SII902X_TPI_I2S_SCK_EDGE_FALLING;elsei2s_config_reg |= SII902X_TPI_I2S_SCK_EDGE_RISING;if (daifmt->frame_clk_inv)i2s_config_reg |= SII902X_TPI_I2S_WS_POLARITY_LOW;elsei2s_config_reg |= SII902X_TPI_I2S_WS_POLARITY_HIGH;if (params->channels > 2)config_byte2_reg |= SII902X_TPI_AUDIO_LAYOUT_8_CHANNELS;elseconfig_byte2_reg |= SII902X_TPI_AUDIO_LAYOUT_2_CHANNELS;switch (params->sample_width) {case 16:config_byte3_reg |= SII902X_TPI_AUDIO_SAMPLE_SIZE_16;break;case 20:config_byte3_reg |= SII902X_TPI_AUDIO_SAMPLE_SIZE_20;break;case 24:case 32:config_byte3_reg |= SII902X_TPI_AUDIO_SAMPLE_SIZE_24;break;default:dev_err(dev, "%s: Unsupported sample width %u\n", __func__,params->sample_width);return -EINVAL;}for (i = 0; i < ARRAY_SIZE(sii902x_sample_freq); i++) {if (params->sample_rate == sii902x_sample_freq[i].freq) {config_byte3_reg |= sii902x_sample_freq[i].val;break;}}ret = clk_prepare_enable(sii902x->audio.mclk);if (ret) {dev_err(dev, "Enabling mclk failed: %d\n", ret);return ret;}if (sii902x->audio.mclk) {mclk_rate = clk_get_rate(sii902x->audio.mclk);ret = sii902x_select_mclk_div(&i2s_config_reg,params->sample_rate, mclk_rate);if (mclk_rate != ret * params->sample_rate)dev_dbg(dev, "Inaccurate reference clock (%ld/%d != %u)\n",mclk_rate, ret, params->sample_rate);}mutex_lock(&sii902x->mutex);/*上锁*/ret = regmap_write(sii902x->regmap,SII902X_TPI_AUDIO_CONFIG_BYTE2_REG,config_byte2_reg);/*向偏移地址0x26地址写入config_byte2_reg的值*/if (ret < 0)goto out;ret = regmap_write(sii902x->regmap, SII902X_TPI_I2S_INPUT_CONFIG_REG,i2s_config_reg);/*向I2S输入配置寄存器(地址为0x20)写入i2s_config_reg的值*/if (ret)goto out;for (i = 0; i < ARRAY_SIZE(sii902x->audio.i2s_fifo_sequence) &&sii902x->audio.i2s_fifo_sequence[i]; i++)regmap_write(sii902x->regmap,SII902X_TPI_I2S_ENABLE_MAPPING_REG,sii902x->audio.i2s_fifo_sequence[i]);/*向Audio配置寄存器(起始地址为0x1F)写入sii902x->audio.i2s_fifo_sequence[i]的值*/ret = regmap_write(sii902x->regmap, SII902X_TPI_AUDIO_CONFIG_BYTE3_REG,config_byte3_reg);/*向"TPI Audio配置寄存器"(地址为0x27)写入config_byte3_reg的值*//*bit5:3为SF[2:0],Audio采样频率SF[2:0]=000b,Audio采样频率参考流头;SF[2:0]=001b,Audio采样频率为32KHz;SF[2:0]=010b,Audio采样频率为44.1KHz;SF[2:0]=011b,Audio采样频率为48KHz;SF[2:0]=100b,Audio采样频率为88.2KHz;SF[2:0]=101b,Audio采样频率为96KHz;SF[2:0]=110b,Audio采样频率为176.4KHz;SF[2:0]=111b,Audio采样频率为192KHz;bit7:6为SS[1:0],Audio采样位数;SS[1:0]=00,Audio采样位数参考流头;SS[1:0]=01,Audio采样位数为16位;SS[1:0]=10,Audio采样位数为20位;SS[1:0]=1q,Audio采样位数为24位;*/if (ret)goto out;ret = regmap_bulk_write(sii902x->regmap, SII902X_TPI_I2S_STRM_HDR_BASE,params->iec.status,min((size_t) SII902X_TPI_I2S_STRM_HDR_SIZE,sizeof(params->iec.status)));/*向设备写入多个寄存器*//*I2S通道状态寄存器起始地址为0x21*//*待数据的首地址为(params->iec.status)*//*写入的字节数量为( min((size_t) SII902X_TPI_I2S_STRM_HDR_SIZE,sizeof(params->iec.status)) )*/if (ret)goto out;ret = hdmi_audio_infoframe_pack(&params->cea, infoframe_buf,sizeof(infoframe_buf));if (ret < 0) {dev_err(dev, "%s: Failed to pack audio infoframe: %d\n",__func__, ret);goto out;}ret = regmap_bulk_write(sii902x->regmap,SII902X_TPI_MISC_INFOFRAME_BASE,infoframe_buf,min(ret, SII902X_TPI_MISC_INFOFRAME_SIZE));/*向设备写入多个寄存器*//*sii902x"TPI杂项信息帧数据寄存器"起始地址为0xBF*//*待数据的首地址为(infoframe_buf)*//*写入的字节数量为( min(ret, SII902X_TPI_MISC_INFOFRAME_SIZE) )*/if (ret)goto out;/* Decode Level 0 Packets */ret = regmap_write(sii902x->regmap, SII902X_IND_SET_PAGE, 0x02);/*向页寄存器(地址为0xBC)写入0x02,选择内部page1*/if (ret)goto out;ret = regmap_write(sii902x->regmap, SII902X_IND_OFFSET, 0x24);/*向变址寄存器(地址为0xBD)写入0x24*/if (ret)goto out;ret = regmap_write(sii902x->regmap, SII902X_IND_VALUE, 0x02);/*由页寄存器和变址寄存器选中的寄存器,即为当前寄存器(其固定地址为0xBE),即向当前寄存器写入0x02,TPI音频配置*/if (ret)goto out;dev_dbg(dev, "%s: hdmi audio enabled\n", __func__);
out:mutex_unlock(&sii902x->mutex);/*解锁*/if (ret) {clk_disable_unprepare(sii902x->audio.mclk);dev_err(dev, "%s: hdmi audio enable failed: %d\n", __func__,ret);}return ret;
}static void sii902x_audio_shutdown(struct device *dev, void *data)
{struct sii902x *sii902x = dev_get_drvdata(dev);mutex_lock(&sii902x->mutex);/*上锁*/regmap_write(sii902x->regmap, SII902X_TPI_AUDIO_CONFIG_BYTE2_REG,SII902X_TPI_AUDIO_INTERFACE_DISABLE);/*向偏移地址0x26地址写入0x00的值*/mutex_unlock(&sii902x->mutex);/*解锁*/clk_disable_unprepare(sii902x->audio.mclk);
}//函数功能:使音色更柔和
static int sii902x_audio_digital_mute(struct device *dev,void *data, bool enable)
{struct sii902x *sii902x = dev_get_drvdata(dev);mutex_lock(&sii902x->mutex);/*上锁*/sii902x_mute(sii902x, enable);/*mute=1,修改"TPI Audio配置寄存器(地址为0x26)"的bit4为1,使能乐器声音为柔和的*/mutex_unlock(&sii902x->mutex);/*解锁*/return 0;
}static int sii902x_audio_get_eld(struct device *dev, void *data,uint8_t *buf, size_t len)
{struct sii902x *sii902x = dev_get_drvdata(dev);mutex_lock(&sii902x->mutex);/*上锁*/memcpy(buf, sii902x->connector.eld,min(sizeof(sii902x->connector.eld), len));mutex_unlock(&sii902x->mutex);/*解锁*/return 0;
}static int sii902x_audio_get_dai_id(struct snd_soc_component *component,struct device_node *endpoint)
{struct of_endpoint of_ep;int ret;ret = of_graph_parse_endpoint(endpoint, &of_ep);if (ret < 0)return ret;/** HDMI sound should be located at reg = <3>* Return expected DAI index 0.*/if (of_ep.port == SII902X_AUDIO_PORT_INDEX)return 0;return -EINVAL;
}static const struct hdmi_codec_ops sii902x_audio_codec_ops = {.hw_params = sii902x_audio_hw_params,.audio_shutdown = sii902x_audio_shutdown,.digital_mute = sii902x_audio_digital_mute,.get_eld = sii902x_audio_get_eld,.get_dai_id = sii902x_audio_get_dai_id,
};static int sii902x_audio_codec_init(struct sii902x *sii902x,struct device *dev)
{static const u8 audio_fifo_id[] = {SII902X_TPI_I2S_CONFIG_FIFO0,SII902X_TPI_I2S_CONFIG_FIFO1,SII902X_TPI_I2S_CONFIG_FIFO2,SII902X_TPI_I2S_CONFIG_FIFO3,};static const u8 i2s_lane_id[] = {SII902X_TPI_I2S_SELECT_SD0,SII902X_TPI_I2S_SELECT_SD1,SII902X_TPI_I2S_SELECT_SD2,SII902X_TPI_I2S_SELECT_SD3,};struct hdmi_codec_pdata codec_data = {.ops = &sii902x_audio_codec_ops,.i2s = 1, /* Only i2s support for now. */.spdif = 0,.max_i2s_channels = 0,};u8 lanes[4];int num_lanes, i;if (!of_property_read_bool(dev->of_node, "#sound-dai-cells")) {dev_dbg(dev, "%s: No \"#sound-dai-cells\", no audio\n",__func__);return 0;}num_lanes = of_property_read_variable_u8_array(dev->of_node,"sil,i2s-data-lanes",lanes, 1,ARRAY_SIZE(lanes));if (num_lanes == -EINVAL) {dev_dbg(dev,"%s: No \"sil,i2s-data-lanes\", use default <0>\n",__func__);num_lanes = 1;lanes[0] = 0;} else if (num_lanes < 0) {dev_err(dev,"%s: Error gettin \"sil,i2s-data-lanes\": %d\n",__func__, num_lanes);return num_lanes;}codec_data.max_i2s_channels = 2 * num_lanes;for (i = 0; i < num_lanes; i++)sii902x->audio.i2s_fifo_sequence[i] |= audio_fifo_id[i] |i2s_lane_id[lanes[i]] |	SII902X_TPI_I2S_FIFO_ENABLE;sii902x->audio.mclk = devm_clk_get_optional(dev, "mclk");if (IS_ERR(sii902x->audio.mclk)) {dev_err(dev, "%s: No clock (audio mclk) found: %ld\n",__func__, PTR_ERR(sii902x->audio.mclk));return PTR_ERR(sii902x->audio.mclk);}sii902x->audio.pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,&codec_data, sizeof(codec_data));return PTR_ERR_OR_ZERO(sii902x->audio.pdev);
}static const struct regmap_range sii902x_volatile_ranges[] = {{ .range_min = 0, .range_max = 0xff },
};static const struct regmap_access_table sii902x_volatile_table = {.yes_ranges = sii902x_volatile_ranges,.n_yes_ranges = ARRAY_SIZE(sii902x_volatile_ranges),
};static const struct regmap_config sii902x_regmap_config = {.reg_bits = 8,.val_bits = 8,.disable_locking = true, /* struct sii902x mutex should be enough */.max_register = SII902X_TPI_MISC_INFOFRAME_END,.volatile_table = &sii902x_volatile_table,.cache_type = REGCACHE_NONE,
};//函数功能:sii902x中断服务程序
static irqreturn_t sii902x_interrupt(int irq, void *data)
{struct sii902x *sii902x = data;unsigned int status = 0;mutex_lock(&sii902x->mutex);/*上锁*/regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status);/*读寄存器地址为0x3D的值,返回值保存到status中"*/regmap_write(sii902x->regmap, SII902X_INT_STATUS, status);/*向偏移地址0x3D地址写入status的值*/mutex_unlock(&sii902x->mutex);/*解锁*/if ((status & SII902X_HOTPLUG_EVENT) && sii902x->bridge.dev)drm_helper_hpd_irq_event(sii902x->bridge.dev);return IRQ_HANDLED;
}/** The purpose of sii902x_i2c_bypass_select is to enable the pass through* mode of the HDMI transmitter. Do not use regmap from within this function,* only use sii902x_*_unlocked functions to read/modify/write registers.* We are holding the parent adapter lock here, keep this in mind before* adding more i2c transactions.** Also, since SII902X_SYS_CTRL_DATA is used with regmap_update_bits elsewhere* in this driver, we need to make sure that we only touch 0x1A[2:1] from* within sii902x_i2c_bypass_select and sii902x_i2c_bypass_deselect, and that* we leave the remaining bits as we have found them.*/
static int sii902x_i2c_bypass_select(struct i2c_mux_core *mux, u32 chan_id)
{struct sii902x *sii902x = i2c_mux_priv(mux);struct device *dev = &sii902x->i2c->dev;unsigned long timeout;u8 status;int ret;ret = sii902x_update_bits_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,SII902X_SYS_CTRL_DDC_BUS_REQ,SII902X_SYS_CTRL_DDC_BUS_REQ);/*通过I2C,修改地址为0x1A寄存器中的bit2,将bit2置1*//*主机请求使用DDC*/if (ret)return ret;timeout = jiffies + msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);/*修改后的超时时间,打算延时500ms*/do {ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,&status);/*通过I2C读取地址为0x1A寄存器的值,返回值在status中*/if (ret)return ret;} while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) &&time_before(jiffies, timeout));
/*time_before(),如果jiffies<timeout,没有超过预设的系统节拍数,time_before()返回真;*/if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {dev_err(dev, "Failed to acquire the i2c bus\n");return -ETIMEDOUT;}return sii902x_write_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,status);/*通过I2C,将status的值写入地址为Ox1A的寄存器中*/
}/** The purpose of sii902x_i2c_bypass_deselect is to disable the pass through* mode of the HDMI transmitter. Do not use regmap from within this function,* only use sii902x_*_unlocked functions to read/modify/write registers.* We are holding the parent adapter lock here, keep this in mind before* adding more i2c transactions.** Also, since SII902X_SYS_CTRL_DATA is used with regmap_update_bits elsewhere* in this driver, we need to make sure that we only touch 0x1A[2:1] from* within sii902x_i2c_bypass_select and sii902x_i2c_bypass_deselect, and that* we leave the remaining bits as we have found them.*/
static int sii902x_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 chan_id)
{struct sii902x *sii902x = i2c_mux_priv(mux);struct device *dev = &sii902x->i2c->dev;unsigned long timeout;unsigned int retries;u8 status;int ret;/** When the HDMI transmitter is in pass through mode, we need an* (undocumented) additional delay between STOP and START conditions* to guarantee the bus won't get stuck.*/udelay(30);/** Sometimes the I2C bus can stall after failure to use the* EDID channel. Retry a few times to see if things clear* up, else continue anyway.*/retries = 5;do {ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,&status);/*通过I2C读取地址为reg寄存器的值,返回值在status中*/retries--;} while (ret && retries);if (ret) {dev_err(dev, "failed to read status (%d)\n", ret);return ret;}ret = sii902x_update_bits_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,SII902X_SYS_CTRL_DDC_BUS_REQ |SII902X_SYS_CTRL_DDC_BUS_GRTD, 0);/*通过I2C,修改地址为0x1A寄存器中的bit2和bit1,将bit2和bit1置0*//*bit2置0主机不使用DDC,bit1置0表示主机不使用总线*/if (ret)return ret;timeout = jiffies + msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);/*修改后的超时时间,打算延时500ms*/do {ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,&status);/*通过I2C读取地址为0x1A寄存器的值,返回值在status中*/if (ret)return ret;} while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |SII902X_SYS_CTRL_DDC_BUS_GRTD) &&time_before(jiffies, timeout));/*time_before(),如果jiffies<timeout,没有超过预设的系统节拍数,time_before()返回真;*/if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |SII902X_SYS_CTRL_DDC_BUS_GRTD)) {dev_err(dev, "failed to release the i2c bus\n");return -ETIMEDOUT;}return 0;
}static const struct drm_bridge_timings default_sii902x_timings = {.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE| DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE| DRM_BUS_FLAG_DE_HIGH,
};/*
函数功能: i2c驱动的probe函数,当驱动与设备匹配以后,此函数就会执行
参数client : i2c设备
参数id : i2c设备ID
返回值: 0,成功;其他负值,失败
*/
static int sii902x_probe(struct i2c_client *client,const struct i2c_device_id *id)
{struct device *dev = &client->dev;unsigned int status = 0;struct sii902x *sii902x;unsigned char data[2] = { SII902X_CEC_SETUP, 0};struct i2c_msg msg = {.addr	= SII902X_CEC_I2C_ADDR << 1,/*sii902x的I2X地址*/.flags	= 0,/*标记为发送数据*/.len	= 2,/*data[]为2个字节*/.buf	= data,/*待写入的数据*/};u8 chipid[4];int ret;ret = i2c_check_functionality(client->adapter,I2C_FUNC_SMBUS_BYTE_DATA);/*如果适配器支持我们需要的一切,则返回1,否则返回0*/if (!ret) {dev_err(dev, "I2C adapter not suitable\n");return -EIO;}sii902x = devm_kzalloc(dev, sizeof(*sii902x), GFP_KERNEL);/*向内核申请一块内存,当设备驱动程序被卸载时,内存会被自动释放*/if (!sii902x)return -ENOMEM;sii902x->i2c = client;sii902x->regmap = devm_regmap_init_i2c(client, &sii902x_regmap_config);/*创建regmap实例*/if (IS_ERR(sii902x->regmap))return PTR_ERR(sii902x->regmap);sii902x->reset_gpio = devm_gpiod_get_optional(dev, "reset",GPIOD_OUT_LOW);/*用于获取可选的GPIO设备。它是Linux内核中的一个函数,用于在设备树中查找GPIO设备,并返回一个GPI0描述符。如果找不到GPIO设备,则返回NULL。该函数使用devm_前缀,表示它是一个设备管理函数,可以自动释放资源。*/if (IS_ERR(sii902x->reset_gpio)) {dev_err(dev, "Failed to retrieve/request reset gpio: %ld\n",PTR_ERR(sii902x->reset_gpio));return PTR_ERR(sii902x->reset_gpio);}mutex_init(&sii902x->mutex);/*初始化互斥体*/sii902x->supplies[0].supply = "iovcc";sii902x->supplies[1].supply = "cvcc12";ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sii902x->supplies),sii902x->supplies);/*用于在设备驱动程序中获取组相关的稳压器。这个函数使用设备管理器(devm)机制,因此可以确保在设备被释放时自动注销稳压器。这个函数需要传入一个指向设备结构体的指针、一个指向稳压器结构体数组的指针、以及数组中稳压器的数量。函数返回一个整数值,表示成功获取的稳压器数量。如果返回的值与期望值不同,则表示出现了错误。*/if (ret) {if(ret != -EPROBE_DEFER)dev_err(dev, "regulator_bulk_get failed\n");return ret;}ret = regulator_bulk_enable(ARRAY_SIZE(sii902x->supplies),sii902x->supplies);if (ret) {dev_err(dev, "regulator_bulk_enable failed\n");return ret;}sii902x_reset(sii902x);/*sii902复位*/ret = regmap_write(sii902x->regmap, SII902X_REG_TPI_RQB, 0x0);/*硬件复位第1步,向偏移地址0xC7地址写入0x00*/if (ret)goto err_disable_regulator;ret = regmap_bulk_read(sii902x->regmap, SII902X_REG_CHIPID(0),&chipid, 4);/*读芯片的ID和版本;地址范围0x1B~0x1D,保存的是TPI芯片的ID和版本*/if (ret) {dev_err(dev, "regmap_read failed %d\n", ret);goto err_disable_regulator;}if (chipid[0] != 0xb0) {dev_err(dev, "Invalid chipid: %02x (expecting 0xb0)\n",chipid[0]);ret = -EINVAL;goto err_disable_regulator;}/** By default, CEC must be disabled to allow other CEC devives* to bypass the bridge.*/ret = i2c_transfer(client->adapter, &msg, 1);/*发送“sii902x地址“,发送“要写入数据的寄存器首地址“,接着写入“该寄存器的数据“*//*因为只有“一条写消息“,因此消息数量为1*/if (ret < 0)dev_warn(&client->dev, "Failed to disable CEC device!\n");/* Clear all pending interrupts */regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status);/*读寄存器地址为0x3D的值,返回值保存到status中"*/regmap_write(sii902x->regmap, SII902X_INT_STATUS, status);/*向寄存器地址为0x3D中写入"TPI中断状态值"*/if (client->irq > 0) {regmap_update_bits(sii902x->regmap, SII902X_INT_ENABLE,SII902X_HOTPLUG_EVENT,SII902X_HOTPLUG_EVENT);/*修改"中断使能寄存器"的bit0为1,使能中断引脚输出*/ret = devm_request_threaded_irq(dev, client->irq, NULL,sii902x_interrupt,IRQF_ONESHOT, dev_name(dev),sii902x);/*注册中断处理程序,且中断线程化,并具有自动资源管理的功能。*/if (ret)goto err_disable_regulator;}sii902x->bridge.funcs = &sii902x_bridge_funcs;sii902x->bridge.of_node = dev->of_node;sii902x->bridge.timings = &default_sii902x_timings;drm_bridge_add(&sii902x->bridge);sii902x_audio_codec_init(sii902x, dev);i2c_set_clientdata(client, sii902x);/*将sii902x变量的地址绑定client*//*就可以通过i2c_get_clientdata(client)获取sii902x变量指针*/sii902x->i2cmux = i2c_mux_alloc(client->adapter, dev,1, 0, I2C_MUX_GATE,sii902x_i2c_bypass_select,sii902x_i2c_bypass_deselect);if (!sii902x->i2cmux)return -ENOMEM;sii902x->i2cmux->priv = sii902x;ret = i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0);if (ret) {dev_err(dev, "Couldn't add i2c mux adapter\n");return ret;}return 0;err_disable_regulator:regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies),sii902x->supplies);return ret;
}/*sii902x驱动的remove函数 */
static int sii902x_remove(struct i2c_client *client){struct sii902x *sii902x = i2c_get_clientdata(client);/*通过i2c_get_clientdata(client)获取sii902x变量的指针值*/i2c_mux_del_adapters(sii902x->i2cmux);drm_bridge_remove(&sii902x->bridge);regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies),sii902x->supplies);return 0;
}static int sii902x_pm_suspend(struct device *dev)
{struct i2c_client *client = to_i2c_client(dev);struct sii902x *sii902x = i2c_get_clientdata(client);/*通过i2c_get_clientdata(client)获取sii902x变量的指针值*/DRM_DEBUG_DRIVER("\n");if (sii902x->reset_gpio)gpiod_set_value(sii902x->reset_gpio, 1);/*1表示设置引脚输出高电平*/regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies),sii902x->supplies);return 0;
}static int sii902x_pm_resume(struct device *dev)
{struct i2c_client *client = to_i2c_client(dev);struct sii902x *sii902x = i2c_get_clientdata(client);/*通过i2c_get_clientdata(client)获取sii902x变量的指针值*/unsigned char data[2] = { SII902X_CEC_SETUP, 0};struct i2c_msg msg = {.addr	= SII902X_CEC_I2C_ADDR << 1,/*sii902x的I2X地址*/.flags	= 0,/*标记为发送数据*/.len	= 2,/*data[]为2个字节*/.buf	= data,/*待写入的数据*/};int ret;DRM_DEBUG_DRIVER("\n");ret = regulator_bulk_enable(ARRAY_SIZE(sii902x->supplies),sii902x->supplies);if (ret) {DRM_ERROR("regulator_bulk_enable failed\n");return ret;}if (sii902x->reset_gpio)gpiod_set_value(sii902x->reset_gpio, 0);/*0表示设置引脚输出低电平*/regmap_write(sii902x->regmap, SII902X_REG_TPI_RQB, 0x00);/*向偏移地址0xC7地址写入0x00的值*/ret = i2c_transfer(client->adapter, &msg, 1);/*发送“sii902x地址“,发送“要写入数据的寄存器首地址“,接着写入“该寄存器的数据“*//*因为只有“一条写消息“,因此消息数量为1*/if (ret < 0)DRM_ERROR("Failed to disable CEC device!\n");if (client->irq > 0)regmap_update_bits(sii902x->regmap, SII902X_INT_ENABLE,SII902X_HOTPLUG_EVENT,SII902X_HOTPLUG_EVENT);return 0;
}static const struct dev_pm_ops sii902x_pm_ops = {SET_SYSTEM_SLEEP_PM_OPS(sii902x_pm_suspend, sii902x_pm_resume)
};/*设备树匹配表*/
static const struct of_device_id sii902x_dt_ids[] = {{ .compatible = "sil,sii9022", },/*在stm32mp157d-atk.dts设备树文件中,定义“compatible = "sil,sii9022";”*/{/*这是一个空元素,在编写of_device_id时最后一个元素一定要为空*//* sentinel */}
};
MODULE_DEVICE_TABLE(of, sii902x_dt_ids);/*传统匹配方式ID列表*/
static const struct i2c_device_id sii902x_i2c_ids[] = {{ "sii9022", 0 },{ },
};
MODULE_DEVICE_TABLE(i2c, sii902x_i2c_ids);/*初始化i2c_driver结构变量sii902x_driver,i2c驱动结构体 */
static struct i2c_driver sii902x_driver = {.probe = sii902x_probe,/*i2c驱动的probe函数为sii902x_probe()*/.remove = sii902x_remove,/*i2c驱动的remove函数为sii902x_remove()*/.driver = {.name = "sii902x",/* 驱动名字,用于和设备匹配 */.of_match_table = sii902x_dt_ids,/*设备树匹配表*/.pm = &sii902x_pm_ops,},.id_table = sii902x_i2c_ids,/*传统匹配方式ID列表*/
};
module_i2c_driver(sii902x_driver);MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");//添加作者名字
MODULE_DESCRIPTION("SII902x RGB -> HDMI bridges");//模块介绍
MODULE_LICENSE("GPL");//LICENSE采用“GPL协议”

7、测试结果

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/68947.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ANSYS Workbench打开cdb文件

背景&#xff1a; 前面一篇文章已经说过ANSYS Mechanical APDL打开cdb文件-CSDN博客&#xff0c;经典ANSYS界面可以打开HyperMesh中生成的cdb文件&#xff0c;如果是workbench&#xff0c;那么该如何操作&#xff1f; 方法&#xff1a; 首先打开ANSYS的workbench软件&#xf…

计算图 Compute Graph 和自动求导 Autograd | PyTorch 深度学习实战

前一篇文章&#xff0c;Tensor 基本操作5 device 管理&#xff0c;使用 GPU 设备 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started PyTorch 计算图和 Autograd 微积分之于机器学习Computational Graphs 计算图Autograd…

探秘Linux IO虚拟化:virtio的奇幻之旅

在当今数字化时代&#xff0c;虚拟化技术早已成为推动计算机领域发展的重要力量。想象一下&#xff0c;一台物理主机上能同时运行多个相互隔离的虚拟机&#xff0c;每个虚拟机都仿佛拥有自己独立的硬件资源&#xff0c;这一切是如何实现的呢&#xff1f;今天&#xff0c;就让我…

Mac本地部署DeekSeek-R1下载太慢怎么办?

Ubuntu 24 本地安装DeekSeek-R1 在命令行先安装ollama curl -fsSL https://ollama.com/install.sh | sh 下载太慢&#xff0c;使用讯雷&#xff0c;mac版下载链接 https://ollama.com/download/Ollama-darwin.zip 进入网站 deepseek-r1:8b&#xff0c;看内存大小4G就8B模型 …

基于UKF-IMM无迹卡尔曼滤波与交互式多模型的轨迹跟踪算法matlab仿真,对比EKF-IMM和UKF

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于UKF-IMM无迹卡尔曼滤波与交互式多模型的轨迹跟踪算法matlab仿真,对比EKF-IMM和UKF。 2.测试软件版本以及运行结果展示 MATLAB2022A版本运行 3.核心程序 .…

基于脉冲响应不变法的IIR滤波器设计与MATLAB实现

一、设计原理 脉冲响应不变法是一种将模拟滤波器转换为数字滤波器的经典方法。其核心思想是通过对模拟滤波器的冲激响应进行等间隔采样来获得数字滤波器的单位脉冲响应。 设计步骤&#xff1a; 确定数字滤波器性能指标 将数字指标转换为等效的模拟滤波器指标 设计对应的模拟…

Java设计模式:行为型模式→状态模式

Java 状态模式详解 1. 定义 状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在内部状态改变时改变其行为。状态模式通过将状态需要的行为封装在不同的状态类中&#xff0c;实现对象行为的动态改变。该模式的核心思想是分离不同状态…

游戏引擎 Unity - Unity 下载与安装

Unity Unity 首次发布于 2005 年&#xff0c;属于 Unity Technologies Unity 使用的开发技术有&#xff1a;C# Unity 的适用平台&#xff1a;PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域&#xff1a;开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…

Vue指令v-on

目录 一、Vue中的v-on指令是什么&#xff1f;二、v-on指令的简写三、v-on指令的使用 一、Vue中的v-on指令是什么&#xff1f; v-on指令的作用是&#xff1a;为元素绑定事件。 二、v-on指令的简写 “v-on&#xff1a;“指令可以简写为”” 三、v-on指令的使用 1、v-on指令绑…

C++游戏开发实战:从引擎架构到物理碰撞

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 C 是游戏开发中最受欢迎的编程语言之一&#xff0c;因其高性能、低延迟和强大的底层控制能力&#xff0c;被广泛用于游戏…

【贪心算法篇】:“贪心”之旅--算法练习题中的智慧与策略(二)

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;贪心算法篇–CSDN博客 文章目录 前言例题1.买卖股票的最佳时机2.买卖股票的最佳时机23.k次取…

unity学习25:用 transform 进行旋转和移动,简单的太阳地球月亮模型,以及父子级关系

目录 备注内容 1游戏物体的父子级关系 1.1 父子物体 1.2 坐标关系 1.3 父子物体实际是用 每个gameobject的tranform来关联的 2 获取gameObject的静态数据 2.1 具体命令 2.2 具体代码 2.3 输出结果 3 获取gameObject 的方向 3.1 游戏里默认的3个方向 3.2 获取方向代…

基于深度学习的视觉检测小项目(十七) 用户管理后台的编程

完成了用户管理功能的阶段。下一阶段进入AI功能相关。所有的资源见文章链接。 补充完后台代码的用户管理界面代码&#xff1a; import sqlite3from PySide6.QtCore import Slot from PySide6.QtWidgets import QDialog, QMessageBoxfrom . import user_manage # 导入使用ui…

Vue指令v-html

目录 一、Vue中的v-html指令是什么&#xff1f;二、v-html指令与v-text指令的区别&#xff1f; 一、Vue中的v-html指令是什么&#xff1f; v-html指令的作用是&#xff1a;设置元素的innerHTML&#xff0c;内容中有html结构会被解析为标签。 二、v-html指令与v-text指令的区别…

模型蒸馏(ChatGPT文档)

文章来源&#xff1a; https://chatgpt.cadn.net.cn/docs/guides_distillation 模型蒸馏 使用蒸馏技术改进较小的模型。 模型蒸馏允许您利用大型模型的输出来微调较小的模型&#xff0c;使其能够在特定任务上实现类似的性能。此过程可以显著降低成本和延迟&#xff0c;因为较小…

deepseek本地部署+结合思路

deepseek本地部署 配置&#xff1a; 建议配置 运行内存16GB 显卡&#xff1a;4060 操作系统&#xff1a;win11/win10 存储&#xff1a;512GB 一、安装Python 3.11环境&#xff08;参见&#xff09; 超详细的Python安装和环境搭建教程_python安装教程-CSDN博客 二、安装…

加载数据,并切分

# Step 3 . WebBaseLoader 配置为专门从 Lilian Weng 的博客文章中抓取和加载内容。它仅针对网页的相关部分&#xff08;例如帖子内容、标题和标头&#xff09;进行处理。 加载信息 from langchain_community.document_loaders import WebBaseLoader loader WebBaseLoader(w…

解锁豆瓣高清海报(二) 使用 OpenCV 拼接和压缩

解锁豆瓣高清海报(二): 使用 OpenCV 拼接和压缩 脚本地址: 项目地址: Gazer PixelWeaver.py pixel_squeezer_cv2.py 前瞻 继上一篇“解锁豆瓣高清海报(一) 深度爬虫与requests进阶之路”成功爬取豆瓣电影海报之后&#xff0c;本文将介绍如何使用 OpenCV 对这些海报进行智…

OSCP - Proving Grounds - Roquefort

主要知识点 githook 注入Linux path覆盖 具体步骤 依旧是nmap扫描开始&#xff0c;3000端口不是很熟悉&#xff0c;先看一下 Nmap scan report for 192.168.54.67 Host is up (0.00083s latency). Not shown: 65530 filtered tcp ports (no-response) PORT STATE SERV…

最新功能发布!AllData数据中台核心菜单汇总

🔥🔥 AllData大数据产品是可定义数据中台,以数据平台为底座,以数据中台为桥梁,以机器学习平台为中层框架,以大模型应用为上游产品,提供全链路数字化解决方案。 ✨奥零数据科技官网:http://www.aolingdata.com ✨AllData开源项目:https://github.com/alldatacenter/…