xref: /linux/drivers/media/platform/nxp/imx8mq-mipi-csi2.c (revision 0cdee263bc5e7b20f657ea09f9272f50c568f35b)
1f33fd8d7SMartin Kepplinger // SPDX-License-Identifier: GPL-2.0
2f33fd8d7SMartin Kepplinger /*
3f33fd8d7SMartin Kepplinger  * NXP i.MX8MQ SoC series MIPI-CSI2 receiver driver
4f33fd8d7SMartin Kepplinger  *
5f33fd8d7SMartin Kepplinger  * Copyright (C) 2021 Purism SPC
6f33fd8d7SMartin Kepplinger  */
7f33fd8d7SMartin Kepplinger 
8642b70d5SFrank Li #include <linux/bitfield.h>
9f33fd8d7SMartin Kepplinger #include <linux/clk.h>
10f33fd8d7SMartin Kepplinger #include <linux/clk-provider.h>
11f33fd8d7SMartin Kepplinger #include <linux/delay.h>
12f33fd8d7SMartin Kepplinger #include <linux/errno.h>
13f33fd8d7SMartin Kepplinger #include <linux/interconnect.h>
14f33fd8d7SMartin Kepplinger #include <linux/interrupt.h>
15f33fd8d7SMartin Kepplinger #include <linux/io.h>
16f33fd8d7SMartin Kepplinger #include <linux/kernel.h>
17f33fd8d7SMartin Kepplinger #include <linux/mfd/syscon.h>
18f33fd8d7SMartin Kepplinger #include <linux/module.h>
19f33fd8d7SMartin Kepplinger #include <linux/mutex.h>
20f33fd8d7SMartin Kepplinger #include <linux/of.h>
21f33fd8d7SMartin Kepplinger #include <linux/platform_device.h>
22f33fd8d7SMartin Kepplinger #include <linux/pm_runtime.h>
23f33fd8d7SMartin Kepplinger #include <linux/regmap.h>
24f33fd8d7SMartin Kepplinger #include <linux/regulator/consumer.h>
25f33fd8d7SMartin Kepplinger #include <linux/reset.h>
26f33fd8d7SMartin Kepplinger #include <linux/spinlock.h>
27f33fd8d7SMartin Kepplinger 
28f33fd8d7SMartin Kepplinger #include <media/v4l2-common.h>
29f33fd8d7SMartin Kepplinger #include <media/v4l2-device.h>
30f33fd8d7SMartin Kepplinger #include <media/v4l2-fwnode.h>
31f33fd8d7SMartin Kepplinger #include <media/v4l2-mc.h>
32f33fd8d7SMartin Kepplinger #include <media/v4l2-subdev.h>
33f33fd8d7SMartin Kepplinger 
34f33fd8d7SMartin Kepplinger #define MIPI_CSI2_DRIVER_NAME			"imx8mq-mipi-csi2"
35f33fd8d7SMartin Kepplinger #define MIPI_CSI2_SUBDEV_NAME			MIPI_CSI2_DRIVER_NAME
36f33fd8d7SMartin Kepplinger 
37f33fd8d7SMartin Kepplinger #define MIPI_CSI2_PAD_SINK			0
38f33fd8d7SMartin Kepplinger #define MIPI_CSI2_PAD_SOURCE			1
39f33fd8d7SMartin Kepplinger #define MIPI_CSI2_PADS_NUM			2
40f33fd8d7SMartin Kepplinger 
41f33fd8d7SMartin Kepplinger #define MIPI_CSI2_DEF_PIX_WIDTH			640
42f33fd8d7SMartin Kepplinger #define MIPI_CSI2_DEF_PIX_HEIGHT		480
43f33fd8d7SMartin Kepplinger 
44f33fd8d7SMartin Kepplinger /* Register map definition */
45f33fd8d7SMartin Kepplinger 
46f33fd8d7SMartin Kepplinger /* i.MX8MQ CSI-2 controller CSR */
47f33fd8d7SMartin Kepplinger #define CSI2RX_CFG_NUM_LANES			0x100
48f33fd8d7SMartin Kepplinger #define CSI2RX_CFG_DISABLE_DATA_LANES		0x104
49f33fd8d7SMartin Kepplinger #define CSI2RX_BIT_ERR				0x108
50f33fd8d7SMartin Kepplinger #define CSI2RX_IRQ_STATUS			0x10c
51f33fd8d7SMartin Kepplinger #define CSI2RX_IRQ_MASK				0x110
52f33fd8d7SMartin Kepplinger #define CSI2RX_IRQ_MASK_ALL			0x1ff
53f33fd8d7SMartin Kepplinger #define CSI2RX_IRQ_MASK_ULPS_STATUS_CHANGE	0x8
54f33fd8d7SMartin Kepplinger #define CSI2RX_ULPS_STATUS			0x114
55f33fd8d7SMartin Kepplinger #define CSI2RX_PPI_ERRSOT_HS			0x118
56f33fd8d7SMartin Kepplinger #define CSI2RX_PPI_ERRSOTSYNC_HS		0x11c
57f33fd8d7SMartin Kepplinger #define CSI2RX_PPI_ERRESC			0x120
58f33fd8d7SMartin Kepplinger #define CSI2RX_PPI_ERRSYNCESC			0x124
59f33fd8d7SMartin Kepplinger #define CSI2RX_PPI_ERRCONTROL			0x128
60f33fd8d7SMartin Kepplinger #define CSI2RX_CFG_DISABLE_PAYLOAD_0		0x12c
61f33fd8d7SMartin Kepplinger #define CSI2RX_CFG_VID_VC_IGNORE		0x180
62f33fd8d7SMartin Kepplinger #define CSI2RX_CFG_VID_VC			0x184
63f33fd8d7SMartin Kepplinger #define CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL	0x188
64f33fd8d7SMartin Kepplinger #define CSI2RX_CFG_DISABLE_PAYLOAD_1		0x130
65f33fd8d7SMartin Kepplinger 
66382d53e9SGuoniu.zhou struct csi_state;
67382d53e9SGuoniu.zhou 
68f33fd8d7SMartin Kepplinger enum {
69f33fd8d7SMartin Kepplinger 	ST_POWERED	= 1,
70f33fd8d7SMartin Kepplinger 	ST_STREAMING	= 2,
71f33fd8d7SMartin Kepplinger 	ST_SUSPENDED	= 4,
72f33fd8d7SMartin Kepplinger };
73f33fd8d7SMartin Kepplinger 
74f33fd8d7SMartin Kepplinger enum imx8mq_mipi_csi_clk {
75f33fd8d7SMartin Kepplinger 	CSI2_CLK_CORE,
76f33fd8d7SMartin Kepplinger 	CSI2_CLK_ESC,
77f33fd8d7SMartin Kepplinger 	CSI2_CLK_UI,
78f33fd8d7SMartin Kepplinger 	CSI2_NUM_CLKS,
79f33fd8d7SMartin Kepplinger };
80f33fd8d7SMartin Kepplinger 
81f33fd8d7SMartin Kepplinger static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = {
82f33fd8d7SMartin Kepplinger 	[CSI2_CLK_CORE] = "core",
83f33fd8d7SMartin Kepplinger 	[CSI2_CLK_ESC] = "esc",
84f33fd8d7SMartin Kepplinger 	[CSI2_CLK_UI] = "ui",
85f33fd8d7SMartin Kepplinger };
86f33fd8d7SMartin Kepplinger 
87f33fd8d7SMartin Kepplinger #define CSI2_NUM_CLKS	ARRAY_SIZE(imx8mq_mipi_csi_clk_id)
88f33fd8d7SMartin Kepplinger 
89382d53e9SGuoniu.zhou struct imx8mq_plat_data {
90382d53e9SGuoniu.zhou 	int (*enable)(struct csi_state *state, u32 hs_settle);
91382d53e9SGuoniu.zhou 	void (*disable)(struct csi_state *state);
92642b70d5SFrank Li 	bool use_reg_csr;
93382d53e9SGuoniu.zhou };
94f33fd8d7SMartin Kepplinger 
95f33fd8d7SMartin Kepplinger /*
96f33fd8d7SMartin Kepplinger  * The send level configures the number of entries that must accumulate in
97f33fd8d7SMartin Kepplinger  * the Pixel FIFO before the data will be transferred to the video output.
98f33fd8d7SMartin Kepplinger  * The exact value needed for this configuration is dependent on the rate at
99f33fd8d7SMartin Kepplinger  * which the sensor transfers data to the CSI-2 Controller and the user
100f33fd8d7SMartin Kepplinger  * video clock.
101f33fd8d7SMartin Kepplinger  *
102f33fd8d7SMartin Kepplinger  * The calculation is the classical rate-in rate-out type of problem: If the
103f33fd8d7SMartin Kepplinger  * video bandwidth is 10% faster than the incoming mipi data and the video
104f33fd8d7SMartin Kepplinger  * line length is 500 pixels, then the fifo should be allowed to fill
105f33fd8d7SMartin Kepplinger  * 10% of the line length or 50 pixels. If the gap data is ok, then the level
106f33fd8d7SMartin Kepplinger  * can be set to 16 and ignored.
107f33fd8d7SMartin Kepplinger  */
108f33fd8d7SMartin Kepplinger #define CSI2RX_SEND_LEVEL			64
109f33fd8d7SMartin Kepplinger 
110f33fd8d7SMartin Kepplinger struct csi_state {
111f33fd8d7SMartin Kepplinger 	struct device *dev;
112382d53e9SGuoniu.zhou 	const struct imx8mq_plat_data *pdata;
113f33fd8d7SMartin Kepplinger 	void __iomem *regs;
114f33fd8d7SMartin Kepplinger 	struct clk_bulk_data clks[CSI2_NUM_CLKS];
115f33fd8d7SMartin Kepplinger 	struct reset_control *rst;
116f33fd8d7SMartin Kepplinger 	struct regulator *mipi_phy_regulator;
117f33fd8d7SMartin Kepplinger 
118f33fd8d7SMartin Kepplinger 	struct v4l2_subdev sd;
119f33fd8d7SMartin Kepplinger 	struct media_pad pads[MIPI_CSI2_PADS_NUM];
120f33fd8d7SMartin Kepplinger 	struct v4l2_async_notifier notifier;
121f33fd8d7SMartin Kepplinger 	struct v4l2_subdev *src_sd;
122f33fd8d7SMartin Kepplinger 
12394d964e5SLaurent Pinchart 	struct v4l2_mbus_config_mipi_csi2 bus;
124f33fd8d7SMartin Kepplinger 
125bc85e79dSMartin Kepplinger 	struct mutex lock; /* Protect state */
126f33fd8d7SMartin Kepplinger 	u32 state;
127f33fd8d7SMartin Kepplinger 
128f33fd8d7SMartin Kepplinger 	struct regmap *phy_gpr;
129f33fd8d7SMartin Kepplinger 	u8 phy_gpr_reg;
130f33fd8d7SMartin Kepplinger 
131f33fd8d7SMartin Kepplinger 	struct icc_path			*icc_path;
132f33fd8d7SMartin Kepplinger 	s32				icc_path_bw;
133f33fd8d7SMartin Kepplinger };
134f33fd8d7SMartin Kepplinger 
135f33fd8d7SMartin Kepplinger /* -----------------------------------------------------------------------------
136f33fd8d7SMartin Kepplinger  * Format helpers
137f33fd8d7SMartin Kepplinger  */
138f33fd8d7SMartin Kepplinger 
139f33fd8d7SMartin Kepplinger struct csi2_pix_format {
140f33fd8d7SMartin Kepplinger 	u32 code;
141f33fd8d7SMartin Kepplinger 	u8 width;
142f33fd8d7SMartin Kepplinger };
143f33fd8d7SMartin Kepplinger 
144382d53e9SGuoniu.zhou /* -----------------------------------------------------------------------------
145382d53e9SGuoniu.zhou  * i.MX8MQ GPR
146382d53e9SGuoniu.zhou  */
147382d53e9SGuoniu.zhou 
148382d53e9SGuoniu.zhou #define	GPR_CSI2_1_RX_ENABLE		BIT(13)
149382d53e9SGuoniu.zhou #define	GPR_CSI2_1_VID_INTFC_ENB	BIT(12)
150382d53e9SGuoniu.zhou #define	GPR_CSI2_1_HSEL			BIT(10)
151382d53e9SGuoniu.zhou #define	GPR_CSI2_1_CONT_CLK_MODE	BIT(8)
152382d53e9SGuoniu.zhou #define	GPR_CSI2_1_S_PRG_RXHS_SETTLE(x)	(((x) & 0x3f) << 2)
153382d53e9SGuoniu.zhou 
imx8mq_gpr_enable(struct csi_state * state,u32 hs_settle)154382d53e9SGuoniu.zhou static int imx8mq_gpr_enable(struct csi_state *state, u32 hs_settle)
155382d53e9SGuoniu.zhou {
156382d53e9SGuoniu.zhou 	regmap_update_bits(state->phy_gpr,
157382d53e9SGuoniu.zhou 			   state->phy_gpr_reg,
158382d53e9SGuoniu.zhou 			   0x3fff,
159382d53e9SGuoniu.zhou 			   GPR_CSI2_1_RX_ENABLE |
160382d53e9SGuoniu.zhou 			   GPR_CSI2_1_VID_INTFC_ENB |
161382d53e9SGuoniu.zhou 			   GPR_CSI2_1_HSEL |
162382d53e9SGuoniu.zhou 			   GPR_CSI2_1_CONT_CLK_MODE |
163382d53e9SGuoniu.zhou 			   GPR_CSI2_1_S_PRG_RXHS_SETTLE(hs_settle));
164382d53e9SGuoniu.zhou 
165382d53e9SGuoniu.zhou 	return 0;
166382d53e9SGuoniu.zhou }
167382d53e9SGuoniu.zhou 
168382d53e9SGuoniu.zhou static const struct imx8mq_plat_data imx8mq_data = {
169382d53e9SGuoniu.zhou 	.enable = imx8mq_gpr_enable,
170382d53e9SGuoniu.zhou };
171382d53e9SGuoniu.zhou 
172642b70d5SFrank Li /* -----------------------------------------------------------------------------
173642b70d5SFrank Li  * i.MX8QXP
174642b70d5SFrank Li  */
175642b70d5SFrank Li 
176642b70d5SFrank Li #define CSI2SS_PL_CLK_INTERVAL_US		100
177642b70d5SFrank Li #define CSI2SS_PL_CLK_TIMEOUT_US		100000
178642b70d5SFrank Li 
179642b70d5SFrank Li #define CSI2SS_PLM_CTRL				0x0
180642b70d5SFrank Li #define CSI2SS_PLM_CTRL_ENABLE_PL		BIT(0)
181642b70d5SFrank Li #define CSI2SS_PLM_CTRL_VSYNC_OVERRIDE		BIT(9)
182642b70d5SFrank Li #define CSI2SS_PLM_CTRL_HSYNC_OVERRIDE		BIT(10)
183642b70d5SFrank Li #define CSI2SS_PLM_CTRL_VALID_OVERRIDE		BIT(11)
184642b70d5SFrank Li #define CSI2SS_PLM_CTRL_POLARITY_HIGH		BIT(12)
185642b70d5SFrank Li #define CSI2SS_PLM_CTRL_PL_CLK_RUN		BIT(31)
186642b70d5SFrank Li 
187642b70d5SFrank Li #define CSI2SS_PHY_CTRL				0x4
188642b70d5SFrank Li #define CSI2SS_PHY_CTRL_RX_ENABLE		BIT(0)
189642b70d5SFrank Li #define CSI2SS_PHY_CTRL_AUTO_PD_EN		BIT(1)
190642b70d5SFrank Li #define CSI2SS_PHY_CTRL_DDRCLK_EN		BIT(2)
191642b70d5SFrank Li #define CSI2SS_PHY_CTRL_CONT_CLK_MODE		BIT(3)
192642b70d5SFrank Li #define CSI2SS_PHY_CTRL_RX_HS_SETTLE_MASK	GENMASK(9, 4)
193642b70d5SFrank Li #define CSI2SS_PHY_CTRL_RTERM_SEL		BIT(21)
194642b70d5SFrank Li #define CSI2SS_PHY_CTRL_PD			BIT(22)
195642b70d5SFrank Li 
196642b70d5SFrank Li #define CSI2SS_DATA_TYPE_DISABLE_BF		0x38
197642b70d5SFrank Li #define CSI2SS_DATA_TYPE_DISABLE_BF_MASK	GENMASK(23, 0)
198642b70d5SFrank Li 
199642b70d5SFrank Li #define CSI2SS_CTRL_CLK_RESET			0x44
200642b70d5SFrank Li #define CSI2SS_CTRL_CLK_RESET_EN		BIT(0)
201642b70d5SFrank Li 
imx8qxp_gpr_enable(struct csi_state * state,u32 hs_settle)202642b70d5SFrank Li static int imx8qxp_gpr_enable(struct csi_state *state, u32 hs_settle)
203642b70d5SFrank Li {
204642b70d5SFrank Li 	int ret;
205642b70d5SFrank Li 	u32 val;
206642b70d5SFrank Li 
207642b70d5SFrank Li 	/* Clear format */
208642b70d5SFrank Li 	regmap_clear_bits(state->phy_gpr, CSI2SS_DATA_TYPE_DISABLE_BF,
209642b70d5SFrank Li 			  CSI2SS_DATA_TYPE_DISABLE_BF_MASK);
210642b70d5SFrank Li 
211642b70d5SFrank Li 	regmap_write(state->phy_gpr, CSI2SS_PLM_CTRL, 0x0);
212642b70d5SFrank Li 
213642b70d5SFrank Li 	regmap_write(state->phy_gpr, CSI2SS_PHY_CTRL,
214642b70d5SFrank Li 		     FIELD_PREP(CSI2SS_PHY_CTRL_RX_HS_SETTLE_MASK, hs_settle) |
215642b70d5SFrank Li 		     CSI2SS_PHY_CTRL_RX_ENABLE | CSI2SS_PHY_CTRL_DDRCLK_EN |
216642b70d5SFrank Li 		     CSI2SS_PHY_CTRL_CONT_CLK_MODE | CSI2SS_PHY_CTRL_PD |
217642b70d5SFrank Li 		     CSI2SS_PHY_CTRL_RTERM_SEL | CSI2SS_PHY_CTRL_AUTO_PD_EN);
218642b70d5SFrank Li 
219642b70d5SFrank Li 	ret = regmap_read_poll_timeout(state->phy_gpr, CSI2SS_PLM_CTRL,
220642b70d5SFrank Li 				       val, !(val & CSI2SS_PLM_CTRL_PL_CLK_RUN),
221642b70d5SFrank Li 				       CSI2SS_PL_CLK_INTERVAL_US,
222642b70d5SFrank Li 				       CSI2SS_PL_CLK_TIMEOUT_US);
223642b70d5SFrank Li 
224642b70d5SFrank Li 	if (ret) {
225642b70d5SFrank Li 		dev_err(state->dev, "Timeout waiting for Pixel-Link clock\n");
226642b70d5SFrank Li 		return ret;
227642b70d5SFrank Li 	}
228642b70d5SFrank Li 
229642b70d5SFrank Li 	/* Enable Pixel link Master */
230642b70d5SFrank Li 	regmap_set_bits(state->phy_gpr, CSI2SS_PLM_CTRL,
231642b70d5SFrank Li 			CSI2SS_PLM_CTRL_ENABLE_PL | CSI2SS_PLM_CTRL_VALID_OVERRIDE);
232642b70d5SFrank Li 
233642b70d5SFrank Li 	/* PHY Enable */
234642b70d5SFrank Li 	regmap_clear_bits(state->phy_gpr, CSI2SS_PHY_CTRL,
235642b70d5SFrank Li 			  CSI2SS_PHY_CTRL_PD | CSI2SS_PLM_CTRL_POLARITY_HIGH);
236642b70d5SFrank Li 
237642b70d5SFrank Li 	/* Release Reset */
238642b70d5SFrank Li 	regmap_set_bits(state->phy_gpr, CSI2SS_CTRL_CLK_RESET, CSI2SS_CTRL_CLK_RESET_EN);
239642b70d5SFrank Li 
240642b70d5SFrank Li 	return ret;
241642b70d5SFrank Li }
242642b70d5SFrank Li 
imx8qxp_gpr_disable(struct csi_state * state)243642b70d5SFrank Li static void imx8qxp_gpr_disable(struct csi_state *state)
244642b70d5SFrank Li {
245642b70d5SFrank Li 	/* Disable Pixel Link */
246642b70d5SFrank Li 	regmap_write(state->phy_gpr, CSI2SS_PLM_CTRL, 0x0);
247642b70d5SFrank Li 
248642b70d5SFrank Li 	/* Disable PHY */
249642b70d5SFrank Li 	regmap_write(state->phy_gpr, CSI2SS_PHY_CTRL, 0x0);
250642b70d5SFrank Li 
251642b70d5SFrank Li 	regmap_clear_bits(state->phy_gpr, CSI2SS_CTRL_CLK_RESET,
252642b70d5SFrank Li 			  CSI2SS_CTRL_CLK_RESET_EN);
253642b70d5SFrank Li };
254642b70d5SFrank Li 
255642b70d5SFrank Li static const struct imx8mq_plat_data imx8qxp_data = {
256642b70d5SFrank Li 	.enable = imx8qxp_gpr_enable,
257642b70d5SFrank Li 	.disable = imx8qxp_gpr_disable,
258642b70d5SFrank Li 	.use_reg_csr = true,
259642b70d5SFrank Li };
260642b70d5SFrank Li 
261f33fd8d7SMartin Kepplinger static const struct csi2_pix_format imx8mq_mipi_csi_formats[] = {
262f33fd8d7SMartin Kepplinger 	/* RAW (Bayer and greyscale) formats. */
263f33fd8d7SMartin Kepplinger 	{
264f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
265f33fd8d7SMartin Kepplinger 		.width = 8,
266f33fd8d7SMartin Kepplinger 	}, {
267f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
268f33fd8d7SMartin Kepplinger 		.width = 8,
269f33fd8d7SMartin Kepplinger 	}, {
270f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
271f33fd8d7SMartin Kepplinger 		.width = 8,
272f33fd8d7SMartin Kepplinger 	}, {
273f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
274f33fd8d7SMartin Kepplinger 		.width = 8,
275f33fd8d7SMartin Kepplinger 	}, {
276f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_Y8_1X8,
277f33fd8d7SMartin Kepplinger 		.width = 8,
278f33fd8d7SMartin Kepplinger 	}, {
279f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SBGGR10_1X10,
280f33fd8d7SMartin Kepplinger 		.width = 10,
281f33fd8d7SMartin Kepplinger 	}, {
282f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGBRG10_1X10,
283f33fd8d7SMartin Kepplinger 		.width = 10,
284f33fd8d7SMartin Kepplinger 	}, {
285f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
286f33fd8d7SMartin Kepplinger 		.width = 10,
287f33fd8d7SMartin Kepplinger 	}, {
288f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
289f33fd8d7SMartin Kepplinger 		.width = 10,
290f33fd8d7SMartin Kepplinger 	}, {
291f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_Y10_1X10,
292f33fd8d7SMartin Kepplinger 		.width = 10,
293f33fd8d7SMartin Kepplinger 	}, {
294f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
295f33fd8d7SMartin Kepplinger 		.width = 12,
296f33fd8d7SMartin Kepplinger 	}, {
297f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
298f33fd8d7SMartin Kepplinger 		.width = 12,
299f33fd8d7SMartin Kepplinger 	}, {
300f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
301f33fd8d7SMartin Kepplinger 		.width = 12,
302f33fd8d7SMartin Kepplinger 	}, {
303f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
304f33fd8d7SMartin Kepplinger 		.width = 12,
305f33fd8d7SMartin Kepplinger 	}, {
306f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_Y12_1X12,
307f33fd8d7SMartin Kepplinger 		.width = 12,
308f33fd8d7SMartin Kepplinger 	}, {
309f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SBGGR14_1X14,
310f33fd8d7SMartin Kepplinger 		.width = 14,
311f33fd8d7SMartin Kepplinger 	}, {
312f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGBRG14_1X14,
313f33fd8d7SMartin Kepplinger 		.width = 14,
314f33fd8d7SMartin Kepplinger 	}, {
315f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGRBG14_1X14,
316f33fd8d7SMartin Kepplinger 		.width = 14,
317f33fd8d7SMartin Kepplinger 	}, {
318f33fd8d7SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SRGGB14_1X14,
319f33fd8d7SMartin Kepplinger 		.width = 14,
320884c8bd9SJacopo Mondi 	},
321f33fd8d7SMartin Kepplinger 	/* YUV formats */
322884c8bd9SJacopo Mondi 	{
323884c8bd9SJacopo Mondi 		.code = MEDIA_BUS_FMT_YUYV8_1X16,
324f33fd8d7SMartin Kepplinger 		.width = 16,
325f33fd8d7SMartin Kepplinger 	}, {
326884c8bd9SJacopo Mondi 		.code = MEDIA_BUS_FMT_UYVY8_1X16,
327f33fd8d7SMartin Kepplinger 		.width = 16,
328f33fd8d7SMartin Kepplinger 	}
329f33fd8d7SMartin Kepplinger };
330f33fd8d7SMartin Kepplinger 
find_csi2_format(u32 code)331f33fd8d7SMartin Kepplinger static const struct csi2_pix_format *find_csi2_format(u32 code)
332f33fd8d7SMartin Kepplinger {
333f33fd8d7SMartin Kepplinger 	unsigned int i;
334f33fd8d7SMartin Kepplinger 
335f33fd8d7SMartin Kepplinger 	for (i = 0; i < ARRAY_SIZE(imx8mq_mipi_csi_formats); i++)
336f33fd8d7SMartin Kepplinger 		if (code == imx8mq_mipi_csi_formats[i].code)
337f33fd8d7SMartin Kepplinger 			return &imx8mq_mipi_csi_formats[i];
338f33fd8d7SMartin Kepplinger 	return NULL;
339f33fd8d7SMartin Kepplinger }
340f33fd8d7SMartin Kepplinger 
341f33fd8d7SMartin Kepplinger /* -----------------------------------------------------------------------------
342f33fd8d7SMartin Kepplinger  * Hardware configuration
343f33fd8d7SMartin Kepplinger  */
344f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_write(struct csi_state * state,u32 reg,u32 val)345f33fd8d7SMartin Kepplinger static inline void imx8mq_mipi_csi_write(struct csi_state *state, u32 reg, u32 val)
346f33fd8d7SMartin Kepplinger {
347f33fd8d7SMartin Kepplinger 	writel(val, state->regs + reg);
348f33fd8d7SMartin Kepplinger }
349f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_sw_reset(struct csi_state * state)350f33fd8d7SMartin Kepplinger static int imx8mq_mipi_csi_sw_reset(struct csi_state *state)
351f33fd8d7SMartin Kepplinger {
352f33fd8d7SMartin Kepplinger 	int ret;
353f33fd8d7SMartin Kepplinger 
354f33fd8d7SMartin Kepplinger 	/*
355f33fd8d7SMartin Kepplinger 	 * these are most likely self-clearing reset bits. to make it
356f33fd8d7SMartin Kepplinger 	 * more clear, the reset-imx7 driver should implement the
357f33fd8d7SMartin Kepplinger 	 * .reset() operation.
358f33fd8d7SMartin Kepplinger 	 */
359f33fd8d7SMartin Kepplinger 	ret = reset_control_assert(state->rst);
360f33fd8d7SMartin Kepplinger 	if (ret < 0) {
361f33fd8d7SMartin Kepplinger 		dev_err(state->dev, "Failed to assert resets: %d\n", ret);
362f33fd8d7SMartin Kepplinger 		return ret;
363f33fd8d7SMartin Kepplinger 	}
364f33fd8d7SMartin Kepplinger 
365f33fd8d7SMartin Kepplinger 	return 0;
366f33fd8d7SMartin Kepplinger }
367f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_set_params(struct csi_state * state)368f33fd8d7SMartin Kepplinger static void imx8mq_mipi_csi_set_params(struct csi_state *state)
369f33fd8d7SMartin Kepplinger {
370f33fd8d7SMartin Kepplinger 	int lanes = state->bus.num_data_lanes;
371f33fd8d7SMartin Kepplinger 
372f33fd8d7SMartin Kepplinger 	imx8mq_mipi_csi_write(state, CSI2RX_CFG_NUM_LANES, lanes - 1);
373f33fd8d7SMartin Kepplinger 	imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES,
374f33fd8d7SMartin Kepplinger 			      (0xf << lanes) & 0xf);
375f33fd8d7SMartin Kepplinger 	imx8mq_mipi_csi_write(state, CSI2RX_IRQ_MASK, CSI2RX_IRQ_MASK_ALL);
376f33fd8d7SMartin Kepplinger 	/*
377f33fd8d7SMartin Kepplinger 	 * 0x180 bit 0 controls the Virtual Channel behaviour: when set the
378f33fd8d7SMartin Kepplinger 	 * interface ignores the Virtual Channel (VC) field in received packets;
379f33fd8d7SMartin Kepplinger 	 * when cleared it causes the interface to only accept packets whose VC
380f33fd8d7SMartin Kepplinger 	 * matches the value to which VC is set at offset 0x184.
381f33fd8d7SMartin Kepplinger 	 */
382f33fd8d7SMartin Kepplinger 	imx8mq_mipi_csi_write(state, CSI2RX_CFG_VID_VC_IGNORE, 1);
383f33fd8d7SMartin Kepplinger 	imx8mq_mipi_csi_write(state, CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL,
384f33fd8d7SMartin Kepplinger 			      CSI2RX_SEND_LEVEL);
385f33fd8d7SMartin Kepplinger }
386f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_clk_enable(struct csi_state * state)387f33fd8d7SMartin Kepplinger static int imx8mq_mipi_csi_clk_enable(struct csi_state *state)
388f33fd8d7SMartin Kepplinger {
389f33fd8d7SMartin Kepplinger 	return clk_bulk_prepare_enable(CSI2_NUM_CLKS, state->clks);
390f33fd8d7SMartin Kepplinger }
391f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_clk_disable(struct csi_state * state)392f33fd8d7SMartin Kepplinger static void imx8mq_mipi_csi_clk_disable(struct csi_state *state)
393f33fd8d7SMartin Kepplinger {
394f33fd8d7SMartin Kepplinger 	clk_bulk_disable_unprepare(CSI2_NUM_CLKS, state->clks);
395f33fd8d7SMartin Kepplinger }
396f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_clk_get(struct csi_state * state)397f33fd8d7SMartin Kepplinger static int imx8mq_mipi_csi_clk_get(struct csi_state *state)
398f33fd8d7SMartin Kepplinger {
399f33fd8d7SMartin Kepplinger 	unsigned int i;
400f33fd8d7SMartin Kepplinger 
401f33fd8d7SMartin Kepplinger 	for (i = 0; i < CSI2_NUM_CLKS; i++)
402f33fd8d7SMartin Kepplinger 		state->clks[i].id = imx8mq_mipi_csi_clk_id[i];
403f33fd8d7SMartin Kepplinger 
404f33fd8d7SMartin Kepplinger 	return devm_clk_bulk_get(state->dev, CSI2_NUM_CLKS, state->clks);
405f33fd8d7SMartin Kepplinger }
406f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_calc_hs_settle(struct csi_state * state,struct v4l2_subdev_state * sd_state,u32 * hs_settle)40736f6b2a3SMartin Kepplinger static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state,
408bc85e79dSMartin Kepplinger 					  struct v4l2_subdev_state *sd_state,
409bc85e79dSMartin Kepplinger 					  u32 *hs_settle)
410f33fd8d7SMartin Kepplinger {
411e7bad98cSSakari Ailus 	struct media_pad *src_pad;
412f33fd8d7SMartin Kepplinger 	s64 link_freq;
413f33fd8d7SMartin Kepplinger 	u32 lane_rate;
414f33fd8d7SMartin Kepplinger 	unsigned long esc_clk_rate;
415f33fd8d7SMartin Kepplinger 	u32 min_ths_settle, max_ths_settle, ths_settle_ns, esc_clk_period_ns;
41636f6b2a3SMartin Kepplinger 	const struct v4l2_mbus_framefmt *fmt;
41736f6b2a3SMartin Kepplinger 	const struct csi2_pix_format *csi2_fmt;
418f33fd8d7SMartin Kepplinger 
419e7bad98cSSakari Ailus 	src_pad = media_entity_remote_source_pad_unique(&sd_state->sd->entity);
420e7bad98cSSakari Ailus 	if (IS_ERR(src_pad)) {
421e7bad98cSSakari Ailus 		dev_err(state->dev, "can't get source pad of %s (%ld)\n",
422e7bad98cSSakari Ailus 			sd_state->sd->name, PTR_ERR(src_pad));
423e7bad98cSSakari Ailus 		return PTR_ERR(src_pad);
424e7bad98cSSakari Ailus 	}
425e7bad98cSSakari Ailus 
426f33fd8d7SMartin Kepplinger 	/* Calculate the line rate from the pixel rate. */
42736f6b2a3SMartin Kepplinger 
428bc0e8d91SSakari Ailus 	fmt = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SINK);
42936f6b2a3SMartin Kepplinger 	csi2_fmt = find_csi2_format(fmt->code);
43036f6b2a3SMartin Kepplinger 
431e7bad98cSSakari Ailus 	link_freq = v4l2_get_link_freq(src_pad, csi2_fmt->width,
432f33fd8d7SMartin Kepplinger 				       state->bus.num_data_lanes * 2);
433f33fd8d7SMartin Kepplinger 	if (link_freq < 0) {
434f33fd8d7SMartin Kepplinger 		dev_err(state->dev, "Unable to obtain link frequency: %d\n",
435f33fd8d7SMartin Kepplinger 			(int)link_freq);
436f33fd8d7SMartin Kepplinger 		return link_freq;
437f33fd8d7SMartin Kepplinger 	}
438f33fd8d7SMartin Kepplinger 
439f33fd8d7SMartin Kepplinger 	lane_rate = link_freq * 2;
440f33fd8d7SMartin Kepplinger 	if (lane_rate < 80000000 || lane_rate > 1500000000) {
441f33fd8d7SMartin Kepplinger 		dev_dbg(state->dev, "Out-of-bound lane rate %u\n", lane_rate);
442f33fd8d7SMartin Kepplinger 		return -EINVAL;
443f33fd8d7SMartin Kepplinger 	}
444f33fd8d7SMartin Kepplinger 
445f33fd8d7SMartin Kepplinger 	/*
446f33fd8d7SMartin Kepplinger 	 * The D-PHY specification requires Ths-settle to be in the range
447f33fd8d7SMartin Kepplinger 	 * 85ns + 6*UI to 140ns + 10*UI, with the unit interval UI being half
448f33fd8d7SMartin Kepplinger 	 * the clock period.
449f33fd8d7SMartin Kepplinger 	 *
450f33fd8d7SMartin Kepplinger 	 * The Ths-settle value is expressed in the hardware as a multiple of
451f33fd8d7SMartin Kepplinger 	 * the Esc clock period:
452f33fd8d7SMartin Kepplinger 	 *
453f33fd8d7SMartin Kepplinger 	 * Ths-settle = (PRG_RXHS_SETTLE + 1) * Tperiod of RxClkInEsc
454f33fd8d7SMartin Kepplinger 	 *
455f33fd8d7SMartin Kepplinger 	 * Due to the one cycle inaccuracy introduced by rounding, the
456f33fd8d7SMartin Kepplinger 	 * documentation recommends picking a value away from the boundaries.
457f33fd8d7SMartin Kepplinger 	 * Let's pick the average.
458f33fd8d7SMartin Kepplinger 	 */
459f33fd8d7SMartin Kepplinger 	esc_clk_rate = clk_get_rate(state->clks[CSI2_CLK_ESC].clk);
460f33fd8d7SMartin Kepplinger 	if (!esc_clk_rate) {
461f33fd8d7SMartin Kepplinger 		dev_err(state->dev, "Could not get esc clock rate.\n");
462f33fd8d7SMartin Kepplinger 		return -EINVAL;
463f33fd8d7SMartin Kepplinger 	}
464f33fd8d7SMartin Kepplinger 
465f33fd8d7SMartin Kepplinger 	dev_dbg(state->dev, "esc clk rate: %lu\n", esc_clk_rate);
466f33fd8d7SMartin Kepplinger 	esc_clk_period_ns = 1000000000 / esc_clk_rate;
467f33fd8d7SMartin Kepplinger 
468f33fd8d7SMartin Kepplinger 	min_ths_settle = 85 + 6 * 1000000 / (lane_rate / 1000);
469f33fd8d7SMartin Kepplinger 	max_ths_settle = 140 + 10 * 1000000 / (lane_rate / 1000);
470f33fd8d7SMartin Kepplinger 	ths_settle_ns = (min_ths_settle + max_ths_settle) / 2;
471f33fd8d7SMartin Kepplinger 
472bc85e79dSMartin Kepplinger 	*hs_settle = ths_settle_ns / esc_clk_period_ns - 1;
473f33fd8d7SMartin Kepplinger 
474f33fd8d7SMartin Kepplinger 	dev_dbg(state->dev, "lane rate %u Ths_settle %u hs_settle %u\n",
475bc85e79dSMartin Kepplinger 		lane_rate, ths_settle_ns, *hs_settle);
476f33fd8d7SMartin Kepplinger 
477f33fd8d7SMartin Kepplinger 	return 0;
478f33fd8d7SMartin Kepplinger }
479f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_start_stream(struct csi_state * state,struct v4l2_subdev_state * sd_state)48036f6b2a3SMartin Kepplinger static int imx8mq_mipi_csi_start_stream(struct csi_state *state,
48136f6b2a3SMartin Kepplinger 					struct v4l2_subdev_state *sd_state)
482f33fd8d7SMartin Kepplinger {
483f33fd8d7SMartin Kepplinger 	int ret;
4846970888dSHans Verkuil 	u32 hs_settle = 0;
485f33fd8d7SMartin Kepplinger 
486f33fd8d7SMartin Kepplinger 	ret = imx8mq_mipi_csi_sw_reset(state);
487f33fd8d7SMartin Kepplinger 	if (ret)
488f33fd8d7SMartin Kepplinger 		return ret;
489f33fd8d7SMartin Kepplinger 
490f33fd8d7SMartin Kepplinger 	imx8mq_mipi_csi_set_params(state);
491bc85e79dSMartin Kepplinger 	ret = imx8mq_mipi_csi_calc_hs_settle(state, sd_state, &hs_settle);
492f33fd8d7SMartin Kepplinger 	if (ret)
493f33fd8d7SMartin Kepplinger 		return ret;
494f33fd8d7SMartin Kepplinger 
495382d53e9SGuoniu.zhou 	ret = state->pdata->enable(state, hs_settle);
496382d53e9SGuoniu.zhou 	if (ret)
497382d53e9SGuoniu.zhou 		return ret;
498f33fd8d7SMartin Kepplinger 
499f33fd8d7SMartin Kepplinger 	return 0;
500f33fd8d7SMartin Kepplinger }
501f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_stop_stream(struct csi_state * state)502f33fd8d7SMartin Kepplinger static void imx8mq_mipi_csi_stop_stream(struct csi_state *state)
503f33fd8d7SMartin Kepplinger {
504bc85e79dSMartin Kepplinger 	imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES, 0xf);
505382d53e9SGuoniu.zhou 
506382d53e9SGuoniu.zhou 	if (state->pdata->disable)
507382d53e9SGuoniu.zhou 		state->pdata->disable(state);
508f33fd8d7SMartin Kepplinger }
509f33fd8d7SMartin Kepplinger 
510f33fd8d7SMartin Kepplinger /* -----------------------------------------------------------------------------
511f33fd8d7SMartin Kepplinger  * V4L2 subdev operations
512f33fd8d7SMartin Kepplinger  */
513f33fd8d7SMartin Kepplinger 
mipi_sd_to_csi2_state(struct v4l2_subdev * sdev)514f33fd8d7SMartin Kepplinger static struct csi_state *mipi_sd_to_csi2_state(struct v4l2_subdev *sdev)
515f33fd8d7SMartin Kepplinger {
516f33fd8d7SMartin Kepplinger 	return container_of(sdev, struct csi_state, sd);
517f33fd8d7SMartin Kepplinger }
518f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_s_stream(struct v4l2_subdev * sd,int enable)519f33fd8d7SMartin Kepplinger static int imx8mq_mipi_csi_s_stream(struct v4l2_subdev *sd, int enable)
520f33fd8d7SMartin Kepplinger {
521f33fd8d7SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
52236f6b2a3SMartin Kepplinger 	struct v4l2_subdev_state *sd_state;
523f33fd8d7SMartin Kepplinger 	int ret = 0;
524f33fd8d7SMartin Kepplinger 
525f33fd8d7SMartin Kepplinger 	if (enable) {
526f33fd8d7SMartin Kepplinger 		ret = pm_runtime_resume_and_get(state->dev);
527f33fd8d7SMartin Kepplinger 		if (ret < 0)
528f33fd8d7SMartin Kepplinger 			return ret;
529f33fd8d7SMartin Kepplinger 	}
530f33fd8d7SMartin Kepplinger 
531f33fd8d7SMartin Kepplinger 	mutex_lock(&state->lock);
532f33fd8d7SMartin Kepplinger 
533f33fd8d7SMartin Kepplinger 	if (enable) {
534f33fd8d7SMartin Kepplinger 		if (state->state & ST_SUSPENDED) {
535f33fd8d7SMartin Kepplinger 			ret = -EBUSY;
536f33fd8d7SMartin Kepplinger 			goto unlock;
537f33fd8d7SMartin Kepplinger 		}
538f33fd8d7SMartin Kepplinger 
53936f6b2a3SMartin Kepplinger 		sd_state = v4l2_subdev_lock_and_get_active_state(sd);
54036f6b2a3SMartin Kepplinger 		ret = imx8mq_mipi_csi_start_stream(state, sd_state);
54136f6b2a3SMartin Kepplinger 		v4l2_subdev_unlock_state(sd_state);
54236f6b2a3SMartin Kepplinger 
543f33fd8d7SMartin Kepplinger 		if (ret < 0)
544f33fd8d7SMartin Kepplinger 			goto unlock;
545f33fd8d7SMartin Kepplinger 
546f33fd8d7SMartin Kepplinger 		ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1);
547f33fd8d7SMartin Kepplinger 		if (ret < 0)
548f33fd8d7SMartin Kepplinger 			goto unlock;
549f33fd8d7SMartin Kepplinger 
550f33fd8d7SMartin Kepplinger 		state->state |= ST_STREAMING;
551f33fd8d7SMartin Kepplinger 	} else {
552f33fd8d7SMartin Kepplinger 		v4l2_subdev_call(state->src_sd, video, s_stream, 0);
553f33fd8d7SMartin Kepplinger 		imx8mq_mipi_csi_stop_stream(state);
554f33fd8d7SMartin Kepplinger 		state->state &= ~ST_STREAMING;
555f33fd8d7SMartin Kepplinger 	}
556f33fd8d7SMartin Kepplinger 
557f33fd8d7SMartin Kepplinger unlock:
558f33fd8d7SMartin Kepplinger 	mutex_unlock(&state->lock);
559f33fd8d7SMartin Kepplinger 
560f33fd8d7SMartin Kepplinger 	if (!enable || ret < 0)
561f33fd8d7SMartin Kepplinger 		pm_runtime_put(state->dev);
562f33fd8d7SMartin Kepplinger 
563f33fd8d7SMartin Kepplinger 	return ret;
564f33fd8d7SMartin Kepplinger }
565f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_init_state(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state)5665755be5fSLaurent Pinchart static int imx8mq_mipi_csi_init_state(struct v4l2_subdev *sd,
567f33fd8d7SMartin Kepplinger 				      struct v4l2_subdev_state *sd_state)
568f33fd8d7SMartin Kepplinger {
569f33fd8d7SMartin Kepplinger 	struct v4l2_mbus_framefmt *fmt_sink;
570f33fd8d7SMartin Kepplinger 	struct v4l2_mbus_framefmt *fmt_source;
571f33fd8d7SMartin Kepplinger 
572bc0e8d91SSakari Ailus 	fmt_sink = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SINK);
573bc0e8d91SSakari Ailus 	fmt_source = v4l2_subdev_state_get_format(sd_state,
574bc0e8d91SSakari Ailus 						  MIPI_CSI2_PAD_SOURCE);
575f33fd8d7SMartin Kepplinger 
576f33fd8d7SMartin Kepplinger 	fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10;
577f33fd8d7SMartin Kepplinger 	fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH;
578f33fd8d7SMartin Kepplinger 	fmt_sink->height = MIPI_CSI2_DEF_PIX_HEIGHT;
579f33fd8d7SMartin Kepplinger 	fmt_sink->field = V4L2_FIELD_NONE;
580f33fd8d7SMartin Kepplinger 
581f33fd8d7SMartin Kepplinger 	fmt_sink->colorspace = V4L2_COLORSPACE_RAW;
582f33fd8d7SMartin Kepplinger 	fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);
583f33fd8d7SMartin Kepplinger 	fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);
584f33fd8d7SMartin Kepplinger 	fmt_sink->quantization =
585f33fd8d7SMartin Kepplinger 		V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,
586f33fd8d7SMartin Kepplinger 					      fmt_sink->ycbcr_enc);
587f33fd8d7SMartin Kepplinger 
588f33fd8d7SMartin Kepplinger 	*fmt_source = *fmt_sink;
589f33fd8d7SMartin Kepplinger 
590f33fd8d7SMartin Kepplinger 	return 0;
591f33fd8d7SMartin Kepplinger }
592f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)593f33fd8d7SMartin Kepplinger static int imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd,
594f33fd8d7SMartin Kepplinger 					  struct v4l2_subdev_state *sd_state,
595f33fd8d7SMartin Kepplinger 					  struct v4l2_subdev_mbus_code_enum *code)
596f33fd8d7SMartin Kepplinger {
597f33fd8d7SMartin Kepplinger 	/*
598f33fd8d7SMartin Kepplinger 	 * We can't transcode in any way, the source format is identical
599f33fd8d7SMartin Kepplinger 	 * to the sink format.
600f33fd8d7SMartin Kepplinger 	 */
601f33fd8d7SMartin Kepplinger 	if (code->pad == MIPI_CSI2_PAD_SOURCE) {
602f33fd8d7SMartin Kepplinger 		struct v4l2_mbus_framefmt *fmt;
603f33fd8d7SMartin Kepplinger 
604f33fd8d7SMartin Kepplinger 		if (code->index > 0)
605f33fd8d7SMartin Kepplinger 			return -EINVAL;
606f33fd8d7SMartin Kepplinger 
607bc0e8d91SSakari Ailus 		fmt = v4l2_subdev_state_get_format(sd_state, code->pad);
608f33fd8d7SMartin Kepplinger 		code->code = fmt->code;
609f33fd8d7SMartin Kepplinger 		return 0;
610f33fd8d7SMartin Kepplinger 	}
611f33fd8d7SMartin Kepplinger 
612f33fd8d7SMartin Kepplinger 	if (code->pad != MIPI_CSI2_PAD_SINK)
613f33fd8d7SMartin Kepplinger 		return -EINVAL;
614f33fd8d7SMartin Kepplinger 
615f33fd8d7SMartin Kepplinger 	if (code->index >= ARRAY_SIZE(imx8mq_mipi_csi_formats))
616f33fd8d7SMartin Kepplinger 		return -EINVAL;
617f33fd8d7SMartin Kepplinger 
618f33fd8d7SMartin Kepplinger 	code->code = imx8mq_mipi_csi_formats[code->index].code;
619f33fd8d7SMartin Kepplinger 
620f33fd8d7SMartin Kepplinger 	return 0;
621f33fd8d7SMartin Kepplinger }
622f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * sdformat)623f33fd8d7SMartin Kepplinger static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd,
624f33fd8d7SMartin Kepplinger 				   struct v4l2_subdev_state *sd_state,
625f33fd8d7SMartin Kepplinger 				   struct v4l2_subdev_format *sdformat)
626f33fd8d7SMartin Kepplinger {
62736f6b2a3SMartin Kepplinger 	const struct csi2_pix_format *csi2_fmt;
628f33fd8d7SMartin Kepplinger 	struct v4l2_mbus_framefmt *fmt;
629f33fd8d7SMartin Kepplinger 
630f33fd8d7SMartin Kepplinger 	/*
631f33fd8d7SMartin Kepplinger 	 * The device can't transcode in any way, the source format can't be
632f33fd8d7SMartin Kepplinger 	 * modified.
633f33fd8d7SMartin Kepplinger 	 */
634f33fd8d7SMartin Kepplinger 	if (sdformat->pad == MIPI_CSI2_PAD_SOURCE)
63536f6b2a3SMartin Kepplinger 		return v4l2_subdev_get_fmt(sd, sd_state, sdformat);
636f33fd8d7SMartin Kepplinger 
637f33fd8d7SMartin Kepplinger 	if (sdformat->pad != MIPI_CSI2_PAD_SINK)
638f33fd8d7SMartin Kepplinger 		return -EINVAL;
639f33fd8d7SMartin Kepplinger 
640f33fd8d7SMartin Kepplinger 	csi2_fmt = find_csi2_format(sdformat->format.code);
641f33fd8d7SMartin Kepplinger 	if (!csi2_fmt)
642f33fd8d7SMartin Kepplinger 		csi2_fmt = &imx8mq_mipi_csi_formats[0];
643f33fd8d7SMartin Kepplinger 
644bc0e8d91SSakari Ailus 	fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad);
645f33fd8d7SMartin Kepplinger 
646f33fd8d7SMartin Kepplinger 	fmt->code = csi2_fmt->code;
647f33fd8d7SMartin Kepplinger 	fmt->width = sdformat->format.width;
648f33fd8d7SMartin Kepplinger 	fmt->height = sdformat->format.height;
649f33fd8d7SMartin Kepplinger 
650f33fd8d7SMartin Kepplinger 	sdformat->format = *fmt;
651f33fd8d7SMartin Kepplinger 
652f33fd8d7SMartin Kepplinger 	/* Propagate the format from sink to source. */
653bc0e8d91SSakari Ailus 	fmt = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SOURCE);
654f33fd8d7SMartin Kepplinger 	*fmt = sdformat->format;
655f33fd8d7SMartin Kepplinger 
656f33fd8d7SMartin Kepplinger 	return 0;
657f33fd8d7SMartin Kepplinger }
658f33fd8d7SMartin Kepplinger 
659f33fd8d7SMartin Kepplinger static const struct v4l2_subdev_video_ops imx8mq_mipi_csi_video_ops = {
660f33fd8d7SMartin Kepplinger 	.s_stream	= imx8mq_mipi_csi_s_stream,
661f33fd8d7SMartin Kepplinger };
662f33fd8d7SMartin Kepplinger 
663f33fd8d7SMartin Kepplinger static const struct v4l2_subdev_pad_ops imx8mq_mipi_csi_pad_ops = {
664f33fd8d7SMartin Kepplinger 	.enum_mbus_code		= imx8mq_mipi_csi_enum_mbus_code,
66536f6b2a3SMartin Kepplinger 	.get_fmt		= v4l2_subdev_get_fmt,
666f33fd8d7SMartin Kepplinger 	.set_fmt		= imx8mq_mipi_csi_set_fmt,
667f33fd8d7SMartin Kepplinger };
668f33fd8d7SMartin Kepplinger 
669f33fd8d7SMartin Kepplinger static const struct v4l2_subdev_ops imx8mq_mipi_csi_subdev_ops = {
670f33fd8d7SMartin Kepplinger 	.video	= &imx8mq_mipi_csi_video_ops,
671f33fd8d7SMartin Kepplinger 	.pad	= &imx8mq_mipi_csi_pad_ops,
672f33fd8d7SMartin Kepplinger };
673f33fd8d7SMartin Kepplinger 
6745755be5fSLaurent Pinchart static const struct v4l2_subdev_internal_ops imx8mq_mipi_csi_internal_ops = {
6755755be5fSLaurent Pinchart 	.init_state		= imx8mq_mipi_csi_init_state,
6765755be5fSLaurent Pinchart };
6775755be5fSLaurent Pinchart 
678f33fd8d7SMartin Kepplinger /* -----------------------------------------------------------------------------
679f33fd8d7SMartin Kepplinger  * Media entity operations
680f33fd8d7SMartin Kepplinger  */
681f33fd8d7SMartin Kepplinger 
682f33fd8d7SMartin Kepplinger static const struct media_entity_operations imx8mq_mipi_csi_entity_ops = {
683f33fd8d7SMartin Kepplinger 	.link_validate	= v4l2_subdev_link_validate,
684f33fd8d7SMartin Kepplinger 	.get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
685f33fd8d7SMartin Kepplinger };
686f33fd8d7SMartin Kepplinger 
687f33fd8d7SMartin Kepplinger /* -----------------------------------------------------------------------------
688f33fd8d7SMartin Kepplinger  * Async subdev notifier
689f33fd8d7SMartin Kepplinger  */
690f33fd8d7SMartin Kepplinger 
691f33fd8d7SMartin Kepplinger static struct csi_state *
mipi_notifier_to_csi2_state(struct v4l2_async_notifier * n)692f33fd8d7SMartin Kepplinger mipi_notifier_to_csi2_state(struct v4l2_async_notifier *n)
693f33fd8d7SMartin Kepplinger {
694f33fd8d7SMartin Kepplinger 	return container_of(n, struct csi_state, notifier);
695f33fd8d7SMartin Kepplinger }
696f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_notify_bound(struct v4l2_async_notifier * notifier,struct v4l2_subdev * sd,struct v4l2_async_connection * asd)697f33fd8d7SMartin Kepplinger static int imx8mq_mipi_csi_notify_bound(struct v4l2_async_notifier *notifier,
698f33fd8d7SMartin Kepplinger 					struct v4l2_subdev *sd,
699adb2dcd5SSakari Ailus 					struct v4l2_async_connection *asd)
700f33fd8d7SMartin Kepplinger {
701f33fd8d7SMartin Kepplinger 	struct csi_state *state = mipi_notifier_to_csi2_state(notifier);
702f33fd8d7SMartin Kepplinger 	struct media_pad *sink = &state->sd.entity.pads[MIPI_CSI2_PAD_SINK];
703f33fd8d7SMartin Kepplinger 
704f33fd8d7SMartin Kepplinger 	state->src_sd = sd;
705f33fd8d7SMartin Kepplinger 
706f33fd8d7SMartin Kepplinger 	return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
707f33fd8d7SMartin Kepplinger 					       MEDIA_LNK_FL_IMMUTABLE);
708f33fd8d7SMartin Kepplinger }
709f33fd8d7SMartin Kepplinger 
710f33fd8d7SMartin Kepplinger static const struct v4l2_async_notifier_operations imx8mq_mipi_csi_notify_ops = {
711f33fd8d7SMartin Kepplinger 	.bound = imx8mq_mipi_csi_notify_bound,
712f33fd8d7SMartin Kepplinger };
713f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_async_register(struct csi_state * state)714f33fd8d7SMartin Kepplinger static int imx8mq_mipi_csi_async_register(struct csi_state *state)
715f33fd8d7SMartin Kepplinger {
716f33fd8d7SMartin Kepplinger 	struct v4l2_fwnode_endpoint vep = {
717f33fd8d7SMartin Kepplinger 		.bus_type = V4L2_MBUS_CSI2_DPHY,
718f33fd8d7SMartin Kepplinger 	};
719adb2dcd5SSakari Ailus 	struct v4l2_async_connection *asd;
720f33fd8d7SMartin Kepplinger 	struct fwnode_handle *ep;
721f33fd8d7SMartin Kepplinger 	unsigned int i;
722f33fd8d7SMartin Kepplinger 	int ret;
723f33fd8d7SMartin Kepplinger 
724b8ec754aSSakari Ailus 	v4l2_async_subdev_nf_init(&state->notifier, &state->sd);
725f33fd8d7SMartin Kepplinger 
726f33fd8d7SMartin Kepplinger 	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0,
727f33fd8d7SMartin Kepplinger 					     FWNODE_GRAPH_ENDPOINT_NEXT);
728f33fd8d7SMartin Kepplinger 	if (!ep)
729f33fd8d7SMartin Kepplinger 		return -ENOTCONN;
730f33fd8d7SMartin Kepplinger 
731f33fd8d7SMartin Kepplinger 	ret = v4l2_fwnode_endpoint_parse(ep, &vep);
732f33fd8d7SMartin Kepplinger 	if (ret)
733f33fd8d7SMartin Kepplinger 		goto err_parse;
734f33fd8d7SMartin Kepplinger 
735f33fd8d7SMartin Kepplinger 	for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) {
736f33fd8d7SMartin Kepplinger 		if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) {
737f33fd8d7SMartin Kepplinger 			dev_err(state->dev,
738f33fd8d7SMartin Kepplinger 				"data lanes reordering is not supported");
739f33fd8d7SMartin Kepplinger 			ret = -EINVAL;
740f33fd8d7SMartin Kepplinger 			goto err_parse;
741f33fd8d7SMartin Kepplinger 		}
742f33fd8d7SMartin Kepplinger 	}
743f33fd8d7SMartin Kepplinger 
744f33fd8d7SMartin Kepplinger 	state->bus = vep.bus.mipi_csi2;
745f33fd8d7SMartin Kepplinger 
746f33fd8d7SMartin Kepplinger 	dev_dbg(state->dev, "data lanes: %d flags: 0x%08x\n",
747f33fd8d7SMartin Kepplinger 		state->bus.num_data_lanes,
748f33fd8d7SMartin Kepplinger 		state->bus.flags);
749f33fd8d7SMartin Kepplinger 
7503c8c1539SSakari Ailus 	asd = v4l2_async_nf_add_fwnode_remote(&state->notifier, ep,
751adb2dcd5SSakari Ailus 					      struct v4l2_async_connection);
752f33fd8d7SMartin Kepplinger 	if (IS_ERR(asd)) {
753f33fd8d7SMartin Kepplinger 		ret = PTR_ERR(asd);
754f33fd8d7SMartin Kepplinger 		goto err_parse;
755f33fd8d7SMartin Kepplinger 	}
756f33fd8d7SMartin Kepplinger 
757f33fd8d7SMartin Kepplinger 	fwnode_handle_put(ep);
758f33fd8d7SMartin Kepplinger 
759f33fd8d7SMartin Kepplinger 	state->notifier.ops = &imx8mq_mipi_csi_notify_ops;
760f33fd8d7SMartin Kepplinger 
761b8ec754aSSakari Ailus 	ret = v4l2_async_nf_register(&state->notifier);
762f33fd8d7SMartin Kepplinger 	if (ret)
763f33fd8d7SMartin Kepplinger 		return ret;
764f33fd8d7SMartin Kepplinger 
765f33fd8d7SMartin Kepplinger 	return v4l2_async_register_subdev(&state->sd);
766f33fd8d7SMartin Kepplinger 
767f33fd8d7SMartin Kepplinger err_parse:
768f33fd8d7SMartin Kepplinger 	fwnode_handle_put(ep);
769f33fd8d7SMartin Kepplinger 
770f33fd8d7SMartin Kepplinger 	return ret;
771f33fd8d7SMartin Kepplinger }
772f33fd8d7SMartin Kepplinger 
773f33fd8d7SMartin Kepplinger /* -----------------------------------------------------------------------------
774f33fd8d7SMartin Kepplinger  * Suspend/resume
775f33fd8d7SMartin Kepplinger  */
776f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_pm_suspend(struct device * dev)7773a6cddabSMuhammad Usama Anjum static void imx8mq_mipi_csi_pm_suspend(struct device *dev)
778f33fd8d7SMartin Kepplinger {
779f33fd8d7SMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
780f33fd8d7SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
781f33fd8d7SMartin Kepplinger 
782f33fd8d7SMartin Kepplinger 	mutex_lock(&state->lock);
783f33fd8d7SMartin Kepplinger 
784f33fd8d7SMartin Kepplinger 	if (state->state & ST_POWERED) {
785f33fd8d7SMartin Kepplinger 		imx8mq_mipi_csi_stop_stream(state);
786f33fd8d7SMartin Kepplinger 		imx8mq_mipi_csi_clk_disable(state);
787f33fd8d7SMartin Kepplinger 		state->state &= ~ST_POWERED;
788f33fd8d7SMartin Kepplinger 	}
789f33fd8d7SMartin Kepplinger 
790f33fd8d7SMartin Kepplinger 	mutex_unlock(&state->lock);
791f33fd8d7SMartin Kepplinger }
792f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_pm_resume(struct device * dev)793f0c2ba1eSMartin Kepplinger static int imx8mq_mipi_csi_pm_resume(struct device *dev)
794f33fd8d7SMartin Kepplinger {
795f33fd8d7SMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
796f33fd8d7SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
79736f6b2a3SMartin Kepplinger 	struct v4l2_subdev_state *sd_state;
798f33fd8d7SMartin Kepplinger 	int ret = 0;
799f33fd8d7SMartin Kepplinger 
800f33fd8d7SMartin Kepplinger 	mutex_lock(&state->lock);
801f33fd8d7SMartin Kepplinger 
802f33fd8d7SMartin Kepplinger 	if (!(state->state & ST_POWERED)) {
803f33fd8d7SMartin Kepplinger 		state->state |= ST_POWERED;
804f33fd8d7SMartin Kepplinger 		ret = imx8mq_mipi_csi_clk_enable(state);
805f33fd8d7SMartin Kepplinger 	}
806f33fd8d7SMartin Kepplinger 	if (state->state & ST_STREAMING) {
80736f6b2a3SMartin Kepplinger 		sd_state = v4l2_subdev_lock_and_get_active_state(sd);
80836f6b2a3SMartin Kepplinger 		ret = imx8mq_mipi_csi_start_stream(state, sd_state);
80936f6b2a3SMartin Kepplinger 		v4l2_subdev_unlock_state(sd_state);
810f33fd8d7SMartin Kepplinger 		if (ret)
811f33fd8d7SMartin Kepplinger 			goto unlock;
812f33fd8d7SMartin Kepplinger 	}
813f33fd8d7SMartin Kepplinger 
814f33fd8d7SMartin Kepplinger 	state->state &= ~ST_SUSPENDED;
815f33fd8d7SMartin Kepplinger 
816f33fd8d7SMartin Kepplinger unlock:
817f33fd8d7SMartin Kepplinger 	mutex_unlock(&state->lock);
818f33fd8d7SMartin Kepplinger 
819f33fd8d7SMartin Kepplinger 	return ret ? -EAGAIN : 0;
820f33fd8d7SMartin Kepplinger }
821f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_suspend(struct device * dev)8224fb5babeSFabio Estevam static int imx8mq_mipi_csi_suspend(struct device *dev)
823f33fd8d7SMartin Kepplinger {
824f0c2ba1eSMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
825f0c2ba1eSMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
826f0c2ba1eSMartin Kepplinger 
8273a6cddabSMuhammad Usama Anjum 	imx8mq_mipi_csi_pm_suspend(dev);
828f0c2ba1eSMartin Kepplinger 
829f0c2ba1eSMartin Kepplinger 	state->state |= ST_SUSPENDED;
830f0c2ba1eSMartin Kepplinger 
8313a6cddabSMuhammad Usama Anjum 	return 0;
832f33fd8d7SMartin Kepplinger }
833f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_resume(struct device * dev)8344fb5babeSFabio Estevam static int imx8mq_mipi_csi_resume(struct device *dev)
835f33fd8d7SMartin Kepplinger {
836f0c2ba1eSMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
837f0c2ba1eSMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
838f0c2ba1eSMartin Kepplinger 
839f0c2ba1eSMartin Kepplinger 	if (!(state->state & ST_SUSPENDED))
840f0c2ba1eSMartin Kepplinger 		return 0;
841f0c2ba1eSMartin Kepplinger 
842f0c2ba1eSMartin Kepplinger 	return imx8mq_mipi_csi_pm_resume(dev);
843f33fd8d7SMartin Kepplinger }
844f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_runtime_suspend(struct device * dev)8454fb5babeSFabio Estevam static int imx8mq_mipi_csi_runtime_suspend(struct device *dev)
846f33fd8d7SMartin Kepplinger {
847f0c2ba1eSMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
848f0c2ba1eSMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
849f0c2ba1eSMartin Kepplinger 	int ret;
850f0c2ba1eSMartin Kepplinger 
8513a6cddabSMuhammad Usama Anjum 	imx8mq_mipi_csi_pm_suspend(dev);
852f0c2ba1eSMartin Kepplinger 
853f0c2ba1eSMartin Kepplinger 	ret = icc_set_bw(state->icc_path, 0, 0);
854f0c2ba1eSMartin Kepplinger 	if (ret)
855f0c2ba1eSMartin Kepplinger 		dev_err(dev, "icc_set_bw failed with %d\n", ret);
856f0c2ba1eSMartin Kepplinger 
857f0c2ba1eSMartin Kepplinger 	return ret;
858f33fd8d7SMartin Kepplinger }
859f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_runtime_resume(struct device * dev)8604fb5babeSFabio Estevam static int imx8mq_mipi_csi_runtime_resume(struct device *dev)
861f33fd8d7SMartin Kepplinger {
862f0c2ba1eSMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
863f0c2ba1eSMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
864f0c2ba1eSMartin Kepplinger 	int ret;
865f0c2ba1eSMartin Kepplinger 
866f0c2ba1eSMartin Kepplinger 	ret = icc_set_bw(state->icc_path, 0, state->icc_path_bw);
867f0c2ba1eSMartin Kepplinger 	if (ret) {
868f0c2ba1eSMartin Kepplinger 		dev_err(dev, "icc_set_bw failed with %d\n", ret);
869f0c2ba1eSMartin Kepplinger 		return ret;
870f0c2ba1eSMartin Kepplinger 	}
871f0c2ba1eSMartin Kepplinger 
872f0c2ba1eSMartin Kepplinger 	return imx8mq_mipi_csi_pm_resume(dev);
873f33fd8d7SMartin Kepplinger }
874f33fd8d7SMartin Kepplinger 
875f33fd8d7SMartin Kepplinger static const struct dev_pm_ops imx8mq_mipi_csi_pm_ops = {
8764fb5babeSFabio Estevam 	RUNTIME_PM_OPS(imx8mq_mipi_csi_runtime_suspend,
8774fb5babeSFabio Estevam 		       imx8mq_mipi_csi_runtime_resume, NULL)
8784fb5babeSFabio Estevam 	SYSTEM_SLEEP_PM_OPS(imx8mq_mipi_csi_suspend, imx8mq_mipi_csi_resume)
879f33fd8d7SMartin Kepplinger };
880f33fd8d7SMartin Kepplinger 
881f33fd8d7SMartin Kepplinger /* -----------------------------------------------------------------------------
882f33fd8d7SMartin Kepplinger  * Probe/remove & platform driver
883f33fd8d7SMartin Kepplinger  */
884f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_subdev_init(struct csi_state * state)885f33fd8d7SMartin Kepplinger static int imx8mq_mipi_csi_subdev_init(struct csi_state *state)
886f33fd8d7SMartin Kepplinger {
887f33fd8d7SMartin Kepplinger 	struct v4l2_subdev *sd = &state->sd;
88836f6b2a3SMartin Kepplinger 	int ret;
889f33fd8d7SMartin Kepplinger 
890f33fd8d7SMartin Kepplinger 	v4l2_subdev_init(sd, &imx8mq_mipi_csi_subdev_ops);
8915755be5fSLaurent Pinchart 	sd->internal_ops = &imx8mq_mipi_csi_internal_ops;
892f33fd8d7SMartin Kepplinger 	sd->owner = THIS_MODULE;
893f33fd8d7SMartin Kepplinger 	snprintf(sd->name, sizeof(sd->name), "%s %s",
894f33fd8d7SMartin Kepplinger 		 MIPI_CSI2_SUBDEV_NAME, dev_name(state->dev));
895f33fd8d7SMartin Kepplinger 
896f33fd8d7SMartin Kepplinger 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
897f33fd8d7SMartin Kepplinger 
898f33fd8d7SMartin Kepplinger 	sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
899f33fd8d7SMartin Kepplinger 	sd->entity.ops = &imx8mq_mipi_csi_entity_ops;
900f33fd8d7SMartin Kepplinger 
901f33fd8d7SMartin Kepplinger 	sd->dev = state->dev;
902f33fd8d7SMartin Kepplinger 
903f33fd8d7SMartin Kepplinger 	state->pads[MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK
904f33fd8d7SMartin Kepplinger 					 | MEDIA_PAD_FL_MUST_CONNECT;
905f33fd8d7SMartin Kepplinger 	state->pads[MIPI_CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE
906f33fd8d7SMartin Kepplinger 					   | MEDIA_PAD_FL_MUST_CONNECT;
90736f6b2a3SMartin Kepplinger 	ret = media_entity_pads_init(&sd->entity, MIPI_CSI2_PADS_NUM,
908f33fd8d7SMartin Kepplinger 				     state->pads);
90936f6b2a3SMartin Kepplinger 	if (ret)
91036f6b2a3SMartin Kepplinger 		return ret;
91136f6b2a3SMartin Kepplinger 
91236f6b2a3SMartin Kepplinger 	ret = v4l2_subdev_init_finalize(sd);
91336f6b2a3SMartin Kepplinger 	if (ret) {
91436f6b2a3SMartin Kepplinger 		media_entity_cleanup(&sd->entity);
91536f6b2a3SMartin Kepplinger 		return ret;
91636f6b2a3SMartin Kepplinger 	}
91736f6b2a3SMartin Kepplinger 
91836f6b2a3SMartin Kepplinger 	return 0;
919f33fd8d7SMartin Kepplinger }
920f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_release_icc(struct platform_device * pdev)921f33fd8d7SMartin Kepplinger static void imx8mq_mipi_csi_release_icc(struct platform_device *pdev)
922f33fd8d7SMartin Kepplinger {
923f33fd8d7SMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
924f33fd8d7SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
925f33fd8d7SMartin Kepplinger 
926f33fd8d7SMartin Kepplinger 	icc_put(state->icc_path);
927f33fd8d7SMartin Kepplinger }
928f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_init_icc(struct platform_device * pdev)929f33fd8d7SMartin Kepplinger static int imx8mq_mipi_csi_init_icc(struct platform_device *pdev)
930f33fd8d7SMartin Kepplinger {
931f33fd8d7SMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
932f33fd8d7SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
933f33fd8d7SMartin Kepplinger 
934f33fd8d7SMartin Kepplinger 	/* Optional interconnect request */
935f33fd8d7SMartin Kepplinger 	state->icc_path = of_icc_get(&pdev->dev, "dram");
936f33fd8d7SMartin Kepplinger 	if (IS_ERR_OR_NULL(state->icc_path))
937f33fd8d7SMartin Kepplinger 		return PTR_ERR_OR_ZERO(state->icc_path);
938f33fd8d7SMartin Kepplinger 
939f33fd8d7SMartin Kepplinger 	state->icc_path_bw = MBps_to_icc(700);
940f33fd8d7SMartin Kepplinger 
941f33fd8d7SMartin Kepplinger 	return 0;
942f33fd8d7SMartin Kepplinger }
943f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_parse_dt(struct csi_state * state)944f33fd8d7SMartin Kepplinger static int imx8mq_mipi_csi_parse_dt(struct csi_state *state)
945f33fd8d7SMartin Kepplinger {
946f33fd8d7SMartin Kepplinger 	struct device *dev = state->dev;
947f33fd8d7SMartin Kepplinger 	struct device_node *np = state->dev->of_node;
948f33fd8d7SMartin Kepplinger 	struct device_node *node;
949f33fd8d7SMartin Kepplinger 	phandle ph;
950f33fd8d7SMartin Kepplinger 	u32 out_val[2];
951f33fd8d7SMartin Kepplinger 	int ret = 0;
952f33fd8d7SMartin Kepplinger 
953f33fd8d7SMartin Kepplinger 	state->rst = devm_reset_control_array_get_exclusive(dev);
954f33fd8d7SMartin Kepplinger 	if (IS_ERR(state->rst)) {
955f33fd8d7SMartin Kepplinger 		dev_err(dev, "Failed to get reset: %pe\n", state->rst);
956f33fd8d7SMartin Kepplinger 		return PTR_ERR(state->rst);
957f33fd8d7SMartin Kepplinger 	}
958f33fd8d7SMartin Kepplinger 
959642b70d5SFrank Li 	if (state->pdata->use_reg_csr) {
960642b70d5SFrank Li 		const struct regmap_config regmap_config = {
961642b70d5SFrank Li 			.reg_bits = 32,
962642b70d5SFrank Li 			.val_bits = 32,
963642b70d5SFrank Li 			.reg_stride = 4,
964642b70d5SFrank Li 		};
965642b70d5SFrank Li 		void __iomem *base;
966642b70d5SFrank Li 
967642b70d5SFrank Li 		base = devm_platform_ioremap_resource(to_platform_device(dev), 1);
968642b70d5SFrank Li 		if (IS_ERR(base))
969*cee06ca7SDan Carpenter 			return dev_err_probe(dev, PTR_ERR(base), "Missing CSR register\n");
970642b70d5SFrank Li 
971642b70d5SFrank Li 		state->phy_gpr = devm_regmap_init_mmio(dev, base, &regmap_config);
972642b70d5SFrank Li 		if (IS_ERR(state->phy_gpr))
973642b70d5SFrank Li 			return dev_err_probe(dev, PTR_ERR(state->phy_gpr),
974642b70d5SFrank Li 					     "Failed to init CSI MMIO regmap\n");
975642b70d5SFrank Li 		return 0;
976642b70d5SFrank Li 	}
977642b70d5SFrank Li 
978f33fd8d7SMartin Kepplinger 	ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val,
979f33fd8d7SMartin Kepplinger 					 ARRAY_SIZE(out_val));
980f33fd8d7SMartin Kepplinger 	if (ret) {
981f33fd8d7SMartin Kepplinger 		dev_err(dev, "no fsl,mipi-phy-gpr property found: %d\n", ret);
982f33fd8d7SMartin Kepplinger 		return ret;
983f33fd8d7SMartin Kepplinger 	}
984f33fd8d7SMartin Kepplinger 
985f33fd8d7SMartin Kepplinger 	ph = *out_val;
986f33fd8d7SMartin Kepplinger 
987f33fd8d7SMartin Kepplinger 	node = of_find_node_by_phandle(ph);
988f33fd8d7SMartin Kepplinger 	if (!node) {
989f33fd8d7SMartin Kepplinger 		dev_err(dev, "Error finding node by phandle\n");
990f33fd8d7SMartin Kepplinger 		return -ENODEV;
991f33fd8d7SMartin Kepplinger 	}
992f33fd8d7SMartin Kepplinger 	state->phy_gpr = syscon_node_to_regmap(node);
993f33fd8d7SMartin Kepplinger 	of_node_put(node);
994f33fd8d7SMartin Kepplinger 	if (IS_ERR(state->phy_gpr)) {
995f33fd8d7SMartin Kepplinger 		dev_err(dev, "failed to get gpr regmap: %pe\n", state->phy_gpr);
996f33fd8d7SMartin Kepplinger 		return PTR_ERR(state->phy_gpr);
997f33fd8d7SMartin Kepplinger 	}
998f33fd8d7SMartin Kepplinger 
999f33fd8d7SMartin Kepplinger 	state->phy_gpr_reg = out_val[1];
1000f33fd8d7SMartin Kepplinger 	dev_dbg(dev, "phy gpr register set to 0x%x\n", state->phy_gpr_reg);
1001f33fd8d7SMartin Kepplinger 
1002f33fd8d7SMartin Kepplinger 	return ret;
1003f33fd8d7SMartin Kepplinger }
1004f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_probe(struct platform_device * pdev)1005f33fd8d7SMartin Kepplinger static int imx8mq_mipi_csi_probe(struct platform_device *pdev)
1006f33fd8d7SMartin Kepplinger {
1007f33fd8d7SMartin Kepplinger 	struct device *dev = &pdev->dev;
1008f33fd8d7SMartin Kepplinger 	struct csi_state *state;
1009f33fd8d7SMartin Kepplinger 	int ret;
1010f33fd8d7SMartin Kepplinger 
1011f33fd8d7SMartin Kepplinger 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
1012f33fd8d7SMartin Kepplinger 	if (!state)
1013f33fd8d7SMartin Kepplinger 		return -ENOMEM;
1014f33fd8d7SMartin Kepplinger 
1015f33fd8d7SMartin Kepplinger 	state->dev = dev;
1016f33fd8d7SMartin Kepplinger 
1017382d53e9SGuoniu.zhou 	state->pdata = of_device_get_match_data(dev);
1018382d53e9SGuoniu.zhou 
1019f33fd8d7SMartin Kepplinger 	ret = imx8mq_mipi_csi_parse_dt(state);
1020f33fd8d7SMartin Kepplinger 	if (ret < 0) {
1021f33fd8d7SMartin Kepplinger 		dev_err(dev, "Failed to parse device tree: %d\n", ret);
1022f33fd8d7SMartin Kepplinger 		return ret;
1023f33fd8d7SMartin Kepplinger 	}
1024f33fd8d7SMartin Kepplinger 
1025f33fd8d7SMartin Kepplinger 	/* Acquire resources. */
1026f33fd8d7SMartin Kepplinger 	state->regs = devm_platform_ioremap_resource(pdev, 0);
1027f33fd8d7SMartin Kepplinger 	if (IS_ERR(state->regs))
1028f33fd8d7SMartin Kepplinger 		return PTR_ERR(state->regs);
1029f33fd8d7SMartin Kepplinger 
1030f33fd8d7SMartin Kepplinger 	ret = imx8mq_mipi_csi_clk_get(state);
1031f33fd8d7SMartin Kepplinger 	if (ret < 0)
1032f33fd8d7SMartin Kepplinger 		return ret;
1033f33fd8d7SMartin Kepplinger 
1034f33fd8d7SMartin Kepplinger 	platform_set_drvdata(pdev, &state->sd);
1035f33fd8d7SMartin Kepplinger 
1036f33fd8d7SMartin Kepplinger 	mutex_init(&state->lock);
1037f33fd8d7SMartin Kepplinger 
1038f33fd8d7SMartin Kepplinger 	ret = imx8mq_mipi_csi_subdev_init(state);
1039f33fd8d7SMartin Kepplinger 	if (ret < 0)
1040f33fd8d7SMartin Kepplinger 		goto mutex;
1041f33fd8d7SMartin Kepplinger 
1042f33fd8d7SMartin Kepplinger 	ret = imx8mq_mipi_csi_init_icc(pdev);
1043f33fd8d7SMartin Kepplinger 	if (ret)
1044f33fd8d7SMartin Kepplinger 		goto mutex;
1045f33fd8d7SMartin Kepplinger 
1046f33fd8d7SMartin Kepplinger 	/* Enable runtime PM. */
1047f33fd8d7SMartin Kepplinger 	pm_runtime_enable(dev);
1048f33fd8d7SMartin Kepplinger 	if (!pm_runtime_enabled(dev)) {
1049f0c2ba1eSMartin Kepplinger 		ret = imx8mq_mipi_csi_runtime_resume(dev);
1050f33fd8d7SMartin Kepplinger 		if (ret < 0)
1051f33fd8d7SMartin Kepplinger 			goto icc;
1052f33fd8d7SMartin Kepplinger 	}
1053f33fd8d7SMartin Kepplinger 
1054f33fd8d7SMartin Kepplinger 	ret = imx8mq_mipi_csi_async_register(state);
1055f33fd8d7SMartin Kepplinger 	if (ret < 0)
1056f33fd8d7SMartin Kepplinger 		goto cleanup;
1057f33fd8d7SMartin Kepplinger 
1058f33fd8d7SMartin Kepplinger 	return 0;
1059f33fd8d7SMartin Kepplinger 
1060f33fd8d7SMartin Kepplinger cleanup:
1061f33fd8d7SMartin Kepplinger 	pm_runtime_disable(&pdev->dev);
1062f0c2ba1eSMartin Kepplinger 	imx8mq_mipi_csi_runtime_suspend(&pdev->dev);
1063f33fd8d7SMartin Kepplinger 
1064f33fd8d7SMartin Kepplinger 	media_entity_cleanup(&state->sd.entity);
106536f6b2a3SMartin Kepplinger 	v4l2_subdev_cleanup(&state->sd);
10663c8c1539SSakari Ailus 	v4l2_async_nf_unregister(&state->notifier);
10673c8c1539SSakari Ailus 	v4l2_async_nf_cleanup(&state->notifier);
1068f33fd8d7SMartin Kepplinger 	v4l2_async_unregister_subdev(&state->sd);
1069f33fd8d7SMartin Kepplinger icc:
1070f33fd8d7SMartin Kepplinger 	imx8mq_mipi_csi_release_icc(pdev);
1071f33fd8d7SMartin Kepplinger mutex:
1072f33fd8d7SMartin Kepplinger 	mutex_destroy(&state->lock);
1073f33fd8d7SMartin Kepplinger 
1074f33fd8d7SMartin Kepplinger 	return ret;
1075f33fd8d7SMartin Kepplinger }
1076f33fd8d7SMartin Kepplinger 
imx8mq_mipi_csi_remove(struct platform_device * pdev)1077162a87b5SUwe Kleine-König static void imx8mq_mipi_csi_remove(struct platform_device *pdev)
1078f33fd8d7SMartin Kepplinger {
1079f33fd8d7SMartin Kepplinger 	struct v4l2_subdev *sd = platform_get_drvdata(pdev);
1080f33fd8d7SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
1081f33fd8d7SMartin Kepplinger 
10823c8c1539SSakari Ailus 	v4l2_async_nf_unregister(&state->notifier);
10833c8c1539SSakari Ailus 	v4l2_async_nf_cleanup(&state->notifier);
1084f33fd8d7SMartin Kepplinger 	v4l2_async_unregister_subdev(&state->sd);
1085f33fd8d7SMartin Kepplinger 
1086f33fd8d7SMartin Kepplinger 	pm_runtime_disable(&pdev->dev);
1087f0c2ba1eSMartin Kepplinger 	imx8mq_mipi_csi_runtime_suspend(&pdev->dev);
1088f33fd8d7SMartin Kepplinger 	media_entity_cleanup(&state->sd.entity);
108936f6b2a3SMartin Kepplinger 	v4l2_subdev_cleanup(&state->sd);
1090f33fd8d7SMartin Kepplinger 	mutex_destroy(&state->lock);
1091f33fd8d7SMartin Kepplinger 	pm_runtime_set_suspended(&pdev->dev);
1092f33fd8d7SMartin Kepplinger 	imx8mq_mipi_csi_release_icc(pdev);
1093f33fd8d7SMartin Kepplinger }
1094f33fd8d7SMartin Kepplinger 
1095f33fd8d7SMartin Kepplinger static const struct of_device_id imx8mq_mipi_csi_of_match[] = {
1096382d53e9SGuoniu.zhou 	{ .compatible = "fsl,imx8mq-mipi-csi2", .data = &imx8mq_data },
1097642b70d5SFrank Li 	{ .compatible = "fsl,imx8qxp-mipi-csi2", .data = &imx8qxp_data },
1098f33fd8d7SMartin Kepplinger 	{ /* sentinel */ },
1099f33fd8d7SMartin Kepplinger };
1100f33fd8d7SMartin Kepplinger MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match);
1101f33fd8d7SMartin Kepplinger 
1102f33fd8d7SMartin Kepplinger static struct platform_driver imx8mq_mipi_csi_driver = {
1103f33fd8d7SMartin Kepplinger 	.probe		= imx8mq_mipi_csi_probe,
1104b8fc42dcSUwe Kleine-König 	.remove		= imx8mq_mipi_csi_remove,
1105f33fd8d7SMartin Kepplinger 	.driver		= {
1106f33fd8d7SMartin Kepplinger 		.of_match_table = imx8mq_mipi_csi_of_match,
1107f33fd8d7SMartin Kepplinger 		.name		= MIPI_CSI2_DRIVER_NAME,
11084fb5babeSFabio Estevam 		.pm		= pm_ptr(&imx8mq_mipi_csi_pm_ops),
1109f33fd8d7SMartin Kepplinger 	},
1110f33fd8d7SMartin Kepplinger };
1111f33fd8d7SMartin Kepplinger 
1112f33fd8d7SMartin Kepplinger module_platform_driver(imx8mq_mipi_csi_driver);
1113f33fd8d7SMartin Kepplinger 
1114f33fd8d7SMartin Kepplinger MODULE_DESCRIPTION("i.MX8MQ MIPI CSI-2 receiver driver");
1115f33fd8d7SMartin Kepplinger MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@puri.sm>");
1116f33fd8d7SMartin Kepplinger MODULE_LICENSE("GPL v2");
1117f33fd8d7SMartin Kepplinger MODULE_ALIAS("platform:imx8mq-mipi-csi2");
1118