File locking refactored (#631)

Remove FileMutex and wire locking into driver subsystems.
This commit is contained in:
Ken Van Hoeylandt
2026-08-28 01:06:53 +02:00
committed by GitHub
parent 92ca046681
commit 020aa471e2
44 changed files with 766 additions and 993 deletions
@@ -27,6 +27,7 @@ constexpr auto* TAG = "esp_epaper";
constexpr uint16_t MAX_PANEL_DIMENSION = 2048;
struct EspEpaperInternal {
Device* spi_controller;
/** Opaque esp_epaper device, owns the panel's pins and SPI device. */
epd_handle_t epd;
epd_panel_info_t info;
@@ -77,7 +78,9 @@ static error_t esp_epaper_reset(Device* device) {
auto* internal = static_cast<EspEpaperInternal*>(device_get_driver_data(device));
xSemaphoreTake(internal->panel_mutex, portMAX_DELAY);
// epd_wake() toggles the reset pin and re-runs the full init sequence.
spi_controller_lock(internal->spi_controller);
const esp_err_t ret = epd_wake(internal->epd);
spi_controller_unlock(internal->spi_controller);
// epd_wake() re-inits the panel, so it is awake (and drawable) again.
if (ret == ESP_OK) {
internal->display_on = true;
@@ -89,7 +92,9 @@ static error_t esp_epaper_reset(Device* device) {
static error_t esp_epaper_init(Device* device) {
auto* internal = static_cast<EspEpaperInternal*>(device_get_driver_data(device));
xSemaphoreTake(internal->panel_mutex, portMAX_DELAY);
spi_controller_lock(internal->spi_controller);
const esp_err_t ret = epd_wake(internal->epd);
spi_controller_unlock(internal->spi_controller);
if (ret == ESP_OK) {
internal->display_on = true;
}
@@ -133,7 +138,9 @@ static error_t esp_epaper_draw_bitmap(Device* device, int32_t x_start, int32_t y
source = internal->rotate_buffer;
}
spi_controller_lock(internal->spi_controller);
const esp_err_t ret = epd_update(internal->epd, source, config->update_mode);
spi_controller_unlock(internal->spi_controller);
xSemaphoreGive(internal->panel_mutex);
if (ret != ESP_OK) {
LOG_E(TAG, "epd_update failed: %s", esp_err_to_name(ret));
@@ -152,6 +159,7 @@ static error_t esp_epaper_disp_on_off(Device* device, bool on_off) {
}
bool ok = true;
spi_controller_lock(internal->spi_controller);
if (on_off) {
if (epd_wake(internal->epd) != ESP_OK) {
LOG_E(TAG, "epd_wake failed");
@@ -163,6 +171,7 @@ static error_t esp_epaper_disp_on_off(Device* device, bool on_off) {
ok = false;
}
}
spi_controller_unlock(internal->spi_controller);
if (ok) {
internal->display_on = on_off;
@@ -220,7 +229,9 @@ static const DisplayApi esp_epaper_display_api = {
static void free_internal(EspEpaperInternal* internal) {
if (internal->epd != nullptr) {
spi_controller_lock(internal->spi_controller);
epd_deinit(internal->epd);
spi_controller_unlock(internal->spi_controller);
}
if (internal->rotate_buffer != nullptr) {
free(internal->rotate_buffer);
@@ -284,6 +295,7 @@ static error_t start(Device* device) {
return ERROR_OUT_OF_MEMORY;
}
internal->spi_controller = parent;
internal->epd = epd;
internal->panel_mutex = xSemaphoreCreateMutex();
if (internal->panel_mutex == nullptr) {
@@ -331,7 +343,9 @@ static error_t stop(Device* device) {
xSemaphoreTake(internal->panel_mutex, portMAX_DELAY);
if (internal->display_on) {
// Leave the panel in deep sleep.
spi_controller_lock(internal->spi_controller);
epd_sleep(internal->epd);
spi_controller_unlock(internal->spi_controller);
internal->display_on = false;
}
xSemaphoreGive(internal->panel_mutex);
+48 -9
View File
@@ -25,6 +25,7 @@ constexpr auto* TAG = "GC9A01";
#define GET_CONFIG(device) (static_cast<const Gc9a01Config*>((device)->config))
struct Gc9a01Internal {
Device* spi_controller;
esp_lcd_panel_io_handle_t io_handle;
esp_lcd_panel_handle_t panel_handle;
// See st7796-module's identical field for why this exists: draw_bitmap() must block until
@@ -63,6 +64,7 @@ static error_t start(Device* device) {
return ERROR_OUT_OF_MEMORY;
}
internal->spi_controller = parent;
internal->draw_done_semaphore = xSemaphoreCreateBinary();
if (internal->draw_done_semaphore == nullptr) {
free(internal);
@@ -123,6 +125,7 @@ static error_t start(Device* device) {
// Bring-up sequence, order matches the deprecated HAL's Gc9a01Display (proven correct on real
// Waveshare S3 Touch LCD 1.28 hardware). Every failure path below must clean up fully: unlike
// stop_device, this is never retried by the kernel if start_device fails.
spi_controller_lock(internal->spi_controller);
bool ok =
esp_lcd_panel_reset(internal->panel_handle) == ESP_OK &&
esp_lcd_panel_init(internal->panel_handle) == ESP_OK;
@@ -134,6 +137,7 @@ static error_t start(Device* device) {
ok = ok && ((!config->mirror_x && !config->mirror_y) || esp_lcd_panel_mirror(internal->panel_handle, config->mirror_x, config->mirror_y) == ESP_OK);
ok = ok && (!config->invert_color || esp_lcd_panel_invert_color(internal->panel_handle, true) == ESP_OK);
ok = ok && esp_lcd_panel_disp_on_off(internal->panel_handle, true) == ESP_OK;
spi_controller_unlock(internal->spi_controller);
if (!ok) {
LOG_E(TAG, "Failed to bring up panel");
@@ -151,9 +155,11 @@ static error_t start(Device* device) {
static error_t stop(Device* device) {
auto* internal = static_cast<Gc9a01Internal*>(device_get_driver_data(device));
spi_controller_lock(internal->spi_controller);
if (internal->panel_handle != nullptr) {
if (esp_lcd_panel_del(internal->panel_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->panel_handle = nullptr;
@@ -162,10 +168,12 @@ static error_t stop(Device* device) {
if (internal->io_handle != nullptr) {
if (esp_lcd_panel_io_del(internal->io_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel IO");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->io_handle = nullptr;
}
spi_controller_unlock(internal->spi_controller);
vSemaphoreDelete(internal->draw_done_semaphore);
free(internal);
@@ -179,12 +187,18 @@ static error_t stop(Device* device) {
static error_t gc9a01_reset(Device* device) {
auto* internal = static_cast<Gc9a01Internal*>(device_get_driver_data(device));
return esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t gc9a01_init(Device* device) {
auto* internal = static_cast<Gc9a01Internal*>(device_get_driver_data(device));
return esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t gc9a01_draw_bitmap(Device* device, int32_t x_start, int32_t y_start, int32_t x_end, int32_t y_end, const void* color_data) {
@@ -192,22 +206,35 @@ static error_t gc9a01_draw_bitmap(Device* device, int32_t x_start, int32_t y_sta
xSemaphoreTake(internal->draw_done_semaphore, 0);
if (esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data) != ESP_OK) {
spi_controller_lock(internal->spi_controller);
esp_err_t ret = esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data);
if (ret != ESP_OK) {
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
// Hold the bus lock across the wait too, not just the queueing call: the command/data phases
// aren't wrapped in their own acquire_bus by esp_lcd_panel_io_spi, so another bus user could
// otherwise interleave with this transfer while it's still in flight.
xSemaphoreTake(internal->draw_done_semaphore, portMAX_DELAY);
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
static error_t gc9a01_mirror(Device* device, bool x_axis, bool y_axis) {
auto* internal = static_cast<Gc9a01Internal*>(device_get_driver_data(device));
return esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t gc9a01_swap_xy(Device* device, bool swap_axes) {
auto* internal = static_cast<Gc9a01Internal*>(device_get_driver_data(device));
return esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static bool gc9a01_get_swap_xy(Device* device) {
@@ -224,7 +251,10 @@ static bool gc9a01_get_mirror_y(Device* device) {
static error_t gc9a01_set_gap(Device* device, int32_t x_gap, int32_t y_gap) {
auto* internal = static_cast<Gc9a01Internal*>(device_get_driver_data(device));
return esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static int32_t gc9a01_get_gap_x(Device* device) {
@@ -237,17 +267,26 @@ static int32_t gc9a01_get_gap_y(Device* device) {
static error_t gc9a01_invert_color(Device* device, bool invert_color_data) {
auto* internal = static_cast<Gc9a01Internal*>(device_get_driver_data(device));
return esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t gc9a01_disp_on_off(Device* device, bool on_off) {
auto* internal = static_cast<Gc9a01Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t gc9a01_disp_sleep(Device* device, bool sleep) {
auto* internal = static_cast<Gc9a01Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// The deprecated HAL's Gc9a01Display always set swap_bytes=true on its lvgl_port config
@@ -51,6 +51,7 @@ static constexpr uint8_t DEEP_SLEEP_CHECK_CODE = 0xA5;
extern "C" {
struct Gdeq031t10Internal {
Device* spi_controller;
spi_device_handle_t spi_device;
struct GpioDescriptor* dc;
struct GpioDescriptor* reset; // optional
@@ -123,12 +124,14 @@ static bool wait_while_busy(Gdeq031t10Internal* internal) {
}
static void hardware_reset(Gdeq031t10Internal* internal) {
spi_controller_lock(internal->spi_controller);
if (internal->reset != nullptr) {
gpio_descriptor_set_level(internal->reset, false);
delay_millis(10);
gpio_descriptor_set_level(internal->reset, true);
delay_millis(10);
}
spi_controller_unlock(internal->spi_controller);
}
static bool init_full(Gdeq031t10Internal* internal, bool mirror_180) {
@@ -137,10 +140,12 @@ static bool init_full(Gdeq031t10Internal* internal, bool mirror_180) {
// mode change needs partial mode's lingering VCOM/data-interval setting cleared, and
// waking from deep sleep is only possible by toggling RST.
hardware_reset(internal);
spi_controller_lock(internal->spi_controller);
bool ok = write_command(internal, CMD_PANEL_SETTING);
ok = ok && write_data_byte(internal, mirror_180 ? 0x13 : 0x1F);
ok = ok && write_command(internal, CMD_POWER_ON);
ok = ok && wait_while_busy(internal);
spi_controller_unlock(internal->spi_controller);
if (!ok) {
LOG_E(TAG, "Full init failed");
return false;
@@ -164,6 +169,7 @@ static bool init_with_fast_lut(Gdeq031t10Internal* internal, bool mirror_180, en
case GDEQ031T10_REFRESH_PARTIAL: timing = 0x79; break;
default: return true; // GDEQ031T10_REFRESH_FULL: init_full() already did everything
}
spi_controller_lock(internal->spi_controller);
bool ok = write_command(internal, CMD_FAST_MODE_ENABLE);
ok = ok && write_data_byte(internal, 0x02);
ok = ok && write_command(internal, CMD_FAST_MODE_TIMING);
@@ -172,6 +178,7 @@ static bool init_with_fast_lut(Gdeq031t10Internal* internal, bool mirror_180, en
ok = write_command(internal, CMD_VCOM_DATA_INTERVAL);
ok = ok && write_data_byte(internal, 0xD7);
}
spi_controller_unlock(internal->spi_controller);
if (!ok) {
LOG_E(TAG, "Mode init failed");
return false;
@@ -185,7 +192,10 @@ static bool ensure_panel_ready(Gdeq031t10Internal* internal, bool mirror_180, en
return init_with_fast_lut(internal, mirror_180, mode);
} else if (!internal->panel_power_on) {
// Registers still hold the mode; only the charge pump was idled.
if (!write_command(internal, CMD_POWER_ON) || !wait_while_busy(internal)) {
spi_controller_lock(internal->spi_controller);
bool ok = write_command(internal, CMD_POWER_ON) && wait_while_busy(internal);
spi_controller_unlock(internal->spi_controller);
if (!ok) {
LOG_E(TAG, "Panel did not become ready after power-on");
return false;
}
@@ -198,11 +208,13 @@ static bool panel_power_off(Gdeq031t10Internal* internal) {
// Command the panel off regardless of whether BUSY confirms it: retrying
// forever here would just as likely hang, and a stuck-BUSY panel is
// already unusable either way.
spi_controller_lock(internal->spi_controller);
bool ok = write_command(internal, CMD_POWER_ON_OFF); // 0x02 standalone = power off
if (!ok || !wait_while_busy(internal)) {
LOG_E(TAG, "Panel did not confirm power-off");
ok = false;
}
spi_controller_unlock(internal->spi_controller);
internal->panel_power_on = false;
return ok;
}
@@ -222,12 +234,18 @@ static void refresh_full(Gdeq031t10Internal* internal, bool mirror_180, enum Gde
return;
}
// Single logical bus operation spanning old/new frame data and the refresh trigger: an SD
// transaction slipping in between any of these writes would corrupt the sequence the panel
// is mid-way through parsing.
spi_controller_lock(internal->spi_controller);
// shadow_framebuffer holds the panel's actual current content (matches the vendor's tracked
// oldData[] buffer) - send it as "old" data so the controller computes correct per-pixel
// transitions.
LOG_I(TAG, "write old data from shadow_buffer");
if (!write_command(internal, CMD_DATA_START_OLD) || !write_data(internal, internal->shadow_framebuffer, FRAMEBUFFER_SIZE)) {
LOG_E(TAG, "Failed to send old frame data");
spi_controller_unlock(internal->spi_controller);
return;
}
@@ -235,11 +253,13 @@ static void refresh_full(Gdeq031t10Internal* internal, bool mirror_180, enum Gde
// is showing right now until this refresh is confirmed below.
if (!write_command(internal, CMD_DATA_START_NEW) || !write_data(internal, render_bitmap, FRAMEBUFFER_SIZE)) {
LOG_E(TAG, "Failed to send new frame data");
spi_controller_unlock(internal->spi_controller);
return;
}
if (!write_command(internal, CMD_DISPLAY_REFRESH)) {
LOG_E(TAG, "Failed to trigger display refresh");
spi_controller_unlock(internal->spi_controller);
return;
}
delay_millis(1); // datasheet requires >=200us settle before polling BUSY
@@ -252,6 +272,7 @@ static void refresh_full(Gdeq031t10Internal* internal, bool mirror_180, enum Gde
// content the panel never displayed, and hide the difference from the change scan.
LOG_E(TAG, "Full refresh did not complete");
}
spi_controller_unlock(internal->spi_controller);
// EPD_DeepSleep(): power off, then actually deep-sleep rather than just idling the charge
// pump. panel_mode_valid=false forces the next refresh_full() call back through a fresh
@@ -277,6 +298,11 @@ static void refresh_window(Gdeq031t10Internal* internal, bool mirror_180, const
const uint16_t y = static_cast<uint16_t>(first_row);
const uint16_t ye = static_cast<uint16_t>(last_row);
// Single logical bus operation spanning the window setup, old/new region data and the
// refresh trigger: an SD transaction slipping in between any of these writes would corrupt
// the sequence the panel is mid-way through parsing.
spi_controller_lock(internal->spi_controller);
// Set the partial RAM window (GxEPD2 GDEQ031T10 sequence).
bool ok = write_command(internal, CMD_PARTIAL_IN);
ok = ok && write_command(internal, CMD_PARTIAL_WINDOW);
@@ -290,6 +316,7 @@ static void refresh_window(Gdeq031t10Internal* internal, bool mirror_180, const
if (!ok) {
LOG_E(TAG, "Failed to set partial refresh window");
write_command(internal, CMD_PARTIAL_OUT); // best-effort: leave partial-window mode
spi_controller_unlock(internal->spi_controller);
return;
}
@@ -303,6 +330,7 @@ static void refresh_window(Gdeq031t10Internal* internal, bool mirror_180, const
if (!write_command(internal, CMD_DATA_START_OLD) || !write_data(internal, internal->region_buffer, n)) {
LOG_E(TAG, "Failed to send old window data");
write_command(internal, CMD_PARTIAL_OUT);
spi_controller_unlock(internal->spi_controller);
return;
}
@@ -317,6 +345,7 @@ static void refresh_window(Gdeq031t10Internal* internal, bool mirror_180, const
if (!write_command(internal, CMD_DATA_START_NEW) || !write_data(internal, internal->region_buffer, n)) {
LOG_E(TAG, "Failed to send new window data");
write_command(internal, CMD_PARTIAL_OUT);
spi_controller_unlock(internal->spi_controller);
return;
}
@@ -340,6 +369,7 @@ static void refresh_window(Gdeq031t10Internal* internal, bool mirror_180, const
}
}
write_command(internal, CMD_PARTIAL_OUT);
spi_controller_unlock(internal->spi_controller);
}
// endregion
@@ -467,8 +497,10 @@ static error_t gdeq031t10_disp_on_off(Device* device, bool on_off) {
panel_power_off(internal);
}
delay_millis(100);
spi_controller_lock(internal->spi_controller);
write_command(internal, CMD_DEEP_SLEEP);
write_data_byte(internal, DEEP_SLEEP_CHECK_CODE);
spi_controller_unlock(internal->spi_controller);
// Deep sleep needs a reset to wake, which restores register defaults.
internal->panel_mode_valid = false;
}
@@ -566,6 +598,7 @@ static error_t start(Device* device) {
return ERROR_OUT_OF_MEMORY;
}
internal->spi_controller = parent;
internal->dc = gpio_descriptor_acquire(
config->pin_dc.gpio_controller,
config->pin_dc.pin,
@@ -651,8 +684,10 @@ static error_t stop(Device* device) {
panel_power_off(internal);
}
delay_millis(100);
spi_controller_lock(internal->spi_controller);
write_command(internal, CMD_DEEP_SLEEP);
write_data_byte(internal, DEEP_SLEEP_CHECK_CODE);
spi_controller_unlock(internal->spi_controller);
internal->display_on = false;
}
xSemaphoreGive(internal->panel_mutex);
+16
View File
@@ -111,6 +111,7 @@ constexpr uint8_t INIT_CMDS[] = {
} // namespace
struct Hx8357Internal {
Device* spi_controller;
spi_device_handle_t spi_handle;
gpio_num_t dc_pin;
size_t max_transfer_size;
@@ -210,6 +211,7 @@ static error_t start(Device* device) {
return ERROR_OUT_OF_MEMORY;
}
internal->spi_controller = parent;
internal->dc_pin = static_cast<gpio_num_t>(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
@@ -275,9 +277,11 @@ static error_t start(Device* device) {
internal->mirror_x = config->mirror_x;
internal->mirror_y = config->mirror_y;
spi_controller_lock(internal->spi_controller);
run_init_cmds(internal);
send_madctl(internal);
send_cmd(internal, config->invert_color ? HX8357_INVON : HX8357_INVOFF);
spi_controller_unlock(internal->spi_controller);
device_set_driver_data(device, internal);
return ERROR_NONE;
@@ -329,6 +333,7 @@ static error_t hx8357_draw_bitmap(Device* device, int32_t x_start, int32_t y_sta
static_cast<uint8_t>((y2 >> 8) & 0xFF), static_cast<uint8_t>(y2 & 0xFF),
};
spi_controller_lock(internal->spi_controller);
send_cmd(internal, HX8357_CASET);
send_data(internal, xb, 4);
send_cmd(internal, HX8357_PASET);
@@ -337,6 +342,7 @@ static error_t hx8357_draw_bitmap(Device* device, int32_t x_start, int32_t y_sta
const size_t pixel_count = static_cast<size_t>(x_end - x_start) * static_cast<size_t>(y_end - y_start);
send_data(internal, static_cast<const uint8_t*>(color_data), pixel_count * 3); // RGB888 = 3 bytes/pixel
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
@@ -345,14 +351,18 @@ static error_t hx8357_mirror(Device* device, bool x_axis, bool y_axis) {
auto* internal = static_cast<Hx8357Internal*>(device_get_driver_data(device));
internal->mirror_x = x_axis;
internal->mirror_y = y_axis;
spi_controller_lock(internal->spi_controller);
send_madctl(internal);
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
static error_t hx8357_swap_xy(Device* device, bool swap_axes) {
auto* internal = static_cast<Hx8357Internal*>(device_get_driver_data(device));
internal->swap_xy = swap_axes;
spi_controller_lock(internal->spi_controller);
send_madctl(internal);
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
@@ -373,19 +383,25 @@ static bool hx8357_get_mirror_y(Device* device) {
static error_t hx8357_invert_color(Device* device, bool invert_color_data) {
auto* internal = static_cast<Hx8357Internal*>(device_get_driver_data(device));
spi_controller_lock(internal->spi_controller);
send_cmd(internal, invert_color_data ? HX8357_INVON : HX8357_INVOFF);
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
static error_t hx8357_disp_on_off(Device* device, bool on_off) {
auto* internal = static_cast<Hx8357Internal*>(device_get_driver_data(device));
spi_controller_lock(internal->spi_controller);
send_cmd(internal, on_off ? HX8357_DISPON : 0x28 /* HX8357_DISPOFF */);
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
static error_t hx8357_disp_sleep(Device* device, bool sleep) {
auto* internal = static_cast<Hx8357Internal*>(device_get_driver_data(device));
spi_controller_lock(internal->spi_controller);
send_cmd(internal, sleep ? 0x10 /* HX8357_SLPIN */ : HX8357_SLPOUT);
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
+53 -12
View File
@@ -34,6 +34,7 @@
static const uint8_t GAMMA_CURVE_VALUES[4] = { 0x01, 0x04, 0x02, 0x08 };
struct Ili9341Internal {
Device* spi_controller;
esp_lcd_panel_io_handle_t io_handle;
esp_lcd_panel_handle_t panel_handle;
// Given from ISR context by on_color_trans_done() once a queued SPI transfer physically
@@ -108,6 +109,7 @@ static error_t start(Device* device) {
return ERROR_OUT_OF_MEMORY;
}
internal->spi_controller = parent;
internal->draw_done_semaphore = xSemaphoreCreateBinary();
if (internal->draw_done_semaphore == nullptr) {
free(internal);
@@ -187,8 +189,10 @@ static error_t start(Device* device) {
// Bring-up sequence, order matches EspLcdDisplayV2::applyConfiguration (proven correct on real ILI9341 panels).
// Every failure path below must clean up fully: unlike stop_device, this is never retried by the kernel
// if start_device fails (see device_start() in TactilityKernel), so a partial failure here would leak.
bool ok =
pulse_reset(internal->reset_descriptor) == ERROR_NONE &&
bool ok = pulse_reset(internal->reset_descriptor) == ERROR_NONE;
spi_controller_lock(internal->spi_controller);
ok = ok &&
esp_lcd_panel_reset(internal->panel_handle) == ESP_OK &&
esp_lcd_panel_init(internal->panel_handle) == ESP_OK &&
(!config->invert_color || esp_lcd_panel_invert_color(internal->panel_handle, true) == ESP_OK);
@@ -203,6 +207,7 @@ static error_t start(Device* device) {
ok = ok && (!config->invert_color || esp_lcd_panel_invert_color(internal->panel_handle, true) == ESP_OK);
ok = ok && (config->gamma_curve >= 4 || esp_lcd_panel_io_tx_param(internal->io_handle, LCD_CMD_GAMSET, &GAMMA_CURVE_VALUES[config->gamma_curve], 1) == ESP_OK);
ok = ok && esp_lcd_panel_disp_on_off(internal->panel_handle, true) == ESP_OK;
spi_controller_unlock(internal->spi_controller);
if (!ok) {
LOG_E(TAG, "Failed to bring up panel");
@@ -223,9 +228,11 @@ static error_t start(Device* device) {
static error_t stop(Device* device) {
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
spi_controller_lock(internal->spi_controller);
if (internal->panel_handle != nullptr) {
if (esp_lcd_panel_del(internal->panel_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->panel_handle = nullptr;
@@ -234,10 +241,12 @@ static error_t stop(Device* device) {
if (internal->io_handle != nullptr) {
if (esp_lcd_panel_io_del(internal->io_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel IO");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->io_handle = nullptr;
}
spi_controller_unlock(internal->spi_controller);
if (internal->reset_descriptor != nullptr) {
gpio_descriptor_release(internal->reset_descriptor);
@@ -256,16 +265,23 @@ static error_t stop(Device* device) {
static error_t ili9341_reset(Device* device) {
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
spi_controller_lock(internal->spi_controller);
error_t error = pulse_reset(internal->reset_descriptor);
if (error != ERROR_NONE) {
spi_controller_unlock(internal->spi_controller);
return error;
}
return esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
error_t result = esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t ili9341_init(Device* device) {
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
return esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t ili9341_draw_bitmap(Device* device, int32_t x_start, int32_t y_start, int32_t x_end, int32_t y_end, const void* color_data) {
@@ -276,26 +292,39 @@ static error_t ili9341_draw_bitmap(Device* device, int32_t x_start, int32_t y_st
// satisfied by this draw's own transfer completing.
xSemaphoreTake(internal->draw_done_semaphore, 0);
if (esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data) != ESP_OK) {
spi_controller_lock(internal->spi_controller);
esp_err_t ret = esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data);
if (ret != ESP_OK) {
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
// Block until the SPI transfer physically completes: DisplayApi's draw_bitmap is a synchronous
// contract (see lvgl_display.c), so the caller must be able to safely reuse/overwrite
// color_data as soon as this call returns. esp_lcd_panel_draw_bitmap() only queues the
// transfer and returns once it's handed to the SPI peripheral, not once it's finished.
// transfer and returns once it's handed to the SPI peripheral, not once it's finished. Hold the
// bus lock across the wait too, not just the queueing call: the command/data phases aren't
// wrapped in their own acquire_bus by esp_lcd_panel_io_spi, so another bus user could otherwise
// interleave with this transfer while it's still in flight.
xSemaphoreTake(internal->draw_done_semaphore, portMAX_DELAY);
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
static error_t ili9341_mirror(Device* device, bool x_axis, bool y_axis) {
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
return esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t ili9341_swap_xy(Device* device, bool swap_axes) {
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
return esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// Reads the devicetree-configured baseline, not live hardware state: swap_xy()/mirror() calls made after
@@ -314,7 +343,10 @@ static bool ili9341_get_mirror_y(Device* device) {
static error_t ili9341_set_gap(Device* device, int32_t x_gap, int32_t y_gap) {
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
return esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// Reads the devicetree-configured baseline, not live hardware state - see DisplayApi::get_gap_x().
@@ -334,17 +366,26 @@ static int32_t ili9341_get_gap_y(Device* device) {
static error_t ili9341_invert_color(Device* device, bool invert_color_data) {
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
return esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t ili9341_disp_on_off(Device* device, bool on_off) {
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t ili9341_disp_sleep(Device* device, bool sleep) {
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// bgr_order only selects the panel controller's rgb_ele_order (applied in start(), below) so the
+49 -10
View File
@@ -26,6 +26,7 @@
#define GET_CONFIG(device) (static_cast<const Ili9488Config*>((device)->config))
struct Ili9488Internal {
Device* spi_controller;
esp_lcd_panel_io_handle_t io_handle;
esp_lcd_panel_handle_t panel_handle;
// Given from ISR context by on_color_trans_done() once a queued SPI transfer physically
@@ -68,6 +69,7 @@ static error_t start(Device* device) {
return ERROR_OUT_OF_MEMORY;
}
internal->spi_controller = parent;
internal->draw_done_semaphore = xSemaphoreCreateBinary();
if (internal->draw_done_semaphore == nullptr) {
free(internal);
@@ -131,6 +133,7 @@ static error_t start(Device* device) {
// Bring-up sequence, order matches EspLcdDisplayV2::applyConfiguration (proven correct on real ILI9488 panels).
// Every failure path below must clean up fully: unlike stop_device, this is never retried by the kernel
// if start_device fails (see device_start() in TactilityKernel), so a partial failure here would leak.
spi_controller_lock(internal->spi_controller);
bool ok =
esp_lcd_panel_reset(internal->panel_handle) == ESP_OK &&
esp_lcd_panel_init(internal->panel_handle) == ESP_OK &&
@@ -145,6 +148,7 @@ static error_t start(Device* device) {
ok = ok && ((!config->mirror_x && !config->mirror_y) || esp_lcd_panel_mirror(internal->panel_handle, config->mirror_x, config->mirror_y) == ESP_OK);
ok = ok && (!config->invert_color || esp_lcd_panel_invert_color(internal->panel_handle, true) == ESP_OK);
ok = ok && esp_lcd_panel_disp_on_off(internal->panel_handle, true) == ESP_OK;
spi_controller_unlock(internal->spi_controller);
if (!ok) {
LOG_E(TAG, "Failed to bring up panel");
@@ -162,9 +166,11 @@ static error_t start(Device* device) {
static error_t stop(Device* device) {
auto* internal = static_cast<Ili9488Internal*>(device_get_driver_data(device));
spi_controller_lock(internal->spi_controller);
if (internal->panel_handle != nullptr) {
if (esp_lcd_panel_del(internal->panel_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->panel_handle = nullptr;
@@ -173,10 +179,12 @@ static error_t stop(Device* device) {
if (internal->io_handle != nullptr) {
if (esp_lcd_panel_io_del(internal->io_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel IO");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->io_handle = nullptr;
}
spi_controller_unlock(internal->spi_controller);
vSemaphoreDelete(internal->draw_done_semaphore);
free(internal);
@@ -190,12 +198,18 @@ static error_t stop(Device* device) {
static error_t ili9488_reset(Device* device) {
auto* internal = static_cast<Ili9488Internal*>(device_get_driver_data(device));
return esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t ili9488_init(Device* device) {
auto* internal = static_cast<Ili9488Internal*>(device_get_driver_data(device));
return esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t ili9488_draw_bitmap(Device* device, int32_t x_start, int32_t y_start, int32_t x_end, int32_t y_end, const void* color_data) {
@@ -206,26 +220,39 @@ static error_t ili9488_draw_bitmap(Device* device, int32_t x_start, int32_t y_st
// satisfied by this draw's own transfer completing.
xSemaphoreTake(internal->draw_done_semaphore, 0);
if (esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data) != ESP_OK) {
spi_controller_lock(internal->spi_controller);
esp_err_t ret = esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data);
if (ret != ESP_OK) {
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
// Block until the SPI transfer physically completes: DisplayApi's draw_bitmap is a synchronous
// contract (see lvgl_display.c), so the caller must be able to safely reuse/overwrite
// color_data as soon as this call returns. esp_lcd_panel_draw_bitmap() only queues the
// transfer and returns once it's handed to the SPI peripheral, not once it's finished.
// transfer and returns once it's handed to the SPI peripheral, not once it's finished. Hold the
// bus lock across the wait too, not just the queueing call: the command/data phases aren't
// wrapped in their own acquire_bus by esp_lcd_panel_io_spi, so another bus user could otherwise
// interleave with this transfer while it's still in flight.
xSemaphoreTake(internal->draw_done_semaphore, portMAX_DELAY);
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
static error_t ili9488_mirror(Device* device, bool x_axis, bool y_axis) {
auto* internal = static_cast<Ili9488Internal*>(device_get_driver_data(device));
return esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t ili9488_swap_xy(Device* device, bool swap_axes) {
auto* internal = static_cast<Ili9488Internal*>(device_get_driver_data(device));
return esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// Reads the devicetree-configured baseline, not live hardware state: swap_xy()/mirror() calls made after
@@ -244,7 +271,10 @@ static bool ili9488_get_mirror_y(Device* device) {
static error_t ili9488_set_gap(Device* device, int32_t x_gap, int32_t y_gap) {
auto* internal = static_cast<Ili9488Internal*>(device_get_driver_data(device));
return esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// Reads the devicetree-configured baseline, not live hardware state - see DisplayApi::get_gap_x().
@@ -264,17 +294,26 @@ static int32_t ili9488_get_gap_y(Device* device) {
static error_t ili9488_invert_color(Device* device, bool invert_color_data) {
auto* internal = static_cast<Ili9488Internal*>(device_get_driver_data(device));
return esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t ili9488_disp_on_off(Device* device, bool on_off) {
auto* internal = static_cast<Ili9488Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t ili9488_disp_sleep(Device* device, bool sleep) {
auto* internal = static_cast<Ili9488Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// bgr_order only selects the panel controller's rgb_ele_order (applied in start(), above) so the
+48 -9
View File
@@ -31,6 +31,7 @@ constexpr auto* TAG = "JD9853";
static const uint8_t GAMMA_CURVE_VALUES[4] = { 0x01, 0x04, 0x02, 0x08 };
struct Jd9853Internal {
Device* spi_controller;
esp_lcd_panel_io_handle_t io_handle;
esp_lcd_panel_handle_t panel_handle;
// See st7796-module's identical field for why this exists: draw_bitmap() must block until
@@ -69,6 +70,7 @@ static error_t start(Device* device) {
return ERROR_OUT_OF_MEMORY;
}
internal->spi_controller = parent;
internal->draw_done_semaphore = xSemaphoreCreateBinary();
if (internal->draw_done_semaphore == nullptr) {
free(internal);
@@ -134,6 +136,7 @@ static error_t start(Device* device) {
// Bring-up sequence, order matches the deprecated HAL's Jd9853Display (proven correct on real
// Waveshare S3 Touch LCD 1.47 hardware). Every failure path below must clean up fully: unlike
// stop_device, this is never retried by the kernel if start_device fails.
spi_controller_lock(internal->spi_controller);
bool ok =
esp_lcd_panel_reset(internal->panel_handle) == ESP_OK &&
esp_lcd_panel_init(internal->panel_handle) == ESP_OK &&
@@ -147,6 +150,7 @@ static error_t start(Device* device) {
ok = ok && (!config->invert_color || esp_lcd_panel_invert_color(internal->panel_handle, true) == ESP_OK);
ok = ok && (config->gamma_curve >= 4 || esp_lcd_panel_io_tx_param(internal->io_handle, LCD_CMD_GAMSET, &GAMMA_CURVE_VALUES[config->gamma_curve], 1) == ESP_OK);
ok = ok && esp_lcd_panel_disp_on_off(internal->panel_handle, true) == ESP_OK;
spi_controller_unlock(internal->spi_controller);
if (!ok) {
LOG_E(TAG, "Failed to bring up panel");
@@ -164,9 +168,11 @@ static error_t start(Device* device) {
static error_t stop(Device* device) {
auto* internal = static_cast<Jd9853Internal*>(device_get_driver_data(device));
spi_controller_lock(internal->spi_controller);
if (internal->panel_handle != nullptr) {
if (esp_lcd_panel_del(internal->panel_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->panel_handle = nullptr;
@@ -175,10 +181,12 @@ static error_t stop(Device* device) {
if (internal->io_handle != nullptr) {
if (esp_lcd_panel_io_del(internal->io_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel IO");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->io_handle = nullptr;
}
spi_controller_unlock(internal->spi_controller);
vSemaphoreDelete(internal->draw_done_semaphore);
free(internal);
@@ -192,12 +200,18 @@ static error_t stop(Device* device) {
static error_t jd9853_reset(Device* device) {
auto* internal = static_cast<Jd9853Internal*>(device_get_driver_data(device));
return esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t jd9853_init(Device* device) {
auto* internal = static_cast<Jd9853Internal*>(device_get_driver_data(device));
return esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t jd9853_draw_bitmap(Device* device, int32_t x_start, int32_t y_start, int32_t x_end, int32_t y_end, const void* color_data) {
@@ -205,22 +219,35 @@ static error_t jd9853_draw_bitmap(Device* device, int32_t x_start, int32_t y_sta
xSemaphoreTake(internal->draw_done_semaphore, 0);
if (esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data) != ESP_OK) {
spi_controller_lock(internal->spi_controller);
esp_err_t ret = esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data);
if (ret != ESP_OK) {
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
// Hold the bus lock across the wait too, not just the queueing call: the command/data phases
// aren't wrapped in their own acquire_bus by esp_lcd_panel_io_spi, so another bus user could
// otherwise interleave with this transfer while it's still in flight.
xSemaphoreTake(internal->draw_done_semaphore, portMAX_DELAY);
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
static error_t jd9853_mirror(Device* device, bool x_axis, bool y_axis) {
auto* internal = static_cast<Jd9853Internal*>(device_get_driver_data(device));
return esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t jd9853_swap_xy(Device* device, bool swap_axes) {
auto* internal = static_cast<Jd9853Internal*>(device_get_driver_data(device));
return esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static bool jd9853_get_swap_xy(Device* device) {
@@ -237,7 +264,10 @@ static bool jd9853_get_mirror_y(Device* device) {
static error_t jd9853_set_gap(Device* device, int32_t x_gap, int32_t y_gap) {
auto* internal = static_cast<Jd9853Internal*>(device_get_driver_data(device));
return esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static int32_t jd9853_get_gap_x(Device* device) {
@@ -250,17 +280,26 @@ static int32_t jd9853_get_gap_y(Device* device) {
static error_t jd9853_invert_color(Device* device, bool invert_color_data) {
auto* internal = static_cast<Jd9853Internal*>(device_get_driver_data(device));
return esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t jd9853_disp_on_off(Device* device, bool on_off) {
auto* internal = static_cast<Jd9853Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t jd9853_disp_sleep(Device* device, bool sleep) {
auto* internal = static_cast<Jd9853Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// The deprecated HAL's Jd9853Display always set swap_bytes=true on its lvgl_port config
+49 -10
View File
@@ -32,6 +32,7 @@
static const uint8_t GAMMA_CURVE_VALUES[4] = { 0x01, 0x04, 0x02, 0x08 };
struct St7735Internal {
Device* spi_controller;
esp_lcd_panel_io_handle_t io_handle;
esp_lcd_panel_handle_t panel_handle;
// Given from ISR context by on_color_trans_done() once a queued SPI transfer physically
@@ -74,6 +75,7 @@ static error_t start(Device* device) {
return ERROR_OUT_OF_MEMORY;
}
internal->spi_controller = parent;
internal->draw_done_semaphore = xSemaphoreCreateBinary();
if (internal->draw_done_semaphore == nullptr) {
free(internal);
@@ -134,6 +136,7 @@ static error_t start(Device* device) {
// Bring-up sequence, order matches st7789-module (proven correct on real panels).
// Every failure path below must clean up fully: unlike stop_device, this is never retried by the kernel
// if start_device fails (see device_start() in TactilityKernel), so a partial failure here would leak.
spi_controller_lock(internal->spi_controller);
bool ok =
esp_lcd_panel_reset(internal->panel_handle) == ESP_OK &&
esp_lcd_panel_init(internal->panel_handle) == ESP_OK &&
@@ -152,6 +155,7 @@ static error_t start(Device* device) {
ok = ok && (!config->invert_color || esp_lcd_panel_invert_color(internal->panel_handle, true) == ESP_OK);
ok = ok && (config->gamma_curve >= 4 || esp_lcd_panel_io_tx_param(internal->io_handle, LCD_CMD_GAMSET, &GAMMA_CURVE_VALUES[config->gamma_curve], 1) == ESP_OK);
ok = ok && esp_lcd_panel_disp_on_off(internal->panel_handle, true) == ESP_OK;
spi_controller_unlock(internal->spi_controller);
if (!ok) {
LOG_E(TAG, "Failed to bring up panel");
@@ -169,9 +173,11 @@ static error_t start(Device* device) {
static error_t stop(Device* device) {
auto* internal = static_cast<St7735Internal*>(device_get_driver_data(device));
spi_controller_lock(internal->spi_controller);
if (internal->panel_handle != nullptr) {
if (esp_lcd_panel_del(internal->panel_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->panel_handle = nullptr;
@@ -180,10 +186,12 @@ static error_t stop(Device* device) {
if (internal->io_handle != nullptr) {
if (esp_lcd_panel_io_del(internal->io_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel IO");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->io_handle = nullptr;
}
spi_controller_unlock(internal->spi_controller);
vSemaphoreDelete(internal->draw_done_semaphore);
free(internal);
@@ -197,12 +205,18 @@ static error_t stop(Device* device) {
static error_t st7735_reset(Device* device) {
auto* internal = static_cast<St7735Internal*>(device_get_driver_data(device));
return esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7735_init(Device* device) {
auto* internal = static_cast<St7735Internal*>(device_get_driver_data(device));
return esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7735_draw_bitmap(Device* device, int32_t x_start, int32_t y_start, int32_t x_end, int32_t y_end, const void* color_data) {
@@ -213,26 +227,39 @@ static error_t st7735_draw_bitmap(Device* device, int32_t x_start, int32_t y_sta
// satisfied by this draw's own transfer completing.
xSemaphoreTake(internal->draw_done_semaphore, 0);
if (esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data) != ESP_OK) {
spi_controller_lock(internal->spi_controller);
esp_err_t ret = esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data);
if (ret != ESP_OK) {
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
// Block until the SPI transfer physically completes: DisplayApi's draw_bitmap is a synchronous
// contract (see lvgl_display.c), so the caller must be able to safely reuse/overwrite
// color_data as soon as this call returns. esp_lcd_panel_draw_bitmap() only queues the
// transfer and returns once it's handed to the SPI peripheral, not once it's finished.
// transfer and returns once it's handed to the SPI peripheral, not once it's finished. Hold the
// bus lock across the wait too, not just the queueing call: the command/data phases aren't
// wrapped in their own acquire_bus by esp_lcd_panel_io_spi, so another bus user could otherwise
// interleave with this transfer while it's still in flight.
xSemaphoreTake(internal->draw_done_semaphore, portMAX_DELAY);
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
static error_t st7735_mirror(Device* device, bool x_axis, bool y_axis) {
auto* internal = static_cast<St7735Internal*>(device_get_driver_data(device));
return esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7735_swap_xy(Device* device, bool swap_axes) {
auto* internal = static_cast<St7735Internal*>(device_get_driver_data(device));
return esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// Reads the devicetree-configured baseline, not live hardware state: swap_xy()/mirror() calls made after
@@ -251,7 +278,10 @@ static bool st7735_get_mirror_y(Device* device) {
static error_t st7735_set_gap(Device* device, int32_t x_gap, int32_t y_gap) {
auto* internal = static_cast<St7735Internal*>(device_get_driver_data(device));
return esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// Reads the devicetree-configured baseline, not live hardware state - see DisplayApi::get_gap_x().
@@ -271,17 +301,26 @@ static int32_t st7735_get_gap_y(Device* device) {
static error_t st7735_invert_color(Device* device, bool invert_color_data) {
auto* internal = static_cast<St7735Internal*>(device_get_driver_data(device));
return esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7735_disp_on_off(Device* device, bool on_off) {
auto* internal = static_cast<St7735Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7735_disp_sleep(Device* device, bool sleep) {
auto* internal = static_cast<St7735Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static enum DisplayColorFormat st7735_get_color_format(Device* device) {
+49 -10
View File
@@ -32,6 +32,7 @@
static const uint8_t GAMMA_CURVE_VALUES[4] = { 0x01, 0x04, 0x02, 0x08 };
struct St7789Internal {
Device* spi_controller;
esp_lcd_panel_io_handle_t io_handle;
esp_lcd_panel_handle_t panel_handle;
// Given from ISR context by on_color_trans_done() once a queued SPI transfer physically
@@ -74,6 +75,7 @@ static error_t start(Device* device) {
return ERROR_OUT_OF_MEMORY;
}
internal->spi_controller = parent;
internal->draw_done_semaphore = xSemaphoreCreateBinary();
if (internal->draw_done_semaphore == nullptr) {
free(internal);
@@ -134,6 +136,7 @@ static error_t start(Device* device) {
// Bring-up sequence, order matches EspLcdDisplayV2::applyConfiguration (proven correct on real ST7789 panels).
// Every failure path below must clean up fully: unlike stop_device, this is never retried by the kernel
// if start_device fails (see device_start() in TactilityKernel), so a partial failure here would leak.
spi_controller_lock(internal->spi_controller);
bool ok =
esp_lcd_panel_reset(internal->panel_handle) == ESP_OK &&
esp_lcd_panel_init(internal->panel_handle) == ESP_OK &&
@@ -152,6 +155,7 @@ static error_t start(Device* device) {
ok = ok && (!config->invert_color || esp_lcd_panel_invert_color(internal->panel_handle, true) == ESP_OK);
ok = ok && (config->gamma_curve >= 4 || esp_lcd_panel_io_tx_param(internal->io_handle, LCD_CMD_GAMSET, &GAMMA_CURVE_VALUES[config->gamma_curve], 1) == ESP_OK);
ok = ok && esp_lcd_panel_disp_on_off(internal->panel_handle, true) == ESP_OK;
spi_controller_unlock(internal->spi_controller);
if (!ok) {
LOG_E(TAG, "Failed to bring up panel");
@@ -169,9 +173,11 @@ static error_t start(Device* device) {
static error_t stop(Device* device) {
auto* internal = static_cast<St7789Internal*>(device_get_driver_data(device));
spi_controller_lock(internal->spi_controller);
if (internal->panel_handle != nullptr) {
if (esp_lcd_panel_del(internal->panel_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->panel_handle = nullptr;
@@ -180,10 +186,12 @@ static error_t stop(Device* device) {
if (internal->io_handle != nullptr) {
if (esp_lcd_panel_io_del(internal->io_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel IO");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->io_handle = nullptr;
}
spi_controller_unlock(internal->spi_controller);
vSemaphoreDelete(internal->draw_done_semaphore);
free(internal);
@@ -197,12 +205,18 @@ static error_t stop(Device* device) {
static error_t st7789_reset(Device* device) {
auto* internal = static_cast<St7789Internal*>(device_get_driver_data(device));
return esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7789_init(Device* device) {
auto* internal = static_cast<St7789Internal*>(device_get_driver_data(device));
return esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7789_draw_bitmap(Device* device, int32_t x_start, int32_t y_start, int32_t x_end, int32_t y_end, const void* color_data) {
@@ -213,26 +227,39 @@ static error_t st7789_draw_bitmap(Device* device, int32_t x_start, int32_t y_sta
// satisfied by this draw's own transfer completing.
xSemaphoreTake(internal->draw_done_semaphore, 0);
if (esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data) != ESP_OK) {
spi_controller_lock(internal->spi_controller);
esp_err_t ret = esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data);
if (ret != ESP_OK) {
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
// Block until the SPI transfer physically completes: DisplayApi's draw_bitmap is a synchronous
// contract (see lvgl_display.c), so the caller must be able to safely reuse/overwrite
// color_data as soon as this call returns. esp_lcd_panel_draw_bitmap() only queues the
// transfer and returns once it's handed to the SPI peripheral, not once it's finished.
// transfer and returns once it's handed to the SPI peripheral, not once it's finished. Hold the
// bus lock across the wait too, not just the queueing call: the command/data phases aren't
// wrapped in their own acquire_bus by esp_lcd_panel_io_spi, so another bus user could otherwise
// interleave with this transfer while it's still in flight.
xSemaphoreTake(internal->draw_done_semaphore, portMAX_DELAY);
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
static error_t st7789_mirror(Device* device, bool x_axis, bool y_axis) {
auto* internal = static_cast<St7789Internal*>(device_get_driver_data(device));
return esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7789_swap_xy(Device* device, bool swap_axes) {
auto* internal = static_cast<St7789Internal*>(device_get_driver_data(device));
return esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// Reads the devicetree-configured baseline, not live hardware state: swap_xy()/mirror() calls made after
@@ -251,7 +278,10 @@ static bool st7789_get_mirror_y(Device* device) {
static error_t st7789_set_gap(Device* device, int32_t x_gap, int32_t y_gap) {
auto* internal = static_cast<St7789Internal*>(device_get_driver_data(device));
return esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// Reads the devicetree-configured baseline, not live hardware state - see DisplayApi::get_gap_x().
@@ -271,17 +301,26 @@ static int32_t st7789_get_gap_y(Device* device) {
static error_t st7789_invert_color(Device* device, bool invert_color_data) {
auto* internal = static_cast<St7789Internal*>(device_get_driver_data(device));
return esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7789_disp_on_off(Device* device, bool on_off) {
auto* internal = static_cast<St7789Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7789_disp_sleep(Device* device, bool sleep) {
auto* internal = static_cast<St7789Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static enum DisplayColorFormat st7789_get_color_format(Device* device) {
+49 -10
View File
@@ -32,6 +32,7 @@
static const uint8_t GAMMA_CURVE_VALUES[4] = { 0x01, 0x04, 0x02, 0x08 };
struct St7796Internal {
Device* spi_controller;
esp_lcd_panel_io_handle_t io_handle;
esp_lcd_panel_handle_t panel_handle;
// Given from ISR context by on_color_trans_done() once a queued SPI transfer physically
@@ -74,6 +75,7 @@ static error_t start(Device* device) {
return ERROR_OUT_OF_MEMORY;
}
internal->spi_controller = parent;
internal->draw_done_semaphore = xSemaphoreCreateBinary();
if (internal->draw_done_semaphore == nullptr) {
free(internal);
@@ -134,6 +136,7 @@ static error_t start(Device* device) {
// Bring-up sequence, order matches EspLcdDisplayV2::applyConfiguration (proven correct on real ST7796 panels).
// Every failure path below must clean up fully: unlike stop_device, this is never retried by the kernel
// if start_device fails (see device_start() in TactilityKernel), so a partial failure here would leak.
spi_controller_lock(internal->spi_controller);
bool ok =
esp_lcd_panel_reset(internal->panel_handle) == ESP_OK &&
esp_lcd_panel_init(internal->panel_handle) == ESP_OK &&
@@ -151,6 +154,7 @@ static error_t start(Device* device) {
ok = ok && (!config->invert_color || esp_lcd_panel_invert_color(internal->panel_handle, true) == ESP_OK);
ok = ok && (config->gamma_curve >= 4 || esp_lcd_panel_io_tx_param(internal->io_handle, LCD_CMD_GAMSET, &GAMMA_CURVE_VALUES[config->gamma_curve], 1) == ESP_OK);
ok = ok && esp_lcd_panel_disp_on_off(internal->panel_handle, true) == ESP_OK;
spi_controller_unlock(internal->spi_controller);
if (!ok) {
LOG_E(TAG, "Failed to bring up panel");
@@ -168,9 +172,11 @@ static error_t start(Device* device) {
static error_t stop(Device* device) {
auto* internal = static_cast<St7796Internal*>(device_get_driver_data(device));
spi_controller_lock(internal->spi_controller);
if (internal->panel_handle != nullptr) {
if (esp_lcd_panel_del(internal->panel_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->panel_handle = nullptr;
@@ -179,10 +185,12 @@ static error_t stop(Device* device) {
if (internal->io_handle != nullptr) {
if (esp_lcd_panel_io_del(internal->io_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel IO");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->io_handle = nullptr;
}
spi_controller_unlock(internal->spi_controller);
vSemaphoreDelete(internal->draw_done_semaphore);
free(internal);
@@ -196,12 +204,18 @@ static error_t stop(Device* device) {
static error_t st7796_reset(Device* device) {
auto* internal = static_cast<St7796Internal*>(device_get_driver_data(device));
return esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7796_init(Device* device) {
auto* internal = static_cast<St7796Internal*>(device_get_driver_data(device));
return esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7796_draw_bitmap(Device* device, int32_t x_start, int32_t y_start, int32_t x_end, int32_t y_end, const void* color_data) {
@@ -212,26 +226,39 @@ static error_t st7796_draw_bitmap(Device* device, int32_t x_start, int32_t y_sta
// satisfied by this draw's own transfer completing.
xSemaphoreTake(internal->draw_done_semaphore, 0);
if (esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data) != ESP_OK) {
spi_controller_lock(internal->spi_controller);
esp_err_t ret = esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data);
if (ret != ESP_OK) {
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
// Block until the SPI transfer physically completes: DisplayApi's draw_bitmap is a synchronous
// contract (see lvgl_display.c), so the caller must be able to safely reuse/overwrite
// color_data as soon as this call returns. esp_lcd_panel_draw_bitmap() only queues the
// transfer and returns once it's handed to the SPI peripheral, not once it's finished.
// transfer and returns once it's handed to the SPI peripheral, not once it's finished. Hold the
// bus lock across the wait too, not just the queueing call: the command/data phases aren't
// wrapped in their own acquire_bus by esp_lcd_panel_io_spi, so another bus user could otherwise
// interleave with this transfer while it's still in flight.
xSemaphoreTake(internal->draw_done_semaphore, portMAX_DELAY);
spi_controller_unlock(internal->spi_controller);
return ERROR_NONE;
}
static error_t st7796_mirror(Device* device, bool x_axis, bool y_axis) {
auto* internal = static_cast<St7796Internal*>(device_get_driver_data(device));
return esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7796_swap_xy(Device* device, bool swap_axes) {
auto* internal = static_cast<St7796Internal*>(device_get_driver_data(device));
return esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// Reads the devicetree-configured baseline, not live hardware state: swap_xy()/mirror() calls made after
@@ -250,7 +277,10 @@ static bool st7796_get_mirror_y(Device* device) {
static error_t st7796_set_gap(Device* device, int32_t x_gap, int32_t y_gap) {
auto* internal = static_cast<St7796Internal*>(device_get_driver_data(device));
return esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// Reads the devicetree-configured baseline, not live hardware state - see DisplayApi::get_gap_x().
@@ -264,17 +294,26 @@ static int32_t st7796_get_gap_y(Device* device) {
static error_t st7796_invert_color(Device* device, bool invert_color_data) {
auto* internal = static_cast<St7796Internal*>(device_get_driver_data(device));
return esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7796_disp_on_off(Device* device, bool on_off) {
auto* internal = static_cast<St7796Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t st7796_disp_sleep(Device* device, bool sleep) {
auto* internal = static_cast<St7796Internal*>(device_get_driver_data(device));
return esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
// bgr_order only selects the panel controller's rgb_ele_order (applied in start(), above) so the
+29 -3
View File
@@ -28,6 +28,7 @@
#define POWER_SUPPLY_MIN_MV 3200
struct Xpt2046Internal {
Device* spi_controller;
esp_lcd_panel_io_handle_t io_handle;
esp_lcd_touch_handle_t touch_handle;
Device* power_supply_device;
@@ -74,7 +75,9 @@ static error_t ps_get_property(Device* device, PowerSupplyProperty property, Pow
auto* parent_internal = static_cast<Xpt2046Internal*>(device_get_driver_data(parent));
int battery_mv;
spi_controller_lock(parent_internal->spi_controller);
error_t error = read_battery_mv(parent_internal->touch_handle, &battery_mv);
spi_controller_unlock(parent_internal->spi_controller);
if (error != ERROR_NONE) {
return error;
}
@@ -181,8 +184,12 @@ static error_t start(Device* device) {
return ERROR_OUT_OF_MEMORY;
}
internal->spi_controller = parent;
const esp_lcd_panel_io_spi_config_t io_config = ESP_LCD_TOUCH_IO_SPI_XPT2046_CONFIG(cs_pin.pin);
spi_controller_lock(internal->spi_controller);
esp_err_t ret = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)spi_config->host, &io_config, &internal->io_handle);
spi_controller_unlock(internal->spi_controller);
if (ret != ESP_OK) {
LOG_E(TAG, "Failed to create panel IO: %s", esp_err_to_name(ret));
free(internal);
@@ -209,10 +216,14 @@ static error_t start(Device* device) {
.driver_data = nullptr,
};
spi_controller_lock(internal->spi_controller);
ret = esp_lcd_touch_new_spi_xpt2046(internal->io_handle, &touch_config, &internal->touch_handle);
spi_controller_unlock(internal->spi_controller);
if (ret != ESP_OK) {
LOG_E(TAG, "Failed to create touch handle: %s", esp_err_to_name(ret));
spi_controller_lock(internal->spi_controller);
esp_lcd_panel_io_del(internal->io_handle);
spi_controller_unlock(internal->spi_controller);
free(internal);
return ERROR_RESOURCE;
}
@@ -224,8 +235,10 @@ static error_t start(Device* device) {
error_t error = create_power_supply_child(device, internal->power_supply_device);
if (error != ERROR_NONE) {
LOG_E(TAG, "Failed to create power-supply device");
spi_controller_lock(internal->spi_controller);
esp_lcd_touch_del(internal->touch_handle);
esp_lcd_panel_io_del(internal->io_handle);
spi_controller_unlock(internal->spi_controller);
free(internal);
return error;
}
@@ -244,9 +257,11 @@ static error_t stop(Device* device) {
// esp_lcd_touch_del() only releases the touch-side resources; the panel IO handle is owned
// separately and needs its own deletion.
spi_controller_lock(internal->spi_controller);
if (internal->touch_handle != nullptr) {
if (esp_lcd_touch_del(internal->touch_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete touch handle");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->touch_handle = nullptr;
@@ -255,10 +270,12 @@ static error_t stop(Device* device) {
if (internal->io_handle != nullptr) {
if (esp_lcd_panel_io_del(internal->io_handle) != ESP_OK) {
LOG_E(TAG, "Failed to delete panel IO handle");
spi_controller_unlock(internal->spi_controller);
return ERROR_RESOURCE;
}
internal->io_handle = nullptr;
}
spi_controller_unlock(internal->spi_controller);
free(internal);
device_set_driver_data(device, nullptr);
@@ -271,18 +288,27 @@ static error_t stop(Device* device) {
static error_t xpt2046_enter_sleep(Device* device) {
auto* internal = static_cast<Xpt2046Internal*>(device_get_driver_data(device));
return esp_lcd_touch_enter_sleep(internal->touch_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_touch_enter_sleep(internal->touch_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t xpt2046_exit_sleep(Device* device) {
auto* internal = static_cast<Xpt2046Internal*>(device_get_driver_data(device));
return esp_lcd_touch_exit_sleep(internal->touch_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_touch_exit_sleep(internal->touch_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static error_t xpt2046_read_data(Device* device, TickType_t timeout) {
(void)timeout; // esp_lcd_touch_read_data() has no timeout parameter
auto* internal = static_cast<Xpt2046Internal*>(device_get_driver_data(device));
return esp_lcd_touch_read_data(internal->touch_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_lock(internal->spi_controller);
error_t result = esp_lcd_touch_read_data(internal->touch_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
spi_controller_unlock(internal->spi_controller);
return result;
}
static bool xpt2046_get_touched_points(Device* device, uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* point_count, uint8_t max_point_count) {