MIPI DBI/ST7789

    1. Modify the DTS file corresponding to your board form factor, enable spi0, and configure the relevant pins and driver. If you need to use the SPI display as a framebuffer, you may refer to this device tree configuration (using the FBTFT driver). As for the tinydrm driver you mentioned, I have not used it before, and I am unsure whether it will conflict with Rockchip's driver (which requires the display to be bound to a VOP).

      Code: Select all

      /**********spi0**********/
      &spi0 {
      	status = "okay";
      	pinctrl-0 = <&rm_io1_spi0_clk &rm_io4_spi0_mosi &rm_io3_spi0_csn0>;
      	#address-cells = <1>;
      	#size-cells = <0>;
      
      	fbtft@0{
      		status = "okay";
      		compatible = "sitronix,st7789v";
      		reg = <0>;
      		spi-max-frequency = <50000000>;
      		fps = <60>;
      		buswidth = <8>;
      		debug = <0x7>;
      		dc-gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>;      //DC
      		reset-gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_LOW>;    //RES
      	};
      };
    2. After running ./build.sh config, select Kernel and set the defconfig fragments to empty. This will prevent the kernel configuration from being overwritten.
    3. config.txt is a mechanism specific to the Raspberry Pi. On the Luckfox Lyra, the initialization sequence and resolution must be properly configured directly in the driver source code.
  • Thank you for that. I did realize the config.txt is specific to RPi after a short while. I'm aiming for the TinyDRM driver since many sources say the FBTFT driver is deprecated. If all else fails I'll go with FBTFT but I'm hoping with some of my own research we all can find a solution.

    I'm able to get the system to recognize the panel-mipi-dbi-spi driver without conflicts. The problem now is that the display doesn't render anything when I run `modetest -M panel-mipi-dbi -s 31:240x240@XR24`. I do see it flicker a bit, as if it's initializing, but I don't see the test pattern. Per the author, I also created the required "firmware" file (https://github.com/notro/panel-mipi-dbi/wiki); the driver will not initialize without it.

    I disconnected the display and put SCK/MOSI on a scope. I ran the modetest command again, and I see about three or four "bursts" followed by the actual display render data. None of them contain the hex commands in the firmware file. I'm still troubleshooting whether this is a driver problem, a hardware problem, or a "me" problem.

    See attached images for details on what the scope picked up. Expected init sequence is as follows:

    Code: Select all

    # Adafruit ST7789 MiniPiTFT LCD and TFT Bonnet
    # width=240,height=240
    
    command 0x01  # _SWRESET and Delay 150ms
    delay 150
    
    command 0x11  # _SLPOUT and Delay 10ms
    delay 10
    
    command 0x3A 0x55  # _COLMOD and Delay 10ms
    delay 10
    
    command 0x36 0x08  # _MADCTL Botton->Top Refresh
    command 0x21  # _INVON Hack and Delay 10ms
    delay 10
    
    command 0x13  # _NORON and Delay 10ms
    delay 10
    
    # Command 36h sets the read order from frame memory to the display panel
    # Remember to swap width/height on 0/180 rotations
    #command 0x36 0x00 # rotation 0
    command 0x36 0xA0 # rotation 90
    #command 0x36 0xC0 # rotation 180
    #command 0x36 0x60 # rotation 270
    
    command 0x29  # _DISPON and Delay 500ms
    delay 250
    delay 250
    

    The relevant DTS fragment that I know works, at least with respect to the driver loading and seeing the SPI bus is as follows:

    Code: Select all

    /**********SPI**********/
    &spi1 {
    	status = "okay";
    	pinctrl-names = "default";
    	pinctrl-0 = <&spi1_clk_pins &spi1_csn1_pins>;
    	#address-cells = <1>;
    	#size-cells = <0>;
    
    	panel@1 {
    		status = "okay";
    		compatible = "panel", "panel-mipi-dbi-spi";
    		reg = <1>;
    		spi-max-frequency = <20000000>;
    		dc_gpios=<&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>;
    		reset_gpios=<&gpio0 RK_PA2 GPIO_ACTIVE_LOW>;
    		backlight-gpios=<&gpio1 RK_PD3 GPIO_ACTIVE_HIGH>;
    		width-mm=<33>;
    		height-mm=<33>;
                    debug = <3>;
    		write-only;
    		spi-cpha;
    		panel-timing {
    			hactive = <240>;
    			vactive = <240>;
    			hback-porch = <0>;
    			vback-porch = <0>;
    
    			clock-frequency = <0>;
    			hfront-porch = <0>;
    			hsync-len = <0>;
    			vfront-porch = <0>;
    			vsync-len = <0>;
    		};
    	};
    	spidev@1 {
    		compatible = "rockchip,spidev";
    		reg = <1>;
    		status = "disabled";
    		spi-max-frequency = <10000000>;
    	};
    };
    

    I'm still not quite sure how to remap SPI0 to alternate RMII pins, since `<&rm_io1_spi0_clk &rm_io4_spi0_mosi &rm_io3_spi0_csn0>` did not work, but I can live with SPI1.1 instead of SPI0.0 for now.

    Thanks again for any assistance!

    **UPDATE** I compared against my RasPi Zero that does work, and even that isn't sending the correct init sequence. It also requires the clock polarity to be inverted, which, according to the display datasheet, is incorrect. The init sequence taken from Notro's GitHub doesn't work, but another one I found on Reddit does work. So at this point I'm lost.

    Attachments
    scope-overview.png
    scope-chunk4.png
    scope-chunk3.png
    scope-chunk2.png
    scope-chunk1.png
    Last edited by nobuddy2012 on 2026-05-29 20:52, edited 1 time in total.
  • One more thing to add -- I have confirmed that my scope is not (entirely) reading dud values. I rebuilt the kernel with the trace option and found that the modetest, by way of the SPI controller, is in fact sending the wrong data.

    Code: Select all

            modetest-1679    [002] .....   147.744264: spi_transfer_start: spi1.1 75c4c684 len=9 tx=[00-00-00-00-00-00-00-00-2a] rx=[]
            modetest-1679    [002] .....   147.744407: spi_transfer_start: spi1.1 75c4c684 len=9 tx=[80-40-20-1e-f0-00-00-00-00] rx=[]
            modetest-1679    [002] .....   147.744586: spi_transfer_start: spi1.1 75c4c684 len=9 tx=[00-00-00-00-00-00-00-00-2b] rx=[]
            modetest-1679    [002] .....   147.744695: spi_transfer_start: spi1.1 75c4c684 len=9 tx=[80-40-20-1e-f0-00-00-00-00] rx=[]
            modetest-1679    [002] .....   147.744797: spi_transfer_start: spi1.1 0853b70c len=9 tx=[00-00-00-00-00-00-00-00-2c] rx=[]
    

    I know this isn't terribly helpful to resolving my issue but I'm putting this here for any future viewers. I've reached out to the TinyDRM author in hopes he might have some thoughts.

    If all else fails, I have confirmed FBTFT works. So thank you very much @Crocodile!

  • Good news! I finally got it working! I can get `modeset` to display a test pattern. In the absence of other guides, I thought I would post here what I did to accomplish this.

    1. Modify the device tree! I added the following to the end of my `rk3506-luckfox-lyra-sd.dts`:

      Code: Select all

      /**********DISPLAY**********/
      &pinctrl {
      	// Reset Pin -- Pull Up
      	rm_io2 {
      		rm_io2_pins {
      			rockchip,pins = <0 RK_PA2 7 &pcfg_pull_up>;
      		};
      	};
      	// DC Pin -- Pull None
      	rm_io3 {
      		rm_io3_pins {
      			rockchip,pins = <0 RK_PA3 7 &pcfg_pull_none>;
      		};
      	};
      
      };
      
      &spi1 {
      	status = "okay";
      	pinctrl-names = "default";
      	pinctrl-0 = <&spi1_clk_pins &spi1_csn1_pins &rm_io2_pins &rm_io3_pins>;
      	#address-cells = <1>;
      	#size-cells = <0>;
      
      	panel@1 {
      		status = "okay";
      		compatible = "panel", "panel-mipi-dbi-spi";
      		reg = <1>;
      		spi-max-frequency = <80000000>;
      		dc-gpios=<&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>;
      		reset-gpios=<&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>;
      		backlight=<&backlight_mipi_dbi>;
      		width-mm=<33>;
      		height-mm=<33>;
      		write-only;
      		spi-cpha;
      		spi-cpol;
      		panel-timing {
      			hactive = <240>;
      			vactive = <240>;
      			hback-porch = <0>;
      			vback-porch = <0>;
      
      			clock-frequency = <0>;
      			hfront-porch = <0>;
      			hsync-len = <0>;
      			vfront-porch = <0>;
      			vsync-len = <0>;
      		};
      	};
      
      	spidev@1 {
      		compatible = "rockchip,spidev";
      		reg = <1>;
      		status = "disabled";
      		spi-max-frequency = <10000000>;
      	};
      };
      

      I have defined backlight_mipi_dbi above in the first block as such. This is optional. Just remember to remove or change the backlight parameter from your `panel@1` block if you do:

      Code: Select all

      backlight_mipi_dbi: backlight@1 {
      		status = "okay";
      		pinctrl-names = "default";
      		#address-cells = <1>;
      		#size-cells = <0>;
      		reg=<0>;
      		compatible = "gpio-backlight";
      		gpios = <&gpio1 RK_PD3 GPIO_ACTIVE_HIGH>;
      };
      
    2. Wire up the LCD module! I tested this with Adafruit 1.54" 240x240 Wide Angle TFT LCD Display . Per the above Device Tree, the pins should be as follows:
      RM_IO2 - RST (Reset)
      RM_IO3 - DC (Data/Command Select)
      RM_IO7 - TFTCS (Chip Select)
      RM_IO8 - SCK (SPI Clock)
      RM_IO9 - MOSI (SPI Master Out)
      RM_IO31 - BL (Backlight)
      If you have altered the pin muxes, you know what you're doing, so you're on your own here.

    3. Generate the display firmware! To do this, you will need a Python tool created by the mipi-panel-dbi driver author. This tool can be found at https://github.com/notro/panel-mipi-dbi/. DO NOT USE HIS EXAMPLE IN THE WIKI! There is a bug in the driver that prevents the firmware init codes from being sent when the kernel modeset occurs. Use this instead:

      Code: Select all

      command 0x11                            # 0x1000011
      delay 255                               # 0x20000ff
      
      command 0x36 0x78                       # MADCTL MX | MV | ML | RGB
      
      command 0x3a 0x05                       # 0x100003a 0x05
      command 0x21                            # 0x1000021
      command 0x2a 0x00 0x01 0x00 0x3f        # 0x100002a 0x00 0x01 0x00 0x3f
      command 0x2b 0x00 0x00 0x00 0xef        # 0x100002b 0x00 0x00 0x00 0xef
      command 0xb2 0x0c 0x0c 0x00 0x33 0x33   # 0x10000b2 0x0c 0x0c 0x00 0x33 0x33
      command 0xb7 0x35                       # 0x10000b7 0x35
      command 0xbb 0x1a                       # 0x10000bb 0x1a # Set VCOM to 0.75V
      command 0xc0 0x0c                       # 0x10000c0 0x0c
      command 0xc2 0x01                       # 0x10000c2 0x01
      command 0xc3 0x0b                       # 0x10000c3 0x0b # VRHS 4.1V + ...
      command 0xc4 0x20                       # 0x10000c4 0x20
      command 0xc6 0x0f                       # 0x10000c6 0x0f
      command 0xd0 0xa4 0xa1                  # 0x10000d0 0xa4 0xa1
      command 0xE0 0x00 0x19 0x1E 0x0A 0x09 0x15 0x3D 0x44 0x51 0x12 0x03 0x00 0x3F 0x3F # Set Positive Voltage Gamma Control
      command 0xE1 0x00 0x18 0x1E 0x0A 0x09 0x25 0x3F 0x43 0x52 0x33 0x03 0x00 0x3F 0x3F # Set Negative Voltage Gamma Control
      command 0x29                            # 0x1000029
      

      There is more to this, and it can be found at https://github.com/tquiggle/Diecast-Rem ... 1.3-HAT.sh. I am posting this here in case that link ever goes stale. Save the output file as panel.bin. It might also be a good idea to add this to your buildroot config so it automatically goes into /lib/firmware/panel.bin.

    4. Flash and fire it up! If you have not added the firmware.bin file to your buildroot config, you will need to add this to /lib/firmware as soon as you boot. Once everything is set, run `modetest -M panel-mipi-dbi -s 31:240x240@XR24` in a terminal and be amazed!

    Attachments
    testpattern.jpg
  • Congratulations on successfully using TinyDrm, and thank you for sharing.