From 1f2b1dedccdf21b629c45ece80b4ca32f6653296 Mon Sep 17 00:00:00 2001
From: Nick Brassel <nick@tzarc.org>
Date: Wed, 13 Apr 2022 18:00:18 +1000
Subject: Quantum Painter (#10174)

* Install dependencies before executing unit tests.

* Split out UTF-8 decoder.

* Fixup python formatting rules.

* Add documentation for QGF/QFF and the RLE format used.

* Add CLI commands for converting images and fonts.

* Add stub rules.mk for QP.

* Add stream type.

* Add base driver and comms interfaces.

* Add support for SPI, SPI+D/C comms drivers.

* Include <qp.h> when enabled.

* Add base support for SPI+D/C+RST panels, as well as concrete implementation of ST7789.

* Add support for GC9A01.

* Add support for ILI9341.

* Add support for ILI9163.

* Add support for SSD1351.

* Implement qp_setpixel, including pixdata buffer management.

* Implement qp_line.

* Implement qp_rect.

* Implement qp_circle.

* Implement qp_ellipse.

* Implement palette interpolation.

* Allow for streams to work with either flash or RAM.

* Image loading.

* Font loading.

* QGF palette loading.

* Progressive decoder of pixel data supporting Raw+RLE, 1-,2-,4-,8-bpp monochrome and palette-based images.

* Image drawing.

* Animations.

* Font rendering.

* Check against 256 colours, dump out the loaded palette if debugging enabled.

* Fix build.

* AVR is not the intended audience.

* `qmk format-c`

* Generation fix.

* First batch of docs.

* More docs and examples.

* Review comments.

* Public API documentation.
---
 drivers/painter/comms/qp_comms_spi.c | 137 +++++++++++++++++++++++++++++++++++
 drivers/painter/comms/qp_comms_spi.h |  51 +++++++++++++
 2 files changed, 188 insertions(+)
 create mode 100644 drivers/painter/comms/qp_comms_spi.c
 create mode 100644 drivers/painter/comms/qp_comms_spi.h

(limited to 'drivers/painter/comms')

diff --git a/drivers/painter/comms/qp_comms_spi.c b/drivers/painter/comms/qp_comms_spi.c
new file mode 100644
index 0000000000..e644ba9f84
--- /dev/null
+++ b/drivers/painter/comms/qp_comms_spi.c
@@ -0,0 +1,137 @@
+// Copyright 2021 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifdef QUANTUM_PAINTER_SPI_ENABLE
+
+#    include "spi_master.h"
+#    include "qp_comms_spi.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Base SPI support
+
+bool qp_comms_spi_init(painter_device_t device) {
+    struct painter_driver_t *     driver       = (struct painter_driver_t *)device;
+    struct qp_comms_spi_config_t *comms_config = (struct qp_comms_spi_config_t *)driver->comms_config;
+
+    // Initialize the SPI peripheral
+    spi_init();
+
+    // Set up CS as output high
+    setPinOutput(comms_config->chip_select_pin);
+    writePinHigh(comms_config->chip_select_pin);
+
+    return true;
+}
+
+bool qp_comms_spi_start(painter_device_t device) {
+    struct painter_driver_t *     driver       = (struct painter_driver_t *)device;
+    struct qp_comms_spi_config_t *comms_config = (struct qp_comms_spi_config_t *)driver->comms_config;
+
+    return spi_start(comms_config->chip_select_pin, comms_config->lsb_first, comms_config->mode, comms_config->divisor);
+}
+
+uint32_t qp_comms_spi_send_data(painter_device_t device, const void *data, uint32_t byte_count) {
+    uint32_t       bytes_remaining = byte_count;
+    const uint8_t *p               = (const uint8_t *)data;
+    while (bytes_remaining > 0) {
+        uint32_t bytes_this_loop = bytes_remaining < 1024 ? bytes_remaining : 1024;
+        spi_transmit(p, bytes_this_loop);
+        p += bytes_this_loop;
+        bytes_remaining -= bytes_this_loop;
+    }
+
+    return byte_count - bytes_remaining;
+}
+
+void qp_comms_spi_stop(painter_device_t device) {
+    struct painter_driver_t *     driver       = (struct painter_driver_t *)device;
+    struct qp_comms_spi_config_t *comms_config = (struct qp_comms_spi_config_t *)driver->comms_config;
+    spi_stop();
+    writePinHigh(comms_config->chip_select_pin);
+}
+
+const struct painter_comms_vtable_t spi_comms_vtable = {
+    .comms_init  = qp_comms_spi_init,
+    .comms_start = qp_comms_spi_start,
+    .comms_send  = qp_comms_spi_send_data,
+    .comms_stop  = qp_comms_spi_stop,
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// SPI with D/C and RST pins
+
+#    ifdef QUANTUM_PAINTER_SPI_DC_RESET_ENABLE
+
+bool qp_comms_spi_dc_reset_init(painter_device_t device) {
+    if (!qp_comms_spi_init(device)) {
+        return false;
+    }
+
+    struct painter_driver_t *              driver       = (struct painter_driver_t *)device;
+    struct qp_comms_spi_dc_reset_config_t *comms_config = (struct qp_comms_spi_dc_reset_config_t *)driver->comms_config;
+
+    // Set up D/C as output low, if specified
+    if (comms_config->dc_pin != NO_PIN) {
+        setPinOutput(comms_config->dc_pin);
+        writePinLow(comms_config->dc_pin);
+    }
+
+    // Set up RST as output, if specified, performing a reset in the process
+    if (comms_config->reset_pin != NO_PIN) {
+        setPinOutput(comms_config->reset_pin);
+        writePinLow(comms_config->reset_pin);
+        wait_ms(20);
+        writePinHigh(comms_config->reset_pin);
+        wait_ms(20);
+    }
+
+    return true;
+}
+
+uint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void *data, uint32_t byte_count) {
+    struct painter_driver_t *              driver       = (struct painter_driver_t *)device;
+    struct qp_comms_spi_dc_reset_config_t *comms_config = (struct qp_comms_spi_dc_reset_config_t *)driver->comms_config;
+    writePinHigh(comms_config->dc_pin);
+    return qp_comms_spi_send_data(device, data, byte_count);
+}
+
+void qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd) {
+    struct painter_driver_t *              driver       = (struct painter_driver_t *)device;
+    struct qp_comms_spi_dc_reset_config_t *comms_config = (struct qp_comms_spi_dc_reset_config_t *)driver->comms_config;
+    writePinLow(comms_config->dc_pin);
+    spi_write(cmd);
+}
+
+void qp_comms_spi_dc_reset_bulk_command_sequence(painter_device_t device, const uint8_t *sequence, size_t sequence_len) {
+    for (size_t i = 0; i < sequence_len;) {
+        uint8_t command   = sequence[i];
+        uint8_t delay     = sequence[i + 1];
+        uint8_t num_bytes = sequence[i + 2];
+        qp_comms_spi_dc_reset_send_command(device, command);
+        if (num_bytes > 0) {
+            qp_comms_spi_dc_reset_send_data(device, &sequence[i + 3], num_bytes);
+        }
+        if (delay > 0) {
+            wait_ms(delay);
+        }
+        i += (3 + num_bytes);
+    }
+}
+
+const struct painter_comms_with_command_vtable_t spi_comms_with_dc_vtable = {
+    .base =
+        {
+            .comms_init  = qp_comms_spi_dc_reset_init,
+            .comms_start = qp_comms_spi_start,
+            .comms_send  = qp_comms_spi_dc_reset_send_data,
+            .comms_stop  = qp_comms_spi_stop,
+        },
+    .send_command          = qp_comms_spi_dc_reset_send_command,
+    .bulk_command_sequence = qp_comms_spi_dc_reset_bulk_command_sequence,
+};
+
+#    endif // QUANTUM_PAINTER_SPI_DC_RESET_ENABLE
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#endif // QUANTUM_PAINTER_SPI_ENABLE
diff --git a/drivers/painter/comms/qp_comms_spi.h b/drivers/painter/comms/qp_comms_spi.h
new file mode 100644
index 0000000000..9989987327
--- /dev/null
+++ b/drivers/painter/comms/qp_comms_spi.h
@@ -0,0 +1,51 @@
+// Copyright 2021 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#ifdef QUANTUM_PAINTER_SPI_ENABLE
+
+#    include <stdint.h>
+
+#    include "gpio.h"
+#    include "qp_internal.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Base SPI support
+
+struct qp_comms_spi_config_t {
+    pin_t    chip_select_pin;
+    uint16_t divisor;
+    bool     lsb_first;
+    int8_t   mode;
+};
+
+bool     qp_comms_spi_init(painter_device_t device);
+bool     qp_comms_spi_start(painter_device_t device);
+uint32_t qp_comms_spi_send_data(painter_device_t device, const void* data, uint32_t byte_count);
+void     qp_comms_spi_stop(painter_device_t device);
+
+extern const struct painter_comms_vtable_t spi_comms_vtable;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// SPI with D/C and RST pins
+
+#    ifdef QUANTUM_PAINTER_SPI_DC_RESET_ENABLE
+
+struct qp_comms_spi_dc_reset_config_t {
+    struct qp_comms_spi_config_t spi_config;
+    pin_t                        dc_pin;
+    pin_t                        reset_pin;
+};
+
+void     qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd);
+uint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void* data, uint32_t byte_count);
+void     qp_comms_spi_dc_reset_bulk_command_sequence(painter_device_t device, const uint8_t* sequence, size_t sequence_len);
+
+extern const struct painter_comms_with_command_vtable_t spi_comms_with_dc_vtable;
+
+#    endif // QUANTUM_PAINTER_SPI_DC_RESET_ENABLE
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#endif // QUANTUM_PAINTER_SPI_ENABLE
-- 
cgit v1.2.3