notro / fbtft

Linux Framebuffer drivers for small TFT LCD display modules. Development has moved to https://git.kernel.org/cgit/linux/kernel/git/gregkh/staging.git/tree/drivers/staging/fbtft?h=staging-testing
1.85k stars 496 forks source link

How to use FBTFT for 2.0" TFT Rocktech RK020JH100 (ILI9335) #443

Closed JasperNuytten closed 6 years ago

JasperNuytten commented 7 years ago

Hi,

I'm struggling to get this LCD (Rocketech RK020JH100, datasheet attached) properly working together with the FBTFT driver using this board: http://variwiki.com/index.php?title=DART-6UL I have a Yocto build (Jethro Release 01) running on the board, I have build Yocto and the Linux Kernel from source code and am booting from an SD-card.

I succeeded in getting any image on the LCD, unfortunately it's not running fast enough. Whenever I would load an image on the screen or whenever the console needs to output a lot of data I can see a horizontal line running over my screen, which is according to me a sign of a low FPS. (Please correct me if I'm wrong). However, the flickering cursor on the screen that indicates where you type runs smoothly. According to me this means that only new data parts are updated.

I got this result by adding the device in the Device Tree configuration, editing the kernel defconfig file for the board and by editing the fb_ili9325.c driver code. I'm not using SPI and it's also not possible (according to my conclusion using the datasheet) as Rocktech gives me no possibility to select this option. I therefore send the data using the GPIO's of the board, so I'm sending data parallel, I believe this is called a platform device?

I have read about this issue (https://github.com/notro/fbtft/issues/311) where somebody had more or less the same issue as me. The suggestion was bypassing the gpiolib, unfortunately he's working with a Raspberry Pi and I haven't figured out yet how to change the code for a board such as mine.

Anyway, here are all of the details I think are necessary for a first conclusion. Hopefully somebody here will be able to help me. I Have been breaking my head over this for a long time now. Just an FYI, my knowledge level is probably beginner - intermediate concerning Linux/LCD-technology/terminology.

Kind regards


INFO


Device Tree configuration(changed the GPIO's using the 9335 datasheet)

/   {
    itdb28 {
        compatible = "ilitek,ili9325";
        status = "okay";

        rotate = <0>;
        bgr;
        buswidth = <8>;
        reset-gpios = <&gpio3 23 0>;
        dc-gpios = <&gpio3 12 0>;
        cs-gpios = <&gpio3 1 0>;
        wr-gpios = <&gpio3 25 0>;
        rd-gpios = <&gpio3 24 0>;
        db-gpios = <&gpio3 7 0>,
            <&gpio3 19 0>,
            <&gpio3 9 0>,
            <&gpio3 10 0>,
            <&gpio3 11 0>,
            <&gpio3 15 0>,
            <&gpio3 16 0>,
            <&gpio3 17 0>;
            // LED pin drives backlight directly. Use transistor (50mA)
            // led-gpios = <&gpio 4 1>;
        debug = <2>;
        };
    };

defconfig file

CONFIG_FB_TFT=y
CONFIG_FB_TFT_ILI9325=m
CONFIG_FB_TFT_FBTFT_DEVICE=m
CONFIG_FB_FLEX=m

Driver code (original ILI9325.c ==> Edited to work with ILI9335 controller)

static int init_display(struct fbtft_par *par)
{
    fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);

    par->fbtftops.reset(par);

    if (par->gpio.cs != -1)
        gpio_set_value(par->gpio.cs, 0);  /* Activate chip */

    bt &= 0x07;
    vc &= 0x07;
    vrh &= 0x0f;
    vdv &= 0x1f;
    vcm &= 0x3f;

    /* Initialization sequence from ILI9325 Application Notes */

    /* ----------- Start Initial Sequence ----------- */
    //write_reg(par, 0x00E3, 0x3008); /* Set internal timing */
    //write_reg(par, 0x00E7, 0x0012); /* Set internal timing */
    //write_reg(par, 0x00EF, 0x1231); /* Set internal timing */
    write_reg(par, 0x0001, 0x0100); /* set SS and SM bit */
    write_reg(par, 0x0002, 0x0700); /* set 1 line inversion */

    write_reg(par, 0x03, 0x1030);           //normal orientation   
    write_reg(par, 0x05, 0x0133);
    write_reg(par, 0x08, 0x0080);

    write_reg(par, 0x0004, 0x0000); /* Resize register */
    write_reg(par, 0x0008, 0x0207); /* set the back porch and front porch */
    write_reg(par, 0x0009, 0x0000); /* set non-display area refresh cycle */
    //write_reg(par, 0x000A, 0x0000); /* FMARK function */

    write_reg(par, 0x000A, 0x0008); /* FMARK function */

    write_reg(par, 0x000C, 0x0000); /* RGB interface setting */
    write_reg(par, 0x000D, 0x0000); /* Frame marker Position */
    //write_reg(par, 0x000F, 0x0000); /* RGB interface polarity */

    write_reg(par, 0x60, 0x3700); /* RGB interface polarity */

    /* ----------- Power On sequence ----------- */
    write_reg(par, 0x0010, 0x0000); /* SAP, BT[3:0], AP, DSTB, SLP, STB */
    write_reg(par, 0x0011, 0x0007); /* DC1[2:0], DC0[2:0], VC[2:0] */
    write_reg(par, 0x0012, 0x0000); /* VREG1OUT voltage */
    write_reg(par, 0x0013, 0x0000); /* VDV[4:0] for VCOM amplitude */
    mdelay(200); /* Dis-charge capacitor power voltage */
    write_reg(par, 0x0010, /* SAP, BT[3:0], AP, DSTB, SLP, STB */
        (1 << 12) | (bt << 8) | (1 << 7) | (0x01 << 4));
    write_reg(par, 0x0011, 0x220 | vc); /* DC1[2:0], DC0[2:0], VC[2:0] */
    mdelay(50); /* Delay 50ms */
    write_reg(par, 0x0012, vrh); /* Internal reference voltage= Vci; */
    mdelay(50); /* Delay 50ms */
    write_reg(par, 0x0013, vdv << 8); /* Set VDV[4:0] for VCOM amplitude */
    write_reg(par, 0x0029, vcm); /* Set VCM[5:0] for VCOMH */
    write_reg(par, 0x002B, 0x000C); /* Set Frame Rate */
    mdelay(50); /* Delay 50ms */
    write_reg(par, 0x0020, 0x0000); /* GRAM horizontal Address */
    write_reg(par, 0x0021, 0x0000); /* GRAM Vertical Address */

    /*------------------ Set GRAM area --------------- */
    write_reg(par, 0x0050, 0x0000); /* Horizontal GRAM Start Address */
    write_reg(par, 0x0051, 0x00EF); /* Horizontal GRAM End Address */
    write_reg(par, 0x0052, 0x0000); /* Vertical GRAM Start Address */
    write_reg(par, 0x0053, 0x013F); /* Vertical GRAM Start Address */
    write_reg(par, 0x0060, 0xA700); /* Gate Scan Line */
    //write_reg(par, 0x0061, 0x0001); /* NDL,VLE, REV */

    write_reg(par, 0x0061, 0x0000); /* NDL,VLE, REV */

    write_reg(par, 0x006A, 0x0000); /* set scrolling line */

    /*-------------- Partial Display Control --------- */
    write_reg(par, 0x0080, 0x0000);
    write_reg(par, 0x0081, 0x0000);
    write_reg(par, 0x0082, 0x0000);
    write_reg(par, 0x0083, 0x0000);
    write_reg(par, 0x0084, 0x0000);
    write_reg(par, 0x0085, 0x0000);

    /*-------------- Panel Control ------------------- */
    write_reg(par, 0x0090, 0x0010);
    write_reg(par, 0x0092, 0x0600);
    write_reg(par, 0x0007, 0x0133); /* 262K color and display ON */

    return 0;
}

Output dmesg

fb_ili9325: module is from the staging directory, the quality is unknown, you have been warned.
fbtft_of_value: buswidth = 8
fbtft_of_value: debug = 2
fbtft_of_value: rotate = 0
fb_ili9325 itdb28: fbtft_request_one_gpio: 'reset-gpios' = GPIO87
fb_ili9325 itdb28: fbtft_request_one_gpio: 'dc-gpios' = GPIO76
fb_ili9325 itdb28: fbtft_request_one_gpio: 'rd-gpios' = GPIO88
fb_ili9325 itdb28: fbtft_request_one_gpio: 'wr-gpios' = GPIO89
fb_ili9325 itdb28: fbtft_request_one_gpio: 'cs-gpios' = GPIO65
fb_ili9325 itdb28: fbtft_request_one_gpio: 'db-gpios' = GPIO71
fb_ili9325 itdb28: fbtft_request_one_gpio: 'db-gpios' = GPIO83
fb_ili9325 itdb28: fbtft_request_one_gpio: 'db-gpios' = GPIO73
fb_ili9325 itdb28: fbtft_request_one_gpio: 'db-gpios' = GPIO74
fb_ili9325 itdb28: fbtft_request_one_gpio: 'db-gpios' = GPIO75
fb_ili9325 itdb28: fbtft_request_one_gpio: 'db-gpios' = GPIO79
fb_ili9325 itdb28: fbtft_request_one_gpio: 'db-gpios' = GPIO80
fb_ili9325 itdb28: fbtft_request_one_gpio: 'db-gpios' = GPIO81
fb_ili9325 itdb28: Display update: 131 kB/s (1134.553 ms), fps=0 (0.000 ms)
graphics fb0: fb_ili9325 frame buffer, 240x320, 150 KiB video memory, 4 KiB DMA buffer memory, fps=20

RK020JH100 SPEC.PDF

notro commented 7 years ago

Parallel gpio bus is slow. To get any decent performance the board has to support a parallel bus natively. The Pi for example has such a bus, but there's no support in fbtft for it, since the driver isn't in mainline.

I haven't look into this, since SPI with DMA is IMO a much better solution, being supported on most boards.

gpiolib has a gpiod_set_array_value() function that can set multiple gpios at once. I tried it on the Raspbery Pi, but the gain wasn't much. This can be different on your board.

If this is the driver used on your board: http://lxr.free-electrons.com/source/drivers/gpio/gpio-mxc.c

Then it seems that .set_multiple() is supported: mxc_gpio_probe() -> bgpio_init() -> bgpio_setup_io(): gc->set_multiple =

This means that it's possible for you to try it if you want. Here's the code I used (tinydrm, fbtft successor): https://github.com/notro/tinydrm/blob/master/tinydrm-regmap-i80.c You would need to apply it to this function in fbtft: http://lxr.free-electrons.com/ident?i=fbtft_write_gpio8_wr

But be warned, you might end up with little performance gain. If your board supports SPI DMA, I suggest using a SPI display.

JasperNuytten commented 7 years ago

notro,

thanks for the information! I have to conclude that driving the data through the GPIO-interface will be too slow. When attaching one pin to an oscilloscope and having it toggle continuously I only get a frequency of 0.84 Mhz, which isn't sufficient.

So I'll have to figure out another way. I would love to try SPI, unfortunately I don't have the possibility to change the display at the moment, so that's not an option.

Thanks for your assistance!

JasperNuytten commented 7 years ago

notro

Is it possible to use DMA for a platform device (8-bit databus)? Could this not improve the speed?

kind regards

notro commented 7 years ago

Is it possible to use DMA for a platform device (8-bit databus)?

That would require a dedicated hardware bus. As I mentioned the Pi has one, but there is no subsystem for intel8080 type busses in the kernel, so it would require a lot of work to get this supported in the kernel. A hack is ofc possible as I mentioned.

JasperNuytten commented 7 years ago

I'm sorry but maybe I'm not quite understanding what it is that you mean with "hack". Is it using that multiple set method you suggested? If so, I looked it up but unfortunately this method is not yet implemented in the kernel version I'm using (4.1) so that's probably not an option unless I'm completely missing the point.

notro commented 7 years ago

Yes, I refer to .set_multiple().

notro commented 6 years ago

Closing issue since there has been no activity for more than 2 months. Reopen if needed.