From 4d41d8497aaa4a5999a4f769adcb616a96f735b5 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Tue, 14 Jul 2026 01:07:08 +0200 Subject: [PATCH] unphone display and touch migration --- Devices/unphone/CMakeLists.txt | 2 +- Devices/unphone/Source/Configuration.cpp | 12 +- Devices/unphone/Source/InitBoot.cpp | 10 +- .../unphone/Source/devices/Hx8357Display.cpp | 119 ----- .../unphone/Source/devices/Hx8357Display.h | 66 --- Devices/unphone/Source/devices/Touch.cpp | 12 - Devices/unphone/Source/devices/Touch.h | 8 - Devices/unphone/Source/hx8357/README.md | 4 - Devices/unphone/Source/hx8357/disp_spi.c | 311 ------------ Devices/unphone/Source/hx8357/disp_spi.h | 81 --- Devices/unphone/Source/hx8357/hx8357.c | 292 ----------- Devices/unphone/Source/hx8357/hx8357.h | 133 ----- Devices/unphone/Source/lvgl_spi_conf.h | 7 - Devices/unphone/devicetree.yaml | 2 + Devices/unphone/unphone.dts | 18 +- Drivers/XPT2046/CMakeLists.txt | 5 - Drivers/XPT2046/README.md | 3 - Drivers/XPT2046/Source/Xpt2046Touch.cpp | 37 -- Drivers/XPT2046/Source/Xpt2046Touch.h | 59 --- Drivers/hx8357-module/CMakeLists.txt | 11 + Drivers/hx8357-module/LICENSE-Apache-2.0.md | 195 ++++++++ Drivers/hx8357-module/README.md | 7 + .../hx8357-module/bindings/himax,hx8357.yaml | 64 +++ Drivers/hx8357-module/devicetree.yaml | 3 + .../hx8357-module/include/bindings/hx8357.h | 7 + .../hx8357-module/include/drivers/hx8357.h | 33 ++ Drivers/hx8357-module/include/hx8357_module.h | 14 + Drivers/hx8357-module/source/hx8357.cpp | 462 ++++++++++++++++++ Drivers/hx8357-module/source/module.cpp | 32 ++ .../bindings/ilitek,ili9341.yaml | 2 +- .../bindings/ilitek,ili9488.yaml | 2 +- .../bindings/sitronix,st7789-i8080.yaml | 2 +- .../bindings/sitronix,st7789.yaml | 2 +- 33 files changed, 856 insertions(+), 1161 deletions(-) delete mode 100644 Devices/unphone/Source/devices/Hx8357Display.cpp delete mode 100644 Devices/unphone/Source/devices/Hx8357Display.h delete mode 100644 Devices/unphone/Source/devices/Touch.cpp delete mode 100644 Devices/unphone/Source/devices/Touch.h delete mode 100644 Devices/unphone/Source/hx8357/README.md delete mode 100644 Devices/unphone/Source/hx8357/disp_spi.c delete mode 100644 Devices/unphone/Source/hx8357/disp_spi.h delete mode 100644 Devices/unphone/Source/hx8357/hx8357.c delete mode 100644 Devices/unphone/Source/hx8357/hx8357.h delete mode 100644 Devices/unphone/Source/lvgl_spi_conf.h delete mode 100644 Drivers/XPT2046/CMakeLists.txt delete mode 100644 Drivers/XPT2046/README.md delete mode 100644 Drivers/XPT2046/Source/Xpt2046Touch.cpp delete mode 100644 Drivers/XPT2046/Source/Xpt2046Touch.h create mode 100644 Drivers/hx8357-module/CMakeLists.txt create mode 100644 Drivers/hx8357-module/LICENSE-Apache-2.0.md create mode 100644 Drivers/hx8357-module/README.md create mode 100644 Drivers/hx8357-module/bindings/himax,hx8357.yaml create mode 100644 Drivers/hx8357-module/devicetree.yaml create mode 100644 Drivers/hx8357-module/include/bindings/hx8357.h create mode 100644 Drivers/hx8357-module/include/drivers/hx8357.h create mode 100644 Drivers/hx8357-module/include/hx8357_module.h create mode 100644 Drivers/hx8357-module/source/hx8357.cpp create mode 100644 Drivers/hx8357-module/source/module.cpp diff --git a/Devices/unphone/CMakeLists.txt b/Devices/unphone/CMakeLists.txt index 061470ddc..0a4fc7e73 100644 --- a/Devices/unphone/CMakeLists.txt +++ b/Devices/unphone/CMakeLists.txt @@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*) idf_component_register( SRCS ${SOURCE_FILES} INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_io_expander esp_io_expander_tca95xx_16bit BQ24295 XPT2046 + REQUIRES Tactility esp_lvgl_port esp_io_expander esp_io_expander_tca95xx_16bit BQ24295 ) diff --git a/Devices/unphone/Source/Configuration.cpp b/Devices/unphone/Source/Configuration.cpp index b32363ff9..75331ec46 100644 --- a/Devices/unphone/Source/Configuration.cpp +++ b/Devices/unphone/Source/Configuration.cpp @@ -1,17 +1,7 @@ -#include "UnPhoneFeatures.h" -#include "devices/Hx8357Display.h" - #include bool initBoot(); -static tt::hal::DeviceVector createDevices() { - return { - createDisplay(), - }; -} - extern const tt::hal::Configuration hardwareConfiguration = { - .initBoot = initBoot, - .createDevices = createDevices + .initBoot = initBoot }; diff --git a/Devices/unphone/Source/InitBoot.cpp b/Devices/unphone/Source/InitBoot.cpp index 188803ab4..3be9bb5cd 100644 --- a/Devices/unphone/Source/InitBoot.cpp +++ b/Devices/unphone/Source/InitBoot.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include @@ -161,7 +160,10 @@ static bool unPhonePowerOn() { bootStats.printInfo(); bootStats.notifyBootStart(); - bq24295 = std::make_shared(device_find_by_name("i2c_internal")); + ::Device* i2c_internal = nullptr; + check(device_get_by_name("i2c_internal", &i2c_internal) == ERROR_NONE); + bq24295 = std::make_shared(i2c_internal); + device_put(i2c_internal); unPhoneFeatures = std::make_shared(bq24295); @@ -172,7 +174,9 @@ static bool unPhonePowerOn() { unPhoneFeatures->printInfo(); - unPhoneFeatures->setBacklightPower(false); + // Kernel devicetree devices (incl. the hx8357 display) already started by kernel_init() + // before initBoot() runs, so it's safe to turn the backlight on here now. + unPhoneFeatures->setBacklightPower(true); unPhoneFeatures->setVibePower(false); unPhoneFeatures->setIrPower(false); unPhoneFeatures->setExpanderPower(false); diff --git a/Devices/unphone/Source/devices/Hx8357Display.cpp b/Devices/unphone/Source/devices/Hx8357Display.cpp deleted file mode 100644 index db462e6a9..000000000 --- a/Devices/unphone/Source/devices/Hx8357Display.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "Hx8357Display.h" -#include "Touch.h" - -#include - -#include -#include -#include - -constexpr auto* TAG = "Hx8357Display"; - -constexpr auto BUFFER_SIZE = (UNPHONE_LCD_HORIZONTAL_RESOLUTION * UNPHONE_LCD_DRAW_BUFFER_HEIGHT * LV_COLOR_DEPTH / 8); - -extern std::shared_ptr unPhoneFeatures; - -bool Hx8357Display::start() { - LOG_I(TAG, "start"); - - disp_spi_add_device(SPI2_HOST); - - hx8357_reset(GPIO_NUM_46); - hx8357_init(UNPHONE_LCD_PIN_DC); - uint8_t madctl = (1U << MADCTL_BIT_INDEX_COLUMN_ADDRESS_ORDER); - hx8357_set_madctl(madctl); - - return true; -} - -bool Hx8357Display::stop() { - LOG_I(TAG, "stop"); - disp_spi_remove_device(); - return true; -} - -bool Hx8357Display::startLvgl() { - LOG_I(TAG, "startLvgl"); - - if (lvglDisplay != nullptr) { - LOG_W(TAG, "LVGL was already started"); - return false; - } - - lvglDisplay = lv_display_create(UNPHONE_LCD_HORIZONTAL_RESOLUTION, UNPHONE_LCD_VERTICAL_RESOLUTION); - lv_display_set_physical_resolution(lvglDisplay, UNPHONE_LCD_HORIZONTAL_RESOLUTION, UNPHONE_LCD_VERTICAL_RESOLUTION); - lv_display_set_color_format(lvglDisplay, LV_COLOR_FORMAT_NATIVE); - - // TODO malloc to use SPIRAM - buffer = static_cast(heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_DMA)); - assert(buffer != nullptr); - - lv_display_set_buffers( - lvglDisplay, - buffer, - nullptr, - BUFFER_SIZE, - LV_DISPLAY_RENDER_MODE_PARTIAL - ); - - lv_display_set_flush_cb(lvglDisplay, hx8357_flush); - - if (lvglDisplay == nullptr) { - LOG_I(TAG, "Failed"); - return false; - } - - unPhoneFeatures->setBacklightPower(true); - - auto touch_device = getTouchDevice(); - if (touch_device != nullptr) { - touch_device->startLvgl(lvglDisplay); - } - - return true; -} - -bool Hx8357Display::stopLvgl() { - LOG_I(TAG, "stopLvgl"); - - if (lvglDisplay == nullptr) { - LOG_W(TAG, "LVGL was already stopped"); - return false; - } - - // Just in case - disp_wait_for_pending_transactions(); - - auto touch_device = getTouchDevice(); - if (touch_device != nullptr && touch_device->getLvglIndev() != nullptr) { - LOG_I(TAG, "Stopping touch device"); - touch_device->stopLvgl(); - } - - lv_display_delete(lvglDisplay); - lvglDisplay = nullptr; - - heap_caps_free(buffer); - buffer = nullptr; - - return true; -} - -std::shared_ptr Hx8357Display::getTouchDevice() { - if (touchDevice == nullptr) { - touchDevice = std::reinterpret_pointer_cast(createTouch()); - LOG_I(TAG, "Created touch device"); - } - - return touchDevice; -} - -std::shared_ptr createDisplay() { - return std::make_shared(); -} - -bool Hx8357Display::Hx8357Driver::drawBitmap(int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) { - lv_area_t area = { xStart, yStart, xEnd, yEnd }; - hx8357_flush(nullptr, &area, (uint8_t*)pixelData); - return true; -} diff --git a/Devices/unphone/Source/devices/Hx8357Display.h b/Devices/unphone/Source/devices/Hx8357Display.h deleted file mode 100644 index 24b0076f6..000000000 --- a/Devices/unphone/Source/devices/Hx8357Display.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -#include - -#define UNPHONE_LCD_SPI_HOST SPI2_HOST -#define UNPHONE_LCD_PIN_CS GPIO_NUM_48 -#define UNPHONE_LCD_PIN_DC GPIO_NUM_47 -#define UNPHONE_LCD_PIN_RESET GPIO_NUM_46 -#define UNPHONE_LCD_SPI_FREQUENCY 27000000 -#define UNPHONE_LCD_HORIZONTAL_RESOLUTION 320 -#define UNPHONE_LCD_VERTICAL_RESOLUTION 480 -#define UNPHONE_LCD_DRAW_BUFFER_HEIGHT (UNPHONE_LCD_VERTICAL_RESOLUTION / 15) - -class Hx8357Display : public tt::hal::display::DisplayDevice { - - uint8_t* buffer = nullptr; - lv_display_t* lvglDisplay = nullptr; - std::shared_ptr touchDevice; - std::shared_ptr nativeDisplay; - - class Hx8357Driver : public tt::hal::display::DisplayDriver { - public: - tt::hal::display::ColorFormat getColorFormat() const override { return tt::hal::display::ColorFormat::RGB888; } - uint16_t getPixelWidth() const override { return UNPHONE_LCD_HORIZONTAL_RESOLUTION; } - uint16_t getPixelHeight() const override { return UNPHONE_LCD_VERTICAL_RESOLUTION; } - bool drawBitmap(int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) override; - }; - -public: - - std::string getName() const final { return "HX8357"; } - std::string getDescription() const final { return "SPI display"; } - - bool start() override; - - bool stop() override; - - bool supportsLvgl() const override { return true; } - - bool startLvgl() override; - - bool stopLvgl() override; - - std::shared_ptr getTouchDevice() override; - - lv_display_t* getLvglDisplay() const override { return lvglDisplay; } - - // TODO: Set to true after fixing UnPhoneDisplayDriver - bool supportsDisplayDriver() const override { return false; } - - std::shared_ptr getDisplayDriver() override { - if (nativeDisplay == nullptr) { - nativeDisplay = std::make_shared(); - } - assert(nativeDisplay != nullptr); - return nativeDisplay; - } -}; - -std::shared_ptr createDisplay(); diff --git a/Devices/unphone/Source/devices/Touch.cpp b/Devices/unphone/Source/devices/Touch.cpp deleted file mode 100644 index 8c9a19b4d..000000000 --- a/Devices/unphone/Source/devices/Touch.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "Touch.h" - -std::shared_ptr createTouch() { - auto configuration = std::make_unique( - SPI2_HOST, - GPIO_NUM_38, - 320, - 480 - ); - - return std::make_shared(std::move(configuration)); -} diff --git a/Devices/unphone/Source/devices/Touch.h b/Devices/unphone/Source/devices/Touch.h deleted file mode 100644 index a6d20c0f3..000000000 --- a/Devices/unphone/Source/devices/Touch.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include -#include - -extern std::shared_ptr touchInstance; - -std::shared_ptr createTouch(); diff --git a/Devices/unphone/Source/hx8357/README.md b/Devices/unphone/Source/hx8357/README.md deleted file mode 100644 index c580f6ced..000000000 --- a/Devices/unphone/Source/hx8357/README.md +++ /dev/null @@ -1,4 +0,0 @@ -The files in this folder are from https://github.com/lvgl/lvgl_esp32_drivers -The original license is an MIT license: https://github.com/lvgl/lvgl_esp32_drivers/blob/master/LICENSE - -You may use the files in this folder under the original license, or under GPL v3 from the main Tactility project. \ No newline at end of file diff --git a/Devices/unphone/Source/hx8357/disp_spi.c b/Devices/unphone/Source/hx8357/disp_spi.c deleted file mode 100644 index a81e4f60c..000000000 --- a/Devices/unphone/Source/hx8357/disp_spi.c +++ /dev/null @@ -1,311 +0,0 @@ -#define LV_USE_PRIVATE_API 1 // For actual lv_obj_t declaration - -/** - * @file disp_spi.c - * - */ - -/********************* - * INCLUDES - *********************/ -#include "esp_system.h" -#include "driver/gpio.h" -#include "driver/spi_master.h" -#include "esp_log.h" - -#define TAG "disp_spi" - -#include - -#include -#include -#include - -#include - -#include "disp_spi.h" -//#include "disp_driver.h" - -//#include "../lvgl_helpers.h" -#include "../lvgl_spi_conf.h" - -/****************************************************************************** - * Notes about DMA spi_transaction_ext_t structure pooling - * - * An xQueue is used to hold a pool of reusable SPI spi_transaction_ext_t - * structures that get used for all DMA SPI transactions. While an xQueue may - * seem like overkill it is an already built-in RTOS feature that comes at - * little cost. xQueues are also ISR safe if it ever becomes necessary to - * access the pool in the ISR callback. - * - * When a DMA request is sent, a transaction structure is removed from the - * pool, filled out, and passed off to the esp32 SPI driver. Later, when - * servicing pending SPI transaction results, the transaction structure is - * recycled back into the pool for later reuse. This matches the DMA SPI - * transaction life cycle requirements of the esp32 SPI driver. - * - * When polling or synchronously sending SPI requests, and as required by the - * esp32 SPI driver, all pending DMA transactions are first serviced. Then the - * polling SPI request takes place. - * - * When sending an asynchronous DMA SPI request, if the pool is empty, some - * small percentage of pending transactions are first serviced before sending - * any new DMA SPI transactions. Not too many and not too few as this balance - * controls DMA transaction latency. - * - * It is therefore not the design that all pending transactions must be - * serviced and placed back into the pool with DMA SPI requests - that - * will happen eventually. The pool just needs to contain enough to float some - * number of in-flight SPI requests to speed up the overall DMA SPI data rate - * and reduce transaction latency. If however a display driver uses some - * polling SPI requests or calls disp_wait_for_pending_transactions() directly, - * the pool will reach the full state more often and speed up DMA queuing. - * - *****************************************************************************/ - -/********************* - * DEFINES - *********************/ -#define SPI_TRANSACTION_POOL_SIZE 50 /* maximum number of DMA transactions simultaneously in-flight */ - -/* DMA Transactions to reserve before queueing additional DMA transactions. A 1/10th seems to be a good balance. Too many (or all) and it will increase latency. */ -#define SPI_TRANSACTION_POOL_RESERVE_PERCENTAGE 10 -#if SPI_TRANSACTION_POOL_SIZE >= SPI_TRANSACTION_POOL_RESERVE_PERCENTAGE -#define SPI_TRANSACTION_POOL_RESERVE (SPI_TRANSACTION_POOL_SIZE / SPI_TRANSACTION_POOL_RESERVE_PERCENTAGE) -#else -#define SPI_TRANSACTION_POOL_RESERVE 1 /* defines minimum size */ -#endif - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ -static void spi_ready(spi_transaction_t*trans); - -/********************** - * STATIC VARIABLES - **********************/ -static spi_host_device_t spi_host; -static spi_device_handle_t spi; -static QueueHandle_t TransactionPool = NULL; -static transaction_cb_t chained_post_cb; - -/********************** - * MACROS - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ -void disp_spi_add_device_config(spi_host_device_t host, spi_device_interface_config_t *devcfg) -{ - spi_host=host; - chained_post_cb=devcfg->post_cb; - devcfg->post_cb=spi_ready; - esp_err_t ret=spi_bus_add_device(host, devcfg, &spi); - assert(ret==ESP_OK); -} - -void disp_spi_add_device(spi_host_device_t host) -{ - disp_spi_add_device_with_speed(host, SPI_TFT_CLOCK_SPEED_HZ); -} - -void disp_spi_add_device_with_speed(spi_host_device_t host, int clock_speed_hz) -{ - ESP_LOGI(TAG, "Adding SPI device"); - ESP_LOGI(TAG, "Clock speed: %dHz, mode: %d, CS pin: %d", - clock_speed_hz, SPI_TFT_SPI_MODE, DISP_SPI_CS); - - spi_device_interface_config_t devcfg={ - .clock_speed_hz = clock_speed_hz, - .mode = SPI_TFT_SPI_MODE, - .spics_io_num=DISP_SPI_CS, // CS pin - .input_delay_ns=DISP_SPI_INPUT_DELAY_NS, - .queue_size=SPI_TRANSACTION_POOL_SIZE, - .pre_cb=NULL, - .post_cb=NULL, -#if defined(DISP_SPI_HALF_DUPLEX) - .flags = SPI_DEVICE_NO_DUMMY | SPI_DEVICE_HALFDUPLEX, /* dummy bits should be explicitly handled via DISP_SPI_VARIABLE_DUMMY as needed */ -#else - #if defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) - .flags = 0, - #elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875) - .flags = SPI_DEVICE_NO_DUMMY, - #endif -#endif - }; - - disp_spi_add_device_config(host, &devcfg); - - /* create the transaction pool and fill it with ptrs to spi_transaction_ext_t to reuse */ - if(TransactionPool == NULL) { - TransactionPool = xQueueCreate(SPI_TRANSACTION_POOL_SIZE, sizeof(spi_transaction_ext_t*)); - assert(TransactionPool != NULL); - for (size_t i = 0; i < SPI_TRANSACTION_POOL_SIZE; i++) - { - spi_transaction_ext_t* pTransaction = (spi_transaction_ext_t*)heap_caps_malloc(sizeof(spi_transaction_ext_t), MALLOC_CAP_DMA); - assert(pTransaction != NULL); - memset(pTransaction, 0, sizeof(spi_transaction_ext_t)); - xQueueSend(TransactionPool, &pTransaction, portMAX_DELAY); - } - } -} - -void disp_spi_change_device_speed(int clock_speed_hz) -{ - if (clock_speed_hz <= 0) { - clock_speed_hz = SPI_TFT_CLOCK_SPEED_HZ; - } - ESP_LOGI(TAG, "Changing SPI device clock speed: %d", clock_speed_hz); - disp_spi_remove_device(); - disp_spi_add_device_with_speed(spi_host, clock_speed_hz); -} - -void disp_spi_remove_device() -{ - /* Wait for previous pending transaction results */ - disp_wait_for_pending_transactions(); - - esp_err_t ret=spi_bus_remove_device(spi); - assert(ret==ESP_OK); -} - -void disp_spi_transaction(const uint8_t *data, size_t length, - int flags, uint8_t *out, - uint64_t addr, uint8_t dummy_bits) -{ - if (0 == length) { - return; - } - - spi_transaction_ext_t t = {0}; - - /* transaction length is in bits */ - t.base.length = length * 8; - - if (length <= 4 && data != NULL) { - t.base.flags = SPI_TRANS_USE_TXDATA; - memcpy(t.base.tx_data, data, length); - } else { - t.base.tx_buffer = data; - } - - if (flags & DISP_SPI_RECEIVE) { - assert(out != NULL && (flags & (DISP_SPI_SEND_POLLING | DISP_SPI_SEND_SYNCHRONOUS))); - t.base.rx_buffer = out; - -#if defined(DISP_SPI_HALF_DUPLEX) - t.base.rxlength = t.base.length; - t.base.length = 0; /* no MOSI phase in half-duplex reads */ -#else - t.base.rxlength = 0; /* in full-duplex mode, zero means same as tx length */ -#endif - } - - if (flags & DISP_SPI_ADDRESS_8) { - t.address_bits = 8; - } else if (flags & DISP_SPI_ADDRESS_16) { - t.address_bits = 16; - } else if (flags & DISP_SPI_ADDRESS_24) { - t.address_bits = 24; - } else if (flags & DISP_SPI_ADDRESS_32) { - t.address_bits = 32; - } - if (t.address_bits) { - t.base.addr = addr; - t.base.flags |= SPI_TRANS_VARIABLE_ADDR; - } - -#if defined(DISP_SPI_HALF_DUPLEX) - if (flags & DISP_SPI_MODE_DIO) { - t.base.flags |= SPI_TRANS_MODE_DIO; - } else if (flags & DISP_SPI_MODE_QIO) { - t.base.flags |= SPI_TRANS_MODE_QIO; - } - - if (flags & DISP_SPI_MODE_DIOQIO_ADDR) { - t.base.flags |= SPI_TRANS_MODE_DIOQIO_ADDR; - } - - if ((flags & DISP_SPI_VARIABLE_DUMMY) && dummy_bits) { - t.dummy_bits = dummy_bits; - t.base.flags |= SPI_TRANS_VARIABLE_DUMMY; - } -#endif - - /* Save flags for pre/post transaction processing */ - t.base.user = (void *) flags; - - /* Poll/Complete/Queue transaction */ - if (flags & DISP_SPI_SEND_POLLING) { - disp_wait_for_pending_transactions(); /* before polling, all previous pending transactions need to be serviced */ - spi_device_polling_transmit(spi, (spi_transaction_t *) &t); - } else if (flags & DISP_SPI_SEND_SYNCHRONOUS) { - disp_wait_for_pending_transactions(); /* before synchronous queueing, all previous pending transactions need to be serviced */ - spi_device_transmit(spi, (spi_transaction_t *) &t); - } else { - - /* if necessary, ensure we can queue new transactions by servicing some previous transactions */ - if(uxQueueMessagesWaiting(TransactionPool) == 0) { - spi_transaction_t *presult; - while(uxQueueMessagesWaiting(TransactionPool) < SPI_TRANSACTION_POOL_RESERVE) { - if (spi_device_get_trans_result(spi, &presult, 1) == ESP_OK) { - xQueueSend(TransactionPool, &presult, portMAX_DELAY); /* back to the pool to be reused */ - } - } - } - - spi_transaction_ext_t *pTransaction = NULL; - xQueueReceive(TransactionPool, &pTransaction, portMAX_DELAY); - memcpy(pTransaction, &t, sizeof(t)); - if (spi_device_queue_trans(spi, (spi_transaction_t *) pTransaction, portMAX_DELAY) != ESP_OK) { - xQueueSend(TransactionPool, &pTransaction, portMAX_DELAY); /* send failed transaction back to the pool to be reused */ - } - } -} - - -void disp_wait_for_pending_transactions(void) -{ - spi_transaction_t *presult; - - while(uxQueueMessagesWaiting(TransactionPool) < SPI_TRANSACTION_POOL_SIZE) { /* service until the transaction reuse pool is full again */ - if (spi_device_get_trans_result(spi, &presult, 1) == ESP_OK) { - xQueueSend(TransactionPool, &presult, portMAX_DELAY); - } - } -} - -void disp_spi_acquire(void) -{ - esp_err_t ret = spi_device_acquire_bus(spi, portMAX_DELAY); - assert(ret == ESP_OK); -} - -void disp_spi_release(void) -{ - spi_device_release_bus(spi); -} - -/********************** - * STATIC FUNCTIONS - **********************/ - -static void IRAM_ATTR spi_ready(spi_transaction_t *trans) -{ - disp_spi_send_flag_t flags = (disp_spi_send_flag_t) trans->user; - - if (flags & DISP_SPI_SIGNAL_FLUSH) { - lv_disp_t* disp = lv_refr_get_disp_refreshing(); - lv_disp_flush_ready(disp); - } - - if (chained_post_cb) { - chained_post_cb(trans); - } -} - diff --git a/Devices/unphone/Source/hx8357/disp_spi.h b/Devices/unphone/Source/hx8357/disp_spi.h deleted file mode 100644 index 453946cc1..000000000 --- a/Devices/unphone/Source/hx8357/disp_spi.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @file disp_spi.h - * - */ - -#ifndef DISP_SPI_H -#define DISP_SPI_H - -#ifdef __cplusplus -extern "C" { -#endif - -/********************* - * INCLUDES - *********************/ -#include -#include -#include - -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ -typedef enum _disp_spi_send_flag_t { - DISP_SPI_SEND_QUEUED = 0x00000000, - DISP_SPI_SEND_POLLING = 0x00000001, - DISP_SPI_SEND_SYNCHRONOUS = 0x00000002, - DISP_SPI_SIGNAL_FLUSH = 0x00000004, - DISP_SPI_RECEIVE = 0x00000008, - DISP_SPI_CMD_8 = 0x00000010, /* Reserved */ - DISP_SPI_CMD_16 = 0x00000020, /* Reserved */ - DISP_SPI_ADDRESS_8 = 0x00000040, - DISP_SPI_ADDRESS_16 = 0x00000080, - DISP_SPI_ADDRESS_24 = 0x00000100, - DISP_SPI_ADDRESS_32 = 0x00000200, - DISP_SPI_MODE_DIO = 0x00000400, - DISP_SPI_MODE_QIO = 0x00000800, - DISP_SPI_MODE_DIOQIO_ADDR = 0x00001000, - DISP_SPI_VARIABLE_DUMMY = 0x00002000, -} disp_spi_send_flag_t; - - -/********************** - * GLOBAL PROTOTYPES - **********************/ -void disp_spi_add_device(spi_host_device_t host); -void disp_spi_add_device_config(spi_host_device_t host, spi_device_interface_config_t *devcfg); -void disp_spi_add_device_with_speed(spi_host_device_t host, int clock_speed_hz); -void disp_spi_change_device_speed(int clock_speed_hz); -void disp_spi_remove_device(); - -/* Important! - All buffers should also be 32-bit aligned and DMA capable to prevent extra allocations and copying. - When DMA reading (even in polling mode) the ESP32 always read in 4-byte chunks even if less is requested. - Extra space will be zero filled. Always ensure the out buffer is large enough to hold at least 4 bytes! -*/ -void disp_spi_transaction(const uint8_t *data, size_t length, - int flags, uint8_t *out, uint64_t addr, uint8_t dummy_bits); - -void disp_wait_for_pending_transactions(void); -void disp_spi_acquire(void); -void disp_spi_release(void); - -static inline void disp_spi_send_data(uint8_t *data, size_t length) { - disp_spi_transaction(data, length, DISP_SPI_SEND_POLLING, NULL, 0, 0); -} - -static inline void disp_spi_send_colors(uint8_t *data, size_t length) { - disp_spi_transaction(data, length, - DISP_SPI_SEND_QUEUED | DISP_SPI_SIGNAL_FLUSH, - NULL, 0, 0); -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /*DISP_SPI_H*/ diff --git a/Devices/unphone/Source/hx8357/hx8357.c b/Devices/unphone/Source/hx8357/hx8357.c deleted file mode 100644 index 51375ef34..000000000 --- a/Devices/unphone/Source/hx8357/hx8357.c +++ /dev/null @@ -1,292 +0,0 @@ -/** - * @file HX8357.c - * - * Roughly based on the Adafruit_HX8357_Library - * - * This library should work with: - * Adafruit 3.5" TFT 320x480 + Touchscreen Breakout - * http://www.adafruit.com/products/2050 - * - * Adafruit TFT FeatherWing - 3.5" 480x320 Touchscreen for Feathers - * https://www.adafruit.com/product/3651 - * - */ - -/********************* - * INCLUDES - *********************/ -#include "hx8357.h" -#include "disp_spi.h" -#include "driver/gpio.h" -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" - -/********************* - * DEFINES - *********************/ - -#define TAG "HX8357" - -/********************** - * TYPEDEFS - **********************/ - -static gpio_num_t dcPin = GPIO_NUM_NC; - -/*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 PROTOTYPES - **********************/ -static void hx8357_send_cmd(uint8_t cmd); -static void hx8357_send_data(void * data, uint16_t length); -static void hx8357_send_color(void * data, uint16_t length); - - -/********************** - * INITIALIZATION ARRAYS - **********************/ -// Taken from the Adafruit driver -static const uint8_t - initb[] = { - HX8357B_SETPOWER, 3, - 0x44, 0x41, 0x06, - HX8357B_SETVCOM, 2, - 0x40, 0x10, - HX8357B_SETPWRNORMAL, 2, - 0x05, 0x12, - HX8357B_SET_PANEL_DRIVING, 5, - 0x14, 0x3b, 0x00, 0x02, 0x11, - HX8357B_SETDISPLAYFRAME, 1, - 0x0c, // 6.8mhz - HX8357B_SETPANELRELATED, 1, - 0x01, // BGR - 0xEA, 3, // seq_undefined1, 3 args - 0x03, 0x00, 0x00, - 0xEB, 4, // undef2, 4 args - 0x40, 0x54, 0x26, 0xdb, - HX8357B_SETGAMMA, 12, - 0x00, 0x15, 0x00, 0x22, 0x00, 0x08, 0x77, 0x26, 0x66, 0x22, 0x04, 0x00, - HX8357_MADCTL, 1, - 0xC0, - HX8357_COLMOD, 1, - 0x55, - HX8357_PASET, 4, - 0x00, 0x00, 0x01, 0xDF, - HX8357_CASET, 4, - 0x00, 0x00, 0x01, 0x3F, - HX8357B_SETDISPMODE, 1, - 0x00, // CPU (DBI) and internal oscillation ?? - HX8357_SLPOUT, 0x80 + 120/5, // Exit sleep, then delay 120 ms - HX8357_DISPON, 0x80 + 10/5, // Main screen turn on, delay 10 ms - 0 // END OF COMMAND LIST - }, initd[] = { - HX8357_SWRESET, 0x80 + 100/5, // Soft reset, then delay 10 ms - HX8357D_SETC, 3, - 0xFF, 0x83, 0x57, - 0xFF, 0x80 + 500/5, // No command, just delay 300 ms - HX8357_SETRGB, 4, - 0x80, 0x00, 0x06, 0x06, // 0x80 enables SDO pin (0x00 disables) - HX8357D_SETCOM, 1, - 0x25, // -1.52V - HX8357_SETOSC, 1, - 0x68, // Normal mode 70Hz, Idle mode 55 Hz - HX8357_SETPANEL, 1, - 0x05, // BGR, Gate direction swapped - HX8357_SETPWR1, 6, - 0x00, // Not deep standby - 0x15, // BT - 0x1C, // VSPR - 0x1C, // VSNR - 0x83, // AP - 0xAA, // FS - HX8357D_SETSTBA, 6, - 0x50, // OPON normal - 0x50, // OPON idle - 0x01, // STBA - 0x3C, // STBA - 0x1E, // STBA - 0x08, // GEN - HX8357D_SETCYC, 7, - 0x02, // NW 0x02 - 0x40, // RTN - 0x00, // DIV - 0x2A, // DUM - 0x2A, // DUM - 0x0D, // GDON - 0x78, // GDOFF - HX8357D_SETGAMMA, 34, - 0x02, 0x0A, 0x11, 0x1d, 0x23, 0x35, 0x41, 0x4b, 0x4b, - 0x42, 0x3A, 0x27, 0x1B, 0x08, 0x09, 0x03, 0x02, 0x0A, - 0x11, 0x1d, 0x23, 0x35, 0x41, 0x4b, 0x4b, 0x42, 0x3A, - 0x27, 0x1B, 0x08, 0x09, 0x03, 0x00, 0x01, - HX8357_COLMOD, 1, - 0x57, // 0x55 = 16 bit, 0x57 = 24bit - HX8357_MADCTL, 1, - 0xC0, - HX8357_TEON, 1, - 0x00, // TW off - HX8357_TEARLINE, 2, - 0x00, 0x02, - HX8357_SLPOUT, 0x80 + 150/5, // Exit Sleep, then delay 150 ms - HX8357_DISPON, 0x80 + 50/5, // Main screen turn on, delay 50 ms - 0, // END OF COMMAND LIST - }; - -/********************** - * STATIC VARIABLES - **********************/ - -/********************** - * MACROS - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ -static uint8_t displayType = HX8357D; - -void hx8357_reset(gpio_num_t resetPin) { - if (resetPin != GPIO_NUM_NC) { - esp_rom_gpio_pad_select_gpio(resetPin); - gpio_set_direction(resetPin, GPIO_MODE_OUTPUT); - - //Reset the display - gpio_set_level(resetPin, 0); - vTaskDelay(10 / portTICK_PERIOD_MS); - gpio_set_level(resetPin, 1); - vTaskDelay(120 / portTICK_PERIOD_MS); - } -} - -void hx8357_init(gpio_num_t newDcPin) { - ESP_LOGI(TAG, "Initialization."); - - dcPin = newDcPin; - - //Initialize non-SPI GPIOs - esp_rom_gpio_pad_select_gpio(dcPin); - gpio_set_direction(dcPin, GPIO_MODE_OUTPUT); - - //Send all the commands - const uint8_t *addr = (displayType == HX8357B) ? initb : initd; - uint8_t cmd, x, numArgs; - while((cmd = *addr++) > 0) { // '0' command ends list - x = *addr++; - numArgs = x & 0x7F; - if (cmd != 0xFF) { // '255' is ignored - if (x & 0x80) { // If high bit set, numArgs is a delay time - hx8357_send_cmd(cmd); - } else { - hx8357_send_cmd(cmd); - hx8357_send_data((void *) addr, numArgs); - addr += numArgs; - } - } - if (x & 0x80) { // If high bit set... - vTaskDelay(numArgs * 5 / portTICK_PERIOD_MS); // numArgs is actually a delay time (5ms units) - } - } - -#if HX8357_INVERT_COLORS - hx8357_send_cmd(HX8357_INVON); -#else - hx8357_send_cmd(HX8357_INVOFF); -#endif -} - -//(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map); -void hx8357_flush(lv_disp_t* drv, const lv_area_t * area, uint8_t * color_map) -{ - uint32_t size = lv_area_get_width(area) * lv_area_get_height(area); - - /* Column addresses */ - uint8_t xb[] = { - (uint8_t) (area->x1 >> 8) & 0xFF, - (uint8_t) (area->x1) & 0xFF, - (uint8_t) (area->x2 >> 8) & 0xFF, - (uint8_t) (area->x2) & 0xFF, - }; - - /* Page addresses */ - uint8_t yb[] = { - (uint8_t) (area->y1 >> 8) & 0xFF, - (uint8_t) (area->y1) & 0xFF, - (uint8_t) (area->y2 >> 8) & 0xFF, - (uint8_t) (area->y2) & 0xFF, - }; - - /*Column addresses*/ - hx8357_send_cmd(HX8357_CASET); - hx8357_send_data(xb, 4); - - /*Page addresses*/ - hx8357_send_cmd(HX8357_PASET); - hx8357_send_data(yb, 4); - - /*Memory write*/ - hx8357_send_cmd(HX8357_RAMWR); - hx8357_send_color((void*)color_map, size * (LV_COLOR_DEPTH / 8)); -} - -void hx8357_set_madctl(uint8_t value) { - hx8357_send_cmd(HX8357_MADCTL); - hx8357_send_data(&value, 1); -} -/********************** - * STATIC FUNCTIONS - **********************/ - - -static void hx8357_send_cmd(uint8_t cmd) -{ - disp_wait_for_pending_transactions(); - gpio_set_level(dcPin, 0); /*Command mode*/ - disp_spi_send_data(&cmd, 1); -} - - -static void hx8357_send_data(void * data, uint16_t length) -{ - disp_wait_for_pending_transactions(); - gpio_set_level(dcPin, 1); /*Data mode*/ - disp_spi_send_data(data, length); -} - - -static void hx8357_send_color(void * data, uint16_t length) -{ - disp_wait_for_pending_transactions(); - gpio_set_level(dcPin, 1); /*Data mode*/ - disp_spi_send_colors(data, length); -} - -uint8_t hx8357d_get_gamma_curve_count() { - return 4; -} - -void hx8357d_set_gamme_curve(uint8_t index) { - uint8_t curve = 1; - switch (index) { - case 0: - curve = 0x01; - break; - case 1: - curve = 0x02; - break; - case 2: - curve = 0x04; - break; - case 3: - curve = 0x08; - break; - } - hx8357_send_cmd(HX8357D_SETGAMMA_BY_ID); - hx8357_send_data(&curve, 1); -} diff --git a/Devices/unphone/Source/hx8357/hx8357.h b/Devices/unphone/Source/hx8357/hx8357.h deleted file mode 100644 index 1a4d11edf..000000000 --- a/Devices/unphone/Source/hx8357/hx8357.h +++ /dev/null @@ -1,133 +0,0 @@ -/** - * @file hx8357.h - * - * Roughly based on the Adafruit_HX8357_Library - * - * This library should work with: - * Adafruit 3.5" TFT 320x480 + Touchscreen Breakout - * http://www.adafruit.com/products/2050 - * - * Adafruit TFT FeatherWing - 3.5" 480x320 Touchscreen for Feathers - * https://www.adafruit.com/product/3651 - * - * Datasheet: - * https://cdn-shop.adafruit.com/datasheets/HX8357-D_DS_April2012.pdf - */ - -#ifndef HX8357_H -#define HX8357_H - -#ifdef __cplusplus -extern "C" { -#endif - -/********************* - * INCLUDES - *********************/ -#include -#include - -#include -#include - -#define HX8357D 0xD ///< Our internal const for D type -#define HX8357B 0xB ///< Our internal const for B type - -#define HX8357_TFTWIDTH 320 ///< 320 pixels wide -#define HX8357_TFTHEIGHT 480 ///< 480 pixels tall - -#define HX8357_NOP 0x00 ///< No op -#define HX8357_SWRESET 0x01 ///< software reset -#define HX8357_RDDID 0x04 ///< Read ID -#define HX8357_RDDST 0x09 ///< (unknown) - -#define HX8357_RDPOWMODE 0x0A ///< Read power mode Read power mode -#define HX8357_RDMADCTL 0x0B ///< Read MADCTL -#define HX8357_RDCOLMOD 0x0C ///< Column entry mode -#define HX8357_RDDIM 0x0D ///< Read display image mode -#define HX8357_RDDSDR 0x0F ///< Read dosplay signal mode - -#define HX8357_SLPIN 0x10 ///< Enter sleep mode -#define HX8357_SLPOUT 0x11 ///< Exit sleep mode -#define HX8357B_PTLON 0x12 ///< Partial mode on -#define HX8357B_NORON 0x13 ///< Normal mode - -#define HX8357_INVOFF 0x20 ///< Turn off invert -#define HX8357_INVON 0x21 ///< Turn on invert -#define HX8357_DISPOFF 0x28 ///< Display on -#define HX8357_DISPON 0x29 ///< Display off - -#define HX8357_CASET 0x2A ///< Column addr set -#define HX8357_PASET 0x2B ///< Page addr set -#define HX8357_RAMWR 0x2C ///< Write VRAM -#define HX8357_RAMRD 0x2E ///< Read VRAm - -#define HX8357B_PTLAR 0x30 ///< (unknown) -#define HX8357_TEON 0x35 ///< Tear enable on -#define HX8357_TEARLINE 0x44 ///< (unknown) -#define HX8357_MADCTL 0x36 ///< Memory access control -#define HX8357_COLMOD 0x3A ///< Color mode - -#define HX8357_SETOSC 0xB0 ///< Set oscillator -#define HX8357_SETPWR1 0xB1 ///< Set power control -#define HX8357B_SETDISPLAY 0xB2 ///< Set display mode -#define HX8357_SETRGB 0xB3 ///< Set RGB interface -#define HX8357D_SETCOM 0xB6 ///< Set VCOM voltage - -#define HX8357B_SETDISPMODE 0xB4 ///< Set display mode -#define HX8357D_SETCYC 0xB4 ///< Set display cycle reg -#define HX8357B_SETOTP 0xB7 ///< Set OTP memory -#define HX8357D_SETC 0xB9 ///< Enable extension command - -#define HX8357B_SET_PANEL_DRIVING 0xC0 ///< Set panel drive mode -#define HX8357D_SETSTBA 0xC0 ///< Set source option -#define HX8357B_SETDGC 0xC1 ///< Set DGC settings -#define HX8357B_SETID 0xC3 ///< Set ID -#define HX8357B_SETDDB 0xC4 ///< Set DDB -#define HX8357B_SETDISPLAYFRAME 0xC5 ///< Set display frame -#define HX8357B_GAMMASET 0xC8 ///< Set Gamma correction -#define HX8357B_SETCABC 0xC9 ///< Set CABC -#define HX8357_SETPANEL 0xCC ///< Set Panel - -#define HX8357B_SETPOWER 0xD0 ///< Set power control -#define HX8357B_SETVCOM 0xD1 ///< Set VCOM -#define HX8357B_SETPWRNORMAL 0xD2 ///< Set power normal - -#define HX8357B_RDID1 0xDA ///< Read ID #1 -#define HX8357B_RDID2 0xDB ///< Read ID #2 -#define HX8357B_RDID3 0xDC ///< Read ID #3 -#define HX8357B_RDID4 0xDD ///< Read ID #4 - -#define HX8357D_SETGAMMA 0xE0 ///< Set Gamma curve data -#define HX8357D_SETGAMMA_BY_ID 0x26 ///< Set Gamma curve by curve identifier (0x01, 0x02, 0x04, 0x08) -#define HX8357B_SETGAMMA 0xC8 ///< Set Gamma -#define HX8357B_SETPANELRELATED 0xE9 ///< Set panel related - -// MADCTL -// See datasheet page 123: https://cdn-shop.adafruit.com/datasheets/HX8357-D_DS_April2012.pdf -#define MADCTL_BIT_INDEX_COMMON_OUTPUTS_RAM 0 // N/A - set to 0 -#define MADCTL_BIT_INDEX_SEGMENT_OUTPUTS_RAM 1 // N/A - set to 0 -#define MADCTL_BIT_INDEX_DATA_LATCH_ORDER 2 // 0 = left-to-right refresh, 1 = right-to-left -#define MADCTL_BIT_INDEX_RGB_BGR_ORDER 3 // 0 = RGB, 1 = BGR -#define MADCTL_BIT_INDEX_LINE_ADDRESS_ORDER 4 // 0 = top-to-bottom refresh, 1 = bottom-to-top -#define MADCTL_BIT_INDEX_PAGE_COLUMN_ORDER 5 // 0 = normal, 1 = reverse -#define MADCTL_BIT_INDEX_COLUMN_ADDRESS_ORDER 6 // 0 = left-to-right, 1 = right-to-left -#define MADCTL_BIT_INDEX_PAGE_ADDRESS_ORDER 7 // 0 = top-to-bottom, 1 = bottom-to-top - -void hx8357_reset(gpio_num_t resetPin); -void hx8357_init(gpio_num_t dcPin); -void hx8357_set_madctl(uint8_t value); -void hx8357_flush(lv_disp_t* drv, const lv_area_t* area, uint8_t* color_map); - -uint8_t hx8357d_get_gamma_curve_count(); -/** - * Note: this doesn't work, even though the manual says it should - * Page 141: https://cdn-shop.adafruit.com/datasheets/HX8357-D_DS_April2012.pdf - */ -void hx8357d_set_gamme_curve(uint8_t index); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /*HX8357_H*/ diff --git a/Devices/unphone/Source/lvgl_spi_conf.h b/Devices/unphone/Source/lvgl_spi_conf.h deleted file mode 100644 index bde9214c3..000000000 --- a/Devices/unphone/Source/lvgl_spi_conf.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#define SPI_TFT_CLOCK_SPEED_HZ (26*1000*1000) -#define SPI_TFT_SPI_MODE (0) -#define DISP_SPI_CS GPIO_NUM_48 -#define DISP_SPI_INPUT_DELAY_NS 0 - diff --git a/Devices/unphone/devicetree.yaml b/Devices/unphone/devicetree.yaml index c2902c36a..1185f608a 100644 --- a/Devices/unphone/devicetree.yaml +++ b/Devices/unphone/devicetree.yaml @@ -1,3 +1,5 @@ dependencies: - Platforms/platform-esp32 + - Drivers/hx8357-module + - Drivers/xpt2046-module dts: unphone.dts diff --git a/Devices/unphone/unphone.dts b/Devices/unphone/unphone.dts index da2c1f737..892d68885 100644 --- a/Devices/unphone/unphone.dts +++ b/Devices/unphone/unphone.dts @@ -7,8 +7,8 @@ #include #include #include -#include -#include +#include +#include / { compatible = "root"; @@ -37,7 +37,7 @@ pin-scl = <&gpio0 4 GPIO_FLAG_NONE>; }; - sdcard_spi: spi0 { + spi0 { compatible = "espressif,esp32-spi"; host = ; cs-gpios = <&gpio0 48 GPIO_FLAG_NONE>, // Display @@ -49,11 +49,19 @@ max-transfer-size = <65536>; display@0 { - compatible = "display-placeholder"; + compatible = "himax,hx8357"; + horizontal-resolution = <320>; + vertical-resolution = <480>; + mirror-x; + pixel-clock-hz = <26000000>; + pin-dc = <&gpio0 47 GPIO_FLAG_NONE>; + pin-reset = <&gpio0 46 GPIO_FLAG_NONE>; }; touch@1 { - compatible = "pointer-placeholder"; + compatible = "xptek,xpt2046"; + x-max = <320>; + y-max = <480>; }; sdcard@2 { diff --git a/Drivers/XPT2046/CMakeLists.txt b/Drivers/XPT2046/CMakeLists.txt deleted file mode 100644 index 3d442aa9a..000000000 --- a/Drivers/XPT2046/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility EspLcdCompat esp_lcd_touch_xpt2046 -) diff --git a/Drivers/XPT2046/README.md b/Drivers/XPT2046/README.md deleted file mode 100644 index c822faa17..000000000 --- a/Drivers/XPT2046/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# XPT2046 - -A basic XPT2046 touch driver. diff --git a/Drivers/XPT2046/Source/Xpt2046Touch.cpp b/Drivers/XPT2046/Source/Xpt2046Touch.cpp deleted file mode 100644 index 4c39a63cb..000000000 --- a/Drivers/XPT2046/Source/Xpt2046Touch.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "Xpt2046Touch.h" - -#include - -#include -#include - -bool Xpt2046Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { - const esp_lcd_panel_io_spi_config_t io_config = ESP_LCD_TOUCH_IO_SPI_XPT2046_CONFIG(configuration->spiPinCs); - return esp_lcd_new_panel_io_spi(configuration->spiDevice, &io_config, &outHandle) == ESP_OK; -} - -bool Xpt2046Touch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& config, esp_lcd_touch_handle_t& panelHandle) { - return esp_lcd_touch_new_spi_xpt2046(ioHandle, &config, &panelHandle) == ESP_OK; -} - -esp_lcd_touch_config_t Xpt2046Touch::createEspLcdTouchConfig() { - return { - .x_max = configuration->xMax, - .y_max = configuration->yMax, - .rst_gpio_num = GPIO_NUM_NC, - .int_gpio_num = GPIO_NUM_NC, - .levels = { - .reset = 0, - .interrupt = 0, - }, - .flags = { - .swap_xy = configuration->swapXy, - .mirror_x = configuration->mirrorX, - .mirror_y = configuration->mirrorY, - }, - .process_coordinates = nullptr, - .interrupt_callback = nullptr, - .user_data = configuration.get(), - .driver_data = nullptr - }; -} diff --git a/Drivers/XPT2046/Source/Xpt2046Touch.h b/Drivers/XPT2046/Source/Xpt2046Touch.h deleted file mode 100644 index 975f4a279..000000000 --- a/Drivers/XPT2046/Source/Xpt2046Touch.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include - -#include - -class Xpt2046Touch : public EspLcdTouch { - -public: - - class Configuration { - public: - - Configuration( - esp_lcd_spi_bus_handle_t spiDevice, - gpio_num_t spiPinCs, - uint16_t xMax, - uint16_t yMax, - bool swapXy = false, - bool mirrorX = false, - bool mirrorY = false - ) : spiDevice(spiDevice), - spiPinCs(spiPinCs), - xMax(xMax), - yMax(yMax), - swapXy(swapXy), - mirrorX(mirrorX), - mirrorY(mirrorY) - {} - - esp_lcd_spi_bus_handle_t spiDevice; - gpio_num_t spiPinCs; - uint16_t xMax; - uint16_t yMax; - bool swapXy; - bool mirrorX; - bool mirrorY; - }; - -private: - - std::unique_ptr configuration; - - bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) override; - - bool createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) override; - - esp_lcd_touch_config_t createEspLcdTouchConfig() override; - -public: - - explicit Xpt2046Touch(std::unique_ptr inConfiguration) : configuration(std::move(inConfiguration)) { - assert(configuration != nullptr); - } - - std::string getName() const final { return "XPT2046"; } - - std::string getDescription() const final { return "XPT2046 SPI touch driver"; } -}; diff --git a/Drivers/hx8357-module/CMakeLists.txt b/Drivers/hx8357-module/CMakeLists.txt new file mode 100644 index 000000000..d81a6638d --- /dev/null +++ b/Drivers/hx8357-module/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.20) + +include("${CMAKE_CURRENT_LIST_DIR}/../../Buildscripts/module.cmake") + +file(GLOB_RECURSE SOURCE_FILES "source/*.c*") + +tactility_add_module(hx8357-module + SRCS ${SOURCE_FILES} + INCLUDE_DIRS include/ + REQUIRES TactilityKernel platform-esp32 driver +) diff --git a/Drivers/hx8357-module/LICENSE-Apache-2.0.md b/Drivers/hx8357-module/LICENSE-Apache-2.0.md new file mode 100644 index 000000000..f5f4b8b5e --- /dev/null +++ b/Drivers/hx8357-module/LICENSE-Apache-2.0.md @@ -0,0 +1,195 @@ +Apache License +============== + +_Version 2.0, January 2004_ +_<>_ + +### Terms and Conditions for use, reproduction, and distribution + +#### 1. Definitions + +“License” shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +“Licensor” shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +“Legal Entity” shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, “control” means **(i)** the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the +outstanding shares, or **(iii)** beneficial ownership of such entity. + +“You” (or “Your”) shall mean an individual or Legal Entity exercising +permissions granted by this License. + +“Source” form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +“Object” form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +“Work” shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +“Derivative Works” shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +“Contribution” shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +“submitted” means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as “Not a Contribution.” + +“Contributor” shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +#### 2. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +#### 3. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +#### 4. Redistribution + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +* **(a)** You must give any other recipients of the Work or Derivative Works a copy of +this License; and +* **(b)** You must cause any modified files to carry prominent notices stating that You +changed the files; and +* **(c)** You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +#### 5. Submission of Contributions + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +#### 6. Trademarks + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +#### 7. Disclaimer of Warranty + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +#### 8. Limitation of Liability + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +#### 9. Accepting Warranty or Additional Liability + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +_END OF TERMS AND CONDITIONS_ + +### APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets `[]` replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same “printed page” as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/Drivers/hx8357-module/README.md b/Drivers/hx8357-module/README.md new file mode 100644 index 000000000..b0fdc5586 --- /dev/null +++ b/Drivers/hx8357-module/README.md @@ -0,0 +1,7 @@ +# HX8357 Display Driver + +A kernel driver for the `HX8357-D` display panel (24bpp/RGB888). No ESP-IDF `esp_lcd` component exists for this controller, so this driver speaks its raw SPI command protocol directly rather than wrapping `esp_lcd_panel_io`/`esp_lcd_panel`. + +See https://cdn-shop.adafruit.com/datasheets/HX8357-D_DS_April2012.pdf + +License: [Apache v2.0](LICENSE-Apache-2.0.md) diff --git a/Drivers/hx8357-module/bindings/himax,hx8357.yaml b/Drivers/hx8357-module/bindings/himax,hx8357.yaml new file mode 100644 index 000000000..779297667 --- /dev/null +++ b/Drivers/hx8357-module/bindings/himax,hx8357.yaml @@ -0,0 +1,64 @@ +description: > + Himax HX8357-D display panel. 24bpp/RGB888 only. No ESP-IDF esp_lcd component exists for this + controller, so this driver speaks the panel's raw SPI command protocol directly (bit-banged + DC line, blocking spi_device_transmit()) rather than wrapping esp_lcd_panel_io/esp_lcd_panel. + No bgr-order property: unlike the RGB565 panels (ili9341-module, st7789-module), there is no + DISPLAY_COLOR_FORMAT_BGR888 in the kernel model, so a BGR-wired panel isn't representable here. + +compatible: "himax,hx8357" + +bus: spi + +properties: + horizontal-resolution: + type: int + required: true + description: Horizontal resolution in pixels + vertical-resolution: + type: int + required: true + description: Vertical resolution in pixels + gap-x: + type: int + default: 0 + description: X offset applied to all draw operations + gap-y: + type: int + default: 0 + description: Y offset applied to all draw operations + swap-xy: + type: boolean + default: false + description: Swap the X and Y axes + mirror-x: + type: boolean + default: true + description: Mirror the X axis + mirror-y: + type: boolean + default: false + description: Mirror the Y axis + invert-color: + type: boolean + default: false + description: Invert the panel's color output + pixel-clock-hz: + type: int + default: 26000000 + description: SPI pixel clock frequency in Hz + pin-dc: + type: phandles + required: true + description: Data/Command GPIO pin + pin-reset: + type: phandles + default: GPIO_PIN_SPEC_NONE + description: Reset GPIO pin + reset-active-high: + type: boolean + default: false + description: Whether the reset pin is active high + backlight: + type: phandle + default: "NULL" + description: Optional reference to this display's backlight device diff --git a/Drivers/hx8357-module/devicetree.yaml b/Drivers/hx8357-module/devicetree.yaml new file mode 100644 index 000000000..a07d6f334 --- /dev/null +++ b/Drivers/hx8357-module/devicetree.yaml @@ -0,0 +1,3 @@ +dependencies: + - TactilityKernel +bindings: bindings diff --git a/Drivers/hx8357-module/include/bindings/hx8357.h b/Drivers/hx8357-module/include/bindings/hx8357.h new file mode 100644 index 000000000..24171f08b --- /dev/null +++ b/Drivers/hx8357-module/include/bindings/hx8357.h @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include + +DEFINE_DEVICETREE(hx8357, struct Hx8357Config) diff --git a/Drivers/hx8357-module/include/drivers/hx8357.h b/Drivers/hx8357-module/include/drivers/hx8357.h new file mode 100644 index 000000000..ec2f8a132 --- /dev/null +++ b/Drivers/hx8357-module/include/drivers/hx8357.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include + +struct Hx8357Config { + uint16_t horizontal_resolution; + uint16_t vertical_resolution; + int32_t gap_x; + int32_t gap_y; + bool swap_xy; + bool mirror_x; + bool mirror_y; + bool invert_color; + uint32_t pixel_clock_hz; + struct GpioPinSpec pin_dc; + struct GpioPinSpec pin_reset; + bool reset_active_high; + // Optional reference to this display's backlight device, NULL if none. + struct Device* backlight; +}; + +#ifdef __cplusplus +} +#endif diff --git a/Drivers/hx8357-module/include/hx8357_module.h b/Drivers/hx8357-module/include/hx8357_module.h new file mode 100644 index 000000000..5239f8aa4 --- /dev/null +++ b/Drivers/hx8357-module/include/hx8357_module.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct Module hx8357_module; + +#ifdef __cplusplus +} +#endif diff --git a/Drivers/hx8357-module/source/hx8357.cpp b/Drivers/hx8357-module/source/hx8357.cpp new file mode 100644 index 000000000..8d7adc267 --- /dev/null +++ b/Drivers/hx8357-module/source/hx8357.cpp @@ -0,0 +1,462 @@ +// SPDX-License-Identifier: Apache-2.0 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#define TAG "HX8357" +#define GET_CONFIG(device) (static_cast((device)->config)) + +namespace { + +// HX8357-D command set (subset actually used). See +// https://cdn-shop.adafruit.com/datasheets/HX8357-D_DS_April2012.pdf +constexpr uint8_t HX8357_SWRESET = 0x01; +constexpr uint8_t HX8357_SLPOUT = 0x11; +constexpr uint8_t HX8357_INVOFF = 0x20; +constexpr uint8_t HX8357_INVON = 0x21; +constexpr uint8_t HX8357_DISPON = 0x29; +constexpr uint8_t HX8357_CASET = 0x2A; +constexpr uint8_t HX8357_PASET = 0x2B; +constexpr uint8_t HX8357_RAMWR = 0x2C; +constexpr uint8_t HX8357_MADCTL = 0x36; +constexpr uint8_t HX8357_COLMOD = 0x3A; +constexpr uint8_t HX8357_TEON = 0x35; +constexpr uint8_t HX8357_TEARLINE = 0x44; +constexpr uint8_t HX8357_SETOSC = 0xB0; +constexpr uint8_t HX8357_SETPWR1 = 0xB1; +constexpr uint8_t HX8357_SETRGB = 0xB3; +constexpr uint8_t HX8357D_SETCOM = 0xB6; +constexpr uint8_t HX8357D_SETCYC = 0xB4; +constexpr uint8_t HX8357D_SETC = 0xB9; +constexpr uint8_t HX8357D_SETSTBA = 0xC0; +constexpr uint8_t HX8357_SETPANEL = 0xCC; +constexpr uint8_t HX8357D_SETGAMMA = 0xE0; + +// MADCTL bit indices, see the datasheet page 123. +constexpr uint8_t MADCTL_BIT_PAGE_COLUMN_ORDER = 5; // swap-xy +constexpr uint8_t MADCTL_BIT_COLUMN_ADDRESS_ORDER = 6; // mirror-x +constexpr uint8_t MADCTL_BIT_PAGE_ADDRESS_ORDER = 7; // mirror-y + +// Bring-up command list, ported verbatim (byte-for-byte) from the deprecated HAL's hx8357.c +// initd[] table (Devices/unphone/Source/hx8357/hx8357.c) - the HX8357B variant (initb[]) is +// dead code there (displayType is hardcoded to HX8357D) and was not ported. Format matches the +// original: cmd, then a length byte (bit7 set means "this is actually a delay in 5ms units, no +// data follows"), then that many data bytes. A single 0 cmd byte ends the list. +constexpr uint8_t INIT_CMDS[] = { + HX8357_SWRESET, 0x80 + 100 / 5, // Soft reset, then delay 100 ms + HX8357D_SETC, 3, + 0xFF, 0x83, 0x57, + 0xFF, 0x80 + 500 / 5, // No command, just delay 500 ms + HX8357_SETRGB, 4, + 0x80, 0x00, 0x06, 0x06, // 0x80 enables SDO pin (0x00 disables) + HX8357D_SETCOM, 1, + 0x25, // -1.52V + HX8357_SETOSC, 1, + 0x68, // Normal mode 70Hz, Idle mode 55 Hz + HX8357_SETPANEL, 1, + 0x05, // BGR, Gate direction swapped + HX8357_SETPWR1, 6, + 0x00, // Not deep standby + 0x15, // BT + 0x1C, // VSPR + 0x1C, // VSNR + 0x83, // AP + 0xAA, // FS + HX8357D_SETSTBA, 6, + 0x50, // OPON normal + 0x50, // OPON idle + 0x01, // STBA + 0x3C, // STBA + 0x1E, // STBA + 0x08, // GEN + HX8357D_SETCYC, 7, + 0x02, // NW 0x02 + 0x40, // RTN + 0x00, // DIV + 0x2A, // DUM + 0x2A, // DUM + 0x0D, // GDON + 0x78, // GDOFF + HX8357D_SETGAMMA, 34, + 0x02, 0x0A, 0x11, 0x1d, 0x23, 0x35, 0x41, 0x4b, 0x4b, + 0x42, 0x3A, 0x27, 0x1B, 0x08, 0x09, 0x03, 0x02, 0x0A, + 0x11, 0x1d, 0x23, 0x35, 0x41, 0x4b, 0x4b, 0x42, 0x3A, + 0x27, 0x1B, 0x08, 0x09, 0x03, 0x00, 0x01, + HX8357_COLMOD, 1, + 0x57, // 24bpp + HX8357_MADCTL, 1, + 0xC0, + HX8357_TEON, 1, + 0x00, // TW off + HX8357_TEARLINE, 2, + 0x00, 0x02, + HX8357_SLPOUT, 0x80 + 150 / 5, // Exit sleep, then delay 150 ms + HX8357_DISPON, 0x80 + 50 / 5, // Main screen turn on, delay 50 ms + 0, // END OF COMMAND LIST +}; + +} // namespace + +struct Hx8357Internal { + spi_device_handle_t spi_handle; + gpio_num_t dc_pin; + size_t max_transfer_size; +}; + +static int pin_or_unused(const struct GpioPinSpec& pin) { + return pin.gpio_controller == nullptr ? -1 : static_cast(pin.pin); +} + +// region Bring-up protocol + +static void spi_send(spi_device_handle_t spi, const uint8_t* data, size_t length) { + if (length == 0) { + return; + } + spi_transaction_t transaction = {}; + transaction.length = length * 8; + if (length <= 4) { + transaction.flags = SPI_TRANS_USE_TXDATA; + memcpy(transaction.tx_data, data, length); + } else { + transaction.tx_buffer = data; + } + // Blocking: physically completes before returning, unlike esp_lcd_panel_draw_bitmap() - + // no semaphore/ISR-callback dance needed to honor DisplayApi's synchronous draw_bitmap contract. + spi_device_transmit(spi, &transaction); +} + +static void send_cmd(const Hx8357Internal* internal, uint8_t cmd) { + gpio_set_level(internal->dc_pin, 0); + spi_send(internal->spi_handle, &cmd, 1); +} + +// Chunked to stay under the SPI bus's max single-transaction transfer size (e.g. RAMWR pixel +// bursts can be hundreds of KB). CS toggles between chunks, which the panel tolerates: it only +// resets its RAMWR auto-increment pointer on a new command byte, not on CS deselect. +static void send_data(const Hx8357Internal* internal, const uint8_t* data, size_t length) { + gpio_set_level(internal->dc_pin, 1); + while (length > 0) { + size_t chunk = length < internal->max_transfer_size ? length : internal->max_transfer_size; + spi_send(internal->spi_handle, data, chunk); + data += chunk; + length -= chunk; + } +} + +static void run_init_cmds(const Hx8357Internal* internal) { + const uint8_t* addr = INIT_CMDS; + uint8_t cmd; + while ((cmd = *addr++) > 0) { // 0 command ends the list + uint8_t x = *addr++; + uint8_t num_args = x & 0x7F; + if (cmd != 0xFF) { // 0xFF is a no-op placeholder (only used to carry a delay) + send_cmd(internal, cmd); + if (!(x & 0x80)) { + send_data(internal, addr, num_args); + addr += num_args; + } + } + if (x & 0x80) { // high bit set: num_args is actually a delay, in 5ms units + vTaskDelay(pdMS_TO_TICKS(num_args * 5)); + } + } +} + +static void send_madctl(const Hx8357Internal* internal, const Hx8357Config* config) { + uint8_t madctl = 0; + if (config->swap_xy) madctl |= (1 << MADCTL_BIT_PAGE_COLUMN_ORDER); + if (config->mirror_x) madctl |= (1 << MADCTL_BIT_COLUMN_ADDRESS_ORDER); + if (config->mirror_y) madctl |= (1 << MADCTL_BIT_PAGE_ADDRESS_ORDER); + send_cmd(internal, HX8357_MADCTL); + send_data(internal, &madctl, 1); +} + +// endregion + +// region Driver lifecycle + +static error_t start(Device* device) { + auto* parent = device_get_parent(device); + check(device_get_type(parent) == &SPI_CONTROLLER_TYPE); + + const auto* spi_config = static_cast(parent->config); + const auto* config = GET_CONFIG(device); + + struct GpioPinSpec cs_pin; + if (esp32_spi_get_cs_pin(device, &cs_pin) != ERROR_NONE) { + LOG_E(TAG, "Failed to resolve CS pin"); + return ERROR_RESOURCE; + } + + auto* internal = static_cast(malloc(sizeof(Hx8357Internal))); + if (internal == nullptr) { + return ERROR_OUT_OF_MEMORY; + } + + internal->dc_pin = static_cast(pin_or_unused(config->pin_dc)); + // Clamped below the bus's configured max_transfer_size: that value only bounds the DMA + // buffer/descriptor allocation, not the SPI peripheral's own per-transaction bit-length + // register (e.g. 18 bits = 32768 bytes on ESP32-S3, 24 bits elsewhere) - so a large + // max-transfer-size in the devicetree (sized for e.g. an SD card) can still overflow a + // single spi_device_transmit() here. 4096 is safely under that hardware limit on every + // ESP32 variant. + constexpr size_t MAX_SPI_CHUNK_SIZE = 4096; + internal->max_transfer_size = (spi_config->max_transfer_size > 0 && static_cast(spi_config->max_transfer_size) < MAX_SPI_CHUNK_SIZE) + ? static_cast(spi_config->max_transfer_size) + : MAX_SPI_CHUNK_SIZE; + gpio_config_t dc_config = { + .pin_bit_mask = 1ULL << internal->dc_pin, + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + if (gpio_config(&dc_config) != ESP_OK) { + LOG_E(TAG, "Failed to configure DC pin"); + free(internal); + return ERROR_RESOURCE; + } + + spi_device_interface_config_t device_config = { + .mode = 0, + .clock_speed_hz = static_cast(config->pixel_clock_hz), + .spics_io_num = pin_or_unused(cs_pin), + .flags = 0, + .queue_size = 1, + }; + + if (spi_bus_add_device((spi_host_device_t)spi_config->host, &device_config, &internal->spi_handle) != ESP_OK) { + LOG_E(TAG, "Failed to add SPI device"); + free(internal); + return ERROR_RESOURCE; + } + + // Hardware reset, in addition to the SWRESET the bring-up command list sends - matches the + // deprecated HAL's Hx8357Display::start(), which did both. + int reset_pin = pin_or_unused(config->pin_reset); + if (reset_pin != -1) { + gpio_config_t reset_config = { + .pin_bit_mask = 1ULL << reset_pin, + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + if (gpio_config(&reset_config) != ESP_OK) { + LOG_E(TAG, "Failed to configure reset pin"); + spi_bus_remove_device(internal->spi_handle); + free(internal); + return ERROR_RESOURCE; + } + gpio_set_level(static_cast(reset_pin), config->reset_active_high ? 1 : 0); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(static_cast(reset_pin), config->reset_active_high ? 0 : 1); + vTaskDelay(pdMS_TO_TICKS(120)); + } + + run_init_cmds(internal); + send_madctl(internal, config); + send_cmd(internal, config->invert_color ? HX8357_INVON : HX8357_INVOFF); + + device_set_driver_data(device, internal); + return ERROR_NONE; +} + +static error_t stop(Device* device) { + auto* internal = static_cast(device_get_driver_data(device)); + + if (spi_bus_remove_device(internal->spi_handle) != ESP_OK) { + LOG_E(TAG, "Failed to remove SPI device"); + free(internal); + return ERROR_RESOURCE; + } + + free(internal); + return ERROR_NONE; +} + +// endregion + +// region DisplayApi + +static error_t hx8357_reset(Device*) { + // No standalone reset beyond what start_device() already does; matches the deprecated HAL + // (its reset() DisplayDevice override was never wired to anything beyond initial bring-up). + return ERROR_NONE; +} + +static error_t hx8357_init(Device*) { + return ERROR_NONE; +} + +static error_t hx8357_draw_bitmap(Device* device, int32_t x_start, int32_t y_start, int32_t x_end, int32_t y_end, const void* color_data) { + auto* internal = static_cast(device_get_driver_data(device)); + const auto* config = GET_CONFIG(device); + + // x_end/y_end are exclusive (see lvgl_display.c); CASET/PASET want an inclusive last pixel. + const int32_t x1 = x_start + config->gap_x; + const int32_t x2 = x_end + config->gap_x - 1; + const int32_t y1 = y_start + config->gap_y; + const int32_t y2 = y_end + config->gap_y - 1; + + const uint8_t xb[4] = { + static_cast((x1 >> 8) & 0xFF), static_cast(x1 & 0xFF), + static_cast((x2 >> 8) & 0xFF), static_cast(x2 & 0xFF), + }; + const uint8_t yb[4] = { + static_cast((y1 >> 8) & 0xFF), static_cast(y1 & 0xFF), + static_cast((y2 >> 8) & 0xFF), static_cast(y2 & 0xFF), + }; + + send_cmd(internal, HX8357_CASET); + send_data(internal, xb, 4); + send_cmd(internal, HX8357_PASET); + send_data(internal, yb, 4); + send_cmd(internal, HX8357_RAMWR); + + const size_t pixel_count = static_cast(x_end - x_start) * static_cast(y_end - y_start); + send_data(internal, static_cast(color_data), pixel_count * 3); // RGB888 = 3 bytes/pixel + + return ERROR_NONE; +} + +static error_t hx8357_mirror(Device* device, bool x_axis, bool y_axis) { + auto* internal = static_cast(device_get_driver_data(device)); + auto* config = GET_CONFIG(device); + // Reads back through the same config the panel was started with; swap_xy/gap are unaffected. + Hx8357Config effective = *config; + effective.mirror_x = x_axis; + effective.mirror_y = y_axis; + send_madctl(internal, &effective); + return ERROR_NONE; +} + +static error_t hx8357_swap_xy(Device* device, bool swap_axes) { + auto* internal = static_cast(device_get_driver_data(device)); + auto* config = GET_CONFIG(device); + Hx8357Config effective = *config; + effective.swap_xy = swap_axes; + send_madctl(internal, &effective); + return ERROR_NONE; +} + +// Reads the devicetree-configured baseline, not live hardware state - same convention as +// ili9341-module/st7789-module: mirror()/swap_xy() calls after start_device() don't change +// what "rotation 0" means here. +static bool hx8357_get_swap_xy(Device* device) { + return GET_CONFIG(device)->swap_xy; +} + +static bool hx8357_get_mirror_x(Device* device) { + return GET_CONFIG(device)->mirror_x; +} + +static bool hx8357_get_mirror_y(Device* device) { + return GET_CONFIG(device)->mirror_y; +} + +static error_t hx8357_set_gap(Device*, int32_t, int32_t) { + // Not supported by this controller's fixed CASET/PASET-per-draw protocol beyond the static + // gap-x/gap-y devicetree config already folded into draw_bitmap(); matches the deprecated + // HAL, which never exposed a runtime gap either. + return ERROR_NOT_SUPPORTED; +} + +static error_t hx8357_invert_color(Device* device, bool invert_color_data) { + auto* internal = static_cast(device_get_driver_data(device)); + send_cmd(internal, invert_color_data ? HX8357_INVON : HX8357_INVOFF); + return ERROR_NONE; +} + +static error_t hx8357_disp_on_off(Device* device, bool on_off) { + auto* internal = static_cast(device_get_driver_data(device)); + send_cmd(internal, on_off ? HX8357_DISPON : 0x28 /* HX8357_DISPOFF */); + return ERROR_NONE; +} + +static error_t hx8357_disp_sleep(Device* device, bool sleep) { + auto* internal = static_cast(device_get_driver_data(device)); + send_cmd(internal, sleep ? 0x10 /* HX8357_SLPIN */ : HX8357_SLPOUT); + return ERROR_NONE; +} + +static enum DisplayColorFormat hx8357_get_color_format(Device*) { + return DISPLAY_COLOR_FORMAT_RGB888; +} + +static uint16_t hx8357_get_resolution_x(Device* device) { + return GET_CONFIG(device)->horizontal_resolution; +} + +static uint16_t hx8357_get_resolution_y(Device* device) { + return GET_CONFIG(device)->vertical_resolution; +} + +static void hx8357_get_frame_buffer(Device*, uint8_t, void** out_buffer) { + *out_buffer = nullptr; +} + +static uint8_t hx8357_get_frame_buffer_count(Device*) { + return 0; +} + +static error_t hx8357_get_backlight(Device* device, Device** backlight) { + auto* configured_backlight = GET_CONFIG(device)->backlight; + if (configured_backlight == nullptr) { + return ERROR_NOT_SUPPORTED; + } + *backlight = configured_backlight; + return ERROR_NONE; +} + +// endregion + +static const DisplayApi hx8357_display_api = { + .reset = hx8357_reset, + .init = hx8357_init, + .draw_bitmap = hx8357_draw_bitmap, + .mirror = hx8357_mirror, + .swap_xy = hx8357_swap_xy, + .get_swap_xy = hx8357_get_swap_xy, + .get_mirror_x = hx8357_get_mirror_x, + .get_mirror_y = hx8357_get_mirror_y, + .set_gap = hx8357_set_gap, + .invert_color = hx8357_invert_color, + .disp_on_off = hx8357_disp_on_off, + .disp_sleep = hx8357_disp_sleep, + .get_color_format = hx8357_get_color_format, + .get_resolution_x = hx8357_get_resolution_x, + .get_resolution_y = hx8357_get_resolution_y, + .get_frame_buffer = hx8357_get_frame_buffer, + .get_frame_buffer_count = hx8357_get_frame_buffer_count, + .get_backlight = hx8357_get_backlight, +}; + +Driver hx8357_driver = { + .name = "hx8357", + .compatible = (const char*[]) { "himax,hx8357", nullptr }, + .start_device = start, + .stop_device = stop, + .api = &hx8357_display_api, + .device_type = &DISPLAY_TYPE, + .owner = &hx8357_module, + .internal = nullptr +}; diff --git a/Drivers/hx8357-module/source/module.cpp b/Drivers/hx8357-module/source/module.cpp new file mode 100644 index 000000000..538243a77 --- /dev/null +++ b/Drivers/hx8357-module/source/module.cpp @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 +#include +#include +#include + +extern "C" { + +extern Driver hx8357_driver; + +static error_t start() { + /* We crash when construct fails, because if a single driver fails to construct, + * there is no guarantee that the previously constructed drivers can be destroyed */ + check(driver_construct_add(&hx8357_driver) == ERROR_NONE); + return ERROR_NONE; +} + +static error_t stop() { + /* We crash when destruct fails, because if a single driver fails to destruct, + * there is no guarantee that the previously destroyed drivers can be recovered */ + check(driver_remove_destruct(&hx8357_driver) == ERROR_NONE); + return ERROR_NONE; +} + +Module hx8357_module = { + .name = "hx8357", + .start = start, + .stop = stop, + .symbols = nullptr, + .internal = nullptr +}; + +} // extern "C" diff --git a/Drivers/ili9341-module/bindings/ilitek,ili9341.yaml b/Drivers/ili9341-module/bindings/ilitek,ili9341.yaml index 0d805b82b..38bc1ced1 100644 --- a/Drivers/ili9341-module/bindings/ilitek,ili9341.yaml +++ b/Drivers/ili9341-module/bindings/ilitek,ili9341.yaml @@ -71,5 +71,5 @@ properties: description: Whether the reset pin is active high backlight: type: phandle - default: NULL + default: "NULL" description: Optional reference to this display's backlight device diff --git a/Drivers/ili9488-module/bindings/ilitek,ili9488.yaml b/Drivers/ili9488-module/bindings/ilitek,ili9488.yaml index 797390bfd..cba33f327 100644 --- a/Drivers/ili9488-module/bindings/ilitek,ili9488.yaml +++ b/Drivers/ili9488-module/bindings/ilitek,ili9488.yaml @@ -71,5 +71,5 @@ properties: description: Whether the reset pin is active high backlight: type: phandle - default: NULL + default: "NULL" description: Optional reference to this display's backlight device diff --git a/Drivers/st7789-i8080-module/bindings/sitronix,st7789-i8080.yaml b/Drivers/st7789-i8080-module/bindings/sitronix,st7789-i8080.yaml index fcc0e4585..15305bac7 100644 --- a/Drivers/st7789-i8080-module/bindings/sitronix,st7789-i8080.yaml +++ b/Drivers/st7789-i8080-module/bindings/sitronix,st7789-i8080.yaml @@ -63,5 +63,5 @@ properties: description: Whether the reset pin is active high backlight: type: phandle - default: NULL + default: "NULL" description: Optional reference to this display's backlight device diff --git a/Drivers/st7789-module/bindings/sitronix,st7789.yaml b/Drivers/st7789-module/bindings/sitronix,st7789.yaml index a3f0d43ab..39c5a33db 100644 --- a/Drivers/st7789-module/bindings/sitronix,st7789.yaml +++ b/Drivers/st7789-module/bindings/sitronix,st7789.yaml @@ -71,5 +71,5 @@ properties: description: Whether the reset pin is active high backlight: type: phandle - default: NULL + default: "NULL" description: Optional reference to this display's backlight device