qp_gc9107.c (5658B)
1 // Copyright 2024 Fernando Birra 2 // SPDX-License-Identifier: GPL-2.0-or-later 3 #include "qp_internal.h" 4 #include "qp_comms.h" 5 #include "qp_gc9107.h" 6 #include "qp_gc9xxx_opcodes.h" 7 #include "qp_gc9107_opcodes.h" 8 #include "qp_tft_panel.h" 9 10 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 11 // Driver storage 12 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 13 14 tft_panel_dc_reset_painter_device_t gc9107_drivers[GC9107_NUM_DEVICES] = {0}; 15 16 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 17 // Initialization 18 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 19 __attribute__((weak)) bool qp_gc9107_init(painter_device_t device, painter_rotation_t rotation) { 20 // A lot of these "unknown" opcodes are sourced from other OSS projects and are seemingly required for this display to function. 21 // clang-format off 22 const uint8_t gc9107_init_sequence[] = { 23 GC9XXX_SET_INTER_REG_ENABLE1, 5, 0, 24 GC9XXX_SET_INTER_REG_ENABLE2, 5, 0, 25 GC9107_SET_FUNCTION_CTL6, 0, 1, GC9107_ALLOW_SET_COMPLEMENT_RGB | 0x08 | GC9107_ALLOW_SET_FRAMERATE, 26 GC9107_SET_COMPLEMENT_RGB, 0, 1, GC9107_COMPLEMENT_WITH_LSB, 27 0xAB, 0, 1, 0x0E, 28 GC9107_SET_FRAME_RATE, 0, 1, 0x19, 29 GC9XXX_SET_PIXEL_FORMAT, 0, 1, GC9107_PIXEL_FORMAT_16_BPP_IFPF, 30 GC9XXX_CMD_SLEEP_OFF, 120, 0, 31 GC9XXX_CMD_DISPLAY_ON, 20, 0 32 }; 33 34 // clang-format on 35 if (!qp_comms_bulk_command_sequence(device, gc9107_init_sequence, sizeof(gc9107_init_sequence))) { 36 return false; 37 } 38 39 // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM) 40 const uint8_t madctl[] = { 41 [QP_ROTATION_0] = GC9XXX_MADCTL_BGR, 42 [QP_ROTATION_90] = GC9XXX_MADCTL_BGR | GC9XXX_MADCTL_MX | GC9XXX_MADCTL_MV, 43 [QP_ROTATION_180] = GC9XXX_MADCTL_BGR | GC9XXX_MADCTL_MX | GC9XXX_MADCTL_MY, 44 [QP_ROTATION_270] = GC9XXX_MADCTL_BGR | GC9XXX_MADCTL_MV | GC9XXX_MADCTL_MY, 45 }; 46 return qp_comms_command_databyte(device, GC9XXX_SET_MEM_ACS_CTL, madctl[rotation]); 47 } 48 49 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 50 // Driver vtable 51 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 52 53 const tft_panel_dc_reset_painter_driver_vtable_t gc9107_driver_vtable = { 54 .base = 55 { 56 .init = qp_gc9107_init, 57 .power = qp_tft_panel_power, 58 .clear = qp_tft_panel_clear, 59 .flush = qp_tft_panel_flush, 60 .pixdata = qp_tft_panel_pixdata, 61 .viewport = qp_tft_panel_viewport, 62 .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped, 63 .append_pixels = qp_tft_panel_append_pixels_rgb565, 64 .append_pixdata = qp_tft_panel_append_pixdata, 65 }, 66 .num_window_bytes = 2, 67 .swap_window_coords = false, 68 .opcodes = 69 { 70 .display_on = GC9XXX_CMD_DISPLAY_ON, 71 .display_off = GC9XXX_CMD_DISPLAY_OFF, 72 .set_column_address = GC9XXX_SET_COL_ADDR, 73 .set_row_address = GC9XXX_SET_ROW_ADDR, 74 .enable_writes = GC9XXX_SET_MEM, 75 }, 76 }; 77 78 #ifdef QUANTUM_PAINTER_GC9107_SPI_ENABLE 79 // Factory function for creating a handle to the GC9107 device 80 painter_device_t qp_gc9107_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) { 81 for (uint32_t i = 0; i < GC9107_NUM_DEVICES; ++i) { 82 tft_panel_dc_reset_painter_device_t *driver = &gc9107_drivers[i]; 83 if (!driver->base.driver_vtable) { 84 driver->base.driver_vtable = (const painter_driver_vtable_t *)&gc9107_driver_vtable; 85 driver->base.comms_vtable = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable; 86 driver->base.native_bits_per_pixel = 16; // RGB565 87 driver->base.panel_width = panel_width; 88 driver->base.panel_height = panel_height; 89 driver->base.rotation = QP_ROTATION_0; 90 driver->base.offset_x = 2; 91 driver->base.offset_y = 1; 92 93 // SPI and other pin configuration 94 driver->base.comms_config = &driver->spi_dc_reset_config; 95 driver->spi_dc_reset_config.spi_config.chip_select_pin = chip_select_pin; 96 driver->spi_dc_reset_config.spi_config.divisor = spi_divisor; 97 driver->spi_dc_reset_config.spi_config.lsb_first = false; 98 driver->spi_dc_reset_config.spi_config.mode = spi_mode; 99 driver->spi_dc_reset_config.dc_pin = dc_pin; 100 driver->spi_dc_reset_config.reset_pin = reset_pin; 101 driver->spi_dc_reset_config.command_params_uses_command_pin = false; 102 103 if (!qp_internal_register_device((painter_device_t)driver)) { 104 memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t)); 105 return NULL; 106 } 107 108 return (painter_device_t)driver; 109 } 110 } 111 return NULL; 112 } 113 114 #endif // QUANTUM_PAINTER_GC9107_SPI_ENABLE