diff --git a/Devices/guition-jc1060p470ciwy/device.properties b/Devices/guition-jc1060p470ciwy/device.properties
index 601c0ff06..eaee031d3 100644
--- a/Devices/guition-jc1060p470ciwy/device.properties
+++ b/Devices/guition-jc1060p470ciwy/device.properties
@@ -24,3 +24,5 @@ sdkconfig.CONFIG_ESP_HOSTED_P4_DEV_BOARD_FUNC_BOARD=y
sdkconfig.CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE=y
sdkconfig.CONFIG_SLAVE_IDF_TARGET_ESP32C6=y
sdkconfig.CONFIG_ESP_HOSTED_USE_MEMPOOL=n
+sdkconfig.CONFIG_ESP_HOSTED_ENABLE_PEER_DATA_TRANSFER=y
+sdkconfig.CONFIG_ESP_HOSTED_MAX_CUSTOM_MSG_HANDLERS=8
diff --git a/Devices/guition-jc4880p443ciw/LICENSE-Apache-2.0.md b/Devices/guition-jc4880p443ciw/LICENSE-Apache-2.0.md
new file mode 100644
index 000000000..f5f4b8b5e
--- /dev/null
+++ b/Devices/guition-jc4880p443ciw/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/Devices/guition-jc4880p443ciw/device.properties b/Devices/guition-jc4880p443ciw/device.properties
new file mode 100644
index 000000000..08d38fa4e
--- /dev/null
+++ b/Devices/guition-jc4880p443ciw/device.properties
@@ -0,0 +1,27 @@
+general.vendor=Guition
+general.name=JC4880P443C-I-W
+hardware.target=ESP32P4
+hardware.flashSize=16MB
+hardware.spiRam=true
+hardware.spiRamMode=HEX
+hardware.spiRamSpeed=200M
+hardware.esptoolFlashFreq=80M
+hardware.bluetooth=true
+storage.userDataLocation=SD
+display.size=4.3"
+display.dpi=217
+
+lvgl.fontSize=20
+lvgl.colorDepth=16
+sdkconfig.CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
+sdkconfig.CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
+sdkconfig.CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
+sdkconfig.CONFIG_ESP_HOSTED_ENABLED=y
+sdkconfig.CONFIG_ESP_HOSTED_P4_DEV_BOARD_FUNC_BOARD=y
+sdkconfig.CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE=y
+sdkconfig.CONFIG_SLAVE_IDF_TARGET_ESP32C6=y
+sdkconfig.CONFIG_ESP_HOSTED_USE_MEMPOOL=n
+
+# ESP-NOW bridge support
+sdkconfig.CONFIG_ESP_HOSTED_ENABLE_PEER_DATA_TRANSFER=y
+sdkconfig.CONFIG_ESP_HOSTED_MAX_CUSTOM_MSG_HANDLERS=8
diff --git a/Devices/guition-jc4880p443ciw/guition,jc4880p443ciw.dts b/Devices/guition-jc4880p443ciw/guition,jc4880p443ciw.dts
new file mode 100644
index 000000000..8a55ba8b9
--- /dev/null
+++ b/Devices/guition-jc4880p443ciw/guition,jc4880p443ciw.dts
@@ -0,0 +1,174 @@
+/dts-v1/;
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+/ {
+ compatible = "root";
+ model = "Guition JC4880P443C-I-W";
+
+ wifi0 {
+ compatible = "espressif,esp32-wifi";
+ };
+
+ ble0 {
+ compatible = "espressif,esp32-ble";
+ };
+
+ gpio0 {
+ compatible = "espressif,esp32-gpio";
+ gpio-count = <55>;
+ };
+
+ // Speaker and microphone (ES8311)
+ i2s0 {
+ compatible = "espressif,esp32-i2s";
+ port = ;
+ pin-bclk = <&gpio0 12 GPIO_FLAG_NONE>;
+ pin-ws = <&gpio0 10 GPIO_FLAG_NONE>;
+ pin-data-out = <&gpio0 9 GPIO_FLAG_NONE>;
+ pin-data-in = <&gpio0 48 GPIO_FLAG_NONE>;
+ pin-mclk = <&gpio0 13 GPIO_FLAG_NONE>;
+ };
+
+ i2c_internal: i2c0 {
+ compatible = "espressif,esp32-i2c-master";
+ port = ;
+ clock-frequency = <400000>;
+ pin-sda = <&gpio0 7 GPIO_FLAG_NONE>;
+ pin-scl = <&gpio0 8 GPIO_FLAG_NONE>;
+
+ touch0: gt911@5D {
+ compatible = "goodix,gt911";
+ reg = <0x5D>;
+ x-max = <480>;
+ y-max = <800>;
+ status = "okay";
+ };
+
+ es8311: es8311@18 {
+ compatible = "everest,es8311";
+ reg = <0x18>;
+ i2s = <&i2s0>;
+ };
+ es7243e: es7243e@14 {
+ compatible = "everest,es7243e";
+ reg = <0x14>;
+ i2s = <&i2s0>;
+ };
+ };
+
+ sdcard0: sdcard0 {
+ compatible = "espressif,esp32-sdmmc";
+ slot = <0>;
+ bus-width = <4>;
+ pin-clk = <&gpio0 43 GPIO_FLAG_NONE>;
+ pin-cmd = <&gpio0 44 GPIO_FLAG_NONE>;
+ pin-d0 = <&gpio0 39 GPIO_FLAG_NONE>;
+ pin-d1 = <&gpio0 40 GPIO_FLAG_NONE>;
+ pin-d2 = <&gpio0 41 GPIO_FLAG_NONE>;
+ pin-d3 = <&gpio0 42 GPIO_FLAG_NONE>;
+ on-chip-ldo-chan = <4>;
+ };
+
+ display_backlight_pwm {
+ compatible = "espressif,esp32-pwm-ledc";
+ pin = <&gpio0 23 GPIO_FLAG_NONE>;
+ period-ns = <200000>;
+ ledc-timer = <0>;
+ ledc-channel = <0>;
+ status = "okay";
+ };
+
+ display_backlight {
+ compatible = "pwm-backlight";
+ pwm = <&display_backlight_pwm>;
+ status = "okay";
+ };
+
+ // Speaker PA enable (GPIO11 active high)
+ pa_enable {
+ compatible = "gpio-hog";
+ pin = <&gpio0 11 GPIO_FLAG_NONE>;
+ mode = ;
+ status = "okay";
+ };
+
+ display0: st7701-dsi@0 {
+ compatible = "sitronix,st7701-dsi";
+ horizontal-resolution = <480>;
+ vertical-resolution = <800>;
+ bits-per-pixel = <16>;
+ pin-reset = <&gpio0 5 GPIO_FLAG_NONE>;
+ ldo-channel = <3>;
+ ldo-voltage-mv = <2500>;
+ dsi-bus-id = <0>;
+ num-data-lanes = <2>;
+ lane-bit-rate-mbps = <500>;
+ dpi-clock-freq-mhz = <34>;
+ hsync-pulse-width = <12>;
+ hsync-back-porch = <42>;
+ hsync-front-porch = <42>;
+ vsync-pulse-width = <2>;
+ vsync-back-porch = <8>;
+ vsync-front-porch = <166>;
+ num-fbs = <2>;
+ use-dma2d;
+ allow-tearing;
+ backlight = <&display_backlight>;
+ init-sequence = [
+ 0xFF 0x05 0x00 0x77 0x01 0x00 0x00 0x13
+ 0xEF 0x01 0x00 0x08
+ 0xFF 0x05 0x00 0x77 0x01 0x00 0x00 0x10
+ 0xC0 0x02 0x00 0x63 0x00
+ 0xC1 0x02 0x00 0x0D 0x02
+ 0xC2 0x02 0x00 0x10 0x08
+ 0xCC 0x01 0x00 0x10
+ 0xB0 0x10 0x00 0x80 0x09 0x53 0x0C 0xD0 0x07 0x0C 0x09 0x09 0x28 0x06 0xD4 0x13 0x69 0x2B 0x71
+ 0xB1 0x10 0x00 0x80 0x94 0x5A 0x10 0xD3 0x06 0x0A 0x08 0x08 0x25 0x03 0xD3 0x12 0x66 0x6A 0x0D
+ 0xFF 0x05 0x00 0x77 0x01 0x00 0x00 0x11
+ 0xB0 0x01 0x00 0x5D
+ 0xB1 0x01 0x00 0x58
+ 0xB2 0x01 0x00 0x87
+ 0xB3 0x01 0x00 0x80
+ 0xB5 0x01 0x00 0x4E
+ 0xB7 0x01 0x00 0x85
+ 0xB8 0x01 0x00 0x21
+ 0xB9 0x02 0x00 0x10 0x1F
+ 0xBB 0x01 0x00 0x03
+ 0xBC 0x01 0x00 0x00
+ 0xC1 0x01 0x00 0x78
+ 0xC2 0x01 0x00 0x78
+ 0xD0 0x01 0x00 0x88
+ 0xE0 0x03 0x00 0x00 0x3A 0x02
+ 0xE1 0x0B 0x00 0x04 0xA0 0x00 0xA0 0x05 0xA0 0x00 0xA0 0x00 0x40 0x40
+ 0xE2 0x0D 0x00 0x30 0x00 0x40 0x40 0x32 0xA0 0x00 0xA0 0x00 0xA0 0x00 0xA0 0x00
+ 0xE3 0x04 0x00 0x00 0x00 0x33 0x33
+ 0xE4 0x02 0x00 0x44 0x44
+ 0xE5 0x10 0x00 0x09 0x2E 0xA0 0xA0 0x0B 0x30 0xA0 0xA0 0x05 0x2A 0xA0 0xA0 0x07 0x2C 0xA0 0xA0
+ 0xE6 0x04 0x00 0x00 0x00 0x33 0x33
+ 0xE7 0x02 0x00 0x44 0x44
+ 0xE8 0x10 0x00 0x08 0x2D 0xA0 0xA0 0x0A 0x2F 0xA0 0xA0 0x04 0x29 0xA0 0xA0 0x06 0x2B 0xA0 0xA0
+ 0xEB 0x07 0x00 0x00 0x00 0x4E 0x4E 0x00 0x00 0x00
+ 0xEC 0x02 0x00 0x08 0x01
+ 0xED 0x10 0x00 0xB0 0x2B 0x98 0xA4 0x56 0x7F 0xFF 0xFF 0xFF 0xFF 0xF7 0x65 0x4A 0x89 0xB2 0x0B
+ 0xEF 0x06 0x00 0x08 0x08 0x08 0x45 0x3F 0x54
+ 0xFF 0x05 0x00 0x77 0x01 0x00 0x00 0x00
+ 0x11 0x01 0x78 0x00
+ 0x29 0x01 0x14 0x00
+ ];
+ status = "okay";
+ };
+};
diff --git a/Devices/guition-jc4880p443ciw/module.yaml b/Devices/guition-jc4880p443ciw/module.yaml
new file mode 100644
index 000000000..39a0a524c
--- /dev/null
+++ b/Devices/guition-jc4880p443ciw/module.yaml
@@ -0,0 +1,9 @@
+dependencies:
+ - Platforms/platform-esp32
+ - Drivers/st7701-module
+ - Drivers/gt911-module
+ - Drivers/es8311-module
+ - Drivers/audio-stream-module
+ - Drivers/es7243e-module
+dts: guition,jc4880p443ciw.dts
+dts-only: true
diff --git a/Drivers/st7701-module/CMakeLists.txt b/Drivers/st7701-module/CMakeLists.txt
index d3c19e8bf..028596d96 100644
--- a/Drivers/st7701-module/CMakeLists.txt
+++ b/Drivers/st7701-module/CMakeLists.txt
@@ -7,5 +7,5 @@ file(GLOB_RECURSE SOURCE_FILES "source/*.c*")
tactility_add_module(st7701-module
SRCS ${SOURCE_FILES}
INCLUDE_DIRS include/
- REQUIRES TactilityKernel platform-esp32 esp_lcd_st7701 esp_lcd_panel_io_additions esp_lcd driver
+ REQUIRES TactilityKernel platform-esp32 esp_lcd_st7701 esp_lcd_panel_io_additions esp_lcd driver esp_hw_support
)
diff --git a/Drivers/st7701-module/bindings/sitronix,st7701-dsi.yaml b/Drivers/st7701-module/bindings/sitronix,st7701-dsi.yaml
new file mode 100644
index 000000000..058d01f82
--- /dev/null
+++ b/Drivers/st7701-module/bindings/sitronix,st7701-dsi.yaml
@@ -0,0 +1,122 @@
+description: >
+ Sitronix ST7701S MIPI-DSI display panel, driven over ESP-IDF's esp_lcd_st7701 component (DPI/DBI
+ interface, ESP32-P4 and other SOC_MIPI_DSI_SUPPORTED targets only). Owns the MIPI DSI PHY LDO
+ channel and DSI bus directly, so unlike SPI/RGB panels it has no parent bus controller device.
+
+compatible: "sitronix,st7701-dsi"
+
+properties:
+ horizontal-resolution:
+ type: int
+ required: true
+ description: Horizontal resolution in pixels
+ vertical-resolution:
+ type: int
+ required: true
+ description: Vertical resolution in pixels
+ bits-per-pixel:
+ type: int
+ default: 16
+ description: Color depth in bits per pixel (16 or 24)
+ bgr-order:
+ type: boolean
+ default: false
+ description: Use BGR element order instead of RGB
+ invert-color:
+ type: boolean
+ default: false
+ description: Invert the panel's color output
+ mirror-x:
+ type: boolean
+ default: false
+ description: Mirror the X axis
+ mirror-y:
+ type: boolean
+ default: false
+ description: Mirror the Y axis
+ pin-reset:
+ type: phandles
+ default: GPIO_PIN_SPEC_NONE
+ description: Reset GPIO pin. Falls back to a software reset (sent over the DBI command
+ interface) if not set.
+ ldo-channel:
+ type: int
+ required: true
+ description: LDO channel index powering the MIPI DSI PHY (chan_id in esp_ldo_channel_config_t)
+ ldo-voltage-mv:
+ type: int
+ required: true
+ description: Voltage to supply to the MIPI DSI PHY LDO channel, in mV
+ dsi-bus-id:
+ type: int
+ default: 0
+ description: Which DSI controller to use, index from 0
+ num-data-lanes:
+ type: int
+ default: 2
+ description: Number of MIPI DSI data lanes. 0 falls back to the maximum available.
+ lane-bit-rate-mbps:
+ type: int
+ default: 500
+ description: MIPI DSI lane bit rate, in Mbps
+ dpi-clock-freq-mhz:
+ type: int
+ required: true
+ description: MIPI DPI clock frequency, in MHz
+ hsync-pulse-width:
+ type: int
+ required: true
+ description: Horizontal sync width, in PCLK periods
+ hsync-back-porch:
+ type: int
+ required: true
+ description: Number of PCLK periods between hsync and the start of line active data
+ hsync-front-porch:
+ type: int
+ required: true
+ description: Number of PCLK periods between the end of active data and the next hsync
+ vsync-pulse-width:
+ type: int
+ required: true
+ description: Vertical sync width, in lines
+ vsync-back-porch:
+ type: int
+ required: true
+ description: Number of invalid lines between vsync and the start of the frame
+ vsync-front-porch:
+ type: int
+ required: true
+ description: Number of invalid lines between the end of the frame and the next vsync
+ num-fbs:
+ type: int
+ default: 2
+ description: Number of screen-sized frame buffers to allocate (0 or 1 = single-buffered)
+ use-dma2d:
+ type: boolean
+ default: true
+ description: Use DMA2D to copy user buffers into the frame buffer when necessary (only
+ meaningful on SOC_DMA2D_SUPPORTED targets - must be false otherwise)
+ disable-lp:
+ type: boolean
+ default: false
+ description: Disable MIPI DSI low-power mode
+ allow-tearing:
+ type: boolean
+ default: false
+ description: By default, when draw_bitmap's color_data is one of the panel's own frame
+ buffers (i.e. LVGL is bound directly onto them), draw_bitmap waits for a full scan-out to
+ complete before returning so the caller can't start overwriting a buffer still being
+ displayed. Set this to trade away that tear-free guarantee for lower latency (e.g. when
+ other tasks occasionally block timing for long enough that waiting causes visible stalls).
+ init-sequence:
+ type: array
+ element-type: uint8_t
+ description: >
+ Custom vendor bring-up sequence, flattened into bytes as a run of
+ [cmd, data-length, delay-ms, data-length bytes of data...] entries, e.g.
+ `init-sequence = [0x11 0 120 0x29 0 20];`.
+ Omit to use the ST7701 component's own built-in default sequence.
+ backlight:
+ type: phandle
+ default: "NULL"
+ description: Optional reference to this display's backlight device
diff --git a/Drivers/st7701-module/include/bindings/st7701_dsi.h b/Drivers/st7701-module/include/bindings/st7701_dsi.h
new file mode 100644
index 000000000..09f57a9de
--- /dev/null
+++ b/Drivers/st7701-module/include/bindings/st7701_dsi.h
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: Apache-2.0
+#pragma once
+
+#include
+#include
+
+DEFINE_DEVICETREE(st7701_dsi, struct St7701DsiConfig)
diff --git a/Drivers/st7701-module/include/drivers/st7701_dsi.h b/Drivers/st7701-module/include/drivers/st7701_dsi.h
new file mode 100644
index 000000000..caca1e0f8
--- /dev/null
+++ b/Drivers/st7701-module/include/drivers/st7701_dsi.h
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: Apache-2.0
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+
+#include
+#include
+
+struct St7701DsiConfig {
+ uint16_t horizontal_resolution;
+ uint16_t vertical_resolution;
+ uint8_t bits_per_pixel;
+ bool bgr_order;
+ bool invert_color;
+ bool mirror_x;
+ bool mirror_y;
+
+ struct GpioPinSpec pin_reset;
+
+ int32_t ldo_channel;
+ int32_t ldo_voltage_mv;
+
+ uint8_t dsi_bus_id;
+ uint8_t num_data_lanes;
+ uint32_t lane_bit_rate_mbps;
+
+ uint32_t dpi_clock_freq_mhz;
+ uint32_t hsync_pulse_width;
+ uint32_t hsync_back_porch;
+ uint32_t hsync_front_porch;
+ uint32_t vsync_pulse_width;
+ uint32_t vsync_back_porch;
+ uint32_t vsync_front_porch;
+ uint8_t num_fbs;
+ bool use_dma2d;
+ bool disable_lp;
+
+ bool allow_tearing;
+
+ const uint8_t* init_sequence;
+ uint32_t init_sequence_length;
+
+ struct Device* backlight;
+};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Drivers/st7701-module/source/module.cpp b/Drivers/st7701-module/source/module.cpp
index 88c835bf6..ebef16e65 100644
--- a/Drivers/st7701-module/source/module.cpp
+++ b/Drivers/st7701-module/source/module.cpp
@@ -4,10 +4,18 @@
extern "C" {
+#include
+
extern Driver st7701_driver;
+#if SOC_MIPI_DSI_SUPPORTED
+extern Driver st7701_dsi_driver;
+#endif
static Driver* const st7701_drivers[] = {
&st7701_driver,
+#if SOC_MIPI_DSI_SUPPORTED
+ &st7701_dsi_driver,
+#endif
nullptr
};
diff --git a/Drivers/st7701-module/source/st7701_dsi.cpp b/Drivers/st7701-module/source/st7701_dsi.cpp
new file mode 100644
index 000000000..280db017c
--- /dev/null
+++ b/Drivers/st7701-module/source/st7701_dsi.cpp
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: Apache-2.0
+#include
+#if SOC_MIPI_DSI_SUPPORTED
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+#define TAG "St7701Dsi"
+#define GET_CONFIG(device) (static_cast((device)->config))
+
+constexpr size_t MAX_CACHED_FRAME_BUFFERS = 2;
+
+struct St7701DsiInternal {
+ esp_ldo_channel_handle_t ldo_handle;
+ esp_lcd_dsi_bus_handle_t dsi_bus_handle;
+ esp_lcd_panel_io_handle_t io_handle;
+ esp_lcd_panel_handle_t panel_handle;
+ void* frame_buffers[MAX_CACHED_FRAME_BUFFERS];
+ uint8_t frame_buffer_count;
+ size_t frame_buffer_size_bytes;
+ SemaphoreHandle_t frame_complete_semaphore;
+ st7701_lcd_init_cmd_t* parsed_init_cmds;
+};
+
+static bool on_refresh_done(esp_lcd_panel_handle_t, esp_lcd_dpi_panel_event_data_t*, void* user_ctx) {
+ auto* internal = static_cast(user_ctx);
+ BaseType_t high_task_woken = pdFALSE;
+ xSemaphoreGiveFromISR(internal->frame_complete_semaphore, &high_task_woken);
+ return high_task_woken == pdTRUE;
+}
+
+static int pin_or_unused(const GpioPinSpec& pin) {
+ return pin.gpio_controller == nullptr ? -1 : static_cast(pin.pin);
+}
+
+static lcd_color_format_t color_format_from_bits_per_pixel(uint8_t bits_per_pixel) {
+ return bits_per_pixel == 24 ? LCD_COLOR_FMT_RGB888 : LCD_COLOR_FMT_RGB565;
+}
+
+static bool parse_init_sequence(const uint8_t* bytes, uint32_t length, st7701_lcd_init_cmd_t** out_cmds, uint16_t* out_count) {
+ uint32_t count = 0;
+ for (uint32_t offset = 0; offset < length; count++) {
+ if (offset + 3 > length) {
+ LOG_E(TAG, "init-sequence truncated: entry header runs past the end of the array");
+ return false;
+ }
+ offset += 3 + bytes[offset + 1];
+ if (offset > length) {
+ LOG_E(TAG, "init-sequence truncated: entry data runs past the end of the array");
+ return false;
+ }
+ }
+
+ auto* cmds = static_cast(malloc(count * sizeof(st7701_lcd_init_cmd_t)));
+ if (cmds == nullptr) {
+ return false;
+ }
+
+ uint32_t offset = 0;
+ for (uint32_t i = 0; i < count; i++) {
+ uint8_t data_len = bytes[offset + 1];
+ cmds[i] = {
+ .cmd = bytes[offset],
+ .data = data_len > 0 ? &bytes[offset + 3] : nullptr,
+ .data_bytes = data_len,
+ .delay_ms = bytes[offset + 2],
+ };
+ offset += 3 + data_len;
+ }
+
+ *out_cmds = cmds;
+ *out_count = (uint16_t)count;
+ return true;
+}
+
+
+static error_t start_device(Device* device) {
+ const auto* config = GET_CONFIG(device);
+ if (config->bits_per_pixel == 18) {
+ LOG_E(TAG, "18bpp (RGB666) is unsupported on this ESP-IDF version");
+ return ERROR_NOT_SUPPORTED;
+ }
+
+ auto* internal = static_cast(malloc(sizeof(St7701DsiInternal)));
+ if (internal == nullptr) {
+ return ERROR_OUT_OF_MEMORY;
+ }
+ internal->parsed_init_cmds = nullptr;
+
+ const st7701_lcd_init_cmd_t* init_cmds = nullptr;
+ uint16_t init_cmds_size = 0;
+ if (config->init_sequence != nullptr && config->init_sequence_length > 0) {
+ if (!parse_init_sequence(config->init_sequence, config->init_sequence_length, &internal->parsed_init_cmds, &init_cmds_size)) {
+ LOG_E(TAG, "Failed to parse init-sequence property");
+ free(internal);
+ return ERROR_INVALID_ARGUMENT;
+ }
+ init_cmds = internal->parsed_init_cmds;
+ }
+
+ esp_ldo_channel_config_t ldo_config = {
+ .chan_id = config->ldo_channel,
+ .voltage_mv = config->ldo_voltage_mv,
+ .flags = {},
+ };
+ if (esp_ldo_acquire_channel(&ldo_config, &internal->ldo_handle) != ESP_OK) {
+ LOG_E(TAG, "Failed to acquire LDO channel for MIPI DSI PHY");
+ free(internal->parsed_init_cmds);
+ free(internal);
+ return ERROR_RESOURCE;
+ }
+
+ const esp_lcd_dsi_bus_config_t bus_config = {
+ .bus_id = config->dsi_bus_id,
+ .num_data_lanes = config->num_data_lanes,
+ .phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
+ .lane_bit_rate_mbps = static_cast(config->lane_bit_rate_mbps),
+ };
+ if (esp_lcd_new_dsi_bus(&bus_config, &internal->dsi_bus_handle) != ESP_OK) {
+ LOG_E(TAG, "Failed to create MIPI DSI bus");
+ esp_ldo_release_channel(internal->ldo_handle);
+ free(internal->parsed_init_cmds);
+ free(internal);
+ return ERROR_RESOURCE;
+ }
+
+ const esp_lcd_dbi_io_config_t dbi_config = {
+ .virtual_channel = 0,
+ .lcd_cmd_bits = 8,
+ .lcd_param_bits = 8,
+ };
+ if (esp_lcd_new_panel_io_dbi(internal->dsi_bus_handle, &dbi_config, &internal->io_handle) != ESP_OK) {
+ LOG_E(TAG, "Failed to create panel IO");
+ esp_lcd_del_dsi_bus(internal->dsi_bus_handle);
+ esp_ldo_release_channel(internal->ldo_handle);
+ free(internal->parsed_init_cmds);
+ free(internal);
+ return ERROR_RESOURCE;
+ }
+
+ const lcd_color_format_t color_format = color_format_from_bits_per_pixel(config->bits_per_pixel);
+ const esp_lcd_dpi_panel_config_t dpi_config = {
+ .virtual_channel = 0,
+ .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
+ .dpi_clock_freq_mhz = static_cast(config->dpi_clock_freq_mhz),
+ .in_color_format = color_format,
+ .out_color_format = color_format,
+ .num_fbs = static_cast(config->num_fbs > MAX_CACHED_FRAME_BUFFERS ? MAX_CACHED_FRAME_BUFFERS : config->num_fbs),
+ .video_timing = {
+ .h_size = config->horizontal_resolution,
+ .v_size = config->vertical_resolution,
+ .hsync_pulse_width = config->hsync_pulse_width,
+ .hsync_back_porch = config->hsync_back_porch,
+ .hsync_front_porch = config->hsync_front_porch,
+ .vsync_pulse_width = config->vsync_pulse_width,
+ .vsync_back_porch = config->vsync_back_porch,
+ .vsync_front_porch = config->vsync_front_porch,
+ },
+ .flags = {
+ .disable_lp = config->disable_lp,
+ },
+ };
+
+ st7701_vendor_config_t vendor_config = {
+ .init_cmds = init_cmds,
+ .init_cmds_size = init_cmds_size,
+ .mipi_config = {
+ .dsi_bus = internal->dsi_bus_handle,
+ .dpi_config = &dpi_config,
+ },
+ .flags = {
+ .use_mipi_interface = 1,
+ },
+ };
+
+ const esp_lcd_panel_dev_config_t panel_config = {
+ .rgb_ele_order = config->bgr_order ? LCD_RGB_ELEMENT_ORDER_BGR : LCD_RGB_ELEMENT_ORDER_RGB,
+ .data_endian = LCD_RGB_DATA_ENDIAN_LITTLE,
+ .bits_per_pixel = config->bits_per_pixel,
+ .reset_gpio_num = static_cast(pin_or_unused(config->pin_reset)),
+ .vendor_config = &vendor_config,
+ .flags = { .reset_active_high = false },
+ };
+
+ if (esp_lcd_new_panel_st7701(internal->io_handle, &panel_config, &internal->panel_handle) != ESP_OK) {
+ LOG_E(TAG, "Failed to create panel");
+ esp_lcd_panel_io_del(internal->io_handle);
+ esp_lcd_del_dsi_bus(internal->dsi_bus_handle);
+ esp_ldo_release_channel(internal->ldo_handle);
+ free(internal->parsed_init_cmds);
+ free(internal);
+ return ERROR_RESOURCE;
+ }
+
+ if (config->use_dma2d) {
+ esp_lcd_dpi_panel_enable_dma2d(internal->panel_handle);
+ }
+
+ bool ok =
+ esp_lcd_panel_reset(internal->panel_handle) == ESP_OK &&
+ esp_lcd_panel_init(internal->panel_handle) == ESP_OK &&
+ esp_lcd_panel_invert_color(internal->panel_handle, config->invert_color) == ESP_OK &&
+ esp_lcd_panel_mirror(internal->panel_handle, config->mirror_x, config->mirror_y) == ESP_OK &&
+ esp_lcd_panel_disp_on_off(internal->panel_handle, true) == ESP_OK;
+
+ if (!ok) {
+ LOG_E(TAG, "Failed to bring up panel");
+ esp_lcd_panel_del(internal->panel_handle);
+ esp_lcd_panel_io_del(internal->io_handle);
+ esp_lcd_del_dsi_bus(internal->dsi_bus_handle);
+ esp_ldo_release_channel(internal->ldo_handle);
+ free(internal->parsed_init_cmds);
+ free(internal);
+ return ERROR_RESOURCE;
+ }
+
+ internal->frame_buffer_count = 0;
+ internal->frame_buffer_size_bytes = (size_t)config->horizontal_resolution * config->vertical_resolution *
+ ((config->bits_per_pixel + 7) / 8);
+ if (config->num_fbs > 0) {
+ size_t fb_num = config->num_fbs < MAX_CACHED_FRAME_BUFFERS ? config->num_fbs : MAX_CACHED_FRAME_BUFFERS;
+ esp_err_t ret;
+ switch (fb_num) {
+ case 1:
+ ret = esp_lcd_dpi_panel_get_frame_buffer(internal->panel_handle, 1, &internal->frame_buffers[0]);
+ break;
+ case 2:
+ ret = esp_lcd_dpi_panel_get_frame_buffer(internal->panel_handle, 2, &internal->frame_buffers[0], &internal->frame_buffers[1]);
+ break;
+ default:
+ ret = ESP_OK;
+ break;
+ }
+
+ if (ret != ESP_OK) {
+ LOG_E(TAG, "Failed to get frame buffer(s): %s", esp_err_to_name(ret));
+ esp_lcd_panel_del(internal->panel_handle);
+ esp_lcd_panel_io_del(internal->io_handle);
+ esp_lcd_del_dsi_bus(internal->dsi_bus_handle);
+ esp_ldo_release_channel(internal->ldo_handle);
+ free(internal->parsed_init_cmds);
+ free(internal);
+ return ERROR_RESOURCE;
+ }
+ internal->frame_buffer_count = (uint8_t)fb_num;
+ }
+
+ internal->frame_complete_semaphore = xSemaphoreCreateBinary();
+ if (internal->frame_complete_semaphore == nullptr) {
+ esp_lcd_panel_del(internal->panel_handle);
+ esp_lcd_panel_io_del(internal->io_handle);
+ esp_lcd_del_dsi_bus(internal->dsi_bus_handle);
+ esp_ldo_release_channel(internal->ldo_handle);
+ free(internal->parsed_init_cmds);
+ free(internal);
+ return ERROR_OUT_OF_MEMORY;
+ }
+
+ esp_lcd_dpi_panel_event_callbacks_t callbacks = {};
+ callbacks.on_frame_buf_complete = on_refresh_done;
+ if (esp_lcd_dpi_panel_register_event_callbacks(internal->panel_handle, &callbacks, internal) != ESP_OK) {
+ LOG_E(TAG, "Failed to register panel event callbacks");
+ vSemaphoreDelete(internal->frame_complete_semaphore);
+ esp_lcd_panel_del(internal->panel_handle);
+ esp_lcd_panel_io_del(internal->io_handle);
+ esp_lcd_del_dsi_bus(internal->dsi_bus_handle);
+ esp_ldo_release_channel(internal->ldo_handle);
+ free(internal->parsed_init_cmds);
+ free(internal);
+ return ERROR_RESOURCE;
+ }
+
+ device_set_driver_data(device, internal);
+ return ERROR_NONE;
+}
+
+static error_t stop_device(Device* device) {
+ auto* internal = static_cast(device_get_driver_data(device));
+
+ if (internal->panel_handle != nullptr) {
+ if (esp_lcd_panel_del(internal->panel_handle) != ESP_OK) {
+ LOG_E(TAG, "Failed to delete panel");
+ return ERROR_RESOURCE;
+ }
+ internal->panel_handle = nullptr;
+ }
+
+ if (internal->io_handle != nullptr) {
+ if (esp_lcd_panel_io_del(internal->io_handle) != ESP_OK) {
+ LOG_E(TAG, "Failed to delete panel IO");
+ return ERROR_RESOURCE;
+ }
+ internal->io_handle = nullptr;
+ }
+
+ if (internal->dsi_bus_handle != nullptr) {
+ if (esp_lcd_del_dsi_bus(internal->dsi_bus_handle) != ESP_OK) {
+ LOG_E(TAG, "Failed to delete DSI bus");
+ return ERROR_RESOURCE;
+ }
+ internal->dsi_bus_handle = nullptr;
+ }
+
+ if (internal->ldo_handle != nullptr) {
+ if (esp_ldo_release_channel(internal->ldo_handle) != ESP_OK) {
+ LOG_E(TAG, "Failed to release LDO channel");
+ return ERROR_RESOURCE;
+ }
+ internal->ldo_handle = nullptr;
+ }
+
+ vSemaphoreDelete(internal->frame_complete_semaphore);
+ free(internal->parsed_init_cmds);
+ free(internal);
+ device_set_driver_data(device, nullptr);
+ return ERROR_NONE;
+}
+
+
+
+static error_t st7701_dsi_reset(Device* device) {
+ auto* internal = static_cast(device_get_driver_data(device));
+ return esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
+}
+
+static error_t st7701_dsi_init(Device* device) {
+ auto* internal = static_cast(device_get_driver_data(device));
+ return esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
+}
+
+static bool st7701_dsi_color_data_is_frame_buffer(const St7701DsiInternal* internal, const void* color_data) {
+ const auto* ptr = static_cast(color_data);
+ for (uint8_t i = 0; i < internal->frame_buffer_count; i++) {
+ const auto* base = static_cast(internal->frame_buffers[i]);
+ if (ptr >= base && ptr < base + internal->frame_buffer_size_bytes) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static error_t st7701_dsi_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));
+
+ bool wait_for_scanout = !GET_CONFIG(device)->allow_tearing && st7701_dsi_color_data_is_frame_buffer(internal, color_data);
+ if (wait_for_scanout) {
+ xSemaphoreTake(internal->frame_complete_semaphore, 0);
+ }
+
+ if (esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data) != ESP_OK) {
+ return ERROR_RESOURCE;
+ }
+
+ if (wait_for_scanout) {
+ xSemaphoreTake(internal->frame_complete_semaphore, portMAX_DELAY);
+ }
+
+ return ERROR_NONE;
+}
+
+static error_t st7701_dsi_mirror(Device* device, bool x_axis, bool y_axis) {
+ auto* internal = static_cast(device_get_driver_data(device));
+ return esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
+}
+
+static bool st7701_dsi_get_mirror_x(Device* device) {
+ return GET_CONFIG(device)->mirror_x;
+}
+
+static bool st7701_dsi_get_mirror_y(Device* device) {
+ return GET_CONFIG(device)->mirror_y;
+}
+
+
+static error_t st7701_dsi_invert_color(Device* device, bool invert_color_data) {
+ auto* internal = static_cast(device_get_driver_data(device));
+ return esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
+}
+
+static error_t st7701_dsi_disp_on_off(Device* device, bool on_off) {
+ auto* internal = static_cast(device_get_driver_data(device));
+ return esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
+}
+
+static error_t st7701_dsi_disp_sleep(Device* device, bool sleep) {
+ auto* internal = static_cast(device_get_driver_data(device));
+ return esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
+}
+
+static enum DisplayColorFormat st7701_dsi_get_color_format(Device* device) {
+ return GET_CONFIG(device)->bits_per_pixel == 24 ? DISPLAY_COLOR_FORMAT_RGB888 : DISPLAY_COLOR_FORMAT_RGB565;
+}
+
+static uint16_t st7701_dsi_get_resolution_x(Device* device) {
+ return GET_CONFIG(device)->horizontal_resolution;
+}
+
+static uint16_t st7701_dsi_get_resolution_y(Device* device) {
+ return GET_CONFIG(device)->vertical_resolution;
+}
+
+static void st7701_dsi_get_frame_buffer(Device* device, uint8_t index, void** out_buffer) {
+ auto* internal = static_cast(device_get_driver_data(device));
+ *out_buffer = index < internal->frame_buffer_count ? internal->frame_buffers[index] : nullptr;
+}
+
+static uint8_t st7701_dsi_get_frame_buffer_count(Device* device) {
+ auto* internal = static_cast(device_get_driver_data(device));
+ return internal->frame_buffer_count;
+}
+
+static error_t st7701_dsi_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;
+}
+
+
+static const DisplayApi st7701_dsi_display_api = {
+ .capabilities = DISPLAY_CAPABILITY_CAP_MIRROR | DISPLAY_CAPABILITY_INVERT_COLOR |
+ DISPLAY_CAPABILITY_ON_OFF | DISPLAY_CAPABILITY_BACKLIGHT | DISPLAY_CAPABILITY_SLEEP,
+ .reset = st7701_dsi_reset,
+ .init = st7701_dsi_init,
+ .draw_bitmap = st7701_dsi_draw_bitmap,
+ .clear = nullptr,
+ .refresh = nullptr,
+ .mirror = st7701_dsi_mirror,
+ .swap_xy = nullptr,
+ .get_swap_xy = nullptr,
+ .get_mirror_x = st7701_dsi_get_mirror_x,
+ .get_mirror_y = st7701_dsi_get_mirror_y,
+ .set_gap = nullptr,
+ .get_gap_x = nullptr,
+ .get_gap_y = nullptr,
+ .invert_color = st7701_dsi_invert_color,
+ .disp_on_off = st7701_dsi_disp_on_off,
+ .disp_sleep = st7701_dsi_disp_sleep,
+ .get_color_format = st7701_dsi_get_color_format,
+ .get_resolution_x = st7701_dsi_get_resolution_x,
+ .get_resolution_y = st7701_dsi_get_resolution_y,
+ .get_frame_buffer = st7701_dsi_get_frame_buffer,
+ .get_frame_buffer_count = st7701_dsi_get_frame_buffer_count,
+ .get_backlight = st7701_dsi_get_backlight,
+ .has_capability = nullptr,
+};
+
+Driver st7701_dsi_driver = {
+ .name = "st7701_dsi",
+ .compatible = (const char*[]) { "sitronix,st7701-dsi", nullptr },
+ .start_device = start_device,
+ .stop_device = stop_device,
+ .api = &st7701_dsi_display_api,
+ .device_type = &DISPLAY_TYPE,
+ .internal = nullptr
+};
+
+#endif // SOC_MIPI_DSI_SUPPORTED
diff --git a/Tactility/Source/app/alertdialog/AlertDialog.cpp b/Tactility/Source/app/alertdialog/AlertDialog.cpp
index b27f7a7c1..7523fd86e 100644
--- a/Tactility/Source/app/alertdialog/AlertDialog.cpp
+++ b/Tactility/Source/app/alertdialog/AlertDialog.cpp
@@ -179,8 +179,8 @@ extern const ::AppManifest manifest = {
.category = APP_CATEGORY_SYSTEM,
.location = { .type = APP_LOCATION_MEMORY, .location = reinterpret_cast(appMain) },
.flags = APP_MANIFEST_FLAG_HIDDEN,
- // Requires 3072 is not enough on certain devices
- .stack = { .depth = 4096, .desired_memory_capability = MEMORY_CAPABILITY_EXTERNAL },
+ // window_manager_remove() rebuilds the resurfaced window's widgets on this task
+ .stack = { .depth = 6144, .desired_memory_capability = MEMORY_CAPABILITY_EXTERNAL },
};
}
diff --git a/Tactility/Source/app/inputdialog/InputDialog.cpp b/Tactility/Source/app/inputdialog/InputDialog.cpp
index 5474e0216..25bdf6446 100644
--- a/Tactility/Source/app/inputdialog/InputDialog.cpp
+++ b/Tactility/Source/app/inputdialog/InputDialog.cpp
@@ -201,7 +201,7 @@ extern const ::AppManifest manifest = {
.category = APP_CATEGORY_SYSTEM,
.location = { .type = APP_LOCATION_MEMORY, .location = reinterpret_cast(appMain) },
.flags = APP_MANIFEST_FLAG_HIDDEN,
- .stack = { .depth = 3072, .desired_memory_capability = MEMORY_CAPABILITY_EXTERNAL },
+ .stack = { .depth = 6144, .desired_memory_capability = MEMORY_CAPABILITY_EXTERNAL },
};
}
diff --git a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp
index b028458a7..5cd88152a 100644
--- a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp
+++ b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp
@@ -179,7 +179,7 @@ extern const AppManifest manifest = {
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast(appMain) },
.flags = APP_MANIFEST_FLAG_HIDDEN,
- .stack = { .depth = 3072, .desired_memory_capability = MEMORY_CAPABILITY_EXTERNAL },
+ .stack = { .depth = 6144, .desired_memory_capability = MEMORY_CAPABILITY_EXTERNAL },
};
}