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

news2025/2/4 17:42:26

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			0x8e

struct 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;
	else
		sii902x->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;
	else
		i2s_config_reg |= SII902X_TPI_I2S_SCK_EDGE_RISING;

	if (daifmt->frame_clk_inv)
		i2s_config_reg |= SII902X_TPI_I2S_WS_POLARITY_LOW;
	else
		i2s_config_reg |= SII902X_TPI_I2S_WS_POLARITY_HIGH;

	if (params->channels > 2)
		config_byte2_reg |= SII902X_TPI_AUDIO_LAYOUT_8_CHANNELS;
	else
		config_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.coloradmin.cn/o/2291908.html

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

相关文章

测试工程师的DS使用指南

目录 引言DeepSeek在测试设计中的应用 2.1 智能用例生成2.2 边界值分析2.3 异常场景设计DeepSeek在自动化测试中的应用 3.1 脚本智能转换3.2 日志智能分析3.3 测试数据生成DeepSeek在质量保障体系中的应用 4.1 测试策略优化4.2 缺陷模式预测4.3 技术方案验证DeepSeek在测试效能…

Qt常用控件 输入类控件

文章目录 1.QLineEdit1.1 常用属性1.2 常用信号1.3 例子1&#xff0c;录入用户信息1.4 例子2&#xff0c;正则验证手机号1.5 例子3&#xff0c;验证输入的密码1.6 例子4&#xff0c;显示密码 2. QTextEdit2.1 常用属性2.2 常用信号2.3 例子1&#xff0c;获取输入框的内容2.4 例…

linux运行级别

运行级别&#xff1a;指linux系统在启动和运行过程中所处的不同的状态。 运行级别之间的切换&#xff1a;init (级别数) 示例&#xff1a; linux的运行级别一共有7种&#xff0c;分别是&#xff1a; 运行级别0&#xff1a;停机状态 运行级别1&#xff1a;单用户模式/救援模式…

数据结构课程设计(四)校园导航

4 校园导航 4.1 需求规格说明 【问题描述】 一个学校平面图&#xff0c;至少包括10个以上的场所&#xff0c;每个场所带有编号、坐标、名称、类别等信息&#xff0c;两个场所间可以有路径相通&#xff0c;路长&#xff08;耗时&#xff09;各有不同。要求读取该校园平面图&a…

嵌入式知识点总结 操作系统 专题提升(四)-上下文

针对于嵌入式软件杂乱的知识点总结起来&#xff0c;提供给读者学习复习对下述内容的强化。 目录 1.上下文有哪些?怎么理解? 2.为什么会有上下文这种概念? 3.什么情况下进行用户态到内核态的切换? 4.中断上下文代码中有哪些注意事项&#xff1f; 5.请问线程需要保存哪些…

Elasticsearch基本使用详解

文章目录 Elasticsearch基本使用详解一、引言二、环境搭建1、安装 Elasticsearch2、安装 Kibana&#xff08;可选&#xff09; 三、索引操作1、创建索引2、查看索引3、删除索引 四、数据操作1、插入数据2、查询数据&#xff08;1&#xff09;简单查询&#xff08;2&#xff09;…

xxl-job 在 Java 项目的使用 以一个代驾项目中的订单模块举例

能搜到这里的最起码一定知道 xxl-job 是用来干什么的&#xff0c;我就不多啰嗦怎么下载以及它的历史了 首先我们要知道 xxl-job 这个框架的结构&#xff0c;如下图&#xff1a; xxl-job-master&#xff1a;xxl-job-admin&#xff1a;调度中心xxl-job-core&#xff1a;公共依赖…

Alibaba开发规范_异常日志之日志规约:最佳实践与常见陷阱

文章目录 引言1. 使用SLF4J日志门面规则解释代码示例正例反例 2. 日志文件的保存时间规则解释 3. 日志文件的命名规范规则解释代码示例正例反例 4. 使用占位符进行日志拼接规则解释代码示例正例反例 5. 日志级别的开关判断规则解释代码示例正例反例 6. 避免重复打印日志规则解释…

SQLAlchemy 2.0的简单使用教程

SQLAlchemy 2.0相比1.x进行了很大的更新&#xff0c;目前网上的教程不多&#xff0c;以下以链接mysql为例介绍一下基本的使用方法 环境及依赖 Python:3.8 mysql:8.3 Flask:3.0.3 SQLAlchemy:2.0.37 PyMySQL:1.1.1使用步骤 1、创建引擎&#xff0c;链接到mysql engine crea…

OpenGL学习笔记(七):Camera 摄像机(视图变换、LookAt矩阵、Camera类的实现)

文章目录 摄像机/观察空间/视图变换LookAt矩阵移动相机&#xff08;处理键盘输入&#xff09;移动速度欧拉角移动视角&#xff08;处理鼠标输入&#xff09;缩放场景&#xff08;处理滚轮输入&#xff09;Camera类 摄像机/观察空间/视图变换 在上一节变换中&#xff0c;我们讨…

『VUE』vue-quill-editor富文本编辑器添加按钮houver提示(详细图文注释)

目录 预览效果新建一个config.js存放标题编写添加提示的方法调用添加标题方法的生命周期总结 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 预览效果 新建一个config.js存放标题 export const titleConfig [{ Choice: .ql-bold…

如何使用 DeepSeek 和 Dexscreener 构建免费的 AI 加密交易机器人?

我使用DeepSeek AI和Dexscreener API构建的一个简单的 AI 加密交易机器人实现了这一目标。在本文中&#xff0c;我将逐步指导您如何构建像我一样的机器人。 DeepSeek 最近发布了R1&#xff0c;这是一种先进的 AI 模型。您可以将其视为 ChatGPT 的免费开源版本&#xff0c;但增加…

微信登录模块封装

文章目录 1.资质申请2.combinations-wx-login-starter1.目录结构2.pom.xml 引入okhttp依赖3.WxLoginProperties.java 属性配置4.WxLoginUtil.java 后端通过 code 获取 access_token的工具类5.WxLoginAutoConfiguration.java 自动配置类6.spring.factories 激活自动配置类 3.com…

SRS代码目录

代码目录&#xff1a; src/目录下核心代码&#xff1a; core&#xff1a;核心功能模块&#xff0c;包括日志、配置、错误处理等&#xff1b;protocol&#xff1a;实现RTMP、HTTP-FLV、HLS等协议的模块&#xff1b;app&#xff1a;应用层的实现&#xff0c;包括流的发布、播放…

机器学习--1.KNN机器学习入门

1、机器学习概述 1.1、什么是机器学习 机器学习&#xff08;Machine Learning&#xff09;是人工智能&#xff08;Artificial Intelligence&#xff09;领域的一个子集&#xff0c;它主要关注如何让计算机系统通过经验学习&#xff08;数据&#xff09;并自动改进性能。机器学…

Adaptive LLM Transformer²

看到了一个不错的论文https://arxiv.org/pdf/2501.06252 TRANSFORMER-SQUARED: SELF-ADAPTIVE LLMS 挺有意思的&#xff0c;是一家日本AI公司SakanaAI的论文&#xff08;我以前写过他们的不训练提升模型的能力的文章&#xff0c;感兴趣可以去翻&#xff09;它家有Lion Jones坐镇…

基于LabVIEW的Modbus-RTU设备通信失败问题分析与解决

在使用 LabVIEW 通过 Modbus-RTU 协议与工业设备进行通信时&#xff0c;可能遇到无法正常发送或接收指令的问题。常见原因包括协议参数配置错误、硬件连接问题、数据帧格式不正确等。本文以某 RGBW 控制器调光失败为例&#xff0c;提出了一种通用的排查思路&#xff0c;帮助开发…

直方图:摄影中的视觉数据指南

目录 一、直方图基础&#xff1a;揭开它的神秘面纱 二、解读直方图类型&#xff1a;亮度与色彩的密码 &#xff08;一&#xff09;亮度直方图 &#xff08;二&#xff09;RGB 直方图 三、拍摄中巧用直方图&#xff1a;优化曝光与效果 &#xff08;一&#xff09;精准判断曝…

IM 即时通讯系统-51-MPush开源实时消息推送系统

IM 开源系列 IM 即时通讯系统-41-开源 野火IM 专注于即时通讯实时音视频技术&#xff0c;提供优质可控的IMRTC能力 IM 即时通讯系统-42-基于netty实现的IM服务端,提供客户端jar包,可集成自己的登录系统 IM 即时通讯系统-43-简单的仿QQ聊天安卓APP IM 即时通讯系统-44-仿QQ即…

【Linux】从硬件到软件了解进程

个人主页~ 从硬件到软件了解进程 一、冯诺依曼体系结构二、操作系统三、操作系统进程管理1、概念2、PCB和task_struct3、查看进程4、通过系统调用fork创建进程&#xff08;1&#xff09;简述&#xff08;2&#xff09;系统调用生成子进程的过程〇提出问题①fork函数②父子进程关…