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 }