xref: /openbmc/linux/drivers/media/platform/nxp/imx8mq-mipi-csi2.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1cd063027SMartin Kepplinger // SPDX-License-Identifier: GPL-2.0
2cd063027SMartin Kepplinger /*
3cd063027SMartin Kepplinger  * NXP i.MX8MQ SoC series MIPI-CSI2 receiver driver
4cd063027SMartin Kepplinger  *
5cd063027SMartin Kepplinger  * Copyright (C) 2021 Purism SPC
6cd063027SMartin Kepplinger  */
7cd063027SMartin Kepplinger 
8cd063027SMartin Kepplinger #include <linux/clk.h>
9cd063027SMartin Kepplinger #include <linux/clk-provider.h>
10cd063027SMartin Kepplinger #include <linux/delay.h>
11cd063027SMartin Kepplinger #include <linux/errno.h>
12cd063027SMartin Kepplinger #include <linux/interconnect.h>
13cd063027SMartin Kepplinger #include <linux/interrupt.h>
14cd063027SMartin Kepplinger #include <linux/io.h>
15cd063027SMartin Kepplinger #include <linux/kernel.h>
16cd063027SMartin Kepplinger #include <linux/mfd/syscon.h>
17cd063027SMartin Kepplinger #include <linux/module.h>
18cd063027SMartin Kepplinger #include <linux/mutex.h>
19cd063027SMartin Kepplinger #include <linux/of.h>
20cd063027SMartin Kepplinger #include <linux/platform_device.h>
21cd063027SMartin Kepplinger #include <linux/pm_runtime.h>
22cd063027SMartin Kepplinger #include <linux/regmap.h>
23cd063027SMartin Kepplinger #include <linux/regulator/consumer.h>
24cd063027SMartin Kepplinger #include <linux/reset.h>
25cd063027SMartin Kepplinger #include <linux/spinlock.h>
26cd063027SMartin Kepplinger 
27cd063027SMartin Kepplinger #include <media/v4l2-common.h>
28cd063027SMartin Kepplinger #include <media/v4l2-device.h>
29cd063027SMartin Kepplinger #include <media/v4l2-fwnode.h>
30cd063027SMartin Kepplinger #include <media/v4l2-mc.h>
31cd063027SMartin Kepplinger #include <media/v4l2-subdev.h>
32cd063027SMartin Kepplinger 
33cd063027SMartin Kepplinger #define MIPI_CSI2_DRIVER_NAME			"imx8mq-mipi-csi2"
34cd063027SMartin Kepplinger #define MIPI_CSI2_SUBDEV_NAME			MIPI_CSI2_DRIVER_NAME
35cd063027SMartin Kepplinger 
36cd063027SMartin Kepplinger #define MIPI_CSI2_PAD_SINK			0
37cd063027SMartin Kepplinger #define MIPI_CSI2_PAD_SOURCE			1
38cd063027SMartin Kepplinger #define MIPI_CSI2_PADS_NUM			2
39cd063027SMartin Kepplinger 
40cd063027SMartin Kepplinger #define MIPI_CSI2_DEF_PIX_WIDTH			640
41cd063027SMartin Kepplinger #define MIPI_CSI2_DEF_PIX_HEIGHT		480
42cd063027SMartin Kepplinger 
43cd063027SMartin Kepplinger /* Register map definition */
44cd063027SMartin Kepplinger 
45cd063027SMartin Kepplinger /* i.MX8MQ CSI-2 controller CSR */
46cd063027SMartin Kepplinger #define CSI2RX_CFG_NUM_LANES			0x100
47cd063027SMartin Kepplinger #define CSI2RX_CFG_DISABLE_DATA_LANES		0x104
48cd063027SMartin Kepplinger #define CSI2RX_BIT_ERR				0x108
49cd063027SMartin Kepplinger #define CSI2RX_IRQ_STATUS			0x10c
50cd063027SMartin Kepplinger #define CSI2RX_IRQ_MASK				0x110
51cd063027SMartin Kepplinger #define CSI2RX_IRQ_MASK_ALL			0x1ff
52cd063027SMartin Kepplinger #define CSI2RX_IRQ_MASK_ULPS_STATUS_CHANGE	0x8
53cd063027SMartin Kepplinger #define CSI2RX_ULPS_STATUS			0x114
54cd063027SMartin Kepplinger #define CSI2RX_PPI_ERRSOT_HS			0x118
55cd063027SMartin Kepplinger #define CSI2RX_PPI_ERRSOTSYNC_HS		0x11c
56cd063027SMartin Kepplinger #define CSI2RX_PPI_ERRESC			0x120
57cd063027SMartin Kepplinger #define CSI2RX_PPI_ERRSYNCESC			0x124
58cd063027SMartin Kepplinger #define CSI2RX_PPI_ERRCONTROL			0x128
59cd063027SMartin Kepplinger #define CSI2RX_CFG_DISABLE_PAYLOAD_0		0x12c
60cd063027SMartin Kepplinger #define CSI2RX_CFG_VID_VC_IGNORE		0x180
61cd063027SMartin Kepplinger #define CSI2RX_CFG_VID_VC			0x184
62cd063027SMartin Kepplinger #define CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL	0x188
63cd063027SMartin Kepplinger #define CSI2RX_CFG_DISABLE_PAYLOAD_1		0x130
64cd063027SMartin Kepplinger 
65cd063027SMartin Kepplinger enum {
66cd063027SMartin Kepplinger 	ST_POWERED	= 1,
67cd063027SMartin Kepplinger 	ST_STREAMING	= 2,
68cd063027SMartin Kepplinger 	ST_SUSPENDED	= 4,
69cd063027SMartin Kepplinger };
70cd063027SMartin Kepplinger 
71cd063027SMartin Kepplinger enum imx8mq_mipi_csi_clk {
72cd063027SMartin Kepplinger 	CSI2_CLK_CORE,
73cd063027SMartin Kepplinger 	CSI2_CLK_ESC,
74cd063027SMartin Kepplinger 	CSI2_CLK_UI,
75cd063027SMartin Kepplinger 	CSI2_NUM_CLKS,
76cd063027SMartin Kepplinger };
77cd063027SMartin Kepplinger 
78cd063027SMartin Kepplinger static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = {
79cd063027SMartin Kepplinger 	[CSI2_CLK_CORE] = "core",
80cd063027SMartin Kepplinger 	[CSI2_CLK_ESC] = "esc",
81cd063027SMartin Kepplinger 	[CSI2_CLK_UI] = "ui",
82cd063027SMartin Kepplinger };
83cd063027SMartin Kepplinger 
84cd063027SMartin Kepplinger #define CSI2_NUM_CLKS	ARRAY_SIZE(imx8mq_mipi_csi_clk_id)
85cd063027SMartin Kepplinger 
86cd063027SMartin Kepplinger #define	GPR_CSI2_1_RX_ENABLE		BIT(13)
87cd063027SMartin Kepplinger #define	GPR_CSI2_1_VID_INTFC_ENB	BIT(12)
88cd063027SMartin Kepplinger #define	GPR_CSI2_1_HSEL			BIT(10)
89cd063027SMartin Kepplinger #define	GPR_CSI2_1_CONT_CLK_MODE	BIT(8)
90cd063027SMartin Kepplinger #define	GPR_CSI2_1_S_PRG_RXHS_SETTLE(x)	(((x) & 0x3f) << 2)
91cd063027SMartin Kepplinger 
92cd063027SMartin Kepplinger /*
93cd063027SMartin Kepplinger  * The send level configures the number of entries that must accumulate in
94cd063027SMartin Kepplinger  * the Pixel FIFO before the data will be transferred to the video output.
95cd063027SMartin Kepplinger  * The exact value needed for this configuration is dependent on the rate at
96cd063027SMartin Kepplinger  * which the sensor transfers data to the CSI-2 Controller and the user
97cd063027SMartin Kepplinger  * video clock.
98cd063027SMartin Kepplinger  *
99cd063027SMartin Kepplinger  * The calculation is the classical rate-in rate-out type of problem: If the
100cd063027SMartin Kepplinger  * video bandwidth is 10% faster than the incoming mipi data and the video
101cd063027SMartin Kepplinger  * line length is 500 pixels, then the fifo should be allowed to fill
102cd063027SMartin Kepplinger  * 10% of the line length or 50 pixels. If the gap data is ok, then the level
103cd063027SMartin Kepplinger  * can be set to 16 and ignored.
104cd063027SMartin Kepplinger  */
105cd063027SMartin Kepplinger #define CSI2RX_SEND_LEVEL			64
106cd063027SMartin Kepplinger 
107cd063027SMartin Kepplinger struct csi_state {
108cd063027SMartin Kepplinger 	struct device *dev;
109cd063027SMartin Kepplinger 	void __iomem *regs;
110cd063027SMartin Kepplinger 	struct clk_bulk_data clks[CSI2_NUM_CLKS];
111cd063027SMartin Kepplinger 	struct reset_control *rst;
112cd063027SMartin Kepplinger 	struct regulator *mipi_phy_regulator;
113cd063027SMartin Kepplinger 
114cd063027SMartin Kepplinger 	struct v4l2_subdev sd;
115cd063027SMartin Kepplinger 	struct media_pad pads[MIPI_CSI2_PADS_NUM];
116cd063027SMartin Kepplinger 	struct v4l2_async_notifier notifier;
117cd063027SMartin Kepplinger 	struct v4l2_subdev *src_sd;
118cd063027SMartin Kepplinger 
119cd063027SMartin Kepplinger 	struct v4l2_mbus_config_mipi_csi2 bus;
120cd063027SMartin Kepplinger 
121cd063027SMartin Kepplinger 	struct mutex lock; /* Protect state */
122cd063027SMartin Kepplinger 	u32 state;
123cd063027SMartin Kepplinger 
124cd063027SMartin Kepplinger 	struct regmap *phy_gpr;
125cd063027SMartin Kepplinger 	u8 phy_gpr_reg;
126cd063027SMartin Kepplinger 
127cd063027SMartin Kepplinger 	struct icc_path			*icc_path;
128cd063027SMartin Kepplinger 	s32				icc_path_bw;
129cd063027SMartin Kepplinger };
130cd063027SMartin Kepplinger 
131cd063027SMartin Kepplinger /* -----------------------------------------------------------------------------
132cd063027SMartin Kepplinger  * Format helpers
133cd063027SMartin Kepplinger  */
134cd063027SMartin Kepplinger 
135cd063027SMartin Kepplinger struct csi2_pix_format {
136cd063027SMartin Kepplinger 	u32 code;
137cd063027SMartin Kepplinger 	u8 width;
138cd063027SMartin Kepplinger };
139cd063027SMartin Kepplinger 
140cd063027SMartin Kepplinger static const struct csi2_pix_format imx8mq_mipi_csi_formats[] = {
141cd063027SMartin Kepplinger 	/* RAW (Bayer and greyscale) formats. */
142cd063027SMartin Kepplinger 	{
143cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
144cd063027SMartin Kepplinger 		.width = 8,
145cd063027SMartin Kepplinger 	}, {
146cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
147cd063027SMartin Kepplinger 		.width = 8,
148cd063027SMartin Kepplinger 	}, {
149cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
150cd063027SMartin Kepplinger 		.width = 8,
151cd063027SMartin Kepplinger 	}, {
152cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
153cd063027SMartin Kepplinger 		.width = 8,
154cd063027SMartin Kepplinger 	}, {
155cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_Y8_1X8,
156cd063027SMartin Kepplinger 		.width = 8,
157cd063027SMartin Kepplinger 	}, {
158cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SBGGR10_1X10,
159cd063027SMartin Kepplinger 		.width = 10,
160cd063027SMartin Kepplinger 	}, {
161cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGBRG10_1X10,
162cd063027SMartin Kepplinger 		.width = 10,
163cd063027SMartin Kepplinger 	}, {
164cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
165cd063027SMartin Kepplinger 		.width = 10,
166cd063027SMartin Kepplinger 	}, {
167cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
168cd063027SMartin Kepplinger 		.width = 10,
169cd063027SMartin Kepplinger 	}, {
170cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_Y10_1X10,
171cd063027SMartin Kepplinger 		.width = 10,
172cd063027SMartin Kepplinger 	}, {
173cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
174cd063027SMartin Kepplinger 		.width = 12,
175cd063027SMartin Kepplinger 	}, {
176cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
177cd063027SMartin Kepplinger 		.width = 12,
178cd063027SMartin Kepplinger 	}, {
179cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
180cd063027SMartin Kepplinger 		.width = 12,
181cd063027SMartin Kepplinger 	}, {
182cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
183cd063027SMartin Kepplinger 		.width = 12,
184cd063027SMartin Kepplinger 	}, {
185cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_Y12_1X12,
186cd063027SMartin Kepplinger 		.width = 12,
187cd063027SMartin Kepplinger 	}, {
188cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SBGGR14_1X14,
189cd063027SMartin Kepplinger 		.width = 14,
190cd063027SMartin Kepplinger 	}, {
191cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGBRG14_1X14,
192cd063027SMartin Kepplinger 		.width = 14,
193cd063027SMartin Kepplinger 	}, {
194cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SGRBG14_1X14,
195cd063027SMartin Kepplinger 		.width = 14,
196cd063027SMartin Kepplinger 	}, {
197cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_SRGGB14_1X14,
198cd063027SMartin Kepplinger 		.width = 14,
199cd063027SMartin Kepplinger 	},
200cd063027SMartin Kepplinger 	/* YUV formats */
201cd063027SMartin Kepplinger 	{
202cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_YUYV8_1X16,
203cd063027SMartin Kepplinger 		.width = 16,
204cd063027SMartin Kepplinger 	}, {
205cd063027SMartin Kepplinger 		.code = MEDIA_BUS_FMT_UYVY8_1X16,
206cd063027SMartin Kepplinger 		.width = 16,
207cd063027SMartin Kepplinger 	}
208cd063027SMartin Kepplinger };
209cd063027SMartin Kepplinger 
find_csi2_format(u32 code)210cd063027SMartin Kepplinger static const struct csi2_pix_format *find_csi2_format(u32 code)
211cd063027SMartin Kepplinger {
212cd063027SMartin Kepplinger 	unsigned int i;
213cd063027SMartin Kepplinger 
214cd063027SMartin Kepplinger 	for (i = 0; i < ARRAY_SIZE(imx8mq_mipi_csi_formats); i++)
215cd063027SMartin Kepplinger 		if (code == imx8mq_mipi_csi_formats[i].code)
216cd063027SMartin Kepplinger 			return &imx8mq_mipi_csi_formats[i];
217cd063027SMartin Kepplinger 	return NULL;
218cd063027SMartin Kepplinger }
219cd063027SMartin Kepplinger 
220cd063027SMartin Kepplinger /* -----------------------------------------------------------------------------
221cd063027SMartin Kepplinger  * Hardware configuration
222cd063027SMartin Kepplinger  */
223cd063027SMartin Kepplinger 
imx8mq_mipi_csi_write(struct csi_state * state,u32 reg,u32 val)224cd063027SMartin Kepplinger static inline void imx8mq_mipi_csi_write(struct csi_state *state, u32 reg, u32 val)
225cd063027SMartin Kepplinger {
226cd063027SMartin Kepplinger 	writel(val, state->regs + reg);
227cd063027SMartin Kepplinger }
228cd063027SMartin Kepplinger 
imx8mq_mipi_csi_sw_reset(struct csi_state * state)229cd063027SMartin Kepplinger static int imx8mq_mipi_csi_sw_reset(struct csi_state *state)
230cd063027SMartin Kepplinger {
231cd063027SMartin Kepplinger 	int ret;
232cd063027SMartin Kepplinger 
233cd063027SMartin Kepplinger 	/*
234cd063027SMartin Kepplinger 	 * these are most likely self-clearing reset bits. to make it
235cd063027SMartin Kepplinger 	 * more clear, the reset-imx7 driver should implement the
236cd063027SMartin Kepplinger 	 * .reset() operation.
237cd063027SMartin Kepplinger 	 */
238cd063027SMartin Kepplinger 	ret = reset_control_assert(state->rst);
239cd063027SMartin Kepplinger 	if (ret < 0) {
240cd063027SMartin Kepplinger 		dev_err(state->dev, "Failed to assert resets: %d\n", ret);
241cd063027SMartin Kepplinger 		return ret;
242cd063027SMartin Kepplinger 	}
243cd063027SMartin Kepplinger 
244cd063027SMartin Kepplinger 	return 0;
245cd063027SMartin Kepplinger }
246cd063027SMartin Kepplinger 
imx8mq_mipi_csi_set_params(struct csi_state * state)247cd063027SMartin Kepplinger static void imx8mq_mipi_csi_set_params(struct csi_state *state)
248cd063027SMartin Kepplinger {
249cd063027SMartin Kepplinger 	int lanes = state->bus.num_data_lanes;
250cd063027SMartin Kepplinger 
251cd063027SMartin Kepplinger 	imx8mq_mipi_csi_write(state, CSI2RX_CFG_NUM_LANES, lanes - 1);
252cd063027SMartin Kepplinger 	imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES,
253cd063027SMartin Kepplinger 			      (0xf << lanes) & 0xf);
254cd063027SMartin Kepplinger 	imx8mq_mipi_csi_write(state, CSI2RX_IRQ_MASK, CSI2RX_IRQ_MASK_ALL);
255cd063027SMartin Kepplinger 	/*
256cd063027SMartin Kepplinger 	 * 0x180 bit 0 controls the Virtual Channel behaviour: when set the
257cd063027SMartin Kepplinger 	 * interface ignores the Virtual Channel (VC) field in received packets;
258cd063027SMartin Kepplinger 	 * when cleared it causes the interface to only accept packets whose VC
259cd063027SMartin Kepplinger 	 * matches the value to which VC is set at offset 0x184.
260cd063027SMartin Kepplinger 	 */
261cd063027SMartin Kepplinger 	imx8mq_mipi_csi_write(state, CSI2RX_CFG_VID_VC_IGNORE, 1);
262cd063027SMartin Kepplinger 	imx8mq_mipi_csi_write(state, CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL,
263cd063027SMartin Kepplinger 			      CSI2RX_SEND_LEVEL);
264cd063027SMartin Kepplinger }
265cd063027SMartin Kepplinger 
imx8mq_mipi_csi_clk_enable(struct csi_state * state)266cd063027SMartin Kepplinger static int imx8mq_mipi_csi_clk_enable(struct csi_state *state)
267cd063027SMartin Kepplinger {
268cd063027SMartin Kepplinger 	return clk_bulk_prepare_enable(CSI2_NUM_CLKS, state->clks);
269cd063027SMartin Kepplinger }
270cd063027SMartin Kepplinger 
imx8mq_mipi_csi_clk_disable(struct csi_state * state)271cd063027SMartin Kepplinger static void imx8mq_mipi_csi_clk_disable(struct csi_state *state)
272cd063027SMartin Kepplinger {
273cd063027SMartin Kepplinger 	clk_bulk_disable_unprepare(CSI2_NUM_CLKS, state->clks);
274cd063027SMartin Kepplinger }
275cd063027SMartin Kepplinger 
imx8mq_mipi_csi_clk_get(struct csi_state * state)276cd063027SMartin Kepplinger static int imx8mq_mipi_csi_clk_get(struct csi_state *state)
277cd063027SMartin Kepplinger {
278cd063027SMartin Kepplinger 	unsigned int i;
279cd063027SMartin Kepplinger 
280cd063027SMartin Kepplinger 	for (i = 0; i < CSI2_NUM_CLKS; i++)
281cd063027SMartin Kepplinger 		state->clks[i].id = imx8mq_mipi_csi_clk_id[i];
282cd063027SMartin Kepplinger 
283cd063027SMartin Kepplinger 	return devm_clk_bulk_get(state->dev, CSI2_NUM_CLKS, state->clks);
284cd063027SMartin Kepplinger }
285cd063027SMartin Kepplinger 
imx8mq_mipi_csi_calc_hs_settle(struct csi_state * state,struct v4l2_subdev_state * sd_state,u32 * hs_settle)286cd063027SMartin Kepplinger static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state,
287cd063027SMartin Kepplinger 					  struct v4l2_subdev_state *sd_state,
288cd063027SMartin Kepplinger 					  u32 *hs_settle)
289cd063027SMartin Kepplinger {
290cd063027SMartin Kepplinger 	s64 link_freq;
291cd063027SMartin Kepplinger 	u32 lane_rate;
292cd063027SMartin Kepplinger 	unsigned long esc_clk_rate;
293cd063027SMartin Kepplinger 	u32 min_ths_settle, max_ths_settle, ths_settle_ns, esc_clk_period_ns;
294cd063027SMartin Kepplinger 	const struct v4l2_mbus_framefmt *fmt;
295cd063027SMartin Kepplinger 	const struct csi2_pix_format *csi2_fmt;
296cd063027SMartin Kepplinger 
297cd063027SMartin Kepplinger 	/* Calculate the line rate from the pixel rate. */
298cd063027SMartin Kepplinger 
299cd063027SMartin Kepplinger 	fmt = v4l2_subdev_get_pad_format(&state->sd, sd_state, MIPI_CSI2_PAD_SINK);
300cd063027SMartin Kepplinger 	csi2_fmt = find_csi2_format(fmt->code);
301cd063027SMartin Kepplinger 
302cd063027SMartin Kepplinger 	link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler,
303cd063027SMartin Kepplinger 				       csi2_fmt->width,
304cd063027SMartin Kepplinger 				       state->bus.num_data_lanes * 2);
305cd063027SMartin Kepplinger 	if (link_freq < 0) {
306cd063027SMartin Kepplinger 		dev_err(state->dev, "Unable to obtain link frequency: %d\n",
307cd063027SMartin Kepplinger 			(int)link_freq);
308cd063027SMartin Kepplinger 		return link_freq;
309cd063027SMartin Kepplinger 	}
310cd063027SMartin Kepplinger 
311cd063027SMartin Kepplinger 	lane_rate = link_freq * 2;
312cd063027SMartin Kepplinger 	if (lane_rate < 80000000 || lane_rate > 1500000000) {
313cd063027SMartin Kepplinger 		dev_dbg(state->dev, "Out-of-bound lane rate %u\n", lane_rate);
314cd063027SMartin Kepplinger 		return -EINVAL;
315cd063027SMartin Kepplinger 	}
316cd063027SMartin Kepplinger 
317cd063027SMartin Kepplinger 	/*
318cd063027SMartin Kepplinger 	 * The D-PHY specification requires Ths-settle to be in the range
319cd063027SMartin Kepplinger 	 * 85ns + 6*UI to 140ns + 10*UI, with the unit interval UI being half
320cd063027SMartin Kepplinger 	 * the clock period.
321cd063027SMartin Kepplinger 	 *
322cd063027SMartin Kepplinger 	 * The Ths-settle value is expressed in the hardware as a multiple of
323cd063027SMartin Kepplinger 	 * the Esc clock period:
324cd063027SMartin Kepplinger 	 *
325cd063027SMartin Kepplinger 	 * Ths-settle = (PRG_RXHS_SETTLE + 1) * Tperiod of RxClkInEsc
326cd063027SMartin Kepplinger 	 *
327cd063027SMartin Kepplinger 	 * Due to the one cycle inaccuracy introduced by rounding, the
328cd063027SMartin Kepplinger 	 * documentation recommends picking a value away from the boundaries.
329cd063027SMartin Kepplinger 	 * Let's pick the average.
330cd063027SMartin Kepplinger 	 */
331cd063027SMartin Kepplinger 	esc_clk_rate = clk_get_rate(state->clks[CSI2_CLK_ESC].clk);
332cd063027SMartin Kepplinger 	if (!esc_clk_rate) {
333cd063027SMartin Kepplinger 		dev_err(state->dev, "Could not get esc clock rate.\n");
334cd063027SMartin Kepplinger 		return -EINVAL;
335cd063027SMartin Kepplinger 	}
336cd063027SMartin Kepplinger 
337cd063027SMartin Kepplinger 	dev_dbg(state->dev, "esc clk rate: %lu\n", esc_clk_rate);
338cd063027SMartin Kepplinger 	esc_clk_period_ns = 1000000000 / esc_clk_rate;
339cd063027SMartin Kepplinger 
340cd063027SMartin Kepplinger 	min_ths_settle = 85 + 6 * 1000000 / (lane_rate / 1000);
341cd063027SMartin Kepplinger 	max_ths_settle = 140 + 10 * 1000000 / (lane_rate / 1000);
342cd063027SMartin Kepplinger 	ths_settle_ns = (min_ths_settle + max_ths_settle) / 2;
343cd063027SMartin Kepplinger 
344cd063027SMartin Kepplinger 	*hs_settle = ths_settle_ns / esc_clk_period_ns - 1;
345cd063027SMartin Kepplinger 
346cd063027SMartin Kepplinger 	dev_dbg(state->dev, "lane rate %u Ths_settle %u hs_settle %u\n",
347cd063027SMartin Kepplinger 		lane_rate, ths_settle_ns, *hs_settle);
348cd063027SMartin Kepplinger 
349cd063027SMartin Kepplinger 	return 0;
350cd063027SMartin Kepplinger }
351cd063027SMartin Kepplinger 
imx8mq_mipi_csi_start_stream(struct csi_state * state,struct v4l2_subdev_state * sd_state)352cd063027SMartin Kepplinger static int imx8mq_mipi_csi_start_stream(struct csi_state *state,
353cd063027SMartin Kepplinger 					struct v4l2_subdev_state *sd_state)
354cd063027SMartin Kepplinger {
355cd063027SMartin Kepplinger 	int ret;
356cd063027SMartin Kepplinger 	u32 hs_settle = 0;
357cd063027SMartin Kepplinger 
358cd063027SMartin Kepplinger 	ret = imx8mq_mipi_csi_sw_reset(state);
359cd063027SMartin Kepplinger 	if (ret)
360cd063027SMartin Kepplinger 		return ret;
361cd063027SMartin Kepplinger 
362cd063027SMartin Kepplinger 	imx8mq_mipi_csi_set_params(state);
363cd063027SMartin Kepplinger 	ret = imx8mq_mipi_csi_calc_hs_settle(state, sd_state, &hs_settle);
364cd063027SMartin Kepplinger 	if (ret)
365cd063027SMartin Kepplinger 		return ret;
366cd063027SMartin Kepplinger 
367cd063027SMartin Kepplinger 	regmap_update_bits(state->phy_gpr,
368cd063027SMartin Kepplinger 			   state->phy_gpr_reg,
369cd063027SMartin Kepplinger 			   0x3fff,
370cd063027SMartin Kepplinger 			   GPR_CSI2_1_RX_ENABLE |
371cd063027SMartin Kepplinger 			   GPR_CSI2_1_VID_INTFC_ENB |
372cd063027SMartin Kepplinger 			   GPR_CSI2_1_HSEL |
373cd063027SMartin Kepplinger 			   GPR_CSI2_1_CONT_CLK_MODE |
374cd063027SMartin Kepplinger 			   GPR_CSI2_1_S_PRG_RXHS_SETTLE(hs_settle));
375cd063027SMartin Kepplinger 
376cd063027SMartin Kepplinger 	return 0;
377cd063027SMartin Kepplinger }
378cd063027SMartin Kepplinger 
imx8mq_mipi_csi_stop_stream(struct csi_state * state)379cd063027SMartin Kepplinger static void imx8mq_mipi_csi_stop_stream(struct csi_state *state)
380cd063027SMartin Kepplinger {
381cd063027SMartin Kepplinger 	imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES, 0xf);
382cd063027SMartin Kepplinger }
383cd063027SMartin Kepplinger 
384cd063027SMartin Kepplinger /* -----------------------------------------------------------------------------
385cd063027SMartin Kepplinger  * V4L2 subdev operations
386cd063027SMartin Kepplinger  */
387cd063027SMartin Kepplinger 
mipi_sd_to_csi2_state(struct v4l2_subdev * sdev)388cd063027SMartin Kepplinger static struct csi_state *mipi_sd_to_csi2_state(struct v4l2_subdev *sdev)
389cd063027SMartin Kepplinger {
390cd063027SMartin Kepplinger 	return container_of(sdev, struct csi_state, sd);
391cd063027SMartin Kepplinger }
392cd063027SMartin Kepplinger 
imx8mq_mipi_csi_s_stream(struct v4l2_subdev * sd,int enable)393cd063027SMartin Kepplinger static int imx8mq_mipi_csi_s_stream(struct v4l2_subdev *sd, int enable)
394cd063027SMartin Kepplinger {
395cd063027SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
396cd063027SMartin Kepplinger 	struct v4l2_subdev_state *sd_state;
397cd063027SMartin Kepplinger 	int ret = 0;
398cd063027SMartin Kepplinger 
399cd063027SMartin Kepplinger 	if (enable) {
400cd063027SMartin Kepplinger 		ret = pm_runtime_resume_and_get(state->dev);
401cd063027SMartin Kepplinger 		if (ret < 0)
402cd063027SMartin Kepplinger 			return ret;
403cd063027SMartin Kepplinger 	}
404cd063027SMartin Kepplinger 
405cd063027SMartin Kepplinger 	mutex_lock(&state->lock);
406cd063027SMartin Kepplinger 
407cd063027SMartin Kepplinger 	if (enable) {
408cd063027SMartin Kepplinger 		if (state->state & ST_SUSPENDED) {
409cd063027SMartin Kepplinger 			ret = -EBUSY;
410cd063027SMartin Kepplinger 			goto unlock;
411cd063027SMartin Kepplinger 		}
412cd063027SMartin Kepplinger 
413cd063027SMartin Kepplinger 		sd_state = v4l2_subdev_lock_and_get_active_state(sd);
414cd063027SMartin Kepplinger 		ret = imx8mq_mipi_csi_start_stream(state, sd_state);
415cd063027SMartin Kepplinger 		v4l2_subdev_unlock_state(sd_state);
416cd063027SMartin Kepplinger 
417cd063027SMartin Kepplinger 		if (ret < 0)
418cd063027SMartin Kepplinger 			goto unlock;
419cd063027SMartin Kepplinger 
420cd063027SMartin Kepplinger 		ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1);
421cd063027SMartin Kepplinger 		if (ret < 0)
422cd063027SMartin Kepplinger 			goto unlock;
423cd063027SMartin Kepplinger 
424cd063027SMartin Kepplinger 		state->state |= ST_STREAMING;
425cd063027SMartin Kepplinger 	} else {
426cd063027SMartin Kepplinger 		v4l2_subdev_call(state->src_sd, video, s_stream, 0);
427cd063027SMartin Kepplinger 		imx8mq_mipi_csi_stop_stream(state);
428cd063027SMartin Kepplinger 		state->state &= ~ST_STREAMING;
429cd063027SMartin Kepplinger 	}
430cd063027SMartin Kepplinger 
431cd063027SMartin Kepplinger unlock:
432cd063027SMartin Kepplinger 	mutex_unlock(&state->lock);
433cd063027SMartin Kepplinger 
434cd063027SMartin Kepplinger 	if (!enable || ret < 0)
435cd063027SMartin Kepplinger 		pm_runtime_put(state->dev);
436cd063027SMartin Kepplinger 
437cd063027SMartin Kepplinger 	return ret;
438cd063027SMartin Kepplinger }
439cd063027SMartin Kepplinger 
imx8mq_mipi_csi_init_cfg(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state)440cd063027SMartin Kepplinger static int imx8mq_mipi_csi_init_cfg(struct v4l2_subdev *sd,
441cd063027SMartin Kepplinger 				    struct v4l2_subdev_state *sd_state)
442cd063027SMartin Kepplinger {
443cd063027SMartin Kepplinger 	struct v4l2_mbus_framefmt *fmt_sink;
444cd063027SMartin Kepplinger 	struct v4l2_mbus_framefmt *fmt_source;
445cd063027SMartin Kepplinger 
446cd063027SMartin Kepplinger 	fmt_sink = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SINK);
447cd063027SMartin Kepplinger 	fmt_source = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SOURCE);
448cd063027SMartin Kepplinger 
449cd063027SMartin Kepplinger 	fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10;
450cd063027SMartin Kepplinger 	fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH;
451cd063027SMartin Kepplinger 	fmt_sink->height = MIPI_CSI2_DEF_PIX_HEIGHT;
452cd063027SMartin Kepplinger 	fmt_sink->field = V4L2_FIELD_NONE;
453cd063027SMartin Kepplinger 
454cd063027SMartin Kepplinger 	fmt_sink->colorspace = V4L2_COLORSPACE_RAW;
455cd063027SMartin Kepplinger 	fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);
456cd063027SMartin Kepplinger 	fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);
457cd063027SMartin Kepplinger 	fmt_sink->quantization =
458cd063027SMartin Kepplinger 		V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,
459cd063027SMartin Kepplinger 					      fmt_sink->ycbcr_enc);
460cd063027SMartin Kepplinger 
461cd063027SMartin Kepplinger 	*fmt_source = *fmt_sink;
462cd063027SMartin Kepplinger 
463cd063027SMartin Kepplinger 	return 0;
464cd063027SMartin Kepplinger }
465cd063027SMartin Kepplinger 
imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)466cd063027SMartin Kepplinger static int imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd,
467cd063027SMartin Kepplinger 					  struct v4l2_subdev_state *sd_state,
468cd063027SMartin Kepplinger 					  struct v4l2_subdev_mbus_code_enum *code)
469cd063027SMartin Kepplinger {
470cd063027SMartin Kepplinger 	/*
471cd063027SMartin Kepplinger 	 * We can't transcode in any way, the source format is identical
472cd063027SMartin Kepplinger 	 * to the sink format.
473cd063027SMartin Kepplinger 	 */
474cd063027SMartin Kepplinger 	if (code->pad == MIPI_CSI2_PAD_SOURCE) {
475cd063027SMartin Kepplinger 		struct v4l2_mbus_framefmt *fmt;
476cd063027SMartin Kepplinger 
477cd063027SMartin Kepplinger 		if (code->index > 0)
478cd063027SMartin Kepplinger 			return -EINVAL;
479cd063027SMartin Kepplinger 
480cd063027SMartin Kepplinger 		fmt = v4l2_subdev_get_pad_format(sd, sd_state, code->pad);
481cd063027SMartin Kepplinger 		code->code = fmt->code;
482cd063027SMartin Kepplinger 		return 0;
483cd063027SMartin Kepplinger 	}
484cd063027SMartin Kepplinger 
485cd063027SMartin Kepplinger 	if (code->pad != MIPI_CSI2_PAD_SINK)
486cd063027SMartin Kepplinger 		return -EINVAL;
487cd063027SMartin Kepplinger 
488cd063027SMartin Kepplinger 	if (code->index >= ARRAY_SIZE(imx8mq_mipi_csi_formats))
489cd063027SMartin Kepplinger 		return -EINVAL;
490cd063027SMartin Kepplinger 
491cd063027SMartin Kepplinger 	code->code = imx8mq_mipi_csi_formats[code->index].code;
492cd063027SMartin Kepplinger 
493cd063027SMartin Kepplinger 	return 0;
494cd063027SMartin Kepplinger }
495cd063027SMartin Kepplinger 
imx8mq_mipi_csi_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * sdformat)496cd063027SMartin Kepplinger static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd,
497cd063027SMartin Kepplinger 				   struct v4l2_subdev_state *sd_state,
498cd063027SMartin Kepplinger 				   struct v4l2_subdev_format *sdformat)
499cd063027SMartin Kepplinger {
500cd063027SMartin Kepplinger 	const struct csi2_pix_format *csi2_fmt;
501cd063027SMartin Kepplinger 	struct v4l2_mbus_framefmt *fmt;
502cd063027SMartin Kepplinger 
503cd063027SMartin Kepplinger 	/*
504cd063027SMartin Kepplinger 	 * The device can't transcode in any way, the source format can't be
505cd063027SMartin Kepplinger 	 * modified.
506cd063027SMartin Kepplinger 	 */
507cd063027SMartin Kepplinger 	if (sdformat->pad == MIPI_CSI2_PAD_SOURCE)
508cd063027SMartin Kepplinger 		return v4l2_subdev_get_fmt(sd, sd_state, sdformat);
509cd063027SMartin Kepplinger 
510cd063027SMartin Kepplinger 	if (sdformat->pad != MIPI_CSI2_PAD_SINK)
511cd063027SMartin Kepplinger 		return -EINVAL;
512cd063027SMartin Kepplinger 
513cd063027SMartin Kepplinger 	csi2_fmt = find_csi2_format(sdformat->format.code);
514cd063027SMartin Kepplinger 	if (!csi2_fmt)
515cd063027SMartin Kepplinger 		csi2_fmt = &imx8mq_mipi_csi_formats[0];
516cd063027SMartin Kepplinger 
517cd063027SMartin Kepplinger 	fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad);
518cd063027SMartin Kepplinger 
519cd063027SMartin Kepplinger 	fmt->code = csi2_fmt->code;
520cd063027SMartin Kepplinger 	fmt->width = sdformat->format.width;
521cd063027SMartin Kepplinger 	fmt->height = sdformat->format.height;
522cd063027SMartin Kepplinger 
523cd063027SMartin Kepplinger 	sdformat->format = *fmt;
524cd063027SMartin Kepplinger 
525cd063027SMartin Kepplinger 	/* Propagate the format from sink to source. */
526cd063027SMartin Kepplinger 	fmt = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SOURCE);
527cd063027SMartin Kepplinger 	*fmt = sdformat->format;
528cd063027SMartin Kepplinger 
529cd063027SMartin Kepplinger 	return 0;
530cd063027SMartin Kepplinger }
531cd063027SMartin Kepplinger 
532cd063027SMartin Kepplinger static const struct v4l2_subdev_video_ops imx8mq_mipi_csi_video_ops = {
533cd063027SMartin Kepplinger 	.s_stream	= imx8mq_mipi_csi_s_stream,
534cd063027SMartin Kepplinger };
535cd063027SMartin Kepplinger 
536cd063027SMartin Kepplinger static const struct v4l2_subdev_pad_ops imx8mq_mipi_csi_pad_ops = {
537cd063027SMartin Kepplinger 	.init_cfg		= imx8mq_mipi_csi_init_cfg,
538cd063027SMartin Kepplinger 	.enum_mbus_code		= imx8mq_mipi_csi_enum_mbus_code,
539cd063027SMartin Kepplinger 	.get_fmt		= v4l2_subdev_get_fmt,
540cd063027SMartin Kepplinger 	.set_fmt		= imx8mq_mipi_csi_set_fmt,
541cd063027SMartin Kepplinger };
542cd063027SMartin Kepplinger 
543cd063027SMartin Kepplinger static const struct v4l2_subdev_ops imx8mq_mipi_csi_subdev_ops = {
544cd063027SMartin Kepplinger 	.video	= &imx8mq_mipi_csi_video_ops,
545cd063027SMartin Kepplinger 	.pad	= &imx8mq_mipi_csi_pad_ops,
546cd063027SMartin Kepplinger };
547cd063027SMartin Kepplinger 
548cd063027SMartin Kepplinger /* -----------------------------------------------------------------------------
549cd063027SMartin Kepplinger  * Media entity operations
550cd063027SMartin Kepplinger  */
551cd063027SMartin Kepplinger 
552cd063027SMartin Kepplinger static const struct media_entity_operations imx8mq_mipi_csi_entity_ops = {
553cd063027SMartin Kepplinger 	.link_validate	= v4l2_subdev_link_validate,
554cd063027SMartin Kepplinger 	.get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
555cd063027SMartin Kepplinger };
556cd063027SMartin Kepplinger 
557cd063027SMartin Kepplinger /* -----------------------------------------------------------------------------
558cd063027SMartin Kepplinger  * Async subdev notifier
559cd063027SMartin Kepplinger  */
560cd063027SMartin Kepplinger 
561cd063027SMartin Kepplinger static struct csi_state *
mipi_notifier_to_csi2_state(struct v4l2_async_notifier * n)562cd063027SMartin Kepplinger mipi_notifier_to_csi2_state(struct v4l2_async_notifier *n)
563cd063027SMartin Kepplinger {
564cd063027SMartin Kepplinger 	return container_of(n, struct csi_state, notifier);
565cd063027SMartin Kepplinger }
566cd063027SMartin Kepplinger 
imx8mq_mipi_csi_notify_bound(struct v4l2_async_notifier * notifier,struct v4l2_subdev * sd,struct v4l2_async_connection * asd)567cd063027SMartin Kepplinger static int imx8mq_mipi_csi_notify_bound(struct v4l2_async_notifier *notifier,
568cd063027SMartin Kepplinger 					struct v4l2_subdev *sd,
569adb2dcd5SSakari Ailus 					struct v4l2_async_connection *asd)
570cd063027SMartin Kepplinger {
571cd063027SMartin Kepplinger 	struct csi_state *state = mipi_notifier_to_csi2_state(notifier);
572cd063027SMartin Kepplinger 	struct media_pad *sink = &state->sd.entity.pads[MIPI_CSI2_PAD_SINK];
573cd063027SMartin Kepplinger 
574cd063027SMartin Kepplinger 	state->src_sd = sd;
575cd063027SMartin Kepplinger 
576cd063027SMartin Kepplinger 	return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
577cd063027SMartin Kepplinger 					       MEDIA_LNK_FL_IMMUTABLE);
578cd063027SMartin Kepplinger }
579cd063027SMartin Kepplinger 
580cd063027SMartin Kepplinger static const struct v4l2_async_notifier_operations imx8mq_mipi_csi_notify_ops = {
581cd063027SMartin Kepplinger 	.bound = imx8mq_mipi_csi_notify_bound,
582cd063027SMartin Kepplinger };
583cd063027SMartin Kepplinger 
imx8mq_mipi_csi_async_register(struct csi_state * state)584cd063027SMartin Kepplinger static int imx8mq_mipi_csi_async_register(struct csi_state *state)
585cd063027SMartin Kepplinger {
586cd063027SMartin Kepplinger 	struct v4l2_fwnode_endpoint vep = {
587cd063027SMartin Kepplinger 		.bus_type = V4L2_MBUS_CSI2_DPHY,
588cd063027SMartin Kepplinger 	};
589adb2dcd5SSakari Ailus 	struct v4l2_async_connection *asd;
590cd063027SMartin Kepplinger 	struct fwnode_handle *ep;
591cd063027SMartin Kepplinger 	unsigned int i;
592cd063027SMartin Kepplinger 	int ret;
593cd063027SMartin Kepplinger 
594*b8ec754aSSakari Ailus 	v4l2_async_subdev_nf_init(&state->notifier, &state->sd);
595cd063027SMartin Kepplinger 
596cd063027SMartin Kepplinger 	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0,
597cd063027SMartin Kepplinger 					     FWNODE_GRAPH_ENDPOINT_NEXT);
598cd063027SMartin Kepplinger 	if (!ep)
599cd063027SMartin Kepplinger 		return -ENOTCONN;
600cd063027SMartin Kepplinger 
601cd063027SMartin Kepplinger 	ret = v4l2_fwnode_endpoint_parse(ep, &vep);
602cd063027SMartin Kepplinger 	if (ret)
603cd063027SMartin Kepplinger 		goto err_parse;
604cd063027SMartin Kepplinger 
605cd063027SMartin Kepplinger 	for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) {
606cd063027SMartin Kepplinger 		if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) {
607cd063027SMartin Kepplinger 			dev_err(state->dev,
608cd063027SMartin Kepplinger 				"data lanes reordering is not supported");
609cd063027SMartin Kepplinger 			ret = -EINVAL;
610cd063027SMartin Kepplinger 			goto err_parse;
611cd063027SMartin Kepplinger 		}
612cd063027SMartin Kepplinger 	}
613cd063027SMartin Kepplinger 
614cd063027SMartin Kepplinger 	state->bus = vep.bus.mipi_csi2;
615cd063027SMartin Kepplinger 
616cd063027SMartin Kepplinger 	dev_dbg(state->dev, "data lanes: %d flags: 0x%08x\n",
617cd063027SMartin Kepplinger 		state->bus.num_data_lanes,
618cd063027SMartin Kepplinger 		state->bus.flags);
619cd063027SMartin Kepplinger 
620cd063027SMartin Kepplinger 	asd = v4l2_async_nf_add_fwnode_remote(&state->notifier, ep,
621adb2dcd5SSakari Ailus 					      struct v4l2_async_connection);
622cd063027SMartin Kepplinger 	if (IS_ERR(asd)) {
623cd063027SMartin Kepplinger 		ret = PTR_ERR(asd);
624cd063027SMartin Kepplinger 		goto err_parse;
625cd063027SMartin Kepplinger 	}
626cd063027SMartin Kepplinger 
627cd063027SMartin Kepplinger 	fwnode_handle_put(ep);
628cd063027SMartin Kepplinger 
629cd063027SMartin Kepplinger 	state->notifier.ops = &imx8mq_mipi_csi_notify_ops;
630cd063027SMartin Kepplinger 
631*b8ec754aSSakari Ailus 	ret = v4l2_async_nf_register(&state->notifier);
632cd063027SMartin Kepplinger 	if (ret)
633cd063027SMartin Kepplinger 		return ret;
634cd063027SMartin Kepplinger 
635cd063027SMartin Kepplinger 	return v4l2_async_register_subdev(&state->sd);
636cd063027SMartin Kepplinger 
637cd063027SMartin Kepplinger err_parse:
638cd063027SMartin Kepplinger 	fwnode_handle_put(ep);
639cd063027SMartin Kepplinger 
640cd063027SMartin Kepplinger 	return ret;
641cd063027SMartin Kepplinger }
642cd063027SMartin Kepplinger 
643cd063027SMartin Kepplinger /* -----------------------------------------------------------------------------
644cd063027SMartin Kepplinger  * Suspend/resume
645cd063027SMartin Kepplinger  */
646cd063027SMartin Kepplinger 
imx8mq_mipi_csi_pm_suspend(struct device * dev)647cd063027SMartin Kepplinger static void imx8mq_mipi_csi_pm_suspend(struct device *dev)
648cd063027SMartin Kepplinger {
649cd063027SMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
650cd063027SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
651cd063027SMartin Kepplinger 
652cd063027SMartin Kepplinger 	mutex_lock(&state->lock);
653cd063027SMartin Kepplinger 
654cd063027SMartin Kepplinger 	if (state->state & ST_POWERED) {
655cd063027SMartin Kepplinger 		imx8mq_mipi_csi_stop_stream(state);
656cd063027SMartin Kepplinger 		imx8mq_mipi_csi_clk_disable(state);
657cd063027SMartin Kepplinger 		state->state &= ~ST_POWERED;
658cd063027SMartin Kepplinger 	}
659cd063027SMartin Kepplinger 
660cd063027SMartin Kepplinger 	mutex_unlock(&state->lock);
661cd063027SMartin Kepplinger }
662cd063027SMartin Kepplinger 
imx8mq_mipi_csi_pm_resume(struct device * dev)663cd063027SMartin Kepplinger static int imx8mq_mipi_csi_pm_resume(struct device *dev)
664cd063027SMartin Kepplinger {
665cd063027SMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
666cd063027SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
667cd063027SMartin Kepplinger 	struct v4l2_subdev_state *sd_state;
668cd063027SMartin Kepplinger 	int ret = 0;
669cd063027SMartin Kepplinger 
670cd063027SMartin Kepplinger 	mutex_lock(&state->lock);
671cd063027SMartin Kepplinger 
672cd063027SMartin Kepplinger 	if (!(state->state & ST_POWERED)) {
673cd063027SMartin Kepplinger 		state->state |= ST_POWERED;
674cd063027SMartin Kepplinger 		ret = imx8mq_mipi_csi_clk_enable(state);
675cd063027SMartin Kepplinger 	}
676cd063027SMartin Kepplinger 	if (state->state & ST_STREAMING) {
677cd063027SMartin Kepplinger 		sd_state = v4l2_subdev_lock_and_get_active_state(sd);
678cd063027SMartin Kepplinger 		ret = imx8mq_mipi_csi_start_stream(state, sd_state);
679cd063027SMartin Kepplinger 		v4l2_subdev_unlock_state(sd_state);
680cd063027SMartin Kepplinger 		if (ret)
681cd063027SMartin Kepplinger 			goto unlock;
682cd063027SMartin Kepplinger 	}
683cd063027SMartin Kepplinger 
684cd063027SMartin Kepplinger 	state->state &= ~ST_SUSPENDED;
685cd063027SMartin Kepplinger 
686cd063027SMartin Kepplinger unlock:
687cd063027SMartin Kepplinger 	mutex_unlock(&state->lock);
688cd063027SMartin Kepplinger 
689cd063027SMartin Kepplinger 	return ret ? -EAGAIN : 0;
690cd063027SMartin Kepplinger }
691cd063027SMartin Kepplinger 
imx8mq_mipi_csi_suspend(struct device * dev)692cd063027SMartin Kepplinger static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev)
693cd063027SMartin Kepplinger {
694cd063027SMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
695cd063027SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
696cd063027SMartin Kepplinger 
697cd063027SMartin Kepplinger 	imx8mq_mipi_csi_pm_suspend(dev);
698cd063027SMartin Kepplinger 
699cd063027SMartin Kepplinger 	state->state |= ST_SUSPENDED;
700cd063027SMartin Kepplinger 
701cd063027SMartin Kepplinger 	return 0;
702cd063027SMartin Kepplinger }
703cd063027SMartin Kepplinger 
imx8mq_mipi_csi_resume(struct device * dev)704cd063027SMartin Kepplinger static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev)
705cd063027SMartin Kepplinger {
706cd063027SMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
707cd063027SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
708cd063027SMartin Kepplinger 
709cd063027SMartin Kepplinger 	if (!(state->state & ST_SUSPENDED))
710cd063027SMartin Kepplinger 		return 0;
711cd063027SMartin Kepplinger 
712cd063027SMartin Kepplinger 	return imx8mq_mipi_csi_pm_resume(dev);
713cd063027SMartin Kepplinger }
714cd063027SMartin Kepplinger 
imx8mq_mipi_csi_runtime_suspend(struct device * dev)715cd063027SMartin Kepplinger static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev)
716cd063027SMartin Kepplinger {
717cd063027SMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
718cd063027SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
719cd063027SMartin Kepplinger 	int ret;
720cd063027SMartin Kepplinger 
721cd063027SMartin Kepplinger 	imx8mq_mipi_csi_pm_suspend(dev);
722cd063027SMartin Kepplinger 
723cd063027SMartin Kepplinger 	ret = icc_set_bw(state->icc_path, 0, 0);
724cd063027SMartin Kepplinger 	if (ret)
725cd063027SMartin Kepplinger 		dev_err(dev, "icc_set_bw failed with %d\n", ret);
726cd063027SMartin Kepplinger 
727cd063027SMartin Kepplinger 	return ret;
728cd063027SMartin Kepplinger }
729cd063027SMartin Kepplinger 
imx8mq_mipi_csi_runtime_resume(struct device * dev)730cd063027SMartin Kepplinger static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev)
731cd063027SMartin Kepplinger {
732cd063027SMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
733cd063027SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
734cd063027SMartin Kepplinger 	int ret;
735cd063027SMartin Kepplinger 
736cd063027SMartin Kepplinger 	ret = icc_set_bw(state->icc_path, 0, state->icc_path_bw);
737cd063027SMartin Kepplinger 	if (ret) {
738cd063027SMartin Kepplinger 		dev_err(dev, "icc_set_bw failed with %d\n", ret);
739cd063027SMartin Kepplinger 		return ret;
740cd063027SMartin Kepplinger 	}
741cd063027SMartin Kepplinger 
742cd063027SMartin Kepplinger 	return imx8mq_mipi_csi_pm_resume(dev);
743cd063027SMartin Kepplinger }
744cd063027SMartin Kepplinger 
745cd063027SMartin Kepplinger static const struct dev_pm_ops imx8mq_mipi_csi_pm_ops = {
746cd063027SMartin Kepplinger 	SET_RUNTIME_PM_OPS(imx8mq_mipi_csi_runtime_suspend,
747cd063027SMartin Kepplinger 			   imx8mq_mipi_csi_runtime_resume,
748cd063027SMartin Kepplinger 			   NULL)
749cd063027SMartin Kepplinger 	SET_SYSTEM_SLEEP_PM_OPS(imx8mq_mipi_csi_suspend, imx8mq_mipi_csi_resume)
750cd063027SMartin Kepplinger };
751cd063027SMartin Kepplinger 
752cd063027SMartin Kepplinger /* -----------------------------------------------------------------------------
753cd063027SMartin Kepplinger  * Probe/remove & platform driver
754cd063027SMartin Kepplinger  */
755cd063027SMartin Kepplinger 
imx8mq_mipi_csi_subdev_init(struct csi_state * state)756cd063027SMartin Kepplinger static int imx8mq_mipi_csi_subdev_init(struct csi_state *state)
757cd063027SMartin Kepplinger {
758cd063027SMartin Kepplinger 	struct v4l2_subdev *sd = &state->sd;
759cd063027SMartin Kepplinger 	int ret;
760cd063027SMartin Kepplinger 
761cd063027SMartin Kepplinger 	v4l2_subdev_init(sd, &imx8mq_mipi_csi_subdev_ops);
762cd063027SMartin Kepplinger 	sd->owner = THIS_MODULE;
763cd063027SMartin Kepplinger 	snprintf(sd->name, sizeof(sd->name), "%s %s",
764cd063027SMartin Kepplinger 		 MIPI_CSI2_SUBDEV_NAME, dev_name(state->dev));
765cd063027SMartin Kepplinger 
766cd063027SMartin Kepplinger 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
767cd063027SMartin Kepplinger 
768cd063027SMartin Kepplinger 	sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
769cd063027SMartin Kepplinger 	sd->entity.ops = &imx8mq_mipi_csi_entity_ops;
770cd063027SMartin Kepplinger 
771cd063027SMartin Kepplinger 	sd->dev = state->dev;
772cd063027SMartin Kepplinger 
773cd063027SMartin Kepplinger 	state->pads[MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK
774cd063027SMartin Kepplinger 					 | MEDIA_PAD_FL_MUST_CONNECT;
775cd063027SMartin Kepplinger 	state->pads[MIPI_CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE
776cd063027SMartin Kepplinger 					   | MEDIA_PAD_FL_MUST_CONNECT;
777cd063027SMartin Kepplinger 	ret = media_entity_pads_init(&sd->entity, MIPI_CSI2_PADS_NUM,
778cd063027SMartin Kepplinger 				     state->pads);
779cd063027SMartin Kepplinger 	if (ret)
780cd063027SMartin Kepplinger 		return ret;
781cd063027SMartin Kepplinger 
782cd063027SMartin Kepplinger 	ret = v4l2_subdev_init_finalize(sd);
783cd063027SMartin Kepplinger 	if (ret) {
784cd063027SMartin Kepplinger 		media_entity_cleanup(&sd->entity);
785cd063027SMartin Kepplinger 		return ret;
786cd063027SMartin Kepplinger 	}
787cd063027SMartin Kepplinger 
788cd063027SMartin Kepplinger 	return 0;
789cd063027SMartin Kepplinger }
790cd063027SMartin Kepplinger 
imx8mq_mipi_csi_release_icc(struct platform_device * pdev)791cd063027SMartin Kepplinger static void imx8mq_mipi_csi_release_icc(struct platform_device *pdev)
792cd063027SMartin Kepplinger {
793cd063027SMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
794cd063027SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
795cd063027SMartin Kepplinger 
796cd063027SMartin Kepplinger 	icc_put(state->icc_path);
797cd063027SMartin Kepplinger }
798cd063027SMartin Kepplinger 
imx8mq_mipi_csi_init_icc(struct platform_device * pdev)799cd063027SMartin Kepplinger static int imx8mq_mipi_csi_init_icc(struct platform_device *pdev)
800cd063027SMartin Kepplinger {
801cd063027SMartin Kepplinger 	struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
802cd063027SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
803cd063027SMartin Kepplinger 
804cd063027SMartin Kepplinger 	/* Optional interconnect request */
805cd063027SMartin Kepplinger 	state->icc_path = of_icc_get(&pdev->dev, "dram");
806cd063027SMartin Kepplinger 	if (IS_ERR_OR_NULL(state->icc_path))
807cd063027SMartin Kepplinger 		return PTR_ERR_OR_ZERO(state->icc_path);
808cd063027SMartin Kepplinger 
809cd063027SMartin Kepplinger 	state->icc_path_bw = MBps_to_icc(700);
810cd063027SMartin Kepplinger 
811cd063027SMartin Kepplinger 	return 0;
812cd063027SMartin Kepplinger }
813cd063027SMartin Kepplinger 
imx8mq_mipi_csi_parse_dt(struct csi_state * state)814cd063027SMartin Kepplinger static int imx8mq_mipi_csi_parse_dt(struct csi_state *state)
815cd063027SMartin Kepplinger {
816cd063027SMartin Kepplinger 	struct device *dev = state->dev;
817cd063027SMartin Kepplinger 	struct device_node *np = state->dev->of_node;
818cd063027SMartin Kepplinger 	struct device_node *node;
819cd063027SMartin Kepplinger 	phandle ph;
820cd063027SMartin Kepplinger 	u32 out_val[2];
821cd063027SMartin Kepplinger 	int ret = 0;
822cd063027SMartin Kepplinger 
823cd063027SMartin Kepplinger 	state->rst = devm_reset_control_array_get_exclusive(dev);
824cd063027SMartin Kepplinger 	if (IS_ERR(state->rst)) {
825cd063027SMartin Kepplinger 		dev_err(dev, "Failed to get reset: %pe\n", state->rst);
826cd063027SMartin Kepplinger 		return PTR_ERR(state->rst);
827cd063027SMartin Kepplinger 	}
828cd063027SMartin Kepplinger 
829cd063027SMartin Kepplinger 	ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val,
830cd063027SMartin Kepplinger 					 ARRAY_SIZE(out_val));
831cd063027SMartin Kepplinger 	if (ret) {
832cd063027SMartin Kepplinger 		dev_err(dev, "no fsl,mipi-phy-gpr property found: %d\n", ret);
833cd063027SMartin Kepplinger 		return ret;
834cd063027SMartin Kepplinger 	}
835cd063027SMartin Kepplinger 
836cd063027SMartin Kepplinger 	ph = *out_val;
837cd063027SMartin Kepplinger 
838cd063027SMartin Kepplinger 	node = of_find_node_by_phandle(ph);
839cd063027SMartin Kepplinger 	if (!node) {
840cd063027SMartin Kepplinger 		dev_err(dev, "Error finding node by phandle\n");
841cd063027SMartin Kepplinger 		return -ENODEV;
842cd063027SMartin Kepplinger 	}
843cd063027SMartin Kepplinger 	state->phy_gpr = syscon_node_to_regmap(node);
844cd063027SMartin Kepplinger 	of_node_put(node);
845cd063027SMartin Kepplinger 	if (IS_ERR(state->phy_gpr)) {
846cd063027SMartin Kepplinger 		dev_err(dev, "failed to get gpr regmap: %pe\n", state->phy_gpr);
847cd063027SMartin Kepplinger 		return PTR_ERR(state->phy_gpr);
848cd063027SMartin Kepplinger 	}
849cd063027SMartin Kepplinger 
850cd063027SMartin Kepplinger 	state->phy_gpr_reg = out_val[1];
851cd063027SMartin Kepplinger 	dev_dbg(dev, "phy gpr register set to 0x%x\n", state->phy_gpr_reg);
852cd063027SMartin Kepplinger 
853cd063027SMartin Kepplinger 	return ret;
854cd063027SMartin Kepplinger }
855cd063027SMartin Kepplinger 
imx8mq_mipi_csi_probe(struct platform_device * pdev)856cd063027SMartin Kepplinger static int imx8mq_mipi_csi_probe(struct platform_device *pdev)
857cd063027SMartin Kepplinger {
858cd063027SMartin Kepplinger 	struct device *dev = &pdev->dev;
859cd063027SMartin Kepplinger 	struct csi_state *state;
860cd063027SMartin Kepplinger 	int ret;
861cd063027SMartin Kepplinger 
862cd063027SMartin Kepplinger 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
863cd063027SMartin Kepplinger 	if (!state)
864cd063027SMartin Kepplinger 		return -ENOMEM;
865cd063027SMartin Kepplinger 
866cd063027SMartin Kepplinger 	state->dev = dev;
867cd063027SMartin Kepplinger 
868cd063027SMartin Kepplinger 	ret = imx8mq_mipi_csi_parse_dt(state);
869cd063027SMartin Kepplinger 	if (ret < 0) {
870cd063027SMartin Kepplinger 		dev_err(dev, "Failed to parse device tree: %d\n", ret);
871cd063027SMartin Kepplinger 		return ret;
872cd063027SMartin Kepplinger 	}
873cd063027SMartin Kepplinger 
874cd063027SMartin Kepplinger 	/* Acquire resources. */
875cd063027SMartin Kepplinger 	state->regs = devm_platform_ioremap_resource(pdev, 0);
876cd063027SMartin Kepplinger 	if (IS_ERR(state->regs))
877cd063027SMartin Kepplinger 		return PTR_ERR(state->regs);
878cd063027SMartin Kepplinger 
879cd063027SMartin Kepplinger 	ret = imx8mq_mipi_csi_clk_get(state);
880cd063027SMartin Kepplinger 	if (ret < 0)
881cd063027SMartin Kepplinger 		return ret;
882cd063027SMartin Kepplinger 
883cd063027SMartin Kepplinger 	platform_set_drvdata(pdev, &state->sd);
884cd063027SMartin Kepplinger 
885cd063027SMartin Kepplinger 	mutex_init(&state->lock);
886cd063027SMartin Kepplinger 
887cd063027SMartin Kepplinger 	ret = imx8mq_mipi_csi_subdev_init(state);
888cd063027SMartin Kepplinger 	if (ret < 0)
889cd063027SMartin Kepplinger 		goto mutex;
890cd063027SMartin Kepplinger 
891cd063027SMartin Kepplinger 	ret = imx8mq_mipi_csi_init_icc(pdev);
892cd063027SMartin Kepplinger 	if (ret)
893cd063027SMartin Kepplinger 		goto mutex;
894cd063027SMartin Kepplinger 
895cd063027SMartin Kepplinger 	/* Enable runtime PM. */
896cd063027SMartin Kepplinger 	pm_runtime_enable(dev);
897cd063027SMartin Kepplinger 	if (!pm_runtime_enabled(dev)) {
898cd063027SMartin Kepplinger 		ret = imx8mq_mipi_csi_runtime_resume(dev);
899cd063027SMartin Kepplinger 		if (ret < 0)
900cd063027SMartin Kepplinger 			goto icc;
901cd063027SMartin Kepplinger 	}
902cd063027SMartin Kepplinger 
903cd063027SMartin Kepplinger 	ret = imx8mq_mipi_csi_async_register(state);
904cd063027SMartin Kepplinger 	if (ret < 0)
905cd063027SMartin Kepplinger 		goto cleanup;
906cd063027SMartin Kepplinger 
907cd063027SMartin Kepplinger 	return 0;
908cd063027SMartin Kepplinger 
909cd063027SMartin Kepplinger cleanup:
910cd063027SMartin Kepplinger 	pm_runtime_disable(&pdev->dev);
911cd063027SMartin Kepplinger 	imx8mq_mipi_csi_runtime_suspend(&pdev->dev);
912cd063027SMartin Kepplinger 
913cd063027SMartin Kepplinger 	media_entity_cleanup(&state->sd.entity);
914cd063027SMartin Kepplinger 	v4l2_subdev_cleanup(&state->sd);
915cd063027SMartin Kepplinger 	v4l2_async_nf_unregister(&state->notifier);
916cd063027SMartin Kepplinger 	v4l2_async_nf_cleanup(&state->notifier);
917cd063027SMartin Kepplinger 	v4l2_async_unregister_subdev(&state->sd);
918cd063027SMartin Kepplinger icc:
919cd063027SMartin Kepplinger 	imx8mq_mipi_csi_release_icc(pdev);
920cd063027SMartin Kepplinger mutex:
921cd063027SMartin Kepplinger 	mutex_destroy(&state->lock);
922cd063027SMartin Kepplinger 
923cd063027SMartin Kepplinger 	return ret;
924cd063027SMartin Kepplinger }
925cd063027SMartin Kepplinger 
imx8mq_mipi_csi_remove(struct platform_device * pdev)926cd063027SMartin Kepplinger static void imx8mq_mipi_csi_remove(struct platform_device *pdev)
927cd063027SMartin Kepplinger {
928cd063027SMartin Kepplinger 	struct v4l2_subdev *sd = platform_get_drvdata(pdev);
929cd063027SMartin Kepplinger 	struct csi_state *state = mipi_sd_to_csi2_state(sd);
930cd063027SMartin Kepplinger 
931cd063027SMartin Kepplinger 	v4l2_async_nf_unregister(&state->notifier);
932cd063027SMartin Kepplinger 	v4l2_async_nf_cleanup(&state->notifier);
933cd063027SMartin Kepplinger 	v4l2_async_unregister_subdev(&state->sd);
934cd063027SMartin Kepplinger 
935cd063027SMartin Kepplinger 	pm_runtime_disable(&pdev->dev);
936cd063027SMartin Kepplinger 	imx8mq_mipi_csi_runtime_suspend(&pdev->dev);
937cd063027SMartin Kepplinger 	media_entity_cleanup(&state->sd.entity);
938cd063027SMartin Kepplinger 	v4l2_subdev_cleanup(&state->sd);
939cd063027SMartin Kepplinger 	mutex_destroy(&state->lock);
940cd063027SMartin Kepplinger 	pm_runtime_set_suspended(&pdev->dev);
941cd063027SMartin Kepplinger 	imx8mq_mipi_csi_release_icc(pdev);
942cd063027SMartin Kepplinger }
943cd063027SMartin Kepplinger 
944cd063027SMartin Kepplinger static const struct of_device_id imx8mq_mipi_csi_of_match[] = {
945cd063027SMartin Kepplinger 	{ .compatible = "fsl,imx8mq-mipi-csi2", },
946cd063027SMartin Kepplinger 	{ /* sentinel */ },
947cd063027SMartin Kepplinger };
948cd063027SMartin Kepplinger MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match);
949cd063027SMartin Kepplinger 
950cd063027SMartin Kepplinger static struct platform_driver imx8mq_mipi_csi_driver = {
951cd063027SMartin Kepplinger 	.probe		= imx8mq_mipi_csi_probe,
952cd063027SMartin Kepplinger 	.remove_new	= imx8mq_mipi_csi_remove,
953cd063027SMartin Kepplinger 	.driver		= {
954cd063027SMartin Kepplinger 		.of_match_table = imx8mq_mipi_csi_of_match,
955cd063027SMartin Kepplinger 		.name		= MIPI_CSI2_DRIVER_NAME,
956cd063027SMartin Kepplinger 		.pm		= &imx8mq_mipi_csi_pm_ops,
957cd063027SMartin Kepplinger 	},
958cd063027SMartin Kepplinger };
959cd063027SMartin Kepplinger 
960cd063027SMartin Kepplinger module_platform_driver(imx8mq_mipi_csi_driver);
961cd063027SMartin Kepplinger 
962cd063027SMartin Kepplinger MODULE_DESCRIPTION("i.MX8MQ MIPI CSI-2 receiver driver");
963cd063027SMartin Kepplinger MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@puri.sm>");
964cd063027SMartin Kepplinger MODULE_LICENSE("GPL v2");
965cd063027SMartin Kepplinger MODULE_ALIAS("platform:imx8mq-mipi-csi2");
966