Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QP] Add ILI9488 support. #17438

Merged
merged 1 commit into from
Jul 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/quantum_painter.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Hardware supported:
| GC9A01 | RGB LCD (circular) | 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS = gc9a01_spi` |
| ILI9163 | RGB LCD | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS = ili9163_spi` |
| ILI9341 | RGB LCD | 240x320 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS = ili9341_spi` |
| ILI9488 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS = ili9488_spi` |
| SSD1351 | RGB OLED | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS = ssd1351_spi` |
| ST7789 | RGB LCD | 240x320, 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS = st7789_spi` |

Expand Down Expand Up @@ -654,6 +655,30 @@ The maximum number of displays can be configured by changing the following in yo
#define ILI9341_NUM_DEVICES 3
```

### ILI9488 :id=qp-driver-ili9488

Enabling support for the ILI9488 in Quantum Painter is done by adding the following to `rules.mk`:

```make
QUANTUM_PAINTER_ENABLE = yes
QUANTUM_PAINTER_DRIVERS = ili9488_spi
```

Creating a ILI9488 device in firmware can then be done with the following API:

```c
painter_device_t qp_ili9488_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);
```

The device handle returned from the `qp_ili9488_make_spi_device` function can be used to perform all other drawing operations.

The maximum number of displays can be configured by changing the following in your `config.h` (default is 1):

```c
// 3 displays:
#define ILI9488_NUM_DEVICES 3
```

### SSD1351 :id=qp-driver-ssd1351

Enabling support for the SSD1351 in Quantum Painter is done by adding the following to `rules.mk`:
Expand Down
9 changes: 4 additions & 5 deletions drivers/painter/gc9a01/qp_gc9a01.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,11 @@ const struct tft_panel_dc_reset_painter_driver_vtable_t gc9a01_driver_vtable = {
.flush = qp_tft_panel_flush,
.pixdata = qp_tft_panel_pixdata,
.viewport = qp_tft_panel_viewport,
.palette_convert = qp_tft_panel_palette_convert,
.append_pixels = qp_tft_panel_append_pixels,
.palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,
.append_pixels = qp_tft_panel_append_pixels_rgb565,
},
.rgb888_to_native16bit = qp_rgb888_to_rgb565_swapped,
.num_window_bytes = 2,
.swap_window_coords = false,
.num_window_bytes = 2,
.swap_window_coords = false,
.opcodes =
{
.display_on = GC9A01_CMD_DISPLAY_ON,
Expand Down
9 changes: 4 additions & 5 deletions drivers/painter/ili9xxx/qp_ili9163.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,11 @@ const struct tft_panel_dc_reset_painter_driver_vtable_t ili9163_driver_vtable =
.flush = qp_tft_panel_flush,
.pixdata = qp_tft_panel_pixdata,
.viewport = qp_tft_panel_viewport,
.palette_convert = qp_tft_panel_palette_convert,
.append_pixels = qp_tft_panel_append_pixels,
.palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,
.append_pixels = qp_tft_panel_append_pixels_rgb565,
},
.rgb888_to_native16bit = qp_rgb888_to_rgb565_swapped,
.num_window_bytes = 2,
.swap_window_coords = false,
.num_window_bytes = 2,
.swap_window_coords = false,
.opcodes =
{
.display_on = ILI9XXX_CMD_DISPLAY_ON,
Expand Down
9 changes: 4 additions & 5 deletions drivers/painter/ili9xxx/qp_ili9341.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,11 @@ const struct tft_panel_dc_reset_painter_driver_vtable_t ili9341_driver_vtable =
.flush = qp_tft_panel_flush,
.pixdata = qp_tft_panel_pixdata,
.viewport = qp_tft_panel_viewport,
.palette_convert = qp_tft_panel_palette_convert,
.append_pixels = qp_tft_panel_append_pixels,
.palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,
.append_pixels = qp_tft_panel_append_pixels_rgb565,
},
.rgb888_to_native16bit = qp_rgb888_to_rgb565_swapped,
.num_window_bytes = 2,
.swap_window_coords = false,
.num_window_bytes = 2,
.swap_window_coords = false,
.opcodes =
{
.display_on = ILI9XXX_CMD_DISPLAY_ON,
Expand Down
120 changes: 120 additions & 0 deletions drivers/painter/ili9xxx/qp_ili9488.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright 2021 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#include "qp_internal.h"
#include "qp_comms.h"
#include "qp_ili9488.h"
#include "qp_ili9xxx_opcodes.h"
#include "qp_tft_panel.h"

#ifdef QUANTUM_PAINTER_ILI9488_SPI_ENABLE
# include <qp_comms_spi.h>
#endif // QUANTUM_PAINTER_ILI9488_SPI_ENABLE

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Common

// Driver storage
tft_panel_dc_reset_painter_device_t ili9488_drivers[ILI9488_NUM_DEVICES] = {0};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Initialization

bool qp_ili9488_init(painter_device_t device, painter_rotation_t rotation) {
// clang-format off
const uint8_t ili9488_init_sequence[] = {
// Command, Delay, N, Data[N]
ILI9XXX_CMD_RESET, 120, 0,
ILI9XXX_SET_PGAMMA, 0, 15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F,
ILI9XXX_SET_NGAMMA, 0, 15, 0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F,
ILI9XXX_SET_POWER_CTL_1, 0, 2, 0x17, 0x15,
ILI9XXX_SET_POWER_CTL_2, 0, 1, 0x41,
ILI9XXX_SET_VCOM_CTL_1, 0, 3, 0x00, 0x12, 0x80,
ILI9XXX_SET_PIX_FMT, 0, 1, 0x66,
ILI9XXX_SET_RGB_IF_SIG_CTL, 0, 1, 0x80,
ILI9XXX_SET_FRAME_CTL_NORMAL, 0, 1, 0xA0,
ILI9XXX_SET_INVERSION_CTL, 0, 1, 0x02,
ILI9XXX_SET_FUNCTION_CTL, 0, 2, 0x02, 0x02,
ILI9XXX_SET_IMAGE_FUNCTION, 0, 1, 0x00,
ILI9XXX_SET_PUMP_RATIO_CTL, 0, 4, 0xA9, 0x51, 0x2C, 0x82,
ILI9XXX_CMD_SLEEP_OFF, 5, 0,
ILI9XXX_CMD_DISPLAY_ON, 20, 0
};
// clang-format on
qp_comms_bulk_command_sequence(device, ili9488_init_sequence, sizeof(ili9488_init_sequence));

// Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)
const uint8_t madctl[] = {
[QP_ROTATION_0] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MY,
[QP_ROTATION_90] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MX | ILI9XXX_MADCTL_MV | ILI9XXX_MADCTL_MY,
[QP_ROTATION_180] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MX,
[QP_ROTATION_270] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MV,
};
qp_comms_command_databyte(device, ILI9XXX_SET_MEM_ACS_CTL, madctl[rotation]);

return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Driver vtable

const struct tft_panel_dc_reset_painter_driver_vtable_t ili9488_driver_vtable = {
.base =
{
.init = qp_ili9488_init,
.power = qp_tft_panel_power,
.clear = qp_tft_panel_clear,
.flush = qp_tft_panel_flush,
.pixdata = qp_tft_panel_pixdata,
.viewport = qp_tft_panel_viewport,
.palette_convert = qp_tft_panel_palette_convert_rgb888,
.append_pixels = qp_tft_panel_append_pixels_rgb888,
},
.num_window_bytes = 2,
.swap_window_coords = false,
.opcodes =
{
.display_on = ILI9XXX_CMD_DISPLAY_ON,
.display_off = ILI9XXX_CMD_DISPLAY_OFF,
.set_column_address = ILI9XXX_SET_COL_ADDR,
.set_row_address = ILI9XXX_SET_PAGE_ADDR,
.enable_writes = ILI9XXX_SET_MEM,
},
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SPI

#ifdef QUANTUM_PAINTER_ILI9488_SPI_ENABLE

// Factory function for creating a handle to the ILI9488 device
painter_device_t qp_ili9488_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {
for (uint32_t i = 0; i < ILI9488_NUM_DEVICES; ++i) {
tft_panel_dc_reset_painter_device_t *driver = &ili9488_drivers[i];
if (!driver->base.driver_vtable) {
driver->base.driver_vtable = (const struct painter_driver_vtable_t *)&ili9488_driver_vtable;
driver->base.comms_vtable = (const struct painter_comms_vtable_t *)&spi_comms_with_dc_vtable;
driver->base.native_bits_per_pixel = 24; // RGB888
driver->base.panel_width = panel_width;
driver->base.panel_height = panel_height;
driver->base.rotation = QP_ROTATION_0;
driver->base.offset_x = 0;
driver->base.offset_y = 0;

// SPI and other pin configuration
driver->base.comms_config = &driver->spi_dc_reset_config;
driver->spi_dc_reset_config.spi_config.chip_select_pin = chip_select_pin;
driver->spi_dc_reset_config.spi_config.divisor = spi_divisor;
driver->spi_dc_reset_config.spi_config.lsb_first = false;
driver->spi_dc_reset_config.spi_config.mode = spi_mode;
driver->spi_dc_reset_config.dc_pin = dc_pin;
driver->spi_dc_reset_config.reset_pin = reset_pin;
return (painter_device_t)driver;
}
}
return NULL;
}

#endif // QUANTUM_PAINTER_ILI9488_SPI_ENABLE

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
37 changes: 37 additions & 0 deletions drivers/painter/ili9xxx/qp_ili9488.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2021 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "gpio.h"
#include "qp_internal.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter ILI9488 configurables (add to your keyboard's config.h)

#ifndef ILI9488_NUM_DEVICES
/**
* @def This controls the maximum number of ILI9488 devices that Quantum Painter can communicate with at any one time.
* Increasing this number allows for multiple displays to be used.
*/
# define ILI9488_NUM_DEVICES 1
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter ILI9488 device factories

#ifdef QUANTUM_PAINTER_ILI9488_SPI_ENABLE
/**
* Factory method for an ILI9488 SPI LCD device.
*
* @param panel_width[in] the width of the display panel
* @param panel_height[in] the height of the display panel
* @param chip_select_pin[in] the GPIO pin used for SPI chip select
* @param dc_pin[in] the GPIO pin used for D/C control
* @param reset_pin[in] the GPIO pin used for RST
* @param spi_divisor[in] the SPI divisor to use when communicating with the display
* @param spi_mode[in] the SPI mode to use when communicating with the display
* @return the device handle used with all drawing routines in Quantum Painter
*/
painter_device_t qp_ili9488_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);
#endif // QUANTUM_PAINTER_ILI9488_SPI_ENABLE
1 change: 1 addition & 0 deletions drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
#define ILI9XXX_SET_NGAMMA 0xE1 // Set negative gamma
#define ILI9XXX_SET_DGAMMA_CTL_1 0xE2 // Set digital gamma ctl 1
#define ILI9XXX_SET_DGAMMA_CTL_2 0xE3 // Set digital gamma ctl 2
#define ILI9XXX_SET_IMAGE_FUNCTION 0xE9 // Set image function
#define ILI9XXX_ENABLE_3_GAMMA 0xF2 // Enable 3 gamma
#define ILI9XXX_SET_IF_CTL 0xF6 // Set interface control
#define ILI9XXX_SET_PUMP_RATIO_CTL 0xF7 // Set pump ratio control
Expand Down
4 changes: 2 additions & 2 deletions drivers/painter/ssd1351/qp_ssd1351.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ const struct tft_panel_dc_reset_painter_driver_vtable_t ssd1351_driver_vtable =
.flush = qp_tft_panel_flush,
.pixdata = qp_tft_panel_pixdata,
.viewport = qp_tft_panel_viewport,
.palette_convert = qp_tft_panel_palette_convert,
.append_pixels = qp_tft_panel_append_pixels,
.palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,
.append_pixels = qp_tft_panel_append_pixels_rgb565,
},
.rgb888_to_native16bit = qp_rgb888_to_rgb565_swapped,
.num_window_bytes = 1,
Expand Down
9 changes: 4 additions & 5 deletions drivers/painter/st77xx/qp_st7789.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,11 @@ const struct tft_panel_dc_reset_painter_driver_vtable_t st7789_driver_vtable = {
.flush = qp_tft_panel_flush,
.pixdata = qp_tft_panel_pixdata,
.viewport = qp_tft_panel_viewport,
.palette_convert = qp_tft_panel_palette_convert,
.append_pixels = qp_tft_panel_append_pixels,
.palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,
.append_pixels = qp_tft_panel_append_pixels_rgb565,
},
.rgb888_to_native16bit = qp_rgb888_to_rgb565_swapped,
.num_window_bytes = 2,
.swap_window_coords = false,
.num_window_bytes = 2,
.swap_window_coords = false,
.opcodes =
{
.display_on = ST77XX_CMD_DISPLAY_ON,
Expand Down
60 changes: 30 additions & 30 deletions drivers/painter/tft_panel/qp_tft_panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,6 @@

#define BYTE_SWAP(x) (((((uint16_t)(x)) >> 8) & 0x00FF) | ((((uint16_t)(x)) << 8) & 0xFF00))

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Native pixel format conversion

uint16_t qp_rgb888_to_rgb565(uint8_t r, uint8_t g, uint8_t b) {
uint16_t rgb565 = (((uint16_t)r) >> 3) << 11 | (((uint16_t)g) >> 2) << 5 | (((uint16_t)b) >> 3);
return rgb565;
}

uint16_t qp_rgb888_to_rgb565_swapped(uint8_t r, uint8_t g, uint8_t b) {
uint16_t rgb565 = (((uint16_t)r) >> 3) << 11 | (((uint16_t)g) >> 2) << 5 | (((uint16_t)b) >> 3);
return BYTE_SWAP(rgb565);
}

uint16_t qp_rgb888_to_bgr565(uint8_t r, uint8_t g, uint8_t b) {
uint16_t bgr565 = (((uint16_t)b) >> 3) << 11 | (((uint16_t)g) >> 2) << 5 | (((uint16_t)r) >> 3);
return bgr565;
}

uint16_t qp_rgb888_to_bgr565_swapped(uint8_t r, uint8_t g, uint8_t b) {
uint16_t bgr565 = (((uint16_t)b) >> 3) << 11 | (((uint16_t)g) >> 2) << 5 | (((uint16_t)r) >> 3);
return BYTE_SWAP(bgr565);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter API implementations

Expand Down Expand Up @@ -105,26 +82,49 @@ bool qp_tft_panel_viewport(painter_device_t device, uint16_t left, uint16_t top,

// Stream pixel data to the current write position in GRAM
bool qp_tft_panel_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count) {
qp_comms_send(device, pixel_data, native_pixel_count * sizeof(uint16_t));
struct painter_driver_t *driver = (struct painter_driver_t *)device;
qp_comms_send(device, pixel_data, native_pixel_count * driver->native_bits_per_pixel / 8);
return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Convert supplied palette entries into their native equivalents
bool qp_tft_panel_palette_convert(painter_device_t device, int16_t palette_size, qp_pixel_t *palette) {
struct painter_driver_t * driver = (struct painter_driver_t *)device;
struct tft_panel_dc_reset_painter_driver_vtable_t *vtable = (struct tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable;

bool qp_tft_panel_palette_convert_rgb565_swapped(painter_device_t device, int16_t palette_size, qp_pixel_t *palette) {
for (int16_t i = 0; i < palette_size; ++i) {
RGB rgb = hsv_to_rgb_nocie((HSV){palette[i].hsv888.h, palette[i].hsv888.s, palette[i].hsv888.v});
palette[i].rgb565 = vtable->rgb888_to_native16bit(rgb.r, rgb.g, rgb.b);
RGB rgb = hsv_to_rgb_nocie((HSV){palette[i].hsv888.h, palette[i].hsv888.s, palette[i].hsv888.v});
uint16_t rgb565 = (((uint16_t)rgb.r) >> 3) << 11 | (((uint16_t)rgb.g) >> 2) << 5 | (((uint16_t)rgb.b) >> 3);
palette[i].rgb565 = BYTE_SWAP(rgb565);
}
return true;
}

bool qp_tft_panel_palette_convert_rgb888(painter_device_t device, int16_t palette_size, qp_pixel_t *palette) {
for (int16_t i = 0; i < palette_size; ++i) {
RGB rgb = hsv_to_rgb_nocie((HSV){palette[i].hsv888.h, palette[i].hsv888.s, palette[i].hsv888.v});
palette[i].rgb888.r = rgb.r;
palette[i].rgb888.g = rgb.g;
palette[i].rgb888.b = rgb.b;
}
return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Append pixels to the target location, keyed by the pixel index
bool qp_tft_panel_append_pixels(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices) {

bool qp_tft_panel_append_pixels_rgb565(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices) {
uint16_t *buf = (uint16_t *)target_buffer;
for (uint32_t i = 0; i < pixel_count; ++i) {
buf[pixel_offset + i] = palette[palette_indices[i]].rgb565;
}
return true;
}

bool qp_tft_panel_append_pixels_rgb888(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices) {
for (uint32_t i = 0; i < pixel_count; ++i) {
target_buffer[(pixel_offset + i) * 3 + 0] = palette[palette_indices[i]].rgb888.r;
target_buffer[(pixel_offset + i) * 3 + 1] = palette[palette_indices[i]].rgb888.g;
target_buffer[(pixel_offset + i) * 3 + 2] = palette[palette_indices[i]].rgb888.b;
}
return true;
}
Loading