diff --git a/boards/st/stm32n6570_dk/Kconfig.defconfig b/boards/st/stm32n6570_dk/Kconfig.defconfig index 0196c3c0b854..2d792ba9657f 100644 --- a/boards/st/stm32n6570_dk/Kconfig.defconfig +++ b/boards/st/stm32n6570_dk/Kconfig.defconfig @@ -12,4 +12,13 @@ config NET_L2_ETHERNET endif # NETWORKING +if DISPLAY + +# MEMC needs to be enabled in order to store +# display frame buffer to external PSRAM +config MEMC + default y + +endif # DISPLAY + endif # BOARD_STM32N6570_DK diff --git a/boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi b/boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi index a71e2bfc08d1..f2f3316b6293 100644 --- a/boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi +++ b/boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi @@ -6,8 +6,10 @@ #include #include +#include "zephyr/dt-bindings/display/panel.h" #include #include +#include #include "arduino_r3_connector.dtsi" / { @@ -16,6 +18,7 @@ zephyr,shell-uart = &usart1; zephyr,sram = &axisram2; zephyr,canbus = &fdcan1; + zephyr,display = <dc; spi-flash0 = &mx66uw1g45g; }; @@ -74,6 +77,15 @@ status = "okay"; }; +&pll4 { + clocks = <&clk_hsi>; + div-m = <4>; + mul-n = <75>; + div-p1 = <1>; + div-p2 = <1>; + status = "okay"; +}; + &ic1 { pll-src = <1>; ic-div = <2>; @@ -104,6 +116,12 @@ status = "okay"; }; +&ic16 { + pll-src = <4>; + ic-div = <60>; + status = "okay"; +}; + &perck { clocks = <&rcc STM32_SRC_HSI PER_SEL(0)>; status = "okay"; @@ -288,3 +306,42 @@ zephyr_udc0: &usbotg_hs1 { reg = <0x0>; }; }; + +<dc { + clocks = <&rcc STM32_CLOCK(APB5, 1)>, + <&rcc STM32_SRC_IC16 LTDC_SEL(2)>; + pinctrl-0 = <<dc_r0_pg0 <dc_r1_pd9 <dc_r2_pd15 <dc_r3_pb4 + <dc_r4_ph4 <dc_r5_pa15 <dc_r6_pg11 <dc_r7_pd8 + <dc_g0_pg12 <dc_g1_pg1 <dc_g2_pa1 <dc_g3_pa0 + <dc_g4_pb15 <dc_g5_pb12 <dc_g6_pb11 <dc_g7_pg8 + <dc_b0_pg15 <dc_b1_pa7 <dc_b2_pb2 <dc_b3_pg6 + <dc_b4_ph3 <dc_b5_ph6 <dc_b6_pa8 <dc_b7_pa2 + <dc_de_pg13 <dc_clk_pb13 <dc_hsync_pb14 <dc_vsync_pe11>; + pinctrl-names = "default"; + disp-on-gpios = <&gpioq 3 GPIO_ACTIVE_HIGH>; + bl-ctrl-gpios = <&gpioq 6 GPIO_ACTIVE_HIGH>; + + ext-sdram = <&psram>; + + status = "okay"; + + width = <800>; + height = <480>; + pixel-format = ; + display-timings { + compatible = "zephyr,panel-timing"; + de-active = <0>; + pixelclk-active = <0>; + hsync-active = <0>; + vsync-active = <0>; + hsync-len = <4>; + vsync-len = <4>; + hback-porch = <8>; + vback-porch = <8>; + hfront-porch = <8>; + vfront-porch = <8>; + }; + def-back-color-red = <0xFF>; + def-back-color-green = <0xFF>; + def-back-color-blue = <0xFF>; +}; diff --git a/drivers/clock_control/clock_stm32_ll_h7.c b/drivers/clock_control/clock_stm32_ll_h7.c index e99ea44452a9..5d6bcb72fa99 100644 --- a/drivers/clock_control/clock_stm32_ll_h7.c +++ b/drivers/clock_control/clock_stm32_ll_h7.c @@ -406,6 +406,11 @@ static int stm32_clock_control_configure(const struct device *dev, return err; } + if (pclken->enr == NO_SEL) { + /* Domain clock is fixed. Nothing to set. Exit */ + return 0; + } + z_stm32_hsem_lock(CFG_HW_RCC_SEMID, HSEM_LOCK_DEFAULT_RETRY); sys_clear_bits(DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_DT_CLKSEL_REG_GET(pclken->enr), diff --git a/drivers/display/Kconfig.stm32_ltdc b/drivers/display/Kconfig.stm32_ltdc index 135986ca967e..6b74d3d0e794 100644 --- a/drivers/display/Kconfig.stm32_ltdc +++ b/drivers/display/Kconfig.stm32_ltdc @@ -8,6 +8,7 @@ menuconfig STM32_LTDC default y depends on DT_HAS_ST_STM32_LTDC_ENABLED select USE_STM32_HAL_LTDC + select USE_STM32_HAL_RIF if SOC_SERIES_STM32N6X select CACHE_MANAGEMENT if CPU_HAS_DCACHE select PINCTRL help diff --git a/drivers/display/display_stm32_ltdc.c b/drivers/display/display_stm32_ltdc.c index 200cd286eac8..2de41651fd9e 100644 --- a/drivers/display/display_stm32_ltdc.c +++ b/drivers/display/display_stm32_ltdc.c @@ -76,7 +76,8 @@ struct display_stm32_ltdc_config { uint32_t height; struct gpio_dt_spec disp_on_gpio; struct gpio_dt_spec bl_ctrl_gpio; - struct stm32_pclken pclken; + const struct stm32_pclken *pclken; + size_t pclk_len; const struct reset_dt_spec reset; const struct pinctrl_dev_config *pctrl; void (*irq_config_func)(const struct device *dev); @@ -297,11 +298,22 @@ static int stm32_ltdc_display_blanking_on(const struct device *dev) return display_blanking_on(display_dev); } +/* This symbol takes the value 1 if one of the device instances */ +/* is configured in dts with a domain clock */ +#if STM32_DT_INST_DEV_DOMAIN_CLOCK_SUPPORT +#define STM32_LTDC_DOMAIN_CLOCK_SUPPORT 1 +#else +#define STM32_LTDC_DOMAIN_CLOCK_SUPPORT 0 +#endif + static int stm32_ltdc_init(const struct device *dev) { int err; const struct display_stm32_ltdc_config *config = dev->config; struct display_stm32_ltdc_data *data = dev->data; +#if defined(CONFIG_SOC_SERIES_STM32N6X) + RIMC_MasterConfig_t rimc = {0}; +#endif /* Configure and set display on/off GPIO */ if (config->disp_on_gpio.port) { @@ -337,12 +349,23 @@ static int stm32_ltdc_init(const struct device *dev) /* Turn on LTDC peripheral clock */ err = clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), - (clock_control_subsys_t) &config->pclken); + (clock_control_subsys_t) &config->pclken[0]); if (err < 0) { LOG_ERR("Could not enable LTDC peripheral clock"); return err; } + if (IS_ENABLED(STM32_LTDC_DOMAIN_CLOCK_SUPPORT) && (config->pclk_len > 1)) { + /* Enable LTDC clock source */ + err = clock_control_configure(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), + (clock_control_subsys_t) &config->pclken[1], + NULL); + if (err < 0) { + LOG_ERR("Could not configure LTDC peripheral clock"); + return err; + } + } + #if defined(CONFIG_SOC_SERIES_STM32F4X) LL_RCC_PLLSAI_Disable(); LL_RCC_PLLSAI_ConfigDomain_LTDC(LL_RCC_PLLSOURCE_HSE, @@ -416,6 +439,15 @@ static int stm32_ltdc_init(const struct device *dev) return err; } +#if defined(CONFIG_SOC_SERIES_STM32N6X) + /* Configure RIF for LTDC layer 1 */ + rimc.MasterCID = RIF_CID_1; + rimc.SecPriv = RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV; + HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_LTDC1 , &rimc); + HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_LTDCL1, + RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV); +#endif + /* Disable layer 2, since it not used */ __HAL_LTDC_LAYER_DISABLE(&data->hltdc, LTDC_LAYER_2); @@ -455,7 +487,7 @@ static int stm32_ltdc_suspend(const struct device *dev) /* Turn off LTDC peripheral clock */ err = clock_control_off(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), - (clock_control_subsys_t) &config->pclken); + (clock_control_subsys_t) &config->pclken[0]); return err; } @@ -501,8 +533,10 @@ static DEVICE_API(display, stm32_ltdc_display_api) = { #define FRAME_BUFFER_SECTION __stm32_sdram1_section #elif DT_SAME_NODE(DT_INST_PHANDLE(0, ext_sdram), DT_NODELABEL(sdram2)) #define FRAME_BUFFER_SECTION __stm32_sdram2_section +#elif DT_SAME_NODE(DT_INST_PHANDLE(0, ext_sdram), DT_NODELABEL(psram)) +#define FRAME_BUFFER_SECTION __stm32_psram_section #else -#error "LTDC ext-sdram property in device tree does not reference SDRAM1 or SDRAM2 node" +#error "LTDC ext-sdram property in device tree does not reference SDRAM1 or SDRAM2 node or PSRAM node" #define FRAME_BUFFER_SECTION #endif /* DT_SAME_NODE(DT_INST_PHANDLE(0, ext_sdram), DT_NODELABEL(sdram1)) */ @@ -632,6 +666,9 @@ static DEVICE_API(display, stm32_ltdc_display_api) = { }, \ }, \ }; \ + static const struct stm32_pclken pclken_##inst[] = \ + STM32_DT_INST_CLOCKS(inst); \ + \ static const struct display_stm32_ltdc_config stm32_ltdc_config_##inst = { \ .width = DT_INST_PROP(inst, width), \ .height = DT_INST_PROP(inst, height), \ @@ -640,10 +677,8 @@ static DEVICE_API(display, stm32_ltdc_display_api) = { .bl_ctrl_gpio = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, bl_ctrl_gpios), \ (GPIO_DT_SPEC_INST_GET(inst, bl_ctrl_gpios)), ({ 0 })), \ .reset = RESET_DT_SPEC_INST_GET(0), \ - .pclken = { \ - .enr = DT_INST_CLOCKS_CELL(inst, bits), \ - .bus = DT_INST_CLOCKS_CELL(inst, bus) \ - }, \ + .pclken = pclken_##inst, \ + .pclk_len = DT_INST_NUM_CLOCKS(inst), \ .pctrl = STM32_LTDC_DEVICE_PINCTRL_GET(inst), \ .irq_config_func = stm32_ltdc_irq_config_func_##inst, \ .display_controller = DEVICE_DT_GET_OR_NULL( \ diff --git a/drivers/memc/CMakeLists.txt b/drivers/memc/CMakeLists.txt index 188a40dfcd6a..afd05f4ecf1b 100644 --- a/drivers/memc/CMakeLists.txt +++ b/drivers/memc/CMakeLists.txt @@ -7,6 +7,7 @@ zephyr_library_sources_ifdef(CONFIG_MEMC_STM32_SDRAM memc_stm32_sdram.c) zephyr_linker_sources_ifdef(CONFIG_MEMC_STM32_SDRAM SECTIONS memc_stm32_sdram.ld) zephyr_library_sources_ifdef(CONFIG_MEMC_STM32_NOR_PSRAM memc_stm32_nor_psram.c) zephyr_library_sources_ifdef(CONFIG_MEMC_STM32_XSPI_PSRAM memc_stm32_xspi_psram.c) +zephyr_linker_sources_ifdef(CONFIG_MEMC_STM32_XSPI_PSRAM SECTIONS memc_stm32_xspi_psram.ld) zephyr_library_sources_ifdef(CONFIG_MEMC_MCUX_FLEXSPI memc_mcux_flexspi.c) zephyr_library_sources_ifdef(CONFIG_MEMC_MCUX_FLEXSPI_W956A8MBYA memc_mcux_flexspi_w956a8mbya.c) diff --git a/drivers/memc/memc_stm32_xspi_psram.ld b/drivers/memc/memc_stm32_xspi_psram.ld new file mode 100644 index 000000000000..6ef15da28415 --- /dev/null +++ b/drivers/memc/memc_stm32_xspi_psram.ld @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ +#if DT_NODE_HAS_STATUS(DT_NODELABEL(psram), okay) +GROUP_START(PSRAM) + + SECTION_PROLOGUE(_STM32_PSRAM_SECTION_NAME, (NOLOAD),) + { + *(.stm32_psram) + *(".stm32_psram.*") + } GROUP_LINK_IN(PSRAM) + +GROUP_END(PSRAM) +#endif diff --git a/dts/arm/st/n6/stm32n6.dtsi b/dts/arm/st/n6/stm32n6.dtsi index d1a31635dff2..7a59ea838690 100644 --- a/dts/arm/st/n6/stm32n6.dtsi +++ b/dts/arm/st/n6/stm32n6.dtsi @@ -731,6 +731,17 @@ clocks = <&rcc STM32_CLOCK(AHB5, 27)>; #phy-cells = <0>; }; + + ltdc: ltdc@58001000 { + compatible = "st,stm32-ltdc"; + reg = <0x58001000 0x1000>; + interrupts = <193 0>, <194 0>; + interrupt-names = "ltdc", "ltdc_er"; + clocks = <&rcc STM32_CLOCK(APB5, 1)>, + <&rcc STM32_SRC_PCLK5 LTDC_SEL(0)>; + resets = <&rctl STM32_RESET(APB5, 1)>; + status = "disabled"; + }; }; }; diff --git a/include/zephyr/linker/section_tags.h b/include/zephyr/linker/section_tags.h index 560d7611b7ae..ab73c0445016 100644 --- a/include/zephyr/linker/section_tags.h +++ b/include/zephyr/linker/section_tags.h @@ -47,6 +47,7 @@ #define __imx_boot_container_section Z_GENERIC_SECTION(_IMX_BOOT_CONTAINER_SECTION_NAME) #define __stm32_sdram1_section Z_GENERIC_SECTION(_STM32_SDRAM1_SECTION_NAME) #define __stm32_sdram2_section Z_GENERIC_SECTION(_STM32_SDRAM2_SECTION_NAME) +#define __stm32_psram_section Z_GENERIC_SECTION(_STM32_PSRAM_SECTION_NAME) #define __stm32_backup_sram_section Z_GENERIC_SECTION(_STM32_BACKUP_SRAM_SECTION_NAME) #endif /* CONFIG_ARM */ diff --git a/include/zephyr/linker/sections.h b/include/zephyr/linker/sections.h index 36159427fa67..dc6fce72e6be 100644 --- a/include/zephyr/linker/sections.h +++ b/include/zephyr/linker/sections.h @@ -71,6 +71,7 @@ #define _STM32_SDRAM1_SECTION_NAME .stm32_sdram1 #define _STM32_SDRAM2_SECTION_NAME .stm32_sdram2 +#define _STM32_PSRAM_SECTION_NAME .stm32_psram #define _STM32_BACKUP_SRAM_SECTION_NAME .stm32_backup_sram diff --git a/samples/drivers/display/boards/stm32n6570_dk.conf b/samples/drivers/display/boards/stm32n6570_dk.conf new file mode 100644 index 000000000000..89de7acad83c --- /dev/null +++ b/samples/drivers/display/boards/stm32n6570_dk.conf @@ -0,0 +1,6 @@ +# Copyright (c) 2025 ST Microelectronics +# SPDX-License-Identifier: Apache-2.0 + +# On STM32N6 DK board, display being larger, it requires +# more memory to draw squares on the display (40K) +CONFIG_HEAP_MEM_POOL_SIZE=65536 diff --git a/samples/drivers/display/boards/stm32n6570_dk_stm32n657xx_sb.conf b/samples/drivers/display/boards/stm32n6570_dk_stm32n657xx_sb.conf new file mode 100644 index 000000000000..89de7acad83c --- /dev/null +++ b/samples/drivers/display/boards/stm32n6570_dk_stm32n657xx_sb.conf @@ -0,0 +1,6 @@ +# Copyright (c) 2025 ST Microelectronics +# SPDX-License-Identifier: Apache-2.0 + +# On STM32N6 DK board, display being larger, it requires +# more memory to draw squares on the display (40K) +CONFIG_HEAP_MEM_POOL_SIZE=65536