[PATCH v2 6/6] media: synopsys: csi2rx: add i.MX93 support
Michael Riesch
michael.riesch at collabora.com
Mon Feb 16 00:29:33 PST 2026
Hi Frank,
Thanks for the update.
On 2/13/26 21:25, Frank Li wrote:
> The i.MX93 uses a newer version of the DW CSI-2 controller with a changed
> register layout and included an Image Pixel Interface (IPI), which convert
and an integrated Image Pixel Interface (IPI), which converts
> the received CSI-2 packets, from byte to pixel format, and produces a
remove both ","
remove extra space between "and" and "produces"
> pixel data bus containing vertical and horizontal synchronism information.
synchronization information?
>
> The reset flow also differs, so add the .assert_reset(), .deassert_reset(),
> and .idi_enable() callbacks to support it.
>
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
> change in v2
> - move macro define to header
> - use new register access method
> - Keep check register exist to avoid print error message. keep slicence to
> access unexisting register may hide problem.
> ---
> drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 163 ++++++++++++++++++++++-
> 1 file changed, 159 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
> index b00ae5fb328da4cc78fe36b629d6661d438e124a..ff617a38e8e9ffed4701c15f59aebeca8e4c5553 100644
> --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
> +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
> @@ -35,6 +35,22 @@
> #define DW_REG_EXIST BIT(31)
> #define DW_REG(x) (DW_REG_EXIST | (x))
>
> +#define DPHY_TEST_CTRL0_TEST_CLR BIT(0)
> +
> +#define IPI_VCID_VC(x) FIELD_PREP(GENMASK(1, 0), (x))
> +#define IPI_VCID_VC_0_1(x) FIELD_PREP(GENMASK(3, 2), (x))
> +#define IPI_VCID_VC_2 BIT(4)
> +
> +#define IPI_DATA_TYPE_DT(x) FIELD_PREP(GENMASK(5, 0), (x))
> +#define IPI_DATA_TYPE_EMB_DATA_EN BIT(8)
> +
> +#define IPI_MODE_CONTROLLER BIT(1)
> +#define IPI_MODE_COLOR_MODE16 BIT(8)
> +#define IPI_MODE_CUT_THROUGH BIT(16)
> +#define IPI_MODE_ENABLE BIT(24)
> +
> +#define IPI_MEM_FLUSH_AUTO BIT(8)
> +
> enum dw_mipi_csi2rx_regs_index {
> DW_MIPI_CSI2RX_N_LANES,
> DW_MIPI_CSI2RX_RESETN,
> @@ -44,6 +60,16 @@ enum dw_mipi_csi2rx_regs_index {
> DW_MIPI_CSI2RX_MSK1,
> DW_MIPI_CSI2RX_MSK2,
> DW_MIPI_CSI2RX_CONTROL,
> + /* imx93 (v150) new register */
> + DW_MIPI_CSI2RX_DPHY_RSTZ,
> + DW_MIPI_CSI2RX_PHY_TST_CTRL0,
> + DW_MIPI_CSI2RX_PHY_TST_CTRL1,
> + DW_MIPI_CSI2RX_PHY_SHUTDOWNZ,
> + DW_MIPI_CSI2RX_IPI_DATATYPE,
> + DW_MIPI_CSI2RX_IPI_MEM_FLUSH,
> + DW_MIPI_CSI2RX_IPI_MODE,
> + DW_MIPI_CSI2RX_IPI_SOFTRSTN,
> + DW_MIPI_CSI2RX_IPI_VCID,
>
> DW_MIPI_CSI2RX_MAX,
> };
> @@ -54,8 +80,13 @@ enum {
> DW_MIPI_CSI2RX_PAD_MAX,
> };
>
> +struct dw_mipi_csi2rx_device;
> +
> struct dw_mipi_csi2rx_drvdata {
> const u32 *regs;
> + void (*dphy_assert_reset)(struct dw_mipi_csi2rx_device *csi2);
> + void (*dphy_deassert_reset)(struct dw_mipi_csi2rx_device *csi2);
> + void (*ipi_enable)(struct dw_mipi_csi2rx_device *csi2);
> };
>
> struct dw_mipi_csi2rx_format {
> @@ -103,6 +134,21 @@ static const struct dw_mipi_csi2rx_drvdata rk3568_drvdata = {
> .regs = rk3568_regs,
> };
>
> +static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = {
> + [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4),
> + [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x8),
> + [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40),
> + [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44),
> + [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48),
> + [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50),
> + [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54),
> + [DW_MIPI_CSI2RX_IPI_MODE] = DW_REG(0x80),
> + [DW_MIPI_CSI2RX_IPI_VCID] = DW_REG(0x84),
> + [DW_MIPI_CSI2RX_IPI_DATATYPE] = DW_REG(0x88),
> + [DW_MIPI_CSI2RX_IPI_MEM_FLUSH] = DW_REG(0x8c),
> + [DW_MIPI_CSI2RX_IPI_SOFTRSTN] = DW_REG(0xa0),
> +};
> +
> static const struct v4l2_mbus_framefmt default_format = {
> .width = 3840,
> .height = 2160,
> @@ -319,14 +365,32 @@ static int dw_mipi_csi2rx_start(struct dw_mipi_csi2rx_device *csi2)
> return -EINVAL;
> }
>
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0);
> +
> + if (csi2->drvdata->dphy_assert_reset)
> + csi2->drvdata->dphy_assert_reset(csi2);
> +
> control |= SW_DATATYPE_FS(0x00) | SW_DATATYPE_FE(0x01) |
> SW_DATATYPE_LS(0x02) | SW_DATATYPE_LE(0x03);
>
> dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_N_LANES, lanes - 1);
> - dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control);
> +
> + if (dw_mipi_csi2rx_is_exist(csi2, DW_MIPI_CSI2RX_CONTROL))
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control);
> +
> + ret = phy_power_on(csi2->phy);
> + if (ret)
> + return ret;
> +
> + if (csi2->drvdata->dphy_deassert_reset)
> + csi2->drvdata->dphy_deassert_reset(csi2);
> +
> dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 1);
>
> - return phy_power_on(csi2->phy);
> + if (csi2->drvdata->ipi_enable)
> + csi2->drvdata->ipi_enable(csi2);
> +
> + return 0;
> }
>
> static void dw_mipi_csi2rx_stop(struct dw_mipi_csi2rx_device *csi2)
> @@ -334,8 +398,12 @@ static void dw_mipi_csi2rx_stop(struct dw_mipi_csi2rx_device *csi2)
> phy_power_off(csi2->phy);
>
> dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0);
> - dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0);
> - dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0);
> +
> + if (dw_mipi_csi2rx_is_exist(csi2, DW_MIPI_CSI2RX_MSK1))
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0);
> +
> + if (dw_mipi_csi2rx_is_exist(csi2, DW_MIPI_CSI2RX_MSK2))
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0);
> }
>
> static const struct media_entity_operations dw_mipi_csi2rx_media_ops = {
> @@ -687,7 +755,94 @@ static void dw_mipi_csi2rx_unregister(struct dw_mipi_csi2rx_device *csi2)
> v4l2_async_nf_cleanup(&csi2->notifier);
> }
>
> +static void imx93_csi2rx_dphy_assert_reset(struct dw_mipi_csi2rx_device *csi2)
> +{
> + u32 val;
> +
> + /* Release synopsis DPHY test codes from reset */
Synopsys
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_DPHY_RSTZ, 0);
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ, 0);
> +
> + val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0);
> + val &= ~DPHY_TEST_CTRL0_TEST_CLR;
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0, val);
> +
> + /*
> + * ndelay is not necessary have MMIO operation, need dummy read to make
> + * sure above write reach target.
/* use read to ensure that the write operation above reaches its target */
?
(Technically it's not a dummy read, as you use the result after the ndelay)
> + */
> + val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0);
> + /* Wait for at least 15ns */
> + ndelay(15);
> + val |= DPHY_TEST_CTRL0_TEST_CLR;
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0, val);
> +}
> +
> +static void imx93_csi2rx_dphy_deassert_reset(struct dw_mipi_csi2rx_device *csi2)
> +{
> + /* Release PHY from reset */
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ, 0x1);
> + /*
> + * ndelay is not necessary have MMIO operation, need dummy read to make
> + * sure above write reach target.
> + */
Adjust comment accordingly.
> + dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ);
> + ndelay(5);
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_DPHY_RSTZ, 0x1);
> +
> + dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_DPHY_RSTZ);
> + ndelay(5);
> +}
> +
> +static void imx93_csi2rx_dphy_ipi_enable(struct dw_mipi_csi2rx_device *csi2)
> +{
> + int dt = csi2->formats->csi_dt;
> + u32 val;
> +
> + /* Do IPI soft reset */
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_SOFTRSTN, 0x0);
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_SOFTRSTN, 0x1);
> +
> + /* Select virtual channel and data type to be processed by IPI */
> + val = IPI_DATA_TYPE_DT(dt);
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_DATATYPE, val);
> +
> + /* Set virtual channel 0 as default */
> + val = IPI_VCID_VC(0);
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_VCID, val);
> +
> + /*
> + * Select IPI camera timing mode and allow the pixel stream
> + * to be non-continuous when pixel interface FIFO is empty
> + */
> + val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_IPI_MODE);
> + val &= ~IPI_MODE_CONTROLLER;
> + val &= ~IPI_MODE_COLOR_MODE16;
> + val |= IPI_MODE_CUT_THROUGH;
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val);
> +
> + /* Memory is automatically flushed at each Frame Start */
> + val = IPI_MEM_FLUSH_AUTO;
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MEM_FLUSH, val);
> +
> + /* Enable IPI */
> + val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_IPI_MODE);
> + val |= IPI_MODE_ENABLE;
> + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val);
> +}
> +
> +static const struct dw_mipi_csi2rx_drvdata imx93_drvdata = {
> + .regs = imx93_regs,
> + .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset,
> + .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset,
> + .ipi_enable = imx93_csi2rx_dphy_ipi_enable,
> +};
> +
> static const struct of_device_id dw_mipi_csi2rx_of_match[] = {
> + {
> + .compatible = "fsl,imx93-mipi-csi2",
> + .data = &imx93_drvdata,
> + },
> {
> .compatible = "rockchip,rk3568-mipi-csi2",
> .data = &rk3568_drvdata,
>
With the nitpicks addressed,
Reviewed-by: Michael Riesch <michael.riesch at collabora.com>
Best regards,
Michael
More information about the Linux-rockchip
mailing list