Skip to content

STM32N6: Add display support #88641

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions boards/st/stm32n6570_dk/Kconfig.defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
57 changes: 57 additions & 0 deletions boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

#include <st/n6/stm32n657X0.dtsi>
#include <st/n6/stm32n657x0hxq-pinctrl.dtsi>
#include "zephyr/dt-bindings/display/panel.h"
#include <zephyr/dt-bindings/flash_controller/xspi.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>
#include <zephyr/dt-bindings/video/video-interfaces.h>
#include "arduino_r3_connector.dtsi"

/ {
Expand All @@ -16,6 +18,7 @@
zephyr,shell-uart = &usart1;
zephyr,sram = &axisram2;
zephyr,canbus = &fdcan1;
zephyr,display = &ltdc;
spi-flash0 = &mx66uw1g45g;
};

Expand Down Expand Up @@ -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>;
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -288,3 +306,42 @@ zephyr_udc0: &usbotg_hs1 {
reg = <0x0>;
};
};

&ltdc {
clocks = <&rcc STM32_CLOCK(APB5, 1)>,
<&rcc STM32_SRC_IC16 LTDC_SEL(2)>;
pinctrl-0 = <&ltdc_r0_pg0 &ltdc_r1_pd9 &ltdc_r2_pd15 &ltdc_r3_pb4
&ltdc_r4_ph4 &ltdc_r5_pa15 &ltdc_r6_pg11 &ltdc_r7_pd8
&ltdc_g0_pg12 &ltdc_g1_pg1 &ltdc_g2_pa1 &ltdc_g3_pa0
&ltdc_g4_pb15 &ltdc_g5_pb12 &ltdc_g6_pb11 &ltdc_g7_pg8
&ltdc_b0_pg15 &ltdc_b1_pa7 &ltdc_b2_pb2 &ltdc_b3_pg6
&ltdc_b4_ph3 &ltdc_b5_ph6 &ltdc_b6_pa8 &ltdc_b7_pa2
&ltdc_de_pg13 &ltdc_clk_pb13 &ltdc_hsync_pb14 &ltdc_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 = <PANEL_PIXEL_FORMAT_RGB_565>;
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>;
};
5 changes: 5 additions & 0 deletions drivers/clock_control/clock_stm32_ll_h7.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions drivers/display/Kconfig.stm32_ltdc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
51 changes: 43 additions & 8 deletions drivers/display/display_stm32_ltdc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)) */

Expand Down Expand Up @@ -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), \
Expand All @@ -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( \
Expand Down
1 change: 1 addition & 0 deletions drivers/memc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
16 changes: 16 additions & 0 deletions drivers/memc/memc_stm32_xspi_psram.ld
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions dts/arm/st/n6/stm32n6.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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";
};
};
};

Expand Down
1 change: 1 addition & 0 deletions include/zephyr/linker/section_tags.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down
1 change: 1 addition & 0 deletions include/zephyr/linker/sections.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions samples/drivers/display/boards/stm32n6570_dk.conf
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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