qmk_firmware

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

qp_oled_panel.c (10060B)


      1 // Copyright 2023 Nick Brassel (@tzarc)
      2 // SPDX-License-Identifier: GPL-2.0-or-later
      3 
      4 #include "color.h"
      5 #include "qp_internal.h"
      6 #include "qp_comms.h"
      7 #include "qp_draw.h"
      8 #include "qp_oled_panel.h"
      9 
     10 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     11 // Quantum Painter API implementations
     12 
     13 // Power control
     14 bool qp_oled_panel_power(painter_device_t device, bool power_on) {
     15     painter_driver_t                   *driver = (painter_driver_t *)device;
     16     oled_panel_painter_driver_vtable_t *vtable = (oled_panel_painter_driver_vtable_t *)driver->driver_vtable;
     17     qp_comms_command(device, power_on ? vtable->opcodes.display_on : vtable->opcodes.display_off);
     18     return true;
     19 }
     20 
     21 // Screen clear
     22 bool qp_oled_panel_clear(painter_device_t device) {
     23     painter_driver_t *driver = (painter_driver_t *)device;
     24     driver->driver_vtable->init(device, driver->rotation); // Re-init the display
     25     return true;
     26 }
     27 
     28 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     29 // Surface passthru
     30 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     31 
     32 bool qp_oled_panel_passthru_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count) {
     33     oled_panel_painter_device_t *driver = (oled_panel_painter_device_t *)device;
     34     return driver->surface.base.validate_ok && driver->surface.base.driver_vtable->pixdata(&driver->surface.base, pixel_data, native_pixel_count);
     35 }
     36 
     37 bool qp_oled_panel_passthru_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {
     38     oled_panel_painter_device_t *driver = (oled_panel_painter_device_t *)device;
     39     return driver->surface.base.validate_ok && driver->surface.base.driver_vtable->viewport(&driver->surface.base, left, top, right, bottom);
     40 }
     41 
     42 bool qp_oled_panel_passthru_palette_convert(painter_device_t device, int16_t palette_size, qp_pixel_t *palette) {
     43     oled_panel_painter_device_t *driver = (oled_panel_painter_device_t *)device;
     44     return driver->surface.base.validate_ok && driver->surface.base.driver_vtable->palette_convert(&driver->surface.base, palette_size, palette);
     45 }
     46 
     47 bool qp_oled_panel_passthru_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) {
     48     oled_panel_painter_device_t *driver = (oled_panel_painter_device_t *)device;
     49     return driver->surface.base.validate_ok && driver->surface.base.driver_vtable->append_pixels(&driver->surface.base, target_buffer, palette, pixel_offset, pixel_count, palette_indices);
     50 }
     51 
     52 bool qp_oled_panel_passthru_append_pixdata(painter_device_t device, uint8_t *target_buffer, uint32_t pixdata_offset, uint8_t pixdata_byte) {
     53     oled_panel_painter_device_t *driver = (oled_panel_painter_device_t *)device;
     54     return driver->surface.base.validate_ok && driver->surface.base.driver_vtable->append_pixdata(&driver->surface.base, target_buffer, pixdata_offset, pixdata_byte);
     55 }
     56 
     57 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     58 // Flush helpers
     59 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     60 
     61 void qp_oled_panel_page_column_flush_rot0(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer) {
     62     painter_driver_t                   *driver = (painter_driver_t *)device;
     63     oled_panel_painter_driver_vtable_t *vtable = (oled_panel_painter_driver_vtable_t *)driver->driver_vtable;
     64 
     65     // TODO: account for offset_x/y in base driver
     66     int min_page   = dirty->t / 8;
     67     int max_page   = dirty->b / 8;
     68     int min_column = dirty->l;
     69     int max_column = dirty->r;
     70 
     71     for (int page = min_page; page <= max_page; ++page) {
     72         int     cols_required = max_column - min_column + 1;
     73         uint8_t column_data[cols_required];
     74         memset(column_data, 0, cols_required);
     75         for (int x = min_column; x <= max_column; ++x) {
     76             uint16_t data_offset = x - min_column;
     77             for (int y = 0; y < 8; ++y) {
     78                 uint32_t pixel_num   = ((page * 8) + y) * driver->panel_width + x;
     79                 uint32_t byte_offset = pixel_num / 8;
     80                 uint8_t  bit_offset  = pixel_num % 8;
     81                 column_data[data_offset] |= ((framebuffer[byte_offset] & (1 << bit_offset)) >> bit_offset) << y;
     82             }
     83         }
     84 
     85         int actual_page  = page;
     86         int start_column = min_column;
     87         qp_comms_command(device, vtable->opcodes.set_page | actual_page);
     88         qp_comms_command(device, vtable->opcodes.set_column_lsb | (start_column & 0x0F));
     89         qp_comms_command(device, vtable->opcodes.set_column_msb | (start_column & 0xF0) >> 4);
     90         qp_comms_send(device, column_data, cols_required);
     91     }
     92 }
     93 
     94 void qp_oled_panel_page_column_flush_rot90(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer) {
     95     painter_driver_t                   *driver = (painter_driver_t *)device;
     96     oled_panel_painter_driver_vtable_t *vtable = (oled_panel_painter_driver_vtable_t *)driver->driver_vtable;
     97 
     98     // TODO: account for offset_x/y in base driver
     99     int num_columns = driver->panel_width;
    100     int min_page    = dirty->l / 8;
    101     int max_page    = dirty->r / 8;
    102     int min_column  = dirty->t;
    103     int max_column  = dirty->b;
    104 
    105     for (int page = min_page; page <= max_page; ++page) {
    106         int     cols_required = max_column - min_column + 1;
    107         uint8_t column_data[cols_required];
    108         memset(column_data, 0, cols_required);
    109         for (int y = min_column; y <= max_column; ++y) {
    110             uint16_t data_offset = cols_required - 1 - (y - min_column);
    111             for (int x = 0; x < 8; ++x) {
    112                 uint32_t pixel_num   = y * driver->panel_height + ((page * 8) + x);
    113                 uint32_t byte_offset = pixel_num / 8;
    114                 uint8_t  bit_offset  = pixel_num % 8;
    115                 column_data[data_offset] |= ((framebuffer[byte_offset] & (1 << bit_offset)) >> bit_offset) << x;
    116             }
    117         }
    118 
    119         int actual_page  = page;
    120         int start_column = num_columns - 1 - max_column;
    121         qp_comms_command(device, vtable->opcodes.set_page | actual_page);
    122         qp_comms_command(device, vtable->opcodes.set_column_lsb | (start_column & 0x0F));
    123         qp_comms_command(device, vtable->opcodes.set_column_msb | (start_column & 0xF0) >> 4);
    124         qp_comms_send(device, column_data, cols_required);
    125     }
    126 }
    127 
    128 void qp_oled_panel_page_column_flush_rot180(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer) {
    129     painter_driver_t                   *driver = (painter_driver_t *)device;
    130     oled_panel_painter_driver_vtable_t *vtable = (oled_panel_painter_driver_vtable_t *)driver->driver_vtable;
    131 
    132     // TODO: account for offset_x/y in base driver
    133     int num_pages   = driver->panel_height / 8;
    134     int num_columns = driver->panel_width;
    135     int min_page    = dirty->t / 8;
    136     int max_page    = dirty->b / 8;
    137     int min_column  = dirty->l;
    138     int max_column  = dirty->r;
    139 
    140     for (int page = min_page; page <= max_page; ++page) {
    141         int     cols_required = max_column - min_column + 1;
    142         uint8_t column_data[cols_required];
    143         memset(column_data, 0, cols_required);
    144         for (int x = min_column; x <= max_column; ++x) {
    145             uint16_t data_offset = cols_required - 1 - (x - min_column);
    146             for (int y = 0; y < 8; ++y) {
    147                 uint32_t pixel_num   = ((page * 8) + y) * driver->panel_width + x;
    148                 uint32_t byte_offset = pixel_num / 8;
    149                 uint8_t  bit_offset  = pixel_num % 8;
    150                 column_data[data_offset] |= ((framebuffer[byte_offset] & (1 << bit_offset)) >> bit_offset) << (7 - y);
    151             }
    152         }
    153 
    154         int actual_page  = num_pages - 1 - page;
    155         int start_column = num_columns - 1 - max_column;
    156         qp_comms_command(device, vtable->opcodes.set_page | actual_page);
    157         qp_comms_command(device, vtable->opcodes.set_column_lsb | (start_column & 0x0F));
    158         qp_comms_command(device, vtable->opcodes.set_column_msb | (start_column & 0xF0) >> 4);
    159         qp_comms_send(device, column_data, cols_required);
    160     }
    161 }
    162 
    163 void qp_oled_panel_page_column_flush_rot270(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer) {
    164     painter_driver_t                   *driver = (painter_driver_t *)device;
    165     oled_panel_painter_driver_vtable_t *vtable = (oled_panel_painter_driver_vtable_t *)driver->driver_vtable;
    166 
    167     // TODO: account for offset_x/y in base driver
    168     int num_pages  = driver->panel_height / 8;
    169     int min_page   = dirty->l / 8;
    170     int max_page   = dirty->r / 8;
    171     int min_column = dirty->t;
    172     int max_column = dirty->b;
    173 
    174     for (int page = min_page; page <= max_page; ++page) {
    175         int     cols_required = max_column - min_column + 1;
    176         uint8_t column_data[cols_required];
    177         memset(column_data, 0, cols_required);
    178         for (int y = min_column; y <= max_column; ++y) {
    179             uint16_t data_offset = y - min_column;
    180             for (int x = 0; x < 8; ++x) {
    181                 uint32_t pixel_num   = y * driver->panel_height + ((page * 8) + x);
    182                 uint32_t byte_offset = pixel_num / 8;
    183                 uint8_t  bit_offset  = pixel_num % 8;
    184                 column_data[data_offset] |= ((framebuffer[byte_offset] & (1 << bit_offset)) >> bit_offset) << (7 - x);
    185             }
    186         }
    187 
    188         int actual_page  = num_pages - 1 - page;
    189         int start_column = min_column;
    190         qp_comms_command(device, vtable->opcodes.set_page | actual_page);
    191         qp_comms_command(device, vtable->opcodes.set_column_lsb | (start_column & 0x0F));
    192         qp_comms_command(device, vtable->opcodes.set_column_msb | (start_column & 0xF0) >> 4);
    193         qp_comms_send(device, column_data, cols_required);
    194     }
    195 }