linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/8] Add support for i.MX94 DCIF
@ 2025-07-09 12:23 Laurentiu Palcu
  2025-07-09 12:23 ` [PATCH 1/8] clk: imx95-blk-ctl: Cache registers when RPM routines are called Laurentiu Palcu
                   ` (7 more replies)
  0 siblings, 8 replies; 21+ messages in thread
From: Laurentiu Palcu @ 2025-07-09 12:23 UTC (permalink / raw)
  To: imx, Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Laurentiu Palcu, Philipp Zabel,
	Shawn Guo, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Marek Vasut
  Cc: dri-devel, Abel Vesa, devicetree, linux-kernel, linux-arm-kernel,
	linux-clk

Hi,

This patch-set adds support for the i.MX94 Display Control Interface.
It applies on top of Peng Fan's BLK_CTL patches: [1].

Also, included in the patch-set are a few extra patches that the DCIF
driver depends on for functioning properly:
 * 1/8       : this is a fix for i.MX95 BLK_CTL driver;
 * 2/8 - 4/8 : add support for i.MX94 to fsl-ldb driver. It also
			   contains a patch (3/8) from Liu Ying that was already reviewed
			   and was part of another patch-set ([2]), but was never merged;

Thanks,
Laurentiu

[1] https://lkml.org/lkml/2025/7/7/78
[2] https://lkml.org/lkml/2024/11/14/262

Laurentiu Palcu (6):
  clk: imx95-blk-ctl: Cache registers when RPM routines are called
  dt-bindings: display: fsl,ldb: Add binding for i.MX94
  drm/bridge: fsl-ldb: Add support for i.MX94
  dt-bindings: display: imx: Add bindings for i.MX94 DCIF
  arm64: dts: imx943-evk: Add support for DCIF and LVDS
  MAINTAINERS: Add entry for i.MX94 DCIF driver

Liu Ying (1):
  drm/bridge: fsl-ldb: Get the next non-panel bridge

Sandor Yu (1):
  drm/imx: Add support for i.MX94 DCIF

 .../bindings/display/bridge/fsl,ldb.yaml      |   2 +
 .../bindings/display/imx/nxp,imx94-dcif.yaml  | 101 +++
 MAINTAINERS                                   |   8 +
 arch/arm64/boot/dts/freescale/imx943-evk.dts  | 126 ++++
 arch/arm64/boot/dts/freescale/imx943.dtsi     |  56 +-
 drivers/clk/imx/clk-imx95-blk-ctl.c           |  55 +-
 drivers/gpu/drm/bridge/fsl-ldb.c              |  47 +-
 drivers/gpu/drm/imx/Kconfig                   |   1 +
 drivers/gpu/drm/imx/Makefile                  |   1 +
 drivers/gpu/drm/imx/dcif/Kconfig              |  12 +
 drivers/gpu/drm/imx/dcif/Makefile             |   5 +
 drivers/gpu/drm/imx/dcif/dcif-crc.c           | 211 ++++++
 drivers/gpu/drm/imx/dcif/dcif-crc.h           |  52 ++
 drivers/gpu/drm/imx/dcif/dcif-crtc.c          | 696 ++++++++++++++++++
 drivers/gpu/drm/imx/dcif/dcif-drv.c           | 293 ++++++++
 drivers/gpu/drm/imx/dcif/dcif-drv.h           |  87 +++
 drivers/gpu/drm/imx/dcif/dcif-kms.c           | 101 +++
 drivers/gpu/drm/imx/dcif/dcif-plane.c         | 269 +++++++
 drivers/gpu/drm/imx/dcif/dcif-reg.h           | 326 ++++++++
 19 files changed, 2406 insertions(+), 43 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/imx/nxp,imx94-dcif.yaml
 create mode 100644 drivers/gpu/drm/imx/dcif/Kconfig
 create mode 100644 drivers/gpu/drm/imx/dcif/Makefile
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-crc.c
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-crc.h
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-crtc.c
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-drv.c
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-drv.h
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-kms.c
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-plane.c
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-reg.h

-- 
2.46.1


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 1/8] clk: imx95-blk-ctl: Cache registers when RPM routines are called
  2025-07-09 12:23 [PATCH 0/8] Add support for i.MX94 DCIF Laurentiu Palcu
@ 2025-07-09 12:23 ` Laurentiu Palcu
  2025-07-11  4:03   ` Frank Li
  2025-07-09 12:23 ` [PATCH 2/8] dt-bindings: display: fsl,ldb: Add binding for i.MX94 Laurentiu Palcu
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Palcu @ 2025-07-09 12:23 UTC (permalink / raw)
  To: imx, Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd,
	Shawn Guo, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
  Cc: dri-devel, Laurentiu Palcu, Abel Vesa, linux-clk,
	linux-arm-kernel, linux-kernel

If runtime PM is used for the clock providers and they're part of a
power domain, then the power domain supply will be cut off when runtime
suspended. That means all BLK CTL registers belonging to that power
domain will be reset. Hence, the clock settings will revert to default
values messing up the consumer clock settings.

Also, fix the suspend/resume routines as well, as the clock was left ON
when going to suspend.

Fixes: 5224b189462f ("clk: imx: add i.MX95 BLK CTL clk driver")
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
---
 drivers/clk/imx/clk-imx95-blk-ctl.c | 55 ++++++++++++++++++-----------
 1 file changed, 34 insertions(+), 21 deletions(-)

diff --git a/drivers/clk/imx/clk-imx95-blk-ctl.c b/drivers/clk/imx/clk-imx95-blk-ctl.c
index 7e88877a62451..7f9bbca517284 100644
--- a/drivers/clk/imx/clk-imx95-blk-ctl.c
+++ b/drivers/clk/imx/clk-imx95-blk-ctl.c
@@ -448,12 +448,36 @@ static int imx95_bc_probe(struct platform_device *pdev)
 	return ret;
 }
 
+static void __maybe_unused imx95_bc_save_reg(struct imx95_blk_ctl *bc)
+{
+	const struct imx95_blk_ctl_dev_data *bc_data;
+
+	bc_data = of_device_get_match_data(bc->dev);
+	if (!bc_data)
+		return;
+
+	bc->clk_reg_restore = readl(bc->base + bc_data->clk_reg_offset);
+}
+
+static void __maybe_unused imx95_bc_restore_reg(struct imx95_blk_ctl *bc)
+{
+	const struct imx95_blk_ctl_dev_data *bc_data;
+
+	bc_data = of_device_get_match_data(bc->dev);
+	if (!bc_data)
+		return;
+
+	writel(bc->clk_reg_restore, bc->base + bc_data->clk_reg_offset);
+}
+
 #ifdef CONFIG_PM
 static int imx95_bc_runtime_suspend(struct device *dev)
 {
 	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
 
+	imx95_bc_save_reg(bc);
 	clk_disable_unprepare(bc->clk_apb);
+
 	return 0;
 }
 
@@ -461,7 +485,10 @@ static int imx95_bc_runtime_resume(struct device *dev)
 {
 	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
 
-	return clk_prepare_enable(bc->clk_apb);
+	clk_prepare_enable(bc->clk_apb);
+	imx95_bc_restore_reg(bc);
+
+	return 0;
 }
 #endif
 
@@ -469,22 +496,12 @@ static int imx95_bc_runtime_resume(struct device *dev)
 static int imx95_bc_suspend(struct device *dev)
 {
 	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
-	const struct imx95_blk_ctl_dev_data *bc_data;
-	int ret;
 
-	bc_data = of_device_get_match_data(dev);
-	if (!bc_data)
+	if (pm_runtime_suspended(dev))
 		return 0;
 
-	if (bc_data->rpm_enabled) {
-		ret = pm_runtime_get_sync(bc->dev);
-		if (ret < 0) {
-			pm_runtime_put_noidle(bc->dev);
-			return ret;
-		}
-	}
-
-	bc->clk_reg_restore = readl(bc->base + bc_data->clk_reg_offset);
+	imx95_bc_save_reg(bc);
+	clk_disable_unprepare(bc->clk_apb);
 
 	return 0;
 }
@@ -492,16 +509,12 @@ static int imx95_bc_suspend(struct device *dev)
 static int imx95_bc_resume(struct device *dev)
 {
 	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
-	const struct imx95_blk_ctl_dev_data *bc_data;
 
-	bc_data = of_device_get_match_data(dev);
-	if (!bc_data)
+	if (pm_runtime_suspended(dev))
 		return 0;
 
-	writel(bc->clk_reg_restore, bc->base + bc_data->clk_reg_offset);
-
-	if (bc_data->rpm_enabled)
-		pm_runtime_put(bc->dev);
+	clk_prepare_enable(bc->clk_apb);
+	imx95_bc_restore_reg(bc);
 
 	return 0;
 }
-- 
2.46.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 2/8] dt-bindings: display: fsl,ldb: Add binding for i.MX94
  2025-07-09 12:23 [PATCH 0/8] Add support for i.MX94 DCIF Laurentiu Palcu
  2025-07-09 12:23 ` [PATCH 1/8] clk: imx95-blk-ctl: Cache registers when RPM routines are called Laurentiu Palcu
@ 2025-07-09 12:23 ` Laurentiu Palcu
  2025-07-10 22:15   ` Rob Herring
  2025-07-09 12:23 ` [PATCH 3/8] drm/bridge: fsl-ldb: Get the next non-panel bridge Laurentiu Palcu
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Palcu @ 2025-07-09 12:23 UTC (permalink / raw)
  To: imx, Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Marek Vasut
  Cc: dri-devel, Laurentiu Palcu, devicetree, linux-kernel

i.MX94 LDB controller is compatible with i.MX93.

Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
---
 Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml b/Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml
index 07388bf2b90df..a54b8f1478049 100644
--- a/Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml
@@ -20,6 +20,7 @@ properties:
       - fsl,imx6sx-ldb
       - fsl,imx8mp-ldb
       - fsl,imx93-ldb
+      - fsl,imx94-ldb
 
   clocks:
     maxItems: 1
@@ -68,6 +69,7 @@ allOf:
             enum:
               - fsl,imx6sx-ldb
               - fsl,imx93-ldb
+              - fsl,imx94-ldb
     then:
       properties:
         ports:
-- 
2.46.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 3/8] drm/bridge: fsl-ldb: Get the next non-panel bridge
  2025-07-09 12:23 [PATCH 0/8] Add support for i.MX94 DCIF Laurentiu Palcu
  2025-07-09 12:23 ` [PATCH 1/8] clk: imx95-blk-ctl: Cache registers when RPM routines are called Laurentiu Palcu
  2025-07-09 12:23 ` [PATCH 2/8] dt-bindings: display: fsl,ldb: Add binding for i.MX94 Laurentiu Palcu
@ 2025-07-09 12:23 ` Laurentiu Palcu
  2025-07-11  4:19   ` Frank Li
  2025-07-09 12:23 ` [PATCH 4/8] drm/bridge: fsl-ldb: Add support for i.MX94 Laurentiu Palcu
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Palcu @ 2025-07-09 12:23 UTC (permalink / raw)
  To: imx, Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter
  Cc: dri-devel, Liu Ying, Laurentiu Palcu, linux-kernel

From: Liu Ying <victor.liu@nxp.com>

The next bridge in bridge chain could be a panel bridge or a non-panel
bridge.  Use devm_drm_of_get_bridge() to replace the combination
function calls of of_drm_find_panel() and devm_drm_panel_bridge_add()
to get either a panel bridge or a non-panel bridge, instead of getting
a panel bridge only.

Signed-off-by: Liu Ying <victor.liu@nxp.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
---
 drivers/gpu/drm/bridge/fsl-ldb.c | 31 +++++++++++--------------------
 1 file changed, 11 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c
index 5c3cf37200bce..665053d0cb79d 100644
--- a/drivers/gpu/drm/bridge/fsl-ldb.c
+++ b/drivers/gpu/drm/bridge/fsl-ldb.c
@@ -15,7 +15,6 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_of.h>
-#include <drm/drm_panel.h>
 
 #define LDB_CTRL_CH0_ENABLE			BIT(0)
 #define LDB_CTRL_CH0_DI_SELECT			BIT(1)
@@ -86,7 +85,7 @@ static const struct fsl_ldb_devdata fsl_ldb_devdata[] = {
 struct fsl_ldb {
 	struct device *dev;
 	struct drm_bridge bridge;
-	struct drm_bridge *panel_bridge;
+	struct drm_bridge *next_bridge;
 	struct clk *clk;
 	struct regmap *regmap;
 	const struct fsl_ldb_devdata *devdata;
@@ -118,7 +117,7 @@ static int fsl_ldb_attach(struct drm_bridge *bridge,
 {
 	struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
 
-	return drm_bridge_attach(encoder, fsl_ldb->panel_bridge,
+	return drm_bridge_attach(encoder, fsl_ldb->next_bridge,
 				 bridge, flags);
 }
 
@@ -292,9 +291,7 @@ static const struct drm_bridge_funcs funcs = {
 static int fsl_ldb_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct device_node *panel_node;
 	struct device_node *remote1, *remote2;
-	struct drm_panel *panel;
 	struct fsl_ldb *fsl_ldb;
 	int dual_link;
 
@@ -317,33 +314,27 @@ static int fsl_ldb_probe(struct platform_device *pdev)
 	if (IS_ERR(fsl_ldb->regmap))
 		return PTR_ERR(fsl_ldb->regmap);
 
-	/* Locate the remote ports and the panel node */
+	/* Locate the remote ports. */
 	remote1 = of_graph_get_remote_node(dev->of_node, 1, 0);
 	remote2 = of_graph_get_remote_node(dev->of_node, 2, 0);
 	fsl_ldb->ch0_enabled = (remote1 != NULL);
 	fsl_ldb->ch1_enabled = (remote2 != NULL);
-	panel_node = of_node_get(remote1 ? remote1 : remote2);
 	of_node_put(remote1);
 	of_node_put(remote2);
 
-	if (!fsl_ldb->ch0_enabled && !fsl_ldb->ch1_enabled) {
-		of_node_put(panel_node);
-		return dev_err_probe(dev, -ENXIO, "No panel node found");
-	}
+	if (!fsl_ldb->ch0_enabled && !fsl_ldb->ch1_enabled)
+		return dev_err_probe(dev, -ENXIO, "No next bridge node found");
 
 	dev_dbg(dev, "Using %s\n",
 		fsl_ldb_is_dual(fsl_ldb) ? "dual-link mode" :
 		fsl_ldb->ch0_enabled ? "channel 0" : "channel 1");
 
-	panel = of_drm_find_panel(panel_node);
-	of_node_put(panel_node);
-	if (IS_ERR(panel))
-		return PTR_ERR(panel);
-
-	fsl_ldb->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
-	if (IS_ERR(fsl_ldb->panel_bridge))
-		return PTR_ERR(fsl_ldb->panel_bridge);
-
+	fsl_ldb->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node,
+						      fsl_ldb->ch0_enabled ? 1 : 2,
+						      0);
+	if (IS_ERR(fsl_ldb->next_bridge))
+		return dev_err_probe(dev, PTR_ERR(fsl_ldb->next_bridge),
+				     "failed to get next bridge\n");
 
 	if (fsl_ldb_is_dual(fsl_ldb)) {
 		struct device_node *port1, *port2;
-- 
2.46.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 4/8] drm/bridge: fsl-ldb: Add support for i.MX94
  2025-07-09 12:23 [PATCH 0/8] Add support for i.MX94 DCIF Laurentiu Palcu
                   ` (2 preceding siblings ...)
  2025-07-09 12:23 ` [PATCH 3/8] drm/bridge: fsl-ldb: Get the next non-panel bridge Laurentiu Palcu
@ 2025-07-09 12:23 ` Laurentiu Palcu
  2025-07-11  4:26   ` Frank Li
  2025-07-09 12:23 ` [PATCH 5/8] dt-bindings: display: imx: Add bindings for i.MX94 DCIF Laurentiu Palcu
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Palcu @ 2025-07-09 12:23 UTC (permalink / raw)
  To: imx, Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter
  Cc: dri-devel, Laurentiu Palcu, linux-kernel

Since i.MX94 series LDB controller is compatible, add support for it.

Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
---
 drivers/gpu/drm/bridge/fsl-ldb.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c
index 665053d0cb79d..4052e1ea9201f 100644
--- a/drivers/gpu/drm/bridge/fsl-ldb.c
+++ b/drivers/gpu/drm/bridge/fsl-ldb.c
@@ -57,6 +57,7 @@ enum fsl_ldb_devtype {
 	IMX6SX_LDB,
 	IMX8MP_LDB,
 	IMX93_LDB,
+	IMX94_LDB,
 };
 
 struct fsl_ldb_devdata {
@@ -64,21 +65,31 @@ struct fsl_ldb_devdata {
 	u32 lvds_ctrl;
 	bool lvds_en_bit;
 	bool single_ctrl_reg;
+	u32 max_clk_khz;
 };
 
 static const struct fsl_ldb_devdata fsl_ldb_devdata[] = {
 	[IMX6SX_LDB] = {
 		.ldb_ctrl = 0x18,
 		.single_ctrl_reg = true,
+		.max_clk_khz = 80000,
 	},
 	[IMX8MP_LDB] = {
 		.ldb_ctrl = 0x5c,
 		.lvds_ctrl = 0x128,
+		.max_clk_khz = 80000,
 	},
 	[IMX93_LDB] = {
 		.ldb_ctrl = 0x20,
 		.lvds_ctrl = 0x24,
 		.lvds_en_bit = true,
+		.max_clk_khz = 80000,
+	},
+	[IMX94_LDB] = {
+		.ldb_ctrl = 0x04,
+		.lvds_ctrl = 0x08,
+		.lvds_en_bit = true,
+		.max_clk_khz = 165000,
 	},
 };
 
@@ -270,8 +281,9 @@ fsl_ldb_mode_valid(struct drm_bridge *bridge,
 		   const struct drm_display_mode *mode)
 {
 	struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
+	u32 ch_max_clk_khz = fsl_ldb->devdata->max_clk_khz;
 
-	if (mode->clock > (fsl_ldb_is_dual(fsl_ldb) ? 160000 : 80000))
+	if (mode->clock > (fsl_ldb_is_dual(fsl_ldb) ? 2 * ch_max_clk_khz : ch_max_clk_khz))
 		return MODE_CLOCK_HIGH;
 
 	return MODE_OK;
@@ -377,6 +389,8 @@ static const struct of_device_id fsl_ldb_match[] = {
 	  .data = &fsl_ldb_devdata[IMX8MP_LDB], },
 	{ .compatible = "fsl,imx93-ldb",
 	  .data = &fsl_ldb_devdata[IMX93_LDB], },
+	{ .compatible = "fsl,imx94-ldb",
+	  .data = &fsl_ldb_devdata[IMX94_LDB], },
 	{ /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, fsl_ldb_match);
-- 
2.46.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 5/8] dt-bindings: display: imx: Add bindings for i.MX94 DCIF
  2025-07-09 12:23 [PATCH 0/8] Add support for i.MX94 DCIF Laurentiu Palcu
                   ` (3 preceding siblings ...)
  2025-07-09 12:23 ` [PATCH 4/8] drm/bridge: fsl-ldb: Add support for i.MX94 Laurentiu Palcu
@ 2025-07-09 12:23 ` Laurentiu Palcu
  2025-07-10 22:18   ` Rob Herring
  2025-07-09 12:23 ` [PATCH 6/8] drm/imx: Add support " Laurentiu Palcu
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Palcu @ 2025-07-09 12:23 UTC (permalink / raw)
  To: imx, Laurentiu Palcu, Philipp Zabel, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam
  Cc: dri-devel, devicetree, linux-arm-kernel, linux-kernel

DCIF is the i.MX94 Display Controller Interface which is used to
drive a TFT LCD panel or connects to a display interface depending
on the chip configuration.

Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
---
 .../bindings/display/imx/nxp,imx94-dcif.yaml  | 101 ++++++++++++++++++
 1 file changed, 101 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/imx/nxp,imx94-dcif.yaml

diff --git a/Documentation/devicetree/bindings/display/imx/nxp,imx94-dcif.yaml b/Documentation/devicetree/bindings/display/imx/nxp,imx94-dcif.yaml
new file mode 100644
index 0000000000000..e4cab43c77ebc
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/imx/nxp,imx94-dcif.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2025 NXP
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/imx/nxp,imx94-dcif.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: i.MX94 Display Control Interface (DCIF)
+
+maintainers:
+  - Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
+
+description:
+  The Display Control Interface(DCIF) is a system master that fetches graphics
+  stored in memory and displays them on a TFT LCD panel or connects to a
+  display interface depending on the chip configuration.
+
+properties:
+  compatible:
+    const: nxp,imx94-dcif
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    items:
+      - description:
+          Interrupt output for CPU domain 0 (controlled by common registers group).
+      - description:
+          Interrupt output for CPU domain 1 (controlled by background layer registers group).
+      - description:
+          Interrupt output for CPU domain 2 (controlled by foreground layer registers group).
+
+  interrupt-names:
+    items:
+      - const: common
+      - const: bg_layer
+      - const: fg_layer
+
+  clocks:
+    items:
+      - description: APB bus clock
+      - description: AXI bus clock
+      - description: Pixel clock
+
+  clock-names:
+    items:
+      - const: apb
+      - const: axi
+      - const: pix
+
+  assigned-clocks:
+    description: Reference to DISPLAYMIX CSR clock mux.
+    maxItems: 1
+
+  assigned-clock-parents:
+    description: Parent of the clock mux.
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+  nxp,blk-ctrl:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: A phandle which points to NXP displaymix blk-ctrl.
+
+  port:
+    $ref: /schemas/graph.yaml#/properties/port
+    description: Display Pixel Interface(DPI) output port
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    soc {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        display-controller@4b120000 {
+            compatible = "nxp,imx94-dcif";
+            reg = <0x0 0x4b120000 0x0 0x300000>;
+            interrupts = <GIC_SPI 377 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 378 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 379 IRQ_TYPE_LEVEL_HIGH>;
+            interrupt-names = "common", "bg_layer", "fg_layer";
+            clocks = <&scmi_clk 69>, <&scmi_clk 70>, <&dispmix_csr 0>;
+            clock-names = "apb", "axi", "pix";
+            assigned-clocks = <&dispmix_csr 0>;
+            assigned-clock-parents = <&ldb_pll_pixel>;
+            power-domains = <&scmi_devpd 11>;
+            nxp,blk-ctrl = <&dispmix_csr>;
+
+            port {
+                dcif_out: endpoint {
+                    remote-endpoint = <&ldb_in>;
+                };
+            };
+        };
+    };
-- 
2.46.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 6/8] drm/imx: Add support for i.MX94 DCIF
  2025-07-09 12:23 [PATCH 0/8] Add support for i.MX94 DCIF Laurentiu Palcu
                   ` (4 preceding siblings ...)
  2025-07-09 12:23 ` [PATCH 5/8] dt-bindings: display: imx: Add bindings for i.MX94 DCIF Laurentiu Palcu
@ 2025-07-09 12:23 ` Laurentiu Palcu
  2025-07-11  5:01   ` Frank Li
  2025-07-09 12:23 ` [PATCH 7/8] arm64: dts: imx943-evk: Add support for DCIF and LVDS Laurentiu Palcu
  2025-07-09 12:23 ` [PATCH 8/8] MAINTAINERS: Add entry for i.MX94 DCIF driver Laurentiu Palcu
  7 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Palcu @ 2025-07-09 12:23 UTC (permalink / raw)
  To: imx, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Shawn Guo, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Laurentiu Palcu
  Cc: dri-devel, Sandor Yu, linux-kernel, linux-arm-kernel

From: Sandor Yu <sandor.yu@nxp.com>

This patch adds support for the i.MX94 Display Control Interface.

Signed-off-by: Sandor Yu <sandor.yu@nxp.com>
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
---
 drivers/gpu/drm/imx/Kconfig           |   1 +
 drivers/gpu/drm/imx/Makefile          |   1 +
 drivers/gpu/drm/imx/dcif/Kconfig      |  12 +
 drivers/gpu/drm/imx/dcif/Makefile     |   5 +
 drivers/gpu/drm/imx/dcif/dcif-crc.c   | 211 ++++++++
 drivers/gpu/drm/imx/dcif/dcif-crc.h   |  52 ++
 drivers/gpu/drm/imx/dcif/dcif-crtc.c  | 696 ++++++++++++++++++++++++++
 drivers/gpu/drm/imx/dcif/dcif-drv.c   | 293 +++++++++++
 drivers/gpu/drm/imx/dcif/dcif-drv.h   |  87 ++++
 drivers/gpu/drm/imx/dcif/dcif-kms.c   | 101 ++++
 drivers/gpu/drm/imx/dcif/dcif-plane.c | 269 ++++++++++
 drivers/gpu/drm/imx/dcif/dcif-reg.h   | 326 ++++++++++++
 12 files changed, 2054 insertions(+)
 create mode 100644 drivers/gpu/drm/imx/dcif/Kconfig
 create mode 100644 drivers/gpu/drm/imx/dcif/Makefile
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-crc.c
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-crc.h
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-crtc.c
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-drv.c
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-drv.h
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-kms.c
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-plane.c
 create mode 100644 drivers/gpu/drm/imx/dcif/dcif-reg.h

diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
index 3e8c6edbc17c2..1b6ced5c60b51 100644
--- a/drivers/gpu/drm/imx/Kconfig
+++ b/drivers/gpu/drm/imx/Kconfig
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
 source "drivers/gpu/drm/imx/dc/Kconfig"
+source "drivers/gpu/drm/imx/dcif/Kconfig"
 source "drivers/gpu/drm/imx/dcss/Kconfig"
 source "drivers/gpu/drm/imx/ipuv3/Kconfig"
 source "drivers/gpu/drm/imx/lcdc/Kconfig"
diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
index c7b317640d71d..2b9fd85eefaa3 100644
--- a/drivers/gpu/drm/imx/Makefile
+++ b/drivers/gpu/drm/imx/Makefile
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_DRM_IMX8_DC) += dc/
+obj-$(CONFIG_DRM_IMX_DCIF) += dcif/
 obj-$(CONFIG_DRM_IMX_DCSS) += dcss/
 obj-$(CONFIG_DRM_IMX) += ipuv3/
 obj-$(CONFIG_DRM_IMX_LCDC) += lcdc/
diff --git a/drivers/gpu/drm/imx/dcif/Kconfig b/drivers/gpu/drm/imx/dcif/Kconfig
new file mode 100644
index 0000000000000..f16652c49d211
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcif/Kconfig
@@ -0,0 +1,12 @@
+config DRM_IMX_DCIF
+	tristate "DRM support for NXP i.MX943 DCIF"
+	select DRM_KMS_HELPER
+	select VIDEOMODE_HELPERS
+	select DRM_GEM_DMA_HELPER
+	select DRM_DISPLAY_HELPER
+	select DRM_BRIDGE_CONNECTOR
+	select DRM_CLIENT_SELECTION
+	depends on DRM && OF && ARCH_MXC
+	depends on COMMON_CLK
+	help
+	  enable NXP i.MX943 Display Control Interface(DCIF) support
diff --git a/drivers/gpu/drm/imx/dcif/Makefile b/drivers/gpu/drm/imx/dcif/Makefile
new file mode 100644
index 0000000000000..b429572040f0e
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcif/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+imx-dcif-drm-objs := dcif-crc.o dcif-crtc.o dcif-drv.o dcif-kms.o dcif-plane.o
+
+obj-$(CONFIG_DRM_IMX_DCIF) += imx-dcif-drm.o
diff --git a/drivers/gpu/drm/imx/dcif/dcif-crc.c b/drivers/gpu/drm/imx/dcif/dcif-crc.c
new file mode 100644
index 0000000000000..2af363bd96169
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcif/dcif-crc.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_rect.h>
+
+#include "dcif-crc.h"
+#include "dcif-reg.h"
+
+#define MAX_DCIF_CRC_NUM       4
+
+static int dcif_crc_config(struct dcif_dev *dcif, struct drm_rect *roi, int ncrc)
+{
+	int pos, size;
+
+	if (ncrc >= MAX_DCIF_CRC_NUM)
+		return -EINVAL;
+
+	pos = DCIF_CRC_POS_CRC_HOR_POS(roi->x1) |
+	      DCIF_CRC_POS_CRC_VER_POS(roi->y1);
+	size = DCIF_CRC_SIZE_CRC_HOR_SIZE(roi->x2 - roi->x1) |
+	       DCIF_CRC_SIZE_CRC_VER_SIZE(roi->y2 - roi->y1);
+
+	regmap_write(dcif->regmap, DCIF_CRC_POS_R(ncrc), pos);
+	regmap_write(dcif->regmap, DCIF_CRC_SIZE_R(ncrc), size);
+
+	regmap_set_bits(dcif->regmap, DCIF_CRC_CTRL,
+			DCIF_CRC_CTRL_CRC_EN(ncrc) | DCIF_CRC_CTRL_CRC_ERR_CNT_RST);
+
+	return 0;
+}
+
+void dcif_crtc_enable_crc_source(struct dcif_dev *dcif,
+				 enum dcif_crc_source source,
+				 struct drm_rect *roi,
+				 int ncrc)
+{
+	if (ncrc >= MAX_DCIF_CRC_NUM)
+		return;
+
+	if (source == DCIF_CRC_SRC_NONE)
+		return;
+
+	if (dcif->crc_is_enabled)
+		return;
+
+	dcif_crc_config(dcif, roi, ncrc);
+
+	regmap_set_bits(dcif->regmap, DCIF_CRC_CTRL,
+			DCIF_CRC_CTRL_CRC_MODE | DCIF_CRC_CTRL_CRC_SHADOW_LOAD_EN |
+			DCIF_CRC_CTRL_CRC_TRIG);
+
+	dcif->crc_is_enabled = true;
+}
+
+void dcif_crtc_disable_crc_source(struct dcif_dev *dcif, int ncrc)
+{
+	if (!dcif->crc_is_enabled)
+		return;
+
+	if (ncrc >= MAX_DCIF_CRC_NUM)
+		return;
+
+	regmap_clear_bits(dcif->regmap, DCIF_CRC_CTRL, DCIF_CRC_CTRL_CRC_EN(ncrc));
+
+	dcif->crc_is_enabled = false;
+}
+
+/*
+ * Supported modes and source names:
+ * 1) auto mode:
+ *    "auto" should be selected as the source name.
+ *    The evaluation window is the same to the display region as
+ *    indicated by drm_crtc_state->adjusted_mode.
+ *
+ * 2) region of interest(ROI) mode:
+ *    "roi:x1,y1,x2,y2" should be selected as the source name.
+ *    The region of interest is defined by the inclusive upper left
+ *    position at (x1, y1) and the exclusive lower right position
+ *    at (x2, y2), see struct drm_rect for the same idea.
+ *    The evaluation window is the region of interest.
+ */
+static int
+dcif_crc_parse_source(const char *source_name, enum dcif_crc_source *s,
+		      struct drm_rect *roi)
+{
+	static const char roi_prefix[] = "roi:";
+
+	if (!source_name) {
+		*s = DCIF_CRC_SRC_NONE;
+	} else if (!strcmp(source_name, "auto")) {
+		*s = DCIF_CRC_SRC_FRAME;
+	} else if (strstarts(source_name, roi_prefix)) {
+		char *options, *opt;
+		int len = strlen(roi_prefix);
+		int params[4];
+		int i = 0, ret;
+
+		options = kstrdup(source_name + len, GFP_KERNEL);
+
+		while ((opt = strsep(&options, ",")) != NULL) {
+			if (i > 3)
+				return -EINVAL;
+
+			ret = kstrtouint(opt, 10, &params[i]);
+			if (ret < 0)
+				return ret;
+
+			if (params[i] < 0)
+				return -EINVAL;
+
+			i++;
+		}
+
+		if (i != 4)
+			return -EINVAL;
+
+		roi->x1 = params[0];
+		roi->y1 = params[1];
+		roi->x2 = params[2];
+		roi->y2 = params[3];
+
+		if (!drm_rect_visible(roi))
+			return -EINVAL;
+
+		*s = DCIF_CRC_SRC_FRAME_ROI;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int dcif_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
+				size_t *values_cnt)
+{
+	struct dcif_dev *dcif = crtc_to_dcif_dev(crtc);
+	enum dcif_crc_source source;
+	struct drm_rect roi;
+
+	if (dcif_crc_parse_source(source_name, &source, &roi) < 0) {
+		dev_dbg(dcif->drm.dev, "unknown source %s\n", source_name);
+		return -EINVAL;
+	}
+
+	*values_cnt = 1;
+
+	return 0;
+}
+
+int dcif_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name)
+{
+	struct dcif_dev *dcif = crtc_to_dcif_dev(crtc);
+	struct drm_modeset_acquire_ctx ctx;
+	struct drm_crtc_state *crtc_state;
+	struct drm_atomic_state *state;
+	struct drm_rect roi = {0, 0, 0, 0};
+	enum dcif_crc_source source;
+	int ret;
+
+	if (dcif_crc_parse_source(source_name, &source, &roi) < 0) {
+		dev_dbg(dcif->drm.dev, "unknown source %s\n", source_name);
+		return -EINVAL;
+	}
+
+	/* Perform an atomic commit to set the CRC source. */
+	drm_modeset_acquire_init(&ctx, 0);
+
+	state = drm_atomic_state_alloc(crtc->dev);
+	if (!state) {
+		ret = -ENOMEM;
+		goto unlock;
+	}
+
+	state->acquire_ctx = &ctx;
+
+retry:
+	crtc_state = drm_atomic_get_crtc_state(state, crtc);
+	if (!IS_ERR(crtc_state)) {
+		struct dcif_crtc_state *dcif_crtc_state;
+
+		dcif_crtc_state = to_dcif_crtc_state(crtc_state);
+
+		dcif_crtc_state->crc.source = source;
+		dcif_copy_roi(&roi, &dcif_crtc_state->crc.roi);
+
+		ret = drm_atomic_commit(state);
+	} else {
+		ret = PTR_ERR(crtc_state);
+	}
+
+	if (ret == -EDEADLK) {
+		drm_atomic_state_clear(state);
+		drm_modeset_backoff(&ctx);
+		goto retry;
+	}
+
+	drm_atomic_state_put(state);
+
+unlock:
+	drm_modeset_drop_locks(&ctx);
+	drm_modeset_acquire_fini(&ctx);
+
+	return ret;
+}
+
diff --git a/drivers/gpu/drm/imx/dcif/dcif-crc.h b/drivers/gpu/drm/imx/dcif/dcif-crc.h
new file mode 100644
index 0000000000000..ea1b18362a344
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcif/dcif-crc.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright 2025 NXP
+ */
+
+#ifndef __DCIF_CRC_H__
+#define __DCIF_CRC_H__
+
+#include <linux/types.h>
+
+#include "dcif-drv.h"
+
+static inline bool to_enable_dcif_crc(struct dcif_crtc_state *new_dcstate,
+				      struct dcif_crtc_state *old_dcstate)
+{
+	return old_dcstate->crc.source == DCIF_CRC_SRC_NONE &&
+	       new_dcstate->crc.source != DCIF_CRC_SRC_NONE;
+}
+
+static inline bool to_disable_dcif_crc(struct dcif_crtc_state *new_dcstate,
+				       struct dcif_crtc_state *old_dcstate)
+{
+	return old_dcstate->crc.source != DCIF_CRC_SRC_NONE &&
+	       new_dcstate->crc.source == DCIF_CRC_SRC_NONE;
+}
+
+static inline void dcif_copy_roi(struct drm_rect *from, struct drm_rect *to)
+{
+	to->x1 = from->x1;
+	to->y1 = from->y1;
+	to->x2 = from->x2;
+	to->y2 = from->y2;
+}
+
+#ifdef CONFIG_DEBUG_FS
+int dcif_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
+				size_t *values_cnt);
+int dcif_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name);
+void dcif_crtc_enable_crc_source(struct dcif_dev *dcif,
+				 enum dcif_crc_source source,
+				 struct drm_rect *roi,
+				 int nCRC);
+void dcif_crtc_disable_crc_source(struct dcif_dev *dcif, int nCRC);
+#else
+#define dcif_crtc_verify_crc_source	NULL
+#define dcif_crtc_set_crc_source	NULL
+#define dcif_crtc_enable_crc_source	NULL
+#define dcif_crtc_disable_crc_source	NULL
+#endif
+
+#endif /* __DCIF_CRC_H__ */
diff --git a/drivers/gpu/drm/imx/dcif/dcif-crtc.c b/drivers/gpu/drm/imx/dcif/dcif-crtc.c
new file mode 100644
index 0000000000000..cf92498b343af
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcif/dcif-crtc.c
@@ -0,0 +1,696 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/media-bus-format.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+
+#include "dcif-crc.h"
+#include "dcif-drv.h"
+#include "dcif-reg.h"
+
+#define DCIF_MAX_PIXEL_CLOCK		148500000
+
+/* -----------------------------------------------------------------------------
+ * CRTC
+ */
+
+/*
+ * For conversion from YCbCr to RGB, the CSC operates as follows:
+ *
+ * |R|   |A1 A2 A3|   |Y  + D1|
+ * |G| = |B1 B2 B3| * |Cb + D2|
+ * |B|   |C1 C2 C3|   |Cr + D3|
+ *
+ * The A, B and C coefficients are expressed as signed Q3.8 fixed point values and
+ * the D coefficients as signed Q9.0.
+ */
+static const u32 dcif_yuv2rgb_coeffs[3][2][6] = {
+	[DRM_COLOR_YCBCR_BT601] = {
+		[DRM_COLOR_YCBCR_LIMITED_RANGE] = {
+			/*
+			 * BT.601 limited range:
+			 *
+			 * |R|   |1.1644  0.0000  1.5960|   |Y  - 16 |
+			 * |G| = |1.1644 -0.3917 -0.8129| * |Cb - 128|
+			 * |B|   |1.1644  2.0172  0.0000|   |Cr - 128|
+			 */
+			DCIF_CSC_COEF0_L0_A1(0x12a) | DCIF_CSC_COEF0_L0_A2(0x000),
+			DCIF_CSC_COEF1_L0_A3(0x199) | DCIF_CSC_COEF1_L0_B1(0x12a),
+			DCIF_CSC_COEF2_L0_B2(0x79c) | DCIF_CSC_COEF2_L0_B3(0x730),
+			DCIF_CSC_COEF3_L0_C1(0x12a) | DCIF_CSC_COEF3_L0_C2(0x204),
+			DCIF_CSC_COEF4_L0_C3(0x000) | DCIF_CSC_COEF4_L0_D1(0x1f0),
+			DCIF_CSC_COEF5_L0_D2(0x180) | DCIF_CSC_COEF5_L0_D3(0x180),
+		},
+		[DRM_COLOR_YCBCR_FULL_RANGE] = {
+			/*
+			 * BT.601 full range:
+			 *
+			 * |R|   |1.0000  0.0000  1.4020|   |Y  - 0  |
+			 * |G| = |1.0000 -0.3441 -0.7141| * |Cb - 128|
+			 * |B|   |1.0000  1.7720  0.0000|   |Cr - 128|
+			 */
+			DCIF_CSC_COEF0_L0_A1(0x100) | DCIF_CSC_COEF0_L0_A2(0x000),
+			DCIF_CSC_COEF1_L0_A3(0x167) | DCIF_CSC_COEF1_L0_B1(0x100),
+			DCIF_CSC_COEF2_L0_B2(0x7a8) | DCIF_CSC_COEF2_L0_B3(0x749),
+			DCIF_CSC_COEF3_L0_C1(0x100) | DCIF_CSC_COEF3_L0_C2(0x1c6),
+			DCIF_CSC_COEF4_L0_C3(0x000) | DCIF_CSC_COEF4_L0_D1(0x000),
+			DCIF_CSC_COEF5_L0_D2(0x180) | DCIF_CSC_COEF5_L0_D3(0x180),
+		},
+	},
+	[DRM_COLOR_YCBCR_BT709] = {
+		[DRM_COLOR_YCBCR_LIMITED_RANGE] = {
+			/*
+			 * Rec.709 limited range:
+			 *
+			 * |R|   |1.1644  0.0000  1.7927|   |Y  - 16 |
+			 * |G| = |1.1644 -0.2132 -0.5329| * |Cb - 128|
+			 * |B|   |1.1644  2.1124  0.0000|   |Cr - 128|
+			 */
+			DCIF_CSC_COEF0_L0_A1(0x12a) | DCIF_CSC_COEF0_L0_A2(0x000),
+			DCIF_CSC_COEF1_L0_A3(0x1cb) | DCIF_CSC_COEF1_L0_B1(0x12a),
+			DCIF_CSC_COEF2_L0_B2(0x7c9) | DCIF_CSC_COEF2_L0_B3(0x778),
+			DCIF_CSC_COEF3_L0_C1(0x12a) | DCIF_CSC_COEF3_L0_C2(0x21d),
+			DCIF_CSC_COEF4_L0_C3(0x000) | DCIF_CSC_COEF4_L0_D1(0x1f0),
+			DCIF_CSC_COEF5_L0_D2(0x180) | DCIF_CSC_COEF5_L0_D3(0x180),
+		},
+		[DRM_COLOR_YCBCR_FULL_RANGE] = {
+			/*
+			 * Rec.709 full range:
+			 *
+			 * |R|   |1.0000  0.0000  1.5748|   |Y  - 0  |
+			 * |G| = |1.0000 -0.1873 -0.4681| * |Cb - 128|
+			 * |B|   |1.0000  1.8556  0.0000|   |Cr - 128|
+			 */
+			DCIF_CSC_COEF0_L0_A1(0x100) | DCIF_CSC_COEF0_L0_A2(0x000),
+			DCIF_CSC_COEF1_L0_A3(0x193) | DCIF_CSC_COEF1_L0_B1(0x100),
+			DCIF_CSC_COEF2_L0_B2(0x7d0) | DCIF_CSC_COEF2_L0_B3(0x788),
+			DCIF_CSC_COEF3_L0_C1(0x100) | DCIF_CSC_COEF3_L0_C2(0x1db),
+			DCIF_CSC_COEF4_L0_C3(0x000) | DCIF_CSC_COEF4_L0_D1(0x000),
+			DCIF_CSC_COEF5_L0_D2(0x180) | DCIF_CSC_COEF5_L0_D3(0x180),
+		},
+	},
+	[DRM_COLOR_YCBCR_BT2020] = {
+		[DRM_COLOR_YCBCR_LIMITED_RANGE] = {
+			/*
+			 * BT.2020 limited range:
+			 *
+			 * |R|   |1.1644  0.0000  1.6787|   |Y  - 16 |
+			 * |G| = |1.1644 -0.1874 -0.6505| * |Cb - 128|
+			 * |B|   |1.1644  2.1418  0.0000|   |Cr - 128|
+			 */
+			DCIF_CSC_COEF0_L0_A1(0x12a) | DCIF_CSC_COEF0_L0_A2(0x000),
+			DCIF_CSC_COEF1_L0_A3(0x1ae) | DCIF_CSC_COEF1_L0_B1(0x12a),
+			DCIF_CSC_COEF2_L0_B2(0x7d0) | DCIF_CSC_COEF2_L0_B3(0x759),
+			DCIF_CSC_COEF3_L0_C1(0x12a) | DCIF_CSC_COEF3_L0_C2(0x224),
+			DCIF_CSC_COEF4_L0_C3(0x000) | DCIF_CSC_COEF4_L0_D1(0x1f0),
+			DCIF_CSC_COEF5_L0_D2(0x180) | DCIF_CSC_COEF5_L0_D3(0x180),
+		},
+		[DRM_COLOR_YCBCR_FULL_RANGE] = {
+			/*
+			 * BT.2020 full range:
+			 *
+			 * |R|   |1.0000  0.0000  1.4746|   |Y  - 0  |
+			 * |G| = |1.0000 -0.1646 -0.5714| * |Cb - 128|
+			 * |B|   |1.0000  1.8814  0.0000|   |Cr - 128|
+			 */
+			DCIF_CSC_COEF0_L0_A1(0x100) | DCIF_CSC_COEF0_L0_A2(0x000),
+			DCIF_CSC_COEF1_L0_A3(0x179) | DCIF_CSC_COEF1_L0_B1(0x100),
+			DCIF_CSC_COEF2_L0_B2(0x7d6) | DCIF_CSC_COEF2_L0_B3(0x76e),
+			DCIF_CSC_COEF3_L0_C1(0x100) | DCIF_CSC_COEF3_L0_C2(0x1e2),
+			DCIF_CSC_COEF4_L0_C3(0x000) | DCIF_CSC_COEF4_L0_D1(0x000),
+			DCIF_CSC_COEF5_L0_D2(0x180) | DCIF_CSC_COEF5_L0_D3(0x180),
+		},
+	},
+};
+
+static enum drm_mode_status dcif_crtc_mode_valid(struct drm_crtc *crtc,
+						 const struct drm_display_mode *mode)
+{
+	if (mode->crtc_clock > DCIF_MAX_PIXEL_CLOCK)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
+static void dcif_set_formats(struct dcif_dev *dcif, struct drm_plane_state *plane_state,
+			     const u32 bus_format)
+{
+	const u32 format = plane_state->fb->format->format;
+	struct drm_device *drm = &dcif->drm;
+	bool in_yuv = false;
+	u32 reg = 0;
+
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_RGB565_1X16:
+		reg |= DCIF_DPI_CTRL_DATA_PATTERN(PATTERN_RGB565);
+		break;
+	case MEDIA_BUS_FMT_RGB888_1X24:
+		reg |= DCIF_DPI_CTRL_DATA_PATTERN(PATTERN_RGB888);
+		break;
+	case MEDIA_BUS_FMT_RBG888_1X24:
+		reg |= DCIF_DPI_CTRL_DATA_PATTERN(PATTERN_RBG888);
+		break;
+	case MEDIA_BUS_FMT_BGR888_1X24:
+		reg |= DCIF_DPI_CTRL_DATA_PATTERN(PATTERN_BGR888);
+		break;
+	case MEDIA_BUS_FMT_GBR888_1X24:
+		reg |= DCIF_DPI_CTRL_DATA_PATTERN(PATTERN_GBR888);
+		break;
+	default:
+		dev_err(drm->dev, "Unknown media bus format 0x%x\n", bus_format);
+		break;
+	}
+
+	regmap_update_bits(dcif->regmap, DCIF_DPI_CTRL, DCIF_DPI_CTRL_DATA_PATTERN_MASK, reg);
+
+	reg = 0;
+	switch (format) {
+	/* RGB Formats */
+	case DRM_FORMAT_RGB565:
+		reg |= DCIF_CTRLDESC0_FORMAT(CTRLDESCL0_FORMAT_RGB565);
+		break;
+	case DRM_FORMAT_RGB888:
+		reg |= DCIF_CTRLDESC0_FORMAT(CTRLDESCL0_FORMAT_RGB888);
+		break;
+	case DRM_FORMAT_XRGB1555:
+		reg |= DCIF_CTRLDESC0_FORMAT(CTRLDESCL0_FORMAT_ARGB1555);
+		break;
+	case DRM_FORMAT_XRGB4444:
+		reg |= DCIF_CTRLDESC0_FORMAT(CTRLDESCL0_FORMAT_ARGB4444);
+		break;
+	case DRM_FORMAT_XBGR8888:
+		reg |= DCIF_CTRLDESC0_FORMAT(CTRLDESCL0_FORMAT_ABGR8888);
+		break;
+	case DRM_FORMAT_XRGB8888:
+		reg |= DCIF_CTRLDESC0_FORMAT(CTRLDESCL0_FORMAT_ARGB8888);
+		break;
+
+	/* YUV Formats */
+	case DRM_FORMAT_YUYV:
+		reg |= DCIF_CTRLDESC0_FORMAT(CTRLDESCL0_FORMAT_YCBCR422) |
+		       DCIF_CTRLDESC0_YUV_FORMAT(CTRLDESCL0_YUV_FORMAT_VY2UY1);
+		in_yuv = true;
+		break;
+	case DRM_FORMAT_YVYU:
+		reg |= DCIF_CTRLDESC0_FORMAT(CTRLDESCL0_FORMAT_YCBCR422) |
+		       DCIF_CTRLDESC0_YUV_FORMAT(CTRLDESCL0_YUV_FORMAT_UY2VY1);
+		in_yuv = true;
+		break;
+	case DRM_FORMAT_UYVY:
+		reg |= DCIF_CTRLDESC0_FORMAT(CTRLDESCL0_FORMAT_YCBCR422) |
+		       DCIF_CTRLDESC0_YUV_FORMAT(CTRLDESCL0_YUV_FORMAT_Y2VY1U);
+		in_yuv = true;
+		break;
+	case DRM_FORMAT_VYUY:
+		reg |= DCIF_CTRLDESC0_FORMAT(CTRLDESCL0_FORMAT_YCBCR422) |
+		       DCIF_CTRLDESC0_YUV_FORMAT(CTRLDESCL0_YUV_FORMAT_Y2UY1V);
+		in_yuv = true;
+		break;
+
+	default:
+		dev_err(drm->dev, "Unknown pixel format 0x%x\n", format);
+		break;
+	}
+
+	regmap_update_bits(dcif->regmap, DCIF_CTRLDESC0(0),
+			   DCIF_CTRLDESC0_FORMAT_MASK | DCIF_CTRLDESC0_YUV_FORMAT_MASK,
+			   reg);
+
+	if (in_yuv) {
+		/* Enable CSC YCbCr -> RGB */
+		const u32 *coeffs =
+			dcif_yuv2rgb_coeffs[plane_state->color_encoding][plane_state->color_range];
+
+		regmap_bulk_write(dcif->regmap, DCIF_CSC_COEF0_L0, coeffs, 6);
+
+		regmap_write(dcif->regmap, DCIF_CSC_CTRL_L0,
+			     DCIF_CSC_CTRL_L0_CSC_EN |
+			     DCIF_CSC_CTRL_L0_CSC_MODE_YCBCR2RGB);
+	} else {
+		regmap_write(dcif->regmap, DCIF_CSC_CTRL_L0, 0);
+	}
+}
+
+static void dcif_set_mode(struct dcif_dev *dcif, u32 bus_flags)
+{
+	struct drm_display_mode *m = &dcif->crtc.state->adjusted_mode;
+	u32 reg = 0;
+
+	if (m->flags & DRM_MODE_FLAG_NHSYNC)
+		reg |= DCIF_DPI_CTRL_HSYNC_POL_LOW;
+	if (m->flags & DRM_MODE_FLAG_NVSYNC)
+		reg |= DCIF_DPI_CTRL_VSYNC_POL_LOW;
+	if (bus_flags & DRM_BUS_FLAG_DE_LOW)
+		reg |= DCIF_DPI_CTRL_DE_POL_LOW;
+	if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
+		reg |= DCIF_DPI_CTRL_PCLK_EDGE_FALLING;
+
+	regmap_update_bits(dcif->regmap, DCIF_DPI_CTRL, DCIF_DPI_CTRL_POL_MASK, reg);
+
+	/* config display timings */
+	reg = DCIF_DISP_SIZE_DISP_WIDTH(m->hdisplay) |
+	      DCIF_DISP_SIZE_DISP_HEIGHT(m->vdisplay);
+	regmap_write(dcif->regmap, DCIF_DISP_SIZE, reg);
+
+	reg = DCIF_DPI_HSYN_PAR_BP_H(m->htotal - m->hsync_end) |
+	      DCIF_DPI_HSYN_PAR_FP_H(m->hsync_start - m->hdisplay);
+	regmap_write(dcif->regmap, DCIF_DPI_HSYN_PAR, reg);
+
+	reg = DCIF_DPI_VSYN_PAR_BP_V(m->vtotal - m->vsync_end) |
+	      DCIF_DPI_VSYN_PAR_FP_V(m->vsync_start - m->vdisplay);
+	regmap_write(dcif->regmap, DCIF_DPI_VSYN_PAR, reg);
+
+	reg = DCIF_DPI_VSYN_HSYN_WIDTH_PW_V(m->vsync_end - m->vsync_start) |
+	      DCIF_DPI_VSYN_HSYN_WIDTH_PW_H(m->hsync_end - m->hsync_start);
+	regmap_write(dcif->regmap, DCIF_DPI_VSYN_HSYN_WIDTH, reg);
+
+	/* Layer 0 frame size */
+	reg = DCIF_CTRLDESC2_HEIGHT(m->vdisplay) |
+	      DCIF_CTRLDESC2_WIDTH(m->hdisplay);
+	regmap_write(dcif->regmap, DCIF_CTRLDESC2(0), reg);
+
+	/*
+	 * Configure P_SIZE, T_SIZE and pitch
+	 * 1. P_SIZE and T_SIZE should never be less than AXI bus width.
+	 * 2. P_SIZE should never be less than T_SIZE.
+	 */
+	reg = DCIF_CTRLDESC3_P_SIZE(2) | DCIF_CTRLDESC3_T_SIZE(2) |
+	      DCIF_CTRLDESC3_PITCH(dcif->crtc.primary->state->fb->pitches[0]);
+	regmap_write(dcif->regmap, DCIF_CTRLDESC3(0), reg);
+}
+
+static void dcif_enable_plane_panic(struct dcif_dev *dcif)
+{
+	u32 reg;
+
+	/* Set FIFO Panic watermarks, low 1/3, high 2/3. */
+	reg = DCIF_PANIC_THRES_LOW(1 * PANIC0_THRES_MAX / 3) |
+	      DCIF_PANIC_THRES_HIGH(2 * PANIC0_THRES_MAX / 3) |
+	      DCIF_PANIC_THRES_REQ_EN;
+	regmap_write(dcif->regmap, DCIF_PANIC_THRES(0), reg);
+	regmap_write(dcif->regmap, DCIF_PANIC_THRES(1), reg);
+
+	regmap_set_bits(dcif->regmap, DCIF_IE1(dcif->cpu_domain),
+			DCIF_INT1_FIFO_PANIC0 | DCIF_INT1_FIFO_PANIC1);
+}
+
+static void dcif_disable_plane_panic(struct dcif_dev *dcif)
+{
+	regmap_clear_bits(dcif->regmap, DCIF_IE1(dcif->cpu_domain),
+			  DCIF_INT1_FIFO_PANIC0 | DCIF_INT1_FIFO_PANIC1);
+	regmap_clear_bits(dcif->regmap, DCIF_PANIC_THRES(0), DCIF_PANIC_THRES_REQ_EN);
+	regmap_clear_bits(dcif->regmap, DCIF_PANIC_THRES(1), DCIF_PANIC_THRES_REQ_EN);
+}
+
+static void dcif_enable_controller(struct dcif_dev *dcif)
+{
+	/* Enable Display */
+	regmap_set_bits(dcif->regmap, DCIF_DISP_CTRL, DCIF_DISP_CTRL_DISP_ON);
+
+	/* Enable layer 0 */
+	regmap_set_bits(dcif->regmap, DCIF_CTRLDESC0(0), DCIF_CTRLDESC0_EN);
+}
+
+static void dcif_disable_controller(struct dcif_dev *dcif)
+{
+	u32 reg;
+	int ret;
+
+	/* Disable layer 0 */
+	regmap_clear_bits(dcif->regmap, DCIF_CTRLDESC0(0), DCIF_CTRLDESC0_EN);
+
+	ret = regmap_read_poll_timeout(dcif->regmap, DCIF_CTRLDESC0(0), reg,
+				       !(reg & DCIF_CTRLDESC0_EN), 0,
+				       36000); /* Wait ~2 frame times max */
+	if (ret)
+		drm_err(&dcif->drm, "Failed to disable controller!\n");
+
+	/* Disable Display */
+	regmap_clear_bits(dcif->regmap, DCIF_DISP_CTRL, DCIF_DISP_CTRL_DISP_ON);
+}
+
+static void dcif_shadow_load_enable(struct dcif_dev *dcif)
+{
+	regmap_write_bits(dcif->regmap, DCIF_CTRLDESC0(0), DCIF_CTRLDESC0_SHADOW_LOAD_EN,
+			  DCIF_CTRLDESC0_SHADOW_LOAD_EN);
+}
+
+static void dcif_reset_block(struct dcif_dev *dcif)
+{
+	regmap_set_bits(dcif->regmap, DCIF_DISP_CTRL, DCIF_DISP_CTRL_SW_RST);
+
+	regmap_clear_bits(dcif->regmap, DCIF_DISP_CTRL, DCIF_DISP_CTRL_SW_RST);
+}
+
+static void dcif_crtc_atomic_destroy_state(struct drm_crtc *crtc,
+					   struct drm_crtc_state *state)
+{
+	__drm_atomic_helper_crtc_destroy_state(state);
+	kfree(to_dcif_crtc_state(state));
+}
+
+static void dcif_crtc_reset(struct drm_crtc *crtc)
+{
+	struct dcif_crtc_state *state;
+
+	if (crtc->state)
+		dcif_crtc_atomic_destroy_state(crtc, crtc->state);
+
+	crtc->state = NULL;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (state)
+		__drm_atomic_helper_crtc_reset(crtc, &state->base);
+}
+
+static struct drm_crtc_state *dcif_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
+{
+	struct dcif_crtc_state *old = to_dcif_crtc_state(crtc->state);
+	struct dcif_crtc_state *new;
+
+	if (WARN_ON(!crtc->state))
+		return NULL;
+
+	new = kzalloc(sizeof(*new), GFP_KERNEL);
+	if (!new)
+		return NULL;
+
+	__drm_atomic_helper_crtc_duplicate_state(crtc, &new->base);
+
+	new->bus_format = old->bus_format;
+	new->bus_flags = old->bus_flags;
+	new->crc.source = old->crc.source;
+	dcif_copy_roi(&old->crc.roi, &new->crc.roi);
+
+	return &new->base;
+}
+
+static void dcif_crtc_mode_set_nofb(struct drm_crtc_state *crtc_state,
+				    struct drm_plane_state *plane_state)
+{
+	struct dcif_crtc_state *dcif_crtc_state = to_dcif_crtc_state(crtc_state);
+	struct drm_device *drm = crtc_state->crtc->dev;
+	struct dcif_dev *dcif = crtc_to_dcif_dev(crtc_state->crtc);
+	struct drm_display_mode *m = &crtc_state->adjusted_mode;
+
+	dev_dbg(drm->dev, "Pixel clock: %dkHz\n", m->crtc_clock);
+	dev_dbg(drm->dev, "Bridge bus_flags: 0x%08X\n", dcif_crtc_state->bus_flags);
+	dev_dbg(drm->dev, "Mode flags: 0x%08X\n", m->flags);
+
+	dcif_reset_block(dcif);
+
+	dcif_set_formats(dcif, plane_state, dcif_crtc_state->bus_format);
+
+	dcif_set_mode(dcif, dcif_crtc_state->bus_flags);
+}
+
+static void dcif_crtc_queue_state_event(struct drm_crtc *crtc)
+{
+	struct dcif_dev *dcif = crtc_to_dcif_dev(crtc);
+
+	spin_lock_irq(&crtc->dev->event_lock);
+	if (crtc->state->event) {
+		WARN_ON(drm_crtc_vblank_get(crtc));
+		WARN_ON(dcif->event);
+		dcif->event = crtc->state->event;
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&crtc->dev->event_lock);
+}
+
+static struct drm_bridge *dcif_crtc_get_bridge(struct drm_crtc *crtc,
+					       struct drm_crtc_state *crtc_state)
+{
+	struct drm_connector_state *conn_state;
+	struct drm_encoder *encoder;
+	struct drm_connector *conn;
+	struct drm_bridge *bridge;
+	int i;
+
+	for_each_new_connector_in_state(crtc_state->state, conn, conn_state, i) {
+		if (crtc != conn_state->crtc)
+			continue;
+
+		encoder = conn_state->best_encoder;
+
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
+		if (bridge)
+			return bridge;
+	}
+
+	return NULL;
+}
+
+static void dcif_crtc_query_output_bus_format(struct drm_crtc *crtc,
+					      struct drm_crtc_state *crtc_state)
+{
+	struct dcif_crtc_state *dcif_state = to_dcif_crtc_state(crtc_state);
+	struct drm_bridge_state *bridge_state;
+	struct drm_bridge *bridge;
+
+	dcif_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+	dcif_state->bus_flags = 0;
+
+	bridge = dcif_crtc_get_bridge(crtc, crtc_state);
+	if (!bridge)
+		return;
+
+	bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, bridge);
+	if (!bridge_state)
+		return;
+
+	dcif_state->bus_format = bridge_state->input_bus_cfg.format;
+	dcif_state->bus_flags = bridge_state->input_bus_cfg.flags;
+}
+
+static int dcif_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
+{
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	bool enable_primary = crtc_state->plane_mask & drm_plane_mask(crtc->primary);
+	int ret;
+
+	if (crtc_state->active && !enable_primary)
+		return -EINVAL;
+
+	dcif_crtc_query_output_bus_format(crtc, crtc_state);
+
+	if (crtc_state->active_changed && crtc_state->active) {
+		if (!crtc_state->mode_changed) {
+			crtc_state->mode_changed = true;
+			ret = drm_atomic_helper_check_modeset(crtc->dev, state);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void dcif_crtc_atomic_flush(struct drm_crtc *crtc,
+				   struct drm_atomic_state *state)
+{
+	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
+	struct dcif_crtc_state *old_dcif_crtc_state = to_dcif_crtc_state(old_crtc_state);
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	struct dcif_crtc_state *dcif_crtc_state = to_dcif_crtc_state(crtc_state);
+	bool need_modeset = drm_atomic_crtc_needs_modeset(crtc->state);
+	struct dcif_dev *dcif = crtc_to_dcif_dev(crtc);
+
+	dcif_shadow_load_enable(dcif);
+
+	if (!crtc->state->active && !old_crtc_state->active)
+		return;
+
+	if (!need_modeset && to_disable_dcif_crc(dcif_crtc_state, old_dcif_crtc_state))
+		dcif_crtc_disable_crc_source(dcif, 0);
+
+	if (!need_modeset)
+		dcif_crtc_queue_state_event(crtc);
+
+	if (!need_modeset && to_enable_dcif_crc(dcif_crtc_state, old_dcif_crtc_state))
+		dcif_crtc_enable_crc_source(dcif, dcif_crtc_state->crc.source,
+					    &dcif_crtc_state->crc.roi, 0);
+}
+
+static void dcif_crtc_atomic_enable(struct drm_crtc *crtc,
+				    struct drm_atomic_state *state)
+{
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, crtc->primary);
+	struct dcif_crtc_state *dcif_crtc_state = to_dcif_crtc_state(crtc_state);
+	struct drm_display_mode *adj = &crtc_state->adjusted_mode;
+	struct dcif_dev *dcif = crtc_to_dcif_dev(crtc);
+	struct drm_device *drm = crtc->dev;
+	dma_addr_t baseaddr;
+
+	dev_dbg(drm->dev, "mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(adj));
+
+	/* enable power when we start to set mode for CRTC */
+	pm_runtime_get_sync(drm->dev);
+
+	drm_crtc_vblank_on(crtc);
+
+	dcif_crtc_mode_set_nofb(crtc_state, plane_state);
+
+	baseaddr = drm_fb_dma_get_gem_addr(plane_state->fb, plane_state, 0);
+	if (baseaddr)
+		regmap_write(dcif->regmap, DCIF_CTRLDESC4(0), baseaddr);
+
+	dcif_enable_plane_panic(dcif);
+	dcif_enable_controller(dcif);
+
+	dcif_crtc_queue_state_event(crtc);
+
+	if (dcif->has_crc && dcif_crtc_state->crc.source != DCIF_CRC_SRC_NONE)
+		dcif_crtc_enable_crc_source(dcif, dcif_crtc_state->crc.source,
+					    &dcif_crtc_state->crc.roi, 0);
+}
+
+static void dcif_crtc_atomic_disable(struct drm_crtc *crtc,
+				     struct drm_atomic_state *state)
+{
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	struct dcif_crtc_state *dcif_crtc_state = to_dcif_crtc_state(crtc_state);
+	struct dcif_dev *dcif = crtc_to_dcif_dev(crtc);
+	struct drm_device *drm = crtc->dev;
+
+	if (dcif->has_crc && dcif_crtc_state->crc.source != DCIF_CRC_SRC_NONE)
+		dcif_crtc_disable_crc_source(dcif, 0);
+
+	dcif_disable_controller(dcif);
+	dcif_disable_plane_panic(dcif);
+
+	drm_crtc_vblank_off(crtc);
+
+	pm_runtime_put_sync(drm->dev);
+
+	spin_lock_irq(&crtc->dev->event_lock);
+	if (crtc->state->event && !crtc->state->active) {
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&crtc->dev->event_lock);
+}
+
+static const struct drm_crtc_helper_funcs dcif_crtc_helper_funcs = {
+	.mode_valid	= dcif_crtc_mode_valid,
+	.atomic_check	= dcif_crtc_atomic_check,
+	.atomic_flush	= dcif_crtc_atomic_flush,
+	.atomic_enable	= dcif_crtc_atomic_enable,
+	.atomic_disable	= dcif_crtc_atomic_disable,
+};
+
+static int dcif_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct dcif_dev *dcif = crtc_to_dcif_dev(crtc);
+	int domain = dcif->cpu_domain;
+
+	/* Clear and enable VS_BLANK IRQ */
+	regmap_set_bits(dcif->regmap, DCIF_IS0(domain), DCIF_INT0_VS_BLANK);
+	regmap_set_bits(dcif->regmap, DCIF_IE0(domain), DCIF_INT0_VS_BLANK);
+
+	return 0;
+}
+
+static void dcif_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct dcif_dev *dcif = crtc_to_dcif_dev(crtc);
+	int domain = dcif->cpu_domain;
+
+	/* Disable and clear VS_BLANK IRQ */
+	regmap_clear_bits(dcif->regmap, DCIF_IE0(domain), DCIF_INT0_VS_BLANK);
+	regmap_clear_bits(dcif->regmap, DCIF_IS0(domain), DCIF_INT0_VS_BLANK);
+}
+
+static const struct drm_crtc_funcs dcif_crtc_funcs = {
+	.reset			= dcif_crtc_reset,
+	.destroy		= drm_crtc_cleanup,
+	.set_config		= drm_atomic_helper_set_config,
+	.page_flip		= drm_atomic_helper_page_flip,
+	.atomic_duplicate_state	= dcif_crtc_atomic_duplicate_state,
+	.atomic_destroy_state	= dcif_crtc_atomic_destroy_state,
+	.enable_vblank		= dcif_crtc_enable_vblank,
+	.disable_vblank		= dcif_crtc_disable_vblank,
+	.set_crc_source		= dcif_crtc_set_crc_source,
+	.verify_crc_source	= dcif_crtc_verify_crc_source,
+};
+
+irqreturn_t dcif_irq_handler(int irq, void *data)
+{
+	struct drm_device *drm = data;
+	struct dcif_dev *dcif = to_dcif_dev(drm);
+	int domain = dcif->cpu_domain;
+	unsigned long flags;
+	u32 stat0, stat1, crc;
+
+	regmap_read(dcif->regmap, DCIF_IS0(domain), &stat0);
+	regmap_read(dcif->regmap, DCIF_IS1(domain), &stat1);
+
+	if (stat0 & DCIF_INT0_VS_BLANK) {
+		drm_crtc_handle_vblank(&dcif->crtc);
+
+		spin_lock_irqsave(&drm->event_lock, flags);
+		if (dcif->event) {
+			drm_crtc_send_vblank_event(&dcif->crtc, dcif->event);
+			dcif->event = NULL;
+			drm_crtc_vblank_put(&dcif->crtc);
+		}
+		if (dcif->crc_is_enabled) {
+			regmap_read(dcif->regmap, DCIF_CRC_VAL_R(0), &crc);
+			drm_crtc_add_crc_entry(&dcif->crtc, false, 0, &crc);
+			dev_dbg(drm->dev, "crc=0x%x\n",  crc);
+		}
+		spin_unlock_irqrestore(&drm->event_lock, flags);
+	}
+
+	if (stat1 & (DCIF_INT1_FIFO_PANIC0 | DCIF_INT1_FIFO_PANIC1)) {
+		u32 panic = stat1 & (DCIF_INT1_FIFO_PANIC0 | DCIF_INT1_FIFO_PANIC1);
+
+		dev_dbg_ratelimited(drm->dev, "FIFO panic on %s\n",
+				    panic == (DCIF_INT1_FIFO_PANIC0 | DCIF_INT1_FIFO_PANIC1) ?
+				    "layers 0 & 1" : panic == DCIF_INT1_FIFO_PANIC0 ? "layer 0" :
+				    "layer 1");
+	}
+
+	/* W1C */
+	regmap_write(dcif->regmap, DCIF_IS0(domain), stat0);
+	regmap_write(dcif->regmap, DCIF_IS1(domain), stat1);
+
+	return IRQ_HANDLED;
+}
+
+int dcif_crtc_init(struct dcif_dev *dcif)
+{
+	int ret;
+
+	ret = dcif_plane_init(dcif);
+	if (ret)
+		return ret;
+
+	drm_crtc_helper_add(&dcif->crtc, &dcif_crtc_helper_funcs);
+	ret = drm_crtc_init_with_planes(&dcif->drm, &dcif->crtc, &dcif->planes.primary, NULL,
+					&dcif_crtc_funcs, NULL);
+	if (ret) {
+		drm_err(&dcif->drm, "failed to initialize CRTC: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/imx/dcif/dcif-drv.c b/drivers/gpu/drm/imx/dcif/dcif-drv.c
new file mode 100644
index 0000000000000..95fa5bf227ff5
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcif/dcif-drv.c
@@ -0,0 +1,293 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+
+#include <drm/clients/drm_client_setup.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fbdev_dma.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_modeset_helper.h>
+#include <drm/drm_probe_helper.h>
+
+#include "dcif-drv.h"
+#include "dcif-reg.h"
+
+#define QOS_SETTING			0x1c
+#define  DISPLAY_PANIC_QOS_MASK		0x70
+#define  DISPLAY_PANIC_QOS(n)		(((n) & 0x7) << 4)
+#define  DISPLAY_ARQOS_MASK		0x7
+#define  DISPLAY_ARQOS(n)		((n) & 0x7)
+
+#define DCIF_CPU_DOMAIN			2
+
+#define DRIVER_NAME			"imx-dcif-drm"
+
+DEFINE_DRM_GEM_DMA_FOPS(dcif_driver_fops);
+
+static struct drm_driver dcif_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
+	DRM_GEM_DMA_DRIVER_OPS,
+	DRM_FBDEV_DMA_DRIVER_OPS,
+	.fops			= &dcif_driver_fops,
+	.name			= "imx-dcif",
+	.desc			= "i.MX DCIF DRM graphics",
+	.major			= 1,
+	.minor			= 0,
+	.patchlevel		= 0,
+};
+
+static int dcif_set_qos(struct dcif_dev *dcif)
+{
+	struct drm_device *drm = &dcif->drm;
+	int ret;
+
+	ret = regmap_update_bits(dcif->blkctrl_regmap, QOS_SETTING,
+				 DISPLAY_PANIC_QOS_MASK | DISPLAY_ARQOS_MASK,
+				 DISPLAY_PANIC_QOS(0x3) | DISPLAY_ARQOS(0x3));
+	if (ret < 0)
+		dev_err(drm->dev, "failed to set QoS: %d\n", ret);
+
+	return ret;
+}
+
+static void dcif_read_chip_info(struct dcif_dev *dcif)
+{
+	struct drm_device *drm = &dcif->drm;
+	u32 val, vmin, vmaj;
+
+	pm_runtime_get_sync(drm->dev);
+
+	regmap_read(dcif->regmap, DCIF_VER, &val);
+
+	dcif->has_crc = val & 0x2;
+
+	vmin = DCIF_VER_GET_MINOR(val);
+	vmaj = DCIF_VER_GET_MAJOR(val);
+	DRM_DEV_DEBUG(drm->dev, "DCIF version is %d.%d\n", vmaj, vmin);
+
+	pm_runtime_put_sync(drm->dev);
+}
+
+static const struct regmap_config dcif_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.fast_io = true,
+	.max_register = 0x20250,
+	.cache_type = REGCACHE_NONE,
+	.disable_locking = true,
+};
+
+static const char * const dcif_clks[] = {
+	"apb",
+	"axi",
+	"pix",
+};
+
+static int dcif_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct dcif_dev *dcif;
+	struct drm_device *drm;
+	int ret;
+	int i;
+
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	dcif = devm_drm_dev_alloc(&pdev->dev, &dcif_driver, struct dcif_dev, drm);
+	if (IS_ERR(dcif))
+		return PTR_ERR(dcif);
+
+	/* CPU 0 domain for interrupt control */
+	dcif->cpu_domain = DCIF_CPU_DOMAIN;
+
+	drm = &dcif->drm;
+	dev_set_drvdata(&pdev->dev, dcif);
+
+	dcif->reg_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(dcif->reg_base))
+		return dev_err_probe(drm->dev, PTR_ERR(dcif->reg_base),
+				     "failed to get reg base\n");
+
+	dcif->irq[0] = platform_get_irq(pdev, 0);
+	if (dcif->irq[0] < 0)
+		return dev_err_probe(drm->dev, dcif->irq[0],
+				     "failed to get domain0 irq\n");
+
+	dcif->irq[1] = platform_get_irq(pdev, 1);
+	if (dcif->irq[1] < 0)
+		return dev_err_probe(drm->dev, dcif->irq[1],
+				     "failed to get domain1 irq\n");
+
+	dcif->irq[2] = platform_get_irq(pdev, 2);
+	if (dcif->irq[2] < 0)
+		return dev_err_probe(drm->dev, dcif->irq[2],
+				     "failed to get domain2 irq\n");
+
+	dcif->blkctrl_regmap = syscon_regmap_lookup_by_phandle(np, "nxp,blk-ctrl");
+	if (IS_ERR(dcif->blkctrl_regmap))
+		return dev_err_probe(drm->dev, PTR_ERR(dcif->blkctrl_regmap),
+				     "failed to get blk-ctrl regmap\n");
+
+	dcif->regmap = devm_regmap_init_mmio(drm->dev, dcif->reg_base, &dcif_regmap_config);
+	if (IS_ERR(dcif->regmap))
+		return dev_err_probe(drm->dev, PTR_ERR(dcif->regmap),
+				     "failed to init DCIF regmap\n");
+
+	dcif->num_clks = ARRAY_SIZE(dcif_clks);
+	dcif->clks = devm_kcalloc(drm->dev, dcif->num_clks, sizeof(*dcif->clks), GFP_KERNEL);
+	if (!dcif->clks)
+		return -ENOMEM;
+
+	for (i = 0; i < dcif->num_clks; i++)
+		dcif->clks[i].id = dcif_clks[i];
+
+	ret = devm_clk_bulk_get(drm->dev, dcif->num_clks, dcif->clks);
+	if (ret)
+		return dev_err_probe(drm->dev, ret, "cannot get required clocks\n");
+
+	ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
+	if (ret)
+		return dev_err_probe(drm->dev, ret, "failed to set dma mask and coherent\n");
+
+	pm_runtime_enable(drm->dev);
+
+	ret = devm_request_irq(drm->dev, dcif->irq[dcif->cpu_domain],
+			       dcif_irq_handler, 0, drm->driver->name, drm);
+	if (ret < 0) {
+		dev_err(drm->dev, "failed to install IRQ handler: %d\n", ret);
+		goto err_irq_install;
+	}
+
+	dcif_read_chip_info(dcif);
+
+	ret = dcif_kms_prepare(dcif);
+	if (ret)
+		goto err_irq_install;
+
+	ret = drm_dev_register(drm, 0);
+	if (ret) {
+		dev_err(drm->dev, "failed to register drm device: %d\n", ret);
+		goto err_register;
+	}
+
+	drm_client_setup(drm, NULL);
+
+	return 0;
+
+err_register:
+	drm_kms_helper_poll_fini(drm);
+err_irq_install:
+	pm_runtime_disable(drm->dev);
+	return ret;
+}
+
+static void dcif_remove(struct platform_device *pdev)
+{
+	struct dcif_dev *dcif = dev_get_drvdata(&pdev->dev);
+	struct drm_device *drm = &dcif->drm;
+
+	drm_dev_unregister(drm);
+
+	drm_kms_helper_poll_fini(drm);
+
+	drm_atomic_helper_shutdown(drm);
+
+	pm_runtime_disable(drm->dev);
+}
+
+static int dcif_runtime_suspend(struct device *dev)
+{
+	struct dcif_dev *dcif = dev_get_drvdata(dev);
+
+	clk_bulk_disable_unprepare(dcif->num_clks, dcif->clks);
+
+	return 0;
+}
+
+static int dcif_runtime_resume(struct device *dev)
+{
+	struct dcif_dev *dcif = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_bulk_prepare_enable(dcif->num_clks, dcif->clks);
+	if (ret) {
+		dev_err(dev, "failed to enable clocks: %d\n", ret);
+		return ret;
+	}
+
+	ret = dcif_set_qos(dcif);
+	if (ret) {
+		clk_bulk_disable_unprepare(dcif->num_clks, dcif->clks);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dcif_suspend(struct device *dev)
+{
+	struct dcif_dev *dcif = dev_get_drvdata(dev);
+	int ret;
+
+	ret = drm_mode_config_helper_suspend(&dcif->drm);
+	if (ret < 0)
+		return ret;
+
+	if (pm_runtime_suspended(dev))
+		return 0;
+	
+	return dcif_runtime_suspend(dev);
+}
+
+static int dcif_resume(struct device *dev)
+{
+	struct dcif_dev *dcif = dev_get_drvdata(dev);
+	int ret;
+
+	if (!pm_runtime_suspended(dev)) {
+		ret = dcif_runtime_resume(dev);
+		if (ret < 0)
+			return ret;
+	}
+
+	return drm_mode_config_helper_resume(&dcif->drm);
+}
+
+static const struct dev_pm_ops dcif_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(dcif_suspend, dcif_resume)
+	SET_RUNTIME_PM_OPS(dcif_runtime_suspend, dcif_runtime_resume, NULL)
+};
+
+static const struct of_device_id dcif_dt_ids[] = {
+	{ .compatible = "nxp,imx94-dcif", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dcif_dt_ids);
+
+static struct platform_driver dcif_platform_driver = {
+	.probe	= dcif_probe,
+	.remove	= dcif_remove,
+	.driver	= {
+		.name		= DRIVER_NAME,
+		.of_match_table	= dcif_dt_ids,
+		.pm		= pm_ptr(&dcif_pm_ops),
+	},
+};
+module_platform_driver(dcif_platform_driver);
+
+MODULE_AUTHOR("NXP Semiconductor");
+MODULE_DESCRIPTION("i.MX94 DCIF DRM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/imx/dcif/dcif-drv.h b/drivers/gpu/drm/imx/dcif/dcif-drv.h
new file mode 100644
index 0000000000000..f4073c90b40d4
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcif/dcif-drv.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright 2025 NXP
+ */
+
+#ifndef __DCIF_DRV_H__
+#define __DCIF_DRV_H__
+
+#include <linux/clk.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_vblank.h>
+
+struct dcif_crc;
+
+struct dcif_dev {
+	struct drm_device drm;
+	void __iomem *reg_base;
+
+	struct regmap *regmap;
+	struct regmap *blkctrl_regmap;
+	int irq[3];
+
+	unsigned int num_clks;
+	struct clk_bulk_data *clks;
+
+	struct drm_crtc crtc;
+	struct {
+		struct drm_plane primary;
+		struct drm_plane overlay;
+	} planes;
+	struct drm_encoder encoder;
+
+	struct drm_pending_vblank_event *event;
+	/* Implement crc */
+	bool has_crc;
+	bool crc_is_enabled;
+
+	/* CPU domain for interrupt control */
+	int cpu_domain;
+};
+
+enum dcif_crc_source {
+	DCIF_CRC_SRC_NONE,
+	DCIF_CRC_SRC_FRAME,
+	DCIF_CRC_SRC_FRAME_ROI,
+};
+
+struct dcif_crc {
+	enum dcif_crc_source	source;
+	struct drm_rect		roi;
+};
+
+struct dcif_crtc_state {
+	struct drm_crtc_state	base;
+	struct dcif_crc		crc;
+	u32			bus_format;
+	u32			bus_flags;
+};
+
+static inline struct dcif_dev *to_dcif_dev(struct drm_device *drm_dev)
+{
+	return container_of(drm_dev, struct dcif_dev, drm);
+}
+
+static inline struct dcif_dev *crtc_to_dcif_dev(struct drm_crtc *crtc)
+{
+	return to_dcif_dev(crtc->dev);
+}
+
+static inline struct dcif_crtc_state *to_dcif_crtc_state(struct drm_crtc_state *s)
+{
+	return container_of(s, struct dcif_crtc_state, base);
+}
+
+irqreturn_t dcif_irq_handler(int irq, void *data);
+int dcif_crtc_init(struct dcif_dev *dcif);
+int dcif_plane_init(struct dcif_dev *dcif);
+int dcif_kms_prepare(struct dcif_dev *dcif);
+
+#endif
diff --git a/drivers/gpu/drm/imx/dcif/dcif-kms.c b/drivers/gpu/drm/imx/dcif/dcif-kms.c
new file mode 100644
index 0000000000000..80ba95d2e5d3e
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcif/dcif-kms.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "dcif-drv.h"
+#include "dcif-reg.h"
+
+static int dcif_kms_init(struct dcif_dev *dcif)
+{
+	struct drm_device *drm = &dcif->drm;
+	struct device_node *np = drm->dev->of_node;
+	struct drm_connector *connector;
+	struct drm_bridge *bridge;
+	int ret;
+
+	ret = dcif_crtc_init(dcif);
+	if (ret)
+		return ret;
+
+	bridge = devm_drm_of_get_bridge(drm->dev, np, 0, 0);
+	if (IS_ERR(bridge))
+		return dev_err_probe(drm->dev, PTR_ERR(bridge), "Failed to find bridge\n");
+
+	dcif->encoder.possible_crtcs = drm_crtc_mask(&dcif->crtc);
+	ret = drm_simple_encoder_init(drm, &dcif->encoder, DRM_MODE_ENCODER_NONE);
+	if (ret) {
+		drm_err(drm, "failed to initialize encoder: %d\n", ret);
+		return ret;
+	}
+
+	ret = drm_bridge_attach(&dcif->encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+	if (ret) {
+		drm_err(drm, "failed to attach bridge to encoder: %d\n", ret);
+		return ret;
+	}
+
+	connector = drm_bridge_connector_init(drm, &dcif->encoder);
+	if (IS_ERR(connector)) {
+		drm_err(drm, "failed to initialize bridge connector: %d\n", ret);
+		return PTR_ERR(connector);
+	}
+
+	ret = drm_connector_attach_encoder(connector, &dcif->encoder);
+	if (ret)
+		drm_err(drm, "failed to attach encoder to connector: %d\n", ret);
+
+	return ret;
+}
+
+static const struct drm_mode_config_funcs dcif_mode_config_funcs = {
+	.fb_create     = drm_gem_fb_create,
+	.atomic_check  = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static const struct drm_mode_config_helper_funcs dcif_mode_config_helpers = {
+	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+int dcif_kms_prepare(struct dcif_dev *dcif)
+{
+	struct drm_device *drm = &dcif->drm;
+	int ret;
+
+	ret = drmm_mode_config_init(drm);
+	if (ret)
+		return ret;
+
+	ret = dcif_kms_init(dcif);
+	if (ret)
+		return ret;
+
+	drm->mode_config.min_width	= 1;
+	drm->mode_config.min_height	= 1;
+	drm->mode_config.max_width	= 1920;
+	drm->mode_config.max_height	= 1920;
+	drm->mode_config.funcs		= &dcif_mode_config_funcs;
+	drm->mode_config.helper_private	= &dcif_mode_config_helpers;
+
+	ret = drm_vblank_init(drm, 1);
+	if (ret < 0) {
+		drm_err(drm, "failed to initialize vblank: %d\n", ret);
+		return ret;
+	}
+
+	drm_mode_config_reset(drm);
+
+	drm_kms_helper_poll_init(drm);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/imx/dcif/dcif-plane.c b/drivers/gpu/drm/imx/dcif/dcif-plane.c
new file mode 100644
index 0000000000000..54ab8edd11e0c
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcif/dcif-plane.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_rect.h>
+
+#include "dcif-drv.h"
+#include "dcif-reg.h"
+
+static const u32 dcif_primary_plane_formats[] = {
+	/* RGB */
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_XRGB1555,
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_XRGB8888,
+
+	/* Packed YCbCr */
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_VYUY,
+};
+
+static const u32 dcif_overlay_plane_formats[] = {
+	/* RGB */
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_XRGB1555,
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_XRGB8888,
+};
+
+static inline struct dcif_dev *plane_to_dcif_dev(struct drm_plane *plane)
+{
+	return to_dcif_dev(plane->dev);
+}
+
+static inline dma_addr_t drm_plane_state_to_baseaddr(struct drm_plane_state *state)
+{
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_dma_object *dma_obj;
+	unsigned int x = state->src.x1 >> 16;
+	unsigned int y = state->src.y1 >> 16;
+
+	dma_obj = drm_fb_dma_get_gem_obj(fb, 0);
+
+	return dma_obj->dma_addr + fb->offsets[0] + fb->pitches[0] * y + fb->format->cpp[0] * x;
+}
+
+static int dcif_plane_get_layer_id(struct drm_plane *plane)
+{
+	return (plane->type == DRM_PLANE_TYPE_PRIMARY) ? 0 : 1;
+}
+
+static int dcif_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
+	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
+	struct dcif_dev *dcif = plane_to_dcif_dev(plane);
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	struct drm_framebuffer *old_fb = old_plane_state->fb;
+	struct drm_crtc_state *crtc_state;
+
+	if (!fb)
+		return 0;
+
+	crtc_state = drm_atomic_get_new_crtc_state(state, &dcif->crtc);
+	if (WARN_ON(!crtc_state))
+		return -EINVAL;
+
+	/*
+	 * Force CRTC mode change if framebuffer stride or pixel format have changed.
+	 */
+	if (plane->type == DRM_PLANE_TYPE_PRIMARY && old_fb &&
+	    (fb->pitches[0] != old_fb->pitches[0] || fb->format->format != old_fb->format->format))
+		crtc_state->mode_changed = true;
+
+	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
+						   DRM_PLANE_NO_SCALING, DRM_PLANE_NO_SCALING, true,
+						   true);
+}
+
+static void dcif_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
+	struct dcif_dev *dcif = plane_to_dcif_dev(plane);
+	int layer_id = dcif_plane_get_layer_id(plane);
+	struct drm_framebuffer *fb = new_state->fb;
+	u32 crtc_x, crtc_y, crtc_h, crtc_w;
+	u32 layer_fmt = 0, yuv_fmt = 0;
+	dma_addr_t baseaddr;
+	u32 reg;
+
+	if (!fb)
+		return;
+
+	crtc_x = new_state->crtc_x;
+	crtc_y = new_state->crtc_y;
+	crtc_h = new_state->crtc_h;
+	crtc_w = new_state->crtc_w;
+
+	/* visible portion of plane on crtc */
+	regmap_write(dcif->regmap, DCIF_CTRLDESC1(layer_id),
+		     DCIF_CTRLDESC1_POSX(crtc_x) | DCIF_CTRLDESC1_POSY(crtc_y));
+	regmap_write(dcif->regmap, DCIF_CTRLDESC2(layer_id),
+		     DCIF_CTRLDESC2_WIDTH(crtc_w) | DCIF_CTRLDESC2_HEIGHT(crtc_h));
+
+	/* pitch size */
+	reg = DCIF_CTRLDESC3_P_SIZE(2) | DCIF_CTRLDESC3_T_SIZE(2) |
+	      DCIF_CTRLDESC3_PITCH(fb->pitches[0]);
+	regmap_write(dcif->regmap, DCIF_CTRLDESC3(layer_id), reg);
+
+	/*  address */
+	baseaddr = drm_fb_dma_get_gem_addr(new_state->fb, new_state, 0);
+
+	drm_dbg_kms(plane->dev, "[PLANE:%d:%s] fb address %pad, pitch 0x%08x\n",
+		    plane->base.id, plane->name, &baseaddr, fb->pitches[0]);
+
+	regmap_write(dcif->regmap, DCIF_CTRLDESC4(layer_id), baseaddr);
+
+	/* Format */
+	switch (fb->format->format) {
+	/* RGB Formats */
+	case DRM_FORMAT_RGB565:
+		layer_fmt = CTRLDESCL0_FORMAT_RGB565;
+		break;
+	case DRM_FORMAT_RGB888:
+		layer_fmt = CTRLDESCL0_FORMAT_RGB888;
+		break;
+	case DRM_FORMAT_XRGB1555:
+		layer_fmt = CTRLDESCL0_FORMAT_ARGB1555;
+		break;
+	case DRM_FORMAT_XRGB4444:
+		layer_fmt = CTRLDESCL0_FORMAT_ARGB4444;
+		break;
+	case DRM_FORMAT_XBGR8888:
+		layer_fmt = CTRLDESCL0_FORMAT_ABGR8888;
+		break;
+	case DRM_FORMAT_XRGB8888:
+		layer_fmt = CTRLDESCL0_FORMAT_ARGB8888;
+		break;
+
+	/* YUV Formats */
+	case DRM_FORMAT_YUYV:
+		layer_fmt = CTRLDESCL0_FORMAT_YCBCR422;
+		yuv_fmt = CTRLDESCL0_YUV_FORMAT_VY2UY1;
+		break;
+	case DRM_FORMAT_YVYU:
+		layer_fmt = CTRLDESCL0_FORMAT_YCBCR422;
+		yuv_fmt = CTRLDESCL0_YUV_FORMAT_UY2VY1;
+		break;
+	case DRM_FORMAT_UYVY:
+		layer_fmt = CTRLDESCL0_FORMAT_YCBCR422;
+		yuv_fmt = CTRLDESCL0_YUV_FORMAT_Y2VY1U;
+		break;
+	case DRM_FORMAT_VYUY:
+		layer_fmt = CTRLDESCL0_FORMAT_YCBCR422;
+		yuv_fmt = CTRLDESCL0_YUV_FORMAT_Y2UY1V;
+		break;
+
+	default:
+		dev_err(dcif->drm.dev, "Unknown pixel format 0x%x\n", fb->format->format);
+		break;
+	}
+
+	if (plane->type == DRM_PLANE_TYPE_OVERLAY && yuv_fmt == CTRLDESCL0_YUV_FORMAT_Y2UY1V) {
+		dev_err(dcif->drm.dev, "Overlay plane could not support YUV format\n");
+		return;
+	}
+
+	reg = DCIF_CTRLDESC0_EN | DCIF_CTRLDESC0_SHADOW_LOAD_EN |
+	      DCIF_CTRLDESC0_FORMAT(layer_fmt) | DCIF_CTRLDESC0_YUV_FORMAT(yuv_fmt);
+
+	/* Alpha */
+	reg |= DCIF_CTRLDESC0_GLOBAL_ALPHA(new_state->alpha >> 8) | ALPHA_GLOBAL;
+
+	regmap_write(dcif->regmap, DCIF_CTRLDESC0(layer_id), reg);
+}
+
+static void dcif_overlay_plane_atomic_disable(struct drm_plane *plane,
+					      struct drm_atomic_state *state)
+{
+	struct dcif_dev *dcif = plane_to_dcif_dev(plane);
+
+	regmap_update_bits(dcif->regmap, DCIF_CTRLDESC0(1),
+			   DCIF_CTRLDESC0_EN | DCIF_CTRLDESC0_SHADOW_LOAD_EN,
+			   DCIF_CTRLDESC0_SHADOW_LOAD_EN);
+}
+
+static const struct drm_plane_helper_funcs dcif_primary_plane_helper_funcs = {
+	.prepare_fb	= drm_gem_plane_helper_prepare_fb,
+	.atomic_check	= dcif_plane_atomic_check,
+	.atomic_update	= dcif_plane_atomic_update,
+};
+
+static const struct drm_plane_helper_funcs dcif_overlay_plane_helper_funcs = {
+	.atomic_check	= dcif_plane_atomic_check,
+	.atomic_update	= dcif_plane_atomic_update,
+	.atomic_disable = dcif_overlay_plane_atomic_disable,
+};
+
+static const struct drm_plane_funcs dcif_plane_funcs = {
+	.update_plane		= drm_atomic_helper_update_plane,
+	.disable_plane		= drm_atomic_helper_disable_plane,
+	.destroy		= drm_plane_cleanup,
+	.reset			= drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
+};
+
+int dcif_plane_init(struct dcif_dev *dcif)
+{
+	const u32 supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
+					BIT(DRM_COLOR_YCBCR_BT709) |
+					BIT(DRM_COLOR_YCBCR_BT2020);
+	const u32 supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
+				     BIT(DRM_COLOR_YCBCR_FULL_RANGE);
+	int ret;
+
+	/* primary plane */
+	drm_plane_helper_add(&dcif->planes.primary, &dcif_primary_plane_helper_funcs);
+	ret = drm_universal_plane_init(&dcif->drm, &dcif->planes.primary, 1, &dcif_plane_funcs,
+				       dcif_primary_plane_formats,
+				       ARRAY_SIZE(dcif_primary_plane_formats), NULL,
+				       DRM_PLANE_TYPE_PRIMARY, NULL);
+	if (ret) {
+		drm_err(&dcif->drm, "failed to initialize primary plane: %d\n", ret);
+		return ret;
+	}
+
+	ret = drm_plane_create_color_properties(&dcif->planes.primary, supported_encodings,
+						supported_ranges, DRM_COLOR_YCBCR_BT601,
+						DRM_COLOR_YCBCR_LIMITED_RANGE);
+	if (ret)
+		return ret;
+
+	ret = drm_plane_create_alpha_property(&dcif->planes.primary);
+	if (ret)
+		return ret;
+
+	/* overlay plane */
+	drm_plane_helper_add(&dcif->planes.overlay, &dcif_overlay_plane_helper_funcs);
+	ret = drm_universal_plane_init(&dcif->drm, &dcif->planes.overlay, 1, &dcif_plane_funcs,
+				       dcif_overlay_plane_formats,
+				       ARRAY_SIZE(dcif_overlay_plane_formats), NULL,
+				       DRM_PLANE_TYPE_OVERLAY, NULL);
+	if (ret) {
+		drm_err(&dcif->drm, "failed to initialize overlay plane: %d\n", ret);
+		return ret;
+	}
+
+	return drm_plane_create_alpha_property(&dcif->planes.overlay);
+}
diff --git a/drivers/gpu/drm/imx/dcif/dcif-reg.h b/drivers/gpu/drm/imx/dcif/dcif-reg.h
new file mode 100644
index 0000000000000..224f1d18f8c81
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcif/dcif-reg.h
@@ -0,0 +1,326 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright 2025 NXP
+ */
+#ifndef __DCIF_REG_H__
+#define __DCIF_REG_H__
+
+#include <linux/bits.h>
+
+/* Version ID Register */
+#define DCIF_VER				0x0
+
+/* Parameter Registers */
+#define DCIF_PAR_0				0x4
+#define DCIF_PAR_1				0x8
+
+/* Display Control and Parameter Registers */
+#define DCIF_DISP_CTRL				0x10
+#define DCIF_DISP_PAR				0x14
+#define DCIF_DISP_SIZE				0x18
+
+/* Display Status Registers */
+#define DCIF_DISP_SR0				0x1C
+#define DCIF_DISP_SR1				0x20
+
+/* Interrupt Enable and Status Registers, n=0-2*/
+#define DCIF_IE0(n)				(0x24 + (n) * 0x10000)
+#define DCIF_IS0(n)				(0x28 + (n) * 0x10000)
+#define DCIF_IE1(n)				(0x2C + (n) * 0x10000)
+#define DCIF_IS1(n)				(0x30 + (n) * 0x10000)
+
+/* DPI Control and Sync Parameter Registers */
+#define DCIF_DPI_CTRL				0x40
+#define DCIF_DPI_HSYN_PAR			0x44
+#define DCIF_DPI_VSYN_PAR			0x48
+#define DCIF_DPI_VSYN_HSYN_WIDTH		0x4C
+
+/* Control Descriptor Registers, n=0-1*/
+#define DCIF_CTRLDESC0(n)			(0x10000 + (n) * 0x10000)
+#define DCIF_CTRLDESC1(n)			(0x10004 + (n) * 0x10000)
+#define DCIF_CTRLDESC2(n)			(0x10008 + (n) * 0x10000)
+#define DCIF_CTRLDESC3(n)			(0x1000C + (n) * 0x10000)
+#define DCIF_CTRLDESC4(n)			(0x10010 + (n) * 0x10000)
+#define DCIF_CTRLDESC5(n)			(0x10014 + (n) * 0x10000)
+#define DCIF_CTRLDESC6(n)			(0x10018 + (n) * 0x10000)
+
+/* CLUT control Register */
+#define DCIF_CLUT_CTRL				0x1003C
+
+/* FIFO Panic Threshold Register, n=0-1 */
+#define DCIF_PANIC_THRES(n)			(0x10040 + (n) * 0x10000)
+
+/* Layer Status Register 0, n=0-1 */
+#define DCIF_LAYER_SR0(n)			(0x10044 + (n) * 0x10000)
+
+/* Color Space Conversion Control and Coefficient Registers for Layer 0 */
+#define DCIF_CSC_CTRL_L0			0x10050
+#define DCIF_CSC_COEF0_L0			0x10054
+#define DCIF_CSC_COEF1_L0			0x10058
+#define DCIF_CSC_COEF2_L0			0x1005C
+#define DCIF_CSC_COEF3_L0			0x10060
+#define DCIF_CSC_COEF4_L0			0x10064
+#define DCIF_CSC_COEF5_L0			0x10068
+
+/* CRC Control, Threshold, and Histogram Coefficient Registers */
+#define DCIF_CRC_CTRL				0x20100
+#define DCIF_CRC_THRES				0x20104
+#define DCIF_CRC_HIST_COEF			0x20108
+#define DCIF_CRC_ERR_CNT			0x2010C
+#define DCIF_CRC_SR				0x20110
+#define DCIF_CRC_HIST_CNT_B(n)			(0x20114 + (n) * 4)
+
+/* CRC Region Position, Size, Value, and Expected Value Registers, n=0-3 */
+#define DCIF_CRC_POS_R(n)			(0x20214 + (n) * 0x10)
+#define DCIF_CRC_SIZE_R(n)			(0x20218 + (n) * 0x10)
+#define DCIF_CRC_VAL_R(n)			(0x2021C + (n) * 0x10)
+#define DCIF_CRC_EXP_VAL_R(n)			(0x20220 + (n) * 0x10)
+
+/* VER - Version ID Register */
+#define DCIF_VER_GET_FEATURE(x)			FIELD_GET(GENMASK(15, 0), x)
+#define DCIF_VER_GET_MINOR(x)			FIELD_GET(GENMASK(23, 16), x)
+#define DCIF_VER_GET_MAJOR(x)			FIELD_GET(GENMASK(31, 24), x)
+
+/* PAR_0 - Parameter Register 0 */
+#define DCIF_PAR_0_LAYER_NUM(x)			FIELD_PREP(GENMASK(3, 0), x)
+#define DCIF_PAR_0_DOMAIN_NUM(x)		FIELD_PREP(GENMASK(5, 4), x)
+#define DCIF_PAR_0_AXI_DATA_WIDTH(x)		FIELD_PREP(GENMASK(7, 6), x)
+#define DCIF_PAR_0_CLUT_RAM_NUM(x)		FIELD_PREP(GENMASK(11, 8), x)
+#define DCIF_PAR_0_CSC_NUM(x)			FIELD_PREP(GENMASK(13, 12), x)
+#define DCIF_PAR_0_CRC_REGION_NUM(x)		FIELD_PREP(GENMASK(18, 16), x)
+#define DCIF_PAR_0_BACKUP(x)			FIELD_PREP(GENMASK(31, 28), x)
+
+/* PAR_1 - Parameter Register 1 */
+#define DCIF_PAR_1_LAYER0_FIFO_SIZE(x)		FIELD_PREP(GENMASK(3, 0), x)
+#define DCIF_PAR_1_LAYER1_FIFO_SIZE(x)		FIELD_PREP(GENMASK(7, 4), x)
+
+/* DISP_CTRL - Display Control Register */
+#define DCIF_DISP_CTRL_DISP_ON			BIT(0)
+#define DCIF_DISP_CTRL_AXI_RD_HOLD		BIT(30)
+#define DCIF_DISP_CTRL_SW_RST			BIT(31)
+
+/* DISP_PAR - Display Parameter Register */
+#define DCIF_DISP_PAR_BGND_B(x)			FIELD_PREP(GENMASK(7, 0), x)
+#define DCIF_DISP_PAR_BGND_G(x)			FIELD_PREP(GENMASK(15, 8), x)
+#define DCIF_DISP_PAR_BGND_R(x)			FIELD_PREP(GENMASK(23, 16), x)
+
+/* DISP_SIZE - Display Size Register */
+#define DCIF_DISP_SIZE_DISP_WIDTH(x)		FIELD_PREP(GENMASK(11, 0), x)
+#define DCIF_DISP_SIZE_DISP_HEIGHT(x)		FIELD_PREP(GENMASK(27, 16), x)
+
+/* DISP_SR0 - Display Status Register 0 */
+#define DCIF_DISP_SR0_AXI_RD_PEND(x)		FIELD_PREP(GENMASK(4, 0), x)
+#define DCIF_DISP_SR0_DPI_BUSY(x)		FIELD_PREP(GENMASK(14, 14), x)
+#define DCIF_DISP_SR0_AXI_RD_BUSY(x)		FIELD_PREP(GENMASK(15, 15), x)
+#define DCIF_DISP_SR0_TXFIFO_CNT(x)		FIELD_PREP(GENMASK(23, 16), x)
+
+/* DISP_SR1 - Display Status Register 1 */
+#define DCIF_DISP_SR1_H_CNT(x)			FIELD_PREP(GENMASK(11, 0), x)
+#define DCIF_DISP_SR1_V_CNT(x)			FIELD_PREP(GENMASK(27, 16), x)
+
+/* INT0 - Interrupt Enable/Status  Register 0 for Domain 0/1/2 */
+#define DCIF_INT0_VSYNC				BIT(0)
+#define DCIF_INT0_UNDERRUN			BIT(1)
+#define DCIF_INT0_VS_BLANK			BIT(2)
+#define DCIF_INT0_HIST_DONE			BIT(5)
+#define DCIF_INT0_CRC_ERR			BIT(6)
+#define DCIF_INT0_CRC_ERR_SAT			BIT(7)
+
+/* INT1 - Interrupt Enable/Status Register 1 for Domain 0/1/2 */
+#define DCIF_INT1_FIFO_PANIC0			BIT(0)
+#define DCIF_INT1_FIFO_PANIC1			BIT(1)
+#define DCIF_INT1_DMA_ERR0			BIT(8)
+#define DCIF_INT1_DMA_ERR1			BIT(9)
+#define DCIF_INT1_DMA_DONE0			BIT(16)
+#define DCIF_INT1_DMA_DONE1			BIT(17)
+#define DCIF_INT1_FIFO_EMPTY0			BIT(24)
+#define DCIF_INT1_FIFO_EMPTY1			BIT(25)
+
+/* DPI_CTRL - DPI Control Register */
+#define DCIF_DPI_CTRL_HSYNC_POL_LOW		BIT(0)
+#define DCIF_DPI_CTRL_VSYNC_POL_LOW		BIT(1)
+#define DCIF_DPI_CTRL_DE_POL_LOW		BIT(2)
+#define DCIF_DPI_CTRL_PCLK_EDGE_FALLING		BIT(3)
+#define DCIF_DPI_CTRL_POL_MASK			GENMASK(3, 0)
+
+#define DCIF_DPI_CTRL_DATA_INV(x)		FIELD_PREP(GENMASK(4, 4), x)
+#define DCIF_DPI_CTRL_DEF_BGND_EN(x)		FIELD_PREP(GENMASK(5, 5), x)
+#define DCIF_DPI_CTRL_FETCH_OPT(x)		FIELD_PREP(GENMASK(9, 8), x)
+#define DCIF_DPI_CTRL_DISP_MODE(x)		FIELD_PREP(GENMASK(13, 12), x)
+#define DCIF_DPI_CTRL_DATA_PATTERN_MASK		GENMASK(18, 16)
+#define DCIF_DPI_CTRL_DATA_PATTERN(x)		FIELD_PREP(GENMASK(18, 16), x)
+#define PATTERN_RGB888				0
+#define PATTERN_RBG888				1
+#define PATTERN_GBR888				2
+#define PATTERN_GRB888				3
+#define PATTERN_BRG888				4
+#define PATTERN_BGR888				5
+#define PATTERN_RGB555				6
+#define PATTERN_RGB565				7
+
+/* DPI_HSYN_PAR - DPI Horizontal Sync Parameter Register */
+#define DCIF_DPI_HSYN_PAR_FP_H(x)		FIELD_PREP(GENMASK(11, 0), x)
+#define DCIF_DPI_HSYN_PAR_BP_H(x)		FIELD_PREP(GENMASK(27, 16), x)
+
+/* DPI_VSYN_PAR - DPI Vertical Sync Parameter Register */
+#define DCIF_DPI_VSYN_PAR_FP_V(x)		FIELD_PREP(GENMASK(11, 0), x)
+#define DCIF_DPI_VSYN_PAR_BP_V(x)		FIELD_PREP(GENMASK(27, 16), x)
+
+/* DPI_VSYN_HSYN_WIDTH - DPI Vertical and Horizontal Pulse Width Parameter Register */
+#define DCIF_DPI_VSYN_HSYN_WIDTH_PW_H(x)	FIELD_PREP(GENMASK(11, 0), x)
+#define DCIF_DPI_VSYN_HSYN_WIDTH_PW_V(x)	FIELD_PREP(GENMASK(27, 16), x)
+
+/* CTRLDESC0 - Control Descriptor Register 0 */
+#define DCIF_CTRLDESC0_AB_MODE(x)		FIELD_PREP(GENMASK(1, 0), x)
+#define ALPHA_EMBEDDED				0
+#define ALPHA_GLOBAL				1
+#define DCIF_CTRLDESC0_YUV_FORMAT_MASK		GENMASK(15, 14)
+#define DCIF_CTRLDESC0_YUV_FORMAT(x)		FIELD_PREP(GENMASK(15, 14), x)
+#define CTRLDESCL0_YUV_FORMAT_Y2VY1U		0x0
+#define CTRLDESCL0_YUV_FORMAT_Y2UY1V		0x1
+#define CTRLDESCL0_YUV_FORMAT_VY2UY1		0x2
+#define CTRLDESCL0_YUV_FORMAT_UY2VY1		0x3
+#define DCIF_CTRLDESC0_GLOBAL_ALPHA(x)		FIELD_PREP(GENMASK(23, 16), x)
+#define DCIF_CTRLDESC0_FORMAT_MASK		GENMASK(27, 24)
+#define DCIF_CTRLDESC0_FORMAT(x)		FIELD_PREP(GENMASK(27, 24), x)
+#define CTRLDESCL0_FORMAT_RGB565		0x4
+#define CTRLDESCL0_FORMAT_ARGB1555		0x5
+#define CTRLDESCL0_FORMAT_ARGB4444		0x6
+#define CTRLDESCL0_FORMAT_YCBCR422		0x7
+#define CTRLDESCL0_FORMAT_RGB888		0x8
+#define CTRLDESCL0_FORMAT_ARGB8888		0x9
+#define CTRLDESCL0_FORMAT_ABGR8888		0xa
+#define DCIF_CTRLDESC0_SHADOW_LOAD_EN		BIT(30)
+#define DCIF_CTRLDESC0_EN			BIT(31)
+
+/* CTRLDESC1 - Control Descriptor Register 1 */
+#define DCIF_CTRLDESC1_POSX(x)			FIELD_PREP(GENMASK(11, 0), x)
+#define DCIF_CTRLDESC1_POSY(x)			FIELD_PREP(GENMASK(27, 16), x)
+
+/* CTRLDESC2 - Control Descriptor Register */
+#define DCIF_CTRLDESC2_WIDTH(x)			FIELD_PREP(GENMASK(11, 0), x)
+#define DCIF_CTRLDESC2_HEIGHT(x)		FIELD_PREP(GENMASK(27, 16), x)
+
+/* CTRLDESC3 - Control Descriptor Register 3 */
+#define DCIF_CTRLDESC3_PITCH(x)			FIELD_PREP(GENMASK(15, 0), x)
+#define DCIF_CTRLDESC3_T_SIZE(x)		FIELD_PREP(GENMASK(17, 16), x)
+#define DCIF_CTRLDESC3_P_SIZE(x)		FIELD_PREP(GENMASK(22, 20), x)
+
+/* CTRLDESC4 - Control Descriptor Register 4 */
+#define DCIF_CTRLDESC4_ADDR(x)FIELD_PREP(GENMASK(31, 0), x)
+
+/* CTRLDESC6 - Control Descriptor Register 6  */
+#define DCIF_CTRLDESC6_BCLR_B(x)		FIELD_PREP(GENMASK(7, 0), x)
+#define DCIF_CTRLDESC6_BCLR_G(x)		FIELD_PREP(GENMASK(15, 8), x)
+#define DCIF_CTRLDESC6_BCLR_R(x)		FIELD_PREP(GENMASK(23, 16), x)
+#define DCIF_CTRLDESC6_BCLR_A(x)		FIELD_PREP(GENMASK(31, 24), x)
+
+/* CLUT_CTRL - CLUT control Register */
+#define DCIF_CLUT_CTRL_CLUT0_SEL(x)		FIELD_PREP(GENMASK(0, 0), x)
+#define DCIF_CLUT_CTRL_CLUT1_SEL(x)		FIELD_PREP(GENMASK(3, 3), x)
+#define DCIF_CLUT_CTRL_CLUT_MUX(x)		FIELD_PREP(GENMASK(29, 28), x)
+#define DCIF_CLUT_CTRL_CLUT_SHADOW_LOAD_EN(x)	FIELD_PREP(GENMASK(31, 31), x)
+
+/* PANIC_THRES_L0 - FIFO Panic Threshold Register For Layer 0 */
+#define DCIF_PANIC_THRES_LOW_MASK		GENMASK(11, 0)
+#define DCIF_PANIC_THRES_LOW(x)			FIELD_PREP(GENMASK(11, 00), x)
+#define DCIF_PANIC_THRES_HIGH_MASK		GENMASK(27, 16)
+#define DCIF_PANIC_THRES_HIGH(x)		FIELD_PREP(GENMASK(27, 16), x)
+#define DCIF_PANIC_THRES_REQ_EN			BIT(31)
+#define PANIC0_THRES_MAX			511
+
+/* LAYER_SR0_L0 - Layer Status Register 0 for Layer 0 */
+#define DCIF_LAYER_SR0_L0_FIFO_CNT_MASK		GENMASK(9, 0)
+#define DCIF_LAYER_SR0_L0_FIFO_CNT(x)		FIELD_PREP(GENMASK(9, 0), x)
+
+/* CSC_CTRL_L0 - Color Space Conversion Control Register For Layer 0 */
+#define DCIF_CSC_CTRL_L0_CSC_EN			BIT(0)
+#define DCIF_CSC_CTRL_L0_CSC_MODE_YCBCR2RGB	BIT(1)
+
+/* CSC_COEF0_L0 - Color Space Conversion Coefficient Register 0 For Layer 0 */
+#define DCIF_CSC_COEF0_L0_A1(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
+#define DCIF_CSC_COEF0_L0_A2(x)			FIELD_PREP_CONST(GENMASK(26, 16), x)
+
+/* CSC_COEF1_L0 - Color Space Conversion Coefficient Register 1 For Layer 0 */
+#define DCIF_CSC_COEF1_L0_A3(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
+#define DCIF_CSC_COEF1_L0_B1(x)			FIELD_PREP_CONST(GENMASK(26, 16), x)
+
+/* CSC_COEF2_L0 - Color Space Conversion Coefficient Register 2 For Layer 0 */
+#define DCIF_CSC_COEF2_L0_B2(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
+#define DCIF_CSC_COEF2_L0_B3(x)			FIELD_PREP_CONST(GENMASK(26, 16), x)
+
+/* CSC_COEF3_L0 - Color Space Conversion Coefficient Register 3 For Layer 0 */
+#define DCIF_CSC_COEF3_L0_C1(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
+#define DCIF_CSC_COEF3_L0_C2(x)			FIELD_PREP_CONST(GENMASK(26, 16), x)
+
+/* CSC_COEF4_L0 - Color Space Conversion Coefficient Register 4 For Layer 0 */
+#define DCIF_CSC_COEF4_L0_C3(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
+#define DCIF_CSC_COEF4_L0_D1(x)			FIELD_PREP_CONST(GENMASK(24, 16), x)
+
+/* CSC_COEF5_L0 - Color Space Conversion Coefficient Register 5 For Layer 0 */
+#define DCIF_CSC_COEF5_L0_D2(x)			FIELD_PREP_CONST(GENMASK(8, 0), x)
+#define DCIF_CSC_COEF5_L0_D3(x)			FIELD_PREP_CONST(GENMASK(24, 16), x)
+
+/* CTRLDESC0_L1 - Control Descriptor Register 0 for Layer 1 */
+#define DCIF_CTRLDESC0_L1_AB_MODE_MASK		GENMASK(1, 0)
+#define DCIF_CTRLDESC0_L1_AB_MODE(x)		FIELD_PREP(GENMASK(1, 0), x)
+
+/* CRC_CTRL - CRC Control Register */
+#define DCIF_CRC_CTRL_CRC_EN(x)			(1 << (x))
+#define DCIF_CRC_CTRL_HIST_REGION_SEL(x)	FIELD_PREP(GENMASK(17, 16), x)
+#define DCIF_CRC_CTRL_HIST_MODE			BIT(21)
+#define DCIF_CRC_CTRL_HIST_TRIG			BIT(22)
+#define DCIF_CRC_CTRL_HIST_EN			BIT(23)
+#define DCIF_CRC_CTRL_CRC_MODE			BIT(28)
+#define DCIF_CRC_CTRL_CRC_TRIG			BIT(29)
+#define DCIF_CRC_CTRL_CRC_ERR_CNT_RST		BIT(30)
+#define DCIF_CRC_CTRL_CRC_SHADOW_LOAD_EN	BIT(31)
+
+/* CRC_THRES - CRC Threshold Register */
+#define DCIF_CRC_THRES_CRC_THRESHOLD_MASK	GENMASK(31, 0)
+#define DCIF_CRC_THRES_CRC_THRESHOLD(x)		FIELD_PREP(GENMASK(31, 0), x)
+
+/* CRC_HIST_COEF - CRC Region Histogram Coefficient Register */
+#define DCIF_CRC_HIST_COEF_HIST_WB_MASK		GENMASK(7, 0)
+#define DCIF_CRC_HIST_COEF_HIST_WB(x)		FIELD_PREP(GENMASK(7, 0), x)
+#define DCIF_CRC_HIST_COEF_HIST_WG_MASK		GENMASK(15, 8)
+#define DCIF_CRC_HIST_COEF_HIST_WG(x)		FIELD_PREP(GENMASK(15, 8), x)
+#define DCIF_CRC_HIST_COEF_HIST_WR_MASK		GENMASK(23, 16)
+#define DCIF_CRC_HIST_COEF_HIST_WR(x)		FIELD_PREP(GENMASK(23, 16), x)
+
+/* CRC_ERR_CNT - CRC Error Counter Register */
+#define DCIF_CRC_ERR_CNT_CRC_ERR_CNT_MASK	GENMASK(31, 0)
+#define DCIF_CRC_ERR_CNT_CRC_ERR_CNT(x)		FIELD_PREP(GENMASK(31, 0), x)
+
+/* CRC_SR - CRC Status Register */
+#define DCIF_CRC_SR_HIST_CNT_SAT_MASK		BIT(13)
+#define DCIF_CRC_SR_HIST_CNT_SAT(x)		FIELD_PREP(GENMASK(13, 13), x)
+#define DCIF_CRC_SR_HIST_SAT_MASK		BIT(14)
+#define DCIF_CRC_SR_HIST_SAT(x)			FIELD_PREP(GENMASK(14, 14), x)
+#define DCIF_CRC_SR_HIST_BUSY_MASK		BIT(15)
+#define DCIF_CRC_SR_HIST_BUSY(x)		FIELD_PREP(GENMASK(15, 15), x)
+#define DCIF_CRC_SR_CRC_STATUS_MASK		BIT(31)
+#define DCIF_CRC_SR_CRC_STATUS(x)		FIELD_PREP(GENMASK(31, 31), x)
+
+/* CRC Region Histogram Counter Register For Bin n */
+#define DCIF_B_BIN_CNT_MASK			GENMASK(20, 0)
+#define DCIF_B_BIN_CNT(x)			FIELD_PREP(GENMASK(20, 0), x)
+
+/* CRC_POS - CRC Position Register */
+#define DCIF_CRC_POS_CRC_HOR_POS(x)		FIELD_PREP(GENMASK(11, 0), x)
+#define DCIF_CRC_POS_CRC_VER_POS(x)		FIELD_PREP(GENMASK(27, 16), x)
+
+/* CRC_SIZE - CRC Size Register */
+#define DCIF_CRC_SIZE_CRC_HOR_SIZE(x)		FIELD_PREP(GENMASK(11, 0), x)
+#define DCIF_CRC_SIZE_CRC_VER_SIZE(x)		FIELD_PREP(GENMASK(27, 16), x)
+
+/* CRC_VAL - CRC Value Register */
+#define DCIF_CRC_VAL_CRC_VAL_MASK		GENMASK(31, 0)
+#define DCIF_CRC_VAL_CRC_VAL(x)			FIELD_PREP(GENMASK(31, 0), x)
+
+/* CRC_EXP_VAL - CRC Expected Value Register */
+#define DCIF_CRC_EXP_VAL_CRC_EXP_VAL_MASK	GENMASK(31, 0)
+#define DCIF_CRC_EXP_VAL_CRC_EXP_VAL(x)		FIELD_PREP(GENMASK(31, 0), x)
+
+#endif /* __DCIF_REG_H__ */
-- 
2.46.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 7/8] arm64: dts: imx943-evk: Add support for DCIF and LVDS
  2025-07-09 12:23 [PATCH 0/8] Add support for i.MX94 DCIF Laurentiu Palcu
                   ` (5 preceding siblings ...)
  2025-07-09 12:23 ` [PATCH 6/8] drm/imx: Add support " Laurentiu Palcu
@ 2025-07-09 12:23 ` Laurentiu Palcu
  2025-07-11  5:07   ` Frank Li
  2025-07-09 12:23 ` [PATCH 8/8] MAINTAINERS: Add entry for i.MX94 DCIF driver Laurentiu Palcu
  7 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Palcu @ 2025-07-09 12:23 UTC (permalink / raw)
  To: imx, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
  Cc: dri-devel, Laurentiu Palcu, devicetree, linux-arm-kernel,
	linux-kernel

Add DT nodes for DCIF and LVDS in imx943.dtsi and activate them in
imx943-evk.dts.

Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
---
 arch/arm64/boot/dts/freescale/imx943-evk.dts | 126 +++++++++++++++++++
 arch/arm64/boot/dts/freescale/imx943.dtsi    |  56 ++++++++-
 2 files changed, 181 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/freescale/imx943-evk.dts b/arch/arm64/boot/dts/freescale/imx943-evk.dts
index c8c3eff9df1a2..e7de7ba406407 100644
--- a/arch/arm64/boot/dts/freescale/imx943-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx943-evk.dts
@@ -125,6 +125,132 @@ memory@80000000 {
 		reg = <0x0 0x80000000 0x0 0x80000000>;
 		device_type = "memory";
 	};
+
+	hdmi-connector {
+		compatible = "hdmi-connector";
+		label = "hdmi";
+		type = "a";
+
+		port {
+			hdmi_connector_in: endpoint {
+				remote-endpoint = <&it6263_out>;
+			};
+		};
+	};
+};
+
+&dcif {
+	status = "okay";
+};
+
+&ldb {
+	assigned-clocks = <&scmi_clk IMX94_CLK_LDBPLL_VCO>,
+			  <&scmi_clk IMX94_CLK_LDBPLL>;
+	assigned-clock-rates = <4158000000>, <1039500000>;
+	status = "okay";
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@1 {
+			reg = <1>;
+
+			lvds_out: endpoint {
+				remote-endpoint = <&it6263_in>;
+			};
+		};
+	};
+};
+
+&lpi2c3 {
+	clock-frequency = <400000>;
+	pinctrl-0 = <&pinctrl_lpi2c3>;
+	pinctrl-names = "default";
+	status = "okay";
+
+	pca9548_i2c3: i2c-mux@77 {
+		compatible = "nxp,pca9548";
+		reg = <0x77>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		i2c@0 {
+			reg = <0>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		i2c@1 {
+			reg = <1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		i2c@2 {
+			reg = <2>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		i2c@3 {
+			reg = <3>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			lvds-to-hdmi-bridge@4c {
+				compatible = "ite,it6263";
+				reg = <0x4c>;
+				data-mapping = "jeida-24";
+				reset-gpios = <&pcal6416_i2c3_u171 8 GPIO_ACTIVE_HIGH>;
+
+				ports {
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					port@0 {
+						reg = <0>;
+
+						it6263_in: endpoint {
+							remote-endpoint = <&lvds_out>;
+						};
+					};
+
+					port@2 {
+						reg = <2>;
+
+						it6263_out: endpoint {
+							remote-endpoint = <&hdmi_connector_in>;
+						};
+					};
+				};
+			};
+		};
+
+		i2c@4 {
+			reg = <4>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		i2c@5 {
+			reg = <5>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		i2c@6 {
+			reg = <6>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		i2c@7 {
+			reg = <7>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+	};
 };
 
 &lpi2c3 {
diff --git a/arch/arm64/boot/dts/freescale/imx943.dtsi b/arch/arm64/boot/dts/freescale/imx943.dtsi
index 657c81b6016f2..db00a94812e18 100644
--- a/arch/arm64/boot/dts/freescale/imx943.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx943.dtsi
@@ -148,7 +148,7 @@ l3_cache: l3-cache {
 		};
 	};
 
-	clock-ldb-pll-div7 {
+	clock_ldb_pll_div7: clock-ldb-pll-div7 {
 		compatible = "fixed-factor-clock";
 		#clock-cells = <0>;
 		clocks = <&scmi_clk IMX94_CLK_LDBPLL>;
@@ -173,10 +173,64 @@ dispmix_csr: syscon@4b010000 {
 
 		lvds_csr: syscon@4b0c0000 {
 			compatible = "nxp,imx94-lvds-csr", "syscon";
+			#address-cells = <1>;
+			#size-cells = <1>;
 			reg = <0x0 0x4b0c0000 0x0 0x10000>;
 			clocks = <&scmi_clk IMX94_CLK_DISPAPB>;
 			#clock-cells = <1>;
 			power-domains = <&scmi_devpd IMX94_PD_DISPLAY>;
+
+			ldb: ldb@4 {
+				compatible = "fsl,imx94-ldb";
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0x4 0x4>, <0x8 0x4>;
+				reg-names = "ldb", "lvds";
+				clocks = <&lvds_csr IMX94_CLK_DISPMIX_LVDS_CLK_GATE>;
+				clock-names = "ldb";
+				status = "disabled";
+
+				ports {
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					port@0 {
+						reg = <0>;
+
+						lvds_in: endpoint {
+							remote-endpoint = <&dcif_out>;
+						};
+					};
+
+					port@1 {
+						reg = <1>;
+					};
+				};
+			};
+		};
+
+		dcif: display-controller@4b120000 {
+			compatible = "nxp,imx94-dcif";
+			reg = <0x0 0x4b120000 0x0 0x300000>;
+			interrupts = <GIC_SPI 377 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 378 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 379 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "common", "bg_layer", "fg_layer";
+			clocks = <&scmi_clk IMX94_CLK_DISPAPB>,
+				 <&scmi_clk IMX94_CLK_DISPAXI>,
+				 <&dispmix_csr IMX94_CLK_DISPMIX_CLK_SEL>;
+			clock-names = "apb", "axi", "pix";
+			assigned-clocks = <&dispmix_csr IMX94_CLK_DISPMIX_CLK_SEL>;
+			assigned-clock-parents = <&clock_ldb_pll_div7>;
+			power-domains = <&scmi_devpd IMX94_PD_DISPLAY>;
+			nxp,blk-ctrl = <&dispmix_csr>;
+			status = "disabled";
+
+			port {
+				dcif_out: endpoint {
+					remote-endpoint = <&lvds_in>;
+				};
+			};
 		};
 	};
 };
-- 
2.46.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 8/8] MAINTAINERS: Add entry for i.MX94 DCIF driver
  2025-07-09 12:23 [PATCH 0/8] Add support for i.MX94 DCIF Laurentiu Palcu
                   ` (6 preceding siblings ...)
  2025-07-09 12:23 ` [PATCH 7/8] arm64: dts: imx943-evk: Add support for DCIF and LVDS Laurentiu Palcu
@ 2025-07-09 12:23 ` Laurentiu Palcu
  2025-07-09 12:53   ` Daniel Baluta
  7 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Palcu @ 2025-07-09 12:23 UTC (permalink / raw)
  To: imx; +Cc: dri-devel, Laurentiu Palcu, linux-kernel

The driver is part of DRM subsystem and is located in
drivers/gpu/drm/imx/dcif.

Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 8be3e0fbe72b8..fbec7e42e693b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18066,6 +18066,14 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml
 F:	drivers/media/platform/nxp/imx-jpeg
 
+NXP i.MX 94 DCIF DRIVER
+M:	Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
+L:	dri-devel@lists.freedesktop.org
+S:	Maintained
+T:	git https://gitlab.freedesktop.org/drm/misc/kernel.git
+F:	Documentation/devicetree/bindings/display/imx/nxp,imx94-dcif.yaml
+F:	drivers/gpu/drm/imx/dcif/
+
 NXP i.MX CLOCK DRIVERS
 M:	Abel Vesa <abelvesa@kernel.org>
 R:	Peng Fan <peng.fan@nxp.com>
-- 
2.46.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH 8/8] MAINTAINERS: Add entry for i.MX94 DCIF driver
  2025-07-09 12:23 ` [PATCH 8/8] MAINTAINERS: Add entry for i.MX94 DCIF driver Laurentiu Palcu
@ 2025-07-09 12:53   ` Daniel Baluta
  0 siblings, 0 replies; 21+ messages in thread
From: Daniel Baluta @ 2025-07-09 12:53 UTC (permalink / raw)
  To: Laurentiu Palcu; +Cc: imx, dri-devel, linux-kernel

On Wed, Jul 9, 2025 at 3:26 PM Laurentiu Palcu
<laurentiu.palcu@oss.nxp.com> wrote:
>
> The driver is part of DRM subsystem and is located in
> drivers/gpu/drm/imx/dcif.
>
> Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> ---
>  MAINTAINERS | 8 ++++++++
>  1 file changed, 8 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8be3e0fbe72b8..fbec7e42e693b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -18066,6 +18066,14 @@ S:     Maintained
>  F:     Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml
>  F:     drivers/media/platform/nxp/imx-jpeg
>
> +NXP i.MX 94 DCIF DRIVER
> +M:     Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> +L:     dri-devel@lists.freedesktop.org

Please also add imx@lists.linux.dev

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 2/8] dt-bindings: display: fsl,ldb: Add binding for i.MX94
  2025-07-09 12:23 ` [PATCH 2/8] dt-bindings: display: fsl,ldb: Add binding for i.MX94 Laurentiu Palcu
@ 2025-07-10 22:15   ` Rob Herring
  0 siblings, 0 replies; 21+ messages in thread
From: Rob Herring @ 2025-07-10 22:15 UTC (permalink / raw)
  To: Laurentiu Palcu
  Cc: imx, Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter,
	Krzysztof Kozlowski, Conor Dooley, Marek Vasut, dri-devel,
	devicetree, linux-kernel

On Wed, Jul 09, 2025 at 03:23:21PM +0300, Laurentiu Palcu wrote:
> i.MX94 LDB controller is compatible with i.MX93.

That's not what the schema says.

> 
> Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> ---
>  Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml b/Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml
> index 07388bf2b90df..a54b8f1478049 100644
> --- a/Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml
> +++ b/Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml
> @@ -20,6 +20,7 @@ properties:
>        - fsl,imx6sx-ldb
>        - fsl,imx8mp-ldb
>        - fsl,imx93-ldb
> +      - fsl,imx94-ldb
>  
>    clocks:
>      maxItems: 1
> @@ -68,6 +69,7 @@ allOf:
>              enum:
>                - fsl,imx6sx-ldb
>                - fsl,imx93-ldb
> +              - fsl,imx94-ldb
>      then:
>        properties:
>          ports:
> -- 
> 2.46.1
> 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 5/8] dt-bindings: display: imx: Add bindings for i.MX94 DCIF
  2025-07-09 12:23 ` [PATCH 5/8] dt-bindings: display: imx: Add bindings for i.MX94 DCIF Laurentiu Palcu
@ 2025-07-10 22:18   ` Rob Herring
  0 siblings, 0 replies; 21+ messages in thread
From: Rob Herring @ 2025-07-10 22:18 UTC (permalink / raw)
  To: Laurentiu Palcu
  Cc: imx, Philipp Zabel, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, dri-devel, devicetree,
	linux-arm-kernel, linux-kernel

On Wed, Jul 09, 2025 at 03:23:24PM +0300, Laurentiu Palcu wrote:
> DCIF is the i.MX94 Display Controller Interface which is used to
> drive a TFT LCD panel or connects to a display interface depending
> on the chip configuration.

Drop 'bindings for' in the subject. You already said 'bindings' once.

> 
> Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> ---
>  .../bindings/display/imx/nxp,imx94-dcif.yaml  | 101 ++++++++++++++++++
>  1 file changed, 101 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/imx/nxp,imx94-dcif.yaml
> 
> diff --git a/Documentation/devicetree/bindings/display/imx/nxp,imx94-dcif.yaml b/Documentation/devicetree/bindings/display/imx/nxp,imx94-dcif.yaml
> new file mode 100644
> index 0000000000000..e4cab43c77ebc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/imx/nxp,imx94-dcif.yaml
> @@ -0,0 +1,101 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +# Copyright 2025 NXP
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/imx/nxp,imx94-dcif.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: i.MX94 Display Control Interface (DCIF)
> +
> +maintainers:
> +  - Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> +
> +description:
> +  The Display Control Interface(DCIF) is a system master that fetches graphics
> +  stored in memory and displays them on a TFT LCD panel or connects to a
> +  display interface depending on the chip configuration.
> +
> +properties:
> +  compatible:
> +    const: nxp,imx94-dcif
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    items:
> +      - description:
> +          Interrupt output for CPU domain 0 (controlled by common registers group).
> +      - description:
> +          Interrupt output for CPU domain 1 (controlled by background layer registers group).
> +      - description:
> +          Interrupt output for CPU domain 2 (controlled by foreground layer registers group).
> +
> +  interrupt-names:
> +    items:
> +      - const: common
> +      - const: bg_layer
> +      - const: fg_layer
> +
> +  clocks:
> +    items:
> +      - description: APB bus clock
> +      - description: AXI bus clock
> +      - description: Pixel clock
> +
> +  clock-names:
> +    items:
> +      - const: apb
> +      - const: axi
> +      - const: pix
> +
> +  assigned-clocks:
> +    description: Reference to DISPLAYMIX CSR clock mux.
> +    maxItems: 1
> +
> +  assigned-clock-parents:
> +    description: Parent of the clock mux.
> +    maxItems: 1

You don't have to document assigned-clocks. Allowed anywhere with 
'clocks'.

> +
> +  power-domains:
> +    maxItems: 1
> +
> +  nxp,blk-ctrl:
> +    $ref: /schemas/types.yaml#/definitions/phandle
> +    description: A phandle which points to NXP displaymix blk-ctrl.
> +
> +  port:
> +    $ref: /schemas/graph.yaml#/properties/port
> +    description: Display Pixel Interface(DPI) output port
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> +    soc {
> +        #address-cells = <2>;
> +        #size-cells = <2>;
> +
> +        display-controller@4b120000 {
> +            compatible = "nxp,imx94-dcif";
> +            reg = <0x0 0x4b120000 0x0 0x300000>;
> +            interrupts = <GIC_SPI 377 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 378 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 379 IRQ_TYPE_LEVEL_HIGH>;
> +            interrupt-names = "common", "bg_layer", "fg_layer";
> +            clocks = <&scmi_clk 69>, <&scmi_clk 70>, <&dispmix_csr 0>;
> +            clock-names = "apb", "axi", "pix";
> +            assigned-clocks = <&dispmix_csr 0>;
> +            assigned-clock-parents = <&ldb_pll_pixel>;
> +            power-domains = <&scmi_devpd 11>;
> +            nxp,blk-ctrl = <&dispmix_csr>;
> +
> +            port {
> +                dcif_out: endpoint {
> +                    remote-endpoint = <&ldb_in>;
> +                };
> +            };
> +        };
> +    };
> -- 
> 2.46.1
> 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 1/8] clk: imx95-blk-ctl: Cache registers when RPM routines are called
  2025-07-09 12:23 ` [PATCH 1/8] clk: imx95-blk-ctl: Cache registers when RPM routines are called Laurentiu Palcu
@ 2025-07-11  4:03   ` Frank Li
  2025-07-15 12:03     ` Laurentiu Palcu
  0 siblings, 1 reply; 21+ messages in thread
From: Frank Li @ 2025-07-11  4:03 UTC (permalink / raw)
  To: Laurentiu Palcu
  Cc: imx, Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd,
	Shawn Guo, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	dri-devel, Abel Vesa, linux-clk, linux-arm-kernel, linux-kernel

Subject:
save and store registers at suspend()/resume() function

On Wed, Jul 09, 2025 at 03:23:20PM +0300, Laurentiu Palcu wrote:
> If runtime PM is used for the clock providers and they're part of a
> power domain, then the power domain supply will be cut off when runtime
> suspended. That means all BLK CTL registers belonging to that power
> domain will be reset. Hence, the clock settings will revert to default
> values messing up the consumer clock settings.

Needn't "hence ..."

Save/restore register value at suspend/resume functions to fix this problem.

>
> Also, fix the suspend/resume routines as well, as the clock was left ON
> when going to suspend.

Do you means fix the problem clock left ON after suspend?

Pomain cut off, why clock can left on?

>
> Fixes: 5224b189462f ("clk: imx: add i.MX95 BLK CTL clk driver")
> Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> ---
>  drivers/clk/imx/clk-imx95-blk-ctl.c | 55 ++++++++++++++++++-----------
>  1 file changed, 34 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/clk/imx/clk-imx95-blk-ctl.c b/drivers/clk/imx/clk-imx95-blk-ctl.c
> index 7e88877a62451..7f9bbca517284 100644
> --- a/drivers/clk/imx/clk-imx95-blk-ctl.c
> +++ b/drivers/clk/imx/clk-imx95-blk-ctl.c
> @@ -448,12 +448,36 @@ static int imx95_bc_probe(struct platform_device *pdev)
>  	return ret;
>  }
>
> +static void __maybe_unused imx95_bc_save_reg(struct imx95_blk_ctl *bc)
> +{
> +	const struct imx95_blk_ctl_dev_data *bc_data;
> +
> +	bc_data = of_device_get_match_data(bc->dev);
> +	if (!bc_data)
> +		return;
> +
> +	bc->clk_reg_restore = readl(bc->base + bc_data->clk_reg_offset);
> +}
> +
> +static void __maybe_unused imx95_bc_restore_reg(struct imx95_blk_ctl *bc)
> +{
> +	const struct imx95_blk_ctl_dev_data *bc_data;
> +
> +	bc_data = of_device_get_match_data(bc->dev);

Generally, bc_data should in imx95_blk_ctl_dev_data and set once at probe.

So imx95_bc_save_reg() and imx95_bc_restore_reg() will be simpfied.

> +	if (!bc_data)
> +		return;
> +
> +	writel(bc->clk_reg_restore, bc->base + bc_data->clk_reg_offset);
> +}
> +
>  #ifdef CONFIG_PM
>  static int imx95_bc_runtime_suspend(struct device *dev)
>  {
>  	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
>
> +	imx95_bc_save_reg(bc);

this help function just one line. direct use

writel(bc->clk_reg_restore, bc->base + bc->bc_data->clk_reg_offset);

>  	clk_disable_unprepare(bc->clk_apb);
> +
>  	return 0;
>  }
>
> @@ -461,7 +485,10 @@ static int imx95_bc_runtime_resume(struct device *dev)
>  {
>  	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
>
> -	return clk_prepare_enable(bc->clk_apb);
> +	clk_prepare_enable(bc->clk_apb);

Need check ret value;

> +	imx95_bc_restore_reg(bc);
> +
> +	return 0;
>  }
>  #endif
>
> @@ -469,22 +496,12 @@ static int imx95_bc_runtime_resume(struct device *dev)
>  static int imx95_bc_suspend(struct device *dev)
>  {
>  	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
> -	const struct imx95_blk_ctl_dev_data *bc_data;
> -	int ret;
>
> -	bc_data = of_device_get_match_data(dev);
> -	if (!bc_data)
> +	if (pm_runtime_suspended(dev))
>  		return 0;
>
> -	if (bc_data->rpm_enabled) {
> -		ret = pm_runtime_get_sync(bc->dev);
> -		if (ret < 0) {
> -			pm_runtime_put_noidle(bc->dev);
> -			return ret;
> -		}
> -	}
> -
> -	bc->clk_reg_restore = readl(bc->base + bc_data->clk_reg_offset);
> +	imx95_bc_save_reg(bc);
> +	clk_disable_unprepare(bc->clk_apb);
>
>  	return 0;
>  }
> @@ -492,16 +509,12 @@ static int imx95_bc_suspend(struct device *dev)
>  static int imx95_bc_resume(struct device *dev)
>  {
>  	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
> -	const struct imx95_blk_ctl_dev_data *bc_data;
>
> -	bc_data = of_device_get_match_data(dev);
> -	if (!bc_data)
> +	if (pm_runtime_suspended(dev))
>  		return 0;
>
> -	writel(bc->clk_reg_restore, bc->base + bc_data->clk_reg_offset);
> -
> -	if (bc_data->rpm_enabled)
> -		pm_runtime_put(bc->dev);
> +	clk_prepare_enable(bc->clk_apb);
> +	imx95_bc_restore_reg(bc);
>
>  	return 0;
>  }

look like imx95_bc_suspend(resume) is simple enough

Can you use DEFINE_RUNTIME_DEV_PM_OPS?

Frank

> --
> 2.46.1
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 3/8] drm/bridge: fsl-ldb: Get the next non-panel bridge
  2025-07-09 12:23 ` [PATCH 3/8] drm/bridge: fsl-ldb: Get the next non-panel bridge Laurentiu Palcu
@ 2025-07-11  4:19   ` Frank Li
  2025-07-11  5:31     ` Liu Ying
  0 siblings, 1 reply; 21+ messages in thread
From: Frank Li @ 2025-07-11  4:19 UTC (permalink / raw)
  To: Laurentiu Palcu
  Cc: imx, Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, dri-devel,
	Liu Ying, linux-kernel

On Wed, Jul 09, 2025 at 03:23:22PM +0300, Laurentiu Palcu wrote:
> From: Liu Ying <victor.liu@nxp.com>
>
> The next bridge in bridge chain could be a panel bridge or a non-panel
> bridge.  Use devm_drm_of_get_bridge() to replace the combination
         ^ extra space.

> function calls of of_drm_find_panel() and devm_drm_panel_bridge_add()
> to get either a panel bridge or a non-panel bridge, instead of getting
> a panel bridge only.

According to code change, devm_drm_of_get_bridge() replace
devm_drm_panel_bridge_add(of_drm_find_panel()).

what relationship with panel bridge or a non-panel bridge?

Frank

>
> Signed-off-by: Liu Ying <victor.liu@nxp.com>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> ---
>  drivers/gpu/drm/bridge/fsl-ldb.c | 31 +++++++++++--------------------
>  1 file changed, 11 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c
> index 5c3cf37200bce..665053d0cb79d 100644
> --- a/drivers/gpu/drm/bridge/fsl-ldb.c
> +++ b/drivers/gpu/drm/bridge/fsl-ldb.c
> @@ -15,7 +15,6 @@
>  #include <drm/drm_atomic_helper.h>
>  #include <drm/drm_bridge.h>
>  #include <drm/drm_of.h>
> -#include <drm/drm_panel.h>
>
>  #define LDB_CTRL_CH0_ENABLE			BIT(0)
>  #define LDB_CTRL_CH0_DI_SELECT			BIT(1)
> @@ -86,7 +85,7 @@ static const struct fsl_ldb_devdata fsl_ldb_devdata[] = {
>  struct fsl_ldb {
>  	struct device *dev;
>  	struct drm_bridge bridge;
> -	struct drm_bridge *panel_bridge;
> +	struct drm_bridge *next_bridge;
>  	struct clk *clk;
>  	struct regmap *regmap;
>  	const struct fsl_ldb_devdata *devdata;
> @@ -118,7 +117,7 @@ static int fsl_ldb_attach(struct drm_bridge *bridge,
>  {
>  	struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
>
> -	return drm_bridge_attach(encoder, fsl_ldb->panel_bridge,
> +	return drm_bridge_attach(encoder, fsl_ldb->next_bridge,
>  				 bridge, flags);
>  }
>
> @@ -292,9 +291,7 @@ static const struct drm_bridge_funcs funcs = {
>  static int fsl_ldb_probe(struct platform_device *pdev)
>  {
>  	struct device *dev = &pdev->dev;
> -	struct device_node *panel_node;
>  	struct device_node *remote1, *remote2;
> -	struct drm_panel *panel;
>  	struct fsl_ldb *fsl_ldb;
>  	int dual_link;
>
> @@ -317,33 +314,27 @@ static int fsl_ldb_probe(struct platform_device *pdev)
>  	if (IS_ERR(fsl_ldb->regmap))
>  		return PTR_ERR(fsl_ldb->regmap);
>
> -	/* Locate the remote ports and the panel node */
> +	/* Locate the remote ports. */
>  	remote1 = of_graph_get_remote_node(dev->of_node, 1, 0);
>  	remote2 = of_graph_get_remote_node(dev->of_node, 2, 0);
>  	fsl_ldb->ch0_enabled = (remote1 != NULL);
>  	fsl_ldb->ch1_enabled = (remote2 != NULL);
> -	panel_node = of_node_get(remote1 ? remote1 : remote2);
>  	of_node_put(remote1);
>  	of_node_put(remote2);
>
> -	if (!fsl_ldb->ch0_enabled && !fsl_ldb->ch1_enabled) {
> -		of_node_put(panel_node);
> -		return dev_err_probe(dev, -ENXIO, "No panel node found");
> -	}
> +	if (!fsl_ldb->ch0_enabled && !fsl_ldb->ch1_enabled)
> +		return dev_err_probe(dev, -ENXIO, "No next bridge node found");
>
>  	dev_dbg(dev, "Using %s\n",
>  		fsl_ldb_is_dual(fsl_ldb) ? "dual-link mode" :
>  		fsl_ldb->ch0_enabled ? "channel 0" : "channel 1");
>
> -	panel = of_drm_find_panel(panel_node);
> -	of_node_put(panel_node);
> -	if (IS_ERR(panel))
> -		return PTR_ERR(panel);
> -
> -	fsl_ldb->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
> -	if (IS_ERR(fsl_ldb->panel_bridge))
> -		return PTR_ERR(fsl_ldb->panel_bridge);
> -
> +	fsl_ldb->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node,
> +						      fsl_ldb->ch0_enabled ? 1 : 2,
> +						      0);
> +	if (IS_ERR(fsl_ldb->next_bridge))
> +		return dev_err_probe(dev, PTR_ERR(fsl_ldb->next_bridge),
> +				     "failed to get next bridge\n");
>
>  	if (fsl_ldb_is_dual(fsl_ldb)) {
>  		struct device_node *port1, *port2;
> --
> 2.46.1
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 4/8] drm/bridge: fsl-ldb: Add support for i.MX94
  2025-07-09 12:23 ` [PATCH 4/8] drm/bridge: fsl-ldb: Add support for i.MX94 Laurentiu Palcu
@ 2025-07-11  4:26   ` Frank Li
  0 siblings, 0 replies; 21+ messages in thread
From: Frank Li @ 2025-07-11  4:26 UTC (permalink / raw)
  To: Laurentiu Palcu
  Cc: imx, Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, dri-devel,
	linux-kernel

On Wed, Jul 09, 2025 at 03:23:23PM +0300, Laurentiu Palcu wrote:
> Since i.MX94 series LDB controller is compatible, add support for it.

Add support for the LDB controller in the i.MX94 series. Compared to i.MX93,
the i.MX94 LDB uses different register addresses for ldb_ctrl and lvds_ctrl,
and supports a higher maximum clock frequency.

Introduce max_clk_khz in the driver data to handle the frequency difference.

Frank

>
> Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> ---
>  drivers/gpu/drm/bridge/fsl-ldb.c | 16 +++++++++++++++-
>  1 file changed, 15 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c
> index 665053d0cb79d..4052e1ea9201f 100644
> --- a/drivers/gpu/drm/bridge/fsl-ldb.c
> +++ b/drivers/gpu/drm/bridge/fsl-ldb.c
> @@ -57,6 +57,7 @@ enum fsl_ldb_devtype {
>  	IMX6SX_LDB,
>  	IMX8MP_LDB,
>  	IMX93_LDB,
> +	IMX94_LDB,
>  };
>
>  struct fsl_ldb_devdata {
> @@ -64,21 +65,31 @@ struct fsl_ldb_devdata {
>  	u32 lvds_ctrl;
>  	bool lvds_en_bit;
>  	bool single_ctrl_reg;
> +	u32 max_clk_khz;
>  };
>
>  static const struct fsl_ldb_devdata fsl_ldb_devdata[] = {
>  	[IMX6SX_LDB] = {
>  		.ldb_ctrl = 0x18,
>  		.single_ctrl_reg = true,
> +		.max_clk_khz = 80000,
>  	},
>  	[IMX8MP_LDB] = {
>  		.ldb_ctrl = 0x5c,
>  		.lvds_ctrl = 0x128,
> +		.max_clk_khz = 80000,
>  	},
>  	[IMX93_LDB] = {
>  		.ldb_ctrl = 0x20,
>  		.lvds_ctrl = 0x24,
>  		.lvds_en_bit = true,
> +		.max_clk_khz = 80000,
> +	},
> +	[IMX94_LDB] = {
> +		.ldb_ctrl = 0x04,
> +		.lvds_ctrl = 0x08,
> +		.lvds_en_bit = true,
> +		.max_clk_khz = 165000,
>  	},
>  };
>
> @@ -270,8 +281,9 @@ fsl_ldb_mode_valid(struct drm_bridge *bridge,
>  		   const struct drm_display_mode *mode)
>  {
>  	struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
> +	u32 ch_max_clk_khz = fsl_ldb->devdata->max_clk_khz;
>
> -	if (mode->clock > (fsl_ldb_is_dual(fsl_ldb) ? 160000 : 80000))
> +	if (mode->clock > (fsl_ldb_is_dual(fsl_ldb) ? 2 * ch_max_clk_khz : ch_max_clk_khz))
>  		return MODE_CLOCK_HIGH;
>
>  	return MODE_OK;
> @@ -377,6 +389,8 @@ static const struct of_device_id fsl_ldb_match[] = {
>  	  .data = &fsl_ldb_devdata[IMX8MP_LDB], },
>  	{ .compatible = "fsl,imx93-ldb",
>  	  .data = &fsl_ldb_devdata[IMX93_LDB], },
> +	{ .compatible = "fsl,imx94-ldb",
> +	  .data = &fsl_ldb_devdata[IMX94_LDB], },
>  	{ /* sentinel */ },
>  };
>  MODULE_DEVICE_TABLE(of, fsl_ldb_match);
> --
> 2.46.1
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 6/8] drm/imx: Add support for i.MX94 DCIF
  2025-07-09 12:23 ` [PATCH 6/8] drm/imx: Add support " Laurentiu Palcu
@ 2025-07-11  5:01   ` Frank Li
  2025-07-15 12:04     ` Laurentiu Palcu
  0 siblings, 1 reply; 21+ messages in thread
From: Frank Li @ 2025-07-11  5:01 UTC (permalink / raw)
  To: Laurentiu Palcu
  Cc: imx, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Shawn Guo, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, dri-devel, Sandor Yu,
	linux-kernel, linux-arm-kernel

On Wed, Jul 09, 2025 at 03:23:25PM +0300, Laurentiu Palcu wrote:
> From: Sandor Yu <sandor.yu@nxp.com>
>
> This patch adds support for the i.MX94 Display Control Interface.

Needn't word "this patch"

Can you list feature here?

>
> Signed-off-by: Sandor Yu <sandor.yu@nxp.com>
> Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> ---
...
> +static int
> +dcif_crc_parse_source(const char *source_name, enum dcif_crc_source *s,
> +		      struct drm_rect *roi)
> +{
> +	static const char roi_prefix[] = "roi:";
> +
> +	if (!source_name) {
> +		*s = DCIF_CRC_SRC_NONE;
> +	} else if (!strcmp(source_name, "auto")) {
> +		*s = DCIF_CRC_SRC_FRAME;
> +	} else if (strstarts(source_name, roi_prefix)) {
> +		char *options, *opt;
> +		int len = strlen(roi_prefix);
> +		int params[4];
> +		int i = 0, ret;
> +
> +		options = kstrdup(source_name + len, GFP_KERNEL);

where free options?

> +
> +		while ((opt = strsep(&options, ",")) != NULL) {
> +			if (i > 3)
> +				return -EINVAL;
> +
> +			ret = kstrtouint(opt, 10, &params[i]);
> +			if (ret < 0)
> +				return ret;
> +
> +			if (params[i] < 0)
> +				return -EINVAL;
> +
> +			i++;
> +		}
....
> +	/* Perform an atomic commit to set the CRC source. */
> +	drm_modeset_acquire_init(&ctx, 0);
> +
> +	state = drm_atomic_state_alloc(crtc->dev);
> +	if (!state) {
> +		ret = -ENOMEM;
> +		goto unlock;
> +	}
> +
> +	state->acquire_ctx = &ctx;
> +
> +retry:
> +	crtc_state = drm_atomic_get_crtc_state(state, crtc);
> +	if (!IS_ERR(crtc_state)) {
> +		struct dcif_crtc_state *dcif_crtc_state;
> +
> +		dcif_crtc_state = to_dcif_crtc_state(crtc_state);
> +
> +		dcif_crtc_state->crc.source = source;
> +		dcif_copy_roi(&roi, &dcif_crtc_state->crc.roi);
> +
> +		ret = drm_atomic_commit(state);
> +	} else {
> +		ret = PTR_ERR(crtc_state);
> +	}
> +
> +	if (ret == -EDEADLK) {
> +		drm_atomic_state_clear(state);
> +		drm_modeset_backoff(&ctx);
> +		goto retry;

Need max retry time, otherwise may dead loop here.

> +	}
> +
> +	drm_atomic_state_put(state);
> +
> +unlock:
> +	drm_modeset_drop_locks(&ctx);
> +	drm_modeset_acquire_fini(&ctx);
> +
> +	return ret;
> +}
> +
> diff --git a/drivers/gpu/drm/imx/dcif/dcif-crc.h b/drivers/gpu/drm/imx/dcif/dcif-crc.h
> new file mode 100644
> index 0000000000000..ea1b18362a344
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/dcif/dcif-crc.h
> @@ -0,0 +1,52 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
....
> +
> +static void dcif_crtc_atomic_disable(struct drm_crtc *crtc,
> +				     struct drm_atomic_state *state)
> +{
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +	struct dcif_crtc_state *dcif_crtc_state = to_dcif_crtc_state(crtc_state);
> +	struct dcif_dev *dcif = crtc_to_dcif_dev(crtc);
> +	struct drm_device *drm = crtc->dev;
> +
> +	if (dcif->has_crc && dcif_crtc_state->crc.source != DCIF_CRC_SRC_NONE)
> +		dcif_crtc_disable_crc_source(dcif, 0);
> +
> +	dcif_disable_controller(dcif);
> +	dcif_disable_plane_panic(dcif);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	pm_runtime_put_sync(drm->dev);
> +
> +	spin_lock_irq(&crtc->dev->event_lock);

suggest use scope_guard(spin_lock_irq)(&crtc->dev->event_lock)

check other place.

> +	if (crtc->state->event && !crtc->state->active) {
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		crtc->state->event = NULL;
> +	}
> +	spin_unlock_irq(&crtc->dev->event_lock);
> +}
> +
....
> +
> +static int dcif_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct dcif_dev *dcif;
> +	struct drm_device *drm;
> +	int ret;
> +	int i;
> +
> +	if (!pdev->dev.of_node)
> +		return -ENODEV;
> +
> +	dcif = devm_drm_dev_alloc(&pdev->dev, &dcif_driver, struct dcif_dev, drm);
> +	if (IS_ERR(dcif))
> +		return PTR_ERR(dcif);
> +
> +	/* CPU 0 domain for interrupt control */
> +	dcif->cpu_domain = DCIF_CPU_DOMAIN;
> +
> +	drm = &dcif->drm;
> +	dev_set_drvdata(&pdev->dev, dcif);
> +
> +	dcif->reg_base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(dcif->reg_base))
> +		return dev_err_probe(drm->dev, PTR_ERR(dcif->reg_base),
> +				     "failed to get reg base\n");
> +
> +	dcif->irq[0] = platform_get_irq(pdev, 0);
> +	if (dcif->irq[0] < 0)
> +		return dev_err_probe(drm->dev, dcif->irq[0],
> +				     "failed to get domain0 irq\n");
> +
> +	dcif->irq[1] = platform_get_irq(pdev, 1);
> +	if (dcif->irq[1] < 0)
> +		return dev_err_probe(drm->dev, dcif->irq[1],
> +				     "failed to get domain1 irq\n");
> +
> +	dcif->irq[2] = platform_get_irq(pdev, 2);
> +	if (dcif->irq[2] < 0)
> +		return dev_err_probe(drm->dev, dcif->irq[2],
> +				     "failed to get domain2 irq\n");

can you use loop for above 3 irqs.

> +
> +	dcif->blkctrl_regmap = syscon_regmap_lookup_by_phandle(np, "nxp,blk-ctrl");
> +	if (IS_ERR(dcif->blkctrl_regmap))
> +		return dev_err_probe(drm->dev, PTR_ERR(dcif->blkctrl_regmap),
> +				     "failed to get blk-ctrl regmap\n");

You just set QOS value by blkctrl_regmap. Is it possible to set it blk-ctrl
drivers? Or do it when power on the power domain to avoid use customer's
syscon to other nodes.

Frank

> +
> +	dcif->regmap = devm_regmap_init_mmio(drm->dev, dcif->reg_base, &dcif_regmap_config);
> +	if (IS_ERR(dcif->regmap))
> +		return dev_err_probe(drm->dev, PTR_ERR(dcif->regmap),
> +				     "failed to init DCIF regmap\n");
> +
> +	dcif->num_clks = ARRAY_SIZE(dcif_clks);
> +	dcif->clks = devm_kcalloc(drm->dev, dcif->num_clks, sizeof(*dcif->clks), GFP_KERNEL);
> +	if (!dcif->clks)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < dcif->num_clks; i++)
> +		dcif->clks[i].id = dcif_clks[i];
> +
> +	ret = devm_clk_bulk_get(drm->dev, dcif->num_clks, dcif->clks);
> +	if (ret)
> +		return dev_err_probe(drm->dev, ret, "cannot get required clocks\n");
> +
> +	ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
> +	if (ret)
> +		return dev_err_probe(drm->dev, ret, "failed to set dma mask and coherent\n");

When DMA_BIT_MASK(32) > 32, never return failure. Needn't check this
return value.

> +
> +	pm_runtime_enable(drm->dev);

devm_pm_runtime_enable()

> +
> +	ret = devm_request_irq(drm->dev, dcif->irq[dcif->cpu_domain],
> +			       dcif_irq_handler, 0, drm->driver->name, drm);
> +	if (ret < 0) {
> +		dev_err(drm->dev, "failed to install IRQ handler: %d\n", ret);
> +		goto err_irq_install;
> +	}
> +
> +	dcif_read_chip_info(dcif);
> +
> +	ret = dcif_kms_prepare(dcif);
> +	if (ret)
> +		goto err_irq_install;
> +
> +	ret = drm_dev_register(drm, 0);
> +	if (ret) {
> +		dev_err(drm->dev, "failed to register drm device: %d\n", ret);
> +		goto err_register;
> +	}
> +
> +	drm_client_setup(drm, NULL);
> +
> +	return 0;
> +
> +err_register:
> +	drm_kms_helper_poll_fini(drm);

use devm_add_action_or_reset to avoid goto here.

> +err_irq_install:
> +	pm_runtime_disable(drm->dev);
> +	return ret;
> +}
> +
> +static void dcif_remove(struct platform_device *pdev)
> +{
> +	struct dcif_dev *dcif = dev_get_drvdata(&pdev->dev);
> +	struct drm_device *drm = &dcif->drm;
> +
> +	drm_dev_unregister(drm);
> +
> +	drm_kms_helper_poll_fini(drm);
> +
> +	drm_atomic_helper_shutdown(drm);
> +
> +	pm_runtime_disable(drm->dev);
> +}
> +
> +static int dcif_runtime_suspend(struct device *dev)
> +{
> +	struct dcif_dev *dcif = dev_get_drvdata(dev);
> +
> +	clk_bulk_disable_unprepare(dcif->num_clks, dcif->clks);
> +
> +	return 0;
> +}
> +
> +static int dcif_runtime_resume(struct device *dev)
> +{
> +	struct dcif_dev *dcif = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = clk_bulk_prepare_enable(dcif->num_clks, dcif->clks);
> +	if (ret) {
> +		dev_err(dev, "failed to enable clocks: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = dcif_set_qos(dcif);
> +	if (ret) {
> +		clk_bulk_disable_unprepare(dcif->num_clks, dcif->clks);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dcif_suspend(struct device *dev)
> +{
> +	struct dcif_dev *dcif = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = drm_mode_config_helper_suspend(&dcif->drm);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (pm_runtime_suspended(dev))
> +		return 0;
> +
> +	return dcif_runtime_suspend(dev);
> +}
> +
> +static int dcif_resume(struct device *dev)
> +{
> +	struct dcif_dev *dcif = dev_get_drvdata(dev);
> +	int ret;
> +
> +	if (!pm_runtime_suspended(dev)) {
> +		ret = dcif_runtime_resume(dev);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return drm_mode_config_helper_resume(&dcif->drm);
> +}
> +
> +static const struct dev_pm_ops dcif_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(dcif_suspend, dcif_resume)
> +	SET_RUNTIME_PM_OPS(dcif_runtime_suspend, dcif_runtime_resume, NULL)
> +};
> +
> +static const struct of_device_id dcif_dt_ids[] = {
> +	{ .compatible = "nxp,imx94-dcif", },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, dcif_dt_ids);
> +
> +static struct platform_driver dcif_platform_driver = {
> +	.probe	= dcif_probe,
> +	.remove	= dcif_remove,
> +	.driver	= {
> +		.name		= DRIVER_NAME,
> +		.of_match_table	= dcif_dt_ids,
> +		.pm		= pm_ptr(&dcif_pm_ops),
> +	},
> +};
> +module_platform_driver(dcif_platform_driver);
> +
...
> + * Copyright 2025 NXP
> + */
> +#ifndef __DCIF_REG_H__
> +#define __DCIF_REG_H__
> +
> +#include <linux/bits.h>
> +
> +/* Version ID Register */
> +#define DCIF_VER				0x0
> +
> +/* Parameter Registers */
> +#define DCIF_PAR_0				0x4
> +#define DCIF_PAR_1				0x8
> +
> +/* Display Control and Parameter Registers */
> +#define DCIF_DISP_CTRL				0x10
> +#define DCIF_DISP_PAR				0x14
> +#define DCIF_DISP_SIZE				0x18
> +
> +/* Display Status Registers */
> +#define DCIF_DISP_SR0				0x1C
> +#define DCIF_DISP_SR1				0x20
> +
> +/* Interrupt Enable and Status Registers, n=0-2*/
> +#define DCIF_IE0(n)				(0x24 + (n) * 0x10000)
> +#define DCIF_IS0(n)				(0x28 + (n) * 0x10000)
> +#define DCIF_IE1(n)				(0x2C + (n) * 0x10000)
> +#define DCIF_IS1(n)				(0x30 + (n) * 0x10000)
> +
> +/* DPI Control and Sync Parameter Registers */
> +#define DCIF_DPI_CTRL				0x40
> +#define DCIF_DPI_HSYN_PAR			0x44
> +#define DCIF_DPI_VSYN_PAR			0x48
> +#define DCIF_DPI_VSYN_HSYN_WIDTH		0x4C
> +
> +/* Control Descriptor Registers, n=0-1*/
> +#define DCIF_CTRLDESC0(n)			(0x10000 + (n) * 0x10000)
> +#define DCIF_CTRLDESC1(n)			(0x10004 + (n) * 0x10000)
> +#define DCIF_CTRLDESC2(n)			(0x10008 + (n) * 0x10000)
> +#define DCIF_CTRLDESC3(n)			(0x1000C + (n) * 0x10000)
> +#define DCIF_CTRLDESC4(n)			(0x10010 + (n) * 0x10000)
> +#define DCIF_CTRLDESC5(n)			(0x10014 + (n) * 0x10000)
> +#define DCIF_CTRLDESC6(n)			(0x10018 + (n) * 0x10000)
> +
> +/* CLUT control Register */
> +#define DCIF_CLUT_CTRL				0x1003C
> +
> +/* FIFO Panic Threshold Register, n=0-1 */
> +#define DCIF_PANIC_THRES(n)			(0x10040 + (n) * 0x10000)
> +
> +/* Layer Status Register 0, n=0-1 */
> +#define DCIF_LAYER_SR0(n)			(0x10044 + (n) * 0x10000)
> +
> +/* Color Space Conversion Control and Coefficient Registers for Layer 0 */
> +#define DCIF_CSC_CTRL_L0			0x10050
> +#define DCIF_CSC_COEF0_L0			0x10054
> +#define DCIF_CSC_COEF1_L0			0x10058
> +#define DCIF_CSC_COEF2_L0			0x1005C
> +#define DCIF_CSC_COEF3_L0			0x10060
> +#define DCIF_CSC_COEF4_L0			0x10064
> +#define DCIF_CSC_COEF5_L0			0x10068
> +
> +/* CRC Control, Threshold, and Histogram Coefficient Registers */
> +#define DCIF_CRC_CTRL				0x20100
> +#define DCIF_CRC_THRES				0x20104
> +#define DCIF_CRC_HIST_COEF			0x20108
> +#define DCIF_CRC_ERR_CNT			0x2010C
> +#define DCIF_CRC_SR				0x20110
> +#define DCIF_CRC_HIST_CNT_B(n)			(0x20114 + (n) * 4)
> +
> +/* CRC Region Position, Size, Value, and Expected Value Registers, n=0-3 */
> +#define DCIF_CRC_POS_R(n)			(0x20214 + (n) * 0x10)
> +#define DCIF_CRC_SIZE_R(n)			(0x20218 + (n) * 0x10)
> +#define DCIF_CRC_VAL_R(n)			(0x2021C + (n) * 0x10)
> +#define DCIF_CRC_EXP_VAL_R(n)			(0x20220 + (n) * 0x10)
> +
> +/* VER - Version ID Register */
> +#define DCIF_VER_GET_FEATURE(x)			FIELD_GET(GENMASK(15, 0), x)
> +#define DCIF_VER_GET_MINOR(x)			FIELD_GET(GENMASK(23, 16), x)
> +#define DCIF_VER_GET_MAJOR(x)			FIELD_GET(GENMASK(31, 24), x)

can you move it close register offset define.

Frank

> +
> +/* PAR_0 - Parameter Register 0 */
> +#define DCIF_PAR_0_LAYER_NUM(x)			FIELD_PREP(GENMASK(3, 0), x)
> +#define DCIF_PAR_0_DOMAIN_NUM(x)		FIELD_PREP(GENMASK(5, 4), x)
> +#define DCIF_PAR_0_AXI_DATA_WIDTH(x)		FIELD_PREP(GENMASK(7, 6), x)
> +#define DCIF_PAR_0_CLUT_RAM_NUM(x)		FIELD_PREP(GENMASK(11, 8), x)
> +#define DCIF_PAR_0_CSC_NUM(x)			FIELD_PREP(GENMASK(13, 12), x)
> +#define DCIF_PAR_0_CRC_REGION_NUM(x)		FIELD_PREP(GENMASK(18, 16), x)
> +#define DCIF_PAR_0_BACKUP(x)			FIELD_PREP(GENMASK(31, 28), x)
> +
> +/* PAR_1 - Parameter Register 1 */
> +#define DCIF_PAR_1_LAYER0_FIFO_SIZE(x)		FIELD_PREP(GENMASK(3, 0), x)
> +#define DCIF_PAR_1_LAYER1_FIFO_SIZE(x)		FIELD_PREP(GENMASK(7, 4), x)
> +
> +/* DISP_CTRL - Display Control Register */
> +#define DCIF_DISP_CTRL_DISP_ON			BIT(0)
> +#define DCIF_DISP_CTRL_AXI_RD_HOLD		BIT(30)
> +#define DCIF_DISP_CTRL_SW_RST			BIT(31)
> +
> +/* DISP_PAR - Display Parameter Register */
> +#define DCIF_DISP_PAR_BGND_B(x)			FIELD_PREP(GENMASK(7, 0), x)
> +#define DCIF_DISP_PAR_BGND_G(x)			FIELD_PREP(GENMASK(15, 8), x)
> +#define DCIF_DISP_PAR_BGND_R(x)			FIELD_PREP(GENMASK(23, 16), x)
> +
> +/* DISP_SIZE - Display Size Register */
> +#define DCIF_DISP_SIZE_DISP_WIDTH(x)		FIELD_PREP(GENMASK(11, 0), x)
> +#define DCIF_DISP_SIZE_DISP_HEIGHT(x)		FIELD_PREP(GENMASK(27, 16), x)
> +
> +/* DISP_SR0 - Display Status Register 0 */
> +#define DCIF_DISP_SR0_AXI_RD_PEND(x)		FIELD_PREP(GENMASK(4, 0), x)
> +#define DCIF_DISP_SR0_DPI_BUSY(x)		FIELD_PREP(GENMASK(14, 14), x)
> +#define DCIF_DISP_SR0_AXI_RD_BUSY(x)		FIELD_PREP(GENMASK(15, 15), x)
> +#define DCIF_DISP_SR0_TXFIFO_CNT(x)		FIELD_PREP(GENMASK(23, 16), x)
> +
> +/* DISP_SR1 - Display Status Register 1 */
> +#define DCIF_DISP_SR1_H_CNT(x)			FIELD_PREP(GENMASK(11, 0), x)
> +#define DCIF_DISP_SR1_V_CNT(x)			FIELD_PREP(GENMASK(27, 16), x)
> +
> +/* INT0 - Interrupt Enable/Status  Register 0 for Domain 0/1/2 */
> +#define DCIF_INT0_VSYNC				BIT(0)
> +#define DCIF_INT0_UNDERRUN			BIT(1)
> +#define DCIF_INT0_VS_BLANK			BIT(2)
> +#define DCIF_INT0_HIST_DONE			BIT(5)
> +#define DCIF_INT0_CRC_ERR			BIT(6)
> +#define DCIF_INT0_CRC_ERR_SAT			BIT(7)
> +
> +/* INT1 - Interrupt Enable/Status Register 1 for Domain 0/1/2 */
> +#define DCIF_INT1_FIFO_PANIC0			BIT(0)
> +#define DCIF_INT1_FIFO_PANIC1			BIT(1)
> +#define DCIF_INT1_DMA_ERR0			BIT(8)
> +#define DCIF_INT1_DMA_ERR1			BIT(9)
> +#define DCIF_INT1_DMA_DONE0			BIT(16)
> +#define DCIF_INT1_DMA_DONE1			BIT(17)
> +#define DCIF_INT1_FIFO_EMPTY0			BIT(24)
> +#define DCIF_INT1_FIFO_EMPTY1			BIT(25)
> +
> +/* DPI_CTRL - DPI Control Register */
> +#define DCIF_DPI_CTRL_HSYNC_POL_LOW		BIT(0)
> +#define DCIF_DPI_CTRL_VSYNC_POL_LOW		BIT(1)
> +#define DCIF_DPI_CTRL_DE_POL_LOW		BIT(2)
> +#define DCIF_DPI_CTRL_PCLK_EDGE_FALLING		BIT(3)
> +#define DCIF_DPI_CTRL_POL_MASK			GENMASK(3, 0)
> +
> +#define DCIF_DPI_CTRL_DATA_INV(x)		FIELD_PREP(GENMASK(4, 4), x)
> +#define DCIF_DPI_CTRL_DEF_BGND_EN(x)		FIELD_PREP(GENMASK(5, 5), x)
> +#define DCIF_DPI_CTRL_FETCH_OPT(x)		FIELD_PREP(GENMASK(9, 8), x)
> +#define DCIF_DPI_CTRL_DISP_MODE(x)		FIELD_PREP(GENMASK(13, 12), x)
> +#define DCIF_DPI_CTRL_DATA_PATTERN_MASK		GENMASK(18, 16)
> +#define DCIF_DPI_CTRL_DATA_PATTERN(x)		FIELD_PREP(GENMASK(18, 16), x)
> +#define PATTERN_RGB888				0
> +#define PATTERN_RBG888				1
> +#define PATTERN_GBR888				2
> +#define PATTERN_GRB888				3
> +#define PATTERN_BRG888				4
> +#define PATTERN_BGR888				5
> +#define PATTERN_RGB555				6
> +#define PATTERN_RGB565				7
> +
> +/* DPI_HSYN_PAR - DPI Horizontal Sync Parameter Register */
> +#define DCIF_DPI_HSYN_PAR_FP_H(x)		FIELD_PREP(GENMASK(11, 0), x)
> +#define DCIF_DPI_HSYN_PAR_BP_H(x)		FIELD_PREP(GENMASK(27, 16), x)
> +
> +/* DPI_VSYN_PAR - DPI Vertical Sync Parameter Register */
> +#define DCIF_DPI_VSYN_PAR_FP_V(x)		FIELD_PREP(GENMASK(11, 0), x)
> +#define DCIF_DPI_VSYN_PAR_BP_V(x)		FIELD_PREP(GENMASK(27, 16), x)
> +
> +/* DPI_VSYN_HSYN_WIDTH - DPI Vertical and Horizontal Pulse Width Parameter Register */
> +#define DCIF_DPI_VSYN_HSYN_WIDTH_PW_H(x)	FIELD_PREP(GENMASK(11, 0), x)
> +#define DCIF_DPI_VSYN_HSYN_WIDTH_PW_V(x)	FIELD_PREP(GENMASK(27, 16), x)
> +
> +/* CTRLDESC0 - Control Descriptor Register 0 */
> +#define DCIF_CTRLDESC0_AB_MODE(x)		FIELD_PREP(GENMASK(1, 0), x)
> +#define ALPHA_EMBEDDED				0
> +#define ALPHA_GLOBAL				1
> +#define DCIF_CTRLDESC0_YUV_FORMAT_MASK		GENMASK(15, 14)
> +#define DCIF_CTRLDESC0_YUV_FORMAT(x)		FIELD_PREP(GENMASK(15, 14), x)
> +#define CTRLDESCL0_YUV_FORMAT_Y2VY1U		0x0
> +#define CTRLDESCL0_YUV_FORMAT_Y2UY1V		0x1
> +#define CTRLDESCL0_YUV_FORMAT_VY2UY1		0x2
> +#define CTRLDESCL0_YUV_FORMAT_UY2VY1		0x3
> +#define DCIF_CTRLDESC0_GLOBAL_ALPHA(x)		FIELD_PREP(GENMASK(23, 16), x)
> +#define DCIF_CTRLDESC0_FORMAT_MASK		GENMASK(27, 24)
> +#define DCIF_CTRLDESC0_FORMAT(x)		FIELD_PREP(GENMASK(27, 24), x)
> +#define CTRLDESCL0_FORMAT_RGB565		0x4
> +#define CTRLDESCL0_FORMAT_ARGB1555		0x5
> +#define CTRLDESCL0_FORMAT_ARGB4444		0x6
> +#define CTRLDESCL0_FORMAT_YCBCR422		0x7
> +#define CTRLDESCL0_FORMAT_RGB888		0x8
> +#define CTRLDESCL0_FORMAT_ARGB8888		0x9
> +#define CTRLDESCL0_FORMAT_ABGR8888		0xa
> +#define DCIF_CTRLDESC0_SHADOW_LOAD_EN		BIT(30)
> +#define DCIF_CTRLDESC0_EN			BIT(31)
> +
> +/* CTRLDESC1 - Control Descriptor Register 1 */
> +#define DCIF_CTRLDESC1_POSX(x)			FIELD_PREP(GENMASK(11, 0), x)
> +#define DCIF_CTRLDESC1_POSY(x)			FIELD_PREP(GENMASK(27, 16), x)
> +
> +/* CTRLDESC2 - Control Descriptor Register */
> +#define DCIF_CTRLDESC2_WIDTH(x)			FIELD_PREP(GENMASK(11, 0), x)
> +#define DCIF_CTRLDESC2_HEIGHT(x)		FIELD_PREP(GENMASK(27, 16), x)
> +
> +/* CTRLDESC3 - Control Descriptor Register 3 */
> +#define DCIF_CTRLDESC3_PITCH(x)			FIELD_PREP(GENMASK(15, 0), x)
> +#define DCIF_CTRLDESC3_T_SIZE(x)		FIELD_PREP(GENMASK(17, 16), x)
> +#define DCIF_CTRLDESC3_P_SIZE(x)		FIELD_PREP(GENMASK(22, 20), x)
> +
> +/* CTRLDESC4 - Control Descriptor Register 4 */
> +#define DCIF_CTRLDESC4_ADDR(x)FIELD_PREP(GENMASK(31, 0), x)
> +
> +/* CTRLDESC6 - Control Descriptor Register 6  */
> +#define DCIF_CTRLDESC6_BCLR_B(x)		FIELD_PREP(GENMASK(7, 0), x)
> +#define DCIF_CTRLDESC6_BCLR_G(x)		FIELD_PREP(GENMASK(15, 8), x)
> +#define DCIF_CTRLDESC6_BCLR_R(x)		FIELD_PREP(GENMASK(23, 16), x)
> +#define DCIF_CTRLDESC6_BCLR_A(x)		FIELD_PREP(GENMASK(31, 24), x)
> +
> +/* CLUT_CTRL - CLUT control Register */
> +#define DCIF_CLUT_CTRL_CLUT0_SEL(x)		FIELD_PREP(GENMASK(0, 0), x)
> +#define DCIF_CLUT_CTRL_CLUT1_SEL(x)		FIELD_PREP(GENMASK(3, 3), x)
> +#define DCIF_CLUT_CTRL_CLUT_MUX(x)		FIELD_PREP(GENMASK(29, 28), x)
> +#define DCIF_CLUT_CTRL_CLUT_SHADOW_LOAD_EN(x)	FIELD_PREP(GENMASK(31, 31), x)
> +
> +/* PANIC_THRES_L0 - FIFO Panic Threshold Register For Layer 0 */
> +#define DCIF_PANIC_THRES_LOW_MASK		GENMASK(11, 0)
> +#define DCIF_PANIC_THRES_LOW(x)			FIELD_PREP(GENMASK(11, 00), x)
> +#define DCIF_PANIC_THRES_HIGH_MASK		GENMASK(27, 16)
> +#define DCIF_PANIC_THRES_HIGH(x)		FIELD_PREP(GENMASK(27, 16), x)
> +#define DCIF_PANIC_THRES_REQ_EN			BIT(31)
> +#define PANIC0_THRES_MAX			511
> +
> +/* LAYER_SR0_L0 - Layer Status Register 0 for Layer 0 */
> +#define DCIF_LAYER_SR0_L0_FIFO_CNT_MASK		GENMASK(9, 0)
> +#define DCIF_LAYER_SR0_L0_FIFO_CNT(x)		FIELD_PREP(GENMASK(9, 0), x)
> +
> +/* CSC_CTRL_L0 - Color Space Conversion Control Register For Layer 0 */
> +#define DCIF_CSC_CTRL_L0_CSC_EN			BIT(0)
> +#define DCIF_CSC_CTRL_L0_CSC_MODE_YCBCR2RGB	BIT(1)
> +
> +/* CSC_COEF0_L0 - Color Space Conversion Coefficient Register 0 For Layer 0 */
> +#define DCIF_CSC_COEF0_L0_A1(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
> +#define DCIF_CSC_COEF0_L0_A2(x)			FIELD_PREP_CONST(GENMASK(26, 16), x)
> +
> +/* CSC_COEF1_L0 - Color Space Conversion Coefficient Register 1 For Layer 0 */
> +#define DCIF_CSC_COEF1_L0_A3(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
> +#define DCIF_CSC_COEF1_L0_B1(x)			FIELD_PREP_CONST(GENMASK(26, 16), x)
> +
> +/* CSC_COEF2_L0 - Color Space Conversion Coefficient Register 2 For Layer 0 */
> +#define DCIF_CSC_COEF2_L0_B2(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
> +#define DCIF_CSC_COEF2_L0_B3(x)			FIELD_PREP_CONST(GENMASK(26, 16), x)
> +
> +/* CSC_COEF3_L0 - Color Space Conversion Coefficient Register 3 For Layer 0 */
> +#define DCIF_CSC_COEF3_L0_C1(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
> +#define DCIF_CSC_COEF3_L0_C2(x)			FIELD_PREP_CONST(GENMASK(26, 16), x)
> +
> +/* CSC_COEF4_L0 - Color Space Conversion Coefficient Register 4 For Layer 0 */
> +#define DCIF_CSC_COEF4_L0_C3(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
> +#define DCIF_CSC_COEF4_L0_D1(x)			FIELD_PREP_CONST(GENMASK(24, 16), x)
> +
> +/* CSC_COEF5_L0 - Color Space Conversion Coefficient Register 5 For Layer 0 */
> +#define DCIF_CSC_COEF5_L0_D2(x)			FIELD_PREP_CONST(GENMASK(8, 0), x)
> +#define DCIF_CSC_COEF5_L0_D3(x)			FIELD_PREP_CONST(GENMASK(24, 16), x)
> +
> +/* CTRLDESC0_L1 - Control Descriptor Register 0 for Layer 1 */
> +#define DCIF_CTRLDESC0_L1_AB_MODE_MASK		GENMASK(1, 0)
> +#define DCIF_CTRLDESC0_L1_AB_MODE(x)		FIELD_PREP(GENMASK(1, 0), x)
> +
> +/* CRC_CTRL - CRC Control Register */
> +#define DCIF_CRC_CTRL_CRC_EN(x)			(1 << (x))
> +#define DCIF_CRC_CTRL_HIST_REGION_SEL(x)	FIELD_PREP(GENMASK(17, 16), x)
> +#define DCIF_CRC_CTRL_HIST_MODE			BIT(21)
> +#define DCIF_CRC_CTRL_HIST_TRIG			BIT(22)
> +#define DCIF_CRC_CTRL_HIST_EN			BIT(23)
> +#define DCIF_CRC_CTRL_CRC_MODE			BIT(28)
> +#define DCIF_CRC_CTRL_CRC_TRIG			BIT(29)
> +#define DCIF_CRC_CTRL_CRC_ERR_CNT_RST		BIT(30)
> +#define DCIF_CRC_CTRL_CRC_SHADOW_LOAD_EN	BIT(31)
> +
> +/* CRC_THRES - CRC Threshold Register */
> +#define DCIF_CRC_THRES_CRC_THRESHOLD_MASK	GENMASK(31, 0)
> +#define DCIF_CRC_THRES_CRC_THRESHOLD(x)		FIELD_PREP(GENMASK(31, 0), x)
> +
> +/* CRC_HIST_COEF - CRC Region Histogram Coefficient Register */
> +#define DCIF_CRC_HIST_COEF_HIST_WB_MASK		GENMASK(7, 0)
> +#define DCIF_CRC_HIST_COEF_HIST_WB(x)		FIELD_PREP(GENMASK(7, 0), x)
> +#define DCIF_CRC_HIST_COEF_HIST_WG_MASK		GENMASK(15, 8)
> +#define DCIF_CRC_HIST_COEF_HIST_WG(x)		FIELD_PREP(GENMASK(15, 8), x)
> +#define DCIF_CRC_HIST_COEF_HIST_WR_MASK		GENMASK(23, 16)
> +#define DCIF_CRC_HIST_COEF_HIST_WR(x)		FIELD_PREP(GENMASK(23, 16), x)
> +
> +/* CRC_ERR_CNT - CRC Error Counter Register */
> +#define DCIF_CRC_ERR_CNT_CRC_ERR_CNT_MASK	GENMASK(31, 0)
> +#define DCIF_CRC_ERR_CNT_CRC_ERR_CNT(x)		FIELD_PREP(GENMASK(31, 0), x)
> +
> +/* CRC_SR - CRC Status Register */
> +#define DCIF_CRC_SR_HIST_CNT_SAT_MASK		BIT(13)
> +#define DCIF_CRC_SR_HIST_CNT_SAT(x)		FIELD_PREP(GENMASK(13, 13), x)
> +#define DCIF_CRC_SR_HIST_SAT_MASK		BIT(14)
> +#define DCIF_CRC_SR_HIST_SAT(x)			FIELD_PREP(GENMASK(14, 14), x)
> +#define DCIF_CRC_SR_HIST_BUSY_MASK		BIT(15)
> +#define DCIF_CRC_SR_HIST_BUSY(x)		FIELD_PREP(GENMASK(15, 15), x)
> +#define DCIF_CRC_SR_CRC_STATUS_MASK		BIT(31)
> +#define DCIF_CRC_SR_CRC_STATUS(x)		FIELD_PREP(GENMASK(31, 31), x)
> +
> +/* CRC Region Histogram Counter Register For Bin n */
> +#define DCIF_B_BIN_CNT_MASK			GENMASK(20, 0)
> +#define DCIF_B_BIN_CNT(x)			FIELD_PREP(GENMASK(20, 0), x)
> +
> +/* CRC_POS - CRC Position Register */
> +#define DCIF_CRC_POS_CRC_HOR_POS(x)		FIELD_PREP(GENMASK(11, 0), x)
> +#define DCIF_CRC_POS_CRC_VER_POS(x)		FIELD_PREP(GENMASK(27, 16), x)
> +
> +/* CRC_SIZE - CRC Size Register */
> +#define DCIF_CRC_SIZE_CRC_HOR_SIZE(x)		FIELD_PREP(GENMASK(11, 0), x)
> +#define DCIF_CRC_SIZE_CRC_VER_SIZE(x)		FIELD_PREP(GENMASK(27, 16), x)
> +
> +/* CRC_VAL - CRC Value Register */
> +#define DCIF_CRC_VAL_CRC_VAL_MASK		GENMASK(31, 0)
> +#define DCIF_CRC_VAL_CRC_VAL(x)			FIELD_PREP(GENMASK(31, 0), x)
> +
> +/* CRC_EXP_VAL - CRC Expected Value Register */
> +#define DCIF_CRC_EXP_VAL_CRC_EXP_VAL_MASK	GENMASK(31, 0)
> +#define DCIF_CRC_EXP_VAL_CRC_EXP_VAL(x)		FIELD_PREP(GENMASK(31, 0), x)
> +
> +#endif /* __DCIF_REG_H__ */
> --
> 2.46.1
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 7/8] arm64: dts: imx943-evk: Add support for DCIF and LVDS
  2025-07-09 12:23 ` [PATCH 7/8] arm64: dts: imx943-evk: Add support for DCIF and LVDS Laurentiu Palcu
@ 2025-07-11  5:07   ` Frank Li
  2025-07-15 12:05     ` Laurentiu Palcu
  0 siblings, 1 reply; 21+ messages in thread
From: Frank Li @ 2025-07-11  5:07 UTC (permalink / raw)
  To: Laurentiu Palcu
  Cc: imx, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, dri-devel,
	devicetree, linux-arm-kernel, linux-kernel

On Wed, Jul 09, 2025 at 03:23:26PM +0300, Laurentiu Palcu wrote:
> Add DT nodes for DCIF and LVDS in imx943.dtsi and activate them in
> imx943-evk.dts.
>
> Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> ---
>  arch/arm64/boot/dts/freescale/imx943-evk.dts | 126 +++++++++++++++++++

Shawn require board dts need sperate patch.

>  arch/arm64/boot/dts/freescale/imx943.dtsi    |  56 ++++++++-
>  2 files changed, 181 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/boot/dts/freescale/imx943-evk.dts b/arch/arm64/boot/dts/freescale/imx943-evk.dts
> index c8c3eff9df1a2..e7de7ba406407 100644
> --- a/arch/arm64/boot/dts/freescale/imx943-evk.dts
> +++ b/arch/arm64/boot/dts/freescale/imx943-evk.dts
> @@ -125,6 +125,132 @@ memory@80000000 {
>  		reg = <0x0 0x80000000 0x0 0x80000000>;
>  		device_type = "memory";
>  	};
> +
> +	hdmi-connector {
> +		compatible = "hdmi-connector";
> +		label = "hdmi";
> +		type = "a";
> +
> +		port {
> +			hdmi_connector_in: endpoint {
> +				remote-endpoint = <&it6263_out>;
> +			};
> +		};
> +	};
> +};
> +
> +&dcif {
> +	status = "okay";
> +};
> +
> +&ldb {
> +	assigned-clocks = <&scmi_clk IMX94_CLK_LDBPLL_VCO>,
> +			  <&scmi_clk IMX94_CLK_LDBPLL>;
> +	assigned-clock-rates = <4158000000>, <1039500000>;
> +	status = "okay";
> +
> +	ports {
> +		#address-cells = <1>;
> +		#size-cells = <0>;

imx94.dts already set it

Frank
> +
> +		port@1 {
> +			reg = <1>;
> +
> +			lvds_out: endpoint {
> +				remote-endpoint = <&it6263_in>;
> +			};
> +		};
> +	};
> +};
> +
> +&lpi2c3 {
> +	clock-frequency = <400000>;
> +	pinctrl-0 = <&pinctrl_lpi2c3>;
> +	pinctrl-names = "default";
> +	status = "okay";
> +
> +	pca9548_i2c3: i2c-mux@77 {
> +		compatible = "nxp,pca9548";
> +		reg = <0x77>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		i2c@0 {
> +			reg = <0>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		i2c@1 {
> +			reg = <1>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		i2c@2 {
> +			reg = <2>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		i2c@3 {
> +			reg = <3>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			lvds-to-hdmi-bridge@4c {
> +				compatible = "ite,it6263";
> +				reg = <0x4c>;
> +				data-mapping = "jeida-24";
> +				reset-gpios = <&pcal6416_i2c3_u171 8 GPIO_ACTIVE_HIGH>;
> +
> +				ports {
> +					#address-cells = <1>;
> +					#size-cells = <0>;
> +
> +					port@0 {
> +						reg = <0>;
> +
> +						it6263_in: endpoint {
> +							remote-endpoint = <&lvds_out>;
> +						};
> +					};
> +
> +					port@2 {
> +						reg = <2>;
> +
> +						it6263_out: endpoint {
> +							remote-endpoint = <&hdmi_connector_in>;
> +						};
> +					};
> +				};
> +			};
> +		};
> +
> +		i2c@4 {
> +			reg = <4>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		i2c@5 {
> +			reg = <5>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		i2c@6 {
> +			reg = <6>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		i2c@7 {
> +			reg = <7>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +	};
>  };
>
>  &lpi2c3 {
> diff --git a/arch/arm64/boot/dts/freescale/imx943.dtsi b/arch/arm64/boot/dts/freescale/imx943.dtsi
> index 657c81b6016f2..db00a94812e18 100644
> --- a/arch/arm64/boot/dts/freescale/imx943.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx943.dtsi
> @@ -148,7 +148,7 @@ l3_cache: l3-cache {
>  		};
>  	};
>
> -	clock-ldb-pll-div7 {
> +	clock_ldb_pll_div7: clock-ldb-pll-div7 {
>  		compatible = "fixed-factor-clock";
>  		#clock-cells = <0>;
>  		clocks = <&scmi_clk IMX94_CLK_LDBPLL>;
> @@ -173,10 +173,64 @@ dispmix_csr: syscon@4b010000 {
>
>  		lvds_csr: syscon@4b0c0000 {
>  			compatible = "nxp,imx94-lvds-csr", "syscon";
> +			#address-cells = <1>;
> +			#size-cells = <1>;
>  			reg = <0x0 0x4b0c0000 0x0 0x10000>;
>  			clocks = <&scmi_clk IMX94_CLK_DISPAPB>;
>  			#clock-cells = <1>;
>  			power-domains = <&scmi_devpd IMX94_PD_DISPLAY>;
> +
> +			ldb: ldb@4 {
> +				compatible = "fsl,imx94-ldb";
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +				reg = <0x4 0x4>, <0x8 0x4>;
> +				reg-names = "ldb", "lvds";
> +				clocks = <&lvds_csr IMX94_CLK_DISPMIX_LVDS_CLK_GATE>;
> +				clock-names = "ldb";
> +				status = "disabled";
> +
> +				ports {
> +					#address-cells = <1>;
> +					#size-cells = <0>;
> +
> +					port@0 {
> +						reg = <0>;
> +
> +						lvds_in: endpoint {
> +							remote-endpoint = <&dcif_out>;
> +						};
> +					};
> +
> +					port@1 {
> +						reg = <1>;
> +					};
> +				};
> +			};
> +		};
> +
> +		dcif: display-controller@4b120000 {
> +			compatible = "nxp,imx94-dcif";
> +			reg = <0x0 0x4b120000 0x0 0x300000>;
> +			interrupts = <GIC_SPI 377 IRQ_TYPE_LEVEL_HIGH>,
> +				     <GIC_SPI 378 IRQ_TYPE_LEVEL_HIGH>,
> +				     <GIC_SPI 379 IRQ_TYPE_LEVEL_HIGH>;
> +			interrupt-names = "common", "bg_layer", "fg_layer";
> +			clocks = <&scmi_clk IMX94_CLK_DISPAPB>,
> +				 <&scmi_clk IMX94_CLK_DISPAXI>,
> +				 <&dispmix_csr IMX94_CLK_DISPMIX_CLK_SEL>;
> +			clock-names = "apb", "axi", "pix";
> +			assigned-clocks = <&dispmix_csr IMX94_CLK_DISPMIX_CLK_SEL>;
> +			assigned-clock-parents = <&clock_ldb_pll_div7>;
> +			power-domains = <&scmi_devpd IMX94_PD_DISPLAY>;
> +			nxp,blk-ctrl = <&dispmix_csr>;
> +			status = "disabled";
> +
> +			port {
> +				dcif_out: endpoint {
> +					remote-endpoint = <&lvds_in>;
> +				};
> +			};
>  		};
>  	};
>  };
> --
> 2.46.1
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 3/8] drm/bridge: fsl-ldb: Get the next non-panel bridge
  2025-07-11  4:19   ` Frank Li
@ 2025-07-11  5:31     ` Liu Ying
  0 siblings, 0 replies; 21+ messages in thread
From: Liu Ying @ 2025-07-11  5:31 UTC (permalink / raw)
  To: Frank Li, Laurentiu Palcu
  Cc: imx, Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, dri-devel,
	linux-kernel

On 07/11/2025, Frank Li wrote:
> On Wed, Jul 09, 2025 at 03:23:22PM +0300, Laurentiu Palcu wrote:
>> From: Liu Ying <victor.liu@nxp.com>
>>
>> The next bridge in bridge chain could be a panel bridge or a non-panel
>> bridge.  Use devm_drm_of_get_bridge() to replace the combination
>          ^ extra space.

It's intentional for readers to be aware of the start of a next sentence
easily.  It's not uncommon in kernel commit messages.

> 
>> function calls of of_drm_find_panel() and devm_drm_panel_bridge_add()
>> to get either a panel bridge or a non-panel bridge, instead of getting
>> a panel bridge only.
> 
> According to code change, devm_drm_of_get_bridge() replace
> devm_drm_panel_bridge_add(of_drm_find_panel()).
> 
> what relationship with panel bridge or a non-panel bridge?

Here, the former represents a panel with only video sink, and the latter
represents a bridge with both video sink and video source.

> 
> Frank
> 
>>
>> Signed-off-by: Liu Ying <victor.liu@nxp.com>
>> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
>> Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
>> ---
>>  drivers/gpu/drm/bridge/fsl-ldb.c | 31 +++++++++++--------------------
>>  1 file changed, 11 insertions(+), 20 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c
>> index 5c3cf37200bce..665053d0cb79d 100644
>> --- a/drivers/gpu/drm/bridge/fsl-ldb.c
>> +++ b/drivers/gpu/drm/bridge/fsl-ldb.c
>> @@ -15,7 +15,6 @@
>>  #include <drm/drm_atomic_helper.h>
>>  #include <drm/drm_bridge.h>
>>  #include <drm/drm_of.h>
>> -#include <drm/drm_panel.h>
>>
>>  #define LDB_CTRL_CH0_ENABLE			BIT(0)
>>  #define LDB_CTRL_CH0_DI_SELECT			BIT(1)
>> @@ -86,7 +85,7 @@ static const struct fsl_ldb_devdata fsl_ldb_devdata[] = {
>>  struct fsl_ldb {
>>  	struct device *dev;
>>  	struct drm_bridge bridge;
>> -	struct drm_bridge *panel_bridge;
>> +	struct drm_bridge *next_bridge;
>>  	struct clk *clk;
>>  	struct regmap *regmap;
>>  	const struct fsl_ldb_devdata *devdata;
>> @@ -118,7 +117,7 @@ static int fsl_ldb_attach(struct drm_bridge *bridge,
>>  {
>>  	struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
>>
>> -	return drm_bridge_attach(encoder, fsl_ldb->panel_bridge,
>> +	return drm_bridge_attach(encoder, fsl_ldb->next_bridge,
>>  				 bridge, flags);
>>  }
>>
>> @@ -292,9 +291,7 @@ static const struct drm_bridge_funcs funcs = {
>>  static int fsl_ldb_probe(struct platform_device *pdev)
>>  {
>>  	struct device *dev = &pdev->dev;
>> -	struct device_node *panel_node;
>>  	struct device_node *remote1, *remote2;
>> -	struct drm_panel *panel;
>>  	struct fsl_ldb *fsl_ldb;
>>  	int dual_link;
>>
>> @@ -317,33 +314,27 @@ static int fsl_ldb_probe(struct platform_device *pdev)
>>  	if (IS_ERR(fsl_ldb->regmap))
>>  		return PTR_ERR(fsl_ldb->regmap);
>>
>> -	/* Locate the remote ports and the panel node */
>> +	/* Locate the remote ports. */
>>  	remote1 = of_graph_get_remote_node(dev->of_node, 1, 0);
>>  	remote2 = of_graph_get_remote_node(dev->of_node, 2, 0);
>>  	fsl_ldb->ch0_enabled = (remote1 != NULL);
>>  	fsl_ldb->ch1_enabled = (remote2 != NULL);
>> -	panel_node = of_node_get(remote1 ? remote1 : remote2);
>>  	of_node_put(remote1);
>>  	of_node_put(remote2);
>>
>> -	if (!fsl_ldb->ch0_enabled && !fsl_ldb->ch1_enabled) {
>> -		of_node_put(panel_node);
>> -		return dev_err_probe(dev, -ENXIO, "No panel node found");
>> -	}
>> +	if (!fsl_ldb->ch0_enabled && !fsl_ldb->ch1_enabled)
>> +		return dev_err_probe(dev, -ENXIO, "No next bridge node found");
>>
>>  	dev_dbg(dev, "Using %s\n",
>>  		fsl_ldb_is_dual(fsl_ldb) ? "dual-link mode" :
>>  		fsl_ldb->ch0_enabled ? "channel 0" : "channel 1");
>>
>> -	panel = of_drm_find_panel(panel_node);
>> -	of_node_put(panel_node);
>> -	if (IS_ERR(panel))
>> -		return PTR_ERR(panel);
>> -
>> -	fsl_ldb->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
>> -	if (IS_ERR(fsl_ldb->panel_bridge))
>> -		return PTR_ERR(fsl_ldb->panel_bridge);
>> -
>> +	fsl_ldb->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node,
>> +						      fsl_ldb->ch0_enabled ? 1 : 2,
>> +						      0);
>> +	if (IS_ERR(fsl_ldb->next_bridge))
>> +		return dev_err_probe(dev, PTR_ERR(fsl_ldb->next_bridge),
>> +				     "failed to get next bridge\n");
>>
>>  	if (fsl_ldb_is_dual(fsl_ldb)) {
>>  		struct device_node *port1, *port2;
>> --
>> 2.46.1
>>

-- 
Regards,
Liu Ying

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 1/8] clk: imx95-blk-ctl: Cache registers when RPM routines are called
  2025-07-11  4:03   ` Frank Li
@ 2025-07-15 12:03     ` Laurentiu Palcu
  0 siblings, 0 replies; 21+ messages in thread
From: Laurentiu Palcu @ 2025-07-15 12:03 UTC (permalink / raw)
  To: Frank Li
  Cc: imx, Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd,
	Shawn Guo, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	dri-devel, Abel Vesa, linux-clk, linux-arm-kernel, linux-kernel

Hi Frank,

On Fri, Jul 11, 2025 at 12:03:22AM -0400, Frank Li wrote:
> Subject:
> save and store registers at suspend()/resume() function
> 
> On Wed, Jul 09, 2025 at 03:23:20PM +0300, Laurentiu Palcu wrote:
> > If runtime PM is used for the clock providers and they're part of a
> > power domain, then the power domain supply will be cut off when runtime
> > suspended. That means all BLK CTL registers belonging to that power
> > domain will be reset. Hence, the clock settings will revert to default
> > values messing up the consumer clock settings.
> 
> Needn't "hence ..."
> 
> Save/restore register value at suspend/resume functions to fix this problem.
> 
> >
> > Also, fix the suspend/resume routines as well, as the clock was left ON
> > when going to suspend.
> 
> Do you means fix the problem clock left ON after suspend?
> 
> Pomain cut off, why clock can left on?

I'm referring to the BLK_CTL clock supplier here. We need to call
clk_disable_unprepare() in suspend() too.

> 
> >
> > Fixes: 5224b189462f ("clk: imx: add i.MX95 BLK CTL clk driver")
> > Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> > ---
> >  drivers/clk/imx/clk-imx95-blk-ctl.c | 55 ++++++++++++++++++-----------
> >  1 file changed, 34 insertions(+), 21 deletions(-)
> >
> > diff --git a/drivers/clk/imx/clk-imx95-blk-ctl.c b/drivers/clk/imx/clk-imx95-blk-ctl.c
> > index 7e88877a62451..7f9bbca517284 100644
> > --- a/drivers/clk/imx/clk-imx95-blk-ctl.c
> > +++ b/drivers/clk/imx/clk-imx95-blk-ctl.c
> > @@ -448,12 +448,36 @@ static int imx95_bc_probe(struct platform_device *pdev)
> >  	return ret;
> >  }
> >
> > +static void __maybe_unused imx95_bc_save_reg(struct imx95_blk_ctl *bc)
> > +{
> > +	const struct imx95_blk_ctl_dev_data *bc_data;
> > +
> > +	bc_data = of_device_get_match_data(bc->dev);
> > +	if (!bc_data)
> > +		return;
> > +
> > +	bc->clk_reg_restore = readl(bc->base + bc_data->clk_reg_offset);
> > +}
> > +
> > +static void __maybe_unused imx95_bc_restore_reg(struct imx95_blk_ctl *bc)
> > +{
> > +	const struct imx95_blk_ctl_dev_data *bc_data;
> > +
> > +	bc_data = of_device_get_match_data(bc->dev);
> 
> Generally, bc_data should in imx95_blk_ctl_dev_data and set once at probe.
> 
> So imx95_bc_save_reg() and imx95_bc_restore_reg() will be simpfied.

Agree, I'll do this in a separate patch though for easier review.

> 
> > +	if (!bc_data)
> > +		return;
> > +
> > +	writel(bc->clk_reg_restore, bc->base + bc_data->clk_reg_offset);
> > +}
> > +
> >  #ifdef CONFIG_PM
> >  static int imx95_bc_runtime_suspend(struct device *dev)
> >  {
> >  	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
> >
> > +	imx95_bc_save_reg(bc);
> 
> this help function just one line. direct use
> 
> writel(bc->clk_reg_restore, bc->base + bc->bc_data->clk_reg_offset);

ok.

> 
> >  	clk_disable_unprepare(bc->clk_apb);
> > +
> >  	return 0;
> >  }
> >
> > @@ -461,7 +485,10 @@ static int imx95_bc_runtime_resume(struct device *dev)
> >  {
> >  	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
> >
> > -	return clk_prepare_enable(bc->clk_apb);
> > +	clk_prepare_enable(bc->clk_apb);
> 
> Need check ret value;
> 
> > +	imx95_bc_restore_reg(bc);
> > +
> > +	return 0;
> >  }
> >  #endif
> >
> > @@ -469,22 +496,12 @@ static int imx95_bc_runtime_resume(struct device *dev)
> >  static int imx95_bc_suspend(struct device *dev)
> >  {
> >  	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
> > -	const struct imx95_blk_ctl_dev_data *bc_data;
> > -	int ret;
> >
> > -	bc_data = of_device_get_match_data(dev);
> > -	if (!bc_data)
> > +	if (pm_runtime_suspended(dev))
> >  		return 0;
> >
> > -	if (bc_data->rpm_enabled) {
> > -		ret = pm_runtime_get_sync(bc->dev);
> > -		if (ret < 0) {
> > -			pm_runtime_put_noidle(bc->dev);
> > -			return ret;
> > -		}
> > -	}
> > -
> > -	bc->clk_reg_restore = readl(bc->base + bc_data->clk_reg_offset);
> > +	imx95_bc_save_reg(bc);
> > +	clk_disable_unprepare(bc->clk_apb);
> >
> >  	return 0;
> >  }
> > @@ -492,16 +509,12 @@ static int imx95_bc_suspend(struct device *dev)
> >  static int imx95_bc_resume(struct device *dev)
> >  {
> >  	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
> > -	const struct imx95_blk_ctl_dev_data *bc_data;
> >
> > -	bc_data = of_device_get_match_data(dev);
> > -	if (!bc_data)
> > +	if (pm_runtime_suspended(dev))
> >  		return 0;
> >
> > -	writel(bc->clk_reg_restore, bc->base + bc_data->clk_reg_offset);
> > -
> > -	if (bc_data->rpm_enabled)
> > -		pm_runtime_put(bc->dev);
> > +	clk_prepare_enable(bc->clk_apb);
> > +	imx95_bc_restore_reg(bc);
> >
> >  	return 0;
> >  }
> 
> look like imx95_bc_suspend(resume) is simple enough
> 
> Can you use DEFINE_RUNTIME_DEV_PM_OPS?

Not really. Some clocks provided by BLK_CTL use RPM, others don't. If we
use DEFINE_RUNTIME_DEV_PM_OPS, then suspend()/resume() routines will
never be called when rpm_enable is false.

Thanks,
Laurentiu

> 
> Frank
> 
> > --
> > 2.46.1
> >

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 6/8] drm/imx: Add support for i.MX94 DCIF
  2025-07-11  5:01   ` Frank Li
@ 2025-07-15 12:04     ` Laurentiu Palcu
  0 siblings, 0 replies; 21+ messages in thread
From: Laurentiu Palcu @ 2025-07-15 12:04 UTC (permalink / raw)
  To: Frank Li
  Cc: imx, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Shawn Guo, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, dri-devel, Sandor Yu,
	linux-kernel, linux-arm-kernel

Hi Frank,

On Fri, Jul 11, 2025 at 01:01:20AM -0400, Frank Li wrote:
> On Wed, Jul 09, 2025 at 03:23:25PM +0300, Laurentiu Palcu wrote:
> > From: Sandor Yu <sandor.yu@nxp.com>
> >
> > This patch adds support for the i.MX94 Display Control Interface.
> 
> Needn't word "this patch"
> 
> Can you list feature here?

I will add some more info about i.MX94 DCIF in commit message.

> 
> >
> > Signed-off-by: Sandor Yu <sandor.yu@nxp.com>
> > Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> > ---
> ...
> > +static int
> > +dcif_crc_parse_source(const char *source_name, enum dcif_crc_source *s,
> > +		      struct drm_rect *roi)
> > +{
> > +	static const char roi_prefix[] = "roi:";
> > +
> > +	if (!source_name) {
> > +		*s = DCIF_CRC_SRC_NONE;
> > +	} else if (!strcmp(source_name, "auto")) {
> > +		*s = DCIF_CRC_SRC_FRAME;
> > +	} else if (strstarts(source_name, roi_prefix)) {
> > +		char *options, *opt;
> > +		int len = strlen(roi_prefix);
> > +		int params[4];
> > +		int i = 0, ret;
> > +
> > +		options = kstrdup(source_name + len, GFP_KERNEL);
> 
> where free options?

Good catch, I'll fix it.

> 
> > +
> > +		while ((opt = strsep(&options, ",")) != NULL) {
> > +			if (i > 3)
> > +				return -EINVAL;
> > +
> > +			ret = kstrtouint(opt, 10, &params[i]);
> > +			if (ret < 0)
> > +				return ret;
> > +
> > +			if (params[i] < 0)
> > +				return -EINVAL;
> > +
> > +			i++;
> > +		}
> ....
> > +	/* Perform an atomic commit to set the CRC source. */
> > +	drm_modeset_acquire_init(&ctx, 0);
> > +
> > +	state = drm_atomic_state_alloc(crtc->dev);
> > +	if (!state) {
> > +		ret = -ENOMEM;
> > +		goto unlock;
> > +	}
> > +
> > +	state->acquire_ctx = &ctx;
> > +
> > +retry:
> > +	crtc_state = drm_atomic_get_crtc_state(state, crtc);
> > +	if (!IS_ERR(crtc_state)) {
> > +		struct dcif_crtc_state *dcif_crtc_state;
> > +
> > +		dcif_crtc_state = to_dcif_crtc_state(crtc_state);
> > +
> > +		dcif_crtc_state->crc.source = source;
> > +		dcif_copy_roi(&roi, &dcif_crtc_state->crc.roi);
> > +
> > +		ret = drm_atomic_commit(state);
> > +	} else {
> > +		ret = PTR_ERR(crtc_state);
> > +	}
> > +
> > +	if (ret == -EDEADLK) {
> > +		drm_atomic_state_clear(state);
> > +		drm_modeset_backoff(&ctx);
> > +		goto retry;
> 
> Need max retry time, otherwise may dead loop here.

Calling both drm_atomic_state_clear() and drm_modeset_backoff() is meant
for deadlock avoidance.

> 
> > +	}
> > +
> > +	drm_atomic_state_put(state);
> > +
> > +unlock:
> > +	drm_modeset_drop_locks(&ctx);
> > +	drm_modeset_acquire_fini(&ctx);
> > +
> > +	return ret;
> > +}
> > +
> > diff --git a/drivers/gpu/drm/imx/dcif/dcif-crc.h b/drivers/gpu/drm/imx/dcif/dcif-crc.h
> > new file mode 100644
> > index 0000000000000..ea1b18362a344
> > --- /dev/null
> > +++ b/drivers/gpu/drm/imx/dcif/dcif-crc.h
> > @@ -0,0 +1,52 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +
> ....
> > +
> > +static void dcif_crtc_atomic_disable(struct drm_crtc *crtc,
> > +				     struct drm_atomic_state *state)
> > +{
> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> > +	struct dcif_crtc_state *dcif_crtc_state = to_dcif_crtc_state(crtc_state);
> > +	struct dcif_dev *dcif = crtc_to_dcif_dev(crtc);
> > +	struct drm_device *drm = crtc->dev;
> > +
> > +	if (dcif->has_crc && dcif_crtc_state->crc.source != DCIF_CRC_SRC_NONE)
> > +		dcif_crtc_disable_crc_source(dcif, 0);
> > +
> > +	dcif_disable_controller(dcif);
> > +	dcif_disable_plane_panic(dcif);
> > +
> > +	drm_crtc_vblank_off(crtc);
> > +
> > +	pm_runtime_put_sync(drm->dev);
> > +
> > +	spin_lock_irq(&crtc->dev->event_lock);
> 
> suggest use scope_guard(spin_lock_irq)(&crtc->dev->event_lock)
> 
> check other place.

ok

> 
> > +	if (crtc->state->event && !crtc->state->active) {
> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +		crtc->state->event = NULL;
> > +	}
> > +	spin_unlock_irq(&crtc->dev->event_lock);
> > +}
> > +
> ....
> > +
> > +static int dcif_probe(struct platform_device *pdev)
> > +{
> > +	struct device_node *np = pdev->dev.of_node;
> > +	struct dcif_dev *dcif;
> > +	struct drm_device *drm;
> > +	int ret;
> > +	int i;
> > +
> > +	if (!pdev->dev.of_node)
> > +		return -ENODEV;
> > +
> > +	dcif = devm_drm_dev_alloc(&pdev->dev, &dcif_driver, struct dcif_dev, drm);
> > +	if (IS_ERR(dcif))
> > +		return PTR_ERR(dcif);
> > +
> > +	/* CPU 0 domain for interrupt control */
> > +	dcif->cpu_domain = DCIF_CPU_DOMAIN;
> > +
> > +	drm = &dcif->drm;
> > +	dev_set_drvdata(&pdev->dev, dcif);
> > +
> > +	dcif->reg_base = devm_platform_ioremap_resource(pdev, 0);
> > +	if (IS_ERR(dcif->reg_base))
> > +		return dev_err_probe(drm->dev, PTR_ERR(dcif->reg_base),
> > +				     "failed to get reg base\n");
> > +
> > +	dcif->irq[0] = platform_get_irq(pdev, 0);
> > +	if (dcif->irq[0] < 0)
> > +		return dev_err_probe(drm->dev, dcif->irq[0],
> > +				     "failed to get domain0 irq\n");
> > +
> > +	dcif->irq[1] = platform_get_irq(pdev, 1);
> > +	if (dcif->irq[1] < 0)
> > +		return dev_err_probe(drm->dev, dcif->irq[1],
> > +				     "failed to get domain1 irq\n");
> > +
> > +	dcif->irq[2] = platform_get_irq(pdev, 2);
> > +	if (dcif->irq[2] < 0)
> > +		return dev_err_probe(drm->dev, dcif->irq[2],
> > +				     "failed to get domain2 irq\n");
> 
> can you use loop for above 3 irqs.

sure, no problem. Good idea!

> 
> > +
> > +	dcif->blkctrl_regmap = syscon_regmap_lookup_by_phandle(np, "nxp,blk-ctrl");
> > +	if (IS_ERR(dcif->blkctrl_regmap))
> > +		return dev_err_probe(drm->dev, PTR_ERR(dcif->blkctrl_regmap),
> > +				     "failed to get blk-ctrl regmap\n");
> 
> You just set QOS value by blkctrl_regmap. Is it possible to set it blk-ctrl
> drivers? Or do it when power on the power domain to avoid use customer's
> syscon to other nodes.

The QoS is set in the resume() function already.

> 
> Frank
> 
> > +
> > +	dcif->regmap = devm_regmap_init_mmio(drm->dev, dcif->reg_base, &dcif_regmap_config);
> > +	if (IS_ERR(dcif->regmap))
> > +		return dev_err_probe(drm->dev, PTR_ERR(dcif->regmap),
> > +				     "failed to init DCIF regmap\n");
> > +
> > +	dcif->num_clks = ARRAY_SIZE(dcif_clks);
> > +	dcif->clks = devm_kcalloc(drm->dev, dcif->num_clks, sizeof(*dcif->clks), GFP_KERNEL);
> > +	if (!dcif->clks)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < dcif->num_clks; i++)
> > +		dcif->clks[i].id = dcif_clks[i];
> > +
> > +	ret = devm_clk_bulk_get(drm->dev, dcif->num_clks, dcif->clks);
> > +	if (ret)
> > +		return dev_err_probe(drm->dev, ret, "cannot get required clocks\n");
> > +
> > +	ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
> > +	if (ret)
> > +		return dev_err_probe(drm->dev, ret, "failed to set dma mask and coherent\n");
> 
> When DMA_BIT_MASK(32) > 32, never return failure. Needn't check this
> return value.
> 
> > +
> > +	pm_runtime_enable(drm->dev);
> 
> devm_pm_runtime_enable()
> 
> > +
> > +	ret = devm_request_irq(drm->dev, dcif->irq[dcif->cpu_domain],
> > +			       dcif_irq_handler, 0, drm->driver->name, drm);
> > +	if (ret < 0) {
> > +		dev_err(drm->dev, "failed to install IRQ handler: %d\n", ret);
> > +		goto err_irq_install;
> > +	}
> > +
> > +	dcif_read_chip_info(dcif);
> > +
> > +	ret = dcif_kms_prepare(dcif);
> > +	if (ret)
> > +		goto err_irq_install;
> > +
> > +	ret = drm_dev_register(drm, 0);
> > +	if (ret) {
> > +		dev_err(drm->dev, "failed to register drm device: %d\n", ret);
> > +		goto err_register;
> > +	}
> > +
> > +	drm_client_setup(drm, NULL);
> > +
> > +	return 0;
> > +
> > +err_register:
> > +	drm_kms_helper_poll_fini(drm);
> 
> use devm_add_action_or_reset to avoid goto here.

I guess I'll just use drmm_kms_helper_poll_init() in the
dcif_kms_prepare() and that will take care of calling the fini()
callback when DRM device is destroyed.

> 
> > +err_irq_install:
> > +	pm_runtime_disable(drm->dev);
> > +	return ret;
> > +}
> > +
> > +static void dcif_remove(struct platform_device *pdev)
> > +{
> > +	struct dcif_dev *dcif = dev_get_drvdata(&pdev->dev);
> > +	struct drm_device *drm = &dcif->drm;
> > +
> > +	drm_dev_unregister(drm);
> > +
> > +	drm_kms_helper_poll_fini(drm);
> > +
> > +	drm_atomic_helper_shutdown(drm);
> > +
> > +	pm_runtime_disable(drm->dev);
> > +}
> > +
> > +static int dcif_runtime_suspend(struct device *dev)
> > +{
> > +	struct dcif_dev *dcif = dev_get_drvdata(dev);
> > +
> > +	clk_bulk_disable_unprepare(dcif->num_clks, dcif->clks);
> > +
> > +	return 0;
> > +}
> > +
> > +static int dcif_runtime_resume(struct device *dev)
> > +{
> > +	struct dcif_dev *dcif = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	ret = clk_bulk_prepare_enable(dcif->num_clks, dcif->clks);
> > +	if (ret) {
> > +		dev_err(dev, "failed to enable clocks: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = dcif_set_qos(dcif);
> > +	if (ret) {
> > +		clk_bulk_disable_unprepare(dcif->num_clks, dcif->clks);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int dcif_suspend(struct device *dev)
> > +{
> > +	struct dcif_dev *dcif = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	ret = drm_mode_config_helper_suspend(&dcif->drm);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	if (pm_runtime_suspended(dev))
> > +		return 0;
> > +
> > +	return dcif_runtime_suspend(dev);
> > +}
> > +
> > +static int dcif_resume(struct device *dev)
> > +{
> > +	struct dcif_dev *dcif = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	if (!pm_runtime_suspended(dev)) {
> > +		ret = dcif_runtime_resume(dev);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> > +	return drm_mode_config_helper_resume(&dcif->drm);
> > +}
> > +
> > +static const struct dev_pm_ops dcif_pm_ops = {
> > +	SET_SYSTEM_SLEEP_PM_OPS(dcif_suspend, dcif_resume)
> > +	SET_RUNTIME_PM_OPS(dcif_runtime_suspend, dcif_runtime_resume, NULL)
> > +};
> > +
> > +static const struct of_device_id dcif_dt_ids[] = {
> > +	{ .compatible = "nxp,imx94-dcif", },
> > +	{ /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, dcif_dt_ids);
> > +
> > +static struct platform_driver dcif_platform_driver = {
> > +	.probe	= dcif_probe,
> > +	.remove	= dcif_remove,
> > +	.driver	= {
> > +		.name		= DRIVER_NAME,
> > +		.of_match_table	= dcif_dt_ids,
> > +		.pm		= pm_ptr(&dcif_pm_ops),
> > +	},
> > +};
> > +module_platform_driver(dcif_platform_driver);
> > +
> ...
> > + * Copyright 2025 NXP
> > + */
> > +#ifndef __DCIF_REG_H__
> > +#define __DCIF_REG_H__
> > +
> > +#include <linux/bits.h>
> > +
> > +/* Version ID Register */
> > +#define DCIF_VER				0x0
> > +
> > +/* Parameter Registers */
> > +#define DCIF_PAR_0				0x4
> > +#define DCIF_PAR_1				0x8
> > +
> > +/* Display Control and Parameter Registers */
> > +#define DCIF_DISP_CTRL				0x10
> > +#define DCIF_DISP_PAR				0x14
> > +#define DCIF_DISP_SIZE				0x18
> > +
> > +/* Display Status Registers */
> > +#define DCIF_DISP_SR0				0x1C
> > +#define DCIF_DISP_SR1				0x20
> > +
> > +/* Interrupt Enable and Status Registers, n=0-2*/
> > +#define DCIF_IE0(n)				(0x24 + (n) * 0x10000)
> > +#define DCIF_IS0(n)				(0x28 + (n) * 0x10000)
> > +#define DCIF_IE1(n)				(0x2C + (n) * 0x10000)
> > +#define DCIF_IS1(n)				(0x30 + (n) * 0x10000)
> > +
> > +/* DPI Control and Sync Parameter Registers */
> > +#define DCIF_DPI_CTRL				0x40
> > +#define DCIF_DPI_HSYN_PAR			0x44
> > +#define DCIF_DPI_VSYN_PAR			0x48
> > +#define DCIF_DPI_VSYN_HSYN_WIDTH		0x4C
> > +
> > +/* Control Descriptor Registers, n=0-1*/
> > +#define DCIF_CTRLDESC0(n)			(0x10000 + (n) * 0x10000)
> > +#define DCIF_CTRLDESC1(n)			(0x10004 + (n) * 0x10000)
> > +#define DCIF_CTRLDESC2(n)			(0x10008 + (n) * 0x10000)
> > +#define DCIF_CTRLDESC3(n)			(0x1000C + (n) * 0x10000)
> > +#define DCIF_CTRLDESC4(n)			(0x10010 + (n) * 0x10000)
> > +#define DCIF_CTRLDESC5(n)			(0x10014 + (n) * 0x10000)
> > +#define DCIF_CTRLDESC6(n)			(0x10018 + (n) * 0x10000)
> > +
> > +/* CLUT control Register */
> > +#define DCIF_CLUT_CTRL				0x1003C
> > +
> > +/* FIFO Panic Threshold Register, n=0-1 */
> > +#define DCIF_PANIC_THRES(n)			(0x10040 + (n) * 0x10000)
> > +
> > +/* Layer Status Register 0, n=0-1 */
> > +#define DCIF_LAYER_SR0(n)			(0x10044 + (n) * 0x10000)
> > +
> > +/* Color Space Conversion Control and Coefficient Registers for Layer 0 */
> > +#define DCIF_CSC_CTRL_L0			0x10050
> > +#define DCIF_CSC_COEF0_L0			0x10054
> > +#define DCIF_CSC_COEF1_L0			0x10058
> > +#define DCIF_CSC_COEF2_L0			0x1005C
> > +#define DCIF_CSC_COEF3_L0			0x10060
> > +#define DCIF_CSC_COEF4_L0			0x10064
> > +#define DCIF_CSC_COEF5_L0			0x10068
> > +
> > +/* CRC Control, Threshold, and Histogram Coefficient Registers */
> > +#define DCIF_CRC_CTRL				0x20100
> > +#define DCIF_CRC_THRES				0x20104
> > +#define DCIF_CRC_HIST_COEF			0x20108
> > +#define DCIF_CRC_ERR_CNT			0x2010C
> > +#define DCIF_CRC_SR				0x20110
> > +#define DCIF_CRC_HIST_CNT_B(n)			(0x20114 + (n) * 4)
> > +
> > +/* CRC Region Position, Size, Value, and Expected Value Registers, n=0-3 */
> > +#define DCIF_CRC_POS_R(n)			(0x20214 + (n) * 0x10)
> > +#define DCIF_CRC_SIZE_R(n)			(0x20218 + (n) * 0x10)
> > +#define DCIF_CRC_VAL_R(n)			(0x2021C + (n) * 0x10)
> > +#define DCIF_CRC_EXP_VAL_R(n)			(0x20220 + (n) * 0x10)
> > +
> > +/* VER - Version ID Register */
> > +#define DCIF_VER_GET_FEATURE(x)			FIELD_GET(GENMASK(15, 0), x)
> > +#define DCIF_VER_GET_MINOR(x)			FIELD_GET(GENMASK(23, 16), x)
> > +#define DCIF_VER_GET_MAJOR(x)			FIELD_GET(GENMASK(31, 24), x)
> 
> can you move it close register offset define.

ok, I'll move them.

Thanks,
Laurentiu

> 
> Frank
> 
> > +
> > +/* PAR_0 - Parameter Register 0 */
> > +#define DCIF_PAR_0_LAYER_NUM(x)			FIELD_PREP(GENMASK(3, 0), x)
> > +#define DCIF_PAR_0_DOMAIN_NUM(x)		FIELD_PREP(GENMASK(5, 4), x)
> > +#define DCIF_PAR_0_AXI_DATA_WIDTH(x)		FIELD_PREP(GENMASK(7, 6), x)
> > +#define DCIF_PAR_0_CLUT_RAM_NUM(x)		FIELD_PREP(GENMASK(11, 8), x)
> > +#define DCIF_PAR_0_CSC_NUM(x)			FIELD_PREP(GENMASK(13, 12), x)
> > +#define DCIF_PAR_0_CRC_REGION_NUM(x)		FIELD_PREP(GENMASK(18, 16), x)
> > +#define DCIF_PAR_0_BACKUP(x)			FIELD_PREP(GENMASK(31, 28), x)
> > +
> > +/* PAR_1 - Parameter Register 1 */
> > +#define DCIF_PAR_1_LAYER0_FIFO_SIZE(x)		FIELD_PREP(GENMASK(3, 0), x)
> > +#define DCIF_PAR_1_LAYER1_FIFO_SIZE(x)		FIELD_PREP(GENMASK(7, 4), x)
> > +
> > +/* DISP_CTRL - Display Control Register */
> > +#define DCIF_DISP_CTRL_DISP_ON			BIT(0)
> > +#define DCIF_DISP_CTRL_AXI_RD_HOLD		BIT(30)
> > +#define DCIF_DISP_CTRL_SW_RST			BIT(31)
> > +
> > +/* DISP_PAR - Display Parameter Register */
> > +#define DCIF_DISP_PAR_BGND_B(x)			FIELD_PREP(GENMASK(7, 0), x)
> > +#define DCIF_DISP_PAR_BGND_G(x)			FIELD_PREP(GENMASK(15, 8), x)
> > +#define DCIF_DISP_PAR_BGND_R(x)			FIELD_PREP(GENMASK(23, 16), x)
> > +
> > +/* DISP_SIZE - Display Size Register */
> > +#define DCIF_DISP_SIZE_DISP_WIDTH(x)		FIELD_PREP(GENMASK(11, 0), x)
> > +#define DCIF_DISP_SIZE_DISP_HEIGHT(x)		FIELD_PREP(GENMASK(27, 16), x)
> > +
> > +/* DISP_SR0 - Display Status Register 0 */
> > +#define DCIF_DISP_SR0_AXI_RD_PEND(x)		FIELD_PREP(GENMASK(4, 0), x)
> > +#define DCIF_DISP_SR0_DPI_BUSY(x)		FIELD_PREP(GENMASK(14, 14), x)
> > +#define DCIF_DISP_SR0_AXI_RD_BUSY(x)		FIELD_PREP(GENMASK(15, 15), x)
> > +#define DCIF_DISP_SR0_TXFIFO_CNT(x)		FIELD_PREP(GENMASK(23, 16), x)
> > +
> > +/* DISP_SR1 - Display Status Register 1 */
> > +#define DCIF_DISP_SR1_H_CNT(x)			FIELD_PREP(GENMASK(11, 0), x)
> > +#define DCIF_DISP_SR1_V_CNT(x)			FIELD_PREP(GENMASK(27, 16), x)
> > +
> > +/* INT0 - Interrupt Enable/Status  Register 0 for Domain 0/1/2 */
> > +#define DCIF_INT0_VSYNC				BIT(0)
> > +#define DCIF_INT0_UNDERRUN			BIT(1)
> > +#define DCIF_INT0_VS_BLANK			BIT(2)
> > +#define DCIF_INT0_HIST_DONE			BIT(5)
> > +#define DCIF_INT0_CRC_ERR			BIT(6)
> > +#define DCIF_INT0_CRC_ERR_SAT			BIT(7)
> > +
> > +/* INT1 - Interrupt Enable/Status Register 1 for Domain 0/1/2 */
> > +#define DCIF_INT1_FIFO_PANIC0			BIT(0)
> > +#define DCIF_INT1_FIFO_PANIC1			BIT(1)
> > +#define DCIF_INT1_DMA_ERR0			BIT(8)
> > +#define DCIF_INT1_DMA_ERR1			BIT(9)
> > +#define DCIF_INT1_DMA_DONE0			BIT(16)
> > +#define DCIF_INT1_DMA_DONE1			BIT(17)
> > +#define DCIF_INT1_FIFO_EMPTY0			BIT(24)
> > +#define DCIF_INT1_FIFO_EMPTY1			BIT(25)
> > +
> > +/* DPI_CTRL - DPI Control Register */
> > +#define DCIF_DPI_CTRL_HSYNC_POL_LOW		BIT(0)
> > +#define DCIF_DPI_CTRL_VSYNC_POL_LOW		BIT(1)
> > +#define DCIF_DPI_CTRL_DE_POL_LOW		BIT(2)
> > +#define DCIF_DPI_CTRL_PCLK_EDGE_FALLING		BIT(3)
> > +#define DCIF_DPI_CTRL_POL_MASK			GENMASK(3, 0)
> > +
> > +#define DCIF_DPI_CTRL_DATA_INV(x)		FIELD_PREP(GENMASK(4, 4), x)
> > +#define DCIF_DPI_CTRL_DEF_BGND_EN(x)		FIELD_PREP(GENMASK(5, 5), x)
> > +#define DCIF_DPI_CTRL_FETCH_OPT(x)		FIELD_PREP(GENMASK(9, 8), x)
> > +#define DCIF_DPI_CTRL_DISP_MODE(x)		FIELD_PREP(GENMASK(13, 12), x)
> > +#define DCIF_DPI_CTRL_DATA_PATTERN_MASK		GENMASK(18, 16)
> > +#define DCIF_DPI_CTRL_DATA_PATTERN(x)		FIELD_PREP(GENMASK(18, 16), x)
> > +#define PATTERN_RGB888				0
> > +#define PATTERN_RBG888				1
> > +#define PATTERN_GBR888				2
> > +#define PATTERN_GRB888				3
> > +#define PATTERN_BRG888				4
> > +#define PATTERN_BGR888				5
> > +#define PATTERN_RGB555				6
> > +#define PATTERN_RGB565				7
> > +
> > +/* DPI_HSYN_PAR - DPI Horizontal Sync Parameter Register */
> > +#define DCIF_DPI_HSYN_PAR_FP_H(x)		FIELD_PREP(GENMASK(11, 0), x)
> > +#define DCIF_DPI_HSYN_PAR_BP_H(x)		FIELD_PREP(GENMASK(27, 16), x)
> > +
> > +/* DPI_VSYN_PAR - DPI Vertical Sync Parameter Register */
> > +#define DCIF_DPI_VSYN_PAR_FP_V(x)		FIELD_PREP(GENMASK(11, 0), x)
> > +#define DCIF_DPI_VSYN_PAR_BP_V(x)		FIELD_PREP(GENMASK(27, 16), x)
> > +
> > +/* DPI_VSYN_HSYN_WIDTH - DPI Vertical and Horizontal Pulse Width Parameter Register */
> > +#define DCIF_DPI_VSYN_HSYN_WIDTH_PW_H(x)	FIELD_PREP(GENMASK(11, 0), x)
> > +#define DCIF_DPI_VSYN_HSYN_WIDTH_PW_V(x)	FIELD_PREP(GENMASK(27, 16), x)
> > +
> > +/* CTRLDESC0 - Control Descriptor Register 0 */
> > +#define DCIF_CTRLDESC0_AB_MODE(x)		FIELD_PREP(GENMASK(1, 0), x)
> > +#define ALPHA_EMBEDDED				0
> > +#define ALPHA_GLOBAL				1
> > +#define DCIF_CTRLDESC0_YUV_FORMAT_MASK		GENMASK(15, 14)
> > +#define DCIF_CTRLDESC0_YUV_FORMAT(x)		FIELD_PREP(GENMASK(15, 14), x)
> > +#define CTRLDESCL0_YUV_FORMAT_Y2VY1U		0x0
> > +#define CTRLDESCL0_YUV_FORMAT_Y2UY1V		0x1
> > +#define CTRLDESCL0_YUV_FORMAT_VY2UY1		0x2
> > +#define CTRLDESCL0_YUV_FORMAT_UY2VY1		0x3
> > +#define DCIF_CTRLDESC0_GLOBAL_ALPHA(x)		FIELD_PREP(GENMASK(23, 16), x)
> > +#define DCIF_CTRLDESC0_FORMAT_MASK		GENMASK(27, 24)
> > +#define DCIF_CTRLDESC0_FORMAT(x)		FIELD_PREP(GENMASK(27, 24), x)
> > +#define CTRLDESCL0_FORMAT_RGB565		0x4
> > +#define CTRLDESCL0_FORMAT_ARGB1555		0x5
> > +#define CTRLDESCL0_FORMAT_ARGB4444		0x6
> > +#define CTRLDESCL0_FORMAT_YCBCR422		0x7
> > +#define CTRLDESCL0_FORMAT_RGB888		0x8
> > +#define CTRLDESCL0_FORMAT_ARGB8888		0x9
> > +#define CTRLDESCL0_FORMAT_ABGR8888		0xa
> > +#define DCIF_CTRLDESC0_SHADOW_LOAD_EN		BIT(30)
> > +#define DCIF_CTRLDESC0_EN			BIT(31)
> > +
> > +/* CTRLDESC1 - Control Descriptor Register 1 */
> > +#define DCIF_CTRLDESC1_POSX(x)			FIELD_PREP(GENMASK(11, 0), x)
> > +#define DCIF_CTRLDESC1_POSY(x)			FIELD_PREP(GENMASK(27, 16), x)
> > +
> > +/* CTRLDESC2 - Control Descriptor Register */
> > +#define DCIF_CTRLDESC2_WIDTH(x)			FIELD_PREP(GENMASK(11, 0), x)
> > +#define DCIF_CTRLDESC2_HEIGHT(x)		FIELD_PREP(GENMASK(27, 16), x)
> > +
> > +/* CTRLDESC3 - Control Descriptor Register 3 */
> > +#define DCIF_CTRLDESC3_PITCH(x)			FIELD_PREP(GENMASK(15, 0), x)
> > +#define DCIF_CTRLDESC3_T_SIZE(x)		FIELD_PREP(GENMASK(17, 16), x)
> > +#define DCIF_CTRLDESC3_P_SIZE(x)		FIELD_PREP(GENMASK(22, 20), x)
> > +
> > +/* CTRLDESC4 - Control Descriptor Register 4 */
> > +#define DCIF_CTRLDESC4_ADDR(x)FIELD_PREP(GENMASK(31, 0), x)
> > +
> > +/* CTRLDESC6 - Control Descriptor Register 6  */
> > +#define DCIF_CTRLDESC6_BCLR_B(x)		FIELD_PREP(GENMASK(7, 0), x)
> > +#define DCIF_CTRLDESC6_BCLR_G(x)		FIELD_PREP(GENMASK(15, 8), x)
> > +#define DCIF_CTRLDESC6_BCLR_R(x)		FIELD_PREP(GENMASK(23, 16), x)
> > +#define DCIF_CTRLDESC6_BCLR_A(x)		FIELD_PREP(GENMASK(31, 24), x)
> > +
> > +/* CLUT_CTRL - CLUT control Register */
> > +#define DCIF_CLUT_CTRL_CLUT0_SEL(x)		FIELD_PREP(GENMASK(0, 0), x)
> > +#define DCIF_CLUT_CTRL_CLUT1_SEL(x)		FIELD_PREP(GENMASK(3, 3), x)
> > +#define DCIF_CLUT_CTRL_CLUT_MUX(x)		FIELD_PREP(GENMASK(29, 28), x)
> > +#define DCIF_CLUT_CTRL_CLUT_SHADOW_LOAD_EN(x)	FIELD_PREP(GENMASK(31, 31), x)
> > +
> > +/* PANIC_THRES_L0 - FIFO Panic Threshold Register For Layer 0 */
> > +#define DCIF_PANIC_THRES_LOW_MASK		GENMASK(11, 0)
> > +#define DCIF_PANIC_THRES_LOW(x)			FIELD_PREP(GENMASK(11, 00), x)
> > +#define DCIF_PANIC_THRES_HIGH_MASK		GENMASK(27, 16)
> > +#define DCIF_PANIC_THRES_HIGH(x)		FIELD_PREP(GENMASK(27, 16), x)
> > +#define DCIF_PANIC_THRES_REQ_EN			BIT(31)
> > +#define PANIC0_THRES_MAX			511
> > +
> > +/* LAYER_SR0_L0 - Layer Status Register 0 for Layer 0 */
> > +#define DCIF_LAYER_SR0_L0_FIFO_CNT_MASK		GENMASK(9, 0)
> > +#define DCIF_LAYER_SR0_L0_FIFO_CNT(x)		FIELD_PREP(GENMASK(9, 0), x)
> > +
> > +/* CSC_CTRL_L0 - Color Space Conversion Control Register For Layer 0 */
> > +#define DCIF_CSC_CTRL_L0_CSC_EN			BIT(0)
> > +#define DCIF_CSC_CTRL_L0_CSC_MODE_YCBCR2RGB	BIT(1)
> > +
> > +/* CSC_COEF0_L0 - Color Space Conversion Coefficient Register 0 For Layer 0 */
> > +#define DCIF_CSC_COEF0_L0_A1(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
> > +#define DCIF_CSC_COEF0_L0_A2(x)			FIELD_PREP_CONST(GENMASK(26, 16), x)
> > +
> > +/* CSC_COEF1_L0 - Color Space Conversion Coefficient Register 1 For Layer 0 */
> > +#define DCIF_CSC_COEF1_L0_A3(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
> > +#define DCIF_CSC_COEF1_L0_B1(x)			FIELD_PREP_CONST(GENMASK(26, 16), x)
> > +
> > +/* CSC_COEF2_L0 - Color Space Conversion Coefficient Register 2 For Layer 0 */
> > +#define DCIF_CSC_COEF2_L0_B2(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
> > +#define DCIF_CSC_COEF2_L0_B3(x)			FIELD_PREP_CONST(GENMASK(26, 16), x)
> > +
> > +/* CSC_COEF3_L0 - Color Space Conversion Coefficient Register 3 For Layer 0 */
> > +#define DCIF_CSC_COEF3_L0_C1(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
> > +#define DCIF_CSC_COEF3_L0_C2(x)			FIELD_PREP_CONST(GENMASK(26, 16), x)
> > +
> > +/* CSC_COEF4_L0 - Color Space Conversion Coefficient Register 4 For Layer 0 */
> > +#define DCIF_CSC_COEF4_L0_C3(x)			FIELD_PREP_CONST(GENMASK(10, 0), x)
> > +#define DCIF_CSC_COEF4_L0_D1(x)			FIELD_PREP_CONST(GENMASK(24, 16), x)
> > +
> > +/* CSC_COEF5_L0 - Color Space Conversion Coefficient Register 5 For Layer 0 */
> > +#define DCIF_CSC_COEF5_L0_D2(x)			FIELD_PREP_CONST(GENMASK(8, 0), x)
> > +#define DCIF_CSC_COEF5_L0_D3(x)			FIELD_PREP_CONST(GENMASK(24, 16), x)
> > +
> > +/* CTRLDESC0_L1 - Control Descriptor Register 0 for Layer 1 */
> > +#define DCIF_CTRLDESC0_L1_AB_MODE_MASK		GENMASK(1, 0)
> > +#define DCIF_CTRLDESC0_L1_AB_MODE(x)		FIELD_PREP(GENMASK(1, 0), x)
> > +
> > +/* CRC_CTRL - CRC Control Register */
> > +#define DCIF_CRC_CTRL_CRC_EN(x)			(1 << (x))
> > +#define DCIF_CRC_CTRL_HIST_REGION_SEL(x)	FIELD_PREP(GENMASK(17, 16), x)
> > +#define DCIF_CRC_CTRL_HIST_MODE			BIT(21)
> > +#define DCIF_CRC_CTRL_HIST_TRIG			BIT(22)
> > +#define DCIF_CRC_CTRL_HIST_EN			BIT(23)
> > +#define DCIF_CRC_CTRL_CRC_MODE			BIT(28)
> > +#define DCIF_CRC_CTRL_CRC_TRIG			BIT(29)
> > +#define DCIF_CRC_CTRL_CRC_ERR_CNT_RST		BIT(30)
> > +#define DCIF_CRC_CTRL_CRC_SHADOW_LOAD_EN	BIT(31)
> > +
> > +/* CRC_THRES - CRC Threshold Register */
> > +#define DCIF_CRC_THRES_CRC_THRESHOLD_MASK	GENMASK(31, 0)
> > +#define DCIF_CRC_THRES_CRC_THRESHOLD(x)		FIELD_PREP(GENMASK(31, 0), x)
> > +
> > +/* CRC_HIST_COEF - CRC Region Histogram Coefficient Register */
> > +#define DCIF_CRC_HIST_COEF_HIST_WB_MASK		GENMASK(7, 0)
> > +#define DCIF_CRC_HIST_COEF_HIST_WB(x)		FIELD_PREP(GENMASK(7, 0), x)
> > +#define DCIF_CRC_HIST_COEF_HIST_WG_MASK		GENMASK(15, 8)
> > +#define DCIF_CRC_HIST_COEF_HIST_WG(x)		FIELD_PREP(GENMASK(15, 8), x)
> > +#define DCIF_CRC_HIST_COEF_HIST_WR_MASK		GENMASK(23, 16)
> > +#define DCIF_CRC_HIST_COEF_HIST_WR(x)		FIELD_PREP(GENMASK(23, 16), x)
> > +
> > +/* CRC_ERR_CNT - CRC Error Counter Register */
> > +#define DCIF_CRC_ERR_CNT_CRC_ERR_CNT_MASK	GENMASK(31, 0)
> > +#define DCIF_CRC_ERR_CNT_CRC_ERR_CNT(x)		FIELD_PREP(GENMASK(31, 0), x)
> > +
> > +/* CRC_SR - CRC Status Register */
> > +#define DCIF_CRC_SR_HIST_CNT_SAT_MASK		BIT(13)
> > +#define DCIF_CRC_SR_HIST_CNT_SAT(x)		FIELD_PREP(GENMASK(13, 13), x)
> > +#define DCIF_CRC_SR_HIST_SAT_MASK		BIT(14)
> > +#define DCIF_CRC_SR_HIST_SAT(x)			FIELD_PREP(GENMASK(14, 14), x)
> > +#define DCIF_CRC_SR_HIST_BUSY_MASK		BIT(15)
> > +#define DCIF_CRC_SR_HIST_BUSY(x)		FIELD_PREP(GENMASK(15, 15), x)
> > +#define DCIF_CRC_SR_CRC_STATUS_MASK		BIT(31)
> > +#define DCIF_CRC_SR_CRC_STATUS(x)		FIELD_PREP(GENMASK(31, 31), x)
> > +
> > +/* CRC Region Histogram Counter Register For Bin n */
> > +#define DCIF_B_BIN_CNT_MASK			GENMASK(20, 0)
> > +#define DCIF_B_BIN_CNT(x)			FIELD_PREP(GENMASK(20, 0), x)
> > +
> > +/* CRC_POS - CRC Position Register */
> > +#define DCIF_CRC_POS_CRC_HOR_POS(x)		FIELD_PREP(GENMASK(11, 0), x)
> > +#define DCIF_CRC_POS_CRC_VER_POS(x)		FIELD_PREP(GENMASK(27, 16), x)
> > +
> > +/* CRC_SIZE - CRC Size Register */
> > +#define DCIF_CRC_SIZE_CRC_HOR_SIZE(x)		FIELD_PREP(GENMASK(11, 0), x)
> > +#define DCIF_CRC_SIZE_CRC_VER_SIZE(x)		FIELD_PREP(GENMASK(27, 16), x)
> > +
> > +/* CRC_VAL - CRC Value Register */
> > +#define DCIF_CRC_VAL_CRC_VAL_MASK		GENMASK(31, 0)
> > +#define DCIF_CRC_VAL_CRC_VAL(x)			FIELD_PREP(GENMASK(31, 0), x)
> > +
> > +/* CRC_EXP_VAL - CRC Expected Value Register */
> > +#define DCIF_CRC_EXP_VAL_CRC_EXP_VAL_MASK	GENMASK(31, 0)
> > +#define DCIF_CRC_EXP_VAL_CRC_EXP_VAL(x)		FIELD_PREP(GENMASK(31, 0), x)
> > +
> > +#endif /* __DCIF_REG_H__ */
> > --
> > 2.46.1
> >

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 7/8] arm64: dts: imx943-evk: Add support for DCIF and LVDS
  2025-07-11  5:07   ` Frank Li
@ 2025-07-15 12:05     ` Laurentiu Palcu
  0 siblings, 0 replies; 21+ messages in thread
From: Laurentiu Palcu @ 2025-07-15 12:05 UTC (permalink / raw)
  To: Frank Li
  Cc: imx, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, dri-devel,
	devicetree, linux-arm-kernel, linux-kernel

Hi Frank,

On Fri, Jul 11, 2025 at 01:07:06AM -0400, Frank Li wrote:
> On Wed, Jul 09, 2025 at 03:23:26PM +0300, Laurentiu Palcu wrote:
> > Add DT nodes for DCIF and LVDS in imx943.dtsi and activate them in
> > imx943-evk.dts.
> >
> > Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> > ---
> >  arch/arm64/boot/dts/freescale/imx943-evk.dts | 126 +++++++++++++++++++
> 
> Shawn require board dts need sperate patch.

Ok, I'll create a patch for each file.

Thanks,
Laurentiu

> 
> >  arch/arm64/boot/dts/freescale/imx943.dtsi    |  56 ++++++++-
> >  2 files changed, 181 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/arm64/boot/dts/freescale/imx943-evk.dts b/arch/arm64/boot/dts/freescale/imx943-evk.dts
> > index c8c3eff9df1a2..e7de7ba406407 100644
> > --- a/arch/arm64/boot/dts/freescale/imx943-evk.dts
> > +++ b/arch/arm64/boot/dts/freescale/imx943-evk.dts
> > @@ -125,6 +125,132 @@ memory@80000000 {
> >  		reg = <0x0 0x80000000 0x0 0x80000000>;
> >  		device_type = "memory";
> >  	};
> > +
> > +	hdmi-connector {
> > +		compatible = "hdmi-connector";
> > +		label = "hdmi";
> > +		type = "a";
> > +
> > +		port {
> > +			hdmi_connector_in: endpoint {
> > +				remote-endpoint = <&it6263_out>;
> > +			};
> > +		};
> > +	};
> > +};
> > +
> > +&dcif {
> > +	status = "okay";
> > +};
> > +
> > +&ldb {
> > +	assigned-clocks = <&scmi_clk IMX94_CLK_LDBPLL_VCO>,
> > +			  <&scmi_clk IMX94_CLK_LDBPLL>;
> > +	assigned-clock-rates = <4158000000>, <1039500000>;
> > +	status = "okay";
> > +
> > +	ports {
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> 
> imx94.dts already set it
> 
> Frank
> > +
> > +		port@1 {
> > +			reg = <1>;
> > +
> > +			lvds_out: endpoint {
> > +				remote-endpoint = <&it6263_in>;
> > +			};
> > +		};
> > +	};
> > +};
> > +
> > +&lpi2c3 {
> > +	clock-frequency = <400000>;
> > +	pinctrl-0 = <&pinctrl_lpi2c3>;
> > +	pinctrl-names = "default";
> > +	status = "okay";
> > +
> > +	pca9548_i2c3: i2c-mux@77 {
> > +		compatible = "nxp,pca9548";
> > +		reg = <0x77>;
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +
> > +		i2c@0 {
> > +			reg = <0>;
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +		};
> > +
> > +		i2c@1 {
> > +			reg = <1>;
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +		};
> > +
> > +		i2c@2 {
> > +			reg = <2>;
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +		};
> > +
> > +		i2c@3 {
> > +			reg = <3>;
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +
> > +			lvds-to-hdmi-bridge@4c {
> > +				compatible = "ite,it6263";
> > +				reg = <0x4c>;
> > +				data-mapping = "jeida-24";
> > +				reset-gpios = <&pcal6416_i2c3_u171 8 GPIO_ACTIVE_HIGH>;
> > +
> > +				ports {
> > +					#address-cells = <1>;
> > +					#size-cells = <0>;
> > +
> > +					port@0 {
> > +						reg = <0>;
> > +
> > +						it6263_in: endpoint {
> > +							remote-endpoint = <&lvds_out>;
> > +						};
> > +					};
> > +
> > +					port@2 {
> > +						reg = <2>;
> > +
> > +						it6263_out: endpoint {
> > +							remote-endpoint = <&hdmi_connector_in>;
> > +						};
> > +					};
> > +				};
> > +			};
> > +		};
> > +
> > +		i2c@4 {
> > +			reg = <4>;
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +		};
> > +
> > +		i2c@5 {
> > +			reg = <5>;
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +		};
> > +
> > +		i2c@6 {
> > +			reg = <6>;
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +		};
> > +
> > +		i2c@7 {
> > +			reg = <7>;
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +		};
> > +	};
> >  };
> >
> >  &lpi2c3 {
> > diff --git a/arch/arm64/boot/dts/freescale/imx943.dtsi b/arch/arm64/boot/dts/freescale/imx943.dtsi
> > index 657c81b6016f2..db00a94812e18 100644
> > --- a/arch/arm64/boot/dts/freescale/imx943.dtsi
> > +++ b/arch/arm64/boot/dts/freescale/imx943.dtsi
> > @@ -148,7 +148,7 @@ l3_cache: l3-cache {
> >  		};
> >  	};
> >
> > -	clock-ldb-pll-div7 {
> > +	clock_ldb_pll_div7: clock-ldb-pll-div7 {
> >  		compatible = "fixed-factor-clock";
> >  		#clock-cells = <0>;
> >  		clocks = <&scmi_clk IMX94_CLK_LDBPLL>;
> > @@ -173,10 +173,64 @@ dispmix_csr: syscon@4b010000 {
> >
> >  		lvds_csr: syscon@4b0c0000 {
> >  			compatible = "nxp,imx94-lvds-csr", "syscon";
> > +			#address-cells = <1>;
> > +			#size-cells = <1>;
> >  			reg = <0x0 0x4b0c0000 0x0 0x10000>;
> >  			clocks = <&scmi_clk IMX94_CLK_DISPAPB>;
> >  			#clock-cells = <1>;
> >  			power-domains = <&scmi_devpd IMX94_PD_DISPLAY>;
> > +
> > +			ldb: ldb@4 {
> > +				compatible = "fsl,imx94-ldb";
> > +				#address-cells = <1>;
> > +				#size-cells = <0>;
> > +				reg = <0x4 0x4>, <0x8 0x4>;
> > +				reg-names = "ldb", "lvds";
> > +				clocks = <&lvds_csr IMX94_CLK_DISPMIX_LVDS_CLK_GATE>;
> > +				clock-names = "ldb";
> > +				status = "disabled";
> > +
> > +				ports {
> > +					#address-cells = <1>;
> > +					#size-cells = <0>;
> > +
> > +					port@0 {
> > +						reg = <0>;
> > +
> > +						lvds_in: endpoint {
> > +							remote-endpoint = <&dcif_out>;
> > +						};
> > +					};
> > +
> > +					port@1 {
> > +						reg = <1>;
> > +					};
> > +				};
> > +			};
> > +		};
> > +
> > +		dcif: display-controller@4b120000 {
> > +			compatible = "nxp,imx94-dcif";
> > +			reg = <0x0 0x4b120000 0x0 0x300000>;
> > +			interrupts = <GIC_SPI 377 IRQ_TYPE_LEVEL_HIGH>,
> > +				     <GIC_SPI 378 IRQ_TYPE_LEVEL_HIGH>,
> > +				     <GIC_SPI 379 IRQ_TYPE_LEVEL_HIGH>;
> > +			interrupt-names = "common", "bg_layer", "fg_layer";
> > +			clocks = <&scmi_clk IMX94_CLK_DISPAPB>,
> > +				 <&scmi_clk IMX94_CLK_DISPAXI>,
> > +				 <&dispmix_csr IMX94_CLK_DISPMIX_CLK_SEL>;
> > +			clock-names = "apb", "axi", "pix";
> > +			assigned-clocks = <&dispmix_csr IMX94_CLK_DISPMIX_CLK_SEL>;
> > +			assigned-clock-parents = <&clock_ldb_pll_div7>;
> > +			power-domains = <&scmi_devpd IMX94_PD_DISPLAY>;
> > +			nxp,blk-ctrl = <&dispmix_csr>;
> > +			status = "disabled";
> > +
> > +			port {
> > +				dcif_out: endpoint {
> > +					remote-endpoint = <&lvds_in>;
> > +				};
> > +			};
> >  		};
> >  	};
> >  };
> > --
> > 2.46.1
> >

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2025-07-15 12:05 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-09 12:23 [PATCH 0/8] Add support for i.MX94 DCIF Laurentiu Palcu
2025-07-09 12:23 ` [PATCH 1/8] clk: imx95-blk-ctl: Cache registers when RPM routines are called Laurentiu Palcu
2025-07-11  4:03   ` Frank Li
2025-07-15 12:03     ` Laurentiu Palcu
2025-07-09 12:23 ` [PATCH 2/8] dt-bindings: display: fsl,ldb: Add binding for i.MX94 Laurentiu Palcu
2025-07-10 22:15   ` Rob Herring
2025-07-09 12:23 ` [PATCH 3/8] drm/bridge: fsl-ldb: Get the next non-panel bridge Laurentiu Palcu
2025-07-11  4:19   ` Frank Li
2025-07-11  5:31     ` Liu Ying
2025-07-09 12:23 ` [PATCH 4/8] drm/bridge: fsl-ldb: Add support for i.MX94 Laurentiu Palcu
2025-07-11  4:26   ` Frank Li
2025-07-09 12:23 ` [PATCH 5/8] dt-bindings: display: imx: Add bindings for i.MX94 DCIF Laurentiu Palcu
2025-07-10 22:18   ` Rob Herring
2025-07-09 12:23 ` [PATCH 6/8] drm/imx: Add support " Laurentiu Palcu
2025-07-11  5:01   ` Frank Li
2025-07-15 12:04     ` Laurentiu Palcu
2025-07-09 12:23 ` [PATCH 7/8] arm64: dts: imx943-evk: Add support for DCIF and LVDS Laurentiu Palcu
2025-07-11  5:07   ` Frank Li
2025-07-15 12:05     ` Laurentiu Palcu
2025-07-09 12:23 ` [PATCH 8/8] MAINTAINERS: Add entry for i.MX94 DCIF driver Laurentiu Palcu
2025-07-09 12:53   ` Daniel Baluta

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).