From 0e94c7b6782d5658ee93a770dd3149dd174e8d27 Mon Sep 17 00:00:00 2001 From: NellowTCS Date: Mon, 19 Oct 2026 12:20:58 -0700 Subject: [PATCH] Guition JC4880P443C-I-W (#702) --- .../guition-jc1060p470ciwy/device.properties | 2 + .../LICENSE-Apache-2.0.md | 195 +++++++ .../guition-jc4880p443ciw/device.properties | 27 + .../guition,jc4880p443ciw.dts | 174 +++++++ Devices/guition-jc4880p443ciw/module.yaml | 9 + Drivers/st7701-module/CMakeLists.txt | 2 +- .../bindings/sitronix,st7701-dsi.yaml | 122 +++++ .../include/bindings/st7701_dsi.h | 7 + .../include/drivers/st7701_dsi.h | 53 ++ Drivers/st7701-module/source/module.cpp | 8 + Drivers/st7701-module/source/st7701_dsi.cpp | 477 ++++++++++++++++++ .../Source/app/alertdialog/AlertDialog.cpp | 4 +- .../Source/app/inputdialog/InputDialog.cpp | 2 +- .../app/selectiondialog/SelectionDialog.cpp | 2 +- 14 files changed, 1079 insertions(+), 5 deletions(-) create mode 100644 Devices/guition-jc4880p443ciw/LICENSE-Apache-2.0.md create mode 100644 Devices/guition-jc4880p443ciw/device.properties create mode 100644 Devices/guition-jc4880p443ciw/guition,jc4880p443ciw.dts create mode 100644 Devices/guition-jc4880p443ciw/module.yaml create mode 100644 Drivers/st7701-module/bindings/sitronix,st7701-dsi.yaml create mode 100644 Drivers/st7701-module/include/bindings/st7701_dsi.h create mode 100644 Drivers/st7701-module/include/drivers/st7701_dsi.h create mode 100644 Drivers/st7701-module/source/st7701_dsi.cpp 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 }, }; }