diff options
| author | Frank Li <Frank.Li@nxp.com> | 2026-02-16 22:18:44 +0300 |
|---|---|---|
| committer | Hans Verkuil <hverkuil+cisco@kernel.org> | 2026-03-26 16:33:08 +0300 |
| commit | ec40b431f0abe682db2e443b857d638cd3653330 (patch) | |
| tree | a0dd6caddb1e1aa619b2ca0097a9c4fa470c9575 | |
| parent | 7425b2eaf50a90f57cde586a4a7a0e2e9c2fc0d1 (diff) | |
| download | linux-ec40b431f0abe682db2e443b857d638cd3653330.tar.xz | |
media: synopsys: csi2rx: add i.MX93 support
The i.MX93 uses a newer version of the DW CSI-2 controller with a changed
register layout and an integrated Image Pixel Interface (IPI), which
converts the received CSI-2 packets from byte to pixel format and produces
a pixel data bus containing vertical and horizontal synchronization
information.
The reset flow also differs, so add the .assert_reset(), .deassert_reset(),
and .idi_enable() callbacks to support it.
Reviewed-by: Michael Riesch <michael.riesch@collabora.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
[Sakari Ailus: include missing linux/bitfield.h.]
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
| -rw-r--r-- | drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 160 |
1 files changed, 156 insertions, 4 deletions
diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index 536e5df2b417..ce17f986279e 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -7,6 +7,7 @@ * Copyright (C) 2026 Collabora, Ltd. */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> @@ -34,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, @@ -43,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, }; @@ -53,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 { @@ -100,6 +132,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, @@ -320,14 +367,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_has_reg(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) @@ -335,8 +400,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_has_reg(csi2, DW_MIPI_CSI2RX_MSK1)) + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0); + + if (dw_mipi_csi2rx_has_reg(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 = { @@ -686,8 +755,91 @@ 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 Synopsys DPHY test codes from reset */ + 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); + + 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 + * ensure that the write operation above reaches its target. + */ + 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, }, |
