From: sashiko-bot@kernel.org
To: "Liu Ying" <victor.liu@nxp.com>
Cc: dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org,
imx@lists.linux.dev, robh@kernel.org, conor+dt@kernel.org,
Frank.Li@kernel.org
Subject: Re: [PATCH v8 08/15] drm/imx: dc: Add DPR channel support
Date: Fri, 30 Oct 2026 10:17:45 +0000 [thread overview]
Message-ID: <sashiko-outbox-190821@kernel.org> (raw)
In-Reply-To: <20261030-imx8-dc-prefetch-v8-8-c4f81afd4533@nxp.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] drm/imx: dc-dprc: deadlock and invalid lock type in dc_dprc_disable_repeat_en()
- [High] drm/imx: dc-dprc: runtime PM race and leak in dc_dprc_wrap_irq_handler()
- [Medium] drm/imx: dc-dprc: RPM reference leak and PRG imbalance in dc_dprc_configure()
--
commit 0da98ec378c5f86195b2badbfc2867b3e806cb34
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), which fetches display data, transforms it to linear
format, and stores it in RTRAM alongside its associated PRG.
> 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..29188fa72a327
> --- /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);
> + }
> +
> + 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);
[Severity: Medium]
As reported by Sashiko on v7 [1], can dc_dprc_configure() leak the runtime
PM reference on dprc->dev and leave dprc->prg->dev unbalanced or unclocked
when start is true?
In dc_dprc_configure(), pm_runtime_resume_and_get(dev) is called when start
is true before checking format->format. If format->format hits the default
case, the function returns early without calling pm_runtime_put(dev) and
before calling dc_prg_configure(). That leaks the runtime PM reference on
dprc->dev and leaves dprc->prg->dev runtime-suspended; a subsequent call to
dc_dprc_disable() -> dc_prg_disable() will then write to prg->reg while
unclocked and underflow prg->dev's runtime PM usage count.
Similarly, when dc_dprc_configure() calls dc_prg_configure() with start
set to 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 on pm_runtime_resume_and_get()
failure, dc_dprc_configure() proceeds unconditionally to dc_dprc_enable()
-> dc_prg_enable() and dc_dprc_reg_update() -> dc_prg_reg_update(), writing
to prg->reg while prg->clks are disabled and leaving the runtime PM
references between dprc->dev and prg->dev unbalanced.
> +
> + 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)
> +{
> + guard(spinlock)(&dprc->lock);
> + regmap_write(dprc->reg, IRQ_MASK + SET, IRQ_DPR_CRTL_DONE);
> +
> + synchronize_hardirq(dprc->wrap_irq);
> +
> + regmap_write(dprc->reg, SYSTEM_CTRL0 + CLR, REPEAT_EN);
> + dev_dbg(dprc->dev, "disable REPEAT_EN\n");
> +}
[Severity: High]
As reported by Sashiko on v7 [1], won't holding dprc->lock across
synchronize_hardirq(dprc->wrap_irq) in dc_dprc_disable_repeat_en()
deadlock if dc_dprc_wrap_irq_handler() is concurrently running?
In dc_dprc_disable_repeat_en(), guard(spinlock)(&dprc->lock) holds
dprc->lock for the entire function scope, including while waiting in
synchronize_hardirq(dprc->wrap_irq). If wrap_irq fires on another CPU (either
from DPRC before IRQ_MASK + SET takes effect, or from another device sharing
the IRQF_SHARED interrupt line) and enters dc_dprc_wrap_irq_handler(),
IRQD_IRQ_INPROGRESS is set before dc_dprc_wrap_irq_handler() attempts to
acquire dprc->lock:
CPU 0 CPU 1 (hardirq wrap_irq)
dc_dprc_disable_repeat_en() handle_irq_event()
guard(spinlock)(&dprc->lock) /* sets IRQD_IRQ_INPROGRESS */
... dc_dprc_wrap_irq_handler()
synchronize_hardirq(dprc->wrap_irq) scoped_guard(spinlock, &dprc->lock)
/* waits for IRQD_IRQ_INPROGRESS */ /* spins waiting for dprc->lock */
Also, if dc_dprc_disable_repeat_en() is ever called with local IRQs enabled,
guard(spinlock) does not disable local interrupts, so wrap_irq on the same
CPU can preempt and self-deadlock on dprc->lock.
In addition, as noted in the comment on dc_dprc_regmap_config,
dc_dprc_disable_repeat_en() is called with local IRQs and preemption
disabled. However, dprc->lock is declared as spinlock_t rather than
raw_spinlock_t, and dc_dprc_wrap_irq_handler() -> dc_dprc_ctrl_done_handle()
-> dc_prg_shadow_enable() writes to prg->reg (whose dc_prg_regmap_config
does not set use_raw_spinlock = true) from hardirq context. On PREEMPT_RT or
with CONFIG_PROVE_RAW_LOCK_NESTING enabled, acquiring a non-raw spinlock_t
in hardirq context or with local IRQs/preemption disabled triggers an
invalid wait-context splat.
Finally, because devm_request_irq() is called without IRQF_NO_THREAD,
dc_dprc_wrap_irq_handler() is force-threaded under PREEMPT_RT or threadirqs.
In that configuration, synchronize_hardirq() does not wait for the IRQ
thread to complete, so dc_dprc_ctrl_done_handle() can still run concurrently
and write REPEAT_EN back to SYSTEM_CTRL0 and SHADOW_EN to prg->reg after
dc_dprc_disable_repeat_en() and dc_dprc_disable().
> +
> +/*
> + * dc_dprc_disable_repeat_en() must be called beforehand to mask
> + * IRQ_DPR_CRTL_DONE and synchronize wrap_irq.
> + */
> +void dc_dprc_disable(struct dc_dprc *dprc)
> +{
> + dc_prg_disable(dprc->prg);
> +
> + pm_runtime_put(dprc->dev);
> +
> + dev_dbg(dprc->dev, "disable\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;
> +}
[Severity: High]
As reported by Sashiko on v7 [1], can dc_dprc_wrap_irq_handler() still race
with runtime suspend or leak the active runtime PM state when wrap_irq is
shared?
Because wrap_irq is requested with IRQF_SHARED, another DPRC instance can
trigger dc_dprc_wrap_irq_handler() at any time:
1. In rpm_suspend(), after rpm_check_suspend_allowed() verifies usage_count
is 0, it sets runtime_status to RPM_SUSPENDING and drops dev->power.lock
to invoke dc_dprc_runtime_suspend(), which disables dprc->clks. Because
pm_runtime_get_noresume(dev) and pm_runtime_active(dev) in
dc_dprc_wrap_irq_handler() do not synchronize with dev->power.lock or
in-flight suspend, if dc_dprc_wrap_irq_handler() runs right before
RPM_SUSPENDING is set, it passes the pm_runtime_active(dev) check and
reads IRQ_MASK and IRQ_MASK_STATUS from dprc->reg while
dc_dprc_runtime_suspend() concurrently disables dprc->clks.
2. pm_runtime_active(dev) returns true whenever dev->power.disable_depth > 0,
even if the device is runtime-suspended with its clocks off (for example,
during late system suspend after __device_suspend_late() disables runtime
PM, during devres teardown on unbind, or when CONFIG_PM is disabled). Any
shared IRQ during those windows will read dprc->reg with clocks disabled.
3. If dc_dprc_wrap_irq_handler() increments usage_count from 1 to 2 via
pm_runtime_get_noresume(dev) while dc_dprc_disable() concurrently calls
pm_runtime_put(dprc->dev), dc_dprc_disable() drops usage_count from 2 to
1 without scheduling idle/suspend. When dc_dprc_wrap_irq_handler() then
calls pm_runtime_put_noidle(dev), usage_count drops from 1 to 0 without
triggering rpm_idle() or rpm_suspend(), leaving dprc->dev permanently
active with its clocks enabled.
[1] https://lore.kernel.org/r/20261026-imx8-dc-prefetch-v7-8-a0b50cc4fc65@nxp.com
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030-imx8-dc-prefetch-v8-0-c4f81afd4533@nxp.com?part=8
next prev parent reply other threads:[~2026-10-30 10:17 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 9:54 [PATCH v8 00/15] drm/imx: dc: Use prefetch engine Liu Ying
2026-10-30 9:54 ` [PATCH v8 01/15] dt-bindings: display: imx: Add i.MX8qxp/qm PRG binding Liu Ying
2026-10-30 9:54 ` [PATCH v8 02/15] dt-bindings: display: imx: Add i.MX8qxp/qm DPR channel binding Liu Ying
2026-10-30 9:54 ` [PATCH v8 03/15] drm/imx: dc-plane: Disable plane when it is invisible Liu Ying
2026-10-30 9:54 ` [PATCH v8 04/15] drm/imx: dc-fu: Fix dimensions Liu Ying
2026-10-30 9:54 ` [PATCH v8 05/15] drm/imx: dc: Flag CRTC status in GERNERALPURPOSE register Liu Ying
2026-10-30 10:07 ` sashiko-bot
2026-10-30 9:54 ` [PATCH v8 06/15] drm/imx: dc-crtc: Disable at boot Liu Ying
2026-10-30 10:11 ` sashiko-bot
2026-10-30 13:05 ` Laurentiu Palcu
2026-10-30 9:54 ` [PATCH v8 07/15] drm/imx: dc: Add PRG support Liu Ying
2026-10-30 10:06 ` sashiko-bot
2026-10-30 13:05 ` Laurentiu Palcu
2026-10-30 9:54 ` [PATCH v8 08/15] drm/imx: dc: Add DPR channel support Liu Ying
2026-10-30 10:17 ` sashiko-bot [this message]
2026-10-30 13:06 ` Laurentiu Palcu
2026-10-30 9:54 ` [PATCH v8 09/15] drm/imx: dc: Use TCON operation mode Liu Ying
2026-10-30 9:54 ` [PATCH v8 10/15] drm/imx: dc-ed: Support getting source selection Liu Ying
2026-10-30 9:54 ` [PATCH v8 11/15] drm/imx: dc-lb: Support getting secondary input selection Liu Ying
2026-10-30 9:54 ` [PATCH v8 12/15] drm/imx: dc-ed: Drop initial source selection Liu Ying
2026-10-30 9:54 ` [PATCH v8 13/15] drm/imx: dc-lb: Drop initial primary and secondary input selections Liu Ying
2026-10-30 9:54 ` [PATCH v8 14/15] drm/imx: dc-fu: Get DPR channel Liu Ying
2026-10-30 9:54 ` [PATCH v8 15/15] drm/imx: dc: Use prefetch engine Liu Ying
2026-10-30 10:29 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-190821@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=Frank.Li@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=imx@lists.linux.dev \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=victor.liu@nxp.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.