xref: /linux/drivers/media/platform/qcom/camss/camss-csid.c (revision 801ca0e7f9be141ae8a7b17e47d6af588768ae49)
1b873663bSTodor Tomov // SPDX-License-Identifier: GPL-2.0
2ec6859b2STodor Tomov /*
3ec6859b2STodor Tomov  * camss-csid.c
4ec6859b2STodor Tomov  *
5ec6859b2STodor Tomov  * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module
6ec6859b2STodor Tomov  *
7ec6859b2STodor Tomov  * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
8ec6859b2STodor Tomov  * Copyright (C) 2015-2018 Linaro Ltd.
9ec6859b2STodor Tomov  */
10ec6859b2STodor Tomov #include <linux/clk.h>
11ec6859b2STodor Tomov #include <linux/completion.h>
12ec6859b2STodor Tomov #include <linux/interrupt.h>
133799eca5SArnd Bergmann #include <linux/io.h>
14ec6859b2STodor Tomov #include <linux/kernel.h>
15ec6859b2STodor Tomov #include <linux/of.h>
16ec6859b2STodor Tomov #include <linux/platform_device.h>
1702afa816STodor Tomov #include <linux/pm_runtime.h>
18ec6859b2STodor Tomov #include <linux/regulator/consumer.h>
19ec6859b2STodor Tomov #include <media/media-entity.h>
20ec6859b2STodor Tomov #include <media/v4l2-device.h>
21988b3ae3STodor Tomov #include <media/v4l2-event.h>
22ec6859b2STodor Tomov #include <media/v4l2-subdev.h>
23ec6859b2STodor Tomov 
24ec6859b2STodor Tomov #include "camss-csid.h"
25e19b14b1SRobert Foss #include "camss-csid-gen1.h"
26ec6859b2STodor Tomov #include "camss.h"
27ec6859b2STodor Tomov 
28b4436a18SJonathan Marek /* offset of CSID registers in VFE region for VFE 480 */
29b4436a18SJonathan Marek #define VFE_480_CSID_OFFSET 0x1200
30b4436a18SJonathan Marek #define VFE_480_LITE_CSID_OFFSET 0x200
31b4436a18SJonathan Marek 
32ec6859b2STodor Tomov #define MSM_CSID_NAME "msm_csid"
33ec6859b2STodor Tomov 
3476005817SRobert Foss const char * const csid_testgen_modes[] = {
3576005817SRobert Foss 	"Disabled",
3676005817SRobert Foss 	"Incrementing",
3776005817SRobert Foss 	"Alternating 0x55/0xAA",
3876005817SRobert Foss 	"All Zeros 0x00",
3976005817SRobert Foss 	"All Ones 0xFF",
4076005817SRobert Foss 	"Pseudo-random Data",
4176005817SRobert Foss 	"User Specified",
42eebe6d00SRobert Foss 	"Complex pattern",
43eebe6d00SRobert Foss 	"Color box",
44eebe6d00SRobert Foss 	"Color bars",
4576005817SRobert Foss 	NULL
46ec6859b2STodor Tomov };
47ec6859b2STodor Tomov 
4876005817SRobert Foss u32 csid_find_code(u32 *codes, unsigned int ncodes,
4976005817SRobert Foss 		   unsigned int match_format_idx, u32 match_code)
507e37f47fSTodor Tomov {
517e37f47fSTodor Tomov 	int i;
527e37f47fSTodor Tomov 
5376005817SRobert Foss 	if (!match_code && (match_format_idx >= ncodes))
547e37f47fSTodor Tomov 		return 0;
557e37f47fSTodor Tomov 
5676005817SRobert Foss 	for (i = 0; i < ncodes; i++)
5776005817SRobert Foss 		if (match_code) {
5876005817SRobert Foss 			if (codes[i] == match_code)
5976005817SRobert Foss 				return match_code;
607e37f47fSTodor Tomov 		} else {
6176005817SRobert Foss 			if (i == match_format_idx)
6276005817SRobert Foss 				return codes[i];
637e37f47fSTodor Tomov 		}
647e37f47fSTodor Tomov 
6576005817SRobert Foss 	return codes[0];
667e37f47fSTodor Tomov }
677e37f47fSTodor Tomov 
6876005817SRobert Foss const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats,
6976005817SRobert Foss 					     unsigned int nformats,
70cba3819dSTodor Tomov 					     u32 code)
71ec6859b2STodor Tomov {
72ec6859b2STodor Tomov 	unsigned int i;
73ec6859b2STodor Tomov 
7476005817SRobert Foss 	for (i = 0; i < nformats; i++)
75cba3819dSTodor Tomov 		if (code == formats[i].code)
76cba3819dSTodor Tomov 			return &formats[i];
77ec6859b2STodor Tomov 
78ec6859b2STodor Tomov 	WARN(1, "Unknown format\n");
79ec6859b2STodor Tomov 
80cba3819dSTodor Tomov 	return &formats[0];
81ec6859b2STodor Tomov }
82ec6859b2STodor Tomov 
83ec6859b2STodor Tomov /*
84ec6859b2STodor Tomov  * csid_set_clock_rates - Calculate and set clock rates on CSID module
85ec6859b2STodor Tomov  * @csiphy: CSID device
86ec6859b2STodor Tomov  */
87ec6859b2STodor Tomov static int csid_set_clock_rates(struct csid_device *csid)
88ec6859b2STodor Tomov {
899c3e59deSTodor Tomov 	struct device *dev = csid->camss->dev;
9078c2cc28SAndrey Konovalov 	const struct csid_format *fmt;
9178c2cc28SAndrey Konovalov 	s64 link_freq;
92ec6859b2STodor Tomov 	int i, j;
93ec6859b2STodor Tomov 	int ret;
94ec6859b2STodor Tomov 
9578c2cc28SAndrey Konovalov 	fmt = csid_get_fmt_entry(csid->formats, csid->nformats,
9678c2cc28SAndrey Konovalov 				 csid->fmt[MSM_CSIPHY_PAD_SINK].code);
9778c2cc28SAndrey Konovalov 	link_freq = camss_get_link_freq(&csid->subdev.entity, fmt->bpp,
9878c2cc28SAndrey Konovalov 					csid->phy.lane_cnt);
9978c2cc28SAndrey Konovalov 	if (link_freq < 0)
10078c2cc28SAndrey Konovalov 		link_freq = 0;
101ec6859b2STodor Tomov 
102ec6859b2STodor Tomov 	for (i = 0; i < csid->nclocks; i++) {
103ec6859b2STodor Tomov 		struct camss_clock *clock = &csid->clock[i];
104ec6859b2STodor Tomov 
105ec6859b2STodor Tomov 		if (!strcmp(clock->name, "csi0") ||
1069c3e59deSTodor Tomov 		    !strcmp(clock->name, "csi1") ||
1079c3e59deSTodor Tomov 		    !strcmp(clock->name, "csi2") ||
1089c3e59deSTodor Tomov 		    !strcmp(clock->name, "csi3")) {
10978c2cc28SAndrey Konovalov 			u64 min_rate = link_freq / 4;
110ec6859b2STodor Tomov 			long rate;
111ec6859b2STodor Tomov 
112ec6859b2STodor Tomov 			camss_add_clock_margin(&min_rate);
113ec6859b2STodor Tomov 
114ec6859b2STodor Tomov 			for (j = 0; j < clock->nfreqs; j++)
115ec6859b2STodor Tomov 				if (min_rate < clock->freq[j])
116ec6859b2STodor Tomov 					break;
117ec6859b2STodor Tomov 
118ec6859b2STodor Tomov 			if (j == clock->nfreqs) {
119ec6859b2STodor Tomov 				dev_err(dev,
120ec6859b2STodor Tomov 					"Pixel clock is too high for CSID\n");
121ec6859b2STodor Tomov 				return -EINVAL;
122ec6859b2STodor Tomov 			}
123ec6859b2STodor Tomov 
124ec6859b2STodor Tomov 			/* if sensor pixel clock is not available */
125ec6859b2STodor Tomov 			/* set highest possible CSID clock rate */
126ec6859b2STodor Tomov 			if (min_rate == 0)
127ec6859b2STodor Tomov 				j = clock->nfreqs - 1;
128ec6859b2STodor Tomov 
129ec6859b2STodor Tomov 			rate = clk_round_rate(clock->clk, clock->freq[j]);
130ec6859b2STodor Tomov 			if (rate < 0) {
131ec6859b2STodor Tomov 				dev_err(dev, "clk round rate failed: %ld\n",
132ec6859b2STodor Tomov 					rate);
133ec6859b2STodor Tomov 				return -EINVAL;
134ec6859b2STodor Tomov 			}
135ec6859b2STodor Tomov 
136ec6859b2STodor Tomov 			ret = clk_set_rate(clock->clk, rate);
137ec6859b2STodor Tomov 			if (ret < 0) {
138ec6859b2STodor Tomov 				dev_err(dev, "clk set rate failed: %d\n", ret);
139ec6859b2STodor Tomov 				return ret;
140ec6859b2STodor Tomov 			}
141eebe6d00SRobert Foss 		} else if (clock->nfreqs) {
142eebe6d00SRobert Foss 			clk_set_rate(clock->clk, clock->freq[0]);
143ec6859b2STodor Tomov 		}
144ec6859b2STodor Tomov 	}
145ec6859b2STodor Tomov 
146ec6859b2STodor Tomov 	return 0;
147ec6859b2STodor Tomov }
148ec6859b2STodor Tomov 
149ec6859b2STodor Tomov /*
150ec6859b2STodor Tomov  * csid_set_power - Power on/off CSID module
151ec6859b2STodor Tomov  * @sd: CSID V4L2 subdevice
152ec6859b2STodor Tomov  * @on: Requested power state
153ec6859b2STodor Tomov  *
154ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
155ec6859b2STodor Tomov  */
156ec6859b2STodor Tomov static int csid_set_power(struct v4l2_subdev *sd, int on)
157ec6859b2STodor Tomov {
158ec6859b2STodor Tomov 	struct csid_device *csid = v4l2_get_subdevdata(sd);
159c5af8db8SBryan O'Donoghue 	struct camss *camss = csid->camss;
160c5af8db8SBryan O'Donoghue 	struct device *dev = camss->dev;
161c5af8db8SBryan O'Donoghue 	struct vfe_device *vfe = &camss->vfe[csid->id];
1620d814017SBryan O'Donoghue 	int ret = 0;
163ec6859b2STodor Tomov 
164ec6859b2STodor Tomov 	if (on) {
165b2c2715eSBryan O'Donoghue 		/*
166b2c2715eSBryan O'Donoghue 		 * From SDM845 onwards, the VFE needs to be powered on before
167b2c2715eSBryan O'Donoghue 		 * switching on the CSID. Do so unconditionally, as there is no
168b2c2715eSBryan O'Donoghue 		 * drawback in following the same powering order on older SoCs.
169b2c2715eSBryan O'Donoghue 		 */
170c5af8db8SBryan O'Donoghue 		ret = vfe_get(vfe);
171c5af8db8SBryan O'Donoghue 		if (ret < 0)
172c5af8db8SBryan O'Donoghue 			return ret;
173c5af8db8SBryan O'Donoghue 
17409dfb36cSMauro Carvalho Chehab 		ret = pm_runtime_resume_and_get(dev);
17509dfb36cSMauro Carvalho Chehab 		if (ret < 0)
176ec6859b2STodor Tomov 			return ret;
177ec6859b2STodor Tomov 
1780d814017SBryan O'Donoghue 		ret = regulator_bulk_enable(csid->num_supplies,
1790d814017SBryan O'Donoghue 					    csid->supplies);
18002afa816STodor Tomov 		if (ret < 0) {
18102afa816STodor Tomov 			pm_runtime_put_sync(dev);
18202afa816STodor Tomov 			return ret;
18302afa816STodor Tomov 		}
18402afa816STodor Tomov 
185ec6859b2STodor Tomov 		ret = csid_set_clock_rates(csid);
186ec6859b2STodor Tomov 		if (ret < 0) {
1870d814017SBryan O'Donoghue 			regulator_bulk_disable(csid->num_supplies,
1880d814017SBryan O'Donoghue 					       csid->supplies);
18902afa816STodor Tomov 			pm_runtime_put_sync(dev);
190ec6859b2STodor Tomov 			return ret;
191ec6859b2STodor Tomov 		}
192ec6859b2STodor Tomov 
193ec6859b2STodor Tomov 		ret = camss_enable_clocks(csid->nclocks, csid->clock, dev);
194ec6859b2STodor Tomov 		if (ret < 0) {
1950d814017SBryan O'Donoghue 			regulator_bulk_disable(csid->num_supplies,
1960d814017SBryan O'Donoghue 					       csid->supplies);
19702afa816STodor Tomov 			pm_runtime_put_sync(dev);
198ec6859b2STodor Tomov 			return ret;
199ec6859b2STodor Tomov 		}
200ec6859b2STodor Tomov 
2013c4ed72aSMilen Mitkov 		csid->phy.need_vc_update = true;
2023c4ed72aSMilen Mitkov 
203ec6859b2STodor Tomov 		enable_irq(csid->irq);
204ec6859b2STodor Tomov 
20576005817SRobert Foss 		ret = csid->ops->reset(csid);
206ec6859b2STodor Tomov 		if (ret < 0) {
207ec6859b2STodor Tomov 			disable_irq(csid->irq);
208ec6859b2STodor Tomov 			camss_disable_clocks(csid->nclocks, csid->clock);
2090d814017SBryan O'Donoghue 			regulator_bulk_disable(csid->num_supplies,
2100d814017SBryan O'Donoghue 					       csid->supplies);
21102afa816STodor Tomov 			pm_runtime_put_sync(dev);
212ec6859b2STodor Tomov 			return ret;
213ec6859b2STodor Tomov 		}
214ec6859b2STodor Tomov 
21576005817SRobert Foss 		csid->ops->hw_version(csid);
216ec6859b2STodor Tomov 	} else {
217ec6859b2STodor Tomov 		disable_irq(csid->irq);
218ec6859b2STodor Tomov 		camss_disable_clocks(csid->nclocks, csid->clock);
2190d814017SBryan O'Donoghue 		regulator_bulk_disable(csid->num_supplies,
2200d814017SBryan O'Donoghue 				       csid->supplies);
22102afa816STodor Tomov 		pm_runtime_put_sync(dev);
222c5af8db8SBryan O'Donoghue 		vfe_put(vfe);
223ec6859b2STodor Tomov 	}
224ec6859b2STodor Tomov 
225ec6859b2STodor Tomov 	return ret;
226ec6859b2STodor Tomov }
227ec6859b2STodor Tomov 
228ec6859b2STodor Tomov /*
229ec6859b2STodor Tomov  * csid_set_stream - Enable/disable streaming on CSID module
230ec6859b2STodor Tomov  * @sd: CSID V4L2 subdevice
231ec6859b2STodor Tomov  * @enable: Requested streaming state
232ec6859b2STodor Tomov  *
233ec6859b2STodor Tomov  * Main configuration of CSID module is also done here.
234ec6859b2STodor Tomov  *
235ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
236ec6859b2STodor Tomov  */
237ec6859b2STodor Tomov static int csid_set_stream(struct v4l2_subdev *sd, int enable)
238ec6859b2STodor Tomov {
239ec6859b2STodor Tomov 	struct csid_device *csid = v4l2_get_subdevdata(sd);
240ec6859b2STodor Tomov 	int ret;
241ec6859b2STodor Tomov 
24276005817SRobert Foss 	if (enable) {
243ec6859b2STodor Tomov 		ret = v4l2_ctrl_handler_setup(&csid->ctrls);
244ec6859b2STodor Tomov 		if (ret < 0) {
2459c3e59deSTodor Tomov 			dev_err(csid->camss->dev,
246ec6859b2STodor Tomov 				"could not sync v4l2 controls: %d\n", ret);
247ec6859b2STodor Tomov 			return ret;
248ec6859b2STodor Tomov 		}
249ec6859b2STodor Tomov 
25076005817SRobert Foss 		if (!csid->testgen.enabled &&
251b2e44430SLaurent Pinchart 		    !media_pad_remote_pad_first(&csid->pads[MSM_CSID_PAD_SINK]))
252ec6859b2STodor Tomov 			return -ENOLINK;
253ec6859b2STodor Tomov 	}
254ec6859b2STodor Tomov 
2553c4ed72aSMilen Mitkov 	if (csid->phy.need_vc_update) {
25676005817SRobert Foss 		csid->ops->configure_stream(csid, enable);
2573c4ed72aSMilen Mitkov 		csid->phy.need_vc_update = false;
2583c4ed72aSMilen Mitkov 	}
259ec6859b2STodor Tomov 
260ec6859b2STodor Tomov 	return 0;
261ec6859b2STodor Tomov }
262ec6859b2STodor Tomov 
263ec6859b2STodor Tomov /*
264ec6859b2STodor Tomov  * __csid_get_format - Get pointer to format structure
265ec6859b2STodor Tomov  * @csid: CSID device
266c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
267ec6859b2STodor Tomov  * @pad: pad from which format is requested
268ec6859b2STodor Tomov  * @which: TRY or ACTIVE format
269ec6859b2STodor Tomov  *
270ec6859b2STodor Tomov  * Return pointer to TRY or ACTIVE format structure
271ec6859b2STodor Tomov  */
272ec6859b2STodor Tomov static struct v4l2_mbus_framefmt *
273ec6859b2STodor Tomov __csid_get_format(struct csid_device *csid,
2740d346d2aSTomi Valkeinen 		  struct v4l2_subdev_state *sd_state,
275ec6859b2STodor Tomov 		  unsigned int pad,
276ec6859b2STodor Tomov 		  enum v4l2_subdev_format_whence which)
277ec6859b2STodor Tomov {
278ec6859b2STodor Tomov 	if (which == V4L2_SUBDEV_FORMAT_TRY)
279bc0e8d91SSakari Ailus 		return v4l2_subdev_state_get_format(sd_state, pad);
280ec6859b2STodor Tomov 
281ec6859b2STodor Tomov 	return &csid->fmt[pad];
282ec6859b2STodor Tomov }
283ec6859b2STodor Tomov 
284ec6859b2STodor Tomov /*
285ec6859b2STodor Tomov  * csid_try_format - Handle try format by pad subdev method
286ec6859b2STodor Tomov  * @csid: CSID device
287c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
288ec6859b2STodor Tomov  * @pad: pad on which format is requested
289ec6859b2STodor Tomov  * @fmt: pointer to v4l2 format structure
290ec6859b2STodor Tomov  * @which: wanted subdev format
291ec6859b2STodor Tomov  */
292ec6859b2STodor Tomov static void csid_try_format(struct csid_device *csid,
2930d346d2aSTomi Valkeinen 			    struct v4l2_subdev_state *sd_state,
294ec6859b2STodor Tomov 			    unsigned int pad,
295ec6859b2STodor Tomov 			    struct v4l2_mbus_framefmt *fmt,
296ec6859b2STodor Tomov 			    enum v4l2_subdev_format_whence which)
297ec6859b2STodor Tomov {
298ec6859b2STodor Tomov 	unsigned int i;
299ec6859b2STodor Tomov 
300ec6859b2STodor Tomov 	switch (pad) {
301ec6859b2STodor Tomov 	case MSM_CSID_PAD_SINK:
302ec6859b2STodor Tomov 		/* Set format on sink pad */
303ec6859b2STodor Tomov 
304cba3819dSTodor Tomov 		for (i = 0; i < csid->nformats; i++)
305cba3819dSTodor Tomov 			if (fmt->code == csid->formats[i].code)
306ec6859b2STodor Tomov 				break;
307ec6859b2STodor Tomov 
308ec6859b2STodor Tomov 		/* If not found, use UYVY as default */
309cba3819dSTodor Tomov 		if (i >= csid->nformats)
31089936bfbSMartin Dørum 			fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
311ec6859b2STodor Tomov 
312ec6859b2STodor Tomov 		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
313ec6859b2STodor Tomov 		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
314ec6859b2STodor Tomov 
315ec6859b2STodor Tomov 		fmt->field = V4L2_FIELD_NONE;
316ec6859b2STodor Tomov 		fmt->colorspace = V4L2_COLORSPACE_SRGB;
317ec6859b2STodor Tomov 
318ec6859b2STodor Tomov 		break;
319ec6859b2STodor Tomov 
320ec6859b2STodor Tomov 	case MSM_CSID_PAD_SRC:
321ec6859b2STodor Tomov 		if (csid->testgen_mode->cur.val == 0) {
3227e37f47fSTodor Tomov 			/* Test generator is disabled, */
3237e37f47fSTodor Tomov 			/* keep pad formats in sync */
3247e37f47fSTodor Tomov 			u32 code = fmt->code;
325ec6859b2STodor Tomov 
3260d346d2aSTomi Valkeinen 			*fmt = *__csid_get_format(csid, sd_state,
327ec6859b2STodor Tomov 						      MSM_CSID_PAD_SINK, which);
32876005817SRobert Foss 			fmt->code = csid->ops->src_pad_code(csid, fmt->code, 0, code);
329ec6859b2STodor Tomov 		} else {
330ec6859b2STodor Tomov 			/* Test generator is enabled, set format on source */
331ec6859b2STodor Tomov 			/* pad to allow test generator usage */
332ec6859b2STodor Tomov 
333cba3819dSTodor Tomov 			for (i = 0; i < csid->nformats; i++)
334cba3819dSTodor Tomov 				if (csid->formats[i].code == fmt->code)
335ec6859b2STodor Tomov 					break;
336ec6859b2STodor Tomov 
337ec6859b2STodor Tomov 			/* If not found, use UYVY as default */
338cba3819dSTodor Tomov 			if (i >= csid->nformats)
33989936bfbSMartin Dørum 				fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
340ec6859b2STodor Tomov 
341ec6859b2STodor Tomov 			fmt->width = clamp_t(u32, fmt->width, 1, 8191);
342ec6859b2STodor Tomov 			fmt->height = clamp_t(u32, fmt->height, 1, 8191);
343ec6859b2STodor Tomov 
344ec6859b2STodor Tomov 			fmt->field = V4L2_FIELD_NONE;
345ec6859b2STodor Tomov 		}
346ec6859b2STodor Tomov 		break;
347ec6859b2STodor Tomov 	}
348ec6859b2STodor Tomov 
349ec6859b2STodor Tomov 	fmt->colorspace = V4L2_COLORSPACE_SRGB;
350ec6859b2STodor Tomov }
351ec6859b2STodor Tomov 
352ec6859b2STodor Tomov /*
353ec6859b2STodor Tomov  * csid_enum_mbus_code - Handle pixel format enumeration
354ec6859b2STodor Tomov  * @sd: CSID V4L2 subdevice
355c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
356ec6859b2STodor Tomov  * @code: pointer to v4l2_subdev_mbus_code_enum structure
357ec6859b2STodor Tomov  * return -EINVAL or zero on success
358ec6859b2STodor Tomov  */
359ec6859b2STodor Tomov static int csid_enum_mbus_code(struct v4l2_subdev *sd,
3600d346d2aSTomi Valkeinen 			       struct v4l2_subdev_state *sd_state,
361ec6859b2STodor Tomov 			       struct v4l2_subdev_mbus_code_enum *code)
362ec6859b2STodor Tomov {
363ec6859b2STodor Tomov 	struct csid_device *csid = v4l2_get_subdevdata(sd);
364ec6859b2STodor Tomov 
365ec6859b2STodor Tomov 	if (code->pad == MSM_CSID_PAD_SINK) {
366cba3819dSTodor Tomov 		if (code->index >= csid->nformats)
367ec6859b2STodor Tomov 			return -EINVAL;
368ec6859b2STodor Tomov 
369cba3819dSTodor Tomov 		code->code = csid->formats[code->index].code;
370ec6859b2STodor Tomov 	} else {
371ec6859b2STodor Tomov 		if (csid->testgen_mode->cur.val == 0) {
3727e37f47fSTodor Tomov 			struct v4l2_mbus_framefmt *sink_fmt;
373ec6859b2STodor Tomov 
3740d346d2aSTomi Valkeinen 			sink_fmt = __csid_get_format(csid, sd_state,
3757e37f47fSTodor Tomov 						     MSM_CSID_PAD_SINK,
376ec6859b2STodor Tomov 						     code->which);
377ec6859b2STodor Tomov 
37876005817SRobert Foss 			code->code = csid->ops->src_pad_code(csid, sink_fmt->code,
3797e37f47fSTodor Tomov 						       code->index, 0);
3807e37f47fSTodor Tomov 			if (!code->code)
3817e37f47fSTodor Tomov 				return -EINVAL;
382ec6859b2STodor Tomov 		} else {
383cba3819dSTodor Tomov 			if (code->index >= csid->nformats)
384ec6859b2STodor Tomov 				return -EINVAL;
385ec6859b2STodor Tomov 
386cba3819dSTodor Tomov 			code->code = csid->formats[code->index].code;
387ec6859b2STodor Tomov 		}
388ec6859b2STodor Tomov 	}
389ec6859b2STodor Tomov 
390ec6859b2STodor Tomov 	return 0;
391ec6859b2STodor Tomov }
392ec6859b2STodor Tomov 
393ec6859b2STodor Tomov /*
394ec6859b2STodor Tomov  * csid_enum_frame_size - Handle frame size enumeration
395ec6859b2STodor Tomov  * @sd: CSID V4L2 subdevice
396c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
397ec6859b2STodor Tomov  * @fse: pointer to v4l2_subdev_frame_size_enum structure
398ec6859b2STodor Tomov  * return -EINVAL or zero on success
399ec6859b2STodor Tomov  */
400ec6859b2STodor Tomov static int csid_enum_frame_size(struct v4l2_subdev *sd,
4010d346d2aSTomi Valkeinen 				struct v4l2_subdev_state *sd_state,
402ec6859b2STodor Tomov 				struct v4l2_subdev_frame_size_enum *fse)
403ec6859b2STodor Tomov {
404ec6859b2STodor Tomov 	struct csid_device *csid = v4l2_get_subdevdata(sd);
405ec6859b2STodor Tomov 	struct v4l2_mbus_framefmt format;
406ec6859b2STodor Tomov 
407ec6859b2STodor Tomov 	if (fse->index != 0)
408ec6859b2STodor Tomov 		return -EINVAL;
409ec6859b2STodor Tomov 
410ec6859b2STodor Tomov 	format.code = fse->code;
411ec6859b2STodor Tomov 	format.width = 1;
412ec6859b2STodor Tomov 	format.height = 1;
4130d346d2aSTomi Valkeinen 	csid_try_format(csid, sd_state, fse->pad, &format, fse->which);
414ec6859b2STodor Tomov 	fse->min_width = format.width;
415ec6859b2STodor Tomov 	fse->min_height = format.height;
416ec6859b2STodor Tomov 
417ec6859b2STodor Tomov 	if (format.code != fse->code)
418ec6859b2STodor Tomov 		return -EINVAL;
419ec6859b2STodor Tomov 
420ec6859b2STodor Tomov 	format.code = fse->code;
421ec6859b2STodor Tomov 	format.width = -1;
422ec6859b2STodor Tomov 	format.height = -1;
4230d346d2aSTomi Valkeinen 	csid_try_format(csid, sd_state, fse->pad, &format, fse->which);
424ec6859b2STodor Tomov 	fse->max_width = format.width;
425ec6859b2STodor Tomov 	fse->max_height = format.height;
426ec6859b2STodor Tomov 
427ec6859b2STodor Tomov 	return 0;
428ec6859b2STodor Tomov }
429ec6859b2STodor Tomov 
430ec6859b2STodor Tomov /*
431ec6859b2STodor Tomov  * csid_get_format - Handle get format by pads subdev method
432ec6859b2STodor Tomov  * @sd: CSID V4L2 subdevice
433c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
434ec6859b2STodor Tomov  * @fmt: pointer to v4l2 subdev format structure
435ec6859b2STodor Tomov  *
436ec6859b2STodor Tomov  * Return -EINVAL or zero on success
437ec6859b2STodor Tomov  */
438ec6859b2STodor Tomov static int csid_get_format(struct v4l2_subdev *sd,
4390d346d2aSTomi Valkeinen 			   struct v4l2_subdev_state *sd_state,
440ec6859b2STodor Tomov 			   struct v4l2_subdev_format *fmt)
441ec6859b2STodor Tomov {
442ec6859b2STodor Tomov 	struct csid_device *csid = v4l2_get_subdevdata(sd);
443ec6859b2STodor Tomov 	struct v4l2_mbus_framefmt *format;
444ec6859b2STodor Tomov 
4450d346d2aSTomi Valkeinen 	format = __csid_get_format(csid, sd_state, fmt->pad, fmt->which);
446ec6859b2STodor Tomov 	if (format == NULL)
447ec6859b2STodor Tomov 		return -EINVAL;
448ec6859b2STodor Tomov 
449ec6859b2STodor Tomov 	fmt->format = *format;
450ec6859b2STodor Tomov 
451ec6859b2STodor Tomov 	return 0;
452ec6859b2STodor Tomov }
453ec6859b2STodor Tomov 
454ec6859b2STodor Tomov /*
455ec6859b2STodor Tomov  * csid_set_format - Handle set format by pads subdev method
456ec6859b2STodor Tomov  * @sd: CSID V4L2 subdevice
457c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
458ec6859b2STodor Tomov  * @fmt: pointer to v4l2 subdev format structure
459ec6859b2STodor Tomov  *
460ec6859b2STodor Tomov  * Return -EINVAL or zero on success
461ec6859b2STodor Tomov  */
462ec6859b2STodor Tomov static int csid_set_format(struct v4l2_subdev *sd,
4630d346d2aSTomi Valkeinen 			   struct v4l2_subdev_state *sd_state,
464ec6859b2STodor Tomov 			   struct v4l2_subdev_format *fmt)
465ec6859b2STodor Tomov {
466ec6859b2STodor Tomov 	struct csid_device *csid = v4l2_get_subdevdata(sd);
467ec6859b2STodor Tomov 	struct v4l2_mbus_framefmt *format;
4683c4ed72aSMilen Mitkov 	int i;
469ec6859b2STodor Tomov 
4700d346d2aSTomi Valkeinen 	format = __csid_get_format(csid, sd_state, fmt->pad, fmt->which);
471ec6859b2STodor Tomov 	if (format == NULL)
472ec6859b2STodor Tomov 		return -EINVAL;
473ec6859b2STodor Tomov 
4740d346d2aSTomi Valkeinen 	csid_try_format(csid, sd_state, fmt->pad, &fmt->format, fmt->which);
475ec6859b2STodor Tomov 	*format = fmt->format;
476ec6859b2STodor Tomov 
4773c4ed72aSMilen Mitkov 	/* Propagate the format from sink to source pads */
478ec6859b2STodor Tomov 	if (fmt->pad == MSM_CSID_PAD_SINK) {
4793c4ed72aSMilen Mitkov 		for (i = MSM_CSID_PAD_FIRST_SRC; i < MSM_CSID_PADS_NUM; ++i) {
4803c4ed72aSMilen Mitkov 			format = __csid_get_format(csid, sd_state, i, fmt->which);
481ec6859b2STodor Tomov 
482ec6859b2STodor Tomov 			*format = fmt->format;
4833c4ed72aSMilen Mitkov 			csid_try_format(csid, sd_state, i, format, fmt->which);
4843c4ed72aSMilen Mitkov 		}
485ec6859b2STodor Tomov 	}
486ec6859b2STodor Tomov 
487ec6859b2STodor Tomov 	return 0;
488ec6859b2STodor Tomov }
489ec6859b2STodor Tomov 
490ec6859b2STodor Tomov /*
491ec6859b2STodor Tomov  * csid_init_formats - Initialize formats on all pads
492ec6859b2STodor Tomov  * @sd: CSID V4L2 subdevice
493ec6859b2STodor Tomov  * @fh: V4L2 subdev file handle
494ec6859b2STodor Tomov  *
495ec6859b2STodor Tomov  * Initialize all pad formats with default values.
496ec6859b2STodor Tomov  *
497ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
498ec6859b2STodor Tomov  */
499ec6859b2STodor Tomov static int csid_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
500ec6859b2STodor Tomov {
501ec6859b2STodor Tomov 	struct v4l2_subdev_format format = {
502ec6859b2STodor Tomov 		.pad = MSM_CSID_PAD_SINK,
503ec6859b2STodor Tomov 		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
504ec6859b2STodor Tomov 			      V4L2_SUBDEV_FORMAT_ACTIVE,
505ec6859b2STodor Tomov 		.format = {
50689936bfbSMartin Dørum 			.code = MEDIA_BUS_FMT_UYVY8_1X16,
507ec6859b2STodor Tomov 			.width = 1920,
508ec6859b2STodor Tomov 			.height = 1080
509ec6859b2STodor Tomov 		}
510ec6859b2STodor Tomov 	};
511ec6859b2STodor Tomov 
5120d346d2aSTomi Valkeinen 	return csid_set_format(sd, fh ? fh->state : NULL, &format);
513ec6859b2STodor Tomov }
514ec6859b2STodor Tomov 
515ec6859b2STodor Tomov /*
516ec6859b2STodor Tomov  * csid_set_test_pattern - Set test generator's pattern mode
517ec6859b2STodor Tomov  * @csid: CSID device
518ec6859b2STodor Tomov  * @value: desired test pattern mode
519ec6859b2STodor Tomov  *
520ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
521ec6859b2STodor Tomov  */
522ec6859b2STodor Tomov static int csid_set_test_pattern(struct csid_device *csid, s32 value)
523ec6859b2STodor Tomov {
524ec6859b2STodor Tomov 	struct csid_testgen_config *tg = &csid->testgen;
525ec6859b2STodor Tomov 
526ec6859b2STodor Tomov 	/* If CSID is linked to CSIPHY, do not allow to enable test generator */
527b2e44430SLaurent Pinchart 	if (value && media_pad_remote_pad_first(&csid->pads[MSM_CSID_PAD_SINK]))
528ec6859b2STodor Tomov 		return -EBUSY;
529ec6859b2STodor Tomov 
530ec6859b2STodor Tomov 	tg->enabled = !!value;
531ec6859b2STodor Tomov 
53276005817SRobert Foss 	return csid->ops->configure_testgen_pattern(csid, value);
533ec6859b2STodor Tomov }
534ec6859b2STodor Tomov 
535ec6859b2STodor Tomov /*
536ec6859b2STodor Tomov  * csid_s_ctrl - Handle set control subdev method
537ec6859b2STodor Tomov  * @ctrl: pointer to v4l2 control structure
538ec6859b2STodor Tomov  *
539ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
540ec6859b2STodor Tomov  */
541ec6859b2STodor Tomov static int csid_s_ctrl(struct v4l2_ctrl *ctrl)
542ec6859b2STodor Tomov {
543ec6859b2STodor Tomov 	struct csid_device *csid = container_of(ctrl->handler,
544ec6859b2STodor Tomov 						struct csid_device, ctrls);
545ec6859b2STodor Tomov 	int ret = -EINVAL;
546ec6859b2STodor Tomov 
547ec6859b2STodor Tomov 	switch (ctrl->id) {
548ec6859b2STodor Tomov 	case V4L2_CID_TEST_PATTERN:
549ec6859b2STodor Tomov 		ret = csid_set_test_pattern(csid, ctrl->val);
550ec6859b2STodor Tomov 		break;
551ec6859b2STodor Tomov 	}
552ec6859b2STodor Tomov 
553ec6859b2STodor Tomov 	return ret;
554ec6859b2STodor Tomov }
555ec6859b2STodor Tomov 
556ec6859b2STodor Tomov static const struct v4l2_ctrl_ops csid_ctrl_ops = {
557ec6859b2STodor Tomov 	.s_ctrl = csid_s_ctrl,
558ec6859b2STodor Tomov };
559ec6859b2STodor Tomov 
560ec6859b2STodor Tomov /*
561ec6859b2STodor Tomov  * msm_csid_subdev_init - Initialize CSID device structure and resources
562ec6859b2STodor Tomov  * @csid: CSID device
563ec6859b2STodor Tomov  * @res: CSID module resources table
564ec6859b2STodor Tomov  * @id: CSID module id
565ec6859b2STodor Tomov  *
566ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
567ec6859b2STodor Tomov  */
5689c3e59deSTodor Tomov int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
5691643b787SBryan O'Donoghue 			 const struct camss_subdev_resources *res, u8 id)
570ec6859b2STodor Tomov {
5719c3e59deSTodor Tomov 	struct device *dev = camss->dev;
572ec6859b2STodor Tomov 	struct platform_device *pdev = to_platform_device(dev);
573ec6859b2STodor Tomov 	int i, j;
574ec6859b2STodor Tomov 	int ret;
575ec6859b2STodor Tomov 
5769c3e59deSTodor Tomov 	csid->camss = camss;
577ec6859b2STodor Tomov 	csid->id = id;
578c23c7998SBryan O'Donoghue 	csid->ops = res->ops;
579ec6859b2STodor Tomov 
58076005817SRobert Foss 	csid->ops->subdev_init(csid);
581cba3819dSTodor Tomov 
582ec6859b2STodor Tomov 	/* Memory */
583ec6859b2STodor Tomov 
5845900b051SBryan O'Donoghue 	if (camss->res->version == CAMSS_8250) {
585b4436a18SJonathan Marek 		/* for titan 480, CSID registers are inside the VFE region,
586b4436a18SJonathan Marek 		 * between the VFE "top" and "bus" registers. this requires
587b4436a18SJonathan Marek 		 * VFE to be initialized before CSID
588b4436a18SJonathan Marek 		 */
589b4436a18SJonathan Marek 		if (id >= 2) /* VFE/CSID lite */
590b4436a18SJonathan Marek 			csid->base = camss->vfe[id].base + VFE_480_LITE_CSID_OFFSET;
591b4436a18SJonathan Marek 		else
592b4436a18SJonathan Marek 			csid->base = camss->vfe[id].base + VFE_480_CSID_OFFSET;
593b4436a18SJonathan Marek 	} else {
594414e0a64Sdingsenjie 		csid->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
5956c0ed6d4SYang Yingliang 		if (IS_ERR(csid->base))
596ec6859b2STodor Tomov 			return PTR_ERR(csid->base);
597b4436a18SJonathan Marek 	}
598ec6859b2STodor Tomov 
599ec6859b2STodor Tomov 	/* Interrupt */
600ec6859b2STodor Tomov 
601b416be3aSLad Prabhakar 	ret = platform_get_irq_byname(pdev, res->interrupt[0]);
602b416be3aSLad Prabhakar 	if (ret < 0)
603b416be3aSLad Prabhakar 		return ret;
604ec6859b2STodor Tomov 
605b416be3aSLad Prabhakar 	csid->irq = ret;
606ec6859b2STodor Tomov 	snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d",
607ec6859b2STodor Tomov 		 dev_name(dev), MSM_CSID_NAME, csid->id);
60876005817SRobert Foss 	ret = devm_request_irq(dev, csid->irq, csid->ops->isr,
60914480e8dSTian Tao 			       IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN,
61014480e8dSTian Tao 			       csid->irq_name, csid);
611ec6859b2STodor Tomov 	if (ret < 0) {
612ec6859b2STodor Tomov 		dev_err(dev, "request_irq failed: %d\n", ret);
613ec6859b2STodor Tomov 		return ret;
614ec6859b2STodor Tomov 	}
615ec6859b2STodor Tomov 
616ec6859b2STodor Tomov 	/* Clocks */
617ec6859b2STodor Tomov 
618ec6859b2STodor Tomov 	csid->nclocks = 0;
619ec6859b2STodor Tomov 	while (res->clock[csid->nclocks])
620ec6859b2STodor Tomov 		csid->nclocks++;
621ec6859b2STodor Tomov 
622ec6859b2STodor Tomov 	csid->clock = devm_kcalloc(dev, csid->nclocks, sizeof(*csid->clock),
623ec6859b2STodor Tomov 				    GFP_KERNEL);
624ec6859b2STodor Tomov 	if (!csid->clock)
625ec6859b2STodor Tomov 		return -ENOMEM;
626ec6859b2STodor Tomov 
627ec6859b2STodor Tomov 	for (i = 0; i < csid->nclocks; i++) {
628ec6859b2STodor Tomov 		struct camss_clock *clock = &csid->clock[i];
629ec6859b2STodor Tomov 
630ec6859b2STodor Tomov 		clock->clk = devm_clk_get(dev, res->clock[i]);
631ec6859b2STodor Tomov 		if (IS_ERR(clock->clk))
632ec6859b2STodor Tomov 			return PTR_ERR(clock->clk);
633ec6859b2STodor Tomov 
634ec6859b2STodor Tomov 		clock->name = res->clock[i];
635ec6859b2STodor Tomov 
636ec6859b2STodor Tomov 		clock->nfreqs = 0;
637ec6859b2STodor Tomov 		while (res->clock_rate[i][clock->nfreqs])
638ec6859b2STodor Tomov 			clock->nfreqs++;
639ec6859b2STodor Tomov 
640ec6859b2STodor Tomov 		if (!clock->nfreqs) {
641ec6859b2STodor Tomov 			clock->freq = NULL;
642ec6859b2STodor Tomov 			continue;
643ec6859b2STodor Tomov 		}
644ec6859b2STodor Tomov 
645ec6859b2STodor Tomov 		clock->freq = devm_kcalloc(dev,
646ec6859b2STodor Tomov 					   clock->nfreqs,
647ec6859b2STodor Tomov 					   sizeof(*clock->freq),
648ec6859b2STodor Tomov 					   GFP_KERNEL);
649ec6859b2STodor Tomov 		if (!clock->freq)
650ec6859b2STodor Tomov 			return -ENOMEM;
651ec6859b2STodor Tomov 
652ec6859b2STodor Tomov 		for (j = 0; j < clock->nfreqs; j++)
653ec6859b2STodor Tomov 			clock->freq[j] = res->clock_rate[i][j];
654ec6859b2STodor Tomov 	}
655ec6859b2STodor Tomov 
656ec6859b2STodor Tomov 	/* Regulator */
6570d814017SBryan O'Donoghue 	for (i = 0; i < ARRAY_SIZE(res->regulators); i++) {
6580d814017SBryan O'Donoghue 		if (res->regulators[i])
6590d814017SBryan O'Donoghue 			csid->num_supplies++;
660ec6859b2STodor Tomov 	}
661ec6859b2STodor Tomov 
6620d814017SBryan O'Donoghue 	if (csid->num_supplies) {
6630d814017SBryan O'Donoghue 		csid->supplies = devm_kmalloc_array(camss->dev,
6640d814017SBryan O'Donoghue 						    csid->num_supplies,
6654c25384dSYang Yingliang 						    sizeof(*csid->supplies),
6660d814017SBryan O'Donoghue 						    GFP_KERNEL);
6670d814017SBryan O'Donoghue 		if (!csid->supplies)
6680d814017SBryan O'Donoghue 			return -ENOMEM;
6690d814017SBryan O'Donoghue 	}
6700d814017SBryan O'Donoghue 
6710d814017SBryan O'Donoghue 	for (i = 0; i < csid->num_supplies; i++)
6720d814017SBryan O'Donoghue 		csid->supplies[i].supply = res->regulators[i];
6730d814017SBryan O'Donoghue 
6740d814017SBryan O'Donoghue 	ret = devm_regulator_bulk_get(camss->dev, csid->num_supplies,
6750d814017SBryan O'Donoghue 				      csid->supplies);
6760d814017SBryan O'Donoghue 	if (ret)
6770d814017SBryan O'Donoghue 		return ret;
6780d814017SBryan O'Donoghue 
679ec6859b2STodor Tomov 	init_completion(&csid->reset_complete);
680ec6859b2STodor Tomov 
681ec6859b2STodor Tomov 	return 0;
682ec6859b2STodor Tomov }
683ec6859b2STodor Tomov 
684ec6859b2STodor Tomov /*
685ec6859b2STodor Tomov  * msm_csid_get_csid_id - Get CSID HW module id
686ec6859b2STodor Tomov  * @entity: Pointer to CSID media entity structure
687ec6859b2STodor Tomov  * @id: Return CSID HW module id here
688ec6859b2STodor Tomov  */
689ec6859b2STodor Tomov void msm_csid_get_csid_id(struct media_entity *entity, u8 *id)
690ec6859b2STodor Tomov {
691ec6859b2STodor Tomov 	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
692ec6859b2STodor Tomov 	struct csid_device *csid = v4l2_get_subdevdata(sd);
693ec6859b2STodor Tomov 
694ec6859b2STodor Tomov 	*id = csid->id;
695ec6859b2STodor Tomov }
696ec6859b2STodor Tomov 
697ec6859b2STodor Tomov /*
698ec6859b2STodor Tomov  * csid_get_lane_assign - Calculate CSI2 lane assign configuration parameter
699ec6859b2STodor Tomov  * @lane_cfg - CSI2 lane configuration
700ec6859b2STodor Tomov  *
701ec6859b2STodor Tomov  * Return lane assign
702ec6859b2STodor Tomov  */
703ec6859b2STodor Tomov static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg)
704ec6859b2STodor Tomov {
705ec6859b2STodor Tomov 	u32 lane_assign = 0;
706ec6859b2STodor Tomov 	int i;
707ec6859b2STodor Tomov 
708ec6859b2STodor Tomov 	for (i = 0; i < lane_cfg->num_data; i++)
709ec6859b2STodor Tomov 		lane_assign |= lane_cfg->data[i].pos << (i * 4);
710ec6859b2STodor Tomov 
711ec6859b2STodor Tomov 	return lane_assign;
712ec6859b2STodor Tomov }
713ec6859b2STodor Tomov 
714ec6859b2STodor Tomov /*
715ec6859b2STodor Tomov  * csid_link_setup - Setup CSID connections
716ec6859b2STodor Tomov  * @entity: Pointer to media entity structure
717ec6859b2STodor Tomov  * @local: Pointer to local pad
718ec6859b2STodor Tomov  * @remote: Pointer to remote pad
719ec6859b2STodor Tomov  * @flags: Link flags
720ec6859b2STodor Tomov  *
721ec6859b2STodor Tomov  * Return 0 on success
722ec6859b2STodor Tomov  */
723ec6859b2STodor Tomov static int csid_link_setup(struct media_entity *entity,
724ec6859b2STodor Tomov 			   const struct media_pad *local,
725ec6859b2STodor Tomov 			   const struct media_pad *remote, u32 flags)
726ec6859b2STodor Tomov {
727ec6859b2STodor Tomov 	if (flags & MEDIA_LNK_FL_ENABLED)
728b2e44430SLaurent Pinchart 		if (media_pad_remote_pad_first(local))
729ec6859b2STodor Tomov 			return -EBUSY;
730ec6859b2STodor Tomov 
731ec6859b2STodor Tomov 	if ((local->flags & MEDIA_PAD_FL_SINK) &&
732ec6859b2STodor Tomov 	    (flags & MEDIA_LNK_FL_ENABLED)) {
733ec6859b2STodor Tomov 		struct v4l2_subdev *sd;
734ec6859b2STodor Tomov 		struct csid_device *csid;
735ec6859b2STodor Tomov 		struct csiphy_device *csiphy;
736ec6859b2STodor Tomov 		struct csiphy_lanes_cfg *lane_cfg;
737ec6859b2STodor Tomov 
738ec6859b2STodor Tomov 		sd = media_entity_to_v4l2_subdev(entity);
739ec6859b2STodor Tomov 		csid = v4l2_get_subdevdata(sd);
740ec6859b2STodor Tomov 
741ec6859b2STodor Tomov 		/* If test generator is enabled */
742ec6859b2STodor Tomov 		/* do not allow a link from CSIPHY to CSID */
743ec6859b2STodor Tomov 		if (csid->testgen_mode->cur.val != 0)
744ec6859b2STodor Tomov 			return -EBUSY;
745ec6859b2STodor Tomov 
746ec6859b2STodor Tomov 		sd = media_entity_to_v4l2_subdev(remote->entity);
747ec6859b2STodor Tomov 		csiphy = v4l2_get_subdevdata(sd);
748ec6859b2STodor Tomov 
749ec6859b2STodor Tomov 		/* If a sensor is not linked to CSIPHY */
750ec6859b2STodor Tomov 		/* do no allow a link from CSIPHY to CSID */
751ec6859b2STodor Tomov 		if (!csiphy->cfg.csi2)
752ec6859b2STodor Tomov 			return -EPERM;
753ec6859b2STodor Tomov 
754ec6859b2STodor Tomov 		csid->phy.csiphy_id = csiphy->id;
755ec6859b2STodor Tomov 
756ec6859b2STodor Tomov 		lane_cfg = &csiphy->cfg.csi2->lane_cfg;
757ec6859b2STodor Tomov 		csid->phy.lane_cnt = lane_cfg->num_data;
758ec6859b2STodor Tomov 		csid->phy.lane_assign = csid_get_lane_assign(lane_cfg);
7593c4ed72aSMilen Mitkov 	}
7603c4ed72aSMilen Mitkov 	/* Decide which virtual channels to enable based on which source pads are enabled */
7613c4ed72aSMilen Mitkov 	if (local->flags & MEDIA_PAD_FL_SOURCE) {
7623c4ed72aSMilen Mitkov 		struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
7633c4ed72aSMilen Mitkov 		struct csid_device *csid = v4l2_get_subdevdata(sd);
7643c4ed72aSMilen Mitkov 		struct device *dev = csid->camss->dev;
765ec6859b2STodor Tomov 
7663c4ed72aSMilen Mitkov 		if (flags & MEDIA_LNK_FL_ENABLED)
7673c4ed72aSMilen Mitkov 			csid->phy.en_vc |= BIT(local->index - 1);
7683c4ed72aSMilen Mitkov 		else
7693c4ed72aSMilen Mitkov 			csid->phy.en_vc &= ~BIT(local->index - 1);
7703c4ed72aSMilen Mitkov 
7713c4ed72aSMilen Mitkov 		csid->phy.need_vc_update = true;
7723c4ed72aSMilen Mitkov 
7733c4ed72aSMilen Mitkov 		dev_dbg(dev, "%s: Enabled CSID virtual channels mask 0x%x\n",
7743c4ed72aSMilen Mitkov 			__func__, csid->phy.en_vc);
775ec6859b2STodor Tomov 	}
776ec6859b2STodor Tomov 
777ec6859b2STodor Tomov 	return 0;
778ec6859b2STodor Tomov }
779ec6859b2STodor Tomov 
780ec6859b2STodor Tomov static const struct v4l2_subdev_core_ops csid_core_ops = {
781ec6859b2STodor Tomov 	.s_power = csid_set_power,
782988b3ae3STodor Tomov 	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
783988b3ae3STodor Tomov 	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
784ec6859b2STodor Tomov };
785ec6859b2STodor Tomov 
786ec6859b2STodor Tomov static const struct v4l2_subdev_video_ops csid_video_ops = {
787ec6859b2STodor Tomov 	.s_stream = csid_set_stream,
788ec6859b2STodor Tomov };
789ec6859b2STodor Tomov 
790ec6859b2STodor Tomov static const struct v4l2_subdev_pad_ops csid_pad_ops = {
791ec6859b2STodor Tomov 	.enum_mbus_code = csid_enum_mbus_code,
792ec6859b2STodor Tomov 	.enum_frame_size = csid_enum_frame_size,
793ec6859b2STodor Tomov 	.get_fmt = csid_get_format,
794ec6859b2STodor Tomov 	.set_fmt = csid_set_format,
795ec6859b2STodor Tomov };
796ec6859b2STodor Tomov 
797ec6859b2STodor Tomov static const struct v4l2_subdev_ops csid_v4l2_ops = {
798ec6859b2STodor Tomov 	.core = &csid_core_ops,
799ec6859b2STodor Tomov 	.video = &csid_video_ops,
800ec6859b2STodor Tomov 	.pad = &csid_pad_ops,
801ec6859b2STodor Tomov };
802ec6859b2STodor Tomov 
803ec6859b2STodor Tomov static const struct v4l2_subdev_internal_ops csid_v4l2_internal_ops = {
804ec6859b2STodor Tomov 	.open = csid_init_formats,
805ec6859b2STodor Tomov };
806ec6859b2STodor Tomov 
807ec6859b2STodor Tomov static const struct media_entity_operations csid_media_ops = {
808ec6859b2STodor Tomov 	.link_setup = csid_link_setup,
809ec6859b2STodor Tomov 	.link_validate = v4l2_subdev_link_validate,
810ec6859b2STodor Tomov };
811ec6859b2STodor Tomov 
812ec6859b2STodor Tomov /*
813ec6859b2STodor Tomov  * msm_csid_register_entity - Register subdev node for CSID module
814ec6859b2STodor Tomov  * @csid: CSID device
815ec6859b2STodor Tomov  * @v4l2_dev: V4L2 device
816ec6859b2STodor Tomov  *
817ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
818ec6859b2STodor Tomov  */
819ec6859b2STodor Tomov int msm_csid_register_entity(struct csid_device *csid,
820ec6859b2STodor Tomov 			     struct v4l2_device *v4l2_dev)
821ec6859b2STodor Tomov {
822ec6859b2STodor Tomov 	struct v4l2_subdev *sd = &csid->subdev;
823ec6859b2STodor Tomov 	struct media_pad *pads = csid->pads;
8249c3e59deSTodor Tomov 	struct device *dev = csid->camss->dev;
8253c4ed72aSMilen Mitkov 	int i;
826ec6859b2STodor Tomov 	int ret;
827ec6859b2STodor Tomov 
828ec6859b2STodor Tomov 	v4l2_subdev_init(sd, &csid_v4l2_ops);
829ec6859b2STodor Tomov 	sd->internal_ops = &csid_v4l2_internal_ops;
830988b3ae3STodor Tomov 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
831988b3ae3STodor Tomov 		     V4L2_SUBDEV_FL_HAS_EVENTS;
832ec6859b2STodor Tomov 	snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
833ec6859b2STodor Tomov 		 MSM_CSID_NAME, csid->id);
834ec6859b2STodor Tomov 	v4l2_set_subdevdata(sd, csid);
835ec6859b2STodor Tomov 
836ec6859b2STodor Tomov 	ret = v4l2_ctrl_handler_init(&csid->ctrls, 1);
837ec6859b2STodor Tomov 	if (ret < 0) {
838ec6859b2STodor Tomov 		dev_err(dev, "Failed to init ctrl handler: %d\n", ret);
839ec6859b2STodor Tomov 		return ret;
840ec6859b2STodor Tomov 	}
841ec6859b2STodor Tomov 
842ec6859b2STodor Tomov 	csid->testgen_mode = v4l2_ctrl_new_std_menu_items(&csid->ctrls,
843ec6859b2STodor Tomov 				&csid_ctrl_ops, V4L2_CID_TEST_PATTERN,
84476005817SRobert Foss 				csid->testgen.nmodes, 0, 0,
84576005817SRobert Foss 				csid->testgen.modes);
846ec6859b2STodor Tomov 
847ec6859b2STodor Tomov 	if (csid->ctrls.error) {
848ec6859b2STodor Tomov 		dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error);
849ec6859b2STodor Tomov 		ret = csid->ctrls.error;
850ec6859b2STodor Tomov 		goto free_ctrl;
851ec6859b2STodor Tomov 	}
852ec6859b2STodor Tomov 
853ec6859b2STodor Tomov 	csid->subdev.ctrl_handler = &csid->ctrls;
854ec6859b2STodor Tomov 
855ec6859b2STodor Tomov 	ret = csid_init_formats(sd, NULL);
856ec6859b2STodor Tomov 	if (ret < 0) {
857ec6859b2STodor Tomov 		dev_err(dev, "Failed to init format: %d\n", ret);
858ec6859b2STodor Tomov 		goto free_ctrl;
859ec6859b2STodor Tomov 	}
860ec6859b2STodor Tomov 
861ec6859b2STodor Tomov 	pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
8623c4ed72aSMilen Mitkov 	for (i = MSM_CSID_PAD_FIRST_SRC; i < MSM_CSID_PADS_NUM; ++i)
8633c4ed72aSMilen Mitkov 		pads[i].flags = MEDIA_PAD_FL_SOURCE;
864ec6859b2STodor Tomov 
86550795910SAndrey Konovalov 	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
866ec6859b2STodor Tomov 	sd->entity.ops = &csid_media_ops;
867ec6859b2STodor Tomov 	ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads);
868ec6859b2STodor Tomov 	if (ret < 0) {
869ec6859b2STodor Tomov 		dev_err(dev, "Failed to init media entity: %d\n", ret);
870ec6859b2STodor Tomov 		goto free_ctrl;
871ec6859b2STodor Tomov 	}
872ec6859b2STodor Tomov 
873ec6859b2STodor Tomov 	ret = v4l2_device_register_subdev(v4l2_dev, sd);
874ec6859b2STodor Tomov 	if (ret < 0) {
875ec6859b2STodor Tomov 		dev_err(dev, "Failed to register subdev: %d\n", ret);
876ec6859b2STodor Tomov 		goto media_cleanup;
877ec6859b2STodor Tomov 	}
878ec6859b2STodor Tomov 
879ec6859b2STodor Tomov 	return 0;
880ec6859b2STodor Tomov 
881ec6859b2STodor Tomov media_cleanup:
882ec6859b2STodor Tomov 	media_entity_cleanup(&sd->entity);
883ec6859b2STodor Tomov free_ctrl:
884ec6859b2STodor Tomov 	v4l2_ctrl_handler_free(&csid->ctrls);
885ec6859b2STodor Tomov 
886ec6859b2STodor Tomov 	return ret;
887ec6859b2STodor Tomov }
888ec6859b2STodor Tomov 
889ec6859b2STodor Tomov /*
890ec6859b2STodor Tomov  * msm_csid_unregister_entity - Unregister CSID module subdev node
891ec6859b2STodor Tomov  * @csid: CSID device
892ec6859b2STodor Tomov  */
893ec6859b2STodor Tomov void msm_csid_unregister_entity(struct csid_device *csid)
894ec6859b2STodor Tomov {
895ec6859b2STodor Tomov 	v4l2_device_unregister_subdev(&csid->subdev);
896ec6859b2STodor Tomov 	media_entity_cleanup(&csid->subdev.entity);
897ec6859b2STodor Tomov 	v4l2_ctrl_handler_free(&csid->ctrls);
898ec6859b2STodor Tomov }
899*801ca0e7SMatti Lehtimäki 
900*801ca0e7SMatti Lehtimäki inline bool csid_is_lite(struct csid_device *csid)
901*801ca0e7SMatti Lehtimäki {
902*801ca0e7SMatti Lehtimäki 	return csid->camss->res->csid_res[csid->id].is_lite;
903*801ca0e7SMatti Lehtimäki }
904