diff --git a/drivers/clock_control/clock_stm32_ll_u5.c b/drivers/clock_control/clock_stm32_ll_u5.c index 35611555e4c98..db9d63bbb0779 100644 --- a/drivers/clock_control/clock_stm32_ll_u5.c +++ b/drivers/clock_control/clock_stm32_ll_u5.c @@ -145,7 +145,8 @@ int enabled_clock(uint32_t src_clk) ((src_clk == STM32_SRC_PLL2_R) && IS_ENABLED(STM32_PLL2_R_ENABLED)) || ((src_clk == STM32_SRC_PLL3_P) && IS_ENABLED(STM32_PLL3_P_ENABLED)) || ((src_clk == STM32_SRC_PLL3_Q) && IS_ENABLED(STM32_PLL3_Q_ENABLED)) || - ((src_clk == STM32_SRC_PLL3_R) && IS_ENABLED(STM32_PLL3_R_ENABLED))) { + ((src_clk == STM32_SRC_PLL3_R) && IS_ENABLED(STM32_PLL3_R_ENABLED)) || + (src_clk == STM32_SRC_DSIPHY)) { return 0; } diff --git a/drivers/mipi_dsi/dsi_stm32.c b/drivers/mipi_dsi/dsi_stm32.c index 0d200862dddbe..9d18425ce9b31 100644 --- a/drivers/mipi_dsi/dsi_stm32.c +++ b/drivers/mipi_dsi/dsi_stm32.c @@ -38,6 +38,7 @@ struct mipi_dsi_stm32_config { const struct device *rcc; const struct reset_dt_spec reset; struct stm32_pclken dsi_clk; + struct stm32_pclken dsisrc_clk; struct stm32_pclken ref_clk; struct stm32_pclken pix_clk; uint32_t data_lanes; @@ -56,6 +57,27 @@ struct mipi_dsi_stm32_data { uint32_t pixel_clk_khz; }; +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32u5_mipi_dsi) +/* Configures DSI PHY as DSI clock source (STM32U5 specific) */ +static int stm32_dsi_clock_source_config(const struct device *dev) +{ + const struct mipi_dsi_stm32_config *config = dev->config; + int ret; + + ret = clock_control_configure(config->rcc, (clock_control_subsys_t)&config->dsisrc_clk, + NULL); + if (ret < 0) { + LOG_ERR("Failed to configure DSI clock source (%d)", ret); + return ret; + } + + LOG_DBG("DSI kernel clock source selection, RCC_CCIPR2_DSIHOSTSEL: %u", + __HAL_RCC_GET_DSI_SOURCE() == RCC_DSICLKSOURCE_DSIPHY); + + return 0; +} +#endif + static void mipi_dsi_stm32_log_config(const struct device *dev) { const struct mipi_dsi_stm32_config *config = dev->config; @@ -66,9 +88,20 @@ static void mipi_dsi_stm32_log_config(const struct device *dev) LOG_DBG(" AutomaticClockLaneControl 0x%x", data->hdsi.Init.AutomaticClockLaneControl); LOG_DBG(" TXEscapeCkdiv %u", data->hdsi.Init.TXEscapeCkdiv); LOG_DBG(" NumberOfLanes %u", data->hdsi.Init.NumberOfLanes); +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32u5_mipi_dsi) + LOG_DBG(" PHYFrequencyRange 0x%x", data->hdsi.Init.PHYFrequencyRange); + LOG_DBG(" PHYLowPowerOffset 0x%x", data->hdsi.Init.PHYLowPowerOffset); +#endif + + LOG_DBG("PLLInit configuration:"); LOG_DBG(" PLLNDIV %u", data->pll_init.PLLNDIV); LOG_DBG(" PLLIDF %u", data->pll_init.PLLIDF); LOG_DBG(" PLLODF %u", data->pll_init.PLLODF); +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32u5_mipi_dsi) + LOG_DBG(" PLLVCORange 0x%x", data->pll_init.PLLVCORange); + LOG_DBG(" PLLChargePump 0x%x", data->pll_init.PLLChargePump); + LOG_DBG(" PLLTuning 0x%x", data->pll_init.PLLTuning); +#endif LOG_DBG("HAL_DSI_ConfigVideoMode setup:"); LOG_DBG(" VirtualChannelID %u", data->vid_cfg.VirtualChannelID); @@ -167,9 +200,15 @@ static int mipi_dsi_stm32_host_init(const struct device *dev) return ret; } +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32u5_mipi_dsi) + /* LANE_BYTE_CLOCK = CLK_IN / PLLIDF * 2 * PLLNDIV / PLLODF / 8 */ + data->lane_clk_khz = hse_clock / data->pll_init.PLLIDF * 2 * data->pll_init.PLLNDIV / + data->pll_init.PLLODF / 8 / 1000; +#else /* LANE_BYTE_CLOCK = CLK_IN / PLLIDF * 2 * PLLNDIV / 2 / PLLODF / 8 */ data->lane_clk_khz = hse_clock / data->pll_init.PLLIDF * 2 * data->pll_init.PLLNDIV / 2 / (1UL << data->pll_init.PLLODF) / 8 / 1000; +#endif /* stm32x_hal_dsi: The values 0 and 1 stop the TX_ESC clock generation */ data->hdsi.Init.TXEscapeCkdiv = 0; @@ -212,6 +251,8 @@ static int mipi_dsi_stm32_host_init(const struct device *dev) return -ret; } +#ifndef CONFIG_SOC_SERIES_STM32U5X + if (config->lp_rx_filter_freq) { ret = HAL_DSI_SetLowPowerRXFilter(&data->hdsi, config->lp_rx_filter_freq); if (ret != HAL_OK) { @@ -219,6 +260,7 @@ static int mipi_dsi_stm32_host_init(const struct device *dev) return -ret; } } +#endif ret = HAL_DSI_ConfigErrorMonitor(&data->hdsi, config->active_errors); if (ret != HAL_OK) { @@ -289,6 +331,14 @@ static int mipi_dsi_stm32_attach(const struct device *dev, uint8_t channel, return -ret; } +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32u5_mipi_dsi) + ret = stm32_dsi_clock_source_config(dev); + if (ret < 0) { + LOG_ERR("Failed to configure DSI clock source"); + return ret; + } +#endif + if (IS_ENABLED(CONFIG_MIPI_DSI_LOG_LEVEL_DBG)) { mipi_dsi_stm32_log_config(dev); } @@ -457,6 +507,12 @@ static int mipi_dsi_stm32_init(const struct device *dev) .enr = DT_INST_CLOCKS_CELL_BY_NAME(inst, dsiclk, bits), \ .bus = DT_INST_CLOCKS_CELL_BY_NAME(inst, dsiclk, bus), \ }, \ + COND_CODE_1(DT_INST_CLOCKS_HAS_NAME(inst, dsisrc), \ + (.dsisrc_clk = { \ + .enr = DT_INST_CLOCKS_CELL_BY_NAME(inst, dsisrc, bits), \ + .bus = DT_INST_CLOCKS_CELL_BY_NAME(inst, dsisrc, bus), \ + },), \ + (.dsisrc_clk = {0},)) \ .ref_clk = { \ .enr = DT_INST_CLOCKS_CELL_BY_NAME(inst, refclk, bits), \ .bus = DT_INST_CLOCKS_CELL_BY_NAME(inst, refclk, bus), \ @@ -479,6 +535,13 @@ static int mipi_dsi_stm32_init(const struct device *dev) DT_INST_PROP(inst, non_continuous) ? \ DSI_AUTO_CLK_LANE_CTRL_ENABLE : \ DSI_AUTO_CLK_LANE_CTRL_DISABLE, \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, phy_freq_range), \ + (.PHYFrequencyRange = DT_INST_PROP(inst, phy_freq_range),),\ + ()) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, phy_low_power_offset), \ + (.PHYLowPowerOffset = \ + DT_INST_PROP(inst, phy_low_power_offset),), \ + ()) \ }, \ }, \ .host_timeouts = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, host_timeouts), \ @@ -494,15 +557,25 @@ static int mipi_dsi_stm32_init(const struct device *dev) DSI_DATA_ENABLE_ACTIVE_HIGH : DSI_DATA_ENABLE_ACTIVE_LOW, \ .LooselyPacked = DT_INST_PROP(inst, loosely_packed) ? \ DSI_LOOSELY_PACKED_ENABLE : DSI_LOOSELY_PACKED_DISABLE, \ - .LPLargestPacketSize = DT_INST_PROP_OR(inst, largest_packet_size, 4), \ + .LPLargestPacketSize = DT_INST_PROP_OR(inst, largest_packet_size, 4), \ .LPVACTLargestPacketSize = DT_INST_PROP_OR(inst, largest_packet_size, 4), \ .FrameBTAAcknowledgeEnable = DT_INST_PROP(inst, bta_ack_disable) ? \ - DSI_FBTAA_DISABLE : DSI_FBTAA_ENABLE, \ + DSI_FBTAA_DISABLE : DSI_FBTAA_ENABLE, \ }, \ .pll_init = { \ .PLLNDIV = DT_INST_PROP(inst, pll_ndiv), \ .PLLIDF = DT_INST_PROP(inst, pll_idf), \ .PLLODF = DT_INST_PROP(inst, pll_odf), \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, pll_vco_range), \ + (.PLLVCORange = DT_INST_PROP(inst, pll_vco_range),), \ + ()) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, pll_charge_pump), \ + (.PLLChargePump = \ + DT_INST_PROP(inst, pll_charge_pump),), \ + ()) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, pll_tuning), \ + (.PLLTuning = DT_INST_PROP(inst, pll_tuning),), \ + ()) \ }, \ }; \ DEVICE_DT_INST_DEFINE(inst, &mipi_dsi_stm32_init, NULL, \ diff --git a/dts/arm/st/u5/stm32u599.dtsi b/dts/arm/st/u5/stm32u599.dtsi index 21540ca9bb35e..4cb5fbd902df7 100644 --- a/dts/arm/st/u5/stm32u599.dtsi +++ b/dts/arm/st/u5/stm32u599.dtsi @@ -26,6 +26,20 @@ status = "disabled"; }; + mipi_dsi: dsihost@40016c00 { + compatible = "st,stm32u5-mipi-dsi", "st,stm32-mipi-dsi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x40016C00 0x1000>; + clock-names = "dsiclk", "dsisrc", "refclk", "pixelclk"; + clocks = <&rcc STM32_CLOCK(APB2, 27U)>, + <&rcc STM32_SRC_DSIPHY DSI_SEL(1)>, + <&rcc STM32_SRC_HSE NO_SEL>, + <&rcc STM32_SRC_PLL3_R NO_SEL>; + resets = <&rctl STM32_RESET(APB2, 27U)>; + status = "disabled"; + }; + xspi1: spi@420d3400 { compatible = "st,stm32-xspi"; reg = <0x420d3400 0x400>, diff --git a/dts/bindings/mipi-dsi/st,stm32u5-mipi-dsi.yaml b/dts/bindings/mipi-dsi/st,stm32u5-mipi-dsi.yaml new file mode 100644 index 0000000000000..0d58d8c282d6a --- /dev/null +++ b/dts/bindings/mipi-dsi/st,stm32u5-mipi-dsi.yaml @@ -0,0 +1,132 @@ +# +# Copyright (c) 2025 Charles Dias +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: | + STM32U5 series MIPI DSI controller. + +compatible: "st,stm32u5-mipi-dsi" + +include: + - name: st,stm32-mipi-dsi.yaml + property-blocklist: + - clock-names + +properties: + clock-names: + type: string-array + required: true + description: | + "dsiclk" DSI clock enable. + "dsisrc" DSI clock source. + "refclk" External crystal or oscillator clock. + "pixelclk" LTDC pixel clock. + "refclk" and "pixelclk" are only used to retrieve the frequency for timing calculation. + + phy-freq-range: + required: true + type: int + enum: + - 0x0 + - 0x1 + - 0x2 + - 0x3 + - 0x4 + - 0x5 + - 0x6 + - 0x7 + - 0x8 + description: | + D-PHY PLL input frequency range. This is used to select the appropriate + frequency range for the D-PHY PLL operation. + 0x0 : DSI_DPHY_FRANGE_80MHZ_100MHZ + 0x1 : DSI_DPHY_FRANGE_100MHZ_120MHZ + 0x2 : DSI_DPHY_FRANGE_120MHZ_160MHZ + 0x3 : DSI_DPHY_FRANGE_160MHZ_200MHZ + 0x4 : DSI_DPHY_FRANGE_200MHZ_240MHZ + 0x5 : DSI_DPHY_FRANGE_240MHZ_320MHZ + 0x6 : DSI_DPHY_FRANGE_320MHZ_390MHZ + 0x7 : DSI_DPHY_FRANGE_390MHZ_450MHZ + 0x8 : DSI_DPHY_FRANGE_450MHZ_510MHZ + + phy-low-power-offset: + required: true + type: int + enum: + - 0x0 + - 0x1 + - 0x2 + - 0x3 + - 0x4 + - 0x5 + - 0x6 + - 0x7 + - 0x8 + - 0x9 + - 0xA + - 0xB + - 0xC + - 0xD + - 0xE + - 0xF + description: | + D-PHY low power offset configuration specific to STM32U5 series. + 0x0 : PHY_LP_OFFSSET_0_CLKP (0 CLKP) + 0x1 : PHY_LP_OFFSSET_1_CLKP (+1 CLKP) + 0x2 : PHY_LP_OFFSSET_2_CLKP (+2 CLKP) + 0x3 : PHY_LP_OFFSSET_3_CLKP (+3 CLKP) + 0x4 : PHY_LP_OFFSSET_4_CLKP (+4 CLKP) + 0x5 : PHY_LP_OFFSSET_5_CLKP (+5 CLKP) + 0x6 : PHY_LP_OFFSSET_6_CLKP (+6 CLKP) + 0x7 : PHY_LP_OFFSSET_7_CLKP (+7 CLKP) + 0x8 : PHY_LP_OFFSSET_MINUS_1_CLKP (-1 CLKP) + 0x9 : PHY_LP_OFFSSET_MINUS_2_CLKP (-2 CLKP) + 0xA : PHY_LP_OFFSSET_MINUS_3_CLKP (-3 CLKP) + 0xB : PHY_LP_OFFSSET_MINUS_4_CLKP (-4 CLKP) + 0xC : PHY_LP_OFFSSET_MINUS_5_CLKP (-5 CLKP) + 0xD : PHY_LP_OFFSSET_MINUS_6_CLKP (-6 CLKP) + 0xE : PHY_LP_OFFSSET_MINUS_7_CLKP (-7 CLKP) + 0xF : PHY_LP_OFFSSET_MINUS_8_CLKP (-8 CLKP) + + pll-vco-range: + required: true + type: int + enum: + - 0x0 + - 0x1 + description: | + PLL VCO frequency range configuration for STM32U5 D-PHY. + 0x0 : DSI_DPHY_VCO_FRANGE_500MHZ_800MHZ + 0x1 : DSI_DPHY_VCO_FRANGE_800MHZ_1GHZ + + pll-charge-pump: + required: true + type: int + enum: + - 0x0 + - 0x1 + - 0x2 + - 0x3 + description: | + PLL charge pump configuration for STM32U5 D-PHY. + Valid values: + 0x0 : DSI_PLL_CHARGE_PUMP_2000HZ_4400HZ + 0x1 : DSI_PLL_CHARGE_PUMP_4400HZ_14100HZ + 0x0 : DSI_PLL_CHARGE_PUMP_14100HZ_30900HZ + 0x3 : DSI_PLL_CHARGE_PUMP_30900HZ_45700HZ + 0x2 : DSI_PLL_CHARGE_PUMP_45700HZ_50000HZ + + pll-tuning: + required: true + type: int + enum: + - 0x0 + - 0x1 + - 0x2 + description: | + PLL tuning parameter (loop filter) for STM32U5 D-PHY. + 0x0 : DSI_PLL_LOOP_FILTER_2000HZ_4400HZ + 0x1 : DSI_PLL_LOOP_FILTER_4400HZ_30900HZ + 0x2 : DSI_PLL_LOOP_FILTER_30900HZ_50000HZ diff --git a/include/zephyr/dt-bindings/clock/stm32u5_clock.h b/include/zephyr/dt-bindings/clock/stm32u5_clock.h index 68d74fe40add1..6a68e6ae8a686 100644 --- a/include/zephyr/dt-bindings/clock/stm32u5_clock.h +++ b/include/zephyr/dt-bindings/clock/stm32u5_clock.h @@ -38,6 +38,8 @@ #define STM32_SRC_PLL3_P (STM32_SRC_PLL2_R + 1) #define STM32_SRC_PLL3_Q (STM32_SRC_PLL3_P + 1) #define STM32_SRC_PLL3_R (STM32_SRC_PLL3_Q + 1) +/** DSI PHY clock */ +#define STM32_SRC_DSIPHY (STM32_SRC_PLL3_R + 1) /** Clock muxes */ /* #define STM32_SRC_ICLK TBD */ @@ -89,7 +91,7 @@ #define SAE_SEL(val) STM32_DT_CLOCK_SELECT((val), 1, 11, CCIPR2_REG) #define RNG_SEL(val) STM32_DT_CLOCK_SELECT((val), 3, 12, CCIPR2_REG) #define SDMMC_SEL(val) STM32_DT_CLOCK_SELECT((val), 1, 14, CCIPR2_REG) -#define DSIHOST_SEL(val) STM32_DT_CLOCK_SELECT((val), 1, 15, CCIPR2_REG) +#define DSI_SEL(val) STM32_DT_CLOCK_SELECT((val), 1, 15, CCIPR2_REG) #define USART6_SEL(val) STM32_DT_CLOCK_SELECT((val), 1, 16, CCIPR2_REG) #define LTDC_SEL(val) STM32_DT_CLOCK_SELECT((val), 1, 18, CCIPR2_REG) #define OCTOSPI_SEL(val) STM32_DT_CLOCK_SELECT((val), 3, 20, CCIPR2_REG)