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

Convert 16bit to 18bit #487

Closed rogueturnip closed 3 years ago

rogueturnip commented 6 years ago

I'm hoping someone might be able to help me with the last part of a driver I'm working on. This is for the ili9488 which I based of the existing ili9486. The driver works except for the colors.

Im using 4 wire spi so I can only use 18bit color mode but this isn't supported in the fbtft bpp setting. What I was hoping to do is use 16bit and then convert the color. Basically do rgb565 to rgb565 and send to the display controller.

I'm a bit stuck on where to put code and what to do in the module. I'm guessing I need a write vmem function but not sure how do the simple change to the color. Basically for the 5 bit red and blue I need to /32*64 to get it to 6.

Thanks for the help.

rogueturnip commented 6 years ago

If anyone has time to look, I've create a write_vmem function in the driver that changes the input 16bit color depth to 18bit for the SPI driver. I don't have my buydisplay 3.5 LCD here to test my latest changes but would appreciate if someone can tell me it looks go or if I'm missing something big.

int write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len)
{
    u16 *vmem16;
    //u16 *txbuf16 = par->txbuf.buf;
    u32 *txbuf32 = par->txbuf.buf;
    int x, y;
    int ret = 0;

    /* buffer to convert RGB565 -> RBG666 18bpp*/
  signed short *convert_buf = kmalloc_array(par->info->var.xres * par->info->var.yres, sizeof(signed short), GFP_NOIO);

    fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n", __func__, offset, len);

    remain = len/2;
    vmem16 = (u16 *)(par->info->screen_buffer + offset);

    /* converting to rgb666 */
    for (x = 0; x < par->info->var.xres; ++x)
        for (y = 0; y < par->info->var.yres; ++y) {
            u32 pixel = vmem16[y *  par->info->var.xres + x];
            u16 b = pixel & 0x1f;
            u16 g = (pixel & (0x3f << 5)) >> 5;
            u16 r = (pixel & (0x1f << 11)) >> 11;

            u8 r8 = (r & 0x1F) << 3;
            u8 g8 = (g & 0x3F) << 2;
            u8 b8 = (b & 0x1F) << 3;

            pixel = (r8 << 16) + (g8 << 8) + (b8);

            if (pixel > 0x3FFFF)
                pixel = 0x3FFFF;

            convert_buf[y *  par->info->var.xres + x] = pixel;
        }

    gpio_set_value(par->gpio.dc, 1);

    /* Write data */
    txbuf32 = convert_buf;
    ret = par->fbtftops.write(par, par->txbuf.buf, len);
    if (ret < 0)
        dev_err(par->info->device, "%s: write failed and returned: %d\n", __func__, ret);

    kfree(convert_buf);

    return ret;
}
notro commented 6 years ago

Im using 4 wire spi so I can only use 18bit color mode

Why is that? The ILI9488 datasheet list RGB565.

4.7.2. DBI Type-C Option 3 (4-Line Serial Interface)

The available display data formats are:
     8 colors, RGB 1, 1, 1 bits input (set Standard Command 3Ah, DBI [2:0] as 001)
     65K-Colors, RGB 5, 6, 5 bits input data (set Standard Command 3Ah, DBI [2:0] as 101)
     262K-Colors, RGB 6, 6, 6 bits input data (set Standard Command 3Ah, DBI [2:0] as 110) 
rogueturnip commented 6 years ago

The display I’m specifically using is the Buydisplay 3.5 one. When I set up the unit with RGB565 I was getting the images but the color was very wrong. I did some research and went deep into the spec sheet and it appears the spec sheet has some indication that it supports 65K colors but others that have build drivers for Arduino and other MCUs found that with SPI it only does 8 colors or 262K colors, that’s where I landed. If you look into the 4.7.2 section just below what you quoted it only shows the timing form 3bit and 18bit.

I’ve got the formula to do the conversion figured out: RGB(565) is converted to RGB(666) by appending the MSBs R5[4] respectively B5[4] as the low order bit, while G6[5:0] remains untouched: R6[5:0] = (R5[4:0] << 1) + (R5[4] >> 5)  B6[5:0] = (B5[4:0] << 1) + (B5[4] >> 5)  Where I’m struggling is the part that would transfer the bits from the write_vmem into the spi_write. However, I could be going at this totally wrong to. 😊

I really do appreciate the help!

Sent from Mail for Windows 10

From: Noralf Trønnes Sent: November 5, 2017 10:49 AM To: notro/fbtft Cc: rogueturnip; Author Subject: Re: [notro/fbtft] Convert 16bit to 18bit (#487)

Im using 4 wire spi so I can only use 18bit color mode Why is that? The ILI9488 datasheet list RGB565. 4.7.2. DBI Type-C Option 3 (4-Line Serial Interface) The available display data formats are: 8 colors, RGB 1, 1, 1 bits input (set Standard Command 3Ah, DBI [2:0] as 001) 65K-Colors, RGB 5, 6, 5 bits input data (set Standard Command 3Ah, DBI [2:0] as 101) 262K-Colors, RGB 6, 6, 6 bits input data (set Standard Command 3Ah, DBI [2:0] as 110) — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

rogueturnip commented 6 years ago

I should mention, I'm hoping that once I get this working in fbtft to port it over to tinydrm.

rogueturnip commented 6 years ago

Just thought I'd make sure I wasn't crazy, I set the 0x3A register to be 16bit again and the display won't initialize (just the backlight) so I wanted to share this just so you know I tested it. Looks like the only way will be to convert the 16bit to 18bit.

rogueturnip commented 6 years ago

Going to document my progress in case others are playing along.

I copied over the stock write_vmem16_bus8 and started with that as the basis for my code changes. I was getting core dumps when I started mucking with the pixels so I had to put bpp = <24> to make sure there was enough memory. That has cleared up the core dumps. Now I'm working on getting the color converted propertly.

rogueturnip commented 6 years ago

So I think I've got myself stuck. I'm able to do all the changes to the pixels to move to a 18bit value but every variable used to hold vmem or txbuf is a U16. I've tried bumping them up to U32 to hold the 24bit pixel value but I end up with core dumps.

Am I right to assume that fbtft has been designed to work up to 16bit and anything bigger would require deeper changes than just a driver?

Would I be better off to jump right over to tinydrm or will I land in the same spot? I have the option to use the same display but with 8bit parallel, which I wanted to avoid due to limited GPIO, but I can move there if the SPI option won't work.

Thanks!

notro commented 6 years ago

We start with 16 bits per pixel stored in 16-bit and convert it to 18 bits per pixel stored in 24-bit. Or 3 bytes.

Maybe something like this:

/* 16bpp converted to 18bpp stored in 24-bit over 8-bit databus */
int write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len)
{
    u16 *vmem16;
    u8 *txbuf = par->txbuf.buf;
    size_t remain;
    size_t to_copy;
    size_t tx_array_size;
    int i;
    int ret = 0;

    fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n",
        __func__, offset, len);

    /* remaining number of pixels to send */
    remain = len / 2;
    vmem16 = (u16 *)(par->info->screen_buffer + offset);

    if (par->gpio.dc != -1)
        gpio_set_value(par->gpio.dc, 1);

    /* number of pixels that fits in the transmit buffer */
    tx_array_size = par->txbuf.len / 3;

    while (remain) {
        /* number of pixels to copy in one iteration of the loop */
        to_copy = min(tx_array_size, remain);
        dev_dbg(par->info->device, "    to_copy=%zu, remain=%zu\n",
                        to_copy, remain - to_copy);

        for (i = 0; i < to_copy; i++) {
            u16 pixel = vmem16[i];
            u16 b = pixel & 0x1f;
            u16 g = (pixel & (0x3f << 5)) >> 5;
            u16 r = (pixel & (0x1f << 11)) >> 11;

            u8 r8 = (r & 0x1F) << 3;
            u8 g8 = (g & 0x3F) << 2;
            u8 b8 = (b & 0x1F) << 3;

            txbuf[i * 3 + 0] = r8;
            txbuf[i * 3 + 1] = g8;
            txbuf[i * 3 + 2] = b8;
        }

        vmem16 = vmem16 + to_copy;
        ret = par->fbtftops.write(par, par->txbuf.buf, to_copy * 3);
        if (ret < 0)
            return ret;
        remain -= to_copy;
    }

    return ret;
}

Are you using 3 or 4 wire/line SPI mode?

rogueturnip commented 6 years ago

Thank you so much! I can't believe how close I was to your solution. I had to bit shift the rgb left cause that's how the ili9488 expects it but it is all working now.

Thanks again! When I get some time I'm going to try to move the driver to tinydrm

Redferne commented 6 years ago

@rogueturnip Did everything work in the end? I'm stuck with this Kedei TFT from AliExpress. http://s.aliexpress.com/263yeU7n Would your changes work on this display?

rogueturnip commented 6 years ago

It did work out. We got it working thanks to @notro help.

The code was pretty close to what he suggested. I can’t tell you if it will work with your display but if it’s an ili9488 there is a good chance.

Ideally I’d like to get this over on the tinydrm code base since that’s where things should be but I don’t have time to do that port.

From: Redferne Bellini Sent: January 7, 2018 5:08 AM To: notro/fbtft Cc: rogueturnip; Mention Subject: Re: [notro/fbtft] Convert 16bit to 18bit (#487)

@rogueturnip Did everything work in the end? I'm stuck with this Kedei TFT from AliExpress. http://s.aliexpress.com/263yeU7n Would your changes work on this display? — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

kumarmohan7 commented 6 years ago

Hi, Please help to how to interface ili9488 SPI 4 Wire 16 Bit (5-6-5) in Raspberry Pi. i try to interface in 18 bit mode flexfb, but not working,,, Thanks

kumarmohan7 commented 6 years ago

@rogueturnip How to add write_vmem16_bus8 for ili9488 SPI 4 Wire in raspberry pi How to add in fbtft driver in raspberry pi

github-actions[bot] commented 3 years ago

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.