qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

qp_gc9a01.c (6633B)


      1 // Copyright 2021 Paul Cotter (@gr1mr3aver)
      2 // Copyright 2023 Nick Brassel (@tzarc)
      3 // SPDX-License-Identifier: GPL-2.0-or-later
      4 
      5 #include "qp_internal.h"
      6 #include "qp_comms.h"
      7 #include "qp_gc9a01.h"
      8 #include "qp_gc9xxx_opcodes.h"
      9 #include "qp_gc9a01_opcodes.h"
     10 #include "qp_tft_panel.h"
     11 
     12 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     13 // Driver storage
     14 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     15 
     16 tft_panel_dc_reset_painter_device_t gc9a01_drivers[GC9A01_NUM_DEVICES] = {0};
     17 
     18 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     19 // Initialization
     20 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     21 __attribute__((weak)) bool qp_gc9a01_init(painter_device_t device, painter_rotation_t rotation) {
     22     // A lot of these "unknown" opcodes are sourced from other OSS projects and are seemingly required for this display to function.
     23     // clang-format off
     24 
     25     const uint8_t gc9a01_init_sequence[] = {
     26         // Command,                 Delay,  N, Data[N]
     27         GC9XXX_SET_INTER_REG_ENABLE1,   0,  0,
     28         GC9XXX_SET_INTER_REG_ENABLE2,   0,  0,
     29         0x84,                           0,  1, 0x40,
     30         GC9A01_SET_FUNCTION_CTL,        0,  3, 0x00, GC9A01_SOURCE_OUTPUT_SCAN_DIRECTION_S360_TO_S1 | GC9A01_GATE_OUTPUT_SCAN_DIRECTION_G1_TO_G32, GC9A01_LCD_DRIVE_LINE_240, // Only works if the previous command is present (undocumented)
     31         GC9A01_SET_POWER_CTL_2,         0,  1, 0x20,
     32         GC9A01_SET_POWER_CTL_3,         0,  1, 0x20,
     33         GC9A01_SET_POWER_CTL_4,         0,  1, 0x22,
     34         GC9XXX_SET_GAMMA1,              0,  6, 0x45, 0x09, 0x08, 0x08, 0x26, 0x2A,
     35         GC9XXX_SET_GAMMA2,              0,  6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6F,
     36         GC9A01_SET_GAMMA3,              0,  6, 0x45, 0x09, 0x08, 0x08, 0x26, 0x2A,
     37         GC9A01_SET_GAMMA4,              0,  6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6F,
     38         0x66,                           0, 10, 0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00,
     39         0x67,                           0, 10, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98,
     40         GC9XXX_CMD_TEARING_ON,          0,  0,
     41         GC9XXX_SET_PIXEL_FORMAT,        0,  1, GC9A01_PIXEL_FORMAT_16_BPP_DBI,
     42         GC9XXX_CMD_INVERT_ON,           0,  0,
     43         GC9XXX_CMD_SLEEP_OFF,           120, 0,
     44         GC9XXX_CMD_DISPLAY_ON,          20,  0
     45     };
     46     // clang-format on
     47 
     48     if (!qp_comms_bulk_command_sequence(device, gc9a01_init_sequence, sizeof(gc9a01_init_sequence))) {
     49         return false;
     50     }
     51 
     52     // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)
     53     const uint8_t madctl[] = {
     54         [QP_ROTATION_0]   = GC9XXX_MADCTL_BGR,
     55         [QP_ROTATION_90]  = GC9XXX_MADCTL_BGR | GC9XXX_MADCTL_MX | GC9XXX_MADCTL_MV,
     56         [QP_ROTATION_180] = GC9XXX_MADCTL_BGR | GC9XXX_MADCTL_MX | GC9XXX_MADCTL_MY,
     57         [QP_ROTATION_270] = GC9XXX_MADCTL_BGR | GC9XXX_MADCTL_MV | GC9XXX_MADCTL_MY,
     58     };
     59     return qp_comms_command_databyte(device, GC9XXX_SET_MEM_ACS_CTL, madctl[rotation]);
     60 }
     61 
     62 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     63 // Driver vtable
     64 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     65 
     66 const tft_panel_dc_reset_painter_driver_vtable_t gc9a01_driver_vtable = {
     67     .base =
     68         {
     69             .init            = qp_gc9a01_init,
     70             .power           = qp_tft_panel_power,
     71             .clear           = qp_tft_panel_clear,
     72             .flush           = qp_tft_panel_flush,
     73             .pixdata         = qp_tft_panel_pixdata,
     74             .viewport        = qp_tft_panel_viewport,
     75             .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,
     76             .append_pixels   = qp_tft_panel_append_pixels_rgb565,
     77             .append_pixdata  = qp_tft_panel_append_pixdata,
     78         },
     79     .num_window_bytes   = 2,
     80     .swap_window_coords = false,
     81     .opcodes =
     82         {
     83             .display_on         = GC9XXX_CMD_DISPLAY_ON,
     84             .display_off        = GC9XXX_CMD_DISPLAY_OFF,
     85             .set_column_address = GC9XXX_SET_COL_ADDR,
     86             .set_row_address    = GC9XXX_SET_ROW_ADDR,
     87             .enable_writes      = GC9XXX_SET_MEM,
     88         },
     89 };
     90 
     91 #ifdef QUANTUM_PAINTER_GC9A01_SPI_ENABLE
     92 // Factory function for creating a handle to the ILI9341 device
     93 painter_device_t qp_gc9a01_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) {
     94     for (uint32_t i = 0; i < GC9A01_NUM_DEVICES; ++i) {
     95         tft_panel_dc_reset_painter_device_t *driver = &gc9a01_drivers[i];
     96         if (!driver->base.driver_vtable) {
     97             driver->base.driver_vtable         = (const painter_driver_vtable_t *)&gc9a01_driver_vtable;
     98             driver->base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;
     99             driver->base.native_bits_per_pixel = 16; // RGB565
    100             driver->base.panel_width           = panel_width;
    101             driver->base.panel_height          = panel_height;
    102             driver->base.rotation              = QP_ROTATION_0;
    103             driver->base.offset_x              = 0;
    104             driver->base.offset_y              = 0;
    105 
    106             // SPI and other pin configuration
    107             driver->base.comms_config                                   = &driver->spi_dc_reset_config;
    108             driver->spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;
    109             driver->spi_dc_reset_config.spi_config.divisor              = spi_divisor;
    110             driver->spi_dc_reset_config.spi_config.lsb_first            = false;
    111             driver->spi_dc_reset_config.spi_config.mode                 = spi_mode;
    112             driver->spi_dc_reset_config.dc_pin                          = dc_pin;
    113             driver->spi_dc_reset_config.reset_pin                       = reset_pin;
    114             driver->spi_dc_reset_config.command_params_uses_command_pin = false;
    115 
    116             if (!qp_internal_register_device((painter_device_t)driver)) {
    117                 memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
    118                 return NULL;
    119             }
    120 
    121             return (painter_device_t)driver;
    122         }
    123     }
    124     return NULL;
    125 }
    126 
    127 #endif // QUANTUM_PAINTER_GC9A01_SPI_ENABLE