From: dillon min dillon.minfei@gmail.com
This patchset has following changes:
V3: merge original tiny/ili9341.c driver to panel/panel-ilitek-ili9341.c to support serial spi & parallel rgb interface in one driver. update ilitek,ili9341.yaml dts binding documentation. update stm32f429-disco dts binding
V2: verify ilitek,ili9341.yaml with make O=../linux-stm32 dt_binding_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/display/panel/ ilitek,ili9341.yaml
V1: add ili9341 drm panel driver add ltdc, spi5 controller for stm32f429-disco add ltdc, spi5 pin map for stm32f429-disco add docs about ili9341 fix ltdc driver loading hang in clk set rate bug
dillon min (5): ARM: dts: stm32: Add pin map for ltdc, spi5 on stm32f429-disco board dt-bindings: display: panel: Add ilitek ili9341 panel bindings ARM: dts: stm32: enable ltdc binding with ili9341 on stm32429-disco board clk: stm32: Fix stm32f429 ltdc driver loading hang in clk set rate. keep ltdc clk running after kernel startup drm/panel: Add ilitek ili9341 driver
.../bindings/display/panel/ilitek,ili9341.yaml | 68 ++ arch/arm/boot/dts/stm32f4-pinctrl.dtsi | 67 ++ arch/arm/boot/dts/stm32f429-disco.dts | 39 ++ drivers/clk/clk-stm32f4.c | 5 +- drivers/gpu/drm/panel/Kconfig | 12 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-ilitek-ili9341.c | 700 +++++++++++++++++++++ 7 files changed, 890 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/display/panel/ilitek,ili9341.yaml create mode 100644 drivers/gpu/drm/panel/panel-ilitek-ili9341.c
From: dillon min dillon.minfei@gmail.com
This patch adds the pin configuration for ltdc, spi5 controller on stm32f429-disco board.
Signed-off-by: dillon min dillon.minfei@gmail.com --- arch/arm/boot/dts/stm32f4-pinctrl.dtsi | 67 ++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f4-pinctrl.dtsi b/arch/arm/boot/dts/stm32f4-pinctrl.dtsi index 392fa14..0eb107f 100644 --- a/arch/arm/boot/dts/stm32f4-pinctrl.dtsi +++ b/arch/arm/boot/dts/stm32f4-pinctrl.dtsi @@ -316,6 +316,73 @@ }; };
+ ltdc_pins_f429_disco: ltdc-1 { + pins { + pinmux = <STM32_PINMUX('C', 6, AF14)>, + /* LCD_HSYNC */ + <STM32_PINMUX('A', 4, AF14)>, + /* LCD_VSYNC */ + <STM32_PINMUX('G', 7, AF14)>, + /* LCD_CLK */ + <STM32_PINMUX('C', 10, AF14)>, + /* LCD_R2 */ + <STM32_PINMUX('B', 0, AF9)>, + /* LCD_R3 */ + <STM32_PINMUX('A', 11, AF14)>, + /* LCD_R4 */ + <STM32_PINMUX('A', 12, AF14)>, + /* LCD_R5 */ + <STM32_PINMUX('B', 1, AF9)>, + /* LCD_R6*/ + <STM32_PINMUX('G', 6, AF14)>, + /* LCD_R7 */ + <STM32_PINMUX('A', 6, AF14)>, + /* LCD_G2 */ + <STM32_PINMUX('G', 10, AF9)>, + /* LCD_G3 */ + <STM32_PINMUX('B', 10, AF14)>, + /* LCD_G4 */ + <STM32_PINMUX('D', 6, AF14)>, + /* LCD_B2 */ + <STM32_PINMUX('G', 11, AF14)>, + /* LCD_B3*/ + <STM32_PINMUX('B', 11, AF14)>, + /* LCD_G5 */ + <STM32_PINMUX('C', 7, AF14)>, + /* LCD_G6 */ + <STM32_PINMUX('D', 3, AF14)>, + /* LCD_G7 */ + <STM32_PINMUX('G', 12, AF9)>, + /* LCD_B4 */ + <STM32_PINMUX('A', 3, AF14)>, + /* LCD_B5 */ + <STM32_PINMUX('B', 8, AF14)>, + /* LCD_B6 */ + <STM32_PINMUX('B', 9, AF14)>, + /* LCD_B7 */ + <STM32_PINMUX('F', 10, AF14)>; + /* LCD_DE */ + slew-rate = <2>; + }; + }; + + spi5_pins: spi5-0 { + pins1 { + pinmux = <STM32_PINMUX('F', 7, AF5)>, + /* SPI5_CLK */ + <STM32_PINMUX('F', 9, AF5)>; + /* SPI5_MOSI */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = <STM32_PINMUX('F', 8, AF5)>; + /* SPI5_MISO */ + bias-disable; + }; + }; + dcmi_pins: dcmi-0 { pins { pinmux = <STM32_PINMUX('A', 4, AF13)>, /* DCMI_HSYNC */
From: dillon min dillon.minfei@gmail.com
Add documentation for "ilitek,ili9341" panel.
Signed-off-by: dillon min dillon.minfei@gmail.com ---
Changes:
V3: change compatible to st,sf-tc240t-9370-t, match #vendor,#lcd_module style
V2: verifyied with make dt_binding_check
V1: none
.../bindings/display/panel/ilitek,ili9341.yaml | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/ilitek,ili9341.yaml
diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9341.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9341.yaml new file mode 100644 index 0000000..9f694d8 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9341.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/ilitek,ili9341.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ilitek-9341 Display Panel + +maintainers: + - Dillon Min dillon.minfei@gmail.com + +description: | + Ilitek ILI9341 TFT panel driver with SPI control bus + This is a driver for 320x240 TFT panels, accepting a rgb input + streams with 16 bits or 18 bits. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + # ili9341 240*320 Color on stm32f429-disco board + - st,sf-tc240t-9370-t + - const: ilitek,ili9341 + + reg: true + + dc-gpios: + maxItems: 1 + description: Display data/command selection (D/CX) + + spi-3wire: true + + spi-max-frequency: + const: 10000000 + + port: true + +additionalProperties: false + +required: + - compatible + - reg + - dc-gpios + - port + +examples: + - |+ + spi { + #address-cells = <1>; + #size-cells = <0>; + panel: display@0 { + compatible = "st,sf-tc240t-9370-t", "ilitek,ili9341"; + reg = <0>; + spi-3wire; + spi-max-frequency = <10000000>; + dc-gpios = <&gpiod 13 0>; + port { + panel_in: endpoint { + remote-endpoint = <&display_out>; + }; + }; + }; + }; +... +
On Tue, May 12, 2020 at 9:03 AM dillon.minfei@gmail.com wrote:
From: dillon min dillon.minfei@gmail.com
Add documentation for "ilitek,ili9341" panel.
Signed-off-by: dillon min dillon.minfei@gmail.com
This looks good to me. Reviewed-by: Linus Walleij linus.walleij@linaro.org
Yours, Linus Walleij
From: dillon min dillon.minfei@gmail.com
Enable the ltdc & ili9341 on stm32429-disco board.
Signed-off-by: dillon min dillon.minfei@gmail.com ---
Changes:
V3: change dts binding compatible to "st,sf-tc240t-9370-t"
V2: none
v1: none
arch/arm/boot/dts/stm32f429-disco.dts | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429-disco.dts b/arch/arm/boot/dts/stm32f429-disco.dts index 30c0f67..4475e40 100644 --- a/arch/arm/boot/dts/stm32f429-disco.dts +++ b/arch/arm/boot/dts/stm32f429-disco.dts @@ -49,6 +49,8 @@ #include "stm32f429.dtsi" #include "stm32f429-pinctrl.dtsi" #include <dt-bindings/input/input.h> +#include <dt-bindings/interrupt-controller/irq.h> +#include <dt-bindings/gpio/gpio.h>
/ { model = "STMicroelectronics STM32F429i-DISCO board"; @@ -127,3 +129,40 @@ pinctrl-names = "default"; status = "okay"; }; + +<dc { + status = "okay"; + pinctrl-0 = <<dc_pins_f429_disco>; + pinctrl-names = "default"; + + port { + ltdc_out_rgb: endpoint { + remote-endpoint = <&panel_in_rgb>; + }; + }; +}; + +&spi5 { + status = "okay"; + pinctrl-0 = <&spi5_pins>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + cs-gpios = <&gpioc 2 GPIO_ACTIVE_LOW>; + dmas = <&dma2 3 2 0x400 0x0>, + <&dma2 4 2 0x400 0x0>; + dma-names = "rx", "tx"; + display: display@0{ + /* Connect panel-ilitek-9341 to stm32 via ltdc*/ + compatible = "st,sf-tc240t-9370-t"; + reg = <0>; + spi-3wire; + spi-max-frequency = <10000000>; + dc-gpios = <&gpiod 13 0>; + port { + panel_in_rgb: endpoint { + remote-endpoint = <<dc_out_rgb>; + }; + }; + }; +};
On Tue, May 12, 2020 at 9:04 AM dillon.minfei@gmail.com wrote:
From: dillon min dillon.minfei@gmail.com
Enable the ltdc & ili9341 on stm32429-disco board.
Signed-off-by: dillon min dillon.minfei@gmail.com
This mostly looks good but...
+&spi5 {
status = "okay";
pinctrl-0 = <&spi5_pins>;
pinctrl-names = "default";
#address-cells = <1>;
#size-cells = <0>;
cs-gpios = <&gpioc 2 GPIO_ACTIVE_LOW>;
dmas = <&dma2 3 2 0x400 0x0>,
<&dma2 4 2 0x400 0x0>;
dma-names = "rx", "tx";
These DMA assignments seem to be SoC things and should rather be in the DTS(I) file where &spi5 is defined, right? stm32f429.dtsi I suppose?
It is likely the same no matter which device is using spi5.
Yours, Linus Walleij
Hi, Linus,
thanks for reviewing.
On Thu, May 14, 2020 at 4:24 PM Linus Walleij linus.walleij@linaro.org wrote:
On Tue, May 12, 2020 at 9:04 AM dillon.minfei@gmail.com wrote:
From: dillon min dillon.minfei@gmail.com
Enable the ltdc & ili9341 on stm32429-disco board.
Signed-off-by: dillon min dillon.minfei@gmail.com
This mostly looks good but...
+&spi5 {
status = "okay";
pinctrl-0 = <&spi5_pins>;
pinctrl-names = "default";
#address-cells = <1>;
#size-cells = <0>;
cs-gpios = <&gpioc 2 GPIO_ACTIVE_LOW>;
dmas = <&dma2 3 2 0x400 0x0>,
<&dma2 4 2 0x400 0x0>;
dma-names = "rx", "tx";
These DMA assignments seem to be SoC things and should rather be in the DTS(I) file where &spi5 is defined, right? stm32f429.dtsi I suppose?
It is likely the same no matter which device is using spi5.
Yours, Linus Walleij
Yes, the dma assignments can be moved to stm32f429.dtsi file. i will change it.
thanks.
best regards.
dillon,
On 5/14/20 10:24 AM, Linus Walleij wrote:
On Tue, May 12, 2020 at 9:04 AM dillon.minfei@gmail.com wrote:
From: dillon min dillon.minfei@gmail.com
Enable the ltdc & ili9341 on stm32429-disco board.
Signed-off-by: dillon min dillon.minfei@gmail.com
This mostly looks good but...
+&spi5 {
status = "okay";
pinctrl-0 = <&spi5_pins>;
pinctrl-names = "default";
#address-cells = <1>;
#size-cells = <0>;
cs-gpios = <&gpioc 2 GPIO_ACTIVE_LOW>;
dmas = <&dma2 3 2 0x400 0x0>,
<&dma2 4 2 0x400 0x0>;
dma-names = "rx", "tx";
These DMA assignments seem to be SoC things and should rather be in the DTS(I) file where &spi5 is defined, right? stm32f429.dtsi I suppose?
I agree with Linus, DMA have to be defined in SoC dtsi. And if a board doesn't want to use it, we use the "delete-property".
It is likely the same no matter which device is using spi5.
Yours, Linus Walleij
Hi Alexandre,
On Thu, May 14, 2020 at 8:53 PM Alexandre Torgue alexandre.torgue@st.com wrote:
On 5/14/20 10:24 AM, Linus Walleij wrote:
On Tue, May 12, 2020 at 9:04 AM dillon.minfei@gmail.com wrote:
From: dillon min dillon.minfei@gmail.com
Enable the ltdc & ili9341 on stm32429-disco board.
Signed-off-by: dillon min dillon.minfei@gmail.com
This mostly looks good but...
+&spi5 {
status = "okay";
pinctrl-0 = <&spi5_pins>;
pinctrl-names = "default";
#address-cells = <1>;
#size-cells = <0>;
cs-gpios = <&gpioc 2 GPIO_ACTIVE_LOW>;
dmas = <&dma2 3 2 0x400 0x0>,
<&dma2 4 2 0x400 0x0>;
dma-names = "rx", "tx";
These DMA assignments seem to be SoC things and should rather be in the DTS(I) file where &spi5 is defined, right? stm32f429.dtsi I suppose?
I agree with Linus, DMA have to be defined in SoC dtsi. And if a board doesn't want to use it, we use the "delete-property".
Yes, will move to Soc dtsi in next submits.
i'm working on write a v4l2-m2m driver for dma2d of stm32 to support pixel conversion alpha blending between foreground and background graphics.
as you know, some soc's engineer trying to add this function to drm system.
do you know st's planning about soc's hardware accelerator driver on stm32mp? such as chrom-art, will add to drm subsystem via ioctl to access, or to v4l2,
thanks.
It is likely the same no matter which device is using spi5.
Yours, Linus Walleij
On 5/14/20 3:07 PM, dillon min wrote:
Hi Alexandre,
On Thu, May 14, 2020 at 8:53 PM Alexandre Torgue alexandre.torgue@st.com wrote:
On 5/14/20 10:24 AM, Linus Walleij wrote:
On Tue, May 12, 2020 at 9:04 AM dillon.minfei@gmail.com wrote:
From: dillon min dillon.minfei@gmail.com
Enable the ltdc & ili9341 on stm32429-disco board.
Signed-off-by: dillon min dillon.minfei@gmail.com
This mostly looks good but...
+&spi5 {
status = "okay";
pinctrl-0 = <&spi5_pins>;
pinctrl-names = "default";
#address-cells = <1>;
#size-cells = <0>;
cs-gpios = <&gpioc 2 GPIO_ACTIVE_LOW>;
dmas = <&dma2 3 2 0x400 0x0>,
<&dma2 4 2 0x400 0x0>;
dma-names = "rx", "tx";
These DMA assignments seem to be SoC things and should rather be in the DTS(I) file where &spi5 is defined, right? stm32f429.dtsi I suppose?
I agree with Linus, DMA have to be defined in SoC dtsi. And if a board doesn't want to use it, we use the "delete-property".
Yes, will move to Soc dtsi in next submits.
i'm working on write a v4l2-m2m driver for dma2d of stm32 to support pixel conversion alpha blending between foreground and background graphics.
as you know, some soc's engineer trying to add this function to drm system.
do you know st's planning about soc's hardware accelerator driver on stm32mp? such as chrom-art, will add to drm subsystem via ioctl to access, or to v4l2,
On stm32mp we do not plan to use chrom-art in drm or v4l2 because it does fit with userland way of working. We use the GPU to do conversion, scaling, blending and composition in only one go. As explain here [1] DRM subsytem it isn't a solution and v4l2-m2m isn't used in any mainline compositors like Weston or android surfaceflinger.
Benjamin
[1] https://www.phoronix.com/scan.php?page=news_item&px=Linux-DRM-No-2D-Acce...
thanks.
It is likely the same no matter which device is using spi5.
Yours, Linus Walleij
Linux-stm32 mailing list Linux-stm32@st-md-mailman.stormreply.com https://st-md-mailman.stormreply.com/mailman/listinfo/linux-stm32
Hi Benjamin,
thanks for reply.
On Fri, May 15, 2020 at 4:31 PM Benjamin GAIGNARD benjamin.gaignard@st.com wrote:
On 5/14/20 3:07 PM, dillon min wrote:
Hi Alexandre,
On Thu, May 14, 2020 at 8:53 PM Alexandre Torgue alexandre.torgue@st.com wrote:
On 5/14/20 10:24 AM, Linus Walleij wrote:
On Tue, May 12, 2020 at 9:04 AM dillon.minfei@gmail.com wrote:
From: dillon min dillon.minfei@gmail.com
Enable the ltdc & ili9341 on stm32429-disco board.
Signed-off-by: dillon min dillon.minfei@gmail.com
This mostly looks good but...
+&spi5 {
status = "okay";
pinctrl-0 = <&spi5_pins>;
pinctrl-names = "default";
#address-cells = <1>;
#size-cells = <0>;
cs-gpios = <&gpioc 2 GPIO_ACTIVE_LOW>;
dmas = <&dma2 3 2 0x400 0x0>,
<&dma2 4 2 0x400 0x0>;
dma-names = "rx", "tx";
These DMA assignments seem to be SoC things and should rather be in the DTS(I) file where &spi5 is defined, right? stm32f429.dtsi I suppose?
I agree with Linus, DMA have to be defined in SoC dtsi. And if a board doesn't want to use it, we use the "delete-property".
Yes, will move to Soc dtsi in next submits.
i'm working on write a v4l2-m2m driver for dma2d of stm32 to support pixel conversion alpha blending between foreground and background graphics.
as you know, some soc's engineer trying to add this function to drm system.
do you know st's planning about soc's hardware accelerator driver on stm32mp? such as chrom-art, will add to drm subsystem via ioctl to access, or to v4l2,
On stm32mp we do not plan to use chrom-art in drm or v4l2 because it does fit with userland way of working. We use the GPU to do conversion, scaling, blending and composition in only one go. As explain here [1] DRM subsytem it isn't a solution and v4l2-m2m isn't used in any mainline compositors like Weston or android surfaceflinger.
Benjamin
After check stm32mp's datasheets, they don't have chrom-art ip inside. sorry for didn't check it yet.
for stm32h7 series with chrom-art, jpeg hardware accelerator inside. does st has plan to setup a driver to support it ? i prefer v4l2-m2m should be easier to implement it. co work with dcmi, fbdev.
thanks.
best regards.
Dillon
[1] https://www.phoronix.com/scan.php?page=news_item&px=Linux-DRM-No-2D-Acce...
thanks.
It is likely the same no matter which device is using spi5.
Yours, Linus Walleij
Linux-stm32 mailing list Linux-stm32@st-md-mailman.stormreply.com https://st-md-mailman.stormreply.com/mailman/listinfo/linux-stm32
On 5/15/20 11:24 AM, dillon min wrote:
Hi Benjamin,
thanks for reply.
On Fri, May 15, 2020 at 4:31 PM Benjamin GAIGNARD benjamin.gaignard@st.com wrote:
On 5/14/20 3:07 PM, dillon min wrote:
Hi Alexandre,
On Thu, May 14, 2020 at 8:53 PM Alexandre Torgue alexandre.torgue@st.com wrote:
On 5/14/20 10:24 AM, Linus Walleij wrote:
On Tue, May 12, 2020 at 9:04 AM dillon.minfei@gmail.com wrote:
From: dillon min dillon.minfei@gmail.com
Enable the ltdc & ili9341 on stm32429-disco board.
Signed-off-by: dillon min dillon.minfei@gmail.com
This mostly looks good but...
+&spi5 {
status = "okay";
pinctrl-0 = <&spi5_pins>;
pinctrl-names = "default";
#address-cells = <1>;
#size-cells = <0>;
cs-gpios = <&gpioc 2 GPIO_ACTIVE_LOW>;
dmas = <&dma2 3 2 0x400 0x0>,
<&dma2 4 2 0x400 0x0>;
dma-names = "rx", "tx";
These DMA assignments seem to be SoC things and should rather be in the DTS(I) file where &spi5 is defined, right? stm32f429.dtsi I suppose?
I agree with Linus, DMA have to be defined in SoC dtsi. And if a board doesn't want to use it, we use the "delete-property".
Yes, will move to Soc dtsi in next submits.
i'm working on write a v4l2-m2m driver for dma2d of stm32 to support pixel conversion alpha blending between foreground and background graphics.
as you know, some soc's engineer trying to add this function to drm system.
do you know st's planning about soc's hardware accelerator driver on stm32mp? such as chrom-art, will add to drm subsystem via ioctl to access, or to v4l2,
On stm32mp we do not plan to use chrom-art in drm or v4l2 because it does fit with userland way of working. We use the GPU to do conversion, scaling, blending and composition in only one go. As explain here [1] DRM subsytem it isn't a solution and v4l2-m2m isn't used in any mainline compositors like Weston or android surfaceflinger.
Benjamin
After check stm32mp's datasheets, they don't have chrom-art ip inside. sorry for didn't check it yet.
for stm32h7 series with chrom-art, jpeg hardware accelerator inside. does st has plan to setup a driver to support it ? i prefer v4l2-m2m should be easier to implement it. co work with dcmi, fbdev.
ST doesn't plan to create a driver for chrom-art because nothing in mainline userland could use it.
Benjamin
thanks.
best regards.
Dillon
[1] https://www.phoronix.com/scan.php?page=news_item&px=Linux-DRM-No-2D-Acce...
thanks.
It is likely the same no matter which device is using spi5.
Yours, Linus Walleij
Linux-stm32 mailing list Linux-stm32@st-md-mailman.stormreply.com https://st-md-mailman.stormreply.com/mailman/listinfo/linux-stm32
Hi Benjamin,
got it, thanks a lot.
best regards
Dillon
On Fri, May 15, 2020 at 5:34 PM Benjamin GAIGNARD benjamin.gaignard@st.com wrote:
On 5/15/20 11:24 AM, dillon min wrote:
Hi Benjamin,
thanks for reply.
On Fri, May 15, 2020 at 4:31 PM Benjamin GAIGNARD benjamin.gaignard@st.com wrote:
On 5/14/20 3:07 PM, dillon min wrote:
Hi Alexandre,
On Thu, May 14, 2020 at 8:53 PM Alexandre Torgue alexandre.torgue@st.com wrote:
On 5/14/20 10:24 AM, Linus Walleij wrote:
On Tue, May 12, 2020 at 9:04 AM dillon.minfei@gmail.com wrote:
> From: dillon min dillon.minfei@gmail.com > > Enable the ltdc & ili9341 on stm32429-disco board. > > Signed-off-by: dillon min dillon.minfei@gmail.com This mostly looks good but...
> +&spi5 { > + status = "okay"; > + pinctrl-0 = <&spi5_pins>; > + pinctrl-names = "default"; > + #address-cells = <1>; > + #size-cells = <0>; > + cs-gpios = <&gpioc 2 GPIO_ACTIVE_LOW>; > + dmas = <&dma2 3 2 0x400 0x0>, > + <&dma2 4 2 0x400 0x0>; > + dma-names = "rx", "tx"; These DMA assignments seem to be SoC things and should rather be in the DTS(I) file where &spi5 is defined, right? stm32f429.dtsi I suppose?
I agree with Linus, DMA have to be defined in SoC dtsi. And if a board doesn't want to use it, we use the "delete-property".
Yes, will move to Soc dtsi in next submits.
i'm working on write a v4l2-m2m driver for dma2d of stm32 to support pixel conversion alpha blending between foreground and background graphics.
as you know, some soc's engineer trying to add this function to drm system.
do you know st's planning about soc's hardware accelerator driver on stm32mp? such as chrom-art, will add to drm subsystem via ioctl to access, or to v4l2,
On stm32mp we do not plan to use chrom-art in drm or v4l2 because it does fit with userland way of working. We use the GPU to do conversion, scaling, blending and composition in only one go. As explain here [1] DRM subsytem it isn't a solution and v4l2-m2m isn't used in any mainline compositors like Weston or android surfaceflinger.
Benjamin
After check stm32mp's datasheets, they don't have chrom-art ip inside. sorry for didn't check it yet.
for stm32h7 series with chrom-art, jpeg hardware accelerator inside. does st has plan to setup a driver to support it ? i prefer v4l2-m2m should be easier to implement it. co work with dcmi, fbdev.
ST doesn't plan to create a driver for chrom-art because nothing in mainline userland could use it.
Benjamin
thanks.
best regards.
Dillon
[1] https://www.phoronix.com/scan.php?page=news_item&px=Linux-DRM-No-2D-Acce...
thanks.
It is likely the same no matter which device is using spi5.
Yours, Linus Walleij
Linux-stm32 mailing list Linux-stm32@st-md-mailman.stormreply.com https://st-md-mailman.stormreply.com/mailman/listinfo/linux-stm32
From: dillon min dillon.minfei@gmail.com
as store stm32f4_rcc_register_pll return to the wrong offset of clks, so ltdc gate clk is null. need change clks[PLL_VCO_SAI] to clks[PLL_SAI]
add CLK_IGNORE_UNUSED for ltdc to make sure clk not be freed by clk_disable_unused
Signed-off-by: dillon min dillon.minfei@gmail.com --- drivers/clk/clk-stm32f4.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c index 18117ce..0ba73de 100644 --- a/drivers/clk/clk-stm32f4.c +++ b/drivers/clk/clk-stm32f4.c @@ -129,7 +129,8 @@ static const struct stm32f4_gate_data stm32f429_gates[] __initconst = { { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, - { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div", + CLK_IGNORE_UNUSED }, };
static const struct stm32f4_gate_data stm32f469_gates[] __initconst = { @@ -1757,7 +1758,7 @@ static void __init stm32f4_rcc_init(struct device_node *np) clks[PLL_VCO_I2S] = stm32f4_rcc_register_pll("vco_in", &data->pll_data[1], &stm32f4_clk_lock);
- clks[PLL_VCO_SAI] = stm32f4_rcc_register_pll("vco_in", + clks[PLL_SAI] = stm32f4_rcc_register_pll("vco_in", &data->pll_data[2], &stm32f4_clk_lock);
for (n = 0; n < MAX_POST_DIV; n++) {
Quoting dillon.minfei@gmail.com (2020-05-12 00:03:36)
From: dillon min dillon.minfei@gmail.com
as store stm32f4_rcc_register_pll return to the wrong offset of clks,
Use () on functions, i.e. stm32f4_rcc_register_pll().
so ltdc gate clk is null. need change clks[PLL_VCO_SAI] to clks[PLL_SAI]
And quote variables like 'clks[PLL_VCO_SAI]'
add CLK_IGNORE_UNUSED for ltdc to make sure clk not be freed by clk_disable_unused
clk_disable_unused() doesn't free anything. Why does ltdc not need to be turned off if it isn't used? Is it critical to system operation? Should it be marked with the critical clk flag then? The CLK_IGNORE_UNUSED flag is almost always wrong to use.
Signed-off-by: dillon min dillon.minfei@gmail.com
drivers/clk/clk-stm32f4.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c index 18117ce..0ba73de 100644 --- a/drivers/clk/clk-stm32f4.c +++ b/drivers/clk/clk-stm32f4.c @@ -129,7 +129,8 @@ static const struct stm32f4_gate_data stm32f429_gates[] __initconst = { { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div",
CLK_IGNORE_UNUSED },
};
static const struct stm32f4_gate_data stm32f469_gates[] __initconst = { @@ -1757,7 +1758,7 @@ static void __init stm32f4_rcc_init(struct device_node *np) clks[PLL_VCO_I2S] = stm32f4_rcc_register_pll("vco_in", &data->pll_data[1], &stm32f4_clk_lock);
clks[PLL_VCO_SAI] = stm32f4_rcc_register_pll("vco_in",
clks[PLL_SAI] = stm32f4_rcc_register_pll("vco_in", &data->pll_data[2], &stm32f4_clk_lock); for (n = 0; n < MAX_POST_DIV; n++) {
Hi Stephen,
thanks for reviewing.
On Fri, May 15, 2020 at 5:02 AM Stephen Boyd sboyd@kernel.org wrote:
Quoting dillon.minfei@gmail.com (2020-05-12 00:03:36)
From: dillon min dillon.minfei@gmail.com
as store stm32f4_rcc_register_pll return to the wrong offset of clks,
Use () on functions, i.e. stm32f4_rcc_register_pll().
ok
so ltdc gate clk is null. need change clks[PLL_VCO_SAI] to clks[PLL_SAI]
And quote variables like 'clks[PLL_VCO_SAI]'
ok
add CLK_IGNORE_UNUSED for ltdc to make sure clk not be freed by clk_disable_unused
clk_disable_unused() doesn't free anything. Why does ltdc not need to be turned off if it isn't used? Is it critical to system operation? Should it be marked with the critical clk flag then? The CLK_IGNORE_UNUSED flag is almost always wrong to use.
Yes, you are right. thanks. CLK_IGNORE_UNUSED just hide the root cause. after deeper debugging. i need to drop the last changes , they are not the root cause.
post diff and analyse here first, i will resubmit clk's changes in next patchset with gyro and ili9341.
--- a/drivers/clk/clk-stm32f4.c +++ b/drivers/clk/clk-stm32f4.c @@ -129,8 +129,6 @@ static const struct stm32f4_gate_data stm32f429_gates[] __initconst = { { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, - { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div", - CLK_IGNORE_UNUSED }, };
static const struct stm32f4_gate_data stm32f469_gates[] __initconst = { @@ -558,13 +556,13 @@ static const struct clk_div_table post_divr_table[] = {
#define MAX_POST_DIV 3 static const struct stm32f4_pll_post_div_data post_div_data[MAX_POST_DIV] = { - { CLK_I2SQ_PDIV, PLL_I2S, "plli2s-q-div", "plli2s-q", + { CLK_I2SQ_PDIV, PLL_VCO_I2S, "plli2s-q-div", "plli2s-q", CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 0, 5, 0, NULL},
- { CLK_SAIQ_PDIV, PLL_SAI, "pllsai-q-div", "pllsai-q", + { CLK_SAIQ_PDIV, PLL_VCO_SAI, "pllsai-q-div", "pllsai-q", CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 8, 5, 0, NULL },
- { NO_IDX, PLL_SAI, "pllsai-r-div", "pllsai-r", CLK_SET_RATE_PARENT, + { NO_IDX, PLL_VCO_SAI, "pllsai-r-div", "pllsai-r", CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 16, 2, 0, post_divr_table }, };
@@ -1758,7 +1756,7 @@ static void __init stm32f4_rcc_init(struct device_node *np) clks[PLL_VCO_I2S] = stm32f4_rcc_register_pll("vco_in", &data->pll_data[1], &stm32f4_clk_lock);
- clks[PLL_SAI] = stm32f4_rcc_register_pll("vco_in", + clks[PLL_VCO_SAI] = stm32f4_rcc_register_pll("vco_in", &data->pll_data[2], &stm32f4_clk_lock);
for (n = 0; n < MAX_POST_DIV; n++) {
issue 1: ili9341 hang in clk set rate, the root cause should be PLL_VCO_SAI, PLL_SAI mismatch for 'clks[]'
1, first at stm32f4_rcc_init() , clks[PLL_VCO_SAI] = stm32f4_rcc_register_pll("vco_in", &data->pll_data[2], &stm32f4_clk_lock); the clk_hw from stm32f4_rcc_register_pll() is store to 'clks[7]', defined in 'include/dt-bindings/clock/stm32fx-clock.h'
2, next hw = clk_register_pll_div(post_div->name, post_div->parent, post_div->flag, base + post_div->offset, post_div->shift, post_div->width, post_div->flag_div, post_div->div_table, clks[post_div->pll_num], &stm32f4_clk_lock); the 'clks[post_div->pll_num]', the pll_num is PLL_SAI, the value is 2, defined at enum { PLL, PLL_I2S, PLL_SAI, }; 'post_div_data[]'
so 7 != 2 offset of 'clks[]', input the wrong 'clks[]' to clk_register_pll_div. cause to_clk_gate result is null, crashed in ltdc driver's loading.
issue 2: clk_disable_unused() turn off ltdc clock. 1, ltdc clk is defined in 'stm32f429_gates[]', register to clk core, but there is no user to use it. ltdc driver use dts binding name "lcd", connect to CLK_LCD, then aux clk 'lcd-tft ' 2, as no one use 'stm32f429_gates[]' s ltdc clock , so the enable_count is zero, after kernel startup it's been turn off by clk_disable_unused() is fine.
my chages for this is remove the ltdc from 'stm32f429_gates[]' but this modification still need 'clk-stm32f4.c''s maintainer to check if there is side effect.
Signed-off-by: dillon min dillon.minfei@gmail.com
drivers/clk/clk-stm32f4.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c index 18117ce..0ba73de 100644 --- a/drivers/clk/clk-stm32f4.c +++ b/drivers/clk/clk-stm32f4.c @@ -129,7 +129,8 @@ static const struct stm32f4_gate_data stm32f429_gates[] __initconst = { { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div",
CLK_IGNORE_UNUSED },
};
static const struct stm32f4_gate_data stm32f469_gates[] __initconst = { @@ -1757,7 +1758,7 @@ static void __init stm32f4_rcc_init(struct device_node *np) clks[PLL_VCO_I2S] = stm32f4_rcc_register_pll("vco_in", &data->pll_data[1], &stm32f4_clk_lock);
clks[PLL_VCO_SAI] = stm32f4_rcc_register_pll("vco_in",
clks[PLL_SAI] = stm32f4_rcc_register_pll("vco_in", &data->pll_data[2], &stm32f4_clk_lock); for (n = 0; n < MAX_POST_DIV; n++) {
From: dillon min dillon.minfei@gmail.com
Currently, we can use tinydrm ili9341 driver to drive ili9341 panel by spi interface (both register configuration and video)
ili9341 have parallel and mcu interface as well, we extend the support to parallel rgb interface with DRM_MODE_CONNECTOR_DPI
this driver can work as parallel rgb or serial spi mode by different dts binding. for serial spi interface dts binding configuration, refer to: Documentation/devicetree/bindings/display/ilitek,ili9341.txt
Signed-off-by: dillon min dillon.minfei@gmail.com ---
Changes:
V3: 1 Add support for original dts binding "adafruit,yx240qv29", merged from tiny/ili9341.c, which is serial spi interface for register configuration and video trasfer. 2 change the dts binding to st,sf-tc240t-9370-t for parallel rgb interface.
V2: none
V1: Add support for parallel rgb interface
drivers/gpu/drm/panel/Kconfig | 12 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-ilitek-ili9341.c | 700 +++++++++++++++++++++++++++ 3 files changed, 713 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-ilitek-ili9341.c
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index a1723c1..c938bee 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -95,6 +95,18 @@ config DRM_PANEL_ILITEK_IL9322 Say Y here if you want to enable support for Ilitek IL9322 QVGA (320x240) RGB, YUV and ITU-T BT.656 panels.
+config DRM_PANEL_ILITEK_ILI9341 + tristate "Ilitek ILI9341 240x320 QVGA panels" + depends on OF && SPI + depends on DRM_KMS_HELPER + depends on DRM_KMS_CMA_HELPER + depends on BACKLIGHT_CLASS_DEVICE + select DRM_MIPI_DBI + help + Say Y here if you want to enable support for Ilitek IL9341 + QVGA (240x320) RGB panels. support serial & parallel rgb + interface. + config DRM_PANEL_ILITEK_ILI9881C tristate "Ilitek ILI9881C-based panels" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 96a883c..16947d7 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_DRM_PANEL_ELIDA_KD35T133) += panel-elida-kd35t133.o obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02) += panel-feixin-k101-im2ba02.o obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o +obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c new file mode 100644 index 0000000..17339db --- /dev/null +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c @@ -0,0 +1,700 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Ilitek ILI9341 TFT LCD drm_panel driver. + * + * This panel can be configured to support: + * - 16-bit parallel RGB interface + * - 18-bit parallel RGB interface + * + * Copyright (C) 2020 Dillon Min dillon.minfei@gmail.com + * Derived from drivers/drm/gpu/panel/panel-ilitek-ili9322.c + */ + +#include <linux/bitops.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/delay.h> +#include <video/mipi_display.h> +#include <drm/drm_mipi_dbi.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_atomic_helper.h> + +#include <drm/drm_drv.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> + +#define ILI9341_SLEEP_OUT 0x11 /* Sleep out register */ +#define ILI9341_GAMMA 0x26 /* Gamma register */ +#define ILI9341_DISPLAY_OFF 0x28 /* Display off register */ +#define ILI9341_DISPLAY_ON 0x29 /* Display on register */ +#define ILI9341_COLUMN_ADDR 0x2a /* Colomn address register */ +#define ILI9341_PAGE_ADDR 0x2b /* Page address register */ +#define ILI9341_GRAM 0x2c /* GRAM register */ +#define ILI9341_MAC 0x36 /* Memory Access Control register*/ +#define ILI9341_PIXEL_FORMAT 0x3A /* Pixel Format register */ +#define ILI9341_RGB_INTERFACE 0xb0 /* RGB Interface Signal Control */ +#define ILI9341_FRC 0xb1 /* Frame Rate Control register */ +#define ILI9341_DFC 0xb6 /* Display Function Control + * register + */ +#define ILI9341_POWER1 0xc0 /* Power Control 1 register */ +#define ILI9341_POWER2 0xc1 /* Power Control 2 register */ +#define ILI9341_VCOM1 0xc5 /* VCOM Control 1 register */ +#define ILI9341_VCOM2 0xc7 /* VCOM Control 2 register */ +#define ILI9341_POWERA 0xcb /* Power control A register */ +#define ILI9341_POWERB 0xcf /* Power control B register */ +#define ILI9341_PGAMMA 0xe0 /* Positive Gamma Correction + * register + */ +#define ILI9341_NGAMMA 0xe1 /* Negative Gamma Correction + * register + */ +#define ILI9341_DTCA 0xe8 /* Driver timing control A */ +#define ILI9341_DTCB 0xea /* Driver timing control B */ +#define ILI9341_POWER_SEQ 0xed /* Power on sequence register */ +#define ILI9341_3GAMMA_EN 0xf2 /* 3 Gamma enable register */ +#define ILI9341_INTERFACE 0xf6 /* Interface control register */ +#define ILI9341_PRC 0xf7 /* Pump ratio control register */ +#define ILI9341_ETMOD 0xb7 + +#define ILI9341_MADCTL_BGR BIT(3) +#define ILI9341_MADCTL_MV BIT(5) +#define ILI9341_MADCTL_MX BIT(6) +#define ILI9341_MADCTL_MY BIT(7) + +/** + * ili9341_command - ili9341 command with optional parameter(s) + * @ili: struct ili9341 + * @cmd: Command + * @seq...: Optional parameter(s) + * + * Send command to the controller. + * + * Returns: + * Zero on success, negative error code on failure. + */ +#define ili9341_command(ili, cmd, seq...) \ +({ \ + u8 d[] = { seq }; \ + _ili9341_command(ili, cmd, d, ARRAY_SIZE(d)); \ +}) + +/** + * enum ili9341_input - the format of the incoming signal to the panel + * + * The panel can be connected to various input streams and four of them can + * be selected by electronic straps on the display. However it is possible + * to select another mode or override the electronic default with this + * setting. + */ +enum ili9341_input { + ILI9341_INPUT_PRGB_18_BITS = 0x0, + ILI9341_INPUT_PRGB_16_BITS = 0x1, + ILI9341_INPUT_UNKNOWN = 0xf, +}; + +/** + * struct ili9341_config - the system specific ILI9341 configuration + * @max_spi_speed: 10000000 + * @input: the input/entry type used in this system, can be 16 or 18 bits + * @dclk_active_high: data/pixel clock active high, data will be clocked + * in on the rising edge of the DCLK (this is usually the case). + * @de_active_high: DE (data entry) is active high + * @hsync_active_high: HSYNC is active high + * @vsync_active_high: VSYNC is active high + */ +struct ili9341_config { + u32 max_spi_speed; + enum ili9341_input input; + bool dclk_active_high; + bool de_active_high; + bool hsync_active_high; + bool vsync_active_high; +}; + +struct ili9341 { + struct device *dev; + const struct ili9341_config *conf; + struct drm_panel panel; + struct gpio_desc *reset_gpio; + struct gpio_desc *dc_gpio; + enum ili9341_input input; + u32 max_spi_speed; + struct regulator *vcc; +}; + +static inline struct ili9341 *panel_to_ili9341(struct drm_panel *panel) +{ + return container_of(panel, struct ili9341, panel); +} + +int ili9341_spi_transfer(struct spi_device *spi, u32 speed_hz, + u8 bpw, const void *buf, size_t len) +{ + size_t max_chunk = spi_max_transfer_size(spi); + struct spi_transfer tr = { + .bits_per_word = bpw, + .speed_hz = speed_hz, + .len = len, + }; + struct spi_message m; + size_t chunk; + int ret; + + spi_message_init_with_transfers(&m, &tr, 1); + + while (len) { + chunk = min(len, max_chunk); + + tr.tx_buf = buf; + tr.len = chunk; + buf += chunk; + len -= chunk; + + ret = spi_sync(spi, &m); + if (ret) { + dev_err(&spi->dev, "spi_sync error: %d\n", ret); + return ret; + } + } + return 0; +} + +static int _ili9341_command(struct ili9341 *ili, u8 cmd, const void *data, + size_t count) +{ + struct spi_device *spi = to_spi_device(ili->dev); + int ret = 0; + + gpiod_set_value_cansleep(ili->dc_gpio, 0); + + ret = ili9341_spi_transfer(spi, ili->max_spi_speed, 8, + (const void *)&cmd, 1); + if (ret || data == NULL || count == 0) + return ret; + + gpiod_set_value_cansleep(ili->dc_gpio, 1); + + return ili9341_spi_transfer(spi, ili->max_spi_speed, 8, + data, count); +} + +static int ili9341_dpi_init(struct ili9341 *ili) +{ + ili9341_command(ili, 0xca, 0xc3, 0x08, 0x50); + ili9341_command(ili, ILI9341_POWERB, 0x00, 0xc1, 0x30); + ili9341_command(ili, ILI9341_POWER_SEQ, 0x64, 0x03, 0x12, 0x81); + ili9341_command(ili, ILI9341_DTCA, 0x85, 0x00, 0x78); + ili9341_command(ili, ILI9341_POWERA, 0x39, 0x2c, 0x00, 0x34, 0x02); + ili9341_command(ili, ILI9341_PRC, 0x20); + ili9341_command(ili, ILI9341_DTCB, 0x00, 0x00); + ili9341_command(ili, ILI9341_FRC, 0x00, 0x1b); + ili9341_command(ili, ILI9341_DFC, 0x0a, 0xa2); + ili9341_command(ili, ILI9341_POWER1, 0x10); + ili9341_command(ili, ILI9341_POWER2, 0x10); + ili9341_command(ili, ILI9341_VCOM1, 0x45, 0x15); + ili9341_command(ili, ILI9341_VCOM2, 0x90); + ili9341_command(ili, ILI9341_MAC, 0xc8); + ili9341_command(ili, ILI9341_3GAMMA_EN, 0x00); + ili9341_command(ili, ILI9341_RGB_INTERFACE, 0xc2); + ili9341_command(ili, ILI9341_DFC, 0x0a, 0xa7, 0x27, 0x04); + ili9341_command(ili, ILI9341_COLUMN_ADDR, 0x00, 0x00, 0x00, 0xef); + ili9341_command(ili, ILI9341_PAGE_ADDR, 0x00, 0x00, 0x01, 0x3f); + ili9341_command(ili, ILI9341_INTERFACE, 0x01, 0x00, 0x06); + if (ili->input == ILI9341_INPUT_PRGB_18_BITS) + ili9341_command(ili, ILI9341_PIXEL_FORMAT, 0x66); + else + ili9341_command(ili, ILI9341_PIXEL_FORMAT, 0x56); + ili9341_command(ili, ILI9341_GRAM); + msleep(200); + ili9341_command(ili, ILI9341_GAMMA, 0x01); + ili9341_command(ili, ILI9341_PGAMMA, 0x0f, 0x29, 0x24, 0x0c, 0x0e, + 0x09, 0x4e, 0x78, 0x3c, 0x09, + 0x13, 0x05, 0x17, 0x11, 0x00); + ili9341_command(ili, ILI9341_NGAMMA, 0x00, 0x16, 0x1b, 0x04, 0x11, + 0x07, 0x31, 0x33, 0x42, 0x05, + 0x0c, 0x0a, 0x28, 0x2f, 0x0f); + ili9341_command(ili, ILI9341_SLEEP_OUT); + msleep(200); + ili9341_command(ili, ILI9341_DISPLAY_ON); + ili9341_command(ili, ILI9341_GRAM); + + dev_info(ili->dev, "initialized display rgb interface\n"); + + return 0; +} + +static int ili9341_dpi_power_on(struct ili9341 *ili) +{ + int ret = 0; + + /* Assert RESET */ + gpiod_set_value(ili->reset_gpio, 1); + + /* Enable power */ + if (!IS_ERR(ili->vcc)) { + ret = regulator_enable(ili->vcc); + if (ret < 0) { + dev_err(ili->dev, "unable to enable vcc\n"); + return ret; + } + } + msleep(20); + + /* De-assert RESET */ + gpiod_set_value(ili->reset_gpio, 0); + msleep(10); + + return 0; +} + +static int ili9341_dpi_power_off(struct ili9341 *ili) +{ + /* Disable power */ + if (!IS_ERR(ili->vcc)) + return regulator_disable(ili->vcc); + + return 0; +} + +static int ili9341_dpi_disable(struct drm_panel *panel) +{ + struct ili9341 *ili = panel_to_ili9341(panel); + + ili9341_command(ili, ILI9341_DISPLAY_OFF); + + return 0; +} + +static int ili9341_dpi_unprepare(struct drm_panel *panel) +{ + struct ili9341 *ili = panel_to_ili9341(panel); + + return ili9341_dpi_power_off(ili); +} + +static int ili9341_dpi_prepare(struct drm_panel *panel) +{ + struct ili9341 *ili = panel_to_ili9341(panel); + int ret; + + ret = ili9341_dpi_power_on(ili); + if (ret < 0) + return ret; + + ret = ili9341_dpi_init(ili); + if (ret < 0) + ili9341_dpi_unprepare(panel); + + return ret; +} + +static int ili9341_dpi_enable(struct drm_panel *panel) +{ + struct ili9341 *ili = panel_to_ili9341(panel); + + ili9341_command(ili, ILI9341_DISPLAY_ON); + + return 0; +} + +/* This is the only mode listed for parallel RGB in the datasheet */ +static const struct drm_display_mode rgb_240x320_mode = { + .clock = 6100, + .hdisplay = 240, + .hsync_start = 240 + 10, + .hsync_end = 240 + 10 + 10, + .htotal = 240 + 10 + 10 + 20, + .vdisplay = 320, + .vsync_start = 320 + 4, + .vsync_end = 320 + 4 + 2, + .vtotal = 320 + 4 + 2 + 2, + .vrefresh = 60, + .flags = 0, + .width_mm = 65, + .height_mm = 50, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +}; + +static int ili9341_dpi_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct ili9341 *ili = panel_to_ili9341(panel); + struct drm_device *drm = connector->dev; + struct drm_display_mode *mode; + struct drm_display_info *info; + + info = &connector->display_info; + info->width_mm = rgb_240x320_mode.width_mm; + info->height_mm = rgb_240x320_mode.height_mm; + + if (ili->conf->dclk_active_high) + info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; + else + info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; + + if (ili->conf->de_active_high) + info->bus_flags |= DRM_BUS_FLAG_DE_HIGH; + else + info->bus_flags |= DRM_BUS_FLAG_DE_LOW; + + mode = drm_mode_duplicate(drm, &rgb_240x320_mode); + if (!mode) { + DRM_ERROR("bad mode or failed to add mode\n"); + return -EINVAL; + } + drm_mode_set_name(mode); + + /* Set up the polarity */ + if (ili->conf->hsync_active_high) + mode->flags |= DRM_MODE_FLAG_PHSYNC; + else + mode->flags |= DRM_MODE_FLAG_NHSYNC; + + if (ili->conf->vsync_active_high) + mode->flags |= DRM_MODE_FLAG_PVSYNC; + else + mode->flags |= DRM_MODE_FLAG_NVSYNC; + + drm_mode_probed_add(connector, mode); + + return 1; /* Number of modes */ +} + +static const struct drm_panel_funcs ili9341_dpi_funcs = { + .disable = ili9341_dpi_disable, + .unprepare = ili9341_dpi_unprepare, + .prepare = ili9341_dpi_prepare, + .enable = ili9341_dpi_enable, + .get_modes = ili9341_dpi_get_modes, +}; + +static int ili9341_dpi_probe(struct spi_device *spi) +{ + int ret; + struct device *dev = &spi->dev; + struct ili9341 *ili; + + ili = devm_kzalloc(dev, sizeof(struct ili9341), GFP_KERNEL); + if (!ili) + return -ENOMEM; + + spi_set_drvdata(spi, ili); + + ili->dev = dev; + /* + * Every new incarnation of this display must have a unique + * data entry for the system in this driver. + */ + ili->conf = of_device_get_match_data(dev); + if (!ili->conf) { + dev_err(dev, "missing device configuration\n"); + return -ENODEV; + } + + ili->vcc = devm_regulator_get_optional(dev, "vcc"); + if (IS_ERR(ili->vcc)) + dev_err(dev, "get optional vcc failed\n"); + + ili->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ili->reset_gpio)) { + dev_err(dev, "failed to get RESET GPIO\n"); + return PTR_ERR(ili->reset_gpio); + } + + ili->dc_gpio = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW); + if (IS_ERR(ili->dc_gpio)) { + dev_err(dev, "failed to get DC GPIO\n"); + return PTR_ERR(ili->dc_gpio); + } + + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret < 0) { + dev_err(dev, "spi setup failed.\n"); + return ret; + } + + ili->input = ili->conf->input; + ili->max_spi_speed = ili->conf->max_spi_speed; + + drm_panel_init(&ili->panel, dev, &ili9341_dpi_funcs, + DRM_MODE_CONNECTOR_DPI); + + return drm_panel_add(&ili->panel); +} + + + +static void ili9341_dbi_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) +{ + struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); + struct mipi_dbi *dbi = &dbidev->dbi; + u8 addr_mode; + int ret, idx; + + if (!drm_dev_enter(pipe->crtc.dev, &idx)) + return; + + DRM_DEBUG_KMS("\n"); + + ret = mipi_dbi_poweron_conditional_reset(dbidev); + if (ret < 0) + goto out_exit; + if (ret == 1) + goto out_enable; + + mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); + + mipi_dbi_command(dbi, ILI9341_POWERB, 0x00, 0xc1, 0x30); + mipi_dbi_command(dbi, ILI9341_POWER_SEQ, 0x64, 0x03, 0x12, 0x81); + mipi_dbi_command(dbi, ILI9341_DTCA, 0x85, 0x00, 0x78); + mipi_dbi_command(dbi, ILI9341_POWERA, 0x39, 0x2c, 0x00, 0x34, 0x02); + mipi_dbi_command(dbi, ILI9341_PRC, 0x20); + mipi_dbi_command(dbi, ILI9341_DTCB, 0x00, 0x00); + + /* Power Control */ + mipi_dbi_command(dbi, ILI9341_POWER1, 0x23); + mipi_dbi_command(dbi, ILI9341_POWER2, 0x10); + /* VCOM */ + mipi_dbi_command(dbi, ILI9341_VCOM1, 0x3e, 0x28); + mipi_dbi_command(dbi, ILI9341_VCOM2, 0x86); + + /* Memory Access Control */ + mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, + MIPI_DCS_PIXEL_FMT_16BIT); + + /* Frame Rate */ + mipi_dbi_command(dbi, ILI9341_FRC, 0x00, 0x1b); + + /* Gamma */ + mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, 0x00); + mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x01); + mipi_dbi_command(dbi, ILI9341_PGAMMA, + 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1, + 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00); + mipi_dbi_command(dbi, ILI9341_NGAMMA, + 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1, + 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f); + + /* DDRAM */ + mipi_dbi_command(dbi, ILI9341_ETMOD, 0x07); + + /* Display */ + mipi_dbi_command(dbi, ILI9341_DFC, 0x08, 0x82, 0x27, 0x00); + mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); + msleep(100); + + mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); + msleep(100); + +out_enable: + switch (dbidev->rotation) { + default: + addr_mode = ILI9341_MADCTL_MX; + break; + case 90: + addr_mode = ILI9341_MADCTL_MV; + break; + case 180: + addr_mode = ILI9341_MADCTL_MY; + break; + case 270: + addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY | + ILI9341_MADCTL_MX; + break; + } + addr_mode |= ILI9341_MADCTL_BGR; + mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); + mipi_dbi_enable_flush(dbidev, crtc_state, plane_state); + DRM_DEBUG_KMS("initialized display serial interface\n"); +out_exit: + drm_dev_exit(idx); +} + +static const struct drm_simple_display_pipe_funcs ili9341_dbi_funcs = { + .enable = ili9341_dbi_enable, + .disable = mipi_dbi_pipe_disable, + .update = mipi_dbi_pipe_update, + .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, +}; + +static const struct drm_display_mode ili9341_dbi_mode = { + DRM_SIMPLE_MODE(240, 320, 37, 49), +}; + +DEFINE_DRM_GEM_CMA_FOPS(ili9341_dbi_fops); + +static struct drm_driver ili9341_dbi_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &ili9341_dbi_fops, + .release = mipi_dbi_release, + DRM_GEM_CMA_VMAP_DRIVER_OPS, + .debugfs_init = mipi_dbi_debugfs_init, + .name = "ili9341", + .desc = "Ilitek ILI9341", + .date = "20180514", + .major = 1, + .minor = 0, +}; +static int ili9341_dbi_probe(struct spi_device *spi) +{ + struct mipi_dbi_dev *dbidev; + struct drm_device *drm; + struct mipi_dbi *dbi; + struct gpio_desc *dc; + struct device *dev = &spi->dev; + u32 rotation = 0; + int ret; + + dbidev = kzalloc(sizeof(*dbidev), GFP_KERNEL); + if (!dbidev) + return -ENOMEM; + + dbi = &dbidev->dbi; + drm = &dbidev->drm; + ret = devm_drm_dev_init(dev, drm, &ili9341_dbi_driver); + if (ret) { + kfree(dbidev); + return ret; + } + + drm_mode_config_init(drm); + + dbi->reset = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(dbi->reset)) { + DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); + return PTR_ERR(dbi->reset); + } + + dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW); + if (IS_ERR(dc)) { + DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n"); + return PTR_ERR(dc); + } + + dbidev->regulator = devm_regulator_get_optional(dev, "vcc"); + if (IS_ERR(dbidev->regulator)) + dev_err(dev, "get optional vcc failed\n"); + + dbidev->backlight = devm_of_find_backlight(dev); + if (IS_ERR(dbidev->backlight)) + return PTR_ERR(dbidev->backlight); + + device_property_read_u32(dev, "rotation", &rotation); + + ret = mipi_dbi_spi_init(spi, dbi, dc); + if (ret) + return ret; + + ret = mipi_dbi_dev_init(dbidev, &ili9341_dbi_funcs, + &ili9341_dbi_mode, rotation); + if (ret) + return ret; + + drm_mode_config_reset(drm); + + ret = drm_dev_register(drm, 0); + if (ret) + return ret; + + spi_set_drvdata(spi, drm); + + drm_fbdev_generic_setup(drm, 0); + + return 0; +} +static int ili9341_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + + if (!strcmp(id->name, "sf-tc240t-9370-t")) + return ili9341_dpi_probe(spi); + else if (!strcmp(id->name, "yx240qv29")) + return ili9341_dbi_probe(spi); + + return -1; +} + +static int ili9341_remove(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct ili9341 *ili = spi_get_drvdata(spi); + struct drm_device *drm = spi_get_drvdata(spi); + + if (!strcmp(id->name, "sf-tc240t-9370-t")) { + ili9341_dpi_power_off(ili); + drm_panel_remove(&ili->panel); + } else if (!strcmp(id->name, "yx240qv29")) { + drm_dev_unplug(drm); + drm_atomic_helper_shutdown(drm); + } + return 0; +} + +static void ili9341_shutdown(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + + if (!strcmp(id->name, "yx240qv29")) + drm_atomic_helper_shutdown(spi_get_drvdata(spi)); +} +/* + * The Stm32f429-disco board has a panel ili9341 connected to ltdc controller + */ +static const struct ili9341_config ili9341_stm32f429_disco_data = { + .dclk_active_high = true, + .de_active_high = false, + .hsync_active_high = false, + .vsync_active_high = false, + .max_spi_speed = 10000000, + .input = ILI9341_INPUT_PRGB_16_BITS, +}; + +static const struct of_device_id ili9341_of_match[] = { + { + .compatible = "st,sf-tc240t-9370-t", + .data = &ili9341_stm32f429_disco_data, + }, + { + /* porting from tiny/ili9341.c + * for original mipi dbi compitable + */ + .compatible = "adafruit,yx240qv29", + .data = NULL, + }, +}; +MODULE_DEVICE_TABLE(of, ili9341_of_match); + +static const struct spi_device_id ili9341_id[] = { + { "yx240qv29", 0 }, + { "sf-tc240t-9370-t", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ili9341_id); + +static struct spi_driver ili9341_driver = { + .probe = ili9341_probe, + .remove = ili9341_remove, + .shutdown = ili9341_shutdown, + .id_table = ili9341_id, + .driver = { + .name = "panel-ilitek-ili9341", + .of_match_table = ili9341_of_match, + }, +}; +module_spi_driver(ili9341_driver); + +MODULE_AUTHOR("Dillon Min dillon.minfei@gmail.com"); +MODULE_DESCRIPTION("ILI9341 LCD panel driver"); +MODULE_LICENSE("GPL v2");
Hi Dillon,
thanks for your patch! Overall this looks like a good start.
On Tue, May 12, 2020 at 9:04 AM dillon.minfei@gmail.com wrote:
#define ILI9341_SLEEP_OUT 0x11 /* Sleep out register */
This is not a register, also just use MIPI_DCS_EXIT_SLEEP_MODE in the code.
+#define ILI9341_DFC 0xb6 /* Display Function Control
* register
*/
This commenting style doesn't work, either just put it after /* the define */ and don't care if the line gets a bit long and checkpatch complains, or
/* * Put it above the define like this */ #define FOO 0x00
+/**
- struct ili9341_config - the system specific ILI9341 configuration
Nice with this per-system config, it makes the driver easy to maintain for new users.
+static int ili9341_dpi_init(struct ili9341 *ili) +{
ili9341_command(ili, 0xca, 0xc3, 0x08, 0x50);
This stuff is a bit hard to understand, don't you think?
But given that register 0xCA seems undocumented I don't know if there is anything more you can do, so it is OK I suppose.
ili9341_command(ili, ILI9341_POWERB, 0x00, 0xc1, 0x30);
This command is described in the manual page 196. Version: V1.11 Document No.: ILI9341_DS_V1.11.pdf https://dflund.se/~triad/ILI9341_v1.11.pdf
And this goes for all the below commands. Please add some more defines from the datasheet and have less magic numbers in the driver.
ili9341_command(ili, ILI9341_POWER_SEQ, 0x64, 0x03, 0x12, 0x81);
ili9341_command(ili, ILI9341_DTCA, 0x85, 0x00, 0x78);
ili9341_command(ili, ILI9341_POWERA, 0x39, 0x2c, 0x00, 0x34, 0x02);
ili9341_command(ili, ILI9341_PRC, 0x20);
ili9341_command(ili, ILI9341_DTCB, 0x00, 0x00);
ili9341_command(ili, ILI9341_FRC, 0x00, 0x1b);
ili9341_command(ili, ILI9341_DFC, 0x0a, 0xa2);
ili9341_command(ili, ILI9341_POWER1, 0x10);
ili9341_command(ili, ILI9341_POWER2, 0x10);
ili9341_command(ili, ILI9341_VCOM1, 0x45, 0x15);
ili9341_command(ili, ILI9341_VCOM2, 0x90);
ili9341_command(ili, ILI9341_MAC, 0xc8);
ili9341_command(ili, ILI9341_3GAMMA_EN, 0x00);
ili9341_command(ili, ILI9341_RGB_INTERFACE, 0xc2);
ili9341_command(ili, ILI9341_DFC, 0x0a, 0xa7, 0x27, 0x04);
ili9341_command(ili, ILI9341_COLUMN_ADDR, 0x00, 0x00, 0x00, 0xef);
ili9341_command(ili, ILI9341_PAGE_ADDR, 0x00, 0x00, 0x01, 0x3f);
ili9341_command(ili, ILI9341_INTERFACE, 0x01, 0x00, 0x06);
if (ili->input == ILI9341_INPUT_PRGB_18_BITS)
ili9341_command(ili, ILI9341_PIXEL_FORMAT, 0x66);
else
ili9341_command(ili, ILI9341_PIXEL_FORMAT, 0x56);
ili9341_command(ili, ILI9341_GRAM);
msleep(200);
I think some of the above should not be hardcoded but should instead be stored in fields in struct ili9341_config. I know it can be a bit tedious but it makes things much more clear.
ili9341_command(ili, ILI9341_GAMMA, 0x01);
ili9341_command(ili, ILI9341_PGAMMA, 0x0f, 0x29, 0x24, 0x0c, 0x0e,
0x09, 0x4e, 0x78, 0x3c, 0x09,
0x13, 0x05, 0x17, 0x11, 0x00);
ili9341_command(ili, ILI9341_NGAMMA, 0x00, 0x16, 0x1b, 0x04, 0x11,
0x07, 0x31, 0x33, 0x42, 0x05,
0x0c, 0x0a, 0x28, 0x2f, 0x0f);
This should definately be in ili9341_config, as it is a screen property.
In the long run I would like these panels to support setting gamma from userspace etc but it is a big tedious work to get that right so hard-coding a default per-variant is fine.
You can check in e.g. panel-novatek-nt35510.c how I encoded such sequences in per-variant data.
+static int ili9341_dpi_power_off(struct ili9341 *ili) +{
/* Disable power */
if (!IS_ERR(ili->vcc))
return regulator_disable(ili->vcc);
return 0;
+}
Usually you should also assert RESET when disabling power.
+/* This is the only mode listed for parallel RGB in the datasheet */ +static const struct drm_display_mode rgb_240x320_mode = {
.clock = 6100,
.hdisplay = 240,
.hsync_start = 240 + 10,
.hsync_end = 240 + 10 + 10,
.htotal = 240 + 10 + 10 + 20,
.vdisplay = 320,
.vsync_start = 320 + 4,
.vsync_end = 320 + 4 + 2,
.vtotal = 320 + 4 + 2 + 2,
.vrefresh = 60,
.flags = 0,
.width_mm = 65,
.height_mm = 50,
The width and height should certainly be om the ili9341_config as it is a per-panel property. You can just fill in in in the below .get_modes() function. Or assign the whole mode as part of the ili9341_config if that is easier.
return drm_panel_add(&ili->panel);
+}
Surplus whitespace here.
mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
mipi_dbi_command(dbi, ILI9341_POWERB, 0x00, 0xc1, 0x30);
mipi_dbi_command(dbi, ILI9341_POWER_SEQ, 0x64, 0x03, 0x12, 0x81);
Some of these are just copies of the above init sequence, so it makes even more sense to just have these settings stored in ili9341_config.
mipi_dbi_command(dbi, ILI9341_DTCA, 0x85, 0x00, 0x78);
mipi_dbi_command(dbi, ILI9341_POWERA, 0x39, 0x2c, 0x00, 0x34, 0x02);
mipi_dbi_command(dbi, ILI9341_PRC, 0x20);
mipi_dbi_command(dbi, ILI9341_DTCB, 0x00, 0x00);
/* Power Control */
mipi_dbi_command(dbi, ILI9341_POWER1, 0x23);
mipi_dbi_command(dbi, ILI9341_POWER2, 0x10);
/* VCOM */
mipi_dbi_command(dbi, ILI9341_VCOM1, 0x3e, 0x28);
mipi_dbi_command(dbi, ILI9341_VCOM2, 0x86);
/* Memory Access Control */
mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT,
MIPI_DCS_PIXEL_FMT_16BIT);
/* Frame Rate */
mipi_dbi_command(dbi, ILI9341_FRC, 0x00, 0x1b);
/* Gamma */
mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, 0x00);
mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x01);
mipi_dbi_command(dbi, ILI9341_PGAMMA,
0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1,
0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00);
mipi_dbi_command(dbi, ILI9341_NGAMMA,
0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1,
0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f);
It seems to be copies of the stuff above, but why is there a different gamma if you use DBI?
I suspect only one of them is really needed and it is not even necessary to set if up in two places.
+out_enable:
switch (dbidev->rotation) {
default:
addr_mode = ILI9341_MADCTL_MX;
break;> +out_enable:
switch (dbidev->rotation) {
default:
addr_mode = ILI9341_MADCTL_MX;
break;
case 90:
addr_mode = ILI9341_MADCTL_MV;
break;
case 180:
addr_mode = ILI9341_MADCTL_MY;
break;
case 270:
addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY |
ILI9341_MADCTL_MX;
break;
}
addr_mode |= ILI9341_MADCTL_BGR;
mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
mipi_dbi_enable_flush(dbidev, crtc_state, plane_state);
DRM_DEBUG_KMS("initialized display serial interface\n");
+out_exit:
drm_dev_exit(idx);
+}
case 90:
addr_mode = ILI9341_MADCTL_MV;
break;
case 180:
addr_mode = ILI9341_MADCTL_MY;
break;
case 270:
addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY |
ILI9341_MADCTL_MX;
break;
}
addr_mode |= ILI9341_MADCTL_BGR;
mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
Since you use MIPI_DCS_* define here, check if this applies to more of the commands above so you don't need custom defines for them. e.g. ILI9341_SLEEP_OUT 0x11 = MIPI_DCS_EXIT_SLEEP_MODE and that isn't even a register right, it is just a command? (Noted in the beginning.)
Yours, Linus Walleij
Hi Linus,
Thanks for reviewing.
On Thu, May 14, 2020 at 4:14 PM Linus Walleij linus.walleij@linaro.org wrote:
Hi Dillon,
thanks for your patch! Overall this looks like a good start.
On Tue, May 12, 2020 at 9:04 AM dillon.minfei@gmail.com wrote:
#define ILI9341_SLEEP_OUT 0x11 /* Sleep out register */
This is not a register, also just use MIPI_DCS_EXIT_SLEEP_MODE in the code.
Yes, i will try to reuse MIPI_DCS_xxx.
+#define ILI9341_DFC 0xb6 /* Display Function Control
* register
*/
This commenting style doesn't work, either just put it after /* the define */ and don't care if the line gets a bit long and checkpatch complains, or
/*
- Put it above the define like this
*/ #define FOO 0x00
Ok, will change this comments.
+/**
- struct ili9341_config - the system specific ILI9341 configuration
Nice with this per-system config, it makes the driver easy to maintain for new users.
Yes, will try to move more system related configurations to this part, instead of hard code.
+static int ili9341_dpi_init(struct ili9341 *ili) +{
ili9341_command(ili, 0xca, 0xc3, 0x08, 0x50);
This stuff is a bit hard to understand, don't you think?
But given that register 0xCA seems undocumented I don't know if there is anything more you can do, so it is OK I suppose.
ili9341_command(ili, ILI9341_POWERB, 0x00, 0xc1, 0x30);
This command is described in the manual page 196. Version: V1.11 Document No.: ILI9341_DS_V1.11.pdf https://dflund.se/~triad/ILI9341_v1.11.pdf
Yes, "ili9341_command(ili, 0xca, 0xc3, 0x08, 0x50);" i ported from st's sdk. will use ILI9341_XXX to replace these magic numbers
And this goes for all the below commands. Please add some more defines from the datasheet and have less magic numbers in the driver.
ili9341_command(ili, ILI9341_POWER_SEQ, 0x64, 0x03, 0x12, 0x81);
ili9341_command(ili, ILI9341_DTCA, 0x85, 0x00, 0x78);
ili9341_command(ili, ILI9341_POWERA, 0x39, 0x2c, 0x00, 0x34, 0x02);
ili9341_command(ili, ILI9341_PRC, 0x20);
ili9341_command(ili, ILI9341_DTCB, 0x00, 0x00);
ili9341_command(ili, ILI9341_FRC, 0x00, 0x1b);
ili9341_command(ili, ILI9341_DFC, 0x0a, 0xa2);
ili9341_command(ili, ILI9341_POWER1, 0x10);
ili9341_command(ili, ILI9341_POWER2, 0x10);
ili9341_command(ili, ILI9341_VCOM1, 0x45, 0x15);
ili9341_command(ili, ILI9341_VCOM2, 0x90);
ili9341_command(ili, ILI9341_MAC, 0xc8);
ili9341_command(ili, ILI9341_3GAMMA_EN, 0x00);
ili9341_command(ili, ILI9341_RGB_INTERFACE, 0xc2);
ili9341_command(ili, ILI9341_DFC, 0x0a, 0xa7, 0x27, 0x04);
ili9341_command(ili, ILI9341_COLUMN_ADDR, 0x00, 0x00, 0x00, 0xef);
ili9341_command(ili, ILI9341_PAGE_ADDR, 0x00, 0x00, 0x01, 0x3f);
ili9341_command(ili, ILI9341_INTERFACE, 0x01, 0x00, 0x06);
if (ili->input == ILI9341_INPUT_PRGB_18_BITS)
ili9341_command(ili, ILI9341_PIXEL_FORMAT, 0x66);
else
ili9341_command(ili, ILI9341_PIXEL_FORMAT, 0x56);
ili9341_command(ili, ILI9341_GRAM);
msleep(200);
I think some of the above should not be hard coded but should instead be stored in fields in struct ili9341_config. I know it can be a bit tedious but it makes things much more clear.
Ok, will go deeper to find out some register configuration move to system config like rgb bus 16/18 bits
ili9341_command(ili, ILI9341_GAMMA, 0x01);
ili9341_command(ili, ILI9341_PGAMMA, 0x0f, 0x29, 0x24, 0x0c, 0x0e,
0x09, 0x4e, 0x78, 0x3c, 0x09,
0x13, 0x05, 0x17, 0x11, 0x00);
ili9341_command(ili, ILI9341_NGAMMA, 0x00, 0x16, 0x1b, 0x04, 0x11,
0x07, 0x31, 0x33, 0x42, 0x05,
0x0c, 0x0a, 0x28, 0x2f, 0x0f);
This should definately be in ili9341_config, as it is a screen property.
In the long run I would like these panels to support setting gamma from userspace etc but it is a big tedious work to get that right so hard-coding a default per-variant is fine.
ok, will refer to panel-ilitek-ili9322 and panel-novatek-nt35510 driver.
You can check in e.g. panel-novatek-nt35510.c how I encoded such sequences in per-variant data.
+static int ili9341_dpi_power_off(struct ili9341 *ili) +{
/* Disable power */
if (!IS_ERR(ili->vcc))
return regulator_disable(ili->vcc);
return 0;
+}
Usually you should also assert RESET when disabling power.
ok
+/* This is the only mode listed for parallel RGB in the datasheet */ +static const struct drm_display_mode rgb_240x320_mode = {
.clock = 6100,
.hdisplay = 240,
.hsync_start = 240 + 10,
.hsync_end = 240 + 10 + 10,
.htotal = 240 + 10 + 10 + 20,
.vdisplay = 320,
.vsync_start = 320 + 4,
.vsync_end = 320 + 4 + 2,
.vtotal = 320 + 4 + 2 + 2,
.vrefresh = 60,
.flags = 0,
.width_mm = 65,
.height_mm = 50,
The width and height should certainly be om the ili9341_config as it is a per-panel property. You can just fill in in in the below .get_modes() function. Or assign the whole mode as part of the ili9341_config if that is easier.
ok, lcd timing part will move to ili9341 config
return drm_panel_add(&ili->panel);
+}
Surplus whitespace here.
ok, will delete it.
mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
mipi_dbi_command(dbi, ILI9341_POWERB, 0x00, 0xc1, 0x30);
mipi_dbi_command(dbi, ILI9341_POWER_SEQ, 0x64, 0x03, 0x12, 0x81);
Some of these are just copies of the above init sequence, so it makes even more sense to just have these settings stored in ili9341_config.
ok.
mipi_dbi_command(dbi, ILI9341_DTCA, 0x85, 0x00, 0x78);
mipi_dbi_command(dbi, ILI9341_POWERA, 0x39, 0x2c, 0x00, 0x34, 0x02);
mipi_dbi_command(dbi, ILI9341_PRC, 0x20);
mipi_dbi_command(dbi, ILI9341_DTCB, 0x00, 0x00);
/* Power Control */
mipi_dbi_command(dbi, ILI9341_POWER1, 0x23);
mipi_dbi_command(dbi, ILI9341_POWER2, 0x10);
/* VCOM */
mipi_dbi_command(dbi, ILI9341_VCOM1, 0x3e, 0x28);
mipi_dbi_command(dbi, ILI9341_VCOM2, 0x86);
/* Memory Access Control */
mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT,
MIPI_DCS_PIXEL_FMT_16BIT);
/* Frame Rate */
mipi_dbi_command(dbi, ILI9341_FRC, 0x00, 0x1b);
/* Gamma */
mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, 0x00);
mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x01);
mipi_dbi_command(dbi, ILI9341_PGAMMA,
0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1,
0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00);
mipi_dbi_command(dbi, ILI9341_NGAMMA,
0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1,
0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f);
It seems to be copies of the stuff above, but why is there a different gamma if you use DBI?
for dbi interface, currently i just copy the code from tiny/ili9341.c. as so many boards use this driver now, like raspberry pi, etc i'm afraid it's will not work after modification. so, just leave the original code there.
I suspect only one of them is really needed and it is not even necessary to set if up in two places.
as i know, dbi interface use spi to transfer video data and register set to panel. but dpi use rgb bus transfer video data, spi set register, they are two different type for drm. i can't register two different interface into drm at the same time. so, i use two path.
for code management, they have some common part, like register init. power on/off process. i will try to reuse most common functions. to make code easier to be understand.
anther question: is there any panel driver have dbi & dpi or dpi & dsi supported? which i mean support two different panel interface in one driver. thanks
+out_enable:
switch (dbidev->rotation) {
default:
addr_mode = ILI9341_MADCTL_MX;
break;> +out_enable:
switch (dbidev->rotation) {
default:
addr_mode = ILI9341_MADCTL_MX;
break;
case 90:
addr_mode = ILI9341_MADCTL_MV;
break;
case 180:
addr_mode = ILI9341_MADCTL_MY;
break;
case 270:
addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY |
ILI9341_MADCTL_MX;
break;
}
addr_mode |= ILI9341_MADCTL_BGR;
mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
mipi_dbi_enable_flush(dbidev, crtc_state, plane_state);
DRM_DEBUG_KMS("initialized display serial interface\n");
+out_exit:
drm_dev_exit(idx);
+}
case 90:
addr_mode = ILI9341_MADCTL_MV;
break;
case 180:
addr_mode = ILI9341_MADCTL_MY;
break;
case 270:
addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY |
ILI9341_MADCTL_MX;
break;
}
addr_mode |= ILI9341_MADCTL_BGR;
mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
Since you use MIPI_DCS_* define here, check if this applies to more of the commands above so you don't need custom defines for them. e.g. ILI9341_SLEEP_OUT 0x11 = MIPI_DCS_EXIT_SLEEP_MODE and that isn't even a register right, it is just a command? (Noted in the beginning.)
ok, will try to reuse MIPI_DCS_xxx
Yours, Linus Walleij
On Thu, May 14, 2020 at 12:22 PM dillon min dillon.minfei@gmail.com wrote:
/* Gamma */
mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, 0x00);
mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x01);
mipi_dbi_command(dbi, ILI9341_PGAMMA,
0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1,
0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00);
mipi_dbi_command(dbi, ILI9341_NGAMMA,
0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1,
0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f);
It seems to be copies of the stuff above, but why is there a different gamma if you use DBI?
for dbi interface, currently i just copy the code from tiny/ili9341.c. as so many boards use this driver now, like raspberry pi, etc i'm afraid it's will not work after modification. so, just leave the original code there.
OK if you move it to ili9341_config it will be clear which panels need this gamma and which panels need another gamma.
I think there should be one ili9341_config for the new st,* variant and one for the old DBI variant.
anther question: is there any panel driver have dbi & dpi or dpi & dsi supported? which i mean support two different panel interface in one driver. thanks
Usually you split the driver in three files becuase a driver can only list one initcall, and also it makes it modularized.
There is nothing in-tree but look at my branch here: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git/log...
You see how I split up the s6e63m0 driver in one SPI part and one DSI part: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git/com... https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git/com...
The overall idea should work the same with DBI.
Yours, Linus Walleij
Yours, Linus Walleij
Hi Linus,
Linus Walleij linus.walleij@linaro.org 于2020年5月14日周四 下午10:08写道:
On Thu, May 14, 2020 at 12:22 PM dillon min dillon.minfei@gmail.com wrote:
/* Gamma */
mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, 0x00);
mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x01);
mipi_dbi_command(dbi, ILI9341_PGAMMA,
0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1,
0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00);
mipi_dbi_command(dbi, ILI9341_NGAMMA,
0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1,
0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f);
It seems to be copies of the stuff above, but why is there a different gamma if you use DBI?
for dbi interface, currently i just copy the code from tiny/ili9341.c. as so many boards use this driver now, like raspberry pi, etc i'm afraid it's will not work after modification. so, just leave the original code there.
OK if you move it to ili9341_config it will be clear which panels need this gamma and which panels need another gamma.
I think there should be one ili9341_config for the new st,* variant and one for the old DBI variant.
Okay, it's a good idea to setup two different configs. ili9341_dbi_config, ili9341_dpi_config. first for old dbi variant, second for dpi variant.
anther question: is there any panel driver have dbi & dpi or dpi & dsi supported? which i mean support two different panel interface in one driver. thanks
Usually you split the driver in three files becuase a driver can only list one initcall, and also it makes it modularized.
There is nothing in-tree but look at my branch here: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git/log...
You see how I split up the s6e63m0 driver in one SPI part and one DSI part: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git/com... https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git/com...
The overall idea should work the same with DBI.
Yours, Linus Walleij
Yours, Linus Walleij
Thanks for share, but i thinks it's not suitable to my case. the difference is for panel s6e63m0 1 spi only for panel register configuration 2 dpi for rgb video data transfer (drm_panel_init(..., DRM_MODE_CONNECTOR_DPI);)
my case: 1 support ili9341 by drm mipi dbi (only spi bus, "3/4 line serial interface", pdf chap 7.6.1), Ie, raspberry pi + ili9341 + spi 2 support ili9341 by spi & dpi (spi for register set, dpi for rgb data, "6/16/18 bit parallel rgb interface", pdf chap 7.6.8) Ie, stm32f429+ili9341+ltdc+spi these two communication type has no dependency with each other.
DBI has much more difference than DPI & SPI, or DSI & SPI in drm. drm_mipi_dbi support both panel register configuration and video data transfer via spi with or without dc pins. also the registration to drm is difference, for dbi is mipi_dbi_dev_init and drm_fbdev_generic_setup for panel driver is drm_panel_init and drm_panel_add at soc's view, we can drive ili9341 to work just by drm mipi dbi (actually it's dbi -> spi bus) without host's lcd or dsi controller but for some panel can't transfer video data by spi, and with dpi or dsi interface, must connect to host's lcd or dsi controller.
so, for ilitek-ili9341 , it's use different dts binding drive to register different client type (dbi, or dpi & spi) to drm. actually, there is a driver tiny/ili9341.c support this panel by spi bus only already. but, from Sam's suggestion, should extend this driver to support dpi & spi interface, this is the background of panel-ilitek-ili9341.c
thanks.
best regards.
dillon
dri-devel@lists.freedesktop.org