aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jack/jack.c3
-rw-r--r--jack/jack.h1
-rw-r--r--keymaps/beiwagon/keymap.c14
-rw-r--r--keymaps/onekey/f411_ls0x/3270_11.qff.c11
-rw-r--r--keymaps/onekey/f411_ls0x/keymap.c15
-rw-r--r--keymaps/onekey/f411_ls0x/keymap.json3
-rw-r--r--keymaps/onekey/f411_ls0x/rules.mk3
-rw-r--r--modules/ls0xx/ls0xx.c277
-rw-r--r--modules/ls0xx/ls0xx.h27
-rw-r--r--modules/ls0xx/ls0xx_opcodes.h9
-rw-r--r--modules/ls0xx/qmk_module.json4
-rw-r--r--modules/ls0xx/rules.mk7
12 files changed, 359 insertions, 15 deletions
diff --git a/jack/jack.c b/jack/jack.c
index 9a59cb0..f2e9944 100644
--- a/jack/jack.c
+++ b/jack/jack.c
@@ -80,6 +80,7 @@ void keyboard_post_init_user(void) {
if (!autocorrect_is_enabled())
autocorrect_enable();
#endif
+ keyboard_post_init_keymap();
}
void matrix_scan_user(void) {
@@ -103,6 +104,8 @@ uint16_t keycode_config(uint16_t keycode) { return keycode; }
uint8_t mod_config(uint8_t mod) { return mod; }
#endif
+__attribute__((weak)) void keyboard_post_init_keymap(void) {}
+
#ifdef RP2040_MATH_IN_ROM
typedef void (*init_fn)(void);
diff --git a/jack/jack.h b/jack/jack.h
index 0bce15b..eb9182c 100644
--- a/jack/jack.h
+++ b/jack/jack.h
@@ -29,6 +29,7 @@ void tap_code_register(uint8_t code, uint8_t mods, uint16_t delay,
bool register_tap);
void process_tap_code_buffer(void);
void tap_string(const char *str);
+void keyboard_post_init_keymap(void);
#ifdef TAP_DANCE_ENABLE
void td_reset(tap_dance_state_t *state, void *user_data);
void em_dash_mins(tap_dance_state_t *state, void *user_data);
diff --git a/keymaps/beiwagon/keymap.c b/keymaps/beiwagon/keymap.c
index 00b8dd9..eaa9c44 100644
--- a/keymaps/beiwagon/keymap.c
+++ b/keymaps/beiwagon/keymap.c
@@ -5,16 +5,22 @@
// clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_BASE] = LAYOUT_jack(
- G(KC_F), G(KC_T), G(KC_R),
+ G(KC_F), G(KC_Q), G(KC_R),
KC_VOLD, PNP, KC_VOLU,
REMOVE, XRGB_HUI, XRGB_NXT,
XRGB_TOG, XRGB_HUD, TG(_LOWER)
),
[_LOWER] = LAYOUT_jack(
- QK_MAKE, _______, _______,
- QK_BOOT, _______, _______,
- EE_CLR, _______, _______,
+ QK_BOOT, EE_CLR, QK_MAKE,
+ _______, _______, _______,
+ _______, _______, _______,
_______, _______, _______
)
};
// clang-format on
+
+void keyboard_post_init_keymap(void) {
+ for (int i = 0; i < RGB_MATRIX_LED_COUNT; ++i)
+ if (g_led_config.flags[i] & LED_FLAG_UNDERGLOW)
+ g_led_config.flags[i] &= ~LED_FLAG_UNDERGLOW;
+}
diff --git a/keymaps/onekey/f411_ls0x/3270_11.qff.c b/keymaps/onekey/f411_ls0x/3270_11.qff.c
index 43e5cbc..c241e51 100644
--- a/keymaps/onekey/f411_ls0x/3270_11.qff.c
+++ b/keymaps/onekey/f411_ls0x/3270_11.qff.c
@@ -1,15 +1,18 @@
-// Copyright 2025 QMK -- generated source code only, font retains original copyright
-// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright 2025 QMK -- generated source code only, font retains original
+// copyright SPDX-License-Identifier: GPL-2.0-or-later
// This file was auto-generated by `painter-convert-font-image` with arguments:
// format | mono2
-// unicode_glyphs |
+// unicode_glyphs |
// output | qmk_firmware
// input | 3270_11.png
// Font's metadata
// ---------------
-// Glyphs: , !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~
+// Glyphs: , !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6,
+// 7, 8, 9, :, ;, <, =, >, ?, @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P,
+// Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, `, a, b, c, d, e, f, g, h, i, j,
+// k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~
#include <qp.h>
diff --git a/keymaps/onekey/f411_ls0x/keymap.c b/keymaps/onekey/f411_ls0x/keymap.c
index 75d65f3..584bce6 100644
--- a/keymaps/onekey/f411_ls0x/keymap.c
+++ b/keymaps/onekey/f411_ls0x/keymap.c
@@ -3,12 +3,11 @@
#include QMK_KEYBOARD_H
#include "qp.h"
#include "qp_surface.h"
-#include "3270_11.qff.h"
+#include "ls0xx.h"
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
LAYOUT_ortho_1x1(KC_A)};
-static painter_font_handle_t font;
static painter_device_t display;
static uint8_t framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(
LS0XX_WIDTH, LS0XX_HEIGHT, 1)] = {0};
@@ -17,7 +16,15 @@ void keyboard_post_init_user(void) {
display =
qp_ls0xx_device_t_make_spi_device(LS0XX_WIDTH, LS0XX_HEIGHT, LS0XX_CS_PIN,
LS0XX_SPI_DIVISOR, 0, framebuffer);
- font = qp_load_font_mem(font_3270_11);
qp_init(display, QP_ROTATION_0);
- qp_drawtext(display, 0, 0, font, "foo bar baz");
+}
+
+void housekeeping_task_user(void) {
+ static uint32_t last_draw = 0;
+ if (timer_elapsed32(last_draw) > 33) {
+ last_draw = timer_read32();
+ for (int i = 0; i < LS0XX_HEIGHT - 1; i += 8)
+ qp_rect(display, 0, i, 7, i + 7, 255, 255, 255, true);
+ qp_flush(display);
+ }
}
diff --git a/keymaps/onekey/f411_ls0x/keymap.json b/keymaps/onekey/f411_ls0x/keymap.json
new file mode 100644
index 0000000..9eba3d8
--- /dev/null
+++ b/keymaps/onekey/f411_ls0x/keymap.json
@@ -0,0 +1,3 @@
+{
+ "modules": ["ls0xx"]
+}
diff --git a/keymaps/onekey/f411_ls0x/rules.mk b/keymaps/onekey/f411_ls0x/rules.mk
index e1e5f55..7166553 100644
--- a/keymaps/onekey/f411_ls0x/rules.mk
+++ b/keymaps/onekey/f411_ls0x/rules.mk
@@ -1,6 +1,3 @@
-CONSOLE_ENABLE = yes
BOOTMAGIC_ENABLE = yes
QUANTUM_PAINTER_ENABLE = yes
-QUANTUM_PAINTER_DRIVERS += ls0xx_spi
SRC += 3270_11.qff.c
-EXTRAFLAGS += -Wno-maybe-uninitialized
diff --git a/modules/ls0xx/ls0xx.c b/modules/ls0xx/ls0xx.c
new file mode 100644
index 0000000..01a02c6
--- /dev/null
+++ b/modules/ls0xx/ls0xx.c
@@ -0,0 +1,277 @@
+// Copyright Pablo Martinez (@elpekenin) <[email protected]>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "ls0xx.h"
+
+#include "ls0xx_opcodes.h"
+#include "qp_comms_spi.h"
+#include "qp_surface_internal.h"
+#include "spi_master.h"
+
+typedef struct {
+ painter_driver_t base; // must be first, so it can be cast to/from the
+ // painter_device_t* type
+ qp_comms_spi_config_t spi_config;
+ surface_painter_device_t surface;
+} ls0xx_painter_device_t;
+
+// need custom comms vtable for inverted CS logic
+bool inverted_cs_init(painter_device_t device) {
+ painter_driver_t *driver = (painter_driver_t *)device;
+ qp_comms_spi_config_t *comms_config =
+ (qp_comms_spi_config_t *)driver->comms_config;
+
+ // Initialize the SPI peripheral
+ spi_init();
+
+ // Set up CS as output high
+ gpio_set_pin_output(comms_config->chip_select_pin);
+ gpio_write_pin_low(comms_config->chip_select_pin);
+
+ return true;
+}
+
+bool inverted_cs_start(painter_device_t device) {
+ painter_driver_t *driver = (painter_driver_t *)device;
+ qp_comms_spi_config_t *comms_config =
+ (qp_comms_spi_config_t *)driver->comms_config;
+
+ spi_start_config_t config = (spi_start_config_t){
+ .slave_pin = comms_config->chip_select_pin,
+ .lsb_first = comms_config->lsb_first,
+ .mode = comms_config->mode,
+ .divisor = comms_config->divisor,
+ .cs_active_low = false,
+ };
+
+ return spi_start_extended(&config);
+}
+
+const painter_comms_vtable_t spi_comms_inverted_cs_vtable = {
+ .comms_init = inverted_cs_init,
+ .comms_start = inverted_cs_start,
+ .comms_send = qp_comms_spi_send_data,
+ .comms_stop = qp_comms_spi_stop,
+};
+
+ls0xx_painter_device_t ls0xx_device_t_drivers[LS0XX_NUM_DEVICES] = {0};
+
+__attribute__((weak)) bool qp_ls0xx_init(painter_device_t device,
+ painter_rotation_t rotation) {
+ ls0xx_painter_device_t *ls0xx = (ls0xx_painter_device_t *)device;
+ painter_driver_t *surface = (painter_driver_t *)&ls0xx->surface;
+
+ // Change the surface geometry based on the panel rotation
+ if (rotation == QP_ROTATION_90 || rotation == QP_ROTATION_270) {
+ surface->panel_width = ls0xx->base.panel_height;
+ surface->panel_height = ls0xx->base.panel_width;
+ } else {
+ surface->panel_width = ls0xx->base.panel_width;
+ surface->panel_height = ls0xx->base.panel_height;
+ }
+
+ // Init the internal surface
+ if (!surface->driver_vtable->init(surface, QP_ROTATION_0)) {
+ qp_dprintf("Failed to init internal surface in qp_ls0xx_init\n");
+ return false;
+ }
+
+ ls0xx->base.rotation = rotation;
+
+ writePinHigh(ls0xx->spi_config.chip_select_pin);
+ const uint8_t ls0xx_init_sequence[] = {LS0XX_CLEAR, 0};
+ spi_transmit(ls0xx_init_sequence, ARRAY_SIZE(ls0xx_init_sequence));
+ writePinLow(ls0xx->spi_config.chip_select_pin);
+
+ return true;
+}
+
+bool qp_ls0xx_passthru_power(painter_device_t device, bool power_on) {
+ // No-op
+ return true;
+}
+
+bool qp_ls0xx_passthru_clear(painter_device_t device) {
+ // Just re-init the display
+ painter_driver_t *driver = (painter_driver_t *)device;
+ return qp_ls0xx_init(device, driver->rotation);
+}
+
+bool qp_ls0xx_flush(painter_device_t device) {
+ ls0xx_painter_device_t *ls0xx = (ls0xx_painter_device_t *)device;
+ surface_painter_device_t *surface = &(ls0xx->surface);
+ surface_dirty_data_t dirty = surface->dirty;
+
+ if (!dirty.is_dirty) {
+ // nothing to be sent
+ return true;
+ }
+
+ // find out dirty area. update is done on **complete** horizontal lines,
+ // which means that if device is sideways, we need to check l/r instead
+ uint16_t top = 0;
+ uint16_t bottom = 0;
+ switch (ls0xx->base.rotation) {
+ case QP_ROTATION_0:
+ top = dirty.t;
+ bottom = dirty.b;
+ break;
+
+ // TODO: Check whether this are backwards
+ case QP_ROTATION_90:
+ top = dirty.l;
+ bottom = dirty.r;
+ break;
+
+ case QP_ROTATION_180:
+ top = dirty.b;
+ bottom = dirty.t;
+ break;
+
+ case QP_ROTATION_270:
+ top = dirty.r;
+ bottom = dirty.l;
+ break;
+ }
+
+ // bytes sent for each row's data
+ uint8_t bytes_per_line =
+ (ls0xx->base.panel_width + 7) / 8 * ls0xx->base.native_bits_per_pixel;
+ // offset used to access such data
+ uint16_t buffer_offset = top * bytes_per_line;
+
+ // start sending
+ writePinHigh(ls0xx->spi_config.chip_select_pin);
+ uint8_t cmd = LS0XX_WRITE | LS0XX_VCOM;
+ spi_transmit(&cmd, 1);
+
+ // dummy data for alignment, value doesn't matter
+ uint8_t dummy = 0;
+
+ // iterate over the lines
+ for (uint8_t i = 0; i < bottom + 1 - top; ++i) {
+ // set y-pos (counts from 1, needs the `+1`)
+ uint8_t n_line = bitrev(i + top + 1);
+ spi_transmit(&n_line, 1);
+
+ // send data
+ spi_transmit(&surface->u8buffer[buffer_offset], bytes_per_line);
+ buffer_offset += bytes_per_line;
+
+ spi_transmit(&dummy, 1);
+ }
+
+ spi_transmit(&dummy, 1);
+ writePinLow(ls0xx->spi_config.chip_select_pin);
+
+ // clear surface's dirty area, no API to prevent extra prints
+ surface->base.driver_vtable->flush(surface);
+
+ return true;
+}
+
+bool qp_ls0xx_passthru_viewport(painter_device_t device, uint16_t left,
+ uint16_t top, uint16_t right, uint16_t bottom) {
+ ls0xx_painter_device_t *ls0xx = (ls0xx_painter_device_t *)device;
+ painter_driver_t *surface = (painter_driver_t *)&ls0xx->surface;
+
+ return surface->driver_vtable->viewport(surface, left, top, right, bottom);
+}
+
+bool qp_ls0xx_passthru_pixdata(painter_device_t device, const void *pixel_data,
+ uint32_t native_pixel_count) {
+ ls0xx_painter_device_t *ls0xx = (ls0xx_painter_device_t *)device;
+ painter_driver_t *surface = (painter_driver_t *)&ls0xx->surface;
+
+ return surface->driver_vtable->pixdata(surface, pixel_data,
+ native_pixel_count);
+}
+
+bool qp_ls0xx_passthru_palette_convert(painter_device_t device,
+ int16_t palette_size,
+ qp_pixel_t *palette) {
+ ls0xx_painter_device_t *ls0xx = (ls0xx_painter_device_t *)device;
+ painter_driver_t *surface = (painter_driver_t *)&ls0xx->surface;
+
+ return surface->driver_vtable->palette_convert(surface, palette_size,
+ palette);
+}
+
+bool qp_ls0xx_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) {
+ ls0xx_painter_device_t *ls0xx = (ls0xx_painter_device_t *)device;
+ painter_driver_t *surface = (painter_driver_t *)&ls0xx->surface;
+
+ return surface->driver_vtable->append_pixels(surface, target_buffer, palette,
+ pixel_offset, pixel_count,
+ palette_indices);
+}
+
+bool qp_ls0xx_passthru_append_pixdata(painter_device_t device,
+ uint8_t *target_buffer,
+ uint32_t pixdata_offset,
+ uint8_t pixdata_byte) {
+ ls0xx_painter_device_t *ls0xx = (ls0xx_painter_device_t *)device;
+ painter_driver_t *surface = (painter_driver_t *)&ls0xx->surface;
+
+ return surface->driver_vtable->append_pixdata(surface, target_buffer,
+ pixdata_offset, pixdata_byte);
+}
+
+const painter_driver_vtable_t ls0xx_driver_vtable = {
+ .init = qp_ls0xx_init,
+ .power = qp_ls0xx_passthru_power,
+ .clear = qp_ls0xx_passthru_clear,
+ .flush = qp_ls0xx_flush,
+ .pixdata = qp_ls0xx_passthru_pixdata,
+ .viewport = qp_ls0xx_passthru_viewport,
+ .palette_convert = qp_ls0xx_passthru_palette_convert,
+ .append_pixels = qp_ls0xx_passthru_append_pixels,
+ .append_pixdata = qp_ls0xx_passthru_append_pixdata,
+};
+
+painter_device_t qp_ls0xx_device_t_make_spi_device(uint16_t panel_width,
+ uint16_t panel_height,
+ pin_t chip_select_pin,
+ uint16_t spi_divisor,
+ int spi_mode, void *buf) {
+ for (uint32_t i = 0; i < LS0XX_NUM_DEVICES; ++i) {
+ ls0xx_painter_device_t *driver = &ls0xx_device_t_drivers[i];
+ if (!driver->base.driver_vtable) {
+ painter_device_t surface = qp_make_mono1bpp_surface_advanced(
+ &driver->surface, 1, panel_width, panel_height, buf);
+ if (!surface) {
+ return NULL;
+ }
+ driver->base.driver_vtable =
+ (const painter_driver_vtable_t *)&ls0xx_driver_vtable;
+ driver->base.comms_vtable =
+ (const painter_comms_vtable_t *)&spi_comms_inverted_cs_vtable;
+ driver->base.native_bits_per_pixel = 1;
+ driver->base.panel_width = panel_width;
+ driver->base.panel_height = panel_height;
+ driver->base.rotation = QP_ROTATION_0;
+ driver->base.offset_x = 0;
+ driver->base.offset_y = 0;
+
+ // SPI and other pin configuration
+ driver->base.comms_config = &driver->spi_config;
+ driver->spi_config.chip_select_pin = chip_select_pin;
+ driver->spi_config.lsb_first = false;
+ driver->spi_config.divisor = spi_divisor;
+ driver->spi_config.mode = spi_mode;
+
+ if (!qp_internal_register_device((painter_device_t)driver)) {
+ memset(driver, 0, sizeof(ls0xx_painter_device_t));
+ return NULL;
+ }
+
+ return (painter_device_t)driver;
+ }
+ }
+
+ return NULL;
+}
diff --git a/modules/ls0xx/ls0xx.h b/modules/ls0xx/ls0xx.h
new file mode 100644
index 0000000..49339e9
--- /dev/null
+++ b/modules/ls0xx/ls0xx.h
@@ -0,0 +1,27 @@
+// Copyright Pablo Martinez (@elpekenin) <[email protected]>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "qp.h"
+
+#ifndef LS0XX_NUM_DEVICES
+#define LS0XX_NUM_DEVICES 1
+#endif
+
+/**
+ * Factory method for an LS0XX SPI device.
+ *
+ * Args:
+ * panel_width: Width of the display
+ * panel_height: Height of the display
+ * chip_select_pin: GPIO pin used for SPI chip select (CS)
+ * spi_divisor: SPI divisor to use when communicating with the display
+ * spi_mode: SPI mode to use when communicating with the display
+ * buf: Address of the buffer where data will be stored
+ */
+painter_device_t qp_ls0xx_device_t_make_spi_device(uint16_t panel_width,
+ uint16_t panel_height,
+ pin_t chip_select_pin,
+ uint16_t spi_divisor,
+ int spi_mode, void *buf);
diff --git a/modules/ls0xx/ls0xx_opcodes.h b/modules/ls0xx/ls0xx_opcodes.h
new file mode 100644
index 0000000..2c3928a
--- /dev/null
+++ b/modules/ls0xx/ls0xx_opcodes.h
@@ -0,0 +1,9 @@
+// Copyright Pablo Martinez (@elpekenin) <[email protected]>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+// NOTE: written LSB first as the display expects them
+#define LS0XX_WRITE 0x80 // Start sending framebuffer
+#define LS0XX_VCOM 0x40 // Set VCOM high
+#define LS0XX_CLEAR 0x20 // Clear
diff --git a/modules/ls0xx/qmk_module.json b/modules/ls0xx/qmk_module.json
new file mode 100644
index 0000000..4d5af68
--- /dev/null
+++ b/modules/ls0xx/qmk_module.json
@@ -0,0 +1,4 @@
+{
+ "maintainer": "elpekenin",
+ "module_name": "ls0xx"
+}
diff --git a/modules/ls0xx/rules.mk b/modules/ls0xx/rules.mk
new file mode 100644
index 0000000..351f33b
--- /dev/null
+++ b/modules/ls0xx/rules.mk
@@ -0,0 +1,7 @@
+QUANTUM_PAINTER_NEEDS_SURFACE := yes
+
+# FIXME: not sure if this here is good enough, may be processed *after*
+# qp's internal makefile and not impact it
+QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes
+
+SRC += $(MODULE_PATH_LS0XX)/ls0xx.c