Thank you for your contribution! Sashiko AI review found 1 potential issue(s) 
to consider:
- [High] drm/vc4: dsi: synchronize_irq() called before masking INT_EN on timeout
--

commit f8356e0d93ae02c55bbda5440fae3505620f2bfb
Author: Henry Zhang <[email protected]>

drm/vc4: dsi: Recover from a timed-out transfer

When a DSI transfer times out while the block is enabled, this commit resets
the DSI block and its analog front end and restores the hardware
configuration from pre_enable so subsequent transfers can succeed without
requiring a full modeset.

> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> index ea3a6d06b045a..42f50d2ca401c 100644
> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c

[ ... ]

> @@ -1374,6 +1378,114 @@ static ssize_t vc4_dsi_host_transfer(struct 
> mipi_dsi_host *host,
>       return ret;
>  }
>  
> +/*
> + * Reset the DSI block and its analog front end, then restore the
> + * configuration done in pre_enable. Resetting the FIFOs alone does not
> + * clear the state the block can be left in after a transfer timed out
> + * (e.g. on an LP receive timeout): every following transfer then times
> + * out as well, until the next modeset.
> + */
> +static void vc4_dsi_reset_and_restore(struct vc4_dsi *dsi)
> +{
> +     u32 en = dsi->variant->port ? DSI1_CTRL_EN : DSI0_CTRL_CTRL0;
> +     u32 ctrl = DSI_PORT_READ(CTRL);
> +     u32 afec0 = DSI_PORT_READ(PHY_AFEC0);
> +     u32 afec1 = DSI_PORT_READ(PHY_AFEC1);
> +     u32 disp0 = DSI_PORT_READ(DISP0_CTRL) & ~DSI_DISP0_ENABLE;
> +     u32 disp1 = DSI_PORT_READ(DISP1_CTRL);
> +     u32 hstx_to = DSI_PORT_READ(HSTX_TO_CNT);
> +     u32 lprx_to = DSI_PORT_READ(LPRX_TO_CNT);
> +     u32 ta_to = DSI_PORT_READ(TA_TO_CNT);
> +     u32 pr_to = DSI_PORT_READ(PR_TO_CNT);
> +     u32 phyc = DSI_PORT_READ(PHYC);
> +     u32 clt0 = DSI_PORT_READ(HS_CLT0);
> +     u32 clt1 = DSI_PORT_READ(HS_CLT1);
> +     u32 clt2 = DSI_PORT_READ(HS_CLT2);
> +     u32 dlt3 = DSI_PORT_READ(HS_DLT3);
> +     u32 dlt4 = DSI_PORT_READ(HS_DLT4);
> +     u32 dlt5 = DSI_PORT_READ(HS_DLT5);
> +     u32 dlt6 = DSI_PORT_READ(HS_DLT6);
> +     u32 dlt7 = DSI_PORT_READ(HS_DLT7);
> +
> +     /*
> +      * Mask the interrupts while the block and its AFE are in reset: the
> +      * error interrupts would otherwise fire continuously.
> +      */
> +     DSI_PORT_WRITE(INT_EN, 0);

[ ... ]

> +static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
> +                                  const struct mipi_dsi_msg *msg)
> +{
> +     struct vc4_dsi *dsi = host_to_dsi(host);
> +     u32 disp0_ctrl;
> +     ssize_t ret;
> +
> +     guard(mutex)(&dsi->transfer_lock);
> +
> +     ret = vc4_dsi_transfer(dsi, msg);
> +     /* Only a running block can be reset and restored. */
> +     if (ret != -ETIMEDOUT || !dsi->enabled)
> +             return ret;
> +
> +     /*
> +      * Leave the block usable for the next transfer. The message is not
> +      * resent: it may already have reached the peripheral, and only the
> +      * caller knows whether it is safe to repeat.
> +      */
> +     disp0_ctrl = DSI_PORT_READ(DISP0_CTRL);
> +     dev_warn(&dsi->pdev->dev,
> +              "transfer timed out (stat 0x%08x), resetting DSI\n",
> +              DSI_PORT_READ(STAT));
> +     /*
> +      * The completion interrupt of the timed-out transfer may still be
> +      * in flight; let it finish so that it cannot complete the next one.
> +      */
> +     synchronize_irq(dsi->irq);
> +     vc4_dsi_reset_and_restore(dsi);

[Severity: High]
Should INT_EN be masked before calling synchronize_irq(dsi->irq) in
vc4_dsi_host_transfer() in drivers/gpu/drm/vc4/vc4_dsi.c (or should
synchronize_irq(dsi->irq) be called after DSI_PORT_WRITE(INT_EN, 0) in
vc4_dsi_reset_and_restore())?

When vc4_dsi_transfer() returns -ETIMEDOUT via reset_fifo_and_return, it
leaves INTERRUPTS_ALWAYS_ENABLED active in INT_EN and does not clear INT_STAT:

drivers/gpu/drm/vc4/vc4_dsi.c:vc4_dsi_transfer() {
        ...
reset_fifo_and_return:
        ...
        DSI_PORT_WRITE(TXPKT1C, 0);
        DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED));
        return ret;
}

Because synchronize_irq(dsi->irq) in vc4_dsi_host_transfer() runs before
vc4_dsi_reset_and_restore() performs 18 register reads and writes
DSI_PORT_WRITE(INT_EN, 0), any error or timeout interrupt from
INTERRUPTS_ALWAYS_ENABLED can still fire in the window after synchronize_irq()
returns and before INT_EN is cleared.

If an interrupt fires in that window on BCM2835 DSI1 (where
broken_axi_workaround is enabled and devm_request_threaded_irq() is used with
vc4_dsi_irq_defer_to_thread_handler()), the threaded handler
vc4_dsi_irq_handler() will run concurrently with vc4_dsi_reset_and_restore():

drivers/gpu/drm/vc4/vc4_dsi.c:vc4_dsi_irq_handler() {
        u32 stat = DSI_PORT_READ(INT_STAT);
        ...
        DSI_PORT_WRITE(INT_STAT, stat);
}

1. In vc4_dsi_irq_handler(), DSI_PORT_WRITE(INT_STAT, stat) invokes
dsi_dma_workaround_write(), which writes to the shared 4-byte buffer
*dsi->reg_dma_mem and submits a DMA transfer on dsi->reg_dma_chan without
serialization:

drivers/gpu/drm/vc4/vc4_dsi.c:dsi_dma_workaround_write() {
        ...
        *dsi->reg_dma_mem = val;

        tx = chan->device->device_prep_dma_memcpy(chan,
                                                  dsi->reg_paddr + offset,
                                                  dsi->reg_dma_paddr,
                                                  4, 0);
        ...
}

Can this race with the register writes in vc4_dsi_reset_and_restore() and
corrupt the values being written to the DSI registers?

2. While vc4_dsi_reset_and_restore() holds the block and AFE in reset across
mdelay(1), error bits in INT_STAT assert continuously. Since
vc4_dsi_irq_handler() reads raw INT_STAT without masking by INT_EN, if the
concurrent IRQ thread reads INT_STAT while INT_HSTX_TO is asserted during
reset (or if a late completion bit latched before TXPKT1C_CMD_EN was cleared),
won't it call vc4_dsi_xfer_complete() and signal dsi->xfer_completion after
synchronize_irq() has already returned, leaving a stale completion for the
next transfer?

-- 
Sashiko AI review ยท 
https://sashiko.dev/#/patchset/[email protected]?part=4

Reply via email to