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,PH6和PA3是否被使用
打开设备树头文件“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(¶ms->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、测试结果
略
相关文章:
Linux第105步_基于SiI9022A芯片的RGB转HDMI实验
SiI9022A是一款HDMI传输芯片,可以将“音视频接口”转换为HDMI或者DVI格式,是一个视频转换芯片。本实验基于linux的驱动程序设计。 SiI9022A支持输入视频格式有:xvYCC、BTA-T1004、ITU-R.656,内置DE发生器,支持SYNC格式…...
测试工程师的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,录入用户信息1.4 例子2,正则验证手机号1.5 例子3,验证输入的密码1.6 例子4,显示密码 2. QTextEdit2.1 常用属性2.2 常用信号2.3 例子1,获取输入框的内容2.4 例…...
linux运行级别
运行级别:指linux系统在启动和运行过程中所处的不同的状态。 运行级别之间的切换:init (级别数) 示例: linux的运行级别一共有7种,分别是: 运行级别0:停机状态 运行级别1:单用户模式/救援模式…...
数据结构课程设计(四)校园导航
4 校园导航 4.1 需求规格说明 【问题描述】 一个学校平面图,至少包括10个以上的场所,每个场所带有编号、坐标、名称、类别等信息,两个场所间可以有路径相通,路长(耗时)各有不同。要求读取该校园平面图&a…...
50【Windows与Linux】
大家可能有些人听过Linux系统,很多初学者被灌输的理念就是服务器必须要装Linux系统 什么是系统? 比如你的电脑是Windows系统的,你手机是Android系统的,物理设备都需要系统,系统你可以理解成直接控制物理设备的一个东…...
蓝桥杯python基础算法(2-2)——基础算法(D)——进制转换*
目录 五、进制转换 十进制转任意进制,任意进制转十进制 例题 P1230 进制转换 作业 P2095 进制转化 作业 P2489 进制 五、进制转换 十进制转任意进制,任意进制转十进制 int_to_char "0123456789ABCDEF" def Ten_to_K(k, x):answer "…...
CSS 溢出内容处理:从基础到实战
CSS 溢出内容处理:从基础到实战 1. 什么是溢出?示例代码:默认溢出行为 2. 使用 overflow 属性控制溢出2.1 使用 overflow: hidden 裁剪内容示例代码:裁剪溢出内容 2.2 使用 overflow: scroll 显示滚动条示例代码:显示滚…...
嵌入式知识点总结 操作系统 专题提升(四)-上下文
针对于嵌入式软件杂乱的知识点总结起来,提供给读者学习复习对下述内容的强化。 目录 1.上下文有哪些?怎么理解? 2.为什么会有上下文这种概念? 3.什么情况下进行用户态到内核态的切换? 4.中断上下文代码中有哪些注意事项? 5.请问线程需要保存哪些…...
Elasticsearch基本使用详解
文章目录 Elasticsearch基本使用详解一、引言二、环境搭建1、安装 Elasticsearch2、安装 Kibana(可选) 三、索引操作1、创建索引2、查看索引3、删除索引 四、数据操作1、插入数据2、查询数据(1)简单查询(2)…...
携程Android开发面试题及参考答案
在项目中,给别人发的动态点赞功能是如何实现的? 数据库设计:首先要在数据库中为动态表添加一个点赞字段,用于记录点赞数量,同时可能需要一个点赞关系表,记录用户与动态之间的点赞关联,包括点赞时间等信息。界面交互:在 Android 界面上,为点赞按钮设置点击事件监听器。…...
xxl-job 在 Java 项目的使用 以一个代驾项目中的订单模块举例
能搜到这里的最起码一定知道 xxl-job 是用来干什么的,我就不多啰嗦怎么下载以及它的历史了 首先我们要知道 xxl-job 这个框架的结构,如下图: xxl-job-master:xxl-job-admin:调度中心xxl-job-core:公共依赖…...
Alibaba开发规范_异常日志之日志规约:最佳实践与常见陷阱
文章目录 引言1. 使用SLF4J日志门面规则解释代码示例正例反例 2. 日志文件的保存时间规则解释 3. 日志文件的命名规范规则解释代码示例正例反例 4. 使用占位符进行日志拼接规则解释代码示例正例反例 5. 日志级别的开关判断规则解释代码示例正例反例 6. 避免重复打印日志规则解释…...
【数据分析】案例03:当当网近30日热销图书的数据采集与可视化分析(scrapy+openpyxl+matplotlib)
当当网近30日热销图书的数据采集与可视化分析(scrapy+openpyxl+matplotlib) 当当网近30日热销书籍官网写在前面 实验目的:实现当当网近30日热销图书的数据采集与可视化分析。 电脑系统:Windows 使用软件:Visual Studio Code Python版本:python 3.12.4 技术需求:scrapy、…...
需求分析应该从哪些方面来着手做?
需求分析一般可从以下几个方面着手: 业务需求方面 - 与相关方沟通:与业务部门、客户等进行深入交流,通过访谈、问卷调查、会议讨论等方式,明确他们对项目的期望、目标和整体业务需求,了解项目要解决的业务问题及达成的…...
申博经验贴
1. 所谓申博,最重要的就是定制的海投 分成两个部分 1. 定制 要根据每个教授去写不同的,一定不要泛泛的去写,一定要非常非常的具体,要引起教授的兴趣。每个教授每天都会收到几十封邮件,所以要足够的引起教授的注意&a…...
SpringAI 人工智能
随着 AI 技术的不断发展,越来越多的企业开始将 AI 模型集成到其业务系统中,从而提升系统的智能化水平、自动化程度和用户体验。在此背景下,Spring AI 作为一个企业级 AI 框架,提供了丰富的工具和机制,可以帮助开发者将…...
虚幻基础17:动画层接口
能帮到你的话,就给个赞吧 😘 文章目录 animation layer interface animation layer interface 动画层接口:动画图表的集。仅有名字。 添加到动画蓝图中,由动画蓝图实现动画图表。...
SQLAlchemy 2.0的简单使用教程
SQLAlchemy 2.0相比1.x进行了很大的更新,目前网上的教程不多,以下以链接mysql为例介绍一下基本的使用方法 环境及依赖 Python:3.8 mysql:8.3 Flask:3.0.3 SQLAlchemy:2.0.37 PyMySQL:1.1.1使用步骤 1、创建引擎,链接到mysql engine crea…...
Codeforces Round 1002 (Div. 2) A-D
复活!年后首场!本期封面是我自己AI弄的图 A - Milya and Two Arrays 题意 给两个所有数字出现次数都大于2的数组,问能不能修改排序之后对应位置相加得到新的数组使不同数字个数达到3 思路 直接计数就行了,不同的数字匹配一下…...
OpenGL学习笔记(七):Camera 摄像机(视图变换、LookAt矩阵、Camera类的实现)
文章目录 摄像机/观察空间/视图变换LookAt矩阵移动相机(处理键盘输入)移动速度欧拉角移动视角(处理鼠标输入)缩放场景(处理滚轮输入)Camera类 摄像机/观察空间/视图变换 在上一节变换中,我们讨…...
『VUE』vue-quill-editor富文本编辑器添加按钮houver提示(详细图文注释)
目录 预览效果新建一个config.js存放标题编写添加提示的方法调用添加标题方法的生命周期总结 欢迎关注 『VUE』 专栏,持续更新中 欢迎关注 『VUE』 专栏,持续更新中 预览效果 新建一个config.js存放标题 export const titleConfig [{ Choice: .ql-bold…...
如何使用 DeepSeek 和 Dexscreener 构建免费的 AI 加密交易机器人?
我使用DeepSeek AI和Dexscreener API构建的一个简单的 AI 加密交易机器人实现了这一目标。在本文中,我将逐步指导您如何构建像我一样的机器人。 DeepSeek 最近发布了R1,这是一种先进的 AI 模型。您可以将其视为 ChatGPT 的免费开源版本,但增加…...
Kafka流式计算架构
引言 Kafka 凭借其卓越的架构设计,具备极为高效的流式计算能力,在海量数据环境下,依然能够以惊人的速度实现消息的高性能消费,轻松应对高并发、低延迟的严苛业务需求。无论是实时数据处理、复杂事件分析,还是大规模数…...
C++泛型编程06(默认模板实参)
文章目录 1.4 默认模板实参 (Default Template Arguments)示例:灵活定义返回类型 当然,这里是对关于默认模板实参(Default Template Arguments)的内容进行了改进和优化后的叙述: 1.4 默认模板实参 (Default Template Arguments) 在C中&…...
微信登录模块封装
文章目录 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代码目录
代码目录: src/目录下核心代码: core:核心功能模块,包括日志、配置、错误处理等;protocol:实现RTMP、HTTP-FLV、HLS等协议的模块;app:应用层的实现,包括流的发布、播放…...
C++STL(一)——string类
目录 一、string的定义方式二、 string类对象的容量操作三、string类对象的访问及遍历操作四、string类对象的修改操作五、string类非成员函数 一、string的定义方式 string是个管理字符数组的类,其实就是字符数组的顺序表。 它的接口也是非常多的。本章介绍一些常…...
机器学习--1.KNN机器学习入门
1、机器学习概述 1.1、什么是机器学习 机器学习(Machine Learning)是人工智能(Artificial Intelligence)领域的一个子集,它主要关注如何让计算机系统通过经验学习(数据)并自动改进性能。机器学…...
Adaptive LLM Transformer²
看到了一个不错的论文https://arxiv.org/pdf/2501.06252 TRANSFORMER-SQUARED: SELF-ADAPTIVE LLMS 挺有意思的,是一家日本AI公司SakanaAI的论文(我以前写过他们的不训练提升模型的能力的文章,感兴趣可以去翻)它家有Lion Jones坐镇…...
