diff --git a/components/badge23/CMakeLists.txt b/components/badge23/CMakeLists.txt
index 732edf14ee0793c536e049c1b9f274f0de538408..65959cc07b0844519af076d17349682758e801bc 100644
--- a/components/badge23/CMakeLists.txt
+++ b/components/badge23/CMakeLists.txt
@@ -13,6 +13,6 @@ idf_component_register(
         include
     REQUIRES
         badge23_hwconfig
-        gc9a01
+        flow3r_bsp
         espressif__led_strip
 )
diff --git a/components/badge23/display.c b/components/badge23/display.c
index 86b88cf63969ea495a3debeed450ff0a6f288660..92d234956b730875a4a8dac5691f80ea51d5b608 100644
--- a/components/badge23/display.c
+++ b/components/badge23/display.c
@@ -1,58 +1,83 @@
 #include "badge23/display.h"
-#include "gc9a01.h"
 
-#include "esp_log.h"
-#include <freertos/FreeRTOS.h>
-#include <freertos/task.h>
-#include <freertos/timers.h>
-#include <freertos/queue.h>
 #include <stdio.h>
 #include <string.h>
 #include <math.h>
 
+#include "esp_log.h"
 #include "esp_system.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
 
 #include "../../usermodule/uctx/uctx/ctx.h"
 
-volatile Ctx *the_ctx = NULL;
+#include "flow3r_bsp.h"
 
+static Ctx *the_ctx = NULL;
+static volatile bool initialized = false;
+static DMA_ATTR uint16_t ctx_framebuffer[FLOW3R_BSP_DISPLAY_WIDTH * FLOW3R_BSP_DISPLAY_HEIGHT];
 
 void display_ctx_init() {
     the_ctx = ctx_new_for_framebuffer(
-        ScreenBuff,
-        GC9A01_Width,
-        GC9A01_Height,
-        GC9A01_Width * 2,
+        ctx_framebuffer,
+        FLOW3R_BSP_DISPLAY_WIDTH,
+        FLOW3R_BSP_DISPLAY_HEIGHT,
+        FLOW3R_BSP_DISPLAY_WIDTH * 2,
         CTX_FORMAT_RGB565_BYTESWAPPED
     );
 
+	int32_t offset_x = FLOW3R_BSP_DISPLAY_WIDTH / 2;
+	int32_t offset_y = FLOW3R_BSP_DISPLAY_HEIGHT / 2;
     // rotate by 180 deg and translate x and y by 120 px to have (0,0) at the center of the screen
-    ctx_apply_transform(the_ctx,-1,0,120,0,-1,120,0,0,1);
+    ctx_apply_transform(the_ctx,-1,0,offset_x,0,-1,offset_y,0,0,1);
 }
 
-void display_update(){
-    GC9A01_Update();
-}
+static void display_loading_splash(void) {
+    ctx_rgb(the_ctx, 0.157, 0.129, 0.167);
+    ctx_rectangle(the_ctx, -120, -120, 240, 240);
+    ctx_fill(the_ctx);
+    
+    ctx_move_to(the_ctx, 0, 0);
+    ctx_rgb(the_ctx, 0.9, 0.9, 0.9);
+    ctx_text_align(the_ctx, CTX_TEXT_ALIGN_CENTER);
+    ctx_text_baseline(the_ctx, CTX_TEXT_BASELINE_ALPHABETIC);
+    ctx_text(the_ctx, "Loading...");
 
-void display_draw_pixel(uint8_t x, uint8_t y, uint16_t col){
-    GC9A01_DrawPixel(x, y, col);
+    flow3r_bsp_display_send_fb(ctx_framebuffer);
 }
 
-uint16_t display_get_pixel(uint8_t x, uint8_t y){
-    return GC9A01_GetPixel(x,y);
-}
+void display_init() {
+    flow3r_bsp_display_init();
+    display_ctx_init();
+    display_loading_splash();
+    // Delay turning on backlight, otherwise we get a flash of some old
+    // buffer..? Is the display RAMWR not synchronous..?
+    vTaskDelay(100 / portTICK_PERIOD_MS);
+    flow3r_bsp_display_set_backlight(100);
+
+    memset(ctx_framebuffer, 0, FLOW3R_BSP_DISPLAY_WIDTH * FLOW3R_BSP_DISPLAY_HEIGHT * 2);
 
-void display_fill(uint16_t col){
-    GC9A01_FillRect(0, 0, 240, 240, col);
+    initialized = true;
 }
 
-void display_set_backlight(uint8_t percent) {
-    GC9A01_SetBL(percent);
+void display_update(){
+    if (!initialized) {
+        return;
+    }
+    flow3r_bsp_display_send_fb(ctx_framebuffer);
 }
 
-void display_init() {
-    GC9A01_Init();
-    GC9A01_Update();
-    display_ctx_init();
+void display_set_backlight(uint8_t percent) {
+    if (!initialized) {
+        return;
+    }
+    flow3r_bsp_display_set_backlight(percent);
 }
 
+Ctx *display_global_ctx(void) {
+    if (!initialized) {
+        return NULL;
+    }
+    return the_ctx;
+}
\ No newline at end of file
diff --git a/components/badge23/espan.c b/components/badge23/espan.c
index e9884458eee72a5fe2a0f019e2860f0a9e5adeee..9b6d75d4bd166be263ac5587f4517d795b7b2b02 100644
--- a/components/badge23/espan.c
+++ b/components/badge23/espan.c
@@ -93,6 +93,9 @@ void locks_init(){
 
 void os_app_main(void)
 {
+    // Initialize display first as that gives us a nice splash screen.
+    display_init();
+
     locks_init();
     ESP_LOGI(TAG, "Starting on %s...", badge23_hw_name);
     ESP_ERROR_CHECK(i2c_master_init());
@@ -106,7 +109,6 @@ void os_app_main(void)
 
     captouch_force_calibration();
 
-    display_init();
 
     i2c_queue = xQueueCreate(1,1);
 
diff --git a/components/badge23/include/badge23/display.h b/components/badge23/include/badge23/display.h
index d6a7e9faff1832ad51bb7df6dd98313b0d237fc0..ed043c27deb89247baaf3ca1825237a2721afd69 100644
--- a/components/badge23/include/badge23/display.h
+++ b/components/badge23/include/badge23/display.h
@@ -5,11 +5,10 @@
 #include "../../usermodule/uctx/uctx/ctx.h"
 
 void display_init();
-void display_ctx_init();
-void display_update();
-void display_draw_pixel(uint8_t x, uint8_t y, uint16_t col);
-uint16_t display_get_pixel(uint8_t x, uint8_t y);
-void display_fill(uint16_t col);
 void display_set_backlight(uint8_t percent);
 
-extern volatile Ctx *the_ctx;
+// Transitional functionality operating on global framebuffer and global ctx.
+// Will get replaced with 'real' ctx rendering system.
+void display_ctx_init();
+void display_update();
+Ctx *display_global_ctx(void);
diff --git a/components/flow3r_bsp/CMakeLists.txt b/components/flow3r_bsp/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..52d3990ff90db2cccfc6b6ba6eff8bb02cc3bc6c
--- /dev/null
+++ b/components/flow3r_bsp/CMakeLists.txt
@@ -0,0 +1,7 @@
+idf_component_register(
+    SRCS
+		flow3r_bsp_display.c
+		flow3r_bsp_gc9a01.c
+    INCLUDE_DIRS
+		.
+)
diff --git a/components/flow3r_bsp/flow3r_bsp.h b/components/flow3r_bsp/flow3r_bsp.h
new file mode 100644
index 0000000000000000000000000000000000000000..ef1fd76cfec2b23d61df66e2c1989d45e4967dd3
--- /dev/null
+++ b/components/flow3r_bsp/flow3r_bsp.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <stdint.h>
+
+// Initialize badge display. An error will be reported if the initialization
+// failed.
+//
+// Must be called exactly once from a task and cannot be called cocurrently with
+// any other flow3r_bsp_display_* functions.
+//
+// Side effects: initializes singleton flow3r display object. All other
+// flow3r_bsp_display_* functions operate on same object.
+void flow3r_bsp_display_init(void);
+
+// Send a full framebuffer of 240x240 16bpp pixels to the display. No-op if
+// display hasn't been succesfully initialized.
+//
+// Transfer will be performed using DMA/interrupts and will block the calling
+// FreeRTOS task until finished.
+//
+// This must not be called if another transfer is alraedy being performed. The
+// user code should sequence access and make sure not more than one transfer is
+// performed simultaneously.
+void flow3r_bsp_display_send_fb(uint16_t *fb_data);
+
+// Set display backlight, as integer percent value (from 0 to 100, clamped).
+// No-op if display hasn't been succesfully initialized.
+void flow3r_bsp_display_set_backlight(uint8_t percent);
+
+// Currently same on all generations. Might change on future revisions.
+#define FLOW3R_BSP_DISPLAY_WIDTH 240
+#define FLOW3R_BSP_DISPLAY_HEIGHT 240
\ No newline at end of file
diff --git a/components/flow3r_bsp/flow3r_bsp_display.c b/components/flow3r_bsp/flow3r_bsp_display.c
new file mode 100644
index 0000000000000000000000000000000000000000..40f8941acddcf0d9c5786907f5503e3708fa3b53
--- /dev/null
+++ b/components/flow3r_bsp/flow3r_bsp_display.c
@@ -0,0 +1,86 @@
+#include "flow3r_bsp.h"
+
+#include <string.h>
+
+#include "esp_log.h"
+#include "sdkconfig.h"
+
+#include "flow3r_bsp_gc9a01.h"
+
+static const char *TAG = "flow3r-bsp-display";
+
+#if defined(CONFIG_BADGE23_HW_GEN_P1)
+#define FLOW3R_BSP_GC9A01
+flow3r_bsp_gc9a01_config_t gc9a01_config = {
+	.reset_used = 1,
+	.backlight_used = 0,
+
+	.pin_rst = 38,
+	.pin_sck = 39,
+	.pin_mosi = 41,
+	.pin_cs = 40,
+	.pin_dc = 42,
+
+	.host = 2,
+};
+#elif defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P6)
+#define FLOW3R_BSP_GC9A01
+flow3r_bsp_gc9a01_config_t gc9a01_config = {
+	.reset_used = 0,
+	.backlight_used = 1,
+
+	.pin_sck = 41,
+	.pin_mosi = 42,
+	.pin_cs = 40,
+	.pin_dc = 38,
+	.pin_backlight = 46,
+
+	.host = 2,
+};
+#else
+#error "display unimplemented for this badge generation"
+#endif
+
+#ifdef FLOW3R_BSP_GC9A01
+
+static flow3r_bsp_gc9a01_t gc9a01;
+static uint8_t gc9a01_initialized = 0;
+
+void flow3r_bsp_display_init(void) {
+	ESP_LOGI(TAG, "gc9a01 initializing...");
+	esp_err_t ret = flow3r_bsp_gc9a01_init(&gc9a01, &gc9a01_config);
+	if (ret != ESP_OK) {
+		ESP_LOGE(TAG, "gc9a01 init failed: %s", esp_err_to_name(ret));
+	} else {
+		gc9a01_initialized = 1;
+		ESP_LOGI(TAG, "gc9a01 initialized");
+	}
+}
+
+void flow3r_bsp_display_send_fb(uint16_t *fb_data) {
+	if (!gc9a01_initialized) {
+		return;
+	}
+	static bool had_error = false;
+
+	esp_err_t ret = flow3r_bsp_gc9a01_blit_full(&gc9a01, fb_data);
+	if (ret != ESP_OK) {
+		if (!had_error) {
+			ESP_LOGE(TAG, "display blit failed: %s", esp_err_to_name(ret));
+			had_error = true;
+		}
+	} else {
+		if (had_error) {
+			ESP_LOGI(TAG, "display blit success!");
+			had_error = false;
+		}
+	}
+}
+
+void flow3r_bsp_display_set_backlight(uint8_t percent) {
+	if (!gc9a01_initialized) {
+		return;
+	}
+	flow3r_bsp_gc9a01_backlight_set(&gc9a01, percent);
+}
+#endif
\ No newline at end of file
diff --git a/components/flow3r_bsp/flow3r_bsp_gc9a01.c b/components/flow3r_bsp/flow3r_bsp_gc9a01.c
new file mode 100644
index 0000000000000000000000000000000000000000..0c7f51e98af5b4e76a19df43e7e9ae1e1994b7e5
--- /dev/null
+++ b/components/flow3r_bsp/flow3r_bsp_gc9a01.c
@@ -0,0 +1,597 @@
+// Based on the gc9a01 library by Nadyrshin Ruslan / Liyanboy74.
+//
+// Copyright (c) 2021, liyanboy74
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdint.h>
+#include <string.h>
+
+#include "esp_log.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#include "flow3r_bsp_gc9a01.h"
+#include "sdkconfig.h"
+
+#define Cmd_SLPIN       0x10
+#define Cmd_SLPOUT      0x11
+#define Cmd_INVOFF      0x20
+#define Cmd_INVON       0x21
+#define Cmd_DISPOFF     0x28
+#define Cmd_DISPON      0x29
+#define Cmd_CASET       0x2A
+#define Cmd_RASET       0x2B
+#define Cmd_RAMWR       0x2C
+#define Cmd_TEON      	0x35    // Tearing effect line ON
+#define Cmd_MADCTL      0x36    // Memory data access control
+#define Cmd_COLMOD      0x3A   // Pixel format set
+
+#define Cmd_DisplayFunctionControl    0xB6
+#define Cmd_PWCTR1       0xC1    // Power control 1
+#define Cmd_PWCTR2       0xC3    // Power control 2
+#define Cmd_PWCTR3       0xC4    // Power control 3
+#define Cmd_PWCTR4       0xC9    // Power control 4
+#define Cmd_PWCTR7       0xA7    // Power control 7
+
+#define Cmd_FRAMERATE      0xE8
+#define Cmd_InnerReg1Enable   0xFE
+#define Cmd_InnerReg2Enable   0xEF
+
+#define Cmd_GAMMA1       0xF0    // Set gamma 1
+#define Cmd_GAMMA2       0xF1    // Set gamma 2
+#define Cmd_GAMMA3       0xF2    // Set gamma 3
+#define Cmd_GAMMA4       0xF3    // Set gamma 4
+
+#define ColorMode_RGB_16bit  0x50
+#define ColorMode_RGB_18bit  0x60
+#define ColorMode_MCU_12bit  0x03
+#define ColorMode_MCU_16bit  0x05
+#define ColorMode_MCU_18bit  0x06
+
+#define MADCTL_MY        0x80
+#define MADCTL_MX        0x40
+#define MADCTL_MV        0x20
+#define MADCTL_ML        0x10
+#define MADCTL_BGR       0x08
+#define MADCTL_MH        0x04
+
+// Transaction 'user' structure as used by SPI transactions to the display.
+// Provides enough data for the pre-transaction callback to be able to set the
+// DC pin as needed.
+typedef struct {
+	flow3r_bsp_gc9a01_t *gc9a01;
+
+	// DC pin will be set to this in pre-SPI callback.
+	int dc;
+} flow3r_bsp_gc9a01_tx_t;
+
+
+// A full-framebuffer 'blit' operation descriptor. Keeps track of number of
+// underlying DMA SPI transactions left until blit is done.
+typedef struct {
+	flow3r_bsp_gc9a01_t *gc9a01;
+	const uint8_t *fb;
+	size_t left;
+
+	flow3r_bsp_gc9a01_tx_t gc9a01_tx;
+	spi_transaction_t spi_tx;
+} flow3r_bsp_gc9a01_blit_t;
+
+/*
+ The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct.
+*/
+typedef struct {
+    uint8_t cmd;
+    uint8_t data[16];
+    uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
+} flow3r_bsp_gc9a01_init_cmd_t;
+
+static const flow3r_bsp_gc9a01_init_cmd_t flow3r_bsp_gc9a01_init_cmds[] = {
+    {0xef,{0},0},
+    {0xeb,{0x14},1},
+    {0xfe,{0},0},
+    {0xef,{0},0},
+    {0xeb,{0x14},1},
+    {0x84,{0x40},1},
+    {0x85,{0xff},1},
+    {0x86,{0xff},1},
+    {0x87,{0xff},1},
+    {0x88,{0x0a},1},
+    {0x89,{0x21},1},
+    {0x8a,{0x00},1},
+    {0x8b,{0x80},1},
+    {0x8c,{0x01},1},
+    {0x8d,{0x01},1},
+    {0x8e,{0xff},1},
+    {0x8f,{0xff},1},
+    {Cmd_DisplayFunctionControl,{0x00,0x20},2},// Scan direction S360 -> S1
+    {0x90,{0x08,0x08,0x08,0x08},4},
+    {0xbd,{0x06},1},
+    {0xbc,{0x00},1},
+    {0xff,{0x60,0x01,0x04},3},
+    {Cmd_PWCTR2,{0x13},1},
+    {Cmd_PWCTR3,{0x13},1},
+    {Cmd_PWCTR4,{0x22},1},
+    {0xbe,{0x11},1},
+    {0xe1,{0x10,0x0e},2},
+    {0xdf,{0x21,0x0c,0x02},3},
+    {Cmd_GAMMA1,{0x45,0x09,0x08,0x08,0x26,0x2a},6},
+    {Cmd_GAMMA2,{0x43,0x70,0x72,0x36,0x37,0x6f},6},
+    {Cmd_GAMMA3,{0x45,0x09,0x08,0x08,0x26,0x2a},6},
+    {Cmd_GAMMA4,{0x43,0x70,0x72,0x36,0x37,0x6f},6},
+    {0xed,{0x1b,0x0b},2},
+    {0xae,{0x77},1},
+    {0xcd,{0x63},1},
+    {0x70,{0x07,0x07,0x04,0x0e,0x0f,0x09,0x07,0x08,0x03},9},
+    {Cmd_FRAMERATE,{0x34},1},// 4 dot inversion
+    {0x62,{0x18,0x0D,0x71,0xED,0x70,0x70,0x18,0x0F,0x71,0xEF,0x70,0x70},12},
+    {0x63,{0x18,0x11,0x71,0xF1,0x70,0x70,0x18,0x13,0x71,0xF3,0x70,0x70},12},
+    {0x64,{0x28,0x29,0xF1,0x01,0xF1,0x00,0x07},7},
+    {0x66,{0x3C,0x00,0xCD,0x67,0x45,0x45,0x10,0x00,0x00,0x00},10},
+    {0x67,{0x00,0x3C,0x00,0x00,0x00,0x01,0x54,0x10,0x32,0x98},10},
+    {0x74,{0x10,0x85,0x80,0x00,0x00,0x4E,0x00},7},
+    {0x98,{0x3e,0x07},2},
+    {Cmd_TEON,{0},0},// Tearing effect line on
+    {0, {0}, 0xff},//END
+};
+
+// This function is called (in irq context!) just before a transmission starts. It will
+// set the D/C line to the value indicated in the tx's dc field.
+static IRAM_ATTR void flow3r_bsp_gc9a01_pre_transfer_callback(spi_transaction_t *t)
+{
+	flow3r_bsp_gc9a01_tx_t *tx = (flow3r_bsp_gc9a01_tx_t *)t->user;
+    gpio_set_level(tx->gc9a01->config->pin_dc, tx->dc);
+}
+
+/* Send a command to the LCD. Uses spi_device_polling_transmit, which waits
+ * until the transfer is complete.
+ *
+ * Since command transactions are usually small, they are handled in polling
+ * mode for higher speed. The overhead of interrupt transactions is more than
+ * just waiting for the transaction to complete.
+ */
+esp_err_t flow3r_bsp_gc9a01_cmd_sync(flow3r_bsp_gc9a01_t *gc9a01, uint8_t cmd)
+{
+    spi_transaction_t t;
+    memset(&t, 0, sizeof(t));
+
+    t.length = 8;
+    t.tx_buffer = &cmd;
+
+	// As we're running a synchronous transaction, we can allocate the TX object
+	// on the stack, as this frame is guaranteed to be valid until the
+	// transaction completes.
+	flow3r_bsp_gc9a01_tx_t tx = {
+		.gc9a01 = gc9a01,
+		.dc = 0,
+	};
+
+    t.user = (void*)&tx;
+    esp_err_t res = spi_device_polling_transmit(gc9a01->spi, &t);
+	return res;
+}
+
+/* Send data to the LCD. Uses spi_device_polling_transmit, which waits until the
+ * transfer is complete.
+ *
+ * Since data transactions are usually small, they are handled in polling
+ * mode for higher speed. The overhead of interrupt transactions is more than
+ * just waiting for the transaction to complete.
+ */
+esp_err_t flow3r_bsp_gc9a01_data_sync(flow3r_bsp_gc9a01_t *gc9a01, const uint8_t *data, int len) {
+    if (len == 0) {
+		return ESP_OK;
+	}
+
+	// As we're running a synchronous transaction, we can allocate the TX object
+	// on the stack, as this frame is guaranteed to be valid until the
+	// transaction completes.
+	flow3r_bsp_gc9a01_tx_t tx = {
+		.gc9a01 = gc9a01,
+		// DC == 1 for data.
+		.dc = 1,
+	};
+    /*
+    On certain MC's the max SPI DMA transfer length might be smaller than the buffer. We then have to split the transmissions.
+    */
+    int offset = 0;
+    do {
+        spi_transaction_t t;
+        memset(&t, 0, sizeof(t));       //Zero out the transaction
+
+        int tx_len = ((len - offset) < SPI_MAX_DMA_LEN) ? (len - offset) : SPI_MAX_DMA_LEN;
+		// Len is in bytes, transaction length is in bits.
+        t.length = tx_len * 8;
+        t.tx_buffer = data + offset;
+        t.user = (void*)&tx;
+
+		// Transmit!
+        esp_err_t ret = spi_device_polling_transmit(gc9a01->spi, &t);
+		if (ret != ESP_OK) {
+			return ret;
+		}
+        offset += tx_len;
+    }
+    while (offset < len);
+
+	return ESP_OK;
+}
+
+esp_err_t flow3r_bsp_gc9a01_data_byte_sync(flow3r_bsp_gc9a01_t *gc9a01, const uint8_t data) {
+	return flow3r_bsp_gc9a01_data_sync(gc9a01, &data, 1);
+}
+
+static esp_err_t flow3r_bsp_gc9a01_mem_access_mode_set(flow3r_bsp_gc9a01_t *gc9a01, uint8_t rotation, uint8_t vert_mirror, uint8_t horiz_mirror, uint8_t is_bgr) {
+	uint8_t val = 0;
+	rotation &= 7;
+
+	switch (rotation) {
+	case 0:
+		val = 0;
+		break;
+	case 1:
+		val = MADCTL_MX;
+		break;
+	case 2:
+		val = MADCTL_MY;
+		break;
+	case 3:
+		val = MADCTL_MX | MADCTL_MY;
+		break;
+	case 4:
+		val = MADCTL_MV;
+		break;
+	case 5:
+		val = MADCTL_MV | MADCTL_MX;
+		break;
+	case 6:
+		val = MADCTL_MV | MADCTL_MY;
+		break;
+	case 7:
+		val = MADCTL_MV | MADCTL_MX | MADCTL_MY;
+		break;
+	}
+
+	if (vert_mirror)
+		val = MADCTL_ML;
+	if (horiz_mirror)
+		val = MADCTL_MH;
+
+	if (is_bgr)
+		val |= MADCTL_BGR;
+
+	esp_err_t ret = flow3r_bsp_gc9a01_cmd_sync(gc9a01, Cmd_MADCTL);
+	if (ret != ESP_OK) {
+		return ret;
+	}
+	return flow3r_bsp_gc9a01_data_byte_sync(gc9a01, val);
+}
+
+static esp_err_t flow3r_bsp_gc9a01_color_mode_set(flow3r_bsp_gc9a01_t *gc9a01, uint8_t color_mode) {
+	esp_err_t ret = flow3r_bsp_gc9a01_cmd_sync(gc9a01, Cmd_COLMOD);
+	if (ret != ESP_OK) {
+		return ret;
+	}
+	return flow3r_bsp_gc9a01_data_byte_sync(gc9a01, color_mode & 0x77);
+}
+
+static esp_err_t flow3r_bsp_gc9a01_inversion_mode_set(flow3r_bsp_gc9a01_t *gc9a01, uint8_t mode) {
+	if (mode)
+		return flow3r_bsp_gc9a01_cmd_sync(gc9a01, Cmd_INVON);
+	else
+		return flow3r_bsp_gc9a01_cmd_sync(gc9a01, Cmd_INVOFF);
+}
+
+static esp_err_t flow3r_bsp_gc9a01_sleep_mode_set(flow3r_bsp_gc9a01_t *gc9a01, uint8_t mode) {
+	if (mode)
+		return flow3r_bsp_gc9a01_cmd_sync(gc9a01, Cmd_SLPIN);
+	else
+		return flow3r_bsp_gc9a01_cmd_sync(gc9a01, Cmd_SLPOUT);
+	vTaskDelay(500 / portTICK_PERIOD_MS);
+}
+
+static esp_err_t flow3r_bsp_gc9a01_power_set(flow3r_bsp_gc9a01_t *gc9a01, uint8_t mode) {
+	if (mode)
+		return flow3r_bsp_gc9a01_cmd_sync(gc9a01, Cmd_DISPON);
+	else
+		return flow3r_bsp_gc9a01_cmd_sync(gc9a01, Cmd_DISPOFF);
+}
+
+static esp_err_t flow3r_bsp_gc9a01_column_set(flow3r_bsp_gc9a01_t *gc9a01, uint16_t start, uint16_t end) {
+	esp_err_t ret;
+	ret = flow3r_bsp_gc9a01_cmd_sync(gc9a01, Cmd_CASET);
+	if (ret != ESP_OK) {
+		return ret;
+	}
+	ret = flow3r_bsp_gc9a01_data_byte_sync(gc9a01, start >> 8);
+	if (ret != ESP_OK) {
+		return ret;
+	}
+	ret = flow3r_bsp_gc9a01_data_byte_sync(gc9a01, start & 0xFF);
+	if (ret != ESP_OK) {
+		return ret;
+	}
+	ret = flow3r_bsp_gc9a01_data_byte_sync(gc9a01, end >> 8);
+	if (ret != ESP_OK) {
+		return ret;
+	}
+	return flow3r_bsp_gc9a01_data_byte_sync(gc9a01, end & 0xFF);
+}
+
+static esp_err_t flow3r_bsp_gc9a01_row_set(flow3r_bsp_gc9a01_t *gc9a01, uint16_t start, uint16_t end) {
+	esp_err_t ret;
+	ret = flow3r_bsp_gc9a01_cmd_sync(gc9a01, Cmd_RASET);
+	if (ret != ESP_OK) {
+		return ret;
+	}
+	ret = flow3r_bsp_gc9a01_data_byte_sync(gc9a01, start >> 8);
+	if (ret != ESP_OK) {
+		return ret;
+	}
+	ret = flow3r_bsp_gc9a01_data_byte_sync(gc9a01, start & 0xFF);
+	if (ret != ESP_OK) {
+		return ret;
+	}
+	ret = flow3r_bsp_gc9a01_data_byte_sync(gc9a01, end >> 8);
+	if (ret != ESP_OK) {
+		return ret;
+	}
+	return flow3r_bsp_gc9a01_data_byte_sync(gc9a01, end & 0xFF);
+}
+
+esp_err_t flow3r_bsp_gc9a01_backlight_set(flow3r_bsp_gc9a01_t *gc9a01, uint8_t value) {
+	if (gc9a01->config->backlight_used == 0) {
+		return ESP_OK;
+	}
+	if (value > 100) {
+		value = 100;
+	}
+
+	uint16_t max_duty = (1 << (int)gc9a01->bl_timer_config.duty_resolution) - 1;
+	uint16_t duty;
+	if (value >= 100) {
+		duty = max_duty;
+	} else {
+		duty = value * (max_duty / (float)100);
+	}
+
+	gc9a01->bl_channel_config.duty = duty;
+	return ledc_channel_config(&gc9a01->bl_channel_config);
+}
+
+esp_err_t flow3r_bsp_gc9a01_init(flow3r_bsp_gc9a01_t *gc9a01, flow3r_bsp_gc9a01_config_t *config) {
+	memset(gc9a01, 0, sizeof(flow3r_bsp_gc9a01_t));
+	gc9a01->config = config;
+
+	// Configure DC pin.
+	gpio_config_t gpiocfg = {
+		.pin_bit_mask = ((uint64_t)1UL << gc9a01->config->pin_dc),
+		.mode = GPIO_MODE_OUTPUT,
+		.pull_up_en = GPIO_PULLUP_DISABLE,
+		.pull_down_en = GPIO_PULLDOWN_DISABLE,
+		.intr_type = GPIO_INTR_DISABLE,
+	};
+	esp_err_t res = gpio_config(&gpiocfg);
+	if (res != ESP_OK) {
+		return res;
+	}
+	gpio_set_level(gc9a01->config->pin_dc, 0);
+
+	// Configure Reset pin if used.
+	if (gc9a01->config->reset_used) {
+		gpiocfg.pin_bit_mask |= ((uint64_t)1UL << gc9a01->config->pin_rst);
+		res = gpio_config(&gpiocfg);
+		if (res != ESP_OK) {
+			return res;
+		}
+		gpio_set_level(gc9a01->config->pin_rst, 1);
+	}
+
+	// Configure SPI bus.
+	//
+	// TODO(q3k): don't do this here, do this higher up in the BSP. Even if
+	// nothing else sits on this bus.
+    spi_bus_config_t buscfg = {
+        .mosi_io_num = gc9a01->config->pin_mosi,
+        .miso_io_num = GPIO_NUM_NC,
+        .sclk_io_num = gc9a01->config->pin_sck,
+        .quadwp_io_num = GPIO_NUM_NC,
+        .quadhd_io_num = GPIO_NUM_NC,
+        .max_transfer_sz = 250*250*2,
+    };
+
+	// Configure SPI device on bus.
+    spi_device_interface_config_t devcfg = {
+        .clock_speed_hz = 80*1000*1000,
+        .mode = 0,
+        .spics_io_num = gc9a01->config->pin_cs,
+        .queue_size = 7,
+        .pre_cb = flow3r_bsp_gc9a01_pre_transfer_callback,
+    };
+    
+	esp_err_t ret = spi_bus_initialize(gc9a01->config->host, &buscfg, SPI_DMA_CH_AUTO);
+	if (ret != ESP_OK) {
+		return ret;
+	}
+
+    ret = spi_bus_add_device(gc9a01->config->host, &devcfg, &gc9a01->spi);
+	if (ret != ESP_OK) {
+		goto cleanup_spi_bus;
+	}
+
+	// Configure backlight timer/channel if used.
+	if (gc9a01->config->backlight_used) {
+		gc9a01->bl_timer_config.speed_mode = LEDC_LOW_SPEED_MODE;
+		gc9a01->bl_timer_config.duty_resolution = LEDC_TIMER_8_BIT;
+		gc9a01->bl_timer_config.timer_num = LEDC_TIMER_0;
+		gc9a01->bl_timer_config.freq_hz=1000;
+		gc9a01->bl_timer_config.clk_cfg=LEDC_AUTO_CLK;
+		ret = ledc_timer_config(&gc9a01->bl_timer_config);
+		if (ret != ESP_OK) {
+			goto cleanup_spi_device;
+		}
+
+		gc9a01->bl_channel_config.gpio_num = gc9a01->config->pin_backlight;
+		gc9a01->bl_channel_config.speed_mode = LEDC_LOW_SPEED_MODE;
+		gc9a01->bl_channel_config.channel = LEDC_CHANNEL_0;
+		gc9a01->bl_channel_config.intr_type = LEDC_INTR_DISABLE;
+		gc9a01->bl_channel_config.timer_sel = LEDC_TIMER_0;
+		gc9a01->bl_channel_config.duty = 0;
+		gc9a01->bl_channel_config.hpoint = 0;
+		ret = ledc_channel_config(&gc9a01->bl_channel_config);
+		if (ret != ESP_OK) {
+			goto cleanup_spi_device;
+		}
+	}
+
+	// Issue reset if used.
+	if (gc9a01->config->reset_used) {
+		gpio_set_level(gc9a01->config->pin_rst, 0);
+		vTaskDelay(10 / portTICK_PERIOD_MS);
+		gpio_set_level(gc9a01->config->pin_rst, 1);
+		vTaskDelay(150 / portTICK_PERIOD_MS);
+	}
+
+	// Send initialization commands.
+	int ix = 0;
+	while (flow3r_bsp_gc9a01_init_cmds[ix].databytes != 0xff) {
+		const flow3r_bsp_gc9a01_init_cmd_t *cmd = &flow3r_bsp_gc9a01_init_cmds[ix];
+        flow3r_bsp_gc9a01_cmd_sync(gc9a01, cmd->cmd);
+        flow3r_bsp_gc9a01_data_sync(gc9a01, cmd->data, cmd->databytes & 0x1F);
+        if (cmd->databytes & 0x80) {
+			vTaskDelay(100 / portTICK_PERIOD_MS);
+        }
+        ix++;
+	}
+
+	ret = flow3r_bsp_gc9a01_mem_access_mode_set(gc9a01, 0, 0, 0, 1);
+	if (ret != ESP_OK) {
+		goto cleanup_spi_device;
+	}
+	ret = flow3r_bsp_gc9a01_color_mode_set(gc9a01, ColorMode_MCU_16bit);
+	if (ret != ESP_OK) {
+		goto cleanup_spi_device;
+	}
+	ret = flow3r_bsp_gc9a01_inversion_mode_set(gc9a01, 1);
+	if (ret != ESP_OK) {
+		goto cleanup_spi_device;
+	}
+	ret = flow3r_bsp_gc9a01_sleep_mode_set(gc9a01, 0);
+	if (ret != ESP_OK) {
+		goto cleanup_spi_device;
+	}
+	vTaskDelay(120 / portTICK_PERIOD_MS);
+
+	ret = flow3r_bsp_gc9a01_power_set(gc9a01, 1);
+	if (ret != ESP_OK) {
+		goto cleanup_spi_device;
+	}
+	vTaskDelay(20 / portTICK_PERIOD_MS);
+
+	// We always write the entire framebuffer at once.
+	ret = flow3r_bsp_gc9a01_column_set(gc9a01, 0, 239);
+	if (ret != ESP_OK) {
+		goto cleanup_spi_device;
+	}
+	ret = flow3r_bsp_gc9a01_row_set(gc9a01, 0, 239);
+	if (ret != ESP_OK) {
+		goto cleanup_spi_device;
+	}
+
+	return ret;
+
+cleanup_spi_device:
+	spi_bus_remove_device(gc9a01->spi);
+cleanup_spi_bus:
+	spi_bus_free(gc9a01->config->host);
+	return ret;
+}
+
+static esp_err_t flow3r_bsp_gc9a01_blit_next(flow3r_bsp_gc9a01_blit_t *blit) {
+	size_t size = blit->left;
+	if (size > SPI_MAX_DMA_LEN) {
+		size = SPI_MAX_DMA_LEN;
+	}
+
+	blit->gc9a01_tx.gc9a01 = blit->gc9a01;
+	blit->gc9a01_tx.dc = 1;
+
+	// Memzero spi_tx as it gets written by the SPI driver after each
+	// transaction.
+	memset(&blit->spi_tx, 0, sizeof(spi_transaction_t));
+	blit->spi_tx.length = size * 8;
+	blit->spi_tx.tx_buffer = blit->fb;
+	blit->spi_tx.user = &blit->gc9a01_tx;
+
+	blit->left -= size;
+	blit->fb += size;
+
+	return spi_device_queue_trans(blit->gc9a01->spi, &blit->spi_tx, portMAX_DELAY);
+}
+
+static esp_err_t flow3r_bsp_gc9a01_blit_start(flow3r_bsp_gc9a01_t *gc9a01, flow3r_bsp_gc9a01_blit_t *blit, const uint16_t *fb) {
+	memset(blit, 0, sizeof(flow3r_bsp_gc9a01_blit_t));
+
+	blit->gc9a01 = gc9a01;
+	blit->fb = (const uint8_t *)fb;
+	blit->left = 2 * 240 * 240;
+
+	return flow3r_bsp_gc9a01_cmd_sync(gc9a01, Cmd_RAMWR);
+}
+
+static uint8_t flow3r_bsp_gc9a01_blit_done(flow3r_bsp_gc9a01_blit_t *blit) {
+	return blit->left == 0;
+}
+
+static esp_err_t flow3r_bsp_gc9a01_blit_wait_done(flow3r_bsp_gc9a01_blit_t *blit, TickType_t ticks_to_wait) {
+	spi_transaction_t *tx_done;
+	esp_err_t ret = spi_device_get_trans_result(blit->gc9a01->spi, &tx_done, ticks_to_wait);
+	// TODO(q3k): validate that tx_done corresponds to the spi_transaction_tx in
+	// bilt. If not, this means we got some sequencing failure to deal with.
+	return ret;
+}
+
+esp_err_t flow3r_bsp_gc9a01_blit_full(flow3r_bsp_gc9a01_t *gc9a01, const uint16_t *fb) {
+	flow3r_bsp_gc9a01_blit_t blit;
+	esp_err_t res = flow3r_bsp_gc9a01_blit_start(gc9a01, &blit, fb);
+	if (res != ESP_OK) {
+		return res;
+	}
+
+	while (!flow3r_bsp_gc9a01_blit_done(&blit)) {
+		res = flow3r_bsp_gc9a01_blit_next(&blit);
+		if (res != ESP_OK) {
+			return res;
+		}
+		res = flow3r_bsp_gc9a01_blit_wait_done(&blit, portMAX_DELAY);
+		if (res != ESP_OK) {
+			return res;
+		}
+	}
+	return ESP_OK;
+}
diff --git a/components/flow3r_bsp/flow3r_bsp_gc9a01.h b/components/flow3r_bsp/flow3r_bsp_gc9a01.h
new file mode 100644
index 0000000000000000000000000000000000000000..38225683717128001719bb8096cbea9caae5259f
--- /dev/null
+++ b/components/flow3r_bsp/flow3r_bsp_gc9a01.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <stdint.h>
+
+#include "driver/spi_master.h"
+#include "driver/ledc.h"
+
+// Configuration structure for display.
+//
+// The display expects to be the only device on an SPI bus.
+typedef struct {
+	// Boolean: whether the display as a reset pin connected.
+	uint8_t reset_used;
+	// Boolean: whether the display as backlight control connected to the ESP
+	// LED Control peripheral.
+	uint8_t backlight_used;
+
+	// Reset pin, if reset_used.
+	uint8_t pin_rst;
+	// SPI SCK pin.
+	uint8_t pin_sck;
+	// SPI MOSI pin.
+	uint8_t pin_mosi;
+	// SPI CS pin.
+	uint8_t pin_cs;
+	// Data/Command pin.
+	uint8_t pin_dc;
+	// Backlight control pin, if backlight_used.
+	uint8_t pin_backlight;
+
+	// Nubmer of SPI host device (ie. bus) that the display will use.
+	spi_host_device_t host;
+} flow3r_bsp_gc9a01_config_t;
+
+typedef struct {
+	const flow3r_bsp_gc9a01_config_t *config;
+
+	// Allocatged SPI device handle on configured bus.
+	spi_device_handle_t spi;
+
+	// Only if using backlight.
+	ledc_channel_config_t bl_channel_config;
+	ledc_timer_config_t bl_timer_config;
+
+} flow3r_bsp_gc9a01_t;
+
+// Initialize display structure based on config, and then actually initialize
+// display hardware and ESP peripherals.
+//
+// The given gc9a01 structure does not need to be zeroed out.
+//
+// The given config structure must live as long as the display object lives. As
+// currently displays cannot be de-initialized, this means that the
+// configuration structure must live forever.
+//
+// An error will be returned if initialization failed. Initialization can be
+// re-tried.
+esp_err_t flow3r_bsp_gc9a01_init(flow3r_bsp_gc9a01_t *gc9a01, flow3r_bsp_gc9a01_config_t *config);
+
+// Send a full-sized framebuffer to the display using interrupts/DMA, blocking
+// FreeRTOS task until done.
+//
+// This must not be called if another blit is being performed. The user code
+// should sequence access and make sure not more than one blit is performed
+// simultaneously.
+esp_err_t flow3r_bsp_gc9a01_blit_full(flow3r_bsp_gc9a01_t *gc9a01, const uint16_t *fb);
+
+// Set backlight for display, using integer percent value (0-100, clamped).
+esp_err_t flow3r_bsp_gc9a01_backlight_set(flow3r_bsp_gc9a01_t *gc9a01, uint8_t value);
\ No newline at end of file
diff --git a/components/gc9a01/CMakeLists.txt b/components/gc9a01/CMakeLists.txt
deleted file mode 100644
index 739a7727e21526a3b2b0828c0d7337cd601aa58c..0000000000000000000000000000000000000000
--- a/components/gc9a01/CMakeLists.txt
+++ /dev/null
@@ -1 +0,0 @@
-idf_component_register(SRCS "gc9a01.c" REQUIRES driver badge23_hwconfig INCLUDE_DIRS ".")
diff --git a/components/gc9a01/LICENSE b/components/gc9a01/LICENSE
deleted file mode 100644
index 9fd35d36ac2d0b39c3051d6874c9aae48f08cc13..0000000000000000000000000000000000000000
--- a/components/gc9a01/LICENSE
+++ /dev/null
@@ -1,29 +0,0 @@
-BSD 3-Clause License
-
-Copyright (c) 2021, liyanboy74
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this
-   list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its
-   contributors may be used to endorse or promote products derived from
-   this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/components/gc9a01/gc9a01.c b/components/gc9a01/gc9a01.c
deleted file mode 100644
index 0870dc5eb4822f4ac0c045aa10dd1f8b7210902c..0000000000000000000000000000000000000000
--- a/components/gc9a01/gc9a01.c
+++ /dev/null
@@ -1,648 +0,0 @@
-//--------------------------------------------------------------------------------------------------------
-// Nadyrshin Ruslan - [YouTube-channel: https://www.youtube.com/channel/UChButpZaL5kUUl_zTyIDFkQ]
-// Liyanboy74
-//--------------------------------------------------------------------------------------------------------
-#include <stdio.h>
-#include <string.h>
-
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-
-#include "driver/gpio.h"
-#include "driver/spi_master.h"
-#include "driver/ledc.h"
-
-#include "sdkconfig.h"
-#include "gc9a01.h"
-
-#if (GC9A01_RESET_USED)
-#define RESET_HIGH()           gpio_set_level(GC9A01_PIN_NUM_RST,1)
-#define RESET_LOW()            gpio_set_level(GC9A01_PIN_NUM_RST,0)
-#endif
-
-#if(GC9A01_CONTROL_BACK_LIGHT_USED)
-#define BLK_HIGH()             gpio_set_level(GC9A01_PIN_NUM_BCKL,1)
-#define BLK_LOW()              gpio_set_level(GC9A01_PIN_NUM_BCKL,0)
-#endif
-
-#define Cmd_SLPIN       0x10
-#define Cmd_SLPOUT      0x11
-#define Cmd_INVOFF      0x20
-#define Cmd_INVON       0x21
-#define Cmd_DISPOFF     0x28
-#define Cmd_DISPON      0x29
-#define Cmd_CASET       0x2A
-#define Cmd_RASET       0x2B
-#define Cmd_RAMWR       0x2C
-#define Cmd_TEON      	0x35    // Tearing effect line ON
-#define Cmd_MADCTL      0x36    // Memory data access control
-#define Cmd_COLMOD      0x3A   // Pixel format set
-
-#define Cmd_DisplayFunctionControl    0xB6
-#define Cmd_PWCTR1       0xC1    // Power control 1
-#define Cmd_PWCTR2       0xC3    // Power control 2
-#define Cmd_PWCTR3       0xC4    // Power control 3
-#define Cmd_PWCTR4       0xC9    // Power control 4
-#define Cmd_PWCTR7       0xA7    // Power control 7
-
-#define Cmd_FRAMERATE      0xE8
-#define Cmd_InnerReg1Enable   0xFE
-#define Cmd_InnerReg2Enable   0xEF
-
-#define Cmd_GAMMA1       0xF0    // Set gamma 1
-#define Cmd_GAMMA2       0xF1    // Set gamma 2
-#define Cmd_GAMMA3       0xF2    // Set gamma 3
-#define Cmd_GAMMA4       0xF3    // Set gamma 4
-
-#define ColorMode_RGB_16bit  0x50
-#define ColorMode_RGB_18bit  0x60
-#define ColorMode_MCU_12bit  0x03
-#define ColorMode_MCU_16bit  0x05
-#define ColorMode_MCU_18bit  0x06
-
-#define MADCTL_MY        0x80
-#define MADCTL_MX        0x40
-#define MADCTL_MV        0x20
-#define MADCTL_ML        0x10
-#define MADCTL_BGR       0x08
-#define MADCTL_MH        0x04
-
-uint8_t GC9A01_X_Start = 0, GC9A01_Y_Start = 0;
-
-#if (GC9A01_BUFFER_MODE)
-DMA_ATTR uint16_t ScreenBuff[GC9A01_Height * GC9A01_Width];
-#endif
-
-//SPI Config
-spi_device_handle_t spi;
-spi_host_device_t LCD_HOST=GC9A01_SPI_HOST;
-
-//LEDC Config
-#if(GC9A01_CONTROL_BACK_LIGHT_USED)
-#if(GC9A01_CONTROL_BACK_LIGHT_MODE)
-ledc_channel_config_t  ledc_cConfig;
-ledc_timer_config_t    ledc_tConfig;
-void LEDC_PWM_Duty_Set(uint8_t DutyP);
-#endif
-#endif
-
-/*
- The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct.
-*/
-typedef struct {
-    uint8_t cmd;
-    uint8_t data[16];
-    uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
-} lcd_init_cmd_t;
-
-static const lcd_init_cmd_t lcd_init_cmds[]={
-    {0xef,{0},0},
-    {0xeb,{0x14},1},
-    {0xfe,{0},0},
-    {0xef,{0},0},
-    {0xeb,{0x14},1},
-    {0x84,{0x40},1},
-    {0x85,{0xff},1},
-    {0x86,{0xff},1},
-    {0x87,{0xff},1},
-    {0x88,{0x0a},1},
-    {0x89,{0x21},1},
-    {0x8a,{0x00},1},
-    {0x8b,{0x80},1},
-    {0x8c,{0x01},1},
-    {0x8d,{0x01},1},
-    {0x8e,{0xff},1},
-    {0x8f,{0xff},1},
-    {Cmd_DisplayFunctionControl,{0x00,0x20},2},// Scan direction S360 -> S1
-    //{Cmd_MADCTL,{0x08},1},//MemAccessModeSet(0, 0, 0, 1);
-    //{Cmd_COLMOD,{ColorMode_MCU_16bit&0x77},1},
-    {0x90,{0x08,0x08,0x08,0x08},4},
-    {0xbd,{0x06},1},
-    {0xbc,{0x00},1},
-    {0xff,{0x60,0x01,0x04},3},
-    {Cmd_PWCTR2,{0x13},1},
-    {Cmd_PWCTR3,{0x13},1},
-    {Cmd_PWCTR4,{0x22},1},
-    {0xbe,{0x11},1},
-    {0xe1,{0x10,0x0e},2},
-    {0xdf,{0x21,0x0c,0x02},3},
-    {Cmd_GAMMA1,{0x45,0x09,0x08,0x08,0x26,0x2a},6},
-    {Cmd_GAMMA2,{0x43,0x70,0x72,0x36,0x37,0x6f},6},
-    {Cmd_GAMMA3,{0x45,0x09,0x08,0x08,0x26,0x2a},6},
-    {Cmd_GAMMA4,{0x43,0x70,0x72,0x36,0x37,0x6f},6},
-    {0xed,{0x1b,0x0b},2},
-    {0xae,{0x77},1},
-    {0xcd,{0x63},1},
-    {0x70,{0x07,0x07,0x04,0x0e,0x0f,0x09,0x07,0x08,0x03},9},
-    {Cmd_FRAMERATE,{0x34},1},// 4 dot inversion
-    {0x62,{0x18,0x0D,0x71,0xED,0x70,0x70,0x18,0x0F,0x71,0xEF,0x70,0x70},12},
-    {0x63,{0x18,0x11,0x71,0xF1,0x70,0x70,0x18,0x13,0x71,0xF3,0x70,0x70},12},
-    {0x64,{0x28,0x29,0xF1,0x01,0xF1,0x00,0x07},7},
-    {0x66,{0x3C,0x00,0xCD,0x67,0x45,0x45,0x10,0x00,0x00,0x00},10},
-    {0x67,{0x00,0x3C,0x00,0x00,0x00,0x01,0x54,0x10,0x32,0x98},10},
-    {0x74,{0x10,0x85,0x80,0x00,0x00,0x4E,0x00},7},
-    {0x98,{0x3e,0x07},2},
-    {Cmd_TEON,{0},0},// Tearing effect line on
-    {0, {0}, 0xff},//END
-};
-
-//This function is called (in irq context!) just before a transmission starts. It will
-//set the D/C line to the value indicated in the user field.
-static IRAM_ATTR void lcd_spi_pre_transfer_callback(spi_transaction_t *t)
-{
-    int dc=(int)t->user;
-    gpio_set_level(GC9A01_PIN_NUM_DC, dc);
-}
-
-/* Send a command to the LCD. Uses spi_device_polling_transmit, which waits
- * until the transfer is complete.
- *
- * Since command transactions are usually small, they are handled in polling
- * mode for higher speed. The overhead of interrupt transactions is more than
- * just waiting for the transaction to complete.
- */
-void lcd_cmd(uint8_t cmd)
-{
-    esp_err_t ret;
-    spi_transaction_t t;
-    memset(&t, 0, sizeof(t));       //Zero out the transaction
-    t.length=8;                     //Command is 8 bits
-    t.tx_buffer=&cmd;               //The data is the cmd itself
-    t.user=(void*)0;                //D/C needs to be set to 0
-    ret=spi_device_polling_transmit(spi, &t);  //Transmit!
-    assert(ret==ESP_OK);            //Should have had no issues.
-}
-
-/* Send data to the LCD. Uses spi_device_polling_transmit, which waits until the
- * transfer is complete.
- *
- * Since data transactions are usually small, they are handled in polling
- * mode for higher speed. The overhead of interrupt transactions is more than
- * just waiting for the transaction to complete.
- */
-void lcd_data(const uint8_t *data, int len)
-{
-    esp_err_t ret;
-    if (len==0) return;             //no need to send anything
-
-    /*
-    On certain MC's the max SPI DMA transfer length might be smaller than the buffer. We then have to split the transmissions.
-    */
-    int offset = 0;
-    do {
-        spi_transaction_t t;
-        memset(&t, 0, sizeof(t));       //Zero out the transaction
-
-        int tx_len = ((len - offset) < SPI_MAX_DMA_LEN) ? (len - offset) : SPI_MAX_DMA_LEN;
-        t.length=tx_len * 8;                       //Len is in bytes, transaction length is in bits.
-        t.tx_buffer= data + offset;                //Data
-        t.user=(void*)1;                           //D/C needs to be set to 1
-        ret=spi_device_polling_transmit(spi, &t);  //Transmit!
-        assert(ret==ESP_OK);                       //Should have had no issues.
-        offset += tx_len;                          // Add the transmission length to the offset
-    }
-    while (offset < len);
-}
-
-void lcd_send_byte(uint8_t Data)
-{
-	lcd_data(&Data,1);
-}
-
-void delay_ms (uint32_t Delay_ms)
-{
-	vTaskDelay(Delay_ms/portTICK_PERIOD_MS);
-}
-
-uint16_t GC9A01_GetWidth() {
-	return GC9A01_Width;
-}
-
-uint16_t GC9A01_GetHeight() {
-	return GC9A01_Height;
-}
-
-void GC9A01_HardReset(void) {
-	#if (GC9A01_RESET_USED)
-	RESET_LOW();
-	delay_ms(10);
-	RESET_HIGH();
-	delay_ms(150);
-	#endif
-}
-
-void GC9A01_SleepMode(uint8_t Mode) {
-	if (Mode)
-		lcd_cmd(Cmd_SLPIN);
-	else
-		lcd_cmd(Cmd_SLPOUT);
-
-	delay_ms(500);
-}
-
-void GC9A01_InversionMode(uint8_t Mode) {
-	if (Mode)
-		lcd_cmd(Cmd_INVON);
-	else
-		lcd_cmd(Cmd_INVOFF);
-}
-
-void GC9A01_DisplayPower(uint8_t On) {
-	if (On)
-		lcd_cmd(Cmd_DISPON);
-	else
-		lcd_cmd(Cmd_DISPOFF);
-}
-
-static void ColumnSet(uint16_t ColumnStart, uint16_t ColumnEnd) {
-	if (ColumnStart > ColumnEnd)
-		return;
-	if (ColumnEnd > GC9A01_Width)
-		return;
-
-	ColumnStart += GC9A01_X_Start;
-	ColumnEnd += GC9A01_X_Start;
-
-	lcd_cmd(Cmd_CASET);
-	lcd_send_byte(ColumnStart >> 8);
-	lcd_send_byte(ColumnStart & 0xFF);
-	lcd_send_byte(ColumnEnd >> 8);
-	lcd_send_byte(ColumnEnd & 0xFF);
-}
-
-static void RowSet(uint16_t RowStart, uint16_t RowEnd) {
-	if (RowStart > RowEnd)
-		return;
-	if (RowEnd > GC9A01_Height)
-		return;
-
-	RowStart += GC9A01_Y_Start;
-	RowEnd += GC9A01_Y_Start;
-
-	lcd_cmd(Cmd_RASET);
-	lcd_send_byte(RowStart >> 8);
-	lcd_send_byte(RowStart & 0xFF);
-	lcd_send_byte(RowEnd >> 8);
-	lcd_send_byte(RowEnd & 0xFF);
-}
-
-void GC9A01_SetWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) {
-	ColumnSet(x0, x1);
-	RowSet(y0, y1);
-
-	lcd_cmd(Cmd_RAMWR);
-}
-
-static void ColorModeSet(uint8_t ColorMode) {
-	lcd_cmd(Cmd_COLMOD);
-	lcd_send_byte(ColorMode & 0x77);
-}
-
-static void MemAccessModeSet(uint8_t Rotation, uint8_t VertMirror,
-		uint8_t HorizMirror, uint8_t IsBGR) {
-	uint8_t Ret=0;
-	Rotation &= 7;
-
-	lcd_cmd(Cmd_MADCTL);
-
-	switch (Rotation) {
-	case 0:
-		Ret = 0;
-		break;
-	case 1:
-		Ret = MADCTL_MX;
-		break;
-	case 2:
-		Ret = MADCTL_MY;
-		break;
-	case 3:
-		Ret = MADCTL_MX | MADCTL_MY;
-		break;
-	case 4:
-		Ret = MADCTL_MV;
-		break;
-	case 5:
-		Ret = MADCTL_MV | MADCTL_MX;
-		break;
-	case 6:
-		Ret = MADCTL_MV | MADCTL_MY;
-		break;
-	case 7:
-		Ret = MADCTL_MV | MADCTL_MX | MADCTL_MY;
-		break;
-	}
-
-	if (VertMirror)
-		Ret = MADCTL_ML;
-	if (HorizMirror)
-		Ret = MADCTL_MH;
-
-	if (IsBGR)
-		Ret |= MADCTL_BGR;
-
-	lcd_send_byte(Ret);
-}
-
-void GC9A01_SetBL(uint8_t Value)
-{
-	if (Value > 100) Value = 100;
-	#if(GC9A01_CONTROL_BACK_LIGHT_USED)
-		#if(GC9A01_CONTROL_BACK_LIGHT_MODE)
-		LEDC_PWM_Duty_Set(Value);
-		#else
-		if (Value) BLK_HIGH();
-		else BLK_LOW();
-		#endif
-	#endif
-}
-
-//Direct Mode
-#if (!GC9A01_BUFFER_MODE)
-
-	void GC9A01_RamWrite(uint16_t *pBuff, uint16_t Len)
-	{
-	while (Len--)
-	{
-		lcd_send_byte(*pBuff >> 8);
-		lcd_send_byte(*pBuff & 0xFF);
-	}
-	}
-
-	void GC9A01_DrawPixel(int16_t x, int16_t y, uint16_t color)
-	{
-	if ((x < 0) ||(x >= GC9A01_Width) || (y < 0) || (y >= GC9A01_Height))
-		return;
-
-	GC9A01_SetWindow(x, y, x, y);
-	GC9A01_RamWrite(&color, 1);
-	}
-
-	void GC9A01_FillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
-	{
-	if ((x >= GC9A01_Width) || (y >= GC9A01_Height))
-		return;
-
-	if ((x + w) > GC9A01_Width)
-		w = GC9A01_Width - x;
-
-	if ((y + h) > GC9A01_Height)
-		h = GC9A01_Height - y;
-
-	GC9A01_SetWindow(x, y, x + w - 1, y + h - 1);
-
-	for (uint32_t i = 0; i < (h * w); i++)
-		GC9A01_RamWrite(&color, 1);
-	}
-
-//Buffer mode
-#else
-
-	static void SwapBytes(uint16_t *color) {
-		uint8_t temp = *color >> 8;
-		*color = (*color << 8) | temp;
-	}
-
-	uint16_t GC9A01_GetPixel(int16_t x, int16_t y) {
-		if ((x < 0) || (x >= GC9A01_Width) || (y < 0) || (y >= GC9A01_Height))
-			return 0;
-
-		uint16_t color = ScreenBuff[y * GC9A01_Width + x];
-		SwapBytes(&color);
-		return color;
-    }
-
-    void GC9A01_DrawPixel(int16_t x, int16_t y, uint16_t color) {
-            if ((x < 0) || (x >= GC9A01_Width) || (y < 0) || (y >= GC9A01_Height))
-                return;
-            SwapBytes(&color);
-            ScreenBuff[y * GC9A01_Width + x] = color;
-	}
-
-	void GC9A01_FillRect(int16_t x, int16_t y, int16_t w, int16_t h,
-			uint16_t color) {
-		if ((w <= 0) || (h <= 0) || (x >= GC9A01_Width) || (y >= GC9A01_Height))
-			return;
-
-		if (x < 0) {
-			w += x;
-			x = 0;
-		}
-		if (y < 0) {
-			h += y;
-			y = 0;
-		}
-
-		if ((w <= 0) || (h <= 0))
-			return;
-
-		if ((x + w) > GC9A01_Width)
-			w = GC9A01_Width - x;
-		if ((y + h) > GC9A01_Height)
-			h = GC9A01_Height - y;
-
-		SwapBytes(&color);
-
-		for (uint16_t row = 0; row < h; row++) {
-			for (uint16_t col = 0; col < w; col++)
-				//GC9A01_DrawPixel(col, row, color);
-				ScreenBuff[(y + row) * GC9A01_Width + x + col] = color;
-		}
-	}
-
-	void GC9A01_Update()
-	{
-		int len = GC9A01_Width * GC9A01_Height;
-		GC9A01_SetWindow(0, 0, GC9A01_Width - 1, GC9A01_Height - 1);
-		lcd_data((uint8_t*) &ScreenBuff[0], len*2);
-	}
-
-	void GC9A01_Clear(void)
-	{
-		GC9A01_FillRect(0, 0, GC9A01_Width, GC9A01_Height, 0x0000);
-	}
-
-#endif
-
-static void gc9a01_GPIO_init(void)
-{
-	gpio_config_t gpiocfg={
-		.pin_bit_mask= ((uint64_t)1UL<<GC9A01_PIN_NUM_DC),
-		.mode=GPIO_MODE_OUTPUT,
-		.pull_up_en=GPIO_PULLUP_DISABLE,
-		.pull_down_en=GPIO_PULLDOWN_DISABLE,
-		.intr_type=GPIO_INTR_DISABLE,
-	};
-
-	gpio_config(&gpiocfg);
-	gpio_set_level(GC9A01_PIN_NUM_DC,0);
-
-	#if(GC9A01_RESET_USED)
-	gpiocfg.pin_bit_mask|=((uint64_t)1UL<<GC9A01_PIN_NUM_RST);
-	gpio_config(&gpiocfg);
-	gpio_set_level(GC9A01_PIN_NUM_RST,1);
-	#endif
-
-	#if(GC9A01_CONTROL_BACK_LIGHT_USED)
-	#if(!GC9A01_CONTROL_BACK_LIGHT_MODE)
-	gpiocfg.pin_bit_mask|=((uint64_t)1UL<<GC9A01_PIN_NUM_BCKL);
-	gpio_config(&gpiocfg);
-	gpio_set_level(GC9A01_PIN_NUM_BCKL,0);
-	#endif
-	#endif
-
-}
-
-void gc9a01_SPI_init(void)
-{
-	esp_err_t ret;
-    spi_bus_config_t buscfg={
-        .mosi_io_num=GC9A01_PIN_NUM_MOSI,
-        .miso_io_num=GPIO_NUM_NC,
-        .sclk_io_num=GC9A01_PIN_NUM_SCK,
-        .quadwp_io_num=-1,
-        .quadhd_io_num=-1,
-        .max_transfer_sz=250*250*2,
-    };
-    spi_device_interface_config_t devcfg={
-        .clock_speed_hz=GC9A01_SPI_SCK_FREQ_M*1000*1000,
-        .mode=0,
-        .spics_io_num=GC9A01_PIN_NUM_CS,
-        .queue_size=7,
-        .pre_cb=lcd_spi_pre_transfer_callback,
-    };
-
-    ret=spi_bus_initialize(LCD_HOST,&buscfg,SPI_DMA_CH_AUTO);
-    ESP_ERROR_CHECK(ret);
-
-    ret=spi_bus_add_device(LCD_HOST,&devcfg,&spi);
-    ESP_ERROR_CHECK(ret);
-}
-
-#if(GC9A01_CONTROL_BACK_LIGHT_USED)
-#if(GC9A01_CONTROL_BACK_LIGHT_MODE)
-void LEDC_PWM_Duty_Set(uint8_t DutyP)
-{
-	uint16_t Duty,MaxD;
-
-	MaxD=(1<<(int)ledc_tConfig.duty_resolution)-1;
-
-	if(DutyP>=100)Duty=MaxD;
-	else
-	{
-		Duty=DutyP*(MaxD/(float)100);
-	}
-	ledc_cConfig.duty=Duty;
-	ledc_channel_config(&ledc_cConfig);
-}
-
-void LEDC_Channel_Config(void)
-{
-	ledc_cConfig.gpio_num=GC9A01_PIN_NUM_BCKL;
-	ledc_cConfig.speed_mode=LEDC_LOW_SPEED_MODE;
-	ledc_cConfig.channel=LEDC_CHANNEL_0;
-	ledc_cConfig.intr_type=LEDC_INTR_DISABLE;
-	ledc_cConfig.timer_sel=LEDC_TIMER_0;
-	ledc_cConfig.duty=0;
-	ledc_cConfig.hpoint=0;
-	ledc_channel_config(&ledc_cConfig);
-}
-
-void LEDC_Timer_Config(void)
-{
-	ledc_tConfig.speed_mode=LEDC_LOW_SPEED_MODE ;
-	ledc_tConfig.duty_resolution=LEDC_TIMER_8_BIT;
-	//ledc_tConfig.bit_num=LEDC_TIMER_8_BIT;
-	ledc_tConfig.timer_num=LEDC_TIMER_0;
-	ledc_tConfig.freq_hz=1000;
-	ledc_tConfig.clk_cfg=LEDC_AUTO_CLK;
-	ledc_timer_config(&ledc_tConfig);
-}
-#endif
-#endif
-
-void GC9A01_Init()
-{
-	int cmd=0;
-
-	GC9A01_X_Start = 0;
-	GC9A01_Y_Start = 0;
-
-	gc9a01_GPIO_init();
-	gc9a01_SPI_init();
-
-	#if(GC9A01_CONTROL_BACK_LIGHT_USED)
-	#if(GC9A01_CONTROL_BACK_LIGHT_MODE)
-	LEDC_Timer_Config();
-	LEDC_Channel_Config();
-	#endif
-	#endif
-
-	#if(GC9A01_RESET_USED)
-	GC9A01_HardReset();
-	#endif
-
-    //Send all the commands
-    while (lcd_init_cmds[cmd].databytes!=0xff)
-	{
-        lcd_cmd(lcd_init_cmds[cmd].cmd);
-        lcd_data(lcd_init_cmds[cmd].data, lcd_init_cmds[cmd].databytes&0x1F);
-        if (lcd_init_cmds[cmd].databytes&0x80)
-		{
-            delay_ms(100);
-        }
-        cmd++;
-    }
-
-	MemAccessModeSet(0,0,0,1);
-	ColorModeSet(ColorMode_MCU_16bit);
-
-	GC9A01_InversionMode(1);
-	GC9A01_SleepMode(0);
-
-	delay_ms(120);
-	GC9A01_DisplayPower(1);
-	delay_ms(20);
-
-	#if(GC9A01_BUFFER_MODE)
-	GC9A01_Clear();
-	GC9A01_Update();
-	delay_ms(30);
-	#endif
-
-	#if(GC9A01_CONTROL_BACK_LIGHT_USED)
-	GC9A01_SetBL(100);
-	#endif
-}
-
-#if(GC9A01_BUFFER_MODE)
-void GC9A01_Screen_Shot(uint16_t x,uint16_t y,uint16_t width ,uint16_t height,uint16_t * Buffer)
-{
-	uint16_t i,j;
-	for (i=0;i<height;i++)
-	{
-		for(j=0;j<width;j++)
-		{
-			#if(!GC9A01_BUFFER_SCREEN_FAST_MODE)
-			Buffer[i*width+j]=GC9A01_GetPixel(x+j,y+i);
-			#else
-			Buffer[i*width+j]=ScreenBuff[((y+i) * GC9A01_Width )+ (x+j)];
-			#endif
-		}
-	}
-}
-void GC9A01_Screen_Load(uint16_t x,uint16_t y,uint16_t width ,uint16_t height,uint16_t * Buffer)
-{
-	uint16_t i,j;
-	for (i=0;i<height;i++)
-	{
-		for(j=0;j<width;j++)
-		{
-			#if(!GC9A01_BUFFER_SCREEN_FAST_MODE)
-			GC9A01_DrawPixel(x+j,y+i,Buffer[i*width+j]);
-			#else
-			ScreenBuff[((y+i) * GC9A01_Width )+ (x+j)] = Buffer[i*width+j];
-			#endif
-		}
-	}
-}
-#endif
diff --git a/components/gc9a01/gc9a01.h b/components/gc9a01/gc9a01.h
deleted file mode 100644
index 34c6a8ae83b6eeed09ea1559e78c9bc2b10ba1fc..0000000000000000000000000000000000000000
--- a/components/gc9a01/gc9a01.h
+++ /dev/null
@@ -1,67 +0,0 @@
-//--------------------------------------------------------------------------------------------------------
-// Nadyrshin Ruslan - [YouTube-channel: https://www.youtube.com/channel/UChButpZaL5kUUl_zTyIDFkQ]
-// Liyanboy74
-//--------------------------------------------------------------------------------------------------------
-#ifndef _GC9A01_H
-#define _GC9A01_H
-
-#include "sdkconfig.h"
-#include <stdint.h>
-#include "badge23_hwconfig.h"
-
-
-#ifdef CONFIG_GC9A01_PIN_NUM_MOSI
-#warning "idf menuconfig is ignored for display config, it breaks hardware revision switching atm"
-#endif
-
-#if defined(CONFIG_BADGE23_HW_GEN_P1)
-#define USE_SPI3_HOST 1
-#define GC9A01_SPI_HOST 2
-#define GC9A01_PIN_NUM_SCK 39
-#define GC9A01_PIN_NUM_MOSI 41
-#define GC9A01_PIN_NUM_CS 40
-#define GC9A01_PIN_NUM_DC 42
-#define GC9A01_SPI_SCK_FREQ_M 80
-#define GC9A01_RESET_USED 1
-#define GC9A01_PIN_NUM_RST 38
-#define GC9A01_BUFFER_MODE 1
-
-#elif defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P6)
-#define USE_SPI3_HOST 1
-#define GC9A01_SPI_HOST 2
-#define GC9A01_PIN_NUM_SCK 41
-#define GC9A01_PIN_NUM_MOSI 42
-#define GC9A01_PIN_NUM_CS 40
-#define GC9A01_PIN_NUM_DC 38
-#define GC9A01_SPI_SCK_FREQ_M 80
-#define GC9A01_CONTROL_BACK_LIGHT_USED 1
-#define GC9A01_PIN_NUM_BCKL 46
-#define GC9A01_BACK_LIGHT_MODE_PWM 1
-#define GC9A01_CONTROL_BACK_LIGHT_MODE 1
-#define GC9A01_BUFFER_MODE 1
-
-#else
-#error "gc9a01 unimplemented for this badge generation"
-#endif
-
-#define GC9A01_Width	240
-#define GC9A01_Height 	240
-
-extern uint16_t ScreenBuff[GC9A01_Height * GC9A01_Width];
-
-uint16_t GC9A01_GetWidth();
-uint16_t GC9A01_GetHeight();
-
-void GC9A01_Init();
-void GC9A01_SleepMode(uint8_t Mode);
-void GC9A01_DisplayPower(uint8_t On);
-void GC9A01_DrawPixel(int16_t x, int16_t y, uint16_t color);
-void GC9A01_FillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
-void GC9A01_Update();
-void GC9A01_SetBL(uint8_t Value);
-uint16_t GC9A01_GetPixel(int16_t x, int16_t y);
-
-void GC9A01_Screen_Shot(uint16_t x,uint16_t y,uint16_t width ,uint16_t height,uint16_t * Buffer);
-void GC9A01_Screen_Load(uint16_t x,uint16_t y,uint16_t width ,uint16_t height,uint16_t * Buffer);
-
-#endif
diff --git a/components/gc9a01/readme.md b/components/gc9a01/readme.md
deleted file mode 100644
index 46156f31f42a3177b151ba8045dd86fcecb72434..0000000000000000000000000000000000000000
--- a/components/gc9a01/readme.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# GC9A01 ESP-IDF Component    
-
-Clone to `components` folder and run `idf.py menuconfig`
-
-![ESP-IDF_menuconfig](https://user-images.githubusercontent.com/64005694/111914456-43582400-8a87-11eb-9173-375262b5a261.jpg)
-
-**Add as submodule:**
-
-`git submodule add https://github.com/liyanboy74/gc9a01-esp-idf.git components/gc9a01`
-
-**Example Test:**
-
-```c
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-#include "driver/gpio.h"
-#include "sdkconfig.h"
-
-#include "gc9a01.h"
-
-#define STACK_SIZE              2048
-
-void LCD(void * arg)
-{
-    uint16_t Color;
-    GC9A01_Init();
-    for(;;)
-    {
-        Color=rand();
-        GC9A01_FillRect(0,0,239,239,Color);
-        GC9A01_Update();
-        vTaskDelay(1000/portTICK_PERIOD_MS);
-    }
-}
-
-void app_main(void)
-{
-    TaskHandle_t LCDHandle;
-
-    xTaskCreate(LCD,"Test LCD",STACK_SIZE,NULL,tskIDLE_PRIORITY,&LCDHandle);
-    configASSERT(LCDHandle);
-}
-
-```
-
-- If you succeed, it's time to go one layer higher! Try [Dispcolor](https://github.com/liyanboy74/dispcolor)
-- You can also use the [BMP24 to RGB565](https://github.com/liyanboy74/bmp24-to-rgb565) tools to convert and display images
-
diff --git a/sdkconfig.p1 b/sdkconfig.p1
index dfa66a8b35f4606c1287f81d017ea1a49a659172..9d58c19655bb26aa3e875ee6f56a41c1ac7b9348 100644
--- a/sdkconfig.p1
+++ b/sdkconfig.p1
@@ -12,6 +12,10 @@ CONFIG_BT_ENABLED=y
 CONFIG_BT_NIMBLE_ENABLED=y
 CONFIG_BT_NIMBLE_MAX_CONNECTIONS=4
 CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=y
+CONFIG_ESP32S3_SPIRAM_SUPPORT=y
+CONFIG_SPIRAM_MODE_OCT=y
+CONFIG_SPIRAM_TYPE_ESPPSRAM64=y
+CONFIG_SPIRAM_SPEED_80M=y
 CONFIG_PM_ENABLE=y
 CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
 # CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 is not set
diff --git a/sdkconfig.p3 b/sdkconfig.p3
index 1680de7e8e6690eb6531b519983cf998ca72beb2..1b06862f23efb6b225a524bf504ef092fc4cdca3 100644
--- a/sdkconfig.p3
+++ b/sdkconfig.p3
@@ -12,6 +12,10 @@ CONFIG_BT_ENABLED=y
 CONFIG_BT_NIMBLE_ENABLED=y
 CONFIG_BT_NIMBLE_MAX_CONNECTIONS=4
 CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=y
+CONFIG_ESP32S3_SPIRAM_SUPPORT=y
+CONFIG_SPIRAM_MODE_OCT=y
+CONFIG_SPIRAM_TYPE_ESPPSRAM64=y
+CONFIG_SPIRAM_SPEED_80M=y
 CONFIG_PM_ENABLE=y
 CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
 # CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 is not set
diff --git a/sdkconfig.p4 b/sdkconfig.p4
index 3244f8307c78e2e3e2ed23ce8d138ec81a41df61..632271f522d54663acf3e17c0d0fda14ddb31e2c 100644
--- a/sdkconfig.p4
+++ b/sdkconfig.p4
@@ -12,6 +12,10 @@ CONFIG_BT_ENABLED=y
 CONFIG_BT_NIMBLE_ENABLED=y
 CONFIG_BT_NIMBLE_MAX_CONNECTIONS=4
 CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=y
+CONFIG_ESP32S3_SPIRAM_SUPPORT=y
+CONFIG_SPIRAM_MODE_OCT=y
+CONFIG_SPIRAM_TYPE_ESPPSRAM64=y
+CONFIG_SPIRAM_SPEED_80M=y
 CONFIG_PM_ENABLE=y
 CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
 # CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 is not set
@@ -26,4 +30,4 @@ CONFIG_LOG_DEFAULT_LEVEL_ERROR=y
 CONFIG_LWIP_PPP_SUPPORT=y
 CONFIG_LWIP_PPP_PAP_SUPPORT=y
 CONFIG_LWIP_PPP_CHAP_SUPPORT=y
-CONFIG_BADGE23_HW_GEN_P4=y
\ No newline at end of file
+CONFIG_BADGE23_HW_GEN_P4=y
diff --git a/sdkconfig.p6 b/sdkconfig.p6
index 022e70a5d015e78b16c4f99d31b21ab2256a054e..8d298d5bfaba5c174cd4846038d5e2d89867a42b 100644
--- a/sdkconfig.p6
+++ b/sdkconfig.p6
@@ -12,6 +12,10 @@ CONFIG_BT_ENABLED=y
 CONFIG_BT_NIMBLE_ENABLED=y
 CONFIG_BT_NIMBLE_MAX_CONNECTIONS=4
 CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=y
+CONFIG_ESP32S3_SPIRAM_SUPPORT=y
+CONFIG_SPIRAM_MODE_OCT=y
+CONFIG_SPIRAM_TYPE_ESPPSRAM64=y
+CONFIG_SPIRAM_SPEED_80M=y
 CONFIG_PM_ENABLE=y
 CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
 # CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 is not set
diff --git a/usermodule/mp_hardware.c b/usermodule/mp_hardware.c
index fa7dec9ffb64fc5122e73fa8278314987eb9ef9f..ed5cf36587339395ce51ba88b37d04ed2e893c06 100644
--- a/usermodule/mp_hardware.c
+++ b/usermodule/mp_hardware.c
@@ -172,12 +172,13 @@ STATIC mp_obj_t mp_version(void) {
 STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_version_obj, mp_version);
 
 STATIC mp_obj_t mp_get_ctx(size_t n_args, const mp_obj_t *args) {
+    Ctx *ctx = NULL;
     // This might be called before the ctx is ready.
     // HACK: this will go away with the new drawing API.
-    while (the_ctx == NULL) {
+    while ((ctx = display_global_ctx()) == NULL) {
         vTaskDelay(100 / portTICK_PERIOD_MS);
     }
-    mp_obj_t mp_ctx = mp_ctx_from_ctx(the_ctx);
+    mp_obj_t mp_ctx = mp_ctx_from_ctx(ctx);
     return mp_ctx;
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_get_ctx_obj, 0, 0, mp_get_ctx);