[13/25] power: Add iMX8M block ctrl driver for dispmix

Message ID 20240714113302.133399-13-michael@amarulasolutions.com
State New
Headers show
Series
  • [01/25] clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present for gate and mux
Related show

Commit Message

Michael Nazzareno Trimarchi July 14, 2024, 11:32 a.m. UTC
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>
---
 drivers/power/domain/Kconfig              |   6 +
 drivers/power/domain/Makefile             |   1 +
 drivers/power/domain/imx8m-blk-ctrl.c     | 431 ++++++++++++++++++++++
 drivers/power/domain/imx8m-power-domain.c | 213 ++++++++++-
 4 files changed, 649 insertions(+), 2 deletions(-)
 create mode 100644 drivers/power/domain/imx8m-blk-ctrl.c

Patch

diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
index bd82d2f704..fb006b6e8e 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 2daab73eb7..46849fd2a4 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 0000000000..3c22d9de38
--- /dev/null
+++ b/drivers/power/domain/imx8m-blk-ctrl.c
@@ -0,0 +1,431 @@ 
+// 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, 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);
+	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 8b6870c864..40fec70d95 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: