uzbek-plus/b9/b9l/lk2nd-23.1-aurora-b9l.patch
q 4a7224c41f B9: built-in display (ST7735S SPI) in Linux and persistent B9C cache
- B9A: read-only audit of stock LK/DT: the panel is an ST7735S 128x128 on SPI (BLSP1 QUP4,
  16 MHz, mode 3, D/C GPIO116, RESET GPIO118), backlight = PM8916 MPP4 current sink 40 mA.
- B9B: upstream panel-mipi-dbi with the stock init sequence as firmware; MPP4 sink via pinctrl
  + gpio-backlight; first light with correct colours/orientation/offsets.
- B9C: fbcon (6x8, 21x16) on tty1 + getty, UART console kept, aurora-display service;
  cache B9C b9ce13c9 written and verified, cold boots with LTE + Wi-Fi + display.
- B9L (RAM only): lk2nd second stage with an msm8916 SPI panel port shows a picture before Linux.
- Sanitizer: whitelist for b8/ and b9/.
2026-10-03 01:45:02 +03:00

411 lines
15 KiB
Diff

diff --git a/lk2nd/aurora/rules.mk b/lk2nd/aurora/rules.mk
new file mode 100644
index 00000000..3c97f9cd
--- /dev/null
+++ b/lk2nd/aurora/rules.mk
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Aurora B9L (RAM test only): ST7735S SPI panel bring-up for the JZ08AU board (msm8916), stock-LK parameters.
+# Uses the CAF spi_qup driver directly; platform/msm_shared/mdss_spi.o is deliberately NOT built (it is hard-wired
+# to QM215/msm8909 and its presence would pull in lk2nd/device/2nd/spi-display.o, which dereferences a NULL panel.intf).
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+OBJS += \
+ platform/msm_shared/spi_qup.o \
+ $(LOCAL_DIR)/st7735s.o \
diff --git a/lk2nd/aurora/st7735s.c b/lk2nd/aurora/st7735s.c
new file mode 100644
index 00000000..8bfaeae3
--- /dev/null
+++ b/lk2nd/aurora/st7735s.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Aurora JZ08AU (msm8916) ST7735S 128x128 SPI panel bring-up — B9L RAM test only.
+ * Every parameter is taken from the stock Aurora LK (aboot) analysis (logs/b9/b9a, logs/b9/lk):
+ * BLSP1 QUP4 (0x78b8000), 16 MHz, SPI mode 3 (CLK_IDLE_HIGH, INPUT_FIRST=0), CS0, TX only;
+ * D/C = GPIO116, RESET = GPIO118 (1, 0 for 1 ms, 1, wait 120 ms); the 19 stock init commands (ending with RAMWR);
+ * one RGB565 frame (16-bit words); backlight = PM8916 MPP4 current sink 40 mA (MODE 0x61, SINK 7, EN 0x80).
+ * No regulator is touched (stock LK has no regulator code either); L6/L17 state is only read and logged.
+ * The display is NOT registered with fbcon, so lk2nd's own display code cannot draw over the test frame.
+ */
+#include <debug.h>
+#include <reg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <platform.h>
+#include <platform/timer.h>
+#include <platform/gpio.h>
+#include <platform/iomap.h>
+#include <blsp_qup.h>
+#include <spi_qup.h>
+#include <pm8x41_hw.h>
+
+#include <lk2nd/init.h>
+
+#define DC_GPIO 116
+#define RST_GPIO 118
+#define W 128
+#define H 128
+
+#define TLMM_CFG(n) (TLMM_BASE_ADDR + 0x1000 * (n))
+#define TLMM_IO(n) (TLMM_BASE_ADDR + 0x1000 * (n) + 4)
+
+static struct qup_spi_dev *dev;
+
+/* stock LK table @0x8f64849c == stock dtb_01 qcom,mdss-spi-on-command (b9/b9a/decode-panel.py) */
+static const struct { uint8_t len, wait; uint8_t d[17]; } cmds[] = {
+ { 1, 120, { 0x11 } },
+ { 4, 0, { 0xb1, 0x05, 0x3a, 0x3a } },
+ { 4, 0, { 0xb2, 0x05, 0x3a, 0x3a } },
+ { 7, 0, { 0xb3, 0x05, 0x3a, 0x3a, 0x05, 0x3a, 0x3a } },
+ { 2, 0, { 0xb4, 0x03 } },
+ { 4, 0, { 0xc0, 0x62, 0x02, 0x04 } },
+ { 2, 0, { 0xc1, 0xc0 } },
+ { 3, 0, { 0xc2, 0x0d, 0x00 } },
+ { 3, 0, { 0xc3, 0x8d, 0x6a } },
+ { 3, 0, { 0xc4, 0x8d, 0xee } },
+ { 2, 0, { 0xc5, 0x12 } },
+ { 17, 0, { 0xe0, 0x03, 0x1b, 0x12, 0x11, 0x3f, 0x3a, 0x32, 0x34, 0x2f, 0x2b, 0x30, 0x3a, 0x00, 0x01, 0x02, 0x05 } },
+ { 17, 0, { 0xe1, 0x03, 0x1b, 0x12, 0x11, 0x32, 0x2f, 0x2a, 0x2f, 0x2e, 0x2c, 0x35, 0x3f, 0x00, 0x00, 0x01, 0x05 } },
+ { 2, 0, { 0x36, 0xc8 } },
+ { 2, 0, { 0x3a, 0x05 } },
+ { 5, 0, { 0x2a, 0x00, 0x02, 0x00, 0x81 } },
+ { 5, 0, { 0x2b, 0x00, 0x03, 0x00, 0x82 } },
+ { 1, 0, { 0x29 } },
+ { 1, 0, { 0x2c } },
+};
+
+static unsigned t0;
+#define LOG(fmt, ...) dprintf(INFO, "[B9L +%u ms] " fmt "\n", (unsigned)(current_time() - t0), ##__VA_ARGS__)
+
+static int spi_cmd(uint8_t c)
+{
+ int ret;
+ dev->bytes_per_word = 1;
+ dev->bit_shift_en = 1;
+ gpio_set_dir(DC_GPIO, 0); /* D/C low = command */
+ ret = spi_qup_write(dev, &c, 1);
+ gpio_set_dir(DC_GPIO, 2);
+ return ret;
+}
+
+static int spi_data(const uint8_t *b, unsigned len)
+{
+ dev->bytes_per_word = 1;
+ dev->bit_shift_en = 1;
+ gpio_set_dir(DC_GPIO, 2); /* D/C high = parameters */
+ return spi_qup_write(dev, b, len);
+}
+
+static int spi_frame(const uint8_t *b, unsigned len)
+{
+ dev->bytes_per_word = 2; /* RGB565, little-endian u16 in memory, sent MSB first (as stock LK) */
+ dev->bit_shift_en = 1;
+ dev->unpack_en = 0;
+ gpio_set_dir(DC_GPIO, 2);
+ return spi_qup_write(dev, b, len);
+}
+
+static uint16_t rgb(uint8_t r, uint8_t g, uint8_t b)
+{
+ return (r >> 3) << 11 | (g >> 2) << 5 | b >> 3;
+}
+
+/* same chart as the Linux B9B test: 1-px white border, R/G/B/W corners, R|G|B bars, yellow arrow up */
+static void draw_geo(uint16_t *p)
+{
+ const uint16_t R = rgb(255, 0, 0), G = rgb(0, 255, 0), B = rgb(0, 0, 255), Wh = rgb(255, 255, 255), Y = rgb(255, 255, 0);
+ int x, y;
+
+ for (y = 0; y < H; y++)
+ for (x = 0; x < W; x++) {
+ uint16_t c = 0;
+ int ax = x * 2 - 127; /* 2*(x - 63.5) */
+ if (x == 0 || x == W - 1 || y == 0 || y == H - 1)
+ c = Wh;
+ else if (x >= 2 && x < 26 && y >= 2 && y < 26)
+ c = R;
+ else if (x >= W - 26 && x < W - 2 && y >= 2 && y < 26)
+ c = G;
+ else if (x >= 2 && x < 26 && y >= H - 26 && y < H - 2)
+ c = B;
+ else if (x >= W - 26 && x < W - 2 && y >= H - 26 && y < H - 2)
+ c = Wh;
+ else if (y >= 40 && y < 88 && x >= 28 && x < 100)
+ c = (x - 28) < 24 ? R : (x - 28) < 48 ? G : B;
+ else if (y >= 6 && y < 22 && (ax < 0 ? -ax : ax) <= (y - 6))
+ c = Y;
+ else if (y >= 22 && y < 34 && x >= 58 && x < 70)
+ c = Y;
+ p[y * W + x] = c;
+ }
+}
+
+static void log_power(const char *when)
+{
+ /* PM8916 SID1: LDO6 0x4500, LDO17 0x5000 (08 STATUS, 46 EN_CTL); SID0: MPP4 0xa300 (40 MODE, 46 EN, 4c SINK) */
+ LOG("%s: L6 st=%02x en=%02x L17 st=%02x en=%02x vset=%02x MPP4 mode=%02x en=%02x sink=%02x", when,
+ pm8x41_reg_read(0x14508), pm8x41_reg_read(0x14546),
+ pm8x41_reg_read(0x15008), pm8x41_reg_read(0x15046), pm8x41_reg_read(0x15041),
+ pm8x41_reg_read(0xa340), pm8x41_reg_read(0xa346), pm8x41_reg_read(0xa34c));
+}
+
+static void log_hw(const char *when)
+{
+ LOG("%s: TLMM cfg/io 12=%x/%x 13=%x 14=%x 15=%x 116=%x/%x 118=%x/%x QUP4 spi CBCR=%08x RCGR=%08x/%08x", when,
+ readl(TLMM_CFG(12)), readl(TLMM_IO(12)), readl(TLMM_CFG(13)), readl(TLMM_CFG(14)), readl(TLMM_CFG(15)),
+ readl(TLMM_CFG(DC_GPIO)), readl(TLMM_IO(DC_GPIO)), readl(TLMM_CFG(RST_GPIO)), readl(TLMM_IO(RST_GPIO)),
+ readl(CLK_CTL_BASE + 0x501C), readl(CLK_CTL_BASE + 0x5024), readl(CLK_CTL_BASE + 0x5028));
+}
+
+static void aurora_st7735s_init(void)
+{
+ uint16_t *fb;
+ unsigned i, fails = 0;
+ int ret;
+
+ t0 = current_time();
+ LOG("ST7735S bring-up start (stock Aurora parameters)");
+ log_power("before");
+ log_hw("before");
+
+ /* RESET GPIO118: func0 out 8 mA, OE; 1 -> 1 ms -> 0 -> 1 ms -> 1 -> 120 ms (stock target_panel_reset) */
+ gpio_tlmm_config(RST_GPIO, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA, 1);
+ gpio_set_dir(RST_GPIO, 2); mdelay(1);
+ gpio_set_dir(RST_GPIO, 0); mdelay(1);
+ gpio_set_dir(RST_GPIO, 2); mdelay(120);
+ LOG("reset done (GPIO118 high)");
+
+ dev = qup_blsp_spi_init(BLSP_ID_1, QUP_ID_3);
+ if (!dev) {
+ LOG("FAIL: qup_blsp_spi_init(BLSP1, QUP_ID_3)");
+ return;
+ }
+ dev->force_cs_dis = 1; /* stock SPI_IO_CONTROL = 0x401: NO_TRI_STATE | CLK_IDLE_HIGH, CS0, no FORCE_CS */
+ /* D/C GPIO116: func0 out 8 mA, OE (stock) */
+ gpio_tlmm_config(DC_GPIO, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA, 1);
+ gpio_set_dir(DC_GPIO, 2);
+ LOG("QUP4 SPI init done: base=0x%x", dev->qup_base);
+ log_hw("after QUP init");
+
+ for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
+ ret = spi_cmd(cmds[i].d[0]);
+ if (!ret && cmds[i].len > 1)
+ ret = spi_data(&cmds[i].d[1], cmds[i].len - 1);
+ if (ret) {
+ fails++;
+ LOG("cmd %u (0x%02x) FAILED ret=%d", i, cmds[i].d[0], ret);
+ }
+ if (cmds[i].wait)
+ mdelay(cmds[i].wait);
+ }
+ LOG("19 init commands sent, %u failures; SPI_IO_CONTROL=%x SPI_CONFIG=%x", fails,
+ readl(dev->qup_base + SPI_IO_CONTROL), readl(dev->qup_base + SPI_CONFIG));
+
+ fb = memalign(64, W * H * 2);
+ if (!fb) {
+ LOG("FAIL: no memory for frame");
+ return;
+ }
+ draw_geo(fb);
+ ret = spi_frame((const uint8_t *)fb, W * H * 2);
+ LOG("frame %ux%u RGB565 (%u bytes) sent ret=%d (fb @%p)", W, H, W * H * 2, ret, fb);
+
+ /* backlight: PM8916 MPP4 current sink, exactly the stock LK target_backlight_ctrl writes */
+ pm8x41_reg_write(0xa340, 0x61);
+ pm8x41_reg_write(0xa34c, 0x07);
+ pm8x41_reg_write(0xa346, 0x80);
+ mdelay(20);
+ log_power("after backlight");
+ log_hw("end");
+ LOG("ST7735S bring-up end - continuing lk2nd boot");
+}
+LK2ND_INIT(aurora_st7735s_init);
diff --git a/platform/msm8916/acpuclock.c b/platform/msm8916/acpuclock.c
index 158ab395..b7533e6c 100644
--- a/platform/msm8916/acpuclock.c
+++ b/platform/msm8916/acpuclock.c
@@ -529,3 +529,39 @@ void clock_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id)
return;
}
}
+
+/*
+ * Aurora B9L: SPI core clock, copied from msm8909 acpuclock.c. The CAF spi_qup driver always asks for MAX_SPEED_HZ
+ * (50 MHz, qup.h); the Aurora ST7735S panel runs at 16 MHz in stock LK (gcc_blsp1_qup4_spi_apps_clk = 16000000),
+ * so the rate is clamped here.
+ */
+#define AURORA_SPI_MAX_HZ 16000000
+void clock_config_blsp_spi(uint8_t blsp_id, uint8_t qup_id, unsigned long rate)
+{
+ uint8_t ret = 0;
+ char clk_name[64];
+
+ qup_id = qup_id + 1;
+
+ if (blsp_id != BLSP_ID_1) {
+ dprintf(CRITICAL, "Incorrect BLSP-%d configuration\n", blsp_id);
+ ASSERT(0);
+ }
+ if (rate > AURORA_SPI_MAX_HZ)
+ rate = AURORA_SPI_MAX_HZ;
+
+ snprintf(clk_name, sizeof(clk_name), "blsp1_ahb_iface_clk");
+ ret = clk_get_set_enable(clk_name, 0, 1);
+ if (ret) {
+ dprintf(CRITICAL, "%s: Failed to enable %s clock\n", __func__, clk_name);
+ return;
+ }
+
+ snprintf(clk_name, sizeof(clk_name), "gcc_blsp1_qup%u_spi_apps_clk", qup_id);
+ ret = clk_get_set_enable(clk_name, rate, 1);
+ if (ret) {
+ dprintf(CRITICAL, "%s: Failed to enable %s\n", __func__, clk_name);
+ return;
+ }
+ dprintf(INFO, "B9L: %s enabled at %lu Hz\n", clk_name, rate);
+}
diff --git a/platform/msm8916/gpio.c b/platform/msm8916/gpio.c
index 145b81ce..2912792b 100644
--- a/platform/msm8916/gpio.c
+++ b/platform/msm8916/gpio.c
@@ -26,6 +26,7 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <assert.h>
#include <debug.h>
#include <reg.h>
#include <platform/iomap.h>
@@ -99,3 +100,19 @@ void gpio_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id)
ASSERT(0);
}
}
+
+/*
+ * Aurora B9L: BLSP1 QUP4 (QUP_ID_3, 0x78b8000) SPI pins as in stock Aurora dtb_01 spi0_active/spi0_cs0_active:
+ * GPIO12 MOSI, GPIO13 MISO, GPIO15 CLK func 1, 12 mA, no pull; GPIO14 CS0 func 1 (native chip select), 2 mA, no pull.
+ */
+void gpio_config_blsp_spi(uint8_t blsp_id, uint8_t qup_id)
+{
+ if (blsp_id != BLSP_ID_1 || qup_id != QUP_ID_3) {
+ dprintf(CRITICAL, "Unsupported BLSP-%d QUP-%d SPI pin configuration\n", blsp_id, qup_id);
+ ASSERT(0);
+ }
+ gpio_tlmm_config(12, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_12MA, GPIO_DISABLE);
+ gpio_tlmm_config(13, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_12MA, GPIO_DISABLE);
+ gpio_tlmm_config(14, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA, GPIO_DISABLE);
+ gpio_tlmm_config(15, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_12MA, GPIO_DISABLE);
+}
diff --git a/platform/msm8916/include/platform/clock.h b/platform/msm8916/include/platform/clock.h
index fab62591..c0f00312 100644
--- a/platform/msm8916/include/platform/clock.h
+++ b/platform/msm8916/include/platform/clock.h
@@ -95,4 +95,5 @@ void gcc_dsi_clocks_disable(uint8_t dual_dsi);
void clock_ce_enable(uint8_t instance);
void clock_ce_disable(uint8_t instance);
void clock_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id);
+void clock_config_blsp_spi(uint8_t blsp_id, uint8_t qup_id, unsigned long rate);
#endif
diff --git a/platform/msm8916/include/platform/gpio.h b/platform/msm8916/include/platform/gpio.h
index 0437aff4..ec4d5859 100644
--- a/platform/msm8916/include/platform/gpio.h
+++ b/platform/msm8916/include/platform/gpio.h
@@ -70,4 +70,5 @@ void gpio_tlmm_config(uint32_t gpio,
uint8_t drvstr,
uint32_t enable);
void gpio_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id);
+void gpio_config_blsp_spi(uint8_t blsp_id, uint8_t qup_id);
#endif
diff --git a/platform/msm8916/include/platform/iomap.h b/platform/msm8916/include/platform/iomap.h
index 86a804bb..9e9e09d0 100644
--- a/platform/msm8916/include/platform/iomap.h
+++ b/platform/msm8916/include/platform/iomap.h
@@ -126,6 +126,13 @@
#define GCC_BLSP1_QUP4_APPS_CBCR (CLK_CTL_BASE + 0x5020)
#define GCC_BLSP1_QUP4_CFG_RCGR (CLK_CTL_BASE + 0x5004)
#define GCC_BLSP1_QUP4_CMD_RCGR (CLK_CTL_BASE + 0x5000)
+/* Aurora B9L: BLSP1 QUP4 SPI clock (== msm8909 CAF, == Linux gcc-msm8916 0x5024/0x501c) */
+#define GCC_BLSP1_QUP4_SPI_APPS_CBCR (CLK_CTL_BASE + 0x501C)
+#define GCC_BLSP1_QUP4_SPI_APPS_CMD_RCGR (CLK_CTL_BASE + 0x5024)
+#define GCC_BLSP1_QUP4_SPI_CFG_RCGR (CLK_CTL_BASE + 0x5028)
+#define GCC_BLSP1_QUP4_SPI_APPS_M (CLK_CTL_BASE + 0x502C)
+#define GCC_BLSP1_QUP4_SPI_APPS_N (CLK_CTL_BASE + 0x5030)
+#define GCC_BLSP1_QUP4_SPI_APPS_D (CLK_CTL_BASE + 0x5034)
/* GPLL */
#define GPLL0_STATUS (CLK_CTL_BASE + 0x2101C)
diff --git a/platform/msm8916/msm8916-clock.c b/platform/msm8916/msm8916-clock.c
index a1b4b773..bec650a2 100644
--- a/platform/msm8916/msm8916-clock.c
+++ b/platform/msm8916/msm8916-clock.c
@@ -587,6 +587,45 @@ static struct branch_clk gcc_blsp1_qup4_i2c_apps_clk = {
},
};
+/* Aurora B9L: BLSP1 QUP4 SPI clock, copied from msm8909-clock.c (freq table == Linux gcc-msm8916) */
+static struct clk_freq_tbl ftbl_gcc_blsp1_qup4_spi_apps_clk[] = {
+ F( 960000, cxo, 10, 1, 2),
+ F( 4800000, cxo, 4, 0, 0),
+ F( 9600000, cxo, 2, 0, 0),
+ F( 16000000, gpll0, 10, 1, 5),
+ F( 19200000, cxo, 1, 0, 0),
+ F( 25000000, gpll0, 16, 1, 2),
+ F( 50000000, gpll0, 16, 0, 0),
+ F_END
+};
+
+static struct rcg_clk gcc_blsp1_qup4_spi_apps_clk_src =
+{
+ .cmd_reg = (uint32_t *) GCC_BLSP1_QUP4_SPI_APPS_CMD_RCGR,
+ .cfg_reg = (uint32_t *) GCC_BLSP1_QUP4_SPI_CFG_RCGR,
+ .m_reg = (uint32_t *) GCC_BLSP1_QUP4_SPI_APPS_M,
+ .n_reg = (uint32_t *) GCC_BLSP1_QUP4_SPI_APPS_N,
+ .d_reg = (uint32_t *) GCC_BLSP1_QUP4_SPI_APPS_D,
+ .set_rate = clock_lib2_rcg_set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup4_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+
+ .c = {
+ .dbg_name = "gcc_blsp1_qup4_spi_apps_clk_src",
+ .ops = &clk_ops_rcg,
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup4_spi_apps_clk = {
+ .cbcr_reg = (uint32_t *) GCC_BLSP1_QUP4_SPI_APPS_CBCR,
+ .parent = &gcc_blsp1_qup4_spi_apps_clk_src.c,
+
+ .c = {
+ .dbg_name = "gcc_blsp1_qup4_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ },
+};
+
/* Need different gpll0 source value for CPU clocks */
#undef gpll0_source_val
#define gpll0_source_val 4
@@ -678,6 +717,9 @@ static struct clk_lookup msm_clocks_8916[] =
CLK_LOOKUP("gcc_blsp1_qup2_i2c_apps_clk", gcc_blsp1_qup2_i2c_apps_clk.c),
CLK_LOOKUP("blsp1_qup4_ahb_iface_clk", gcc_blsp1_ahb_clk.c),
+ CLK_LOOKUP("blsp1_ahb_iface_clk", gcc_blsp1_ahb_clk.c), /* Aurora B9L: name used by clock_config_blsp_spi */
+ CLK_LOOKUP("gcc_blsp1_qup4_spi_apps_clk_src", gcc_blsp1_qup4_spi_apps_clk_src.c),
+ CLK_LOOKUP("gcc_blsp1_qup4_spi_apps_clk", gcc_blsp1_qup4_spi_apps_clk.c),
CLK_LOOKUP("gcc_blsp1_qup4_i2c_apps_clk_src", gcc_blsp1_qup4_i2c_apps_clk_src.c),
CLK_LOOKUP("gcc_blsp1_qup4_i2c_apps_clk", gcc_blsp1_qup4_i2c_apps_clk.c),
diff --git a/project/lk2nd-msm8916.mk b/project/lk2nd-msm8916.mk
index 192ae770..88c41f4d 100644
--- a/project/lk2nd-msm8916.mk
+++ b/project/lk2nd-msm8916.mk
@@ -1,3 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
TARGET := msm8916
include lk2nd/project/lk2nd.mk
+
+# Aurora B9L (RAM test only): ST7735S SPI panel bring-up module
+ifeq ($(AURORA_SPI_PANEL),1)
+MODULES += lk2nd/aurora
+endif