* [PATCH v7 00/15] drm/imx: dc: Use prefetch engine
@ 2026-10-26 10:05 Liu Ying
2026-10-26 10:05 ` [PATCH v7 01/15] dt-bindings: display: imx: Add i.MX8qxp/qm PRG binding Liu Ying
` (14 more replies)
0 siblings, 15 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying, Frank Li
Hi,
This patch series aims to use prefetch engine in imx8-dc KMS driver.
One prefetch engine consists of one DPR Channel(DPRC) and one or two
Display Prefetch Resolve Gaskets(PRGs). It prefetches data from
DDR, resolves data in tile formats if needed and outputs the result
data to FetchUnit(s). When one PRG is used, a prefetch engine supports
processing pixel formats with one planar, e.g., RGB formats. When two
are used, pixel formats with two planars are supported, e.g., NV12.
For now, this patch series only supports prefetch engines with one PRG
because it is enough for XRGB8888 which is the only pixel format
supported by imx8-dc plane driver. Two PRGs would be supported by
future patches.
Patch 1 and 2 add dt-bindings for DPRC and PRG. They were reviewed
by Rob[1][2]. I slightly modified the patches' commit message to use
imperative mood.
Patch 3 fixes invisible plane issue reported by Sashiko AI bot in v6.
Patch 4 fixes FetchUnit dimensions as related to prefetch engine function.
Patch 5 flags CRTC status in GERNERALPURPOSE register as needed by patch 6.
Patch 6 disables CRTC at boot if needed to support prefetch engine better.
Patch 7 and 8 add PRG and DPRC drivers.
Patch 9 to 14 do preparations before using prefetch engine in imx8-dc KMS
driver.
Patch 15 uses prefetch engine in imx8-dc KMS driver.
[1] https://lore.kernel.org/lkml/20201207165945.GA430214@robh.at.kernel.org/
[2] https://lore.kernel.org/lkml/20201207170206.GA434964@robh.at.kernel.org/
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
Changes in v7:
- Collect Maxime's A-b tags.
- Fix Sashiko AI bot's comments. See detail changelog in each patch.
- Trivial tweaks.
- Cc Alexander.
- Link to v6: https://patch.msgid.link/20260904-imx8-dc-prefetch-v6-0-26643e3f6cb7@nxp.com
Changes in v6:
- Rebase onto the latest drm-misc-next and resolve conflicts in patch 4&7&13.
- Include "dc-drv.h" in patch 5&6 to avoid build warnings with C=1.
- Fix PRG BYPASS programming in patch 5.
- Link to v5: https://lore.kernel.org/r/20251027-imx8-dc-prefetch-v5-0-4ecb6c6d4941@nxp.com
Changes in v5:
- Squash MAINTAINERS file update into patch 1&2. (Krzysztof)
- Link to v4: https://lore.kernel.org/r/20251016-imx8-dc-prefetch-v4-0-dfda347cb3c5@nxp.com
Changes in v4:
- Rebase onto the latest drm-misc-next and resolve conflicts in patch 4&10.
- Sort bits and bitfields in descending order for register PRG_CTRL in patch 6.
- Cc Marek as stakeholder of the driver due to recent i.MX95 DC patches posted.
- Link to v3: https://lore.kernel.org/r/20250929-imx8-dc-prefetch-v3-0-c01d0608add2@nxp.com
Changes in v3:
- Call dc_dprc_enable() only when start == true in patch 7. (Frank)
- Collect Frank's R-b tags.
- Link to v2: https://lore.kernel.org/r/20250923-imx8-dc-prefetch-v2-0-5d69dc9ac8b5@nxp.com
Changes in v2:
- Rebase the patch series onto next-20250922.
- Collect Alexander's and Frank's R-b tags.
- Manage PRG's and DPRC's clocks with bulk interfaces in patch 6&7. (Frank)
- Sort variables in probe function in reverse Christmas tree fashion in patch 7. (Frank)
- Link to v1: https://lore.kernel.org/r/20250704-imx8-dc-prefetch-v1-0-784c03fd645f@nxp.com
---
Liu Ying (15):
dt-bindings: display: imx: Add i.MX8qxp/qm PRG binding
dt-bindings: display: imx: Add i.MX8qxp/qm DPR channel binding
drm/imx: dc-plane: Disable plane when it is invisible
drm/imx: dc-fu: Fix dimensions
drm/imx: dc: Flag CRTC status in GERNERALPURPOSE register
drm/imx: dc-crtc: Disable at boot
drm/imx: dc: Add PRG support
drm/imx: dc: Add DPR channel support
drm/imx: dc: Use TCON operation mode
drm/imx: dc-ed: Support getting source selection
drm/imx: dc-lb: Support getting secondary input selection
drm/imx: dc-ed: Drop initial source selection
drm/imx: dc-lb: Drop initial primary and secondary input selections
drm/imx: dc-fu: Get DPR channel
drm/imx: dc: Use prefetch engine
.../bindings/display/imx/fsl,imx8qxp-dprc.yaml | 100 ++++
.../bindings/display/imx/fsl,imx8qxp-prg.yaml | 60 +++
MAINTAINERS | 2 +
drivers/gpu/drm/imx/dc/Kconfig | 1 +
drivers/gpu/drm/imx/dc/Makefile | 6 +-
drivers/gpu/drm/imx/dc/dc-crtc.c | 214 ++++++++-
drivers/gpu/drm/imx/dc/dc-de.h | 2 +
drivers/gpu/drm/imx/dc/dc-dprc.c | 522 +++++++++++++++++++++
drivers/gpu/drm/imx/dc/dc-dprc.h | 37 ++
drivers/gpu/drm/imx/dc/dc-drv.c | 83 ++++
drivers/gpu/drm/imx/dc/dc-drv.h | 12 +
drivers/gpu/drm/imx/dc/dc-ed.c | 35 +-
drivers/gpu/drm/imx/dc/dc-fg.c | 11 +-
drivers/gpu/drm/imx/dc/dc-fl.c | 7 +-
drivers/gpu/drm/imx/dc/dc-fu.c | 59 ++-
drivers/gpu/drm/imx/dc/dc-fu.h | 11 +-
drivers/gpu/drm/imx/dc/dc-fw.c | 7 +-
drivers/gpu/drm/imx/dc/dc-kms.h | 7 +
drivers/gpu/drm/imx/dc/dc-lb.c | 27 +-
drivers/gpu/drm/imx/dc/dc-pe.h | 2 +
drivers/gpu/drm/imx/dc/dc-plane.c | 100 ++--
drivers/gpu/drm/imx/dc/dc-prg.c | 326 +++++++++++++
drivers/gpu/drm/imx/dc/dc-prg.h | 39 ++
drivers/gpu/drm/imx/dc/dc-tc.c | 119 ++++-
24 files changed, 1726 insertions(+), 63 deletions(-)
---
base-commit: cf61bef3801c3c9d0d8c39026b7b2fb242898a3a
change-id: 20250703-imx8-dc-prefetch-028e306591c6
Best regards,
--
Regards,
Liu Ying
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v7 01/15] dt-bindings: display: imx: Add i.MX8qxp/qm PRG binding
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 10:05 ` [PATCH v7 02/15] dt-bindings: display: imx: Add i.MX8qxp/qm DPR channel binding Liu Ying
` (13 subsequent siblings)
14 siblings, 0 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying
Add bindings for i.MX8qxp/qm Display Prefetch Resolve Gasket.
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
Changed commit message to use imperative mood.
v5:
- Add the DT binding file in MAINTAINERS. (Krzysztof)
---
.../bindings/display/imx/fsl,imx8qxp-prg.yaml | 60 ++++++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 61 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-prg.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-prg.yaml
new file mode 100644
index 000000000000..3ff46e0d4e73
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-prg.yaml
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-prg.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale i.MX8qm/qxp Display Prefetch Resolve Gasket
+
+maintainers:
+ - Liu Ying <victor.liu@nxp.com>
+
+description: |
+ The i.MX8qm/qxp Prefetch Resolve Gasket (PRG) is a gasket interface between
+ RTRAM controller and Display Controller. The main function is to convert
+ the AXI interface to the RTRAM interface, which includes re-mapping the
+ ARADDR to a RTRAM address.
+
+properties:
+ compatible:
+ enum:
+ - fsl,imx8qxp-prg
+ - fsl,imx8qm-prg
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: rtram clock
+ - description: apb clock
+
+ clock-names:
+ items:
+ - const: rtram
+ - const: apb
+
+ power-domains:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - power-domains
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/imx8-lpcg.h>
+ #include <dt-bindings/firmware/imx/rsrc.h>
+ prg@56040000 {
+ compatible = "fsl,imx8qxp-prg";
+ reg = <0x56040000 0x10000>;
+ clocks = <&dc0_prg0_lpcg IMX_LPCG_CLK_0>,
+ <&dc0_prg0_lpcg IMX_LPCG_CLK_4>;
+ clock-names = "rtram", "apb";
+ power-domains = <&pd IMX_SC_R_DC_0>;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index a08087fb6e67..d9a6bdc65826 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8806,6 +8806,7 @@ L: dri-devel@lists.freedesktop.org
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc*.yaml
+F: Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-prg.yaml
F: drivers/gpu/drm/imx/dc/
DRM DRIVERS FOR FREESCALE IMX BRIDGE
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 02/15] dt-bindings: display: imx: Add i.MX8qxp/qm DPR channel binding
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
2026-10-26 10:05 ` [PATCH v7 01/15] dt-bindings: display: imx: Add i.MX8qxp/qm PRG binding Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 10:05 ` [PATCH v7 03/15] drm/imx: dc-plane: Disable plane when it is invisible Liu Ying
` (12 subsequent siblings)
14 siblings, 0 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying
Add bindings for i.MX8qxp/qm Display Prefetch Resolve Channel.
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
Changed commit message to use imperative mood.
v5:
- Add the DT binding file in MAINTAINERS. (Krzysztof)
---
.../bindings/display/imx/fsl,imx8qxp-dprc.yaml | 100 +++++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 101 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dprc.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dprc.yaml
new file mode 100644
index 000000000000..bd94254c1288
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dprc.yaml
@@ -0,0 +1,100 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dprc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale i.MX8qm/qxp Display Prefetch Resolve Channel
+
+maintainers:
+ - Liu Ying <victor.liu@nxp.com>
+
+description: |
+ The i.MX8qm/qxp Display Prefetch Resolve Channel(DPRC) is an engine which
+ fetches display data before the display pipeline needs the data to drive
+ pixels in the active display region. This data is transformed, or resolved,
+ from a variety of tiled buffer formats into linear format, if needed.
+ The DPR works with a double bank memory structure. This memory structure is
+ implemented in the Resolve Tile Memory(RTRAM) and the banks are referred to
+ as A and B. Each bank is either 4 or 8 lines high depending on the source
+ frame buffer format.
+
+properties:
+ compatible:
+ enum:
+ - fsl,imx8qxp-dpr-channel
+ - fsl,imx8qm-dpr-channel
+
+ reg:
+ maxItems: 1
+
+ interrupts-extended:
+ items:
+ - description: DPR wrap interrupt
+ - description: |
+ 'r_rtram_stall' interrupt which indicates relevant i.MX8qm/qxp
+ Prefetch Resolve Gasket(PRG) or PRGs are forcing an underflow
+ condition in the RTRAM.
+
+ interrupt-names:
+ items:
+ - const: dpr_wrap
+ - const: r_rtram_stall
+
+ clocks:
+ items:
+ - description: apb clock
+ - description: b clock
+ - description: rtram clock
+
+ clock-names:
+ items:
+ - const: apb
+ - const: b
+ - const: rtram
+
+ fsl,sc-resource:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: The SCU resource ID associated with this DPRC instance.
+
+ fsl,prgs:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description: |
+ List of phandle which points to PRG or PRGs associated with
+ this DPRC instance.
+
+ power-domains:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts-extended
+ - interrupt-names
+ - clocks
+ - clock-names
+ - fsl,sc-resource
+ - fsl,prgs
+ - power-domains
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/imx8-lpcg.h>
+ #include <dt-bindings/firmware/imx/rsrc.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ dpr-channel@56100000 {
+ compatible = "fsl,imx8qxp-dpr-channel";
+ reg = <0x56100000 0x10000>;
+ interrupts-extended = <&gic GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>,
+ <&dc0_irqsteer 324>;
+ interrupt-names = "dpr_wrap", "r_rtram_stall";
+ clocks = <&dc0_dpr1_lpcg IMX_LPCG_CLK_4>,
+ <&dc0_dpr1_lpcg IMX_LPCG_CLK_5>,
+ <&dc0_rtram1_lpcg IMX_LPCG_CLK_0>;
+ clock-names = "apb", "b", "rtram";
+ fsl,sc-resource = <IMX_SC_R_DC_0_VIDEO0>;
+ fsl,prgs = <&dc0_prg4>, <&dc0_prg5>;
+ power-domains = <&pd IMX_SC_R_DC_0>;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index d9a6bdc65826..cbb8aef958e5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8806,6 +8806,7 @@ L: dri-devel@lists.freedesktop.org
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc*.yaml
+F: Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dprc.yaml
F: Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-prg.yaml
F: drivers/gpu/drm/imx/dc/
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 03/15] drm/imx: dc-plane: Disable plane when it is invisible
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
2026-10-26 10:05 ` [PATCH v7 01/15] dt-bindings: display: imx: Add i.MX8qxp/qm PRG binding Liu Ying
2026-10-26 10:05 ` [PATCH v7 02/15] dt-bindings: display: imx: Add i.MX8qxp/qm DPR channel binding Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-29 9:28 ` Laurentiu Palcu
2026-10-26 10:05 ` [PATCH v7 04/15] drm/imx: dc-fu: Fix dimensions Liu Ying
` (11 subsequent siblings)
14 siblings, 1 reply; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying
Fetch unit backing plane doesn't support invisible plane because
SOURCEBUFFERDIMENSION and FRAMEDIMENSIONS registers only accept
dimension for visible frame. Disable plane when it is invisible
to avoid wrongly programming the two registers with invisible
frame.
Fixes: 711a3b878366 ("drm/imx: Add i.MX8qxp Display Controller KMS")
Signed-off-by: Liu Ying <victor.liu@nxp.com>
--
v7:
- Newly introduced to fix invisible planes. (Sashiko AI bot)
---
drivers/gpu/drm/imx/dc/dc-plane.c | 54 ++++++++++++++++++++++++---------------
1 file changed, 33 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/imx/dc/dc-plane.c b/drivers/gpu/drm/imx/dc/dc-plane.c
index 62dd0576fadc..42c0a46281cb 100644
--- a/drivers/gpu/drm/imx/dc/dc-plane.c
+++ b/drivers/gpu/drm/imx/dc/dc-plane.c
@@ -126,6 +126,33 @@ dc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_commit *state)
return dc_plane_check_fb(plane_state);
}
+/* A prerequisite is that drm_dev_enter() has been called beforehand. */
+static void __dc_plane_atomic_disable(struct drm_plane *plane)
+{
+ struct dc_plane *dplane = to_dc_plane(plane);
+ const struct dc_fu_ops *fu_ops;
+
+ /* disable fetchunit in shadow */
+ fu_ops = dc_fu_get_ops(dplane->fu);
+ fu_ops->disable_src_buf(dplane->fu, DC_FETCHUNIT_FRAC0);
+
+ /* set ExtDst's source to ConstFrame */
+ dc_ed_pec_src_sel(dplane->ed, dc_cf_get_link_id(dplane->cf));
+}
+
+static void dc_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ int idx;
+
+ if (!drm_dev_enter(plane->dev, &idx))
+ return;
+
+ __dc_plane_atomic_disable(plane);
+
+ drm_dev_exit(idx);
+}
+
static void
dc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state)
{
@@ -143,6 +170,11 @@ dc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state)
if (!drm_dev_enter(plane->dev, &idx))
return;
+ if (!new_state->visible) {
+ __dc_plane_atomic_disable(plane);
+ goto out;
+ }
+
src_w = drm_rect_width(&new_state->src) >> 16;
src_h = drm_rect_height(&new_state->src) >> 16;
@@ -171,27 +203,7 @@ dc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state)
/* set ExtDst's source to LayerBlend */
dc_ed_pec_src_sel(dplane->ed, dc_lb_get_link_id(lb));
-
- drm_dev_exit(idx);
-}
-
-static void dc_plane_atomic_disable(struct drm_plane *plane,
- struct drm_atomic_commit *state)
-{
- struct dc_plane *dplane = to_dc_plane(plane);
- const struct dc_fu_ops *fu_ops;
- int idx;
-
- if (!drm_dev_enter(plane->dev, &idx))
- return;
-
- /* disable fetchunit in shadow */
- fu_ops = dc_fu_get_ops(dplane->fu);
- fu_ops->disable_src_buf(dplane->fu, DC_FETCHUNIT_FRAC0);
-
- /* set ExtDst's source to ConstFrame */
- dc_ed_pec_src_sel(dplane->ed, dc_cf_get_link_id(dplane->cf));
-
+out:
drm_dev_exit(idx);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 04/15] drm/imx: dc-fu: Fix dimensions
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
` (2 preceding siblings ...)
2026-10-26 10:05 ` [PATCH v7 03/15] drm/imx: dc-plane: Disable plane when it is invisible Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 10:05 ` [PATCH v7 05/15] drm/imx: dc: Flag CRTC status in GERNERALPURPOSE register Liu Ying
` (10 subsequent siblings)
14 siblings, 0 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying, Frank Li
Fix off-by-one issue in LINEWIDTH, LINECOUNT, FRAMEWIDTH and FRAMEHEIGHT
macro definitions. The first two macros are used to set a fetchunit's
source buffer dimension and the other two are used to set a fetchunit's
frame dimension. It appears that display controller itself works ok
without this fix, however, it enters panic mode when prefetch engine(DPRC
and PRGs) attaches to it without this fix.
Fixes: 0e177d5ce01c ("drm/imx: Add i.MX8qxp Display Controller pixel engine")
Reviewed-by: Alexander Stein <alexander.stein@ew.tq-group.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v4:
- Rebase onto the latest drm-misc-next and resolve conflicts.
v2:
- Collect Alexander's and Frank's R-b tags.
---
drivers/gpu/drm/imx/dc/dc-fu.c | 4 ++--
drivers/gpu/drm/imx/dc/dc-fu.h | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/imx/dc/dc-fu.c b/drivers/gpu/drm/imx/dc/dc-fu.c
index 1d8f74babef8..2e197b3344bd 100644
--- a/drivers/gpu/drm/imx/dc/dc-fu.c
+++ b/drivers/gpu/drm/imx/dc/dc-fu.c
@@ -31,8 +31,8 @@
#define STRIDE(x) FIELD_PREP(STRIDE_MASK, (x) - 1)
/* SOURCEBUFFERDIMENSION */
-#define LINECOUNT(x) FIELD_PREP(GENMASK(29, 16), (x))
-#define LINEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x))
+#define LINECOUNT(x) FIELD_PREP(GENMASK(29, 16), (x) - 1)
+#define LINEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x) - 1)
/* LAYEROFFSET */
#define LAYERYOFFSET(x) FIELD_PREP(GENMASK(30, 16), (x))
diff --git a/drivers/gpu/drm/imx/dc/dc-fu.h b/drivers/gpu/drm/imx/dc/dc-fu.h
index f678de3ca8c0..cc93317e3001 100644
--- a/drivers/gpu/drm/imx/dc/dc-fu.h
+++ b/drivers/gpu/drm/imx/dc/dc-fu.h
@@ -38,8 +38,8 @@
#define YUVCONVERSIONMODE(x) FIELD_PREP(YUVCONVERSIONMODE_MASK, (x))
/* FRAMEDIMENSIONS */
-#define FRAMEHEIGHT(x) FIELD_PREP(GENMASK(29, 16), (x))
-#define FRAMEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x))
+#define FRAMEHEIGHT(x) FIELD_PREP(GENMASK(29, 16), (x) - 1)
+#define FRAMEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x) - 1)
/* CONTROL */
#define INPUTSELECT_MASK GENMASK(4, 3)
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 05/15] drm/imx: dc: Flag CRTC status in GERNERALPURPOSE register
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
` (3 preceding siblings ...)
2026-10-26 10:05 ` [PATCH v7 04/15] drm/imx: dc-fu: Fix dimensions Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-29 9:28 ` Laurentiu Palcu
2026-10-26 10:05 ` [PATCH v7 06/15] drm/imx: dc-crtc: Disable at boot Liu Ying
` (9 subsequent siblings)
14 siblings, 1 reply; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying
Map Display Controller's register GERNERALPURPOSE at offset 0x100 and
create an MMIO regmap to access the register. Create helpers
dc_drv_flag_crtc_{enable,disable}() with the 2 LSB bits in the register
to flag the CRTC enablement/disablement status. Also, create a helper
dc_drv_crtc_was_enabled() so that the CRTC driver can use it to check
if the CRTC was enabled by a previous boot or not. This is a preparation
for an upcoming patch to disable display controller at boot if the CRTC
was enabled by the driver during a previous boot instead of a bootloader.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
--
v7:
- Newly introduced in v7 as a preparation for fixing Sashiko AI bot's
comment on an upcoming patch. (Sashiko AI bot)
---
drivers/gpu/drm/imx/dc/dc-drv.c | 76 +++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/imx/dc/dc-drv.h | 7 ++++
2 files changed, 83 insertions(+)
diff --git a/drivers/gpu/drm/imx/dc/dc-drv.c b/drivers/gpu/drm/imx/dc/dc-drv.c
index 13795a2ad735..ac1321adf1c8 100644
--- a/drivers/gpu/drm/imx/dc/dc-drv.c
+++ b/drivers/gpu/drm/imx/dc/dc-drv.c
@@ -13,6 +13,7 @@
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <drm/clients/drm_client_setup.h>
#include <drm/drm_atomic_helper.h>
@@ -28,9 +29,21 @@
#include "dc-drv.h"
#include "dc-pe.h"
+/* General Purpose register offset used to flag driver usage. */
+#define GENERALPURPOSE 0x100
+#define CRTC_ENABLED(x) BIT(x)
+
struct dc_priv {
struct drm_device *drm;
struct clk *clk_cfg;
+ struct regmap *gp_regmap;
+};
+
+static const struct regmap_config dc_gp_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0,
};
DEFINE_DRM_GEM_DMA_FOPS(dc_drm_driver_fops);
@@ -156,6 +169,8 @@ static int dc_probe(struct platform_device *pdev)
{
struct component_match *match = NULL;
struct dc_priv *priv;
+ struct resource *r;
+ void __iomem *base;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
@@ -167,6 +182,28 @@ static int dc_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cfg),
"failed to get cfg clock\n");
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r)
+ return -EINVAL;
+
+ if (!devm_request_mem_region(&pdev->dev, r->start + GENERALPURPOSE, 4,
+ dev_name(&pdev->dev))) {
+ dev_err(&pdev->dev, "failed to request memory region\n");
+ return -EBUSY;
+ }
+
+ base = devm_ioremap(&pdev->dev, r->start + GENERALPURPOSE, 4);
+ if (!base) {
+ dev_err(&pdev->dev, "failed to do ioremap\n");
+ return -ENOMEM;
+ }
+
+ priv->gp_regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &dc_gp_regmap_config);
+ if (IS_ERR(priv->gp_regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->gp_regmap),
+ "failed to init General Purpose regmap\n");
+
dev_set_drvdata(&pdev->dev, priv);
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
@@ -196,6 +233,45 @@ static void dc_remove(struct platform_device *pdev)
component_master_del(&pdev->dev, &dc_drm_ops);
}
+void dc_drv_set_crtc_enable(struct dc_drm_device *dc_drm,
+ unsigned int crtc_index)
+{
+ struct dc_priv *priv = dev_get_drvdata(dc_drm->base.dev);
+
+ if (WARN_ON(crtc_index > 1))
+ return;
+
+ regmap_set_bits(priv->gp_regmap, 0, CRTC_ENABLED(crtc_index));
+}
+
+void dc_drv_set_crtc_disable(struct dc_drm_device *dc_drm,
+ unsigned int crtc_index)
+{
+ struct dc_priv *priv = dev_get_drvdata(dc_drm->base.dev);
+
+ if (WARN_ON(crtc_index > 1))
+ return;
+
+ regmap_clear_bits(priv->gp_regmap, 0, CRTC_ENABLED(crtc_index));
+}
+
+bool dc_drv_crtc_was_enabled(struct dc_drm_device *dc_drm,
+ unsigned int crtc_index)
+{
+ struct dc_priv *priv = dev_get_drvdata(dc_drm->base.dev);
+ unsigned int val;
+ int ret;
+
+ if (WARN_ON(crtc_index > 1))
+ return false;
+
+ ret = regmap_read(priv->gp_regmap, 0, &val);
+ if (ret)
+ return false;
+
+ return !!(val & CRTC_ENABLED(crtc_index));
+}
+
static int dc_runtime_suspend(struct device *dev)
{
struct dc_priv *priv = dev_get_drvdata(dev);
diff --git a/drivers/gpu/drm/imx/dc/dc-drv.h b/drivers/gpu/drm/imx/dc/dc-drv.h
index eb61b8c76269..dd1302382ab0 100644
--- a/drivers/gpu/drm/imx/dc/dc-drv.h
+++ b/drivers/gpu/drm/imx/dc/dc-drv.h
@@ -99,4 +99,11 @@ static inline int dc_subdev_get_id(const struct dc_subdev_info *info,
void dc_de_post_bind(struct dc_drm_device *dc_drm);
void dc_pe_post_bind(struct dc_drm_device *dc_drm);
+void dc_drv_set_crtc_enable(struct dc_drm_device *dc_drm,
+ unsigned int crtc_index);
+void dc_drv_set_crtc_disable(struct dc_drm_device *dc_drm,
+ unsigned int crtc_index);
+bool dc_drv_crtc_was_enabled(struct dc_drm_device *dc_drm,
+ unsigned int crtc_index);
+
#endif /* __DC_DRV_H__ */
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 06/15] drm/imx: dc-crtc: Disable at boot
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
` (4 preceding siblings ...)
2026-10-26 10:05 ` [PATCH v7 05/15] drm/imx: dc: Flag CRTC status in GERNERALPURPOSE register Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 11:18 ` sashiko-bot
2026-10-29 9:30 ` Laurentiu Palcu
2026-10-26 10:05 ` [PATCH v7 07/15] drm/imx: dc: Add PRG support Liu Ying
` (8 subsequent siblings)
14 siblings, 2 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying, Frank Li
CRTC(s) could still be running after the DRM device is unplugged by
calling drm_dev_unplug(), because the CRTC disablement logic is
protected and bypassed by the drm_dev_enter()/drm_dev_exit() pair.
Hence, Pixel Engine's AXI clock use count(managed by Pixel Engine
driver's runtime PM) and pixel clock use count could be inbalanced
after removing and re-installing the driver module. To fix this,
add a helper dc_crtc_disable_at_boot() and call it to properly
disable all CRTCs before advertising DRM device to user-space by
calling drm_dev_register().
Fixes: 711a3b878366 ("drm/imx: Add i.MX8qxp Display Controller KMS")
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
--
v7:
- Check CRTC status to ensure that clocks were enabled by a previous boot
before disabling them in dc_crtc_disable_at_boot(). (Sashiko AI bot)
- Modify debug message in dc_crtc_disable_at_boot().
v6:
- Rebase onto the latest drm-misc-next and resolve conflicts.
v3:
- Collect Frank's R-b tag.
---
drivers/gpu/drm/imx/dc/dc-crtc.c | 67 ++++++++++++++++++++++++++++++++++------
drivers/gpu/drm/imx/dc/dc-drv.c | 5 +++
drivers/gpu/drm/imx/dc/dc-drv.h | 3 ++
3 files changed, 65 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/imx/dc/dc-crtc.c b/drivers/gpu/drm/imx/dc/dc-crtc.c
index 0d64186a9901..c7dbe5f500b4 100644
--- a/drivers/gpu/drm/imx/dc/dc-crtc.c
+++ b/drivers/gpu/drm/imx/dc/dc-crtc.c
@@ -227,6 +227,7 @@ dc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
struct drm_crtc_state *new_crtc_state =
drm_atomic_get_new_crtc_state(state, crtc);
struct drm_display_mode *adj = &new_crtc_state->adjusted_mode;
+ struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
enum dc_link_id cf_link;
int idx, ret;
@@ -287,29 +288,37 @@ dc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
DC_CRTC_CHECK_FRAMEGEN_FIFO(dc_crtc->fg);
+ dc_drv_set_crtc_enable(dc_drm, drm_crtc_index(crtc));
+
drm_dev_exit(idx);
out:
dc_crtc_queue_state_event(new_crtc_state);
}
-static void
-dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
+static inline void __dc_crtc_disable_fg(struct drm_crtc *crtc)
{
- struct drm_crtc_state *new_crtc_state =
- drm_atomic_get_new_crtc_state(state, crtc);
- struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
- int idx;
-
- if (!drm_dev_enter(crtc->dev, &idx))
- goto out;
enable_irq(dc_crtc->irq_dec_seqcomplete);
dc_fg_disable(dc_crtc->fg);
DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_seqcomplete_done);
disable_irq(dc_crtc->irq_dec_seqcomplete);
+}
+static void
+dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
+{
+ struct drm_crtc_state *new_crtc_state =
+ drm_atomic_get_new_crtc_state(state, crtc);
+ struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
+ struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
+ int idx;
+
+ if (!drm_dev_enter(crtc->dev, &idx))
+ goto out;
+
+ __dc_crtc_disable_fg(crtc);
dc_fg_disable_clock(dc_crtc->fg);
/* request pixel engine power-off as plane is off too */
@@ -318,8 +327,9 @@ dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
/* request display engine power-off when CRTC is disabled */
pm_runtime_put(dc_crtc->de->dev);
- drm_dev_exit(idx);
+ dc_drv_set_crtc_disable(dc_drm, drm_crtc_index(crtc));
+ drm_dev_exit(idx);
out:
drm_crtc_vblank_off(crtc);
@@ -331,6 +341,43 @@ dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
spin_unlock_irq(&crtc->dev->event_lock);
}
+void dc_crtc_disable_at_boot(struct drm_crtc *crtc)
+{
+ struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
+ struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dc_crtc->de->dev);
+ if (ret < 0) {
+ dc_crtc_err(crtc, "failed to get DC display engine RPM: %d\n",
+ ret);
+ return;
+ }
+
+ if (!dc_fg_wait_for_frame_index_moving(dc_crtc->fg)) {
+ dc_crtc_dbg(crtc, "FrameGen frame index isn't moving\n");
+ goto out;
+ }
+
+ dc_crtc_dbg(crtc, "disabling FrameGen at boot\n");
+ __dc_crtc_disable_fg(crtc);
+
+ if (!dc_drv_crtc_was_enabled(dc_drm, drm_crtc_index(crtc)))
+ goto out;
+
+ dc_crtc_dbg(crtc, "disabling clocks at boot\n");
+
+ dc_fg_disable_clock(dc_crtc->fg);
+
+ if (!dc_drm->pe_clk_axi_disabled) {
+ clk_disable_unprepare(dc_drm->pe->clk_axi);
+ dc_drm->pe_clk_axi_disabled = true;
+ }
+
+out:
+ pm_runtime_put(dc_crtc->de->dev);
+}
+
static bool dc_crtc_get_scanout_position(struct drm_crtc *crtc,
bool in_vblank_irq,
int *vpos, int *hpos,
diff --git a/drivers/gpu/drm/imx/dc/dc-drv.c b/drivers/gpu/drm/imx/dc/dc-drv.c
index ac1321adf1c8..09387d62cc0b 100644
--- a/drivers/gpu/drm/imx/dc/dc-drv.c
+++ b/drivers/gpu/drm/imx/dc/dc-drv.c
@@ -17,6 +17,7 @@
#include <drm/clients/drm_client_setup.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fourcc.h>
@@ -108,6 +109,7 @@ static int dc_drm_bind(struct device *dev)
struct dc_priv *priv = dev_get_drvdata(dev);
struct dc_drm_device *dc_drm;
struct drm_device *drm;
+ struct drm_crtc *crtc;
int ret;
dc_drm = devm_drm_dev_alloc(dev, &dc_drm_driver, struct dc_drm_device,
@@ -130,6 +132,9 @@ static int dc_drm_bind(struct device *dev)
if (ret)
return ret;
+ drm_for_each_crtc(crtc, drm)
+ dc_crtc_disable_at_boot(crtc);
+
ret = drm_dev_register(drm, 0);
if (ret) {
dev_err(dev, "failed to register drm device: %d\n", ret);
diff --git a/drivers/gpu/drm/imx/dc/dc-drv.h b/drivers/gpu/drm/imx/dc/dc-drv.h
index dd1302382ab0..1ec9304329eb 100644
--- a/drivers/gpu/drm/imx/dc/dc-drv.h
+++ b/drivers/gpu/drm/imx/dc/dc-drv.h
@@ -50,6 +50,8 @@ struct dc_drm_device {
struct dc_pe *pe;
/** @tc: tcon list */
struct dc_tc *tc[DC_DISPLAYS];
+ /** @pe_clk_axi_disabled: disablement flag at boot */
+ bool pe_clk_axi_disabled;
};
struct dc_subdev_info {
@@ -96,6 +98,7 @@ static inline int dc_subdev_get_id(const struct dc_subdev_info *info,
return -EINVAL;
}
+void dc_crtc_disable_at_boot(struct drm_crtc *crtc);
void dc_de_post_bind(struct dc_drm_device *dc_drm);
void dc_pe_post_bind(struct dc_drm_device *dc_drm);
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 07/15] drm/imx: dc: Add PRG support
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
` (5 preceding siblings ...)
2026-10-26 10:05 ` [PATCH v7 06/15] drm/imx: dc-crtc: Disable at boot Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 11:32 ` sashiko-bot
2026-10-29 9:30 ` Laurentiu Palcu
2026-10-26 10:05 ` [PATCH v7 08/15] drm/imx: dc: Add DPR channel support Liu Ying
` (7 subsequent siblings)
14 siblings, 2 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying, Frank Li
Display Prefetch Resolve Gasket(PRG) is a part of a FetchUnit's
prefetch engine. It sits between a Display Prefetch Resolve
Channel(DPRC) and a FetchUnit. Add a platform driver to support
the PRG.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v7:
- Collect Maxime's A-b tag.
- Return 128B burst size from dc_prg_burst_size_fixup() when baddr == 0.
This avoids undefined behavior of __ffs(0). (Sashiko AI bot)
- Initialize and declare prg_node at the same time. (Sashiko AI bot)
- Add device link between PRG's consumer and PRG. (Sashiko AI bot)
v6:
- Include "dc-drv.h" to avoid build warning with C=1.
- Fix PRG BYPASS programming.
v4:
- Sort bits and bitfields in descending order for register PRG_CTRL.
v2:
- Manage clocks with bulk interfaces. (Frank)
- Collect Frank's R-b tag.
---
drivers/gpu/drm/imx/dc/Makefile | 2 +-
drivers/gpu/drm/imx/dc/dc-drv.c | 1 +
drivers/gpu/drm/imx/dc/dc-drv.h | 1 +
drivers/gpu/drm/imx/dc/dc-prg.c | 314 ++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/imx/dc/dc-prg.h | 35 +++++
5 files changed, 352 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/imx/dc/Makefile b/drivers/gpu/drm/imx/dc/Makefile
index b9d33c074984..e3a06ee3ce1a 100644
--- a/drivers/gpu/drm/imx/dc/Makefile
+++ b/drivers/gpu/drm/imx/dc/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
imx8-dc-drm-objs := dc-cf.o dc-crtc.o dc-de.o dc-drv.o dc-ed.o dc-fg.o dc-fl.o \
- dc-fu.o dc-fw.o dc-ic.o dc-kms.o dc-lb.o dc-pe.o \
+ dc-fu.o dc-fw.o dc-ic.o dc-kms.o dc-lb.o dc-pe.o dc-prg.o \
dc-plane.o dc-tc.o
obj-$(CONFIG_DRM_IMX8_DC) += imx8-dc-drm.o
diff --git a/drivers/gpu/drm/imx/dc/dc-drv.c b/drivers/gpu/drm/imx/dc/dc-drv.c
index 09387d62cc0b..1d01f896e427 100644
--- a/drivers/gpu/drm/imx/dc/dc-drv.c
+++ b/drivers/gpu/drm/imx/dc/dc-drv.c
@@ -351,6 +351,7 @@ static struct platform_driver * const dc_drivers[] = {
&dc_ic_driver,
&dc_lb_driver,
&dc_pe_driver,
+ &dc_prg_driver,
&dc_tc_driver,
&dc_driver,
};
diff --git a/drivers/gpu/drm/imx/dc/dc-drv.h b/drivers/gpu/drm/imx/dc/dc-drv.h
index 1ec9304329eb..27ef2c552549 100644
--- a/drivers/gpu/drm/imx/dc/dc-drv.h
+++ b/drivers/gpu/drm/imx/dc/dc-drv.h
@@ -81,6 +81,7 @@ extern struct platform_driver dc_fw_driver;
extern struct platform_driver dc_ic_driver;
extern struct platform_driver dc_lb_driver;
extern struct platform_driver dc_pe_driver;
+extern struct platform_driver dc_prg_driver;
extern struct platform_driver dc_tc_driver;
static inline int dc_subdev_get_id(const struct dc_subdev_info *info,
diff --git a/drivers/gpu/drm/imx/dc/dc-prg.c b/drivers/gpu/drm/imx/dc/dc-prg.c
new file mode 100644
index 000000000000..43df5683b24d
--- /dev/null
+++ b/drivers/gpu/drm/imx/dc/dc-prg.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include "dc-drv.h"
+#include "dc-prg.h"
+
+#define SET 0x4
+#define CLR 0x8
+#define TOG 0xc
+
+#define PRG_CTRL 0x00
+#define SHADOW_EN BIT(31)
+#define SOFTRST BIT(30)
+#define DES_DATA_TYPE_MASK GENMASK(17, 16)
+#define DES_DATA_TYPE_32BPP FIELD_PREP(DES_DATA_TYPE_MASK, 0)
+#define DES_DATA_TYPE_24BPP FIELD_PREP(DES_DATA_TYPE_MASK, 1)
+#define DES_DATA_TYPE_16BPP FIELD_PREP(DES_DATA_TYPE_MASK, 2)
+#define DES_DATA_TYPE_8BPP FIELD_PREP(DES_DATA_TYPE_MASK, 3)
+#define SHADOW_LOAD_MODE BIT(5)
+#define HANDSHAKE_MODE_4LINES 0
+#define SC_DATA_TYPE_8BIT 0
+#define BYPASS BIT(0)
+
+#define PRG_STATUS 0x10
+
+#define PRG_REG_UPDATE 0x20
+#define REG_UPDATE BIT(0)
+
+#define PRG_STRIDE 0x30
+#define STRIDE(n) FIELD_PREP(GENMASK(15, 0), (n) - 1)
+
+#define PRG_HEIGHT 0x40
+#define HEIGHT(n) FIELD_PREP(GENMASK(15, 0), (n) - 1)
+
+#define PRG_BADDR 0x50
+#define PRG_OFFSET 0x60
+
+#define PRG_WIDTH 0x70
+#define WIDTH(n) FIELD_PREP(GENMASK(15, 0), (n) - 1)
+
+#define DPU_PRG_MAX_STRIDE 0x10000
+
+struct dc_prg {
+ struct device *dev;
+ struct regmap *reg;
+ struct list_head list;
+ struct clk_bulk_data *clks;
+ int num_clks;
+};
+
+static DEFINE_MUTEX(dc_prg_list_mutex);
+static LIST_HEAD(dc_prg_list);
+
+static const struct regmap_config dc_prg_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .fast_io = true,
+ .max_register = PRG_WIDTH + TOG,
+};
+
+static void dc_prg_reset(struct dc_prg *prg)
+{
+ regmap_write(prg->reg, PRG_CTRL + SET, SOFTRST);
+ fsleep(10);
+ regmap_write(prg->reg, PRG_CTRL + CLR, SOFTRST);
+ fsleep(10);
+}
+
+void dc_prg_enable(struct dc_prg *prg)
+{
+ regmap_write(prg->reg, PRG_CTRL + CLR, BYPASS);
+}
+
+void dc_prg_disable(struct dc_prg *prg)
+{
+ regmap_write(prg->reg, PRG_CTRL, BYPASS);
+
+ pm_runtime_put(prg->dev);
+}
+
+void dc_prg_disable_at_boot(struct dc_prg *prg)
+{
+ regmap_write(prg->reg, PRG_CTRL, BYPASS);
+
+ clk_bulk_disable_unprepare(prg->num_clks, prg->clks);
+}
+
+static unsigned int dc_prg_burst_size_fixup(dma_addr_t baddr)
+{
+ unsigned int burst_size;
+
+ if (baddr == 0)
+ return 128U;
+
+ burst_size = 1 << __ffs(baddr);
+ burst_size = round_up(burst_size, 8);
+ burst_size = min(burst_size, 128U);
+
+ return burst_size;
+}
+
+static unsigned int
+dc_prg_stride_fixup(unsigned int stride, unsigned int burst_size)
+{
+ return round_up(stride, burst_size);
+}
+
+void dc_prg_configure(struct dc_prg *prg,
+ unsigned int width, unsigned int height,
+ unsigned int stride, unsigned int bits_per_pixel,
+ dma_addr_t baddr, bool start)
+{
+ struct device *dev = prg->dev;
+ unsigned int burst_size;
+ u32 val;
+ int ret;
+
+ if (start) {
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to get RPM: %d\n", ret);
+ return;
+ }
+ }
+
+ burst_size = dc_prg_burst_size_fixup(baddr);
+
+ stride = dc_prg_stride_fixup(stride, burst_size);
+
+ regmap_write(prg->reg, PRG_STRIDE, STRIDE(stride));
+ regmap_write(prg->reg, PRG_WIDTH, WIDTH(width));
+ regmap_write(prg->reg, PRG_HEIGHT, HEIGHT(height));
+ regmap_write(prg->reg, PRG_OFFSET, 0);
+ regmap_write(prg->reg, PRG_BADDR, baddr);
+
+ val = SHADOW_LOAD_MODE | SC_DATA_TYPE_8BIT | HANDSHAKE_MODE_4LINES;
+
+ switch (bits_per_pixel) {
+ case 32:
+ val |= DES_DATA_TYPE_32BPP;
+ break;
+ case 24:
+ val |= DES_DATA_TYPE_24BPP;
+ break;
+ case 16:
+ val |= DES_DATA_TYPE_16BPP;
+ break;
+ case 8:
+ val |= DES_DATA_TYPE_8BPP;
+ break;
+ }
+
+ /* no shadow for the first frame */
+ val |= start ? BYPASS : SHADOW_EN;
+
+ regmap_write(prg->reg, PRG_CTRL, val);
+}
+
+void dc_prg_reg_update(struct dc_prg *prg)
+{
+ regmap_write(prg->reg, PRG_REG_UPDATE, REG_UPDATE);
+}
+
+void dc_prg_shadow_enable(struct dc_prg *prg)
+{
+ regmap_write(prg->reg, PRG_CTRL + SET, SHADOW_EN);
+}
+
+bool dc_prg_stride_supported(struct dc_prg *prg,
+ unsigned int stride, dma_addr_t baddr)
+{
+ unsigned int burst_size;
+
+ burst_size = dc_prg_burst_size_fixup(baddr);
+
+ stride = dc_prg_stride_fixup(stride, burst_size);
+
+ if (stride > DPU_PRG_MAX_STRIDE)
+ return false;
+
+ return true;
+}
+
+struct dc_prg *
+dc_prg_lookup_by_phandle(struct device *dev, const char *name, int index)
+{
+ struct device_node *prg_node __free(device_node) =
+ of_parse_phandle(dev->of_node, name, index);
+ struct dc_prg *prg;
+
+ if (!prg_node)
+ return NULL;
+
+ guard(mutex)(&dc_prg_list_mutex);
+ list_for_each_entry(prg, &dc_prg_list, list) {
+ if (prg_node == prg->dev->of_node) {
+ device_link_add(dev, prg->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+ return prg;
+ }
+ }
+
+ return NULL;
+}
+
+static int dc_prg_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ void __iomem *base;
+ struct dc_prg *prg;
+ int ret;
+
+ prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL);
+ if (!prg)
+ return -ENOMEM;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ prg->reg = devm_regmap_init_mmio(dev, base, &dc_prg_regmap_config);
+ if (IS_ERR(prg->reg))
+ return PTR_ERR(prg->reg);
+
+ prg->num_clks = devm_clk_bulk_get_all(dev, &prg->clks);
+ if (prg->num_clks < 0)
+ return dev_err_probe(dev, prg->num_clks, "failed to get clocks\n");
+
+ dev_set_drvdata(dev, prg);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable PM runtime\n");
+
+ prg->dev = dev;
+
+ guard(mutex)(&dc_prg_list_mutex);
+ list_add(&prg->list, &dc_prg_list);
+
+ return 0;
+}
+
+static void dc_prg_remove(struct platform_device *pdev)
+{
+ struct dc_prg *prg = dev_get_drvdata(&pdev->dev);
+
+ guard(mutex)(&dc_prg_list_mutex);
+ list_del(&prg->list);
+}
+
+static int dc_prg_runtime_suspend(struct device *dev)
+{
+ struct dc_prg *prg = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(prg->num_clks, prg->clks);
+
+ return 0;
+}
+
+static int dc_prg_runtime_resume(struct device *dev)
+{
+ struct dc_prg *prg = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(prg->num_clks, prg->clks);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks: %d\n", ret);
+ return ret;
+ }
+
+ dc_prg_reset(prg);
+
+ return 0;
+}
+
+static const struct dev_pm_ops dc_prg_pm_ops = {
+ RUNTIME_PM_OPS(dc_prg_runtime_suspend, dc_prg_runtime_resume, NULL)
+};
+
+static const struct of_device_id dc_prg_dt_ids[] = {
+ { .compatible = "fsl,imx8qxp-prg", },
+ { /* sentinel */ }
+};
+
+struct platform_driver dc_prg_driver = {
+ .probe = dc_prg_probe,
+ .remove = dc_prg_remove,
+ .driver = {
+ .name = "imx8-dc-prg",
+ .suppress_bind_attrs = true,
+ .of_match_table = dc_prg_dt_ids,
+ .pm = pm_ptr(&dc_prg_pm_ops),
+ },
+};
diff --git a/drivers/gpu/drm/imx/dc/dc-prg.h b/drivers/gpu/drm/imx/dc/dc-prg.h
new file mode 100644
index 000000000000..6fd9b050bfa1
--- /dev/null
+++ b/drivers/gpu/drm/imx/dc/dc-prg.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2025 NXP
+ */
+
+#ifndef __DC_PRG_H__
+#define __DC_PRG_H__
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+struct dc_prg;
+
+void dc_prg_enable(struct dc_prg *prg);
+
+void dc_prg_disable(struct dc_prg *prg);
+
+void dc_prg_disable_at_boot(struct dc_prg *prg);
+
+void dc_prg_configure(struct dc_prg *prg,
+ unsigned int width, unsigned int height,
+ unsigned int stride, unsigned int bits_per_pixel,
+ dma_addr_t baddr, bool start);
+
+void dc_prg_reg_update(struct dc_prg *prg);
+
+void dc_prg_shadow_enable(struct dc_prg *prg);
+
+bool dc_prg_stride_supported(struct dc_prg *prg,
+ unsigned int stride, dma_addr_t baddr);
+
+struct dc_prg *
+dc_prg_lookup_by_phandle(struct device *dev, const char *name, int index);
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 08/15] drm/imx: dc: Add DPR channel support
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
` (6 preceding siblings ...)
2026-10-26 10:05 ` [PATCH v7 07/15] drm/imx: dc: Add PRG support Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 11:52 ` sashiko-bot
2026-10-29 9:31 ` Laurentiu Palcu
2026-10-26 10:05 ` [PATCH v7 09/15] drm/imx: dc: Use TCON operation mode Liu Ying
` (6 subsequent siblings)
14 siblings, 2 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying, Frank Li
Display Prefetch Resolve Channel(DPRC) is a part of a prefetch engine.
It fetches display data, transforms it to linear format and stores it
to DPRC's RTRAM. PRG, as the other part of a prefetch engine, acts as
a gasket between the RTRAM controller and a FetchUnit. Add a platform
driver to support the DPRC.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v7:
- Collect Maxime's A-b tag.
- Fix dc_dprc_regmap_write_ranges[]. (Sashiko AI bot)
- Fix dc_dprc_wrap_irq_handler() return value. (Sashiko AI bot)
- Check runtime PM status in dc_dprc_wrap_irq_handler(). (Sashiko AI bot)
- Fix DPU_DRPC_MAX_STRIDE macro. (Sashiko AI bot)
- Support deferral probe if platform_get_irq_byname() fails. (Sashiko AI bot)
- Avoid dangling prg->dprc by calling dc_prg_set_dprc() properly.
(Sashiko AI bot)
- Add dc_dprc_get() helper for consumer device to get DPRC properly.
- Set use_raw_spinlock flag in regmap configuration to avoid a potential
hard lockup because dc_dprc_disable_repeat_en() is supposed to be called
with local irq and preempt disabled.
v6:
- Include "dc-drv.h" to avoid build warning with C=1.
v3:
- Call dc_dprc_enable() only when start == true. (Frank)
- Collect Frank's R-b tag.
v2:
- Manage clocks with bulk interfaces. (Frank)
- Sort variables in probe function in reverse Christmas tree fashion. (Frank)
---
drivers/gpu/drm/imx/dc/Kconfig | 1 +
drivers/gpu/drm/imx/dc/Makefile | 6 +-
drivers/gpu/drm/imx/dc/dc-dprc.c | 522 +++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/imx/dc/dc-dprc.h | 37 +++
drivers/gpu/drm/imx/dc/dc-drv.c | 1 +
drivers/gpu/drm/imx/dc/dc-drv.h | 1 +
drivers/gpu/drm/imx/dc/dc-prg.c | 12 +
drivers/gpu/drm/imx/dc/dc-prg.h | 4 +
8 files changed, 581 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/imx/dc/Kconfig b/drivers/gpu/drm/imx/dc/Kconfig
index 415993207f2e..507dc9a92d96 100644
--- a/drivers/gpu/drm/imx/dc/Kconfig
+++ b/drivers/gpu/drm/imx/dc/Kconfig
@@ -1,6 +1,7 @@
config DRM_IMX8_DC
tristate "Freescale i.MX8 Display Controller Graphics"
depends on DRM && COMMON_CLK && OF && (ARCH_MXC || COMPILE_TEST)
+ depends on IMX_SCU
select DRM_CLIENT_SELECTION
select DRM_GEM_DMA_HELPER
select DRM_KMS_HELPER
diff --git a/drivers/gpu/drm/imx/dc/Makefile b/drivers/gpu/drm/imx/dc/Makefile
index e3a06ee3ce1a..fd5d62783971 100644
--- a/drivers/gpu/drm/imx/dc/Makefile
+++ b/drivers/gpu/drm/imx/dc/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
-imx8-dc-drm-objs := dc-cf.o dc-crtc.o dc-de.o dc-drv.o dc-ed.o dc-fg.o dc-fl.o \
- dc-fu.o dc-fw.o dc-ic.o dc-kms.o dc-lb.o dc-pe.o dc-prg.o \
- dc-plane.o dc-tc.o
+imx8-dc-drm-objs := dc-cf.o dc-crtc.o dc-de.o dc-dprc.o dc-drv.o dc-ed.o \
+ dc-fg.o dc-fl.o dc-fu.o dc-fw.o dc-ic.o dc-kms.o dc-lb.o \
+ dc-pe.o dc-prg.o dc-plane.o dc-tc.o
obj-$(CONFIG_DRM_IMX8_DC) += imx8-dc-drm.o
diff --git a/drivers/gpu/drm/imx/dc/dc-dprc.c b/drivers/gpu/drm/imx/dc/dc-dprc.c
new file mode 100644
index 000000000000..a8ddcd117a52
--- /dev/null
+++ b/drivers/gpu/drm/imx/dc/dc-dprc.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware/imx/svc/misc.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/firmware/imx/rsrc.h>
+
+#include "dc-dprc.h"
+#include "dc-drv.h"
+#include "dc-prg.h"
+
+#define SET 0x4
+#define CLR 0x8
+#define TOG 0xc
+
+#define SYSTEM_CTRL0 0x00
+#define SW_SHADOW_LOAD_SEL BIT(4)
+#define SHADOW_LOAD_EN BIT(3)
+#define REPEAT_EN BIT(2)
+#define SOFT_RESET BIT(1)
+#define RUN_EN BIT(0) /* self-clearing */
+
+#define IRQ_MASK 0x20
+#define IRQ_MASK_STATUS 0x30
+#define IRQ_NONMASK_STATUS 0x40
+#define DPR2RTR_FIFO_LOAD_BUF_RDY_UV_ERROR BIT(7)
+#define DPR2RTR_FIFO_LOAD_BUF_RDY_YRGB_ERROR BIT(6)
+#define DPR2RTR_UV_FIFO_OVFL BIT(5)
+#define DPR2RTR_YRGB_FIFO_OVFL BIT(4)
+#define IRQ_AXI_READ_ERROR BIT(3)
+#define IRQ_DPR_SHADOW_LOADED_MASK BIT(2)
+#define IRQ_DPR_RUN BIT(1)
+#define IRQ_DPR_CRTL_DONE BIT(0)
+#define IRQ_CTRL_MASK GENMASK(2, 0)
+
+#define MODE_CTRL0 0x50
+#define A_COMP_SEL(byte) FIELD_PREP(GENMASK(17, 16), (byte))
+#define R_COMP_SEL(byte) FIELD_PREP(GENMASK(15, 14), (byte))
+#define G_COMP_SEL(byte) FIELD_PREP(GENMASK(13, 12), (byte))
+#define B_COMP_SEL(byte) FIELD_PREP(GENMASK(11, 10), (byte))
+#define PIX_SIZE_32BIT FIELD_PREP(GENMASK(7, 6), 0x2)
+#define LINE4 BIT(1)
+#define BUF2 0
+
+#define FRAME_CTRL0 0x70
+#define PITCH(n) FIELD_PREP(GENMASK(31, 16), (n))
+
+#define FRAME_1P_CTRL0 0x90
+#define FRAME_2P_CTRL0 0xe0
+#define MAX_BYTES_PREQ_MASK GENMASK(2, 0)
+#define BYTE_1K FIELD_PREP(MAX_BYTES_PREQ_MASK, 0x4)
+
+#define FRAME_1P_PIX_X_CTRL 0xa0
+#define NUM_X_PIX_WIDE(n) FIELD_PREP(GENMASK(15, 0), (n))
+
+#define FRAME_1P_PIX_Y_CTRL 0xb0
+#define NUM_Y_PIX_HIGH(n) FIELD_PREP(GENMASK(15, 0), (n))
+
+#define FRAME_1P_BASE_ADDR_CTRL0 0xc0
+
+#define FRAME_PIX_X_ULC_CTRL 0xf0
+#define CROP_ULC_X(n) FIELD_PREP(GENMASK(15, 0), (n))
+
+#define FRAME_PIX_Y_ULC_CTRL 0x100
+#define CROP_ULC_Y(n) FIELD_PREP(GENMASK(15, 0), (n))
+
+#define FRAME_2P_BASE_ADDR_CTRL0 0x110
+
+#define STATUS_CTRL0 0x130
+#define STATUS_CTRL1 0x140
+
+#define RTRAM_CTRL0 0x200
+#define THRES_LOW(n) FIELD_PREP(GENMASK(6, 4), (n))
+#define THRES_HIGH(n) FIELD_PREP(GENMASK(3, 1), (n))
+
+#define DPU_DRPC_MAX_STRIDE 0xffff
+#define DPU_DPRC_MAX_RTRAM_WIDTH 2880
+
+struct dc_dprc {
+ struct device *dev;
+ struct regmap *reg;
+ struct list_head list;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ struct imx_sc_ipc *ipc_handle;
+ spinlock_t lock; /* protect IRQ registers */
+ u32 sc_resource;
+ struct dc_prg *prg;
+};
+
+static DEFINE_MUTEX(dc_dprc_list_mutex);
+static LIST_HEAD(dc_dprc_list);
+
+static const struct regmap_range dc_dprc_regmap_write_ranges[] = {
+ regmap_reg_range(SYSTEM_CTRL0, SYSTEM_CTRL0 + TOG),
+ regmap_reg_range(IRQ_MASK, MODE_CTRL0 + TOG),
+ regmap_reg_range(FRAME_CTRL0, FRAME_CTRL0 + TOG),
+ regmap_reg_range(FRAME_1P_CTRL0, FRAME_1P_BASE_ADDR_CTRL0 + TOG),
+ regmap_reg_range(FRAME_PIX_X_ULC_CTRL, FRAME_2P_BASE_ADDR_CTRL0 + TOG),
+ regmap_reg_range(STATUS_CTRL0, STATUS_CTRL0 + TOG),
+ regmap_reg_range(RTRAM_CTRL0, RTRAM_CTRL0 + TOG),
+};
+
+static const struct regmap_range dc_dprc_regmap_read_ranges[] = {
+ regmap_reg_range(SYSTEM_CTRL0, SYSTEM_CTRL0 + TOG),
+ regmap_reg_range(IRQ_MASK, IRQ_MASK_STATUS + TOG),
+ regmap_reg_range(MODE_CTRL0, MODE_CTRL0 + TOG),
+ regmap_reg_range(FRAME_CTRL0, FRAME_CTRL0 + TOG),
+ regmap_reg_range(FRAME_1P_CTRL0, FRAME_1P_BASE_ADDR_CTRL0 + TOG),
+ regmap_reg_range(FRAME_PIX_X_ULC_CTRL, FRAME_2P_BASE_ADDR_CTRL0 + TOG),
+ regmap_reg_range(STATUS_CTRL0, STATUS_CTRL1 + TOG),
+ regmap_reg_range(RTRAM_CTRL0, RTRAM_CTRL0 + TOG),
+};
+
+static const struct regmap_access_table dc_dprc_regmap_write_table = {
+ .yes_ranges = dc_dprc_regmap_write_ranges,
+ .n_yes_ranges = ARRAY_SIZE(dc_dprc_regmap_write_ranges),
+};
+
+static const struct regmap_access_table dc_dprc_regmap_read_table = {
+ .yes_ranges = dc_dprc_regmap_read_ranges,
+ .n_yes_ranges = ARRAY_SIZE(dc_dprc_regmap_read_ranges),
+};
+
+static const struct regmap_config dc_dprc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .fast_io = true,
+ /*
+ * Registers could be accessed with local irq and preempt disabled,
+ * so use a raw spinlock to avoid sleeping on PREEMPT_RT.
+ */
+ .use_raw_spinlock = true,
+ .wr_table = &dc_dprc_regmap_write_table,
+ .rd_table = &dc_dprc_regmap_read_table,
+ .max_register = RTRAM_CTRL0 + TOG,
+};
+
+static void dc_dprc_set_stream_id(struct dc_dprc *dprc, unsigned int stream_id)
+{
+ int ret;
+
+ ret = imx_sc_misc_set_control(dprc->ipc_handle, dprc->sc_resource,
+ IMX_SC_C_KACHUNK_SEL, stream_id);
+ if (ret)
+ dev_err(dprc->dev, "failed to set KACHUNK_SEL: %d\n", ret);
+}
+
+static void dc_dprc_reset(struct dc_dprc *dprc)
+{
+ regmap_write(dprc->reg, SYSTEM_CTRL0 + SET, SOFT_RESET);
+ fsleep(20);
+ regmap_write(dprc->reg, SYSTEM_CTRL0 + CLR, SOFT_RESET);
+ fsleep(20);
+}
+
+static void dc_dprc_enable(struct dc_dprc *dprc)
+{
+ dc_prg_enable(dprc->prg);
+}
+
+static void dc_dprc_reg_update(struct dc_dprc *dprc)
+{
+ dc_prg_reg_update(dprc->prg);
+}
+
+static void dc_dprc_enable_ctrl_done_irq(struct dc_dprc *dprc)
+{
+ guard(spinlock_irqsave)(&dprc->lock);
+ regmap_write(dprc->reg, IRQ_MASK + CLR, IRQ_DPR_CRTL_DONE);
+}
+
+void dc_dprc_configure(struct dc_dprc *dprc, unsigned int stream_id,
+ unsigned int width, unsigned int height,
+ unsigned int stride,
+ const struct drm_format_info *format,
+ dma_addr_t baddr, bool start)
+{
+ unsigned int prg_stride = width * format->cpp[0];
+ unsigned int bpp = format->cpp[0] * 8;
+ struct device *dev = dprc->dev;
+ unsigned int p1_w, p1_h;
+ u32 val;
+ int ret;
+
+ if (start) {
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to get RPM: %d\n", ret);
+ return;
+ }
+
+ dc_dprc_set_stream_id(dprc, stream_id);
+ }
+
+ p1_w = round_up(width, format->cpp[0] == 2 ? 32 : 16);
+ p1_h = round_up(height, 4);
+
+ regmap_write(dprc->reg, FRAME_CTRL0, PITCH(stride));
+ regmap_write(dprc->reg, FRAME_1P_CTRL0, BYTE_1K);
+ regmap_write(dprc->reg, FRAME_1P_PIX_X_CTRL, NUM_X_PIX_WIDE(p1_w));
+ regmap_write(dprc->reg, FRAME_1P_PIX_Y_CTRL, NUM_Y_PIX_HIGH(p1_h));
+ regmap_write(dprc->reg, FRAME_1P_BASE_ADDR_CTRL0, baddr);
+ regmap_write(dprc->reg, FRAME_PIX_X_ULC_CTRL, CROP_ULC_X(0));
+ regmap_write(dprc->reg, FRAME_PIX_Y_ULC_CTRL, CROP_ULC_Y(0));
+
+ regmap_write(dprc->reg, RTRAM_CTRL0, THRES_LOW(3) | THRES_HIGH(7));
+
+ val = LINE4 | BUF2;
+ switch (format->format) {
+ case DRM_FORMAT_XRGB8888:
+ /*
+ * It turns out pixel components are mapped directly
+ * without position change via DPR processing with
+ * the following color component configurations.
+ * Leave the pixel format to be handled by the
+ * display controllers.
+ */
+ val |= A_COMP_SEL(3) | R_COMP_SEL(2) |
+ G_COMP_SEL(1) | B_COMP_SEL(0);
+ val |= PIX_SIZE_32BIT;
+ break;
+ default:
+ dev_err(dev, "unsupported format 0x%08x\n", format->format);
+ return;
+ }
+ regmap_write(dprc->reg, MODE_CTRL0, val);
+
+ if (start) {
+ /* software shadow load for the first frame */
+ val = SW_SHADOW_LOAD_SEL | SHADOW_LOAD_EN;
+ regmap_write(dprc->reg, SYSTEM_CTRL0, val);
+
+ /* and then, run... */
+ val |= RUN_EN | REPEAT_EN;
+ regmap_write(dprc->reg, SYSTEM_CTRL0, val);
+ }
+
+ dc_prg_configure(dprc->prg, width, height, prg_stride, bpp, baddr, start);
+
+ if (start)
+ dc_dprc_enable(dprc);
+
+ dc_dprc_reg_update(dprc);
+
+ if (start)
+ dc_dprc_enable_ctrl_done_irq(dprc);
+
+ dev_dbg(dev, "w: %u, h: %u, s: %u, fmt: 0x%08x\n",
+ width, height, stride, format->format);
+}
+
+void dc_dprc_disable_repeat_en(struct dc_dprc *dprc)
+{
+ regmap_write(dprc->reg, SYSTEM_CTRL0 + CLR, REPEAT_EN);
+ dev_dbg(dprc->dev, "disable REPEAT_EN\n");
+}
+
+void dc_dprc_disable(struct dc_dprc *dprc)
+{
+ dc_prg_disable(dprc->prg);
+
+ pm_runtime_put(dprc->dev);
+
+ dev_dbg(dprc->dev, "disable\n");
+}
+
+void dc_dprc_disable_at_boot(struct dc_dprc *dprc)
+{
+ dc_prg_disable_at_boot(dprc->prg);
+
+ clk_bulk_disable_unprepare(dprc->num_clks, dprc->clks);
+
+ dev_dbg(dprc->dev, "disable at boot\n");
+}
+
+static void dc_dprc_ctrl_done_handle(struct dc_dprc *dprc)
+{
+ regmap_write(dprc->reg, SYSTEM_CTRL0, REPEAT_EN);
+
+ dc_prg_shadow_enable(dprc->prg);
+
+ dev_dbg(dprc->dev, "CTRL done handle\n");
+}
+
+static irqreturn_t dc_dprc_wrap_irq_handler(int irq, void *data)
+{
+ struct dc_dprc *dprc = data;
+ struct device *dev = dprc->dev;
+ u32 mask = 0, status = 0;
+ irqreturn_t result;
+
+ pm_runtime_get_noresume(dev);
+ if (!pm_runtime_active(dev)) {
+ result = IRQ_NONE;
+ goto out;
+ }
+
+ scoped_guard(spinlock, &dprc->lock) {
+ /* cache valid IRQ status */
+ regmap_read(dprc->reg, IRQ_MASK, &mask);
+ regmap_read(dprc->reg, IRQ_MASK_STATUS, &status);
+ status &= ~mask;
+
+ /* mask the IRQ(s) being handled */
+ regmap_write(dprc->reg, IRQ_MASK + SET, status);
+
+ /* clear status register */
+ regmap_write(dprc->reg, IRQ_MASK_STATUS, status);
+ }
+
+ if (status == 0) {
+ result = IRQ_NONE;
+ goto out;
+ }
+
+ if (status & DPR2RTR_FIFO_LOAD_BUF_RDY_UV_ERROR)
+ dev_err(dev, "DPR to RTRAM FIFO load UV buffer ready error\n");
+
+ if (status & DPR2RTR_FIFO_LOAD_BUF_RDY_YRGB_ERROR)
+ dev_err(dev, "DPR to RTRAM FIFO load YRGB buffer ready error\n");
+
+ if (status & DPR2RTR_UV_FIFO_OVFL)
+ dev_err(dev, "DPR to RTRAM FIFO UV FIFO overflow\n");
+
+ if (status & DPR2RTR_YRGB_FIFO_OVFL)
+ dev_err(dev, "DPR to RTRAM FIFO YRGB FIFO overflow\n");
+
+ if (status & IRQ_AXI_READ_ERROR)
+ dev_err(dev, "AXI read error\n");
+
+ if (status & IRQ_DPR_CRTL_DONE)
+ dc_dprc_ctrl_done_handle(dprc);
+
+ result = IRQ_HANDLED;
+out:
+ pm_runtime_put_noidle(dev);
+ return result;
+}
+
+bool dc_dprc_rtram_width_supported(struct dc_dprc *dprc, unsigned int width)
+{
+ return width <= DPU_DPRC_MAX_RTRAM_WIDTH;
+}
+
+bool dc_dprc_stride_supported(struct dc_dprc *dprc,
+ unsigned int stride, unsigned int width,
+ const struct drm_format_info *format,
+ dma_addr_t baddr)
+{
+ unsigned int prg_stride = width * format->cpp[0];
+
+ if (stride > DPU_DRPC_MAX_STRIDE)
+ return false;
+
+ if (!dc_prg_stride_supported(dprc->prg, prg_stride, baddr))
+ return false;
+
+ return true;
+}
+
+struct dc_dprc *dc_dprc_get(struct device *dev, struct dc_dprc *dprc)
+{
+ struct dc_dprc *iter;
+
+ guard(mutex)(&dc_dprc_list_mutex);
+ list_for_each_entry(iter, &dc_dprc_list, list) {
+ if (iter == dprc) {
+ device_link_add(dev, dprc->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+ return dprc;
+ }
+ }
+
+ return NULL;
+}
+
+static int dc_dprc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct resource *res;
+ struct dc_dprc *dprc;
+ void __iomem *base;
+ int ret, wrap_irq;
+
+ dprc = devm_kzalloc(dev, sizeof(*dprc), GFP_KERNEL);
+ if (!dprc)
+ return -ENOMEM;
+
+ ret = imx_scu_get_handle(&dprc->ipc_handle);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get SCU ipc handle\n");
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ dprc->reg = devm_regmap_init_mmio(dev, base, &dc_dprc_regmap_config);
+ if (IS_ERR(dprc->reg))
+ return PTR_ERR(dprc->reg);
+
+ wrap_irq = platform_get_irq_byname(pdev, "dpr_wrap");
+ if (wrap_irq < 0)
+ return dev_err_probe(dev, wrap_irq, "failed to get dpr_wrap IRQ\n");
+
+ dprc->num_clks = devm_clk_bulk_get_all(dev, &dprc->clks);
+ if (dprc->num_clks < 0)
+ return dev_err_probe(dev, dprc->num_clks, "failed to get clocks\n");
+
+ ret = of_property_read_u32(np, "fsl,sc-resource", &dprc->sc_resource);
+ if (ret) {
+ dev_err(dev, "failed to get SC resource %d\n", ret);
+ return ret;
+ }
+
+ dprc->prg = dc_prg_lookup_by_phandle(dev, "fsl,prgs", 0);
+ if (!dprc->prg)
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "failed to lookup PRG\n");
+
+ dprc->dev = dev;
+ spin_lock_init(&dprc->lock);
+
+ ret = devm_request_irq(dev, wrap_irq, dc_dprc_wrap_irq_handler,
+ IRQF_SHARED, dev_name(dev), dprc);
+ if (ret < 0) {
+ dev_err(dev, "failed to request dpr_wrap IRQ(%d): %d\n",
+ wrap_irq, ret);
+ return ret;
+ }
+
+ dev_set_drvdata(dev, dprc);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable PM runtime\n");
+
+ guard(mutex)(&dc_dprc_list_mutex);
+ list_add(&dprc->list, &dc_dprc_list);
+
+ dc_prg_set_dprc(dprc->prg, dprc);
+
+ return 0;
+}
+
+static void dc_dprc_remove(struct platform_device *pdev)
+{
+ struct dc_dprc *dprc = dev_get_drvdata(&pdev->dev);
+
+ dc_prg_set_dprc(dprc->prg, NULL);
+
+ guard(mutex)(&dc_dprc_list_mutex);
+ list_del(&dprc->list);
+}
+
+static int dc_dprc_runtime_suspend(struct device *dev)
+{
+ struct dc_dprc *dprc = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(dprc->num_clks, dprc->clks);
+
+ return 0;
+}
+
+static int dc_dprc_runtime_resume(struct device *dev)
+{
+ struct dc_dprc *dprc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(dprc->num_clks, dprc->clks);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks: %d\n", ret);
+ return ret;
+ }
+
+ dc_dprc_reset(dprc);
+
+ /* disable all control IRQs and enable all error IRQs */
+ guard(spinlock_irqsave)(&dprc->lock);
+ regmap_write(dprc->reg, IRQ_MASK, IRQ_CTRL_MASK);
+
+ return 0;
+}
+
+static const struct dev_pm_ops dc_dprc_pm_ops = {
+ RUNTIME_PM_OPS(dc_dprc_runtime_suspend, dc_dprc_runtime_resume, NULL)
+};
+
+static const struct of_device_id dc_dprc_dt_ids[] = {
+ { .compatible = "fsl,imx8qxp-dpr-channel", },
+ { /* sentinel */ }
+};
+
+struct platform_driver dc_dprc_driver = {
+ .probe = dc_dprc_probe,
+ .remove = dc_dprc_remove,
+ .driver = {
+ .name = "imx8-dc-dpr-channel",
+ .suppress_bind_attrs = true,
+ .of_match_table = dc_dprc_dt_ids,
+ .pm = pm_ptr(&dc_dprc_pm_ops),
+ },
+};
diff --git a/drivers/gpu/drm/imx/dc/dc-dprc.h b/drivers/gpu/drm/imx/dc/dc-dprc.h
new file mode 100644
index 000000000000..45b0f1d3dd6e
--- /dev/null
+++ b/drivers/gpu/drm/imx/dc/dc-dprc.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2025 NXP
+ */
+
+#ifndef __DC_DPRC_H__
+#define __DC_DPRC_H__
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+#include <drm/drm_fourcc.h>
+
+struct dc_dprc;
+
+void dc_dprc_configure(struct dc_dprc *dprc, unsigned int stream_id,
+ unsigned int width, unsigned int height,
+ unsigned int stride,
+ const struct drm_format_info *format,
+ dma_addr_t baddr, bool start);
+
+void dc_dprc_disable_repeat_en(struct dc_dprc *dprc);
+
+void dc_dprc_disable(struct dc_dprc *dprc);
+
+void dc_dprc_disable_at_boot(struct dc_dprc *dprc);
+
+bool dc_dprc_rtram_width_supported(struct dc_dprc *dprc, unsigned int width);
+
+bool dc_dprc_stride_supported(struct dc_dprc *dprc,
+ unsigned int stride, unsigned int width,
+ const struct drm_format_info *format,
+ dma_addr_t baddr);
+
+struct dc_dprc *dc_dprc_get(struct device *dev, struct dc_dprc *dprc);
+
+#endif
diff --git a/drivers/gpu/drm/imx/dc/dc-drv.c b/drivers/gpu/drm/imx/dc/dc-drv.c
index 1d01f896e427..eb4b32f958fe 100644
--- a/drivers/gpu/drm/imx/dc/dc-drv.c
+++ b/drivers/gpu/drm/imx/dc/dc-drv.c
@@ -344,6 +344,7 @@ static struct platform_driver dc_driver = {
static struct platform_driver * const dc_drivers[] = {
&dc_cf_driver,
&dc_de_driver,
+ &dc_dprc_driver,
&dc_ed_driver,
&dc_fg_driver,
&dc_fl_driver,
diff --git a/drivers/gpu/drm/imx/dc/dc-drv.h b/drivers/gpu/drm/imx/dc/dc-drv.h
index 27ef2c552549..c585284e7cb2 100644
--- a/drivers/gpu/drm/imx/dc/dc-drv.h
+++ b/drivers/gpu/drm/imx/dc/dc-drv.h
@@ -74,6 +74,7 @@ int dc_plane_init(struct dc_drm_device *dc_drm, struct dc_plane *dc_plane);
extern struct platform_driver dc_cf_driver;
extern struct platform_driver dc_de_driver;
+extern struct platform_driver dc_dprc_driver;
extern struct platform_driver dc_ed_driver;
extern struct platform_driver dc_fg_driver;
extern struct platform_driver dc_fl_driver;
diff --git a/drivers/gpu/drm/imx/dc/dc-prg.c b/drivers/gpu/drm/imx/dc/dc-prg.c
index 43df5683b24d..9a38d1306480 100644
--- a/drivers/gpu/drm/imx/dc/dc-prg.c
+++ b/drivers/gpu/drm/imx/dc/dc-prg.c
@@ -19,6 +19,7 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+#include "dc-dprc.h"
#include "dc-drv.h"
#include "dc-prg.h"
@@ -64,6 +65,7 @@ struct dc_prg {
struct list_head list;
struct clk_bulk_data *clks;
int num_clks;
+ struct dc_dprc *dprc;
};
static DEFINE_MUTEX(dc_prg_list_mutex);
@@ -222,6 +224,16 @@ dc_prg_lookup_by_phandle(struct device *dev, const char *name, int index)
return NULL;
}
+void dc_prg_set_dprc(struct dc_prg *prg, struct dc_dprc *dprc)
+{
+ prg->dprc = dprc;
+}
+
+struct dc_dprc *dc_prg_get_dprc(struct dc_prg *prg)
+{
+ return prg->dprc;
+}
+
static int dc_prg_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
diff --git a/drivers/gpu/drm/imx/dc/dc-prg.h b/drivers/gpu/drm/imx/dc/dc-prg.h
index 6fd9b050bfa1..f29d154f7de5 100644
--- a/drivers/gpu/drm/imx/dc/dc-prg.h
+++ b/drivers/gpu/drm/imx/dc/dc-prg.h
@@ -32,4 +32,8 @@ bool dc_prg_stride_supported(struct dc_prg *prg,
struct dc_prg *
dc_prg_lookup_by_phandle(struct device *dev, const char *name, int index);
+void dc_prg_set_dprc(struct dc_prg *prg, struct dc_dprc *dprc);
+
+struct dc_dprc *dc_prg_get_dprc(struct dc_prg *prg);
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 09/15] drm/imx: dc: Use TCON operation mode
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
` (7 preceding siblings ...)
2026-10-26 10:05 ` [PATCH v7 08/15] drm/imx: dc: Add DPR channel support Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 10:05 ` [PATCH v7 10/15] drm/imx: dc-ed: Support getting source selection Liu Ying
` (5 subsequent siblings)
14 siblings, 0 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying, Frank Li
In TCON operation mode, sync signals from FrameGen are ignored, but
a much more customized output timing can be generated by the TCON
module. By using TCON operaton mode, generate KACHUNK signal along
with HSYNC/VSYNC/data enable signals. The KACHUNK signal is used as
a synchronization signal inside the prefetch engine(DPRC + PRG(s),
attached to FetchUnit(s)). Carefully switch TCON bypass mode to TCON
operation mode when CRTC is being enabled so that the prefetch engine
may evade the first dumb frame generated by the display controller.
The switching is done with local irq and preempt disabled to ensure
it is done right after FrameGen frame counter moves as instructed by
design team. Set use_raw_spinlock flag in flag in FrameGen and Tcon
regmap configurations to avoid sleeping on PREEMPT_RT when switching.
Also, replace jiffies with timekeeping API ktime_add_ms(), ktime_get()
and ktime_before() in dc_fg_wait_for_frame_index_moving() to avoid a
potential hard lockup when switching.
Since TCON BYPASS bit is controlled by KMS driver when doing atomic
commits, drop the bit setting when initializing TCON. This also
avoids accidentally initializing TCON BYPASS bit to 1 when driver
module removing and re-installing where an upcoming patch would
disable a CRTC at boot in TCON operation mode if needed.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v7:
- Set use_raw_spinlock flag in FrameGen and Tcon regmap configurations to
avoid sleeping on PREEMPT_RT. (Sashiko AI bot)
- Replace jiffies with timekeeping API ktime_add_ms(), ktime_get() and
ktime_before() in dc_fg_wait_for_frame_index_moving() to avoid a
potential hard lockup. (Sashiko AI bot)
- Use adjusted timings(e.g., m->crtc_hdisplay) in dc_tc_cfg_videomode()
instead of logical mode timings (e.g., m->hdisplay). (Sashiko AI bot)
- Update commit message.
v6:
- Rebase onto the latest drm-misc-next and resolve conflicts.
v3:
- Collect Frank's R-b tag.
---
drivers/gpu/drm/imx/dc/dc-crtc.c | 28 +++++++++
drivers/gpu/drm/imx/dc/dc-de.h | 2 +
drivers/gpu/drm/imx/dc/dc-fg.c | 11 +++-
drivers/gpu/drm/imx/dc/dc-kms.h | 2 +
drivers/gpu/drm/imx/dc/dc-tc.c | 119 +++++++++++++++++++++++++++++++++++++--
5 files changed, 155 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/imx/dc/dc-crtc.c b/drivers/gpu/drm/imx/dc/dc-crtc.c
index c7dbe5f500b4..1ca1c9229e94 100644
--- a/drivers/gpu/drm/imx/dc/dc-crtc.c
+++ b/drivers/gpu/drm/imx/dc/dc-crtc.c
@@ -6,8 +6,10 @@
#include <linux/completion.h>
#include <linux/container_of.h>
#include <linux/interrupt.h>
+#include <linux/irqflags.h>
#include <linux/irqreturn.h>
#include <linux/pm_runtime.h>
+#include <linux/preempt.h>
#include <linux/spinlock.h>
#include <drm/drm_atomic.h>
@@ -68,6 +70,14 @@ do { \
__func__); \
} while (0)
+#define DC_CRTC_WAIT_FOR_FRAMEGEN_FRAME_INDEX_MOVING(fg) \
+do { \
+ if (!dc_fg_wait_for_frame_index_moving(fg)) \
+ dc_crtc_err(crtc, \
+ "%s: FrameGen frame index isn't moving\n", \
+ __func__); \
+} while (0)
+
static inline struct dc_crtc *to_dc_crtc(struct drm_crtc *crtc)
{
return container_of(crtc, struct dc_crtc, base);
@@ -230,6 +240,7 @@ dc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
enum dc_link_id cf_link;
+ unsigned long flags;
int idx, ret;
dc_crtc_dbg(crtc, "mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(adj));
@@ -250,6 +261,7 @@ dc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
enable_irq(dc_crtc->irq_ed_safe_shdload);
dc_fg_cfg_videomode(dc_crtc->fg, adj);
+ dc_tc_cfg_videomode(dc_crtc->tc, adj);
dc_cf_framedimensions(dc_crtc->cf_cont,
adj->crtc_hdisplay, adj->crtc_vdisplay);
@@ -274,7 +286,22 @@ dc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
dc_ed_pec_sync_trigger(dc_crtc->ed_cont);
dc_ed_pec_sync_trigger(dc_crtc->ed_safe);
dc_fg_shdtokgen(dc_crtc->fg);
+
+ /* Don't relinquish CPU until TCON is set to operation mode. */
+ local_irq_save(flags);
+ preempt_disable();
+
dc_fg_enable(dc_crtc->fg);
+ /*
+ * Turn TCON into operation mode as soon as the first dumb
+ * frame is generated by DC(we don't relinquish CPU to ensure
+ * this). This makes DPR/PRG be able to evade the frame.
+ */
+ DC_CRTC_WAIT_FOR_FRAMEGEN_FRAME_INDEX_MOVING(dc_crtc->fg);
+ dc_tc_set_operation_mode(dc_crtc->tc);
+
+ local_irq_restore(flags);
+ preempt_enable();
DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_safe_shdload_done);
DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_cont_shdload_done);
@@ -562,6 +589,7 @@ int dc_crtc_init(struct dc_drm_device *dc_drm, int crtc_index)
dc_crtc->ed_cont = pe->ed_cont[crtc_index];
dc_crtc->ed_safe = pe->ed_safe[crtc_index];
dc_crtc->fg = de->fg;
+ dc_crtc->tc = de->tc;
dc_crtc->irq_dec_framecomplete = de->irq_framecomplete;
dc_crtc->irq_dec_seqcomplete = de->irq_seqcomplete;
diff --git a/drivers/gpu/drm/imx/dc/dc-de.h b/drivers/gpu/drm/imx/dc/dc-de.h
index 211f3fcc1a9a..c39f2ef5eea9 100644
--- a/drivers/gpu/drm/imx/dc/dc-de.h
+++ b/drivers/gpu/drm/imx/dc/dc-de.h
@@ -54,6 +54,8 @@ enum drm_mode_status dc_fg_check_clock(struct dc_fg *fg, int clk_khz);
void dc_fg_init(struct dc_fg *fg);
/* Timing Controller Unit */
+void dc_tc_set_operation_mode(struct dc_tc *tc);
+void dc_tc_cfg_videomode(struct dc_tc *tc, struct drm_display_mode *m);
void dc_tc_init(struct dc_tc *tc);
#endif /* __DC_DISPLAY_ENGINE_H__ */
diff --git a/drivers/gpu/drm/imx/dc/dc-fg.c b/drivers/gpu/drm/imx/dc/dc-fg.c
index 3741dc66c0d9..26b786e146d4 100644
--- a/drivers/gpu/drm/imx/dc/dc-fg.c
+++ b/drivers/gpu/drm/imx/dc/dc-fg.c
@@ -8,7 +8,7 @@
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/device.h>
-#include <linux/jiffies.h>
+#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -122,6 +122,11 @@ static const struct regmap_config dc_fg_regmap_config = {
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
+ /*
+ * Registers could be accessed with local irq and preempt disabled,
+ * so use a raw spinlock to avoid sleeping on PREEMPT_RT.
+ */
+ .use_raw_spinlock = true,
.wr_table = &dc_fg_regmap_write_table,
.rd_table = &dc_fg_regmap_read_table,
.max_register = FGCHSTATCLR,
@@ -230,7 +235,7 @@ u32 dc_fg_get_line_index(struct dc_fg *fg)
bool dc_fg_wait_for_frame_index_moving(struct dc_fg *fg)
{
- unsigned long timeout = jiffies + msecs_to_jiffies(100);
+ ktime_t timeout = ktime_add_ms(ktime_get(), 100);
u32 frame_index, last_frame_index;
frame_index = dc_fg_get_frame_index(fg);
@@ -238,7 +243,7 @@ bool dc_fg_wait_for_frame_index_moving(struct dc_fg *fg)
last_frame_index = frame_index;
frame_index = dc_fg_get_frame_index(fg);
} while (last_frame_index == frame_index &&
- time_before(jiffies, timeout));
+ ktime_before(ktime_get(), timeout));
return last_frame_index != frame_index;
}
diff --git a/drivers/gpu/drm/imx/dc/dc-kms.h b/drivers/gpu/drm/imx/dc/dc-kms.h
index cd7860eff986..a25d47eebd28 100644
--- a/drivers/gpu/drm/imx/dc/dc-kms.h
+++ b/drivers/gpu/drm/imx/dc/dc-kms.h
@@ -50,6 +50,8 @@ struct dc_crtc {
struct dc_ed *ed_safe;
/** @fg: framegen */
struct dc_fg *fg;
+ /** @tc: tcon */
+ struct dc_tc *tc;
/**
* @irq_dec_framecomplete:
*
diff --git a/drivers/gpu/drm/imx/dc/dc-tc.c b/drivers/gpu/drm/imx/dc/dc-tc.c
index d0d4faba790e..626758904f77 100644
--- a/drivers/gpu/drm/imx/dc/dc-tc.c
+++ b/drivers/gpu/drm/imx/dc/dc-tc.c
@@ -8,11 +8,30 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <drm/drm_modes.h>
+
#include "dc-drv.h"
#include "dc-de.h"
#define TCON_CTRL 0x410
-#define CTRL_RST_VAL 0x01401408
+#define SPLITPOSITION_MASK GENMASK(29, 16)
+#define SPLITPOSITION(n) FIELD_PREP(SPLITPOSITION_MASK, (n))
+#define DUAL_SWAP BIT(15)
+#define MINILVDS_OPCODE_MASK GENMASK(14, 12)
+#define MODE_4PAIRS FIELD_PREP(MINILVDS_OPCODE_MASK, 0x1)
+#define LVDS_CLOCK_INV BIT(11)
+#define LVDS_BALANCE BIT(10)
+#define LVDSMODE BIT(9)
+#define ENLVDS BIT(8)
+#define INV_CTRL_MASK GENMASK(7, 4)
+#define BYPASS BIT(3)
+#define TCON_SYNC BIT(2)
+#define CHANNELMODE_MASK GENMASK(1, 0)
+#define CTRL_RST_MASK \
+ (SPLITPOSITION_MASK | DUAL_SWAP | MINILVDS_OPCODE_MASK | \
+ LVDS_CLOCK_INV | LVDS_BALANCE | LVDSMODE | ENLVDS | \
+ INV_CTRL_MASK | TCON_SYNC | CHANNELMODE_MASK)
+#define CTRL_RST_VAL (SPLITPOSITION(0x140) | MODE_4PAIRS | LVDS_BALANCE)
/* red: MAPBIT 29-20, green: MAPBIT 19-10, blue: MAPBIT 9-0 */
#define MAPBIT3_0 0x418
@@ -24,6 +43,16 @@
#define MAPBIT27_24 0x430
#define MAPBIT31_28 0x434
+#define SPGPOSON(n) (0x460 + (n) * 16)
+#define SPGMASKON(n) (0x464 + (n) * 16)
+#define SPGPOSOFF(n) (0x468 + (n) * 16)
+#define SPGMASKOFF(n) (0x46c + (n) * 16)
+#define X(n) FIELD_PREP(GENMASK(30, 16), (n))
+#define Y(n) FIELD_PREP(GENMASK(14, 0), (n))
+
+#define SMXSIGS(n) (0x520 + (n) * 8)
+#define SMXFCTTABLE(n) (0x524 + (n) * 8)
+
static const struct dc_subdev_info dc_tc_info[] = {
{ .reg_start = 0x5618c800, .id = 0, },
{ .reg_start = 0x5618e400, .id = 1, },
@@ -32,6 +61,8 @@ static const struct dc_subdev_info dc_tc_info[] = {
static const struct regmap_range dc_tc_regmap_ranges[] = {
regmap_reg_range(TCON_CTRL, TCON_CTRL),
regmap_reg_range(MAPBIT3_0, MAPBIT31_28),
+ regmap_reg_range(SPGPOSON(0), SPGMASKOFF(4)),
+ regmap_reg_range(SMXSIGS(0), SMXFCTTABLE(3)),
};
static const struct regmap_access_table dc_tc_regmap_access_table = {
@@ -44,9 +75,14 @@ static const struct regmap_config dc_tc_regmap_config = {
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
+ /*
+ * Registers could be accessed with local irq and preempt disabled,
+ * so use a raw spinlock to avoid sleeping on PREEMPT_RT.
+ */
+ .use_raw_spinlock = true,
.wr_table = &dc_tc_regmap_access_table,
.rd_table = &dc_tc_regmap_access_table,
- .max_register = MAPBIT31_28,
+ .max_register = SMXFCTTABLE(3),
};
/*
@@ -59,10 +95,85 @@ static const u32 dc_tc_mapbit[] = {
0x13121110, 0x03020100, 0x07060504, 0x00000908,
};
+void dc_tc_set_operation_mode(struct dc_tc *tc)
+{
+ regmap_write_bits(tc->reg, TCON_CTRL, BYPASS, 0);
+}
+
+void dc_tc_cfg_videomode(struct dc_tc *tc, struct drm_display_mode *m)
+{
+ int hdisplay, hsync_start, hsync_end;
+ int vdisplay, vsync_start, vsync_end;
+ int y;
+
+ hdisplay = m->crtc_hdisplay;
+ vdisplay = m->crtc_vdisplay;
+ hsync_start = m->crtc_hsync_start;
+ vsync_start = m->crtc_vsync_start;
+ hsync_end = m->crtc_hsync_end;
+ vsync_end = m->crtc_vsync_end;
+
+ /*
+ * Turn TCON into operation mode later after the first dumb frame is
+ * generated by DC. This makes DPR/PRG be able to evade the frame.
+ */
+ regmap_write_bits(tc->reg, TCON_CTRL, BYPASS, BYPASS);
+
+ /* dsp_control[0]: HSYNC */
+ regmap_write(tc->reg, SPGPOSON(0), X(hsync_start));
+ regmap_write(tc->reg, SPGMASKON(0), 0xffff);
+
+ regmap_write(tc->reg, SPGPOSOFF(0), X(hsync_end));
+ regmap_write(tc->reg, SPGMASKOFF(0), 0xffff);
+
+ regmap_write(tc->reg, SMXSIGS(0), 0x2);
+ regmap_write(tc->reg, SMXFCTTABLE(0), 0x1);
+
+ /* dsp_control[1]: VSYNC */
+ regmap_write(tc->reg, SPGPOSON(1), X(hsync_start) | Y(vsync_start - 1));
+ regmap_write(tc->reg, SPGMASKON(1), 0x0);
+
+ regmap_write(tc->reg, SPGPOSOFF(1), X(hsync_start) | Y(vsync_end - 1));
+ regmap_write(tc->reg, SPGMASKOFF(1), 0x0);
+
+ regmap_write(tc->reg, SMXSIGS(1), 0x3);
+ regmap_write(tc->reg, SMXFCTTABLE(1), 0x1);
+
+ /* dsp_control[2]: data enable */
+ /* horizontal */
+ regmap_write(tc->reg, SPGPOSON(2), 0x0);
+ regmap_write(tc->reg, SPGMASKON(2), 0xffff);
+
+ regmap_write(tc->reg, SPGPOSOFF(2), X(hdisplay));
+ regmap_write(tc->reg, SPGMASKOFF(2), 0xffff);
+
+ /* vertical */
+ regmap_write(tc->reg, SPGPOSON(3), 0x0);
+ regmap_write(tc->reg, SPGMASKON(3), 0x7fff0000);
+
+ regmap_write(tc->reg, SPGPOSOFF(3), Y(vdisplay));
+ regmap_write(tc->reg, SPGMASKOFF(3), 0x7fff0000);
+
+ regmap_write(tc->reg, SMXSIGS(2), 0x2c);
+ regmap_write(tc->reg, SMXFCTTABLE(2), 0x8);
+
+ /* dsp_control[3]: KACHUNK */
+ y = vdisplay + 1;
+
+ regmap_write(tc->reg, SPGPOSON(4), X(0x0) | Y(y));
+ regmap_write(tc->reg, SPGMASKON(4), 0x0);
+
+ regmap_write(tc->reg, SPGPOSOFF(4), X(0x20) | Y(y));
+ regmap_write(tc->reg, SPGMASKOFF(4), 0x0);
+
+ regmap_write(tc->reg, SMXSIGS(3), 0x6);
+ regmap_write(tc->reg, SMXFCTTABLE(3), 0x2);
+}
+
void dc_tc_init(struct dc_tc *tc)
{
- /* reset TCON_CTRL to POR default so that TCON works in bypass mode */
- regmap_write(tc->reg, TCON_CTRL, CTRL_RST_VAL);
+ /* reset TCON_CTRL to POR default except for touching BYPASS bit */
+ regmap_write_bits(tc->reg, TCON_CTRL, CTRL_RST_MASK, CTRL_RST_VAL);
/* set format */
regmap_bulk_write(tc->reg, MAPBIT3_0, dc_tc_mapbit,
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 10/15] drm/imx: dc-ed: Support getting source selection
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
` (8 preceding siblings ...)
2026-10-26 10:05 ` [PATCH v7 09/15] drm/imx: dc: Use TCON operation mode Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 10:41 ` Alexander Stein
2026-10-26 12:01 ` sashiko-bot
2026-10-26 10:05 ` [PATCH v7 11/15] drm/imx: dc-lb: Support getting secondary input selection Liu Ying
` (4 subsequent siblings)
14 siblings, 2 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying, Frank Li
Add a helper to get ExtDst source selection. This is needed by
disabling CRTC at boot in an upcoming commit which tries to get
the source selection.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
--
v7:
- Collect Maxime's A-b tag.
- Check regmap_read()'s return valure. (Sashiko AI bot)
v2:
- Collect Frank's R-b tag.
---
drivers/gpu/drm/imx/dc/dc-ed.c | 29 +++++++++++++++++++++++++++++
drivers/gpu/drm/imx/dc/dc-pe.h | 1 +
2 files changed, 30 insertions(+)
diff --git a/drivers/gpu/drm/imx/dc/dc-ed.c b/drivers/gpu/drm/imx/dc/dc-ed.c
index de1b71315eab..67163dca1363 100644
--- a/drivers/gpu/drm/imx/dc/dc-ed.c
+++ b/drivers/gpu/drm/imx/dc/dc-ed.c
@@ -149,6 +149,35 @@ void dc_ed_pec_src_sel(struct dc_ed *ed, enum dc_link_id src)
}
}
+int dc_ed_pec_src_sel_get(struct dc_ed *ed, enum dc_link_id *src)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_read(ed->reg_pec, PIXENGCFG_DYNAMIC, &val);
+ if (ret)
+ return -EINVAL;
+
+ switch (val) {
+ case LINK_ID_NONE:
+ case LINK_ID_CONSTFRAME0:
+ case LINK_ID_CONSTFRAME4:
+ case LINK_ID_CONSTFRAME1:
+ case LINK_ID_CONSTFRAME5:
+ case LINK_ID_FETCHWARP2:
+ case LINK_ID_FETCHLAYER0:
+ case LINK_ID_LAYERBLEND0:
+ case LINK_ID_LAYERBLEND1:
+ case LINK_ID_LAYERBLEND2:
+ case LINK_ID_LAYERBLEND3:
+ *src = val;
+ dev_dbg(ed->dev, "get source selection: 0x%02x\n", val);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
void dc_ed_pec_sync_trigger(struct dc_ed *ed)
{
regmap_write(ed->reg_pec, PIXENGCFG_TRIGGER, SYNC_TRIGGER);
diff --git a/drivers/gpu/drm/imx/dc/dc-pe.h b/drivers/gpu/drm/imx/dc/dc-pe.h
index f5e01a6eb9e9..0b81dc6b251c 100644
--- a/drivers/gpu/drm/imx/dc/dc-pe.h
+++ b/drivers/gpu/drm/imx/dc/dc-pe.h
@@ -85,6 +85,7 @@ void dc_cf_init(struct dc_cf *cf);
/* External Destination Unit */
void dc_ed_pec_src_sel(struct dc_ed *ed, enum dc_link_id src);
+int dc_ed_pec_src_sel_get(struct dc_ed *ed, enum dc_link_id *src);
void dc_ed_pec_sync_trigger(struct dc_ed *ed);
void dc_ed_init(struct dc_ed *ed);
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 11/15] drm/imx: dc-lb: Support getting secondary input selection
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
` (9 preceding siblings ...)
2026-10-26 10:05 ` [PATCH v7 10/15] drm/imx: dc-ed: Support getting source selection Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 10:05 ` [PATCH v7 12/15] drm/imx: dc-ed: Drop initial source selection Liu Ying
` (3 subsequent siblings)
14 siblings, 0 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying, Frank Li
Add a helper to get LayerBlend secondary input selection. This is
needed by disabling CRTC at boot in an upcoming commit which tries
to get the selection.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
--
v7:
- Collect Maxime's A-b tag.
- Check regmap_read()'s return value.
v4:
- Rebase onto the latest drm-misc-next and resolve conflict.
v2:
- Collect Frank's R-b tag.
---
drivers/gpu/drm/imx/dc/dc-lb.c | 25 +++++++++++++++++++++++++
drivers/gpu/drm/imx/dc/dc-pe.h | 1 +
2 files changed, 26 insertions(+)
diff --git a/drivers/gpu/drm/imx/dc/dc-lb.c b/drivers/gpu/drm/imx/dc/dc-lb.c
index cb614f3c2f69..03c361d25824 100644
--- a/drivers/gpu/drm/imx/dc/dc-lb.c
+++ b/drivers/gpu/drm/imx/dc/dc-lb.c
@@ -19,6 +19,8 @@
#define PIXENGCFG_DYNAMIC_SEC_SEL_MASK GENMASK(13, 8)
#define PIXENGCFG_DYNAMIC_SEC_SEL(x) \
FIELD_PREP(PIXENGCFG_DYNAMIC_SEC_SEL_MASK, (x))
+#define PIXENGCFG_DYNAMIC_SEC_SEL_GET(x) \
+ FIELD_GET(PIXENGCFG_DYNAMIC_SEC_SEL_MASK, (x))
#define PIXENGCFG_DYNAMIC_PRIM_SEL_MASK GENMASK(5, 0)
#define PIXENGCFG_DYNAMIC_PRIM_SEL(x) \
FIELD_PREP(PIXENGCFG_DYNAMIC_PRIM_SEL_MASK, (x))
@@ -181,6 +183,29 @@ void dc_lb_pec_dynamic_sec_sel(struct dc_lb *lb, enum dc_link_id sec)
dev_warn(lb->dev, "invalid secondary input selection:%d\n", sec);
}
+int dc_lb_pec_dynamic_sec_sel_get(struct dc_lb *lb, enum dc_link_id *sec)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_read(lb->reg_pec, PIXENGCFG_DYNAMIC, &val);
+ if (ret)
+ return -EINVAL;
+
+ val = PIXENGCFG_DYNAMIC_SEC_SEL_GET(val);
+
+ switch (val) {
+ case LINK_ID_NONE:
+ case LINK_ID_FETCHWARP2:
+ case LINK_ID_FETCHLAYER0:
+ *sec = val;
+ dev_dbg(lb->dev, "get secondary input selection: 0x%02x\n", val);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
void dc_lb_pec_clken(struct dc_lb *lb, enum dc_pec_clken clken)
{
regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC, CLKEN_MASK,
diff --git a/drivers/gpu/drm/imx/dc/dc-pe.h b/drivers/gpu/drm/imx/dc/dc-pe.h
index 0b81dc6b251c..4e38aead69bc 100644
--- a/drivers/gpu/drm/imx/dc/dc-pe.h
+++ b/drivers/gpu/drm/imx/dc/dc-pe.h
@@ -93,6 +93,7 @@ void dc_ed_init(struct dc_ed *ed);
enum dc_link_id dc_lb_get_link_id(struct dc_lb *lb);
void dc_lb_pec_dynamic_prim_sel(struct dc_lb *lb, enum dc_link_id prim);
void dc_lb_pec_dynamic_sec_sel(struct dc_lb *lb, enum dc_link_id sec);
+int dc_lb_pec_dynamic_sec_sel_get(struct dc_lb *lb, enum dc_link_id *sec);
void dc_lb_pec_clken(struct dc_lb *lb, enum dc_pec_clken clken);
void dc_lb_mode(struct dc_lb *lb, enum dc_lb_mode mode);
void dc_lb_position(struct dc_lb *lb, int x, int y);
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 12/15] drm/imx: dc-ed: Drop initial source selection
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
` (10 preceding siblings ...)
2026-10-26 10:05 ` [PATCH v7 11/15] drm/imx: dc-lb: Support getting secondary input selection Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 10:05 ` [PATCH v7 13/15] drm/imx: dc-lb: Drop initial primary and secondary input selections Liu Ying
` (2 subsequent siblings)
14 siblings, 0 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying, Frank Li
It's unnecessary to set initial ExtDst source selection because KMS
driver would do that when doing atomic commits. Drop the selection.
This is needed as a preparation for reading ExtDst source selection
when trying to disable CRTC at boot in an upcoming commit.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
--
v7:
- Collect Maxime's A-b tag.
v2:
- Collect Frank's R-b tag.
---
drivers/gpu/drm/imx/dc/dc-ed.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/gpu/drm/imx/dc/dc-ed.c b/drivers/gpu/drm/imx/dc/dc-ed.c
index 67163dca1363..2f8d8c94e2d9 100644
--- a/drivers/gpu/drm/imx/dc/dc-ed.c
+++ b/drivers/gpu/drm/imx/dc/dc-ed.c
@@ -205,7 +205,6 @@ static inline void dc_ed_disable_gamma_apply(struct dc_ed *ed)
void dc_ed_init(struct dc_ed *ed)
{
- dc_ed_pec_src_sel(ed, LINK_ID_NONE);
dc_ed_pec_enable_shden(ed);
dc_ed_pec_poweron(ed);
dc_ed_pec_sync_mode_single(ed);
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 13/15] drm/imx: dc-lb: Drop initial primary and secondary input selections
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
` (11 preceding siblings ...)
2026-10-26 10:05 ` [PATCH v7 12/15] drm/imx: dc-ed: Drop initial source selection Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 10:05 ` [PATCH v7 14/15] drm/imx: dc-fu: Get DPR channel Liu Ying
2026-10-26 10:05 ` [PATCH v7 15/15] drm/imx: dc: Use prefetch engine Liu Ying
14 siblings, 0 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying, Frank Li
It's unnecessary to set initial primary and secondary input selections
because KMS driver would do that when doing atomic commits. Drop the
selections. This is needed as a preparation for reading LayerBlend
secondary input selection when trying to disable CRTC at boot in an
upcoming commit.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
--
v7:
- Collect Maxime's A-b tag.
v2:
- Collect Frank's R-b tag.
---
drivers/gpu/drm/imx/dc/dc-lb.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/gpu/drm/imx/dc/dc-lb.c b/drivers/gpu/drm/imx/dc/dc-lb.c
index 03c361d25824..a3b8faa4b8c7 100644
--- a/drivers/gpu/drm/imx/dc/dc-lb.c
+++ b/drivers/gpu/drm/imx/dc/dc-lb.c
@@ -257,8 +257,6 @@ int dc_lb_get_id(struct dc_lb *lb)
void dc_lb_init(struct dc_lb *lb)
{
- dc_lb_pec_dynamic_prim_sel(lb, LINK_ID_NONE);
- dc_lb_pec_dynamic_sec_sel(lb, LINK_ID_NONE);
dc_lb_pec_clken(lb, CLKEN_DISABLE);
dc_lb_shdldsel(lb, BOTH);
dc_lb_shdtoksel(lb, BOTH);
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 14/15] drm/imx: dc-fu: Get DPR channel
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
` (12 preceding siblings ...)
2026-10-26 10:05 ` [PATCH v7 13/15] drm/imx: dc-lb: Drop initial primary and secondary input selections Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 10:05 ` [PATCH v7 15/15] drm/imx: dc: Use prefetch engine Liu Ying
14 siblings, 0 replies; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying, Frank Li
Add a helper dc_fu_get_dprc() to get DPR channel for FetchUnit.
And, call it from dc_{fl,fw)_bind() to get DPR channels for
FetchLayer and FetchWarp.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v7:
- Collect Maxime's A-b tag.
- Call dc_dprc_get() in dc_fu_get_dprc() to create a device link between
consumer FetchUnit and supplier DPRC to avoid potential UAF when
accessing fu->dprc. (Sashiko AI bot)
v2:
- Collect Frank's R-b tag.
---
drivers/gpu/drm/imx/dc/dc-fl.c | 7 ++++++-
drivers/gpu/drm/imx/dc/dc-fu.c | 25 +++++++++++++++++++++++++
drivers/gpu/drm/imx/dc/dc-fu.h | 5 +++++
drivers/gpu/drm/imx/dc/dc-fw.c | 7 ++++++-
4 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/imx/dc/dc-fl.c b/drivers/gpu/drm/imx/dc/dc-fl.c
index 9f03df44a63a..f7b8c4909c80 100644
--- a/drivers/gpu/drm/imx/dc/dc-fl.c
+++ b/drivers/gpu/drm/imx/dc/dc-fl.c
@@ -100,13 +100,18 @@ static int dc_fl_bind(struct device *dev, struct device *master, void *data)
void __iomem *base_cfg;
struct dc_fl *fl;
struct dc_fu *fu;
- int i, id;
+ int ret, i, id;
fl = devm_kzalloc(dev, sizeof(*fl), GFP_KERNEL);
if (!fl)
return -ENOMEM;
fu = &fl->fu;
+ fu->dev = dev;
+
+ ret = dc_fu_get_dprc(fu);
+ if (ret)
+ return ret;
res_pec = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/gpu/drm/imx/dc/dc-fu.c b/drivers/gpu/drm/imx/dc/dc-fu.c
index 2e197b3344bd..d9933218069c 100644
--- a/drivers/gpu/drm/imx/dc/dc-fu.c
+++ b/drivers/gpu/drm/imx/dc/dc-fu.c
@@ -8,8 +8,10 @@
#include <linux/bits.h>
#include <linux/math.h>
+#include "dc-dprc.h"
#include "dc-fu.h"
#include "dc-pe.h"
+#include "dc-prg.h"
/* STATICCONTROL */
#define SHDLDREQSTICKY_MASK GENMASK(31, 24)
@@ -256,3 +258,26 @@ void dc_fu_common_hw_init(struct dc_fu *fu)
dc_fu_set_pixel_blend_mode(fu, i);
}
}
+
+int dc_fu_get_dprc(struct dc_fu *fu)
+{
+ struct dc_dprc *dprc;
+ struct dc_prg *prg;
+
+ prg = dc_prg_lookup_by_phandle(fu->dev, "fsl,prg", 0);
+ if (!prg)
+ return dev_err_probe(fu->dev, -EPROBE_DEFER,
+ "failed to lookup PRG\n");
+
+ dprc = dc_prg_get_dprc(prg);
+ if (!dprc)
+ return dev_err_probe(fu->dev, -EPROBE_DEFER,
+ "failed to get DPR channel from PRG\n");
+
+ fu->dprc = dc_dprc_get(fu->dev, dprc);
+ if (!fu->dprc)
+ return dev_err_probe(fu->dev, -EPROBE_DEFER,
+ "failed to get DPR channel\n");
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/imx/dc/dc-fu.h b/drivers/gpu/drm/imx/dc/dc-fu.h
index cc93317e3001..9fbd7aadd264 100644
--- a/drivers/gpu/drm/imx/dc/dc-fu.h
+++ b/drivers/gpu/drm/imx/dc/dc-fu.h
@@ -8,11 +8,13 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <drm/drm_fourcc.h>
+#include "dc-dprc.h"
#include "dc-pe.h"
#define FRAC_OFFSET 0x28
@@ -99,6 +101,7 @@ struct dc_fu_ops {
};
struct dc_fu {
+ struct device *dev;
struct regmap *reg_pec;
struct regmap *reg_cfg;
char name[21];
@@ -114,6 +117,7 @@ struct dc_fu {
enum dc_link_id link_id;
struct dc_fu_ops ops;
struct dc_lb *lb;
+ struct dc_dprc *dprc;
};
extern const struct dc_fu_ops dc_fu_common_ops;
@@ -123,6 +127,7 @@ void dc_fu_get_pixel_format_shifts(struct dc_fu *fu, u32 format, u32 *shifts);
void dc_fu_shdldreq_sticky(struct dc_fu *fu, u8 layer_mask);
void dc_fu_set_src_bpp(struct dc_fu *fu, enum dc_fu_frac frac, unsigned int bpp);
void dc_fu_common_hw_init(struct dc_fu *fu);
+int dc_fu_get_dprc(struct dc_fu *fu);
const struct dc_fu_ops *dc_fu_get_ops(struct dc_fu *fu);
diff --git a/drivers/gpu/drm/imx/dc/dc-fw.c b/drivers/gpu/drm/imx/dc/dc-fw.c
index 14512c01ea78..030970d4f987 100644
--- a/drivers/gpu/drm/imx/dc/dc-fw.c
+++ b/drivers/gpu/drm/imx/dc/dc-fw.c
@@ -130,13 +130,18 @@ static int dc_fw_bind(struct device *dev, struct device *master, void *data)
void __iomem *base_cfg;
struct dc_fw *fw;
struct dc_fu *fu;
- int i, id;
+ int ret, i, id;
fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL);
if (!fw)
return -ENOMEM;
fu = &fw->fu;
+ fu->dev = dev;
+
+ ret = dc_fu_get_dprc(fu);
+ if (ret)
+ return ret;
base_pec = devm_platform_get_and_ioremap_resource(pdev, 0, &res_pec);
if (IS_ERR(base_pec))
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 15/15] drm/imx: dc: Use prefetch engine
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
` (13 preceding siblings ...)
2026-10-26 10:05 ` [PATCH v7 14/15] drm/imx: dc-fu: Get DPR channel Liu Ying
@ 2026-10-26 10:05 ` Liu Ying
2026-10-26 12:41 ` sashiko-bot
14 siblings, 1 reply; 27+ messages in thread
From: Liu Ying @ 2026-10-26 10:05 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Liu Ying, Frank Li
One prefetch engine consists of one DPR channel and one or two PRGs.
Each PRG handles one planar in a pixel format. Every FetchUnit used
by KMS may attach to a PRG and hence use a prefetch engine. So, to
simplify driver code, always use prefetch engines for FetchUnits in
KMS driver and avoid supporting bypassing them. Aside from configuring
and disabling a prefetch engine along with a FetchUnit for atomic
commits, adapt burst size/stride fixup requirements from PRG in
FetchUnit driver and set use_raw_spinlock flag in ExtDst PIXENGCFG
regmap configuration to avoid sleeping on PREEMPT_RT because
dc_ed_pec_sync_trigger() can be called with local irq and preempt
disabled.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v7:
- Check {old,new}_plane_state against NULL in dc_crtc_atomic_flush().
(Sashiko AI bot)
- Return 128B burst size from dc_fu_burst_size_fixup() when baddr == 0.
This avoids undefined behavior of __ffs(0). (Sashiko AI bot)
- Set use_raw_spinlock flag in ExtDst PIXENGCFG regmap configuration to
avoid sleeping on PREEMPT_RT because dc_ed_pec_sync_trigger() is can be
called with local irq and preempt disabled.
- Modify debug message in dc_crtc_disable_at_boot().
- Adapt dc_drv_crtc_was_enabled() in dc_crtc_disable_at_boot().
- Rename __dc_crtc_disable_fg_along_with_dprc_repeat_en() to
dc_crtc_disable_fg_along_with_dprc_repeat_en().
- Update commit message.
v6:
- Rebase onto the latest drm-misc-next and resolve conflicts.
v3:
- Collect Frank's R-b tag.
---
drivers/gpu/drm/imx/dc/dc-crtc.c | 139 ++++++++++++++++++++++++++++++++++----
drivers/gpu/drm/imx/dc/dc-ed.c | 5 ++
drivers/gpu/drm/imx/dc/dc-fu.c | 30 +++++++-
drivers/gpu/drm/imx/dc/dc-fu.h | 2 +-
drivers/gpu/drm/imx/dc/dc-kms.h | 5 ++
drivers/gpu/drm/imx/dc/dc-plane.c | 46 +++++++++++--
6 files changed, 204 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/imx/dc/dc-crtc.c b/drivers/gpu/drm/imx/dc/dc-crtc.c
index 1ca1c9229e94..2d17afcae8f2 100644
--- a/drivers/gpu/drm/imx/dc/dc-crtc.c
+++ b/drivers/gpu/drm/imx/dc/dc-crtc.c
@@ -25,6 +25,7 @@
#include <drm/drm_vblank.h>
#include "dc-de.h"
+#include "dc-dprc.h"
#include "dc-drv.h"
#include "dc-kms.h"
#include "dc-pe.h"
@@ -204,7 +205,13 @@ dc_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_commit *state)
drm_atomic_get_old_crtc_state(state, crtc);
struct drm_crtc_state *new_crtc_state =
drm_atomic_get_new_crtc_state(state, crtc);
+ struct drm_plane_state *old_plane_state =
+ drm_atomic_get_old_plane_state(state, crtc->primary);
+ struct drm_plane_state *new_plane_state =
+ drm_atomic_get_new_plane_state(state, crtc->primary);
+ struct dc_plane *dc_plane = to_dc_plane(crtc->primary);
struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
+ bool disabling_plane = false;
int idx;
if (drm_atomic_crtc_needs_modeset(new_crtc_state) ||
@@ -216,13 +223,41 @@ dc_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_commit *state)
enable_irq(dc_crtc->irq_ed_cont_shdload);
- /* flush plane update out to display */
- dc_ed_pec_sync_trigger(dc_crtc->ed_cont);
+ if (old_plane_state && new_plane_state)
+ disabling_plane = drm_atomic_plane_disabling(old_plane_state,
+ new_plane_state);
+
+ if (disabling_plane) {
+ unsigned long flags;
+
+ dc_crtc_dbg(crtc, "disabling plane\n");
+
+ /*
+ * Don't relinquish CPU until DPRC REPEAT_EN is disabled and
+ * sync is triggered.
+ */
+ local_irq_save(flags);
+ preempt_disable();
+
+ DC_CRTC_WAIT_FOR_FRAMEGEN_FRAME_INDEX_MOVING(dc_crtc->fg);
+ dc_dprc_disable_repeat_en(dc_plane->fu->dprc);
+ /* flush plane update out to display */
+ dc_ed_pec_sync_trigger(dc_crtc->ed_cont);
+
+ local_irq_restore(flags);
+ preempt_enable();
+ } else {
+ /* flush plane update out to display */
+ dc_ed_pec_sync_trigger(dc_crtc->ed_cont);
+ }
DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_cont_shdload_done);
disable_irq(dc_crtc->irq_ed_cont_shdload);
+ if (disabling_plane)
+ dc_dprc_disable(dc_plane->fu->dprc);
+
DC_CRTC_CHECK_FRAMEGEN_FIFO(dc_crtc->fg);
drm_dev_exit(idx);
@@ -323,14 +358,33 @@ dc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
dc_crtc_queue_state_event(new_crtc_state);
}
-static inline void __dc_crtc_disable_fg(struct drm_crtc *crtc)
+static inline void
+dc_crtc_disable_fg_along_with_dprc_repeat_en(struct drm_crtc *crtc)
{
+ struct dc_plane *dc_plane = to_dc_plane(crtc->primary);
struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
+ unsigned long flags;
- enable_irq(dc_crtc->irq_dec_seqcomplete);
+ /* Don't relinquish CPU until DPRC REPEAT_EN is disabled. */
+ local_irq_save(flags);
+ preempt_disable();
+
+ /*
+ * Sync to FrameGen frame index moving so that
+ * FrameGen can be disabled in the next frame.
+ */
+ DC_CRTC_WAIT_FOR_FRAMEGEN_FRAME_INDEX_MOVING(dc_crtc->fg);
dc_fg_disable(dc_crtc->fg);
- DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_seqcomplete_done);
- disable_irq(dc_crtc->irq_dec_seqcomplete);
+ /*
+ * There is one frame leftover after FrameGen disablement.
+ * Sync to FrameGen frame index moving so that DPRC REPEAT_EN
+ * can be disabled in the next frame.
+ */
+ DC_CRTC_WAIT_FOR_FRAMEGEN_FRAME_INDEX_MOVING(dc_crtc->fg);
+ dc_dprc_disable_repeat_en(dc_plane->fu->dprc);
+
+ local_irq_restore(flags);
+ preempt_enable();
}
static void
@@ -338,14 +392,29 @@ dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
{
struct drm_crtc_state *new_crtc_state =
drm_atomic_get_new_crtc_state(state, crtc);
+ struct drm_crtc_state *old_crtc_state =
+ drm_atomic_get_old_crtc_state(state, crtc);
struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
+ struct dc_plane *dc_plane = to_dc_plane(crtc->primary);
struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
int idx;
if (!drm_dev_enter(crtc->dev, &idx))
goto out;
- __dc_crtc_disable_fg(crtc);
+ enable_irq(dc_crtc->irq_dec_seqcomplete);
+
+ if (old_crtc_state->plane_mask)
+ dc_crtc_disable_fg_along_with_dprc_repeat_en(crtc);
+ else
+ dc_fg_disable(dc_crtc->fg);
+
+ DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_seqcomplete_done);
+ disable_irq(dc_crtc->irq_dec_seqcomplete);
+
+ if (old_crtc_state->plane_mask)
+ dc_dprc_disable(dc_plane->fu->dprc);
+
dc_fg_disable_clock(dc_crtc->fg);
/* request pixel engine power-off as plane is off too */
@@ -371,7 +440,10 @@ dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
void dc_crtc_disable_at_boot(struct drm_crtc *crtc)
{
struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
+ struct dc_plane *dc_plane = to_dc_plane(crtc->primary);
struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
+ enum dc_link_id ed_src, lb_sec;
+ bool disable_dprc = false;
int ret;
ret = pm_runtime_resume_and_get(dc_crtc->de->dev);
@@ -381,16 +453,57 @@ void dc_crtc_disable_at_boot(struct drm_crtc *crtc)
return;
}
+ ret = pm_runtime_resume_and_get(dc_drm->pe->dev);
+ if (ret) {
+ dc_crtc_err(crtc, "failed to get DC pixel engine RPM: %d\n",
+ ret);
+ goto out1;
+ }
+
if (!dc_fg_wait_for_frame_index_moving(dc_crtc->fg)) {
dc_crtc_dbg(crtc, "FrameGen frame index isn't moving\n");
- goto out;
+ goto out2;
}
- dc_crtc_dbg(crtc, "disabling FrameGen at boot\n");
- __dc_crtc_disable_fg(crtc);
+ ret = dc_ed_pec_src_sel_get(dc_crtc->ed_cont, &ed_src);
+ if (ret) {
+ dc_crtc_err(crtc, "failed to get content ExtDst's source\n");
+ goto out2;
+ }
+
+ if (ed_src == LINK_ID_CONSTFRAME0 || ed_src == LINK_ID_CONSTFRAME1)
+ goto disable;
+
+ ret = dc_lb_pec_dynamic_sec_sel_get(dc_plane->lb, &lb_sec);
+ if (ret) {
+ dc_crtc_err(crtc,
+ "failed to get primary plane LayerBlend secondary source\n");
+ goto out2;
+ }
+
+ disable_dprc = true;
+
+disable:
+ enable_irq(dc_crtc->irq_dec_seqcomplete);
+
+ if (disable_dprc) {
+ dc_crtc_dbg(crtc, "disabling FrameGen along with DPRC REPEAT_EN at boot\n");
+ dc_crtc_disable_fg_along_with_dprc_repeat_en(crtc);
+ } else {
+ dc_crtc_dbg(crtc, "disabling FrameGen at boot\n");
+ dc_fg_disable(dc_crtc->fg);
+ }
+
+ DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_seqcomplete_done);
+ disable_irq(dc_crtc->irq_dec_seqcomplete);
if (!dc_drv_crtc_was_enabled(dc_drm, drm_crtc_index(crtc)))
- goto out;
+ goto out2;
+
+ if (disable_dprc) {
+ dc_crtc_dbg(crtc, "disabling DPRC at boot\n");
+ dc_dprc_disable_at_boot(dc_plane->fu->dprc);
+ }
dc_crtc_dbg(crtc, "disabling clocks at boot\n");
@@ -401,7 +514,9 @@ void dc_crtc_disable_at_boot(struct drm_crtc *crtc)
dc_drm->pe_clk_axi_disabled = true;
}
-out:
+out2:
+ pm_runtime_put(dc_drm->pe->dev);
+out1:
pm_runtime_put(dc_crtc->de->dev);
}
diff --git a/drivers/gpu/drm/imx/dc/dc-ed.c b/drivers/gpu/drm/imx/dc/dc-ed.c
index 2f8d8c94e2d9..c90644623904 100644
--- a/drivers/gpu/drm/imx/dc/dc-ed.c
+++ b/drivers/gpu/drm/imx/dc/dc-ed.c
@@ -77,6 +77,11 @@ static const struct regmap_config dc_ed_pec_regmap_config = {
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
+ /*
+ * Registers could be accessed with local irq and preempt disabled,
+ * so use a raw spinlock to avoid sleeping on PREEMPT_RT.
+ */
+ .use_raw_spinlock = true,
.wr_table = &dc_ed_pec_regmap_write_table,
.rd_table = &dc_ed_pec_regmap_read_table,
.volatile_table = &dc_ed_pec_regmap_volatile_table,
diff --git a/drivers/gpu/drm/imx/dc/dc-fu.c b/drivers/gpu/drm/imx/dc/dc-fu.c
index d9933218069c..fd98d02e4d6b 100644
--- a/drivers/gpu/drm/imx/dc/dc-fu.c
+++ b/drivers/gpu/drm/imx/dc/dc-fu.c
@@ -125,13 +125,31 @@ static inline void dc_fu_set_numbuffers(struct dc_fu *fu, unsigned int num)
SETNUMBUFFERS_MASK, SETNUMBUFFERS(num));
}
-static void dc_fu_set_burstlength(struct dc_fu *fu, dma_addr_t baddr)
+static unsigned int dc_fu_burst_size_fixup(dma_addr_t baddr)
{
- unsigned int burst_size, burst_length;
+ unsigned int burst_size;
+
+ if (baddr == 0)
+ return 128U;
burst_size = 1 << __ffs(baddr);
burst_size = round_up(burst_size, 8);
burst_size = min(burst_size, 128U);
+
+ return burst_size;
+}
+
+static unsigned int
+dc_fu_stride_fixup(unsigned int stride, unsigned int burst_size)
+{
+ return round_up(stride, burst_size);
+}
+
+static void dc_fu_set_burstlength(struct dc_fu *fu, dma_addr_t baddr)
+{
+ unsigned int burst_size, burst_length;
+
+ burst_size = dc_fu_burst_size_fixup(baddr);
burst_length = burst_size / 8;
regmap_write_bits(fu->reg_cfg, BURSTBUFFERMANAGEMENT,
@@ -151,8 +169,14 @@ void dc_fu_set_src_bpp(struct dc_fu *fu, enum dc_fu_frac frac, unsigned int bpp)
}
static void dc_fu_set_src_stride(struct dc_fu *fu, enum dc_fu_frac frac,
- unsigned int stride)
+ unsigned int width, int bpp, dma_addr_t baddr)
{
+ unsigned int burst_size = dc_fu_burst_size_fixup(baddr);
+ unsigned int stride;
+
+ stride = width * (bpp / 8);
+ stride = dc_fu_stride_fixup(stride, burst_size);
+
regmap_write_bits(fu->reg_cfg, fu->reg_sourcebufferattributes[frac],
STRIDE_MASK, STRIDE(stride));
}
diff --git a/drivers/gpu/drm/imx/dc/dc-fu.h b/drivers/gpu/drm/imx/dc/dc-fu.h
index 9fbd7aadd264..0527a7b2221d 100644
--- a/drivers/gpu/drm/imx/dc/dc-fu.h
+++ b/drivers/gpu/drm/imx/dc/dc-fu.h
@@ -87,7 +87,7 @@ struct dc_fu_ops {
void (*set_baseaddress)(struct dc_fu *fu, enum dc_fu_frac frac,
dma_addr_t baddr);
void (*set_src_stride)(struct dc_fu *fu, enum dc_fu_frac frac,
- unsigned int stride);
+ unsigned int width, int bpp, dma_addr_t baddr);
void (*set_src_buf_dimensions)(struct dc_fu *fu, enum dc_fu_frac frac,
int w, int h);
void (*set_fmt)(struct dc_fu *fu, enum dc_fu_frac frac,
diff --git a/drivers/gpu/drm/imx/dc/dc-kms.h b/drivers/gpu/drm/imx/dc/dc-kms.h
index a25d47eebd28..8b45b21a6f8a 100644
--- a/drivers/gpu/drm/imx/dc/dc-kms.h
+++ b/drivers/gpu/drm/imx/dc/dc-kms.h
@@ -130,4 +130,9 @@ struct dc_plane {
struct dc_ed *ed;
};
+static inline struct dc_plane *to_dc_plane(struct drm_plane *plane)
+{
+ return container_of(plane, struct dc_plane, base);
+}
+
#endif /* __DC_KMS_H__ */
diff --git a/drivers/gpu/drm/imx/dc/dc-plane.c b/drivers/gpu/drm/imx/dc/dc-plane.c
index 42c0a46281cb..3a9c7548f827 100644
--- a/drivers/gpu/drm/imx/dc/dc-plane.c
+++ b/drivers/gpu/drm/imx/dc/dc-plane.c
@@ -17,6 +17,7 @@
#include <drm/drm_plane_helper.h>
#include <drm/drm_print.h>
+#include "dc-dprc.h"
#include "dc-drv.h"
#include "dc-fu.h"
#include "dc-kms.h"
@@ -44,11 +45,6 @@ static const struct drm_plane_funcs dc_plane_funcs = {
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
-static inline struct dc_plane *to_dc_plane(struct drm_plane *plane)
-{
- return container_of(plane, struct dc_plane, base);
-}
-
static int dc_plane_check_max_source_resolution(struct drm_plane_state *state)
{
int src_h = drm_rect_height(&state->src) >> 16;
@@ -88,6 +84,28 @@ static int dc_plane_check_fb(struct drm_plane_state *state)
return 0;
}
+static int dc_plane_check_dprc(struct drm_plane_state *state)
+{
+ struct dc_plane *dplane = to_dc_plane(state->plane);
+ struct drm_framebuffer *fb = state->fb;
+ dma_addr_t baseaddr = drm_fb_dma_get_gem_addr(fb, state, 0);
+ struct dc_dprc *dprc = dplane->fu->dprc;
+ u32 src_w = drm_rect_width(&state->src) >> 16;
+
+ if (!dc_dprc_rtram_width_supported(dprc, src_w)) {
+ dc_plane_dbg(state->plane, "bad RTRAM width for DPRC\n");
+ return -EINVAL;
+ }
+
+ if (!dc_dprc_stride_supported(dprc, fb->pitches[0], src_w, fb->format,
+ baseaddr)) {
+ dc_plane_dbg(state->plane, "fb bad pitches[0] for DPRC\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int
dc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_commit *state)
{
@@ -123,7 +141,11 @@ dc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_commit *state)
if (ret)
return ret;
- return dc_plane_check_fb(plane_state);
+ ret = dc_plane_check_fb(plane_state);
+ if (ret)
+ return ret;
+
+ return dc_plane_check_dprc(plane_state);
}
/* A prerequisite is that drm_dev_enter() has been called beforehand. */
@@ -158,6 +180,12 @@ dc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state)
{
struct drm_plane_state *new_state =
drm_atomic_get_new_plane_state(state, plane);
+ struct drm_plane_state *old_state =
+ drm_atomic_get_old_plane_state(state, plane);
+ struct drm_crtc_state *new_crtc_state =
+ drm_atomic_get_new_crtc_state(state, new_state->crtc);
+ bool needs_modeset = drm_atomic_crtc_needs_modeset(new_crtc_state);
+ bool prefetch_start = needs_modeset || !old_state->fb;
struct dc_plane *dplane = to_dc_plane(plane);
struct drm_framebuffer *fb = new_state->fb;
const struct dc_fu_ops *fu_ops;
@@ -184,7 +212,8 @@ dc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state)
fu_ops->set_layerblend(fu, lb);
fu_ops->set_burstlength(fu, baseaddr);
- fu_ops->set_src_stride(fu, DC_FETCHUNIT_FRAC0, fb->pitches[0]);
+ fu_ops->set_src_stride(fu, DC_FETCHUNIT_FRAC0, src_w,
+ fb->format->cpp[0] * 8, baseaddr);
fu_ops->set_src_buf_dimensions(fu, DC_FETCHUNIT_FRAC0, src_w, src_h);
fu_ops->set_fmt(fu, DC_FETCHUNIT_FRAC0, fb->format);
fu_ops->set_framedimensions(fu, src_w, src_h);
@@ -193,6 +222,9 @@ dc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state)
dc_plane_dbg(plane, "uses %s\n", fu_ops->get_name(fu));
+ dc_dprc_configure(fu->dprc, new_state->crtc->index, src_w, src_h,
+ fb->pitches[0], fb->format, baseaddr, prefetch_start);
+
dc_lb_pec_dynamic_prim_sel(lb, dc_cf_get_link_id(dplane->cf));
dc_lb_pec_dynamic_sec_sel(lb, fu_ops->get_link_id(fu));
dc_lb_mode(lb, LB_BLEND);
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH v7 10/15] drm/imx: dc-ed: Support getting source selection
2026-10-26 10:05 ` [PATCH v7 10/15] drm/imx: dc-ed: Support getting source selection Liu Ying
@ 2026-10-26 10:41 ` Alexander Stein
2026-10-26 12:01 ` sashiko-bot
1 sibling, 0 replies; 27+ messages in thread
From: Alexander Stein @ 2026-10-26 10:41 UTC (permalink / raw)
To: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov,
linux-arm-kernel
Cc: dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Liu Ying, Frank Li, Liu Ying
Hi,
thanks for the patch and adding me.
Am Montag, 26. Oktober 2026, 11:05:15 Mitteleuropäische Normalzeit schrieb Liu Ying:
> Add a helper to get ExtDst source selection. This is needed by
> disabling CRTC at boot in an upcoming commit which tries to get
> the source selection.
>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Acked-by: Maxime Ripard <mripard@kernel.org>
> Signed-off-by: Liu Ying <victor.liu@nxp.com>
> --
> v7:
> - Collect Maxime's A-b tag.
> - Check regmap_read()'s return valure. (Sashiko AI bot)
>
> v2:
> - Collect Frank's R-b tag.
> ---
> drivers/gpu/drm/imx/dc/dc-ed.c | 29 +++++++++++++++++++++++++++++
> drivers/gpu/drm/imx/dc/dc-pe.h | 1 +
> 2 files changed, 30 insertions(+)
>
> diff --git a/drivers/gpu/drm/imx/dc/dc-ed.c b/drivers/gpu/drm/imx/dc/dc-ed.c
> index de1b71315eab..67163dca1363 100644
> --- a/drivers/gpu/drm/imx/dc/dc-ed.c
> +++ b/drivers/gpu/drm/imx/dc/dc-ed.c
> @@ -149,6 +149,35 @@ void dc_ed_pec_src_sel(struct dc_ed *ed, enum dc_link_id src)
> }
> }
>
> +int dc_ed_pec_src_sel_get(struct dc_ed *ed, enum dc_link_id *src)
> +{
> + u32 val;
> + int ret;
> +
> + ret = regmap_read(ed->reg_pec, PIXENGCFG_DYNAMIC, &val);
> + if (ret)
> + return -EINVAL;
Shouldn't you return ret here instead? With that fixed:
Reviewed-by: Alexander Stein <alexander.stein@ew.tq-group.com>
Best regards
Alexander
> +
> + switch (val) {
> + case LINK_ID_NONE:
> + case LINK_ID_CONSTFRAME0:
> + case LINK_ID_CONSTFRAME4:
> + case LINK_ID_CONSTFRAME1:
> + case LINK_ID_CONSTFRAME5:
> + case LINK_ID_FETCHWARP2:
> + case LINK_ID_FETCHLAYER0:
> + case LINK_ID_LAYERBLEND0:
> + case LINK_ID_LAYERBLEND1:
> + case LINK_ID_LAYERBLEND2:
> + case LINK_ID_LAYERBLEND3:
> + *src = val;
> + dev_dbg(ed->dev, "get source selection: 0x%02x\n", val);
> + return 0;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> void dc_ed_pec_sync_trigger(struct dc_ed *ed)
> {
> regmap_write(ed->reg_pec, PIXENGCFG_TRIGGER, SYNC_TRIGGER);
> diff --git a/drivers/gpu/drm/imx/dc/dc-pe.h b/drivers/gpu/drm/imx/dc/dc-pe.h
> index f5e01a6eb9e9..0b81dc6b251c 100644
> --- a/drivers/gpu/drm/imx/dc/dc-pe.h
> +++ b/drivers/gpu/drm/imx/dc/dc-pe.h
> @@ -85,6 +85,7 @@ void dc_cf_init(struct dc_cf *cf);
>
> /* External Destination Unit */
> void dc_ed_pec_src_sel(struct dc_ed *ed, enum dc_link_id src);
> +int dc_ed_pec_src_sel_get(struct dc_ed *ed, enum dc_link_id *src);
> void dc_ed_pec_sync_trigger(struct dc_ed *ed);
> void dc_ed_init(struct dc_ed *ed);
>
>
>
--
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
http://www.tq-group.com/
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 06/15] drm/imx: dc-crtc: Disable at boot
2026-10-26 10:05 ` [PATCH v7 06/15] drm/imx: dc-crtc: Disable at boot Liu Ying
@ 2026-10-26 11:18 ` sashiko-bot
2026-10-29 9:30 ` Laurentiu Palcu
1 sibling, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-10-26 11:18 UTC (permalink / raw)
To: Liu Ying; +Cc: dri-devel, devicetree, imx, robh, conor+dt, Frank.Li
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] drm/imx: dc-crtc: missing dc_drv_set_crtc_disable() in dc_crtc_disable_at_boot()
--
commit f302747895df5188605a9ca1ea0bd65af3b6d652
Author: Liu Ying <victor.liu@nxp.com>
drm/imx: dc-crtc: Disable at boot
This commit adds a helper dc_crtc_disable_at_boot() and calls it during
dc_drm_bind() before drm_dev_register() to disable any running display
controller pipes and balance clock enable counts after an unplug and rebind
cycle.
> diff --git a/drivers/gpu/drm/imx/dc/dc-crtc.c b/drivers/gpu/drm/imx/dc/dc-crtc.c
> index 0d64186a99018..c7dbe5f500b4a 100644
> --- a/drivers/gpu/drm/imx/dc/dc-crtc.c
> +++ b/drivers/gpu/drm/imx/dc/dc-crtc.c
> @@ -287,12 +288,24 @@ dc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
[ ... ]
> @@ -318,8 +327,9 @@ dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
> /* request display engine power-off when CRTC is disabled */
> pm_runtime_put(dc_crtc->de->dev);
>
> - drm_dev_exit(idx);
> + dc_drv_set_crtc_disable(dc_drm, drm_crtc_index(crtc));
>
> + drm_dev_exit(idx);
> out:
> drm_crtc_vblank_off(crtc);
>
> @@ -331,6 +341,43 @@ dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
> spin_unlock_irq(&crtc->dev->event_lock);
> }
>
> +void dc_crtc_disable_at_boot(struct drm_crtc *crtc)
> +{
> + struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
> + struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
> + int ret;
> +
> + ret = pm_runtime_resume_and_get(dc_crtc->de->dev);
> + if (ret < 0) {
> + dc_crtc_err(crtc, "failed to get DC display engine RPM: %d\n",
> + ret);
> + return;
> + }
> +
> + if (!dc_fg_wait_for_frame_index_moving(dc_crtc->fg)) {
> + dc_crtc_dbg(crtc, "FrameGen frame index isn't moving\n");
> + goto out;
> + }
> +
> + dc_crtc_dbg(crtc, "disabling FrameGen at boot\n");
> + __dc_crtc_disable_fg(crtc);
> +
> + if (!dc_drv_crtc_was_enabled(dc_drm, drm_crtc_index(crtc)))
> + goto out;
> +
> + dc_crtc_dbg(crtc, "disabling clocks at boot\n");
> +
> + dc_fg_disable_clock(dc_crtc->fg);
> +
> + if (!dc_drm->pe_clk_axi_disabled) {
> + clk_disable_unprepare(dc_drm->pe->clk_axi);
> + dc_drm->pe_clk_axi_disabled = true;
> + }
[Severity: Medium]
Should dc_crtc_disable_at_boot() also call dc_drv_set_crtc_disable() after
disabling the clocks here?
When a pipe is enabled via dc_crtc_atomic_enable(), dc_drv_set_crtc_enable()
sets the corresponding bit in priv->gp_regmap:
drivers/gpu/drm/imx/dc/dc-drv.c:dc_drv_set_crtc_enable() {
...
regmap_set_bits(priv->gp_regmap, 0, CRTC_ENABLED(crtc_index));
}
If the device is then unplugged via dc_drm_unbind() -> drm_dev_unplug(),
dc_crtc_atomic_disable() returns early at drm_dev_enter() without calling
dc_drv_set_crtc_disable().
On the next bind in dc_drm_bind() -> dc_crtc_disable_at_boot(), the helper
stops the frame generator, sees dc_drv_crtc_was_enabled() return true, and
disables dc_crtc->fg->clk_disp and dc_drm->pe->clk_axi, dropping their clock
enable and prepare counts back to zero. However, because it does not call
dc_drv_set_crtc_disable(dc_drm, drm_crtc_index(crtc)), the enable bit in
priv->gp_regmap remains set.
If dc_crtc_disable_at_boot() runs again while the frame generator is active
without an intervening atomic enable and disable cycle (for instance, across
a warm reboot where the general-purpose register stays set and the
bootloader enables the display, or if __dc_crtc_disable_fg() times out and
bind is retried), wouldn't dc_drv_crtc_was_enabled() still see the stale bit
and call dc_fg_disable_clock() and clk_disable_unprepare() on clocks that
are already disabled?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261026-imx8-dc-prefetch-v7-0-a0b50cc4fc65@nxp.com?part=6
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 07/15] drm/imx: dc: Add PRG support
2026-10-26 10:05 ` [PATCH v7 07/15] drm/imx: dc: Add PRG support Liu Ying
@ 2026-10-26 11:32 ` sashiko-bot
2026-10-29 9:30 ` Laurentiu Palcu
1 sibling, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-10-26 11:32 UTC (permalink / raw)
To: Liu Ying; +Cc: dri-devel, devicetree, imx, robh, conor+dt, Frank.Li
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] drm/imx: dc-prg: unclocked MMIO and RPM underflow on dc_prg_configure() error
- [Medium] drm/imx: dc: signed shift overflow in dc_prg_burst_size_fixup()
- [Medium] drm/imx: dc-prg: unchecked device_link_add() in dc_prg_lookup_by_phandle()
--
commit 7a29cff8d2e3466268f3191ce1679f0e57e395cf
Author: Liu Ying <victor.liu@nxp.com>
drm/imx: dc: Add PRG support
This commit adds a platform driver to support the Display Prefetch Resolve
Gasket (PRG), which sits between a Display Prefetch Resolve Channel (DPRC)
and a FetchUnit in the i.MX8 display controller prefetch engine.
> diff --git a/drivers/gpu/drm/imx/dc/dc-prg.c b/drivers/gpu/drm/imx/dc/dc-prg.c
> new file mode 100644
> index 0000000000000..43df5683b24d1
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/dc/dc-prg.c
> @@ -0,0 +1,314 @@
[ ... ]
> +void dc_prg_enable(struct dc_prg *prg)
> +{
> + regmap_write(prg->reg, PRG_CTRL + CLR, BYPASS);
> +}
> +
> +void dc_prg_disable(struct dc_prg *prg)
> +{
> + regmap_write(prg->reg, PRG_CTRL, BYPASS);
> +
> + pm_runtime_put(prg->dev);
> +}
> +
> +void dc_prg_disable_at_boot(struct dc_prg *prg)
> +{
> + regmap_write(prg->reg, PRG_CTRL, BYPASS);
> +
> + clk_bulk_disable_unprepare(prg->num_clks, prg->clks);
> +}
> +
> +static unsigned int dc_prg_burst_size_fixup(dma_addr_t baddr)
> +{
> + unsigned int burst_size;
> +
> + if (baddr == 0)
> + return 128U;
> +
> + burst_size = 1 << __ffs(baddr);
> + burst_size = round_up(burst_size, 8);
> + burst_size = min(burst_size, 128U);
> +
> + return burst_size;
> +}
[Severity: Medium]
Can shifting 1 by __ffs(baddr) in dc_prg_burst_size_fixup() overflow a 32-bit
signed int when baddr has a high power-of-two alignment?
__ffs(baddr) takes an unsigned long and returns a bit index up to 63 on
arm64, while 1 is a 32-bit signed int. On i.MX8QXP and i.MX8QM where system
DRAM starts at 0x80000000, a framebuffer allocated at 0x80000000 (or any odd
multiple of 2 GiB) gives __ffs(baddr) == 31, causing 1 << 31 to overflow a
signed 32-bit int.
If baddr is 4 GiB-aligned (so its lower 32 bits are zero and __ffs(baddr) is
32 or greater), shifting a 32-bit int by >= 32 is undefined behavior and on
arm64 wraps the shift count modulo 32, producing a smaller burst_size (such
as 8 instead of 128U) before min(burst_size, 128U) runs.
Since burst_size is capped at 128U, would it make sense to bound the bit
search before shifting (for example, using 1U << __ffs(baddr | 128U) or
checking !(baddr & 0x7f))?
> +static unsigned int
> +dc_prg_stride_fixup(unsigned int stride, unsigned int burst_size)
> +{
> + return round_up(stride, burst_size);
> +}
> +
> +void dc_prg_configure(struct dc_prg *prg,
> + unsigned int width, unsigned int height,
> + unsigned int stride, unsigned int bits_per_pixel,
> + dma_addr_t baddr, bool start)
> +{
> + struct device *dev = prg->dev;
> + unsigned int burst_size;
> + u32 val;
> + int ret;
> +
> + if (start) {
> + ret = pm_runtime_resume_and_get(dev);
> + if (ret < 0) {
> + dev_err(dev, "failed to get RPM: %d\n", ret);
> + return;
> + }
> + }
[Severity: High]
What happens in the caller if pm_runtime_resume_and_get() fails here when
start is true?
Because dc_prg_configure() returns void, callers cannot tell that runtime
resume failed and that PRG clocks remain disabled. When called from the DPR
channel path on initial plane enablement, the caller immediately proceeds to
call dc_prg_enable() and dc_prg_reg_update() (and later
dc_prg_shadow_enable()), performing MMIO writes to PRG_CTRL and
PRG_REG_UPDATE while the PRG clocks and power domain are off.
When the plane or CRTC is later disabled, dc_prg_disable() will also write
PRG_CTRL while unclocked and call pm_runtime_put(prg->dev), underflowing the
device runtime PM usage count.
Should dc_prg_configure() return an error code or otherwise prevent
subsequent register accesses and the unpaired pm_runtime_put()?
[ ... ]
> +struct dc_prg *
> +dc_prg_lookup_by_phandle(struct device *dev, const char *name, int index)
> +{
> + struct device_node *prg_node __free(device_node) =
> + of_parse_phandle(dev->of_node, name, index);
> + struct dc_prg *prg;
> +
> + if (!prg_node)
> + return NULL;
> +
> + guard(mutex)(&dc_prg_list_mutex);
> + list_for_each_entry(prg, &dc_prg_list, list) {
> + if (prg_node == prg->dev->of_node) {
> + device_link_add(dev, prg->dev,
> + DL_FLAG_AUTOREMOVE_CONSUMER);
> + return prg;
> + }
> + }
> +
> + return NULL;
> +}
[Severity: Medium]
Should dc_prg_lookup_by_phandle() check the return value of
device_link_add()?
If device_link_add() fails and returns NULL, dc_prg_lookup_by_phandle() still
returns prg without establishing the DL_FLAG_AUTOREMOVE_CONSUMER link. If the
PRG device is later unbound via dc_prg_remove():
static void dc_prg_remove(struct platform_device *pdev)
{
struct dc_prg *prg = dev_get_drvdata(&pdev->dev);
guard(mutex)(&dc_prg_list_mutex);
list_del(&prg->list);
}
the consumer will not be unbound and can retain a dangling pointer to the
devm-allocated struct dc_prg.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261026-imx8-dc-prefetch-v7-0-a0b50cc4fc65@nxp.com?part=7
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 08/15] drm/imx: dc: Add DPR channel support
2026-10-26 10:05 ` [PATCH v7 08/15] drm/imx: dc: Add DPR channel support Liu Ying
@ 2026-10-26 11:52 ` sashiko-bot
2026-10-29 9:31 ` Laurentiu Palcu
1 sibling, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-10-26 11:52 UTC (permalink / raw)
To: Liu Ying; +Cc: dri-devel, devicetree, imx, robh, conor+dt, Frank.Li
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] drm/imx: dc-dprc: unsafe runtime PM and IRQ sync in dc_dprc_wrap_irq_handler()
- [High] drm/imx: dc: race between dc_dprc_ctrl_done_handle() and DPRC disable
- [Medium] drm/imx: dc: missing error handling and RPM cleanup in dc_dprc_configure()
- [Medium] drm/imx: dc: unchecked device_link_add() and unbind race in dc_dprc_get()
--
commit af166b9c0f4a6b977aa85b013620057bbb1169c8
Author: Liu Ying <victor.liu@nxp.com>
drm/imx: dc: Add DPR channel support
This commit adds a platform driver for the Display Prefetch Resolve Channel
(DPRC) in the i.MX8 Display Controller, working alongside the PRG to fetch
and transform display data into RTRAM.
> diff --git a/drivers/gpu/drm/imx/dc/dc-dprc.c b/drivers/gpu/drm/imx/dc/dc-dprc.c
> new file mode 100644
> index 0000000000000..a8ddcd117a52b
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/dc/dc-dprc.c
[ ... ]
> +void dc_dprc_configure(struct dc_dprc *dprc, unsigned int stream_id,
> + unsigned int width, unsigned int height,
> + unsigned int stride,
> + const struct drm_format_info *format,
> + dma_addr_t baddr, bool start)
> +{
> + unsigned int prg_stride = width * format->cpp[0];
> + unsigned int bpp = format->cpp[0] * 8;
> + struct device *dev = dprc->dev;
> + unsigned int p1_w, p1_h;
> + u32 val;
> + int ret;
> +
> + if (start) {
> + ret = pm_runtime_resume_and_get(dev);
> + if (ret < 0) {
> + dev_err(dev, "failed to get RPM: %d\n", ret);
> + return;
> + }
> +
> + dc_dprc_set_stream_id(dprc, stream_id);
> + }
[ ... ]
> + switch (format->format) {
> + case DRM_FORMAT_XRGB8888:
> + /*
> + * It turns out pixel components are mapped directly
> + * without position change via DPR processing with
> + * the following color component configurations.
> + * Leave the pixel format to be handled by the
> + * display controllers.
> + */
> + val |= A_COMP_SEL(3) | R_COMP_SEL(2) |
> + G_COMP_SEL(1) | B_COMP_SEL(0);
> + val |= PIX_SIZE_32BIT;
> + break;
> + default:
> + dev_err(dev, "unsupported format 0x%08x\n", format->format);
> + return;
> + }
> + regmap_write(dprc->reg, MODE_CTRL0, val);
> +
> + if (start) {
> + /* software shadow load for the first frame */
> + val = SW_SHADOW_LOAD_SEL | SHADOW_LOAD_EN;
> + regmap_write(dprc->reg, SYSTEM_CTRL0, val);
> +
> + /* and then, run... */
> + val |= RUN_EN | REPEAT_EN;
> + regmap_write(dprc->reg, SYSTEM_CTRL0, val);
> + }
> +
> + dc_prg_configure(dprc->prg, width, height, prg_stride, bpp, baddr, start);
> +
> + if (start)
> + dc_dprc_enable(dprc);
> +
> + dc_dprc_reg_update(dprc);
[Severity: Medium]
Can dc_dprc_configure() leak the runtime PM reference on dprc->dev or perform
unclocked register writes to dprc->prg->reg on error?
When start is true, dc_dprc_configure() calls pm_runtime_resume_and_get(dev)
before checking format->format. If the switch hits the default branch, it
returns early without calling pm_runtime_put(dev) and before
dc_prg_configure() acquires a matching runtime PM reference on dprc->prg->dev.
In addition, dc_prg_configure() can fail in pm_runtime_resume_and_get() when
start is true:
drivers/gpu/drm/imx/dc/dc-prg.c:dc_prg_configure() {
if (start) {
ret = pm_runtime_resume_and_get(dev);
if (ret < 0) {
dev_err(dev, "failed to get RPM: %d\n", ret);
return;
}
}
}
Because dc_prg_configure() returns void, dc_dprc_configure() still proceeds to
call dc_dprc_enable() -> dc_prg_enable() and dc_dprc_reg_update() ->
dc_prg_reg_update(), writing to prg->reg while prg->dev remains suspended and
unclocked, and leaving the runtime PM reference counts unbalanced between
dprc->dev and prg->dev.
> +
> + if (start)
> + dc_dprc_enable_ctrl_done_irq(dprc);
> +
> + dev_dbg(dev, "w: %u, h: %u, s: %u, fmt: 0x%08x\n",
> + width, height, stride, format->format);
> +}
> +
> +void dc_dprc_disable_repeat_en(struct dc_dprc *dprc)
> +{
> + regmap_write(dprc->reg, SYSTEM_CTRL0 + CLR, REPEAT_EN);
> + dev_dbg(dprc->dev, "disable REPEAT_EN\n");
> +}
> +
> +void dc_dprc_disable(struct dc_dprc *dprc)
> +{
> + dc_prg_disable(dprc->prg);
> +
> + pm_runtime_put(dprc->dev);
> +
> + dev_dbg(dprc->dev, "disable\n");
> +}
> +
> +void dc_dprc_disable_at_boot(struct dc_dprc *dprc)
> +{
> + dc_prg_disable_at_boot(dprc->prg);
> +
> + clk_bulk_disable_unprepare(dprc->num_clks, dprc->clks);
> +
> + dev_dbg(dprc->dev, "disable at boot\n");
> +}
> +
> +static void dc_dprc_ctrl_done_handle(struct dc_dprc *dprc)
> +{
> + regmap_write(dprc->reg, SYSTEM_CTRL0, REPEAT_EN);
> +
> + dc_prg_shadow_enable(dprc->prg);
> +
> + dev_dbg(dprc->dev, "CTRL done handle\n");
> +}
[Severity: High]
What prevents dc_dprc_wrap_irq_handler() -> dc_dprc_ctrl_done_handle() from
racing with dc_dprc_disable_repeat_en() and dc_dprc_disable()?
When dc_dprc_configure() runs with start set to true, it unmasks the control
done interrupt via dc_dprc_enable_ctrl_done_irq(). If the plane or CRTC is
subsequently disabled, neither dc_dprc_disable_repeat_en() nor
dc_dprc_disable() masks that interrupt or synchronizes with
dc_dprc_wrap_irq_handler():
1. dc_dprc_disable_repeat_en() clears repeat_en in system_ctrl0 with local
interrupts disabled, without taking dprc->lock or masking the control done
interrupt. If the control done interrupt is already pending or running on
another CPU (or in an interrupt thread on preempt_rt),
dc_dprc_ctrl_done_handle() can run right after and write repeat_en back to
system_ctrl0.
2. dc_dprc_disable() calls dc_prg_disable(dprc->prg), which puts the PRG into
bypass mode and calls pm_runtime_put(prg->dev), and then calls asynchronous
pm_runtime_put(dprc->dev). While dprc->dev is still runtime-active or an
interrupt handler is in flight, dc_dprc_ctrl_done_handle() can call
dc_prg_shadow_enable(dprc->prg), writing to prg->reg after prg->dev has
already been disabled and had its runtime PM reference dropped.
> +
> +static irqreturn_t dc_dprc_wrap_irq_handler(int irq, void *data)
> +{
> + struct dc_dprc *dprc = data;
> + struct device *dev = dprc->dev;
> + u32 mask = 0, status = 0;
> + irqreturn_t result;
> +
> + pm_runtime_get_noresume(dev);
> + if (!pm_runtime_active(dev)) {
> + result = IRQ_NONE;
> + goto out;
> + }
[ ... ]
> + result = IRQ_HANDLED;
> +out:
> + pm_runtime_put_noidle(dev);
> + return result;
> +}
[ ... ]
> +struct dc_dprc *dc_dprc_get(struct device *dev, struct dc_dprc *dprc)
> +{
> + struct dc_dprc *iter;
> +
> + guard(mutex)(&dc_dprc_list_mutex);
> + list_for_each_entry(iter, &dc_dprc_list, list) {
> + if (iter == dprc) {
> + device_link_add(dev, dprc->dev,
> + DL_FLAG_AUTOREMOVE_CONSUMER);
> + return dprc;
> + }
> + }
> +
> + return NULL;
> +}
[Severity: Medium]
Should dc_dprc_get() (and dc_prg_lookup_by_phandle() when called from
dc_dprc_probe()) check whether device_link_add() returns NULL, and can
dc_dprc_get() race with driver unbind?
If device_link_add() fails and returns NULL, dc_dprc_get() and
dc_prg_lookup_by_phandle() still return a valid pointer without establishing
the consumer-supplier teardown link. Because dc_dprc_driver is registered
before dc_prg_driver in dc_drivers[], platform_unregister_drivers() unbinds
dc_prg_driver before dc_dprc_driver when no device link exists, so
dc_dprc_remove() calling dc_prg_set_dprc(dprc->prg, NULL) would write to a
freed dprc->prg.
Furthermore, during supplier unbind in __device_release_driver(),
device_links_busy() runs before dc_dprc_remove() acquires dc_dprc_list_mutex
and removes dprc->list from dc_dprc_list. If a consumer calls dc_dprc_get()
during that window, dprc is still on dc_dprc_list, device_link_add() creates
the link after device_links_busy() has already passed, and dc_dprc_get()
returns dprc right as it is being unbound and freed.
> +
> +static int dc_dprc_probe(struct platform_device *pdev)
> +{
[ ... ]
> + ret = devm_request_irq(dev, wrap_irq, dc_dprc_wrap_irq_handler,
> + IRQF_SHARED, dev_name(dev), dprc);
> + if (ret < 0) {
> + dev_err(dev, "failed to request dpr_wrap IRQ(%d): %d\n",
> + wrap_irq, ret);
> + return ret;
> + }
> +
> + dev_set_drvdata(dev, dprc);
> +
> + ret = devm_pm_runtime_enable(dev);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to enable PM runtime\n");
[ ... ]
> +static int dc_dprc_runtime_suspend(struct device *dev)
> +{
> + struct dc_dprc *dprc = dev_get_drvdata(dev);
> +
> + clk_bulk_disable_unprepare(dprc->num_clks, dprc->clks);
> +
> + return 0;
> +}
[Severity: High]
Could dc_dprc_wrap_irq_handler() access unclocked registers or leak the
runtime PM active state when using pm_runtime_get_noresume() and
pm_runtime_active()?
There appear to be three issues with how runtime PM and the shared wrap_irq
interact across dc_dprc_wrap_irq_handler(), dc_dprc_probe(), and
dc_dprc_runtime_suspend():
1. In include/linux/pm_runtime.h, pm_runtime_active(dev) evaluates to true
whenever dev->power.disable_depth is non-zero:
static inline bool pm_runtime_active(struct device *dev)
{
return dev->power.runtime_status == RPM_ACTIVE
|| dev->power.disable_depth;
}
Because dc_dprc_probe() registers the shared interrupt with
devm_request_irq() before calling devm_pm_runtime_enable(dev),
disable_depth is non-zero while the handler is live during probe and
during reverse devres teardown (as well as during late system suspend). If
another device sharing wrap_irq raises an interrupt during these windows,
pm_runtime_active(dev) returns true and dc_dprc_wrap_irq_handler() reads
dprc->reg while dprc->clks are disabled.
2. Even when runtime PM is enabled, pm_runtime_get_noresume() and
pm_runtime_active() do not serialize with rpm_suspend() under
dev->power.lock, and dc_dprc_runtime_suspend() disables dprc->clks without
calling synchronize_irq(). An interrupt arriving after rpm_suspend() has
checked usage_count == 0 can see runtime_status == RPM_ACTIVE and access
MMIO registers while dc_dprc_runtime_suspend() turns off the clocks.
3. If dc_dprc_wrap_irq_handler() increments usage_count from 1 to 2 via
pm_runtime_get_noresume() while dc_dprc_disable() concurrently calls
pm_runtime_put(dprc->dev), dc_dprc_disable() drops usage_count from 2 to 1
without triggering idle/suspend. When dc_dprc_wrap_irq_handler() then exits
via pm_runtime_put_noidle(dev), usage_count drops from 1 to 0 without
running rpm_idle(), leaving the device stuck active with its clocks left on.
Would it be safer to call devm_pm_runtime_enable() before devm_request_irq()
in dc_dprc_probe(), use pm_runtime_get_if_active() paired with
pm_runtime_put() in dc_dprc_wrap_irq_handler(), and synchronize the interrupt
in dc_dprc_runtime_suspend()?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261026-imx8-dc-prefetch-v7-0-a0b50cc4fc65@nxp.com?part=8
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 10/15] drm/imx: dc-ed: Support getting source selection
2026-10-26 10:05 ` [PATCH v7 10/15] drm/imx: dc-ed: Support getting source selection Liu Ying
2026-10-26 10:41 ` Alexander Stein
@ 2026-10-26 12:01 ` sashiko-bot
1 sibling, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-10-26 12:01 UTC (permalink / raw)
To: Liu Ying; +Cc: dri-devel, devicetree, imx, robh, conor+dt, Frank.Li
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] drm/imx: dc-ed: invalid link IDs accepted in dc_ed_pec_src_sel_get()
--
commit e94375137aef3b831a8ffe20e951e2c25abaed0f
Author: Liu Ying <victor.liu@nxp.com>
drm/imx: dc-ed: Support getting source selection
This commit adds dc_ed_pec_src_sel_get() to read and validate the ExtDst
source selection from PIXENGCFG_DYNAMIC.
> diff --git a/drivers/gpu/drm/imx/dc/dc-ed.c b/drivers/gpu/drm/imx/dc/dc-ed.c
> index de1b71315eabd..67163dca1363c 100644
> --- a/drivers/gpu/drm/imx/dc/dc-ed.c
> +++ b/drivers/gpu/drm/imx/dc/dc-ed.c
> @@ -149,6 +149,35 @@ void dc_ed_pec_src_sel(struct dc_ed *ed, enum dc_link_id src)
> }
> }
>
> +int dc_ed_pec_src_sel_get(struct dc_ed *ed, enum dc_link_id *src)
> +{
> + u32 val;
> + int ret;
> +
> + ret = regmap_read(ed->reg_pec, PIXENGCFG_DYNAMIC, &val);
> + if (ret)
> + return -EINVAL;
> +
> + switch (val) {
> + case LINK_ID_NONE:
> + case LINK_ID_CONSTFRAME0:
> + case LINK_ID_CONSTFRAME4:
> + case LINK_ID_CONSTFRAME1:
> + case LINK_ID_CONSTFRAME5:
> + case LINK_ID_FETCHWARP2:
> + case LINK_ID_FETCHLAYER0:
> + case LINK_ID_LAYERBLEND0:
> + case LINK_ID_LAYERBLEND1:
> + case LINK_ID_LAYERBLEND2:
> + case LINK_ID_LAYERBLEND3:
> + *src = val;
> + dev_dbg(ed->dev, "get source selection: 0x%02x\n", val);
> + return 0;
> + default:
> + return -EINVAL;
> + }
> +}
[Severity: Medium]
Should dc_ed_pec_src_sel_get() validate the value read from PIXENGCFG_DYNAMIC
against src_sels[] rather than accepting LINK_ID_FETCHWARP2 and
LINK_ID_FETCHLAYER0?
In drivers/gpu/drm/imx/dc/dc-ed.c, src_sels[] defines the valid source link
IDs for the ExtDst unit and is used by dc_ed_pec_src_sel():
static const enum dc_link_id src_sels[] = {
LINK_ID_NONE,
LINK_ID_CONSTFRAME0,
LINK_ID_CONSTFRAME1,
LINK_ID_CONSTFRAME4,
LINK_ID_CONSTFRAME5,
LINK_ID_LAYERBLEND3,
LINK_ID_LAYERBLEND2,
LINK_ID_LAYERBLEND1,
LINK_ID_LAYERBLEND0,
};
void dc_ed_pec_src_sel(struct dc_ed *ed, enum dc_link_id src)
{
int i;
for (i = 0; i < ARRAY_SIZE(src_sels); i++) {
if (src_sels[i] == src) {
regmap_write(ed->reg_pec, PIXENGCFG_DYNAMIC, src);
return;
}
}
}
Because fetch units only connect to LayerBlend secondary inputs (sec_sels[] in
dc-lb.c), LINK_ID_FETCHWARP2 and LINK_ID_FETCHLAYER0 are rejected by
dc_ed_pec_src_sel(). However, dc_ed_pec_src_sel_get() accepts them and returns
0 instead of -EINVAL, which could cause callers such as
dc_crtc_disable_at_boot() to treat an invalid ExtDst source as an active
pipeline.
Would it be better to iterate over src_sels[] in dc_ed_pec_src_sel_get() just
like dc_ed_pec_src_sel() does?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261026-imx8-dc-prefetch-v7-0-a0b50cc4fc65@nxp.com?part=10
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 15/15] drm/imx: dc: Use prefetch engine
2026-10-26 10:05 ` [PATCH v7 15/15] drm/imx: dc: Use prefetch engine Liu Ying
@ 2026-10-26 12:41 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-10-26 12:41 UTC (permalink / raw)
To: Liu Ying; +Cc: dri-devel, devicetree, imx, robh, conor+dt, Frank.Li
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] drm/imx: dc: mismatched plane visibility checks in DPRC lifecycle paths
- [High] drm/imx: dc: unclocked DPRC access and unused lb_sec in dc_crtc_disable_at_boot()
- [High] drm/imx: dc: dc_plane_atomic_update() starts DPRC on inactive CRTC
--
commit cdc3c29f6a09448d1496c6876f0f852d161beff3
Author: Liu Ying <victor.liu@nxp.com>
drm/imx: dc: Use prefetch engine
This commit enables prefetch engine support (DPRC and PRG) for FetchUnits
in the i.MX8 DC KMS driver, configuring and disabling the prefetch engine
alongside FetchUnits during atomic commits and boot disablement. It also
adds PRG burst size and stride fixups in the FetchUnit driver and sets
use_raw_spinlock in the ExtDst PIXENGCFG regmap config.
> diff --git a/drivers/gpu/drm/imx/dc/dc-crtc.c b/drivers/gpu/drm/imx/dc/dc-crtc.c
> index 1ca1c9229e94d..2d17afcae8f2b 100644
> --- a/drivers/gpu/drm/imx/dc/dc-crtc.c
> +++ b/drivers/gpu/drm/imx/dc/dc-crtc.c
[ ... ]
> @@ -216,13 +223,41 @@ dc_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_commit *state)
>
> enable_irq(dc_crtc->irq_ed_cont_shdload);
>
> - /* flush plane update out to display */
> - dc_ed_pec_sync_trigger(dc_crtc->ed_cont);
> + if (old_plane_state && new_plane_state)
> + disabling_plane = drm_atomic_plane_disabling(old_plane_state,
> + new_plane_state);
> +
> + if (disabling_plane) {
> + unsigned long flags;
> +
> + dc_crtc_dbg(crtc, "disabling plane\n");
> +
> + /*
> + * Don't relinquish CPU until DPRC REPEAT_EN is disabled and
> + * sync is triggered.
> + */
> + local_irq_save(flags);
> + preempt_disable();
> +
> + DC_CRTC_WAIT_FOR_FRAMEGEN_FRAME_INDEX_MOVING(dc_crtc->fg);
> + dc_dprc_disable_repeat_en(dc_plane->fu->dprc);
> + /* flush plane update out to display */
> + dc_ed_pec_sync_trigger(dc_crtc->ed_cont);
> +
> + local_irq_restore(flags);
> + preempt_enable();
> + } else {
> + /* flush plane update out to display */
> + dc_ed_pec_sync_trigger(dc_crtc->ed_cont);
> + }
>
> DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_cont_shdload_done);
>
> disable_irq(dc_crtc->irq_ed_cont_shdload);
>
> + if (disabling_plane)
> + dc_dprc_disable(dc_plane->fu->dprc);
> +
> DC_CRTC_CHECK_FRAMEGEN_FIFO(dc_crtc->fg);
>
> drm_dev_exit(idx);
[ ... ]
> @@ -338,14 +392,29 @@ dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
> {
> struct drm_crtc_state *new_crtc_state =
> drm_atomic_get_new_crtc_state(state, crtc);
> + struct drm_crtc_state *old_crtc_state =
> + drm_atomic_get_old_crtc_state(state, crtc);
> struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
> + struct dc_plane *dc_plane = to_dc_plane(crtc->primary);
> struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
> int idx;
>
> if (!drm_dev_enter(crtc->dev, &idx))
> goto out;
>
> - __dc_crtc_disable_fg(crtc);
> + enable_irq(dc_crtc->irq_dec_seqcomplete);
> +
> + if (old_crtc_state->plane_mask)
> + dc_crtc_disable_fg_along_with_dprc_repeat_en(crtc);
> + else
> + dc_fg_disable(dc_crtc->fg);
> +
> + DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_seqcomplete_done);
> + disable_irq(dc_crtc->irq_dec_seqcomplete);
> +
> + if (old_crtc_state->plane_mask)
> + dc_dprc_disable(dc_plane->fu->dprc);
[Severity: High]
What happens in dc_crtc_atomic_flush(), dc_crtc_atomic_disable(), and
dc_plane_atomic_update() when a primary plane has a valid fb and crtc,
but is positioned completely off-screen so that plane_state->visible is
false?
In dc_plane_atomic_check(), drm_atomic_helper_check_plane_state() is
called with can_position set to true. When the plane is off-screen,
plane_state->visible becomes false while plane_state->fb and
plane_state->crtc remain set, and crtc_state->plane_mask stays non-zero.
In dc_plane_atomic_update(), when !new_state->visible, the code calls
__dc_plane_atomic_disable(plane) and returns early before calling
dc_dprc_configure():
drivers/gpu/drm/imx/dc/dc-plane.c:dc_plane_atomic_update() {
...
bool needs_modeset = drm_atomic_crtc_needs_modeset(new_crtc_state);
bool prefetch_start = needs_modeset || !old_state->fb;
...
if (!new_state->visible) {
__dc_plane_atomic_disable(plane);
goto out;
}
...
dc_dprc_configure(fu->dprc, new_state->crtc->index, src_w, src_h,
fb->pitches[0], fb->format, baseaddr, prefetch_start);
}
However, the DPRC start and stop conditions check old_state->fb,
drm_atomic_plane_disabling() (which checks old_plane_state->crtc &&
!new_plane_state->crtc), and old_crtc_state->plane_mask instead of plane
visibility:
1. If a visible plane moves off-screen (old_plane_state->visible is true,
new_plane_state->visible is false, new_plane_state->crtc is non-NULL),
__dc_plane_atomic_disable() disables the FetchUnit, but
drm_atomic_plane_disabling() in dc_crtc_atomic_flush() evaluates to
false. Doesn't this skip dc_dprc_disable_repeat_en() and
dc_dprc_disable(), leaving DPRC REPEAT_EN running and leaking the runtime
PM references on dprc->dev and prg->dev?
2. If an off-screen plane moves back on-screen without a modeset
(old_state->visible is false with old_state->fb non-NULL, and
new_state->visible is true), prefetch_start in dc_plane_atomic_update()
is false. Won't dc_dprc_configure() and dc_prg_configure() skip
pm_runtime_resume_and_get(), write to unclocked DPRC and PRG registers
while runtime-suspended, and fail to start the DPRC and PRG?
3. If an off-screen plane (old_plane_state->visible is false,
old_plane_state->crtc is non-NULL) is subsequently disabled via
dc_crtc_atomic_flush() or its CRTC is disabled via
dc_crtc_atomic_disable(), dc_dprc_disable_repeat_en() and
dc_dprc_disable() will be called even though DPRC was never started.
Won't that access unclocked DPRC/PRG registers and underflow the runtime
PM usage counts on dprc->dev and prg->dev?
> @@ -371,7 +440,10 @@ dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
> void dc_crtc_disable_at_boot(struct drm_crtc *crtc)
> {
> struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
> + struct dc_plane *dc_plane = to_dc_plane(crtc->primary);
> struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
> + enum dc_link_id ed_src, lb_sec;
> + bool disable_dprc = false;
> int ret;
>
> ret = pm_runtime_resume_and_get(dc_crtc->de->dev);
> @@ -381,16 +453,57 @@ void dc_crtc_disable_at_boot(struct drm_crtc *crtc)
> return;
> }
>
> + ret = pm_runtime_resume_and_get(dc_drm->pe->dev);
> + if (ret) {
> + dc_crtc_err(crtc, "failed to get DC pixel engine RPM: %d\n",
> + ret);
> + goto out1;
> + }
> +
> if (!dc_fg_wait_for_frame_index_moving(dc_crtc->fg)) {
> dc_crtc_dbg(crtc, "FrameGen frame index isn't moving\n");
> - goto out;
> + goto out2;
> + }
> +
> + ret = dc_ed_pec_src_sel_get(dc_crtc->ed_cont, &ed_src);
> + if (ret) {
> + dc_crtc_err(crtc, "failed to get content ExtDst's source\n");
> + goto out2;
> + }
> +
> + if (ed_src == LINK_ID_CONSTFRAME0 || ed_src == LINK_ID_CONSTFRAME1)
> + goto disable;
> +
> + ret = dc_lb_pec_dynamic_sec_sel_get(dc_plane->lb, &lb_sec);
> + if (ret) {
> + dc_crtc_err(crtc,
> + "failed to get primary plane LayerBlend secondary source\n");
> + goto out2;
> }
>
> - dc_crtc_dbg(crtc, "disabling FrameGen at boot\n");
> - __dc_crtc_disable_fg(crtc);
> + disable_dprc = true;
> +
> +disable:
> + enable_irq(dc_crtc->irq_dec_seqcomplete);
> +
> + if (disable_dprc) {
> + dc_crtc_dbg(crtc, "disabling FrameGen along with DPRC REPEAT_EN at boot\n");
> + dc_crtc_disable_fg_along_with_dprc_repeat_en(crtc);
> + } else {
> + dc_crtc_dbg(crtc, "disabling FrameGen at boot\n");
> + dc_fg_disable(dc_crtc->fg);
> + }
> +
> + DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_seqcomplete_done);
> + disable_irq(dc_crtc->irq_dec_seqcomplete);
>
> if (!dc_drv_crtc_was_enabled(dc_drm, drm_crtc_index(crtc)))
> - goto out;
> + goto out2;
> +
> + if (disable_dprc) {
> + dc_crtc_dbg(crtc, "disabling DPRC at boot\n");
> + dc_dprc_disable_at_boot(dc_plane->fu->dprc);
> + }
[Severity: High]
In dc_crtc_disable_at_boot(), lb_sec is read by
dc_lb_pec_dynamic_sec_sel_get() but never inspected afterwards, and
dc_ed_pec_src_sel_get() and dc_lb_pec_dynamic_sec_sel_get() both return 0
when the hardware register is set to LINK_ID_NONE:
drivers/gpu/drm/imx/dc/dc-lb.c:dc_lb_pec_dynamic_sec_sel_get() {
...
switch (val) {
case LINK_ID_NONE:
case LINK_ID_FETCHWARP2:
case LINK_ID_FETCHLAYER0:
*sec = val;
dev_dbg(lb->dev, "get secondary input selection: 0x%02x\n", val);
return 0;
...
}
If ed_src or lb_sec is LINK_ID_NONE, disable_dprc is still set to true.
Furthermore, at the disable label,
dc_crtc_disable_fg_along_with_dprc_repeat_en() writes to DPRC
SYSTEM_CTRL0 + CLR via dc_dprc_disable_repeat_en() before
dc_drv_crtc_was_enabled() is checked and without holding a runtime PM
reference on dprc->dev.
If the bootloader enabled FrameGen without using the DPRC (leaving the
DPRC power domain or clocks off, or leaving lb_sec as LINK_ID_NONE),
could calling dc_dprc_disable_repeat_en() or dc_dprc_disable_at_boot()
access unclocked DPRC/PRG registers and trigger a bus abort / SError
during boot, or disable already-disabled DPRC/PRG clocks?
> diff --git a/drivers/gpu/drm/imx/dc/dc-plane.c b/drivers/gpu/drm/imx/dc/dc-plane.c
> index 42c0a46281cb1..3a9c7548f8279 100644
> --- a/drivers/gpu/drm/imx/dc/dc-plane.c
> +++ b/drivers/gpu/drm/imx/dc/dc-plane.c
[ ... ]
> @@ -158,6 +180,12 @@ dc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state)
> {
> struct drm_plane_state *new_state =
> drm_atomic_get_new_plane_state(state, plane);
> + struct drm_plane_state *old_state =
> + drm_atomic_get_old_plane_state(state, plane);
> + struct drm_crtc_state *new_crtc_state =
> + drm_atomic_get_new_crtc_state(state, new_state->crtc);
> + bool needs_modeset = drm_atomic_crtc_needs_modeset(new_crtc_state);
> + bool prefetch_start = needs_modeset || !old_state->fb;
> struct dc_plane *dplane = to_dc_plane(plane);
> struct drm_framebuffer *fb = new_state->fb;
> const struct dc_fu_ops *fu_ops;
> @@ -184,7 +212,8 @@ dc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state)
>
> fu_ops->set_layerblend(fu, lb);
> fu_ops->set_burstlength(fu, baseaddr);
> - fu_ops->set_src_stride(fu, DC_FETCHUNIT_FRAC0, fb->pitches[0]);
> + fu_ops->set_src_stride(fu, DC_FETCHUNIT_FRAC0, src_w,
> + fb->format->cpp[0] * 8, baseaddr);
> fu_ops->set_src_buf_dimensions(fu, DC_FETCHUNIT_FRAC0, src_w, src_h);
> fu_ops->set_fmt(fu, DC_FETCHUNIT_FRAC0, fb->format);
> fu_ops->set_framedimensions(fu, src_w, src_h);
> @@ -193,6 +222,9 @@ dc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state)
>
> dc_plane_dbg(plane, "uses %s\n", fu_ops->get_name(fu));
>
> + dc_dprc_configure(fu->dprc, new_state->crtc->index, src_w, src_h,
> + fb->pitches[0], fb->format, baseaddr, prefetch_start);
[Severity: High]
Can dc_plane_atomic_update() re-enable DPRC and access suspended Pixel
Engine registers when a CRTC with an active primary plane transitions to
inactive (such as DPMS OFF where new_crtc_state->enable remains true but
new_crtc_state->active becomes false)?
Because the driver uses the default drm_atomic_helper_commit_tail()
without DRM_PLANE_COMMIT_ACTIVE_ONLY, a DPMS OFF commit runs:
1. drm_atomic_helper_commit_modeset_disables() ->
dc_crtc_atomic_disable(), which disables FrameGen, calls
dc_dprc_disable(), and drops runtime PM on dc_drm->pe->dev.
2. drm_atomic_helper_commit_planes(dev, state, 0), where
dc_crtc_atomic_begin() returns early because !new_crtc_state->active:
drivers/gpu/drm/imx/dc/dc-crtc.c:dc_crtc_atomic_begin() {
...
if (!drm_atomic_crtc_needs_modeset(new_crtc_state) ||
!new_crtc_state->active)
return;
...
}
3. dc_plane_atomic_update() then runs on the inactive CRTC. Since
new_crtc_state->active_changed is true,
drm_atomic_crtc_needs_modeset(new_crtc_state) is true, so prefetch_start
is set to true.
Doesn't this cause dc_plane_atomic_update() to write to FetchUnit,
LayerBlend, and ExtDst registers while dc_drm->pe->dev is
runtime-suspended, and call dc_dprc_configure(..., true), which resumes
and starts DPRC and PRG with RUN_EN | REPEAT_EN while the CRTC is disabled
and leaks their runtime PM references?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261026-imx8-dc-prefetch-v7-0-a0b50cc4fc65@nxp.com?part=15
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 03/15] drm/imx: dc-plane: Disable plane when it is invisible
2026-10-26 10:05 ` [PATCH v7 03/15] drm/imx: dc-plane: Disable plane when it is invisible Liu Ying
@ 2026-10-29 9:28 ` Laurentiu Palcu
0 siblings, 0 replies; 27+ messages in thread
From: Laurentiu Palcu @ 2026-10-29 9:28 UTC (permalink / raw)
To: Liu Ying
Cc: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov,
dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein
On Mon, Oct 26, 2026 at 06:05:08PM +0800, Liu Ying wrote:
> Fetch unit backing plane doesn't support invisible plane because
> SOURCEBUFFERDIMENSION and FRAMEDIMENSIONS registers only accept
> dimension for visible frame. Disable plane when it is invisible
> to avoid wrongly programming the two registers with invisible
> frame.
>
> Fixes: 711a3b878366 ("drm/imx: Add i.MX8qxp Display Controller KMS")
> Signed-off-by: Liu Ying <victor.liu@nxp.com>
Reviewed-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
--
Thanks,
Laurentiu
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 05/15] drm/imx: dc: Flag CRTC status in GERNERALPURPOSE register
2026-10-26 10:05 ` [PATCH v7 05/15] drm/imx: dc: Flag CRTC status in GERNERALPURPOSE register Liu Ying
@ 2026-10-29 9:28 ` Laurentiu Palcu
0 siblings, 0 replies; 27+ messages in thread
From: Laurentiu Palcu @ 2026-10-29 9:28 UTC (permalink / raw)
To: Liu Ying
Cc: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov,
dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein
On Mon, Oct 26, 2026 at 06:05:10PM +0800, Liu Ying wrote:
> Map Display Controller's register GERNERALPURPOSE at offset 0x100 and
> create an MMIO regmap to access the register. Create helpers
> dc_drv_flag_crtc_{enable,disable}() with the 2 LSB bits in the register
> to flag the CRTC enablement/disablement status. Also, create a helper
> dc_drv_crtc_was_enabled() so that the CRTC driver can use it to check
> if the CRTC was enabled by a previous boot or not. This is a preparation
> for an upcoming patch to disable display controller at boot if the CRTC
> was enabled by the driver during a previous boot instead of a bootloader.
>
> Signed-off-by: Liu Ying <victor.liu@nxp.com>
> --
> v7:
> - Newly introduced in v7 as a preparation for fixing Sashiko AI bot's
> comment on an upcoming patch. (Sashiko AI bot)
> ---
> drivers/gpu/drm/imx/dc/dc-drv.c | 76 +++++++++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/imx/dc/dc-drv.h | 7 ++++
> 2 files changed, 83 insertions(+)
>
> diff --git a/drivers/gpu/drm/imx/dc/dc-drv.c b/drivers/gpu/drm/imx/dc/dc-drv.c
> index 13795a2ad735..ac1321adf1c8 100644
> --- a/drivers/gpu/drm/imx/dc/dc-drv.c
> +++ b/drivers/gpu/drm/imx/dc/dc-drv.c
> @@ -13,6 +13,7 @@
> #include <linux/platform_device.h>
> #include <linux/pm.h>
> #include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
>
> #include <drm/clients/drm_client_setup.h>
> #include <drm/drm_atomic_helper.h>
> @@ -28,9 +29,21 @@
> #include "dc-drv.h"
> #include "dc-pe.h"
>
> +/* General Purpose register offset used to flag driver usage. */
> +#define GENERALPURPOSE 0x100
> +#define CRTC_ENABLED(x) BIT(x)
> +
> struct dc_priv {
> struct drm_device *drm;
> struct clk *clk_cfg;
> + struct regmap *gp_regmap;
> +};
> +
> +static const struct regmap_config dc_gp_regmap_config = {
> + .reg_bits = 32,
> + .reg_stride = 4,
> + .val_bits = 32,
> + .max_register = 0,
> };
>
> DEFINE_DRM_GEM_DMA_FOPS(dc_drm_driver_fops);
> @@ -156,6 +169,8 @@ static int dc_probe(struct platform_device *pdev)
> {
> struct component_match *match = NULL;
> struct dc_priv *priv;
> + struct resource *r;
> + void __iomem *base;
> int ret;
>
> priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> @@ -167,6 +182,28 @@ static int dc_probe(struct platform_device *pdev)
> return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cfg),
> "failed to get cfg clock\n");
>
> + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!r)
> + return -EINVAL;
> +
> + if (!devm_request_mem_region(&pdev->dev, r->start + GENERALPURPOSE, 4,
> + dev_name(&pdev->dev))) {
> + dev_err(&pdev->dev, "failed to request memory region\n");
> + return -EBUSY;
Use 'return dev_err_probe()'.
> + }
> +
> + base = devm_ioremap(&pdev->dev, r->start + GENERALPURPOSE, 4);
> + if (!base) {
> + dev_err(&pdev->dev, "failed to do ioremap\n");
> + return -ENOMEM;
Use 'return dev_err_probe()'.
> + }
> +
> + priv->gp_regmap = devm_regmap_init_mmio(&pdev->dev, base,
> + &dc_gp_regmap_config);
> + if (IS_ERR(priv->gp_regmap))
> + return dev_err_probe(&pdev->dev, PTR_ERR(priv->gp_regmap),
> + "failed to init General Purpose regmap\n");
> +
> dev_set_drvdata(&pdev->dev, priv);
>
> ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
> @@ -196,6 +233,45 @@ static void dc_remove(struct platform_device *pdev)
> component_master_del(&pdev->dev, &dc_drm_ops);
> }
>
> +void dc_drv_set_crtc_enable(struct dc_drm_device *dc_drm,
> + unsigned int crtc_index)
> +{
> + struct dc_priv *priv = dev_get_drvdata(dc_drm->base.dev);
> +
> + if (WARN_ON(crtc_index > 1))
> + return;
> +
> + regmap_set_bits(priv->gp_regmap, 0, CRTC_ENABLED(crtc_index));
> +}
> +
> +void dc_drv_set_crtc_disable(struct dc_drm_device *dc_drm,
> + unsigned int crtc_index)
> +{
> + struct dc_priv *priv = dev_get_drvdata(dc_drm->base.dev);
> +
> + if (WARN_ON(crtc_index > 1))
> + return;
> +
> + regmap_clear_bits(priv->gp_regmap, 0, CRTC_ENABLED(crtc_index));
> +}
> +
> +bool dc_drv_crtc_was_enabled(struct dc_drm_device *dc_drm,
I believe it would be better to rename this one to 'dc_drv_crtc_is_enabled'.
After all, you're checking whether the crtc is enabled *now*. Reading those
bits in GENERALPURPOSE show the current status, not a past one.
With the above addressed:
Reviewed-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
--
Thanks,
Laurentiu
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 06/15] drm/imx: dc-crtc: Disable at boot
2026-10-26 10:05 ` [PATCH v7 06/15] drm/imx: dc-crtc: Disable at boot Liu Ying
2026-10-26 11:18 ` sashiko-bot
@ 2026-10-29 9:30 ` Laurentiu Palcu
1 sibling, 0 replies; 27+ messages in thread
From: Laurentiu Palcu @ 2026-10-29 9:30 UTC (permalink / raw)
To: Liu Ying
Cc: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov,
dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Frank Li
On Mon, Oct 26, 2026 at 06:05:11PM +0800, Liu Ying wrote:
> CRTC(s) could still be running after the DRM device is unplugged by
> calling drm_dev_unplug(), because the CRTC disablement logic is
> protected and bypassed by the drm_dev_enter()/drm_dev_exit() pair.
> Hence, Pixel Engine's AXI clock use count(managed by Pixel Engine
> driver's runtime PM) and pixel clock use count could be inbalanced
> after removing and re-installing the driver module. To fix this,
> add a helper dc_crtc_disable_at_boot() and call it to properly
> disable all CRTCs before advertising DRM device to user-space by
> calling drm_dev_register().
>
> Fixes: 711a3b878366 ("drm/imx: Add i.MX8qxp Display Controller KMS")
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Signed-off-by: Liu Ying <victor.liu@nxp.com>
> --
> v7:
> - Check CRTC status to ensure that clocks were enabled by a previous boot
> before disabling them in dc_crtc_disable_at_boot(). (Sashiko AI bot)
> - Modify debug message in dc_crtc_disable_at_boot().
>
> v6:
> - Rebase onto the latest drm-misc-next and resolve conflicts.
>
> v3:
> - Collect Frank's R-b tag.
> ---
> drivers/gpu/drm/imx/dc/dc-crtc.c | 67 ++++++++++++++++++++++++++++++++++------
> drivers/gpu/drm/imx/dc/dc-drv.c | 5 +++
> drivers/gpu/drm/imx/dc/dc-drv.h | 3 ++
> 3 files changed, 65 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/gpu/drm/imx/dc/dc-crtc.c b/drivers/gpu/drm/imx/dc/dc-crtc.c
> index 0d64186a9901..c7dbe5f500b4 100644
> --- a/drivers/gpu/drm/imx/dc/dc-crtc.c
> +++ b/drivers/gpu/drm/imx/dc/dc-crtc.c
> @@ -227,6 +227,7 @@ dc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
> struct drm_crtc_state *new_crtc_state =
> drm_atomic_get_new_crtc_state(state, crtc);
> struct drm_display_mode *adj = &new_crtc_state->adjusted_mode;
> + struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
> struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
> enum dc_link_id cf_link;
> int idx, ret;
> @@ -287,29 +288,37 @@ dc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
>
> DC_CRTC_CHECK_FRAMEGEN_FIFO(dc_crtc->fg);
>
> + dc_drv_set_crtc_enable(dc_drm, drm_crtc_index(crtc));
> +
> drm_dev_exit(idx);
>
> out:
> dc_crtc_queue_state_event(new_crtc_state);
> }
>
> -static void
> -dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
> +static inline void __dc_crtc_disable_fg(struct drm_crtc *crtc)
> {
> - struct drm_crtc_state *new_crtc_state =
> - drm_atomic_get_new_crtc_state(state, crtc);
> - struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
> struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
> - int idx;
> -
> - if (!drm_dev_enter(crtc->dev, &idx))
> - goto out;
>
> enable_irq(dc_crtc->irq_dec_seqcomplete);
> dc_fg_disable(dc_crtc->fg);
> DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_seqcomplete_done);
> disable_irq(dc_crtc->irq_dec_seqcomplete);
> +}
>
> +static void
> +dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
> +{
> + struct drm_crtc_state *new_crtc_state =
> + drm_atomic_get_new_crtc_state(state, crtc);
> + struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
> + struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
> + int idx;
> +
> + if (!drm_dev_enter(crtc->dev, &idx))
> + goto out;
> +
> + __dc_crtc_disable_fg(crtc);
> dc_fg_disable_clock(dc_crtc->fg);
>
> /* request pixel engine power-off as plane is off too */
> @@ -318,8 +327,9 @@ dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
> /* request display engine power-off when CRTC is disabled */
> pm_runtime_put(dc_crtc->de->dev);
>
> - drm_dev_exit(idx);
> + dc_drv_set_crtc_disable(dc_drm, drm_crtc_index(crtc));
>
> + drm_dev_exit(idx);
> out:
> drm_crtc_vblank_off(crtc);
>
> @@ -331,6 +341,43 @@ dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
> spin_unlock_irq(&crtc->dev->event_lock);
> }
>
> +void dc_crtc_disable_at_boot(struct drm_crtc *crtc)
Shouldn't this function return int in order to propagate a potential error to
dc_drm_bind()? For example, if pm_runtime_resume_and_get() fails, or frame
index isn't moving, wouldn't it be better for dc_drm_bind() to fail as well?
--
Thanks,
Laurentiu
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 07/15] drm/imx: dc: Add PRG support
2026-10-26 10:05 ` [PATCH v7 07/15] drm/imx: dc: Add PRG support Liu Ying
2026-10-26 11:32 ` sashiko-bot
@ 2026-10-29 9:30 ` Laurentiu Palcu
1 sibling, 0 replies; 27+ messages in thread
From: Laurentiu Palcu @ 2026-10-29 9:30 UTC (permalink / raw)
To: Liu Ying
Cc: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov,
dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Frank Li
On Mon, Oct 26, 2026 at 06:05:12PM +0800, Liu Ying wrote:
> Display Prefetch Resolve Gasket(PRG) is a part of a FetchUnit's
> prefetch engine. It sits between a Display Prefetch Resolve
> Channel(DPRC) and a FetchUnit. Add a platform driver to support
> the PRG.
>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Acked-by: Maxime Ripard <mripard@kernel.org>
> Signed-off-by: Liu Ying <victor.liu@nxp.com>
> ---
> v7:
> - Collect Maxime's A-b tag.
> - Return 128B burst size from dc_prg_burst_size_fixup() when baddr == 0.
> This avoids undefined behavior of __ffs(0). (Sashiko AI bot)
> - Initialize and declare prg_node at the same time. (Sashiko AI bot)
> - Add device link between PRG's consumer and PRG. (Sashiko AI bot)
>
> v6:
> - Include "dc-drv.h" to avoid build warning with C=1.
> - Fix PRG BYPASS programming.
>
> v4:
> - Sort bits and bitfields in descending order for register PRG_CTRL.
>
> v2:
> - Manage clocks with bulk interfaces. (Frank)
> - Collect Frank's R-b tag.
> ---
> drivers/gpu/drm/imx/dc/Makefile | 2 +-
> drivers/gpu/drm/imx/dc/dc-drv.c | 1 +
> drivers/gpu/drm/imx/dc/dc-drv.h | 1 +
> drivers/gpu/drm/imx/dc/dc-prg.c | 314 ++++++++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/imx/dc/dc-prg.h | 35 +++++
> 5 files changed, 352 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/imx/dc/Makefile b/drivers/gpu/drm/imx/dc/Makefile
> index b9d33c074984..e3a06ee3ce1a 100644
> --- a/drivers/gpu/drm/imx/dc/Makefile
> +++ b/drivers/gpu/drm/imx/dc/Makefile
> @@ -1,7 +1,7 @@
> # SPDX-License-Identifier: GPL-2.0
>
> imx8-dc-drm-objs := dc-cf.o dc-crtc.o dc-de.o dc-drv.o dc-ed.o dc-fg.o dc-fl.o \
> - dc-fu.o dc-fw.o dc-ic.o dc-kms.o dc-lb.o dc-pe.o \
> + dc-fu.o dc-fw.o dc-ic.o dc-kms.o dc-lb.o dc-pe.o dc-prg.o \
> dc-plane.o dc-tc.o
>
> obj-$(CONFIG_DRM_IMX8_DC) += imx8-dc-drm.o
> diff --git a/drivers/gpu/drm/imx/dc/dc-drv.c b/drivers/gpu/drm/imx/dc/dc-drv.c
> index 09387d62cc0b..1d01f896e427 100644
> --- a/drivers/gpu/drm/imx/dc/dc-drv.c
> +++ b/drivers/gpu/drm/imx/dc/dc-drv.c
> @@ -351,6 +351,7 @@ static struct platform_driver * const dc_drivers[] = {
> &dc_ic_driver,
> &dc_lb_driver,
> &dc_pe_driver,
> + &dc_prg_driver,
> &dc_tc_driver,
> &dc_driver,
> };
> diff --git a/drivers/gpu/drm/imx/dc/dc-drv.h b/drivers/gpu/drm/imx/dc/dc-drv.h
> index 1ec9304329eb..27ef2c552549 100644
> --- a/drivers/gpu/drm/imx/dc/dc-drv.h
> +++ b/drivers/gpu/drm/imx/dc/dc-drv.h
> @@ -81,6 +81,7 @@ extern struct platform_driver dc_fw_driver;
> extern struct platform_driver dc_ic_driver;
> extern struct platform_driver dc_lb_driver;
> extern struct platform_driver dc_pe_driver;
> +extern struct platform_driver dc_prg_driver;
> extern struct platform_driver dc_tc_driver;
>
> static inline int dc_subdev_get_id(const struct dc_subdev_info *info,
> diff --git a/drivers/gpu/drm/imx/dc/dc-prg.c b/drivers/gpu/drm/imx/dc/dc-prg.c
> new file mode 100644
> index 000000000000..43df5683b24d
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/dc/dc-prg.c
> @@ -0,0 +1,314 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright 2025 NXP
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/bitops.h>
This header can be removed.
> +#include <linux/bits.h>
> +#include <linux/cleanup.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
This one too.
[...]
> +
> +void dc_prg_disable(struct dc_prg *prg)
> +{
> + regmap_write(prg->reg, PRG_CTRL, BYPASS);
> +
> + pm_runtime_put(prg->dev);
I have a question about this. I see Sashiko also flagged an issue regarding the
unconditional put when the pm_runtime_resume_and_get() fails in
dc_prg_configure(). The resume is, probably, unlikely to fail. But, I see a
situation where the dc_prg_configure() is never called: when the plane is
invisible. The dc_prg_disable() would still be called in flush() when the plane
is disabled, would it not?
> +}
> +
> +void dc_prg_disable_at_boot(struct dc_prg *prg)
> +{
> + regmap_write(prg->reg, PRG_CTRL, BYPASS);
> +
> + clk_bulk_disable_unprepare(prg->num_clks, prg->clks);
> +}
> +
> +static unsigned int dc_prg_burst_size_fixup(dma_addr_t baddr)
> +{
> + unsigned int burst_size;
> +
> + if (baddr == 0)
> + return 128U;
> +
> + burst_size = 1 << __ffs(baddr);
> + burst_size = round_up(burst_size, 8);
> + burst_size = min(burst_size, 128U);
> +
> + return burst_size;
> +}
> +
> +static unsigned int
> +dc_prg_stride_fixup(unsigned int stride, unsigned int burst_size)
> +{
> + return round_up(stride, burst_size);
> +}
> +
> +void dc_prg_configure(struct dc_prg *prg,
> + unsigned int width, unsigned int height,
> + unsigned int stride, unsigned int bits_per_pixel,
> + dma_addr_t baddr, bool start)
> +{
> + struct device *dev = prg->dev;
> + unsigned int burst_size;
> + u32 val;
> + int ret;
> +
> + if (start) {
> + ret = pm_runtime_resume_and_get(dev);
> + if (ret < 0) {
> + dev_err(dev, "failed to get RPM: %d\n", ret);
> + return;
> + }
> + }
> +
> + burst_size = dc_prg_burst_size_fixup(baddr);
> +
> + stride = dc_prg_stride_fixup(stride, burst_size);
> +
> + regmap_write(prg->reg, PRG_STRIDE, STRIDE(stride));
> + regmap_write(prg->reg, PRG_WIDTH, WIDTH(width));
> + regmap_write(prg->reg, PRG_HEIGHT, HEIGHT(height));
> + regmap_write(prg->reg, PRG_OFFSET, 0);
> + regmap_write(prg->reg, PRG_BADDR, baddr);
> +
> + val = SHADOW_LOAD_MODE | SC_DATA_TYPE_8BIT | HANDSHAKE_MODE_4LINES;
> +
> + switch (bits_per_pixel) {
> + case 32:
> + val |= DES_DATA_TYPE_32BPP;
> + break;
> + case 24:
> + val |= DES_DATA_TYPE_24BPP;
> + break;
> + case 16:
> + val |= DES_DATA_TYPE_16BPP;
> + break;
> + case 8:
> + val |= DES_DATA_TYPE_8BPP;
> + break;
> + }
> +
> + /* no shadow for the first frame */
> + val |= start ? BYPASS : SHADOW_EN;
> +
> + regmap_write(prg->reg, PRG_CTRL, val);
> +}
> +
> +void dc_prg_reg_update(struct dc_prg *prg)
> +{
> + regmap_write(prg->reg, PRG_REG_UPDATE, REG_UPDATE);
> +}
> +
> +void dc_prg_shadow_enable(struct dc_prg *prg)
> +{
> + regmap_write(prg->reg, PRG_CTRL + SET, SHADOW_EN);
> +}
> +
> +bool dc_prg_stride_supported(struct dc_prg *prg,
It looks like you're not using prg. Can it be removed?
--
Thanks,
Laurentiu
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 08/15] drm/imx: dc: Add DPR channel support
2026-10-26 10:05 ` [PATCH v7 08/15] drm/imx: dc: Add DPR channel support Liu Ying
2026-10-26 11:52 ` sashiko-bot
@ 2026-10-29 9:31 ` Laurentiu Palcu
1 sibling, 0 replies; 27+ messages in thread
From: Laurentiu Palcu @ 2026-10-29 9:31 UTC (permalink / raw)
To: Liu Ying
Cc: Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dmitry Baryshkov,
dri-devel, devicetree, imx, linux-arm-kernel, linux-kernel,
Marek Vasut, Alexander Stein, Frank Li
On Mon, Oct 26, 2026 at 06:05:13PM +0800, Liu Ying wrote:
> Display Prefetch Resolve Channel(DPRC) is a part of a prefetch engine.
> It fetches display data, transforms it to linear format and stores it
> to DPRC's RTRAM. PRG, as the other part of a prefetch engine, acts as
> a gasket between the RTRAM controller and a FetchUnit. Add a platform
> driver to support the DPRC.
>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Acked-by: Maxime Ripard <mripard@kernel.org>
> Signed-off-by: Liu Ying <victor.liu@nxp.com>
> ---
> v7:
> - Collect Maxime's A-b tag.
> - Fix dc_dprc_regmap_write_ranges[]. (Sashiko AI bot)
> - Fix dc_dprc_wrap_irq_handler() return value. (Sashiko AI bot)
> - Check runtime PM status in dc_dprc_wrap_irq_handler(). (Sashiko AI bot)
> - Fix DPU_DRPC_MAX_STRIDE macro. (Sashiko AI bot)
> - Support deferral probe if platform_get_irq_byname() fails. (Sashiko AI bot)
> - Avoid dangling prg->dprc by calling dc_prg_set_dprc() properly.
> (Sashiko AI bot)
> - Add dc_dprc_get() helper for consumer device to get DPRC properly.
> - Set use_raw_spinlock flag in regmap configuration to avoid a potential
> hard lockup because dc_dprc_disable_repeat_en() is supposed to be called
> with local irq and preempt disabled.
>
> v6:
> - Include "dc-drv.h" to avoid build warning with C=1.
>
> v3:
> - Call dc_dprc_enable() only when start == true. (Frank)
> - Collect Frank's R-b tag.
>
> v2:
> - Manage clocks with bulk interfaces. (Frank)
> - Sort variables in probe function in reverse Christmas tree fashion. (Frank)
> ---
> drivers/gpu/drm/imx/dc/Kconfig | 1 +
> drivers/gpu/drm/imx/dc/Makefile | 6 +-
> drivers/gpu/drm/imx/dc/dc-dprc.c | 522 +++++++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/imx/dc/dc-dprc.h | 37 +++
> drivers/gpu/drm/imx/dc/dc-drv.c | 1 +
> drivers/gpu/drm/imx/dc/dc-drv.h | 1 +
> drivers/gpu/drm/imx/dc/dc-prg.c | 12 +
> drivers/gpu/drm/imx/dc/dc-prg.h | 4 +
> 8 files changed, 581 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/imx/dc/Kconfig b/drivers/gpu/drm/imx/dc/Kconfig
> index 415993207f2e..507dc9a92d96 100644
> --- a/drivers/gpu/drm/imx/dc/Kconfig
> +++ b/drivers/gpu/drm/imx/dc/Kconfig
> @@ -1,6 +1,7 @@
> config DRM_IMX8_DC
> tristate "Freescale i.MX8 Display Controller Graphics"
> depends on DRM && COMMON_CLK && OF && (ARCH_MXC || COMPILE_TEST)
> + depends on IMX_SCU
> select DRM_CLIENT_SELECTION
> select DRM_GEM_DMA_HELPER
> select DRM_KMS_HELPER
> diff --git a/drivers/gpu/drm/imx/dc/Makefile b/drivers/gpu/drm/imx/dc/Makefile
> index e3a06ee3ce1a..fd5d62783971 100644
> --- a/drivers/gpu/drm/imx/dc/Makefile
> +++ b/drivers/gpu/drm/imx/dc/Makefile
> @@ -1,7 +1,7 @@
> # SPDX-License-Identifier: GPL-2.0
>
> -imx8-dc-drm-objs := dc-cf.o dc-crtc.o dc-de.o dc-drv.o dc-ed.o dc-fg.o dc-fl.o \
> - dc-fu.o dc-fw.o dc-ic.o dc-kms.o dc-lb.o dc-pe.o dc-prg.o \
> - dc-plane.o dc-tc.o
> +imx8-dc-drm-objs := dc-cf.o dc-crtc.o dc-de.o dc-dprc.o dc-drv.o dc-ed.o \
> + dc-fg.o dc-fl.o dc-fu.o dc-fw.o dc-ic.o dc-kms.o dc-lb.o \
> + dc-pe.o dc-prg.o dc-plane.o dc-tc.o
>
> obj-$(CONFIG_DRM_IMX8_DC) += imx8-dc-drm.o
> diff --git a/drivers/gpu/drm/imx/dc/dc-dprc.c b/drivers/gpu/drm/imx/dc/dc-dprc.c
> new file mode 100644
> index 000000000000..a8ddcd117a52
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/dc/dc-dprc.c
> @@ -0,0 +1,522 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright 2025 NXP
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +#include <linux/cleanup.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/firmware/imx/svc/misc.h>
> +#include <linux/interrupt.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
You can remove the include above. You're not using anything from module.h
AFAIS.
[...]
> +
> +bool dc_dprc_rtram_width_supported(struct dc_dprc *dprc, unsigned int width)
You could probably get rid of dprc as well. You're not using it.
> +{
> + return width <= DPU_DPRC_MAX_RTRAM_WIDTH;
> +}
> +
> +bool dc_dprc_stride_supported(struct dc_dprc *dprc,
> + unsigned int stride, unsigned int width,
> + const struct drm_format_info *format,
> + dma_addr_t baddr)
> +{
> + unsigned int prg_stride = width * format->cpp[0];
> +
> + if (stride > DPU_DRPC_MAX_STRIDE)
> + return false;
> +
> + if (!dc_prg_stride_supported(dprc->prg, prg_stride, baddr))
> + return false;
> +
> + return true;
> +}
> +
> +struct dc_dprc *dc_dprc_get(struct device *dev, struct dc_dprc *dprc)
> +{
> + struct dc_dprc *iter;
> +
> + guard(mutex)(&dc_dprc_list_mutex);
> + list_for_each_entry(iter, &dc_dprc_list, list) {
> + if (iter == dprc) {
> + device_link_add(dev, dprc->dev,
> + DL_FLAG_AUTOREMOVE_CONSUMER);
> + return dprc;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +static int dc_dprc_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + struct resource *res;
> + struct dc_dprc *dprc;
> + void __iomem *base;
> + int ret, wrap_irq;
> +
> + dprc = devm_kzalloc(dev, sizeof(*dprc), GFP_KERNEL);
> + if (!dprc)
> + return -ENOMEM;
> +
> + ret = imx_scu_get_handle(&dprc->ipc_handle);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to get SCU ipc handle\n");
> +
> + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + dprc->reg = devm_regmap_init_mmio(dev, base, &dc_dprc_regmap_config);
> + if (IS_ERR(dprc->reg))
> + return PTR_ERR(dprc->reg);
> +
> + wrap_irq = platform_get_irq_byname(pdev, "dpr_wrap");
> + if (wrap_irq < 0)
> + return dev_err_probe(dev, wrap_irq, "failed to get dpr_wrap IRQ\n");
> +
> + dprc->num_clks = devm_clk_bulk_get_all(dev, &dprc->clks);
> + if (dprc->num_clks < 0)
> + return dev_err_probe(dev, dprc->num_clks, "failed to get clocks\n");
> +
> + ret = of_property_read_u32(np, "fsl,sc-resource", &dprc->sc_resource);
> + if (ret) {
> + dev_err(dev, "failed to get SC resource %d\n", ret);
> + return ret;
You could probably use 'return dev_err_probe()', to be consistent with the
other places where you used the same.
--
Thanks,
Laurentiu
^ permalink raw reply [flat|nested] 27+ messages in thread
end of thread, other threads:[~2026-10-29 9:32 UTC | newest]
Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-26 10:05 [PATCH v7 00/15] drm/imx: dc: Use prefetch engine Liu Ying
2026-10-26 10:05 ` [PATCH v7 01/15] dt-bindings: display: imx: Add i.MX8qxp/qm PRG binding Liu Ying
2026-10-26 10:05 ` [PATCH v7 02/15] dt-bindings: display: imx: Add i.MX8qxp/qm DPR channel binding Liu Ying
2026-10-26 10:05 ` [PATCH v7 03/15] drm/imx: dc-plane: Disable plane when it is invisible Liu Ying
2026-10-29 9:28 ` Laurentiu Palcu
2026-10-26 10:05 ` [PATCH v7 04/15] drm/imx: dc-fu: Fix dimensions Liu Ying
2026-10-26 10:05 ` [PATCH v7 05/15] drm/imx: dc: Flag CRTC status in GERNERALPURPOSE register Liu Ying
2026-10-29 9:28 ` Laurentiu Palcu
2026-10-26 10:05 ` [PATCH v7 06/15] drm/imx: dc-crtc: Disable at boot Liu Ying
2026-10-26 11:18 ` sashiko-bot
2026-10-29 9:30 ` Laurentiu Palcu
2026-10-26 10:05 ` [PATCH v7 07/15] drm/imx: dc: Add PRG support Liu Ying
2026-10-26 11:32 ` sashiko-bot
2026-10-29 9:30 ` Laurentiu Palcu
2026-10-26 10:05 ` [PATCH v7 08/15] drm/imx: dc: Add DPR channel support Liu Ying
2026-10-26 11:52 ` sashiko-bot
2026-10-29 9:31 ` Laurentiu Palcu
2026-10-26 10:05 ` [PATCH v7 09/15] drm/imx: dc: Use TCON operation mode Liu Ying
2026-10-26 10:05 ` [PATCH v7 10/15] drm/imx: dc-ed: Support getting source selection Liu Ying
2026-10-26 10:41 ` Alexander Stein
2026-10-26 12:01 ` sashiko-bot
2026-10-26 10:05 ` [PATCH v7 11/15] drm/imx: dc-lb: Support getting secondary input selection Liu Ying
2026-10-26 10:05 ` [PATCH v7 12/15] drm/imx: dc-ed: Drop initial source selection Liu Ying
2026-10-26 10:05 ` [PATCH v7 13/15] drm/imx: dc-lb: Drop initial primary and secondary input selections Liu Ying
2026-10-26 10:05 ` [PATCH v7 14/15] drm/imx: dc-fu: Get DPR channel Liu Ying
2026-10-26 10:05 ` [PATCH v7 15/15] drm/imx: dc: Use prefetch engine Liu Ying
2026-10-26 12:41 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox