This block delivers power and clocks to the whole display and rendering
pipeline.

Signed-off-by: Miquel Raynal <miquel.ray...@bootlin.com>
---
 drivers/power/domain/Kconfig           |   7 +
 drivers/power/domain/Makefile          |   1 +
 drivers/power/domain/imx8mp-mediamix.c | 185 +++++++++++++++++++++++++
 3 files changed, 193 insertions(+)
 create mode 100644 drivers/power/domain/imx8mp-mediamix.c

diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
index bd82d2f7044..5f5218bd8b5 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 2daab73eb75..4e45b6bb3aa 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 00000000000..63079ff3430
--- /dev/null
+++ b/drivers/power/domain/imx8mp-mediamix.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * i.MX8 MEDIAMIX control block driver
+ * Copyright (C) 2024 Miquel Raynal <miquel.ray...@bootlin.com>
+ * Inspired from Marek Vasut <ma...@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_bus_pd;
+
+       /* Enable blk-ctrl clock to allow reset to propagate */
+       ret = clk_enable(clk);
+       if (ret)
+               goto dis_bus_pd;
+       setbits_le32(priv->base + BLK_CLK_EN, reset);
+
+       /* Power up upstream GPC domain */
+       ret = power_domain_on(domain);
+       if (ret)
+               goto dis_bus_pd;
+
+       /* Wait for reset to propagate */
+       udelay(5);
+
+       /* Release reset */
+       setbits_le32(priv->base + BLK_SFT_RSTN, reset);
+
+       return 0;
+
+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;
+       u32 reset;
+
+       switch (power_domain->id) {
+       case IMX8MP_MEDIABLK_PD_LCDIF_2:
+               domain = &priv->pd_lcdif2;
+               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);
+
+       /* 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 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,
+       .priv_auto      = sizeof(struct imx8mp_mediamix_priv),
+       .ops            = &imx8mp_mediamix_ops,
+};
-- 
2.43.0

Reply via email to