[PATCH 00/26] Support display (and even more) on the BSH SMM S2/PRO boards

This series was created out of the need to supportsimple-framebuffer for BSH boards SMM_S2 and SMM_S2PRO. To achieve this goal, it was necessary to develop additional code and/or drivers for all the required components (i. e. clock, power domain, video, etc.). This series has a Linux counterpart that will also be submitted upstream as soon as possible. Furthermore, given the overlap in topics with the recent series [1], we are open to collaborate on the common parts to ensure proper development.
[1] https://lore.kernel.org/all/20240910101344.110633-1-miquel.raynal@bootlin.co...
Dario Binacchi (6): boot: fdt_simplefb: add a debug message video: mxsfb: support simple frame-buffer video: bridge: samsung: support simple frame-buffer imx8mn_bsh_smm_s2/pro: support simple frame-buffer imx8mn_bsh_smm_s2/pro: enable simple frame-buffer imx8mn_bsh_smm_s2/pro: add splash screen with BSH logo
Michael Trimarchi (20): clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present for gate and mux clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Prevent clock critical path from disabling during reparent and set_rate clk: clk-uclass: Implement CLK_OPS_PARENT_ENABLE clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled clk: imx8mn: Mark IMX8MN_SYS_PLL2 and IMX8MN_SYS_PLL3 as enabled clk: imx8mn: add video clocks support power: Add iMX8M block ctrl driver for dispmix video: Add video link framework video: bridge: Add check_timing interface video: dsi_host: add disable host interface video: Update mxsfb video drivers for iMX8MM/iMX8MN display video: Enable DM_UC_FLAG_SEQ_ALIAS for display and bridge phy: dphy: add support to calculate the timing based on hs_clk_rate phy: dphy: Correct lpx parameter and its derivatives(ta_{get,go,sure}) phy: dphy: Correct clk_pre parameter lib: div64: sync with Linux video: bridge: Add Samsung DSIM bridge video: Add Synaptics R63353 panel driver imx8mn_bsh_smm_s2/pro: Enable display on reference design
.../dts/imx8mn-bsh-smm-s2-u-boot-common.dtsi | 23 + board/bsh/imx8mn_smm_s2/imx8mn_smm_s2.c | 99 ++ boot/fdt_simplefb.c | 2 + common/stdio.c | 4 + configs/imx8mn_bsh_smm_s2_defconfig | 21 + configs/imx8mn_bsh_smm_s2pro_defconfig | 21 + drivers/clk/clk-gate.c | 1 + drivers/clk/clk-mux.c | 2 +- drivers/clk/clk-uclass.c | 67 +- drivers/clk/clk.c | 9 + drivers/clk/imx/clk-imx8mm.c | 73 +- drivers/clk/imx/clk-imx8mn.c | 131 +- drivers/phy/meson-axg-mipi-dphy.c | 2 +- drivers/phy/phy-core-mipi-dphy.c | 36 +- drivers/power/domain/Kconfig | 6 + drivers/power/domain/Makefile | 1 + drivers/power/domain/imx8m-blk-ctrl.c | 438 +++++ drivers/power/domain/imx8m-power-domain.c | 213 ++- drivers/video/Kconfig | 14 + drivers/video/Makefile | 2 + drivers/video/bridge/Kconfig | 13 + drivers/video/bridge/Makefile | 1 + drivers/video/bridge/samsung-dsi-host.c | 1576 +++++++++++++++++ drivers/video/bridge/samsung-dsim.c | 151 ++ drivers/video/bridge/samsung-dsim.h | 20 + drivers/video/bridge/video-bridge-uclass.c | 11 + drivers/video/display-uclass.c | 1 + drivers/video/dsi-host-uclass.c | 10 + drivers/video/mxsfb.c | 144 +- drivers/video/synaptics-r63353.c | 228 +++ drivers/video/video_link.c | 529 ++++++ include/clk.h | 9 + include/configs/imx8mn_bsh_smm_s2.h | 1 + include/configs/imx8mn_bsh_smm_s2_common.h | 4 + include/configs/imx8mn_bsh_smm_s2pro.h | 1 + include/dsi_host.h | 8 + include/linux/clk-provider.h | 1 + include/linux/math64.h | 110 ++ include/phy-mipi-dphy.h | 3 + include/video_bridge.h | 20 + include/video_link.h | 19 + tools/logos/bsh.bmp | Bin 0 -> 10644 bytes 42 files changed, 3942 insertions(+), 83 deletions(-) create mode 100644 drivers/power/domain/imx8m-blk-ctrl.c create mode 100644 drivers/video/bridge/samsung-dsi-host.c create mode 100644 drivers/video/bridge/samsung-dsim.c create mode 100644 drivers/video/bridge/samsung-dsim.h create mode 100644 drivers/video/synaptics-r63353.c create mode 100644 drivers/video/video_link.c create mode 100644 include/video_link.h create mode 100644 tools/logos/bsh.bmp

From: Michael Trimarchi michael@amarulasolutions.com
Gate and mux does not have .set_rate operation, but they could have CLK_SET_PARENT_RATE flag set. In that case it's usually possible to find a parent up the tree which is capable of setting the rate (div, pll, etc). Add clk_generic_set_rate to allow them to trasverse the clock tree.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/clk/clk-gate.c | 1 + drivers/clk/clk-mux.c | 2 +- drivers/clk/clk-uclass.c | 20 ++++++++++++++++++++ drivers/clk/clk.c | 9 +++++++++ include/clk.h | 9 +++++++++ include/linux/clk-provider.h | 1 + 6 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index bf1c6a93b468..bd0d3d44ac8c 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -115,6 +115,7 @@ const struct clk_ops clk_gate_ops = { .enable = clk_gate_enable, .disable = clk_gate_disable, .get_rate = clk_generic_get_rate, + .set_rate = clk_generic_set_rate, };
struct clk *clk_register_gate(struct device *dev, const char *name, diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 62477e15d27a..4feb8e8c821d 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -150,13 +150,13 @@ static int clk_mux_set_parent(struct clk *clk, struct clk *parent) #else writel(reg, mux->reg); #endif - return 0; }
const struct clk_ops clk_mux_ops = { .get_rate = clk_generic_get_rate, .set_parent = clk_mux_set_parent, + .set_rate = clk_generic_set_rate, };
struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 16169dac2340..e8db9b0ff2fd 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -515,6 +515,26 @@ ulong clk_get_parent_rate(struct clk *clk) return pclk->rate; }
+ulong clk_set_parent_rate(struct clk *clk, ulong rate) +{ + const struct clk_ops *ops; + struct clk *pclk; + + debug("%s(clk=%p)\n", __func__, clk); + if (!clk_valid(clk)) + return 0; + + pclk = clk_get_parent(clk); + if (IS_ERR(pclk)) + return -ENODEV; + + ops = clk_dev_ops(pclk->dev); + if (!ops->set_rate) + return -ENOSYS; + + return clk_set_rate(pclk, rate); +} + ulong clk_round_rate(struct clk *clk, ulong rate) { const struct clk_ops *ops; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index b8c2e8d531b9..67c6b23231ff 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -13,6 +13,7 @@ #include <dm/uclass.h> #include <dm/lists.h> #include <dm/device-internal.h> +#include <linux/clk-provider.h>
int clk_register(struct clk *clk, const char *drv_name, const char *name, const char *parent_name) @@ -60,6 +61,14 @@ ulong clk_generic_get_rate(struct clk *clk) return clk_get_parent_rate(clk); }
+ulong clk_generic_set_rate(struct clk *clk, ulong rate) +{ + if (clk->flags & CLK_SET_RATE_PARENT) + return clk_set_parent_rate(clk, rate); + + return clk_get_parent_rate(clk); +} + const char *clk_hw_get_name(const struct clk *hw) { assert(hw); diff --git a/include/clk.h b/include/clk.h index 045e923a529b..f0fd524ee519 100644 --- a/include/clk.h +++ b/include/clk.h @@ -452,6 +452,15 @@ struct clk *clk_get_parent(struct clk *clk); */ ulong clk_get_parent_rate(struct clk *clk);
+/** + * clk_set_parent_rate() - Set parent of current clock rate. + * @clk: A clock struct that was previously successfully requested by + * clk_request/get_by_*(). + * + * Return: clock rate in Hz, or -ve error code. + */ +ulong clk_set_parent_rate(struct clk *clk, ulong rate); + /** * clk_round_rate() - Adjust a rate to the exact rate a clock can provide * @clk: A clock struct that was previously successfully requested by diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 59f9c241b846..459fa2d15ceb 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -253,6 +253,7 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
const char *clk_hw_get_name(const struct clk *hw); ulong clk_generic_get_rate(struct clk *clk); +ulong clk_generic_set_rate(struct clk *clk, ulong rate);
struct clk *dev_get_clk_ptr(struct udevice *dev);

From: Michael Trimarchi michael@amarulasolutions.com
This commit ensures that critical clock paths are not disabled during reparent and set_rate operations within the clock framework, paving the way for the inclusion of new features.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/clk/imx/clk-imx8mn.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-)
diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index 125215e84f41..c96a2ebaa19a 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -29,10 +29,19 @@ static const char * const imx8mn_a53_sels[] = {"clock-osc-24m", "arm_pll_out", " "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", };
+static const char * const imx8mn_noc_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out", + "video_pll_out", "audio_pll2_out", }; + static const char * const imx8mn_ahb_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll_out", };
+static const char * const imx8mn_main_axi_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll1_800m", + "sys_pll2_250m", "sys_pll2_1000m", + "audio_pll1_out", "video_pll_out", + "sys_pll1_100m",}; + static const char * const imx8mn_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out", "video_pll_out", "sys_pll3_out", }; @@ -67,6 +76,9 @@ static const char * const imx8mn_usdhc2_sels[] = {"clock-osc-24m", "sys_pll1_400 "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", };
+static const char * const imx8mn_gic_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll2_100m", "sys_pll1_800m", "clk_ext2", + "clk_ext4", "audio_pll2_out" }; #if CONFIG_IS_ENABLED(DM_SPI) static const char * const imx8mn_ecspi1_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", @@ -286,15 +298,18 @@ static int imx8mn_clk_probe(struct udevice *dev) clk_dm(IMX8MN_CLK_IPG_ROOT, imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1));
+ /* BUS */ + clk_dm(IMX8MN_CLK_MAIN_AXI, + imx8m_clk_composite_critical("main_axi", imx8mn_main_axi_sels, base + 0x8800)); clk_dm(IMX8MN_CLK_ENET_AXI, - imx8m_clk_composite("enet_axi", imx8mn_enet_axi_sels, - base + 0x8880)); + imx8m_clk_composite("enet_axi", imx8mn_enet_axi_sels, base + 0x8880)); clk_dm(IMX8MN_CLK_NAND_USDHC_BUS, - imx8m_clk_composite_critical("nand_usdhc_bus", - imx8mn_nand_usdhc_sels, + imx8m_clk_composite_critical("nand_usdhc_bus", imx8mn_nand_usdhc_sels, base + 0x8900)); clk_dm(IMX8MN_CLK_USB_BUS, imx8m_clk_composite("usb_bus", imx8mn_usb_bus_sels, base + 0x8b80)); + clk_dm(IMX8MN_CLK_NOC, + imx8m_clk_composite_critical("noc", imx8mn_noc_sels, base + 0x8d00));
/* IP */ clk_dm(IMX8MN_CLK_USDHC1, @@ -389,7 +404,8 @@ static int imx8mn_clk_probe(struct udevice *dev) clk_dm(IMX8MN_CLK_PWM4_ROOT, imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0)); #endif - + clk_dm(IMX8MN_CLK_GIC, + imx8m_clk_composite_critical("gic", imx8mn_gic_sels, base + 0xb200)); #if CONFIG_IS_ENABLED(DM_SPI) clk_dm(IMX8MN_CLK_ECSPI1, imx8m_clk_composite("ecspi1", imx8mn_ecspi1_sels, base + 0xb280));

From: Michael Trimarchi michael@amarulasolutions.com
This commit ensures that critical clock paths are not disabled during reparent and set_rate operations within the clock framework, paving the way for the inclusion of new features.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/clk/imx/clk-imx8mm.c | 64 +++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 12 deletions(-)
diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index a91c6767fac5..432348a15cad 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -27,11 +27,25 @@ static const char * const imx8mm_a53_sels[] = {"clock-osc-24m", "arm_pll_out", " "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", };
+#ifndef CONFIG_SPL_BUILD +static const char * const imx8mm_noc_sels[] = {"clock-osc-24m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mm_noc_apb_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll3_out", + "sys_pll2_333m", "sys_pll2_200m", + "sys_pll1_800m", "audio_pll1_out", + "video_pll1_out", }; + static const char * const imx8mm_ahb_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", };
-#ifndef CONFIG_SPL_BUILD +static const char * const imx8mm_main_axi_sels[] = {"clock-osc-24m", "sys_pll2_333m", + "sys_pll1_800m", "sys_pll2_250m", + "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "sys_pll1_100m",}; + static const char * const imx8mm_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; @@ -47,6 +61,11 @@ static const char * const imx8mm_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2 static const char * const imx8mm_enet_phy_sels[] = {"clock-osc-24m", "sys_pll2_50m", "sys_pll2_125m", "sys_pll2_200m", "sys_pll2_500m", "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mm_dram_apb_sels[] = {"clock-osc-24m", "sys_pll2_200m", + "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; #endif
static const char * const imx8mm_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", @@ -82,8 +101,9 @@ static const char * const imx8mm_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m" "audio_pll2_out", "sys_pll1_133m", };
#if CONFIG_IS_ENABLED(PCIE_DW_IMX) -static const char * const imx8mm_pcie1_ctrl_sels[] = {"clock-osc-24m", "sys_pll2_250m", "sys_pll2_200m", - "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_500m", +static const char * const imx8mm_pcie1_ctrl_sels[] = {"clock-osc-24m", "sys_pll2_250m", + "sys_pll2_200m", "sys_pll1_266m", + "sys_pll1_800m", "sys_pll2_500m", "sys_pll2_333m", "sys_pll3_out", };
static const char * const imx8mm_pcie1_phy_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll2_500m", @@ -135,6 +155,10 @@ static const char * const imx8mm_usb_phy_sels[] = {"clock-osc-24m", "sys_pll1_10 "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", };
+static const char * const imx8mm_gic_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll2_100m", "sys_pll1_800m", "clk_ext2", + "clk_ext4", "audio_pll2_out" }; + #if CONFIG_IS_ENABLED(DM_SPI) static const char * const imx8mm_ecspi1_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", @@ -283,18 +307,34 @@ static int imx8mm_clk_probe(struct udevice *dev) imx_clk_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3));
- clk_dm(IMX8MM_CLK_AHB, - imx8m_clk_composite_critical("ahb", imx8mm_ahb_sels, - base + 0x9000)); clk_dm(IMX8MM_CLK_IPG_ROOT, imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1));
+#ifndef CONFIG_SPL_BUILD + /* BUS */ + clk_dm(IMX8MM_CLK_MAIN_AXI, + imx8m_clk_composite_critical("main_axi", + imx8mm_main_axi_sels, base + 0x8800)); + clk_dm(IMX8MM_CLK_ENET_AXI, + imx8m_clk_composite("enet_axi", imx8mm_enet_axi_sels, base + 0x8880)); + clk_dm(IMX8MM_CLK_NOC, + imx8m_clk_composite_critical("noc", imx8mm_noc_sels, base + 0x8d00)); + clk_dm(IMX8MM_CLK_NOC_APB, + imx8m_clk_composite_critical("noc_apb", imx8mm_noc_apb_sels, + base + 0x8d80)); + clk_dm(IMX8MM_CLK_AHB, + imx8m_clk_composite_critical("ahb", imx8mm_ahb_sels, base + 0x9000)); + clk_dm(IMX8MM_CLK_DRAM_APB, + imx8m_clk_composite_critical("dram_apb", imx8mm_dram_apb_sels, + base + 0xa080)); +#endif + clk_dm(IMX8MM_CLK_NAND_USDHC_BUS, - imx8m_clk_composite_critical("nand_usdhc_bus", - imx8mm_nand_usdhc_sels, + imx8m_clk_composite_critical("nand_usdhc_bus", imx8mm_nand_usdhc_sels, base + 0x8900)); + clk_dm(IMX8MM_CLK_USB_BUS, - imx8m_clk_composite("usb_bus", imx8mm_usb_bus_sels, base + 0x8b80)); + imx8m_clk_composite("usb_bus", imx8mm_usb_bus_sels, base + 0x8b80));
/* IP */ #if CONFIG_IS_ENABLED(PCIE_DW_IMX) @@ -358,9 +398,6 @@ static int imx8mm_clk_probe(struct udevice *dev)
/* clks not needed in SPL stage */ #ifndef CONFIG_SPL_BUILD - clk_dm(IMX8MM_CLK_ENET_AXI, - imx8m_clk_composite("enet_axi", imx8mm_enet_axi_sels, - base + 0x8880)); clk_dm(IMX8MM_CLK_ENET_REF, imx8m_clk_composite("enet_ref", imx8mm_enet_ref_sels, base + 0xa980)); @@ -396,6 +433,9 @@ static int imx8mm_clk_probe(struct udevice *dev) imx_clk_gate4("pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0)); #endif
+ clk_dm(IMX8MM_CLK_GIC, + imx8m_clk_composite_critical("gic", imx8mm_gic_sels, base + 0xb200)); + #if CONFIG_IS_ENABLED(DM_SPI) clk_dm(IMX8MM_CLK_ECSPI1, imx8m_clk_composite("ecspi1", imx8mm_ecspi1_sels, base + 0xb280));

On 9/13/24 11:55 AM, Dario Binacchi wrote:
From: Michael Trimarchi michael@amarulasolutions.com
This commit ensures that critical clock paths are not disabled during reparent and set_rate operations within the clock framework, paving the way for the inclusion of new features.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com
drivers/clk/imx/clk-imx8mm.c | 64 +++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 12 deletions(-)
diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index a91c6767fac5..432348a15cad 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -27,11 +27,25 @@ static const char * const imx8mm_a53_sels[] = {"clock-osc-24m", "arm_pll_out", " "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", };
+#ifndef CONFIG_SPL_BUILD +static const char * const imx8mm_noc_sels[] = {"clock-osc-24m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out",
"video_pll1_out", "audio_pll2_out", };
This seems to be adding a lot of new clock, such a change is not described in the commit message.
What problem is this patch solving ? The commit message needs to be updated, it does not seem to match the patch at all.

From: Michael Trimarchi michael@amarulasolutions.com
There are scenario that we need to enable the new parent clock before reparent, or we need to do the same with clk_set_rate. The patch cover those scenario
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/clk/clk-uclass.c | 47 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index e8db9b0ff2fd..b571a3620222 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -582,7 +582,9 @@ static void clk_clean_rate_cache(struct clk *clk) ulong clk_set_rate(struct clk *clk, ulong rate) { const struct clk_ops *ops; + struct clk *pclk; struct clk *clkp; + ulong ret;
debug("%s(clk=%p, rate=%lu)\n", __func__, clk, rate); if (!clk_valid(clk)) @@ -597,11 +599,37 @@ ulong clk_set_rate(struct clk *clk, ulong rate) /* Clean up cached rates for us and all child clocks */ clk_clean_rate_cache(clkp);
- return ops->set_rate(clk, rate); + if (clk->flags & CLK_SET_RATE_UNGATE) { + ret = clk_enable(clk); + if (ret) + return ret; + } + + pclk = clk_get_parent(clk); + if (pclk) { + if (clk->flags & CLK_OPS_PARENT_ENABLE) { + ret = clk_enable(pclk); + if (ret) + goto out; + } + } + + ret = ops->set_rate(clk, rate); + + if (pclk && clk->flags & CLK_OPS_PARENT_ENABLE) + clk_disable(pclk); + +out: + if (clk->flags & CLK_SET_RATE_UNGATE) + clk_disable(clk); + + return ret; }
int clk_set_parent(struct clk *clk, struct clk *parent) { + struct clk *old_parent; + const struct clk_ops *ops; int ret;
@@ -613,6 +641,15 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!ops->set_parent) return -ENOSYS;
+ if (clk->enable_count) + clk_enable(parent); + + old_parent = clk_get_parent(clk); + if (clk->flags & CLK_OPS_PARENT_ENABLE) { + clk_enable(old_parent); + clk_enable(parent); + } + ret = ops->set_parent(clk, parent); if (ret) return ret; @@ -620,6 +657,14 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (CONFIG_IS_ENABLED(CLK_CCF)) ret = device_reparent(clk->dev, parent->dev);
+ if (clk->flags & CLK_OPS_PARENT_ENABLE) { + clk_disable(parent); + clk_disable(old_parent); + } + + if (clk->enable_count) + clk_disable(old_parent); + return ret; }

From: Michael Trimarchi michael@amarulasolutions.com
Both clock are enabled by the bootloader and we need to increase their reference count to avoid disable during reparent operation.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/clk/imx/clk-imx8mm.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index 432348a15cad..0e71ceefe96c 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -176,6 +176,8 @@ static const char * const imx8mm_ecspi3_sels[] = {"clock-osc-24m", "sys_pll2_200 static int imx8mm_clk_probe(struct udevice *dev) { void __iomem *base; + struct clk *clk; + int ret;
base = (void *)ANATOP_BASE_ADDR;
@@ -458,6 +460,13 @@ static int imx8mm_clk_probe(struct udevice *dev) clk_dm(IMX8MM_CLK_QSPI_ROOT, imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0)); #endif + ret = clk_get_by_id(IMX8MM_SYS_PLL2, &clk); + if (!ret) + clk_enable(clk); + + ret = clk_get_by_id(IMX8MM_SYS_PLL3, &clk); + if (!ret) + clk_enable(clk);
clk_dm(IMX8MM_CLK_ARM, imx_clk_mux2_flags("arm_core", base + 0x9880, 24, 1,

On 9/13/24 11:55 AM, Dario Binacchi wrote:
From: Michael Trimarchi michael@amarulasolutions.com
Both clock are enabled by the bootloader and we need to increase their reference count to avoid disable during reparent operation.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com
drivers/clk/imx/clk-imx8mm.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index 432348a15cad..0e71ceefe96c 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -176,6 +176,8 @@ static const char * const imx8mm_ecspi3_sels[] = {"clock-osc-24m", "sys_pll2_200 static int imx8mm_clk_probe(struct udevice *dev) { void __iomem *base;
struct clk *clk;
int ret;
base = (void *)ANATOP_BASE_ADDR;
@@ -458,6 +460,13 @@ static int imx8mm_clk_probe(struct udevice *dev) clk_dm(IMX8MM_CLK_QSPI_ROOT, imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0)); #endif
- ret = clk_get_by_id(IMX8MM_SYS_PLL2, &clk);
- if (!ret)
clk_enable(clk);
- ret = clk_get_by_id(IMX8MM_SYS_PLL3, &clk);
- if (!ret)
clk_enable(clk);
This enables these PLLs for all boards, even the ones which do not need those are use those for other purposes like drive the CLKOUT outputs from them. Please describe DT consumers, do not hack board-specific changes into generic drivers.

From: Michael Trimarchi michael@amarulasolutions.com
Both clock are enabled by the bootloader and we need to increase their reference count to avoid disable during reparent operation.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/clk/imx/clk-imx8mn.c | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index c96a2ebaa19a..449ebf2d0733 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -154,6 +154,7 @@ static const char * const imx8mn_usb_phy_sels[] = {"clock-osc-24m", "sys_pll1_10 static int imx8mn_clk_probe(struct udevice *dev) { struct clk osc_24m_clk; + struct clk *clk; void __iomem *base; int ret;
@@ -420,6 +421,13 @@ static int imx8mn_clk_probe(struct udevice *dev) clk_dm(IMX8MN_CLK_ECSPI3_ROOT, imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); #endif + ret = clk_get_by_id(IMX8MN_SYS_PLL2, &clk); + if (!ret) + clk_enable(clk); + + ret = clk_get_by_id(IMX8MN_SYS_PLL3, &clk); + if (!ret) + clk_enable(clk);
clk_dm(IMX8MN_CLK_ARM, imx_clk_mux2_flags("arm_core", base + 0x9880, 24, 1,

From: Michael Trimarchi michael@amarulasolutions.com
Add clocks support for the video subsystem.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/clk/imx/clk-imx8mn.c | 97 +++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 7 deletions(-)
diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index 449ebf2d0733..7ea4c90fa137 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -17,6 +17,7 @@ static u32 share_count_nand;
static const char * const pll_ref_sels[] = { "clock-osc-24m", "dummy", "dummy", "dummy", }; +static const char * const video_pll_bypass_sels[] = {"video_pll", "video_pll_ref_sel", }; static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; static const char * const sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; @@ -47,17 +48,52 @@ static const char * const imx8mn_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_2 "video_pll_out", "sys_pll3_out", };
#ifndef CONFIG_SPL_BUILD -static const char * const imx8mn_enet_ref_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m", - "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", +static const char * const imx8mn_disp_axi_sels[] = {"clock-osc-24m", "sys_pll2_1000m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll1_40m", + "audio_pll2_out", "clk_ext1", "clk_ext4", }; + +static const char * const imx8mn_disp_apb_sels[] = {"clock-osc-24m", "sys_pll2_125m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll1_40m", + "audio_pll2_out", "clk_ext1", "clk_ext3", }; + +static const char * const imx8mn_disp_pixel_sels[] = {"clock-osc-24m", "video_pll_out", + "audio_pll2_out", "audio_pll1_out", + "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll3_out", "clk_ext4", }; + +static const char * const imx8mn_dsi_core_sels[] = {"clock-osc-24m", "sys_pll1_266m", + "sys_pll2_250m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", + "audio_pll2_out", "video_pll_out", }; + +static const char * const imx8mn_dsi_phy_sels[] = {"clock-osc-24m", "sys_pll2_125m", + "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "clk_ext2", + "audio_pll2_out", "video_pll_out", }; + +static const char * const imx8mn_dsi_dbi_sels[] = {"clock-osc-24m", "sys_pll1_266m", + "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", + "audio_pll2_out", "video_pll_out", }; + +static const char * const imx8mn_camera_pixel_sels[] = {"clock-osc-24m", "sys_pll1_266m", + "sys_pll2_250m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", + "audio_pll2_out", "video_pll_out", }; + +static const char * const imx8mn_enet_ref_sels[] = {"clock-osc-24m", "sys_pll2_125m", + "sys_pll2_50m", "sys_pll2_100m", + "sys_pll1_160m", "audio_pll1_out", "video_pll_out", "clk_ext4", };
-static const char * const imx8mn_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out", - "clk_ext1", "clk_ext2", "clk_ext3", - "clk_ext4", "video_pll_out", }; +static const char * const imx8mn_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", + "audio_pll1_out", "clk_ext1", "clk_ext2", + "clk_ext3", "clk_ext4", "video_pll_out", };
static const char * const imx8mn_enet_phy_sels[] = {"clock-osc-24m", "sys_pll2_50m", "sys_pll2_125m", - "sys_pll2_200m", "sys_pll2_500m", "audio_pll1_out", - "video_pll_out", "audio_pll2_out", }; + "sys_pll2_200m", "sys_pll2_500m", + "audio_pll1_out", "video_pll_out", + "audio_pll2_out", }; #endif
static const char * const imx8mn_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", @@ -151,6 +187,10 @@ static const char * const imx8mn_usb_phy_sels[] = {"clock-osc-24m", "sys_pll1_10 "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", };
+#ifndef CONFIG_SPL_BUILD +static unsigned int share_count_disp; +#endif + static int imx8mn_clk_probe(struct udevice *dev) { struct clk osc_24m_clk; @@ -191,8 +231,19 @@ static int imx8mn_clk_probe(struct udevice *dev) clk_dm(IMX8MN_SYS_PLL3, imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx_1416x_pll)); + clk_dm(IMX8MN_VIDEO_PLL_REF_SEL, + imx_clk_mux("video_pll_ref_sel", base + 0x28, 0, 2, + pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); + clk_dm(IMX8MN_VIDEO_PLL1, + imx_clk_pll14xx("video_pll", "video_pll_ref_sel", + base + 0x28, &imx_1443x_pll));
/* PLL bypass out */ + clk_dm(IMX8MN_VIDEO_PLL_BYPASS, + imx_clk_mux_flags("video_pll_bypass", base + 0x28, 16, 1, + video_pll_bypass_sels, + ARRAY_SIZE(video_pll_bypass_sels), + CLK_SET_RATE_PARENT)); clk_dm(IMX8MN_DRAM_PLL_BYPASS, imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 4, 1, dram_pll_bypass_sels, @@ -220,6 +271,9 @@ static int imx8mn_clk_probe(struct udevice *dev) CLK_SET_RATE_PARENT));
/* PLL out gate */ + clk_dm(IMX8MN_VIDEO_PLL_OUT, + imx_clk_gate("video_pll_out", "video_pll_bypass", + base + 0x28, 13)); clk_dm(IMX8MN_DRAM_PLL_OUT, imx_clk_gate("dram_pll_out", "dram_pll_bypass", base + 0x50, 13)); @@ -376,6 +430,35 @@ static int imx8mn_clk_probe(struct udevice *dev)
/* clks not needed in SPL stage */ #ifndef CONFIG_SPL_BUILD + clk_dm(IMX8MN_CLK_DISP_AXI, + imx8m_clk_composite("disp_axi", imx8mn_disp_axi_sels, base + 0x8a00)); + clk_dm(IMX8MN_CLK_DISP_APB, + imx8m_clk_composite("disp_apb", imx8mn_disp_apb_sels, base + 0x8a80)); + clk_dm(IMX8MN_CLK_DISP_PIXEL, + __imx8m_clk_composite("disp_pixel", imx8mn_disp_pixel_sels, base + 0xa500, + CLK_SET_RATE_PARENT)); + clk_dm(IMX8MN_CLK_DSI_CORE, + imx8m_clk_composite("dsi_core", imx8mn_dsi_core_sels, base + 0xbb00)); + clk_dm(IMX8MN_CLK_DSI_PHY_REF, + imx8m_clk_composite("dsi_phy_ref", imx8mn_dsi_phy_sels, base + 0xbb80)); + clk_dm(IMX8MN_CLK_DSI_DBI, + imx8m_clk_composite("dsi_dbi", imx8mn_dsi_dbi_sels, base + 0xbc00)); + clk_dm(IMX8MN_CLK_CAMERA_PIXEL, + imx8m_clk_composite("camera_pixel", imx8mn_camera_pixel_sels, base + 0xbd00)); + + clk_dm(IMX8MN_CLK_DISP_AXI_ROOT, + imx_clk_gate2_shared2("disp_axi_root_clk", "disp_axi", base + 0x45d0, 0, + &share_count_disp)); + clk_dm(IMX8MN_CLK_DISP_APB_ROOT, + imx_clk_gate2_shared2("disp_apb_root_clk", "disp_apb", base + 0x45d0, 0, + &share_count_disp)); + clk_dm(IMX8MN_CLK_CAMERA_PIXEL_ROOT, + imx_clk_gate2_shared2("camera_pixel_clk", "camera_pixel", base + 0x45d0, 0, + &share_count_disp)); + clk_dm(IMX8MN_CLK_DISP_PIXEL_ROOT, + imx_clk_gate2_shared2("disp_pixel_clk", "disp_pixel", base + 0x45d0, 0, + &share_count_disp)); + clk_dm(IMX8MN_CLK_ENET_REF, imx8m_clk_composite("enet_ref", imx8mn_enet_ref_sels, base + 0xa980));

From: Michael Trimarchi michael@amarulasolutions.com
Add iMX8 block ctrl driver for displaymix on iMX8MM/iMX8MN and mediamix on iMX8MP.
To support blk ctrl driver, the power domain driver on iMX8M needs update to add relevant PGC domains
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/power/domain/Kconfig | 6 + drivers/power/domain/Makefile | 1 + drivers/power/domain/imx8m-blk-ctrl.c | 438 ++++++++++++++++++++++ drivers/power/domain/imx8m-power-domain.c | 213 ++++++++++- 4 files changed, 656 insertions(+), 2 deletions(-) create mode 100644 drivers/power/domain/imx8m-blk-ctrl.c
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index bd82d2f7044b..fb006b6e8e28 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -40,6 +40,12 @@ config IMX8M_POWER_DOMAIN Enable support for manipulating NXP i.MX8M on-SoC power domains via requests to the ATF.
+config IMX8M_BLK_CTRL + bool "Enable i.MX8M block control driver" + depends on POWER_DOMAIN && ARCH_IMX8M + help + Enable support for manipulating NXP i.MX8M on-SoC block control driver + config IMX8MP_HSIOMIX_BLKCTRL bool "Enable i.MX8MP HSIOMIX domain driver" depends on POWER_DOMAIN && IMX8MP diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 2daab73eb758..46849fd2a4db 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_APPLE_PMGR_POWER_DOMAIN) += apple-pmgr.o obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o obj-$(CONFIG_IMX8M_POWER_DOMAIN) += imx8m-power-domain.o +obj-$(CONFIG_IMX8M_BLK_CTRL) += imx8m-blk-ctrl.o obj-$(CONFIG_IMX8MP_HSIOMIX_BLKCTRL) += imx8mp-hsiomix.o obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c new file mode 100644 index 000000000000..4c89078b991b --- /dev/null +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP + */ + +#include <dm.h> +#include <malloc.h> +#include <power-domain-uclass.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <dm/device.h> +#include <dt-bindings/power/imx8mm-power.h> +#include <dt-bindings/power/imx8mn-power.h> +#include <dt-bindings/power/imx8mp-power.h> +#include <clk.h> +#include <linux/delay.h> + +#define BLK_SFT_RSTN 0x0 +#define BLK_CLK_EN 0x4 +#define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */ + +#define DOMAIN_MAX_CLKS 4 + +struct imx8m_blk_ctrl_domain { + struct clk clks[DOMAIN_MAX_CLKS]; + struct power_domain power_dev; +}; + +struct imx8m_blk_ctrl { + void __iomem *base; + struct power_domain bus_power_dev; + struct imx8m_blk_ctrl_domain *domains; +}; + +struct imx8m_blk_ctrl_domain_data { + const char *name; + const char * const *clk_names; + const char *gpc_name; + int num_clks; + u32 rst_mask; + u32 clk_mask; + u32 mipi_phy_rst_mask; +}; + +struct imx8m_blk_ctrl_data { + int max_reg; + const struct imx8m_blk_ctrl_domain_data *domains; + int num_domains; + u32 bus_rst_mask; + u32 bus_clk_mask; +}; + +static int imx8m_blk_ctrl_request(struct power_domain *power_domain) +{ + return 0; +} + +static int imx8m_blk_ctrl_free(struct power_domain *power_domain) +{ + return 0; +} + +static int imx8m_blk_ctrl_enable_domain_clk(struct udevice *dev, ulong domain_id, bool enable) +{ + int ret, i; + struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev); + struct imx8m_blk_ctrl_data *drv_data = + (struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev); + + debug("%s num_clk %u\n", __func__, drv_data->domains[domain_id].num_clks); + + for (i = 0; i < drv_data->domains[domain_id].num_clks; i++) { + debug("%s clk %s\n", __func__, drv_data->domains[domain_id].clk_names[i]); + if (enable) + ret = clk_enable(&priv->domains[domain_id].clks[i]); + else + ret = clk_disable(&priv->domains[domain_id].clks[i]); + if (ret && ret != -ENOENT) { + printf("Failed to %s domain clk %s\n", enable ? "enable" : "disable", + drv_data->domains[domain_id].clk_names[i]); + return ret; + } + } + + return 0; +} + +static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) +{ + struct udevice *dev = power_domain->dev; + struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev); + struct imx8m_blk_ctrl_data *drv_data = + (struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev); + int ret; + + debug("%s, id %lu\n", __func__, power_domain->id); + + if (!priv->domains[power_domain->id].power_dev.dev) + return -ENODEV; + + ret = power_domain_on(&priv->bus_power_dev); + if (ret < 0) { + printf("Failed to power up bus domain %d\n", ret); + return ret; + } + + /* Enable bus clock and deassert bus reset */ + setbits_le32(priv->base + BLK_CLK_EN, drv_data->bus_clk_mask); + setbits_le32(priv->base + BLK_SFT_RSTN, drv_data->bus_rst_mask); + + /* wait for reset to propagate */ + udelay(5); + + /* put devices into reset */ + clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask); + if (drv_data->domains[power_domain->id].mipi_phy_rst_mask) + clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d + rv_data->domains[power_domain->id].mipi_phy_rst_mask); + + /* enable upstream and blk-ctrl clocks to allow reset to propagate */ + ret = imx8m_blk_ctrl_enable_domain_clk(dev, power_domain->id, true); + if (ret) { + printf("failed to enable clocks\n"); + goto bus_powerdown; + } + + /* ungate clk */ + setbits_le32(priv->base + BLK_CLK_EN, drv_data->domains[power_domain->id].clk_mask); + + /* power up upstream GPC domain */ + ret = power_domain_on(&priv->domains[power_domain->id].power_dev); + if (ret < 0) { + printf("Failed to power up peripheral domain %d\n", ret); + goto clk_disable; + } + + /* wait for reset to propagate */ + udelay(5); + + /* release reset */ + setbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask); + if (drv_data->domains[power_domain->id].mipi_phy_rst_mask) + setbits_le32(priv->base + BLK_MIPI_RESET_DIV, + drv_data->domains[power_domain->id].mipi_phy_rst_mask); + + return 0; +clk_disable: + imx8m_blk_ctrl_enable_domain_clk(dev, power_domain->id, false); +bus_powerdown: + power_domain_off(&priv->bus_power_dev); + return ret; +} + +static int imx8m_blk_ctrl_power_off(struct power_domain *power_domain) +{ + struct udevice *dev = power_domain->dev; + struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev); + struct imx8m_blk_ctrl_data *drv_data = + (struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev); + + debug("%s, id %lu\n", __func__, power_domain->id); + + if (!priv->domains[power_domain->id].power_dev.dev) + return -ENODEV; + + /* put devices into reset and disable clocks */ + if (drv_data->domains[power_domain->id].mipi_phy_rst_mask) + clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, + drv_data->domains[power_domain->id].mipi_phy_rst_mask); + + /* assert reset */ + clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask); + + /* gate clk */ + clrbits_le32(priv->base + BLK_CLK_EN, drv_data->domains[power_domain->id].clk_mask); + + /* power down upstream GPC domain */ + power_domain_off(&priv->domains[power_domain->id].power_dev); + + imx8m_blk_ctrl_enable_domain_clk(dev, power_domain->id, false); + + /* power down bus domain */ + power_domain_off(&priv->bus_power_dev); + + return 0; +} + +static int imx8m_blk_ctrl_probe(struct udevice *dev) +{ + int ret, i, j; + struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev); + struct imx8m_blk_ctrl_data *drv_data = + (struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev); + + priv->base = dev_read_addr_ptr(dev); + if (!priv->base) + return -EINVAL; + + priv->domains = kcalloc(drv_data->num_domains, + sizeof(struct imx8m_blk_ctrl_domain), GFP_KERNEL); + + ret = power_domain_get_by_name(dev, &priv->bus_power_dev, "bus"); + if (ret) { + printf("Failed to power_domain_get_by_name %s\n", "bus"); + return ret; + } + + for (j = 0; j < drv_data->num_domains; j++) { + ret = power_domain_get_by_name(dev, &priv->domains[j].power_dev, + drv_data->domains[j].gpc_name); + if (ret) + continue; + + for (i = 0; i < drv_data->domains[j].num_clks; i++) { + ret = clk_get_by_name(dev, drv_data->domains[j].clk_names[i], + &priv->domains[j].clks[i]); + if (ret) { + printf("Failed to get clk %s\n", drv_data->domains[j].clk_names[i]); + return ret; + } + } + } + + return 0; +} + +static int imx8m_blk_ctrl_remove(struct udevice *dev) +{ + struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev); + + kfree(priv->domains); + + return 0; +} + +static const struct imx8m_blk_ctrl_domain_data imx8mm_disp_blk_ctl_domain_data[] = { + [IMX8MM_DISPBLK_PD_CSI_BRIDGE] = { + .name = "dispblk-csi-bridge", + .clk_names = (const char *[]){ "csi-bridge-axi", "csi-bridge-apb", + "csi-bridge-core", }, + .num_clks = 3, + .gpc_name = "csi-bridge", + .rst_mask = BIT(0) | BIT(1) | BIT(2), + .clk_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5), + }, + [IMX8MM_DISPBLK_PD_LCDIF] = { + .name = "dispblk-lcdif", + .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", }, + .num_clks = 3, + .gpc_name = "lcdif", + .clk_mask = BIT(6) | BIT(7), + }, + [IMX8MM_DISPBLK_PD_MIPI_DSI] = { + .name = "dispblk-mipi-dsi", + .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", }, + .num_clks = 2, + .gpc_name = "mipi-dsi", + .rst_mask = BIT(5), + .clk_mask = BIT(8) | BIT(9), + .mipi_phy_rst_mask = BIT(17), + }, + [IMX8MM_DISPBLK_PD_MIPI_CSI] = { + .name = "dispblk-mipi-csi", + .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" }, + .num_clks = 2, + .gpc_name = "mipi-csi", + .rst_mask = BIT(3) | BIT(4), + .clk_mask = BIT(10) | BIT(11), + .mipi_phy_rst_mask = BIT(16), + }, +}; + +static const struct imx8m_blk_ctrl_data imx8mm_disp_blk_ctl_dev_data = { + .max_reg = 0x2c, + .domains = imx8mm_disp_blk_ctl_domain_data, + .num_domains = ARRAY_SIZE(imx8mm_disp_blk_ctl_domain_data), + .bus_rst_mask = BIT(6), + .bus_clk_mask = BIT(12), +}; + +static const struct imx8m_blk_ctrl_domain_data imx8mn_disp_blk_ctl_domain_data[] = { + [IMX8MN_DISPBLK_PD_MIPI_DSI] = { + .name = "dispblk-mipi-dsi", + .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", }, + .num_clks = 2, + .gpc_name = "mipi-dsi", + .rst_mask = BIT(0) | BIT(1), + .clk_mask = BIT(0) | BIT(1), + .mipi_phy_rst_mask = BIT(17), + }, + [IMX8MN_DISPBLK_PD_MIPI_CSI] = { + .name = "dispblk-mipi-csi", + .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" }, + .num_clks = 2, + .gpc_name = "mipi-csi", + .rst_mask = BIT(2) | BIT(3), + .clk_mask = BIT(2) | BIT(3), + .mipi_phy_rst_mask = BIT(16), + }, + [IMX8MN_DISPBLK_PD_LCDIF] = { + .name = "dispblk-lcdif", + .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", }, + .num_clks = 3, + .gpc_name = "lcdif", + .rst_mask = BIT(4) | BIT(5), + .clk_mask = BIT(4) | BIT(5), + }, + [IMX8MN_DISPBLK_PD_ISI] = { + .name = "dispblk-isi", + .clk_names = (const char *[]){ "disp_axi", "disp_apb", "disp_axi_root", + "disp_apb_root"}, + .num_clks = 4, + .gpc_name = "isi", + .rst_mask = BIT(6) | BIT(7), + .clk_mask = BIT(6) | BIT(7), + }, +}; + +static const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = { + .max_reg = 0x84, + .domains = imx8mn_disp_blk_ctl_domain_data, + .num_domains = ARRAY_SIZE(imx8mn_disp_blk_ctl_domain_data), + .bus_rst_mask = BIT(8), + .bus_clk_mask = BIT(8), +}; + +static const struct imx8m_blk_ctrl_domain_data imx8mp_media_blk_ctl_domain_data[] = { + [IMX8MP_MEDIABLK_PD_MIPI_DSI_1] = { + .name = "mediablk-mipi-dsi-1", + .clk_names = (const char *[]){ "apb", "phy", }, + .num_clks = 2, + .gpc_name = "mipi-dsi1", + .rst_mask = BIT(0) | BIT(1), + .clk_mask = BIT(0) | BIT(1), + .mipi_phy_rst_mask = BIT(17), + }, + [IMX8MP_MEDIABLK_PD_MIPI_CSI2_1] = { + .name = "mediablk-mipi-csi2-1", + .clk_names = (const char *[]){ "apb", "cam1" }, + .num_clks = 2, + .gpc_name = "mipi-csi1", + .rst_mask = BIT(2) | BIT(3), + .clk_mask = BIT(2) | BIT(3), + .mipi_phy_rst_mask = BIT(16), + }, + [IMX8MP_MEDIABLK_PD_LCDIF_1] = { + .name = "mediablk-lcdif-1", + .clk_names = (const char *[]){ "disp1", "apb", "axi", }, + .num_clks = 3, + .gpc_name = "lcdif1", + .rst_mask = BIT(4) | BIT(5) | BIT(23), + .clk_mask = BIT(4) | BIT(5) | BIT(23), + }, + [IMX8MP_MEDIABLK_PD_ISI] = { + .name = "mediablk-isi", + .clk_names = (const char *[]){ "axi", "apb" }, + .num_clks = 2, + .gpc_name = "isi", + .rst_mask = BIT(6) | BIT(7), + .clk_mask = BIT(6) | BIT(7), + }, + [IMX8MP_MEDIABLK_PD_MIPI_CSI2_2] = { + .name = "mediablk-mipi-csi2-2", + .clk_names = (const char *[]){ "apb", "cam2" }, + .num_clks = 2, + .gpc_name = "mipi-csi2", + .rst_mask = BIT(9) | BIT(10), + .clk_mask = BIT(9) | BIT(10), + .mipi_phy_rst_mask = BIT(30), + }, + [IMX8MP_MEDIABLK_PD_LCDIF_2] = { + .name = "mediablk-lcdif-2", + .clk_names = (const char *[]){ "disp2", "apb", "axi", }, + .num_clks = 3, + .gpc_name = "lcdif2", + .rst_mask = BIT(11) | BIT(12) | BIT(24), + .clk_mask = BIT(11) | BIT(12) | BIT(24), + }, + [IMX8MP_MEDIABLK_PD_ISP] = { + .name = "mediablk-isp", + .clk_names = (const char *[]){ "isp", "axi", "apb" }, + .num_clks = 3, + .gpc_name = "isp", + .rst_mask = BIT(16) | BIT(17) | BIT(18), + .clk_mask = BIT(16) | BIT(17) | BIT(18), + }, + [IMX8MP_MEDIABLK_PD_DWE] = { + .name = "mediablk-dwe", + .clk_names = (const char *[]){ "axi", "apb" }, + .num_clks = 2, + .gpc_name = "dwe", + .rst_mask = BIT(19) | BIT(20) | BIT(21), + .clk_mask = BIT(19) | BIT(20) | BIT(21), + }, + [IMX8MP_MEDIABLK_PD_MIPI_DSI_2] = { + .name = "mediablk-mipi-dsi-2", + .clk_names = (const char *[]){ "phy", }, + .num_clks = 1, + .gpc_name = "mipi-dsi2", + .rst_mask = BIT(22), + .clk_mask = BIT(22), + .mipi_phy_rst_mask = BIT(29), + }, +}; + +static const struct imx8m_blk_ctrl_data imx8mp_media_blk_ctl_dev_data = { + .max_reg = 0x138, + .domains = imx8mp_media_blk_ctl_domain_data, + .num_domains = ARRAY_SIZE(imx8mp_media_blk_ctl_domain_data), + .bus_rst_mask = BIT(8), + .bus_clk_mask = BIT(8), +}; + +static const struct udevice_id imx8m_blk_ctrl_ids[] = { + {.compatible = "fsl,imx8mm-disp-blk-ctrl", .data = (ulong)&imx8mm_disp_blk_ctl_dev_data}, + {.compatible = "fsl,imx8mn-disp-blk-ctrl", .data = (ulong)&imx8mn_disp_blk_ctl_dev_data}, + {.compatible = "fsl,imx8mp-media-blk-ctrl", .data = (ulong)&imx8mp_media_blk_ctl_dev_data}, + {} +}; + +struct power_domain_ops imx8m_blk_ctrl_ops = { + .request = imx8m_blk_ctrl_request, + .rfree = imx8m_blk_ctrl_free, + .on = imx8m_blk_ctrl_power_on, + .off = imx8m_blk_ctrl_power_off, +}; + +U_BOOT_DRIVER(imx8m_blk_ctrl) = { + .name = "imx8m_blk_ctrl", + .id = UCLASS_POWER_DOMAIN, + .of_match = imx8m_blk_ctrl_ids, + .bind = dm_scan_fdt_dev, + .probe = imx8m_blk_ctrl_probe, + .remove = imx8m_blk_ctrl_remove, + .priv_auto = sizeof(struct imx8m_blk_ctrl), + .ops = &imx8m_blk_ctrl_ops, + .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF, +}; diff --git a/drivers/power/domain/imx8m-power-domain.c b/drivers/power/domain/imx8m-power-domain.c index 8b6870c86463..40fec70d954a 100644 --- a/drivers/power/domain/imx8m-power-domain.c +++ b/drivers/power/domain/imx8m-power-domain.c @@ -32,17 +32,31 @@ DECLARE_GLOBAL_DATA_PTR; #define IMX8M_OTG1_A53_DOMAIN BIT(4) #define IMX8M_PCIE1_A53_DOMAIN BIT(3)
+#define IMX8MM_VPUH1_A53_DOMAIN BIT(15) +#define IMX8MM_VPUG2_A53_DOMAIN BIT(14) +#define IMX8MM_VPUG1_A53_DOMAIN BIT(13) +#define IMX8MM_DISPMIX_A53_DOMAIN BIT(12) +#define IMX8MM_VPUMIX_A53_DOMAIN BIT(10) +#define IMX8MM_GPUMIX_A53_DOMAIN BIT(9) +#define IMX8MM_GPU_A53_DOMAIN (BIT(8) | BIT(11)) +#define IMX8MM_DDR1_A53_DOMAIN BIT(7) #define IMX8MM_OTG2_A53_DOMAIN BIT(5) #define IMX8MM_OTG1_A53_DOMAIN BIT(4) #define IMX8MM_PCIE_A53_DOMAIN BIT(3) +#define IMX8MM_MIPI_A53_DOMAIN BIT(2)
+#define IMX8MN_DISPMIX_A53_DOMAIN BIT(12) +#define IMX8MN_GPUMIX_A53_DOMAIN BIT(9) +#define IMX8MN_DDR1_A53_DOMAIN BIT(7) #define IMX8MN_OTG1_A53_DOMAIN BIT(4) #define IMX8MN_MIPI_A53_DOMAIN BIT(2)
#define IMX8MP_HSIOMIX_A53_DOMAIN BIT(19) +#define IMX8MP_MEDIAMIX_A53_DOMAIN BIT(12) #define IMX8MP_USB2_PHY_A53_DOMAIN BIT(5) #define IMX8MP_USB1_PHY_A53_DOMAIN BIT(4) #define IMX8MP_PCIE_PHY_A53_DOMAIN BIT(3) +#define IMX8MP_MIPI_PHY1_A53_DOMAIN BIT(2)
#define IMX8MP_GPC_PU_PGC_SW_PUP_REQ 0x0d8 #define IMX8MP_GPC_PU_PGC_SW_PDN_REQ 0x0e4 @@ -50,35 +64,72 @@ DECLARE_GLOBAL_DATA_PTR; #define GPC_PU_PGC_SW_PUP_REQ 0x0f8 #define GPC_PU_PGC_SW_PDN_REQ 0x104
+#define IMX8M_PCIE2_SW_Pxx_REQ BIT(13) +#define IMX8M_MIPI_CSI2_SW_Pxx_REQ BIT(12) +#define IMX8M_MIPI_CSI1_SW_Pxx_REQ BIT(11) +#define IMX8M_DISP_SW_Pxx_REQ BIT(10) +#define IMX8M_HDMI_SW_Pxx_REQ BIT(9) +#define IMX8M_VPU_SW_Pxx_REQ BIT(8) +#define IMX8M_GPU_SW_Pxx_REQ BIT(7) +#define IMX8M_DDR2_SW_Pxx_REQ BIT(6) +#define IMX8M_DDR1_SW_Pxx_REQ BIT(5) #define IMX8M_PCIE2_SW_Pxx_REQ BIT(13) #define IMX8M_OTG2_SW_Pxx_REQ BIT(3) #define IMX8M_OTG1_SW_Pxx_REQ BIT(2) #define IMX8M_PCIE1_SW_Pxx_REQ BIT(1)
+#define IMX8MM_VPUH1_SW_Pxx_REQ BIT(13) +#define IMX8MM_VPUG2_SW_Pxx_REQ BIT(12) +#define IMX8MM_VPUG1_SW_Pxx_REQ BIT(11) +#define IMX8MM_DISPMIX_SW_Pxx_REQ BIT(10) +#define IMX8MM_VPUMIX_SW_Pxx_REQ BIT(8) +#define IMX8MM_GPUMIX_SW_Pxx_REQ BIT(7) +#define IMX8MM_GPU_SW_Pxx_REQ (BIT(6) | BIT(9)) +#define IMX8MM_DDR1_SW_Pxx_REQ BIT(5) #define IMX8MM_OTG2_SW_Pxx_REQ BIT(3) #define IMX8MM_OTG1_SW_Pxx_REQ BIT(2) #define IMX8MM_PCIE_SW_Pxx_REQ BIT(1) +#define IMX8MM_MIPI_SW_Pxx_REQ BIT(0)
+#define IMX8MN_DISPMIX_SW_Pxx_REQ BIT(10) +#define IMX8MN_GPUMIX_SW_Pxx_REQ BIT(7) +#define IMX8MN_DDR1_SW_Pxx_REQ BIT(5) #define IMX8MN_OTG1_SW_Pxx_REQ BIT(2) #define IMX8MN_MIPI_SW_Pxx_REQ BIT(0)
#define IMX8MP_HSIOMIX_Pxx_REQ BIT(17) +#define IMX8MP_MEDIMIX_Pxx_REQ BIT(10) #define IMX8MP_USB2_PHY_Pxx_REQ BIT(3) #define IMX8MP_USB1_PHY_Pxx_REQ BIT(2) #define IMX8MP_PCIE_PHY_SW_Pxx_REQ BIT(1) +#define IMX8MP_MIPI_PHY1_SW_Pxx_REQ BIT(0)
#define GPC_M4_PU_PDN_FLG 0x1bc
#define IMX8MP_GPC_PU_PWRHSK 0x190 #define GPC_PU_PWRHSK 0x1fc
+#define IMX8MM_GPUMIX_HSK_PWRDNACKN BIT(29) +#define IMX8MM_GPU_HSK_PWRDNACKN (BIT(27) | BIT(28)) +#define IMX8MM_VPUMIX_HSK_PWRDNACKN BIT(26) +#define IMX8MM_DISPMIX_HSK_PWRDNACKN BIT(25) #define IMX8MM_HSIO_HSK_PWRDNACKN (BIT(23) | BIT(24)) +#define IMX8MM_GPUMIX_HSK_PWRDNREQN BIT(11) +#define IMX8MM_GPU_HSK_PWRDNREQN (BIT(9) | BIT(10)) +#define IMX8MM_VPUMIX_HSK_PWRDNREQN BIT(8) +#define IMX8MM_DISPMIX_HSK_PWRDNREQN BIT(7) #define IMX8MM_HSIO_HSK_PWRDNREQN (BIT(5) | BIT(6))
+#define IMX8MN_GPUMIX_HSK_PWRDNACKN (BIT(29) | BIT(27)) +#define IMX8MN_DISPMIX_HSK_PWRDNACKN BIT(25) #define IMX8MN_HSIO_HSK_PWRDNACKN BIT(23) +#define IMX8MN_GPUMIX_HSK_PWRDNREQN (BIT(11) | BIT(9)) +#define IMX8MN_DISPMIX_HSK_PWRDNREQN BIT(7) #define IMX8MN_HSIO_HSK_PWRDNREQN BIT(5)
+#define IMX8MP_MEDIAMIX_PWRDNACKN BIT(30) #define IMX8MP_HSIOMIX_PWRDNACKN BIT(28) +#define IMX8MP_MEDIAMIX_PWRDNREQN BIT(14) #define IMX8MP_HSIOMIX_PWRDNREQN BIT(12)
/* @@ -92,15 +143,31 @@ DECLARE_GLOBAL_DATA_PTR; #define IMX8M_PGC_OTG2 19 #define IMX8M_PGC_PCIE2 29
+#define IMX8MM_PGC_MIPI 16 #define IMX8MM_PGC_PCIE 17 #define IMX8MM_PGC_OTG1 18 #define IMX8MM_PGC_OTG2 19 - +#define IMX8MM_PGC_DDR1 21 +#define IMX8MM_PGC_GPU2D 22 +#define IMX8MM_PGC_GPUMIX 23 +#define IMX8MM_PGC_VPUMIX 24 +#define IMX8MM_PGC_GPU3D 25 +#define IMX8MM_PGC_DISPMIX 26 +#define IMX8MM_PGC_VPUG1 27 +#define IMX8MM_PGC_VPUG2 28 +#define IMX8MM_PGC_VPUH1 29 + +#define IMX8MN_PGC_MIPI 16 #define IMX8MN_PGC_OTG1 18 +#define IMX8MN_PGC_DDR1 21 +#define IMX8MN_PGC_GPUMIX 23 +#define IMX8MN_PGC_DISPMIX 26
+#define IMX8MP_PGC_MIPI1 12 #define IMX8MP_PGC_PCIE 13 #define IMX8MP_PGC_USB1 14 #define IMX8MP_PGC_USB2 15 +#define IMX8MP_PGC_MEDIAMIX 22 #define IMX8MP_PGC_HSIOMIX 29
#define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40) @@ -142,6 +209,7 @@ struct imx8m_power_domain_plat { void __iomem *base; int resource_id; int has_pd; + int count; };
#if defined(CONFIG_IMX8MM) || defined(CONFIG_IMX8MN) || defined(CONFIG_IMX8MQ) @@ -230,6 +298,82 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = { }, .pgc = BIT(IMX8MM_PGC_OTG2), }, + + [IMX8MM_POWER_DOMAIN_GPUMIX] = { + .bits = { + .pxx = IMX8MM_GPUMIX_SW_Pxx_REQ, + .map = IMX8MM_GPUMIX_A53_DOMAIN, + .hskreq = IMX8MM_GPUMIX_HSK_PWRDNREQN, + .hskack = IMX8MM_GPUMIX_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MM_PGC_GPUMIX), + .keep_clocks = true, + }, + + [IMX8MM_POWER_DOMAIN_GPU] = { + .bits = { + .pxx = IMX8MM_GPU_SW_Pxx_REQ, + .map = IMX8MM_GPU_A53_DOMAIN, + .hskreq = IMX8MM_GPU_HSK_PWRDNREQN, + .hskack = IMX8MM_GPU_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MM_PGC_GPU2D) | BIT(IMX8MM_PGC_GPU3D), + }, + + [IMX8MM_POWER_DOMAIN_VPUMIX] = { + .bits = { + .pxx = IMX8MM_VPUMIX_SW_Pxx_REQ, + .map = IMX8MM_VPUMIX_A53_DOMAIN, + .hskreq = IMX8MM_VPUMIX_HSK_PWRDNREQN, + .hskack = IMX8MM_VPUMIX_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MM_PGC_VPUMIX), + .keep_clocks = true, + }, + + [IMX8MM_POWER_DOMAIN_VPUG1] = { + .bits = { + .pxx = IMX8MM_VPUG1_SW_Pxx_REQ, + .map = IMX8MM_VPUG1_A53_DOMAIN, + }, + .pgc = BIT(IMX8MM_PGC_VPUG1), + }, + + [IMX8MM_POWER_DOMAIN_VPUG2] = { + .bits = { + .pxx = IMX8MM_VPUG2_SW_Pxx_REQ, + .map = IMX8MM_VPUG2_A53_DOMAIN, + }, + .pgc = BIT(IMX8MM_PGC_VPUG2), + }, + + [IMX8MM_POWER_DOMAIN_VPUH1] = { + .bits = { + .pxx = IMX8MM_VPUH1_SW_Pxx_REQ, + .map = IMX8MM_VPUH1_A53_DOMAIN, + }, + .pgc = BIT(IMX8MM_PGC_VPUH1), + .keep_clocks = true, + }, + + [IMX8MM_POWER_DOMAIN_DISPMIX] = { + .bits = { + .pxx = IMX8MM_DISPMIX_SW_Pxx_REQ, + .map = IMX8MM_DISPMIX_A53_DOMAIN, + .hskreq = IMX8MM_DISPMIX_HSK_PWRDNREQN, + .hskack = IMX8MM_DISPMIX_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MM_PGC_DISPMIX), + .keep_clocks = true, + }, + + [IMX8MM_POWER_DOMAIN_MIPI] = { + .bits = { + .pxx = IMX8MM_MIPI_SW_Pxx_REQ, + .map = IMX8MM_MIPI_A53_DOMAIN, + }, + .pgc = BIT(IMX8MM_PGC_MIPI), + }, };
static const struct imx_pgc_domain_data imx8mm_pgc_domain_data = { @@ -258,6 +402,36 @@ static const struct imx_pgc_domain imx8mn_pgc_domains[] = { }, .pgc = BIT(IMX8MN_PGC_OTG1), }, + + [IMX8MN_POWER_DOMAIN_GPUMIX] = { + .bits = { + .pxx = IMX8MN_GPUMIX_SW_Pxx_REQ, + .map = IMX8MN_GPUMIX_A53_DOMAIN, + .hskreq = IMX8MN_GPUMIX_HSK_PWRDNREQN, + .hskack = IMX8MN_GPUMIX_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MN_PGC_GPUMIX), + .keep_clocks = true, + }, + + [IMX8MN_POWER_DOMAIN_DISPMIX] = { + .bits = { + .pxx = IMX8MN_DISPMIX_SW_Pxx_REQ, + .map = IMX8MN_DISPMIX_A53_DOMAIN, + .hskreq = IMX8MN_DISPMIX_HSK_PWRDNREQN, + .hskack = IMX8MN_DISPMIX_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MN_PGC_DISPMIX), + .keep_clocks = true, + }, + + [IMX8MN_POWER_DOMAIN_MIPI] = { + .bits = { + .pxx = IMX8MN_MIPI_SW_Pxx_REQ, + .map = IMX8MN_MIPI_A53_DOMAIN, + }, + .pgc = BIT(IMX8MN_PGC_MIPI), + }, };
static const struct imx_pgc_domain_data imx8mn_pgc_domain_data = { @@ -268,7 +442,15 @@ static const struct imx_pgc_domain_data imx8mn_pgc_domain_data = { #endif
#ifdef CONFIG_IMX8MP -static const struct imx_pgc_domain imx8mp_pgc_domains[] = { +static const struct imx_pgc_domain imx8mp_pgc_domains[19] = { + [IMX8MP_POWER_DOMAIN_MIPI_PHY1] = { + .bits = { + .pxx = IMX8MP_MIPI_PHY1_SW_Pxx_REQ, + .map = IMX8MP_MIPI_PHY1_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_MIPI1), + }, + [IMX8MP_POWER_DOMAIN_PCIE_PHY] = { .bits = { .pxx = IMX8MP_PCIE_PHY_SW_Pxx_REQ, @@ -293,6 +475,17 @@ static const struct imx_pgc_domain imx8mp_pgc_domains[] = { .pgc = BIT(IMX8MP_PGC_USB2), },
+ [IMX8MP_POWER_DOMAIN_MEDIAMIX] = { + .bits = { + .pxx = IMX8MP_MEDIMIX_Pxx_REQ, + .map = IMX8MP_MEDIAMIX_A53_DOMAIN, + .hskreq = IMX8MP_MEDIAMIX_PWRDNREQN, + .hskack = IMX8MP_MEDIAMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_MEDIAMIX), + .keep_clocks = true, + }, + [IMX8MP_POWER_DOMAIN_HSIOMIX] = { .bits = { .pxx = IMX8MP_HSIOMIX_Pxx_REQ, @@ -329,6 +522,11 @@ static int imx8m_power_domain_on(struct power_domain *power_domain) u32 pgc; int ret;
+ if (pdata->count > 0) { /* Already on */ + pdata->count++; + return 0; + } + if (pdata->clk.count) { ret = clk_enable_bulk(&pdata->clk); if (ret) { @@ -373,6 +571,8 @@ static int imx8m_power_domain_on(struct power_domain *power_domain) if (!domain->keep_clocks && pdata->clk.count) clk_disable_bulk(&pdata->clk);
+ pdata->count++; + return 0;
out_clk_disable: @@ -391,6 +591,13 @@ static int imx8m_power_domain_off(struct power_domain *power_domain) u32 pgc; int ret;
+ if (!pdata->count) { /* Already off */ + return 0; + } else if (pdata->count > 1) { + pdata->count--; + return 0; + } + /* Enable reset clocks for all devices in the domain */ if (!domain->keep_clocks && pdata->clk.count) { ret = clk_enable_bulk(&pdata->clk); @@ -439,6 +646,8 @@ static int imx8m_power_domain_off(struct power_domain *power_domain) if (pdata->has_pd) power_domain_off(&pdata->pd);
+ pdata->count--; + return 0;
out_clk_disable:

Hello Dario,
On 13.09.24 11:55, Dario Binacchi wrote:
From: Michael Trimarchi michael@amarulasolutions.com
Add iMX8 block ctrl driver for displaymix on iMX8MM/iMX8MN and mediamix on iMX8MP.
To support blk ctrl driver, the power domain driver on iMX8M needs update to add relevant PGC domains
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com
drivers/power/domain/Kconfig | 6 + drivers/power/domain/Makefile | 1 + drivers/power/domain/imx8m-blk-ctrl.c | 438 ++++++++++++++++++++++ drivers/power/domain/imx8m-power-domain.c | 213 ++++++++++- 4 files changed, 656 insertions(+), 2 deletions(-) create mode 100644 drivers/power/domain/imx8m-blk-ctrl.c
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index bd82d2f7044b..fb006b6e8e28 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -40,6 +40,12 @@ config IMX8M_POWER_DOMAIN Enable support for manipulating NXP i.MX8M on-SoC power domains via requests to the ATF.
+config IMX8M_BLK_CTRL
- bool "Enable i.MX8M block control driver"
- depends on POWER_DOMAIN && ARCH_IMX8M
- help
Enable support for manipulating NXP i.MX8M on-SoC block control driver
- config IMX8MP_HSIOMIX_BLKCTRL bool "Enable i.MX8MP HSIOMIX domain driver" depends on POWER_DOMAIN && IMX8MP
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 2daab73eb758..46849fd2a4db 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_APPLE_PMGR_POWER_DOMAIN) += apple-pmgr.o obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o obj-$(CONFIG_IMX8M_POWER_DOMAIN) += imx8m-power-domain.o +obj-$(CONFIG_IMX8M_BLK_CTRL) += imx8m-blk-ctrl.o obj-$(CONFIG_IMX8MP_HSIOMIX_BLKCTRL) += imx8mp-hsiomix.o obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c new file mode 100644 index 000000000000..4c89078b991b --- /dev/null +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright 2023 NXP
- */
+#include <dm.h> +#include <malloc.h> +#include <power-domain-uclass.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <dm/device.h> +#include <dt-bindings/power/imx8mm-power.h> +#include <dt-bindings/power/imx8mn-power.h> +#include <dt-bindings/power/imx8mp-power.h> +#include <clk.h> +#include <linux/delay.h>
+#define BLK_SFT_RSTN 0x0 +#define BLK_CLK_EN 0x4 +#define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */
+#define DOMAIN_MAX_CLKS 4
+struct imx8m_blk_ctrl_domain {
- struct clk clks[DOMAIN_MAX_CLKS];
- struct power_domain power_dev;
+};
+struct imx8m_blk_ctrl {
- void __iomem *base;
- struct power_domain bus_power_dev;
- struct imx8m_blk_ctrl_domain *domains;
+};
+struct imx8m_blk_ctrl_domain_data {
- const char *name;
- const char * const *clk_names;
- const char *gpc_name;
- int num_clks;
- u32 rst_mask;
- u32 clk_mask;
- u32 mipi_phy_rst_mask;
+};
+struct imx8m_blk_ctrl_data {
- int max_reg;
- const struct imx8m_blk_ctrl_domain_data *domains;
- int num_domains;
- u32 bus_rst_mask;
- u32 bus_clk_mask;
+};
+static int imx8m_blk_ctrl_request(struct power_domain *power_domain) +{
- return 0;
+}
+static int imx8m_blk_ctrl_free(struct power_domain *power_domain) +{
- return 0;
+}
+static int imx8m_blk_ctrl_enable_domain_clk(struct udevice *dev, ulong domain_id, bool enable) +{
- int ret, i;
- struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev);
- struct imx8m_blk_ctrl_data *drv_data =
(struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev);
- debug("%s num_clk %u\n", __func__, drv_data->domains[domain_id].num_clks);
- for (i = 0; i < drv_data->domains[domain_id].num_clks; i++) {
debug("%s clk %s\n", __func__, drv_data->domains[domain_id].clk_names[i]);
if (enable)
ret = clk_enable(&priv->domains[domain_id].clks[i]);
else
ret = clk_disable(&priv->domains[domain_id].clks[i]);
if (ret && ret != -ENOENT) {
printf("Failed to %s domain clk %s\n", enable ? "enable" : "disable",
drv_data->domains[domain_id].clk_names[i]);
return ret;
}
- }
- return 0;
+}
+static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) +{
- struct udevice *dev = power_domain->dev;
- struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev);
- struct imx8m_blk_ctrl_data *drv_data =
(struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev);
- int ret;
- debug("%s, id %lu\n", __func__, power_domain->id);
- if (!priv->domains[power_domain->id].power_dev.dev)
return -ENODEV;
- ret = power_domain_on(&priv->bus_power_dev);
- if (ret < 0) {
printf("Failed to power up bus domain %d\n", ret);
return ret;
- }
- /* Enable bus clock and deassert bus reset */
- setbits_le32(priv->base + BLK_CLK_EN, drv_data->bus_clk_mask);
- setbits_le32(priv->base + BLK_SFT_RSTN, drv_data->bus_rst_mask);
- /* wait for reset to propagate */
- udelay(5);
- /* put devices into reset */
- clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask);
- if (drv_data->domains[power_domain->id].mipi_phy_rst_mask)
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d
rv_data->domains[power_domain->id].mipi_phy_rst_mask);
Does this build for you?
I needed the following fix:
diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c index 4c89078b99..b772d50480 100644 --- a/drivers/power/domain/imx8m-blk-ctrl.c +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -114,8 +114,8 @@ static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) /* put devices into reset */ clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask); if (drv_data->domains[power_domain->id].mipi_phy_rst_mask) - clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d - rv_data->domains[power_domain->id].mipi_phy_rst_mask); + clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, + drv_data->domains[power_domain->id].mipi_phy_rst_mask);
/* enable upstream and blk-ctrl clocks to allow reset to propagate */ ret = imx8m_blk_ctrl_enable_domain_clk(dev, power_domain->id, true);
to get it building.
BTW: Just also working on bootlogo support for an imx8mp based board!
I now applied your patches:
clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present for gate and mux clk: clk-uclass: Implement CLK_OPS_PARENT_ENABLE
And added in my adapted /drivers/clk/imx/clk-imx8mp.c (imported from linux)
clk_dm(IMX8MP_CLK_MEDIA_AXI, imx8m_clk_composite_flags("media_axi", imx8mp_media_axi_sels, ARRAY_SIZE(imx8mp_media_axi_sels), base + 0x8a00, CLK_IS_CRITICAL));
instead of using imx8m_clk_composite, and dropped my approach to get clocks up and working.
Also dropped my similiar approach for mediablock and used your
power: Add iMX8M block ctrl driver for dispmix
And with this 3 patches, bootlogo works also/still fine for me!
I did not applied/need your patches:
clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled clk: imx8mn: Mark IMX8MN_SYS_PLL2 and IMX8MN_SYS_PLL3 as enabled
Of course, they are for imx8mm, but I mean I do not need similiar patches for imx8mp!
Also not applied (as for imx8mm) clk: imx8mn: add video clocks support
but as said, have similiar patch for clk-imx8mp.c
May you check if using imx8m_clk_composite_flags() is working for you?
I did not applied your patches 09/26 and the following patches from your series, as I made a video bridge driver based on
linux driver drivers/gpu/drm/bridge/fsl-ldb.c
and a lcdif driver based on linux:/drivers/gpu/drm/mxsfb/lcdif_drv.c
bye, Heiko

Hello Heiko,
On Tue, Sep 24, 2024 at 11:07 AM Heiko Schocher hs@denx.de wrote:
Hello Dario,
On 13.09.24 11:55, Dario Binacchi wrote:
From: Michael Trimarchi michael@amarulasolutions.com
Add iMX8 block ctrl driver for displaymix on iMX8MM/iMX8MN and mediamix on iMX8MP.
To support blk ctrl driver, the power domain driver on iMX8M needs update to add relevant PGC domains
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com
drivers/power/domain/Kconfig | 6 + drivers/power/domain/Makefile | 1 + drivers/power/domain/imx8m-blk-ctrl.c | 438 ++++++++++++++++++++++ drivers/power/domain/imx8m-power-domain.c | 213 ++++++++++- 4 files changed, 656 insertions(+), 2 deletions(-) create mode 100644 drivers/power/domain/imx8m-blk-ctrl.c
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index bd82d2f7044b..fb006b6e8e28 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -40,6 +40,12 @@ config IMX8M_POWER_DOMAIN Enable support for manipulating NXP i.MX8M on-SoC power domains via requests to the ATF.
+config IMX8M_BLK_CTRL
bool "Enable i.MX8M block control driver"
depends on POWER_DOMAIN && ARCH_IMX8M
help
Enable support for manipulating NXP i.MX8M on-SoC block control driver
- config IMX8MP_HSIOMIX_BLKCTRL bool "Enable i.MX8MP HSIOMIX domain driver" depends on POWER_DOMAIN && IMX8MP
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 2daab73eb758..46849fd2a4db 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_APPLE_PMGR_POWER_DOMAIN) += apple-pmgr.o obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o obj-$(CONFIG_IMX8M_POWER_DOMAIN) += imx8m-power-domain.o +obj-$(CONFIG_IMX8M_BLK_CTRL) += imx8m-blk-ctrl.o obj-$(CONFIG_IMX8MP_HSIOMIX_BLKCTRL) += imx8mp-hsiomix.o obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c new file mode 100644 index 000000000000..4c89078b991b --- /dev/null +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright 2023 NXP
- */
+#include <dm.h> +#include <malloc.h> +#include <power-domain-uclass.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <dm/device.h> +#include <dt-bindings/power/imx8mm-power.h> +#include <dt-bindings/power/imx8mn-power.h> +#include <dt-bindings/power/imx8mp-power.h> +#include <clk.h> +#include <linux/delay.h>
+#define BLK_SFT_RSTN 0x0 +#define BLK_CLK_EN 0x4 +#define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */
+#define DOMAIN_MAX_CLKS 4
+struct imx8m_blk_ctrl_domain {
struct clk clks[DOMAIN_MAX_CLKS];
struct power_domain power_dev;
+};
+struct imx8m_blk_ctrl {
void __iomem *base;
struct power_domain bus_power_dev;
struct imx8m_blk_ctrl_domain *domains;
+};
+struct imx8m_blk_ctrl_domain_data {
const char *name;
const char * const *clk_names;
const char *gpc_name;
int num_clks;
u32 rst_mask;
u32 clk_mask;
u32 mipi_phy_rst_mask;
+};
+struct imx8m_blk_ctrl_data {
int max_reg;
const struct imx8m_blk_ctrl_domain_data *domains;
int num_domains;
u32 bus_rst_mask;
u32 bus_clk_mask;
+};
+static int imx8m_blk_ctrl_request(struct power_domain *power_domain) +{
return 0;
+}
+static int imx8m_blk_ctrl_free(struct power_domain *power_domain) +{
return 0;
+}
+static int imx8m_blk_ctrl_enable_domain_clk(struct udevice *dev, ulong domain_id, bool enable) +{
int ret, i;
struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev);
struct imx8m_blk_ctrl_data *drv_data =
(struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev);
debug("%s num_clk %u\n", __func__, drv_data->domains[domain_id].num_clks);
for (i = 0; i < drv_data->domains[domain_id].num_clks; i++) {
debug("%s clk %s\n", __func__, drv_data->domains[domain_id].clk_names[i]);
if (enable)
ret = clk_enable(&priv->domains[domain_id].clks[i]);
else
ret = clk_disable(&priv->domains[domain_id].clks[i]);
if (ret && ret != -ENOENT) {
printf("Failed to %s domain clk %s\n", enable ? "enable" : "disable",
drv_data->domains[domain_id].clk_names[i]);
return ret;
}
}
return 0;
+}
+static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) +{
struct udevice *dev = power_domain->dev;
struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev);
struct imx8m_blk_ctrl_data *drv_data =
(struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev);
int ret;
debug("%s, id %lu\n", __func__, power_domain->id);
if (!priv->domains[power_domain->id].power_dev.dev)
return -ENODEV;
ret = power_domain_on(&priv->bus_power_dev);
if (ret < 0) {
printf("Failed to power up bus domain %d\n", ret);
return ret;
}
/* Enable bus clock and deassert bus reset */
setbits_le32(priv->base + BLK_CLK_EN, drv_data->bus_clk_mask);
setbits_le32(priv->base + BLK_SFT_RSTN, drv_data->bus_rst_mask);
/* wait for reset to propagate */
udelay(5);
/* put devices into reset */
clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask);
if (drv_data->domains[power_domain->id].mipi_phy_rst_mask)
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d
rv_data->domains[power_domain->id].mipi_phy_rst_mask);
Does this build for you?
I needed the following fix:
diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c index 4c89078b99..b772d50480 100644 --- a/drivers/power/domain/imx8m-blk-ctrl.c +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -114,8 +114,8 @@ static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) /* put devices into reset */ clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask); if (drv_data->domains[power_domain->id].mipi_phy_rst_mask)
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d
rv_data->domains[power_domain->id].mipi_phy_rst_mask);
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV,
drv_data->domains[power_domain->id].mipi_phy_rst_mask); /* enable upstream and blk-ctrl clocks to allow reset to propagate */ ret = imx8m_blk_ctrl_enable_domain_clk(dev, power_domain->id, true);
to get it building.
Yes, you're right. I made some changes suggested by patman, and before applying the patch, I didn't recompile, so I didn't notice.
BTW: Just also working on bootlogo support for an imx8mp based board!
I now applied your patches:
clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present for gate and mux clk: clk-uclass: Implement CLK_OPS_PARENT_ENABLE
And added in my adapted /drivers/clk/imx/clk-imx8mp.c (imported from linux)
clk_dm(IMX8MP_CLK_MEDIA_AXI, imx8m_clk_composite_flags("media_axi", imx8mp_media_axi_sels, ARRAY_SIZE(imx8mp_media_axi_sels), base + 0x8a00, CLK_IS_CRITICAL));
instead of using imx8m_clk_composite, and dropped my approach to get clocks up and working.
Also dropped my similiar approach for mediablock and used your
power: Add iMX8M block ctrl driver for dispmix
And with this 3 patches, bootlogo works also/still fine for me!
I did not applied/need your patches:
clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled clk: imx8mn: Mark IMX8MN_SYS_PLL2 and IMX8MN_SYS_PLL3 as enabled
Of course, they are for imx8mm, but I mean I do not need similiar patches for imx8mp!
Also not applied (as for imx8mm) clk: imx8mn: add video clocks support
but as said, have similiar patch for clk-imx8mp.c
May you check if using imx8m_clk_composite_flags() is working for you?
I will do it
I did not applied your patches 09/26 and the following patches from your series, as I made a video bridge driver based on
linux driver drivers/gpu/drm/bridge/fsl-ldb.c
and a lcdif driver based on linux:/drivers/gpu/drm/mxsfb/lcdif_drv.c
Thank you for you feedback Heiko
Regards, Dario
bye, Heiko -- DENX Software Engineering GmbH, Managing Director: Erika Unter HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-52 Fax: +49-8142-66989-80 Email: hs@denx.de

Hi Dario,
On 30.09.24 15:20, Dario Binacchi wrote:
Hello Heiko,
On Tue, Sep 24, 2024 at 11:07 AM Heiko Schocher hs@denx.de wrote:
Hello Dario,
On 13.09.24 11:55, Dario Binacchi wrote:
From: Michael Trimarchi michael@amarulasolutions.com
Add iMX8 block ctrl driver for displaymix on iMX8MM/iMX8MN and mediamix on iMX8MP.
To support blk ctrl driver, the power domain driver on iMX8M needs update to add relevant PGC domains
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com
drivers/power/domain/Kconfig | 6 + drivers/power/domain/Makefile | 1 + drivers/power/domain/imx8m-blk-ctrl.c | 438 ++++++++++++++++++++++ drivers/power/domain/imx8m-power-domain.c | 213 ++++++++++- 4 files changed, 656 insertions(+), 2 deletions(-) create mode 100644 drivers/power/domain/imx8m-blk-ctrl.c
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index bd82d2f7044b..fb006b6e8e28 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -40,6 +40,12 @@ config IMX8M_POWER_DOMAIN Enable support for manipulating NXP i.MX8M on-SoC power domains via requests to the ATF.
+config IMX8M_BLK_CTRL
bool "Enable i.MX8M block control driver"
depends on POWER_DOMAIN && ARCH_IMX8M
help
Enable support for manipulating NXP i.MX8M on-SoC block control driver
- config IMX8MP_HSIOMIX_BLKCTRL bool "Enable i.MX8MP HSIOMIX domain driver" depends on POWER_DOMAIN && IMX8MP
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 2daab73eb758..46849fd2a4db 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_APPLE_PMGR_POWER_DOMAIN) += apple-pmgr.o obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o obj-$(CONFIG_IMX8M_POWER_DOMAIN) += imx8m-power-domain.o +obj-$(CONFIG_IMX8M_BLK_CTRL) += imx8m-blk-ctrl.o obj-$(CONFIG_IMX8MP_HSIOMIX_BLKCTRL) += imx8mp-hsiomix.o obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c new file mode 100644 index 000000000000..4c89078b991b --- /dev/null +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright 2023 NXP
- */
+#include <dm.h> +#include <malloc.h> +#include <power-domain-uclass.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <dm/device.h> +#include <dt-bindings/power/imx8mm-power.h> +#include <dt-bindings/power/imx8mn-power.h> +#include <dt-bindings/power/imx8mp-power.h> +#include <clk.h> +#include <linux/delay.h>
+#define BLK_SFT_RSTN 0x0 +#define BLK_CLK_EN 0x4 +#define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */
+#define DOMAIN_MAX_CLKS 4
+struct imx8m_blk_ctrl_domain {
struct clk clks[DOMAIN_MAX_CLKS];
struct power_domain power_dev;
+};
+struct imx8m_blk_ctrl {
void __iomem *base;
struct power_domain bus_power_dev;
struct imx8m_blk_ctrl_domain *domains;
+};
+struct imx8m_blk_ctrl_domain_data {
const char *name;
const char * const *clk_names;
const char *gpc_name;
int num_clks;
u32 rst_mask;
u32 clk_mask;
u32 mipi_phy_rst_mask;
+};
+struct imx8m_blk_ctrl_data {
int max_reg;
const struct imx8m_blk_ctrl_domain_data *domains;
int num_domains;
u32 bus_rst_mask;
u32 bus_clk_mask;
+};
+static int imx8m_blk_ctrl_request(struct power_domain *power_domain) +{
return 0;
+}
+static int imx8m_blk_ctrl_free(struct power_domain *power_domain) +{
return 0;
+}
+static int imx8m_blk_ctrl_enable_domain_clk(struct udevice *dev, ulong domain_id, bool enable) +{
int ret, i;
struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev);
struct imx8m_blk_ctrl_data *drv_data =
(struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev);
debug("%s num_clk %u\n", __func__, drv_data->domains[domain_id].num_clks);
for (i = 0; i < drv_data->domains[domain_id].num_clks; i++) {
debug("%s clk %s\n", __func__, drv_data->domains[domain_id].clk_names[i]);
if (enable)
ret = clk_enable(&priv->domains[domain_id].clks[i]);
else
ret = clk_disable(&priv->domains[domain_id].clks[i]);
if (ret && ret != -ENOENT) {
printf("Failed to %s domain clk %s\n", enable ? "enable" : "disable",
drv_data->domains[domain_id].clk_names[i]);
return ret;
}
}
return 0;
+}
+static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) +{
struct udevice *dev = power_domain->dev;
struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev);
struct imx8m_blk_ctrl_data *drv_data =
(struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev);
int ret;
debug("%s, id %lu\n", __func__, power_domain->id);
if (!priv->domains[power_domain->id].power_dev.dev)
return -ENODEV;
ret = power_domain_on(&priv->bus_power_dev);
if (ret < 0) {
printf("Failed to power up bus domain %d\n", ret);
return ret;
}
/* Enable bus clock and deassert bus reset */
setbits_le32(priv->base + BLK_CLK_EN, drv_data->bus_clk_mask);
setbits_le32(priv->base + BLK_SFT_RSTN, drv_data->bus_rst_mask);
/* wait for reset to propagate */
udelay(5);
/* put devices into reset */
clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask);
if (drv_data->domains[power_domain->id].mipi_phy_rst_mask)
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d
rv_data->domains[power_domain->id].mipi_phy_rst_mask);
Does this build for you?
I needed the following fix:
diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c index 4c89078b99..b772d50480 100644 --- a/drivers/power/domain/imx8m-blk-ctrl.c +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -114,8 +114,8 @@ static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) /* put devices into reset */ clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask); if (drv_data->domains[power_domain->id].mipi_phy_rst_mask)
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d
rv_data->domains[power_domain->id].mipi_phy_rst_mask);
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV,
drv_data->domains[power_domain->id].mipi_phy_rst_mask); /* enable upstream and blk-ctrl clocks to allow reset to propagate */ ret = imx8m_blk_ctrl_enable_domain_clk(dev, power_domain->id, true);
to get it building.
Yes, you're right. I made some changes suggested by patman, and before applying the patch, I didn't recompile, so I didn't notice.
BTW: Just also working on bootlogo support for an imx8mp based board!
I now applied your patches:
clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present for gate and mux clk: clk-uclass: Implement CLK_OPS_PARENT_ENABLE
And added in my adapted /drivers/clk/imx/clk-imx8mp.c (imported from linux)
clk_dm(IMX8MP_CLK_MEDIA_AXI, imx8m_clk_composite_flags("media_axi", imx8mp_media_axi_sels, ARRAY_SIZE(imx8mp_media_axi_sels), base + 0x8a00, CLK_IS_CRITICAL));
instead of using imx8m_clk_composite, and dropped my approach to get clocks up and working.
Also dropped my similiar approach for mediablock and used your
power: Add iMX8M block ctrl driver for dispmix
And with this 3 patches, bootlogo works also/still fine for me!
I did not applied/need your patches:
clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled clk: imx8mn: Mark IMX8MN_SYS_PLL2 and IMX8MN_SYS_PLL3 as enabled
Of course, they are for imx8mm, but I mean I do not need similiar patches for imx8mp!
Also not applied (as for imx8mm) clk: imx8mn: add video clocks support
but as said, have similiar patch for clk-imx8mp.c
May you check if using imx8m_clk_composite_flags() is working for you?
I will do it
I did not applied your patches 09/26 and the following patches from your series, as I made a video bridge driver based on
linux driver drivers/gpu/drm/bridge/fsl-ldb.c
and a lcdif driver based on linux:/drivers/gpu/drm/mxsfb/lcdif_drv.c
Thank you for you feedback Heiko
You are welcome!
Hmm.. unfortunately ... I had applied your 2 clock patches, which fixed a problem with enabling parent clocks ... but they broke booting on a carrier which has fec ethernet! After "Net: " output the board hang...
I reverted your 2 clock patches and it bootet again ... so there is a problem ... try to get some more time to look into...
Do you have fec ethernet on your board?
Thanks!
bye, Heiko
Regards, Dario
bye, Heiko -- DENX Software Engineering GmbH, Managing Director: Erika Unter HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-52 Fax: +49-8142-66989-80 Email: hs@denx.de

Hi Heko
On Tue, Oct 1, 2024 at 6:23 AM Heiko Schocher hs@denx.de wrote:
Hi Dario,
On 30.09.24 15:20, Dario Binacchi wrote:
Hello Heiko,
On Tue, Sep 24, 2024 at 11:07 AM Heiko Schocher hs@denx.de wrote:
Hello Dario,
On 13.09.24 11:55, Dario Binacchi wrote:
From: Michael Trimarchi michael@amarulasolutions.com
Add iMX8 block ctrl driver for displaymix on iMX8MM/iMX8MN and mediamix on iMX8MP.
To support blk ctrl driver, the power domain driver on iMX8M needs update to add relevant PGC domains
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com
drivers/power/domain/Kconfig | 6 + drivers/power/domain/Makefile | 1 + drivers/power/domain/imx8m-blk-ctrl.c | 438 ++++++++++++++++++++++ drivers/power/domain/imx8m-power-domain.c | 213 ++++++++++- 4 files changed, 656 insertions(+), 2 deletions(-) create mode 100644 drivers/power/domain/imx8m-blk-ctrl.c
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index bd82d2f7044b..fb006b6e8e28 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -40,6 +40,12 @@ config IMX8M_POWER_DOMAIN Enable support for manipulating NXP i.MX8M on-SoC power domains via requests to the ATF.
+config IMX8M_BLK_CTRL
bool "Enable i.MX8M block control driver"
depends on POWER_DOMAIN && ARCH_IMX8M
help
Enable support for manipulating NXP i.MX8M on-SoC block control driver
- config IMX8MP_HSIOMIX_BLKCTRL bool "Enable i.MX8MP HSIOMIX domain driver" depends on POWER_DOMAIN && IMX8MP
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 2daab73eb758..46849fd2a4db 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_APPLE_PMGR_POWER_DOMAIN) += apple-pmgr.o obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o obj-$(CONFIG_IMX8M_POWER_DOMAIN) += imx8m-power-domain.o +obj-$(CONFIG_IMX8M_BLK_CTRL) += imx8m-blk-ctrl.o obj-$(CONFIG_IMX8MP_HSIOMIX_BLKCTRL) += imx8mp-hsiomix.o obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c new file mode 100644 index 000000000000..4c89078b991b --- /dev/null +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright 2023 NXP
- */
+#include <dm.h> +#include <malloc.h> +#include <power-domain-uclass.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <dm/device.h> +#include <dt-bindings/power/imx8mm-power.h> +#include <dt-bindings/power/imx8mn-power.h> +#include <dt-bindings/power/imx8mp-power.h> +#include <clk.h> +#include <linux/delay.h>
+#define BLK_SFT_RSTN 0x0 +#define BLK_CLK_EN 0x4 +#define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */
+#define DOMAIN_MAX_CLKS 4
+struct imx8m_blk_ctrl_domain {
struct clk clks[DOMAIN_MAX_CLKS];
struct power_domain power_dev;
+};
+struct imx8m_blk_ctrl {
void __iomem *base;
struct power_domain bus_power_dev;
struct imx8m_blk_ctrl_domain *domains;
+};
+struct imx8m_blk_ctrl_domain_data {
const char *name;
const char * const *clk_names;
const char *gpc_name;
int num_clks;
u32 rst_mask;
u32 clk_mask;
u32 mipi_phy_rst_mask;
+};
+struct imx8m_blk_ctrl_data {
int max_reg;
const struct imx8m_blk_ctrl_domain_data *domains;
int num_domains;
u32 bus_rst_mask;
u32 bus_clk_mask;
+};
+static int imx8m_blk_ctrl_request(struct power_domain *power_domain) +{
return 0;
+}
+static int imx8m_blk_ctrl_free(struct power_domain *power_domain) +{
return 0;
+}
+static int imx8m_blk_ctrl_enable_domain_clk(struct udevice *dev, ulong domain_id, bool enable) +{
int ret, i;
struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev);
struct imx8m_blk_ctrl_data *drv_data =
(struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev);
debug("%s num_clk %u\n", __func__, drv_data->domains[domain_id].num_clks);
for (i = 0; i < drv_data->domains[domain_id].num_clks; i++) {
debug("%s clk %s\n", __func__, drv_data->domains[domain_id].clk_names[i]);
if (enable)
ret = clk_enable(&priv->domains[domain_id].clks[i]);
else
ret = clk_disable(&priv->domains[domain_id].clks[i]);
if (ret && ret != -ENOENT) {
printf("Failed to %s domain clk %s\n", enable ? "enable" : "disable",
drv_data->domains[domain_id].clk_names[i]);
return ret;
}
}
return 0;
+}
+static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) +{
struct udevice *dev = power_domain->dev;
struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev);
struct imx8m_blk_ctrl_data *drv_data =
(struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev);
int ret;
debug("%s, id %lu\n", __func__, power_domain->id);
if (!priv->domains[power_domain->id].power_dev.dev)
return -ENODEV;
ret = power_domain_on(&priv->bus_power_dev);
if (ret < 0) {
printf("Failed to power up bus domain %d\n", ret);
return ret;
}
/* Enable bus clock and deassert bus reset */
setbits_le32(priv->base + BLK_CLK_EN, drv_data->bus_clk_mask);
setbits_le32(priv->base + BLK_SFT_RSTN, drv_data->bus_rst_mask);
/* wait for reset to propagate */
udelay(5);
/* put devices into reset */
clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask);
if (drv_data->domains[power_domain->id].mipi_phy_rst_mask)
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d
rv_data->domains[power_domain->id].mipi_phy_rst_mask);
Does this build for you?
I needed the following fix:
diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c index 4c89078b99..b772d50480 100644 --- a/drivers/power/domain/imx8m-blk-ctrl.c +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -114,8 +114,8 @@ static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) /* put devices into reset */ clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask); if (drv_data->domains[power_domain->id].mipi_phy_rst_mask)
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d
rv_data->domains[power_domain->id].mipi_phy_rst_mask);
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV,
drv_data->domains[power_domain->id].mipi_phy_rst_mask); /* enable upstream and blk-ctrl clocks to allow reset to propagate */ ret = imx8m_blk_ctrl_enable_domain_clk(dev, power_domain->id, true);
to get it building.
Yes, you're right. I made some changes suggested by patman, and before applying the patch, I didn't recompile, so I didn't notice.
BTW: Just also working on bootlogo support for an imx8mp based board!
I now applied your patches:
clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present for gate and mux clk: clk-uclass: Implement CLK_OPS_PARENT_ENABLE
And added in my adapted /drivers/clk/imx/clk-imx8mp.c (imported from linux)
clk_dm(IMX8MP_CLK_MEDIA_AXI, imx8m_clk_composite_flags("media_axi", imx8mp_media_axi_sels, ARRAY_SIZE(imx8mp_media_axi_sels), base + 0x8a00, CLK_IS_CRITICAL));
instead of using imx8m_clk_composite, and dropped my approach to get clocks up and working.
Also dropped my similiar approach for mediablock and used your
power: Add iMX8M block ctrl driver for dispmix
And with this 3 patches, bootlogo works also/still fine for me!
I did not applied/need your patches:
clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled clk: imx8mn: Mark IMX8MN_SYS_PLL2 and IMX8MN_SYS_PLL3 as enabled
Of course, they are for imx8mm, but I mean I do not need similiar patches for imx8mp!
Also not applied (as for imx8mm) clk: imx8mn: add video clocks support
but as said, have similiar patch for clk-imx8mp.c
May you check if using imx8m_clk_composite_flags() is working for you?
I will do it
I did not applied your patches 09/26 and the following patches from your series, as I made a video bridge driver based on
linux driver drivers/gpu/drm/bridge/fsl-ldb.c
and a lcdif driver based on linux:/drivers/gpu/drm/mxsfb/lcdif_drv.c
Thank you for you feedback Heiko
You are welcome!
Hmm.. unfortunately ... I had applied your 2 clock patches, which fixed a problem with enabling parent clocks ... but they broke booting on a carrier which has fec ethernet! After "Net: " output the board hang...
I reverted your 2 clock patches and it bootet again ... so there is a problem ... try to get some more time to look into...
We have a fec, but we had I think two patches more on it. I forget to answer Marek about them because I don't have my board now and because he is partially right (or maybe right). Anyway when we boot we could have and we have clocks that are enabled by bootrom or SPL that we need to declare as enable like PLL2/PLL3 those clocks are parts or could be part of reparent so, you need to have a reference counter on them that allow to not disable during the down chain disable and up chain enable. I think that what happens to your ethernet it's that you disable some clock that is critical to the board to survive. I had a patch merged by Tom that prints the clock name so if you enable the debug of the clock you will find that your board stops working during one of this reparinting.
Can you check it?
Michael
Do you have fec ethernet on your board?
Thanks!
bye, Heiko
Regards, Dario
bye, Heiko -- DENX Software Engineering GmbH, Managing Director: Erika Unter HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-52 Fax: +49-8142-66989-80 Email: hs@denx.de
-- DENX Software Engineering GmbH, Managing Director: Erika Unter HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-52 Fax: +49-8142-66989-80 Email: hs@denx.de

Hi Micahel,
On 01.10.24 07:01, Michael Nazzareno Trimarchi wrote:
Hi Heko
On Tue, Oct 1, 2024 at 6:23 AM Heiko Schocher hs@denx.de wrote:
Hi Dario,
On 30.09.24 15:20, Dario Binacchi wrote:
Hello Heiko,
On Tue, Sep 24, 2024 at 11:07 AM Heiko Schocher hs@denx.de wrote:
Hello Dario,
On 13.09.24 11:55, Dario Binacchi wrote:
From: Michael Trimarchi michael@amarulasolutions.com
Add iMX8 block ctrl driver for displaymix on iMX8MM/iMX8MN and mediamix on iMX8MP.
To support blk ctrl driver, the power domain driver on iMX8M needs update to add relevant PGC domains
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com
drivers/power/domain/Kconfig | 6 + drivers/power/domain/Makefile | 1 + drivers/power/domain/imx8m-blk-ctrl.c | 438 ++++++++++++++++++++++ drivers/power/domain/imx8m-power-domain.c | 213 ++++++++++- 4 files changed, 656 insertions(+), 2 deletions(-) create mode 100644 drivers/power/domain/imx8m-blk-ctrl.c
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index bd82d2f7044b..fb006b6e8e28 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -40,6 +40,12 @@ config IMX8M_POWER_DOMAIN Enable support for manipulating NXP i.MX8M on-SoC power domains via requests to the ATF.
+config IMX8M_BLK_CTRL
bool "Enable i.MX8M block control driver"
depends on POWER_DOMAIN && ARCH_IMX8M
help
Enable support for manipulating NXP i.MX8M on-SoC block control driver
- config IMX8MP_HSIOMIX_BLKCTRL bool "Enable i.MX8MP HSIOMIX domain driver" depends on POWER_DOMAIN && IMX8MP
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 2daab73eb758..46849fd2a4db 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_APPLE_PMGR_POWER_DOMAIN) += apple-pmgr.o obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o obj-$(CONFIG_IMX8M_POWER_DOMAIN) += imx8m-power-domain.o +obj-$(CONFIG_IMX8M_BLK_CTRL) += imx8m-blk-ctrl.o obj-$(CONFIG_IMX8MP_HSIOMIX_BLKCTRL) += imx8mp-hsiomix.o obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c new file mode 100644 index 000000000000..4c89078b991b --- /dev/null +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright 2023 NXP
- */
+#include <dm.h> +#include <malloc.h> +#include <power-domain-uclass.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <dm/device.h> +#include <dt-bindings/power/imx8mm-power.h> +#include <dt-bindings/power/imx8mn-power.h> +#include <dt-bindings/power/imx8mp-power.h> +#include <clk.h> +#include <linux/delay.h>
+#define BLK_SFT_RSTN 0x0 +#define BLK_CLK_EN 0x4 +#define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */
+#define DOMAIN_MAX_CLKS 4
+struct imx8m_blk_ctrl_domain {
struct clk clks[DOMAIN_MAX_CLKS];
struct power_domain power_dev;
+};
+struct imx8m_blk_ctrl {
void __iomem *base;
struct power_domain bus_power_dev;
struct imx8m_blk_ctrl_domain *domains;
+};
+struct imx8m_blk_ctrl_domain_data {
const char *name;
const char * const *clk_names;
const char *gpc_name;
int num_clks;
u32 rst_mask;
u32 clk_mask;
u32 mipi_phy_rst_mask;
+};
+struct imx8m_blk_ctrl_data {
int max_reg;
const struct imx8m_blk_ctrl_domain_data *domains;
int num_domains;
u32 bus_rst_mask;
u32 bus_clk_mask;
+};
+static int imx8m_blk_ctrl_request(struct power_domain *power_domain) +{
return 0;
+}
+static int imx8m_blk_ctrl_free(struct power_domain *power_domain) +{
return 0;
+}
+static int imx8m_blk_ctrl_enable_domain_clk(struct udevice *dev, ulong domain_id, bool enable) +{
int ret, i;
struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev);
struct imx8m_blk_ctrl_data *drv_data =
(struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev);
debug("%s num_clk %u\n", __func__, drv_data->domains[domain_id].num_clks);
for (i = 0; i < drv_data->domains[domain_id].num_clks; i++) {
debug("%s clk %s\n", __func__, drv_data->domains[domain_id].clk_names[i]);
if (enable)
ret = clk_enable(&priv->domains[domain_id].clks[i]);
else
ret = clk_disable(&priv->domains[domain_id].clks[i]);
if (ret && ret != -ENOENT) {
printf("Failed to %s domain clk %s\n", enable ? "enable" : "disable",
drv_data->domains[domain_id].clk_names[i]);
return ret;
}
}
return 0;
+}
+static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) +{
struct udevice *dev = power_domain->dev;
struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev);
struct imx8m_blk_ctrl_data *drv_data =
(struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev);
int ret;
debug("%s, id %lu\n", __func__, power_domain->id);
if (!priv->domains[power_domain->id].power_dev.dev)
return -ENODEV;
ret = power_domain_on(&priv->bus_power_dev);
if (ret < 0) {
printf("Failed to power up bus domain %d\n", ret);
return ret;
}
/* Enable bus clock and deassert bus reset */
setbits_le32(priv->base + BLK_CLK_EN, drv_data->bus_clk_mask);
setbits_le32(priv->base + BLK_SFT_RSTN, drv_data->bus_rst_mask);
/* wait for reset to propagate */
udelay(5);
/* put devices into reset */
clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask);
if (drv_data->domains[power_domain->id].mipi_phy_rst_mask)
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d
rv_data->domains[power_domain->id].mipi_phy_rst_mask);
Does this build for you?
I needed the following fix:
diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c index 4c89078b99..b772d50480 100644 --- a/drivers/power/domain/imx8m-blk-ctrl.c +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -114,8 +114,8 @@ static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) /* put devices into reset */ clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask); if (drv_data->domains[power_domain->id].mipi_phy_rst_mask)
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d
rv_data->domains[power_domain->id].mipi_phy_rst_mask);
clrbits_le32(priv->base + BLK_MIPI_RESET_DIV,
drv_data->domains[power_domain->id].mipi_phy_rst_mask); /* enable upstream and blk-ctrl clocks to allow reset to propagate */ ret = imx8m_blk_ctrl_enable_domain_clk(dev, power_domain->id, true);
to get it building.
Yes, you're right. I made some changes suggested by patman, and before applying the patch, I didn't recompile, so I didn't notice.
BTW: Just also working on bootlogo support for an imx8mp based board!
I now applied your patches:
clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present for gate and mux clk: clk-uclass: Implement CLK_OPS_PARENT_ENABLE
And added in my adapted /drivers/clk/imx/clk-imx8mp.c (imported from linux)
clk_dm(IMX8MP_CLK_MEDIA_AXI, imx8m_clk_composite_flags("media_axi", imx8mp_media_axi_sels, ARRAY_SIZE(imx8mp_media_axi_sels), base + 0x8a00, CLK_IS_CRITICAL));
instead of using imx8m_clk_composite, and dropped my approach to get clocks up and working.
Also dropped my similiar approach for mediablock and used your
power: Add iMX8M block ctrl driver for dispmix
And with this 3 patches, bootlogo works also/still fine for me!
I did not applied/need your patches:
clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled clk: imx8mn: Mark IMX8MN_SYS_PLL2 and IMX8MN_SYS_PLL3 as enabled
Of course, they are for imx8mm, but I mean I do not need similiar patches for imx8mp!
Also not applied (as for imx8mm) clk: imx8mn: add video clocks support
but as said, have similiar patch for clk-imx8mp.c
May you check if using imx8m_clk_composite_flags() is working for you?
I will do it
I did not applied your patches 09/26 and the following patches from your series, as I made a video bridge driver based on
linux driver drivers/gpu/drm/bridge/fsl-ldb.c
and a lcdif driver based on linux:/drivers/gpu/drm/mxsfb/lcdif_drv.c
Thank you for you feedback Heiko
You are welcome!
Hmm.. unfortunately ... I had applied your 2 clock patches, which fixed a problem with enabling parent clocks ... but they broke booting on a carrier which has fec ethernet! After "Net: " output the board hang...
I reverted your 2 clock patches and it bootet again ... so there is a problem ... try to get some more time to look into...
We have a fec, but we had I think two patches more on it. I forget to answer Marek about them because I don't have my board now and because he is partially right (or maybe right). Anyway when we boot we could have and we have clocks that are enabled by bootrom or SPL that we need to declare as enable like PLL2/PLL3 those clocks are parts or could be part of reparent so, you need to have a reference counter on them that allow to not disable during the down chain disable and up chain enable. I think that what happens to your ethernet it's that you disable some clock that is critical to the board to survive. I had a patch merged by Tom that
Yep, thats what I think too! If you access registers in a block for which the clock is not enabled ... it just hang...
prints the clock name so if you enable the debug of the clock you will find that your board stops working during one of this reparinting.
I currently work on 2024.07... will rebase if 2024.10 is out...
Ah, you mean:
commit a70d991212c9684e09ed80ece69ce1ff7bfd9f08 Author: Michael Trimarchi michael@amarulasolutions.com Date: Tue Jul 9 08:28:13 2024 +0200
clk: clk-uclass: Print clk name in clk_enable/clk_disable
Print clk name in clk_enable and clk_disable. Make sense to know what clock get disabled/enabled before a system crash or system hang.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com
I have the same change when I debug :-P
IIRC I did not see the clock names ... but I will recheck!
Thanks!
Can you check it?
Yep, will do.
bye, Heiko
Michael
Do you have fec ethernet on your board?
Thanks!
bye, Heiko
Regards, Dario
bye, Heiko -- DENX Software Engineering GmbH, Managing Director: Erika Unter HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-52 Fax: +49-8142-66989-80 Email: hs@denx.de
-- DENX Software Engineering GmbH, Managing Director: Erika Unter HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-52 Fax: +49-8142-66989-80 Email: hs@denx.de

Hello Michael,
On 01.10.24 07:14, Heiko Schocher wrote:
Hi Micahel,
On 01.10.24 07:01, Michael Nazzareno Trimarchi wrote:
Hi Heko
On Tue, Oct 1, 2024 at 6:23 AM Heiko Schocher hs@denx.de wrote:
Hi Dario,
On 30.09.24 15:20, Dario Binacchi wrote:
Hello Heiko,
On Tue, Sep 24, 2024 at 11:07 AM Heiko Schocher hs@denx.de wrote:
Hello Dario,
On 13.09.24 11:55, Dario Binacchi wrote:
From: Michael Trimarchi michael@amarulasolutions.com
Add iMX8 block ctrl driver for displaymix on iMX8MM/iMX8MN and mediamix on iMX8MP.
To support blk ctrl driver, the power domain driver on iMX8M needs update to add relevant PGC domains
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com
drivers/power/domain/Kconfig | 6 + drivers/power/domain/Makefile | 1 + drivers/power/domain/imx8m-blk-ctrl.c | 438 ++++++++++++++++++++++ drivers/power/domain/imx8m-power-domain.c | 213 ++++++++++- 4 files changed, 656 insertions(+), 2 deletions(-) create mode 100644 drivers/power/domain/imx8m-blk-ctrl.c
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index bd82d2f7044b..fb006b6e8e28 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -40,6 +40,12 @@ config IMX8M_POWER_DOMAIN Enable support for manipulating NXP i.MX8M on-SoC power domains via requests to the ATF.
+config IMX8M_BLK_CTRL + bool "Enable i.MX8M block control driver" + depends on POWER_DOMAIN && ARCH_IMX8M + help + Enable support for manipulating NXP i.MX8M on-SoC block control driver
config IMX8MP_HSIOMIX_BLKCTRL bool "Enable i.MX8MP HSIOMIX domain driver" depends on POWER_DOMAIN && IMX8MP diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 2daab73eb758..46849fd2a4db 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_APPLE_PMGR_POWER_DOMAIN) += apple-pmgr.o obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o obj-$(CONFIG_IMX8M_POWER_DOMAIN) += imx8m-power-domain.o +obj-$(CONFIG_IMX8M_BLK_CTRL) += imx8m-blk-ctrl.o obj-$(CONFIG_IMX8MP_HSIOMIX_BLKCTRL) += imx8mp-hsiomix.o obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c new file mode 100644 index 000000000000..4c89078b991b --- /dev/null +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright 2023 NXP
- */
+#include <dm.h> +#include <malloc.h> +#include <power-domain-uclass.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <dm/device.h> +#include <dt-bindings/power/imx8mm-power.h> +#include <dt-bindings/power/imx8mn-power.h> +#include <dt-bindings/power/imx8mp-power.h> +#include <clk.h> +#include <linux/delay.h>
+#define BLK_SFT_RSTN 0x0 +#define BLK_CLK_EN 0x4 +#define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */
+#define DOMAIN_MAX_CLKS 4
+struct imx8m_blk_ctrl_domain { + struct clk clks[DOMAIN_MAX_CLKS]; + struct power_domain power_dev; +};
+struct imx8m_blk_ctrl { + void __iomem *base; + struct power_domain bus_power_dev; + struct imx8m_blk_ctrl_domain *domains; +};
+struct imx8m_blk_ctrl_domain_data { + const char *name; + const char * const *clk_names; + const char *gpc_name; + int num_clks; + u32 rst_mask; + u32 clk_mask; + u32 mipi_phy_rst_mask; +};
+struct imx8m_blk_ctrl_data { + int max_reg; + const struct imx8m_blk_ctrl_domain_data *domains; + int num_domains; + u32 bus_rst_mask; + u32 bus_clk_mask; +};
+static int imx8m_blk_ctrl_request(struct power_domain *power_domain) +{ + return 0; +}
+static int imx8m_blk_ctrl_free(struct power_domain *power_domain) +{ + return 0; +}
+static int imx8m_blk_ctrl_enable_domain_clk(struct udevice *dev, ulong domain_id, bool enable) +{ + int ret, i; + struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev); + struct imx8m_blk_ctrl_data *drv_data = + (struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev);
+ debug("%s num_clk %u\n", __func__, drv_data->domains[domain_id].num_clks);
+ for (i = 0; i < drv_data->domains[domain_id].num_clks; i++) { + debug("%s clk %s\n", __func__, drv_data->domains[domain_id].clk_names[i]); + if (enable) + ret = clk_enable(&priv->domains[domain_id].clks[i]); + else + ret = clk_disable(&priv->domains[domain_id].clks[i]); + if (ret && ret != -ENOENT) { + printf("Failed to %s domain clk %s\n", enable ? "enable" : "disable", + drv_data->domains[domain_id].clk_names[i]); + return ret; + } + }
+ return 0; +}
+static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) +{ + struct udevice *dev = power_domain->dev; + struct imx8m_blk_ctrl *priv = (struct imx8m_blk_ctrl *)dev_get_priv(dev); + struct imx8m_blk_ctrl_data *drv_data = + (struct imx8m_blk_ctrl_data *)dev_get_driver_data(dev); + int ret;
+ debug("%s, id %lu\n", __func__, power_domain->id);
+ if (!priv->domains[power_domain->id].power_dev.dev) + return -ENODEV;
+ ret = power_domain_on(&priv->bus_power_dev); + if (ret < 0) { + printf("Failed to power up bus domain %d\n", ret); + return ret; + }
+ /* Enable bus clock and deassert bus reset */ + setbits_le32(priv->base + BLK_CLK_EN, drv_data->bus_clk_mask); + setbits_le32(priv->base + BLK_SFT_RSTN, drv_data->bus_rst_mask);
+ /* wait for reset to propagate */ + udelay(5);
+ /* put devices into reset */ + clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask); + if (drv_data->domains[power_domain->id].mipi_phy_rst_mask) + clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d + rv_data->domains[power_domain->id].mipi_phy_rst_mask);
Does this build for you?
I needed the following fix:
diff --git a/drivers/power/domain/imx8m-blk-ctrl.c b/drivers/power/domain/imx8m-blk-ctrl.c index 4c89078b99..b772d50480 100644 --- a/drivers/power/domain/imx8m-blk-ctrl.c +++ b/drivers/power/domain/imx8m-blk-ctrl.c @@ -114,8 +114,8 @@ static int imx8m_blk_ctrl_power_on(struct power_domain *power_domain) /* put devices into reset */ clrbits_le32(priv->base + BLK_SFT_RSTN, drv_data->domains[power_domain->id].rst_mask); if (drv_data->domains[power_domain->id].mipi_phy_rst_mask) - clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, d - rv_data->domains[power_domain->id].mipi_phy_rst_mask); + clrbits_le32(priv->base + BLK_MIPI_RESET_DIV, + drv_data->domains[power_domain->id].mipi_phy_rst_mask);
/* enable upstream and blk-ctrl clocks to allow reset to propagate */ ret = imx8m_blk_ctrl_enable_domain_clk(dev, power_domain->id, true);
to get it building.
Yes, you're right. I made some changes suggested by patman, and before applying the patch, I didn't recompile, so I didn't notice.
BTW: Just also working on bootlogo support for an imx8mp based board!
I now applied your patches:
clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present for gate and mux clk: clk-uclass: Implement CLK_OPS_PARENT_ENABLE
And added in my adapted /drivers/clk/imx/clk-imx8mp.c (imported from linux)
clk_dm(IMX8MP_CLK_MEDIA_AXI, imx8m_clk_composite_flags("media_axi", imx8mp_media_axi_sels, ARRAY_SIZE(imx8mp_media_axi_sels), base + 0x8a00, CLK_IS_CRITICAL));
instead of using imx8m_clk_composite, and dropped my approach to get clocks up and working.
Also dropped my similiar approach for mediablock and used your
power: Add iMX8M block ctrl driver for dispmix
And with this 3 patches, bootlogo works also/still fine for me!
I did not applied/need your patches:
clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Prevent clock critical path from disabling during reparent and set_rate clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled clk: imx8mn: Mark IMX8MN_SYS_PLL2 and IMX8MN_SYS_PLL3 as enabled
Of course, they are for imx8mm, but I mean I do not need similiar patches for imx8mp!
Also not applied (as for imx8mm) clk: imx8mn: add video clocks support
but as said, have similiar patch for clk-imx8mp.c
May you check if using imx8m_clk_composite_flags() is working for you?
I will do it
I did not applied your patches 09/26 and the following patches from your series, as I made a video bridge driver based on
linux driver drivers/gpu/drm/bridge/fsl-ldb.c
and a lcdif driver based on linux:/drivers/gpu/drm/mxsfb/lcdif_drv.c
Thank you for you feedback Heiko
You are welcome!
Hmm.. unfortunately ... I had applied your 2 clock patches, which fixed a problem with enabling parent clocks ... but they broke booting on a carrier which has fec ethernet! After "Net: " output the board hang...
I reverted your 2 clock patches and it bootet again ... so there is a problem ... try to get some more time to look into...
We have a fec, but we had I think two patches more on it. I forget to answer Marek about them because I don't have my board now and because he is partially right (or maybe right). Anyway when we boot we could have and we have clocks that are enabled by bootrom or SPL that we need to declare as enable like PLL2/PLL3 those clocks are parts or could be part of reparent so, you need to have a reference counter on them that allow to not disable during the down chain disable and up chain enable. I think that what happens to your ethernet it's that you disable some clock that is critical to the board to survive. I had a patch merged by Tom that
Yep, thats what I think too! If you access registers in a block for which the clock is not enabled ... it just hang...
prints the clock name so if you enable the debug of the clock you will find that your board stops working during one of this reparinting.
I currently work on 2024.07... will rebase if 2024.10 is out...
Ah, you mean:
commit a70d991212c9684e09ed80ece69ce1ff7bfd9f08 Author: Michael Trimarchi michael@amarulasolutions.com Date: Tue Jul 9 08:28:13 2024 +0200
clk: clk-uclass: Print clk name in clk_enable/clk_disable
Print clk name in clk_enable and clk_disable. Make sense to know what clock get disabled/enabled before a system crash or system hang.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com
I have the same change when I debug :-P
IIRC I did not see the clock names ... but I will recheck!
I see with your patch the clock names, so fine... and see [1]
Hmm... I am on imx8mp, and I think the changes the patchset do in
"clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate"
are in clk-imx8mp already ...
Ported the patch from patchset
"[PATCH 05/26] clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled"
to imx8mp [2] and fec ethernet works again for me on imx8mp!
Could you add this if you post a v2 ?
bye, Heiko [1] │ │ <> Net: clk_set_defaults(gpio@30230000) │ │ <> clk_set_default_parents: could not read assigned-clock-parents for 00000000fee74d90 name=gpio@30230000 │ │ <> clk_set_defaults(aristainetos3-fec-rgmii-grp) │ │ <> clk_set_default_parents: could not read assigned-clock-parents for 00000000fee75780 name=aristainetos3-fec-rgmii-grp │ │ <> clk_set_defaults(ethernet@30be0000) │ │ <> clk_get_by_indexed_prop(dev=00000000fee7ad20, index=0, clk=00000000fee5e3b8) │ │ <> clk_of_xlate_default(clk=00000000fee5e3b8) │ │ <> clk_request(dev=00000000fee77480, clk=00000000fee5e3b8) │ │ <> clk_get_by_indexed_prop(dev=00000000fee7ad20, index=0, clk=00000000fee5e390) │ │ <> clk_of_xlate_default(clk=00000000fee5e390) │ │ <> clk_request(dev=00000000fee77480, clk=00000000fee5e390) │ │ <> clk_set_parent(clk=00000000fee825c0, parent=00000000fee80680) │ │ <> clk_get_parent(clk=00000000fee825c0) │ │ <> clk_enable(clk=00000000fee743d0 name=clock-osc-24m) │ │ <> clk_enable(clk=00000000fee80680 name=sys_pll1_266m) │ │ <> clk_enable(clk=00000000fee7fb40 name=sys_pll1_out) │ │ <> clk_disable(clk=00000000fee80680 name=sys_pll1_266m) │ │ <> clk_disable(clk=00000000fee7fb40 name=sys_pll1_out) │ │ <> clk_disable(clk=00000000fee743d0 name=clock-osc-24m) │ │ <> clk_get_by_indexed_prop(dev=00000000fee7ad20, index=1, clk=00000000fee5e3b8) │ │ <> clk_of_xlate_default(clk=00000000fee5e3b8) │ │ <> clk_request(dev=00000000fee77480, clk=00000000fee5e3b8) │ │ <> clk_get_by_indexed_prop(dev=00000000fee7ad20, index=1, clk=00000000fee5e390) │ │ <> clk_of_xlate_default(clk=00000000fee5e390) │ │ <> clk_request(dev=00000000fee77480, clk=00000000fee5e390) │ │ <> clk_set_parent(clk=00000000fee85b80, parent=00000000fee80b80) │ │ <> clk_get_parent(clk=00000000fee85b80) │ │ <> clk_enable(clk=00000000fee743d0 name=clock-osc-24m) │ │ <> clk_enable(clk=00000000fee80b80 name=sys_pll2_100m) │ │ <> clk_enable(clk=00000000fee7fc80 name=sys_pll2_out) │ │ <> clk_enable(clk=00000000fee7f480 name=sys_pll2_bypass) │ │ <> clk_enable(clk=00000000fee7eb80 name=sys_pll2) │ │ <> clk_enable(clk=00000000fee7e280 name=sys_pll2_ref_sel) │ │ <> clk_enable(clk=00000000fee743d0 name=clock-osc-24m) │ │ <> clk_disable(clk=00000000fee80b80 name=sys_pll2_100m) │ │ <> clk_disable(clk=00000000fee7fc80 name=sys_pll2_out) │ │ <> clk_disable(clk=00000000fee7f480 name=sys_pll2_bypass) │ │ <> clk_disable(clk=00000000fee7eb80 name=sys_pll2)
[2] diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c index d6865509cb..f6fa66094d 100644 --- a/drivers/clk/imx/clk-imx8mp.c +++ b/drivers/clk/imx/clk-imx8mp.c @@ -235,6 +235,7 @@ static const char * const imx8mp_clkout_sels[] = {"audio_pll1_out", "audio_pll2_ static int imx8mp_clk_probe(struct udevice *dev) { struct clk osc_24m_clk, osc_32k_clk; + struct clk *clk; void __iomem *base; int ret;
@@ -303,6 +304,14 @@ static int imx8mp_clk_probe(struct udevice *dev) return ret; clk_dm(IMX8MP_CLK_32K, dev_get_clk_ptr(osc_32k_clk.dev));
+ ret = clk_get_by_id(IMX8MP_SYS_PLL2, &clk); + if (!ret) + clk_enable(clk); + + ret = clk_get_by_id(IMX8MP_SYS_PLL3, &clk); + if (!ret) + clk_enable(clk); + base = dev_read_addr_ptr(dev); if (!base) return -EINVAL;

Hi Heiko,
Hmm.. unfortunately ... I had applied your 2 clock patches, which fixed a problem with enabling parent clocks ... but they broke booting on a carrier which has fec ethernet! After "Net: " output the board hang...
I reverted your 2 clock patches and it bootet again ... so there is a problem ... try to get some more time to look into...
We have a fec, but we had I think two patches more on it. I forget to answer Marek about them because I don't have my board now and because he is partially right (or maybe right). Anyway when we boot we could have and we have clocks that are enabled by bootrom or SPL that we need to declare as enable like PLL2/PLL3 those clocks are parts or could be part of reparent so, you need to have a reference counter on them that allow to not disable during the down chain disable and up chain enable. I think that what happens to your ethernet it's that you disable some clock that is critical to the board to survive. I had a patch merged by Tom that
Yep, thats what I think too! If you access registers in a block for which the clock is not enabled ... it just hang...
prints the clock name so if you enable the debug of the clock you will find that your board stops working during one of this reparinting.
I currently work on 2024.07... will rebase if 2024.10 is out...
Ah, you mean:
commit a70d991212c9684e09ed80ece69ce1ff7bfd9f08 Author: Michael Trimarchi michael@amarulasolutions.com Date: Tue Jul 9 08:28:13 2024 +0200
clk: clk-uclass: Print clk name in clk_enable/clk_disable
Print clk name in clk_enable and clk_disable. Make sense to know what clock get disabled/enabled before a system crash or system hang.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com
I have the same change when I debug :-P
IIRC I did not see the clock names ... but I will recheck!
I see with your patch the clock names, so fine... and see [1]
Hmm... I am on imx8mp, and I think the changes the patchset do in
"clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate"
are in clk-imx8mp already ...
Ported the patch from patchset
"[PATCH 05/26] clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled"
to imx8mp [2] and fec ethernet works again for me on imx8mp!
Could you add this if you post a v2 ?
TBH I don't feel like the below change is the correct one, it is too specific. The clock core is recursive and thus should handle the reparenting situations gracefully.
I posted a series that is targeting the LVDS output on imx8mp. You should probably consider checking these patches as well if you work on imx8mp as well. I also had similar breakages with Ethernet which happened during the assigned-clocks handling. This patch, which is more future and platform agnostic, fixed it:
https://lore.kernel.org/u-boot/20240910101344.110633-3-miquel.raynal@bootlin...
My series is complimentary, even though there are some overlaps that we need to merge.
Thanks, Miquèl

Hi Miguel
On Tue, Oct 1, 2024 at 10:29 AM Miquel Raynal miquel.raynal@bootlin.com wrote:
Hi Heiko,
Hmm.. unfortunately ... I had applied your 2 clock patches, which fixed a problem with enabling parent clocks ... but they broke booting on a carrier which has fec ethernet! After "Net: " output the board hang...
I reverted your 2 clock patches and it bootet again ... so there is a problem ... try to get some more time to look into...
We have a fec, but we had I think two patches more on it. I forget to answer Marek about them because I don't have my board now and because he is partially right (or maybe right). Anyway when we boot we could have and we have clocks that are enabled by bootrom or SPL that we need to declare as enable like PLL2/PLL3 those clocks are parts or could be part of reparent so, you need to have a reference counter on them that allow to not disable during the down chain disable and up chain enable. I think that what happens to your ethernet it's that you disable some clock that is critical to the board to survive. I had a patch merged by Tom that
Yep, thats what I think too! If you access registers in a block for which the clock is not enabled ... it just hang...
prints the clock name so if you enable the debug of the clock you will find that your board stops working during one of this reparinting.
I currently work on 2024.07... will rebase if 2024.10 is out...
Ah, you mean:
commit a70d991212c9684e09ed80ece69ce1ff7bfd9f08 Author: Michael Trimarchi michael@amarulasolutions.com Date: Tue Jul 9 08:28:13 2024 +0200
clk: clk-uclass: Print clk name in clk_enable/clk_disable Print clk name in clk_enable and clk_disable. Make sense to know what clock get disabled/enabled before a system crash or system hang. Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
I have the same change when I debug :-P
IIRC I did not see the clock names ... but I will recheck!
I see with your patch the clock names, so fine... and see [1]
Hmm... I am on imx8mp, and I think the changes the patchset do in
"clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate"
are in clk-imx8mp already ...
Ported the patch from patchset
"[PATCH 05/26] clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled"
to imx8mp [2] and fec ethernet works again for me on imx8mp!
Could you add this if you post a v2 ?
TBH I don't feel like the below change is the correct one, it is too specific. The clock core is recursive and thus should handle the reparenting situations gracefully.
I posted a series that is targeting the LVDS output on imx8mp. You should probably consider checking these patches as well if you work on imx8mp as well. I also had similar breakages with Ethernet which happened during the assigned-clocks handling. This patch, which is more future and platform agnostic, fixed it:
https://lore.kernel.org/u-boot/20240910101344.110633-3-miquel.raynal@bootlin...
The clock patches are not specific at all. You need to have it working for the parent for each component. This is a standard way to do it and nothing magic compared to other implementations. I don't see in your series any addressing or reparent clock or take in account that a clock should be enable before reparenting.
My series is complimentary, even though there are some overlaps that we need to merge.
The only collision I can see from your series is that you re-write the imx approach of power domain. Can you please expand here a bit your point?
Michael
Thanks, Miquèl

Hi Michael,
michael@amarulasolutions.com wrote on Tue, 1 Oct 2024 10:33:56 +0200:
Hi Miguel
On Tue, Oct 1, 2024 at 10:29 AM Miquel Raynal miquel.raynal@bootlin.com wrote:
Hi Heiko,
Hmm.. unfortunately ... I had applied your 2 clock patches, which fixed a problem with enabling parent clocks ... but they broke booting on a carrier which has fec ethernet! After "Net: " output the board hang...
I reverted your 2 clock patches and it bootet again ... so there is a problem ... try to get some more time to look into...
We have a fec, but we had I think two patches more on it. I forget to answer Marek about them because I don't have my board now and because he is partially right (or maybe right). Anyway when we boot we could have and we have clocks that are enabled by bootrom or SPL that we need to declare as enable like PLL2/PLL3 those clocks are parts or could be part of reparent so, you need to have a reference counter on them that allow to not disable during the down chain disable and up chain enable. I think that what happens to your ethernet it's that you disable some clock that is critical to the board to survive. I had a patch merged by Tom that
Yep, thats what I think too! If you access registers in a block for which the clock is not enabled ... it just hang...
prints the clock name so if you enable the debug of the clock you will find that your board stops working during one of this reparinting.
I currently work on 2024.07... will rebase if 2024.10 is out...
Ah, you mean:
commit a70d991212c9684e09ed80ece69ce1ff7bfd9f08 Author: Michael Trimarchi michael@amarulasolutions.com Date: Tue Jul 9 08:28:13 2024 +0200
clk: clk-uclass: Print clk name in clk_enable/clk_disable Print clk name in clk_enable and clk_disable. Make sense to know what clock get disabled/enabled before a system crash or system hang. Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
I have the same change when I debug :-P
IIRC I did not see the clock names ... but I will recheck!
I see with your patch the clock names, so fine... and see [1]
Hmm... I am on imx8mp, and I think the changes the patchset do in
"clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate"
are in clk-imx8mp already ...
Ported the patch from patchset
"[PATCH 05/26] clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled"
to imx8mp [2] and fec ethernet works again for me on imx8mp!
Could you add this if you post a v2 ?
TBH I don't feel like the below change is the correct one, it is too specific. The clock core is recursive and thus should handle the reparenting situations gracefully.
I posted a series that is targeting the LVDS output on imx8mp. You should probably consider checking these patches as well if you work on imx8mp as well. I also had similar breakages with Ethernet which happened during the assigned-clocks handling. This patch, which is more future and platform agnostic, fixed it:
https://lore.kernel.org/u-boot/20240910101344.110633-3-miquel.raynal@bootlin...
The clock patches are not specific at all. You need to have it working for the parent for each component.
The diff shown by Heiko is explicitly enabling PLLs by naming them in the iMX driver. This is not the correct approach. The problem of having non-enabled new parents is global. Parent clocks should be enabled before changing muxes, and this should be enforced by the clock core/uclass, not the SoC drivers.
This is a standard way to do it and nothing magic compared to other implementations.
No, naming PLLs explicitly is not the correct approach.
I don't see in your series any addressing or reparent clock or take in account that a clock should be enable before reparenting.
That's exactly the link above, whose diff is pasted here for reference:
@@ -595,6 +595,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!ops->set_parent) return -ENOSYS;
+ ret = clk_enable(parent); + if (ret) + return ret;
My series is complimentary, even though there are some overlaps that we need to merge.
The only collision I can see from your series is that you re-write the imx approach of power domain. Can you please expand here a bit your point?
Well, that's what I'd call an overlap :) There are also similar changes in the clock core IIRC. Well, there is a bit of merging that needs to happen I guess, but if you don't think there is any then my series can enter as-is (once the comments addressed, ofc).
Thanks, Miquèl

Hello Miquel,
On 01.10.24 10:50, Miquel Raynal wrote:
Hi Michael,
michael@amarulasolutions.com wrote on Tue, 1 Oct 2024 10:33:56 +0200:
Hi Miguel
On Tue, Oct 1, 2024 at 10:29 AM Miquel Raynal miquel.raynal@bootlin.com wrote:
Hi Heiko,
> Hmm.. unfortunately ... I had applied your 2 clock patches, which > fixed a problem with enabling parent clocks ... but they broke booting > on a carrier which has fec ethernet! After "Net: " output the board hang... > > I reverted your 2 clock patches and it bootet again ... so there is > a problem ... try to get some more time to look into... >
We have a fec, but we had I think two patches more on it. I forget to answer Marek about them because I don't have my board now and because he is partially right (or maybe right). Anyway when we boot we could have and we have clocks that are enabled by bootrom or SPL that we need to declare as enable like PLL2/PLL3 those clocks are parts or could be part of reparent so, you need to have a reference counter on them that allow to not disable during the down chain disable and up chain enable. I think that what happens to your ethernet it's that you disable some clock that is critical to the board to survive. I had a patch merged by Tom that
Yep, thats what I think too! If you access registers in a block for which the clock is not enabled ... it just hang...
prints the clock name so if you enable the debug of the clock you will find that your board stops working during one of this reparinting.
I currently work on 2024.07... will rebase if 2024.10 is out...
Ah, you mean:
commit a70d991212c9684e09ed80ece69ce1ff7bfd9f08 Author: Michael Trimarchi michael@amarulasolutions.com Date: Tue Jul 9 08:28:13 2024 +0200
clk: clk-uclass: Print clk name in clk_enable/clk_disable Print clk name in clk_enable and clk_disable. Make sense to know what clock get disabled/enabled before a system crash or system hang. Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
I have the same change when I debug :-P
IIRC I did not see the clock names ... but I will recheck!
I see with your patch the clock names, so fine... and see [1]
Hmm... I am on imx8mp, and I think the changes the patchset do in
"clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate"
are in clk-imx8mp already ...
Ported the patch from patchset
"[PATCH 05/26] clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled"
to imx8mp [2] and fec ethernet works again for me on imx8mp!
Could you add this if you post a v2 ?
TBH I don't feel like the below change is the correct one, it is too specific. The clock core is recursive and thus should handle the reparenting situations gracefully.
I posted a series that is targeting the LVDS output on imx8mp. You should probably consider checking these patches as well if you work on imx8mp as well. I also had similar breakages with Ethernet which happened during the assigned-clocks handling. This patch, which is more future and platform agnostic, fixed it:
https://lore.kernel.org/u-boot/20240910101344.110633-3-miquel.raynal@bootlin...
The clock patches are not specific at all. You need to have it working for the parent for each component.
The diff shown by Heiko is explicitly enabling PLLs by naming them in the iMX driver. This is not the correct approach. The problem of having non-enabled new parents is global. Parent clocks should be enabled before changing muxes, and this should be enforced by the clock core/uclass, not the SoC drivers.
Okay, valid argument.
This is a standard way to do it and nothing magic compared to other implementations.
No, naming PLLs explicitly is not the correct approach.
I don't see in your series any addressing or reparent clock or take in account that a clock should be enable before reparenting.
That's exactly the link above, whose diff is pasted here for reference:
@@ -595,6 +595,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!ops->set_parent) return -ENOSYS;
- ret = clk_enable(parent);
- if (ret)
return ret;
As I said before, I had *exact* the same patch and thought I made a big hack :-P
But I wonder ... if this a generic "problem", why nobody had yet problems with it...
Thanks!
bye, Heiko
My series is complimentary, even though there are some overlaps that we need to merge.
The only collision I can see from your series is that you re-write the imx approach of power domain. Can you please expand here a bit your point?
Well, that's what I'd call an overlap :) There are also similar changes in the clock core IIRC. Well, there is a bit of merging that needs to happen I guess, but if you don't think there is any then my series can enter as-is (once the comments addressed, ofc).
Thanks, Miquèl

Hi all
On Tue, Oct 1, 2024 at 10:59 AM Heiko Schocher hs@denx.de wrote:
Hello Miquel,
On 01.10.24 10:50, Miquel Raynal wrote:
Hi Michael,
michael@amarulasolutions.com wrote on Tue, 1 Oct 2024 10:33:56 +0200:
Hi Miguel
On Tue, Oct 1, 2024 at 10:29 AM Miquel Raynal miquel.raynal@bootlin.com wrote:
Hi Heiko,
>> Hmm.. unfortunately ... I had applied your 2 clock patches, which >> fixed a problem with enabling parent clocks ... but they broke booting >> on a carrier which has fec ethernet! After "Net: " output the board hang... >> >> I reverted your 2 clock patches and it bootet again ... so there is >> a problem ... try to get some more time to look into... >> > > We have a fec, but we had I think two patches more on it. I forget to > answer Marek > about them because I don't have my board now and because he is > partially right (or maybe right). > Anyway when we boot we could have and we have clocks that are enabled > by bootrom or SPL that > we need to declare as enable like PLL2/PLL3 those clocks are parts or > could be part of reparent so, > you need to have a reference counter on them that allow to not disable > during the down chain disable > and up chain enable. I think that what happens to your ethernet it's > that you disable some clock that is > critical to the board to survive. I had a patch merged by Tom that
Yep, thats what I think too! If you access registers in a block for which the clock is not enabled ... it just hang...
> prints the clock name so if you enable > the debug of the clock you will find that your board stops working > during one of this reparinting.
I currently work on 2024.07... will rebase if 2024.10 is out...
Ah, you mean:
commit a70d991212c9684e09ed80ece69ce1ff7bfd9f08 Author: Michael Trimarchi michael@amarulasolutions.com Date: Tue Jul 9 08:28:13 2024 +0200
clk: clk-uclass: Print clk name in clk_enable/clk_disable Print clk name in clk_enable and clk_disable. Make sense to know what clock get disabled/enabled before a system crash or system hang. Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
I have the same change when I debug :-P
IIRC I did not see the clock names ... but I will recheck!
I see with your patch the clock names, so fine... and see [1]
Hmm... I am on imx8mp, and I think the changes the patchset do in
"clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate"
are in clk-imx8mp already ...
Ported the patch from patchset
"[PATCH 05/26] clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled"
to imx8mp [2] and fec ethernet works again for me on imx8mp!
Could you add this if you post a v2 ?
TBH I don't feel like the below change is the correct one, it is too specific. The clock core is recursive and thus should handle the reparenting situations gracefully.
I posted a series that is targeting the LVDS output on imx8mp. You should probably consider checking these patches as well if you work on imx8mp as well. I also had similar breakages with Ethernet which happened during the assigned-clocks handling. This patch, which is more future and platform agnostic, fixed it:
https://lore.kernel.org/u-boot/20240910101344.110633-3-miquel.raynal@bootlin...
The clock patches are not specific at all. You need to have it working for the parent for each component.
The diff shown by Heiko is explicitly enabling PLLs by naming them in the iMX driver. This is not the correct approach. The problem of having non-enabled new parents is global. Parent clocks should be enabled before changing muxes, and this should be enforced by the clock core/uclass, not the SoC drivers.
Okay, valid argument.
This is a standard way to do it and nothing magic compared to other implementations.
No, naming PLLs explicitly is not the correct approach.
I don't see in your series any addressing or reparent clock or take in account that a clock should be enable before reparenting.
That's exactly the link above, whose diff is pasted here for reference:
@@ -595,6 +595,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!ops->set_parent) return -ENOSYS;
ret = clk_enable(parent);
if (ret)
return ret;
As I said before, I had *exact* the same patch and thought I made a big hack :-P
But I wonder ... if this a generic "problem", why nobody had yet problems with it...
I think that a generic approach that takes into account the reparent is more valuable, then this. If a clock is enabled by another stage and we don't aware about it we need to mark as enabled. I think that this force of enable it's just a short path that does not solve the generic problem. I really tried to abstract what is really implemented in other OS on the same topic. I don't want to have a discussion about who should merge first.
Michael
Thanks!
bye, Heiko
My series is complimentary, even though there are some overlaps that we need to merge.
The only collision I can see from your series is that you re-write the imx approach of power domain. Can you please expand here a bit your point?
Well, that's what I'd call an overlap :) There are also similar changes in the clock core IIRC. Well, there is a bit of merging that needs to happen I guess, but if you don't think there is any then my series can enter as-is (once the comments addressed, ofc).
Thanks, Miquèl
-- DENX Software Engineering GmbH, Managing Director: Erika Unter HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-52 Fax: +49-8142-66989-80 Email: hs@denx.de
-- Michael Nazzareno Trimarchi Co-Founder & Chief Executive Officer M. +39 347 913 2170 michael@amarulasolutions.com __________________________________
Amarula Solutions BV Joop Geesinkweg 125, 1114 AB, Amsterdam, NL T. +31 (0)85 111 9172 info@amarulasolutions.com www.amarulasolutions.com

Hi Michael,
Ported the patch from patchset
"[PATCH 05/26] clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled"
to imx8mp [2] and fec ethernet works again for me on imx8mp!
Could you add this if you post a v2 ?
TBH I don't feel like the below change is the correct one, it is too specific. The clock core is recursive and thus should handle the reparenting situations gracefully.
I posted a series that is targeting the LVDS output on imx8mp. You should probably consider checking these patches as well if you work on imx8mp as well. I also had similar breakages with Ethernet which happened during the assigned-clocks handling. This patch, which is more future and platform agnostic, fixed it:
https://lore.kernel.org/u-boot/20240910101344.110633-3-miquel.raynal@bootlin...
The clock patches are not specific at all. You need to have it working for the parent for each component.
The diff shown by Heiko is explicitly enabling PLLs by naming them in the iMX driver. This is not the correct approach. The problem of having non-enabled new parents is global. Parent clocks should be enabled before changing muxes, and this should be enforced by the clock core/uclass, not the SoC drivers.
Okay, valid argument.
This is a standard way to do it and nothing magic compared to other implementations.
No, naming PLLs explicitly is not the correct approach.
I don't see in your series any addressing or reparent clock or take in account that a clock should be enable before reparenting.
That's exactly the link above, whose diff is pasted here for reference:
@@ -595,6 +595,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!ops->set_parent) return -ENOSYS;
ret = clk_enable(parent);
if (ret)
return ret;
As I said before, I had *exact* the same patch and thought I made a big hack :-P
But I wonder ... if this a generic "problem", why nobody had yet problems with it...
I think that a generic approach that takes into account the reparent is more valuable, then this.
I'm sorry I don't fully understand your answer. I assume you agree with the generic approach quoted above.
If a clock is enabled by another stage and we don't aware about it we need to mark as enabled.
clk_enable() already handles this kind of situation.
I think that this force of enable it's just a short path that does not solve the generic problem.
What "force of enable"? The parent needs to be enabled before we use it as parent. The logic is always the same: - clk_enable() the new parent - change the parent - clk_disable() the old parent I don't see any short path here.
I really tried to abstract what is really implemented in other OS on the same topic.
FYI, Linux does the clk_enable(parent) like above.
Thanks, Miquèl

Hi
On Tue, Oct 1, 2024 at 12:01 PM Miquel Raynal miquel.raynal@bootlin.com wrote:
Hi Michael,
> Ported the patch from patchset > > "[PATCH 05/26] clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled" > > to imx8mp [2] and fec ethernet works again for me on imx8mp! > > Could you add this if you post a v2 ?
TBH I don't feel like the below change is the correct one, it is too specific. The clock core is recursive and thus should handle the reparenting situations gracefully.
I posted a series that is targeting the LVDS output on imx8mp. You should probably consider checking these patches as well if you work on imx8mp as well. I also had similar breakages with Ethernet which happened during the assigned-clocks handling. This patch, which is more future and platform agnostic, fixed it:
https://lore.kernel.org/u-boot/20240910101344.110633-3-miquel.raynal@bootlin...
The clock patches are not specific at all. You need to have it working for the parent for each component.
The diff shown by Heiko is explicitly enabling PLLs by naming them in the iMX driver. This is not the correct approach. The problem of having non-enabled new parents is global. Parent clocks should be enabled before changing muxes, and this should be enforced by the clock core/uclass, not the SoC drivers.
Okay, valid argument.
This is a standard way to do it and nothing magic compared to other implementations.
No, naming PLLs explicitly is not the correct approach.
I don't see in your series any addressing or reparent clock or take in account that a clock should be enable before reparenting.
That's exactly the link above, whose diff is pasted here for reference:
@@ -595,6 +595,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!ops->set_parent) return -ENOSYS;
ret = clk_enable(parent);
if (ret)
return ret;
As I said before, I had *exact* the same patch and thought I made a big hack :-P
But I wonder ... if this a generic "problem", why nobody had yet problems with it...
I think that a generic approach that takes into account the reparent is more valuable, then this.
I'm sorry I don't fully understand your answer. I assume you agree with the generic approach quoted above.
If a clock is enabled by another stage and we don't aware about it we need to mark as enabled.
clk_enable() already handles this kind of situation.
I think that this force of enable it's just a short path that does not solve the generic problem.
What "force of enable"? The parent needs to be enabled before we use it as parent. The logic is always the same:
- clk_enable() the new parent
- change the parent
- clk_disable() the old parent
I don't see any short path here.
I really tried to abstract what is really implemented in other OS on the same topic.
FYI, Linux does the clk_enable(parent) like above.
You mean linux enables a clock in set_parent even if the clock_chain is not yet enabled. clock a disable clock b disable
clk_set_parent(a, b) b->a now b is enabled?
Michael
Thanks, Miquèl

Hi Michael,
michael@amarulasolutions.com wrote on Tue, 1 Oct 2024 15:02:56 +0200:
Hi
On Tue, Oct 1, 2024 at 12:01 PM Miquel Raynal miquel.raynal@bootlin.com wrote:
Hi Michael,
>> Ported the patch from patchset >> >> "[PATCH 05/26] clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled" >> >> to imx8mp [2] and fec ethernet works again for me on imx8mp! >> >> Could you add this if you post a v2 ? > > TBH I don't feel like the below change is the correct one, it is too > specific. The clock core is recursive and thus should handle the > reparenting situations gracefully. > > I posted a series that is targeting the LVDS output on imx8mp. You > should probably consider checking these patches as well if you work > on imx8mp as well. I also had similar breakages with Ethernet which > happened during the assigned-clocks handling. This patch, which is more > future and platform agnostic, fixed it: > > https://lore.kernel.org/u-boot/20240910101344.110633-3-miquel.raynal@bootlin... >
The clock patches are not specific at all. You need to have it working for the parent for each component.
The diff shown by Heiko is explicitly enabling PLLs by naming them in the iMX driver. This is not the correct approach. The problem of having non-enabled new parents is global. Parent clocks should be enabled before changing muxes, and this should be enforced by the clock core/uclass, not the SoC drivers.
Okay, valid argument.
This is a standard way to do it and nothing magic compared to other implementations.
No, naming PLLs explicitly is not the correct approach.
I don't see in your series any addressing or reparent clock or take in account that a clock should be enable before reparenting.
That's exactly the link above, whose diff is pasted here for reference:
@@ -595,6 +595,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!ops->set_parent) return -ENOSYS;
ret = clk_enable(parent);
if (ret)
return ret;
As I said before, I had *exact* the same patch and thought I made a big hack :-P
But I wonder ... if this a generic "problem", why nobody had yet problems with it...
I think that a generic approach that takes into account the reparent is more valuable, then this.
I'm sorry I don't fully understand your answer. I assume you agree with the generic approach quoted above.
If a clock is enabled by another stage and we don't aware about it we need to mark as enabled.
clk_enable() already handles this kind of situation.
I think that this force of enable it's just a short path that does not solve the generic problem.
What "force of enable"? The parent needs to be enabled before we use it as parent. The logic is always the same:
- clk_enable() the new parent
- change the parent
- clk_disable() the old parent
I don't see any short path here.
I really tried to abstract what is really implemented in other OS on the same topic.
FYI, Linux does the clk_enable(parent) like above.
You mean linux enables a clock in set_parent even if the clock_chain is not yet enabled. clock a disable clock b disable
clk_set_parent(a, b) b->a now b is enabled?
Actually you're right Linux does the enable call only if a flag is set (and anyway it's not in the faulty case reported above) or when the reparented clock is already clocking. I'll have to check again with this clk_enable() solves the situation and what could be done instead.
Thanks, Miquèl

Hi Heiko,
hs@denx.de wrote on Tue, 1 Oct 2024 10:57:27 +0200:
Hello Miquel,
On 01.10.24 10:50, Miquel Raynal wrote:
Hi Michael,
michael@amarulasolutions.com wrote on Tue, 1 Oct 2024 10:33:56 +0200:
Hi Miguel
On Tue, Oct 1, 2024 at 10:29 AM Miquel Raynal miquel.raynal@bootlin.com wrote:
Hi Heiko,
>>>>> Hmm.. unfortunately ... I had applied your 2 clock patches, which >> fixed a problem with enabling parent clocks ... but they broke booting >> on a carrier which has fec ethernet! After "Net: " output the board hang... >> >> I reverted your 2 clock patches and it bootet again ... so there is >> a problem ... try to get some more time to look into... >> >>>>>> > We have a fec, but we had I think two patches more on it. I forget to > answer Marek > about them because I don't have my board now and because he is > partially right (or maybe right). > Anyway when we boot we could have and we have clocks that are enabled > by bootrom or SPL that > we need to declare as enable like PLL2/PLL3 those clocks are parts or > could be part of reparent so, > you need to have a reference counter on them that allow to not disable > during the down chain disable > and up chain enable. I think that what happens to your ethernet it's > that you disable some clock that is > critical to the board to survive. I had a patch merged by Tom that
Yep, thats what I think too! If you access registers in a block for which the clock is not enabled ... it just hang... >>>>>> prints the clock name so if you enable > the debug of the clock you will find that your board stops working > during one of this reparinting.
I currently work on 2024.07... will rebase if 2024.10 is out...
Ah, you mean:
commit a70d991212c9684e09ed80ece69ce1ff7bfd9f08 Author: Michael Trimarchi michael@amarulasolutions.com Date: Tue Jul 9 08:28:13 2024 +0200
clk: clk-uclass: Print clk name in clk_enable/clk_disable Print clk name in clk_enable and clk_disable. Make sense to know what clock get disabled/enabled before a system crash or system hang. Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
I have the same change when I debug :-P
IIRC I did not see the clock names ... but I will recheck!
I see with your patch the clock names, so fine... and see [1]
Hmm... I am on imx8mp, and I think the changes the patchset do in
"clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate"
are in clk-imx8mp already ...
Ported the patch from patchset
"[PATCH 05/26] clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled"
to imx8mp [2] and fec ethernet works again for me on imx8mp!
Could you add this if you post a v2 ?
TBH I don't feel like the below change is the correct one, it is too specific. The clock core is recursive and thus should handle the reparenting situations gracefully.
I posted a series that is targeting the LVDS output on imx8mp. You should probably consider checking these patches as well if you work on imx8mp as well. I also had similar breakages with Ethernet which happened during the assigned-clocks handling. This patch, which is more future and platform agnostic, fixed it:
https://lore.kernel.org/u-boot/20240910101344.110633-3-miquel.raynal@bootlin...
The clock patches are not specific at all. You need to have it working for the parent for each component.
The diff shown by Heiko is explicitly enabling PLLs by naming them in the iMX driver. This is not the correct approach. The problem of having non-enabled new parents is global. Parent clocks should be enabled before changing muxes, and this should be enforced by the clock core/uclass, not the SoC drivers.
Okay, valid argument.
This is a standard way to do it and nothing magic compared to other implementations.
No, naming PLLs explicitly is not the correct approach.
I don't see in your series any addressing or reparent clock or take in account that a clock should be enable before reparenting.
That's exactly the link above, whose diff is pasted here for reference:
@@ -595,6 +595,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!ops->set_parent) return -ENOSYS;
- ret = clk_enable(parent);
- if (ret)
return ret;
As I said before, I had *exact* the same patch and thought I made a big hack :-P
I don't think so :-)
But I wonder ... if this a generic "problem", why nobody had yet problems with it...
It depends on your hardware and how you use it I guess. If you look into the terribly complex clock slices explanation in the imx8mp manual, you'll see what the target clock interface does behind the scenes when you use it. There are like 25 steps internally to prevent glitch-free changes. I suspect one of those actions to stall if the parent is not enabled early enough during the reparenting procedure.
Now, the reason why parents might be disabled is because of the high level of configurability this SoC offers which is likely higher than many other SoCs. But in general, to avoid these stalls, there has been a quite extensive use of assigned-clocks properties. I believe with yet another entry in this list, we could probably make it work, but that's not the correct approach either.
Thanks, Miquèl

Hello Miquel,
On 01.10.24 10:29, Miquel Raynal wrote:
Hi Heiko,
Hmm.. unfortunately ... I had applied your 2 clock patches, which fixed a problem with enabling parent clocks ... but they broke booting on a carrier which has fec ethernet! After "Net: " output the board hang...
I reverted your 2 clock patches and it bootet again ... so there is a problem ... try to get some more time to look into...
We have a fec, but we had I think two patches more on it. I forget to answer Marek about them because I don't have my board now and because he is partially right (or maybe right). Anyway when we boot we could have and we have clocks that are enabled by bootrom or SPL that we need to declare as enable like PLL2/PLL3 those clocks are parts or could be part of reparent so, you need to have a reference counter on them that allow to not disable during the down chain disable and up chain enable. I think that what happens to your ethernet it's that you disable some clock that is critical to the board to survive. I had a patch merged by Tom that
Yep, thats what I think too! If you access registers in a block for which the clock is not enabled ... it just hang...
prints the clock name so if you enable the debug of the clock you will find that your board stops working during one of this reparinting.
I currently work on 2024.07... will rebase if 2024.10 is out...
Ah, you mean:
commit a70d991212c9684e09ed80ece69ce1ff7bfd9f08 Author: Michael Trimarchi michael@amarulasolutions.com Date: Tue Jul 9 08:28:13 2024 +0200
clk: clk-uclass: Print clk name in clk_enable/clk_disable
Print clk name in clk_enable and clk_disable. Make sense to know what clock get disabled/enabled before a system crash or system hang.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com
I have the same change when I debug :-P
IIRC I did not see the clock names ... but I will recheck!
I see with your patch the clock names, so fine... and see [1]
Hmm... I am on imx8mp, and I think the changes the patchset do in
"clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate"
are in clk-imx8mp already ...
Ported the patch from patchset
"[PATCH 05/26] clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled"
to imx8mp [2] and fec ethernet works again for me on imx8mp!
Could you add this if you post a v2 ?
TBH I don't feel like the below change is the correct one, it is too specific. The clock core is recursive and thus should handle the reparenting situations gracefully.
I posted a series that is targeting the LVDS output on imx8mp. You should probably consider checking these patches as well if you work on imx8mp as well. I also had similar breakages with Ethernet which happened during the assigned-clocks handling. This patch, which is more future and platform agnostic, fixed it:
https://lore.kernel.org/u-boot/20240910101344.110633-3-miquel.raynal@bootlin...
Yes, I also had such a "fix" first, before seeing Darios patchset!
Damn, I did not see your patches...
regarding your patch ... I am unsure which version is the best one ... I fear of side effects for other plattforms adding this change in generic "drivers/clk/clk-uclass.c" file...
My series is complimentary, even though there are some overlaps that we need to merge.
I try to find time to test them on my board!
bye, Heiko

Hi Heiko,
I see with your patch the clock names, so fine... and see [1]
Hmm... I am on imx8mp, and I think the changes the patchset do in
"clk: imx8mn: Prevent clock critical path from disabling during reparent and set_rate"
are in clk-imx8mp already ...
Ported the patch from patchset
"[PATCH 05/26] clk: imx8mm: Mark IMX8MM_SYS_PLL2 and IMX8MM_SYS_PLL3 as enabled"
to imx8mp [2] and fec ethernet works again for me on imx8mp!
Could you add this if you post a v2 ?
TBH I don't feel like the below change is the correct one, it is too specific. The clock core is recursive and thus should handle the reparenting situations gracefully.
I posted a series that is targeting the LVDS output on imx8mp. You should probably consider checking these patches as well if you work on imx8mp as well. I also had similar breakages with Ethernet which happened during the assigned-clocks handling. This patch, which is more future and platform agnostic, fixed it:
https://lore.kernel.org/u-boot/20240910101344.110633-3-miquel.raynal@bootlin...
Yes, I also had such a "fix" first, before seeing Darios patchset!
Damn, I did not see your patches...
regarding your patch ... I am unsure which version is the best one ... I fear of side effects for other plattforms adding this change in generic "drivers/clk/clk-uclass.c" file...
FYI, this is the same approach as Linux. I don't expect any hardware related breakage, however if people have already tried to workaround the core's mistakes by doing more than expected in the SoC drivers, maybe we will observe issues.
My series is complimentary, even though there are some overlaps that we need to merge.
I try to find time to test them on my board!
Sure, let me know if you have troubles, it's still fresh :-) I will rework it a bit as it received already interesting feedback, and will note to Cc you (as well as Dario/Michael) in v2.
Thanks, Miquèl

From: Michael Trimarchi michael@amarulasolutions.com
The video link framework bases a port-endpoint gragh in DTB to connect the video components in uclass like: video, display, bridge, and panel.
Using the port-endpoint gragh, we manage multiple video link and user can select one of them for splash screen.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
common/stdio.c | 4 + drivers/video/Kconfig | 6 + drivers/video/Makefile | 1 + drivers/video/video_link.c | 529 +++++++++++++++++++++++++++++++++++++ include/video_link.h | 19 ++ 5 files changed, 559 insertions(+) create mode 100644 drivers/video/video_link.c create mode 100644 include/video_link.h
diff --git a/common/stdio.c b/common/stdio.c index a61220ce4b9c..66360c97ed12 100644 --- a/common/stdio.c +++ b/common/stdio.c @@ -17,6 +17,7 @@ #include <stdio_dev.h> #include <serial.h> #include <splash.h> +#include <video_link.h> #include <i2c.h> #include <asm/global_data.h> #include <dm/device-internal.h> @@ -340,6 +341,9 @@ int stdio_add_devices(void) struct udevice *vdev; int ret;
+ if (IS_ENABLED(CONFIG_VIDEO_LINK)) + video_link_init(); + if (!IS_ENABLED(CONFIG_SYS_CONSOLE_IS_IN_ENV)) { for (ret = uclass_first_device_check(UCLASS_VIDEO, &vdev); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 6e79694fd192..cafdc15f9fe2 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -833,6 +833,12 @@ config VIDEO_SEPS525 source "drivers/video/zynqmp/Kconfig" source "drivers/video/nexell/Kconfig"
+config VIDEO_LINK + bool "Enable video link framework support" + help + This option enables a video link framework basing on port-endpoint graph + to connect video components. + config CONSOLE_SCROLL_LINES int "Number of lines to scroll the console by" default 1 diff --git a/drivers/video/Makefile b/drivers/video/Makefile index f3f70cd04a17..9fd3645994f1 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_$(SPL_TPL_)SIMPLE_PANEL) += simple_panel.o obj-$(CONFIG_VIDEO_LOGO) += u_boot_logo.o obj-$(CONFIG_$(SPL_TPL_)BMP) += bmp.o
+obj-$(CONFIG_VIDEO_LINK) += video_link.o endif
obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_backlight.o diff --git a/drivers/video/video_link.c b/drivers/video/video_link.c new file mode 100644 index 000000000000..001a759faab9 --- /dev/null +++ b/drivers/video/video_link.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 NXP + * + */ + +#include <command.h> +#include <linux/errno.h> + +#include <dm.h> +#include <dm/uclass-internal.h> +#include <dm/device-internal.h> +#include <dm/ofnode.h> +#include <dm/read.h> +#include <video.h> +#include <panel.h> +#include <env.h> + +struct of_endpoint { + unsigned int port; + unsigned int id; + ofnode local_node; +}; + +#define MAX_LINKS 3 +#define MAX_LINK_DEVICES 5 + +struct video_link { + struct udevice *link_devs[MAX_LINK_DEVICES]; + int dev_num; +}; + +struct video_link video_links[MAX_LINKS]; +struct video_link temp_stack; +ulong video_links_num; +ulong curr_video_link; +bool video_off; + +ofnode ofnode_get_child_by_name(ofnode parent, const char *name) +{ + ofnode child; + const char *child_name; + + for (child = ofnode_first_subnode(parent); + ofnode_valid(child); + child = ofnode_next_subnode(child)) { + child_name = ofnode_get_name(child); + + if (!strncmp(child_name, name, strlen(name))) + break; + } + return child; +} + +ofnode ofnode_graph_get_next_endpoint(ofnode parent, + ofnode prev) +{ + ofnode endpoint; + ofnode port; + const char *name; + + if (!ofnode_valid(prev)) { + ofnode node; + + node = ofnode_find_subnode(parent, "ports"); + if (ofnode_valid(node)) + parent = node; + + port = ofnode_get_child_by_name(parent, "port"); + if (!ofnode_valid(port)) { + debug("no port node found in 0x%lx\n", parent.of_offset); + return ofnode_null(); + } + + endpoint = ofnode_first_subnode(port); + if (ofnode_valid(endpoint)) { + debug("get next endpoint %s\n", ofnode_get_name(endpoint)); + return endpoint; + } + } else { + port = ofnode_get_parent(prev); + endpoint = ofnode_next_subnode(prev); + if (ofnode_valid(endpoint)) { + debug("get next endpoint %s\n", ofnode_get_name(endpoint)); + return endpoint; + } + } + + debug("port %s\n", ofnode_get_name(port)); + + while (1) { + do { + port = ofnode_next_subnode(port); + if (!ofnode_valid(port)) + return ofnode_null(); + + name = ofnode_get_name(port); + } while (strncmp(name, "port", 4)); + + /* + * Now that we have a port node, get the next endpoint by + * getting the next child. If the previous endpoint is NULL this + * will return the first child. + */ + endpoint = ofnode_first_subnode(port); + if (ofnode_valid(endpoint)) { + debug("get next endpoint %s\n", ofnode_get_name(endpoint)); + return endpoint; + } + } + + return ofnode_null(); +} + +#define for_each_endpoint_of_node(parent, child) \ + for (child = ofnode_graph_get_next_endpoint(parent, ofnode_null()); ofnode_valid(child); \ + child = ofnode_graph_get_next_endpoint(parent, child)) + +int ofnode_graph_get_endpoint_count(ofnode node) +{ + ofnode endpoint; + int num = 0; + + for_each_endpoint_of_node(node, endpoint) + num++; + + return num; +} + +int ofnode_graph_parse_endpoint(ofnode node, + struct of_endpoint *endpoint) +{ + ofnode port_node = ofnode_get_parent(node); + + memset(endpoint, 0, sizeof(*endpoint)); + + endpoint->local_node = node; + /* + * It doesn't matter whether the two calls below succeed. + * If they don't then the default value 0 is used. + */ + ofnode_read_u32(port_node, "reg", &endpoint->port); + ofnode_read_u32(node, "reg", &endpoint->id); + + return 0; +} + +ofnode ofnode_graph_get_endpoint_by_regs(const ofnode parent, + int port_reg, int reg) +{ + struct of_endpoint endpoint; + ofnode node; + + for_each_endpoint_of_node(parent, node) { + ofnode_graph_parse_endpoint(node, &endpoint); + if (((port_reg == -1) || endpoint.port == port_reg) && + ((reg == -1) || endpoint.id == reg)) { + debug("get node %s\n", ofnode_get_name(node)); + + return node; + } + } + + return ofnode_null(); +} + +ofnode ofnode_graph_get_remote_endpoint(ofnode node) +{ + ofnode remote; + u32 phandle; + int ret; + + ret = ofnode_read_u32(node, "remote-endpoint", &phandle); + if (ret) { + printf("required remote-endpoint property isn't provided\n"); + return ofnode_null(); + } + + remote = ofnode_get_by_phandle(phandle); + if (!ofnode_valid(remote)) { + printf("failed to find remote-endpoint\n"); + return ofnode_null(); + } + + return remote; +} + +ofnode ofnode_graph_get_port_parent(ofnode node) +{ + unsigned int depth; + + if (!ofnode_valid(node)) + return ofnode_null(); + + /* + * Preserve usecount for passed in node as of_get_next_parent() + * will do of_node_put() on it. + */ + + /* Walk 3 levels up only if there is 'ports' node. */ + for (depth = 3; depth && ofnode_valid(node); depth--) { + node = ofnode_get_parent(node); + const char *name = ofnode_get_name(node); + + if (depth == 2 && strcmp(name, "ports")) + break; + } + return node; +} + +ofnode ofnode_graph_get_remote_port_parent(ofnode node) +{ + ofnode np, pp; + + /* Get remote endpoint node. */ + np = ofnode_graph_get_remote_endpoint(node); + + pp = ofnode_graph_get_port_parent(np); + + return pp; +} + +int find_device_by_ofnode(ofnode node, struct udevice **pdev) +{ + int ret; + + if (!ofnode_is_enabled(node)) + return -2; + + ret = uclass_find_device_by_ofnode(UCLASS_DISPLAY, node, pdev); + if (!ret) + return 0; + + ret = uclass_find_device_by_ofnode(UCLASS_DSI_HOST, node, pdev); + if (!ret) + return 0; + + ret = uclass_find_device_by_ofnode(UCLASS_VIDEO_BRIDGE, node, pdev); + if (!ret) + return 0; + + ret = uclass_find_device_by_ofnode(UCLASS_PANEL, node, pdev); + if (!ret) + return 0; + + return -1; +} + +static void video_link_stack_push(struct udevice *dev) +{ + if (temp_stack.dev_num < MAX_LINK_DEVICES) { + temp_stack.link_devs[temp_stack.dev_num] = dev; + temp_stack.dev_num++; + } +} + +static void video_link_stack_pop(void) +{ + if (temp_stack.dev_num > 0) { + temp_stack.link_devs[temp_stack.dev_num] = NULL; + temp_stack.dev_num--; + } +} + +static int duplicate_video_link(void) +{ + if (video_links_num < MAX_LINKS) { + video_links[video_links_num] = temp_stack; + video_links_num++; + + debug("duplicate links num %lu, temp_stack num %d\n", + video_links_num, temp_stack.dev_num); + return 0; + } + + return -ENODEV; +} + +static void video_link_add_node(struct udevice *peer_dev, struct udevice *dev, ofnode dev_node) +{ + int ret = 0; + ofnode remote, endpoint_node; + struct udevice *remote_dev; + bool find = false; + + debug("endpoint cnt %d\n", ofnode_graph_get_endpoint_count(dev_node)); + + video_link_stack_push(dev); + + for_each_endpoint_of_node(dev_node, endpoint_node) { + remote = ofnode_graph_get_remote_port_parent(endpoint_node); + if (!ofnode_valid(remote)) + continue; + + debug("remote %s\n", ofnode_get_name(remote)); + ret = find_device_by_ofnode(remote, &remote_dev); + if (!ret) { + debug("remote dev %s\n", remote_dev->name); + + if (peer_dev && peer_dev == remote_dev) + continue; + + /* it is possible that ofnode of remote_dev is not equal to remote */ + video_link_add_node(dev, remote_dev, remote); + + find = true; + } + } + + /* leaf node or no valid new endpoint, now copy the entire stack to a new video link */ + if (!find) { + ret = duplicate_video_link(); + if (ret) + printf("video link is full\n"); + } + + video_link_stack_pop(); +} + +struct udevice *video_link_get_next_device(struct udevice *curr_dev) +{ + int i, ret; + + if (video_off) + return NULL; + + if (curr_video_link >= video_links_num) { + printf("current video link is not correct\n"); + return NULL; + } + + for (i = 0; i < video_links[curr_video_link].dev_num; i++) { + if (video_links[curr_video_link].link_devs[i] == curr_dev) { + if ((i + 1) < video_links[curr_video_link].dev_num) { + ret = device_probe(video_links[curr_video_link].link_devs[i + 1]); + if (ret) { + printf("probe device is failed, ret %d\n", ret); + return NULL; + } + + return video_links[curr_video_link].link_devs[i + 1]; + } + + debug("fail to find next device, already last one\n"); + return NULL; + } + } + + return NULL; +} + +struct udevice *video_link_get_video_device(void) +{ + int ret; + + if (video_off) + return NULL; + + if (curr_video_link >= video_links_num) + return NULL; + + if (video_links[curr_video_link].dev_num == 0) + return NULL; + + ret = device_probe(video_links[curr_video_link].link_devs[0]); + if (ret) { + printf("probe video device failed, ret %d\n", ret); + return NULL; + } + + return video_links[curr_video_link].link_devs[0]; +} + +int video_link_get_display_timings(struct display_timing *timings) +{ + int i = 0; + int ret; + struct udevice *dev; + + if (video_off) + return -EPERM; + + if (curr_video_link >= video_links_num) + return -ENODEV; + + if (video_links[curr_video_link].dev_num == 0) + return -ENODEV; + + for (i = video_links[curr_video_link].dev_num - 1; i >= 0 ; i--) { + dev = video_links[curr_video_link].link_devs[i]; + if (device_get_uclass_id(dev) == UCLASS_PANEL) { + ret = device_probe(video_links[curr_video_link].link_devs[i]); + if (ret) { + printf("fail to probe panel device %s\n", dev->name); + return ret; + } + + ret = panel_get_display_timing(dev, timings); + if (ret) { + ret = ofnode_decode_display_timing(dev_ofnode(dev), 0, timings); + if (ret) { + printf("fail to get panel timing %s\n", dev->name); + return ret; + } + } + + return 0; + } else if (device_get_uclass_id(dev) == UCLASS_DISPLAY || + device_get_uclass_id(dev) == UCLASS_VIDEO) { + ret = ofnode_decode_display_timing(dev_ofnode(dev), 0, timings); + if (!ret) + return 0; + } + } + + return -EINVAL; +} + +static void list_videolink(bool current_only) +{ + ulong index = 0; + int j; + bool match; + + /* dump the link */ + debug("video link number: %lu\n", video_links_num); + + for (index = 0; index < video_links_num; index++) { + match = false; + if (curr_video_link == index) + match = true; + else if (current_only) + continue; + + printf("[%c]-Video Link %lu", (match) ? '*' : ' ', index); + + if (match) { + struct udevice *video_dev = video_link_get_video_device(); + + if (video_dev) { + printf(" (%u x %u)", video_get_xsize(video_dev), + video_get_ysize(video_dev)); + } + } + + printf("\n"); + + for (j = 0; j < video_links[index].dev_num; j++) { + printf("\t[%d] %s, %s\n", j, video_links[index].link_devs[j]->name, + dev_get_uclass_name(video_links[index].link_devs[j])); + } + } +} + +static int do_videolink(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + char cmd = 'l'; + int ret = 0; + + if (argc > 1) + cmd = argv[1][0]; + + switch (cmd) { + case 'l': /* list */ + list_videolink(false); + break; + default: + ret = CMD_RET_USAGE; + break; + } + + return ret; +} + +int video_link_init(void) +{ + struct udevice *dev; + ulong env_id; + int off; + + memset(&video_links, 0, sizeof(video_links)); + memset(&temp_stack, 0, sizeof(temp_stack)); + + for (uclass_find_first_device(UCLASS_VIDEO, &dev); + dev; + uclass_find_next_device(&dev)) { + video_link_add_node(NULL, dev, dev_ofnode(dev)); + } + + if (video_links_num == 0) { + printf("Fail to setup video link\n"); + return -ENODEV; + } + + /* Read the env variable for default video link */ + off = env_get_yesno("video_off"); + if (off == 1) { + video_off = true; + return 0; + } + + env_id = env_get_ulong("video_link", 10, 0); + if (env_id < video_links_num) + curr_video_link = env_id; + + list_videolink(true); + + return 0; +} + +int video_link_shut_down(void) +{ + struct udevice *video_dev = video_link_get_video_device(); + + if (video_dev) + device_remove(video_dev, DM_REMOVE_NORMAL); + + return 0; +} + +#if CONFIG_IS_ENABLED(SYS_LONGHELP) +static char video_link_help_text[] = + "list\n" + " - show video link info, set video_link variable to select link"; +#endif + +U_BOOT_CMD(videolink, 5, 1, do_videolink, + "list and select video link", video_link_help_text +); diff --git a/include/video_link.h b/include/video_link.h new file mode 100644 index 000000000000..5350bfa9e9d1 --- /dev/null +++ b/include/video_link.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2020 NXP + */ + +#ifndef __VIDEO_LINK +#define __VIDEO_LINK + +int video_link_init(void); + +int video_link_shut_down(void); + +struct udevice *video_link_get_next_device(struct udevice *curr_dev); + +struct udevice *video_link_get_video_device(void); + +int video_link_get_display_timings(struct display_timing *timings); + +#endif

From: Michael Trimarchi michael@amarulasolutions.com
Add new interface check_timing to video bridge uclass. For bridge device who may update timing needs to implement the callback. So host device can sync the timing with the bridge.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/video/bridge/video-bridge-uclass.c | 10 ++++++++++ include/video_bridge.h | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+)
diff --git a/drivers/video/bridge/video-bridge-uclass.c b/drivers/video/bridge/video-bridge-uclass.c index 2084a2e03ee8..7432107238cc 100644 --- a/drivers/video/bridge/video-bridge-uclass.c +++ b/drivers/video/bridge/video-bridge-uclass.c @@ -48,6 +48,16 @@ int video_bridge_check_attached(struct udevice *dev) return ops->check_attached(dev); }
+int video_bridge_check_timing(struct udevice *dev, struct display_timing *timing) +{ + struct video_bridge_ops *ops = video_bridge_get_ops(dev); + + if (ops->check_timing) + return ops->check_timing(dev, timing); + + return 0; +} + int video_bridge_read_edid(struct udevice *dev, u8 *buf, int buf_size) { struct video_bridge_ops *ops = video_bridge_get_ops(dev); diff --git a/include/video_bridge.h b/include/video_bridge.h index 3b429eac578a..8b71b04a1119 100644 --- a/include/video_bridge.h +++ b/include/video_bridge.h @@ -44,6 +44,17 @@ struct video_bridge_ops { */ int (*check_attached)(struct udevice *dev);
+ /** + * check_timing() - check if the timing need update after the bridge device attached + * + * This method is optional - if not provided then return 0 + * + * @dev: Device to check + * @active: The timing to be checked and updated + * Return: 0 if OK, -ve on error + */ + int (*check_timing)(struct udevice *dev, struct display_timing *timing); + /** * set_backlight() - Set the backlight brightness * @@ -98,6 +109,15 @@ int video_bridge_set_active(struct udevice *dev, bool active); */ int video_bridge_check_attached(struct udevice *dev);
+/** + * check_timing() - check if the timing need update after the bridge device attached + * + * @dev: Device to check + * @active: The timing to be checked and updated + * Return: 0 if OK, -ve on error + */ +int video_bridge_check_timing(struct udevice *dev, struct display_timing *timing); + /** * video_bridge_read_edid() - Read information from EDID *

From: Michael Trimarchi michael@amarulasolutions.com
Current operations structure has provided disable callback, but there is no API from dsi_host uclass to use it.
Add this disable host interface, so that dsi bridge can call it to disable host
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/video/dsi-host-uclass.c | 10 ++++++++++ include/dsi_host.h | 8 ++++++++ 2 files changed, 18 insertions(+)
diff --git a/drivers/video/dsi-host-uclass.c b/drivers/video/dsi-host-uclass.c index fde275ad7e2e..745991f8dd94 100644 --- a/drivers/video/dsi-host-uclass.c +++ b/drivers/video/dsi-host-uclass.c @@ -34,6 +34,16 @@ int dsi_host_enable(struct udevice *dev) return ops->enable(dev); }
+int dsi_host_disable(struct udevice *dev) +{ + struct dsi_host_ops *ops = dsi_host_get_ops(dev); + + if (!ops->disable) + return -ENOSYS; + + return ops->disable(dev); +} + UCLASS_DRIVER(dsi_host) = { .id = UCLASS_DSI_HOST, .name = "dsi_host", diff --git a/include/dsi_host.h b/include/dsi_host.h index 83f8839db68d..5873ab698d9e 100644 --- a/include/dsi_host.h +++ b/include/dsi_host.h @@ -70,4 +70,12 @@ int dsi_host_init(struct udevice *dev, */ int dsi_host_enable(struct udevice *dev);
+/** + * dsi_host_disable + * + * @dev: dsi host device + * @return 0 if OK, -ve on error + */ +int dsi_host_disable(struct udevice *dev); + #endif

From: Michael Trimarchi michael@amarulasolutions.com
Update mxsfb for LCD video driver
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/video/mxsfb.c | 139 ++++++++++++++++++++++++++++-------------- 1 file changed, 93 insertions(+), 46 deletions(-)
diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 792d6314d15e..45431f0a1047 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -21,8 +21,13 @@ #include <asm/global_data.h> #include <asm/mach-imx/dma.h> #include <asm/io.h> +#include <reset.h> +#include <panel.h> +#include <video_bridge.h> +#include <video_link.h>
#include "videomodes.h" +#include <dm/device-internal.h>
#define PS2KHZ(ps) (1000000000UL / (ps)) #define HZ2PS(hz) (1000000000UL / ((hz) / 1000)) @@ -30,6 +35,11 @@ #define BITS_PP 18 #define BYTES_PP 4
+struct mxsfb_priv { + fdt_addr_t reg_base; + struct udevice *disp_dev; +}; + struct mxs_dma_desc desc;
/** @@ -56,9 +66,10 @@ __weak void mxsfb_system_setup(void) */
static void mxs_lcd_init(struct udevice *dev, u32 fb_addr, - struct display_timing *timings, int bpp) + struct display_timing *timings, int bpp, bool bridge) { - struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; + struct mxsfb_priv *priv = dev_get_priv(dev); + struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)priv->reg_base; const enum display_flags flags = timings->flags; uint32_t word_len = 0, bus_width = 0; uint8_t valid_data = 0; @@ -109,7 +120,7 @@ static void mxs_lcd_init(struct udevice *dev, u32 fb_addr, } #else /* Kick in the LCDIF clock */ - mxs_set_lcdclk(MXS_LCDIF_BASE, timings->pixelclock.typ / 1000); + mxs_set_lcdclk(priv->reg_base, timings->pixelclock.typ / 1000); #endif
/* Restart the LCDIF block */ @@ -142,26 +153,30 @@ static void mxs_lcd_init(struct udevice *dev, u32 fb_addr, LCDIF_CTRL_BYPASS_COUNT | LCDIF_CTRL_LCDIF_MASTER, ®s->hw_lcdif_ctrl);
- writel(valid_data << LCDIF_CTRL1_BYTE_PACKING_FORMAT_OFFSET, + writel((valid_data << LCDIF_CTRL1_BYTE_PACKING_FORMAT_OFFSET) | + LCDIF_CTRL1_RECOVER_ON_UNDERFLOW, ®s->hw_lcdif_ctrl1);
+ if (bridge) + writel(LCDIF_CTRL2_OUTSTANDING_REQS_REQ_16, ®s->hw_lcdif_ctrl2); + mxsfb_system_setup();
writel((timings->vactive.typ << LCDIF_TRANSFER_COUNT_V_COUNT_OFFSET) | timings->hactive.typ, ®s->hw_lcdif_transfer_count);
- vdctrl0 = LCDIF_VDCTRL0_ENABLE_PRESENT | LCDIF_VDCTRL0_ENABLE_POL | + vdctrl0 = LCDIF_VDCTRL0_ENABLE_PRESENT | LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT | LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT | timings->vsync_len.typ;
- if(flags & DISPLAY_FLAGS_HSYNC_HIGH) + if (flags & DISPLAY_FLAGS_HSYNC_HIGH) vdctrl0 |= LCDIF_VDCTRL0_HSYNC_POL; - if(flags & DISPLAY_FLAGS_VSYNC_HIGH) + if (flags & DISPLAY_FLAGS_VSYNC_HIGH) vdctrl0 |= LCDIF_VDCTRL0_VSYNC_POL; - if(flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) + if (flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) vdctrl0 |= LCDIF_VDCTRL0_DOTCLK_POL; - if(flags & DISPLAY_FLAGS_DE_HIGH) + if (flags & DISPLAY_FLAGS_DE_HIGH) vdctrl0 |= LCDIF_VDCTRL0_ENABLE_POL;
writel(vdctrl0, ®s->hw_lcdif_vdctrl0); @@ -198,10 +213,10 @@ static void mxs_lcd_init(struct udevice *dev, u32 fb_addr, }
static int mxs_probe_common(struct udevice *dev, struct display_timing *timings, - int bpp, u32 fb) + int bpp, u32 fb, bool bridge) { /* Start framebuffer */ - mxs_lcd_init(dev, fb, timings, bpp); + mxs_lcd_init(dev, fb, timings, bpp, bridge);
#ifdef CONFIG_VIDEO_MXS_MODE_SYSTEM /* @@ -212,7 +227,8 @@ static int mxs_probe_common(struct udevice *dev, struct display_timing *timings, * sets the RUN bit, then waits until it gets cleared and repeats this * infinitelly. This way, we get smooth continuous updates of the LCD. */ - struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; + struct mxsfb_priv *priv = dev_get_priv(dev); + struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)priv->reg_base;
memset(&desc, 0, sizeof(struct mxs_dma_desc)); desc.address = (dma_addr_t)&desc; @@ -229,9 +245,9 @@ static int mxs_probe_common(struct udevice *dev, struct display_timing *timings, return 0; }
-static int mxs_remove_common(u32 fb) +static int mxs_remove_common(phys_addr_t reg_base, u32 fb) { - struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; + struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)(reg_base); int timeout = 1000000;
if (!fb) @@ -258,6 +274,7 @@ static int mxs_of_get_timings(struct udevice *dev, int ret = 0; u32 display_phandle; ofnode display_node; + struct mxsfb_priv *priv = dev_get_priv(dev);
ret = ofnode_read_u32(dev_ofnode(dev), "display", &display_phandle); if (ret) { @@ -278,10 +295,19 @@ static int mxs_of_get_timings(struct udevice *dev, return -EINVAL; }
- ret = ofnode_decode_display_timing(display_node, 0, timings); - if (ret) { - dev_err(dev, "failed to get any display timings\n"); - return -EINVAL; + priv->disp_dev = video_link_get_next_device(dev); + if (priv->disp_dev) { + ret = video_link_get_display_timings(timings); + if (ret) { + dev_err(dev, "failed to get any video link display timings\n"); + return -EINVAL; + } + } else { + ret = ofnode_decode_display_timing(display_node, 0, timings); + if (ret) { + dev_err(dev, "failed to get any display timings\n"); + return -EINVAL; + } }
return ret; @@ -291,20 +317,58 @@ static int mxs_video_probe(struct udevice *dev) { struct video_uc_plat *plat = dev_get_uclass_plat(dev); struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct mxsfb_priv *priv = dev_get_priv(dev);
struct display_timing timings; u32 bpp = 0; u32 fb_start, fb_end; int ret; + bool enable_bridge = false;
debug("%s() plat: base 0x%lx, size 0x%x\n", __func__, plat->base, plat->size);
+ priv->reg_base = dev_read_addr(dev); + if (priv->reg_base == FDT_ADDR_T_NONE) { + dev_err(dev, "lcdif base address is not found\n"); + return -EINVAL; + } + ret = mxs_of_get_timings(dev, &timings, &bpp); if (ret) return ret;
- ret = mxs_probe_common(dev, &timings, bpp, plat->base); + if (priv->disp_dev) { + if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) { + if (device_get_uclass_id(priv->disp_dev) == UCLASS_VIDEO_BRIDGE) { + ret = video_bridge_attach(priv->disp_dev); + if (ret) { + dev_err(dev, "fail to attach bridge\n"); + return ret; + } + + ret = video_bridge_set_backlight(priv->disp_dev, 80); + if (ret) { + dev_err(dev, "fail to set backlight\n"); + return ret; + } + + enable_bridge = true; + video_bridge_check_timing(priv->disp_dev, &timings); + } + } + + if (device_get_uclass_id(priv->disp_dev) == UCLASS_PANEL) { + ret = panel_enable_backlight(priv->disp_dev); + if (ret) { + dev_err(dev, "panel %s enable backlight error %d\n", + priv->disp_dev->name, ret); + return ret; + } + } + } + + ret = mxs_probe_common(dev, &timings, bpp, plat->base, enable_bridge); if (ret) return ret;
@@ -343,33 +407,9 @@ static int mxs_video_probe(struct udevice *dev) static int mxs_video_bind(struct udevice *dev) { struct video_uc_plat *plat = dev_get_uclass_plat(dev); - struct display_timing timings; - u32 bpp = 0; - u32 bytes_pp = 0; - int ret;
- ret = mxs_of_get_timings(dev, &timings, &bpp); - if (ret) - return ret; - - switch (bpp) { - case 32: - case 24: - case 18: - bytes_pp = 4; - break; - case 16: - bytes_pp = 2; - break; - case 8: - bytes_pp = 1; - break; - default: - dev_err(dev, "invalid bpp specified (bpp = %i)\n", bpp); - return -EINVAL; - } - - plat->size = timings.hactive.typ * timings.vactive.typ * bytes_pp; + /* Max size supported by LCDIF, because in bind, we can't probe panel */ + plat->size = 1920 * 1080 * 4 * 2;
return 0; } @@ -377,8 +417,12 @@ static int mxs_video_bind(struct udevice *dev) static int mxs_video_remove(struct udevice *dev) { struct video_uc_plat *plat = dev_get_uclass_plat(dev); + struct mxsfb_priv *priv = dev_get_priv(dev); + + if (priv->disp_dev) + device_remove(priv->disp_dev, DM_REMOVE_NORMAL);
- mxs_remove_common(plat->base); + mxs_remove_common(priv->reg_base, plat->base);
return 0; } @@ -389,6 +433,8 @@ static const struct udevice_id mxs_video_ids[] = { { .compatible = "fsl,imx6sx-lcdif" }, { .compatible = "fsl,imx7ulp-lcdif" }, { .compatible = "fsl,imxrt-lcdif" }, + { .compatible = "fsl,imx8mm-lcdif" }, + { .compatible = "fsl,imx8mn-lcdif" }, { /* sentinel */ } };
@@ -400,4 +446,5 @@ U_BOOT_DRIVER(mxs_video) = { .probe = mxs_video_probe, .remove = mxs_video_remove, .flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE, + .priv_auto = sizeof(struct mxsfb_priv), };

From: Michael Trimarchi michael@amarulasolutions.com
To support multiple display or bridge devices, enable the flag, so we can assign the req seq via DTB alias
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/video/bridge/video-bridge-uclass.c | 1 + drivers/video/display-uclass.c | 1 + 2 files changed, 2 insertions(+)
diff --git a/drivers/video/bridge/video-bridge-uclass.c b/drivers/video/bridge/video-bridge-uclass.c index 7432107238cc..5e2b76851fed 100644 --- a/drivers/video/bridge/video-bridge-uclass.c +++ b/drivers/video/bridge/video-bridge-uclass.c @@ -145,6 +145,7 @@ int video_bridge_set_active(struct udevice *dev, bool active) UCLASS_DRIVER(video_bridge) = { .id = UCLASS_VIDEO_BRIDGE, .name = "video_bridge", + .flags = DM_UC_FLAG_SEQ_ALIAS, .per_device_auto = sizeof(struct video_bridge_priv), .pre_probe = video_bridge_pre_probe, }; diff --git a/drivers/video/display-uclass.c b/drivers/video/display-uclass.c index 61a73e1bc2a6..07f05be7dd17 100644 --- a/drivers/video/display-uclass.c +++ b/drivers/video/display-uclass.c @@ -80,5 +80,6 @@ bool display_in_use(struct udevice *dev) UCLASS_DRIVER(display) = { .id = UCLASS_DISPLAY, .name = "display", + .flags = DM_UC_FLAG_SEQ_ALIAS, .per_device_plat_auto = sizeof(struct display_plat), };

From: Michael Trimarchi michael@amarulasolutions.com
Upstream Linux commit 22168675bae7.
For MIPI-CSI sender use-case it is common to specify the allowed link-frequencies which should be used for the MIPI link and is half the hs-clock rate.
This commit adds a helper to calculate the D-PHY timing based on the hs-clock rate so we don't need to calculate the timings within the driver.
Signed-off-by: Marco Felsch m.felsch@pengutronix.de Acked-by: Vinod Koul vkoul@kernel.org Signed-off-by: Sakari Ailus sakari.ailus@linux.intel.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/phy/phy-core-mipi-dphy.c | 30 +++++++++++++++++++++++++----- include/phy-mipi-dphy.h | 3 +++ 2 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c index 8fb985a1e682..727c2b040520 100644 --- a/drivers/phy/phy-core-mipi-dphy.c +++ b/drivers/phy/phy-core-mipi-dphy.c @@ -13,21 +13,23 @@ /* * Minimum D-PHY timings based on MIPI D-PHY specification. Derived * from the valid ranges specified in Section 6.9, Table 14, Page 41 - * of the D-PHY specification (v2.1). + * of the D-PHY specification (v1.2). */ -int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, +static int phy_mipi_dphy_calc_config(unsigned long pixel_clock, unsigned int bpp, unsigned int lanes, + unsigned long long hs_clk_rate, struct phy_configure_opts_mipi_dphy *cfg) { - unsigned long long hs_clk_rate; unsigned long long ui;
if (!cfg) return -EINVAL;
- hs_clk_rate = pixel_clock * bpp; - do_div(hs_clk_rate, lanes); + if (!hs_clk_rate) { + hs_clk_rate = pixel_clock * bpp; + do_div(hs_clk_rate, lanes); + }
ui = ALIGN(PSEC_PER_SEC, hs_clk_rate); do_div(ui, hs_clk_rate); @@ -74,6 +76,24 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, return 0; }
+int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, + unsigned int bpp, + unsigned int lanes, + struct phy_configure_opts_mipi_dphy *cfg) +{ + return phy_mipi_dphy_calc_config(pixel_clock, bpp, lanes, 0, cfg); +} + +int phy_mipi_dphy_get_default_config_for_hsclk(unsigned long long hs_clk_rate, + unsigned int lanes, + struct phy_configure_opts_mipi_dphy *cfg) +{ + if (!hs_clk_rate) + return -EINVAL; + + return phy_mipi_dphy_calc_config(0, 0, lanes, hs_clk_rate, cfg); +} + /* * Validate D-PHY configuration according to MIPI D-PHY specification * (v1.2, Section Section 6.9 "Global Operation Timing Parameters"). diff --git a/include/phy-mipi-dphy.h b/include/phy-mipi-dphy.h index a877ffee845d..1ac128d78dfe 100644 --- a/include/phy-mipi-dphy.h +++ b/include/phy-mipi-dphy.h @@ -279,6 +279,9 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, unsigned int bpp, unsigned int lanes, struct phy_configure_opts_mipi_dphy *cfg); +int phy_mipi_dphy_get_default_config_for_hsclk(unsigned long long hs_clk_rate, + unsigned int lanes, + struct phy_configure_opts_mipi_dphy *cfg); int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg);
#endif /* __PHY_MIPI_DPHY_H_ */

From: Michael Trimarchi michael@amarulasolutions.com
Upstream linux commit 3153fa38e38af5.
According to the comment of the function phy_mipi_dphy_get_default_config(), it uses minimum D-PHY timings based on MIPI D-PHY specification. They are derived from the valid ranges specified in Section 6.9, Table 14, Page 41 of the D-PHY specification (v1.2). The table 14 explicitly mentions that the minimum T-LPX parameter is 50 nanoseconds and the minimum TA-SURE parameter is T-LPX nanoseconds. Likewise, the kernel doc of the 'lpx' and 'ta_sure' members of struct phy_configure_opts_mipi_dphy mentions that the minimum values are 50000 picoseconds and @lpx picoseconds respectively. Also, the function phy_mipi_dphy_config_validate() checks if cfg->lpx is less than 50000 picoseconds and if cfg->ta_sure is less than cfg->lpx, which hints the same minimum values.
Without this patch, the function phy_mipi_dphy_get_default_config() wrongly sets cfg->lpx to 60000 picoseconds and cfg->ta_sure to 2 * cfg->lpx. So, let's correct them to 50000 picoseconds and cfg->lpx respectively.
Note that I've only tested the patch with RM67191 DSI panel on i.MX8mq EVK. Help is needed to test with other i.MX8mq, Meson and Rockchip platforms, as I don't have the hardwares.
Signed-off-by: Liu Ying victor.liu@nxp.com Link: https://lore.kernel.org/r/20220216071257.1647703-1-victor.liu@nxp.com Signed-off-by: Vinod Koul vkoul@kernel.org Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/phy/phy-core-mipi-dphy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c index 727c2b040520..79cab9125002 100644 --- a/drivers/phy/phy-core-mipi-dphy.c +++ b/drivers/phy/phy-core-mipi-dphy.c @@ -64,10 +64,10 @@ static int phy_mipi_dphy_calc_config(unsigned long pixel_clock, cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui);
cfg->init = 100; - cfg->lpx = 60000; + cfg->lpx = 50000; cfg->ta_get = 5 * cfg->lpx; cfg->ta_go = 4 * cfg->lpx; - cfg->ta_sure = 2 * cfg->lpx; + cfg->ta_sure = cfg->lpx; cfg->wakeup = 1000;
cfg->hs_clk_rate = hs_clk_rate;

From: Michael Trimarchi michael@amarulasolutions.com
Upstream linux commit 9a8406ba1a9a29.
The D-PHY specification (v1.2) explicitly mentions that the T-CLK-PRE parameter's unit is Unit Interval(UI) and the minimum value is 8. Also, kernel doc of the 'clk_pre' member of struct phy_configure_opts_mipi_dphy mentions that it should be in UI. However, the dphy core driver wrongly sets 'clk_pre' to 8000, which seems to hint that it's in picoseconds.
So, let's fix the dphy core driver to correctly reflect the T-CLK-PRE parameter's minimum value according to the D-PHY specification.
I'm assuming that all impacted custom drivers shall program values in TxByteClkHS cycles into hardware for the T-CLK-PRE parameter. The D-PHY specification mentions that the frequency of TxByteClkHS is exactly 1/8 the High-Speed(HS) bit rate(each HS bit consumes one UI). So, relevant custom driver code is changed to program those values as DIV_ROUND_UP(cfg->clk_pre, BITS_PER_BYTE), then.
Note that I've only tested the patch with RM67191 DSI panel on i.MX8mq EVK. Help is needed to test with other i.MX8mq, Meson and Rockchip platforms, as I don't have the hardwares.
Tested-by: Liu Ying victor.liu@nxp.com # RM67191 DSI panel on i.MX8mq EVK Reviewed-by: Andrzej Hajda andrzej.hajda@intel.com Reviewed-by: Neil Armstrong narmstrong@baylibre.com # for phy-meson-axg-mipi-dphy.c Tested-by: Neil Armstrong narmstrong@baylibre.com # for phy-meson-axg-mipi-dphy.c Tested-by: Guido Günther agx@sigxcpu.org # Librem 5 (imx8mq) with it's rather picky panel Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Liu Ying victor.liu@nxp.com Link: https://lore.kernel.org/r/20220124024007.1465018-1-victor.liu@nxp.com Signed-off-by: Vinod Koul vkoul@kernel.org Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/phy/meson-axg-mipi-dphy.c | 2 +- drivers/phy/phy-core-mipi-dphy.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/meson-axg-mipi-dphy.c b/drivers/phy/meson-axg-mipi-dphy.c index 3f89de19970b..fb10ccca6ac1 100644 --- a/drivers/phy/meson-axg-mipi-dphy.c +++ b/drivers/phy/meson-axg-mipi-dphy.c @@ -237,7 +237,7 @@ static int phy_meson_axg_mipi_dphy_power_on(struct phy *phy) (DIV_ROUND_UP(priv->config.clk_zero, temp) << 16) | (DIV_ROUND_UP(priv->config.clk_prepare, temp) << 24)); regmap_write(priv->regmap, MIPI_DSI_CLK_TIM1, - DIV_ROUND_UP(priv->config.clk_pre, temp)); + DIV_ROUND_UP(priv->config.clk_pre, BITS_PER_BYTE));
regmap_write(priv->regmap, MIPI_DSI_HS_TIM, DIV_ROUND_UP(priv->config.hs_exit, temp) | diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c index 79cab9125002..27cff85709d9 100644 --- a/drivers/phy/phy-core-mipi-dphy.c +++ b/drivers/phy/phy-core-mipi-dphy.c @@ -36,7 +36,7 @@ static int phy_mipi_dphy_calc_config(unsigned long pixel_clock,
cfg->clk_miss = 0; cfg->clk_post = 60000 + 52 * ui; - cfg->clk_pre = 8000; + cfg->clk_pre = 8; cfg->clk_prepare = 38000; cfg->clk_settle = 95000; cfg->clk_term_en = 0;

From: Michael Trimarchi michael@amarulasolutions.com
Sync with Linux commit fc4a0db4149af ("Merge tag '6.5'").
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
include/linux/math64.h | 110 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+)
diff --git a/include/linux/math64.h b/include/linux/math64.h index eaa9fd5b9685..82bd7472a32c 100644 --- a/include/linux/math64.h +++ b/include/linux/math64.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _LINUX_MATH64_H #define _LINUX_MATH64_H
@@ -12,6 +13,11 @@
/** * div_u64_rem - unsigned 64bit divide with 32bit divisor with remainder + * @dividend: unsigned 64bit dividend + * @divisor: unsigned 32bit divisor + * @remainder: pointer to unsigned 32bit remainder + * + * Return: sets ``*remainder``, then returns dividend / divisor * * This is commonly provided by 32bit archs to provide an optimized 64bit * divide. @@ -24,6 +30,11 @@ static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder)
/** * div_s64_rem - signed 64bit divide with 32bit divisor with remainder + * @dividend: signed 64bit dividend + * @divisor: signed 32bit divisor + * @remainder: pointer to signed 32bit remainder + * + * Return: sets ``*remainder``, then returns dividend / divisor */ static inline s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder) { @@ -33,6 +44,11 @@ static inline s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder)
/** * div64_u64_rem - unsigned 64bit divide with 64bit divisor and remainder + * @dividend: unsigned 64bit dividend + * @divisor: unsigned 64bit divisor + * @remainder: pointer to unsigned 64bit remainder + * + * Return: sets ``*remainder``, then returns dividend / divisor */ static inline u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder) { @@ -42,6 +58,10 @@ static inline u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder)
/** * div64_u64 - unsigned 64bit divide with 64bit divisor + * @dividend: unsigned 64bit dividend + * @divisor: unsigned 64bit divisor + * + * Return: dividend / divisor */ static inline u64 div64_u64(u64 dividend, u64 divisor) { @@ -53,6 +73,10 @@ static inline u64 div64_u64(u64 dividend, u64 divisor)
/** * div64_s64 - signed 64bit divide with 64bit divisor + * @dividend: signed 64bit dividend + * @divisor: signed 64bit divisor + * + * Return: dividend / divisor */ static inline s64 div64_s64(s64 dividend, s64 divisor) { @@ -92,10 +116,14 @@ extern s64 div64_s64(s64 dividend, s64 divisor);
/** * div_u64 - unsigned 64bit divide with 32bit divisor + * @dividend: unsigned 64bit dividend + * @divisor: unsigned 32bit divisor * * This is the most common 64bit divide and should be used if possible, * as many 32bit archs can optimize this variant better than a full 64bit * divide. + * + * Return: dividend / divisor */ #ifndef div_u64 static inline u64 div_u64(u64 dividend, u32 divisor) @@ -107,6 +135,10 @@ static inline u64 div_u64(u64 dividend, u32 divisor)
/** * div_s64 - signed 64bit divide with 32bit divisor + * @dividend: signed 64bit dividend + * @divisor: signed 32bit divisor + * + * Return: dividend / divisor */ #ifndef div_s64 static inline s64 div_s64(s64 dividend, s32 divisor) @@ -228,6 +260,24 @@ static inline u64 mul_u64_u64_shr(u64 a, u64 b, unsigned int shift)
#endif
+#ifndef mul_s64_u64_shr +static inline u64 mul_s64_u64_shr(s64 a, u64 b, unsigned int shift) +{ + u64 ret; + + /* + * Extract the sign before the multiplication and put it back + * afterwards if needed. + */ + ret = mul_u64_u64_shr(abs(a), b, shift); + + if (a < 0) + ret = -((s64)ret); + + return ret; +} +#endif /* mul_s64_u64_shr */ + #ifndef mul_u64_u32_div static inline u64 mul_u64_u32_div(u64 a, u32 mul, u32 divisor) { @@ -257,4 +307,64 @@ static inline u64 mul_u64_u32_div(u64 a, u32 mul, u32 divisor) } #endif /* mul_u64_u32_div */
+u64 mul_u64_u64_div_u64(u64 a, u64 mul, u64 div); + +/** + * DIV64_U64_ROUND_UP - unsigned 64bit divide with 64bit divisor rounded up + * @ll: unsigned 64bit dividend + * @d: unsigned 64bit divisor + * + * Divide unsigned 64bit dividend by unsigned 64bit divisor + * and round up. + * + * Return: dividend / divisor rounded up + */ +#define DIV64_U64_ROUND_UP(ll, d) \ + ({ u64 _tmp = (d); div64_u64((ll) + _tmp - 1, _tmp); }) + +/** + * DIV64_U64_ROUND_CLOSEST - unsigned 64bit divide with 64bit divisor rounded to nearest integer + * @dividend: unsigned 64bit dividend + * @divisor: unsigned 64bit divisor + * + * Divide unsigned 64bit dividend by unsigned 64bit divisor + * and round to closest integer. + * + * Return: dividend / divisor rounded to nearest integer + */ +#define DIV64_U64_ROUND_CLOSEST(dividend, divisor) \ + ({ u64 _tmp = (divisor); div64_u64((dividend) + _tmp / 2, _tmp); }) + +/** + * DIV_U64_ROUND_CLOSEST - unsigned 64bit divide with 32bit divisor rounded to nearest integer + * @dividend: unsigned 64bit dividend + * @divisor: unsigned 32bit divisor + * + * Divide unsigned 64bit dividend by unsigned 32bit divisor + * and round to closest integer. + * + * Return: dividend / divisor rounded to nearest integer + */ +#define DIV_U64_ROUND_CLOSEST(dividend, divisor) \ + ({ u32 _tmp = (divisor); div_u64((u64)(dividend) + _tmp / 2, _tmp); }) + +/** + * DIV_S64_ROUND_CLOSEST - signed 64bit divide with 32bit divisor rounded to nearest integer + * @dividend: signed 64bit dividend + * @divisor: signed 32bit divisor + * + * Divide signed 64bit dividend by signed 32bit divisor + * and round to closest integer. + * + * Return: dividend / divisor rounded to nearest integer + */ +#define DIV_S64_ROUND_CLOSEST(dividend, divisor)( \ +{ \ + s64 __x = (dividend); \ + s32 __d = (divisor); \ + ((__x > 0) == (__d > 0)) ? \ + div_s64((__x + (__d / 2)), __d) : \ + div_s64((__x - (__d / 2)), __d); \ +} \ +) #endif /* _LINUX_MATH64_H */

From: Michael Trimarchi michael@amarulasolutions.com
Samsung MIPI DSIM controller is common DSI IP that can be used in various SoCs like Exynos, i.MX8M Mini/Nano.
In order to access this DSI controller between various platform SoCs, the ideal way to incorporate this in the as a bridge that can support all different platform.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/video/bridge/Kconfig | 13 + drivers/video/bridge/Makefile | 1 + drivers/video/bridge/samsung-dsi-host.c | 1567 +++++++++++++++++++++++ drivers/video/bridge/samsung-dsim.c | 148 +++ drivers/video/bridge/samsung-dsim.h | 20 + 5 files changed, 1749 insertions(+) create mode 100644 drivers/video/bridge/samsung-dsi-host.c create mode 100644 drivers/video/bridge/samsung-dsim.c create mode 100644 drivers/video/bridge/samsung-dsim.h
diff --git a/drivers/video/bridge/Kconfig b/drivers/video/bridge/Kconfig index ab9172737201..5cb1e7af9935 100644 --- a/drivers/video/bridge/Kconfig +++ b/drivers/video/bridge/Kconfig @@ -44,6 +44,19 @@ config VIDEO_BRIDGE_ANALOGIX_ANX6345 The Analogix ANX6345 is RGB-to-DP converter. It enables an eDP LCD panel to be connected to an parallel LCD interface.
+config VIDEO_BRIDGE_SAMSUNG_DSIM + bool "Enable IMX SEC DSI video support" + select MIPI_DPHY_HELPERS + select VIDEO_BRIDGE + select VIDEO_LINK + select VIDEO_MIPI_DSI + help + Enables the common driver code for the Samsung + MIPI DSI block found in SoCs from various vendors. + As this does not provide any functionality by itself (but + rather requires a SoC-specific glue driver to call it), it + can not be enabled from the configuration menu. + config VIDEO_BRIDGE_SOLOMON_SSD2825 bool "Solomon SSD2825 bridge driver" depends on PANEL && DM_GPIO diff --git a/drivers/video/bridge/Makefile b/drivers/video/bridge/Makefile index 58697e3cbe90..8f49013299ae 100644 --- a/drivers/video/bridge/Makefile +++ b/drivers/video/bridge/Makefile @@ -8,5 +8,6 @@ obj-$(CONFIG_VIDEO_BRIDGE_PARADE_DP501) += dp501.o obj-$(CONFIG_VIDEO_BRIDGE_PARADE_PS862X) += ps862x.o obj-$(CONFIG_VIDEO_BRIDGE_NXP_PTN3460) += ptn3460.o obj-$(CONFIG_VIDEO_BRIDGE_ANALOGIX_ANX6345) += anx6345.o +obj-$(CONFIG_VIDEO_BRIDGE_SAMSUNG_DSIM) += samsung-dsim.o samsung-dsi-host.o obj-$(CONFIG_VIDEO_BRIDGE_SOLOMON_SSD2825) += ssd2825.o obj-$(CONFIG_VIDEO_BRIDGE_TOSHIBA_TC358768) += tc358768.o diff --git a/drivers/video/bridge/samsung-dsi-host.c b/drivers/video/bridge/samsung-dsi-host.c new file mode 100644 index 000000000000..dd3e33c4edc7 --- /dev/null +++ b/drivers/video/bridge/samsung-dsi-host.c @@ -0,0 +1,1567 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 Amarula Solutions + * Copyright 2018 NXP + * + */ + +#define LOG_CATEGORY UCLASS_DSI_HOST + +#include <dm.h> +#include <clk.h> +#include <dm/device_compat.h> +#include <asm/io.h> +#include <log.h> +#include <phy-mipi-dphy.h> +#include <linux/err.h> +#include <linux/bug.h> +#include <linux/delay.h> +#include <linux/log2.h> +#include <linux/math64.h> +#include <asm/unaligned.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <div64.h> +#include <video_bridge.h> +#include <panel.h> +#include <dsi_host.h> +#include <asm/arch/gpio.h> +#include <dm/device-internal.h> +#include "samsung-dsim.h" + +#define MIPI_FIFO_TIMEOUT 250000 /* 250ms */ + +#define DRIVER_NAME "samsung_dsi" + +/* reg bit manipulation */ +#define REG_MASK(e, s) (((1 << ((e) - (s) + 1)) - 1) << (s)) +#define REG_PUT(x, e, s) (((x) << (s)) & REG_MASK(e, s)) +#define REG_GET(x, e, s) (((x) & REG_MASK(e, s)) >> (s)) + +#define RGB_STATUS_CMDMODE_INSEL BIT(31) +#define RGB_STATUS_GET_RGBSTATE(x) REG_GET(x, 12, 0) + +#define CLKCTRL_DPHY_SEL_1P5G (0x0 << 29) +#define CLKCTRL_PLLBYPASS BIT(29) +#define CLKCTRL_BYTECLKSRC_DPHY_PLL REG_PUT(0, 26, 25) +#define CLKCTRL_SET_LANEESCCLKEN(x) REG_PUT(x, 23, 19) +#define CLKCTRL_SET_ESCPRESCALER(x) REG_PUT(x, 15, 0) + +#define ESCMODE_SET_STOPSTATE_CNT(X) REG_PUT(x, 31, 21) +#define ESCMODE_FORCESTOPSTATE BIT(20) +#define ESCMODE_FORCEBTA BIT(16) +#define ESCMODE_CMDLPDT BIT(7) +#define ESCMODE_TXLPDT BIT(6) +#define ESCMODE_TXTRIGGERRST BIT(5) + +#define MVPORCH_SET_CMDALLOW(x) REG_PUT(x, 31, 28) +#define MVPORCH_SET_STABLEVFP(x) REG_PUT(x, 26, 16) +#define MVPORCH_SET_MAINVBP(x) REG_PUT(x, 10, 0) + +#define MHPORCH_SET_MAINHFP(x) REG_PUT(x, 31, 16) +#define MHPORCH_SET_MAINHBP(x) REG_PUT(x, 15, 0) + +#define MSYNC_SET_MAINVSA(x) REG_PUT(x, 31, 22) +#define MSYNC_SET_MAINHSA(x) REG_PUT(x, 15, 0) + +#define INTSRC_PLLSTABLE BIT(31) +#define INTSRC_SWRSTRELEASE BIT(30) +#define INTSRC_SFRPLFIFOEMPTY BIT(29) +#define INTSRC_SFRPHFIFOEMPTY BIT(28) +#define INTSRC_FRAMEDONE BIT(24) +#define INTSRC_LPDRTOUT BIT(21) +#define INTSRC_TATOUT BIT(20) +#define INTSRC_RXDATDONE BIT(18) +#define INTSRC_MASK (INTSRC_PLLSTABLE | \ + INTSRC_SWRSTRELEASE | \ + INTSRC_SFRPLFIFOEMPTY | \ + INTSRC_SFRPHFIFOEMPTY | \ + INTSRC_FRAMEDONE | \ + INTSRC_LPDRTOUT | \ + INTSRC_TATOUT | \ + INTSRC_RXDATDONE) + +#define INTMSK_MSKPLLSTABLE BIT(31) +#define INTMSK_MSKSWRELEASE BIT(30) +#define INTMSK_MSKSFRPLFIFOEMPTY BIT(29) +#define INTMSK_MSKSFRPHFIFOEMPTY BIT(28) +#define INTMSK_MSKFRAMEDONE BIT(24) +#define INTMSK_MSKLPDRTOUT BIT(21) +#define INTMSK_MSKTATOUT BIT(20) +#define INTMSK_MSKRXDATDONE BIT(18) + +#define PKTHDR_SET_DATA1(x) REG_PUT(x, 23, 16) +#define PKTHDR_GET_DATA1(x) REG_GET(x, 23, 16) +#define PKTHDR_SET_DATA0(x) REG_PUT(x, 15, 8) +#define PKTHDR_GET_DATA0(x) REG_GET(x, 15, 8) +#define PKTHDR_GET_WC(x) REG_GET(x, 23, 8) +#define PKTHDR_SET_DI(x) REG_PUT(x, 7, 0) +#define PKTHDR_GET_DI(x) REG_GET(x, 7, 0) +#define PKTHDR_SET_DT(x) REG_PUT(x, 5, 0) +#define PKTHDR_GET_DT(x) REG_GET(x, 5, 0) +#define PKTHDR_SET_VC(x) REG_PUT(x, 7, 6) +#define PKTHDR_GET_VC(x) REG_GET(x, 7, 6) + +#define FIFOCTRL_FULLRX BIT(25) +#define FIFOCTRL_EMPTYRX BIT(24) +#define FIFOCTRL_FULLHSFR BIT(23) +#define FIFOCTRL_EMPTYHSFR BIT(22) +#define FIFOCTRL_FULLLSFR BIT(21) +#define FIFOCTRL_EMPTYLSFR BIT(20) +#define FIFOCTRL_FULLHMAIN BIT(11) +#define FIFOCTRL_EMPTYHMAIN BIT(10) +#define FIFOCTRL_FULLLMAIN BIT(9) +#define FIFOCTRL_EMPTYLMAIN BIT(8) +#define FIFOCTRL_NINITRX BIT(4) +#define FIFOCTRL_NINITSFR BIT(3) +#define FIFOCTRL_NINITI80 BIT(2) +#define FIFOCTRL_NINITSUB BIT(1) +#define FIFOCTRL_NINITMAIN BIT(0) + +#define PLLCTRL_DPDNSWAP_CLK BIT(25) +#define PLLCTRL_DPDNSWAP_DAT BIT(24) +#define PLLCTRL_PLLEN BIT(23) +#define PLLCTRL_SET_PMS(x) REG_PUT(x, 19, 1) +#define PLLCTRL_SET_P(x) REG_PUT(x, 18, 13) +#define PLLCTRL_SET_M(x) REG_PUT(x, 12, 3) +#define PLLCTRL_SET_S(x) REG_PUT(x, 2, 0) + +#define MAX_MAIN_HRESOL 2047 +#define MAX_MAIN_VRESOL 2047 +#define MAX_SUB_HRESOL 1024 +#define MAX_SUB_VRESOL 1024 + +/* in KHZ */ +#define MAX_ESC_CLK_FREQ 20000 + +/* dsim all irqs index */ +#define PLLSTABLE 1 +#define SWRSTRELEASE 2 +#define SFRPLFIFOEMPTY 3 +#define SFRPHFIFOEMPTY 4 +#define SYNCOVERRIDE 5 +#define BUSTURNOVER 6 +#define FRAMEDONE 7 +#define LPDRTOUT 8 +#define TATOUT 9 +#define RXDATDONE 10 +#define RXTE 11 +#define RXACK 12 +#define ERRRXECC 13 +#define ERRRXCRC 14 +#define ERRESC3 15 +#define ERRESC2 16 +#define ERRESC1 17 +#define ERRESC0 18 +#define ERRSYNC3 19 +#define ERRSYNC2 20 +#define ERRSYNC1 21 +#define ERRSYNC0 22 +#define ERRCONTROL3 23 +#define ERRCONTROL2 24 +#define ERRCONTROL1 25 +#define ERRCONTROL0 26 + +#define PS_TO_CYCLE(ps, hz) DIV64_U64_ROUND_CLOSEST(((ps) * (hz)), 1000000000000ULL) + +#define MIPI_HFP_PKT_OVERHEAD 6 +#define MIPI_HBP_PKT_OVERHEAD 6 +#define MIPI_HSA_PKT_OVERHEAD 6 + +/* DSIM_STATUS */ +#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) +#define DSIM_STOP_STATE_CLK BIT(8) +#define DSIM_TX_READY_HS_CLK BIT(10) +#define DSIM_PLL_STABLE BIT(31) + +/* DSIM_SWRST */ +#define DSIM_FUNCRST BIT(16) +#define DSIM_SWRST BIT(0) + +/* DSIM_TIMEOUT */ +#define DSIM_LPDR_TIMEOUT(x) ((x) << 0) +#define DSIM_BTA_TIMEOUT(x) ((x) << 16) + +/* DSIM_CLKCTRL */ +#define DSIM_ESC_PRESCALER(x) (((x) & 0xffff) << 0) +#define DSIM_ESC_PRESCALER_MASK (0xffff << 0) +#define DSIM_LANE_ESC_CLK_EN_CLK BIT(19) +#define DSIM_LANE_ESC_CLK_EN_DATA(x) (((x) & 0xf) << 20) +#define DSIM_LANE_ESC_CLK_EN_DATA_MASK (0xf << 20) +#define DSIM_BYTE_CLKEN BIT(24) +#define DSIM_BYTE_CLK_SRC(x) (((x) & 0x3) << 25) +#define DSIM_BYTE_CLK_SRC_MASK (0x3 << 25) +#define DSIM_PLL_BYPASS BIT(27) +#define DSIM_ESC_CLKEN BIT(28) +#define DSIM_TX_REQUEST_HSCLK BIT(31) + +/* DSIM_CONFIG */ +#define DSIM_LANE_EN_CLK BIT(0) +#define DSIM_LANE_EN(x) (((x) & 0xf) << 1) +#define DSIM_NUM_OF_DATA_LANE(x) (((x) & 0x3) << 5) +#define DSIM_SUB_PIX_FORMAT(x) (((x) & 0x7) << 8) +#define DSIM_MAIN_PIX_FORMAT_MASK (0x7 << 12) +#define DSIM_MAIN_PIX_FORMAT_RGB888 (0x7 << 12) +#define DSIM_MAIN_PIX_FORMAT_RGB666 (0x6 << 12) +#define DSIM_MAIN_PIX_FORMAT_RGB666_P (0x5 << 12) +#define DSIM_MAIN_PIX_FORMAT_RGB565 (0x4 << 12) +#define DSIM_SUB_VC(x) (((x) & 0x3) << 16) +#define DSIM_MAIN_VC(x) (((x) & 0x3) << 18) +#define DSIM_HSA_DISABLE_MODE BIT(20) +#define DSIM_HBP_DISABLE_MODE BIT(21) +#define DSIM_HFP_DISABLE_MODE BIT(22) +/* + * The i.MX 8M Mini Applications Processor Reference Manual, + * Rev. 3, 11/2020 Page 4091 + * The i.MX 8M Nano Applications Processor Reference Manual, + * Rev. 2, 07/2022 Page 3058 + * The i.MX 8M Plus Applications Processor Reference Manual, + * Rev. 1, 06/2021 Page 5436 + * all claims this bit is 'HseDisableMode' with the definition + * 0 = Disables transfer + * 1 = Enables transfer + * + * This clearly states that HSE is not a disabled bit. + * + * The naming convention follows as per the manual and the + * driver logic is based on the MIPI_DSI_MODE_VIDEO_HSE flag. + */ +#define DSIM_HSE_DISABLE_MODE BIT(23) +#define DSIM_AUTO_MODE BIT(24) +#define DSIM_VIDEO_MODE BIT(25) +#define DSIM_BURST_MODE BIT(26) +#define DSIM_SYNC_INFORM BIT(27) +#define DSIM_EOT_DISABLE BIT(28) +#define DSIM_MFLUSH_VS BIT(29) +/* This flag is valid only for exynos3250/3472/5260/5430 */ +#define DSIM_CLKLANE_STOP BIT(30) +#define DSIM_NON_CONTINUOUS_CLKLANE BIT(31) + +/* DSIM_ESCMODE */ +#define DSIM_TX_TRIGGER_RST BIT(4) +#define DSIM_TX_LPDT_LP BIT(6) +#define DSIM_CMD_LPDT_LP BIT(7) +#define DSIM_FORCE_BTA BIT(16) +#define DSIM_FORCE_STOP_STATE BIT(20) +#define DSIM_STOP_STATE_CNT(x) (((x) & 0x7ff) << 21) +#define DSIM_STOP_STATE_CNT_MASK (0x7ff << 21) + +/* DSIM_MDRESOL */ +#define DSIM_MAIN_STAND_BY BIT(31) +#define DSIM_MAIN_VRESOL(x, num_bits) (((x) & ((1 << (num_bits)) - 1)) << 16) +#define DSIM_MAIN_HRESOL(x, num_bits) (((x) & ((1 << (num_bits)) - 1)) << 0) + +/* DSIM_MVPORCH */ +#define DSIM_CMD_ALLOW(x) ((x) << 28) +#define DSIM_STABLE_VFP(x) ((x) << 16) +#define DSIM_MAIN_VBP(x) ((x) << 0) +#define DSIM_CMD_ALLOW_MASK (0xf << 28) +#define DSIM_STABLE_VFP_MASK (0x7ff << 16) +#define DSIM_MAIN_VBP_MASK (0x7ff << 0) + +/* DSIM_MHPORCH */ +#define DSIM_MAIN_HFP(x) ((x) << 16) +#define DSIM_MAIN_HBP(x) ((x) << 0) +#define DSIM_MAIN_HFP_MASK ((0xffff) << 16) +#define DSIM_MAIN_HBP_MASK ((0xffff) << 0) + +/* DSIM_MSYNC */ +#define DSIM_MAIN_VSA(x) ((x) << 22) +#define DSIM_MAIN_HSA(x) ((x) << 0) +#define DSIM_MAIN_VSA_MASK ((0x3ff) << 22) +#define DSIM_MAIN_HSA_MASK ((0xffff) << 0) + +/* DSIM_SDRESOL */ +#define DSIM_SUB_STANDY(x) ((x) << 31) +#define DSIM_SUB_VRESOL(x) ((x) << 16) +#define DSIM_SUB_HRESOL(x) ((x) << 0) +#define DSIM_SUB_STANDY_MASK ((0x1) << 31) +#define DSIM_SUB_VRESOL_MASK ((0x7ff) << 16) +#define DSIM_SUB_HRESOL_MASK ((0x7ff) << 0) + +/* DSIM_INTSRC */ +#define DSIM_INT_PLL_STABLE BIT(31) +#define DSIM_INT_SW_RST_RELEASE BIT(30) +#define DSIM_INT_SFR_FIFO_EMPTY BIT(29) +#define DSIM_INT_SFR_HDR_FIFO_EMPTY BIT(28) +#define DSIM_INT_BTA BIT(25) +#define DSIM_INT_FRAME_DONE BIT(24) +#define DSIM_INT_RX_TIMEOUT BIT(21) +#define DSIM_INT_BTA_TIMEOUT BIT(20) +#define DSIM_INT_RX_DONE BIT(18) +#define DSIM_INT_RX_TE BIT(17) +#define DSIM_INT_RX_ACK BIT(16) +#define DSIM_INT_RX_ECC_ERR BIT(15) +#define DSIM_INT_RX_CRC_ERR BIT(14) + +/* DSIM_FIFOCTRL */ +#define DSIM_RX_DATA_FULL BIT(25) +#define DSIM_RX_DATA_EMPTY BIT(24) +#define DSIM_SFR_HEADER_FULL BIT(23) +#define DSIM_SFR_HEADER_EMPTY BIT(22) +#define DSIM_SFR_PAYLOAD_FULL BIT(21) +#define DSIM_SFR_PAYLOAD_EMPTY BIT(20) +#define DSIM_I80_HEADER_FULL BIT(19) +#define DSIM_I80_HEADER_EMPTY BIT(18) +#define DSIM_I80_PAYLOAD_FULL BIT(17) +#define DSIM_I80_PAYLOAD_EMPTY BIT(16) +#define DSIM_SD_HEADER_FULL BIT(15) +#define DSIM_SD_HEADER_EMPTY BIT(14) +#define DSIM_SD_PAYLOAD_FULL BIT(13) +#define DSIM_SD_PAYLOAD_EMPTY BIT(12) +#define DSIM_MD_HEADER_FULL BIT(11) +#define DSIM_MD_HEADER_EMPTY BIT(10) +#define DSIM_MD_PAYLOAD_FULL BIT(9) +#define DSIM_MD_PAYLOAD_EMPTY BIT(8) +#define DSIM_RX_FIFO BIT(4) +#define DSIM_SFR_FIFO BIT(3) +#define DSIM_I80_FIFO BIT(2) +#define DSIM_SD_FIFO BIT(1) +#define DSIM_MD_FIFO BIT(0) + +/* DSIM_PHYACCHR */ +#define DSIM_AFC_EN BIT(14) +#define DSIM_AFC_CTL(x) (((x) & 0x7) << 5) + +/* DSIM_PLLCTRL */ +#define DSIM_FREQ_BAND(x) ((x) << 24) +#define DSIM_PLL_EN BIT(23) +#define DSIM_PLL_P(x, offset) ((x) << (offset)) +#define DSIM_PLL_M(x) ((x) << 4) +#define DSIM_PLL_S(x) ((x) << 1) + +/* DSIM_PHYCTRL */ +#define DSIM_PHYCTRL_ULPS_EXIT(x) (((x) & 0x1ff) << 0) +#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP BIT(30) +#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP BIT(14) + +/* DSIM_PHYTIMING */ +#define DSIM_PHYTIMING_LPX(x) ((x) << 8) +#define DSIM_PHYTIMING_HS_EXIT(x) ((x) << 0) + +/* DSIM_PHYTIMING1 */ +#define DSIM_PHYTIMING1_CLK_PREPARE(x) ((x) << 24) +#define DSIM_PHYTIMING1_CLK_ZERO(x) ((x) << 16) +#define DSIM_PHYTIMING1_CLK_POST(x) ((x) << 8) +#define DSIM_PHYTIMING1_CLK_TRAIL(x) ((x) << 0) + +/* DSIM_PHYTIMING2 */ +#define DSIM_PHYTIMING2_HS_PREPARE(x) ((x) << 16) +#define DSIM_PHYTIMING2_HS_ZERO(x) ((x) << 8) +#define DSIM_PHYTIMING2_HS_TRAIL(x) ((x) << 0) + +#define DSI_MAX_BUS_WIDTH 4 +#define DSI_NUM_VIRTUAL_CHANNELS 4 +#define DSI_TX_FIFO_SIZE 2048 +#define DSI_RX_FIFO_SIZE 256 +#define DSI_XFER_TIMEOUT_MS 100 +#define DSI_RX_FIFO_EMPTY 0x30800002 + +struct samsung_dsim_driver_data { + const unsigned int *reg_ofs; + unsigned int plltmr_reg; + unsigned int has_freqband:1; + unsigned int has_clklane_stop:1; + unsigned int has_broken_fifoctrl_emptyhdr:1; + unsigned int num_clks; + unsigned int min_freq; + unsigned int max_freq; + unsigned int wait_for_reset; + unsigned int num_bits_resol; + unsigned int pll_p_offset; + const unsigned int *reg_values; + unsigned int pll_fin_min; + unsigned int pll_fin_max; + u16 m_min; + u16 m_max; +}; + +enum reg_idx { + DSIM_STATUS_REG, /* Status register */ + DSIM_RGB_STATUS_REG, /* RGB status register */ + DSIM_SWRST_REG, /* Software reset register */ + DSIM_CLKCTRL_REG, /* Clock control register */ + DSIM_TIMEOUT_REG, /* Time out register */ + DSIM_CONFIG_REG, /* Configuration register */ + DSIM_ESCMODE_REG, /* Escape mode register */ + DSIM_MDRESOL_REG, + DSIM_MVPORCH_REG, /* Main display Vporch register */ + DSIM_MHPORCH_REG, /* Main display Hporch register */ + DSIM_MSYNC_REG, /* Main display sync area register */ + DSIM_INTSRC_REG, /* Interrupt source register */ + DSIM_INTMSK_REG, /* Interrupt mask register */ + DSIM_PKTHDR_REG, /* Packet Header FIFO register */ + DSIM_PAYLOAD_REG, /* Payload FIFO register */ + DSIM_RXFIFO_REG, /* Read FIFO register */ + DSIM_FIFOCTRL_REG, /* FIFO status and control register */ + DSIM_PLLCTRL_REG, /* PLL control register */ + DSIM_PHYCTRL_REG, + DSIM_PHYTIMING_REG, + DSIM_PHYTIMING1_REG, + DSIM_PHYTIMING2_REG, + NUM_REGS +}; + +static const unsigned int exynos_reg_ofs[] = { + [DSIM_STATUS_REG] = 0x00, + [DSIM_SWRST_REG] = 0x04, + [DSIM_CLKCTRL_REG] = 0x08, + [DSIM_TIMEOUT_REG] = 0x0c, + [DSIM_CONFIG_REG] = 0x10, + [DSIM_ESCMODE_REG] = 0x14, + [DSIM_MDRESOL_REG] = 0x18, + [DSIM_MVPORCH_REG] = 0x1c, + [DSIM_MHPORCH_REG] = 0x20, + [DSIM_MSYNC_REG] = 0x24, + [DSIM_INTSRC_REG] = 0x2c, + [DSIM_INTMSK_REG] = 0x30, + [DSIM_PKTHDR_REG] = 0x34, + [DSIM_PAYLOAD_REG] = 0x38, + [DSIM_RXFIFO_REG] = 0x3c, + [DSIM_FIFOCTRL_REG] = 0x44, + [DSIM_PLLCTRL_REG] = 0x4c, + [DSIM_PHYCTRL_REG] = 0x5c, + [DSIM_PHYTIMING_REG] = 0x64, + [DSIM_PHYTIMING1_REG] = 0x68, + [DSIM_PHYTIMING2_REG] = 0x6c, +}; + +static const unsigned int exynos5433_reg_ofs[] = { + [DSIM_STATUS_REG] = 0x04, + [DSIM_RGB_STATUS_REG] = 0x08, + [DSIM_SWRST_REG] = 0x0C, + [DSIM_CLKCTRL_REG] = 0x10, + [DSIM_TIMEOUT_REG] = 0x14, + [DSIM_CONFIG_REG] = 0x18, + [DSIM_ESCMODE_REG] = 0x1C, + [DSIM_MDRESOL_REG] = 0x20, + [DSIM_MVPORCH_REG] = 0x24, + [DSIM_MHPORCH_REG] = 0x28, + [DSIM_MSYNC_REG] = 0x2C, + [DSIM_INTSRC_REG] = 0x34, + [DSIM_INTMSK_REG] = 0x38, + [DSIM_PKTHDR_REG] = 0x3C, + [DSIM_PAYLOAD_REG] = 0x40, + [DSIM_RXFIFO_REG] = 0x44, + [DSIM_FIFOCTRL_REG] = 0x4C, + [DSIM_PLLCTRL_REG] = 0x94, + [DSIM_PHYCTRL_REG] = 0xA4, + [DSIM_PHYTIMING_REG] = 0xB4, + [DSIM_PHYTIMING1_REG] = 0xB8, + [DSIM_PHYTIMING2_REG] = 0xBC, +}; + +enum reg_value_idx { + RESET_TYPE, + PLL_TIMER, + STOP_STATE_CNT, + PHYCTRL_ULPS_EXIT, + PHYCTRL_VREG_LP, + PHYCTRL_SLEW_UP, + PHYTIMING_LPX, + PHYTIMING_HS_EXIT, + PHYTIMING_CLK_PREPARE, + PHYTIMING_CLK_ZERO, + PHYTIMING_CLK_POST, + PHYTIMING_CLK_TRAIL, + PHYTIMING_HS_PREPARE, + PHYTIMING_HS_ZERO, + PHYTIMING_HS_TRAIL +}; + +static const unsigned int reg_values[] = { + [RESET_TYPE] = DSIM_SWRST, + [PLL_TIMER] = 500, + [STOP_STATE_CNT] = 0xf, + [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x0af), + [PHYCTRL_VREG_LP] = 0, + [PHYCTRL_SLEW_UP] = 0, + [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x06), + [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0b), + [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x07), + [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x27), + [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0d), + [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x08), + [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x09), + [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x0d), + [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0b), +}; + +static const unsigned int exynos5422_reg_values[] = { + [RESET_TYPE] = DSIM_SWRST, + [PLL_TIMER] = 500, + [STOP_STATE_CNT] = 0xf, + [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0xaf), + [PHYCTRL_VREG_LP] = 0, + [PHYCTRL_SLEW_UP] = 0, + [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x08), + [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0d), + [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09), + [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x30), + [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e), + [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x0a), + [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0c), + [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x11), + [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0d), +}; + +static const unsigned int exynos5433_reg_values[] = { + [RESET_TYPE] = DSIM_FUNCRST, + [PLL_TIMER] = 22200, + [STOP_STATE_CNT] = 0xa, + [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x190), + [PHYCTRL_VREG_LP] = DSIM_PHYCTRL_B_DPHYCTL_VREG_LP, + [PHYCTRL_SLEW_UP] = DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP, + [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x07), + [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0c), + [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09), + [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x2d), + [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e), + [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x09), + [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0b), + [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x10), + [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0c), +}; + +static const unsigned int imx8mm_dsim_reg_values[] = { + [RESET_TYPE] = DSIM_SWRST, + [PLL_TIMER] = 500, + [STOP_STATE_CNT] = 0xf, + [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0xaf), + [PHYCTRL_VREG_LP] = 0, + [PHYCTRL_SLEW_UP] = 0, + [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x06), + [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0b), + [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x07), + [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x26), + [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0d), + [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x08), + [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x08), + [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x0d), + [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0b), +}; + +static const struct samsung_dsim_driver_data exynos3_dsi_driver_data = { + .reg_ofs = exynos_reg_ofs, + .plltmr_reg = 0x50, + .has_freqband = 1, + .has_clklane_stop = 1, + .num_clks = 2, + .max_freq = 1000, + .wait_for_reset = 1, + .num_bits_resol = 11, + .pll_p_offset = 13, + .reg_values = reg_values, + .pll_fin_min = 6, + .pll_fin_max = 12, + .m_min = 41, + .m_max = 125, + .min_freq = 500, + .has_broken_fifoctrl_emptyhdr = 1, +}; + +static const struct samsung_dsim_driver_data exynos4_dsi_driver_data = { + .reg_ofs = exynos_reg_ofs, + .plltmr_reg = 0x50, + .has_freqband = 1, + .has_clklane_stop = 1, + .num_clks = 2, + .max_freq = 1000, + .wait_for_reset = 1, + .num_bits_resol = 11, + .pll_p_offset = 13, + .reg_values = reg_values, + .pll_fin_min = 6, + .pll_fin_max = 12, + .m_min = 41, + .m_max = 125, + .min_freq = 500, + .has_broken_fifoctrl_emptyhdr = 1, +}; + +static const struct samsung_dsim_driver_data exynos5_dsi_driver_data = { + .reg_ofs = exynos_reg_ofs, + .plltmr_reg = 0x58, + .num_clks = 2, + .max_freq = 1000, + .wait_for_reset = 1, + .num_bits_resol = 11, + .pll_p_offset = 13, + .reg_values = reg_values, + .pll_fin_min = 6, + .pll_fin_max = 12, + .m_min = 41, + .m_max = 125, + .min_freq = 500, +}; + +static const struct samsung_dsim_driver_data exynos5433_dsi_driver_data = { + .reg_ofs = exynos5433_reg_ofs, + .plltmr_reg = 0xa0, + .has_clklane_stop = 1, + .num_clks = 5, + .max_freq = 1500, + .wait_for_reset = 0, + .num_bits_resol = 12, + .pll_p_offset = 13, + .reg_values = exynos5433_reg_values, + .pll_fin_min = 6, + .pll_fin_max = 12, + .m_min = 41, + .m_max = 125, + .min_freq = 500, +}; + +static const struct samsung_dsim_driver_data exynos5422_dsi_driver_data = { + .reg_ofs = exynos5433_reg_ofs, + .plltmr_reg = 0xa0, + .has_clklane_stop = 1, + .num_clks = 2, + .max_freq = 1500, + .wait_for_reset = 1, + .num_bits_resol = 12, + .pll_p_offset = 13, + .reg_values = exynos5422_reg_values, + .pll_fin_min = 6, + .pll_fin_max = 12, + .m_min = 41, + .m_max = 125, + .min_freq = 500, +}; + +static const struct samsung_dsim_driver_data imx8mm_dsi_driver_data = { + .reg_ofs = exynos5433_reg_ofs, + .plltmr_reg = 0xa0, + .has_clklane_stop = 1, + .num_clks = 2, + .max_freq = 2100, + .wait_for_reset = 0, + .num_bits_resol = 12, + /* + * Unlike Exynos, PLL_P(PMS_P) offset 14 is used in i.MX8M Mini/Nano/Plus + * downstream driver - drivers/gpu/drm/bridge/sec-dsim.c + */ + .pll_p_offset = 14, + .reg_values = imx8mm_dsim_reg_values, + .pll_fin_min = 2, + .pll_fin_max = 30, + .m_min = 64, + .m_max = 1023, + .min_freq = 1050, +}; + +static const struct samsung_dsim_driver_data * +samsung_dsim_types[DSIM_TYPE_COUNT] = { + [DSIM_TYPE_EXYNOS3250] = &exynos3_dsi_driver_data, + [DSIM_TYPE_EXYNOS4210] = &exynos4_dsi_driver_data, + [DSIM_TYPE_EXYNOS5410] = &exynos5_dsi_driver_data, + [DSIM_TYPE_EXYNOS5422] = &exynos5422_dsi_driver_data, + [DSIM_TYPE_EXYNOS5433] = &exynos5433_dsi_driver_data, + [DSIM_TYPE_IMX8MM] = &imx8mm_dsi_driver_data, + [DSIM_TYPE_IMX8MP] = &imx8mm_dsi_driver_data, +}; + +/* DSIM PLL configuration from spec: + * + * Fout(DDR) = (M * Fin) / (P * 2^S), so Fout / Fin = M / (P * 2^S) + * Fin_pll = Fin / P (6 ~ 12 MHz) + * S: [2:0], M: [12:3], P: [18:13], so + * TODO: 'S' is in [0 ~ 3], 'M' is in, 'P' is in [1 ~ 33] + * + */ + +struct samsung_dsi { + void __iomem *reg_base; + struct clk sclk_mipi; + const struct samsung_dsim_driver_data *driver_data; + + /* kHz clocks */ + u64 pix_clk; + u64 bit_clk; + u64 hs_clock; + + unsigned int lanes; + unsigned int channel; /* virtual channel */ + enum mipi_dsi_pixel_format format; + unsigned long mode_flags; + unsigned int pms; + + struct mipi_dsi_device *device; + u32 max_data_lanes; + + struct mipi_dsi_host dsi_host; + struct display_timing timings; +}; + +static inline void samsung_dsim_write(struct samsung_dsi *dsi, + enum reg_idx idx, u32 val) +{ + writel(val, dsi->reg_base + dsi->driver_data->reg_ofs[idx]); +} + +static inline u32 samsung_dsim_read(struct samsung_dsi *dsi, enum reg_idx idx) +{ + return readl(dsi->reg_base + dsi->driver_data->reg_ofs[idx]); +} + +static int samsung_dsi_wait_for_pkt_done(struct samsung_dsi *dsim, unsigned long timeout) +{ + u32 intsrc; + + do { + intsrc = samsung_dsim_read(dsim, DSIM_INTSRC_REG); + if (intsrc & INTSRC_SFRPLFIFOEMPTY) { + samsung_dsim_write(dsim, DSIM_INTSRC_REG, INTSRC_SFRPLFIFOEMPTY); + return 0; + } + + udelay(1); + } while (--timeout); + + return -ETIMEDOUT; +} + +static int samsung_dsi_wait_for_hdr_done(struct samsung_dsi *dsim, unsigned long timeout) +{ + u32 intsrc; + + do { + intsrc = samsung_dsim_read(dsim, DSIM_INTSRC_REG); + if (intsrc & INTSRC_SFRPHFIFOEMPTY) { + samsung_dsim_write(dsim, DSIM_INTSRC_REG, INTSRC_SFRPHFIFOEMPTY); + return 0; + } + + udelay(1); + } while (--timeout); + + return -ETIMEDOUT; +} + +static int samsung_dsi_wait_for_rx_done(struct samsung_dsi *dsim, + unsigned long timeout) +{ + u32 intsrc; + + do { + intsrc = samsung_dsim_read(dsim, DSIM_INTSRC_REG); + if (intsrc & INTSRC_RXDATDONE) { + samsung_dsim_write(dsim, DSIM_INTSRC_REG, INTSRC_RXDATDONE); + return 0; + } + + udelay(1); + } while (--timeout); + + return -ETIMEDOUT; +} + +static int samsung_dsi_wait_pll_stable(struct samsung_dsi *dsim) +{ + u32 status; + ulong start; + + start = get_timer(0); /* Get current timestamp */ + + do { + status = samsung_dsim_read(dsim, DSIM_STATUS_REG); + if (status & DSIM_PLL_STABLE) + return 0; + } while (get_timer(0) < (start + 100)); /* Wait 100ms */ + + return -ETIMEDOUT; +} + +static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsi *dsi, + unsigned long fin, + unsigned long fout, + u8 *p, u16 *m, u8 *s) +{ + const struct samsung_dsim_driver_data *driver_data = dsi->driver_data; + unsigned long best_freq = 0; + u32 min_delta = 0xffffffff; + u8 p_min, p_max; + u8 _p, best_p; + u16 _m, best_m; + u8 _s, best_s; + + p_min = DIV_ROUND_UP(fin, (MHZ(12))); + p_max = fin / (MHZ(6)); + + for (_p = p_min; _p <= p_max; ++_p) { + for (_s = 0; _s <= 5; ++_s) { + u64 tmp; + u32 delta; + + tmp = (u64)fout * (_p << _s); + do_div(tmp, fin); + _m = tmp; + if (_m < driver_data->m_min || _m > driver_data->m_max) + continue; + + tmp = (u64)_m * fin; + do_div(tmp, _p); + if (tmp < driver_data->min_freq * MHZ(1) || + tmp > driver_data->max_freq * MHZ(1)) + continue; + + tmp = (u64)_m * fin; + do_div(tmp, _p << _s); + + delta = abs(fout - tmp); + if (delta < min_delta) { + best_p = _p; + best_m = _m; + best_s = _s; + min_delta = delta; + best_freq = tmp; + } + } + } + + if (best_freq) { + *p = best_p; + *m = best_m; + *s = best_s; + } + + return best_freq; +} + +static int samsung_dsi_config_pll(struct samsung_dsi *dsim) +{ + int ret; + u32 pllctrl = 0, status, data_lanes_en, stop; + + writel(dsim->driver_data->reg_values[PLL_TIMER], + dsim->reg_base + dsim->driver_data->plltmr_reg); + + /* TODO: config dp/dn swap if requires */ + + pllctrl |= PLLCTRL_SET_PMS(dsim->pms) | PLLCTRL_PLLEN; + samsung_dsim_write(dsim, DSIM_PLLCTRL_REG, pllctrl); + + ret = samsung_dsi_wait_pll_stable(dsim); + if (ret) { + log_err("wait for pll stable time out\n"); + return ret; + } + + /* wait for clk & data lanes to go to stop state */ + mdelay(1); + + data_lanes_en = (0x1 << dsim->lanes) - 1; + status = samsung_dsim_read(dsim, DSIM_STATUS_REG); + if (!(status & DSIM_STOP_STATE_CLK)) { + log_err("clock is not in stop state\n"); + return -EBUSY; + } + + stop = DSIM_STOP_STATE_DAT(status); + if ((stop & data_lanes_en) != data_lanes_en) { + log_err("one or more data lanes is not in stop state\n"); + return -EBUSY; + } + + return 0; +} + +static void samsung_dsi_set_main_mode(struct samsung_dsi *dsim) +{ + u32 bpp, hfp_wc, hbp_wc, hsa_wc, wc; + u32 mdresol = 0, mvporch = 0, mhporch = 0, msync = 0; + struct display_timing *timings = &dsim->timings; + unsigned int num_bits_resol = dsim->driver_data->num_bits_resol; + + mdresol |= DSIM_MAIN_VRESOL(timings->vactive.typ, num_bits_resol) | + DSIM_MAIN_HRESOL(timings->hactive.typ, num_bits_resol); + samsung_dsim_write(dsim, DSIM_MDRESOL_REG, mdresol); + + mvporch |= MVPORCH_SET_MAINVBP(timings->vback_porch.typ) | + MVPORCH_SET_STABLEVFP(timings->vfront_porch.typ) | + MVPORCH_SET_CMDALLOW(0x0); + samsung_dsim_write(dsim, DSIM_MVPORCH_REG, mvporch); + + bpp = mipi_dsi_pixel_format_to_bpp(dsim->format); + + wc = DIV_ROUND_UP(timings->hfront_porch.typ * (bpp >> 3), dsim->lanes); + hfp_wc = wc > MIPI_HFP_PKT_OVERHEAD ? + wc - MIPI_HFP_PKT_OVERHEAD : timings->hfront_porch.typ; + wc = DIV_ROUND_UP(timings->hback_porch.typ * (bpp >> 3), dsim->lanes); + hbp_wc = wc > MIPI_HBP_PKT_OVERHEAD ? + wc - MIPI_HBP_PKT_OVERHEAD : timings->hback_porch.typ; + + mhporch |= MHPORCH_SET_MAINHFP(hfp_wc) | + MHPORCH_SET_MAINHBP(hbp_wc); + + samsung_dsim_write(dsim, DSIM_MHPORCH_REG, mhporch); + + wc = DIV_ROUND_UP(timings->hsync_len.typ * (bpp >> 3), dsim->lanes); + hsa_wc = wc > MIPI_HSA_PKT_OVERHEAD ? + wc - MIPI_HSA_PKT_OVERHEAD : timings->hsync_len.typ; + + msync |= MSYNC_SET_MAINVSA(timings->vsync_len.typ) | + MSYNC_SET_MAINHSA(hsa_wc); + + debug("hfp_wc %u hbp_wc %u hsa_wc %u\n", hfp_wc, hbp_wc, hsa_wc); + + samsung_dsim_write(dsim, DSIM_MSYNC_REG, msync); +} + +static void samsung_dsi_config_dpi(struct samsung_dsi *dsim) +{ + u32 config = 0, rgb_status = 0, data_lanes_en; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) + rgb_status &= ~RGB_STATUS_CMDMODE_INSEL; + else + rgb_status |= RGB_STATUS_CMDMODE_INSEL; + + samsung_dsim_write(dsim, DSIM_RGB_STATUS_REG, rgb_status); + + if (dsim->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) { + config |= DSIM_CLKLANE_STOP; + config |= DSIM_NON_CONTINUOUS_CLKLANE; + } + + if (dsim->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH) + config |= DSIM_MFLUSH_VS; + + /* disable EoT packets in HS mode */ + if (dsim->mode_flags & MIPI_DSI_MODE_EOT_PACKET) + config |= DSIM_EOT_DISABLE; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) { + config |= DSIM_VIDEO_MODE; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) + config |= DSIM_BURST_MODE; + + else if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) + config |= DSIM_SYNC_INFORM; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT) + config |= DSIM_AUTO_MODE; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSE) + config |= DSIM_HSE_DISABLE_MODE; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HFP) + config |= DSIM_HFP_DISABLE_MODE; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HBP) + config |= DSIM_HBP_DISABLE_MODE; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSA) + config |= DSIM_HSA_DISABLE_MODE; + } + + config |= DSIM_MAIN_VC(dsim->channel); + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) { + switch (dsim->format) { + case MIPI_DSI_FMT_RGB888: + config |= DSIM_MAIN_PIX_FORMAT_RGB888; + break; + case MIPI_DSI_FMT_RGB666: + config |= DSIM_MAIN_PIX_FORMAT_RGB666; + break; + case MIPI_DSI_FMT_RGB666_PACKED: + config |= DSIM_MAIN_PIX_FORMAT_RGB666_P; + break; + case MIPI_DSI_FMT_RGB565: + config |= DSIM_MAIN_PIX_FORMAT_RGB565; + break; + default: + log_err("invalid pixel format\n"); + break; + } + } + + /* config data lanes number and enable lanes */ + data_lanes_en = BIT(dsim->lanes) - 1; + config |= (DSIM_NUM_OF_DATA_LANE(dsim->lanes - 1) | DSIM_LANE_EN_CLK | + DSIM_LANE_EN(data_lanes_en)); + + debug("DSIM config 0x%x\n", config); + + samsung_dsim_write(dsim, DSIM_CONFIG_REG, config); +} + +static void samsung_dsi_config_cmd_lpm(struct samsung_dsi *dsim, bool enable) +{ + u32 escmode; + + escmode = samsung_dsim_read(dsim, DSIM_ESCMODE_REG); + + if (enable) + escmode |= ESCMODE_CMDLPDT; + else + escmode &= ~ESCMODE_CMDLPDT; + + samsung_dsim_write(dsim, DSIM_ESCMODE_REG, escmode); +} + +static void samsung_dsi_config_dphy(struct samsung_dsi *dsim) +{ + const struct samsung_dsim_driver_data *driver_data = dsim->driver_data; + const unsigned int *reg_values = driver_data->reg_values; + u32 reg; + struct phy_configure_opts_mipi_dphy cfg; + int clk_prepare, lpx, clk_zero, clk_post, clk_trail; + int hs_exit, hs_prepare, hs_zero, hs_trail; + unsigned long long byte_clock = dsim->hs_clock / 8; + + if (driver_data->has_freqband) + return; + + phy_mipi_dphy_get_default_config_for_hsclk(dsim->hs_clock, dsim->lanes, &cfg); + + /* + * TODO: + * The tech Applications Processor manuals for i.MX8M Mini, Nano, + * and Plus don't state what the definition of the PHYTIMING + * bits are beyond their address and bit position. + * After reviewing NXP's downstream code, it appears + * that the various PHYTIMING registers take the number + * of cycles and use various dividers on them. This + * calculation does not result in an exact match to the + * downstream code, but it is very close to the values + * generated by their lookup table, and it appears + * to sync at a variety of resolutions. If someone + * can get a more accurate mathematical equation needed + * for these registers, this should be updated. + */ + + lpx = PS_TO_CYCLE(cfg.lpx, byte_clock); + hs_exit = PS_TO_CYCLE(cfg.hs_exit, byte_clock); + clk_prepare = PS_TO_CYCLE(cfg.clk_prepare, byte_clock); + clk_zero = PS_TO_CYCLE(cfg.clk_zero, byte_clock); + clk_post = PS_TO_CYCLE(cfg.clk_post, byte_clock); + clk_trail = PS_TO_CYCLE(cfg.clk_trail, byte_clock); + hs_prepare = PS_TO_CYCLE(cfg.hs_prepare, byte_clock); + hs_zero = PS_TO_CYCLE(cfg.hs_zero, byte_clock); + hs_trail = PS_TO_CYCLE(cfg.hs_trail, byte_clock); + + /* B D-PHY: D-PHY Master & Slave Analog Block control */ + reg = reg_values[PHYCTRL_ULPS_EXIT] | reg_values[PHYCTRL_VREG_LP] | + reg_values[PHYCTRL_SLEW_UP]; + + samsung_dsim_write(dsim, DSIM_PHYCTRL_REG, reg); + + /* + * T LPX: Transmitted length of any Low-Power state period + * T HS-EXIT: Time that the transmitter drives LP-11 following a HS + * burst + */ + + reg = DSIM_PHYTIMING_LPX(lpx) | DSIM_PHYTIMING_HS_EXIT(hs_exit); + samsung_dsim_write(dsim, DSIM_PHYTIMING_REG, reg); + + /* + * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00 + * Line state immediately before the HS-0 Line state starting the + * HS transmission + * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to + * transmitting the Clock. + * T CLK_POST: Time that the transmitter continues to send HS clock + * after the last associated Data Lane has transitioned to LP Mode + * Interval is defined as the period from the end of T HS-TRAIL to + * the beginning of T CLK-TRAIL + * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after + * the last payload clock bit of a HS transmission burst + */ + + reg = DSIM_PHYTIMING1_CLK_PREPARE(clk_prepare) | + DSIM_PHYTIMING1_CLK_ZERO(clk_zero) | + DSIM_PHYTIMING1_CLK_POST(clk_post) | + DSIM_PHYTIMING1_CLK_TRAIL(clk_trail); + + samsung_dsim_write(dsim, DSIM_PHYTIMING1_REG, reg); + + /* + * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00 + * Line state immediately before the HS-0 Line state starting the + * HS transmission + * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to + * transmitting the Sync sequence. + * T HS-TRAIL: Time that the transmitter drives the flipped differential + * state after last payload data bit of a HS transmission burst + */ + + reg = DSIM_PHYTIMING2_HS_PREPARE(hs_prepare) | + DSIM_PHYTIMING2_HS_ZERO(hs_zero) | + DSIM_PHYTIMING2_HS_TRAIL(hs_trail); + + samsung_dsim_write(dsim, DSIM_PHYTIMING2_REG, reg); + + reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff); + + samsung_dsim_write(dsim, DSIM_TIMEOUT_REG, reg); +} + +static void samsung_dsim_write_pl_to_sfr_fifo(struct samsung_dsi *dsim, + const void *payload, + size_t length) +{ + u32 pl_data; + + if (!length) + return; + + while (length >= 4) { + pl_data = get_unaligned_le32(payload); + samsung_dsim_write(dsim, DSIM_PAYLOAD_REG, pl_data); + payload += 4; + length -= 4; + } + + pl_data = 0; + switch (length) { + case 3: + pl_data |= ((u8 *)payload)[2] << 16; + case 2: + pl_data |= ((u8 *)payload)[1] << 8; + case 1: + pl_data |= ((u8 *)payload)[0]; + samsung_dsim_write(dsim, DSIM_PAYLOAD_REG, pl_data); + break; + } +} + +static void samsung_dsim_write_ph_to_sfr_fifo(struct samsung_dsi *dsim, + void *header, bool use_lpm) +{ + u32 pkthdr; + + pkthdr = PKTHDR_SET_DATA1(((u8 *)header)[2]) | /* WC MSB */ + PKTHDR_SET_DATA0(((u8 *)header)[1]) | /* WC LSB */ + PKTHDR_SET_DI(((u8 *)header)[0]); /* Data ID */ + + samsung_dsim_write(dsim, DSIM_PKTHDR_REG, pkthdr); +} + +static int samsung_dsim_read_pl_from_sfr_fifo(struct samsung_dsi *dsim, + void *payload, size_t length) +{ + u8 data_type; + u16 word_count = 0; + u32 fifoctrl, ph, pl; + + fifoctrl = samsung_dsim_read(dsim, DSIM_FIFOCTRL_REG); + + if (WARN_ON(fifoctrl & FIFOCTRL_EMPTYRX)) + return -EINVAL; + + ph = samsung_dsim_read(dsim, DSIM_RXFIFO_REG); + data_type = PKTHDR_GET_DT(ph); + switch (data_type) { + case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: + dev_err(dsim->device->dev, "peripheral report error: (0-7)%x, (8-15)%x\n", + PKTHDR_GET_DATA0(ph), PKTHDR_GET_DATA1(ph)); + return -EPROTO; + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: + if (!WARN_ON(length < 2)) { + ((u8 *)payload)[1] = PKTHDR_GET_DATA1(ph); + word_count++; + } + fallthrough; + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: + ((u8 *)payload)[0] = PKTHDR_GET_DATA0(ph); + word_count++; + length = word_count; + break; + case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE: + case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: + word_count = PKTHDR_GET_WC(ph); + if (word_count > length) { + dev_err(dsim->device->dev, "invalid receive buffer length\n"); + return -EINVAL; + } + + length = word_count; + + while (word_count >= 4) { + pl = samsung_dsim_read(dsim, DSIM_RXFIFO_REG); + ((u8 *)payload)[0] = pl & 0xff; + ((u8 *)payload)[1] = (pl >> 8) & 0xff; + ((u8 *)payload)[2] = (pl >> 16) & 0xff; + ((u8 *)payload)[3] = (pl >> 24) & 0xff; + payload += 4; + word_count -= 4; + } + + if (word_count > 0) { + pl = samsung_dsim_read(dsim, DSIM_RXFIFO_REG); + + switch (word_count) { + case 3: + ((u8 *)payload)[2] = (pl >> 16) & 0xff; + case 2: + ((u8 *)payload)[1] = (pl >> 8) & 0xff; + case 1: + ((u8 *)payload)[0] = pl & 0xff; + break; + } + } + + break; + default: + return -EINVAL; + } + + return length; +} + +static void samsung_dsi_init_fifo_pointers(struct samsung_dsi *dsim) +{ + u32 fifoctrl, fifo_ptrs; + + fifoctrl = samsung_dsim_read(dsim, DSIM_FIFOCTRL_REG); + + fifo_ptrs = FIFOCTRL_NINITRX | + FIFOCTRL_NINITSFR | + FIFOCTRL_NINITI80 | + FIFOCTRL_NINITSUB | + FIFOCTRL_NINITMAIN; + + fifoctrl &= ~fifo_ptrs; + samsung_dsim_write(dsim, DSIM_FIFOCTRL_REG, fifoctrl); + udelay(500); + + fifoctrl |= fifo_ptrs; + samsung_dsim_write(dsim, DSIM_FIFOCTRL_REG, fifoctrl); + udelay(500); +} + +static void samsung_dsi_config_clkctrl(struct samsung_dsi *dsim) +{ + u32 clkctrl = 0, data_lanes_en; + u64 byte_clk, esc_prescaler; + + clkctrl |= DSIM_TX_REQUEST_HSCLK; + + /* using 1.5Gbps PHY */ + clkctrl |= CLKCTRL_DPHY_SEL_1P5G; + clkctrl |= DSIM_ESC_CLKEN; + clkctrl &= ~CLKCTRL_PLLBYPASS; + clkctrl |= CLKCTRL_BYTECLKSRC_DPHY_PLL; + clkctrl |= DSIM_BYTE_CLKEN; + + data_lanes_en = (0x1 << dsim->lanes) - 1; + clkctrl |= CLKCTRL_SET_LANEESCCLKEN(0x1 | data_lanes_en << 1); + + /* calculate esc prescaler from byte clock: + * EscClk = ByteClk / EscPrescaler; + */ + byte_clk = dsim->bit_clk >> 3; + esc_prescaler = DIV_ROUND_UP_ULL(byte_clk, MAX_ESC_CLK_FREQ); + + clkctrl |= CLKCTRL_SET_ESCPRESCALER(esc_prescaler); + + debug("DSIM clkctrl 0x%x\n", clkctrl); + + samsung_dsim_write(dsim, DSIM_CLKCTRL_REG, clkctrl); +} + +static void samsung_dsi_set_standby(struct samsung_dsi *dsim, bool standby) +{ + u32 mdresol = 0; + + mdresol = samsung_dsim_read(dsim, DSIM_MDRESOL_REG); + + if (standby) + mdresol |= DSIM_MAIN_STAND_BY; + else + mdresol &= ~DSIM_MAIN_STAND_BY; + + samsung_dsim_write(dsim, DSIM_MDRESOL_REG, mdresol); +} + +static void samsung_dsi_disable_clock(struct samsung_dsi *dsim) +{ + u32 reg; + + reg = samsung_dsim_read(dsim, DSIM_CLKCTRL_REG); + reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK | + DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN); + samsung_dsim_write(dsim, DSIM_CLKCTRL_REG, reg); + + reg = samsung_dsim_read(dsim, DSIM_PLLCTRL_REG); + reg &= ~DSIM_PLL_EN; + samsung_dsim_write(dsim, DSIM_PLLCTRL_REG, reg); +} + +static inline struct samsung_dsi *host_to_dsi(struct mipi_dsi_host *host) +{ + return container_of(host, struct samsung_dsi, dsi_host); +} + +static int samsung_dsi_bridge_clk_set(struct samsung_dsi *dsim_host) +{ + int bpp; + unsigned long pix_clk, bit_clk; + unsigned long fin, fout; + u8 p, s; + u16 m; + + bpp = mipi_dsi_pixel_format_to_bpp(dsim_host->format); + if (bpp < 0) + return -EINVAL; + + pix_clk = dsim_host->timings.pixelclock.typ; + bit_clk = DIV_ROUND_UP_ULL(pix_clk * bpp, dsim_host->lanes); + + dsim_host->pix_clk = DIV_ROUND_UP_ULL(pix_clk, 1000); + dsim_host->bit_clk = DIV_ROUND_UP_ULL(bit_clk, 1000); + + fout = dsim_host->bit_clk; + fin = clk_get_rate(&dsim_host->sclk_mipi); + if (fin == 0) { + log_err("Error: DSI PHY reference clock is disabled\n"); + return -EINVAL; + } + + fout = samsung_dsim_pll_find_pms(dsim_host, fin, bit_clk, &p, &m, &s); + if (!fout) { + log_err("failed to find PLL PMS for requested frequency\n"); + return -EINVAL; + } + dsim_host->pms = PLLCTRL_SET_P(p) | PLLCTRL_SET_M(m) | + PLLCTRL_SET_S(s); + dsim_host->hs_clock = fout; + + debug("%s: bitclk %llu pixclk %llu pms 0x%x\n", __func__, + dsim_host->bit_clk, dsim_host->pix_clk, dsim_host->pms); + + return 0; +} + +static int samsung_dsi_bridge_prepare(struct samsung_dsi *dsim_host) +{ + int ret; + + /* At this moment, the dsim bridge's preceding encoder has + * already been enabled. So the dsim can be configed here + */ + + /* config main display mode */ + samsung_dsi_set_main_mode(dsim_host); + + /* config dsim dpi */ + samsung_dsi_config_dpi(dsim_host); + + /* config dsim pll */ + ret = samsung_dsi_config_pll(dsim_host); + if (ret) { + log_err("dsim pll config failed: %d\n", ret); + return ret; + } + + /* config dphy timings */ + samsung_dsi_config_dphy(dsim_host); + + samsung_dsi_init_fifo_pointers(dsim_host); + + /* config esc clock, byte clock and etc */ + samsung_dsi_config_clkctrl(dsim_host); + + return 0; +} + +static int samsung_dsi_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device) +{ + struct samsung_dsi *dsi = host_to_dsi(host); + + if (!device->lanes || device->lanes > dsi->max_data_lanes) { + log_err("invalid data lanes number\n"); + return -EINVAL; + } + + if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO) || + !((device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) || + (device->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))) { + log_err("unsupported dsi mode\n"); + return -EINVAL; + } + + if (device->format != MIPI_DSI_FMT_RGB888 && + device->format != MIPI_DSI_FMT_RGB565 && + device->format != MIPI_DSI_FMT_RGB666 && + device->format != MIPI_DSI_FMT_RGB666_PACKED) { + log_err("unsupported pixel format: %#x\n", device->format); + return -EINVAL; + } + + dsi->lanes = device->lanes; + dsi->channel = device->channel; + dsi->format = device->format; + dsi->mode_flags = device->mode_flags; + + debug("lanes %u, channel %u, format 0x%x, mode_flags 0x%lx\n", dsi->lanes, + dsi->channel, dsi->format, dsi->mode_flags); + + samsung_dsi_bridge_clk_set(dsi); + samsung_dsi_bridge_prepare(dsi); + + return 0; +} + +static ssize_t samsung_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct samsung_dsi *dsim = host_to_dsi(host); + int ret, nb_bytes; + bool use_lpm; + struct mipi_dsi_packet packet; + + ret = mipi_dsi_create_packet(&packet, msg); + if (ret) { + dev_err(dsim->device->dev, "failed to create dsi packet: %d\n", ret); + return ret; + } + + /* config LPM for CMD TX */ + use_lpm = msg->flags & MIPI_DSI_MSG_USE_LPM ? true : false; + samsung_dsi_config_cmd_lpm(dsim, use_lpm); + + if (packet.payload_length) { /* Long Packet case */ + /* write packet payload */ + samsung_dsim_write_pl_to_sfr_fifo(dsim, packet.payload, + packet.payload_length); + + /* write packet header */ + samsung_dsim_write_ph_to_sfr_fifo(dsim, packet.header, use_lpm); + + ret = samsung_dsi_wait_for_pkt_done(dsim, MIPI_FIFO_TIMEOUT); + if (ret) { + dev_err(dsim->device->dev, "wait tx done timeout!\n"); + return -EBUSY; + } + } else { + /* write packet header */ + samsung_dsim_write_ph_to_sfr_fifo(dsim, packet.header, use_lpm); + + ret = samsung_dsi_wait_for_hdr_done(dsim, MIPI_FIFO_TIMEOUT); + if (ret) { + dev_err(dsim->device->dev, "wait pkthdr tx done time out\n"); + return -EBUSY; + } + } + + /* read packet payload */ + if (unlikely(msg->rx_buf)) { + ret = samsung_dsi_wait_for_rx_done(dsim, MIPI_FIFO_TIMEOUT); + if (ret) { + dev_err(dsim->device->dev, "wait rx done time out\n"); + return -EBUSY; + } + + ret = samsung_dsim_read_pl_from_sfr_fifo(dsim, msg->rx_buf, + msg->rx_len); + if (ret < 0) + return ret; + nb_bytes = msg->rx_len; + } else { + nb_bytes = packet.size; + } + + return nb_bytes; +} + +static const struct mipi_dsi_host_ops samsung_dsi_host_ops = { + .attach = samsung_dsi_host_attach, + .transfer = samsung_dsi_host_transfer, +}; + +static int samsung_dsi_init(struct udevice *dev, + struct mipi_dsi_device *device, + struct display_timing *timings, + unsigned int max_data_lanes, + const struct mipi_dsi_phy_ops *phy_ops) +{ + struct samsung_dsi *dsi = dev_get_priv(dev); + struct udevice *dsi_bridge = device->dev; + enum samsung_dsim_type hw_type = (enum samsung_dsim_type)dev_get_driver_data(dsi_bridge); + + dsi->max_data_lanes = max_data_lanes; + dsi->device = device; + dsi->dsi_host.ops = &samsung_dsi_host_ops; + dsi->driver_data = samsung_dsim_types[hw_type]; + device->host = &dsi->dsi_host; + + dsi->reg_base = (void *)dev_read_addr(device->dev); + if ((fdt_addr_t)dsi->reg_base == FDT_ADDR_T_NONE) { + dev_err(device->dev, "dsi dt register address error\n"); + return -EINVAL; + } + + dsi->timings = *timings; + + return 0; +} + +static int samsung_dsi_enable(struct udevice *dev) +{ + struct samsung_dsi *dsim_host = dev_get_priv(dev); + + /* enable data transfer of dsim */ + samsung_dsi_set_standby(dsim_host, true); + + return 0; +} + +static int samsung_dsi_disable(struct udevice *dev) +{ + u32 intsrc; + struct samsung_dsi *dsim_host = dev_get_priv(dev); + + /* disable data transfer of dsim */ + samsung_dsi_set_standby(dsim_host, false); + + /* disable esc clock & byte clock & dsim pll */ + samsung_dsi_disable_clock(dsim_host); + + /* Clear all intsrc */ + intsrc = samsung_dsim_read(dsim_host, DSIM_INTSRC_REG); + samsung_dsim_write(dsim_host, DSIM_INTSRC_REG, intsrc); + + return 0; +} + +struct dsi_host_ops samsung_dsi_ops = { + .init = samsung_dsi_init, + .enable = samsung_dsi_enable, + .disable = samsung_dsi_disable, +}; + +static int samsung_dsi_probe(struct udevice *dev) +{ + struct samsung_dsi *dsim_host = dev_get_priv(dev); + int ret; + + ret = clk_get_by_name(dev, "sclk_mipi", &dsim_host->sclk_mipi); + if (ret) + debug("Failed to get sclk_mipi clock\n"); + + return ret; +} + +static const struct udevice_id samsung_dsi_ids[] = { + { .compatible = "samsung,sec-mipi-dsi" }, + { } +}; + +U_BOOT_DRIVER(samsung_dsi) = { + .name = "samsung_dsi", + .id = UCLASS_DSI_HOST, + .of_match = samsung_dsi_ids, + .probe = samsung_dsi_probe, + .remove = samsung_dsi_disable, + .ops = &samsung_dsi_ops, + .priv_auto = sizeof(struct samsung_dsi), +}; diff --git a/drivers/video/bridge/samsung-dsim.c b/drivers/video/bridge/samsung-dsim.c new file mode 100644 index 000000000000..986f1d830844 --- /dev/null +++ b/drivers/video/bridge/samsung-dsim.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 Amarula Solutions + * Copyright 2019 NXP + * + */ + +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dsi_host.h> +#include <mipi_dsi.h> +#include <panel.h> +#include <video.h> +#include <video_bridge.h> +#include <video_link.h> +#include <asm/io.h> +#include <asm/arch/gpio.h> +#include <dm/device-internal.h> +#include <linux/iopoll.h> +#include <linux/err.h> +#include <power/regulator.h> +#include "samsung-dsim.h" + +struct samsung_dsim_priv { + struct mipi_dsi_device device; + void __iomem *base; + struct udevice *panel; + struct udevice *dsi_host; +}; + +static int samsung_dsim_attach(struct udevice *dev) +{ + struct samsung_dsim_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + struct mipi_dsi_panel_plat *mplat; + struct display_timing timings; + int ret; + + priv->panel = video_link_get_next_device(dev); + if (!priv->panel || device_get_uclass_id(priv->panel) != UCLASS_PANEL) { + dev_err(dev, "get panel device error\n"); + return -ENODEV; + } + + mplat = dev_get_plat(priv->panel); + mplat->device = &priv->device; + + ret = video_link_get_display_timings(&timings); + if (ret) { + dev_err(dev, "decode display timing error %d\n", ret); + return ret; + } + + ret = uclass_get_device(UCLASS_DSI_HOST, 0, &priv->dsi_host); + if (ret) { + dev_err(dev, "No video dsi host detected %d\n", ret); + return ret; + } + + /* allow to use the compatible */ + device->dev = dev; + ret = dsi_host_init(priv->dsi_host, device, &timings, 4, + NULL); + if (ret) { + dev_err(dev, "failed to initialize mipi dsi host\n"); + return ret; + } + + return 0; +} + +static int samsung_dsim_set_backlight(struct udevice *dev, int percent) +{ + struct samsung_dsim_priv *priv = dev_get_priv(dev); + int ret; + + ret = panel_enable_backlight(priv->panel); + if (ret) { + dev_err(dev, "panel %s enable backlight error %d\n", + priv->panel->name, ret); + return ret; + } + + ret = dsi_host_enable(priv->dsi_host); + if (ret) { + dev_err(dev, "failed to enable mipi dsi host\n"); + return ret; + } + + return 0; +} + +static int samsung_dsim_probe(struct udevice *dev) +{ + struct samsung_dsim_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + + device->dev = dev; + + return 0; +} + +static int samsung_dsim_remove(struct udevice *dev) +{ + struct samsung_dsim_priv *priv = dev_get_priv(dev); + int ret; + + if (priv->panel) + device_remove(priv->panel, DM_REMOVE_NORMAL); + + ret = dsi_host_disable(priv->dsi_host); + if (ret) { + dev_err(dev, "failed to enable mipi dsi host\n"); + return ret; + } + + return 0; +} + +static int samsung_dsim_check_timing(struct udevice *dev, struct display_timing *timings) +{ + timings->flags &= ~DISPLAY_FLAGS_DE_HIGH; + return 0; +} + +struct video_bridge_ops samsung_dsim_ops = { + .attach = samsung_dsim_attach, + .set_backlight = samsung_dsim_set_backlight, + .check_timing = samsung_dsim_check_timing, +}; + +static const struct udevice_id samsung_dsim_ids[] = { + { .compatible = "fsl,imx8mm-mipi-dsim", .data = DSIM_TYPE_IMX8MM }, + { .compatible = "fsl,imx8mn-mipi-dsim", .data = DSIM_TYPE_IMX8MM }, + { } +}; + +U_BOOT_DRIVER(samsung_dsim) = { + .name = "samsung_dsim", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = samsung_dsim_ids, + .bind = dm_scan_fdt_dev, + .remove = samsung_dsim_remove, + .probe = samsung_dsim_probe, + .ops = &samsung_dsim_ops, + .priv_auto = sizeof(struct samsung_dsim_priv), +}; diff --git a/drivers/video/bridge/samsung-dsim.h b/drivers/video/bridge/samsung-dsim.h new file mode 100644 index 000000000000..9bb2a379589b --- /dev/null +++ b/drivers/video/bridge/samsung-dsim.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2024 Amarula Solutions + */ + +#ifndef SAMSUNG_DSIM_H +#define SAMSUNG_DSIM_H + +enum samsung_dsim_type { + DSIM_TYPE_EXYNOS3250, + DSIM_TYPE_EXYNOS4210, + DSIM_TYPE_EXYNOS5410, + DSIM_TYPE_EXYNOS5422, + DSIM_TYPE_EXYNOS5433, + DSIM_TYPE_IMX8MM, + DSIM_TYPE_IMX8MP, + DSIM_TYPE_COUNT, +}; + +#endif

From: Michael Trimarchi michael@amarulasolutions.com
The LS068B3SX02 panel is based on the Synaptics R63353 Controller. Add a driver for it.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/video/Kconfig | 8 ++ drivers/video/Makefile | 1 + drivers/video/synaptics-r63353.c | 228 +++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 drivers/video/synaptics-r63353.c
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index cafdc15f9fe2..520577976571 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -627,6 +627,14 @@ config VIDEO_LCD_SSD2828_RESET The reset pin of SSD2828 chip. This takes a string in the format understood by 'sunxi_name_to_gpio' function, e.g. PH1 for pin 1 of port H.
+config VIDEO_LCD_SYNAPTICS_R63353 + tristate "Synaptics R63353-based DSI LCD panels support" + select VIDEO_MIPI_DSI + default n + help + Say Y if you want to enable support for panels based on the + Synaptics R63353 controller. + config VIDEO_LCD_TDO_TL070WSH30 bool "TDO TL070WSH30 DSI LCD panel support" select VIDEO_MIPI_DSI diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9fd3645994f1..4d836efcfa41 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_VIDEO_LCD_RENESAS_R61307) += renesas-r61307.o obj-$(CONFIG_VIDEO_LCD_RENESAS_R69328) += renesas-r69328.o obj-$(CONFIG_VIDEO_LCD_SAMSUNG_LTL106HL02) += samsung-ltl106hl02.o obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o +obj-$(CONFIG_VIDEO_LCD_SYNAPTICS_R63353) += synaptics-r63353.o obj-$(CONFIG_VIDEO_LCD_TDO_TL070WSH30) += tdo-tl070wsh30.o obj-$(CONFIG_VIDEO_MCDE_SIMPLE) += mcde_simple.o obj-${CONFIG_VIDEO_MESON} += meson/ diff --git a/drivers/video/synaptics-r63353.c b/drivers/video/synaptics-r63353.c new file mode 100644 index 000000000000..86d2aea346b2 --- /dev/null +++ b/drivers/video/synaptics-r63353.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics R63353 Controller driver + * + * Copyright (C) 2020 BSH Hausgerate GmbH + */ + +#include <backlight.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <mipi_dsi.h> +#include <panel.h> +#include <asm/gpio.h> +#include <linux/delay.h> +#include <power/regulator.h> + +#define R63353_INSTR(...) { \ + .len = ARRAY_SIZE(((u8[]) {__VA_ARGS__})), \ + .data = (const u8 *)&(const u8 []){__VA_ARGS__} \ + } + +struct r63353_instr { + size_t len; + const u8 * const data; +}; + +static const struct r63353_instr init_sequence[] = { + R63353_INSTR(0x51, 0xff), + R63353_INSTR(0x53, 0x0c), + R63353_INSTR(0x55, 0x00), + R63353_INSTR(0x84, 0x00), + R63353_INSTR(0x29), + R63353_INSTR(0x11), +}; + +struct r63353_panel_priv { + struct udevice *dvdd; + struct udevice *avdd; + struct udevice *backlight; + struct gpio_desc reset_gpio; +}; + +static const struct display_timing default_timing = { + .pixelclock.typ = 70000000, + .hactive.typ = 640, + .hfront_porch.typ = 35, + .hsync_len.typ = 2, + .hback_porch.typ = 150, + .vactive.typ = 1280, + .vfront_porch.typ = 2, + .vsync_len.typ = 4, + .vback_porch.typ = 1, + .flags = DISPLAY_FLAGS_DE_LOW, +}; + +static int r63353_panel_enable_backlight(struct udevice *dev) +{ + struct r63353_panel_priv *priv = dev_get_priv(dev); + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + struct mipi_dsi_device *dsi = plat->device; + int i, ret; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) + return ret; + + ret = mipi_dsi_dcs_soft_reset(dsi); + if (ret < 0) { + dev_err(dev, "Failed to do Software Reset (%d)\n", ret); + goto fail; + } + + mdelay(20); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to enter sleep mode (%d)\n", ret); + goto fail; + } + + for (i = 0; i < ARRAY_SIZE(init_sequence); i++) { + const struct r63353_instr *instr = &init_sequence[i]; + + ret = mipi_dsi_dcs_write_buffer(dsi, instr->data, instr->len); + if (ret < 0) + goto fail; + } + + mdelay(120); + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to exit sleep mode (%d)\n", ret); + goto fail; + } + + mdelay(10); + + /* This call starts sending the data on the display (LINUX LOGO) */ + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display ON (%d)\n", ret); + goto fail; + } + + ret = backlight_enable(priv->backlight); + if (ret) + return ret; + + return 0; + +fail: + dm_gpio_set_value(&priv->reset_gpio, 0); + + return ret; +} + +static int r63353_panel_get_display_timing(struct udevice *dev, + struct display_timing *timing) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + struct mipi_dsi_device *device = plat->device; + + memcpy(timing, &default_timing, sizeof(*timing)); + + /* fill characteristics of DSI data link */ + if (device) { + device->lanes = plat->lanes; + device->format = plat->format; + device->mode_flags = plat->mode_flags; + } + + return 0; +} + +static int r63353_panel_of_to_plat(struct udevice *dev) +{ + struct r63353_panel_priv *priv = dev_get_priv(dev); + int ret; + + ret = device_get_supply_regulator(dev, "dvdd-supply", &priv->dvdd); + if (ret) { + dev_err(dev, "failed to get power dvdd supply\n"); + return ret; + } + + ret = device_get_supply_regulator(dev, "avdd-supply", &priv->avdd); + if (ret) { + dev_err(dev, "failed to get power avdd supply\n"); + return ret; + } + + ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset_gpio, + GPIOD_IS_OUT); + if (ret) { + dev_err(dev, "failed to get RESET GPIO\n"); + return ret; + } + + return 0; +} + +static int r63353_panel_probe(struct udevice *dev) +{ + struct r63353_panel_priv *priv = dev_get_priv(dev); + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + int ret; + + ret = regulator_set_enable(priv->avdd, true); + if (ret) { + dev_err(dev, "Failed to enable avdd regulator (%d)\n", ret); + return ret; + } + + mdelay(25); + + ret = regulator_set_enable(priv->dvdd, true); + if (ret) { + dev_err(dev, "Failed to enable dvdd regulator (%d)\n", ret); + regulator_set_enable(priv->avdd, false); + return ret; + } + + dm_gpio_set_value(&priv->reset_gpio, 0); + mdelay(10); + dm_gpio_set_value(&priv->reset_gpio, 1); + mdelay(120); + + /* fill characteristics of DSI data link */ + plat->lanes = 2; + plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM | + MIPI_DSI_MODE_VIDEO_SYNC_PULSE | MIPI_DSI_MODE_EOT_PACKET; + + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + dev_err(dev, "%s: Warning: cannot get backlight: ret=%d\n", + __func__, ret); + if (ret != -ENOENT) + return ret; + } + + return 0; +} + +static const struct panel_ops r63353_panel_ops = { + .enable_backlight = r63353_panel_enable_backlight, + .get_display_timing = r63353_panel_get_display_timing, +}; + +static const struct udevice_id r63353_panel_ids[] = { + { .compatible = "syna,r63353" }, + { .compatible = "sharp,ls068b3sx02" }, + { } /* sentinel */ +}; + +U_BOOT_DRIVER(r63353_panel) = { + .name = "r63353_panel", + .id = UCLASS_PANEL, + .of_match = r63353_panel_ids, + .ops = &r63353_panel_ops, + .of_to_plat = r63353_panel_of_to_plat, + .probe = r63353_panel_probe, + .plat_auto = sizeof(struct mipi_dsi_panel_plat), + .priv_auto = sizeof(struct r63353_panel_priv), +};

From: Michael Trimarchi michael@amarulasolutions.com
Co-developed-by: Dario Binacchi dario.binacchi@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com ---
.../dts/imx8mn-bsh-smm-s2-u-boot-common.dtsi | 23 +++++++++++++++++++ configs/imx8mn_bsh_smm_s2_defconfig | 15 ++++++++++++ configs/imx8mn_bsh_smm_s2pro_defconfig | 15 ++++++++++++ 3 files changed, 53 insertions(+)
diff --git a/arch/arm/dts/imx8mn-bsh-smm-s2-u-boot-common.dtsi b/arch/arm/dts/imx8mn-bsh-smm-s2-u-boot-common.dtsi index 19b0d8977539..8cae580e2851 100644 --- a/arch/arm/dts/imx8mn-bsh-smm-s2-u-boot-common.dtsi +++ b/arch/arm/dts/imx8mn-bsh-smm-s2-u-boot-common.dtsi @@ -6,6 +6,19 @@
#include "imx8mn-u-boot.dtsi"
+/ { + aliases { + display0 = &lcdif; + }; + + samsung_dsi_host: dsi-host { + compatible = "samsung,sec-mipi-dsi"; + clocks = <&clk IMX8MN_CLK_DSI_PHY_REF>; + clock-names = "sclk_mipi"; + status = "okay"; + }; +}; + &{/soc@0/bus@30800000/i2c@30a20000/pmic@4b} { bootph-pre-ram; }; @@ -30,6 +43,16 @@ bootph-pre-ram; };
+&lcdif { + display = <&display0>; + + /* panel is r8g8b8 */ + display0: display@0 { + bits-per-pixel = <24>; + bits-per-color = <8>; + }; +}; + &pinctrl_i2c1 { bootph-pre-ram; }; diff --git a/configs/imx8mn_bsh_smm_s2_defconfig b/configs/imx8mn_bsh_smm_s2_defconfig index b4351a392eff..2fa8bc22747e 100644 --- a/configs/imx8mn_bsh_smm_s2_defconfig +++ b/configs/imx8mn_bsh_smm_s2_defconfig @@ -56,6 +56,7 @@ CONFIG_SYS_PROMPT="> " CONFIG_CMD_FUSE=y CONFIG_CMD_USB=y CONFIG_CMD_USB_MASS_STORAGE=y +CONFIG_CMD_BMP=y CONFIG_CMD_MTDPARTS=y CONFIG_MTDIDS_DEFAULT="nand0=gpmi-nand" CONFIG_MTDPARTS_DEFAULT="gpmi-nand:64m(nandboot),16m(nandfit),32m(nandkernel),1m(nanddtb),8m(nandtee),-(nandrootfs)" @@ -74,6 +75,8 @@ CONFIG_FASTBOOT_BUF_ADDR=0x40480000 CONFIG_FASTBOOT_BUF_SIZE=0x20000000 CONFIG_FASTBOOT_FLASH=y CONFIG_FASTBOOT_UUU_SUPPORT=y +CONFIG_DM_GPIO_LOOKUP_LABEL=y +CONFIG_SPL_DM_GPIO_LOOKUP_LABEL=y CONFIG_MXC_GPIO=y CONFIG_DM_I2C=y # CONFIG_MMC is not set @@ -94,12 +97,17 @@ CONFIG_MII=y CONFIG_PINCTRL=y CONFIG_SPL_PINCTRL=y CONFIG_PINCTRL_IMX8M=y +CONFIG_POWER_DOMAIN=y +CONFIG_IMX8M_POWER_DOMAIN=y +CONFIG_IMX8M_BLK_CTRL=y CONFIG_DM_PMIC=y CONFIG_DM_PMIC_BD71837=y CONFIG_SPL_DM_PMIC_BD71837=y CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR_FIXED=y CONFIG_DM_REGULATOR_GPIO=y +CONFIG_DM_PWM=y +CONFIG_PWM_IMX=y CONFIG_DM_SERIAL=y CONFIG_MXC_UART=y CONFIG_SYSRESET=y @@ -112,5 +120,12 @@ CONFIG_USB_GADGET_MANUFACTURER="FSL" CONFIG_USB_GADGET_VENDOR_NUM=0x0525 CONFIG_USB_GADGET_PRODUCT_NUM=0xa4a5 CONFIG_CI_UDC=y +CONFIG_VIDEO=y +CONFIG_VIDEO_LCD_SYNAPTICS_R63353=y +CONFIG_VIDEO_BRIDGE_SAMSUNG_DSIM=y +CONFIG_VIDEO_MXS=y +CONFIG_BMP_16BPP=y +CONFIG_BMP_24BPP=y +CONFIG_BMP_32BPP=y CONFIG_IMX_WATCHDOG=y # CONFIG_FAT_WRITE is not set diff --git a/configs/imx8mn_bsh_smm_s2pro_defconfig b/configs/imx8mn_bsh_smm_s2pro_defconfig index 0faa3376fd3d..b6b62b9d53f9 100644 --- a/configs/imx8mn_bsh_smm_s2pro_defconfig +++ b/configs/imx8mn_bsh_smm_s2pro_defconfig @@ -53,6 +53,7 @@ CONFIG_CMD_FUSE=y CONFIG_CMD_MMC=y CONFIG_CMD_USB=y CONFIG_CMD_USB_MASS_STORAGE=y +CONFIG_CMD_BMP=y CONFIG_OF_CONTROL=y CONFIG_SPL_OF_CONTROL=y CONFIG_SYS_RELOC_GD_ENV_ADDR=y @@ -68,6 +69,8 @@ CONFIG_FASTBOOT_BUF_SIZE=0x20000000 CONFIG_FASTBOOT_FLASH=y CONFIG_FASTBOOT_UUU_SUPPORT=y CONFIG_FASTBOOT_FLASH_MMC_DEV=0 +CONFIG_DM_GPIO_LOOKUP_LABEL=y +CONFIG_SPL_DM_GPIO_LOOKUP_LABEL=y CONFIG_MXC_GPIO=y CONFIG_DM_I2C=y CONFIG_SUPPORT_EMMC_BOOT=y @@ -83,12 +86,17 @@ CONFIG_MII=y CONFIG_PINCTRL=y CONFIG_SPL_PINCTRL=y CONFIG_PINCTRL_IMX8M=y +CONFIG_POWER_DOMAIN=y +CONFIG_IMX8M_POWER_DOMAIN=y +CONFIG_IMX8M_BLK_CTRL=y CONFIG_DM_PMIC=y CONFIG_DM_PMIC_BD71837=y CONFIG_SPL_DM_PMIC_BD71837=y CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR_FIXED=y CONFIG_DM_REGULATOR_GPIO=y +CONFIG_DM_PWM=y +CONFIG_PWM_IMX=y CONFIG_DM_SERIAL=y CONFIG_MXC_UART=y CONFIG_SYSRESET=y @@ -101,5 +109,12 @@ CONFIG_USB_GADGET_MANUFACTURER="FSL" CONFIG_USB_GADGET_VENDOR_NUM=0x0525 CONFIG_USB_GADGET_PRODUCT_NUM=0xa4a5 CONFIG_CI_UDC=y +CONFIG_VIDEO=y +CONFIG_VIDEO_LCD_SYNAPTICS_R63353=y +CONFIG_VIDEO_BRIDGE_SAMSUNG_DSIM=y +CONFIG_VIDEO_MXS=y +CONFIG_BMP_16BPP=y +CONFIG_BMP_24BPP=y +CONFIG_BMP_32BPP=y CONFIG_IMX_WATCHDOG=y # CONFIG_FAT_WRITE is not set

The printed information is useful when adding support for the simple frame buffer to a board.
Co-developed-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
boot/fdt_simplefb.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/boot/fdt_simplefb.c b/boot/fdt_simplefb.c index 53415548459a..22aaad9d5c5b 100644 --- a/boot/fdt_simplefb.c +++ b/boot/fdt_simplefb.c @@ -61,6 +61,8 @@ static int fdt_simplefb_configure_node(void *blob, int off) return -EINVAL; }
+ debug("Simple frame buffer at 0x%lx, pixels %dx%d, format %s\n", + fb_base, xsize, ysize, name); return fdt_setup_simplefb_node(blob, off, fb_base, xsize, ysize, xsize * (1 << bpix) / 8, name); }

If you want to pass the frame-buffer to the kernel, the video output is initialized by U-Boot, and kept by the kernel. The patch does not turn off the power domains or reset the peripheral if you want to support such feature.
Co-developed-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/video/mxsfb.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 45431f0a1047..06b2a2461246 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -422,7 +422,8 @@ static int mxs_video_remove(struct udevice *dev) if (priv->disp_dev) device_remove(priv->disp_dev, DM_REMOVE_NORMAL);
- mxs_remove_common(priv->reg_base, plat->base); + if (!IS_ENABLED(CONFIG_VIDEO_DT_SIMPLEFB)) + mxs_remove_common(priv->reg_base, plat->base);
return 0; } @@ -445,6 +446,10 @@ U_BOOT_DRIVER(mxs_video) = { .bind = mxs_video_bind, .probe = mxs_video_probe, .remove = mxs_video_remove, +#if !IS_ENABLED(CONFIG_VIDEO_DT_SIMPLEFB) .flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE, +#else + .flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE | DM_FLAG_LEAVE_PD_ON, +#endif .priv_auto = sizeof(struct mxsfb_priv), };

If you want to pass the frame-buffer to the kernel, the video output is initialized by U-Boot, and kept by the kernel. The patch does not turn off the power domains or disable the peripheral if you want to support such feature.
Co-developed-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/video/bridge/samsung-dsi-host.c | 9 +++++++++ drivers/video/bridge/samsung-dsim.c | 3 +++ 2 files changed, 12 insertions(+)
diff --git a/drivers/video/bridge/samsung-dsi-host.c b/drivers/video/bridge/samsung-dsi-host.c index dd3e33c4edc7..2d0e8f71a756 100644 --- a/drivers/video/bridge/samsung-dsi-host.c +++ b/drivers/video/bridge/samsung-dsi-host.c @@ -1281,6 +1281,7 @@ static void samsung_dsi_set_standby(struct samsung_dsi *dsim, bool standby) samsung_dsim_write(dsim, DSIM_MDRESOL_REG, mdresol); }
+#if (!IS_ENABLED(CONFIG_VIDEO_DT_SIMPLEFB)) static void samsung_dsi_disable_clock(struct samsung_dsi *dsim) { u32 reg; @@ -1294,6 +1295,7 @@ static void samsung_dsi_disable_clock(struct samsung_dsi *dsim) reg &= ~DSIM_PLL_EN; samsung_dsim_write(dsim, DSIM_PLLCTRL_REG, reg); } +#endif
static inline struct samsung_dsi *host_to_dsi(struct mipi_dsi_host *host) { @@ -1515,6 +1517,12 @@ static int samsung_dsi_enable(struct udevice *dev) return 0; }
+#if IS_ENABLED(CONFIG_VIDEO_DT_SIMPLEFB) +static int samsung_dsi_disable(struct udevice *dev) +{ + return 0; +} +#else static int samsung_dsi_disable(struct udevice *dev) { u32 intsrc; @@ -1532,6 +1540,7 @@ static int samsung_dsi_disable(struct udevice *dev)
return 0; } +#endif
struct dsi_host_ops samsung_dsi_ops = { .init = samsung_dsi_init, diff --git a/drivers/video/bridge/samsung-dsim.c b/drivers/video/bridge/samsung-dsim.c index 986f1d830844..36e45abb3009 100644 --- a/drivers/video/bridge/samsung-dsim.c +++ b/drivers/video/bridge/samsung-dsim.c @@ -144,5 +144,8 @@ U_BOOT_DRIVER(samsung_dsim) = { .remove = samsung_dsim_remove, .probe = samsung_dsim_probe, .ops = &samsung_dsim_ops, +#if IS_ENABLED(CONFIG_VIDEO_DT_SIMPLEFB) + .flags = DM_FLAG_LEAVE_PD_ON, +#endif .priv_auto = sizeof(struct samsung_dsim_priv), };

If you want to pass the frame-buffer to the kernel, the video output is initialized by U-Boot, and kept by the kernel. The commit modifies the device tree to be passed to the kernel just before launching it, to prevent the kernel from reinitializing hardware that has already been configured by the bootloader.
Co-developed-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
board/bsh/imx8mn_smm_s2/imx8mn_smm_s2.c | 99 +++++++++++++++++++++++++ 1 file changed, 99 insertions(+)
diff --git a/board/bsh/imx8mn_smm_s2/imx8mn_smm_s2.c b/board/bsh/imx8mn_smm_s2/imx8mn_smm_s2.c index c99896873991..87eb4e7ed63e 100644 --- a/board/bsh/imx8mn_smm_s2/imx8mn_smm_s2.c +++ b/board/bsh/imx8mn_smm_s2/imx8mn_smm_s2.c @@ -5,6 +5,8 @@
#include <asm/arch/sys_proto.h> #include <env.h> +#include <fdt_simplefb.h> +#include <fdt_support.h>
int board_init(void) { @@ -20,3 +22,100 @@ int board_late_init(void)
return 0; } + +#if (IS_ENABLED(CONFIG_OF_LIBFDT) && IS_ENABLED(CONFIG_FDT_SIMPLEFB) && \ + IS_ENABLED(CONFIG_VIDEO)) +static void smm_s2_setup_simplefb(void *blob) +{ +#define DT_BLK_CTRL_NODE_PATH "/soc@0/bus@32c00000/blk-ctrl@32e28000" +#define DT_GPC_NODE_PATH "/soc@0/bus@30000000/gpc@303a0000" +#define DT_MIPI_DSI_NODE_PATH "/soc@0/bus@32c00000/dsi@32e10000" +#define DT_LCDIF_NODE_PATH "/soc@0/bus@32c00000/lcdif@32e00000" + + const char *dt_addnode[][2] = { + { DT_BLK_CTRL_NODE_PATH, "lcdif" }, + { DT_BLK_CTRL_NODE_PATH, "mipi-dsi" }, + }; + + const char *dt_addprop[][2] = { + { "/regulator-3v3-O2", "regulator-boot-on" }, + { "/regulator-3v3-O3", "regulator-boot-on" }, + { DT_GPC_NODE_PATH "/pgc/power-domain@3", "fsl,boot-on" }, + { DT_GPC_NODE_PATH "/pgc/power-domain@4", "fsl,boot-on" }, + { DT_BLK_CTRL_NODE_PATH "/lcdif", "fsl,boot-on" }, + { DT_BLK_CTRL_NODE_PATH "/mipi-dsi", "fsl,boot-on" }, + { DT_MIPI_DSI_NODE_PATH, "samsung,boot-on" }, + { DT_MIPI_DSI_NODE_PATH "/panel@0", "syna,boot-on" }, + { DT_LCDIF_NODE_PATH, "fsl,boot-on" }, + }; + const char *dt_delprop[][2] = { + { DT_BLK_CTRL_NODE_PATH, "assigned-clock-rates" }, + { DT_GPC_NODE_PATH "/pgc/power-domain@3", "assigned-clock-rates" }, // pgc_dispmix + { DT_LCDIF_NODE_PATH, "assigned-clock-rates" }, + }; + int i, ret, offset; + + ret = fdt_simplefb_enable_and_mem_rsv(blob); + if (ret) { + printf("Failed to enable framebuffer DTS node\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(dt_addnode); i++) { + const char *path = dt_addnode[i][0]; + const char *node = dt_addnode[i][1]; + + offset = fdt_path_offset(blob, path); + if (offset < 0) { + printf("Missing node %s, err=%s\n", path, + fdt_strerror(offset)); + continue; + } + + offset = fdt_find_or_add_subnode(blob, offset, node); + if (offset < 0) + printf("Failed to create node %s, err=%s\n", path, + fdt_strerror(offset)); + else + debug("Add node %s:%s\n", path, node); + } + + for (i = 0; i < ARRAY_SIZE(dt_addprop); i++) { + const char *path = dt_addprop[i][0]; + const char *prop = dt_addprop[i][1]; + + ret = fdt_find_and_setprop(blob, path, prop, NULL, 0, 1); + if (ret < 0) + printf("Failed to add property %s:%s, err=%s\n", path, prop, + fdt_strerror(ret)); + else + debug("Add property %s:%s\n", path, prop); + } + + for (i = 0; i < ARRAY_SIZE(dt_delprop); i++) { + const char *path = dt_delprop[i][0]; + const char *prop = dt_delprop[i][1]; + + offset = fdt_path_offset(blob, path); + if (offset < 0) { + printf("Missing node %s\n", path); + continue; + } + + ret = fdt_delprop(blob, offset, prop); + if (ret < 0) + printf("Failed to delete property %s:%s\n", path, prop); + else + debug("Delete property %s:%s\n", path, prop); + } +} +#endif + +int ft_board_setup(void *blob, struct bd_info *bd) +{ + if (IS_ENABLED(CONFIG_OF_LIBFDT) && IS_ENABLED(CONFIG_FDT_SIMPLEFB) && + IS_ENABLED(CONFIG_VIDEO)) + smm_s2_setup_simplefb(blob); + + return 0; +}

Co-developed-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
configs/imx8mn_bsh_smm_s2_defconfig | 3 +++ configs/imx8mn_bsh_smm_s2pro_defconfig | 3 +++ 2 files changed, 6 insertions(+)
diff --git a/configs/imx8mn_bsh_smm_s2_defconfig b/configs/imx8mn_bsh_smm_s2_defconfig index 2fa8bc22747e..4982b58407c6 100644 --- a/configs/imx8mn_bsh_smm_s2_defconfig +++ b/configs/imx8mn_bsh_smm_s2_defconfig @@ -28,7 +28,9 @@ CONFIG_FIT_EXTERNAL_OFFSET=0x3000 CONFIG_SPL_LOAD_FIT=y CONFIG_SYS_BOOTM_LEN=0x2000000 CONFIG_DISTRO_DEFAULTS=y +CONFIG_OF_BOARD_SETUP=y CONFIG_OF_SYSTEM_SETUP=y +CONFIG_FDT_SIMPLEFB=y CONFIG_DEFAULT_FDT_FILE="freescale/imx8mn-bsh-smm-s2.dtb" CONFIG_SYS_CBSIZE=2048 CONFIG_SYS_PBSIZE=2067 @@ -124,6 +126,7 @@ CONFIG_VIDEO=y CONFIG_VIDEO_LCD_SYNAPTICS_R63353=y CONFIG_VIDEO_BRIDGE_SAMSUNG_DSIM=y CONFIG_VIDEO_MXS=y +CONFIG_VIDEO_DT_SIMPLEFB=y CONFIG_BMP_16BPP=y CONFIG_BMP_24BPP=y CONFIG_BMP_32BPP=y diff --git a/configs/imx8mn_bsh_smm_s2pro_defconfig b/configs/imx8mn_bsh_smm_s2pro_defconfig index b6b62b9d53f9..7ae99adf7fad 100644 --- a/configs/imx8mn_bsh_smm_s2pro_defconfig +++ b/configs/imx8mn_bsh_smm_s2pro_defconfig @@ -29,7 +29,9 @@ CONFIG_FIT_EXTERNAL_OFFSET=0x3000 CONFIG_SPL_LOAD_FIT=y CONFIG_SYS_BOOTM_LEN=0x2000000 CONFIG_DISTRO_DEFAULTS=y +CONFIG_OF_BOARD_SETUP=y CONFIG_OF_SYSTEM_SETUP=y +CONFIG_FDT_SIMPLEFB=y CONFIG_DEFAULT_FDT_FILE="freescale/imx8mn-bsh-smm-s2pro.dtb" CONFIG_SYS_CBSIZE=2048 CONFIG_SYS_PBSIZE=2067 @@ -113,6 +115,7 @@ CONFIG_VIDEO=y CONFIG_VIDEO_LCD_SYNAPTICS_R63353=y CONFIG_VIDEO_BRIDGE_SAMSUNG_DSIM=y CONFIG_VIDEO_MXS=y +CONFIG_VIDEO_DT_SIMPLEFB=y CONFIG_BMP_16BPP=y CONFIG_BMP_24BPP=y CONFIG_BMP_32BPP=y

Display the BSH logo with features VIDEO_LOGO and SPLASH_SCREEN on imx8mn_bsh_smm_s2/pro boards.
With CONFIG_SYS_VENDOR = "bsh", the logo bsh.bmp is selected, loaded at the address indicated by splashimage and centered with "splashpos=m,m".
Co-developed-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com
---
configs/imx8mn_bsh_smm_s2_defconfig | 3 +++ configs/imx8mn_bsh_smm_s2pro_defconfig | 3 +++ include/configs/imx8mn_bsh_smm_s2.h | 1 + include/configs/imx8mn_bsh_smm_s2_common.h | 4 ++++ include/configs/imx8mn_bsh_smm_s2pro.h | 1 + tools/logos/bsh.bmp | Bin 0 -> 10644 bytes 6 files changed, 12 insertions(+) create mode 100644 tools/logos/bsh.bmp
diff --git a/configs/imx8mn_bsh_smm_s2_defconfig b/configs/imx8mn_bsh_smm_s2_defconfig index 4982b58407c6..b3190945c59c 100644 --- a/configs/imx8mn_bsh_smm_s2_defconfig +++ b/configs/imx8mn_bsh_smm_s2_defconfig @@ -123,10 +123,13 @@ CONFIG_USB_GADGET_VENDOR_NUM=0x0525 CONFIG_USB_GADGET_PRODUCT_NUM=0xa4a5 CONFIG_CI_UDC=y CONFIG_VIDEO=y +CONFIG_VIDEO_LOGO=y CONFIG_VIDEO_LCD_SYNAPTICS_R63353=y CONFIG_VIDEO_BRIDGE_SAMSUNG_DSIM=y CONFIG_VIDEO_MXS=y CONFIG_VIDEO_DT_SIMPLEFB=y +CONFIG_SPLASH_SCREEN=y +CONFIG_SPLASH_SCREEN_ALIGN=y CONFIG_BMP_16BPP=y CONFIG_BMP_24BPP=y CONFIG_BMP_32BPP=y diff --git a/configs/imx8mn_bsh_smm_s2pro_defconfig b/configs/imx8mn_bsh_smm_s2pro_defconfig index 7ae99adf7fad..db83a326c053 100644 --- a/configs/imx8mn_bsh_smm_s2pro_defconfig +++ b/configs/imx8mn_bsh_smm_s2pro_defconfig @@ -112,10 +112,13 @@ CONFIG_USB_GADGET_VENDOR_NUM=0x0525 CONFIG_USB_GADGET_PRODUCT_NUM=0xa4a5 CONFIG_CI_UDC=y CONFIG_VIDEO=y +CONFIG_VIDEO_LOGO=y CONFIG_VIDEO_LCD_SYNAPTICS_R63353=y CONFIG_VIDEO_BRIDGE_SAMSUNG_DSIM=y CONFIG_VIDEO_MXS=y CONFIG_VIDEO_DT_SIMPLEFB=y +CONFIG_SPLASH_SCREEN=y +CONFIG_SPLASH_SCREEN_ALIGN=y CONFIG_BMP_16BPP=y CONFIG_BMP_24BPP=y CONFIG_BMP_32BPP=y diff --git a/include/configs/imx8mn_bsh_smm_s2.h b/include/configs/imx8mn_bsh_smm_s2.h index deeed9c2f582..c8bd55fd3744 100644 --- a/include/configs/imx8mn_bsh_smm_s2.h +++ b/include/configs/imx8mn_bsh_smm_s2.h @@ -38,6 +38,7 @@ #define CFG_EXTRA_ENV_SETTINGS \ MEM_LAYOUT_ENV_SETTINGS \ NANDARGS \ + SPLASH_ENV_SETTINGS \ BOOTENV
#define PHYS_SDRAM_SIZE SZ_256M diff --git a/include/configs/imx8mn_bsh_smm_s2_common.h b/include/configs/imx8mn_bsh_smm_s2_common.h index 204fc4b31647..5d6b22e04139 100644 --- a/include/configs/imx8mn_bsh_smm_s2_common.h +++ b/include/configs/imx8mn_bsh_smm_s2_common.h @@ -21,6 +21,10 @@ "fdtfile=" CONFIG_DEFAULT_FDT_FILE "\0" \ "bootcmd_mfg=echo Running fastboot mode; fastboot usb 0\0" \
+#define SPLASH_ENV_SETTINGS \ + "splashimage=0x5bb00000\0" \ + "splashpos=m,m\0" \ + /* Link Definitions */
#define CFG_SYS_INIT_RAM_ADDR 0x40000000 diff --git a/include/configs/imx8mn_bsh_smm_s2pro.h b/include/configs/imx8mn_bsh_smm_s2pro.h index 8619fdde7fdb..dd26e0b885d2 100644 --- a/include/configs/imx8mn_bsh_smm_s2pro.h +++ b/include/configs/imx8mn_bsh_smm_s2pro.h @@ -25,6 +25,7 @@ #define CFG_EXTRA_ENV_SETTINGS \ MEM_LAYOUT_ENV_SETTINGS \ EMMCARGS \ + SPLASH_ENV_SETTINGS \ BOOTENV
#define PHYS_SDRAM_SIZE SZ_512M diff --git a/tools/logos/bsh.bmp b/tools/logos/bsh.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5b6fb1ebd50d70c7f5ce4a51042271e9634d8d81 GIT binary patch literal 10644 zcmeI2eOy%4)_~XQUh}nk@sis0dX-ZAhBT2#Ny(20Qj*A^GBYuaRLsZ-%1<&s((=`` zt{GWmp;?v|R#;eMWQ9c*Rv1NP)bu?~Uz643efB<w!I|d&_x(eD&JWJsXFY4Jeb!!k ztuu}ucSnD*KP&$#GM~Q&9VfCzMO6NWkH`tVMP6+0eC&W3lp`(w?8WHx@z<5?`Ke!S z<;f>?U;34fOib)pl9$(KWM1C!Wg9lM8-BvOf9ZAE>cQsbmLJrG{B%F1r}XIFOJaNU zmY%VFq-W1Q(yM1*>D{ZJ^y%GS`u2&FetqMmf4@^Du0Nl+0TLfKP)?0MO$MAgNCpl_ zkkbYZmO-Z_O2QyM38%~8gd|BEe1@E!I7E_8A1Y@gCCiXAl4a<SGiB(|VUj#_xSW}s zBE!x+ONI{{At}R0$XO{PWyD#dWW<QkGIGS(GHPV1j2?B4oIN^CQqNA4b5heK?VNKZ zEp3dXr;n9$(=%kux#!8)G3QCf*l}`RMy8BA?|d0IE=w{qFOc&y$4l1v<K=>^3uXKT z*>d6d9Lc`$BFWCaSaNbMk&F0Te9;8C<l;+Z!X*>s(g~Nz#7ig1#EF;7WtUBsNqjD! zlq-`jpCY-FuaGIZQ{{>&)8vXPu9T@$uas%i^5n{CS4rNL)8(qX88ZE<nKEN~zRZ}B zFEeM(lKlLuWmf)dxq8+$GW+TRxn}mYQgF>2DJYmD*Is*_%$ZXt*Uh<J3a=}Y>kEse z==vL^xQI{jjdH^cbLGYx=gHg~=gYjg3uOMhg|cA&LRq+Akt|$TB8wK?Bqb$_<))i% zmc=(Mk((DUktH`Tm8DCT$<n3EW!bV@Wcl(Ha?33%WyLMG%E}eD%B?GJliO}xCAZ!7 zzp`r8YFWMdc3H#c_BCr|?d|Ji-P${3-MTyFjywJ%ciy>P*8k@&xoiCf*>Kn0a`%Q( zx%=*Wq_lLS+;h*pvT@_Ra_`3b<i2~$r0l-?<^Hlwa{v7g$fivX$^#EPC=WhZF6HH$ zWpnu!*|PZ|d1%X4dFY{sW$V_5<>7}Pkw+fcCfl|>Dvxe^Odfl5yKH}Khiu=zLw4+V zTpoY?33=j)3aO~rDLX5klqYvSB~Ly1v^@3H)AIDwl~P%`OLpygMxJ@*S$X!EDye#Q zx9qOsv->%D?z!jX`RA*py7~oq;e|c2XU~iBBA=QUYow;;C3)$ky|Q=j%kuKeugEK} zyehB0S}V1D_SNo_efwUM*Is*FUVpt#>gwK*H{RGU`}gmcH}}6OZ@u}Jy!BSS)Yl)7 z0|(xgx8Ht8-g)O8dH3CSrQzKMIoNPe4jz0@-g~c68XMo2_uqeCKKS4RX=-Yc51T%e zk3RfJKKkfm`S{~Qa_G<}^2sNkNON<ueER99^4VvfiRUwqcs!rW=bwKeUwrX}eEH>< z^3_*g$=6?hE#G|ejePsfxAN_`Uh#Up^4)jeiO=Vg@4x?Ee)!=B`SHge<)<HilAnJ1 zS$_WcXYu>}^2;y3$l=3><=0<-mEV5*jnD7$`|rO?AP|s0{`f<V93hXmKSu&b0)qpj zm1Dn;t;4Z6IJOV|kMqET0rekWs}1=9_3^uLK|nqH_S{BnmIpMpjc7;p&*U!c#NWM{ zuRC5e*PUO?-&>KL#qVn761XQyeYZN&hg7*9!~8zxr!v2q-=$`Db3oIXQ*C0_m70%k zZOiB|_l_;xy@Yuyg>(+r4J#dK$DUYb9hu?GS2AC0=C`;OF>7U*i-Frk53g`$tq;uZ zG!o3t<9CIbdnllTxid<=DFJQPI^Crey7jnn-Ksluj~-NCY(Tpi7E%YTe_5{FS{NIK z1higDwNTTvo9f?1WB6;H?o?k)K<64Q$fY3PRz0_%Z(5*Bq9*?(RWmh5C+I9K(RJvm z*5;F2JWEbAmaNlk?etG0m!W0q`*(}>?7;zDc|wjZ?-|hYzvZa!9|0{x&nk!BN3<&v zi?p_DKrd$ez-flNR#Q~FiAHOI9_kXbD1r4lK{ONAg1?D+VEME?*2X)uIO9*D*8R&_ z>!JYxt<_v@*TdK~TRoj!-Qk?*fKEks?Q-k!sph$7YAtuWPbDtEIuFOz4!Z82MrRZ{ z2S%XOK$+oCP7_wda}t&^w!L3Kt27z=Qni^et8Gx2Hzx+PFWhMLEwgqEG7(YPDWHw^ zIcD}Te`O62?W7fq8eRDj7|R(icL)i_la<^%3T_*1?iJ82+7`=H*E8y9^Rwb1tBM`X zR%5C|NhHcU26PkL6%d-VU1t-4(b^sRqEB*Wt=4c-hk$0FS+#Mg_2_g6jQKR0(YW5k zb+jRk)H%e=9?1UV&Fn!UY%X!x<{!if_dC|4MWAoPrbP~$c*r<0hc!>tB<-(VI~cuH zNKfaU8tnFI7veoui+B=#eDvsY?Gk0~+7Yy~X<*n|Fyrjx?m;b0M1U8@3gWhHd(qC? z3~eu3^6+RwI}>Y@bT`rC)#-MRMQH<f7+Xui`K;?4#;cat^}&ZGc8*t<X(uepM0-3d z)n0v&2XbE&QQX8%sn)Lek_^?cu15rJJ@-#&<#sb3PlQ^H)Jo>cU{?Os_&>?+=0h3} za}~1%#v=AkNK_CnN!Zd(y*CGKB?l}+W(YDKZNuzHVl?A8(FCpOVaEF6QM7v5U&fkA z5t!>4U*AeRC7^{6s~~2<BXY3v04usc{p|V%je=OhtZU1uf40)A?T}0gVfr-zxyi`w zK;HtaZ056`u@36PI%`EZi+wtgyXzMR{q4?*A;SrR%wg?<o|sIY3}fYtIGTq<;8ekx z)5>9O;*r^kJw?n^jtgih*I8UoFdE~-ydvVdqpOqpZwlH1V;(Yv$TT361H-3n$&PKU zbw*=h1kNJHw}nu4LK#M+4MPK2A5JPGHE@m2&gv)og+v0@G}-)+5yJ7RqCE;yDLU4` zDTK2I&O)OzoWlM{g|FIN67-3)s|zwdWSktE0A(E`QyFoyU<6J%{N*7O6L(EmlFLlp zdE_pxv$%FO_7C$2Tl_XB^$npUBUOh~HI!LU%D7J9+UQ#m&fv_M!FbK0qrFK(;sATR zH=cG-e?~x8Fp>zv$$|YNP->w}4WSH0rU}XtW;Vl^ZS3IMW;RZikf+0GX>tZJy6|XA zdh={l{YW`gPZ}H(x0#F>jd>9`s~9h9r4Vt4H407)J7cVUc1>e!c7{_xesFVD-#=UA z)IcdAtXiZ}A#FuV37mQCdvhvTt>Fx^;&`j^;DVr6*s~hOLKtqfb*{-dQE>LdaWYa$ z1X2YW=Y?>Fz^NjxZRSf4Xf}+FT)R1lToJOySmW&eZmkq@!+t1KqsW>1ZBiOfjC?zV zYopO*oe&CnyqNLI`9Y6hjE6CuSTgyIDle5>tqP?QikopFa2o7>%xNW&M|VL|TMMm2 z%lgq~m1c2X#X`4Phs+vc4=0411Lg%SBEvUpH%OVpm7Cv2aoXuX&gM0cZ0?CbvN@oY zLv`uZB*!9F&t|!^%%09ug%qQH_{sQ82I8y^x6922tvqZ!sBt`{Vpee%{uK_Vil;)= z6UoJBF#6Yqld!^Ogt<ZgxYJJ^5Dh6o{ZQRJJJj$OYg|XF3N*O-Bk*i)XyuVfJX%Se zY$}Ux+Jr`%{gRAz3pIgiDMmf$nT&mPoM<7Qvf6H@xUt2;P0_n_n{K8eF43tvTsu)Q zb=ErWbn;<Fg7KhI^EJj+WwmHDHqDN}Hu+<VLmn*JR~v~pHzN)(K5o`aG`fp<29?B* z+gp*7jg6^28#SNj=RvcRLDC%(H70c~d(mcLva?sqHA~xdHSt@82BUSWp`3{{+%a|^ zH{D=8q!#U>W!UQE!@gvpN~GJy(93BZROh5gKT6)@i^Y!g03*HJR!27X@_fAPm)<9v z$j?<TT(^sZ)S}<b6%HZJ<mF4W0=+ixqQ^%!5_O8`P^}`ajkYB%(s(nQ>*``_dq49e zwsYFM`IgxVjX`phR*`l5+7VA<kCHW6VX`CJ->mXv-OqZNe7BKRJxmp>_#LfNw1#op zv%<7?Lvj1QBC9*zcsNNt*zM+4p1WVO=$YIO47-fJ+@lD#19aqBxoF*hbhdpO4MgPF zf14+k&C~M^3iiA_uNM7|o@7U!NT_b#NM%;5HS`oK@ue@hC0G4qCzI{WzMS<hlMi!s zyZRCXYP6TO@K|Tl?KNEA66Z~1@kVx>scVze1EnjLb!C5Ur>l%%M^)+JGg`b&HXNcd zspnRkF+K21cU{!VV<(y?*#=9K)XS4Jxwe9g<<-vY>dsWsdxt?WR<QaZ`;fSCJHNu9 zedMRz+6meySWdpphE`0zQ96Ojvs?=zuuKQIqm_hDd$ld3Wsuxno9yn@C^DhaGc=3} z>gTD3TjuQhtX3W?eHXO0T2K6MMAs^4D`2H+{n<|cSrN|R&XKHd8}(cl^pF@ZRdP0S zZifpefjrhm*PhdoZ?Yng%FwwogtHwEwL2u+>2ex3!HLq{oUO)V^r9`qGpp71@*yO4 zE36`B_n;*o(k3EAb=$daFCWffP4l?3c23YQES?1;ff={IrsgStQb5(=zCVaSG5z)Q z5K4vh%8M<_m@9{p!AO*P#=3s;Btm>5isSH>j{e%Ct!YAcB^0;IhBFvWE)|*88G*6_ zeValUJ{X(uC<8t#O+{&H-g2tc{*FeXEZml6+oPraiL(lh?WCc^LaB#hG@5fXgh5uz zbo3P*?N1}Nl)!K@Yj-H7vh+Z4J&eFH`WA*zYM`uU#cV%Jy>F^-FZI9cA^R`f7Qc4J znwSvEDk$Yp>fuj>k`1L1iq#j+ApaF|->z$p_9hF0$=_~=NnbY?Ml}rAzX%kgZ9*%B zXlk}QT-6K7beHSkm@cH0e$jeJ?g;URPGBhB&|l9ES_5YR)+Avk*@4+4C{0i%La{m{ zP^`9A2GP`@ZJ`vSVG@??hY?Heq(gw>dP%knx5m}jO5!Q{bO<EXdRV5zEPz#nJ?4b; z@UG6sxn=c-^BA|fuMYagD*7~?tS~0tsWs?sd7oDgElL~7?;AKf*!f7h9eo6*wV{>C zY8=!A&Y>!pCD^wW`%)k#sgJd+<4vZ?J@qhcXHR|$@w(NLSwTCw$HP0Lc2Ki9Q=NKb zEZizqB2|6((ah;&-XxENYb}bvHP$@RN@dSC@E(>oNaX2G>Y@HhgFrV7ISq2G?uOEg z<|zILz$VB&m}iYRC&O)ayYV1>#@~yn2lMn|`!+0I+u3(v+Xe=7Ggd4?L!<4Il!~&R z-C9N6+=htgqF$b>v2<Iw7Cd+D%eN~-)wh=SaenQ9RlV6~Yw;sR@8bEHHQK}})|nNX z4bQbM0?|~A%N!o>Iu}vN+L{MiE~jb~)ou=66!9*w$a<5c9jqsHP~Ezf^B`n3u+#12 z9qvGLF0LQ!yy2e$*{^BTK5exOhSeWV;7R0T*PfZyPwwradrlA5LzXZm-uT+UGjVlA zVAyKXVQ{w+A>JhSn*vtcuQR9~yJ{)gOy#<VcnI->XXSqDFvHqGy{MY5bwgSzpke4N z)%Ly2IxSWo_gjq-IJUlYD1FUYRG<xKq7AJLFq$-jx8t3(L_Ms(TghT!xHWE->2P|Q zU6jQ42D{Zi+^KttiPmaOp?*!$^|s!#dc#S?j9YO|w|;eqMQ0*1sqBRu;&Fx+>k{Ip zTAN0@-(=`vc781p5JiU*$NYgaT2{fljR~<7zNtRv8v3j5_a-*eR6y>pN=z!9&jh}& zXk-<F?-ZPB)0J{7Eb0ayc9c>@PQkVeY)o^%1zARwT%%rUKC5kkQ(f-73azzHg~Lvs z+0wChW!~-3-8*c>l<)Tc%noN4(fQ^%@=h-l&%7(e9j2dMZRYp6^$oLbw?5z4X-<cD zcivVJ%v%{`O%1m1N@QOP)+5;F$}nHe{496gRvO6WGFHc&shD_kHO-NBD+uPT4D+VD z+iYfE57q|Ex>8u?Q~>Gj9nI#uJLVht?L)f0O|90Mxnuv|;Mo7WIQIWej_rg0FL^-p EzX&OIXaE2J
literal 0 HcmV?d00001

Hi Dario,
dario.binacchi@amarulasolutions.com wrote on Fri, 13 Sep 2024 11:55:42 +0200:
This series was created out of the need to supportsimple-framebuffer for BSH boards SMM_S2 and SMM_S2PRO. To achieve this goal, it was necessary to develop additional code and/or drivers for all the required components (i. e. clock, power domain, video, etc.). This series has a Linux counterpart that will also be submitted upstream as soon as possible. Furthermore, given the overlap in topics with the recent series [1], we are open to collaborate on the common parts to ensure proper development.
[1] https://lore.kernel.org/all/20240910101344.110633-1-miquel.raynal@bootlin.co...
I received a bit of feedback on this U-Boot series, I can take the feedback into account and send a v2. Then, if you are still okay, you can stack your patches on top of mine and do the required adaptations (on both sides).
Thanks, Miquèl

Hi Miquel,
On Mon, Sep 30, 2024 at 11:43 AM Miquel Raynal miquel.raynal@bootlin.com wrote:
Hi Dario,
dario.binacchi@amarulasolutions.com wrote on Fri, 13 Sep 2024 11:55:42 +0200:
This series was created out of the need to supportsimple-framebuffer for BSH boards SMM_S2 and SMM_S2PRO. To achieve this goal, it was necessary to develop additional code and/or drivers for all the required components (i. e. clock, power domain, video, etc.). This series has a Linux counterpart that will also be submitted upstream as soon as possible. Furthermore, given the overlap in topics with the recent series [1], we are open to collaborate on the common parts to ensure proper development.
[1] https://lore.kernel.org/all/20240910101344.110633-1-miquel.raynal@bootlin.co...
I received a bit of feedback on this U-Boot series, I can take the feedback into account and send a v2. Then, if you are still okay, you can stack your patches on top of mine and do the required adaptations (on both sides).
Sure, go ahead with version 2.
Thanks and regards, Dario
Thanks, Miquèl
participants (5)
-
Dario Binacchi
-
Heiko Schocher
-
Marek Vasut
-
Michael Nazzareno Trimarchi
-
Miquel Raynal