Linux内核-HDMI驱动移植
实验原理
STM32MP157A系列SoC中默认没有HDMI相关控制器,FS-MP1A使用SiI9022芯片将RGB信号转化为HDMI信号。STM32MP157A集成LTDC(LCD-TFT Display Controller),提供一个24bit RGB并行接口用于连接到各种LCD和TFT面板。
SiI9022芯片通过I2C5总线与SoC进行交互,通过SoC的LCD_PCLK、LCD_VSYNC、LCD_HSYNC、LCD_DEN与RGB信号线来进行图像信号的传输,通过I2S2总线进行音频数据的传输。
查看原理图确认I2C5、中断、复位管脚对应关系:
原理图网络编号 | 对应管脚 | 管脚功能 | 管脚功能码 |
---|---|---|---|
I2C5_SCL | PA11 | I2C5_SCL | AF4 |
I2C5_SDA | PA12 | I2C5_SDA | AF4 |
HDMI_RST | PA13 | GPIO | |
HDMI_INT | PA14 | INT |
LCD接口管脚对应关系:
原理图网络编号 | 对应管脚 | 管脚功能 | 管脚功能码 |
---|---|---|---|
LCD_R0 | PI15 | LCD_R0 | AF14 |
LCD_R1 | PJ0 | LCD_R1 | AF14 |
LCD_R2 | PJ1 | LCD_R2 | AF14 |
LCD_R3 | PJ2 | LCD_R3 | AF14 |
LCD_R4 | PJ3 | LCD_R4 | AF14 |
LCD_R5 | PJ4 | LCD_R5 | AF14 |
LCD_R6 | PJ5 | LCD_R6 | AF14 |
LCD_R7 | PJ6 | LCD_R7 | AF14 |
LCD_G0 | PJ8 | LCD_G0 | AF14 |
LCD_G1 | PJ7 | LCD_G1 | AF14 |
LCD_G2 | PJ10 | LCD_G2 | AF14 |
LCD_G3 | PJ19 | LCD_G3 | AF14 |
LCD_G4 | PJ11 | LCD_G4 | AF14 |
LCD_G5 | PK0 | LCD_G5 | AF14 |
LCD_G6 | PK1 | LCD_G6 | AF14 |
LCD_G7 | PK2 | LCD_G7 | AF14 |
LCD_B0 | PJ12 | LCD_B0 | AF14 |
LCD_B1 | PJ13 | LCD_B1 | AF14 |
LCD_B2 | PJ14 | LCD_B2 | AF14 |
LCD_B3 | PJ15 | LCD_B3 | AF14 |
LCD_B4 | PK3 | LCD_B4 | AF14 |
LCD_B5 | PK4 | LCD_B5 | AF14 |
LCD_B6 | PK5 | LCD_B6 | AF14 |
LCD_B7 | PK6 | LCD_B7 | AF14 |
LCD_PCLK | PI14 | LCD_PCLK | AF14 |
LCD_DEN | PK7 | LCD_DEN | AF14 |
LCD_HSYNC | PI12 | LCD_HSYNC | AF14 |
LCD_VSYNC | PI13 | LCD_VSYNC | AF14 |
- I2C5设备树节点
参考文档:
Documentation/devicetree/bindings/i2c/i2c-stm32.txt
内核中ST对STM32MP15x系列芯片的设备树资源了做了定义,可参见:
arch/arm/boot/dts/stm32mp151.dtsi
stm32mp151中i2c5定义如下:
i2c5: i2c@40015000 { compatible = "st,stm32mp15-i2c"; reg = <0x40015000 0x400>; interrupt-names = "event", "error"; interrupts-extended = <&exti 25 IRQ_TYPE_LEVEL_HIGH>, <&intc GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc I2C5_K>; resets = <&rcc I2C5_R>; #address-cells = <1>; #size-cells = <0>; dmas = <&dmamux1 115 0x400 0x80000001>, <&dmamux1 116 0x400 0x80000001>; dma-names = "rx", "tx"; power-domains = <&pd_core>; st,syscfg-fmp = <&syscfg 0x4 0x10>; wakeup-source; status = "disabled"; };
上述代码只对i2c5做了基本的初始化,并没有针对不同的硬件设计做适配,所以需结合硬件补全设备树节点信息。
参考stm32mp15xx-dkx.dtsi对于i2c设备节点的描述,修改i2c5内容如下:
&i2c5 { pinctrl-names = "default", "sleep"; pinctrl-0 = <&i2c5_pins_a>; pinctrl-1 = <&i2c5_pins_sleep_a>; i2c-scl-rising-time-ns = <100>; i2c-scl-falling-time-ns = <7>; clock-frequency = <100000>; /* spare dmas for other usage */ /delete-property/dmas; /delete-property/dma-names; status = "okay"; };
由于stm32mp15-pinctrl.dtsi中对于i2c5_pins_a和i2c5_pins_sleep_a的定义与板子实际使用管脚一致,所以无需修改,内容如下:
i2c5_pins_a: i2c5-0 { pins { pinmux = <STM32_PINMUX('A', 11, AF4)>, /* I2C5_SCL */ <STM32_PINMUX('A', 12, AF4)>; /* I2C5_SDA */ bias-disable; drive-open-drain; slew-rate = <0>; }; }; i2c5_pins_sleep_a: i2c5-1 { pins { pinmux = <STM32_PINMUX('A', 11, ANALOG)>, /* I2C5_SCL */ <STM32_PINMUX('A', 12, ANALOG)>; /* I2C5_SDA */ }; };
- I2S2设备树节点
参考文档:
Documentation/devicetree/bindings/i2c/sound/st,stm32-i2s.txt
- LTDC设备树节点
SiI9022实现HDMI输出需要RGB信号作为数据源,LTDC为STM32MP157的LCD显示控制器,可以输出24bit的并行数据,HDMI显示首先需要驱动LTDC。
参考文档:
Documentation/devicetree/bindings/display/st,stm32-ltdc.txt
内核中ST对STM32MP15x系列芯片的设备树资源了做了定义,可参见:
arch/arm/boot/dts/stm32mp151.dtsi
stm32mp151中ltdc定义如下:
ltdc: display-controller@5a001000 { compatible = "st,stm32-ltdc"; reg = <0x5a001000 0x400>; interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc LTDC_PX>; clock-names = "lcd"; resets = <&rcc LTDC_R>; status = "disabled"; };
上述代码只对ltdc做了基本的初始化,并没有针对不同的硬件设计做适配,所以需结合硬件补全设备树节点信息。
参考stm32mp15xx-dkx.dtsi对于ltdc设备节点的描述,需增加内容如下:
<dc { pinctrl-names = "default", "sleep"; pinctrl-0 = <<dc_pins_b>; pinctrl-1 = <<dc_pins_sleep_b>; status = "okay"; port { #address-cells = <1>; #size-cells = <0>; ltdc_ep0_out: endpoint@0 { reg = <0>; /*图像输出通道,需对接显示设备*/ remote-endpoint = <&sii9022_in>; }; }; };
由于stm32mp15-pinctrl.dtsi中对于ltdc_pins_b和ltdc_pins_b的定于与板子实际使用管脚一致,所以无需修改,内容如下:
ltdc_pins_b: ltdc-b-0 { pins { pinmux = <STM32_PINMUX('I', 14, AF14)>, /* LCD_CLK */ <STM32_PINMUX('I', 12, AF14)>, /* LCD_HSYNC */ <STM32_PINMUX('I', 13, AF14)>, /* LCD_VSYNC */ <STM32_PINMUX('K', 7, AF14)>, /* LCD_DE */ <STM32_PINMUX('I', 15, AF14)>, /* LCD_R0 */ <STM32_PINMUX('J', 0, AF14)>, /* LCD_R1 */ <STM32_PINMUX('J', 1, AF14)>, /* LCD_R2 */ <STM32_PINMUX('J', 2, AF14)>, /* LCD_R3 */ <STM32_PINMUX('J', 3, AF14)>, /* LCD_R4 */ <STM32_PINMUX('J', 4, AF14)>, /* LCD_R5 */ <STM32_PINMUX('J', 5, AF14)>, /* LCD_R6 */ <STM32_PINMUX('J', 6, AF14)>, /* LCD_R7 */ <STM32_PINMUX('J', 7, AF14)>, /* LCD_G0 */ <STM32_PINMUX('J', 8, AF14)>, /* LCD_G1 */ <STM32_PINMUX('J', 9, AF14)>, /* LCD_G2 */ <STM32_PINMUX('J', 10, AF14)>, /* LCD_G3 */ <STM32_PINMUX('J', 11, AF14)>, /* LCD_G4 */ <STM32_PINMUX('K', 0, AF14)>, /* LCD_G5 */ <STM32_PINMUX('K', 1, AF14)>, /* LCD_G6 */ <STM32_PINMUX('K', 2, AF14)>, /* LCD_G7 */ <STM32_PINMUX('J', 12, AF14)>, /* LCD_B0 */ <STM32_PINMUX('J', 13, AF14)>, /* LCD_B1 */ <STM32_PINMUX('J', 14, AF14)>, /* LCD_B2 */ <STM32_PINMUX('J', 15, AF14)>, /* LCD_B3 */ <STM32_PINMUX('K', 3, AF14)>, /* LCD_B4 */ <STM32_PINMUX('K', 4, AF14)>, /* LCD_B5 */ <STM32_PINMUX('K', 5, AF14)>, /* LCD_B6 */ <STM32_PINMUX('K', 6, AF14)>; /* LCD_B7 */ bias-disable; drive-push-pull; slew-rate = <1>; }; }; ltdc_pins_sleep_b: ltdc-b-1 { pins { pinmux = <STM32_PINMUX('I', 14, ANALOG)>, /* LCD_CLK */ <STM32_PINMUX('I', 12, ANALOG)>, /* LCD_HSYNC */ <STM32_PINMUX('I', 13, ANALOG)>, /* LCD_VSYNC */ <STM32_PINMUX('K', 7, ANALOG)>, /* LCD_DE */ <STM32_PINMUX('I', 15, ANALOG)>, /* LCD_R0 */ <STM32_PINMUX('J', 0, ANALOG)>, /* LCD_R1 */ <STM32_PINMUX('J', 1, ANALOG)>, /* LCD_R2 */ <STM32_PINMUX('J', 2, ANALOG)>, /* LCD_R3 */ <STM32_PINMUX('J', 3, ANALOG)>, /* LCD_R4 */ <STM32_PINMUX('J', 4, ANALOG)>, /* LCD_R5 */ <STM32_PINMUX('J', 5, ANALOG)>, /* LCD_R6 */ <STM32_PINMUX('J', 6, ANALOG)>, /* LCD_R7 */ <STM32_PINMUX('J', 7, ANALOG)>, /* LCD_G0 */ <STM32_PINMUX('J', 8, ANALOG)>, /* LCD_G1 */ <STM32_PINMUX('J', 9, ANALOG)>, /* LCD_G2 */ <STM32_PINMUX('J', 10, ANALOG)>, /* LCD_G3 */ <STM32_PINMUX('J', 11, ANALOG)>, /* LCD_G4 */ <STM32_PINMUX('K', 0, ANALOG)>, /* LCD_G5 */ <STM32_PINMUX('K', 1, ANALOG)>, /* LCD_G6 */ <STM32_PINMUX('K', 2, ANALOG)>, /* LCD_G7 */ <STM32_PINMUX('J', 12, ANALOG)>, /* LCD_B0 */ <STM32_PINMUX('J', 13, ANALOG)>, /* LCD_B1 */ <STM32_PINMUX('J', 14, ANALOG)>, /* LCD_B2 */ <STM32_PINMUX('J', 15, ANALOG)>, /* LCD_B3 */ <STM32_PINMUX('K', 3, ANALOG)>, /* LCD_B4 */ <STM32_PINMUX('K', 4, ANALOG)>, /* LCD_B5 */ <STM32_PINMUX('K', 5, ANALOG)>, /* LCD_B6 */ <STM32_PINMUX('K', 6, ANALOG)>; /* LCD_B7 */ }; };
- SiI9022设备树节点
参考文档:
Documentation/devicetree/bindings/display/arm,hdlcd.txt
由于SiI9002只是I2C5总线上的一个外设,所以STM32MP157A的通用设备树文件中并没有相关的定义,结合arm,hdlcd.txt和stm32mp15xx-dkx.dtsi对于HDMI的描述,增加SiI9002的支持,需在I2C5节点下添加相关信息,添加内容为:
hdmi-transmitter@39 { compatible = "sil,sii9022"; reg = <0x39>; iovcc-supply = <&v3v3_hdmi>; cvcc12-supply = <&v1v2_hdmi>; reset-gpios = <&gpioa 10 GPIO_ACTIVE_LOW>; interrupts = <1 IRQ_TYPE_EDGE_FALLING>; interrupt-parent = <&gpiog>; #sound-dai-cells = <0>; status = "okay"; ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; sii9022_in: endpoint { /*视频输入端口,与LTDC输出端口对接*/ remote-endpoint = <<dc_ep0_out>; }; }; port@3 { reg = <3>; sii9022_tx_endpoint: endpoint { /*音频输入端口,与声卡输出端口对接*/ remote-endpoint = <&i2s2_endpoint>; }; }; }; };
- 电源节点添加
由于内核中很多驱动会根据电源的方位调整设备的工作方式,所以在设备树中需要传递相关电源电压参数,如sii9022驱动中需要两个电源分别是iovcc和cvcc12,但是在设备树中并没有这两个电源的定义,官方参考板DK1使用的是MPU作为电源管理,而FS-MP1A使用的分离元器件的形式,所以stm32mp15xx-dkx.dtsi中对于电源的定义就不实用了。参考内核中相关文档添加固定电源节点的形式添加iovcc和cvcc12即可,根据sii9022的需求,iovcc和cvcc12电压分别为3.3v和1.2v。
参考文档:
Documentation/devicetree/bindings/regulator/fixed-regulator.yaml
需在设备树根节点下添加,内容如下:
v3v3_hdmi: regulator-v3v3-hdmi { compatible = "regulator-fixed"; regulator-name = "v3v3_hdmi "; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; regulator-always-on; regulator-boot-on; }; v1v2_hdmi: regulator-v1v2-hdmi { compatible = "regulator-fixed"; regulator-name = "v1v2_hdmi"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; regulator-always-on; regulator-boot-on; };
实验目的
熟悉基于Linux操作系统下的HDMI设备驱动移植配置过程。
实验平台
华清远见开发环境,FS-MP1A平台;