This is an automated email from the ASF dual-hosted git repository. linguini1 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 5434dc9da541e22a286146c39233e0686dc3b31c Author: Filipe Cavalcanti <[email protected]> AuthorDate: Fri Jul 24 12:25:01 2026 -0300 arch/risc-v: MIPI-DSI support for Espressif devices Adds lowerhalf MIPI DSI driver for RISC-V Espressif devices, including KConfig options for LDO and MIPI_DSI. Signed-off-by: Filipe Cavalcanti <[email protected]> --- arch/risc-v/src/common/espressif/CMakeLists.txt | 10 +- arch/risc-v/src/common/espressif/Kconfig | 49 + arch/risc-v/src/common/espressif/Make.defs | 10 +- arch/risc-v/src/common/espressif/esp_mipi_dsi.c | 1716 +++++++++++++++++++++++ arch/risc-v/src/common/espressif/esp_mipi_dsi.h | 231 +++ arch/risc-v/src/esp32p4/hal_esp32p4.cmake | 9 + arch/risc-v/src/esp32p4/hal_esp32p4.mk | 6 + 7 files changed, 2029 insertions(+), 2 deletions(-) diff --git a/arch/risc-v/src/common/espressif/CMakeLists.txt b/arch/risc-v/src/common/espressif/CMakeLists.txt index 6975d861c8f..52f1540e2ca 100644 --- a/arch/risc-v/src/common/espressif/CMakeLists.txt +++ b/arch/risc-v/src/common/espressif/CMakeLists.txt @@ -104,6 +104,14 @@ if(CONFIG_ESP_RMT) endif() endif() +if(CONFIG_ESPRESSIF_LDO) + list(APPEND SRCS esp_ldo.c) +endif() + +if(CONFIG_ESPRESSIF_MIPI_DSI) + list(APPEND SRCS esp_mipi_dsi.c) +endif() + if(CONFIG_ESP_SDM) list(APPEND SRCS esp_sdm.c) endif() @@ -225,7 +233,7 @@ if(DEFINED ENV{ESP_HAL_3RDPARTY_VERSION}) CACHE STRING "ESP HAL 3rdparty version") else() set(ESP_HAL_3RDPARTY_VERSION - 0eb59f7e02a4735f2b9a78797e691b66740fcadb + 8d0a898910084206721a0892ab093021bca1496a CACHE STRING "ESP HAL 3rdparty version") endif() diff --git a/arch/risc-v/src/common/espressif/Kconfig b/arch/risc-v/src/common/espressif/Kconfig index cbdbd04bd04..9f41be4cb2f 100644 --- a/arch/risc-v/src/common/espressif/Kconfig +++ b/arch/risc-v/src/common/espressif/Kconfig @@ -2087,6 +2087,55 @@ config ESPRESSIF_EMAC ---help--- Enable Ethernet MAC support. +config ESPRESSIF_LDO + bool + default n + +menuconfig ESPRESSIF_MIPI_DSI + bool "MIPI DSI Host" + depends on ARCH_CHIP_ESP32P4 + default n + select ESPRESSIF_LDO + select DRIVERS_VIDEO + select MIPI_DSI + ---help--- + Enable the Espressif MIPI-DSI host driver behind NuttX's + generic mipi_dsi_host_ops API. Board- and panel-agnostic: LP + DCS/generic transfers, DPI timing configure, optional DW-GDMA + framebuffer bind (esp_mipi_dsi_bind_framebuffer), and video + start/stop. Panel init sequences, reset/power, and /dev/fb0 + registration belong in board or panel drivers. + + Board code must power VDD_MIPI_DPHY (often on-chip LDO channel + 3 @ 2500 mV) before calling esp_mipi_dsi_initialize(). + +if ESPRESSIF_MIPI_DSI + +config ESPRESSIF_MIPI_DSI_BUS + int "DSI host bus number" + default 0 + range 0 0 + ---help--- + ESP32-P4 has a single MIPI-DSI controller (bus 0). + +config ESPRESSIF_MIPI_DSI_LANES + int "Default number of DSI data lanes" + default 2 + range 1 2 + ---help--- + Default lane count when esp_mipi_dsi_initialize(NULL) is + used. Boards may override via esp_mipi_dsi_bus_config_s. + +config ESPRESSIF_MIPI_DSI_LANE_BITRATE_MBPS + int "Default DSI lane bit rate (Mbps)" + default 1000 + range 80 1500 + ---help--- + Default per-lane HS bit rate for initialize(NULL). Override + per board/panel with esp_mipi_dsi_bus_config_s. + +endif # ESPRESSIF_MIPI_DSI + config ESP_COEX_SW_COEXIST_ENABLE bool "Software WiFi/Bluetooth/IEEE 802.15.4 coexistence" depends on (ESPRESSIF_WIFI && ESPRESSIF_BLE) || \ diff --git a/arch/risc-v/src/common/espressif/Make.defs b/arch/risc-v/src/common/espressif/Make.defs index 6f7acb1b98d..5216b98a9ee 100644 --- a/arch/risc-v/src/common/espressif/Make.defs +++ b/arch/risc-v/src/common/espressif/Make.defs @@ -109,6 +109,14 @@ ifeq ($(CONFIG_ESP_RMT),y) endif endif +ifeq ($(CONFIG_ESPRESSIF_LDO),y) + CHIP_CSRCS += esp_ldo.c +endif + +ifeq ($(CONFIG_ESPRESSIF_MIPI_DSI),y) + CHIP_CSRCS += esp_mipi_dsi.c +endif + ifeq ($(CONFIG_ESP_SDM),y) CHIP_CSRCS += esp_sdm.c endif @@ -235,7 +243,7 @@ endif ESP_HAL_3RDPARTY_REPO = esp-hal-3rdparty ifndef ESP_HAL_3RDPARTY_VERSION - ESP_HAL_3RDPARTY_VERSION = 1e9759ca6fbdd22fcadd7108aaab6f4daca1345f + ESP_HAL_3RDPARTY_VERSION = 8d0a898910084206721a0892ab093021bca1496a endif ifndef ESP_HAL_3RDPARTY_URL diff --git a/arch/risc-v/src/common/espressif/esp_mipi_dsi.c b/arch/risc-v/src/common/espressif/esp_mipi_dsi.c new file mode 100644 index 00000000000..8d3910bb587 --- /dev/null +++ b/arch/risc-v/src/common/espressif/esp_mipi_dsi.c @@ -0,0 +1,1716 @@ +/**************************************************************************** + * arch/risc-v/src/common/espressif/esp_mipi_dsi.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <errno.h> +#include <debug.h> +#include <string.h> +#include <math.h> + +#include <nuttx/arch.h> +#include <nuttx/irq.h> +#include <nuttx/kmalloc.h> +#include <nuttx/mutex.h> +#include <nuttx/spinlock.h> +#include <nuttx/video/mipi_dsi.h> +#include <nuttx/video/mipi_display.h> + +#include "esp_attr.h" +#include "esp_cache.h" +#include "esp_clk_tree.h" +#include "esp_irq.h" +#include "esp_private/periph_ctrl.h" +#include "hal/mipi_dsi_hal.h" +#include "hal/mipi_dsi_ll.h" +#include "hal/mipi_dsi_types.h" +#include "hal/dw_gdma_hal.h" +#include "hal/dw_gdma_ll.h" +#include "hal/dw_gdma_types.h" +#include "hal/lcd_types.h" +#include "hal/config.h" +#include "hal/cache_ll.h" +#include "soc/clk_tree_defs.h" +#include "soc/reg_base.h" +#include "esp_private/esp_clk_tree_common.h" + +#include "esp_mipi_dsi.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MIPI_DSI_TIMEOUT_CLK_MHZ 10 +#define MIPI_DSI_ESCAPE_CLK_MHZ 18 +#define MIPI_DSI_PHY_LOCK_RETRIES 100 + +/* Generic cmd/payload FIFO wait (HAL uses infinite while loops). */ + +#define MIPI_DSI_FIFO_TIMEOUT_MS 100 + +/* DW-GDMA channel used for MEM → DSI bridge pixel streaming (MVP: ch0). */ + +#define ESP_MIPI_DSI_DMA_CHAN 0 + +#ifndef CONFIG_ESPRESSIF_MIPI_DSI_BUS +# define CONFIG_ESPRESSIF_MIPI_DSI_BUS 0 +#endif + +#ifndef CONFIG_ESPRESSIF_MIPI_DSI_LANES +# define CONFIG_ESPRESSIF_MIPI_DSI_LANES 2 +#endif + +#ifndef CONFIG_ESPRESSIF_MIPI_DSI_LANE_BITRATE_MBPS +# define CONFIG_ESPRESSIF_MIPI_DSI_LANE_BITRATE_MBPS 1000 +#endif + +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +# define ESP_MIPI_DSI_NC_ADDR(a) ((uintptr_t)CACHE_LL_L2MEM_NON_CACHE_ADDR(a)) +#else +# define ESP_MIPI_DSI_NC_ADDR(a) ((uintptr_t)(a)) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Driver private state for the single MIPI-DSI host instance. */ + +struct esp_mipi_dsi_priv_s +{ + struct mipi_dsi_host host; /* NuttX MIPI-DSI host (must be first) */ + mipi_dsi_hal_context_t hal; /* Espressif MIPI-DSI HAL context */ + dw_gdma_hal_context_t dw_hal; /* DW-GDMA HAL for FB→bridge streaming */ + mutex_t lock; /* Serialize host / video / transfer */ + spinlock_t dmalock; /* ISR vs video_stop/start */ + + FAR struct mipi_dsi_device *device; /* Attached panel device, or NULL */ + FAR void *fb; /* Bound framebuffer base, or NULL */ + FAR dw_gdma_link_list_item_t *lli; /* Cached LLI (CPU view) */ + FAR dw_gdma_link_list_item_t *lli_nc; /* Non-cacheable LLI alias */ + + size_t fb_size; /* Bound framebuffer size in bytes */ + uint16_t dpi_h_res; /* Active width from DPI config */ + uint16_t dpi_v_res; /* Active height from DPI config */ + soc_module_clk_t phy_pllref_clk_src; + soc_module_clk_t phy_cfg_clk_src; + soc_module_clk_t dpi_clk_src; + + int dma_cpuint; /* CPU IRQ allocated for DW-GDMA */ + uint8_t num_data_lanes; /* Active data lane count */ + uint8_t fb_bpp; /* Bound FB bits per pixel */ + float lane_bit_rate_mbps; /* PHY lane bitrate (Mbps) */ + bool initialized; /* Host registered and ready */ + bool dpi_configured; /* DPI timing programmed */ + bool fb_bound; /* Framebuffer bound to DW-GDMA */ + bool dma_enabled; /* Allow ISR to re-arm DW-GDMA */ + bool video_running; /* Host in HS video mode */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int esp_mipi_dsi_attach(FAR struct mipi_dsi_host *host, + FAR struct mipi_dsi_device *device); +static int esp_mipi_dsi_detach(FAR struct mipi_dsi_host *host, + FAR struct mipi_dsi_device *device); +static ssize_t esp_mipi_dsi_transfer(FAR struct mipi_dsi_host *host, + FAR const struct mipi_dsi_msg *msg); +static void esp_mipi_dsi_set_cmd_speed(FAR mipi_dsi_hal_context_t *hal, + bool use_lpm); +static int esp_mipi_dsi_write_short(FAR mipi_dsi_hal_context_t *hal, + uint8_t vc, mipi_dsi_data_type_t dt, + uint16_t header); +static int esp_mipi_dsi_write_long(FAR mipi_dsi_hal_context_t *hal, + uint8_t vc, mipi_dsi_data_type_t dt, + FAR const uint8_t *buf, uint16_t len); +static int esp_mipi_dsi_read_short(FAR mipi_dsi_hal_context_t *hal, + uint8_t vc, mipi_dsi_data_type_t dt, + uint16_t header, FAR void *rx, + uint16_t rx_len); +static lcd_color_format_t esp_mipi_dsi_map_format(uint8_t format); +static int IRAM_ATTR esp_mipi_dsi_dma_isr(int irq, FAR void *context, + FAR void *arg); +static void IRAM_ATTR esp_mipi_dsi_dma_restart( + FAR struct esp_mipi_dsi_priv_s *priv); +static int esp_mipi_dsi_dma_setup(FAR struct esp_mipi_dsi_priv_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct mipi_dsi_host_ops g_esp_mipi_dsi_ops = +{ + .attach = esp_mipi_dsi_attach, + .detach = esp_mipi_dsi_detach, + .transfer = esp_mipi_dsi_transfer, +}; + +static struct esp_mipi_dsi_priv_s g_esp_mipi_dsi = +{ + .host = + { + .bus = CONFIG_ESPRESSIF_MIPI_DSI_BUS, + .ops = &g_esp_mipi_dsi_ops, + }, + .lock = NXMUTEX_INITIALIZER, + .dmalock = SP_UNLOCKED, + .phy_pllref_clk_src = SOC_MOD_CLK_INVALID, + .phy_cfg_clk_src = SOC_MOD_CLK_INVALID, + .dpi_clk_src = SOC_MOD_CLK_INVALID, + .dma_cpuint = -ENOMEM, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp_mipi_dsi_dma_restart + * + * Description: + * Re-arm a single-item DW-GDMA link list so the same framebuffer keeps + * streaming. + * + * Input Parameters: + * priv - Driver private state + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void IRAM_ATTR esp_mipi_dsi_dma_restart( + FAR struct esp_mipi_dsi_priv_s *priv) +{ + FAR dw_gdma_dev_t *dev = priv->dw_hal.dev; + FAR dw_gdma_link_list_item_t *lli_nc = priv->lli_nc; + + if (dev == NULL || lli_nc == NULL || priv->lli == NULL) + { + return; + } + + /* Re-arm via NC alias (ESP-IDF mipi_dsi_dma_trans_done_cb). */ + + dw_gdma_ll_lli_set_block_markers(lli_nc, false, true, true); + dw_gdma_ll_channel_set_link_list_master_port(dev, ESP_MIPI_DSI_DMA_CHAN, + DW_GDMA_LL_MASTER_PORT_MEMORY); + dw_gdma_ll_channel_set_link_list_head_addr(dev, ESP_MIPI_DSI_DMA_CHAN, + (uint32_t)(uintptr_t)priv->lli); + dw_gdma_ll_channel_enable(dev, ESP_MIPI_DSI_DMA_CHAN, true); +} + +/**************************************************************************** + * Name: esp_mipi_dsi_dma_isr + * + * Description: + * DW-GDMA interrupt handler. On DMA_TFR_DONE, re-arm the link list so + * continuous framebuffer streaming continues while video is enabled. + * + * Input Parameters: + * irq - IRQ number (unused) + * context - Interrupt context (unused) + * arg - Pointer to struct esp_mipi_dsi_priv_s + * + * Returned Value: + * Zero (OK) + * + ****************************************************************************/ + +static int IRAM_ATTR esp_mipi_dsi_dma_isr(int irq, FAR void *context, + FAR void *arg) +{ + FAR struct esp_mipi_dsi_priv_s *priv = + (FAR struct esp_mipi_dsi_priv_s *)arg; + FAR dw_gdma_dev_t *dev; + irqstate_t flags; + uint32_t status; + + UNUSED(irq); + UNUSED(context); + + if (priv == NULL || priv->dw_hal.dev == NULL) + { + return OK; + } + + dev = priv->dw_hal.dev; + flags = spin_lock_irqsave(&priv->dmalock); + status = dw_gdma_ll_channel_get_intr_status(dev, ESP_MIPI_DSI_DMA_CHAN); + dw_gdma_ll_channel_clear_intr(dev, ESP_MIPI_DSI_DMA_CHAN, status); + + if ((status & DW_GDMA_LL_CHANNEL_EVENT_DMA_TFR_DONE) != 0 && + priv->dma_enabled) + { + esp_mipi_dsi_dma_restart(priv); + } + + spin_unlock_irqrestore(&priv->dmalock, flags); + return OK; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_dma_setup + * + * Description: + * One-time DW-GDMA controller + channel-0 bring-up for MEM→DSI bridge + * pixel streaming, including interrupt registration. + * + * Input Parameters: + * priv - Driver private state + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int esp_mipi_dsi_dma_setup(FAR struct esp_mipi_dsi_priv_s *priv) +{ + dw_gdma_hal_config_t hal_cfg; + FAR dw_gdma_dev_t *dev; + int ret; + + if (priv->dw_hal.dev != NULL) + { + return OK; + } + + memset(&hal_cfg, 0, sizeof(hal_cfg)); + + PERIPH_RCC_ATOMIC() + { + dw_gdma_ll_enable_bus_clock(0, true); + dw_gdma_ll_reset_register(0); + } + + dw_gdma_hal_init(&priv->dw_hal, &hal_cfg); + dev = priv->dw_hal.dev; + + dw_gdma_ll_channel_set_trans_flow(dev, ESP_MIPI_DSI_DMA_CHAN, + DW_GDMA_ROLE_MEM, + DW_GDMA_ROLE_PERIPH_DSI, + DW_GDMA_FLOW_CTRL_SELF); + dw_gdma_ll_channel_set_src_multi_block_type(dev, ESP_MIPI_DSI_DMA_CHAN, + DW_GDMA_BLOCK_TRANSFER_LIST); + dw_gdma_ll_channel_set_dst_multi_block_type(dev, ESP_MIPI_DSI_DMA_CHAN, + DW_GDMA_BLOCK_TRANSFER_LIST); + dw_gdma_ll_channel_set_src_handshake_interface(dev, ESP_MIPI_DSI_DMA_CHAN, + DW_GDMA_HANDSHAKE_HW); + dw_gdma_ll_channel_set_dst_handshake_interface(dev, ESP_MIPI_DSI_DMA_CHAN, + DW_GDMA_HANDSHAKE_HW); + dw_gdma_ll_channel_set_dst_handshake_periph(dev, ESP_MIPI_DSI_DMA_CHAN, + DW_GDMA_ROLE_PERIPH_DSI); + dw_gdma_ll_channel_set_priority(dev, ESP_MIPI_DSI_DMA_CHAN, 1); + dw_gdma_ll_channel_set_src_outstanding_limit(dev, ESP_MIPI_DSI_DMA_CHAN, + 5); + dw_gdma_ll_channel_set_dst_outstanding_limit(dev, ESP_MIPI_DSI_DMA_CHAN, + 2); + dw_gdma_ll_channel_enable_intr_generation(dev, ESP_MIPI_DSI_DMA_CHAN, + UINT32_MAX, true); + dw_gdma_ll_channel_enable_intr_propagation( + dev, ESP_MIPI_DSI_DMA_CHAN, DW_GDMA_LL_CHANNEL_EVENT_DMA_TFR_DONE, true); + dw_gdma_ll_channel_clear_intr(dev, ESP_MIPI_DSI_DMA_CHAN, UINT32_MAX); + + if (priv->dma_cpuint < 0) + { + ret = esp_setup_irq(DW_GDMA_INTR_SOURCE, + ESP_IRQ_PRIORITY_DEFAULT, + ESP_IRQ_TRIGGER_LEVEL, + esp_mipi_dsi_dma_isr, priv); + if (ret < 0) + { + verr("esp_mipi_dsi: DW-GDMA IRQ setup failed: %d\n", + ret); + return ret; + } + + priv->dma_cpuint = ret; + up_enable_irq(ESP_IRQ_DW_GDMA); + } + + return OK; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_map_format + * + * Description: + * Map a NuttX MIPI-DSI pixel format to the Espressif LCD color format + * used by the DPI host and bridge. + * + * Input Parameters: + * format - MIPI_DSI_FMT_* value from the panel/board DPI config + * + * Returned Value: + * Corresponding lcd_color_format_t (defaults to RGB565). + * + ****************************************************************************/ + +static lcd_color_format_t esp_mipi_dsi_map_format(uint8_t format) +{ + switch (format) + { + case MIPI_DSI_FMT_RGB888: + case MIPI_DSI_FMT_RGB666: + case MIPI_DSI_FMT_RGB666_PACKED: + return LCD_COLOR_FMT_RGB888; + + case MIPI_DSI_FMT_RGB565: + default: + return LCD_COLOR_FMT_RGB565; + } +} + +/**************************************************************************** + * Name: esp_mipi_dsi_set_cmd_speed + * + * Description: + * Configure generic/DCS packet speed (LP vs HS) for command-mode + * transfers. + * + * Input Parameters: + * hal - MIPI-DSI HAL context + * use_lpm - true for low-power mode; false for high-speed + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void esp_mipi_dsi_set_cmd_speed(FAR mipi_dsi_hal_context_t *hal, + bool use_lpm) +{ + mipi_dsi_ll_trans_speed_mode_t speed = + use_lpm ? MIPI_DSI_LL_TRANS_SPEED_LP : MIPI_DSI_LL_TRANS_SPEED_HS; + + mipi_dsi_host_ll_set_gen_short_wr_speed_mode(hal->host, 0, speed); + mipi_dsi_host_ll_set_gen_short_wr_speed_mode(hal->host, 1, speed); + mipi_dsi_host_ll_set_gen_short_wr_speed_mode(hal->host, 2, speed); + mipi_dsi_host_ll_set_gen_long_wr_speed_mode(hal->host, speed); + mipi_dsi_host_ll_set_gen_short_rd_speed_mode(hal->host, 0, speed); + mipi_dsi_host_ll_set_gen_short_rd_speed_mode(hal->host, 1, speed); + mipi_dsi_host_ll_set_gen_short_rd_speed_mode(hal->host, 2, speed); + mipi_dsi_host_ll_set_dcs_short_wr_speed_mode(hal->host, 0, speed); + mipi_dsi_host_ll_set_dcs_short_wr_speed_mode(hal->host, 1, speed); + mipi_dsi_host_ll_set_dcs_long_wr_speed_mode(hal->host, speed); + mipi_dsi_host_ll_set_dcs_short_rd_speed_mode(hal->host, 0, speed); + mipi_dsi_host_ll_set_mrps_speed_mode(hal->host, speed); +} + +/**************************************************************************** + * Name: esp_mipi_dsi_attach + * + * Description: + * Attach a MIPI-DSI device (panel) to this host. Updates the PHY data + * lane count if the device requests a different lane configuration. + * + * Input Parameters: + * host - MIPI-DSI host instance + * device - Device to attach (must not be NULL) + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure + * (-EINVAL, -EBUSY, or lock failure). + * + ****************************************************************************/ + +static int esp_mipi_dsi_attach(FAR struct mipi_dsi_host *host, + FAR struct mipi_dsi_device *device) +{ + FAR struct esp_mipi_dsi_priv_s *priv = + (FAR struct esp_mipi_dsi_priv_s *)host; + int ret; + + if (device == NULL) + { + return -EINVAL; + } + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + if (priv->device != NULL) + { + nxmutex_unlock(&priv->lock); + return -EBUSY; + } + + if (device->lanes == 0 || device->lanes > MIPI_DSI_LL_MAX_DATA_LANES) + { + nxmutex_unlock(&priv->lock); + return -EINVAL; + } + + priv->device = device; + device->host = host; + + /* Prefer device lane count when attached (panel will set this in Phase 3). + * PHY lane number was already programmed at host init from Kconfig. + */ + + if (device->lanes != priv->num_data_lanes) + { + mipi_dsi_phy_ll_set_data_lane_number(priv->hal.host, device->lanes); + priv->num_data_lanes = device->lanes; + } + + nxmutex_unlock(&priv->lock); + + vinfo("esp_mipi_dsi: attached device '%s' lanes=%u fmt=%u flags=0x%lx\n", + device->name, device->lanes, device->format, + (unsigned long)device->mode_flags); + return OK; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_detach + * + * Description: + * Detach a previously attached MIPI-DSI device from this host. + * + * Input Parameters: + * host - MIPI-DSI host instance + * device - Device to detach (must match the currently attached device) + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int esp_mipi_dsi_detach(FAR struct mipi_dsi_host *host, + FAR struct mipi_dsi_device *device) +{ + FAR struct esp_mipi_dsi_priv_s *priv = + (FAR struct esp_mipi_dsi_priv_s *)host; + int ret; + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + if (priv->device == NULL || priv->device != device) + { + nxmutex_unlock(&priv->lock); + return -EINVAL; + } + + priv->device = NULL; + device->host = NULL; + nxmutex_unlock(&priv->lock); + return OK; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_wait_cmd_not_full + * + * Description: + * Poll until the generic command FIFO has space for a new header, or + * until MIPI_DSI_FIFO_TIMEOUT_MS elapses. + * + * Input Parameters: + * hal - MIPI-DSI HAL context + * what - Context string used in the timeout error log + * + * Returned Value: + * Zero (OK) on success; -ETIMEDOUT if the FIFO stays full. + * + ****************************************************************************/ + +static int esp_mipi_dsi_wait_cmd_not_full(FAR mipi_dsi_hal_context_t *hal, + FAR const char *what) +{ + int i; + + for (i = 0; i < MIPI_DSI_FIFO_TIMEOUT_MS; i++) + { + if (!mipi_dsi_host_ll_gen_is_cmd_fifo_full(hal->host)) + { + return OK; + } + + up_mdelay(1); + } + + verr("esp_mipi_dsi: %s timeout (%d ms)\n", what, MIPI_DSI_FIFO_TIMEOUT_MS); + return -ETIMEDOUT; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_wait_pld_not_full + * + * Description: + * Poll until the generic write payload FIFO has space, or until + * MIPI_DSI_FIFO_TIMEOUT_MS elapses. + * + * Input Parameters: + * hal - MIPI-DSI HAL context + * what - Context string used in the timeout error log + * + * Returned Value: + * Zero (OK) on success; -ETIMEDOUT if the FIFO stays full. + * + ****************************************************************************/ + +static int esp_mipi_dsi_wait_pld_not_full(FAR mipi_dsi_hal_context_t *hal, + FAR const char *what) +{ + int i; + + for (i = 0; i < MIPI_DSI_FIFO_TIMEOUT_MS; i++) + { + if (!mipi_dsi_host_ll_gen_is_write_fifo_full(hal->host)) + { + return OK; + } + + up_mdelay(1); + } + + verr("esp_mipi_dsi: %s timeout (%d ms)\n", what, + MIPI_DSI_FIFO_TIMEOUT_MS); + return -ETIMEDOUT; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_wait_cmd_empty + * + * Description: + * Wait until the generic command FIFO drains. An unanswered ACK/BTA + * leaves the entry pending and would otherwise block forever. + * + * Input Parameters: + * hal - MIPI-DSI HAL context + * what - Context string used in the timeout error log + * + * Returned Value: + * Zero (OK) on success; -ETIMEDOUT if the FIFO does not drain. + * + ****************************************************************************/ + +static int esp_mipi_dsi_wait_cmd_empty(FAR mipi_dsi_hal_context_t *hal, + FAR const char *what) +{ + int i; + + for (i = 0; i < MIPI_DSI_FIFO_TIMEOUT_MS; i++) + { + if (mipi_dsi_host_ll_gen_is_cmd_fifo_empty(hal->host)) + { + return OK; + } + + up_mdelay(1); + } + + verr("esp_mipi_dsi: %s timeout (%d ms)\n", what, + MIPI_DSI_FIFO_TIMEOUT_MS); + return -ETIMEDOUT; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_write_short + * + * Description: + * Transmit a short DCS/generic packet by writing the packet header to + * the generic command FIFO and waiting for it to drain. + * + * Input Parameters: + * hal - MIPI-DSI HAL context + * vc - Virtual channel (0..3) + * dt - MIPI-DSI data type + * header - Two-byte short-packet payload / parameters + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int esp_mipi_dsi_write_short(FAR mipi_dsi_hal_context_t *hal, + uint8_t vc, mipi_dsi_data_type_t dt, + uint16_t header) +{ + uint8_t msb = (header >> 8) & 0xff; + uint8_t lsb = header & 0xff; + int ret; + + ret = esp_mipi_dsi_wait_cmd_not_full(hal, "cmd FIFO full (short)"); + if (ret < 0) + { + return ret; + } + + mipi_dsi_host_ll_gen_set_packet_header(hal->host, vc, dt, msb, lsb); + return esp_mipi_dsi_wait_cmd_empty(hal, "cmd FIFO drain (short)"); +} + +/**************************************************************************** + * Name: esp_mipi_dsi_write_long + * + * Description: + * Transmit a long DCS/generic packet: push the payload into the write + * FIFO in 32-bit words, then write the packet header with the length. + * + * Input Parameters: + * hal - MIPI-DSI HAL context + * vc - Virtual channel (0..3) + * dt - MIPI-DSI data type + * buf - Payload bytes (may be NULL only when len is 0) + * len - Payload length in bytes + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int esp_mipi_dsi_write_long(FAR mipi_dsi_hal_context_t *hal, + uint8_t vc, mipi_dsi_data_type_t dt, + FAR const uint8_t *buf, uint16_t len) +{ + FAR const uint8_t *payload = buf; + uint32_t remain = len; + uint32_t temp; + int ret; + + while (remain >= 4) + { + memcpy(&temp, payload, sizeof(temp)); + ret = esp_mipi_dsi_wait_pld_not_full(hal, + "payload FIFO full (long)"); + if (ret < 0) + { + return ret; + } + + mipi_dsi_host_ll_gen_write_payload_fifo(hal->host, temp); + payload += 4; + remain -= 4; + } + + if (remain > 0) + { + temp = 0; + memcpy(&temp, payload, remain); + ret = esp_mipi_dsi_wait_pld_not_full(hal, + "payload FIFO full (long tail)"); + if (ret < 0) + { + return ret; + } + + mipi_dsi_host_ll_gen_write_payload_fifo(hal->host, temp); + } + + ret = esp_mipi_dsi_wait_cmd_not_full(hal, "cmd FIFO full (long)"); + if (ret < 0) + { + return ret; + } + + mipi_dsi_host_ll_gen_set_packet_header(hal->host, vc, dt, + (len >> 8) & 0xff, len & 0xff); + return esp_mipi_dsi_wait_cmd_empty(hal, "cmd FIFO drain (long)"); +} + +/**************************************************************************** + * Name: esp_mipi_dsi_read_short + * + * Description: + * Perform a short-packet read: set the maximum return packet size, + * enable BTA, issue the read request, and copy response bytes from the + * read FIFO into rx. + * + * Input Parameters: + * hal - MIPI-DSI HAL context + * vc - Virtual channel (0..3) + * dt - MIPI-DSI read data type + * header - Two-byte short-packet parameters for the read request + * rx - Buffer to receive response bytes + * rx_len - Size of rx in bytes + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int esp_mipi_dsi_read_short(FAR mipi_dsi_hal_context_t *hal, + uint8_t vc, mipi_dsi_data_type_t dt, + uint16_t header, FAR void *rx, + uint16_t rx_len) +{ + FAR uint8_t *receive = rx; + uint32_t temp; + uint32_t counter = 0; + int i; + int ret; + + ret = esp_mipi_dsi_write_short(hal, vc, MIPI_DSI_DT_SET_MAXIMUM_RETURN_PKT, + rx_len); + if (ret < 0) + { + return ret; + } + + mipi_dsi_host_ll_enable_video_mode(hal->host, false); + mipi_dsi_host_ll_enable_bta(hal->host, true); + mipi_dsi_host_ll_gen_set_rx_vcid(hal->host, vc); + + ret = esp_mipi_dsi_write_short(hal, vc, dt, header); + if (ret < 0) + { + goto errout_bta; + } + + for (i = 0; i < MIPI_DSI_FIFO_TIMEOUT_MS; i++) + { + if (!mipi_dsi_host_ll_gen_is_read_cmd_busy(hal->host)) + { + break; + } + + up_mdelay(1); + } + + if (i >= MIPI_DSI_FIFO_TIMEOUT_MS) + { + verr("esp_mipi_dsi: read cmd busy timeout\n"); + ret = -ETIMEDOUT; + goto errout_bta; + } + + for (i = 0; i < MIPI_DSI_FIFO_TIMEOUT_MS; i++) + { + if (!mipi_dsi_host_ll_gen_is_read_fifo_empty(hal->host)) + { + break; + } + + up_mdelay(1); + } + + if (i >= MIPI_DSI_FIFO_TIMEOUT_MS) + { + verr("esp_mipi_dsi: read FIFO empty timeout\n"); + ret = -ETIMEDOUT; + goto errout_bta; + } + + while (!mipi_dsi_host_ll_gen_is_read_fifo_empty(hal->host)) + { + temp = mipi_dsi_host_ll_gen_read_payload_fifo(hal->host); + for (i = 0; i < 4; i++) + { + if (counter < rx_len) + { + receive[counter++] = (temp >> (8 * i)) & 0xff; + } + } + } + + ret = OK; + +errout_bta: + mipi_dsi_host_ll_enable_bta(hal->host, false); + return ret; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_transfer + * + * Description: + * LP/HS command-mode transfer for DCS and generic short/long packets. + * Reads temporarily leave video mode (BTA); writes may run during HS + * video blanking when the host is already streaming. + * + * Input Parameters: + * host - MIPI-DSI host instance + * msg - Transfer descriptor (type, channel, buffers, flags) + * + * Returned Value: + * Number of bytes transferred on success (tx_len or rx_len); a + * negated errno value on failure. + * + ****************************************************************************/ + +static ssize_t esp_mipi_dsi_transfer(FAR struct mipi_dsi_host *host, + FAR const struct mipi_dsi_msg *msg) +{ + FAR struct esp_mipi_dsi_priv_s *priv = + (FAR struct esp_mipi_dsi_priv_s *)host; + FAR mipi_dsi_hal_context_t *hal = &priv->hal; + FAR const uint8_t *tx = msg->tx_buf; + bool was_video; + bool is_read; + bool use_lpm; + uint16_t header; + int ret; + + if (msg == NULL) + { + return -EINVAL; + } + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + if (!priv->initialized) + { + nxmutex_unlock(&priv->lock); + return -EAGAIN; + } + + /* Keep HS video running for LP command writes (lp_cmd_en in + * blanking). Only tear down video for reads (need BTA / cmd mode). + */ + + was_video = priv->video_running; + is_read = (msg->rx_len > 0 && msg->rx_buf != NULL); + + if (was_video && is_read) + { + mipi_dsi_brg_ll_enable_dpi_output(hal->bridge, false); + mipi_dsi_host_ll_enable_video_mode(hal->host, false); + } + else if (!was_video) + { + mipi_dsi_host_ll_enable_video_mode(hal->host, false); + } + + use_lpm = (msg->flags & MIPI_DSI_MSG_USE_LPM) != 0; + esp_mipi_dsi_set_cmd_speed(hal, use_lpm); + + /* Only request ACK when the message asks for it. + * An unanswered BTA blocks the generic cmd FIFO. + */ + + mipi_dsi_host_ll_enable_cmd_ack(hal->host, + (msg->flags & MIPI_DSI_MSG_REQ_ACK) != 0); + + if (mipi_dsi_packet_format_is_short(msg->type)) + { + header = 0; + if (msg->tx_len >= 1 && tx != NULL) + { + header |= tx[0]; + } + + if (msg->tx_len >= 2 && tx != NULL) + { + header |= ((uint16_t)tx[1]) << 8; + } + + if (is_read) + { + ret = esp_mipi_dsi_read_short(hal, msg->channel, + (mipi_dsi_data_type_t)msg->type, + header, msg->rx_buf, + (uint16_t)msg->rx_len); + } + else + { + ret = esp_mipi_dsi_write_short(hal, msg->channel, + (mipi_dsi_data_type_t)msg->type, + header); + } + } + else if (mipi_dsi_packet_format_is_long(msg->type)) + { + if (msg->tx_len > 0 && tx == NULL) + { + nxmutex_unlock(&priv->lock); + return -EINVAL; + } + + ret = esp_mipi_dsi_write_long(hal, msg->channel, + (mipi_dsi_data_type_t)msg->type, + tx, (uint16_t)msg->tx_len); + } + else + { + nxmutex_unlock(&priv->lock); + return -ENOTSUP; + } + + if (was_video && is_read) + { + mipi_dsi_host_ll_enable_video_mode(hal->host, true); + mipi_dsi_brg_ll_enable_dpi_output(hal->bridge, true); + mipi_dsi_brg_ll_update_dpi_config(hal->bridge); + } + + nxmutex_unlock(&priv->lock); + + if (ret < 0) + { + return ret; + } + + return is_read ? (ssize_t)msg->rx_len : (ssize_t)msg->tx_len; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_wait_phy_ready + * + * Description: + * Wait for the MIPI PHY PLL to lock and for all configured data lanes + * to enter stop state after bring-up. + * + * Input Parameters: + * priv - Driver private state + * + * Returned Value: + * Zero (OK) on success; -ETIMEDOUT if PLL lock or lane stop-state fails. + * + ****************************************************************************/ + +static int esp_mipi_dsi_wait_phy_ready(FAR struct esp_mipi_dsi_priv_s *priv) +{ + int i; + + for (i = 0; i < MIPI_DSI_PHY_LOCK_RETRIES; i++) + { + if (mipi_dsi_phy_ll_is_pll_locked(priv->hal.host)) + { + break; + } + + up_mdelay(1); + } + + if (i >= MIPI_DSI_PHY_LOCK_RETRIES) + { + verr("esp_mipi_dsi: PHY PLL lock timeout\n"); + return -ETIMEDOUT; + } + + for (i = 0; i < MIPI_DSI_PHY_LOCK_RETRIES; i++) + { + if (mipi_dsi_phy_ll_are_lanes_stopped(priv->hal.host, + priv->num_data_lanes)) + { + return OK; + } + + up_mdelay(1); + } + + verr("esp_mipi_dsi: PHY lanes stop-state timeout\n"); + return -ETIMEDOUT; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_hw_init + * + * Description: + * Clock/reset enable and HAL/PHY bring-up (ESP-IDF esp_lcd_new_dsi_bus + * sequence adapted for NuttX). Leaves the host in command mode with LP + * transfers enabled. + * + * Input Parameters: + * priv - Driver private state (lanes and bitrate must already be set) + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int esp_mipi_dsi_hw_init(FAR struct esp_mipi_dsi_priv_s *priv) +{ + mipi_dsi_hal_config_t hal_config; + mipi_dsi_phy_pllref_clock_source_t phy_clk_src; + uint32_t phy_clk_src_freq_hz = 0; + esp_err_t err; + int bus_id = priv->host.bus; + int ret; + + if (priv->num_data_lanes == 0 || + priv->num_data_lanes > MIPI_DSI_LL_MAX_DATA_LANES) + { + return -EINVAL; + } + + if (priv->lane_bit_rate_mbps < MIPI_DSI_LL_MIN_PHY_MBPS || + priv->lane_bit_rate_mbps > MIPI_DSI_LL_MAX_PHY_MBPS) + { + return -EINVAL; + } + + PERIPH_RCC_ATOMIC() + { + mipi_dsi_ll_enable_bus_clock(bus_id, true); + mipi_dsi_ll_reset_register(bus_id); + } + +#ifdef CONFIG_ESP32P4_SELECTS_REV_LESS_V3 + phy_clk_src = MIPI_DSI_PHY_PLLREF_CLK_SRC_DEFAULT_LEGACY; +#else + phy_clk_src = MIPI_DSI_PHY_PLLREF_CLK_SRC_DEFAULT; +#endif + + err = esp_clk_tree_enable_src((soc_module_clk_t)phy_clk_src, true); + if (err != ESP_OK) + { + verr("esp_mipi_dsi: enable PHY PLL ref clk failed: %d\n", + (int)err); + return -EIO; + } + + priv->phy_pllref_clk_src = (soc_module_clk_t)phy_clk_src; + + err = esp_clk_tree_enable_src( + (soc_module_clk_t)MIPI_DSI_PHY_CFG_CLK_SRC_DEFAULT, true); + if (err != ESP_OK) + { + verr("esp_mipi_dsi: enable PHY cfg clk failed: %d\n", + (int)err); + return -EIO; + } + + priv->phy_cfg_clk_src = + (soc_module_clk_t)MIPI_DSI_PHY_CFG_CLK_SRC_DEFAULT; + + PERIPH_RCC_ATOMIC() + { + mipi_dsi_ll_set_phy_config_clock_source(bus_id, + MIPI_DSI_PHY_CFG_CLK_SRC_DEFAULT); + mipi_dsi_ll_enable_phy_config_clock(bus_id, true); + mipi_dsi_ll_set_phy_pllref_clock_source(bus_id, phy_clk_src); + mipi_dsi_ll_set_phy_pll_ref_clock_div(bus_id, 1); + mipi_dsi_ll_enable_phy_pllref_clock(bus_id, true); + } + + memset(&hal_config, 0, sizeof(hal_config)); + hal_config.bus_id = bus_id; + hal_config.lane_bit_rate_mbps = priv->lane_bit_rate_mbps; + hal_config.num_data_lanes = priv->num_data_lanes; + mipi_dsi_hal_init(&priv->hal, &hal_config); + + err = esp_clk_tree_src_get_freq_hz(phy_clk_src, + ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, + &phy_clk_src_freq_hz); + if (err != ESP_OK || phy_clk_src_freq_hz == 0) + { + verr("esp_mipi_dsi: get PHY clk freq failed: %d\n", + (int)err); + return -EIO; + } + + mipi_dsi_hal_configure_phy_pll(&priv->hal, phy_clk_src_freq_hz, + priv->lane_bit_rate_mbps); + + ret = esp_mipi_dsi_wait_phy_ready(priv); + if (ret < 0) + { + return ret; + } + + mipi_dsi_host_ll_enable_video_mode(priv->hal.host, false); + mipi_dsi_host_ll_set_clock_lane_state(priv->hal.host, + MIPI_DSI_LL_CLOCK_LANE_STATE_AUTO); + mipi_dsi_phy_ll_set_switch_time(priv->hal.host, 50, 104, 46, 128); + + mipi_dsi_host_ll_enable_rx_crc(priv->hal.host, true); + mipi_dsi_host_ll_enable_rx_ecc(priv->hal.host, true); + mipi_dsi_host_ll_enable_tx_eotp(priv->hal.host, true, false); + + mipi_dsi_host_ll_set_timeout_clock_division( + priv->hal.host, + (uint32_t)roundf(priv->lane_bit_rate_mbps / 8.0f / + MIPI_DSI_TIMEOUT_CLK_MHZ)); + mipi_dsi_host_ll_set_escape_clock_division( + priv->hal.host, + (uint32_t)roundf(priv->lane_bit_rate_mbps / 8.0f / + MIPI_DSI_ESCAPE_CLK_MHZ)); + mipi_dsi_host_ll_set_timeout_count(priv->hal.host, 0, 0, 0, 0, 0, 0, 0); + mipi_dsi_phy_ll_set_max_read_time(priv->hal.host, 6000); + mipi_dsi_phy_ll_set_stop_wait_time(priv->hal.host, 0x3f); + + mipi_dsi_host_ll_enable_te_ack(priv->hal.host, false); + + /* Keep ACK off unless a transfer sets MIPI_DSI_MSG_REQ_ACK. An + * unanswered BTA holds the generic cmd FIFO and looks like a boot hang. + */ + + mipi_dsi_host_ll_enable_cmd_ack(priv->hal.host, false); + esp_mipi_dsi_set_cmd_speed(&priv->hal, true); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp_mipi_dsi_initialize + * + * Description: + * Enable clocks/resets for the MIPI-DSI host, PHY and bridge; configure + * the PHY PLL for the selected lane bitrate; leave the controller in + * command mode for LP DCS/generic transfers; and register the host via + * mipi_dsi_host_register(). + * + * Does not power VDD_MIPI_DPHY — board code must enable the MIPI PHY + * supply (e.g. on-chip LDO) before calling this. + * + * Input Parameters: + * cfg - Optional bus config (lanes / bitrate). NULL uses Kconfig defaults. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int esp_mipi_dsi_initialize( + FAR const struct esp_mipi_dsi_bus_config_s *cfg) +{ + FAR struct esp_mipi_dsi_priv_s *priv = &g_esp_mipi_dsi; + int ret; + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + if (priv->initialized) + { + nxmutex_unlock(&priv->lock); + return OK; + } + + /* Board may override Kconfig; zero / NULL means use defaults. */ + + if (cfg != NULL && cfg->num_data_lanes != 0) + { + priv->num_data_lanes = cfg->num_data_lanes; + } + else + { + priv->num_data_lanes = CONFIG_ESPRESSIF_MIPI_DSI_LANES; + } + + if (cfg != NULL && cfg->lane_bit_rate_mbps != 0) + { + priv->lane_bit_rate_mbps = (float)cfg->lane_bit_rate_mbps; + } + else + { + priv->lane_bit_rate_mbps = + (float)CONFIG_ESPRESSIF_MIPI_DSI_LANE_BITRATE_MBPS; + } + + ret = esp_mipi_dsi_hw_init(priv); + if (ret < 0) + { + nxmutex_unlock(&priv->lock); + return ret; + } + + ret = mipi_dsi_host_register(&priv->host); + if (ret < 0) + { + nxmutex_unlock(&priv->lock); + verr("esp_mipi_dsi: host register failed: %d\n", ret); + return ret; + } + + /* Only mark ready after registration succeeds so host_get() / retries + * do not observe a half-initialized host. + */ + + priv->initialized = true; + nxmutex_unlock(&priv->lock); + + vinfo("esp_mipi_dsi: host registered (bus=%d, lanes=%u, " + "%u Mbps/lane)\n", + priv->host.bus, priv->num_data_lanes, + (unsigned int)(priv->lane_bit_rate_mbps + 0.5f)); + return OK; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_host_get + * + * Description: + * Return the registered Espressif MIPI-DSI host, or NULL if not + * initialized. + * + * Input Parameters: + * None + * + * Returned Value: + * Pointer to the registered mipi_dsi_host on success; NULL if + * esp_mipi_dsi_initialize() has not completed successfully. + * + ****************************************************************************/ + +FAR struct mipi_dsi_host *esp_mipi_dsi_host_get(void) +{ + if (!g_esp_mipi_dsi.initialized) + { + return NULL; + } + + return &g_esp_mipi_dsi.host; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_configure_dpi + * + * Description: + * Configure DPI clock divider and host/bridge video timing for later + * HS video mode. Does not start streaming and does not bind a + * framebuffer. + * + * Input Parameters: + * cfg - DPI timing / format (must not be NULL; board/panel supplies this) + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int esp_mipi_dsi_configure_dpi( + FAR const struct esp_mipi_dsi_dpi_config_s *cfg) +{ + FAR struct esp_mipi_dsi_priv_s *priv = &g_esp_mipi_dsi; + FAR mipi_dsi_hal_context_t *hal; + mipi_dsi_dpi_clock_source_t dpi_clk_src; + lcd_color_format_t color_fmt; + uint32_t dpi_clk_src_freq_hz = 0; + uint32_t dpi_div; + float dpi_clk_src_mhz; + esp_err_t err; + int bus_id; + int ret; + + if (cfg == NULL || cfg->h_res == 0 || cfg->v_res == 0 || + cfg->dpi_clock_freq_mhz == 0) + { + return -EINVAL; + } + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + if (!priv->initialized) + { + nxmutex_unlock(&priv->lock); + return -EAGAIN; + } + + hal = &priv->hal; + bus_id = priv->host.bus; + dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT; + color_fmt = esp_mipi_dsi_map_format(cfg->format); + + err = esp_clk_tree_src_get_freq_hz(dpi_clk_src, + ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, + &dpi_clk_src_freq_hz); + if (err != ESP_OK || dpi_clk_src_freq_hz == 0) + { + nxmutex_unlock(&priv->lock); + return -EIO; + } + + dpi_clk_src_mhz = (float)dpi_clk_src_freq_hz / 1000000.0f; + dpi_div = mipi_dsi_hal_host_dpi_calculate_divider( + hal, dpi_clk_src_mhz, (float)cfg->dpi_clock_freq_mhz); + + err = esp_clk_tree_enable_src((soc_module_clk_t)dpi_clk_src, true); + if (err != ESP_OK) + { + nxmutex_unlock(&priv->lock); + return -EIO; + } + + priv->dpi_clk_src = (soc_module_clk_t)dpi_clk_src; + + PERIPH_RCC_ATOMIC() + { + mipi_dsi_ll_set_dpi_clock_source(bus_id, dpi_clk_src); + mipi_dsi_ll_set_dpi_clock_div(bus_id, dpi_div); + mipi_dsi_ll_enable_dpi_clock(bus_id, true); + } + + mipi_dsi_host_ll_dpi_set_vcid(hal->host, cfg->virtual_channel); + mipi_dsi_host_ll_dpi_set_color_coding(hal->host, color_fmt, 0); + mipi_dsi_host_ll_dpi_set_timing_polarity(hal->host, false, false, false, + false, false); + + /* Match ESP-IDF dpi panel defaults. */ + + mipi_dsi_host_ll_dpi_enable_lp_horizontal_timing(hal->host, true, true); + mipi_dsi_host_ll_dpi_enable_lp_vertical_timing(hal->host, true, true, + true, true); + mipi_dsi_host_ll_dpi_enable_lp_command(hal->host, true); + mipi_dsi_host_ll_dpi_enable_frame_ack(hal->host, true); + + mipi_dsi_host_ll_dpi_set_video_burst_type( + hal->host, MIPI_DSI_LL_VIDEO_BURST_WITH_SYNC_PULSES); + mipi_dsi_host_ll_dpi_set_video_packet_pixel_num(hal->host, cfg->h_res); + mipi_dsi_host_ll_dpi_set_trunks_num(hal->host, 0); + mipi_dsi_host_ll_dpi_set_null_packet_size(hal->host, 0); + + mipi_dsi_hal_host_dpi_set_horizontal_timing(hal, cfg->hsync_pulse_width, + cfg->hsync_back_porch, + cfg->h_res, + cfg->hsync_front_porch); + mipi_dsi_hal_host_dpi_set_vertical_timing(hal, cfg->vsync_pulse_width, + cfg->vsync_back_porch, + cfg->v_res, + cfg->vsync_front_porch); + + /* Bridge color/timing; DW-GDMA bind fills pixel_bits / flow later. */ + + mipi_dsi_brg_ll_set_input_color_format(hal->bridge, color_fmt); + mipi_dsi_brg_ll_set_output_color_format(hal->bridge, color_fmt, 0); + mipi_dsi_brg_ll_set_underrun_discard_count(hal->bridge, cfg->h_res); + mipi_dsi_brg_ll_enable(hal->bridge, true); + mipi_dsi_brg_ll_update_dpi_config(hal->bridge); + + priv->dpi_h_res = cfg->h_res; + priv->dpi_v_res = cfg->v_res; + priv->dpi_configured = true; + nxmutex_unlock(&priv->lock); + + vinfo("esp_mipi_dsi: DPI configured %ux%u @ %lu MHz\n", + cfg->h_res, cfg->v_res, (unsigned long)cfg->dpi_clock_freq_mhz); + + return OK; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_bind_framebuffer + * + * Description: + * Bind a RGB framebuffer in memory to the DSI bridge via DW-GDMA so that + * esp_mipi_dsi_video_start() can stream pixels continuously. Call after + * esp_mipi_dsi_configure_dpi(). fb must remain valid while video runs; + * after CPU writes, callers should call + * esp_mipi_dsi_flush_framebuffer() on the dirty region before expecting + * the panel to show new pixels. + * + * Input Parameters: + * fb - Framebuffer base (DMA-capable, typically PSRAM) + * fb_size - Size in bytes (h_res * v_res * bpp / 8) + * h_res - Active width in pixels + * v_res - Active height in lines + * bpp - Bits per pixel (16 for RGB565, 24 for RGB888) + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int esp_mipi_dsi_bind_framebuffer(FAR void *fb, size_t fb_size, + uint16_t h_res, uint16_t v_res, + uint8_t bpp) +{ + FAR struct esp_mipi_dsi_priv_s *priv = &g_esp_mipi_dsi; + FAR dw_gdma_link_list_item_t *lli_nc; + size_t expect; + uint32_t block_items; + int ret; + + if (fb == NULL || fb_size == 0 || h_res == 0 || v_res == 0 || + (bpp != 16 && bpp != 24)) + { + return -EINVAL; + } + + expect = (size_t)h_res * (size_t)v_res * ((size_t)bpp / 8); + if (fb_size < expect) + { + return -EINVAL; + } + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + if (!priv->initialized || !priv->dpi_configured) + { + nxmutex_unlock(&priv->lock); + return -EAGAIN; + } + + if (priv->video_running) + { + nxmutex_unlock(&priv->lock); + return -EBUSY; + } + + ret = esp_mipi_dsi_dma_setup(priv); + if (ret < 0) + { + nxmutex_unlock(&priv->lock); + return ret; + } + + if (priv->lli == NULL) + { + FAR dw_gdma_link_list_item_t *lli; + + lli = kmm_memalign(DW_GDMA_LL_LINK_LIST_ALIGNMENT, + sizeof(dw_gdma_link_list_item_t)); + if (lli == NULL) + { + nxmutex_unlock(&priv->lock); + return -ENOMEM; + } + + memset(lli, 0, sizeof(*lli)); + priv->lli = lli; + priv->lli_nc = + (FAR dw_gdma_link_list_item_t *)ESP_MIPI_DSI_NC_ADDR(lli); + +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + /* HAL dw_gdma_new_link_list: C2M|INVALIDATE once, then only + * touch the LLI via the non-cacheable alias. A later C2M from the + * cached view would overwrite NC writes with stale zeros. + */ + + esp_cache_msync(lli, sizeof(*lli), + ESP_CACHE_MSYNC_FLAG_DIR_C2M | + ESP_CACHE_MSYNC_FLAG_INVALIDATE | + ESP_CACHE_MSYNC_FLAG_UNALIGNED); +#endif + } + + lli_nc = priv->lli_nc; + block_items = (uint32_t)((fb_size * 8) / 64); + + /* Configure LLI via NC alias only: PSRAM FB → DSI bridge FIFO. */ + + dw_gdma_ll_lli_set_src_addr(lli_nc, (uint32_t)(uintptr_t)fb); + dw_gdma_ll_lli_set_dst_addr(lli_nc, MIPI_DSI_BRG_MEM_BASE); + dw_gdma_ll_lli_set_trans_block_size(lli_nc, block_items); + dw_gdma_ll_lli_set_src_master_port(lli_nc, (intptr_t)fb); + dw_gdma_ll_lli_set_dst_master_port(lli_nc, MIPI_DSI_BRG_MEM_BASE); + dw_gdma_ll_lli_set_src_trans_width(lli_nc, DW_GDMA_TRANS_WIDTH_64); + dw_gdma_ll_lli_set_dst_trans_width(lli_nc, DW_GDMA_TRANS_WIDTH_64); + dw_gdma_ll_lli_set_src_burst_items(lli_nc, DW_GDMA_BURST_ITEMS_512); + dw_gdma_ll_lli_set_dst_burst_items(lli_nc, DW_GDMA_BURST_ITEMS_256); + dw_gdma_ll_lli_set_src_burst_mode(lli_nc, DW_GDMA_BURST_MODE_INCREMENT); + dw_gdma_ll_lli_set_dst_burst_mode(lli_nc, DW_GDMA_BURST_MODE_FIXED); + dw_gdma_ll_lli_set_src_burst_len(lli_nc, 16); + dw_gdma_ll_lli_set_dst_burst_len(lli_nc, 16); + dw_gdma_ll_lli_set_link_list_master_port(lli_nc, + DW_GDMA_LL_MASTER_PORT_MEMORY); + dw_gdma_ll_lli_set_next_item_addr(lli_nc, 0); + + dw_gdma_ll_lli_set_block_markers(lli_nc, false, true, true); + + esp_cache_msync(fb, fb_size, + ESP_CACHE_MSYNC_FLAG_DIR_C2M | + ESP_CACHE_MSYNC_FLAG_UNALIGNED); + + /* Bridge expects DW-GDMA as flow controller. */ + + mipi_dsi_brg_ll_set_num_pixel_bits(priv->hal.bridge, + (uint32_t)h_res * v_res * bpp); + mipi_dsi_brg_ll_set_flow_controller(priv->hal.bridge, + MIPI_DSI_LL_FLOW_CONTROLLER_DMA); + mipi_dsi_brg_ll_set_multi_block_number(priv->hal.bridge, 1); + mipi_dsi_brg_ll_set_burst_len(priv->hal.bridge, 256); + mipi_dsi_brg_ll_set_empty_threshold(priv->hal.bridge, 1024 - 256); + mipi_dsi_brg_ll_update_dpi_config(priv->hal.bridge); + + priv->fb = fb; + priv->fb_size = fb_size; + priv->fb_bpp = bpp; + priv->fb_bound = true; + nxmutex_unlock(&priv->lock); + + vinfo("esp_mipi_dsi: FB bound %p %ux%u %u bpp (%zu bytes) via DW-GDMA\n", + fb, h_res, v_res, bpp, fb_size); + + return OK; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_flush_framebuffer + * + * Description: + * Cache write-back (C2M) so DW-GDMA sees CPU stores to the framebuffer. + * Call after CPU writes to a bound FB (full or partial region). + * + * Input Parameters: + * addr - Start of the dirty region (typically within the bound FB) + * len - Length in bytes + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int esp_mipi_dsi_flush_framebuffer(FAR void *addr, size_t len) +{ + esp_err_t err; + + if (addr == NULL || len == 0) + { + return -EINVAL; + } + + err = esp_cache_msync(addr, len, + ESP_CACHE_MSYNC_FLAG_DIR_C2M | + ESP_CACHE_MSYNC_FLAG_UNALIGNED); + return (err == ESP_OK) ? OK : -EIO; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_video_start + * + * Description: + * Switch the host to video mode and enable bridge DPI output. If a + * framebuffer was bound, also starts DW-GDMA streaming. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int esp_mipi_dsi_video_start(void) +{ + FAR struct esp_mipi_dsi_priv_s *priv = &g_esp_mipi_dsi; + int ret; + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + if (!priv->initialized) + { + nxmutex_unlock(&priv->lock); + return -EAGAIN; + } + + if (!priv->dpi_configured) + { + nxmutex_unlock(&priv->lock); + verr("esp_mipi_dsi: call esp_mipi_dsi_configure_dpi() first\n"); + return -EINVAL; + } + + if (priv->fb_bound) + { + irqstate_t flags = spin_lock_irqsave(&priv->dmalock); + + priv->dma_enabled = true; + esp_mipi_dsi_dma_restart(priv); + spin_unlock_irqrestore(&priv->dmalock, flags); + } + + mipi_dsi_host_ll_enable_video_mode(priv->hal.host, true); + mipi_dsi_brg_ll_enable_dpi_output(priv->hal.bridge, true); + mipi_dsi_brg_ll_update_dpi_config(priv->hal.bridge); + mipi_dsi_brg_ll_enable_interrupt(priv->hal.bridge, + MIPI_DSI_BRG_LL_EVENT_UNDERRUN, true); + priv->video_running = true; + nxmutex_unlock(&priv->lock); + + vinfo("esp_mipi_dsi: video mode started%s\n", + priv->fb_bound ? " (DW-GDMA streaming)" : " (no FB bound)"); + return OK; +} + +/**************************************************************************** + * Name: esp_mipi_dsi_video_stop + * + * Description: + * Disable bridge DPI output and return the host to command mode for + * further LP transfers. Stops DW-GDMA if running. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int esp_mipi_dsi_video_stop(void) +{ + FAR struct esp_mipi_dsi_priv_s *priv = &g_esp_mipi_dsi; + irqstate_t flags; + int ret; + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + if (!priv->initialized) + { + nxmutex_unlock(&priv->lock); + return -EAGAIN; + } + + /* Drop the enable flag before touching HW so a concurrent TFR_DONE + * ISR cannot re-arm the channel after we disable it. + */ + + flags = spin_lock_irqsave(&priv->dmalock); + priv->dma_enabled = false; + if (priv->dw_hal.dev != NULL) + { + dw_gdma_ll_channel_enable(priv->dw_hal.dev, ESP_MIPI_DSI_DMA_CHAN, + false); + dw_gdma_ll_channel_clear_intr(priv->dw_hal.dev, + ESP_MIPI_DSI_DMA_CHAN, UINT32_MAX); + } + + spin_unlock_irqrestore(&priv->dmalock, flags); + + mipi_dsi_brg_ll_enable_dpi_output(priv->hal.bridge, false); + mipi_dsi_host_ll_enable_video_mode(priv->hal.host, false); + priv->video_running = false; + nxmutex_unlock(&priv->lock); + return OK; +} diff --git a/arch/risc-v/src/common/espressif/esp_mipi_dsi.h b/arch/risc-v/src/common/espressif/esp_mipi_dsi.h new file mode 100644 index 00000000000..0d61aad0dd3 --- /dev/null +++ b/arch/risc-v/src/common/espressif/esp_mipi_dsi.h @@ -0,0 +1,231 @@ +/**************************************************************************** + * arch/risc-v/src/common/espressif/esp_mipi_dsi.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_COMMON_ESPRESSIF_ESP_MIPI_DSI_H +#define __ARCH_RISCV_SRC_COMMON_ESPRESSIF_ESP_MIPI_DSI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> + +#include <nuttx/video/mipi_dsi.h> + +#ifdef CONFIG_ESPRESSIF_MIPI_DSI + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* SoC bus parameters. Board/panel code may override Kconfig defaults. */ + +struct esp_mipi_dsi_bus_config_s +{ + uint8_t num_data_lanes; /* 1..2; 0 = use CONFIG_ESPRESSIF_MIPI_DSI_LANES */ + uint32_t lane_bit_rate_mbps; /* 0 = use CONFIG_ESPRESSIF_MIPI_DSI_LANE_BITRATE_MBPS */ +}; + +/* DPI / video-mode timing for esp_mipi_dsi_configure_dpi(). + * Filled by board or panel drivers — the arch host has no panel defaults. + */ + +struct esp_mipi_dsi_dpi_config_s +{ + uint16_t h_res; /* Active width (pixels) */ + uint16_t v_res; /* Active height (lines) */ + uint16_t hsync_pulse_width; + uint16_t hsync_back_porch; + uint16_t hsync_front_porch; + uint16_t vsync_pulse_width; + uint16_t vsync_back_porch; + uint16_t vsync_front_porch; + uint32_t dpi_clock_freq_mhz; /* Expected DPI pixel clock (MHz) */ + uint8_t virtual_channel; /* DPI virtual channel (0..3) */ + uint8_t format; /* MIPI_DSI_FMT_RGB565 / RGB888 / ... */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: esp_mipi_dsi_initialize + * + * Description: + * Enable clocks/resets for the MIPI-DSI host, PHY and bridge; configure + * the PHY PLL for the selected lane bitrate; leave the controller in + * command mode for LP DCS/generic transfers; and register the host via + * mipi_dsi_host_register(). + * + * Does not power VDD_MIPI_DPHY — board code must enable the MIPI PHY + * supply (e.g. on-chip LDO) before calling this. + * + * Input Parameters: + * cfg - Optional bus config (lanes / bitrate). NULL uses Kconfig defaults. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int esp_mipi_dsi_initialize( + FAR const struct esp_mipi_dsi_bus_config_s *cfg); + +/**************************************************************************** + * Name: esp_mipi_dsi_host_get + * + * Description: + * Return the registered Espressif MIPI-DSI host, or NULL if not + * initialized. + * + * Input Parameters: + * None + * + * Returned Value: + * Pointer to the registered mipi_dsi_host on success; NULL if + * esp_mipi_dsi_initialize() has not completed successfully. + * + ****************************************************************************/ + +FAR struct mipi_dsi_host *esp_mipi_dsi_host_get(void); + +/**************************************************************************** + * Name: esp_mipi_dsi_configure_dpi + * + * Description: + * Configure DPI clock divider and host/bridge video timing for later + * HS video mode. Does not start streaming and does not bind a + * framebuffer. + * + * Input Parameters: + * cfg - DPI timing / format (must not be NULL; board/panel supplies this) + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int esp_mipi_dsi_configure_dpi( + FAR const struct esp_mipi_dsi_dpi_config_s *cfg); + +/**************************************************************************** + * Name: esp_mipi_dsi_bind_framebuffer + * + * Description: + * Bind a RGB framebuffer in memory to the DSI bridge via DW-GDMA so that + * esp_mipi_dsi_video_start() can stream pixels continuously. Call after + * esp_mipi_dsi_configure_dpi(). fb must remain valid while video runs; + * after CPU writes, callers should call + * esp_mipi_dsi_flush_framebuffer() on the dirty region before expecting + * the panel to show new pixels. + * + * Input Parameters: + * fb - Framebuffer base (DMA-capable, typically PSRAM) + * fb_size - Size in bytes (h_res * v_res * bpp / 8) + * h_res - Active width in pixels + * v_res - Active height in lines + * bpp - Bits per pixel (16 for RGB565, 24 for RGB888) + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int esp_mipi_dsi_bind_framebuffer(FAR void *fb, size_t fb_size, + uint16_t h_res, uint16_t v_res, + uint8_t bpp); + +/**************************************************************************** + * Name: esp_mipi_dsi_flush_framebuffer + * + * Description: + * Cache write-back (C2M) so DW-GDMA sees CPU stores to the framebuffer. + * Call after CPU writes to a bound FB (full or partial region). + * + * Input Parameters: + * addr - Start of the dirty region (typically within the bound FB) + * len - Length in bytes + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int esp_mipi_dsi_flush_framebuffer(FAR void *addr, size_t len); + +/**************************************************************************** + * Name: esp_mipi_dsi_video_start + * + * Description: + * Switch the host to video mode and enable bridge DPI output. If a + * framebuffer was bound, also starts DW-GDMA streaming. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int esp_mipi_dsi_video_start(void); + +/**************************************************************************** + * Name: esp_mipi_dsi_video_stop + * + * Description: + * Disable bridge DPI output and return the host to command mode for + * further LP transfers. Stops DW-GDMA if running. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int esp_mipi_dsi_video_stop(void); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_ESPRESSIF_MIPI_DSI */ +#endif /* __ARCH_RISCV_SRC_COMMON_ESPRESSIF_ESP_MIPI_DSI_H */ diff --git a/arch/risc-v/src/esp32p4/hal_esp32p4.cmake b/arch/risc-v/src/esp32p4/hal_esp32p4.cmake index cc36c66decb..254c684aae8 100644 --- a/arch/risc-v/src/esp32p4/hal_esp32p4.cmake +++ b/arch/risc-v/src/esp32p4/hal_esp32p4.cmake @@ -532,6 +532,15 @@ if(CONFIG_ESPRESSIF_SPIRAM) endif() endif() +if(CONFIG_ESPRESSIF_MIPI_DSI) + list( + APPEND + HAL_SRCS + ${ESP_HAL_3RDPARTY_REPO}/components/esp_hal_lcd/mipi_dsi_hal.c + ${ESP_HAL_3RDPARTY_REPO}/components/esp_hal_lcd/${CHIP_SERIES}/mipi_dsi_periph.c + ${ESP_HAL_3RDPARTY_REPO}/components/esp_hal_dma/dw_gdma_hal.c) +endif() + if(CONFIG_ESPRESSIF_IDF_ENV_FPGA) list(APPEND HAL_SRCS ${ESP_HAL_3RDPARTY_REPO}/components/esp_system/fpga_overrides_clk.c diff --git a/arch/risc-v/src/esp32p4/hal_esp32p4.mk b/arch/risc-v/src/esp32p4/hal_esp32p4.mk index 852b0795e24..0899e2c2fcb 100644 --- a/arch/risc-v/src/esp32p4/hal_esp32p4.mk +++ b/arch/risc-v/src/esp32p4/hal_esp32p4.mk @@ -342,6 +342,12 @@ ifeq ($(CONFIG_SPIRAM_FLASH_LOAD_TO_PSRAM),y) CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)esp_psram$(DELIM)xip_impl$(DELIM)mmu_psram_flash_v2.c endif endif + +ifeq ($(CONFIG_ESPRESSIF_MIPI_DSI),y) + CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)esp_hal_lcd$(DELIM)mipi_dsi_hal.c + CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)esp_hal_lcd$(DELIM)$(CHIP_SERIES)$(DELIM)mipi_dsi_periph.c + CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)esp_hal_dma$(DELIM)dw_gdma_hal.c +endif CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)esp_rom$(DELIM)patches$(DELIM)esp_rom_clic.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)esp_rom$(DELIM)patches$(DELIM)esp_rom_crc.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)esp_rom$(DELIM)patches$(DELIM)esp_rom_efuse.c
