qmk_firmware

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

qp_ssd1351.c (6213B)


      1 // Copyright 2021-2023 Nick Brassel (@tzarc)
      2 // SPDX-License-Identifier: GPL-2.0-or-later
      3 
      4 #include "qp_internal.h"
      5 #include "qp_comms.h"
      6 #include "qp_ssd1351.h"
      7 #include "qp_ssd1351_opcodes.h"
      8 #include "qp_tft_panel.h"
      9 
     10 #ifdef QUANTUM_PAINTER_SSD1351_SPI_ENABLE
     11 #    include "qp_comms_spi.h"
     12 #endif // QUANTUM_PAINTER_SSD1351_SPI_ENABLE
     13 
     14 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     15 // Common
     16 
     17 // Driver storage
     18 tft_panel_dc_reset_painter_device_t ssd1351_drivers[SSD1351_NUM_DEVICES] = {0};
     19 
     20 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     21 // Initialization
     22 
     23 __attribute__((weak)) bool qp_ssd1351_init(painter_device_t device, painter_rotation_t rotation) {
     24     tft_panel_dc_reset_painter_device_t *driver = (tft_panel_dc_reset_painter_device_t *)device;
     25 
     26     // clang-format off
     27     const uint8_t ssd1351_init_sequence[] = {
     28         // Command,                 Delay, N, Data[N]
     29         SSD1351_COMMANDLOCK,           5,  1, 0x12,
     30         SSD1351_COMMANDLOCK,           5,  1, 0xB1,
     31         SSD1351_DISPLAYOFF,            5,  0,
     32         SSD1351_CLOCKDIV,              5,  1, 0xF1,
     33         SSD1351_MUXRATIO,              5,  1, 0x7F,
     34         SSD1351_DISPLAYOFFSET,         5,  1, 0x00,
     35         SSD1351_SETGPIO,               5,  1, 0x00,
     36         SSD1351_FUNCTIONSELECT,        5,  1, 0x01,
     37         SSD1351_PRECHARGE,             5,  1, 0x32,
     38         SSD1351_VCOMH,                 5,  1, 0x05,
     39         SSD1351_NORMALDISPLAY,         5,  0,
     40         SSD1351_CONTRASTABC,           5,  3, 0xC8, 0x80, 0xC8,
     41         SSD1351_CONTRASTMASTER,        5,  1, 0x0F,
     42         SSD1351_SETVSL,                5,  3, 0xA0, 0xB5, 0x55,
     43         SSD1351_PRECHARGE2,            5,  1, 0x01,
     44         SSD1351_DISPLAYON,             5,  0,
     45     };
     46     // clang-format on
     47     if (!qp_comms_bulk_command_sequence(device, ssd1351_init_sequence, sizeof(ssd1351_init_sequence))) {
     48         return false;
     49     }
     50 
     51     // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)
     52     const uint8_t madctl[] = {
     53         [QP_ROTATION_0]   = SSD1351_MADCTL_BGR | SSD1351_MADCTL_MY,
     54         [QP_ROTATION_90]  = SSD1351_MADCTL_BGR | SSD1351_MADCTL_MX | SSD1351_MADCTL_MY | SSD1351_MADCTL_MV,
     55         [QP_ROTATION_180] = SSD1351_MADCTL_BGR | SSD1351_MADCTL_MX,
     56         [QP_ROTATION_270] = SSD1351_MADCTL_BGR | SSD1351_MADCTL_MV,
     57     };
     58     if (!qp_comms_command_databyte(device, SSD1351_SETREMAP, madctl[rotation])) {
     59         return false;
     60     }
     61     return qp_comms_command_databyte(device, SSD1351_STARTLINE, (rotation == QP_ROTATION_0 || rotation == QP_ROTATION_90) ? driver->base.panel_height : 0);
     62 }
     63 
     64 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     65 // Driver vtable
     66 
     67 const tft_panel_dc_reset_painter_driver_vtable_t ssd1351_driver_vtable = {
     68     .base =
     69         {
     70             .init            = qp_ssd1351_init,
     71             .power           = qp_tft_panel_power,
     72             .clear           = qp_tft_panel_clear,
     73             .flush           = qp_tft_panel_flush,
     74             .pixdata         = qp_tft_panel_pixdata,
     75             .viewport        = qp_tft_panel_viewport,
     76             .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,
     77             .append_pixels   = qp_tft_panel_append_pixels_rgb565,
     78             .append_pixdata  = qp_tft_panel_append_pixdata,
     79         },
     80     .num_window_bytes   = 1,
     81     .swap_window_coords = true,
     82     .opcodes =
     83         {
     84             .display_on         = SSD1351_DISPLAYON,
     85             .display_off        = SSD1351_DISPLAYOFF,
     86             .set_column_address = SSD1351_SETCOLUMN,
     87             .set_row_address    = SSD1351_SETROW,
     88             .enable_writes      = SSD1351_WRITERAM,
     89         },
     90 };
     91 
     92 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     93 // SPI
     94 
     95 #ifdef QUANTUM_PAINTER_SSD1351_SPI_ENABLE
     96 
     97 // Factory function for creating a handle to the SSD1351 device
     98 painter_device_t qp_ssd1351_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) {
     99     for (uint32_t i = 0; i < SSD1351_NUM_DEVICES; ++i) {
    100         tft_panel_dc_reset_painter_device_t *driver = &ssd1351_drivers[i];
    101         if (!driver->base.driver_vtable) {
    102             driver->base.driver_vtable         = (const painter_driver_vtable_t *)&ssd1351_driver_vtable;
    103             driver->base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;
    104             driver->base.panel_width           = panel_width;
    105             driver->base.panel_height          = panel_height;
    106             driver->base.rotation              = QP_ROTATION_0;
    107             driver->base.offset_x              = 0;
    108             driver->base.offset_y              = 0;
    109             driver->base.native_bits_per_pixel = 16; // RGB565
    110 
    111             // SPI and other pin configuration
    112             driver->base.comms_config                                   = &driver->spi_dc_reset_config;
    113             driver->spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;
    114             driver->spi_dc_reset_config.spi_config.divisor              = spi_divisor;
    115             driver->spi_dc_reset_config.spi_config.lsb_first            = false;
    116             driver->spi_dc_reset_config.spi_config.mode                 = spi_mode;
    117             driver->spi_dc_reset_config.dc_pin                          = dc_pin;
    118             driver->spi_dc_reset_config.reset_pin                       = reset_pin;
    119             driver->spi_dc_reset_config.command_params_uses_command_pin = false;
    120 
    121             if (!qp_internal_register_device((painter_device_t)driver)) {
    122                 memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
    123                 return NULL;
    124             }
    125 
    126             return (painter_device_t)driver;
    127         }
    128     }
    129     return NULL;
    130 }
    131 
    132 #endif // QUANTUM_PAINTER_SSD1351_SPI_ENABLE
    133 
    134 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////