[PATCH v2 03/11] pmdomain: imx8mp-blk-ctrl: add media blk-ctrl power domain support

Johannes Schneider <[email protected]>
Newsgroups org.infradead.lists.barebox
Message-ID <[email protected]>
From: Thomas Haemmerle <[email protected]>

The i.MX8MP LCDIF2 sits inside MEDIAMIX, whose blk-ctrl gates clocks
and holds resets.  Without enabling the MEDIAMIX bus clock and
de-asserting LCDIF2's block-level resets, the first register access
to LCDIF2 AXI-hangs barebox.

Extend the existing HSIO-only blk-ctrl driver for fsl,imx8mp-media-
blk-ctrl:

  - Route the HSIO ADB handshake through a new pre_power_on hook so
    the generic power_on path calls it *before* clk_bulk_prepare_enable,
    preserving v1's ordering while keeping HSIO specifics off the
    generic path.
  - NULL-check bc->power_on/off so blk-ctrls without per-domain
    callbacks (e.g. media) work.
  - Bump DOMAIN_MAX_CLKS from 2 to 3 (LCDIF2 needs disp2, axi, apb).
  - Add an imx8mp_media_blk_ctrl_probe that maxes the LCDIF NoC
    read-hurry, brings the MEDIAMIX fabric up, and de-asserts LCDIF2
    clocks + resets, mirroring imx8mp_media_power_notifier in Linux.

The media_blk_ctrl DTS node keeps its syscon compatible so the LDB
bridge sub-device can still get the regmap via syscon_node_to_regmap().

Signed-off-by: Thomas Haemmerle <[email protected]>
---
 drivers/pmdomain/imx/imx8mp-blk-ctrl.c | 91 ++++++++++++++++++++++++--
 1 file changed, 84 insertions(+), 7 deletions(-)

diff --git a/drivers/pmdomain/imx/imx8mp-blk-ctrl.c b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c
index 3d302cbde7..3db26e91b3 100644
--- a/drivers/pmdomain/imx/imx8mp-blk-ctrl.c
+++ b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c
@@ -17,6 +17,7 @@
 
 #include <dt-bindings/power/imx8mp-power.h>
 
+/* HSIO blk-ctrl registers */
 #define GPR_REG0		0x0
 #define  PCIE_CLOCK_MODULE_EN	BIT(0)
 #define  USB_CLOCK_MODULE_EN	BIT(1)
@@ -32,6 +33,11 @@
 #define  PLL_CKE		BIT(17)
 #define  PLL_RST		BIT(31)
 
+/* Media blk-ctrl registers */
+#define LCDIF_ARCACHE_CTRL	0x40
+#define  LCDIF_1_RD_HURRY	GENMASK(6, 4)
+#define  LCDIF_0_RD_HURRY	GENMASK(2, 0)
+
 struct imx8mp_blk_ctrl_domain;
 
 struct imx8mp_blk_ctrl {
@@ -40,6 +46,7 @@ struct imx8mp_blk_ctrl {
 	struct regmap *regmap;
 	struct imx8mp_blk_ctrl_domain *domains;
 	struct genpd_onecell_data onecell_data;
+	int (*pre_power_on) (struct imx8mp_blk_ctrl *bc);
 	void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
 	void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
 };
@@ -51,7 +58,7 @@ struct imx8mp_blk_ctrl_domain_data {
 	const char *gpc_name;
 };
 
-#define DOMAIN_MAX_CLKS 2
+#define DOMAIN_MAX_CLKS 3
 
 struct imx8mp_blk_ctrl_domain {
 	struct generic_pm_domain genpd;
@@ -65,6 +72,7 @@ struct imx8mp_blk_ctrl_domain {
 struct imx8mp_blk_ctrl_data {
 	int max_reg;
 	int (*probe) (struct imx8mp_blk_ctrl *bc);
+	int (*pre_power_on) (struct imx8mp_blk_ctrl *bc);
 	void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
 	void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
 	const struct imx8mp_blk_ctrl_domain_data *domains;
@@ -253,12 +261,52 @@ static const struct imx8mp_blk_ctrl_domain_data imx8mp_hsio_domain_data[] = {
 static const struct imx8mp_blk_ctrl_data imx8mp_hsio_blk_ctl_dev_data = {
 	.max_reg = 0x24,
 	.probe = imx8mp_hsio_blk_ctrl_probe,
+	.pre_power_on = imx8mp_hsio_propagate_adb_handshake,
 	.power_on = imx8mp_hsio_blk_ctrl_power_on,
 	.power_off = imx8mp_hsio_blk_ctrl_power_off,
 	.domains = imx8mp_hsio_domain_data,
 	.num_domains = ARRAY_SIZE(imx8mp_hsio_domain_data),
 };
 
+/* Media blk-ctrl */
+
+/* MEDIAMIX BLK_CTRL register offsets (i.MX 8M Plus RM section 13) */
+#define BLK_SFT_RSTN		0x00
+#define BLK_CLK_EN		0x04
+
+/* MEDIAMIX bus/APB clock + reset */
+#define MEDIAMIX_BUS_CLK_RST	BIT(8)
+
+/* LCDIF2 in BLK_SFT_RSTN/BLK_CLK_EN: axi (11), apb (12), disp2 pixel (24) */
+#define LCDIF2_CLK_RST_MASK	(BIT(11) | BIT(12) | BIT(24))
+
+static int imx8mp_media_blk_ctrl_probe(struct imx8mp_blk_ctrl *bc)
+{
+	/* maximize LCDIF NoC AXI read priority to avoid scanout underflow */
+	regmap_update_bits(bc->regmap, LCDIF_ARCACHE_CTRL,
+			   FIELD_PREP(LCDIF_1_RD_HURRY, 7) |
+			   FIELD_PREP(LCDIF_0_RD_HURRY, 7),
+			   FIELD_PREP(LCDIF_1_RD_HURRY, 7) |
+			   FIELD_PREP(LCDIF_0_RD_HURRY, 7));
+
+	/* bring MEDIAMIX fabric up before any sub-module access */
+	regmap_set_bits(bc->regmap, BLK_CLK_EN,  MEDIAMIX_BUS_CLK_RST);
+	regmap_set_bits(bc->regmap, BLK_SFT_RSTN, MEDIAMIX_BUS_CLK_RST);
+	udelay(5); /* ADB handshake settle */
+
+	/* LCDIF2 registers AXI-hang until its own clocks + reset are de-asserted */
+	regmap_set_bits(bc->regmap, BLK_CLK_EN,  LCDIF2_CLK_RST_MASK);
+	regmap_set_bits(bc->regmap, BLK_SFT_RSTN, LCDIF2_CLK_RST_MASK);
+
+	return 0;
+}
+
+static const struct imx8mp_blk_ctrl_data imx8mp_media_blk_ctl_dev_data = {
+	.max_reg = 0x138,
+	.probe = imx8mp_media_blk_ctrl_probe,
+	/* num_domains = 0: skip genpd; MEDIAMIX is already on from SPL */
+};
+
 static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd)
 {
 	struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd);
@@ -273,10 +321,12 @@ static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd)
 		return ret;
 	}
 
-	ret = imx8mp_hsio_propagate_adb_handshake(bc);
-	if (ret) {
-		dev_err(bc->dev, "failed to propagate adb handshake\n");
-		goto bus_put;
+	if (bc->pre_power_on) {
+		ret = bc->pre_power_on(bc);
+		if (ret) {
+			dev_err(bc->dev, "pre_power_on failed: %d\n", ret);
+			goto bus_put;
+		}
 	}
 
 	/* enable upstream clocks */
@@ -287,7 +337,8 @@ static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd)
 	}
 
 	/* domain specific blk-ctrl manipulation */
-	bc->power_on(bc, domain);
+	if (bc->power_on)
+		bc->power_on(bc, domain);
 
 	/* power up upstream GPC domain */
 	ret = pm_runtime_resume_and_get_genpd(domain->power_dev);
@@ -322,7 +373,8 @@ static int imx8mp_blk_ctrl_power_off(struct generic_pm_domain *genpd)
 	}
 
 	/* domain specific blk-ctrl manipulation */
-	bc->power_off(bc, domain);
+	if (bc->power_off)
+		bc->power_off(bc, domain);
 
 	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
 
@@ -380,6 +432,27 @@ static int imx8mp_blk_ctrl_probe(struct device *dev)
 	if (!bc->onecell_data.domains)
 		return -ENOMEM;
 
+	/*
+	 * num_domains == 0: blk-ctrl instances (e.g. media) that only need
+	 * the regmap/probe side-effects and no genpd provider.  Power on
+	 * the bus domain so the blk-ctrl's own registers are accessible,
+	 * then run its probe and return.
+	 */
+	if (num_domains == 0) {
+		bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus");
+		if (!IS_ERR_OR_NULL(bc->bus_power_dev)) {
+			ret = pm_runtime_resume_and_get_genpd(bc->bus_power_dev);
+			if (ret < 0)
+				dev_warn(dev, "failed to power on MEDIAMIX (ignoring): %d\n", ret);
+		}
+		if (bc_data->probe) {
+			ret = bc_data->probe(bc);
+			if (ret)
+				return ret;
+		}
+		return 0;
+	}
+
 	bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus");
 	if (IS_ERR(bc->bus_power_dev))
 		return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev),
@@ -387,6 +460,7 @@ static int imx8mp_blk_ctrl_probe(struct device *dev)
 
 	bc->power_off = bc_data->power_off;
 	bc->power_on = bc_data->power_on;
+	bc->pre_power_on = bc_data->pre_power_on;
 
 	for (i = 0; i < num_domains; i++) {
 		const struct imx8mp_blk_ctrl_domain_data *data = &bc_data->domains[i];
@@ -461,6 +535,9 @@ static const struct of_device_id imx8mp_blk_ctrl_of_match[] = {
 	{
 		.compatible = "fsl,imx8mp-hsio-blk-ctrl",
 		.data = &imx8mp_hsio_blk_ctl_dev_data,
+	}, {
+		.compatible = "fsl,imx8mp-media-blk-ctrl",
+		.data = &imx8mp_media_blk_ctl_dev_data,
 	}, {
 		/* Sentinel */
 	}
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.