[PATCH v6 09/12] imx: power-domain: Add support for the MEDIAMIX control block
Adam Ford
aford173 at gmail.com
Thu Apr 3 14:57:53 CEST 2025
On Thu, Apr 3, 2025 at 2:39 AM Miquel Raynal <miquel.raynal at bootlin.com> wrote:
>
> This block delivers power and clocks to the whole display and rendering
> pipeline.
>
> Signed-off-by: Miquel Raynal <miquel.raynal at bootlin.com>
> ---
> drivers/power/domain/Kconfig | 7 ++
> drivers/power/domain/Makefile | 1 +
> drivers/power/domain/imx8mp-mediamix.c | 208 +++++++++++++++++++++++++++++++++
> 3 files changed, 216 insertions(+)
>
> diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
> index bd82d2f7044b3c3f8c88dea27c39193efedb4c84..5f5218bd8b5f4bed8283e91da61ac7c8c3eb97ae 100644
> --- a/drivers/power/domain/Kconfig
> +++ b/drivers/power/domain/Kconfig
> @@ -47,6 +47,13 @@ config IMX8MP_HSIOMIX_BLKCTRL
> help
> Enable support for manipulating NXP i.MX8MP on-SoC HSIOMIX block controller.
>
> +config IMX8MP_MEDIAMIX_BLKCTRL
> + bool "Enable i.MX8MP MEDIAMIX domain driver"
> + depends on POWER_DOMAIN && IMX8MP
> + select CLK
> + help
> + Enable support for manipulating NXP i.MX8MP on-SoC MEDIAMIX block controller.
> +
> config MTK_POWER_DOMAIN
> bool "Enable the MediaTek power domain driver"
> depends on POWER_DOMAIN && ARCH_MEDIATEK
> diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile
> index 110646c503ab97941a2bb86caf53f94542008219..356ec07163fd299f00e81a629ffc06c663a26c25 100644
> --- a/drivers/power/domain/Makefile
> +++ b/drivers/power/domain/Makefile
> @@ -9,6 +9,7 @@ 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_IMX8MP_HSIOMIX_BLKCTRL) += imx8mp-hsiomix.o
> +obj-$(CONFIG_IMX8MP_MEDIAMIX_BLKCTRL) += imx8mp-mediamix.o
> obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o
> obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o
> obj-$(CONFIG_MESON_EE_POWER_DOMAIN) += meson-ee-pwrc.o
> diff --git a/drivers/power/domain/imx8mp-mediamix.c b/drivers/power/domain/imx8mp-mediamix.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..78c32ca3d3a87febdefd5d128d39d817674b8d32
> --- /dev/null
> +++ b/drivers/power/domain/imx8mp-mediamix.c
> @@ -0,0 +1,208 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * i.MX8 MEDIAMIX control block driver
> + * Copyright (C) 2024 Miquel Raynal <miquel.raynal at bootlin.com>
> + * Inspired from Marek Vasut <marex at denx.de> work on the hsiomix driver.
> + */
> +
> +#include <asm/io.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <power-domain-uclass.h>
> +#include <linux/delay.h>
> +
> +#include <dt-bindings/power/imx8mp-power.h>
> +
> +#define BLK_SFT_RSTN 0x0
> +#define BLK_CLK_EN 0x4
> +
> +struct imx8mp_mediamix_priv {
> + void __iomem *base;
> + struct clk clk_apb;
> + struct clk clk_axi;
> + struct clk clk_disp2;
> + struct power_domain pd_bus;
> + struct power_domain pd_lcdif2;
> +};
> +
> +static int imx8mp_mediamix_on(struct power_domain *power_domain)
> +{
> + struct udevice *dev = power_domain->dev;
> + struct imx8mp_mediamix_priv *priv = dev_get_priv(dev);
> + struct power_domain *domain;
> + struct clk *clk;
> + u32 reset;
> + int ret;
> +
> + switch (power_domain->id) {
> + case IMX8MP_MEDIABLK_PD_LCDIF_2:
> + domain = &priv->pd_lcdif2;
> + clk = &priv->clk_disp2;
> + reset = BIT(11) | BIT(12) | BIT(24);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* Make sure bus domain is awake */
> + ret = power_domain_on(&priv->pd_bus);
> + if (ret)
> + return ret;
> +
> + /* Put devices into reset */
> + clrbits_le32(priv->base + BLK_SFT_RSTN, reset);
> +
> + /* Enable upstream clocks */
> + ret = clk_enable(&priv->clk_apb);
> + if (ret)
> + goto dis_bus_pd;
> +
> + ret = clk_enable(&priv->clk_axi);
> + if (ret)
> + goto dis_apb_clk;
> +
> + /* Enable blk-ctrl clock to allow reset to propagate */
> + ret = clk_enable(clk);
I am only offering a suggestion if you ever need to push another rev.
It appears that all the clocks are either on or off and if any fail,
you immediately turn off any clocks that were enabled. There are some
bulk clock options where you can basically tell the driver to turn
them all on or all off.
clk_get_bulk, clk_enable_bulk, clk_disable_bulk, and clk_release_all
are the functions I am thinking of. Your imx8mp_mediamix_priv
structure could then replace all the individual clocks with struct
clk_bulk.
That might shrink the overall code and simplify the readability.
> + if (ret)
> + goto dis_axi_clk;
> + setbits_le32(priv->base + BLK_CLK_EN, reset);
> +
> + /* Power up upstream GPC domain */
> + ret = power_domain_on(domain);
> + if (ret)
> + goto dis_lcdif_clk;
> +
> + /* Wait for reset to propagate */
> + udelay(5);
On Linux, the GPCv2 driver was updated to increase a delay from 5 to
10 uSeconds [1] . I don't know if that's needed here, but it's
something to consider.
adam
[1] - https://web.git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/drivers/pmdomain/imx?h=next-20250403&id=2379fb937de5333991c567eefd7d11b98977d059
> +
> + /* Release reset */
> + setbits_le32(priv->base + BLK_SFT_RSTN, reset);
> +
> + return 0;
> +
> +dis_lcdif_clk:
> + clk_disable(clk);
> +dis_axi_clk:
> + clk_disable(&priv->clk_axi);
> +dis_apb_clk:
> + clk_disable(&priv->clk_apb);
> +dis_bus_pd:
> + power_domain_off(&priv->pd_bus);
> +
> + return ret;
> +}
> +
> +static int imx8mp_mediamix_off(struct power_domain *power_domain)
> +{
> + struct udevice *dev = power_domain->dev;
> + struct imx8mp_mediamix_priv *priv = dev_get_priv(dev);
> + struct power_domain *domain;
> + struct clk *clk;
> + u32 reset;
> +
> + switch (power_domain->id) {
> + case IMX8MP_MEDIABLK_PD_LCDIF_2:
> + domain = &priv->pd_lcdif2;
> + clk = &priv->clk_disp2;
> + reset = BIT(11) | BIT(12) | BIT(24);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* Put devices into reset and disable clocks */
> + clrbits_le32(priv->base + BLK_SFT_RSTN, reset);
> + clrbits_le32(priv->base + BLK_CLK_EN, reset);
> +
> + /* Power down upstream GPC domain */
> + power_domain_off(domain);
> +
> + clk_disable(clk);
> + clk_disable(&priv->clk_axi);
> + clk_disable(&priv->clk_apb);
> +
> + /* Allow bus domain to suspend */
> + power_domain_off(&priv->pd_bus);
> +
> + return 0;
> +}
> +
> +static int imx8mp_mediamix_of_xlate(struct power_domain *power_domain,
> + struct ofnode_phandle_args *args)
> +{
> + power_domain->id = args->args[0];
> +
> + return 0;
> +}
> +
> +static int imx8mp_mediamix_bind(struct udevice *dev)
> +{
> + /* Bind child lcdif */
> + return dm_scan_fdt_dev(dev);
> +}
> +
> +static int imx8mp_mediamix_probe(struct udevice *dev)
> +{
> + struct imx8mp_mediamix_priv *priv = dev_get_priv(dev);
> + int ret;
> +
> + priv->base = dev_read_addr_ptr(dev);
> +
> + ret = clk_get_by_name(dev, "apb", &priv->clk_apb);
> + if (ret < 0)
> + return ret;
> +
> + ret = clk_get_by_name(dev, "axi", &priv->clk_axi);
> + if (ret < 0)
> + return ret;
> +
> + ret = clk_get_by_name(dev, "disp2", &priv->clk_disp2);
> + if (ret < 0)
> + return ret;
> +
> + ret = power_domain_get_by_name(dev, &priv->pd_bus, "bus");
> + if (ret < 0)
> + return ret;
> +
> + ret = power_domain_get_by_name(dev, &priv->pd_lcdif2, "lcdif2");
> + if (ret < 0)
> + goto free_bus_pd;
> +
> + return 0;
> +
> +free_bus_pd:
> + power_domain_free(&priv->pd_bus);
> + return ret;
> +}
> +
> +static int imx8mp_mediamix_remove(struct udevice *dev)
> +{
> + struct imx8mp_mediamix_priv *priv = dev_get_priv(dev);
> +
> + power_domain_free(&priv->pd_lcdif2);
> + power_domain_free(&priv->pd_bus);
> +
> + return 0;
> +}
> +
> +static const struct udevice_id imx8mp_mediamix_ids[] = {
> + { .compatible = "fsl,imx8mp-media-blk-ctrl" },
> + { }
> +};
> +
> +struct power_domain_ops imx8mp_mediamix_ops = {
> + .on = imx8mp_mediamix_on,
> + .off = imx8mp_mediamix_off,
> + .of_xlate = imx8mp_mediamix_of_xlate,
> +};
> +
> +U_BOOT_DRIVER(imx8mp_mediamix) = {
> + .name = "imx8mp_mediamix",
> + .id = UCLASS_POWER_DOMAIN,
> + .of_match = imx8mp_mediamix_ids,
> + .bind = imx8mp_mediamix_bind,
> + .probe = imx8mp_mediamix_probe,
> + .remove = imx8mp_mediamix_remove,
> + .priv_auto = sizeof(struct imx8mp_mediamix_priv),
> + .ops = &imx8mp_mediamix_ops,
> +};
>
> --
> 2.48.1
>
More information about the U-Boot
mailing list