diff --git a/docs/quantum_painter.md b/docs/quantum_painter.md index dc855b1bf67..7f524b07ee3 100644 --- a/docs/quantum_painter.md +++ b/docs/quantum_painter.md @@ -24,6 +24,7 @@ Supported devices: | 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` | +| ILI9486 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9486_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` | | ST7735 | RGB LCD | 132x162, 80x160 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7735_spi` | @@ -281,6 +282,39 @@ The maximum number of displays can be configured by changing the following in yo Native color format rgb565 is compatible with ILI9341 +#### ** ILI9486 ** + +Enabling support for the ILI9486 in Quantum Painter is done by adding the following to `rules.mk`: + +```make +QUANTUM_PAINTER_ENABLE = yes +QUANTUM_PAINTER_DRIVERS += ili9486_spi +``` + +Creating a ILI9486 device in firmware can then be done with the following API: + +```c +painter_device_t qp_ili9486_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); +``` + +There's another variant for this [Waveshare module](https://www.waveshare.com/wiki/3.5inch_TFT_Touch_Shield), because it has a quirky SPI->Parallel converter. You can create it with: + +```c +painter_device_t qp_ili9486_make_spi_waveshare_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 these functions 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 ILI9486_NUM_DEVICES 3 +``` + +Native color format rgb888 is compatible with ILI9486 +Native color format rgb565 is compatible with ILI9486 Waveshare + #### ** ILI9488 ** Enabling support for the ILI9488 in Quantum Painter is done by adding the following to `rules.mk`: diff --git a/drivers/painter/comms/qp_comms_spi.h b/drivers/painter/comms/qp_comms_spi.h index ff323c3c10e..c39ea95f726 100644 --- a/drivers/painter/comms/qp_comms_spi.h +++ b/drivers/painter/comms/qp_comms_spi.h @@ -38,6 +38,7 @@ typedef struct qp_comms_spi_dc_reset_config_t { bool command_params_uses_command_pin; // keep D/C held low when sending command sequences for data bytes } qp_comms_spi_dc_reset_config_t; +bool qp_comms_spi_dc_reset_init(painter_device_t device); void qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd); uint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void* data, uint32_t byte_count); void qp_comms_spi_dc_reset_bulk_command_sequence(painter_device_t device, const uint8_t* sequence, size_t sequence_len); diff --git a/drivers/painter/ili9xxx/qp_ili9486.c b/drivers/painter/ili9xxx/qp_ili9486.c new file mode 100644 index 00000000000..c4f3c15cecf --- /dev/null +++ b/drivers/painter/ili9xxx/qp_ili9486.c @@ -0,0 +1,298 @@ +// Copyright 2021 Nick Brassel (@tzarc) +// Copyright 2023 Pablo Martinez (@elpekenin) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "qp_internal.h" +#include "qp_comms.h" +#include "qp_ili9486.h" +#include "qp_ili9xxx_opcodes.h" +#include "qp_tft_panel.h" + +#ifdef QUANTUM_PAINTER_ILI9486_SPI_ENABLE +# include "spi_master.h" +# include +#endif // QUANTUM_PAINTER_ILI9486_SPI_ENABLE + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common + +// Driver storage +tft_panel_dc_reset_painter_device_t ili9486_drivers[ILI9486_NUM_DEVICES] = {0}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Initialization + +bool qp_ili9486_init(painter_device_t device, painter_rotation_t rotation) { + // clang-format off + const uint8_t ili9486_init_sequence[] = { + // Command, Delay, N, Data[N] + ILI9XXX_CMD_RESET, 120, 0, + ILI9XXX_SET_PIX_FMT, 0, 1, 0x55, + ILI9XXX_SET_PGAMMA, 0, 15, 0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98, 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00, + ILI9XXX_SET_NGAMMA, 0, 15, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00, + ILI9XXX_SET_POWER_CTL_1, 0, 2, 0x0D, 0x0D, + ILI9XXX_SET_POWER_CTL_2, 0, 2, 0x43, 0x00, + ILI9XXX_SET_POWER_CTL_3, 0, 1, 0x00, + ILI9XXX_SET_VCOM_CTL_1, 0, 4, 0x00, 0x48, 0x00, 0x48, + ILI9XXX_SET_INVERSION_CTL, 0, 1, 0x02, + }; + // clang-format on + qp_comms_bulk_command_sequence(device, ili9486_init_sequence, sizeof(ili9486_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, + [QP_ROTATION_90] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MV, + [QP_ROTATION_180] = ILI9XXX_MADCTL_BGR, + [QP_ROTATION_270] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MV, + }; + const uint8_t functl[] = { + [QP_ROTATION_0] = 0x42, + [QP_ROTATION_90] = 0x62, + [QP_ROTATION_180] = 0x22, + [QP_ROTATION_270] = 0x02, + }; + + // clang-format off + uint8_t rotation_sequence[] = { + // Command, Delay, N, Data[N] + ILI9XXX_SET_MEM_ACS_CTL, 0, 1, madctl[rotation], + ILI9XXX_SET_FUNCTION_CTL, 0, 2, 0x00, functl[rotation], + ILI9XXX_CMD_SLEEP_OFF, 5, 0, + ILI9XXX_CMD_DISPLAY_ON, 5, 0, + }; + // clang-format on + qp_comms_bulk_command_sequence(device, rotation_sequence, sizeof(rotation_sequence)); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Driver vtable + +// waveshare variant needs some tweaks due to shift registers +static void qp_comms_spi_dc_reset_send_command_odd_cs_pulse(painter_device_t device, uint8_t cmd) { + painter_driver_t * driver = (painter_driver_t *)device; + qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config; + + writePinLow(comms_config->spi_config.chip_select_pin); + qp_comms_spi_dc_reset_send_command(device, cmd); + writePinHigh(comms_config->spi_config.chip_select_pin); +} + +static uint32_t qp_comms_spi_send_data_odd_cs_pulse(painter_device_t device, const void *data, uint32_t byte_count) { + painter_driver_t * driver = (painter_driver_t *)device; + qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config; + + uint32_t bytes_remaining = byte_count; + const uint8_t *p = (const uint8_t *)data; + uint32_t max_msg_length = 1024; + + writePinHigh(comms_config->dc_pin); + while (bytes_remaining > 0) { + uint32_t bytes_this_loop = QP_MIN(bytes_remaining, max_msg_length); + bool odd_bytes = bytes_this_loop & 1; + + // send data + writePinLow(comms_config->spi_config.chip_select_pin); + spi_transmit(p, bytes_this_loop); + p += bytes_this_loop; + + // extra CS toggle, for alignment + if (odd_bytes) { + writePinHigh(comms_config->spi_config.chip_select_pin); + writePinLow(comms_config->spi_config.chip_select_pin); + } + + bytes_remaining -= bytes_this_loop; + } + + return byte_count - bytes_remaining; +} + +static uint32_t qp_ili9486_send_data_toggling(painter_device_t device, const uint8_t *data, uint32_t byte_count) { + painter_driver_t * driver = (painter_driver_t *)device; + qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config; + + uint32_t ret; + for (uint8_t j = 0; j < byte_count; ++j) { + writePinLow(comms_config->spi_config.chip_select_pin); + ret = qp_comms_spi_dc_reset_send_data(device, &data[j], 1); + writePinHigh(comms_config->spi_config.chip_select_pin); + } + + return ret; +} + +static void qp_comms_spi_send_command_sequence_odd_cs_pulse(painter_device_t device, const uint8_t *sequence, size_t sequence_len) { + for (size_t i = 0; i < sequence_len;) { + uint8_t command = sequence[i]; + uint8_t delay = sequence[i + 1]; + uint8_t num_bytes = sequence[i + 2]; + + qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, command); + if (num_bytes > 0) { + qp_ili9486_send_data_toggling(device, &sequence[i + 3], num_bytes); + } + + if (delay > 0) { + wait_ms(delay); + } + i += (3 + num_bytes); + } +} + +static bool qp_ili9486_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) { + painter_driver_t * driver = (painter_driver_t *)device; + tft_panel_dc_reset_painter_driver_vtable_t *vtable = (tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable; + + // Fix up the drawing location if required + left += driver->offset_x; + right += driver->offset_x; + top += driver->offset_y; + bottom += driver->offset_y; + + // Check if we need to manually swap the window coordinates based on whether or not we're in a sideways rotation + if (vtable->swap_window_coords && (driver->rotation == QP_ROTATION_90 || driver->rotation == QP_ROTATION_270)) { + uint16_t temp; + + temp = left; + left = top; + top = temp; + + temp = right; + right = bottom; + bottom = temp; + } + + // Set up the x-window + uint8_t xbuf[4] = {left >> 8, left & 0xFF, right >> 8, right & 0xFF}; + qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, vtable->opcodes.set_column_address); + qp_ili9486_send_data_toggling(device, xbuf, 4); + + // Set up the y-window + uint8_t ybuf[4] = {top >> 8, top & 0xFF, bottom >> 8, bottom & 0xFF}; + qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, vtable->opcodes.set_row_address); + qp_ili9486_send_data_toggling(device, ybuf, 4); + + // Lock in the window + qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, vtable->opcodes.enable_writes); + return true; +} + +// Regular +const tft_panel_dc_reset_painter_driver_vtable_t ili9486_driver_vtable = { + .base = + { + .init = qp_ili9486_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_rgb565_swapped, + .append_pixels = qp_tft_panel_append_pixels_rgb565, + .append_pixdata = qp_tft_panel_append_pixdata, + }, + .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, + }, +}; + +// Waveshare tweaks +const tft_panel_dc_reset_painter_driver_vtable_t ili9486_waveshare_driver_vtable = { + .base = + { + .init = qp_ili9486_init, + .power = qp_tft_panel_power, + .clear = qp_tft_panel_clear, + .flush = qp_tft_panel_flush, + .pixdata = qp_tft_panel_pixdata, + .viewport = qp_ili9486_viewport, + .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped, + .append_pixels = qp_tft_panel_append_pixels_rgb565, + .append_pixdata = qp_tft_panel_append_pixdata, + }, + .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, + }, +}; + +static const painter_comms_with_command_vtable_t spi_comms_odd_cs_pulse_vtable = { + .base = + { + .comms_init = qp_comms_spi_dc_reset_init, + .comms_start = qp_comms_spi_start, + .comms_send = qp_comms_spi_send_data_odd_cs_pulse, + .comms_stop = qp_comms_spi_stop, + }, + .send_command = qp_comms_spi_dc_reset_send_command_odd_cs_pulse, + .bulk_command_sequence = qp_comms_spi_send_command_sequence_odd_cs_pulse, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SPI + +#ifdef QUANTUM_PAINTER_ILI9486_SPI_ENABLE + +// Factory function for creating a handle to the ILI9486 device +painter_device_t qp_ili9486_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 < ILI9486_NUM_DEVICES; ++i) { + tft_panel_dc_reset_painter_device_t *driver = &ili9486_drivers[i]; + if (!driver->base.driver_vtable) { + driver->base.driver_vtable = (const painter_driver_vtable_t *)&ili9486_driver_vtable; + driver->base.comms_vtable = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable; + driver->base.native_bits_per_pixel = 16; // RGB565 + 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; + + if (!qp_internal_register_device((painter_device_t)driver)) { + memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t)); + return NULL; + } + + return (painter_device_t)driver; + } + } + return NULL; +} + +painter_device_t qp_ili9486_make_spi_waveshare_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) { + painter_device_t device = qp_ili9486_make_spi_device(panel_width, panel_height, chip_select_pin, dc_pin, reset_pin, spi_divisor, spi_mode); + if (device) { + tft_panel_dc_reset_painter_device_t *driver = (tft_panel_dc_reset_painter_device_t *)device; + driver->base.driver_vtable = (const painter_driver_vtable_t *)&ili9486_waveshare_driver_vtable; + driver->base.comms_vtable = (const painter_comms_vtable_t *)&spi_comms_odd_cs_pulse_vtable; + } + return device; +} + +#endif // QUANTUM_PAINTER_ILI9486_SPI_ENABLE + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/drivers/painter/ili9xxx/qp_ili9486.h b/drivers/painter/ili9xxx/qp_ili9486.h new file mode 100644 index 00000000000..9976a78da49 --- /dev/null +++ b/drivers/painter/ili9xxx/qp_ili9486.h @@ -0,0 +1,52 @@ +// Copyright 2021 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "gpio.h" +#include "qp_internal.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantum Painter ILI9486 configurables (add to your keyboard's config.h) + +#ifndef ILI9486_NUM_DEVICES +/** + * @def This controls the maximum number of ILI9486 devices that Quantum Painter can communicate with at any one time. + * Increasing this number allows for multiple displays to be used. + */ +# define ILI9486_NUM_DEVICES 1 +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantum Painter ILI9486 device factories + +#ifdef QUANTUM_PAINTER_ILI9486_SPI_ENABLE +/** + * Factory method for an ILI9486 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_ili9486_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); + +/** + * Factory method for an ILI9486 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_ili9486_make_spi_waveshare_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_ILI9486_SPI_ENABLE diff --git a/drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h b/drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h index f57e638e034..906f6cd7729 100644 --- a/drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h +++ b/drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h @@ -70,6 +70,7 @@ #define ILI9XXX_SET_LIGHT_CTL_8 0xBF // Set backlight ctl 8 #define ILI9XXX_SET_POWER_CTL_1 0xC0 // Set power ctl 1 #define ILI9XXX_SET_POWER_CTL_2 0xC1 // Set power ctl 2 +#define ILI9XXX_SET_POWER_CTL_3 0xC2 // Set power ctl 3 #define ILI9XXX_SET_VCOM_CTL_1 0xC5 // Set VCOM ctl 1 #define ILI9XXX_SET_VCOM_CTL_2 0xC7 // Set VCOM ctl 2 #define ILI9XXX_POWER_CTL_A 0xCB // Set power control A diff --git a/quantum/painter/qp.h b/quantum/painter/qp.h index 873a9d9f329..02acbf589a3 100644 --- a/quantum/painter/qp.h +++ b/quantum/painter/qp.h @@ -509,6 +509,12 @@ int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, pai # define ILI9341_NUM_DEVICES 0 #endif // QUANTUM_PAINTER_ILI9341_ENABLE +#ifdef QUANTUM_PAINTER_ILI9486_ENABLE +# include "qp_ili9486.h" +#else // QUANTUM_PAINTER_ILI9486_ENABLE +# define ILI9486_NUM_DEVICES 0 +#endif // QUANTUM_PAINTER_ILI9486_ENABLE + #ifdef QUANTUM_PAINTER_ILI9488_ENABLE # include "qp_ili9488.h" #else // QUANTUM_PAINTER_ILI9488_ENABLE diff --git a/quantum/painter/qp_internal.c b/quantum/painter/qp_internal.c index 0e81467e26f..1f0f9817967 100644 --- a/quantum/painter/qp_internal.c +++ b/quantum/painter/qp_internal.c @@ -11,6 +11,7 @@ enum { // NOTE: We intentionally do not include surfaces here, despite them conforming to the same API. QP_NUM_DEVICES = (ILI9163_NUM_DEVICES) // ILI9163 + (ILI9341_NUM_DEVICES) // ILI9341 + + (ILI9486_NUM_DEVICES) // ILI9486 + (ILI9488_NUM_DEVICES) // ILI9488 + (ST7789_NUM_DEVICES) // ST7789 + (ST7735_NUM_DEVICES) // ST7735 diff --git a/quantum/painter/rules.mk b/quantum/painter/rules.mk index ca81cffb032..d991a6d7423 100644 --- a/quantum/painter/rules.mk +++ b/quantum/painter/rules.mk @@ -9,6 +9,7 @@ VALID_QUANTUM_PAINTER_DRIVERS := \ surface \ ili9163_spi \ ili9341_spi \ + ili9486_spi \ ili9488_spi \ st7735_spi \ st7789_spi \ @@ -80,6 +81,17 @@ define handle_quantum_painter_driver $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \ $(DRIVER_PATH)/painter/ili9xxx/qp_ili9341.c \ + else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ili9486_spi) + QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes + QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes + OPT_DEFS += -DQUANTUM_PAINTER_ILI9486_ENABLE -DQUANTUM_PAINTER_ILI9486_SPI_ENABLE + COMMON_VPATH += \ + $(DRIVER_PATH)/painter/tft_panel \ + $(DRIVER_PATH)/painter/ili9xxx + SRC += \ + $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \ + $(DRIVER_PATH)/painter/ili9xxx/qp_ili9486.c \ + else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ili9488_spi) QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes