diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml
new file mode 100644
index 0000000..4d21645
--- /dev/null
+++ b/.github/workflows/upload_component.yml
@@ -0,0 +1,25 @@
+name: Push components to Espressif Component Service
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ upload_components:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@master
+ with:
+ submodules: 'recursive'
+ - name: Upload components to component service
+ uses: espressif/upload-components-ci-action@v1
+ with:
+ directories: >
+ display/lcd/esp_lcd_jd9165;
+ display/lcd/esp_lcd_jd9365_8;
+ display/lcd/esp_lcd_jd9365_10_1;
+ display/lcd/esp_lcd_hx8394;
+ display/lcd/esp_lcd_dsi;
+ namespace: "waveshare"
+ api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
diff --git a/README.md b/README.md
index 04abb19..ddff580 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,154 @@
-# Waveshare-ESP32-components
-waveshare componets For ESP Component Registry
+# Waveshare ESP32 Board Support Packages and Drivers
+
+Welcome to the repository for Waveshare ESP32 products! This repository is designed to provide resources, drivers, and
+support packages for Waveshare's ESP32-based boards and compatible display modules. It aims to simplify development and
+accelerate your project setup.
+
+---
+
+## π¦ Repository Contents
+
+### 1. **Board Support Packages (BSP)**
+
+| Board | supported |
+|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
+| [ESP32-P4-NANO](https://www.waveshare.com/esp32-p4-nano.htm)
| β
|
+| [ESP32-S3-Touch-AMOLED-1.8](https://www.waveshare.com/esp32-s3-touch-amoled-1.8.htm)
| π |
+| [ESP32-S3-Touch-LCD-1.69](https://www.waveshare.com/esp32-s3-touch-lcd-1.69.htm)
| π |
+| [ESP32-S3-LCD-1.69](https://www.waveshare.com/esp32-s3-lcd-1.69.htm)
| π |
+| [ESP32-S3-Touch-LCD-4 ](https://www.waveshare.com/esp32-s3-touch-lcd-4.htm)
| π |
+
+### 2. **Display Drivers**
+
+| Product ID | Dependency | tested |
+|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------|--------|
+| [2.8inch DSI LCD](https://www.waveshare.com/2.8inch-dsi-lcd.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [3.4inch DSI LCD (C)](https://www.waveshare.com/3.4inch-dsi-lcd-c.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [4inch DSI LCD (C)](https://www.waveshare.com/4inch-dsi-lcd-c.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [4inch DSI LCD](https://www.waveshare.com/4inch-dsi-lcd.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [5inch DSI LCD (D)](https://www.waveshare.com/5inch-dsi-lcd-d.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [6.25inch DSI LCD](https://www.waveshare.com/6.25inch-dsi-lcd.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [5inch DSI LCD (C)](https://www.waveshare.com/5inch-dsi-lcd-c.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [7inch DSI LCD (C)](https://www.waveshare.com/7inch-dsi-lcd-c-with-case-a.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [7.9inch DSI LCD](https://www.waveshare.com/7.9inch-dsi-lcd.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [7inch DSI LCD (E)](https://www.waveshare.com/7inch-dsi-lcd-e.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [8inch DSI LCD (C)](https://www.waveshare.com/8inch-dsi-lcd-c.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [10.1inch DSI LCD (C)](https://www.waveshare.com/10.1inch-dsi-lcd-c.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [8.8inch DSI LCD](https://www.waveshare.com/8.8inch-dsi-lcd.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [11.9inch DSI LCD](https://www.waveshare.com/11.9inch-dsi-lcd.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | β
|
+| [:101M-8001280-IPS-CT-K](https://www.waveshare.com/101m-8001280-ips-ct-k.htm)
| [waveshare/esp_lcd_jd9365_10_1](display/lcd/esp_lcd_jd9365_10_1) | β
|
+
+## π Documentation
+
+Each library and support package comes with detailed documentation and examples. Key highlights include:
+
+- **Initialization Guides:** Step-by-step instructions for setting up your board and peripherals.
+- **Examples:**
+ - The driver library code provides the simplest low-level drivers
+ - BSP sample programs can be queried in different product wikis, and will be synchronized in
+ the [ESP32-display-support](https://github.com/waveshareteam/ESP32-display-support)
+- **Code Comments:** Inline documentation for better understanding.
+
+---
+
+## π Getting Started
+
+### Prerequisites
+
+- **Hardware:**
+ - Waveshare ESP32 boards
+ - Supported display modules
+ - Compatible touch controllers(Optional)
+- **Software:**
+ - [ESP-IDF](https://github.com/espressif/esp-idf)(Each product has a recommended version on the wiki as well as
+ sample routines)
+
+[//]: # (### Setup)
+
+[//]: # ()
+
+[//]: # (1. Clone the repository:)
+
+[//]: # ()
+
+[//]: # ()
+
+[//]: # (2. Install required dependencies:)
+
+[//]: # ()
+
+[//]: # ()
+
+[//]: # (3. Flash the example projects:)
+
+
+---
+
+## π Features
+
+- **LVGL Support:**
+ - Widgets, animations, and custom UI components for Waveshare displays.
+- **Optimized Drivers:**
+ - DMA and double buffering for smooth graphics.
+- **Peripheral Utilities:**
+ - Simplified access to RTC, I/O expanders, and power management ICs.
+- **Arduino-Compatible Libraries:**
+ - Easy porting of code using the Arduino ecosystem.
+
+---
+
+## π§ Configuration
+
+You can view some of the configurations that have been added via menuconfig, which will allow you to verify the
+functionality directly
+
+---
+
+## π Directory Structure
+
+```
+.
+βββ bsp/ # Board support packages
+βββ display/ # Display drivers
+βββ README.md # Project overview (this file)
+```
+
+---
+
+## π οΈ Contributing
+
+We welcome contributions! Hereβs how you can help:
+
+1. Fork the repository.
+2. Create a new branch for your feature or bug fix.
+3. Commit your changes with clear descriptions.
+4. Submit a pull request for review.
+
+---
+
+## 𧩠Issues and Support
+
+If you encounter any issues:
+
+- Check the [Issues](https://github.com/waveshareteam/Waveshare-ESP32-components/issues) section.
+- Create a new issue with detailed information.
+- Refer to the documentation for troubleshooting tips.
+
+---
+
+## π License
+
+This repository is licensed under the Apache License License. See the `LICENSE` file for details.
+
+---
+
+## π Acknowledgments
+
+- Waveshare for their excellent hardware platforms and software support
+- The Espressif Team for their continuous support.
+- Open-source contributors who make these projects possible.
+
+---
+
+Thank you for using Waveshare-ESP32-components! π
+
diff --git a/bsp/esp32_p4_nano/CMakeLists.txt b/bsp/esp32_p4_nano/CMakeLists.txt
new file mode 100644
index 0000000..8c8014f
--- /dev/null
+++ b/bsp/esp32_p4_nano/CMakeLists.txt
@@ -0,0 +1,8 @@
+
+idf_component_register(
+ SRCS "esp32_p4_nano.c"
+ INCLUDE_DIRS "include"
+ PRIV_INCLUDE_DIRS "priv_include"
+ REQUIRES driver
+ PRIV_REQUIRES esp_lcd usb spiffs fatfs
+)
diff --git a/bsp/esp32_p4_nano/Kconfig b/bsp/esp32_p4_nano/Kconfig
new file mode 100644
index 0000000..63c67f7
--- /dev/null
+++ b/bsp/esp32_p4_nano/Kconfig
@@ -0,0 +1,150 @@
+menu "Board Support Package(ESP32-P4)"
+
+ config BSP_ERROR_CHECK
+ bool "Enable error check in BSP"
+ default y
+ help
+ Error check assert the application before returning the error code.
+
+ menu "I2C"
+ config BSP_I2C_NUM
+ int "I2C peripheral index"
+ default 1
+ range 0 1
+ help
+ ESP32P4 has two I2C peripherals, pick the one you want to use.
+
+ config BSP_I2C_FAST_MODE
+ bool "Enable I2C fast mode"
+ default y
+ help
+ I2C has two speed modes: normal (100kHz) and fast (400kHz).
+
+ config BSP_I2C_CLK_SPEED_HZ
+ int
+ default 400000 if BSP_I2C_FAST_MODE
+ default 100000
+ endmenu
+
+ menu "I2S"
+ config BSP_I2S_NUM
+ int "I2S peripheral index"
+ default 1
+ range 0 2
+ help
+ ESP32P4 has three I2S peripherals, pick the one you want to use.
+ endmenu
+
+ menu "uSD card - Virtual File System"
+ config BSP_SD_FORMAT_ON_MOUNT_FAIL
+ bool "Format uSD card if mounting fails"
+ default n
+ help
+ The SDMMC host will format (FAT) the uSD card if it fails to mount the filesystem.
+
+ config BSP_SD_MOUNT_POINT
+ string "uSD card mount point"
+ default "/sdcard"
+ help
+ Mount point of the uSD card in the Virtual File System
+
+ endmenu
+
+ menu "SPIFFS - Virtual File System"
+ config BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL
+ bool "Format SPIFFS if mounting fails"
+ default n
+ help
+ Format SPIFFS if it fails to mount the filesystem.
+
+ config BSP_SPIFFS_MOUNT_POINT
+ string "SPIFFS mount point"
+ default "/spiffs"
+ help
+ Mount point of SPIFFS in the Virtual File System.
+
+ config BSP_SPIFFS_PARTITION_LABEL
+ string "Partition label of SPIFFS"
+ default "storage"
+ help
+ Partition label which stores SPIFFS.
+
+ config BSP_SPIFFS_MAX_FILES
+ int "Max files supported for SPIFFS VFS"
+ default 5
+ help
+ Supported max files for SPIFFS in the Virtual File System.
+ endmenu
+
+ menu "Display"
+ config BSP_LCD_DPI_BUFFER_NUMS
+ int "Set number of frame buffers"
+ default 1
+ range 1 3
+ help
+ Let DPI LCD driver create a specified number of frame-size buffers. Only when it is set to multiple can the avoiding tearing be turned on.
+
+ config BSP_DISPLAY_LVGL_AVOID_TEAR
+ bool "Avoid tearing effect"
+ depends on BSP_LCD_DPI_BUFFER_NUMS > 1
+ default "n"
+ help
+ Avoid tearing effect through LVGL buffer mode and double frame buffers of RGB LCD. This feature is only available for RGB LCD.
+
+ choice BSP_DISPLAY_LVGL_MODE
+ depends on BSP_DISPLAY_LVGL_AVOID_TEAR
+ prompt "Select LVGL buffer mode"
+ default BSP_DISPLAY_LVGL_FULL_REFRESH
+ config BSP_DISPLAY_LVGL_FULL_REFRESH
+ bool "Full refresh"
+ config BSP_DISPLAY_LVGL_DIRECT_MODE
+ bool "Direct mode"
+ endchoice
+
+ config BSP_DISPLAY_BRIGHTNESS_LEDC_CH
+ int "LEDC channel index"
+ default 1
+ range 0 7
+ help
+ LEDC channel is used to generate PWM signal that controls display brightness.
+ Set LEDC index that should be used.
+
+ choice BSP_LCD_COLOR_FORMAT
+ prompt "Select LCD color format"
+ default BSP_LCD_COLOR_FORMAT_RGB565
+ help
+ Select the LCD color format RGB565/RGB888.
+
+ config BSP_LCD_COLOR_FORMAT_RGB565
+ bool "RGB565"
+ config BSP_LCD_COLOR_FORMAT_RGB888
+ bool "RGB888"
+ endchoice
+
+ choice BSP_LCD_TYPE
+ prompt "Select LCD type"
+ default BSP_LCD_TYPE_1280_800_WAVESHARE_10_1
+ help
+ Select the LCD.
+
+ config BSP_LCD_TYPE_1024_600
+ bool "LCD 7-inch 1024x600 - ek79007"
+ config BSP_LCD_TYPE_1280_800
+ bool "LCD 1280x800 - ili9881c"
+ config BSP_LCD_TYPE_1280_800_WAVESHARE_10_1
+ bool "Waveshare 1280x800 10.1inch Display"
+ endchoice
+
+ config BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS
+ int "MIPI DSI lane bitrate (Mbps)"
+ default 1500
+ range 600 1500
+ help
+ Set the lane bitrate for the MIPI DSI interface in Mbps.
+ Adjust this value based on the display's requirements and capabilities.
+ Try 840, 860, 933, 1000, 1200, 1500.
+
+
+ endmenu
+
+endmenu
diff --git a/bsp/esp32_p4_nano/LICENSE b/bsp/esp32_p4_nano/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/bsp/esp32_p4_nano/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ 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/bsp/esp32_p4_nano/README.md b/bsp/esp32_p4_nano/README.md
new file mode 100644
index 0000000..dba4917
--- /dev/null
+++ b/bsp/esp32_p4_nano/README.md
@@ -0,0 +1,54 @@
+# BSP: Waveshare ESP32-P4-NANO
+
+[![Component Registry](https://components.espressif.com/components/waveshare/esp32_p4_nano/badge.svg)](https://components.espressif.com/components/waveshare/esp32_p4_nano)
+
+ESP32-P4-NANO is a small size and highly integrated development board designed by waveshare electronics based on ESP32-P4 chip
+| HW version | BSP Version |
+| :--------: | :---------: |
+| [V1.0](http://www.waveshare.com/wiki/ESP32-P4-NANO) | ^1 |
+
+## Configuration
+
+Configuration in `menuconfig`.
+
+Selection LCD display `Board Support Package(ESP32-P4) --> Display --> Select LCD type`
+- Waveshare LCD 10.1-inch 800x1280 - jd9365 (default)
+- LCD 7-inch 1280x800 - ili9881c
+- LCD 1024x600 - ek79007
+
+Selection color format `Board Support Package(ESP32-P4) --> Display --> Select LCD color format`
+- RGB565 (default)
+- RGB888
+
+Change MIPI DSI lane bitrate `Board Support Package(ESP32-P4) --> Display --> MIPI DSI lane bitrate (Mbps)`
+- 1500 (default)
+
+## Display Page
+
+[Waveshare 101M-8001280-IPS-CT-K](https://www.waveshare.com/101m-8001280-ips-ct-k.htm)
+
+## BackLight
+```c
+bsp_display_brightness_init();
+
+bsp_display_backlight_on();
+
+bsp_display_backlight_off();
+
+bsp_display_brightness_set(100);
+```
+
+
+### Capabilities and dependencies
+| Capability | Available | Component |Version|
+|-------------|------------------|----------------------------------------------------------------------------------------------------------|-------|
+| DISPLAY |:heavy_check_mark:| [waveshare/esp_lcd_jd9365_10_1](https://components.espressif.com/components/waveshare/esp_lcd_jd9365_10_1) | 0.0.4 |
+| LVGL_PORT |:heavy_check_mark:| [espressif/esp_lvgl_port](https://components.espressif.com/components/espressif/esp_lvgl_port) | ^2 |
+| TOUCH |:heavy_check_mark:|[espressif/esp_lcd_touch_gt911](https://components.espressif.com/components/espressif/esp_lcd_touch_gt911)| ^1 |
+| BUTTONS | :x: | | |
+| AUDIO |:heavy_check_mark:| [espressif/esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev) | 1.2.* |
+|AUDIO_SPEAKER|:heavy_check_mark:| | |
+| AUDIO_MIC |:heavy_check_mark:| | |
+| SDCARD |:heavy_check_mark:| idf | >=5.3 |
+| IMU | :x: | | |
+
diff --git a/bsp/esp32_p4_nano/esp32_p4_nano.c b/bsp/esp32_p4_nano/esp32_p4_nano.c
new file mode 100644
index 0000000..205832c
--- /dev/null
+++ b/bsp/esp32_p4_nano/esp32_p4_nano.c
@@ -0,0 +1,753 @@
+#include "sdkconfig.h"
+#include "driver/gpio.h"
+#include "driver/ledc.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_check.h"
+#include "esp_spiffs.h"
+#include "esp_lcd_panel_ops.h"
+#include "esp_lcd_mipi_dsi.h"
+#include "esp_ldo_regulator.h"
+#include "esp_vfs_fat.h"
+#include "usb/usb_host.h"
+#include "sd_pwr_ctrl_by_on_chip_ldo.h"
+
+#if CONFIG_BSP_LCD_TYPE_1024_600
+#include "esp_lcd_ek79007.h"
+#elif CONFIG_BSP_LCD_TYPE_1280_800
+#include "esp_lcd_ili9881c.h"
+#elif CONFIG_BSP_LCD_TYPE_1280_800_WAVESHARE_10_1
+#include "esp_lcd_jd9365_10_1.h"
+#endif
+
+#include "bsp/esp32_p4_nano.h"
+#include "bsp/display.h"
+#include "bsp/touch.h"
+#include "esp_lcd_touch_gt911.h"
+#include "bsp_err_check.h"
+#include "esp_codec_dev_defaults.h"
+
+static const char *TAG = "ESP32_P4_EV";
+
+#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
+static lv_indev_t *disp_indev = NULL;
+#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
+
+sdmmc_card_t *bsp_sdcard = NULL; // Global uSD card handler
+static bool i2c_initialized = false;
+static TaskHandle_t usb_host_task; // USB Host Library task
+#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0))
+static i2c_master_bus_handle_t i2c_handle = NULL; // I2C Handle
+#endif
+static i2s_chan_handle_t i2s_tx_chan = NULL;
+static i2s_chan_handle_t i2s_rx_chan = NULL;
+static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface */
+
+/* Can be used for `i2s_std_gpio_config_t` and/or `i2s_std_config_t` initialization */
+#define BSP_I2S_GPIO_CFG \
+ { \
+ .mclk = BSP_I2S_MCLK, \
+ .bclk = BSP_I2S_SCLK, \
+ .ws = BSP_I2S_LCLK, \
+ .dout = BSP_I2S_DOUT, \
+ .din = BSP_I2S_DSIN, \
+ .invert_flags = { \
+ .mclk_inv = false, \
+ .bclk_inv = false, \
+ .ws_inv = false, \
+ }, \
+ }
+
+/* This configuration is used by default in `bsp_extra_audio_init()` */
+#define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate) \
+ { \
+ .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sample_rate), \
+ .slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), \
+ .gpio_cfg = BSP_I2S_GPIO_CFG, \
+ }
+
+esp_err_t bsp_i2c_init(void)
+{
+ /* I2C was initialized before */
+ if (i2c_initialized) {
+ return ESP_OK;
+ }
+
+ i2c_master_bus_config_t i2c_bus_conf = {
+ .clk_source = I2C_CLK_SRC_DEFAULT,
+ .sda_io_num = BSP_I2C_SDA,
+ .scl_io_num = BSP_I2C_SCL,
+ .i2c_port = BSP_I2C_NUM,
+ };
+ BSP_ERROR_CHECK_RETURN_ERR(i2c_new_master_bus(&i2c_bus_conf, &i2c_handle));
+
+ i2c_initialized = true;
+
+ return ESP_OK;
+}
+
+esp_err_t bsp_i2c_deinit(void)
+{
+ BSP_ERROR_CHECK_RETURN_ERR(i2c_del_master_bus(i2c_handle));
+ i2c_initialized = false;
+ return ESP_OK;
+}
+
+i2c_master_bus_handle_t bsp_i2c_get_handle(void)
+{
+ return i2c_handle;
+}
+
+esp_err_t bsp_sdcard_mount(void)
+{
+ const esp_vfs_fat_sdmmc_mount_config_t mount_config = {
+#ifdef CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL
+ .format_if_mount_failed = true,
+#else
+ .format_if_mount_failed = false,
+#endif
+ .max_files = 5,
+ .allocation_unit_size = 64 * 1024
+ };
+
+ sdmmc_host_t host = SDMMC_HOST_DEFAULT();
+ host.slot = SDMMC_HOST_SLOT_0;
+ host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
+
+ sd_pwr_ctrl_ldo_config_t ldo_config = {
+ .ldo_chan_id = 4,
+ };
+ sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL;
+ esp_err_t ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver");
+ return ret;
+ }
+ host.pwr_ctrl_handle = pwr_ctrl_handle;
+
+ const sdmmc_slot_config_t slot_config = {
+ /* SD card is connected to Slot 0 pins. Slot 0 uses IO MUX, so not specifying the pins here */
+ .cd = SDMMC_SLOT_NO_CD,
+ .wp = SDMMC_SLOT_NO_WP,
+ .width = 4,
+ .flags = 0,
+ };
+
+ return esp_vfs_fat_sdmmc_mount(BSP_SD_MOUNT_POINT, &host, &slot_config, &mount_config, &bsp_sdcard);
+}
+
+esp_err_t bsp_sdcard_unmount(void)
+{
+ return esp_vfs_fat_sdcard_unmount(BSP_SD_MOUNT_POINT, bsp_sdcard);
+}
+
+esp_err_t bsp_spiffs_mount(void)
+{
+ esp_vfs_spiffs_conf_t conf = {
+ .base_path = CONFIG_BSP_SPIFFS_MOUNT_POINT,
+ .partition_label = CONFIG_BSP_SPIFFS_PARTITION_LABEL,
+ .max_files = CONFIG_BSP_SPIFFS_MAX_FILES,
+#ifdef CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL
+ .format_if_mount_failed = true,
+#else
+ .format_if_mount_failed = false,
+#endif
+ };
+
+ esp_err_t ret_val = esp_vfs_spiffs_register(&conf);
+
+ BSP_ERROR_CHECK_RETURN_ERR(ret_val);
+
+ size_t total = 0, used = 0;
+ ret_val = esp_spiffs_info(conf.partition_label, &total, &used);
+ if (ret_val != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret_val));
+ } else {
+ ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
+ }
+
+ return ret_val;
+}
+
+esp_err_t bsp_spiffs_unmount(void)
+{
+ return esp_vfs_spiffs_unregister(CONFIG_BSP_SPIFFS_PARTITION_LABEL);
+}
+
+esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config)
+{
+ if (i2s_tx_chan && i2s_rx_chan) {
+ /* Audio was initialized before */
+ return ESP_OK;
+ }
+
+ /* Setup I2S peripheral */
+ i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(CONFIG_BSP_I2S_NUM, I2S_ROLE_MASTER);
+ chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer
+ ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &i2s_tx_chan, &i2s_rx_chan));
+
+ /* Setup I2S channels */
+ const i2s_std_config_t std_cfg_default = BSP_I2S_DUPLEX_MONO_CFG(22050);
+ const i2s_std_config_t *p_i2s_cfg = &std_cfg_default;
+ if (i2s_config != NULL) {
+ p_i2s_cfg = i2s_config;
+ }
+
+ if (i2s_tx_chan != NULL) {
+ ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_tx_chan, p_i2s_cfg));
+ ESP_ERROR_CHECK(i2s_channel_enable(i2s_tx_chan));
+ }
+
+ if (i2s_rx_chan != NULL) {
+ ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_rx_chan, p_i2s_cfg));
+ ESP_ERROR_CHECK(i2s_channel_enable(i2s_rx_chan));
+ }
+
+ audio_codec_i2s_cfg_t i2s_cfg = {
+ .port = CONFIG_BSP_I2S_NUM,
+ .tx_handle = i2s_tx_chan,
+ .rx_handle = i2s_rx_chan,
+ };
+ i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg);
+
+ return ESP_OK;
+}
+
+esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void)
+{
+ if (i2s_data_if == NULL) {
+ /* Initilize I2C */
+ ESP_ERROR_CHECK(bsp_i2c_init());
+ /* Configure I2S peripheral and Power Amplifier */
+ ESP_ERROR_CHECK(bsp_audio_init(NULL));
+ }
+ assert(i2s_data_if);
+
+ const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
+
+ audio_codec_i2c_cfg_t i2c_cfg = {
+ .port = BSP_I2C_NUM,
+ .addr = ES8311_CODEC_DEFAULT_ADDR,
+ .bus_handle = i2c_handle,
+ };
+ const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
+ assert(i2c_ctrl_if);
+
+ esp_codec_dev_hw_gain_t gain = {
+ .pa_voltage = 5.0,
+ .codec_dac_voltage = 3.3,
+ };
+
+ es8311_codec_cfg_t es8311_cfg = {
+ .ctrl_if = i2c_ctrl_if,
+ .gpio_if = gpio_if,
+ .codec_mode = ESP_CODEC_DEV_TYPE_OUT,
+ .pa_pin = BSP_POWER_AMP_IO,
+ .pa_reverted = false,
+ .master_mode = false,
+ .use_mclk = true,
+ .digital_mic = false,
+ .invert_mclk = false,
+ .invert_sclk = false,
+ .hw_gain = gain,
+ };
+ const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg);
+ assert(es8311_dev);
+
+ esp_codec_dev_cfg_t codec_dev_cfg = {
+ .dev_type = ESP_CODEC_DEV_TYPE_IN_OUT,
+ .codec_if = es8311_dev,
+ .data_if = i2s_data_if,
+ };
+ return esp_codec_dev_new(&codec_dev_cfg);
+}
+
+esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void)
+{
+ if (i2s_data_if == NULL) {
+ /* Initilize I2C */
+ ESP_ERROR_CHECK(bsp_i2c_init());
+ /* Configure I2S peripheral and Power Amplifier */
+ ESP_ERROR_CHECK(bsp_audio_init(NULL));
+ }
+ assert(i2s_data_if);
+
+ const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
+
+ audio_codec_i2c_cfg_t i2c_cfg = {
+ .port = BSP_I2C_NUM,
+ .addr = ES8311_CODEC_DEFAULT_ADDR,
+ .bus_handle = i2c_handle,
+ };
+ const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
+ assert(i2c_ctrl_if);
+
+ esp_codec_dev_hw_gain_t gain = {
+ .pa_voltage = 5.0,
+ .codec_dac_voltage = 3.3,
+ };
+
+ es8311_codec_cfg_t es8311_cfg = {
+ .ctrl_if = i2c_ctrl_if,
+ .gpio_if = gpio_if,
+ .codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH,
+ .pa_pin = BSP_POWER_AMP_IO,
+ .pa_reverted = false,
+ .master_mode = false,
+ .use_mclk = true,
+ .digital_mic = false,
+ .invert_mclk = false,
+ .invert_sclk = false,
+ .hw_gain = gain,
+ };
+
+ const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg);
+ assert(es8311_dev);
+
+ esp_codec_dev_cfg_t codec_es8311_dev_cfg = {
+ .dev_type = ESP_CODEC_DEV_TYPE_IN,
+ .codec_if = es8311_dev,
+ .data_if = i2s_data_if,
+ };
+ return esp_codec_dev_new(&codec_es8311_dev_cfg);
+}
+
+// Bit number used to represent command and parameter
+#define LCD_LEDC_CH CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH
+
+esp_err_t bsp_display_brightness_init(void)
+{
+ bsp_i2c_init();
+ return ESP_OK;
+}
+
+esp_err_t bsp_display_brightness_set(int brightness_percent)
+{
+ if (brightness_percent > 100) {
+ brightness_percent = 100;
+ }
+ if (brightness_percent < 0) {
+ brightness_percent = 0;
+ }
+
+ uint8_t data = (uint8_t)(255 * brightness_percent * 0.01);
+
+ uint8_t chip_addr = 0x45;
+ uint8_t data_addr = 0x86;
+
+ i2c_device_config_t i2c_dev_conf = {
+ .scl_speed_hz = 100 * 1000,
+ .device_address = chip_addr,
+ };
+
+ i2c_master_dev_handle_t dev_handle = NULL;
+ if (i2c_master_bus_add_device(i2c_handle, &i2c_dev_conf, &dev_handle) != ESP_OK)
+ {
+ return ESP_FAIL;
+ }
+
+ uint8_t data_to_send[2] = {data_addr, data};
+
+ esp_err_t ret = i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
+ if (ret != ESP_OK)
+ {
+ i2c_master_bus_rm_device(dev_handle);
+ return ret;
+ }
+
+ i2c_master_bus_rm_device(dev_handle);
+
+ return ESP_OK;
+}
+
+esp_err_t bsp_display_backlight_off(void)
+{
+ return bsp_display_brightness_set(0);
+}
+
+esp_err_t bsp_display_backlight_on(void)
+{
+ return bsp_display_brightness_set(100);
+}
+
+static esp_err_t bsp_enable_dsi_phy_power(void)
+{
+#if BSP_MIPI_DSI_PHY_PWR_LDO_CHAN > 0
+ // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
+ static esp_ldo_channel_handle_t phy_pwr_chan = NULL;
+ esp_ldo_channel_config_t ldo_cfg = {
+ .chan_id = BSP_MIPI_DSI_PHY_PWR_LDO_CHAN,
+ .voltage_mv = BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
+ };
+ ESP_RETURN_ON_ERROR(esp_ldo_acquire_channel(&ldo_cfg, &phy_pwr_chan), TAG, "Acquire LDO channel for DPHY failed");
+ ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
+#endif // BSP_MIPI_DSI_PHY_PWR_LDO_CHAN > 0
+
+ return ESP_OK;
+}
+
+esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io)
+{
+ esp_err_t ret = ESP_OK;
+ bsp_lcd_handles_t handles;
+ ret = bsp_display_new_with_handles(config, &handles);
+
+ *ret_panel = handles.panel;
+ *ret_io = handles.io;
+
+ return ret;
+}
+
+esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_lcd_handles_t *ret_handles)
+{
+ esp_err_t ret = ESP_OK;
+
+ ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed");
+ ESP_RETURN_ON_ERROR(bsp_enable_dsi_phy_power(), TAG, "DSI PHY power failed");
+
+ /* create MIPI DSI bus first, it will initialize the DSI PHY as well */
+ esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
+ esp_lcd_dsi_bus_config_t bus_config = {
+ .bus_id = 0,
+ .num_data_lanes = BSP_LCD_MIPI_DSI_LANE_NUM,
+ .phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
+ .lane_bit_rate_mbps = BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS,
+ };
+ ESP_RETURN_ON_ERROR(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus), TAG, "New DSI bus init failed");
+
+ ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
+ // we use DBI interface to send LCD commands and parameters
+ esp_lcd_panel_io_handle_t io;
+ esp_lcd_dbi_io_config_t dbi_config = {
+ .virtual_channel = 0,
+ .lcd_cmd_bits = 8, // according to the LCD ILI9881C spec
+ .lcd_param_bits = 8, // according to the LCD ILI9881C spec
+ };
+ ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io), err, TAG, "New panel IO failed");
+
+ esp_lcd_panel_handle_t disp_panel = NULL;
+#if CONFIG_BSP_LCD_TYPE_1024_600
+ // create EK79007 control panel
+ ESP_LOGI(TAG, "Install EK79007 LCD control panel");
+
+#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
+ esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888);
+#else
+ esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
+#endif
+ dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS;
+
+ ek79007_vendor_config_t vendor_config = {
+ .mipi_config = {
+ .dsi_bus = mipi_dsi_bus,
+ .dpi_config = &dpi_config,
+ },
+ };
+ esp_lcd_panel_dev_config_t lcd_dev_config = {
+ .bits_per_pixel = 16,
+ .rgb_ele_order = BSP_LCD_COLOR_SPACE,
+ .reset_gpio_num = BSP_LCD_RST,
+ .vendor_config = &vendor_config,
+ };
+ ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ek79007(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel EK79007 failed");
+ ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed");
+ ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed");
+#elif CONFIG_BSP_LCD_TYPE_1280_800
+ // create ILI9881C control panel
+ ESP_LOGI(TAG, "Install ILI9881C LCD control panel");
+#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
+ esp_lcd_dpi_panel_config_t dpi_config = ILI9881C_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888);
+#else
+ esp_lcd_dpi_panel_config_t dpi_config = ILI9881C_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
+#endif
+ dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS;
+
+ ili9881c_vendor_config_t vendor_config = {
+ .mipi_config = {
+ .dsi_bus = mipi_dsi_bus,
+ .dpi_config = &dpi_config,
+ .lane_num = BSP_LCD_MIPI_DSI_LANE_NUM,
+ },
+ };
+ const esp_lcd_panel_dev_config_t lcd_dev_config = {
+ .reset_gpio_num = BSP_LCD_RST,
+ .rgb_ele_order = BSP_LCD_COLOR_SPACE,
+ .bits_per_pixel = 16,
+ .vendor_config = &vendor_config,
+ };
+ ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ili9881c(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel ILI9881C failed");
+ ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed");
+ ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed");
+ ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(disp_panel, true), err, TAG, "LCD panel ON failed");
+#elif CONFIG_BSP_LCD_TYPE_1280_800_WAVESHARE_10_1
+ ESP_LOGI(TAG, "Install Waveshare LCD control panel");
+
+#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
+ esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888);
+#else
+ esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
+#endif
+ dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS;
+
+ jd9365_vendor_config_t vendor_config = {
+ .flags = {
+ .use_mipi_interface = 1,
+ },
+ .mipi_config = {
+ .dsi_bus = mipi_dsi_bus,
+ .dpi_config = &dpi_config,
+ .lane_num = 2,
+ },
+ };
+ esp_lcd_panel_dev_config_t lcd_dev_config = {
+#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
+ .bits_per_pixel = 24,
+#else
+ .bits_per_pixel = 16,
+#endif
+ .rgb_ele_order = BSP_LCD_COLOR_SPACE,
+ .reset_gpio_num = BSP_LCD_RST,
+ .vendor_config = &vendor_config,
+ };
+ ESP_GOTO_ON_ERROR(esp_lcd_new_panel_jd9365(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel Waveshare failed");
+ ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed");
+ ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed");
+#endif
+
+ /* Return all handles */
+ ret_handles->io = io;
+ ret_handles->mipi_dsi_bus = mipi_dsi_bus;
+ ret_handles->panel = disp_panel;
+ ret_handles->control = NULL;
+
+ ESP_LOGI(TAG, "Display initialized");
+
+ return ret;
+
+err:
+ if (disp_panel) {
+ esp_lcd_panel_del(disp_panel);
+ }
+ if (io) {
+ esp_lcd_panel_io_del(io);
+ }
+ if (mipi_dsi_bus) {
+ esp_lcd_del_dsi_bus(mipi_dsi_bus);
+ }
+ return ret;
+}
+
+esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch)
+{
+ /* Initilize I2C */
+ BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init());
+
+ /* Initialize touch */
+ const esp_lcd_touch_config_t tp_cfg = {
+ .x_max = BSP_LCD_H_RES,
+ .y_max = BSP_LCD_V_RES,
+ .rst_gpio_num = BSP_LCD_TOUCH_RST, // Shared with LCD reset
+ .int_gpio_num = BSP_LCD_TOUCH_INT,
+ .levels = {
+ .reset = 0,
+ .interrupt = 0,
+ },
+ .flags = {
+ .swap_xy = 0,
+#if CONFIG_BSP_LCD_TYPE_1024_600
+ .mirror_x = 1,
+ .mirror_y = 1,
+#elif CONFIG_CONFIG_BSP_LCD_TYPE_1280_800
+ .mirror_x = 0,
+ .mirror_y = 0,
+#elif CONFIG_BSP_LCD_TYPE_1280_800_WAVESHARE_10_1
+ .mirror_x = 1,
+ .mirror_y = 1,
+#endif
+ },
+ };
+ esp_lcd_panel_io_handle_t tp_io_handle = NULL;
+ esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
+ tp_io_config.scl_speed_hz = CONFIG_BSP_I2C_CLK_SPEED_HZ;
+ ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle), TAG, "");
+ return esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, ret_touch);
+}
+
+#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
+static lv_display_t *bsp_display_lcd_init(const bsp_display_cfg_t *cfg)
+{
+ assert(cfg != NULL);
+ bsp_lcd_handles_t lcd_panels;
+ BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new_with_handles(NULL, &lcd_panels));
+
+ /* Add LCD screen */
+ ESP_LOGD(TAG, "Add LCD screen");
+ const lvgl_port_display_cfg_t disp_cfg = {
+ .io_handle = lcd_panels.io,
+ .panel_handle = lcd_panels.panel,
+ .control_handle = lcd_panels.control,
+ .buffer_size = cfg->buffer_size,
+ .double_buffer = cfg->double_buffer,
+ .hres = BSP_LCD_H_RES,
+ .vres = BSP_LCD_V_RES,
+ .monochrome = false,
+ /* Rotation values must be same as used in esp_lcd for initial settings of the screen */
+ .rotation = {
+ .swap_xy = false,
+ .mirror_x = true,
+ .mirror_y = true,
+ },
+#if LVGL_VERSION_MAJOR >= 9
+#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
+ .color_format = LV_COLOR_FORMAT_RGB888,
+#else
+ .color_format = LV_COLOR_FORMAT_RGB565,
+#endif
+#endif
+ .flags = {
+ .buff_dma = cfg->flags.buff_dma,
+ .buff_spiram = cfg->flags.buff_spiram,
+#if LVGL_VERSION_MAJOR >= 9
+ .swap_bytes = (BSP_LCD_BIGENDIAN ? true : false),
+#endif
+#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR
+ .sw_rotate = false, /* Avoid tearing is not supported for SW rotation */
+#else
+ .sw_rotate = cfg->flags.sw_rotate, /* Only SW rotation is supported for 90Β° and 270Β° */
+#endif
+#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH
+ .full_refresh = true,
+#elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE
+ .direct_mode = true,
+#endif
+ }
+ };
+
+ const lvgl_port_display_dsi_cfg_t dpi_cfg = {
+ .flags = {
+#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR
+ .avoid_tearing = true,
+#else
+ .avoid_tearing = false,
+#endif
+ }
+ };
+
+ return lvgl_port_add_disp_dsi(&disp_cfg, &dpi_cfg);
+}
+
+static lv_indev_t *bsp_display_indev_init(lv_display_t *disp)
+{
+ esp_lcd_touch_handle_t tp;
+ BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp));
+ assert(tp);
+
+ /* Add touch input (for selected screen) */
+ const lvgl_port_touch_cfg_t touch_cfg = {
+ .disp = disp,
+ .handle = tp,
+ };
+
+ return lvgl_port_add_touch(&touch_cfg);
+}
+
+lv_display_t *bsp_display_start(void)
+{
+ bsp_display_cfg_t cfg = {
+ .lvgl_port_cfg = ESP_LVGL_PORT_INIT_CONFIG(),
+ .buffer_size = BSP_LCD_DRAW_BUFF_SIZE,
+ .double_buffer = BSP_LCD_DRAW_BUFF_DOUBLE,
+ .flags = {
+#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
+ .buff_dma = false,
+#else
+ .buff_dma = true,
+#endif
+ .buff_spiram = false,
+ .sw_rotate = true,
+ }
+ };
+ return bsp_display_start_with_config(&cfg);
+}
+
+lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg)
+{
+ lv_display_t *disp;
+
+ assert(cfg != NULL);
+ BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_init(&cfg->lvgl_port_cfg));
+
+ BSP_ERROR_CHECK_RETURN_NULL(bsp_display_brightness_init());
+
+ BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL);
+
+ BSP_NULL_CHECK(disp_indev = bsp_display_indev_init(disp), NULL);
+
+ return disp;
+}
+
+lv_indev_t *bsp_display_get_input_dev(void)
+{
+ return disp_indev;
+}
+
+void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation)
+{
+ lv_disp_set_rotation(disp, rotation);
+}
+
+bool bsp_display_lock(uint32_t timeout_ms)
+{
+ return lvgl_port_lock(timeout_ms);
+}
+
+void bsp_display_unlock(void)
+{
+ lvgl_port_unlock();
+}
+
+#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
+
+static void usb_lib_task(void *arg)
+{
+ while (1) {
+ // Start handling system events
+ uint32_t event_flags;
+ usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
+ if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
+ ESP_ERROR_CHECK(usb_host_device_free_all());
+ }
+ if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
+ ESP_LOGI(TAG, "USB: All devices freed");
+ }
+ }
+}
+
+esp_err_t bsp_usb_host_start(bsp_usb_host_power_mode_t mode, bool limit_500mA)
+{
+ //Install USB Host driver. Should only be called once in entire application
+ ESP_LOGI(TAG, "Installing USB Host");
+ const usb_host_config_t host_config = {
+ .skip_phy_setup = false,
+ .intr_flags = ESP_INTR_FLAG_LEVEL1,
+ };
+ BSP_ERROR_CHECK_RETURN_ERR(usb_host_install(&host_config));
+
+ // Create a task that will handle USB library events
+ if (xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, &usb_host_task) != pdTRUE) {
+ ESP_LOGE(TAG, "Creating USB host lib task failed");
+ abort();
+ }
+
+ return ESP_OK;
+}
+
+esp_err_t bsp_usb_host_stop(void)
+{
+ usb_host_uninstall();
+ if (usb_host_task) {
+ vTaskSuspend(usb_host_task);
+ vTaskDelete(usb_host_task);
+ }
+ return ESP_OK;
+}
diff --git a/bsp/esp32_p4_nano/idf_component.yml b/bsp/esp32_p4_nano/idf_component.yml
new file mode 100644
index 0000000..be110de
--- /dev/null
+++ b/bsp/esp32_p4_nano/idf_component.yml
@@ -0,0 +1,20 @@
+dependencies:
+ esp_codec_dev:
+ public: true
+ version: 1.2.*
+ esp_lcd_ek79007: 1.*
+ esp_lcd_ili9881c: 1.*
+ waveshare/esp_lcd_jd9365_10_1: "*"
+ esp_lcd_touch_gt911: ^1
+ espressif/esp_lvgl_port:
+ public: true
+ version: ^2
+ idf: '>=5.3'
+ lvgl/lvgl: '>=8,<10'
+description: a small size and highly integrated development board designed by waveshare electronics based on ESP32-P4 chip
+tags:
+- bsp
+targets:
+- esp32p4
+url: https://www.waveshare.com/esp32-p4-nano.htm
+version: 1.1.2
diff --git a/bsp/esp32_p4_nano/include/bsp/config.h b/bsp/esp32_p4_nano/include/bsp/config.h
new file mode 100644
index 0000000..927f814
--- /dev/null
+++ b/bsp/esp32_p4_nano/include/bsp/config.h
@@ -0,0 +1,10 @@
+#pragma once
+
+/**************************************************************************************************
+ * BSP configuration
+ **************************************************************************************************/
+// By default, this BSP is shipped with LVGL graphical library. Enabling this option will exclude it.
+// If you want to use BSP without LVGL, select BSP version with 'noglib' suffix.
+#if !defined(BSP_CONFIG_NO_GRAPHIC_LIB) // Check if the symbol is not coming from compiler definitions (-D...)
+#define BSP_CONFIG_NO_GRAPHIC_LIB (0)
+#endif
diff --git a/bsp/esp32_p4_nano/include/bsp/display.h b/bsp/esp32_p4_nano/include/bsp/display.h
new file mode 100644
index 0000000..2d75405
--- /dev/null
+++ b/bsp/esp32_p4_nano/include/bsp/display.h
@@ -0,0 +1,187 @@
+#pragma once
+#include "esp_lcd_types.h"
+#include "esp_lcd_mipi_dsi.h"
+#include "sdkconfig.h"
+
+/* LCD color formats */
+#define ESP_LCD_COLOR_FORMAT_RGB565 (1)
+#define ESP_LCD_COLOR_FORMAT_RGB888 (2)
+
+/* LCD display color format */
+#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
+#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB888)
+#else
+#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB565)
+#endif
+/* LCD display color bytes endianess */
+#define BSP_LCD_BIGENDIAN (0)
+/* LCD display color bits */
+#define BSP_LCD_BITS_PER_PIXEL (16)
+/* LCD display color space */
+#define BSP_LCD_COLOR_SPACE (ESP_LCD_COLOR_SPACE_RGB)
+
+#if CONFIG_BSP_LCD_TYPE_1024_600
+/* LCD display definition 1024x600 */
+#define BSP_LCD_H_RES (1024)
+#define BSP_LCD_V_RES (600)
+
+#define BSP_LCD_MIPI_DSI_LCD_HSYNC (1344)
+#define BSP_LCD_MIPI_DSI_LCD_HBP (160)
+#define BSP_LCD_MIPI_DSI_LCD_HFP (160)
+#define BSP_LCD_MIPI_DSI_LCD_VSYNC (635)
+#define BSP_LCD_MIPI_DSI_LCD_VBP (23)
+#define BSP_LCD_MIPI_DSI_LCD_VFP (12)
+#elif CONFIG_BSP_LCD_TYPE_1280_800
+/* LCD display definition 1280x800 */
+#define BSP_LCD_H_RES (800)
+#define BSP_LCD_V_RES (1280)
+
+#define BSP_LCD_MIPI_DSI_LCD_HSYNC (40)
+#define BSP_LCD_MIPI_DSI_LCD_HBP (140)
+#define BSP_LCD_MIPI_DSI_LCD_HFP (40)
+#define BSP_LCD_MIPI_DSI_LCD_VSYNC (4)
+#define BSP_LCD_MIPI_DSI_LCD_VBP (16)
+#define BSP_LCD_MIPI_DSI_LCD_VFP (16)
+#elif CONFIG_BSP_LCD_TYPE_1280_800_WAVESHARE_10_1
+#define BSP_LCD_H_RES (800)
+#define BSP_LCD_V_RES (1280)
+
+#define BSP_LCD_MIPI_DSI_LCD_HSYNC (20)
+#define BSP_LCD_MIPI_DSI_LCD_HBP (20)
+#define BSP_LCD_MIPI_DSI_LCD_HFP (40)
+#define BSP_LCD_MIPI_DSI_LCD_VSYNC (4)
+#define BSP_LCD_MIPI_DSI_LCD_VBP (10)
+#define BSP_LCD_MIPI_DSI_LCD_VFP (30)
+
+#endif
+
+#define BSP_LCD_MIPI_DSI_LANE_NUM (2) // 2 data lanes
+#define BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS (CONFIG_BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS) // 1Gbps
+
+#define BSP_MIPI_DSI_PHY_PWR_LDO_CHAN (3) // LDO_VO3 is connected to VDD_MIPI_DPHY
+#define BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief BSP display configuration structure
+ *
+ */
+typedef struct {
+ int dummy;
+} bsp_display_config_t;
+
+/**
+ * @brief BSP display return handles
+ *
+ */
+typedef struct {
+ esp_lcd_dsi_bus_handle_t mipi_dsi_bus; /*!< MIPI DSI bus handle */
+ esp_lcd_panel_io_handle_t io; /*!< ESP LCD IO handle */
+ esp_lcd_panel_handle_t panel; /*!< ESP LCD panel (color) handle */
+ esp_lcd_panel_handle_t control; /*!< ESP LCD panel (control) handle */
+} bsp_lcd_handles_t;
+
+/**
+ * @brief Create new display panel
+ *
+ * For maximum flexibility, this function performs only reset and initialization of the display.
+ * You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off().
+ * The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(),
+ * bsp_display_brightness_set() (on supported boards) or implement your own backlight control.
+ *
+ * If you want to free resources allocated by this function, you can use esp_lcd API, ie.:
+ *
+ * \code{.c}
+ * esp_lcd_panel_del(panel);
+ * esp_lcd_panel_io_del(io);
+ * esp_lcd_del_dsi_bus(mipi_dsi_bus);
+ * \endcode
+ *
+ * @param[in] config display configuration
+ * @param[out] ret_panel esp_lcd panel handle
+ * @param[out] ret_io esp_lcd IO handle
+ * @return
+ * - ESP_OK On success
+ * - Else esp_lcd failure
+ */
+esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io);
+
+/**
+ * @brief Create new display panel
+ *
+ * For maximum flexibility, this function performs only reset and initialization of the display.
+ * You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off().
+ * The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(),
+ * bsp_display_brightness_set() (on supported boards) or implement your own backlight control.
+ *
+ * If you want to free resources allocated by this function, you can use esp_lcd API, ie.:
+ *
+ * \code{.c}
+ * esp_lcd_panel_del(panel);
+ * esp_lcd_panel_del(control);
+ * esp_lcd_panel_io_del(io);
+ * esp_lcd_del_dsi_bus(mipi_dsi_bus);
+ * \endcode
+ *
+ * @param[in] config display configuration
+ * @param[out] ret_handles all esp_lcd handles in one structure
+ * @return
+ * - ESP_OK On success
+ * - Else esp_lcd failure
+ */
+esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_lcd_handles_t *ret_handles);
+
+/**
+ * @brief Initialize display's brightness
+ *
+ * Brightness is controlled with PWM signal to a pin controlling backlight.
+ *
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_INVALID_ARG Parameter error
+ */
+esp_err_t bsp_display_brightness_init(void);
+
+/**
+ * @brief Set display's brightness
+ *
+ * Brightness is controlled with PWM signal to a pin controlling backlight.
+ * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new()
+ *
+ * @param[in] brightness_percent Brightness in [%]
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_INVALID_ARG Parameter error
+ */
+esp_err_t bsp_display_brightness_set(int brightness_percent);
+
+/**
+ * @brief Turn on display backlight
+ *
+ * Brightness is controlled with PWM signal to a pin controlling backlight.
+ * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new()
+ *
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_INVALID_ARG Parameter error
+ */
+esp_err_t bsp_display_backlight_on(void);
+
+/**
+ * @brief Turn off display backlight
+ *
+ * Brightness is controlled with PWM signal to a pin controlling backlight.
+ * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new()
+ *
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_INVALID_ARG Parameter error
+ */
+esp_err_t bsp_display_backlight_off(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/bsp/esp32_p4_nano/include/bsp/esp-bsp.h b/bsp/esp32_p4_nano/include/bsp/esp-bsp.h
new file mode 100644
index 0000000..b582d2f
--- /dev/null
+++ b/bsp/esp32_p4_nano/include/bsp/esp-bsp.h
@@ -0,0 +1,2 @@
+#pragma once
+#include "bsp/esp32_p4_nano.h"
diff --git a/bsp/esp32_p4_nano/include/bsp/esp32_p4_nano.h b/bsp/esp32_p4_nano/include/bsp/esp32_p4_nano.h
new file mode 100644
index 0000000..f755df3
--- /dev/null
+++ b/bsp/esp32_p4_nano/include/bsp/esp32_p4_nano.h
@@ -0,0 +1,371 @@
+#pragma once
+
+#include "sdkconfig.h"
+#include "driver/gpio.h"
+#include "driver/i2c_master.h"
+#include "driver/sdmmc_host.h"
+#include "driver/i2s_std.h"
+#include "bsp/config.h"
+#include "bsp/display.h"
+#include "esp_codec_dev.h"
+#include "sdkconfig.h"
+
+#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
+#include "lvgl.h"
+#include "esp_lvgl_port.h"
+#endif // BSP_CONFIG_NO_GRAPHIC_LIB == 0
+
+/**************************************************************************************************
+ * BSP Capabilities
+ **************************************************************************************************/
+
+#define BSP_CAPS_DISPLAY 1
+#define BSP_CAPS_TOUCH 1
+#define BSP_CAPS_BUTTONS 0
+#define BSP_CAPS_AUDIO 1
+#define BSP_CAPS_AUDIO_SPEAKER 1
+#define BSP_CAPS_AUDIO_MIC 1
+#define BSP_CAPS_SDCARD 1
+#define BSP_CAPS_IMU 0
+
+/**************************************************************************************************
+ * ESP-BOX pinout
+ **************************************************************************************************/
+/* I2C */
+#define BSP_I2C_SCL (GPIO_NUM_8)
+#define BSP_I2C_SDA (GPIO_NUM_7)
+
+/* Audio */
+#define BSP_I2S_SCLK (GPIO_NUM_12)
+#define BSP_I2S_MCLK (GPIO_NUM_13)
+#define BSP_I2S_LCLK (GPIO_NUM_10)
+#define BSP_I2S_DOUT (GPIO_NUM_9)
+#define BSP_I2S_DSIN (GPIO_NUM_11)
+#define BSP_POWER_AMP_IO (GPIO_NUM_53)
+
+/* Display */
+#if CONFIG_BSP_LCD_TYPE_1024_600
+#define BSP_LCD_BACKLIGHT (GPIO_NUM_26)
+#define BSP_LCD_RST (GPIO_NUM_27)
+#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC)
+#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC)
+#else
+#define BSP_LCD_BACKLIGHT (GPIO_NUM_23)
+#define BSP_LCD_RST (GPIO_NUM_NC)
+#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC)
+#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC)
+#endif
+
+/* uSD card */
+#define BSP_SD_D0 (GPIO_NUM_39)
+#define BSP_SD_D1 (GPIO_NUM_40)
+#define BSP_SD_D2 (GPIO_NUM_41)
+#define BSP_SD_D3 (GPIO_NUM_42)
+#define BSP_SD_CMD (GPIO_NUM_44)
+#define BSP_SD_CLK (GPIO_NUM_43)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**************************************************************************************************
+ *
+ * I2C interface
+ *
+ * There are multiple devices connected to I2C peripheral:
+ * - Codec ES8311 (configuration only)
+ * - LCD Touch controller
+ **************************************************************************************************/
+#define BSP_I2C_NUM CONFIG_BSP_I2C_NUM
+
+/**
+ * @brief Init I2C driver
+ *
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_INVALID_ARG I2C parameter error
+ * - ESP_FAIL I2C driver installation error
+ *
+ */
+esp_err_t bsp_i2c_init(void);
+
+/**
+ * @brief Deinit I2C driver and free its resources
+ *
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_INVALID_ARG I2C parameter error
+ *
+ */
+esp_err_t bsp_i2c_deinit(void);
+
+/**
+ * @brief Get I2C driver handle
+ *
+ * @return
+ * - I2C handle
+ *
+ */
+i2c_master_bus_handle_t bsp_i2c_get_handle(void);
+
+/**************************************************************************************************
+ *
+ * I2S audio interface
+ *
+ * There are two devices connected to the I2S peripheral:
+ * - Codec ES8311 for output(playback) and input(recording) path
+ *
+ * For speaker initialization use bsp_audio_codec_speaker_init() which is inside initialize I2S with bsp_audio_init().
+ * For microphone initialization use bsp_audio_codec_microphone_init() which is inside initialize I2S with bsp_audio_init().
+ * After speaker or microphone initialization, use functions from esp_codec_dev for play/record audio.
+ * Example audio play:
+ * \code{.c}
+ * esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME);
+ * esp_codec_dev_open(spk_codec_dev, &fs);
+ * esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read_from_spiffs);
+ * esp_codec_dev_close(spk_codec_dev);
+ * \endcode
+ **************************************************************************************************/
+
+/**
+ * @brief Init audio
+ *
+ * @note There is no deinit audio function. Users can free audio resources by calling i2s_del_channel()
+ * @warning The type of i2s_config param is depending on IDF version.
+ * @param[in] i2s_config I2S configuration. Pass NULL to use default values (Mono, duplex, 16bit, 22050 Hz)
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip
+ * - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration
+ * - ESP_ERR_NOT_FOUND No available I2S channel found
+ * - ESP_ERR_NO_MEM No memory for storing the channel information
+ * - ESP_ERR_INVALID_STATE This channel has not initialized or already started
+ */
+esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config);
+
+/**
+ * @brief Initialize speaker codec device
+ *
+ * @return Pointer to codec device handle or NULL when error occurred
+ */
+esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void);
+
+/**
+ * @brief Initialize microphone codec device
+ *
+ * @return Pointer to codec device handle or NULL when error occurred
+ */
+esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void);
+
+/**************************************************************************************************
+ *
+ * SPIFFS
+ *
+ * After mounting the SPIFFS, it can be accessed with stdio functions ie.:
+ * \code{.c}
+ * FILE* f = fopen(BSP_SPIFFS_MOUNT_POINT"/hello.txt", "w");
+ * fprintf(f, "Hello World!\n");
+ * fclose(f);
+ * \endcode
+ **************************************************************************************************/
+#define BSP_SPIFFS_MOUNT_POINT CONFIG_BSP_SPIFFS_MOUNT_POINT
+
+/**
+ * @brief Mount SPIFFS to virtual file system
+ *
+ * @return
+ * - ESP_OK on success
+ * - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_register was already called
+ * - ESP_ERR_NO_MEM if memory can not be allocated
+ * - ESP_FAIL if partition can not be mounted
+ * - other error codes
+ */
+esp_err_t bsp_spiffs_mount(void);
+
+/**
+ * @brief Unmount SPIFFS from virtual file system
+ *
+ * @return
+ * - ESP_OK on success
+ * - ESP_ERR_NOT_FOUND if the partition table does not contain SPIFFS partition with given label
+ * - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_unregister was already called
+ * - ESP_ERR_NO_MEM if memory can not be allocated
+ * - ESP_FAIL if partition can not be mounted
+ * - other error codes
+ */
+esp_err_t bsp_spiffs_unmount(void);
+
+/**************************************************************************************************
+ *
+ * uSD card
+ *
+ * After mounting the uSD card, it can be accessed with stdio functions ie.:
+ * \code{.c}
+ * FILE* f = fopen(BSP_MOUNT_POINT"/hello.txt", "w");
+ * fprintf(f, "Hello %s!\n", bsp_sdcard->cid.name);
+ * fclose(f);
+ * \endcode
+ **************************************************************************************************/
+#define BSP_SD_MOUNT_POINT CONFIG_BSP_SD_MOUNT_POINT
+extern sdmmc_card_t *bsp_sdcard;
+
+/**
+ * @brief Mount microSD card to virtual file system
+ *
+ * @return
+ * - ESP_OK on success
+ * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
+ * - ESP_ERR_NO_MEM if memory cannot be allocated
+ * - ESP_FAIL if partition cannot be mounted
+ * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
+ */
+esp_err_t bsp_sdcard_mount(void);
+
+/**
+ * @brief Unmount microSD card from virtual file system
+ *
+ * @return
+ * - ESP_OK on success
+ * - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label
+ * - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount was already called
+ * - ESP_ERR_NO_MEM if memory can not be allocated
+ * - ESP_FAIL if partition can not be mounted
+ * - other error codes from wear levelling library, SPI flash driver, or FATFS drivers
+ */
+esp_err_t bsp_sdcard_unmount(void);
+
+/**************************************************************************************************
+ *
+ * LCD interface
+ *
+ * ESP-BOX is shipped with 2.4inch ST7789 display controller.
+ * It features 16-bit colors, 320x240 resolution and capacitive touch controller.
+ *
+ * LVGL is used as graphics library. LVGL is NOT thread safe, therefore the user must take LVGL mutex
+ * by calling bsp_display_lock() before calling and LVGL API (lv_...) and then give the mutex with
+ * bsp_display_unlock().
+ *
+ * Display's backlight must be enabled explicitly by calling bsp_display_backlight_on()
+ **************************************************************************************************/
+#define BSP_LCD_PIXEL_CLOCK_MHZ (80)
+
+#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
+
+#define BSP_LCD_DRAW_BUFF_SIZE (BSP_LCD_H_RES * 50) // Frame buffer size in pixels
+#define BSP_LCD_DRAW_BUFF_DOUBLE (0)
+
+/**
+ * @brief BSP display configuration structure
+ *
+ */
+typedef struct {
+ lvgl_port_cfg_t lvgl_port_cfg; /*!< LVGL port configuration */
+ uint32_t buffer_size; /*!< Size of the buffer for the screen in pixels */
+ bool double_buffer; /*!< True, if should be allocated two buffers */
+ struct {
+ unsigned int buff_dma: 1; /*!< Allocated LVGL buffer will be DMA capable */
+ unsigned int buff_spiram: 1; /*!< Allocated LVGL buffer will be in PSRAM */
+ unsigned int sw_rotate: 1; /*!< Use software rotation (slower), The feature is unavailable under avoid-tear mode */
+ } flags;
+} bsp_display_cfg_t;
+
+/**
+ * @brief Initialize display
+ *
+ * This function initializes SPI, display controller and starts LVGL handling task.
+ * LCD backlight must be enabled separately by calling bsp_display_brightness_set()
+ *
+ * @return Pointer to LVGL display or NULL when error occured
+ */
+lv_display_t *bsp_display_start(void);
+
+/**
+ * @brief Initialize display
+ *
+ * This function initializes SPI, display controller and starts LVGL handling task.
+ * LCD backlight must be enabled separately by calling bsp_display_brightness_set()
+ *
+ * @param cfg display configuration
+ *
+ * @return Pointer to LVGL display or NULL when error occured
+ */
+lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg);
+
+/**
+ * @brief Get pointer to input device (touch, buttons, ...)
+ *
+ * @note The LVGL input device is initialized in bsp_display_start() function.
+ *
+ * @return Pointer to LVGL input device or NULL when not initialized
+ */
+lv_indev_t *bsp_display_get_input_dev(void);
+
+/**
+ * @brief Take LVGL mutex
+ *
+ * @param timeout_ms Timeout in [ms]. 0 will block indefinitely.
+ * @return true Mutex was taken
+ * @return false Mutex was NOT taken
+ */
+bool bsp_display_lock(uint32_t timeout_ms);
+
+/**
+ * @brief Give LVGL mutex
+ *
+ */
+void bsp_display_unlock(void);
+
+/**
+ * @brief Rotate screen
+ *
+ * Display must be already initialized by calling bsp_display_start()
+ *
+ * @param[in] disp Pointer to LVGL display
+ * @param[in] rotation Angle of the display rotation
+ */
+void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation);
+#endif // BSP_CONFIG_NO_GRAPHIC_LIB == 0
+
+/**************************************************************************************************
+ *
+ * USB
+ *
+ **************************************************************************************************/
+
+
+/**
+ * @brief Power modes of USB Host connector
+ */
+typedef enum bsp_usb_host_power_mode_t {
+ BSP_USB_HOST_POWER_MODE_USB_DEV, //!< Power from USB DEV port
+} bsp_usb_host_power_mode_t;
+
+/**
+ * @brief Start USB host
+ *
+ * This is a one-stop-shop function that will configure the board for USB Host mode
+ * and start USB Host library
+ *
+ * @param[in] mode USB Host connector power mode (Not used on this board)
+ * @param[in] limit_500mA Limit output current to 500mA (Not used on this board)
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_ERR_NO_MEM Memory cannot be allocated
+ */
+esp_err_t bsp_usb_host_start(bsp_usb_host_power_mode_t mode, bool limit_500mA);
+
+/**
+ * @brief Stop USB host
+ *
+ * USB Host lib will be uninstalled and power from connector removed.
+ *
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_INVALID_ARG Parameter error
+ */
+esp_err_t bsp_usb_host_stop(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/bsp/esp32_p4_nano/include/bsp/touch.h b/bsp/esp32_p4_nano/include/bsp/touch.h
new file mode 100644
index 0000000..0f7eb44
--- /dev/null
+++ b/bsp/esp32_p4_nano/include/bsp/touch.h
@@ -0,0 +1,35 @@
+#pragma once
+#include "esp_lcd_touch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief BSP touch configuration structure
+ *
+ */
+typedef struct {
+ void *dummy; /*!< Prepared for future use. */
+} bsp_touch_config_t;
+
+/**
+ * @brief Create new touchscreen
+ *
+ * If you want to free resources allocated by this function, you can use esp_lcd_touch API, ie.:
+ *
+ * \code{.c}
+ * esp_lcd_touch_del(tp);
+ * \endcode
+ *
+ * @param[in] config touch configuration
+ * @param[out] ret_touch esp_lcd_touch touchscreen handle
+ * @return
+ * - ESP_OK On success
+ * - Else esp_lcd_touch failure
+ */
+esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/bsp/esp32_p4_nano/priv_include/bsp_err_check.h b/bsp/esp32_p4_nano/priv_include/bsp_err_check.h
new file mode 100644
index 0000000..44d93dd
--- /dev/null
+++ b/bsp/esp32_p4_nano/priv_include/bsp_err_check.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "esp_check.h"
+#include "sdkconfig.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Assert on error, if selected in menuconfig. Otherwise return error code. */
+#if CONFIG_BSP_ERROR_CHECK
+#define BSP_ERROR_CHECK_RETURN_ERR(x) ESP_ERROR_CHECK(x)
+#define BSP_ERROR_CHECK_RETURN_NULL(x) ESP_ERROR_CHECK(x)
+#define BSP_ERROR_CHECK(x, ret) ESP_ERROR_CHECK(x)
+#define BSP_NULL_CHECK(x, ret) assert(x)
+#define BSP_NULL_CHECK_GOTO(x, goto_tag) assert(x)
+#else
+#define BSP_ERROR_CHECK_RETURN_ERR(x) do { \
+ esp_err_t err_rc_ = (x); \
+ if (unlikely(err_rc_ != ESP_OK)) { \
+ return err_rc_; \
+ } \
+ } while(0)
+
+#define BSP_ERROR_CHECK_RETURN_NULL(x) do { \
+ if (unlikely((x) != ESP_OK)) { \
+ return NULL; \
+ } \
+ } while(0)
+
+#define BSP_NULL_CHECK(x, ret) do { \
+ if ((x) == NULL) { \
+ return ret; \
+ } \
+ } while(0)
+
+#define BSP_ERROR_CHECK(x, ret) do { \
+ if (unlikely((x) != ESP_OK)) { \
+ return ret; \
+ } \
+ } while(0)
+
+#define BSP_NULL_CHECK_GOTO(x, goto_tag) do { \
+ if ((x) == NULL) { \
+ goto goto_tag; \
+ } \
+ } while(0)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/bsp/esp32_s3_touch_amoled_1_8/CMakeLists.txt b/bsp/esp32_s3_touch_amoled_1_8/CMakeLists.txt
new file mode 100644
index 0000000..ba279ca
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/CMakeLists.txt
@@ -0,0 +1,13 @@
+if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "5.0")
+ set(SRC_VER "esp32_s3_touch_amoled_1_8_bsp_idf4.c")
+else()
+ set(SRC_VER "esp32_s3_touch_amoled_1_8_bsp_idf5.c")
+endif()
+
+idf_component_register(
+ SRCS "esp32_s3_touch_amoled_1_8.c" ${SRC_VER}
+ INCLUDE_DIRS "include" "include/bsp"
+ PRIV_INCLUDE_DIRS "priv_include"
+ REQUIRES driver
+ PRIV_REQUIRES esp_lcd usb spiffs fatfs
+)
diff --git a/bsp/esp32_s3_touch_amoled_1_8/Kconfig b/bsp/esp32_s3_touch_amoled_1_8/Kconfig
new file mode 100644
index 0000000..8bc1462
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/Kconfig
@@ -0,0 +1,105 @@
+menu "Board Support Package"
+
+ config BSP_ERROR_CHECK
+ bool "Enable error check in BSP"
+ default y
+ help
+ Error check assert the application before returning the error code.
+
+ menu "I2C"
+ config BSP_I2C_NUM
+ int "I2C peripheral index"
+ default 1
+ range 0 1
+ help
+ ESP32-S2 has two I2C peripherals, pick the one you want to use.
+
+ config BSP_I2C_FAST_MODE
+ bool "Enable I2C fast mode"
+ default y
+ help
+ I2C has two speed modes: normal (100kHz) and fast (400kHz).
+
+ config BSP_I2C_CLK_SPEED_HZ
+ int
+ default 400000 if BSP_I2C_FAST_MODE
+ default 100000
+ endmenu
+
+ menu "SPIFFS - Virtual File System"
+ config BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL
+ bool "Format SPIFFS if mounting fails"
+ default n
+ help
+ Format SPIFFS if it fails to mount the filesystem.
+
+ config BSP_SPIFFS_MOUNT_POINT
+ string "SPIFFS mount point"
+ default "/spiffs"
+ help
+ Mount point of SPIFFS in the Virtual File System.
+
+ config BSP_SPIFFS_PARTITION_LABEL
+ string "Partition label of SPIFFS"
+ default "storage"
+ help
+ Partition label which stores SPIFFS.
+
+ config BSP_SPIFFS_MAX_FILES
+ int "Maximum files that could be open at the same time"
+ default 2
+ help
+ Supported max files for SPIFFS in the Virtual File System.
+ endmenu
+
+ menu "uSD card - Virtual File System"
+ config BSP_SD_FORMAT_ON_MOUNT_FAIL
+ bool "Format uSD card if mounting fails"
+ default n
+ help
+ The SDMMC host will format (FAT) the uSD card if it fails to mount the filesystem.
+
+ config BSP_SD_MOUNT_POINT
+ string "uSD card mount point"
+ default "/sdcard"
+ help
+ Mount point of the uSD card in the Virtual File System
+
+ endmenu
+ menu "Display"
+ config BSP_DISPLAY_LVGL_TASK_PRIORITY
+ int "LVGL task priority"
+ default 2
+ help
+ The Board Support Package will create a task that will periodically handle LVGL operation in lv_timer_handler().
+
+ config BSP_DISPLAY_LVGL_TICK
+ int "LVGL tick period"
+ default 5
+ range 1 100
+ help
+ Period of LVGL tick timer.
+
+ config BSP_DISPLAY_LVGL_MAX_SLEEP
+ int "LVGL maximum sleep time in ms"
+ default 1
+ range 1 500
+ help
+ Maximum time for task sleep in ms.
+
+ config BSP_DISPLAY_BRIGHTNESS_LEDC_CH
+ int "LEDC channel index"
+ default 1
+ range 0 7
+ help
+ LEDC channel is used to generate PWM signal that controls display brightness.
+ Set LEDC index that should be used.
+ endmenu
+
+ config BSP_I2S_NUM
+ int "I2S peripheral index"
+ default 1
+ range 0 1
+ help
+ ESP32S3 has two I2S peripherals, pick the one you want to use.
+endmenu
diff --git a/bsp/esp32_s3_touch_amoled_1_8/LICENSE b/bsp/esp32_s3_touch_amoled_1_8/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ 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/bsp/esp32_s3_touch_amoled_1_8/README.md b/bsp/esp32_s3_touch_amoled_1_8/README.md
new file mode 100644
index 0000000..dba4917
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/README.md
@@ -0,0 +1,54 @@
+# BSP: Waveshare ESP32-P4-NANO
+
+[![Component Registry](https://components.espressif.com/components/waveshare/esp32_p4_nano/badge.svg)](https://components.espressif.com/components/waveshare/esp32_p4_nano)
+
+ESP32-P4-NANO is a small size and highly integrated development board designed by waveshare electronics based on ESP32-P4 chip
+| HW version | BSP Version |
+| :--------: | :---------: |
+| [V1.0](http://www.waveshare.com/wiki/ESP32-P4-NANO) | ^1 |
+
+## Configuration
+
+Configuration in `menuconfig`.
+
+Selection LCD display `Board Support Package(ESP32-P4) --> Display --> Select LCD type`
+- Waveshare LCD 10.1-inch 800x1280 - jd9365 (default)
+- LCD 7-inch 1280x800 - ili9881c
+- LCD 1024x600 - ek79007
+
+Selection color format `Board Support Package(ESP32-P4) --> Display --> Select LCD color format`
+- RGB565 (default)
+- RGB888
+
+Change MIPI DSI lane bitrate `Board Support Package(ESP32-P4) --> Display --> MIPI DSI lane bitrate (Mbps)`
+- 1500 (default)
+
+## Display Page
+
+[Waveshare 101M-8001280-IPS-CT-K](https://www.waveshare.com/101m-8001280-ips-ct-k.htm)
+
+## BackLight
+```c
+bsp_display_brightness_init();
+
+bsp_display_backlight_on();
+
+bsp_display_backlight_off();
+
+bsp_display_brightness_set(100);
+```
+
+
+### Capabilities and dependencies
+| Capability | Available | Component |Version|
+|-------------|------------------|----------------------------------------------------------------------------------------------------------|-------|
+| DISPLAY |:heavy_check_mark:| [waveshare/esp_lcd_jd9365_10_1](https://components.espressif.com/components/waveshare/esp_lcd_jd9365_10_1) | 0.0.4 |
+| LVGL_PORT |:heavy_check_mark:| [espressif/esp_lvgl_port](https://components.espressif.com/components/espressif/esp_lvgl_port) | ^2 |
+| TOUCH |:heavy_check_mark:|[espressif/esp_lcd_touch_gt911](https://components.espressif.com/components/espressif/esp_lcd_touch_gt911)| ^1 |
+| BUTTONS | :x: | | |
+| AUDIO |:heavy_check_mark:| [espressif/esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev) | 1.2.* |
+|AUDIO_SPEAKER|:heavy_check_mark:| | |
+| AUDIO_MIC |:heavy_check_mark:| | |
+| SDCARD |:heavy_check_mark:| idf | >=5.3 |
+| IMU | :x: | | |
+
diff --git a/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp.c b/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp.c
new file mode 100644
index 0000000..cd2d51d
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp.c
@@ -0,0 +1,398 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include
+#include "esp_vfs_fat.h"
+#include "esp_lcd_panel_io.h"
+#include "esp_lcd_panel_vendor.h"
+#include "esp_lcd_panel_ops.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_check.h"
+#include "esp_vfs_fat.h"
+#include "esp_spiffs.h"
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#include "driver/spi_master.h"
+#include "driver/i2c.h"
+
+#include "esp_lcd_sh8601.h"
+#include "esp_lcd_touch_ft5x06.h"
+
+#include "esp_codec_dev_defaults.h"
+#include "bsp/esp32_s3_touch_amoled_1_8_bsp.h"
+#include "bsp_err_check.h"
+#include "bsp/display.h"
+
+static const char *TAG = "ESP32-S3-Touch-AMOLED-1.8-BSP";
+
+sdmmc_card_t *bsp_sdcard = NULL; // Global uSD card handler
+
+static i2c_master_bus_handle_t i2c_handle = NULL; // I2C Handle
+
+static i2s_chan_handle_t i2s_tx_chan = NULL;
+static i2s_chan_handle_t i2s_rx_chan = NULL;
+static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface */
+
+static bool i2c_initialized = false;
+
+esp_err_t bsp_i2c_init(void)
+{
+ /* I2C was initialized before */
+ if (i2c_initialized) {
+ return ESP_OK;
+ }
+
+ const i2c_config_t i2c_conf = {
+ .mode = I2C_MODE_MASTER,
+ .sda_io_num = BSP_I2C_SDA,
+ .sda_pullup_en = GPIO_PULLUP_ENABLE,
+ .scl_io_num = BSP_I2C_SCL,
+ .scl_pullup_en = GPIO_PULLUP_ENABLE,
+ .master.clk_speed = 400000
+ };
+ BSP_ERROR_CHECK_RETURN_ERR(i2c_param_config(BSP_I2C_NUM, &i2c_conf));
+ BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_install(BSP_I2C_NUM, i2c_conf.mode, 0, 0, 0));
+
+ i2c_initialized = true;
+
+ return ESP_OK;
+}
+
+esp_err_t bsp_i2c_deinit(void)
+{
+ BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_delete(BSP_I2C_NUM));
+ i2c_initialized = false;
+ return ESP_OK;
+}
+
+esp_err_t bsp_spiffs_mount(void)
+{
+ esp_vfs_spiffs_conf_t conf = {
+ .base_path = CONFIG_BSP_SPIFFS_MOUNT_POINT,
+ .partition_label = CONFIG_BSP_SPIFFS_PARTITION_LABEL,
+ .max_files = CONFIG_BSP_SPIFFS_MAX_FILES,
+#ifdef CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL
+ .format_if_mount_failed = true,
+#else
+ .format_if_mount_failed = false,
+#endif
+ };
+
+ esp_err_t ret_val = esp_vfs_spiffs_register(&conf);
+
+ BSP_ERROR_CHECK_RETURN_ERR(ret_val);
+
+ size_t total = 0, used = 0;
+ ret_val = esp_spiffs_info(conf.partition_label, &total, &used);
+ if (ret_val != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret_val));
+ } else {
+ ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
+ }
+
+ return ret_val;
+}
+
+esp_err_t bsp_spiffs_unmount(void)
+{
+ return esp_vfs_spiffs_unregister(CONFIG_BSP_SPIFFS_PARTITION_LABEL);
+}
+
+esp_err_t bsp_sdcard_mount(void)
+{
+ gpio_config_t power_gpio_config = {
+ .mode = GPIO_MODE_OUTPUT,
+ .pin_bit_mask = 1ULL << BSP_SD_POWER
+ };
+ ESP_ERROR_CHECK(gpio_config(&power_gpio_config));
+
+ /* SD card power on first */
+ ESP_ERROR_CHECK(gpio_set_level(BSP_SD_POWER, 0));
+
+ const esp_vfs_fat_sdmmc_mount_config_t mount_config = {
+#ifdef CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL
+ .format_if_mount_failed = true,
+#else
+ .format_if_mount_failed = false,
+#endif
+ .max_files = 5,
+ .allocation_unit_size = 16 * 1024
+ };
+
+ sdmmc_host_t host = SDMMC_HOST_DEFAULT();
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+ slot_config.width = 1;
+ slot_config.cmd = BSP_SD_CMD;
+ slot_config.clk = BSP_SD_CLK;
+ slot_config.d0 = BSP_SD_D0;
+
+ return esp_vfs_fat_sdmmc_mount(BSP_SD_MOUNT_POINT, &host, &slot_config, &mount_config, &bsp_sdcard);
+}
+
+esp_err_t bsp_sdcard_unmount(void)
+{
+ return esp_vfs_fat_sdcard_unmount(BSP_SD_MOUNT_POINT, bsp_sdcard);
+}
+
+#define LCD_CMD_BITS (8)
+#define LCD_PARAM_BITS (8)
+#define LCD_LEDC_CH (CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH)
+#define LVGL_TICK_PERIOD_MS (CONFIG_BSP_DISPLAY_LVGL_TICK)
+#define LVGL_MAX_SLEEP_MS (CONFIG_BSP_DISPLAY_LVGL_MAX_SLEEP)
+
+// TODO: brightness cmds sent
+esp_err_t bsp_display_brightness_init(void)
+{
+
+ return ESP_OK;
+}
+
+esp_err_t bsp_display_backlight_off(void)
+{
+ ESP_LOGI(TAG, "Backlight off");
+ return ESP_OK;
+}
+
+esp_err_t bsp_display_backlight_on(void)
+{
+ ESP_LOGI(TAG, "Backlight on");
+ return ESP_OK;
+}
+
+// TODO: SH8601 Display init
+esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io)
+{
+ esp_err_t ret = ESP_OK;
+
+ return ret;
+}
+
+#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
+static lv_display_t *bsp_display_lcd_init(const bsp_display_cfg_t *cfg)
+{
+ assert(cfg != NULL);
+ esp_lcd_panel_io_handle_t io_handle = NULL;
+ esp_lcd_panel_handle_t panel_handle = NULL;
+ const bsp_display_config_t bsp_disp_cfg = {
+ .max_transfer_sz = cfg->trans_size ? (cfg->trans_size * sizeof(uint16_t)): (BSP_LCD_DRAW_BUFF_SIZE * sizeof(uint16_t)),
+ };
+ BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new(&bsp_disp_cfg, &panel_handle, &io_handle));
+
+#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
+ esp_lcd_panel_disp_off(panel_handle, false);
+#else
+ esp_lcd_panel_disp_on_off(panel_handle, true);
+#endif
+
+ /* Add LCD screen */
+ ESP_LOGD(TAG, "Add LCD screen");
+ const lvgl_port_display_cfg_t disp_cfg = {
+ .io_handle = io_handle,
+ .panel_handle = panel_handle,
+ .buffer_size = cfg->buffer_size,
+ .trans_size = cfg->trans_size,
+ .double_buffer = cfg->double_buffer,
+ .hres = BSP_LCD_H_RES,
+ .vres = BSP_LCD_V_RES,
+ .monochrome = false,
+ /* Rotation values must be same as used in esp_lcd for initial settings of the screen */
+ .rotation = {
+ .swap_xy = false,
+ .mirror_x = false,
+ .mirror_y = false,
+ },
+ .flags = {
+ .buff_dma = cfg->flags.buff_dma,
+ .buff_spiram = cfg->flags.buff_spiram,
+#if LVGL_VERSION_MAJOR >= 9
+ .swap_bytes = (BSP_LCD_BIGENDIAN ? true : false),
+#endif
+ }
+ };
+
+ return lvgl_port_add_disp(&disp_cfg);
+}
+
+static lv_indev_t *bsp_display_indev_init(lv_display_t *disp)
+{
+ esp_lcd_touch_handle_t tp;
+ BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp));
+ assert(tp);
+
+ /* Add touch input (for selected screen) */
+ const lvgl_port_touch_cfg_t touch_cfg = {
+ .disp = disp,
+ .handle = tp,
+ };
+
+ return lvgl_port_add_touch(&touch_cfg);
+}
+
+lv_display_t *bsp_display_start(void)
+{
+ bsp_display_cfg_t cfg = {
+ .lvgl_port_cfg = {
+ .task_priority = CONFIG_BSP_DISPLAY_LVGL_TASK_PRIORITY,
+ .task_stack = 6144,
+ .task_affinity = 1,
+ .timer_period_ms = LVGL_TICK_PERIOD_MS,
+ .task_max_sleep_ms = LVGL_MAX_SLEEP_MS,
+ },
+ .buffer_size = BSP_LCD_DRAW_BUFF_SIZE,
+ .double_buffer = BSP_LCD_DRAW_BUFF_DOUBLE,
+ .flags = {
+ .buff_dma = true,
+ .buff_spiram = false,
+ }
+ };
+ return bsp_display_start_with_config(&cfg);
+}
+
+lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg)
+{
+ lv_display_t *disp;
+ assert(cfg != NULL);
+ BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_init(&cfg->lvgl_port_cfg));
+ BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL);
+
+ return disp;
+}
+
+lv_indev_t *bsp_display_get_input_dev(void)
+{
+ return NULL;
+}
+
+bool bsp_display_lock(uint32_t timeout_ms)
+{
+ return lvgl_port_lock(timeout_ms);
+}
+
+void bsp_display_unlock(void)
+{
+ lvgl_port_unlock();
+}
+
+void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation)
+{
+ lv_disp_set_rotation(disp, rotation);
+}
+#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
+
+esp_err_t bsp_iot_button_create(button_handle_t btn_array[], int *btn_cnt, int btn_array_size)
+{
+ esp_err_t ret = ESP_OK;
+ if ((btn_array_size < BSP_BUTTON_NUM) ||
+ (btn_array == NULL)) {
+ return ESP_ERR_INVALID_ARG;
+ }
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+ /* Initialize ADC and get ADC handle */
+ BSP_ERROR_CHECK_RETURN_NULL(bsp_adc_initialize());
+ bsp_adc_handle = bsp_adc_get_handle();
+#endif
+
+ if (btn_cnt) {
+ *btn_cnt = 0;
+ }
+ for (int i = 0; i < BSP_BUTTON_NUM; i++) {
+ btn_array[i] = iot_button_create(&bsp_button_config[i]);
+ if (btn_array[i] == NULL) {
+ ret = ESP_FAIL;
+ break;
+ }
+ if (btn_cnt) {
+ (*btn_cnt)++;
+ }
+ }
+ return ret;
+}
+
+
+#ifdef CONFIG_TOUCH_ELEM_EVENT
+/* Button event handler task */
+static void button_handler_task(void *arg)
+{
+ (void) arg; //Unused
+ touch_elem_message_t element_message;
+ while (1) {
+ /* Waiting for touch element messages */
+ touch_element_message_receive(&element_message, portMAX_DELAY);
+ if (element_message.element_type != TOUCH_ELEM_TYPE_BUTTON) {
+ continue;
+ }
+ /* Decode message */
+ const touch_button_message_t *button_message = touch_button_get_message(&element_message);
+ if (button_message->event == TOUCH_BUTTON_EVT_ON_PRESS) {
+ ESP_LOGI(TAG, "Button[%d] Press", (int)element_message.arg);
+ } else if (button_message->event == TOUCH_BUTTON_EVT_ON_RELEASE) {
+ ESP_LOGI(TAG, "Button[%d] Release", (int)element_message.arg);
+ } else if (button_message->event == TOUCH_BUTTON_EVT_ON_LONGPRESS) {
+ ESP_LOGI(TAG, "Button[%d] LongPress", (int)element_message.arg);
+ }
+ }
+}
+#elif CONFIG_TOUCH_ELEM_CALLBACK
+/* Button callback routine */
+// static void button_handler(touch_button_handle_t out_handle, touch_button_message_t *out_message, void *arg)
+// {
+// (void) out_handle; //Unused
+// if (out_message->event == TOUCH_BUTTON_EVT_ON_PRESS) {
+// ESP_LOGI(TAG, "Button[%d] Press", (int)arg);
+// } else if (out_message->event == TOUCH_BUTTON_EVT_ON_RELEASE) {
+// ESP_LOGI(TAG, "Button[%d] Release", (int)arg);
+// } else if (out_message->event == TOUCH_BUTTON_EVT_ON_LONGPRESS) {
+// ESP_LOGI(TAG, "Button[%d] LongPress", (int)arg);
+// }
+// }
+#endif
+
+void bsp_touch_button_create(touch_button_callback_t button_callback)
+{
+ /* Initialize Touch Element library */
+ touch_elem_global_config_t global_config = TOUCH_ELEM_GLOBAL_DEFAULT_CONFIG();
+ ESP_ERROR_CHECK(touch_element_install(&global_config));
+ ESP_LOGI(TAG, "Touch element library installed");
+
+ touch_button_global_config_t button_global_config = TOUCH_BUTTON_GLOBAL_DEFAULT_CONFIG();
+ ESP_ERROR_CHECK(touch_button_install(&button_global_config));
+ ESP_LOGI(TAG, "Touch button installed");
+ for (int i = 0; i < TOUCH_BUTTON_NUM; i++) {
+ touch_button_config_t button_config = {
+ .channel_num = channel_array[i],
+ .channel_sens = channel_sens_array[i]
+ };
+ /* Create Touch buttons */
+ ESP_ERROR_CHECK(touch_button_create(&button_config, &button_handle[i]));
+ /* Subscribe touch button events (On Press, On Release, On LongPress) */
+ ESP_ERROR_CHECK(touch_button_subscribe_event(button_handle[i],
+ TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_RELEASE | TOUCH_ELEM_EVENT_ON_LONGPRESS,
+ (void *)channel_array[i]));
+#ifdef CONFIG_TOUCH_ELEM_EVENT
+ /* Set EVENT as the dispatch method */
+ ESP_ERROR_CHECK(touch_button_set_dispatch_method(button_handle[i], TOUCH_ELEM_DISP_EVENT));
+#elif CONFIG_TOUCH_ELEM_CALLBACK
+ /* Set EVENT as the dispatch method */
+ ESP_ERROR_CHECK(touch_button_set_dispatch_method(button_handle[i], TOUCH_ELEM_DISP_CALLBACK));
+ /* Register a handler function to handle event messages */
+ ESP_ERROR_CHECK(touch_button_set_callback(button_handle[i], button_callback));
+#endif
+ /* Set LongPress event trigger threshold time */
+ ESP_ERROR_CHECK(touch_button_set_longpress(button_handle[i], 2000));
+ }
+ ESP_LOGI(TAG, "Touch buttons created");
+
+#ifdef CONFIG_TOUCH_ELEM_EVENT
+ /* Create a handler task to handle event messages */
+ xTaskCreate(&button_handler_task, "button_handler_task", 4 * 1024, NULL, 5, NULL);
+#endif
+
+ touch_element_start();
+ ESP_LOGI(TAG, "Touch element library start");
+}
diff --git a/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp_idf4.c b/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp_idf4.c
new file mode 100644
index 0000000..a48778b
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp_idf4.c
@@ -0,0 +1,36 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "esp_err.h"
+#include "bsp/esp32_s3_touch_amoled_1_8_bsp.h"
+#include "bsp_err_check.h"
+#include "esp_codec_dev_defaults.h"
+
+static const char *TAG = "ESP32-S3-Touch-AMOLED-1.8-BSP";
+
+/* Sample rate of MSM261S4030H0 */
+#define BSP_MIC_SAMPLE_RATE (48000u)
+
+/* This configuration is used by default in bsp_audio_init() */
+#define BSP_I2S_SIMPLEX_MONO_CFG(_sample_rate) \
+ { \
+ .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX, \
+ .sample_rate = _sample_rate, \
+ .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, \
+ .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, \
+ .communication_format = I2S_COMM_FORMAT_STAND_I2S, \
+ .dma_buf_count = 3, \
+ .dma_buf_len = 1024, \
+ .use_apll = true, \
+ .tx_desc_auto_clear = true, \
+ .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_IRAM \
+ }
+
+
+esp_err_t bsp_adc_initialize(void)
+{
+ return ESP_OK;
+}
diff --git a/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp_idf5.c b/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp_idf5.c
new file mode 100644
index 0000000..7471a4d
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp_idf5.c
@@ -0,0 +1,35 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "esp_err.h"
+#include "bsp/esp32_s3_touch_amoled_1_8_bsp.h"
+#include "bsp_err_check.h"
+#include "esp_codec_dev_defaults.h"
+
+static const char *TAG = "ESP32-S3-Touch-AMOLED-1.8-BSP";
+
+static adc_oneshot_unit_handle_t bsp_adc_handle = NULL;
+
+esp_err_t bsp_adc_initialize(void)
+{
+ /* ADC was initialized before */
+ if (bsp_adc_handle != NULL) {
+ return ESP_OK;
+ }
+
+ /* Initialize ADC */
+ const adc_oneshot_unit_init_cfg_t init_config1 = {
+ .unit_id = BSP_ADC_UNIT,
+ };
+ BSP_ERROR_CHECK_RETURN_ERR(adc_oneshot_new_unit(&init_config1, &bsp_adc_handle));
+
+ return ESP_OK;
+}
+
+adc_oneshot_unit_handle_t bsp_adc_get_handle(void)
+{
+ return bsp_adc_handle;
+}
diff --git a/bsp/esp32_s3_touch_amoled_1_8/idf_component.yml b/bsp/esp32_s3_touch_amoled_1_8/idf_component.yml
new file mode 100644
index 0000000..89d4cb8
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/idf_component.yml
@@ -0,0 +1,18 @@
+dependencies:
+ esp_codec_dev:
+ version: "==1.1.0"
+ public: true
+ esp_lcd_sh8601: "*"
+ esp_lcd_touch_ft5x06: "*"
+ espressif/esp_lvgl_port:
+ version: "2.4.1"
+ public: true
+ idf: '>=5.3'
+ lvgl/lvgl: '>=8,<10'
+description: Board Support Package (BSP) for Waveshare ESP32-S3-Touch-AMOLED-1.8
+tags:
+- bsp
+targets:
+- esp32s3
+url: https://www.waveshare.com/esp32-s3-touch-amoled-1.8.htm
+version: 0.0.1
diff --git a/bsp/esp32_s3_touch_amoled_1_8/include/bsp/config.h b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/config.h
new file mode 100644
index 0000000..927f814
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/config.h
@@ -0,0 +1,10 @@
+#pragma once
+
+/**************************************************************************************************
+ * BSP configuration
+ **************************************************************************************************/
+// By default, this BSP is shipped with LVGL graphical library. Enabling this option will exclude it.
+// If you want to use BSP without LVGL, select BSP version with 'noglib' suffix.
+#if !defined(BSP_CONFIG_NO_GRAPHIC_LIB) // Check if the symbol is not coming from compiler definitions (-D...)
+#define BSP_CONFIG_NO_GRAPHIC_LIB (0)
+#endif
diff --git a/bsp/esp32_s3_touch_amoled_1_8/include/bsp/display.h b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/display.h
new file mode 100644
index 0000000..48d213e
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/display.h
@@ -0,0 +1,109 @@
+
+
+#pragma once
+#include "esp_lcd_types.h"
+
+/* LCD color formats */
+#define ESP_LCD_COLOR_FORMAT_RGB565 (1)
+#define ESP_LCD_COLOR_FORMAT_RGB888 (2)
+
+/* LCD display color format */
+#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB565)
+/* LCD display color bytes endianess */
+#define BSP_LCD_BIGENDIAN (1)
+/* LCD display color bits */
+#define BSP_LCD_BITS_PER_PIXEL (16)
+/* LCD display color space */
+#define BSP_LCD_COLOR_SPACE (ESP_LCD_COLOR_SPACE_RGB)
+/* LCD display definition */
+#define BSP_LCD_H_RES (368)
+#define BSP_LCD_V_RES (448)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief BSP display configuration structure
+ *
+ */
+typedef struct {
+ int max_transfer_sz; /*!< Maximum transfer size, in bytes. */
+} bsp_display_config_t;
+
+/**
+ * @brief Create new display panel
+ *
+ * For maximum flexibility, this function performs only reset and initialization of the display.
+ * You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off().
+ * The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(),
+ * bsp_display_brightness_set() (on supported boards) or implement your own backlight control.
+ *
+ * If you want to free resources allocated by this function, you can use esp_lcd API, ie.:
+ *
+ * \code{.c}
+ * esp_lcd_panel_del(panel);
+ * esp_lcd_panel_io_del(io);
+ * spi_bus_free(spi_num_from_configuration);
+ * \endcode
+ *
+ * @param[in] config display configuration
+ * @param[out] ret_panel esp_lcd panel handle
+ * @param[out] ret_io esp_lcd IO handle
+ * @return
+ * - ESP_OK On success
+ * - Else esp_lcd failure
+ */
+esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io);
+
+/**
+ * @brief Initialize display's brightness
+ *
+ * Brightness is controlled with PWM signal to a pin controlling backlight.
+ *
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_INVALID_ARG Parameter error
+ */
+esp_err_t bsp_display_brightness_init(void);
+
+/**
+ * @brief Set display's brightness
+ *
+ * Brightness is controlled with PWM signal to a pin controlling backlight.
+ * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new()
+ *
+ * @param[in] brightness_percent Brightness in [%]
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_INVALID_ARG Parameter error
+ */
+esp_err_t bsp_display_brightness_set(int brightness_percent);
+
+/**
+ * @brief Turn on display backlight
+ *
+ * Brightness is controlled with PWM signal to a pin controlling backlight.
+ * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new()
+ *
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_INVALID_ARG Parameter error
+ */
+esp_err_t bsp_display_backlight_on(void);
+
+/**
+ * @brief Turn off display backlight
+ *
+ * Brightness is controlled with PWM signal to a pin controlling backlight.
+ * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new()
+ *
+ * @return
+ * - ESP_OK On success
+ * - ESP_ERR_INVALID_ARG Parameter error
+ */
+esp_err_t bsp_display_backlight_off(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/bsp/esp32_s3_touch_amoled_1_8/include/bsp/esp-bsp.h b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/esp-bsp.h
new file mode 100644
index 0000000..556b294
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/esp-bsp.h
@@ -0,0 +1,2 @@
+#pragma once
+#include "bsp/esp32_s3_touch_amoled_1_8_bsp.h"
diff --git a/bsp/esp32_s3_touch_amoled_1_8/include/bsp/esp32_s3_touch_amoled_1_8_bsp.h b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/esp32_s3_touch_amoled_1_8_bsp.h
new file mode 100644
index 0000000..556b294
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/esp32_s3_touch_amoled_1_8_bsp.h
@@ -0,0 +1,2 @@
+#pragma once
+#include "bsp/esp32_s3_touch_amoled_1_8_bsp.h"
diff --git a/bsp/esp32_s3_touch_amoled_1_8/include/bsp/touch.h b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/touch.h
new file mode 100644
index 0000000..9440bc7
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/touch.h
@@ -0,0 +1,35 @@
+#pragma once
+#include "esp_lcd_touch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief BSP touch configuration structure
+ *
+ */
+typedef struct {
+ void *dummy; /*!< Prepared for future use. */
+} bsp_touch_config_t;
+
+/**
+ * @brief Create new touchscreen
+ *
+ * If you want to free resources allocated by this function, you can use esp_lcd_touch API, ie.:
+ *
+ * \code{.c}
+ * esp_lcd_touch_del(tp);
+ * \endcode
+ *
+ * @param[in] config touch configuration
+ * @param[out] ret_touch esp_lcd_touch touchscreen handle
+ * @return
+ * - ESP_OK On success
+ * - Else esp_lcd_touch failure
+ */
+esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/bsp/esp32_s3_touch_amoled_1_8/priv_include/bsp_err_check.h b/bsp/esp32_s3_touch_amoled_1_8/priv_include/bsp_err_check.h
new file mode 100644
index 0000000..82f3597
--- /dev/null
+++ b/bsp/esp32_s3_touch_amoled_1_8/priv_include/bsp_err_check.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "esp_check.h"
+#include "sdkconfig.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Assert on error, if selected in menuconfig. Otherwise return error code. */
+#if CONFIG_BSP_ERROR_CHECK
+#define BSP_ERROR_CHECK_RETURN_ERR(x) ESP_ERROR_CHECK(x)
+#define BSP_ERROR_CHECK_RETURN_NULL(x) ESP_ERROR_CHECK(x)
+#define BSP_ERROR_CHECK(x, ret) ESP_ERROR_CHECK(x)
+#define BSP_NULL_CHECK(x, ret) assert(x)
+#define BSP_NULL_CHECK_GOTO(x, goto_tag) assert(x)
+#else
+#define BSP_ERROR_CHECK_RETURN_ERR(x) do { \
+ esp_err_t err_rc_ = (x); \
+ if (unlikely(err_rc_ != ESP_OK)) { \
+ return err_rc_; \
+ } \
+ } while(0)
+
+#define BSP_ERROR_CHECK_RETURN_NULL(x) do { \
+ if (unlikely((x) != ESP_OK)) { \
+ return NULL; \
+ } \
+ } while(0)
+
+#define BSP_NULL_CHECK(x, ret) do { \
+ if ((x) == NULL) { \
+ return ret; \
+ } \
+ } while(0)
+
+#define BSP_ERROR_CHECK(x, ret) do { \
+ if (unlikely((x) != ESP_OK)) { \
+ return ret; \
+ } \
+ } while(0)
+
+#define BSP_NULL_CHECK_GOTO(x, goto_tag) do { \
+ if ((x) == NULL) { \
+ goto goto_tag; \
+ } \
+ } while(0)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/display/lcd/esp_lcd_dsi/CHANGELOG.md b/display/lcd/esp_lcd_dsi/CHANGELOG.md
new file mode 100644
index 0000000..45db041
--- /dev/null
+++ b/display/lcd/esp_lcd_dsi/CHANGELOG.md
@@ -0,0 +1,5 @@
+## v0.0.1 - 2025-01-03
+
+### Enhancements:
+
+* Implement the driver for the Waveshare MIPI-DSI LCD controller
\ No newline at end of file
diff --git a/display/lcd/esp_lcd_dsi/CMakeLists.txt b/display/lcd/esp_lcd_dsi/CMakeLists.txt
new file mode 100644
index 0000000..f4ddfba
--- /dev/null
+++ b/display/lcd/esp_lcd_dsi/CMakeLists.txt
@@ -0,0 +1,6 @@
+idf_component_register(SRCS "esp_lcd_dsi.c"
+ INCLUDE_DIRS "include"
+ REQUIRES "esp_lcd")
+
+include(package_manager)
+cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
diff --git a/display/lcd/esp_lcd_dsi/README.md b/display/lcd/esp_lcd_dsi/README.md
new file mode 100644
index 0000000..24ecadc
--- /dev/null
+++ b/display/lcd/esp_lcd_dsi/README.md
@@ -0,0 +1,45 @@
+# ESP LCD DSI
+
+[![Component Registry](https://components.espressif.com/components/waveshare/esp_lcd_dsi/badge.svg)](https://components.espressif.com/components/waveshare/esp_lcd_dsi)
+
+Waveshare DSI display driver, compatible with a variety of DSI displays.
+
+| Communication interface | Component name |
+|:-----------------------:|:--------------:|
+| MIPI-DSI | esp_lcd_dsi |
+
+**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions.
+
+For more information on LCD, please refer to
+the [LCD documentation](https://docs.espressif.com/projects/esp-iot-solution/en/latest/display/lcd/index.html).
+
+## Add to project
+
+Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/).
+You can add them to your project via `idf.py add-dependancy`, e.g.
+
+```
+ idf.py add-dependency "waveshare/esp_lcd_dsi"
+```
+
+Alternatively, you can create `idf_component.yml`. More is
+in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).
+
+* List of supported screens
+
+| Product ID | Screen parameters | tested |
+|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|
+| [2.8inch DSI LCD](https://www.waveshare.com/2.8inch-dsi-lcd.htm)
| dpi_clock_freq_mhz=50
h_size=480
v_size=640
hsync_back_porch=150
hsync_pulse_width=150
hsync_front_porch=50
vsync_back_porch=50
vsync_pulse_width=150
vsync_front_porch=150 | YES |
+| [3.4inch DSI LCD (C)](https://www.waveshare.com/3.4inch-dsi-lcd-c.htm)
| dpi_clock_freq_mhz=48
h_size=800
v_size=800
hsync_back_porch=40
hsync_pulse_width=6
hsync_front_porch=120
vsync_back_porch=12
vsync_pulse_width=4
vsync_front_porch=16 | YES |
+| [4inch DSI LCD (C)](https://www.waveshare.com/4inch-dsi-lcd-c.htm)
| dpi_clock_freq_mhz=48
h_size=720
v_size=720
hsync_back_porch=32
hsync_pulse_width=200
hsync_front_porch=120
vsync_back_porch=4
vsync_pulse_width=16
vsync_front_porch=8 | YES |
+| [4inch DSI LCD](https://www.waveshare.com/4inch-dsi-lcd.htm)
| dpi_clock_freq_mhz=48.6
h_size=480
v_size=800
hsync_back_porch=150
hsync_pulse_width=100
hsync_front_porch=150
vsync_back_porch=20
vsync_pulse_width=100
vsync_front_porch=20 | YES |
+| [5inch DSI LCD (D)](https://www.waveshare.com/5inch-dsi-lcd-d.htm)
| dpi_clock_freq_mhz=80
h_size=720
v_size=1280
hsync_back_porch=100
hsync_pulse_width=100
hsync_front_porch=80
vsync_back_porch=20
vsync_pulse_width=20
vsync_front_porch=20 | YES |
+| [6.25inch DSI LCD](https://www.waveshare.com/6.25inch-dsi-lcd.htm)
| dpi_clock_freq_mhz=48
h_size=720
v_size=1560
hsync_back_porch=50
hsync_pulse_width=50
hsync_front_porch=50
vsync_back_porch=20
vsync_pulse_width=20
vsync_front_porch=20 | YES |
+| [5inch DSI LCD (C)](https://www.waveshare.com/5inch-dsi-lcd-c.htm)
| dpi_clock_freq_mhz=48
h_size=1024
v_size=600
hsync_back_porch=100
hsync_pulse_width=100
hsync_front_porch=100
vsync_back_porch=10
vsync_pulse_width=10
vsync_front_porch=10 | YES |
+| [7inch DSI LCD (C)](https://www.waveshare.com/7inch-dsi-lcd-c-with-case-a.htm)
| dpi_clock_freq_mhz=48
h_size=1024
v_size=600
hsync_back_porch=100
hsync_pulse_width=100
hsync_front_porch=100
vsync_back_porch=10
vsync_pulse_width=10
vsync_front_porch=10 | YES |
+| [7.9inch DSI LCD](https://www.waveshare.com/7.9inch-dsi-lcd.htm)
| dpi_clock_freq_mhz=48
h_size=400
v_size=1280
hsync_back_porch=40
hsync_pulse_width=30
hsync_front_porch=40
vsync_back_porch=20
vsync_pulse_width=10
vsync_front_porch=20 | YES |
+| [7inch DSI LCD (E)](https://www.waveshare.com/7inch-dsi-lcd-e.htm)
| dpi_clock_freq_mhz=80
h_size=1280
v_size=800
hsync_back_porch=156
hsync_pulse_width=40
hsync_front_porch=20
vsync_back_porch=48
vsync_pulse_width=40
vsync_front_porch=40 | YES |
+| [8inch DSI LCD (C)](https://www.waveshare.com/8inch-dsi-lcd-c.htm)
| dpi_clock_freq_mhz=80
h_size=1280
v_size=800
hsync_back_porch=156
hsync_pulse_width=40
hsync_front_porch=20
vsync_back_porch=48
vsync_pulse_width=40
vsync_front_porch=40 | YES |
+| [10.1inch DSI LCD (C)](https://www.waveshare.com/10.1inch-dsi-lcd-c.htm)
| dpi_clock_freq_mhz=80
h_size=1280
v_size=800
hsync_back_porch=156
hsync_pulse_width=40
hsync_front_porch=20
vsync_back_porch=48
vsync_pulse_width=40
vsync_front_porch=40 | YES |
+| [8.8inch DSI LCD](https://www.waveshare.com/8.8inch-dsi-lcd.htm)
| dpi_clock_freq_mhz=48
h_size=480
v_size=1920
hsync_back_porch=50
hsync_pulse_width=50
hsync_front_porch=50
vsync_back_porch=20
vsync_pulse_width=20
vsync_front_porch=20 | YES |
+| [11.9inch DSI LCD](https://www.waveshare.com/11.9inch-dsi-lcd.htm)
| dpi_clock_freq_mhz=48
h_size=320
v_size=1480
hsync_back_porch=60
hsync_pulse_width=60
hsync_front_porch=60
vsync_back_porch=60
vsync_pulse_width=60
vsync_front_porch=60 | YES |
diff --git a/display/lcd/esp_lcd_dsi/esp_lcd_dsi.c b/display/lcd/esp_lcd_dsi/esp_lcd_dsi.c
new file mode 100644
index 0000000..bd43b34
--- /dev/null
+++ b/display/lcd/esp_lcd_dsi/esp_lcd_dsi.c
@@ -0,0 +1,334 @@
+#include "soc/soc_caps.h"
+
+#if SOC_MIPI_DSI_SUPPORTED
+#include "esp_check.h"
+#include "esp_log.h"
+#include "esp_lcd_panel_commands.h"
+#include "esp_lcd_panel_interface.h"
+#include "esp_lcd_panel_io.h"
+#include "esp_lcd_mipi_dsi.h"
+#include "esp_lcd_panel_vendor.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/gpio.h"
+#include "esp_lcd_dsi.h"
+
+#include "i2c_bus.h"
+
+#define DSI_CMD_GS_BIT (1 << 0)
+#define DSI_CMD_SS_BIT (1 << 1)
+
+typedef struct
+{
+ esp_lcd_panel_io_handle_t io;
+ int reset_gpio_num;
+ uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
+ uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
+ const dsi_lcd_init_cmd_t *init_cmds;
+ uint16_t init_cmds_size;
+ struct
+ {
+ unsigned int reset_level : 1;
+ } flags;
+ // To save the original functions of MIPI DPI panel
+ esp_err_t (*del)(esp_lcd_panel_t *panel);
+ esp_err_t (*init)(esp_lcd_panel_t *panel);
+} dsi_panel_t;
+
+static const char *TAG = "Waveshare DSI";
+
+static esp_err_t panel_dsi_del(esp_lcd_panel_t *panel);
+static esp_err_t panel_dsi_init(esp_lcd_panel_t *panel);
+static esp_err_t panel_dsi_reset(esp_lcd_panel_t *panel);
+static esp_err_t panel_dsi_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
+static esp_err_t panel_dsi_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
+static esp_err_t panel_dsi_disp_on_off(esp_lcd_panel_t *panel, bool on_off);
+
+esp_err_t esp_lcd_new_panel_dsi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
+ esp_lcd_panel_handle_t *ret_panel)
+{
+ ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_DSI_VER_MAJOR, ESP_LCD_DSI_VER_MINOR,
+ ESP_LCD_DSI_VER_PATCH);
+ ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
+ dsi_vendor_config_t *vendor_config = (dsi_vendor_config_t *)panel_dev_config->vendor_config;
+ ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
+ "invalid vendor config");
+
+ esp_err_t ret = ESP_OK;
+ dsi_panel_t *dsi = (dsi_panel_t *)calloc(1, sizeof(dsi_panel_t));
+ ESP_RETURN_ON_FALSE(dsi, ESP_ERR_NO_MEM, TAG, "no mem for dsi panel");
+
+ if (panel_dev_config->reset_gpio_num >= 0)
+ {
+ gpio_config_t io_conf = {
+ .mode = GPIO_MODE_OUTPUT,
+ .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
+ };
+ ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
+ }
+
+ switch (panel_dev_config->color_space)
+ {
+ case LCD_RGB_ELEMENT_ORDER_RGB:
+ dsi->madctl_val = 0;
+ break;
+ case LCD_RGB_ELEMENT_ORDER_BGR:
+ dsi->madctl_val |= LCD_CMD_BGR_BIT;
+ break;
+ default:
+ ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
+ break;
+ }
+
+ dsi->io = io;
+ dsi->init_cmds = vendor_config->init_cmds;
+ dsi->init_cmds_size = vendor_config->init_cmds_size;
+ dsi->reset_gpio_num = panel_dev_config->reset_gpio_num;
+ dsi->flags.reset_level = panel_dev_config->flags.reset_active_high;
+
+ i2c_config_t conf = {
+ .mode = I2C_MODE_MASTER,
+ .sda_io_num = 7,
+ // .sda_pullup_en = GPIO_PULLUP_ENABLE,
+ .scl_io_num = 8,
+ // .scl_pullup_en = GPIO_PULLUP_ENABLE,
+ .master.clk_speed = 100000,
+ };
+
+ i2c_bus_handle_t i2c0_bus = i2c_bus_create(I2C_NUM_0, &conf);
+ i2c_bus_device_handle_t i2c0_device1 = i2c_bus_device_create(i2c0_bus, 0x45, 0);
+
+ uint8_t data = 0x01;
+ i2c_bus_write_bytes(i2c0_device1, 0xc0, 1, &data);
+ i2c_bus_write_bytes(i2c0_device1, 0xc2, 1, &data);
+ i2c_bus_write_bytes(i2c0_device1, 0xac, 1, &data);
+
+ data = 0x55;
+ i2c_bus_write_bytes(i2c0_device1, 0xab, 1, &data);
+
+ data = 0x01;
+ i2c_bus_write_bytes(i2c0_device1, 0xaa, 1, &data);
+// data = 0x02;
+ i2c_bus_write_bytes(i2c0_device1, 0xad, 1, &data);
+
+ i2c_bus_device_delete(&i2c0_device1);
+ i2c_bus_delete(&i2c0_bus);
+
+ vTaskDelay(pdMS_TO_TICKS(1000));
+
+ // Create MIPI DPI panel
+ esp_lcd_panel_handle_t panel_handle = NULL;
+ ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
+ "create MIPI DPI panel failed");
+ ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
+
+ // Save the original functions of MIPI DPI panel
+ dsi->del = panel_handle->del;
+ dsi->init = panel_handle->init;
+ // Overwrite the functions of MIPI DPI panel
+ panel_handle->del = panel_dsi_del;
+ panel_handle->init = panel_dsi_init;
+ panel_handle->reset = panel_dsi_reset;
+ panel_handle->mirror = panel_dsi_mirror;
+ panel_handle->invert_color = panel_dsi_invert_color;
+ panel_handle->disp_on_off = panel_dsi_disp_on_off;
+ panel_handle->user_data = dsi;
+ *ret_panel = panel_handle;
+ ESP_LOGD(TAG, "new dsi panel @%p", dsi);
+
+ return ESP_OK;
+
+err:
+ if (dsi)
+ {
+ if (panel_dev_config->reset_gpio_num >= 0)
+ {
+ gpio_reset_pin(panel_dev_config->reset_gpio_num);
+ }
+ free(dsi);
+ }
+ return ret;
+}
+
+static const dsi_lcd_init_cmd_t vendor_specific_init_default[] = {
+ // {cmd, { data }, data_size, delay_ms}
+ {0x11, (uint8_t[]){0x00}, 1, 120},
+ {0x29, (uint8_t[]){0x00}, 1, 20},
+};
+
+static esp_err_t panel_dsi_del(esp_lcd_panel_t *panel)
+{
+ dsi_panel_t *dsi = (dsi_panel_t *)panel->user_data;
+
+ if (dsi->reset_gpio_num >= 0)
+ {
+ gpio_reset_pin(dsi->reset_gpio_num);
+ }
+ // Delete MIPI DPI panel
+ dsi->del(panel);
+ ESP_LOGD(TAG, "del dsi panel @%p", dsi);
+ free(dsi);
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_dsi_init(esp_lcd_panel_t *panel)
+{
+ dsi_panel_t *dsi = (dsi_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = dsi->io;
+ const dsi_lcd_init_cmd_t *init_cmds = NULL;
+ uint16_t init_cmds_size = 0;
+ bool is_cmd_overwritten = false;
+
+ // uint8_t ID[3];
+ // ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed");
+
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){
+ dsi->madctl_val,
+ },
+ 1),
+ TAG, "send command failed");
+
+ // vendor specific initialization, it can be different between manufacturers
+ // should consult the LCD supplier for initialization sequence code
+ if (dsi->init_cmds)
+ {
+ init_cmds = dsi->init_cmds;
+ init_cmds_size = dsi->init_cmds_size;
+ }
+ else
+ {
+ init_cmds = vendor_specific_init_default;
+ init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(dsi_lcd_init_cmd_t);
+ }
+
+ for (int i = 0; i < init_cmds_size; i++)
+ {
+ // Check if the command has been used or conflicts with the internal
+ if (init_cmds[i].data_bytes > 0)
+ {
+ switch (init_cmds[i].cmd)
+ {
+ case LCD_CMD_MADCTL:
+ is_cmd_overwritten = true;
+ dsi->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
+ break;
+ default:
+ is_cmd_overwritten = false;
+ break;
+ }
+
+ if (is_cmd_overwritten)
+ {
+ is_cmd_overwritten = false;
+ ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
+ init_cmds[i].cmd);
+ }
+ }
+
+ // Send command
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed");
+ vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
+ }
+ ESP_LOGD(TAG, "send init commands success");
+
+ ESP_RETURN_ON_ERROR(dsi->init(panel), TAG, "init MIPI DPI panel failed");
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_dsi_reset(esp_lcd_panel_t *panel)
+{
+ dsi_panel_t *dsi = (dsi_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = dsi->io;
+
+ // Perform hardware reset
+ if (dsi->reset_gpio_num >= 0)
+ {
+ gpio_set_level(dsi->reset_gpio_num, !dsi->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(5));
+ gpio_set_level(dsi->reset_gpio_num, dsi->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(10));
+ gpio_set_level(dsi->reset_gpio_num, !dsi->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(120));
+ }
+ else if (io)
+ { // Perform software reset
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
+ vTaskDelay(pdMS_TO_TICKS(120));
+ }
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_dsi_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
+{
+ dsi_panel_t *dsi = (dsi_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = dsi->io;
+ uint8_t command = 0;
+
+ ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
+
+ if (invert_color_data)
+ {
+ command = LCD_CMD_INVON;
+ }
+ else
+ {
+ command = LCD_CMD_INVOFF;
+ }
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_dsi_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
+{
+ dsi_panel_t *dsi = (dsi_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = dsi->io;
+ uint8_t madctl_val = dsi->madctl_val;
+
+ ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
+
+ // Control mirror through LCD command
+ if (mirror_x)
+ {
+ madctl_val |= DSI_CMD_GS_BIT;
+ }
+ else
+ {
+ madctl_val &= ~DSI_CMD_GS_BIT;
+ }
+ if (mirror_y)
+ {
+ madctl_val |= DSI_CMD_SS_BIT;
+ }
+ else
+ {
+ madctl_val &= ~DSI_CMD_SS_BIT;
+ }
+
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){madctl_val}, 1), TAG, "send command failed");
+ dsi->madctl_val = madctl_val;
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_dsi_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
+{
+ dsi_panel_t *dsi = (dsi_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = dsi->io;
+ int command = 0;
+
+ if (on_off)
+ {
+ command = LCD_CMD_DISPON;
+ }
+ else
+ {
+ command = LCD_CMD_DISPOFF;
+ }
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
+ return ESP_OK;
+}
+#endif
diff --git a/display/lcd/esp_lcd_dsi/idf_component.yml b/display/lcd/esp_lcd_dsi/idf_component.yml
new file mode 100644
index 0000000..a8d20db
--- /dev/null
+++ b/display/lcd/esp_lcd_dsi/idf_component.yml
@@ -0,0 +1,13 @@
+version: "1.0.0"
+targets:
+ - esp32p4
+description: "Waveshare DSI display driver, compatible with a variety of DSI displays"
+url: "https://github.com/waveshareteam/Waveshare-ESP32-components/tree/master/esp_lcd_dsi"
+issues: "https://github.com/waveshareteam/Waveshare-ESP32-components/issues"
+dependencies:
+ idf:
+ version: '>=5.3'
+ cmake_utilities: "0.*"
+ espressif/i2c_bus: "^1.0.0"
+
+license: "MIT"
diff --git a/display/lcd/esp_lcd_dsi/include/esp_lcd_dsi.h b/display/lcd/esp_lcd_dsi/include/esp_lcd_dsi.h
new file mode 100644
index 0000000..098a311
--- /dev/null
+++ b/display/lcd/esp_lcd_dsi/include/esp_lcd_dsi.h
@@ -0,0 +1,121 @@
+#pragma once
+
+#include
+#include "soc/soc_caps.h"
+
+#if SOC_MIPI_DSI_SUPPORTED
+#include "esp_lcd_panel_vendor.h"
+#include "esp_lcd_mipi_dsi.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ /**
+ * @brief LCD panel initialization commands.
+ *
+ */
+ typedef struct
+ {
+ int cmd; /*
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/i2c.h"
+#include "driver/spi_master.h"
+#include "driver/gpio.h"
+#include "esp_heap_caps.h"
+#include "esp_log.h"
+#include "esp_timer.h"
+#include "esp_lcd_panel_ops.h"
+#include "esp_lcd_panel_io.h"
+#include "esp_ldo_regulator.h"
+#include "esp_dma_utils.h"
+#include "unity.h"
+#include "unity_test_runner.h"
+#include "unity_test_utils_memory.h"
+#include "esp_lcd_mipi_dsi.h"
+#include "esp_lcd_dsi.h"
+
+#define TEST_LCD_H_RES (480)
+#define TEST_LCD_V_RES (640)
+#define TEST_LCD_BIT_PER_PIXEL (24)
+#define TEST_PIN_NUM_LCD_RST (-1)
+#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used
+#define TEST_LCD_BK_LIGHT_ON_LEVEL (1)
+#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL
+
+#if TEST_LCD_BIT_PER_PIXEL == 24
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888)
+#elif TEST_LCD_BIT_PER_PIXEL == 18
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666)
+#elif TEST_LCD_BIT_PER_PIXEL == 16
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565)
+#endif
+
+#define TEST_DELAY_TIME_MS (3000)
+
+#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3)
+#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
+
+static char *TAG = "waveshare_dsi_test";
+static esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
+static esp_lcd_panel_handle_t panel_handle = NULL;
+static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
+static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
+static SemaphoreHandle_t refresh_finish = NULL;
+
+IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
+{
+ SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx;
+ BaseType_t need_yield = pdFALSE;
+
+ xSemaphoreGiveFromISR(refresh_finish, &need_yield);
+
+ return (need_yield == pdTRUE);
+}
+
+static void test_init_lcd(void)
+{
+#if TEST_PIN_NUM_BK_LIGHT >= 0
+ ESP_LOGI(TAG, "Turn on LCD backlight");
+ gpio_config_t bk_gpio_config = {
+ .mode = GPIO_MODE_OUTPUT,
+ .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT};
+ TEST_ESP_OK(gpio_config(&bk_gpio_config));
+ TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL));
+#endif
+
+ // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
+#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN
+ ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
+ esp_ldo_channel_config_t ldo_mipi_phy_config = {
+ .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN,
+ .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
+ };
+ TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
+#endif
+
+ ESP_LOGI(TAG, "Initialize MIPI DSI bus");
+ esp_lcd_dsi_bus_config_t bus_config = DSI_PANEL_BUS_DSI_2CH_CONFIG();
+ TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
+
+ ESP_LOGI(TAG, "Install panel IO");
+ esp_lcd_dbi_io_config_t dbi_config = DSI_PANEL_IO_DBI_CONFIG();
+ TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
+
+ ESP_LOGI(TAG, "Install LCD driver of dsi");
+ esp_lcd_dpi_panel_config_t dpi_config = DSI_PANEL_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT);
+ dsi_vendor_config_t vendor_config = {
+ .mipi_config = {
+ .dsi_bus = mipi_dsi_bus,
+ .dpi_config = &dpi_config,
+ },
+ };
+ const esp_lcd_panel_dev_config_t panel_config = {
+ .reset_gpio_num = TEST_PIN_NUM_LCD_RST,
+ .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
+ .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL,
+ .vendor_config = &vendor_config,
+ };
+ TEST_ESP_OK(esp_lcd_new_panel_dsi(mipi_dbi_io, &panel_config, &panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_init(panel_handle));
+
+ refresh_finish = xSemaphoreCreateBinary();
+ TEST_ASSERT_NOT_NULL(refresh_finish);
+ esp_lcd_dpi_panel_event_callbacks_t cbs = {
+ .on_color_trans_done = test_notify_refresh_ready,
+ };
+ TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish));
+}
+
+static void test_deinit_lcd(void)
+{
+ TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io));
+ TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus));
+ panel_handle = NULL;
+ mipi_dbi_io = NULL;
+ mipi_dsi_bus = NULL;
+
+ if (ldo_mipi_phy)
+ {
+ TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy));
+ ldo_mipi_phy = NULL;
+ }
+
+ vSemaphoreDelete(refresh_finish);
+ refresh_finish = NULL;
+
+#if TEST_PIN_NUM_BK_LIGHT >= 0
+ TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT));
+#endif
+}
+
+static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res)
+{
+ uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8;
+ uint16_t row_line = v_res / byte_per_pixel / 8;
+ uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA);
+
+ for (int j = 0; j < byte_per_pixel * 8; j++)
+ {
+ for (int i = 0; i < row_line * h_res; i++)
+ {
+ for (int k = 0; k < byte_per_pixel; k++)
+ {
+ color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff;
+ }
+ }
+ TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color));
+ xSemaphoreTake(refresh_finish, portMAX_DELAY);
+ }
+
+ uint16_t color_line = row_line * byte_per_pixel * 8;
+ uint16_t res_line = v_res - color_line;
+ if (res_line)
+ {
+ for (int i = 0; i < res_line * h_res; i++)
+ {
+ for (int k = 0; k < byte_per_pixel; k++)
+ {
+ color[i * byte_per_pixel + k] = 0xff;
+ }
+ }
+ TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color));
+ xSemaphoreTake(refresh_finish, portMAX_DELAY);
+ }
+
+ free(color);
+}
+
+TEST_CASE("test dsi to draw pattern with MIPI interface", "[dsi][draw_pattern]")
+{
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar pattern drawn by hardware");
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL));
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL));
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE));
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+TEST_CASE("test dsi to draw color bar with MIPI interface", "[dsi][draw_color_bar]")
+{
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar drawn by software");
+ test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES);
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+TEST_CASE("test dsi to rotate with MIPI interface", "[dsi][rotate]")
+{
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Mirror the screen");
+ for (size_t i = 0; i < 4; i++)
+ {
+ TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL);
+
+ ESP_LOGI(TAG, "Mirror: %d", i);
+ test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES);
+ vTaskDelay(pdMS_TO_TICKS(1000));
+ }
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case
+#define TEST_MEMORY_LEAK_THRESHOLD (300)
+
+static size_t before_free_8bit;
+static size_t before_free_32bit;
+
+void setUp(void)
+{
+ before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+ before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+}
+
+void tearDown(void)
+{
+ size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+ size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+ unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD);
+ unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD);
+}
+
+void app_main(void)
+{
+ /**
+ * __ __ _ _____ _____ _____
+ * \ \ / / | | | __ \ / ____|_ _|
+ * \ \ /\ / /_ ___ _____ ___| |__ __ _ _ __ ___ | | | | (___ | |
+ * \ \/ \/ / _` \ \ / / _ \/ __| '_ \ / _` | '__/ _ \ | | | |\___ \ | |
+ * \ /\ / (_| |\ V / __/\__ \ | | | (_| | | | __/ | |__| |____) |_| |_
+ * \/ \/ \__,_| \_/ \___||___/_| |_|\__,_|_| \___| |_____/|_____/|_____|
+ */
+ const char *logo =
+ " __ __ _ _____ _____ _____\r\n"
+ " \\ \\ / / | | | __ \\ / ____|_ _|\r\n"
+ " \\ \\ /\\ / /_ ___ _____ ___| |__ __ _ _ __ ___ | | | | (___ | |\r\n"
+ " \\ \\/ \\/ / _` \\ \\ / / _ \\/ __| '_ \\ / _` | '__/ _ \\ | | | |\\___ \\ | |\r\n"
+ " \\ /\\ / (_| |\\ V / __/\\__ \\ | | | (_| | | | __/ | |__| |____) |_| |_\r\n"
+ " \\/ \\/ \\__,_| \\_/ \\___||___/_| |_|\\__,_|_| \\___| |_____/|_____/|_____|\r\n";
+
+ printf("%s", logo);
+
+ // unity_run_menu();
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar pattern drawn by hardware");
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL));
+}
+#endif
diff --git a/display/lcd/esp_lcd_dsi/test_apps/sdkconfig.defaults b/display/lcd/esp_lcd_dsi/test_apps/sdkconfig.defaults
new file mode 100644
index 0000000..521790c
--- /dev/null
+++ b/display/lcd/esp_lcd_dsi/test_apps/sdkconfig.defaults
@@ -0,0 +1,3 @@
+CONFIG_ESP_TASK_WDT_EN=n
+CONFIG_FREERTOS_HZ=1000
+CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
diff --git a/display/lcd/esp_lcd_dsi/test_apps/sdkconfig.defaults.esp32p4 b/display/lcd/esp_lcd_dsi/test_apps/sdkconfig.defaults.esp32p4
new file mode 100644
index 0000000..4621955
--- /dev/null
+++ b/display/lcd/esp_lcd_dsi/test_apps/sdkconfig.defaults.esp32p4
@@ -0,0 +1,4 @@
+CONFIG_COMPILER_OPTIMIZATION_PERF=y
+CONFIG_SPIRAM=y
+CONFIG_SPIRAM_SPEED_200M=y
+CONFIG_IDF_EXPERIMENTAL_FEATURES=y
diff --git a/display/lcd/esp_lcd_hx8394/CHANGELOG.md b/display/lcd/esp_lcd_hx8394/CHANGELOG.md
new file mode 100644
index 0000000..1e655f3
--- /dev/null
+++ b/display/lcd/esp_lcd_hx8394/CHANGELOG.md
@@ -0,0 +1,11 @@
+# ChangeLog
+
+## v1.0.0 -2025-01-03
+
+* Formally release the component, formally maintain the component
+
+## v0.0.1 - 2024-11-14
+
+### Enhancements:
+
+* Implement the driver for the HX8394 MIPI-DSI LCD controller
\ No newline at end of file
diff --git a/display/lcd/esp_lcd_hx8394/CMakeLists.txt b/display/lcd/esp_lcd_hx8394/CMakeLists.txt
new file mode 100644
index 0000000..9635a4d
--- /dev/null
+++ b/display/lcd/esp_lcd_hx8394/CMakeLists.txt
@@ -0,0 +1,7 @@
+idf_component_register(SRCS "esp_lcd_hx8394.c"
+ INCLUDE_DIRS "include"
+ REQUIRES "esp_lcd"
+ PRIV_REQUIRES "esp_driver_gpio")
+
+include(package_manager)
+cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
diff --git a/display/lcd/esp_lcd_hx8394/README.md b/display/lcd/esp_lcd_hx8394/README.md
new file mode 100644
index 0000000..1574ba6
--- /dev/null
+++ b/display/lcd/esp_lcd_hx8394/README.md
@@ -0,0 +1,76 @@
+# ESP LCD HX8394
+
+[![Component Registry](https://components.espressif.com/components/waveshare/esp_lcd_hx8394/badge.svg)](https://components.espressif.com/components/waveshare/esp_lcd_hx8394)
+
+Implementation of the HX8394 LCD controller with esp_lcd component.
+
+| LCD controller | Communication interface | Component name | Link to datasheet |
+| :------------: | :---------------------: | :------------: | :-----------------------------------------------------------------------------------: |
+| HX8394 | MIPI-DSI | esp_lcd_hx8394 | ββ |
+
+**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions.
+
+## Add to project
+
+Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/).
+You can add them to your project via `idf.py add-dependancy`, e.g.
+
+```
+ idf.py add-dependency "waveshare/esp_lcd_hx8394"
+```
+
+Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).
+
+## Example use
+
+```c
+/**
+ * Uncomment these line if use custom initialization commands.
+ * The array should be declared as static const and positioned outside the function.
+ */
+// static const hx8394_lcd_init_cmd_t lcd_init_cmds[] = {
+// {cmd, { data }, data_size, delay_ms}
+// {0x11, (uint8_t []){0x00}, 120, 0},
+// {0x29, (uint8_t []){0x00}, 20, 0},
+// ...
+// };
+
+ ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
+ esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
+ esp_ldo_channel_config_t ldo_mipi_phy_config = {
+ .chan_id = 3,
+ .voltage_mv = 2500,
+ };
+ ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
+
+ ESP_LOGI(TAG, "Initialize MIPI DSI bus");
+ esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
+ esp_lcd_dsi_bus_config_t bus_config = HX8394_PANEL_BUS_DSI_2CH_CONFIG();
+ ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
+
+ ESP_LOGI(TAG, "Install panel IO");
+ esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
+ esp_lcd_dbi_io_config_t dbi_config = HX8394_PANEL_IO_DBI_CONFIG();
+ ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
+
+ ESP_LOGI(TAG, "Install HX8394 panel driver");
+ esp_lcd_panel_handle_t panel_handle = NULL;
+ const esp_lcd_dpi_panel_config_t dpi_config = HX8394_1024_600_PANEL_30HZ_DPI_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT);
+ hx8394_vendor_config_t vendor_config = {
+ .flags = {
+ .use_mipi_interface = 1,
+ },
+ .mipi_config = {
+ .dsi_bus = mipi_dsi_bus,
+ .dpi_config = &dpi_config,
+ },
+ };
+ const esp_lcd_panel_dev_config_t panel_config = {
+ .reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use
+ .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h`
+ .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24)
+ .vendor_config = &vendor_config,
+ };
+ ESP_ERROR_CHECK(esp_lcd_new_panel_hx8394(mipi_dbi_io, &panel_config, &panel_handle));
+ ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
+```
diff --git a/display/lcd/esp_lcd_hx8394/esp_lcd_hx8394.c b/display/lcd/esp_lcd_hx8394/esp_lcd_hx8394.c
new file mode 100644
index 0000000..7ed40fc
--- /dev/null
+++ b/display/lcd/esp_lcd_hx8394/esp_lcd_hx8394.c
@@ -0,0 +1,352 @@
+#include "soc/soc_caps.h"
+
+#if SOC_MIPI_DSI_SUPPORTED
+#include "esp_check.h"
+#include "esp_log.h"
+#include "esp_lcd_panel_commands.h"
+#include "esp_lcd_panel_interface.h"
+#include "esp_lcd_panel_io.h"
+#include "esp_lcd_mipi_dsi.h"
+#include "esp_lcd_panel_vendor.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/gpio.h"
+#include "esp_lcd_hx8394.h"
+
+#define HX8394_CMD_DSI_INT0 (0xBA)
+#define HX8394_DSI_1_LANE (0x00)
+#define HX8394_DSI_2_LANE (0x01)
+#define HX8394_DSI_3_LANE (0x10)
+#define HX8394_DSI_4_LANE (0x11)
+
+typedef struct
+{
+ esp_lcd_panel_io_handle_t io;
+ int reset_gpio_num;
+ uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
+ uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
+ const hx8394_lcd_init_cmd_t *init_cmds;
+ uint16_t init_cmds_size;
+ uint8_t lane_num;
+ struct
+ {
+ unsigned int reset_level : 1;
+ } flags;
+ // To save the original functions of MIPI DPI panel
+ esp_err_t (*del)(esp_lcd_panel_t *panel);
+ esp_err_t (*init)(esp_lcd_panel_t *panel);
+} hx8394_panel_t;
+
+static const char *TAG = "hx8394";
+
+static esp_err_t panel_hx8394_del(esp_lcd_panel_t *panel);
+static esp_err_t panel_hx8394_init(esp_lcd_panel_t *panel);
+static esp_err_t panel_hx8394_reset(esp_lcd_panel_t *panel);
+static esp_err_t panel_hx8394_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
+static esp_err_t panel_hx8394_disp_on_off(esp_lcd_panel_t *panel, bool on_off);
+
+esp_err_t esp_lcd_new_panel_hx8394(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
+ esp_lcd_panel_handle_t *ret_panel)
+{
+ ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_HX8394_VER_MAJOR, ESP_LCD_HX8394_VER_MINOR,
+ ESP_LCD_HX8394_VER_PATCH);
+ ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
+ hx8394_vendor_config_t *vendor_config = (hx8394_vendor_config_t *)panel_dev_config->vendor_config;
+ ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
+ "invalid vendor config");
+
+ esp_err_t ret = ESP_OK;
+ hx8394_panel_t *hx8394 = (hx8394_panel_t *)calloc(1, sizeof(hx8394_panel_t));
+ ESP_RETURN_ON_FALSE(hx8394, ESP_ERR_NO_MEM, TAG, "no mem for hx8394 panel");
+
+ if (panel_dev_config->reset_gpio_num >= 0)
+ {
+ gpio_config_t io_conf = {
+ .mode = GPIO_MODE_OUTPUT,
+ .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
+ };
+ ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
+ }
+
+ switch (panel_dev_config->color_space)
+ {
+ case LCD_RGB_ELEMENT_ORDER_RGB:
+ hx8394->madctl_val = 0;
+ break;
+ case LCD_RGB_ELEMENT_ORDER_BGR:
+ hx8394->madctl_val |= LCD_CMD_BGR_BIT;
+ break;
+ default:
+ ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
+ break;
+ }
+
+ switch (panel_dev_config->bits_per_pixel)
+ {
+ case 16: // RGB565
+ hx8394->colmod_val = 0x55;
+ break;
+ case 18: // RGB666
+ hx8394->colmod_val = 0x66;
+ break;
+ case 24: // RGB888
+ hx8394->colmod_val = 0x77;
+ break;
+ default:
+ ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
+ break;
+ }
+
+ hx8394->io = io;
+ hx8394->init_cmds = vendor_config->init_cmds;
+ hx8394->init_cmds_size = vendor_config->init_cmds_size;
+ hx8394->lane_num = vendor_config->mipi_config.lane_num;
+ hx8394->reset_gpio_num = panel_dev_config->reset_gpio_num;
+ hx8394->flags.reset_level = panel_dev_config->flags.reset_active_high;
+
+ // Create MIPI DPI panel
+ esp_lcd_panel_handle_t panel_handle = NULL;
+ ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
+ "create MIPI DPI panel failed");
+ ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
+
+ // Save the original functions of MIPI DPI panel
+ hx8394->del = panel_handle->del;
+ hx8394->init = panel_handle->init;
+ // Overwrite the functions of MIPI DPI panel
+ panel_handle->del = panel_hx8394_del;
+ panel_handle->init = panel_hx8394_init;
+ panel_handle->reset = panel_hx8394_reset;
+ panel_handle->invert_color = panel_hx8394_invert_color;
+ panel_handle->disp_on_off = panel_hx8394_disp_on_off;
+ panel_handle->user_data = hx8394;
+ *ret_panel = panel_handle;
+ ESP_LOGD(TAG, "new hx8394 panel @%p", hx8394);
+
+ return ESP_OK;
+
+err:
+ if (hx8394)
+ {
+ if (panel_dev_config->reset_gpio_num >= 0)
+ {
+ gpio_reset_pin(panel_dev_config->reset_gpio_num);
+ }
+ free(hx8394);
+ }
+ return ret;
+}
+
+static const hx8394_lcd_init_cmd_t vendor_specific_init_code_default[] = {
+ // {cmd, { data }, data_size, delay_ms}
+ {0xB9, (uint8_t[]){0xFF, 0x83, 0x94}, 3, 0},
+ // {0xD2, (uint8_t []){0x77}, 1, 0},
+ {0xB1, (uint8_t[]){0x48, 0x0A, 0x6A, 0x09, 0x33, 0x54, 0x71, 0x71, 0x2E, 0x45}, 10, 0},
+ {0xBA, (uint8_t[]){0x61, 0x03, 0x68, 0x6B, 0xB2, 0xC0}, 6, 0},
+ {0xB2, (uint8_t[]){0x00, 0x80, 0x64, 0x0C, 0x06, 0x2F}, 6, 0},
+ {0xB4, (uint8_t[]){0x1C, 0x78, 0x1C, 0x78, 0x1C, 0x78, 0x01, 0x0C, 0x86, 0x75, 0x00, 0x3F, 0x1C, 0x78, 0x1C, 0x78, 0x1C, 0x78, 0x01, 0x0C, 0x86}, 21, 0},
+ {0xD3, (uint8_t[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x32, 0x10, 0x05, 0x00, 0x05, 0x32, 0x13, 0xC1, 0x00, 0x01, 0x32, 0x10, 0x08, 0x00, 0x00, 0x37, 0x03, 0x07, 0x07, 0x37, 0x05, 0x05, 0x37, 0x0C, 0x40}, 33, 10},
+ {0xD5, (uint8_t[]){0x18, 0x18, 0x18, 0x18, 0x22, 0x23, 0x20, 0x21, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19}, 45, 10},
+ {0xD6, (uint8_t[]){0x18, 0x18, 0x19, 0x19, 0x21, 0x20, 0x23, 0x22, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18}, 45, 10},
+ // {0xD8, (uint8_t []){0xA2, 0xAA, 0x02, 0xA0, 0xA2, 0xA8, 0x02, 0xA0, 0xB0, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00}, 16, 0},
+ // {0xBD, (uint8_t []){0x01}, 1, 0},
+ // {0xD8, (uint8_t []){0xB0, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0xE2, 0xAA, 0x03, 0xF0, 0xE2, 0xAA, 0x03, 0xF0}, 16, 0},
+ // {0xBD, (uint8_t []){0x02}, 1, 0},
+ // {0xD8, (uint8_t []){0xE2, 0xAA, 0x03, 0xF0, 0xE2, 0xAA, 0x03, 0xF0}, 8, 0},
+ // {0xBD, (uint8_t []){0x00}, 1, 0},
+ {0xE0, (uint8_t[]){0x07, 0x08, 0x09, 0x0D, 0x10, 0x14, 0x16, 0x13, 0x24, 0x36, 0x48, 0x4A, 0x58, 0x6F, 0x76, 0x80, 0x97, 0xA5, 0xA8, 0xB5, 0xC6, 0x62, 0x63, 0x68, 0x6F, 0x72, 0x78, 0x7F, 0x7F, 0x00, 0x02, 0x08, 0x0D, 0x0C, 0x0E, 0x0F, 0x10, 0x24, 0x36, 0x48, 0x4A, 0x58, 0x6F, 0x78, 0x82, 0x99, 0xA4, 0xA0, 0xB1, 0xC0, 0x5E, 0x5E, 0x64, 0x6B, 0x6C, 0x73, 0x7F, 0x7F}, 59, 10},
+ {0xCC, (uint8_t[]){0x08}, 1, 0},
+ {0xC0, (uint8_t[]){0x1F, 0x73}, 2, 0},
+ {0xB6, (uint8_t[]){0x6B, 0x6B}, 2, 0},
+ {0xD4, (uint8_t[]){0x02}, 1, 0},
+ {0xBD, (uint8_t[]){0x01}, 1, 0},
+ {0xB1, (uint8_t[]){0x00}, 1, 0},
+ {0xBD, (uint8_t[]){0x00}, 1, 0},
+ {0xBF, (uint8_t[]){0x40, 0x81, 0x50, 0x00, 0x1A, 0xFC, 0x01}, 7, 0},
+ {0x11, (uint8_t[]){0x00}, 0, 200},
+ {0xB2, (uint8_t[]){0x00, 0x80, 0x64, 0x0C, 0x06, 0x2F, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x18}, 12, 0},
+ {0x29, (uint8_t[]){0x00}, 0, 80},
+
+ //============ Gamma END===========
+};
+
+static esp_err_t panel_hx8394_del(esp_lcd_panel_t *panel)
+{
+ hx8394_panel_t *hx8394 = (hx8394_panel_t *)panel->user_data;
+
+ if (hx8394->reset_gpio_num >= 0)
+ {
+ gpio_reset_pin(hx8394->reset_gpio_num);
+ }
+ // Delete MIPI DPI panel
+ hx8394->del(panel);
+ ESP_LOGD(TAG, "del hx8394 panel @%p", hx8394);
+ free(hx8394);
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_hx8394_init(esp_lcd_panel_t *panel)
+{
+ hx8394_panel_t *hx8394 = (hx8394_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = hx8394->io;
+ const hx8394_lcd_init_cmd_t *init_cmds = NULL;
+ uint16_t init_cmds_size = 0;
+ uint8_t lane_command = HX8394_DSI_2_LANE;
+ bool is_cmd_overwritten = false;
+
+ switch (hx8394->lane_num)
+ {
+ case 0:
+ lane_command = HX8394_DSI_2_LANE;
+ break;
+ case 1:
+ lane_command = HX8394_DSI_1_LANE;
+ break;
+ case 2:
+ lane_command = HX8394_DSI_2_LANE;
+ break;
+ case 3:
+ lane_command = HX8394_DSI_3_LANE;
+ break;
+ case 4:
+ lane_command = HX8394_DSI_4_LANE;
+ break;
+ default:
+ ESP_LOGE(TAG, "Invalid lane number %d", hx8394->lane_num);
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0), TAG,
+ "io tx param failed");
+ vTaskDelay(pdMS_TO_TICKS(120));
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){
+ hx8394->madctl_val,
+ },
+ 1),
+ TAG, "send command failed");
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]){
+ hx8394->colmod_val,
+ },
+ 1),
+ TAG, "send command failed");
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, HX8394_CMD_DSI_INT0, (uint8_t[]){
+ lane_command,
+ },
+ 1),
+ TAG, "send command failed");
+
+ // vendor specific initialization, it can be different between manufacturers
+ // should consult the LCD supplier for initialization sequence code
+ if (hx8394->init_cmds)
+ {
+ init_cmds = hx8394->init_cmds;
+ init_cmds_size = hx8394->init_cmds_size;
+ }
+ else
+ {
+ init_cmds = vendor_specific_init_code_default;
+ init_cmds_size = sizeof(vendor_specific_init_code_default) / sizeof(hx8394_lcd_init_cmd_t);
+ }
+
+ for (int i = 0; i < init_cmds_size; i++)
+ {
+ // Check if the command has been used or conflicts with the internal
+ if (init_cmds[i].data_bytes > 0)
+ {
+ switch (init_cmds[i].cmd)
+ {
+ case LCD_CMD_MADCTL:
+ is_cmd_overwritten = true;
+ hx8394->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
+ break;
+ case LCD_CMD_COLMOD:
+ is_cmd_overwritten = true;
+ hx8394->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
+ break;
+ default:
+ is_cmd_overwritten = false;
+ break;
+ }
+
+ if (is_cmd_overwritten)
+ {
+ is_cmd_overwritten = false;
+ ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
+ init_cmds[i].cmd);
+ }
+ }
+
+ // Send command
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed");
+ vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
+ }
+
+ ESP_LOGD(TAG, "send init commands success");
+
+ ESP_RETURN_ON_ERROR(hx8394->init(panel), TAG, "init MIPI DPI panel failed");
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_hx8394_reset(esp_lcd_panel_t *panel)
+{
+ hx8394_panel_t *hx8394 = (hx8394_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = hx8394->io;
+
+ // Perform hardware reset
+ if (hx8394->reset_gpio_num >= 0)
+ {
+ gpio_set_level(hx8394->reset_gpio_num, hx8394->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(10));
+ gpio_set_level(hx8394->reset_gpio_num, !hx8394->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(10));
+ }
+ else if (io)
+ { // Perform software reset
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
+ vTaskDelay(pdMS_TO_TICKS(120));
+ }
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_hx8394_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
+{
+ hx8394_panel_t *hx8394 = (hx8394_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = hx8394->io;
+ uint8_t command = 0;
+
+ ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
+
+ if (invert_color_data)
+ {
+ command = LCD_CMD_INVON;
+ }
+ else
+ {
+ command = LCD_CMD_INVOFF;
+ }
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_hx8394_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
+{
+ hx8394_panel_t *hx8394 = (hx8394_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = hx8394->io;
+ int command = 0;
+
+ if (on_off)
+ {
+ command = LCD_CMD_DISPON;
+ }
+ else
+ {
+ command = LCD_CMD_DISPOFF;
+ }
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
+ return ESP_OK;
+}
+#endif
diff --git a/display/lcd/esp_lcd_hx8394/idf_component.yml b/display/lcd/esp_lcd_hx8394/idf_component.yml
new file mode 100644
index 0000000..e55493b
--- /dev/null
+++ b/display/lcd/esp_lcd_hx8394/idf_component.yml
@@ -0,0 +1,11 @@
+version: "1.0.0"
+license: "MIT"
+targets:
+ - esp32p4
+description: "ESP LCD FOR Waveshare 5inch DISPLAY HX8394"
+url: "https://github.com/waveshareteam/Waveshare-ESP32-components/tree/master/esp_lcd_hx8394"
+issues: "https://github.com/waveshareteam/Waveshare-ESP32-components/issues"
+dependencies:
+ idf:
+ version: '>=5.3'
+ cmake_utilities: "0.*"
diff --git a/display/lcd/esp_lcd_hx8394/include/esp_lcd_hx8394.h b/display/lcd/esp_lcd_hx8394/include/esp_lcd_hx8394.h
new file mode 100644
index 0000000..1652ef3
--- /dev/null
+++ b/display/lcd/esp_lcd_hx8394/include/esp_lcd_hx8394.h
@@ -0,0 +1,113 @@
+#pragma once
+
+#include
+#include "soc/soc_caps.h"
+
+#if SOC_MIPI_DSI_SUPPORTED
+#include "esp_lcd_panel_vendor.h"
+#include "esp_lcd_mipi_dsi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief LCD panel initialization commands.
+ *
+ */
+typedef struct {
+ int cmd; /*
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/i2c.h"
+#include "driver/spi_master.h"
+#include "driver/gpio.h"
+#include "esp_heap_caps.h"
+#include "esp_log.h"
+#include "esp_timer.h"
+#include "esp_lcd_panel_ops.h"
+#include "esp_lcd_panel_io.h"
+#include "esp_lcd_mipi_dsi.h"
+#include "esp_ldo_regulator.h"
+#include "esp_dma_utils.h"
+#include "unity.h"
+#include "unity_test_runner.h"
+#include "unity_test_utils_memory.h"
+
+#include "esp_lcd_hx8394.h"
+
+#define TEST_LCD_H_RES (720)
+#define TEST_LCD_V_RES (1280)
+#define TEST_LCD_BIT_PER_PIXEL (24)
+#define TEST_PIN_NUM_LCD_RST (-1)
+#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used
+#define TEST_LCD_BK_LIGHT_ON_LEVEL (1)
+#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL
+#define TEST_MIPI_DSI_LANE_NUM (2)
+
+#if TEST_LCD_BIT_PER_PIXEL == 24
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888)
+#elif TEST_LCD_BIT_PER_PIXEL == 18
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666)
+#elif TEST_LCD_BIT_PER_PIXEL == 16
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565)
+#endif
+
+#define TEST_DELAY_TIME_MS (3000)
+
+#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3)
+#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
+
+static char *TAG = "hx8394_test";
+static esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
+static esp_lcd_panel_handle_t panel_handle = NULL;
+static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
+static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
+static SemaphoreHandle_t refresh_finish = NULL;
+
+IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
+{
+ SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx;
+ BaseType_t need_yield = pdFALSE;
+
+ xSemaphoreGiveFromISR(refresh_finish, &need_yield);
+
+ return (need_yield == pdTRUE);
+}
+
+static void test_init_lcd(void)
+{
+#if TEST_PIN_NUM_BK_LIGHT >= 0
+ ESP_LOGI(TAG, "Turn on LCD backlight");
+ gpio_config_t bk_gpio_config = {
+ .mode = GPIO_MODE_OUTPUT,
+ .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT};
+ TEST_ESP_OK(gpio_config(&bk_gpio_config));
+ TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL));
+#endif
+
+ // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
+#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN
+ ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
+ esp_ldo_channel_config_t ldo_mipi_phy_config = {
+ .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN,
+ .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
+ };
+ TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
+#endif
+
+ ESP_LOGI(TAG, "Initialize MIPI DSI bus");
+ esp_lcd_dsi_bus_config_t bus_config = HX8394_PANEL_BUS_DSI_2CH_CONFIG();
+ TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
+
+ ESP_LOGI(TAG, "Install panel IO");
+ esp_lcd_dbi_io_config_t dbi_config = HX8394_PANEL_IO_DBI_CONFIG();
+ TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
+
+ ESP_LOGI(TAG, "Install LCD driver of hx8394");
+ esp_lcd_dpi_panel_config_t dpi_config = HX8394_720_1280_PANEL_30HZ_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT);
+ hx8394_vendor_config_t vendor_config = {
+ .mipi_config = {
+ .dsi_bus = mipi_dsi_bus,
+ .dpi_config = &dpi_config,
+ .lane_num = TEST_MIPI_DSI_LANE_NUM,
+ },
+ };
+ const esp_lcd_panel_dev_config_t panel_config = {
+ .reset_gpio_num = TEST_PIN_NUM_LCD_RST,
+ .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
+ .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL,
+ .vendor_config = &vendor_config,
+ };
+ TEST_ESP_OK(esp_lcd_new_panel_hx8394(mipi_dbi_io, &panel_config, &panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_reset(panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_init(panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true));
+
+ refresh_finish = xSemaphoreCreateBinary();
+ TEST_ASSERT_NOT_NULL(refresh_finish);
+ esp_lcd_dpi_panel_event_callbacks_t cbs = {
+ .on_color_trans_done = test_notify_refresh_ready,
+ };
+ TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish));
+}
+
+static void test_deinit_lcd(void)
+{
+ TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io));
+ TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus));
+ panel_handle = NULL;
+ mipi_dbi_io = NULL;
+ mipi_dsi_bus = NULL;
+
+ if (ldo_mipi_phy)
+ {
+ TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy));
+ ldo_mipi_phy = NULL;
+ }
+
+ vSemaphoreDelete(refresh_finish);
+ refresh_finish = NULL;
+
+#if TEST_PIN_NUM_BK_LIGHT >= 0
+ TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT));
+#endif
+}
+
+static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res)
+{
+ uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8;
+ uint16_t row_line = v_res / byte_per_pixel / 8;
+ uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA);
+
+ for (int j = 0; j < byte_per_pixel * 8; j++)
+ {
+ for (int i = 0; i < row_line * h_res; i++)
+ {
+ for (int k = 0; k < byte_per_pixel; k++)
+ {
+ color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff;
+ }
+ }
+ TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color));
+ xSemaphoreTake(refresh_finish, portMAX_DELAY);
+ }
+
+ uint16_t color_line = row_line * byte_per_pixel * 8;
+ uint16_t res_line = v_res - color_line;
+ if (res_line)
+ {
+ for (int i = 0; i < res_line * h_res; i++)
+ {
+ for (int k = 0; k < byte_per_pixel; k++)
+ {
+ color[i * byte_per_pixel + k] = 0xff;
+ }
+ }
+ TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color));
+ xSemaphoreTake(refresh_finish, portMAX_DELAY);
+ }
+
+ free(color);
+}
+
+TEST_CASE("test hx8394 to draw pattern with MIPI interface", "[hx8394][draw_pattern]")
+{
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar pattern drawn by hardware");
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL));
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL));
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE));
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+TEST_CASE("test hx8394 to draw color bar with MIPI interface", "[hx8394][draw_color_bar]")
+{
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar drawn by software");
+ test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES);
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case
+#define TEST_MEMORY_LEAK_THRESHOLD (300)
+
+static size_t before_free_8bit;
+static size_t before_free_32bit;
+
+void setUp(void)
+{
+ before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+ before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+}
+
+void tearDown(void)
+{
+ size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+ size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+ unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD);
+ unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD);
+}
+
+void app_main(void)
+{
+ /**
+ * _ ___ _____ ____ ___ _ _
+ * | | | \ \ / / _ \___ \ / _ \| || |
+ * | |__| |\ V / (_) |__) | (_) | || |_
+ * | __ | > < > _ <|__ < \__, |__ _|
+ * | | | |/ . \ (_) |__) | / / | |
+ * |_| |_/_/ \_\___/____/ /_/ |_|
+ */
+
+ printf(" _ ___ _____ ____ ___ _ _\r\n");
+ printf(" | | | \\ \\ / / _ \\___ \\ / _ \\| || |\r\n");
+ printf(" | |__| |\\ V / (_) |__) | (_) | || |_ \r\n");
+ printf(" | __ | > < > _ <|__ < \\__, |__ _|\r\n");
+ printf(" | | | |/ . \\ (_) |__) | / / | |\r\n");
+ printf(" |_| |_/_/ \\_\\___/____/ /_/ |_| \r\n");
+ printf(" Waveshare Electronics\n");
+
+ // unity_run_menu();
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar pattern drawn by hardware");
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL));
+}
+#endif
diff --git a/display/lcd/esp_lcd_hx8394/test_apps/sdkconfig.defaults b/display/lcd/esp_lcd_hx8394/test_apps/sdkconfig.defaults
new file mode 100644
index 0000000..521790c
--- /dev/null
+++ b/display/lcd/esp_lcd_hx8394/test_apps/sdkconfig.defaults
@@ -0,0 +1,3 @@
+CONFIG_ESP_TASK_WDT_EN=n
+CONFIG_FREERTOS_HZ=1000
+CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
diff --git a/display/lcd/esp_lcd_hx8394/test_apps/sdkconfig.defaults.esp32p4 b/display/lcd/esp_lcd_hx8394/test_apps/sdkconfig.defaults.esp32p4
new file mode 100644
index 0000000..4621955
--- /dev/null
+++ b/display/lcd/esp_lcd_hx8394/test_apps/sdkconfig.defaults.esp32p4
@@ -0,0 +1,4 @@
+CONFIG_COMPILER_OPTIMIZATION_PERF=y
+CONFIG_SPIRAM=y
+CONFIG_SPIRAM_SPEED_200M=y
+CONFIG_IDF_EXPERIMENTAL_FEATURES=y
diff --git a/display/lcd/esp_lcd_jd9165/CHANGELOG.md b/display/lcd/esp_lcd_jd9165/CHANGELOG.md
new file mode 100644
index 0000000..ea7265a
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9165/CHANGELOG.md
@@ -0,0 +1,16 @@
+# ChangeLog
+
+## v1.0.0 -2025-01-03
+
+* Formally release the component, formally maintain the component
+
+## v0.0.3 -2024-11-14
+
+* Fixed low Lane bit rate and other known issues.
+
+## v0.0.1 - 2024-08-16
+
+### Enhancements:
+
+* Adapted by waveshare electronics according to [esp_lcd_jd9165](https://components.espressif.com/components/espressif/esp_lcd_jd9165)
+
diff --git a/display/lcd/esp_lcd_jd9165/CMakeLists.txt b/display/lcd/esp_lcd_jd9165/CMakeLists.txt
new file mode 100644
index 0000000..6b1843b
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9165/CMakeLists.txt
@@ -0,0 +1,6 @@
+idf_component_register(SRCS "esp_lcd_jd9165.c"
+ INCLUDE_DIRS "include"
+ REQUIRES "esp_lcd")
+
+include(package_manager)
+cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
diff --git a/display/lcd/esp_lcd_jd9165/README.md b/display/lcd/esp_lcd_jd9165/README.md
new file mode 100644
index 0000000..3e79887
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9165/README.md
@@ -0,0 +1,69 @@
+# Waveshare 7 inch Display with JD9165
+
+[![Component Registry](https://components.espressif.com/components/waveshare/esp_lcd_jd9165/badge.svg)](https://components.espressif.com/components/waveshare/esp_lcd_jd9165)
+
+Implementation of the JD9165 LCD controller with esp_lcd component.
+
+| LCD controller | Communication interface | Component name | Link to datasheet |
+| :------------: | :---------------------: | :------------: | :-----------------------------------------------------------------------------------: |
+| JD9165 | MIPI-DSI | esp_lcd_jd9165 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/JD9165DA-H3_DS_V0.01_20200819.pdf) |
+
+**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions.
+
+## Add to project
+
+Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/).
+You can add them to your project via `idf.py add-dependancy`, e.g.
+
+```
+ idf.py add-dependency "waveshare/esp_lcd_jd9165"
+```
+
+Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).
+
+## Example use
+
+```c
+/**
+ * Uncomment these line if use custom initialization commands.
+ * The array should be declared as static const and positioned outside the function.
+ */
+ ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
+ esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
+ esp_ldo_channel_config_t ldo_mipi_phy_config = {
+ .chan_id = 3,
+ .voltage_mv = 2500,
+ };
+ ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
+
+ ESP_LOGI(TAG, "Initialize MIPI DSI bus");
+ esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
+ esp_lcd_dsi_bus_config_t bus_config = JD9165_PANEL_BUS_DSI_2CH_CONFIG();
+ ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
+
+ ESP_LOGI(TAG, "Install panel IO");
+ esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
+ esp_lcd_dbi_io_config_t dbi_config = JD9165_PANEL_IO_DBI_CONFIG();
+ ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
+
+ ESP_LOGI(TAG, "Install JD9165S panel driver");
+ esp_lcd_panel_handle_t panel_handle = NULL;
+ const esp_lcd_dpi_panel_config_t dpi_config = JD9165_1024_600_PANEL_60HZ_DPI_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT);
+ jd9165_vendor_config_t vendor_config = {
+ .flags = {
+ .use_mipi_interface = 1,
+ },
+ .mipi_config = {
+ .dsi_bus = mipi_dsi_bus,
+ .dpi_config = &dpi_config,
+ },
+ };
+ const esp_lcd_panel_dev_config_t panel_config = {
+ .reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use
+ .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h`
+ .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24)
+ .vendor_config = &vendor_config,
+ };
+ ESP_ERROR_CHECK(esp_lcd_new_panel_jd9165(mipi_dbi_io, &panel_config, &panel_handle));
+ ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
+```
diff --git a/display/lcd/esp_lcd_jd9165/esp_lcd_jd9165.c b/display/lcd/esp_lcd_jd9165/esp_lcd_jd9165.c
new file mode 100644
index 0000000..93cd7b3
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9165/esp_lcd_jd9165.c
@@ -0,0 +1,384 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "soc/soc_caps.h"
+
+#if SOC_MIPI_DSI_SUPPORTED
+#include "esp_check.h"
+#include "esp_log.h"
+#include "esp_lcd_panel_commands.h"
+#include "esp_lcd_panel_interface.h"
+#include "esp_lcd_panel_io.h"
+#include "esp_lcd_mipi_dsi.h"
+#include "esp_lcd_panel_vendor.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/gpio.h"
+#include "esp_lcd_jd9165.h"
+
+#define JD9165_CMD_GS_BIT (1 << 0)
+#define JD9165_CMD_SS_BIT (1 << 1)
+
+typedef struct
+{
+ esp_lcd_panel_io_handle_t io;
+ int reset_gpio_num;
+ uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
+ uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
+ const jd9165_lcd_init_cmd_t *init_cmds;
+ uint16_t init_cmds_size;
+ struct
+ {
+ unsigned int reset_level : 1;
+ } flags;
+ // To save the original functions of MIPI DPI panel
+ esp_err_t (*del)(esp_lcd_panel_t *panel);
+ esp_err_t (*init)(esp_lcd_panel_t *panel);
+} jd9165_panel_t;
+
+static const char *TAG = "jd9165";
+
+static esp_err_t panel_jd9165_del(esp_lcd_panel_t *panel);
+static esp_err_t panel_jd9165_init(esp_lcd_panel_t *panel);
+static esp_err_t panel_jd9165_reset(esp_lcd_panel_t *panel);
+static esp_err_t panel_jd9165_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
+static esp_err_t panel_jd9165_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
+static esp_err_t panel_jd9165_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
+static esp_err_t panel_jd9165_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
+static esp_err_t panel_jd9165_disp_on_off(esp_lcd_panel_t *panel, bool on_off);
+
+esp_err_t esp_lcd_new_panel_jd9165(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
+ esp_lcd_panel_handle_t *ret_panel)
+{
+ ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_JD9165_VER_MAJOR, ESP_LCD_JD9165_VER_MINOR,
+ ESP_LCD_JD9165_VER_PATCH);
+ ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
+ jd9165_vendor_config_t *vendor_config = (jd9165_vendor_config_t *)panel_dev_config->vendor_config;
+ ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
+ "invalid vendor config");
+
+ esp_err_t ret = ESP_OK;
+ jd9165_panel_t *jd9165 = (jd9165_panel_t *)calloc(1, sizeof(jd9165_panel_t));
+ ESP_RETURN_ON_FALSE(jd9165, ESP_ERR_NO_MEM, TAG, "no mem for jd9165 panel");
+
+ if (panel_dev_config->reset_gpio_num >= 0)
+ {
+ gpio_config_t io_conf = {
+ .mode = GPIO_MODE_OUTPUT,
+ .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
+ };
+ ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
+ }
+
+ switch (panel_dev_config->color_space)
+ {
+ case LCD_RGB_ELEMENT_ORDER_RGB:
+ jd9165->madctl_val = 0;
+ break;
+ case LCD_RGB_ELEMENT_ORDER_BGR:
+ jd9165->madctl_val |= LCD_CMD_BGR_BIT;
+ break;
+ default:
+ ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
+ break;
+ }
+
+ jd9165->io = io;
+ jd9165->init_cmds = vendor_config->init_cmds;
+ jd9165->init_cmds_size = vendor_config->init_cmds_size;
+ jd9165->reset_gpio_num = panel_dev_config->reset_gpio_num;
+ jd9165->flags.reset_level = panel_dev_config->flags.reset_active_high;
+
+ // Create MIPI DPI panel
+ esp_lcd_panel_handle_t panel_handle = NULL;
+ ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
+ "create MIPI DPI panel failed");
+ ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
+
+ // Save the original functions of MIPI DPI panel
+ jd9165->del = panel_handle->del;
+ jd9165->init = panel_handle->init;
+ // Overwrite the functions of MIPI DPI panel
+ panel_handle->del = panel_jd9165_del;
+ panel_handle->init = panel_jd9165_init;
+ panel_handle->reset = panel_jd9165_reset;
+ panel_handle->mirror = panel_jd9165_mirror;
+ panel_handle->swap_xy = panel_jd9165_swap_xy;
+ panel_handle->set_gap = panel_jd9165_set_gap;
+ panel_handle->invert_color = panel_jd9165_invert_color;
+ panel_handle->disp_on_off = panel_jd9165_disp_on_off;
+ panel_handle->user_data = jd9165;
+ *ret_panel = panel_handle;
+ ESP_LOGD(TAG, "new jd9165 panel @%p", jd9165);
+
+ return ESP_OK;
+
+err:
+ if (jd9165)
+ {
+ if (panel_dev_config->reset_gpio_num >= 0)
+ {
+ gpio_reset_pin(panel_dev_config->reset_gpio_num);
+ }
+ free(jd9165);
+ }
+ return ret;
+}
+
+static const jd9165_lcd_init_cmd_t vendor_specific_init_default[] = {
+ // {cmd, { data }, data_size, delay_ms}
+ {0x30, (uint8_t[]){0x00}, 1, 0},
+ {0xF7, (uint8_t[]){0x49, 0x61, 0x02, 0x00}, 4, 0},
+ {0x30, (uint8_t[]){0x01}, 1, 0},
+ {0x04, (uint8_t[]){0x0C}, 1, 0},
+ {0x05, (uint8_t[]){0x08}, 1, 0},
+ {0x0B, (uint8_t[]){0x10}, 1, 0},
+ {0x0B, (uint8_t[]){0x11}, 1, 0}, // 0x11(2lanes),0x12(3lanes),0x13(4lanes)
+ {0x20, (uint8_t[]){0x04}, 1, 0}, // r_lansel_sel_reg //A2 add
+ {0x1F, (uint8_t[]){0x05}, 1, 0}, // mipi_hs_settle
+ {0x23, (uint8_t[]){0x38}, 1, 0},
+ {0x28, (uint8_t[]){0x18}, 1, 0},
+ {0x29, (uint8_t[]){0x29}, 1, 0},
+ {0x2A, (uint8_t[]){0x01}, 1, 0},
+ {0x2B, (uint8_t[]){0x4C}, 1, 0},
+ {0x2C, (uint8_t[]){0x01}, 1, 0},
+ {0x30, (uint8_t[]){0x02}, 1, 0},
+ {0x00, (uint8_t[]){0x05}, 1, 0},
+ {0x01, (uint8_t[]){0x22}, 1, 0},
+ {0x02, (uint8_t[]){0x08}, 1, 0},
+ {0x03, (uint8_t[]){0x12}, 1, 0},
+ {0x04, (uint8_t[]){0x16}, 1, 0},
+ {0x05, (uint8_t[]){0x64}, 1, 0},
+ {0x06, (uint8_t[]){0x00}, 1, 0},
+ {0x07, (uint8_t[]){0x00}, 1, 0},
+ {0x08, (uint8_t[]){0x78}, 1, 0},
+ {0x09, (uint8_t[]){0x00}, 1, 0},
+ {0x0A, (uint8_t[]){0x04}, 1, 0},
+ {0x0B, (uint8_t[]){0x16, 0x17, 0x0B, 0x0D, 0x0D, 0x0D, 0x11, 0x10, 0x07, 0x07, 0x09}, 11, 0},
+ {0x0C, (uint8_t[]){0x09, 0x1E, 0x1E, 0x1C, 0x1C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D}, 11, 0},
+ {0x0D, (uint8_t[]){0x0A, 0x05, 0x0B, 0x0D, 0x0D, 0x0D, 0x11, 0x10, 0x06, 0x06, 0x08}, 11, 0},
+ {0x0E, (uint8_t[]){0x08, 0x1F, 0x1F, 0x1D, 0x1D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D}, 11, 0},
+ {0x0F, (uint8_t[]){0x0A, 0x05, 0x0D, 0x0B, 0x0D, 0x0D, 0x11, 0x10, 0x1D, 0x1D, 0x1F}, 11, 0},
+ {0x10, (uint8_t[]){0x1F, 0x08, 0x08, 0x06, 0x06, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D}, 11, 0},
+ {0x11, (uint8_t[]){0x16, 0x17, 0x0D, 0x0B, 0x0D, 0x0D, 0x11, 0x10, 0x1C, 0x1C, 0x1E}, 11, 0},
+ {0x12, (uint8_t[]){0x1E, 0x09, 0x09, 0x07, 0x07, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D}, 11, 0},
+ {0x13, (uint8_t[]){0x00, 0x00, 0x00, 0x00}, 4, 0},
+ {0x14, (uint8_t[]){0x00, 0x00, 0x41, 0x41}, 4, 0},
+ {0x15, (uint8_t[]){0x00, 0x00, 0x00, 0x00}, 4, 0},
+ {0x17, (uint8_t[]){0x00}, 1, 0},
+ {0x18, (uint8_t[]){0x85}, 1, 0},
+ {0x19, (uint8_t[]){0x06, 0x09}, 2, 0},
+ {0x1A, (uint8_t[]){0x05, 0x08}, 2, 0},
+ {0x1B, (uint8_t[]){0x0A, 0x04}, 2, 0},
+ {0x26, (uint8_t[]){0x00}, 1, 0},
+ {0x27, (uint8_t[]){0x00}, 1, 0},
+ {0x30, (uint8_t[]){0x06}, 1, 0},
+ {0x12, (uint8_t[]){0x3F, 0x26, 0x27, 0x35, 0x2D, 0x33, 0x3B, 0x3F, 0x3F, 0x37, 0x2D, 0x20, 0x16, 0x08}, 14, 0},
+ {0x13, (uint8_t[]){0x3F, 0x26, 0x28, 0x35, 0x27, 0x2A, 0x2D, 0x2F, 0x35, 0x2C, 0x23, 0x20, 0x16, 0x08}, 14, 0},
+ {0x30, (uint8_t[]){0x0A}, 1, 0},
+ {0x02, (uint8_t[]){0x4F}, 1, 0},
+ {0x0B, (uint8_t[]){0x40}, 1, 0},
+ {0x30, (uint8_t[]){0x0D}, 1, 0},
+ {0x0D, (uint8_t[]){0x04}, 1, 0}, // mipi add //0x0C, 0x04
+ {0x10, (uint8_t[]){0x0C}, 1, 0},
+ {0x11, (uint8_t[]){0x0C}, 1, 0},
+ {0x12, (uint8_t[]){0x0C}, 1, 0},
+ {0x13, (uint8_t[]){0x0C}, 1, 0},
+ {0x30, (uint8_t[]){0x00}, 1, 0},
+
+ {0x11, (uint8_t[]){0x00}, 1, 120},
+ {0x29, (uint8_t[]){0x00}, 1, 20},
+};
+
+static esp_err_t panel_jd9165_del(esp_lcd_panel_t *panel)
+{
+ jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data;
+
+ if (jd9165->reset_gpio_num >= 0)
+ {
+ gpio_reset_pin(jd9165->reset_gpio_num);
+ }
+ // Delete MIPI DPI panel
+ jd9165->del(panel);
+
+ ESP_LOGD(TAG, "del jd9165 panel @%p", jd9165);
+ free(jd9165);
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9165_init(esp_lcd_panel_t *panel)
+{
+ jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9165->io;
+ const jd9165_lcd_init_cmd_t *init_cmds = NULL;
+ uint16_t init_cmds_size = 0;
+ bool is_cmd_overwritten = false;
+
+ uint8_t ID[3];
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed");
+
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){
+ jd9165->madctl_val,
+ },
+ 1),
+ TAG, "send command failed");
+
+ // vendor specific initialization, it can be different between manufacturers
+ // should consult the LCD supplier for initialization sequence code
+ if (jd9165->init_cmds)
+ {
+ init_cmds = jd9165->init_cmds;
+ init_cmds_size = jd9165->init_cmds_size;
+ }
+ else
+ {
+ init_cmds = vendor_specific_init_default;
+ init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9165_lcd_init_cmd_t);
+ }
+
+ for (int i = 0; i < init_cmds_size; i++)
+ {
+ // Check if the command has been used or conflicts with the internal
+ if (init_cmds[i].data_bytes > 0)
+ {
+ switch (init_cmds[i].cmd)
+ {
+ case LCD_CMD_MADCTL:
+ is_cmd_overwritten = true;
+ jd9165->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
+ break;
+ default:
+ is_cmd_overwritten = false;
+ break;
+ }
+
+ if (is_cmd_overwritten)
+ {
+ is_cmd_overwritten = false;
+ ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
+ init_cmds[i].cmd);
+ }
+ }
+
+ // Send command
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed");
+ vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
+ }
+ ESP_LOGD(TAG, "send init commands success");
+
+ ESP_RETURN_ON_ERROR(jd9165->init(panel), TAG, "init MIPI DPI panel failed");
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9165_reset(esp_lcd_panel_t *panel)
+{
+ jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9165->io;
+
+ // Perform hardware reset
+ if (jd9165->reset_gpio_num >= 0)
+ {
+ gpio_set_level(jd9165->reset_gpio_num, !jd9165->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(5));
+ gpio_set_level(jd9165->reset_gpio_num, jd9165->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(10));
+ gpio_set_level(jd9165->reset_gpio_num, !jd9165->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(120));
+ }
+ else if (io)
+ { // Perform software reset
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
+ vTaskDelay(pdMS_TO_TICKS(120));
+ }
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9165_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
+{
+ jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9165->io;
+ uint8_t command = 0;
+
+ ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
+
+ if (invert_color_data)
+ {
+ command = LCD_CMD_INVON;
+ }
+ else
+ {
+ command = LCD_CMD_INVOFF;
+ }
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9165_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
+{
+ jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9165->io;
+ uint8_t madctl_val = jd9165->madctl_val;
+
+ ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
+
+ // Control mirror through LCD command
+ if (mirror_x)
+ {
+ madctl_val |= JD9165_CMD_GS_BIT;
+ }
+ else
+ {
+ madctl_val &= ~JD9165_CMD_GS_BIT;
+ }
+ if (mirror_y)
+ {
+ madctl_val |= JD9165_CMD_SS_BIT;
+ }
+ else
+ {
+ madctl_val &= ~JD9165_CMD_SS_BIT;
+ }
+
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){madctl_val}, 1), TAG, "send command failed");
+ jd9165->madctl_val = madctl_val;
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9165_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
+{
+ ESP_LOGE(TAG, "swap_xy is not supported by this panel");
+ return ESP_ERR_NOT_SUPPORTED;
+}
+
+static esp_err_t panel_jd9165_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
+{
+ ESP_LOGE(TAG, "set_gap is not supported by this panel");
+ return ESP_ERR_NOT_SUPPORTED;
+}
+
+static esp_err_t panel_jd9165_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
+{
+ jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9165->io;
+ int command = 0;
+
+ if (on_off)
+ {
+ command = LCD_CMD_DISPON;
+ }
+ else
+ {
+ command = LCD_CMD_DISPOFF;
+ }
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
+ return ESP_OK;
+}
+#endif
diff --git a/display/lcd/esp_lcd_jd9165/idf_component.yml b/display/lcd/esp_lcd_jd9165/idf_component.yml
new file mode 100644
index 0000000..f219734
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9165/idf_component.yml
@@ -0,0 +1,11 @@
+version: "1.0.0"
+license: "MIT"
+targets:
+ - esp32p4
+description: "ESP LCD FOR Waveshare 7 inch DISPLAY JD9165"
+url: "https://github.com/waveshareteam/Waveshare-ESP32-components/tree/master/esp_lcd_jd9165"
+issues: "https://github.com/waveshareteam/Waveshare-ESP32-components/issues"
+dependencies:
+ idf:
+ version: '>=5.3'
+ cmake_utilities: "0.*"
diff --git a/display/lcd/esp_lcd_jd9165/include/esp_lcd_jd9165.h b/display/lcd/esp_lcd_jd9165/include/esp_lcd_jd9165.h
new file mode 100644
index 0000000..e513f19
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9165/include/esp_lcd_jd9165.h
@@ -0,0 +1,138 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include
+#include "soc/soc_caps.h"
+
+#if SOC_MIPI_DSI_SUPPORTED
+#include "esp_lcd_panel_vendor.h"
+#include "esp_lcd_mipi_dsi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief LCD panel initialization commands.
+ *
+ */
+typedef struct {
+ int cmd; /*
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/i2c.h"
+#include "driver/spi_master.h"
+#include "driver/gpio.h"
+#include "esp_heap_caps.h"
+#include "esp_log.h"
+#include "esp_timer.h"
+#include "esp_lcd_panel_ops.h"
+#include "esp_lcd_panel_io.h"
+#include "esp_ldo_regulator.h"
+#include "esp_dma_utils.h"
+#include "unity.h"
+#include "unity_test_runner.h"
+#include "unity_test_utils_memory.h"
+#include "esp_lcd_mipi_dsi.h"
+#include "esp_lcd_jd9165.h"
+
+#define TEST_LCD_H_RES (1024)
+#define TEST_LCD_V_RES (600)
+#define TEST_LCD_BIT_PER_PIXEL (24)
+#define TEST_PIN_NUM_LCD_RST (-1)
+#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used
+#define TEST_LCD_BK_LIGHT_ON_LEVEL (1)
+#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL
+
+#if TEST_LCD_BIT_PER_PIXEL == 24
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888)
+#elif TEST_LCD_BIT_PER_PIXEL == 18
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666)
+#elif TEST_LCD_BIT_PER_PIXEL == 16
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565)
+#endif
+
+#define TEST_DELAY_TIME_MS (3000)
+
+#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3)
+#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
+
+static char *TAG = "jd9165_test";
+static esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
+static esp_lcd_panel_handle_t panel_handle = NULL;
+static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
+static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
+static SemaphoreHandle_t refresh_finish = NULL;
+
+IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
+{
+ SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx;
+ BaseType_t need_yield = pdFALSE;
+
+ xSemaphoreGiveFromISR(refresh_finish, &need_yield);
+
+ return (need_yield == pdTRUE);
+}
+
+static void test_init_lcd(void)
+{
+#if TEST_PIN_NUM_BK_LIGHT >= 0
+ ESP_LOGI(TAG, "Turn on LCD backlight");
+ gpio_config_t bk_gpio_config = {
+ .mode = GPIO_MODE_OUTPUT,
+ .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT};
+ TEST_ESP_OK(gpio_config(&bk_gpio_config));
+ TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL));
+#endif
+
+ // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
+#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN
+ ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
+ esp_ldo_channel_config_t ldo_mipi_phy_config = {
+ .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN,
+ .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
+ };
+ TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
+#endif
+
+ ESP_LOGI(TAG, "Initialize MIPI DSI bus");
+ esp_lcd_dsi_bus_config_t bus_config = JD9165_PANEL_BUS_DSI_2CH_CONFIG();
+ TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
+
+ ESP_LOGI(TAG, "Install panel IO");
+ esp_lcd_dbi_io_config_t dbi_config = JD9165_PANEL_IO_DBI_CONFIG();
+ TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
+
+ ESP_LOGI(TAG, "Install LCD driver of jd9165");
+ esp_lcd_dpi_panel_config_t dpi_config = JD9165_1024_600_PANEL_60HZ_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT);
+ jd9165_vendor_config_t vendor_config = {
+ .flags = {
+ .use_mipi_interface = 1,
+ },
+ .mipi_config = {
+ .dsi_bus = mipi_dsi_bus,
+ .dpi_config = &dpi_config,
+ },
+ };
+ const esp_lcd_panel_dev_config_t panel_config = {
+ .reset_gpio_num = TEST_PIN_NUM_LCD_RST,
+ .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
+ .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL,
+ .vendor_config = &vendor_config,
+ };
+ TEST_ESP_OK(esp_lcd_new_panel_jd9165(mipi_dbi_io, &panel_config, &panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_init(panel_handle));
+
+ refresh_finish = xSemaphoreCreateBinary();
+ TEST_ASSERT_NOT_NULL(refresh_finish);
+ esp_lcd_dpi_panel_event_callbacks_t cbs = {
+ .on_color_trans_done = test_notify_refresh_ready,
+ };
+ TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish));
+}
+
+static void test_deinit_lcd(void)
+{
+ TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io));
+ TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus));
+ panel_handle = NULL;
+ mipi_dbi_io = NULL;
+ mipi_dsi_bus = NULL;
+
+ if (ldo_mipi_phy)
+ {
+ TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy));
+ ldo_mipi_phy = NULL;
+ }
+
+ vSemaphoreDelete(refresh_finish);
+ refresh_finish = NULL;
+
+#if TEST_PIN_NUM_BK_LIGHT >= 0
+ TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT));
+#endif
+}
+
+static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res)
+{
+ uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8;
+ uint16_t row_line = v_res / byte_per_pixel / 8;
+ uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA);
+
+ for (int j = 0; j < byte_per_pixel * 8; j++)
+ {
+ for (int i = 0; i < row_line * h_res; i++)
+ {
+ for (int k = 0; k < byte_per_pixel; k++)
+ {
+ color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff;
+ }
+ }
+ TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color));
+ xSemaphoreTake(refresh_finish, portMAX_DELAY);
+ }
+
+ uint16_t color_line = row_line * byte_per_pixel * 8;
+ uint16_t res_line = v_res - color_line;
+ if (res_line)
+ {
+ for (int i = 0; i < res_line * h_res; i++)
+ {
+ for (int k = 0; k < byte_per_pixel; k++)
+ {
+ color[i * byte_per_pixel + k] = 0xff;
+ }
+ }
+ TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color));
+ xSemaphoreTake(refresh_finish, portMAX_DELAY);
+ }
+
+ free(color);
+}
+
+TEST_CASE("test jd9165 to draw pattern with MIPI interface", "[jd9165][draw_pattern]")
+{
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar pattern drawn by hardware");
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL));
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL));
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE));
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+TEST_CASE("test jd9165 to draw color bar with MIPI interface", "[jd9165][draw_color_bar]")
+{
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar drawn by software");
+ test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES);
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+TEST_CASE("test jd9165 to rotate with MIPI interface", "[jd9165][rotate]")
+{
+ esp_err_t ret = ESP_OK;
+
+ uint16_t w = 0;
+ uint16_t h = 0;
+ int64_t t = 0;
+
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Rotate the screen");
+ for (size_t i = 0; i < 8; i++)
+ {
+ if (ret != ESP_ERR_NOT_SUPPORTED)
+ {
+ if (i & 4)
+ {
+ w = TEST_LCD_V_RES;
+ h = TEST_LCD_H_RES;
+ }
+ else
+ {
+ w = TEST_LCD_H_RES;
+ h = TEST_LCD_V_RES;
+ }
+ }
+
+ TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL);
+ ret = esp_lcd_panel_swap_xy(panel_handle, i & 4);
+ TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL);
+
+ ESP_LOGI(TAG, "Rotation: %d", i);
+ t = esp_timer_get_time();
+ test_draw_color_bar(panel_handle, w, h);
+ t = esp_timer_get_time() - t;
+ ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f);
+ vTaskDelay(pdMS_TO_TICKS(1000));
+ }
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case
+#define TEST_MEMORY_LEAK_THRESHOLD (300)
+
+static size_t before_free_8bit;
+static size_t before_free_32bit;
+
+void setUp(void)
+{
+ before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+ before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+}
+
+void tearDown(void)
+{
+ size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+ size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+ unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD);
+ unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD);
+}
+
+void app_main(void)
+{
+ /**
+ * _ ____ ___ _ __ ____
+ * | | _ \ / _ \/ |/ /_| ___|
+ * _ | | | | | (_) | | '_ \___ \
+ * | |_| | |_| |\__, | | (_) |__) |
+ * \___/|____/ /_/|_|\___/____/
+ */
+ printf(" _ ____ ___ _ __ ____ \r\n");
+ printf(" | | _ \\ / _ \\/ |/ /_| ___| \r\n");
+ printf(" _ | | | | | (_) | | '_ \\___ \\ \r\n");
+ printf(" | |_| | |_| |\\__, | | (_) |__) |\r\n");
+ printf(" \\___/|____/ /_/|_|\\___/____/ \r\n");
+ // unity_run_menu();
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+// ESP_LOGI(TAG, "Show color bar pattern drawn by hardware");
+// TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL));
+ ESP_LOGI(TAG, "Show color bar pattern drawn by hardware");
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL));
+}
+#endif
diff --git a/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults b/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults
new file mode 100644
index 0000000..521790c
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults
@@ -0,0 +1,3 @@
+CONFIG_ESP_TASK_WDT_EN=n
+CONFIG_FREERTOS_HZ=1000
+CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
diff --git a/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults.esp32p4 b/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults.esp32p4
new file mode 100644
index 0000000..4621955
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults.esp32p4
@@ -0,0 +1,4 @@
+CONFIG_COMPILER_OPTIMIZATION_PERF=y
+CONFIG_SPIRAM=y
+CONFIG_SPIRAM_SPEED_200M=y
+CONFIG_IDF_EXPERIMENTAL_FEATURES=y
diff --git a/display/lcd/esp_lcd_jd9365_10_1/CHANGELOG.md b/display/lcd/esp_lcd_jd9365_10_1/CHANGELOG.md
new file mode 100644
index 0000000..2fcbd5b
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_10_1/CHANGELOG.md
@@ -0,0 +1,22 @@
+# ChangeLog
+
+## v1.0.0 -2025-01-03
+
+* Formally release the component, formally maintain the component
+
+## v0.0.5 -2024-11-14
+
+* Fixed low Lane bit rate and other known issues.
+
+## v0.0.4 - 2024-10-18
+
+* Modify register configuration
+
+## v0.0.1 - 2024-08-16
+
+### Enhancements:
+
+* Adapted by waveshare electronics according to [esp_lcd_jd9365](https://components.espressif.com/components/espressif/esp_lcd_jd9365)
+
+
+
diff --git a/display/lcd/esp_lcd_jd9365_10_1/CMakeLists.txt b/display/lcd/esp_lcd_jd9365_10_1/CMakeLists.txt
new file mode 100644
index 0000000..72c8188
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_10_1/CMakeLists.txt
@@ -0,0 +1,4 @@
+idf_component_register(SRCS "esp_lcd_jd9365_10_1.c" INCLUDE_DIRS "include" PRIV_REQUIRES "driver" REQUIRES "esp_lcd")
+
+include(package_manager)
+cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
diff --git a/display/lcd/esp_lcd_jd9365_10_1/README.md b/display/lcd/esp_lcd_jd9365_10_1/README.md
new file mode 100644
index 0000000..da07cad
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_10_1/README.md
@@ -0,0 +1,73 @@
+# Waveshare 10.1 inch Display with JD9365
+
+[![Component Registry](https://components.espressif.com/components/waveshare/esp_lcd_jd9365_10_1/badge.svg)](https://components.espressif.com/components/waveshare/esp_lcd_jd9365_10_1)
+
+Implementation of the waveshare 10.1 inch JD9365 LCD controller with esp_lcd component.
+
+| LCD controller | Communication interface | Component name | Link to datasheet |
+| :------------: | :---------------------: |:-------------------:| :-----------------------------------------------------------------------------------: |
+| JD9365 | MIPI-DSI | esp_lcd_jd9365_10_1 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/JD9365DA-H3_DS_V0.01_20200819.pdf) |
+
+**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions.
+
+## Add to project
+
+Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/).
+You can add them to your project via `idf.py add-dependancy`, e.g.
+
+```
+ idf.py add-dependency "waveshare/esp_lcd_jd9365_10_1"
+```
+
+Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).
+
+## Example use
+
+```c
+/**
+ * Uncomment these line if use custom initialization commands.
+ * The array should be declared as static const and positioned outside the function.
+ */
+
+ ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
+ esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
+ esp_ldo_channel_config_t ldo_mipi_phy_config = {
+ .chan_id = 3,
+ .voltage_mv = 2500,
+ };
+ ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
+
+ ESP_LOGI(TAG, "Initialize MIPI DSI bus");
+ esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
+ esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG();
+ ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
+
+ ESP_LOGI(TAG, "Install panel IO");
+ esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
+ esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG();
+ ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
+
+ ESP_LOGI(TAG, "Install JD9365S panel driver");
+ esp_lcd_panel_handle_t panel_handle = NULL;
+ const esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT);
+ jd9365_vendor_config_t vendor_config = {
+ .flags = {
+ .use_mipi_interface = 1,
+ },
+ .mipi_config = {
+ .lane_num = 2,
+ .dsi_bus = mipi_dsi_bus,
+ .dpi_config = &dpi_config,
+ },
+ };
+ const esp_lcd_panel_dev_config_t panel_config = {
+ .reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use
+ .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h`
+ .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24)
+ .vendor_config = &vendor_config,
+ };
+ ESP_ERROR_CHECK(esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_config, &panel_handle));
+ ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
+ ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
+ ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
+```
diff --git a/display/lcd/esp_lcd_jd9365_10_1/esp_lcd_jd9365_10_1.c b/display/lcd/esp_lcd_jd9365_10_1/esp_lcd_jd9365_10_1.c
new file mode 100644
index 0000000..2c6a201
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_10_1/esp_lcd_jd9365_10_1.c
@@ -0,0 +1,616 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "soc/soc_caps.h"
+
+#if SOC_MIPI_DSI_SUPPORTED
+#include "esp_check.h"
+#include "esp_log.h"
+#include "esp_lcd_panel_commands.h"
+#include "esp_lcd_panel_interface.h"
+#include "esp_lcd_panel_io.h"
+#include "esp_lcd_mipi_dsi.h"
+#include "esp_lcd_panel_vendor.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/gpio.h"
+#include "esp_lcd_jd9365_10_1.h"
+
+#define JD9365_CMD_PAGE (0xE0)
+#define JD9365_PAGE_USER (0x00)
+
+#define JD9365_CMD_DSI_INT0 (0x80)
+#define JD9365_DSI_1_LANE (0x00)
+#define JD9365_DSI_2_LANE (0x01)
+#define JD9365_DSI_3_LANE (0x10)
+#define JD9365_DSI_4_LANE (0x11)
+
+#define JD9365_CMD_GS_BIT (1 << 0)
+#define JD9365_CMD_SS_BIT (1 << 1)
+
+typedef struct
+{
+ esp_lcd_panel_io_handle_t io;
+ int reset_gpio_num;
+ uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
+ uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
+ const jd9365_lcd_init_cmd_t *init_cmds;
+ uint16_t init_cmds_size;
+ uint8_t lane_num;
+ struct
+ {
+ unsigned int reset_level : 1;
+ } flags;
+ // To save the original functions of MIPI DPI panel
+ esp_err_t (*del)(esp_lcd_panel_t *panel);
+ esp_err_t (*init)(esp_lcd_panel_t *panel);
+} jd9365_panel_t;
+
+static const char *TAG = "jd9365";
+
+static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel);
+static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel);
+static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel);
+static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
+static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
+static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
+static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
+static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off);
+
+esp_err_t esp_lcd_new_panel_jd9365(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
+ esp_lcd_panel_handle_t *ret_panel)
+{
+ ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_JD9365_10_1_VER_MAJOR, ESP_LCD_JD9365_10_1_VER_MINOR,
+ ESP_LCD_JD9365_10_1_VER_PATCH);
+ ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
+ jd9365_vendor_config_t *vendor_config = (jd9365_vendor_config_t *)panel_dev_config->vendor_config;
+ ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
+ "invalid vendor config");
+
+ esp_err_t ret = ESP_OK;
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)calloc(1, sizeof(jd9365_panel_t));
+ ESP_RETURN_ON_FALSE(jd9365, ESP_ERR_NO_MEM, TAG, "no mem for jd9365 panel");
+
+ if (panel_dev_config->reset_gpio_num >= 0)
+ {
+ gpio_config_t io_conf = {
+ .mode = GPIO_MODE_OUTPUT,
+ .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
+ };
+ ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
+ }
+
+ switch (panel_dev_config->color_space)
+ {
+ case LCD_RGB_ELEMENT_ORDER_RGB:
+ jd9365->madctl_val = 0;
+ break;
+ case LCD_RGB_ELEMENT_ORDER_BGR:
+ jd9365->madctl_val |= LCD_CMD_BGR_BIT;
+ break;
+ default:
+ ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
+ break;
+ }
+
+ switch (panel_dev_config->bits_per_pixel)
+ {
+ case 16: // RGB565
+ jd9365->colmod_val = 0x55;
+ break;
+ case 18: // RGB666
+ jd9365->colmod_val = 0x66;
+ break;
+ case 24: // RGB888
+ jd9365->colmod_val = 0x77;
+ break;
+ default:
+ ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
+ break;
+ }
+
+ jd9365->io = io;
+ jd9365->init_cmds = vendor_config->init_cmds;
+ jd9365->init_cmds_size = vendor_config->init_cmds_size;
+ jd9365->lane_num = vendor_config->mipi_config.lane_num;
+ jd9365->reset_gpio_num = panel_dev_config->reset_gpio_num;
+ jd9365->flags.reset_level = panel_dev_config->flags.reset_active_high;
+
+ // Create MIPI DPI panel
+ esp_lcd_panel_handle_t panel_handle = NULL;
+ ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
+ "create MIPI DPI panel failed");
+ ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
+
+ // Save the original functions of MIPI DPI panel
+ jd9365->del = panel_handle->del;
+ jd9365->init = panel_handle->init;
+ // Overwrite the functions of MIPI DPI panel
+ panel_handle->del = panel_jd9365_del;
+ panel_handle->init = panel_jd9365_init;
+ panel_handle->reset = panel_jd9365_reset;
+ panel_handle->mirror = panel_jd9365_mirror;
+ panel_handle->swap_xy = panel_jd9365_swap_xy;
+ panel_handle->set_gap = panel_jd9365_set_gap;
+ panel_handle->invert_color = panel_jd9365_invert_color;
+ panel_handle->disp_on_off = panel_jd9365_disp_on_off;
+ panel_handle->user_data = jd9365;
+ *ret_panel = panel_handle;
+ ESP_LOGD(TAG, "new jd9365 panel @%p", jd9365);
+
+ return ESP_OK;
+
+err:
+ if (jd9365)
+ {
+ if (panel_dev_config->reset_gpio_num >= 0)
+ {
+ gpio_reset_pin(panel_dev_config->reset_gpio_num);
+ }
+ free(jd9365);
+ }
+ return ret;
+}
+
+static const jd9365_lcd_init_cmd_t vendor_specific_init_default[] = {
+ // {cmd, { data }, data_size, delay_ms}
+ // {0xE0, (uint8_t[]){0x00}, 1, 0},
+ {0xE0, (uint8_t[]){0x00}, 1, 0},
+ {0xE1, (uint8_t[]){0x93}, 1, 0},
+ {0xE2, (uint8_t[]){0x65}, 1, 0},
+ {0xE3, (uint8_t[]){0xF8}, 1, 0},
+ {0x80, (uint8_t[]){0x01}, 1, 0},
+
+ {0xE0, (uint8_t[]){0x01}, 1, 0},
+ {0x00, (uint8_t[]){0x00}, 1, 0},
+ {0x01, (uint8_t[]){0x38}, 1, 0},
+ {0x03, (uint8_t[]){0x10}, 1, 0},
+ {0x04, (uint8_t[]){0x38}, 1, 0},
+
+ {0x0C, (uint8_t[]){0x74}, 1, 0},
+
+ {0x17, (uint8_t[]){0x00}, 1, 0},
+ {0x18, (uint8_t[]){0xAF}, 1, 0},
+ {0x19, (uint8_t[]){0x00}, 1, 0},
+ {0x1A, (uint8_t[]){0x00}, 1, 0},
+ {0x1B, (uint8_t[]){0xAF}, 1, 0},
+ {0x1C, (uint8_t[]){0x00}, 1, 0},
+
+ {0x35, (uint8_t[]){0x26}, 1, 0},
+
+ {0x37, (uint8_t[]){0x09}, 1, 0},
+
+ {0x38, (uint8_t[]){0x04}, 1, 0},
+ {0x39, (uint8_t[]){0x00}, 1, 0},
+ {0x3A, (uint8_t[]){0x01}, 1, 0},
+ {0x3C, (uint8_t[]){0x78}, 1, 0},
+ {0x3D, (uint8_t[]){0xFF}, 1, 0},
+ {0x3E, (uint8_t[]){0xFF}, 1, 0},
+ {0x3F, (uint8_t[]){0x7F}, 1, 0},
+
+ {0x40, (uint8_t[]){0x06}, 1, 0},
+ {0x41, (uint8_t[]){0xA0}, 1, 0},
+ {0x42, (uint8_t[]){0x81}, 1, 0},
+ {0x43, (uint8_t[]){0x1E}, 1, 0},
+ {0x44, (uint8_t[]){0x0D}, 1, 0},
+ {0x45, (uint8_t[]){0x28}, 1, 0},
+ //{0x4A, (uint8_t[]){0x35}, 1, 0},//bist
+
+ {0x55, (uint8_t[]){0x02}, 1, 0},
+ {0x57, (uint8_t[]){0x69}, 1, 0},
+ {0x59, (uint8_t[]){0x0A}, 1, 0},
+ {0x5A, (uint8_t[]){0x2A}, 1, 0},
+ {0x5B, (uint8_t[]){0x17}, 1, 0},
+
+ {0x5D, (uint8_t[]){0x7F}, 1, 0},
+ {0x5E, (uint8_t[]){0x6A}, 1, 0},
+ {0x5F, (uint8_t[]){0x5B}, 1, 0},
+ {0x60, (uint8_t[]){0x4F}, 1, 0},
+ {0x61, (uint8_t[]){0x4A}, 1, 0},
+ {0x62, (uint8_t[]){0x3D}, 1, 0},
+ {0x63, (uint8_t[]){0x41}, 1, 0},
+ {0x64, (uint8_t[]){0x2A}, 1, 0},
+ {0x65, (uint8_t[]){0x44}, 1, 0},
+ {0x66, (uint8_t[]){0x43}, 1, 0},
+ {0x67, (uint8_t[]){0x44}, 1, 0},
+ {0x68, (uint8_t[]){0x62}, 1, 0},
+ {0x69, (uint8_t[]){0x52}, 1, 0},
+ {0x6A, (uint8_t[]){0x59}, 1, 0},
+ {0x6B, (uint8_t[]){0x4C}, 1, 0},
+ {0x6C, (uint8_t[]){0x48}, 1, 0},
+ {0x6D, (uint8_t[]){0x3A}, 1, 0},
+ {0x6E, (uint8_t[]){0x26}, 1, 0},
+ {0x6F, (uint8_t[]){0x00}, 1, 0},
+ {0x70, (uint8_t[]){0x7F}, 1, 0},
+ {0x71, (uint8_t[]){0x6A}, 1, 0},
+ {0x72, (uint8_t[]){0x5B}, 1, 0},
+ {0x73, (uint8_t[]){0x4F}, 1, 0},
+ {0x74, (uint8_t[]){0x4A}, 1, 0},
+ {0x75, (uint8_t[]){0x3D}, 1, 0},
+ {0x76, (uint8_t[]){0x41}, 1, 0},
+ {0x77, (uint8_t[]){0x2A}, 1, 0},
+ {0x78, (uint8_t[]){0x44}, 1, 0},
+ {0x79, (uint8_t[]){0x43}, 1, 0},
+ {0x7A, (uint8_t[]){0x44}, 1, 0},
+ {0x7B, (uint8_t[]){0x62}, 1, 0},
+ {0x7C, (uint8_t[]){0x52}, 1, 0},
+ {0x7D, (uint8_t[]){0x59}, 1, 0},
+ {0x7E, (uint8_t[]){0x4C}, 1, 0},
+ {0x7F, (uint8_t[]){0x48}, 1, 0},
+ {0x80, (uint8_t[]){0x3A}, 1, 0},
+ {0x81, (uint8_t[]){0x26}, 1, 0},
+ {0x82, (uint8_t[]){0x00}, 1, 0},
+
+ {0xE0, (uint8_t[]){0x02}, 1, 0},
+ {0x00, (uint8_t[]){0x42}, 1, 0},
+ {0x01, (uint8_t[]){0x42}, 1, 0},
+ {0x02, (uint8_t[]){0x40}, 1, 0},
+ {0x03, (uint8_t[]){0x40}, 1, 0},
+ {0x04, (uint8_t[]){0x5E}, 1, 0},
+ {0x05, (uint8_t[]){0x5E}, 1, 0},
+ {0x06, (uint8_t[]){0x5F}, 1, 0},
+ {0x07, (uint8_t[]){0x5F}, 1, 0},
+ {0x08, (uint8_t[]){0x5F}, 1, 0},
+ {0x09, (uint8_t[]){0x57}, 1, 0},
+ {0x0A, (uint8_t[]){0x57}, 1, 0},
+ {0x0B, (uint8_t[]){0x77}, 1, 0},
+ {0x0C, (uint8_t[]){0x77}, 1, 0},
+ {0x0D, (uint8_t[]){0x47}, 1, 0},
+ {0x0E, (uint8_t[]){0x47}, 1, 0},
+ {0x0F, (uint8_t[]){0x45}, 1, 0},
+ {0x10, (uint8_t[]){0x45}, 1, 0},
+ {0x11, (uint8_t[]){0x4B}, 1, 0},
+ {0x12, (uint8_t[]){0x4B}, 1, 0},
+ {0x13, (uint8_t[]){0x49}, 1, 0},
+ {0x14, (uint8_t[]){0x49}, 1, 0},
+ {0x15, (uint8_t[]){0x5F}, 1, 0},
+
+ {0x16, (uint8_t[]){0x41}, 1, 0},
+ {0x17, (uint8_t[]){0x41}, 1, 0},
+ {0x18, (uint8_t[]){0x40}, 1, 0},
+ {0x19, (uint8_t[]){0x40}, 1, 0},
+ {0x1A, (uint8_t[]){0x5E}, 1, 0},
+ {0x1B, (uint8_t[]){0x5E}, 1, 0},
+ {0x1C, (uint8_t[]){0x5F}, 1, 0},
+ {0x1D, (uint8_t[]){0x5F}, 1, 0},
+ {0x1E, (uint8_t[]){0x5F}, 1, 0},
+ {0x1F, (uint8_t[]){0x57}, 1, 0},
+ {0x20, (uint8_t[]){0x57}, 1, 0},
+ {0x21, (uint8_t[]){0x77}, 1, 0},
+ {0x22, (uint8_t[]){0x77}, 1, 0},
+ {0x23, (uint8_t[]){0x46}, 1, 0},
+ {0x24, (uint8_t[]){0x46}, 1, 0},
+ {0x25, (uint8_t[]){0x44}, 1, 0},
+ {0x26, (uint8_t[]){0x44}, 1, 0},
+ {0x27, (uint8_t[]){0x4A}, 1, 0},
+ {0x28, (uint8_t[]){0x4A}, 1, 0},
+ {0x29, (uint8_t[]){0x48}, 1, 0},
+ {0x2A, (uint8_t[]){0x48}, 1, 0},
+ {0x2B, (uint8_t[]){0x5F}, 1, 0},
+
+ {0x2C, (uint8_t[]){0x01}, 1, 0},
+ {0x2D, (uint8_t[]){0x01}, 1, 0},
+ {0x2E, (uint8_t[]){0x00}, 1, 0},
+ {0x2F, (uint8_t[]){0x00}, 1, 0},
+ {0x30, (uint8_t[]){0x1F}, 1, 0},
+ {0x31, (uint8_t[]){0x1F}, 1, 0},
+ {0x32, (uint8_t[]){0x1E}, 1, 0},
+ {0x33, (uint8_t[]){0x1E}, 1, 0},
+ {0x34, (uint8_t[]){0x1F}, 1, 0},
+ {0x35, (uint8_t[]){0x17}, 1, 0},
+ {0x36, (uint8_t[]){0x17}, 1, 0},
+ {0x37, (uint8_t[]){0x37}, 1, 0},
+ {0x38, (uint8_t[]){0x37}, 1, 0},
+ {0x39, (uint8_t[]){0x08}, 1, 0},
+ {0x3A, (uint8_t[]){0x08}, 1, 0},
+ {0x3B, (uint8_t[]){0x0A}, 1, 0},
+ {0x3C, (uint8_t[]){0x0A}, 1, 0},
+ {0x3D, (uint8_t[]){0x04}, 1, 0},
+ {0x3E, (uint8_t[]){0x04}, 1, 0},
+ {0x3F, (uint8_t[]){0x06}, 1, 0},
+ {0x40, (uint8_t[]){0x06}, 1, 0},
+ {0x41, (uint8_t[]){0x1F}, 1, 0},
+
+ {0x42, (uint8_t[]){0x02}, 1, 0},
+ {0x43, (uint8_t[]){0x02}, 1, 0},
+ {0x44, (uint8_t[]){0x00}, 1, 0},
+ {0x45, (uint8_t[]){0x00}, 1, 0},
+ {0x46, (uint8_t[]){0x1F}, 1, 0},
+ {0x47, (uint8_t[]){0x1F}, 1, 0},
+ {0x48, (uint8_t[]){0x1E}, 1, 0},
+ {0x49, (uint8_t[]){0x1E}, 1, 0},
+ {0x4A, (uint8_t[]){0x1F}, 1, 0},
+ {0x4B, (uint8_t[]){0x17}, 1, 0},
+ {0x4C, (uint8_t[]){0x17}, 1, 0},
+ {0x4D, (uint8_t[]){0x37}, 1, 0},
+ {0x4E, (uint8_t[]){0x37}, 1, 0},
+ {0x4F, (uint8_t[]){0x09}, 1, 0},
+ {0x50, (uint8_t[]){0x09}, 1, 0},
+ {0x51, (uint8_t[]){0x0B}, 1, 0},
+ {0x52, (uint8_t[]){0x0B}, 1, 0},
+ {0x53, (uint8_t[]){0x05}, 1, 0},
+ {0x54, (uint8_t[]){0x05}, 1, 0},
+ {0x55, (uint8_t[]){0x07}, 1, 0},
+ {0x56, (uint8_t[]){0x07}, 1, 0},
+ {0x57, (uint8_t[]){0x1F}, 1, 0},
+
+ {0x58, (uint8_t[]){0x40}, 1, 0},
+ {0x5B, (uint8_t[]){0x30}, 1, 0},
+ {0x5C, (uint8_t[]){0x00}, 1, 0},
+ {0x5D, (uint8_t[]){0x34}, 1, 0},
+ {0x5E, (uint8_t[]){0x05}, 1, 0},
+ {0x5F, (uint8_t[]){0x02}, 1, 0},
+ {0x63, (uint8_t[]){0x00}, 1, 0},
+ {0x64, (uint8_t[]){0x6A}, 1, 0},
+ {0x67, (uint8_t[]){0x73}, 1, 0},
+ {0x68, (uint8_t[]){0x07}, 1, 0},
+ {0x69, (uint8_t[]){0x08}, 1, 0},
+ {0x6A, (uint8_t[]){0x6A}, 1, 0},
+ {0x6B, (uint8_t[]){0x08}, 1, 0},
+
+ {0x6C, (uint8_t[]){0x00}, 1, 0},
+ {0x6D, (uint8_t[]){0x00}, 1, 0},
+ {0x6E, (uint8_t[]){0x00}, 1, 0},
+ {0x6F, (uint8_t[]){0x88}, 1, 0},
+
+ {0x75, (uint8_t[]){0xFF}, 1, 0},
+ {0x77, (uint8_t[]){0xDD}, 1, 0},
+ {0x78, (uint8_t[]){0x2C}, 1, 0},
+ {0x79, (uint8_t[]){0x15}, 1, 0},
+ {0x7A, (uint8_t[]){0x17}, 1, 0},
+ {0x7D, (uint8_t[]){0x14}, 1, 0},
+ {0x7E, (uint8_t[]){0x82}, 1, 0},
+
+ {0xE0, (uint8_t[]){0x04}, 1, 0},
+ {0x00, (uint8_t[]){0x0E}, 1, 0},
+ {0x02, (uint8_t[]){0xB3}, 1, 0},
+ {0x09, (uint8_t[]){0x61}, 1, 0},
+ {0x0E, (uint8_t[]){0x48}, 1, 0},
+ {0x37, (uint8_t[]){0x58}, 1, 0}, // ε
¨εΏ
+ {0x2B, (uint8_t[]){0x0F}, 1, 0}, // ε
¨εΏ
+
+ {0xE0, (uint8_t[]){0x00}, 1, 0},
+
+ {0xE6, (uint8_t[]){0x02}, 1, 0},
+ {0xE7, (uint8_t[]){0x0C}, 1, 0},
+
+ {0x11, (uint8_t[]){0x00}, 1, 120},
+
+ {0x29, (uint8_t[]){0x00}, 1, 20},
+};
+
+static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel)
+{
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
+
+ if (jd9365->reset_gpio_num >= 0)
+ {
+ gpio_reset_pin(jd9365->reset_gpio_num);
+ }
+ // Delete MIPI DPI panel
+ jd9365->del(panel);
+
+ ESP_LOGD(TAG, "del jd9365 panel @%p", jd9365);
+ free(jd9365);
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel)
+{
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9365->io;
+ const jd9365_lcd_init_cmd_t *init_cmds = NULL;
+ uint16_t init_cmds_size = 0;
+ uint8_t lane_command = JD9365_DSI_2_LANE;
+ bool is_user_set = true;
+ bool is_cmd_overwritten = false;
+
+ switch (jd9365->lane_num)
+ {
+ case 1:
+ lane_command = JD9365_DSI_1_LANE;
+ break;
+ case 2:
+ lane_command = JD9365_DSI_2_LANE;
+ break;
+ case 3:
+ lane_command = JD9365_DSI_3_LANE;
+ break;
+ case 4:
+ lane_command = JD9365_DSI_4_LANE;
+ break;
+ default:
+ ESP_LOGE(TAG, "Invalid lane number %d", jd9365->lane_num);
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ uint8_t ID[3];
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed");
+ ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]);
+
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_PAGE, (uint8_t[]){JD9365_PAGE_USER}, 1), TAG, "send command failed");
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){
+ jd9365->madctl_val,
+ },
+ 1),
+ TAG, "send command failed");
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]){
+ jd9365->colmod_val,
+ },
+ 1),
+ TAG, "send command failed");
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_DSI_INT0, (uint8_t[]){
+ lane_command,
+ },
+ 1),
+ TAG, "send command failed");
+
+ // vendor specific initialization, it can be different between manufacturers
+ // should consult the LCD supplier for initialization sequence code
+ if (jd9365->init_cmds)
+ {
+ init_cmds = jd9365->init_cmds;
+ init_cmds_size = jd9365->init_cmds_size;
+ }
+ else
+ {
+ init_cmds = vendor_specific_init_default;
+ init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9365_lcd_init_cmd_t);
+ }
+
+ for (int i = 0; i < init_cmds_size; i++)
+ {
+ // Check if the command has been used or conflicts with the internal
+ if (is_user_set && (init_cmds[i].data_bytes > 0))
+ {
+ switch (init_cmds[i].cmd)
+ {
+ case LCD_CMD_MADCTL:
+ is_cmd_overwritten = true;
+ jd9365->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
+ break;
+ case LCD_CMD_COLMOD:
+ is_cmd_overwritten = true;
+ jd9365->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
+ break;
+ default:
+ is_cmd_overwritten = false;
+ break;
+ }
+
+ if (is_cmd_overwritten)
+ {
+ is_cmd_overwritten = false;
+ ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
+ init_cmds[i].cmd);
+ }
+ }
+
+ // Send command
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed");
+ vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
+
+ // Check if the current cmd is the "page set" cmd
+ if ((init_cmds[i].cmd == JD9365_CMD_PAGE) && (init_cmds[i].data_bytes > 0))
+ {
+ is_user_set = (((uint8_t *)init_cmds[i].data)[0] == JD9365_PAGE_USER);
+ }
+ }
+ ESP_LOGD(TAG, "send init commands success");
+
+ ESP_RETURN_ON_ERROR(jd9365->init(panel), TAG, "init MIPI DPI panel failed");
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel)
+{
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9365->io;
+
+ // Perform hardware reset
+ if (jd9365->reset_gpio_num >= 0)
+ {
+ gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(5));
+ gpio_set_level(jd9365->reset_gpio_num, jd9365->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(10));
+ gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(120));
+ }
+ else if (io)
+ { // Perform software reset
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
+ vTaskDelay(pdMS_TO_TICKS(120));
+ }
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
+{
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9365->io;
+ uint8_t command = 0;
+
+ ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
+
+ if (invert_color_data)
+ {
+ command = LCD_CMD_INVON;
+ }
+ else
+ {
+ command = LCD_CMD_INVOFF;
+ }
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
+{
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9365->io;
+ uint8_t madctl_val = jd9365->madctl_val;
+
+ ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
+
+ // Control mirror through LCD command
+ if (mirror_x)
+ {
+ madctl_val |= JD9365_CMD_GS_BIT;
+ }
+ else
+ {
+ madctl_val &= ~JD9365_CMD_GS_BIT;
+ }
+ if (mirror_y)
+ {
+ madctl_val |= JD9365_CMD_SS_BIT;
+ }
+ else
+ {
+ madctl_val &= ~JD9365_CMD_SS_BIT;
+ }
+
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){madctl_val}, 1), TAG, "send command failed");
+ jd9365->madctl_val = madctl_val;
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
+{
+ ESP_LOGW(TAG, "swap_xy is not supported by this panel");
+ return ESP_ERR_NOT_SUPPORTED;
+}
+
+static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
+{
+ ESP_LOGE(TAG, "set_gap is not supported by this panel");
+ return ESP_ERR_NOT_SUPPORTED;
+}
+
+static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
+{
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9365->io;
+ int command = 0;
+
+ if (on_off)
+ {
+ command = LCD_CMD_DISPON;
+ }
+ else
+ {
+ command = LCD_CMD_DISPOFF;
+ }
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
+ return ESP_OK;
+}
+#endif
diff --git a/display/lcd/esp_lcd_jd9365_10_1/idf_component.yml b/display/lcd/esp_lcd_jd9365_10_1/idf_component.yml
new file mode 100644
index 0000000..b76b3fa
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_10_1/idf_component.yml
@@ -0,0 +1,13 @@
+dependencies:
+ cmake_utilities:
+ version: 0.*
+ idf:
+ version: '>=5.3'
+description: "ESP LCD FOR Waveshare 10.1inch DISPLAY JD9365"
+issues: "https://github.com/waveshareteam/Waveshare-ESP32-components/issues"
+
+license: "MIT"
+targets:
+ - esp32p4
+url: "https://github.com/waveshareteam/Waveshare-ESP32-components/tree/master/esp_lcd_jd9365_10_1"
+version: "1.0.0"
diff --git a/display/lcd/esp_lcd_jd9365_10_1/include/esp_lcd_jd9365_10_1.h b/display/lcd/esp_lcd_jd9365_10_1/include/esp_lcd_jd9365_10_1.h
new file mode 100644
index 0000000..ba0b429
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_10_1/include/esp_lcd_jd9365_10_1.h
@@ -0,0 +1,136 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include
+#include "soc/soc_caps.h"
+
+#if SOC_MIPI_DSI_SUPPORTED
+#include "esp_lcd_panel_vendor.h"
+#include "esp_lcd_mipi_dsi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief LCD panel initialization commands.
+ *
+ */
+typedef struct {
+ int cmd; /*
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/i2c.h"
+#include "driver/spi_master.h"
+#include "driver/gpio.h"
+#include "esp_heap_caps.h"
+#include "esp_log.h"
+#include "esp_timer.h"
+#include "esp_lcd_panel_ops.h"
+#include "esp_lcd_panel_io.h"
+#include "esp_lcd_mipi_dsi.h"
+#include "esp_ldo_regulator.h"
+#include "esp_dma_utils.h"
+#include "unity.h"
+#include "unity_test_runner.h"
+#include "unity_test_utils_memory.h"
+
+#include "esp_lcd_jd9365_10_1.h"
+
+#define TEST_LCD_H_RES (800)
+#define TEST_LCD_V_RES (1280)
+#define TEST_LCD_BIT_PER_PIXEL (24)
+#define TEST_PIN_NUM_LCD_RST (-1)
+#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used
+#define TEST_LCD_BK_LIGHT_ON_LEVEL (1)
+#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL
+#define TEST_MIPI_DSI_LANE_NUM (2)
+
+#if TEST_LCD_BIT_PER_PIXEL == 24
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888)
+#elif TEST_LCD_BIT_PER_PIXEL == 18
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666)
+#elif TEST_LCD_BIT_PER_PIXEL == 16
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565)
+#endif
+
+#define TEST_DELAY_TIME_MS (3000)
+
+#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3)
+#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
+
+static char *TAG = "jd9365_test";
+static esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
+static esp_lcd_panel_handle_t panel_handle = NULL;
+static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
+static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
+static SemaphoreHandle_t refresh_finish = NULL;
+
+IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
+{
+ SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx;
+ BaseType_t need_yield = pdFALSE;
+
+ xSemaphoreGiveFromISR(refresh_finish, &need_yield);
+
+ return (need_yield == pdTRUE);
+}
+
+static void test_init_lcd(void)
+{
+#if TEST_PIN_NUM_BK_LIGHT >= 0
+ ESP_LOGI(TAG, "Turn on LCD backlight");
+ gpio_config_t bk_gpio_config = {
+ .mode = GPIO_MODE_OUTPUT,
+ .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT};
+ TEST_ESP_OK(gpio_config(&bk_gpio_config));
+ TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL));
+#endif
+
+ // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
+#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN
+ ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
+ esp_ldo_channel_config_t ldo_mipi_phy_config = {
+ .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN,
+ .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
+ };
+ TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
+#endif
+
+ ESP_LOGI(TAG, "Initialize MIPI DSI bus");
+ esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG();
+ TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
+
+ ESP_LOGI(TAG, "Install panel IO");
+ esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG();
+ TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
+
+ ESP_LOGI(TAG, "Install LCD driver of jd9365");
+ esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT);
+ jd9365_vendor_config_t vendor_config = {
+ .flags = {
+ .use_mipi_interface = 1,
+ },
+ .mipi_config = {
+ .dsi_bus = mipi_dsi_bus,
+ .dpi_config = &dpi_config,
+ .lane_num = TEST_MIPI_DSI_LANE_NUM,
+ },
+ };
+ const esp_lcd_panel_dev_config_t panel_config = {
+ .reset_gpio_num = TEST_PIN_NUM_LCD_RST,
+ .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
+ .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL,
+ .vendor_config = &vendor_config,
+ };
+ TEST_ESP_OK(esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_config, &panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_reset(panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_init(panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true));
+
+ refresh_finish = xSemaphoreCreateBinary();
+ TEST_ASSERT_NOT_NULL(refresh_finish);
+ esp_lcd_dpi_panel_event_callbacks_t cbs = {
+ .on_color_trans_done = test_notify_refresh_ready,
+ };
+ TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish));
+}
+
+
+
+static void test_deinit_lcd(void)
+{
+ TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io));
+ TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus));
+ panel_handle = NULL;
+ mipi_dbi_io = NULL;
+ mipi_dsi_bus = NULL;
+
+ if (ldo_mipi_phy)
+ {
+ TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy));
+ ldo_mipi_phy = NULL;
+ }
+
+ vSemaphoreDelete(refresh_finish);
+ refresh_finish = NULL;
+
+#if TEST_PIN_NUM_BK_LIGHT >= 0
+ TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT));
+#endif
+}
+
+static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res)
+{
+ uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8;
+ uint16_t row_line = v_res / byte_per_pixel / 8;
+ uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA);
+
+ for (int j = 0; j < byte_per_pixel * 8; j++)
+ {
+ for (int i = 0; i < row_line * h_res; i++)
+ {
+ for (int k = 0; k < byte_per_pixel; k++)
+ {
+ color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff;
+ }
+ }
+ TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color));
+ xSemaphoreTake(refresh_finish, portMAX_DELAY);
+ }
+
+ uint16_t color_line = row_line * byte_per_pixel * 8;
+ uint16_t res_line = v_res - color_line;
+ if (res_line)
+ {
+ for (int i = 0; i < res_line * h_res; i++)
+ {
+ for (int k = 0; k < byte_per_pixel; k++)
+ {
+ color[i * byte_per_pixel + k] = 0xff;
+ }
+ }
+ TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color));
+ xSemaphoreTake(refresh_finish, portMAX_DELAY);
+ }
+
+ free(color);
+}
+
+TEST_CASE("test jd9365 to draw pattern with MIPI interface", "[jd9365][draw_pattern]")
+{
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar pattern drawn by hardware");
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL));
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL));
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE));
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+TEST_CASE("test jd9365 to draw color bar with MIPI interface", "[jd9365][draw_color_bar]")
+{
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar drawn by software");
+ test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES);
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+TEST_CASE("test jd9365 to rotate with MIPI interface", "[jd9365][rotate]")
+{
+ esp_err_t ret = ESP_OK;
+
+ uint16_t w = 0;
+ uint16_t h = 0;
+ int64_t t = 0;
+
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Rotate the screen");
+ for (size_t i = 0; i < 8; i++)
+ {
+ if (ret != ESP_ERR_NOT_SUPPORTED)
+ {
+ if (i & 4)
+ {
+ w = TEST_LCD_V_RES;
+ h = TEST_LCD_H_RES;
+ }
+ else
+ {
+ w = TEST_LCD_H_RES;
+ h = TEST_LCD_V_RES;
+ }
+ }
+
+ TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL);
+ ret = esp_lcd_panel_swap_xy(panel_handle, i & 4);
+ TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL);
+
+ ESP_LOGI(TAG, "Rotation: %d", i);
+ t = esp_timer_get_time();
+ test_draw_color_bar(panel_handle, w, h);
+ t = esp_timer_get_time() - t;
+ ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f);
+ vTaskDelay(pdMS_TO_TICKS(1000));
+ }
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case
+#define TEST_MEMORY_LEAK_THRESHOLD (300)
+
+static size_t before_free_8bit;
+static size_t before_free_32bit;
+
+void setUp(void)
+{
+ before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+ before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+}
+
+void tearDown(void)
+{
+ size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+ size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+ unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD);
+ unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD);
+}
+
+void app_main(void)
+{
+ /**
+ * __ ___ ___ _____ __ ____
+ * \ \ / \/ _ \___ / / /_| ___|
+ * \ \/ /\ / (_) ||_ \| '_ \___ \
+ * /\_/ / /_// \__, |__) | (_) |__) |
+ * \___/___,' /_/____/ \___/____/
+ */
+ printf(" __ ___ ___ _____ __ ____\r\n");
+ printf(" \\ \\ / \\/ _ \\___ / / /_| ___|\r\n");
+ printf(" \\ \\/ /\\ / (_) ||_ \\| '_ \\___ \\\r\n");
+ printf("/\\_/ / /_// \\__, |__) | (_) |__) |\r\n");
+ printf("\\___/___,' /_/____/ \\___/____/\r\n");
+ // unity_run_menu();
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar pattern drawn by hardware");
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL));
+ // vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ // TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL));
+ // vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ // TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE));
+
+ // ESP_LOGI(TAG, "Deinitialize LCD device");
+ // test_deinit_lcd();
+}
diff --git a/display/lcd/esp_lcd_jd9365_10_1/test_apps/sdkconfig.defaults b/display/lcd/esp_lcd_jd9365_10_1/test_apps/sdkconfig.defaults
new file mode 100644
index 0000000..521790c
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_10_1/test_apps/sdkconfig.defaults
@@ -0,0 +1,3 @@
+CONFIG_ESP_TASK_WDT_EN=n
+CONFIG_FREERTOS_HZ=1000
+CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
diff --git a/display/lcd/esp_lcd_jd9365_10_1/test_apps/sdkconfig.defaults.esp32p4 b/display/lcd/esp_lcd_jd9365_10_1/test_apps/sdkconfig.defaults.esp32p4
new file mode 100644
index 0000000..4621955
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_10_1/test_apps/sdkconfig.defaults.esp32p4
@@ -0,0 +1,4 @@
+CONFIG_COMPILER_OPTIMIZATION_PERF=y
+CONFIG_SPIRAM=y
+CONFIG_SPIRAM_SPEED_200M=y
+CONFIG_IDF_EXPERIMENTAL_FEATURES=y
diff --git a/display/lcd/esp_lcd_jd9365_8/CHANGELOG.md b/display/lcd/esp_lcd_jd9365_8/CHANGELOG.md
new file mode 100644
index 0000000..53785af
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_8/CHANGELOG.md
@@ -0,0 +1,16 @@
+# ChangeLog
+
+## v1.0.0 -2025-01-03
+
+* Formally release the component, formally maintain the component
+
+## v0.0.4 -2024-11-14
+
+* Fixed low Lane bit rate and other known issues.
+
+## v0.0.1 - 2024-08-16
+
+### Enhancements:
+
+* Adapted by waveshare electronics according to [esp_lcd_jd9365](https://components.espressif.com/components/espressif/esp_lcd_jd9365)
+
diff --git a/display/lcd/esp_lcd_jd9365_8/CMakeLists.txt b/display/lcd/esp_lcd_jd9365_8/CMakeLists.txt
new file mode 100644
index 0000000..06d2544
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_8/CMakeLists.txt
@@ -0,0 +1,4 @@
+idf_component_register(SRCS "esp_lcd_jd9365_8.c" INCLUDE_DIRS "include" PRIV_REQUIRES "driver" REQUIRES "esp_lcd")
+
+include(package_manager)
+cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
diff --git a/display/lcd/esp_lcd_jd9365_8/README.md b/display/lcd/esp_lcd_jd9365_8/README.md
new file mode 100644
index 0000000..fc78fd3
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_8/README.md
@@ -0,0 +1,73 @@
+# Waveshare 8 inch Display with JD9365
+
+[![Component Registry](https://components.espressif.com/components/waveshare/esp_lcd_jd9365_8/badge.svg)](https://components.espressif.com/components/waveshare/esp_lcd_jd9365_8)
+
+Implementation of the waveshare 8 inch JD9365 LCD controller with esp_lcd component.
+
+| LCD controller | Communication interface | Component name | Link to datasheet |
+| :------------: | :---------------------: |:----------------:| :-----------------------------------------------------------------------------------: |
+| JD9365 | MIPI-DSI | esp_lcd_jd9365_8 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/JD9365DA-H3_DS_V0.01_20200819.pdf) |
+
+**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions.
+
+## Add to project
+
+Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/).
+You can add them to your project via `idf.py add-dependancy`, e.g.
+
+```
+ idf.py add-dependency "waveshare/esp_lcd_jd9365_8"
+```
+
+Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).
+
+## Example use
+
+```c
+/**
+ * Uncomment these line if use custom initialization commands.
+ * The array should be declared as static const and positioned outside the function.
+ */
+
+ ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
+ esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
+ esp_ldo_channel_config_t ldo_mipi_phy_config = {
+ .chan_id = 3,
+ .voltage_mv = 2500,
+ };
+ ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
+
+ ESP_LOGI(TAG, "Initialize MIPI DSI bus");
+ esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
+ esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG();
+ ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
+
+ ESP_LOGI(TAG, "Install panel IO");
+ esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
+ esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG();
+ ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
+
+ ESP_LOGI(TAG, "Install JD9365S panel driver");
+ esp_lcd_panel_handle_t panel_handle = NULL;
+ const esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT);
+ jd9365_vendor_config_t vendor_config = {
+ .flags = {
+ .use_mipi_interface = 1,
+ },
+ .mipi_config = {
+ .lane_num = 2,
+ .dsi_bus = mipi_dsi_bus,
+ .dpi_config = &dpi_config,
+ },
+ };
+ const esp_lcd_panel_dev_config_t panel_config = {
+ .reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use
+ .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h`
+ .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24)
+ .vendor_config = &vendor_config,
+ };
+ ESP_ERROR_CHECK(esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_config, &panel_handle));
+ ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
+ ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
+ ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
+```
diff --git a/display/lcd/esp_lcd_jd9365_8/esp_lcd_jd9365_8.c b/display/lcd/esp_lcd_jd9365_8/esp_lcd_jd9365_8.c
new file mode 100644
index 0000000..93b844a
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_8/esp_lcd_jd9365_8.c
@@ -0,0 +1,588 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "soc/soc_caps.h"
+
+#if SOC_MIPI_DSI_SUPPORTED
+#include "esp_check.h"
+#include "esp_log.h"
+#include "esp_lcd_panel_commands.h"
+#include "esp_lcd_panel_interface.h"
+#include "esp_lcd_panel_io.h"
+#include "esp_lcd_mipi_dsi.h"
+#include "esp_lcd_panel_vendor.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/gpio.h"
+#include "esp_lcd_jd9365_8.h"
+
+#define JD9365_CMD_PAGE (0xE0)
+#define JD9365_PAGE_USER (0x00)
+
+#define JD9365_CMD_DSI_INT0 (0x80)
+#define JD9365_DSI_1_LANE (0x00)
+#define JD9365_DSI_2_LANE (0x01)
+#define JD9365_DSI_3_LANE (0x10)
+#define JD9365_DSI_4_LANE (0x11)
+
+#define JD9365_CMD_GS_BIT (1 << 0)
+#define JD9365_CMD_SS_BIT (1 << 1)
+
+typedef struct
+{
+ esp_lcd_panel_io_handle_t io;
+ int reset_gpio_num;
+ uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
+ uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
+ const jd9365_lcd_init_cmd_t *init_cmds;
+ uint16_t init_cmds_size;
+ uint8_t lane_num;
+ struct
+ {
+ unsigned int reset_level : 1;
+ } flags;
+ // To save the original functions of MIPI DPI panel
+ esp_err_t (*del)(esp_lcd_panel_t *panel);
+ esp_err_t (*init)(esp_lcd_panel_t *panel);
+} jd9365_panel_t;
+
+static const char *TAG = "jd9365";
+
+static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel);
+static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel);
+static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel);
+static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
+static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
+static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
+static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
+static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off);
+
+esp_err_t esp_lcd_new_panel_jd9365(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
+ esp_lcd_panel_handle_t *ret_panel)
+{
+ ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_JD9365_8_VER_MAJOR, ESP_LCD_JD9365_8_VER_MINOR,
+ ESP_LCD_JD9365_8_VER_PATCH);
+ ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
+ jd9365_vendor_config_t *vendor_config = (jd9365_vendor_config_t *)panel_dev_config->vendor_config;
+ ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
+ "invalid vendor config");
+
+ esp_err_t ret = ESP_OK;
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)calloc(1, sizeof(jd9365_panel_t));
+ ESP_RETURN_ON_FALSE(jd9365, ESP_ERR_NO_MEM, TAG, "no mem for jd9365 panel");
+
+ if (panel_dev_config->reset_gpio_num >= 0)
+ {
+ gpio_config_t io_conf = {
+ .mode = GPIO_MODE_OUTPUT,
+ .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
+ };
+ ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
+ }
+
+ switch (panel_dev_config->color_space)
+ {
+ case LCD_RGB_ELEMENT_ORDER_RGB:
+ jd9365->madctl_val = 0;
+ break;
+ case LCD_RGB_ELEMENT_ORDER_BGR:
+ jd9365->madctl_val |= LCD_CMD_BGR_BIT;
+ break;
+ default:
+ ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
+ break;
+ }
+
+ switch (panel_dev_config->bits_per_pixel)
+ {
+ case 16: // RGB565
+ jd9365->colmod_val = 0x55;
+ break;
+ case 18: // RGB666
+ jd9365->colmod_val = 0x66;
+ break;
+ case 24: // RGB888
+ jd9365->colmod_val = 0x77;
+ break;
+ default:
+ ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
+ break;
+ }
+
+ jd9365->io = io;
+ jd9365->init_cmds = vendor_config->init_cmds;
+ jd9365->init_cmds_size = vendor_config->init_cmds_size;
+ jd9365->lane_num = vendor_config->mipi_config.lane_num;
+ jd9365->reset_gpio_num = panel_dev_config->reset_gpio_num;
+ jd9365->flags.reset_level = panel_dev_config->flags.reset_active_high;
+
+ // Create MIPI DPI panel
+ esp_lcd_panel_handle_t panel_handle = NULL;
+ ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
+ "create MIPI DPI panel failed");
+ ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
+
+ // Save the original functions of MIPI DPI panel
+ jd9365->del = panel_handle->del;
+ jd9365->init = panel_handle->init;
+ // Overwrite the functions of MIPI DPI panel
+ panel_handle->del = panel_jd9365_del;
+ panel_handle->init = panel_jd9365_init;
+ panel_handle->reset = panel_jd9365_reset;
+ panel_handle->mirror = panel_jd9365_mirror;
+ panel_handle->swap_xy = panel_jd9365_swap_xy;
+ panel_handle->set_gap = panel_jd9365_set_gap;
+ panel_handle->invert_color = panel_jd9365_invert_color;
+ panel_handle->disp_on_off = panel_jd9365_disp_on_off;
+ panel_handle->user_data = jd9365;
+ *ret_panel = panel_handle;
+ ESP_LOGD(TAG, "new jd9365 panel @%p", jd9365);
+
+ return ESP_OK;
+
+err:
+ if (jd9365)
+ {
+ if (panel_dev_config->reset_gpio_num >= 0)
+ {
+ gpio_reset_pin(panel_dev_config->reset_gpio_num);
+ }
+ free(jd9365);
+ }
+ return ret;
+}
+
+static const jd9365_lcd_init_cmd_t vendor_specific_init_default[] = {
+ // {cmd, { data }, data_size, delay_ms}
+ // {0xE0, (uint8_t[]){0x00}, 1, 0},
+ {0xE0, (uint8_t[]){0x00}, 1, 0},
+ {0xE1, (uint8_t[]){0x93}, 1, 0},
+ {0xE2, (uint8_t[]){0x65}, 1, 0},
+ {0xE3, (uint8_t[]){0xF8}, 1, 0},
+ {0x80, (uint8_t[]){0x01}, 1, 0}, // 0X03οΌ4-LANE;0X02οΌ3-LANE;0X01:2-LANE
+
+ {0xE0, (uint8_t[]){0x01}, 1, 0},
+ {0x00, (uint8_t[]){0x00}, 1, 0},
+ {0x01, (uint8_t[]){0x4E}, 1, 0},
+ {0x03, (uint8_t[]){0x00}, 1, 0},
+ {0x04, (uint8_t[]){0x65}, 1, 0},
+
+ {0x0C, (uint8_t[]){0x74}, 1, 0},
+
+ {0x17, (uint8_t[]){0x00}, 1, 0},
+ {0x18, (uint8_t[]){0xB7}, 1, 0},
+ {0x19, (uint8_t[]){0x00}, 1, 0},
+ {0x1A, (uint8_t[]){0x00}, 1, 0},
+ {0x1B, (uint8_t[]){0xB7}, 1, 0},
+ {0x1C, (uint8_t[]){0x00}, 1, 0},
+
+ {0x24, (uint8_t[]){0xFE}, 1, 0},
+
+ {0x37, (uint8_t[]){0x19}, 1, 0},
+
+ {0x38, (uint8_t[]){0x05}, 1, 0},
+ {0x39, (uint8_t[]){0x00}, 1, 0},
+ {0x3A, (uint8_t[]){0x01}, 1, 0},
+ {0x3B, (uint8_t[]){0x01}, 1, 0},
+ {0x3C, (uint8_t[]){0x70}, 1, 0},
+ {0x3D, (uint8_t[]){0xFF}, 1, 0},
+ {0x3E, (uint8_t[]){0xFF}, 1, 0},
+ {0x3F, (uint8_t[]){0xFF}, 1, 0},
+
+ {0x40, (uint8_t[]){0x06}, 1, 0},
+ {0x41, (uint8_t[]){0xA0}, 1, 0},
+ {0x43, (uint8_t[]){0x1E}, 1, 0},
+ {0x44, (uint8_t[]){0x0F}, 1, 0},
+ {0x45, (uint8_t[]){0x28}, 1, 0},
+ {0x4B, (uint8_t[]){0x04}, 1, 0},
+
+ //{0x4A, (uint8_t[]){0x35}, 1, 0},//bist
+
+ {0x55, (uint8_t[]){0x02}, 1, 0},
+ {0x56, (uint8_t[]){0x01}, 1, 0},
+ {0x57, (uint8_t[]){0xA9}, 1, 0},
+ {0x58, (uint8_t[]){0x0A}, 1, 0},
+ {0x59, (uint8_t[]){0x0A}, 1, 0},
+ {0x5A, (uint8_t[]){0x37}, 1, 0},
+ {0x5B, (uint8_t[]){0x19}, 1, 0},
+
+ {0x5D, (uint8_t[]){0x78}, 1, 0},
+ {0x5E, (uint8_t[]){0x63}, 1, 0},
+ {0x5F, (uint8_t[]){0x54}, 1, 0},
+ {0x60, (uint8_t[]){0x49}, 1, 0},
+ {0x61, (uint8_t[]){0x45}, 1, 0},
+ {0x62, (uint8_t[]){0x38}, 1, 0},
+ {0x63, (uint8_t[]){0x3D}, 1, 0},
+ {0x64, (uint8_t[]){0x28}, 1, 0},
+ {0x65, (uint8_t[]){0x43}, 1, 0},
+ {0x66, (uint8_t[]){0x41}, 1, 0},
+ {0x67, (uint8_t[]){0x43}, 1, 0},
+ {0x68, (uint8_t[]){0x62}, 1, 0},
+ {0x69, (uint8_t[]){0x50}, 1, 0},
+ {0x6A, (uint8_t[]){0x57}, 1, 0},
+ {0x6B, (uint8_t[]){0x49}, 1, 0},
+ {0x6C, (uint8_t[]){0x44}, 1, 0},
+ {0x6D, (uint8_t[]){0x37}, 1, 0},
+ {0x6E, (uint8_t[]){0x23}, 1, 0},
+ {0x6F, (uint8_t[]){0x10}, 1, 0},
+ {0x70, (uint8_t[]){0x78}, 1, 0},
+ {0x71, (uint8_t[]){0x63}, 1, 0},
+ {0x72, (uint8_t[]){0x54}, 1, 0},
+ {0x73, (uint8_t[]){0x49}, 1, 0},
+ {0x74, (uint8_t[]){0x45}, 1, 0},
+ {0x75, (uint8_t[]){0x38}, 1, 0},
+ {0x76, (uint8_t[]){0x3D}, 1, 0},
+ {0x77, (uint8_t[]){0x28}, 1, 0},
+ {0x78, (uint8_t[]){0x43}, 1, 0},
+ {0x79, (uint8_t[]){0x41}, 1, 0},
+ {0x7A, (uint8_t[]){0x43}, 1, 0},
+ {0x7B, (uint8_t[]){0x62}, 1, 0},
+ {0x7C, (uint8_t[]){0x50}, 1, 0},
+ {0x7D, (uint8_t[]){0x57}, 1, 0},
+ {0x7E, (uint8_t[]){0x49}, 1, 0},
+ {0x7F, (uint8_t[]){0x44}, 1, 0},
+ {0x80, (uint8_t[]){0x37}, 1, 0},
+ {0x81, (uint8_t[]){0x23}, 1, 0},
+ {0x82, (uint8_t[]){0x10}, 1, 0},
+
+ {0xE0, (uint8_t[]){0x02}, 1, 0},
+ {0x00, (uint8_t[]){0x47}, 1, 0},
+ {0x01, (uint8_t[]){0x47}, 1, 0},
+ {0x02, (uint8_t[]){0x45}, 1, 0},
+ {0x03, (uint8_t[]){0x45}, 1, 0},
+ {0x04, (uint8_t[]){0x4B}, 1, 0},
+ {0x05, (uint8_t[]){0x4B}, 1, 0},
+ {0x06, (uint8_t[]){0x49}, 1, 0},
+ {0x07, (uint8_t[]){0x49}, 1, 0},
+ {0x08, (uint8_t[]){0x41}, 1, 0},
+ {0x09, (uint8_t[]){0x1F}, 1, 0},
+ {0x0A, (uint8_t[]){0x1F}, 1, 0},
+ {0x0B, (uint8_t[]){0x1F}, 1, 0},
+ {0x0C, (uint8_t[]){0x1F}, 1, 0},
+ {0x0D, (uint8_t[]){0x1F}, 1, 0},
+ {0x0E, (uint8_t[]){0x1F}, 1, 0},
+ {0x0F, (uint8_t[]){0x5F}, 1, 0},
+ {0x10, (uint8_t[]){0x5F}, 1, 0},
+ {0x11, (uint8_t[]){0x57}, 1, 0},
+ {0x12, (uint8_t[]){0x77}, 1, 0},
+ {0x13, (uint8_t[]){0x35}, 1, 0},
+ {0x14, (uint8_t[]){0x1F}, 1, 0},
+ {0x15, (uint8_t[]){0x1F}, 1, 0},
+
+ {0x16, (uint8_t[]){0x46}, 1, 0},
+ {0x17, (uint8_t[]){0x46}, 1, 0},
+ {0x18, (uint8_t[]){0x44}, 1, 0},
+ {0x19, (uint8_t[]){0x44}, 1, 0},
+ {0x1A, (uint8_t[]){0x4A}, 1, 0},
+ {0x1B, (uint8_t[]){0x4A}, 1, 0},
+ {0x1C, (uint8_t[]){0x48}, 1, 0},
+ {0x1D, (uint8_t[]){0x48}, 1, 0},
+ {0x1E, (uint8_t[]){0x40}, 1, 0},
+ {0x1F, (uint8_t[]){0x1F}, 1, 0},
+ {0x20, (uint8_t[]){0x1F}, 1, 0},
+ {0x21, (uint8_t[]){0x1F}, 1, 0},
+ {0x22, (uint8_t[]){0x1F}, 1, 0},
+ {0x23, (uint8_t[]){0x1F}, 1, 0},
+ {0x24, (uint8_t[]){0x1F}, 1, 0},
+ {0x25, (uint8_t[]){0x5F}, 1, 0},
+ {0x26, (uint8_t[]){0x5F}, 1, 0},
+ {0x27, (uint8_t[]){0x57}, 1, 0},
+ {0x28, (uint8_t[]){0x77}, 1, 0},
+ {0x29, (uint8_t[]){0x35}, 1, 0},
+ {0x2A, (uint8_t[]){0x1F}, 1, 0},
+ {0x2B, (uint8_t[]){0x1F}, 1, 0},
+
+ {0x58, (uint8_t[]){0x40}, 1, 0},
+ {0x59, (uint8_t[]){0x00}, 1, 0},
+ {0x5A, (uint8_t[]){0x00}, 1, 0},
+ {0x5B, (uint8_t[]){0x10}, 1, 0},
+ {0x5C, (uint8_t[]){0x06}, 1, 0},
+ {0x5D, (uint8_t[]){0x40}, 1, 0},
+ {0x5E, (uint8_t[]){0x01}, 1, 0},
+ {0x5F, (uint8_t[]){0x02}, 1, 0},
+ {0x60, (uint8_t[]){0x30}, 1, 0},
+ {0x61, (uint8_t[]){0x01}, 1, 0},
+ {0x62, (uint8_t[]){0x02}, 1, 0},
+ {0x63, (uint8_t[]){0x03}, 1, 0},
+ {0x64, (uint8_t[]){0x6B}, 1, 0},
+ {0x65, (uint8_t[]){0x05}, 1, 0},
+ {0x66, (uint8_t[]){0x0C}, 1, 0},
+ {0x67, (uint8_t[]){0x73}, 1, 0},
+ {0x68, (uint8_t[]){0x09}, 1, 0},
+ {0x69, (uint8_t[]){0x03}, 1, 0},
+ {0x6A, (uint8_t[]){0x56}, 1, 0},
+ {0x6B, (uint8_t[]){0x08}, 1, 0},
+ {0x6C, (uint8_t[]){0x00}, 1, 0},
+ {0x6D, (uint8_t[]){0x04}, 1, 0},
+ {0x6E, (uint8_t[]){0x04}, 1, 0},
+ {0x6F, (uint8_t[]){0x88}, 1, 0},
+ {0x70, (uint8_t[]){0x00}, 1, 0},
+ {0x71, (uint8_t[]){0x00}, 1, 0},
+ {0x72, (uint8_t[]){0x06}, 1, 0},
+ {0x73, (uint8_t[]){0x7B}, 1, 0},
+ {0x74, (uint8_t[]){0x00}, 1, 0},
+ {0x75, (uint8_t[]){0xF8}, 1, 0},
+ {0x76, (uint8_t[]){0x00}, 1, 0},
+ {0x77, (uint8_t[]){0xD5}, 1, 0},
+ {0x78, (uint8_t[]){0x2E}, 1, 0},
+ {0x79, (uint8_t[]){0x12}, 1, 0},
+ {0x7A, (uint8_t[]){0x03}, 1, 0},
+ {0x7B, (uint8_t[]){0x00}, 1, 0},
+ {0x7C, (uint8_t[]){0x00}, 1, 0},
+ {0x7D, (uint8_t[]){0x03}, 1, 0},
+ {0x7E, (uint8_t[]){0x7B}, 1, 0},
+
+ {0xE0, (uint8_t[]){0x04}, 1, 0},
+ {0x00, (uint8_t[]){0x0E}, 1, 0},
+ {0x02, (uint8_t[]){0xB3}, 1, 0},
+ {0x09, (uint8_t[]){0x60}, 1, 0},
+ {0x0E, (uint8_t[]){0x2A}, 1, 0},
+ {0x36, (uint8_t[]){0x59}, 1, 0},
+ {0x37, (uint8_t[]){0x58}, 1, 0}, // A133
+ {0x2B, (uint8_t[]){0x0F}, 1, 0}, // A133
+
+ {0xE0, (uint8_t[]){0x00}, 1, 0},
+
+ {0x11, (uint8_t[]){0x00}, 1, 120},
+
+ {0x29, (uint8_t[]){0x00}, 1, 20},
+
+ {0x35, (uint8_t[]){0x00}, 1, 0},
+
+};
+
+static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel)
+{
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
+
+ if (jd9365->reset_gpio_num >= 0)
+ {
+ gpio_reset_pin(jd9365->reset_gpio_num);
+ }
+ // Delete MIPI DPI panel
+ jd9365->del(panel);
+
+ ESP_LOGD(TAG, "del jd9365 panel @%p", jd9365);
+ free(jd9365);
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel)
+{
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9365->io;
+ const jd9365_lcd_init_cmd_t *init_cmds = NULL;
+ uint16_t init_cmds_size = 0;
+ uint8_t lane_command = JD9365_DSI_2_LANE;
+ bool is_user_set = true;
+ bool is_cmd_overwritten = false;
+
+ switch (jd9365->lane_num)
+ {
+ case 1:
+ lane_command = JD9365_DSI_1_LANE;
+ break;
+ case 2:
+ lane_command = JD9365_DSI_2_LANE;
+ break;
+ case 3:
+ lane_command = JD9365_DSI_3_LANE;
+ break;
+ case 4:
+ lane_command = JD9365_DSI_4_LANE;
+ break;
+ default:
+ ESP_LOGE(TAG, "Invalid lane number %d", jd9365->lane_num);
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ uint8_t ID[3];
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed");
+ ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]);
+
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_PAGE, (uint8_t[]){JD9365_PAGE_USER}, 1), TAG, "send command failed");
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){
+ jd9365->madctl_val,
+ },
+ 1),
+ TAG, "send command failed");
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]){
+ jd9365->colmod_val,
+ },
+ 1),
+ TAG, "send command failed");
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_DSI_INT0, (uint8_t[]){
+ lane_command,
+ },
+ 1),
+ TAG, "send command failed");
+
+ // vendor specific initialization, it can be different between manufacturers
+ // should consult the LCD supplier for initialization sequence code
+ if (jd9365->init_cmds)
+ {
+ init_cmds = jd9365->init_cmds;
+ init_cmds_size = jd9365->init_cmds_size;
+ }
+ else
+ {
+ init_cmds = vendor_specific_init_default;
+ init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9365_lcd_init_cmd_t);
+ }
+
+ for (int i = 0; i < init_cmds_size; i++)
+ {
+ // Check if the command has been used or conflicts with the internal
+ if (is_user_set && (init_cmds[i].data_bytes > 0))
+ {
+ switch (init_cmds[i].cmd)
+ {
+ case LCD_CMD_MADCTL:
+ is_cmd_overwritten = true;
+ jd9365->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
+ break;
+ case LCD_CMD_COLMOD:
+ is_cmd_overwritten = true;
+ jd9365->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
+ break;
+ default:
+ is_cmd_overwritten = false;
+ break;
+ }
+
+ if (is_cmd_overwritten)
+ {
+ is_cmd_overwritten = false;
+ ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
+ init_cmds[i].cmd);
+ }
+ }
+
+ // Send command
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed");
+ vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
+
+ // Check if the current cmd is the "page set" cmd
+ if ((init_cmds[i].cmd == JD9365_CMD_PAGE) && (init_cmds[i].data_bytes > 0))
+ {
+ is_user_set = (((uint8_t *)init_cmds[i].data)[0] == JD9365_PAGE_USER);
+ }
+ }
+ ESP_LOGD(TAG, "send init commands success");
+
+ ESP_RETURN_ON_ERROR(jd9365->init(panel), TAG, "init MIPI DPI panel failed");
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel)
+{
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9365->io;
+
+ // Perform hardware reset
+ if (jd9365->reset_gpio_num >= 0)
+ {
+ gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(5));
+ gpio_set_level(jd9365->reset_gpio_num, jd9365->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(10));
+ gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level);
+ vTaskDelay(pdMS_TO_TICKS(120));
+ }
+ else if (io)
+ { // Perform software reset
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
+ vTaskDelay(pdMS_TO_TICKS(120));
+ }
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
+{
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9365->io;
+ uint8_t command = 0;
+
+ ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
+
+ if (invert_color_data)
+ {
+ command = LCD_CMD_INVON;
+ }
+ else
+ {
+ command = LCD_CMD_INVOFF;
+ }
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
+{
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9365->io;
+ uint8_t madctl_val = jd9365->madctl_val;
+
+ ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
+
+ // Control mirror through LCD command
+ if (mirror_x)
+ {
+ madctl_val |= JD9365_CMD_GS_BIT;
+ }
+ else
+ {
+ madctl_val &= ~JD9365_CMD_GS_BIT;
+ }
+ if (mirror_y)
+ {
+ madctl_val |= JD9365_CMD_SS_BIT;
+ }
+ else
+ {
+ madctl_val &= ~JD9365_CMD_SS_BIT;
+ }
+
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){madctl_val}, 1), TAG, "send command failed");
+ jd9365->madctl_val = madctl_val;
+
+ return ESP_OK;
+}
+
+static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
+{
+ ESP_LOGW(TAG, "swap_xy is not supported by this panel");
+ return ESP_ERR_NOT_SUPPORTED;
+}
+
+static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
+{
+ ESP_LOGE(TAG, "set_gap is not supported by this panel");
+ return ESP_ERR_NOT_SUPPORTED;
+}
+
+static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
+{
+ jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
+ esp_lcd_panel_io_handle_t io = jd9365->io;
+ int command = 0;
+
+ if (on_off)
+ {
+ command = LCD_CMD_DISPON;
+ }
+ else
+ {
+ command = LCD_CMD_DISPOFF;
+ }
+ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
+ return ESP_OK;
+}
+#endif
diff --git a/display/lcd/esp_lcd_jd9365_8/idf_component.yml b/display/lcd/esp_lcd_jd9365_8/idf_component.yml
new file mode 100644
index 0000000..b02a48f
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_8/idf_component.yml
@@ -0,0 +1,13 @@
+dependencies:
+ cmake_utilities:
+ version: 0.*
+ idf:
+ version: '>=5.3'
+description: "ESP LCD FOR Waveshare 8 inch DISPLAY JD9365"
+issues: "https://github.com/waveshareteam/Waveshare-ESP32-components/issues"
+
+license: "MIT"
+targets:
+ - esp32p4
+url: "https://github.com/waveshareteam/Waveshare-ESP32-components/tree/master/esp_lcd_jd9365_8"
+version: "1.0.0"
diff --git a/display/lcd/esp_lcd_jd9365_8/include/esp_lcd_jd9365_8.h b/display/lcd/esp_lcd_jd9365_8/include/esp_lcd_jd9365_8.h
new file mode 100644
index 0000000..ab5dcf6
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_8/include/esp_lcd_jd9365_8.h
@@ -0,0 +1,136 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include
+#include "soc/soc_caps.h"
+
+#if SOC_MIPI_DSI_SUPPORTED
+#include "esp_lcd_panel_vendor.h"
+#include "esp_lcd_mipi_dsi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief LCD panel initialization commands.
+ *
+ */
+typedef struct {
+ int cmd; /*
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/i2c.h"
+#include "driver/spi_master.h"
+#include "driver/gpio.h"
+#include "esp_heap_caps.h"
+#include "esp_log.h"
+#include "esp_timer.h"
+#include "esp_lcd_panel_ops.h"
+#include "esp_lcd_panel_io.h"
+#include "esp_lcd_mipi_dsi.h"
+#include "esp_ldo_regulator.h"
+#include "esp_dma_utils.h"
+#include "unity.h"
+#include "unity_test_runner.h"
+#include "unity_test_utils_memory.h"
+
+#include "esp_lcd_jd9365_8.h"
+
+#define TEST_LCD_H_RES (800)
+#define TEST_LCD_V_RES (1280)
+#define TEST_LCD_BIT_PER_PIXEL (24)
+#define TEST_PIN_NUM_LCD_RST (-1)
+#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used
+#define TEST_LCD_BK_LIGHT_ON_LEVEL (1)
+#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL
+#define TEST_MIPI_DSI_LANE_NUM (2)
+
+#if TEST_LCD_BIT_PER_PIXEL == 24
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888)
+#elif TEST_LCD_BIT_PER_PIXEL == 18
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666)
+#elif TEST_LCD_BIT_PER_PIXEL == 16
+#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565)
+#endif
+
+#define TEST_DELAY_TIME_MS (3000)
+
+#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3)
+#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
+
+static char *TAG = "jd9365_test";
+static esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
+static esp_lcd_panel_handle_t panel_handle = NULL;
+static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
+static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
+static SemaphoreHandle_t refresh_finish = NULL;
+
+IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
+{
+ SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx;
+ BaseType_t need_yield = pdFALSE;
+
+ xSemaphoreGiveFromISR(refresh_finish, &need_yield);
+
+ return (need_yield == pdTRUE);
+}
+
+static void test_init_lcd(void)
+{
+#if TEST_PIN_NUM_BK_LIGHT >= 0
+ ESP_LOGI(TAG, "Turn on LCD backlight");
+ gpio_config_t bk_gpio_config = {
+ .mode = GPIO_MODE_OUTPUT,
+ .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT};
+ TEST_ESP_OK(gpio_config(&bk_gpio_config));
+ TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL));
+#endif
+
+ // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
+#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN
+ ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
+ esp_ldo_channel_config_t ldo_mipi_phy_config = {
+ .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN,
+ .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
+ };
+ TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
+#endif
+
+ ESP_LOGI(TAG, "Initialize MIPI DSI bus");
+ esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG();
+ TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
+
+ ESP_LOGI(TAG, "Install panel IO");
+ esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG();
+ TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
+
+ ESP_LOGI(TAG, "Install LCD driver of jd9365");
+ esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT);
+ jd9365_vendor_config_t vendor_config = {
+ .flags = {
+ .use_mipi_interface = 1,
+ },
+ .mipi_config = {
+ .dsi_bus = mipi_dsi_bus,
+ .dpi_config = &dpi_config,
+ .lane_num = TEST_MIPI_DSI_LANE_NUM,
+ },
+ };
+ const esp_lcd_panel_dev_config_t panel_config = {
+ .reset_gpio_num = TEST_PIN_NUM_LCD_RST,
+ .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
+ .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL,
+ .vendor_config = &vendor_config,
+ };
+ TEST_ESP_OK(esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_config, &panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_reset(panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_init(panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true));
+
+ refresh_finish = xSemaphoreCreateBinary();
+ TEST_ASSERT_NOT_NULL(refresh_finish);
+ esp_lcd_dpi_panel_event_callbacks_t cbs = {
+ .on_color_trans_done = test_notify_refresh_ready,
+ };
+ TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish));
+}
+
+static void test_deinit_lcd(void)
+{
+ TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
+ TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io));
+ TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus));
+ panel_handle = NULL;
+ mipi_dbi_io = NULL;
+ mipi_dsi_bus = NULL;
+
+ if (ldo_mipi_phy)
+ {
+ TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy));
+ ldo_mipi_phy = NULL;
+ }
+
+ vSemaphoreDelete(refresh_finish);
+ refresh_finish = NULL;
+
+#if TEST_PIN_NUM_BK_LIGHT >= 0
+ TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT));
+#endif
+}
+
+static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res)
+{
+ uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8;
+ uint16_t row_line = v_res / byte_per_pixel / 8;
+ uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA);
+
+ for (int j = 0; j < byte_per_pixel * 8; j++)
+ {
+ for (int i = 0; i < row_line * h_res; i++)
+ {
+ for (int k = 0; k < byte_per_pixel; k++)
+ {
+ color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff;
+ }
+ }
+ TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color));
+ xSemaphoreTake(refresh_finish, portMAX_DELAY);
+ }
+
+ uint16_t color_line = row_line * byte_per_pixel * 8;
+ uint16_t res_line = v_res - color_line;
+ if (res_line)
+ {
+ for (int i = 0; i < res_line * h_res; i++)
+ {
+ for (int k = 0; k < byte_per_pixel; k++)
+ {
+ color[i * byte_per_pixel + k] = 0xff;
+ }
+ }
+ TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color));
+ xSemaphoreTake(refresh_finish, portMAX_DELAY);
+ }
+
+ free(color);
+}
+
+TEST_CASE("test jd9365 to draw pattern with MIPI interface", "[jd9365][draw_pattern]")
+{
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar pattern drawn by hardware");
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL));
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL));
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE));
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+TEST_CASE("test jd9365 to draw color bar with MIPI interface", "[jd9365][draw_color_bar]")
+{
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar drawn by software");
+ test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES);
+ vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+TEST_CASE("test jd9365 to rotate with MIPI interface", "[jd9365][rotate]")
+{
+ esp_err_t ret = ESP_OK;
+
+ uint16_t w = 0;
+ uint16_t h = 0;
+ int64_t t = 0;
+
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Rotate the screen");
+ for (size_t i = 0; i < 8; i++)
+ {
+ if (ret != ESP_ERR_NOT_SUPPORTED)
+ {
+ if (i & 4)
+ {
+ w = TEST_LCD_V_RES;
+ h = TEST_LCD_H_RES;
+ }
+ else
+ {
+ w = TEST_LCD_H_RES;
+ h = TEST_LCD_V_RES;
+ }
+ }
+
+ TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL);
+ ret = esp_lcd_panel_swap_xy(panel_handle, i & 4);
+ TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL);
+
+ ESP_LOGI(TAG, "Rotation: %d", i);
+ t = esp_timer_get_time();
+ test_draw_color_bar(panel_handle, w, h);
+ t = esp_timer_get_time() - t;
+ ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f);
+ vTaskDelay(pdMS_TO_TICKS(1000));
+ }
+
+ ESP_LOGI(TAG, "Deinitialize LCD device");
+ test_deinit_lcd();
+}
+
+// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case
+#define TEST_MEMORY_LEAK_THRESHOLD (300)
+
+static size_t before_free_8bit;
+static size_t before_free_32bit;
+
+void setUp(void)
+{
+ before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+ before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+}
+
+void tearDown(void)
+{
+ size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+ size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+ unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD);
+ unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD);
+}
+
+void app_main(void)
+{
+ /**
+ * __ ___ ___ _____ __ ____
+ * \ \ / \/ _ \___ / / /_| ___|
+ * \ \/ /\ / (_) ||_ \| '_ \___ \
+ * /\_/ / /_// \__, |__) | (_) |__) |
+ * \___/___,' /_/____/ \___/____/
+ */
+ printf(" __ ___ ___ _____ __ ____\r\n");
+ printf(" \\ \\ / \\/ _ \\___ / / /_| ___|\r\n");
+ printf(" \\ \\/ /\\ / (_) ||_ \\| '_ \\___ \\\r\n");
+ printf("/\\_/ / /_// \\__, |__) | (_) |__) |\r\n");
+ printf("\\___/___,' /_/____/ \\___/____/\r\n");
+ ESP_LOGI(TAG, "Initialize LCD device");
+ test_init_lcd();
+
+ ESP_LOGI(TAG, "Show color bar pattern drawn by hardware");
+ TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL));
+ // vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ // TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL));
+ // vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
+ // TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE));
+
+ // ESP_LOGI(TAG, "Deinitialize LCD device");
+ // test_deinit_lcd();
+}
diff --git a/display/lcd/esp_lcd_jd9365_8/test_apps/sdkconfig.defaults b/display/lcd/esp_lcd_jd9365_8/test_apps/sdkconfig.defaults
new file mode 100644
index 0000000..521790c
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_8/test_apps/sdkconfig.defaults
@@ -0,0 +1,3 @@
+CONFIG_ESP_TASK_WDT_EN=n
+CONFIG_FREERTOS_HZ=1000
+CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
diff --git a/display/lcd/esp_lcd_jd9365_8/test_apps/sdkconfig.defaults.esp32p4 b/display/lcd/esp_lcd_jd9365_8/test_apps/sdkconfig.defaults.esp32p4
new file mode 100644
index 0000000..4621955
--- /dev/null
+++ b/display/lcd/esp_lcd_jd9365_8/test_apps/sdkconfig.defaults.esp32p4
@@ -0,0 +1,4 @@
+CONFIG_COMPILER_OPTIMIZATION_PERF=y
+CONFIG_SPIRAM=y
+CONFIG_SPIRAM_SPEED_200M=y
+CONFIG_IDF_EXPERIMENTAL_FEATURES=y