1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * camss-csiphy.c
4  *
5  * Qualcomm MSM Camera Subsystem - CSIPHY Module
6  *
7  * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
8  * Copyright (C) 2016-2018 Linaro Ltd.
9  */
10 #include <linux/clk.h>
11 #include <linux/delay.h>
12 #include <linux/interrupt.h>
13 #include <linux/kernel.h>
14 #include <linux/of.h>
15 #include <linux/platform_device.h>
16 #include <linux/pm_runtime.h>
17 #include <media/media-entity.h>
18 #include <media/v4l2-device.h>
19 #include <media/v4l2-subdev.h>
20 
21 #include "camss-csiphy.h"
22 #include "camss.h"
23 
24 #define MSM_CSIPHY_NAME "msm_csiphy"
25 
26 struct csiphy_format {
27 	u32 code;
28 	u8 bpp;
29 };
30 
31 static const struct csiphy_format csiphy_formats_8x16[] = {
32 	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
33 	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
34 	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
35 	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
36 	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
37 	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
38 	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
39 	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
40 	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
41 	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
42 	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
43 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
44 	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
45 	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
46 	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
47 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
48 	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
49 };
50 
51 static const struct csiphy_format csiphy_formats_8x96[] = {
52 	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
53 	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
54 	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
55 	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
56 	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
57 	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
58 	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
59 	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
60 	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
61 	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
62 	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
63 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
64 	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
65 	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
66 	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
67 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
68 	{ MEDIA_BUS_FMT_SBGGR14_1X14, 14 },
69 	{ MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
70 	{ MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
71 	{ MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
72 	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
73 };
74 
75 /*
76  * csiphy_get_bpp - map media bus format to bits per pixel
77  * @formats: supported media bus formats array
78  * @nformats: size of @formats array
79  * @code: media bus format code
80  *
81  * Return number of bits per pixel
82  */
83 static u8 csiphy_get_bpp(const struct csiphy_format *formats,
84 			 unsigned int nformats, u32 code)
85 {
86 	unsigned int i;
87 
88 	for (i = 0; i < nformats; i++)
89 		if (code == formats[i].code)
90 			return formats[i].bpp;
91 
92 	WARN(1, "Unknown format\n");
93 
94 	return formats[0].bpp;
95 }
96 
97 /*
98  * csiphy_set_clock_rates - Calculate and set clock rates on CSIPHY module
99  * @csiphy: CSIPHY device
100  */
101 static int csiphy_set_clock_rates(struct csiphy_device *csiphy)
102 {
103 	struct device *dev = csiphy->camss->dev;
104 	u32 pixel_clock;
105 	int i, j;
106 	int ret;
107 
108 	ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock);
109 	if (ret)
110 		pixel_clock = 0;
111 
112 	for (i = 0; i < csiphy->nclocks; i++) {
113 		struct camss_clock *clock = &csiphy->clock[i];
114 
115 		if (!strcmp(clock->name, "csiphy0_timer") ||
116 		    !strcmp(clock->name, "csiphy1_timer") ||
117 		    !strcmp(clock->name, "csiphy2_timer")) {
118 			u8 bpp = csiphy_get_bpp(csiphy->formats,
119 					csiphy->nformats,
120 					csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
121 			u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
122 			u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4);
123 			long round_rate;
124 
125 			camss_add_clock_margin(&min_rate);
126 
127 			for (j = 0; j < clock->nfreqs; j++)
128 				if (min_rate < clock->freq[j])
129 					break;
130 
131 			if (j == clock->nfreqs) {
132 				dev_err(dev,
133 					"Pixel clock is too high for CSIPHY\n");
134 				return -EINVAL;
135 			}
136 
137 			/* if sensor pixel clock is not available */
138 			/* set highest possible CSIPHY clock rate */
139 			if (min_rate == 0)
140 				j = clock->nfreqs - 1;
141 
142 			round_rate = clk_round_rate(clock->clk, clock->freq[j]);
143 			if (round_rate < 0) {
144 				dev_err(dev, "clk round rate failed: %ld\n",
145 					round_rate);
146 				return -EINVAL;
147 			}
148 
149 			csiphy->timer_clk_rate = round_rate;
150 
151 			ret = clk_set_rate(clock->clk, csiphy->timer_clk_rate);
152 			if (ret < 0) {
153 				dev_err(dev, "clk set rate failed: %d\n", ret);
154 				return ret;
155 			}
156 		}
157 	}
158 
159 	return 0;
160 }
161 
162 /*
163  * csiphy_set_power - Power on/off CSIPHY module
164  * @sd: CSIPHY V4L2 subdevice
165  * @on: Requested power state
166  *
167  * Return 0 on success or a negative error code otherwise
168  */
169 static int csiphy_set_power(struct v4l2_subdev *sd, int on)
170 {
171 	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
172 	struct device *dev = csiphy->camss->dev;
173 
174 	if (on) {
175 		int ret;
176 
177 		ret = pm_runtime_get_sync(dev);
178 		if (ret < 0)
179 			return ret;
180 
181 		ret = csiphy_set_clock_rates(csiphy);
182 		if (ret < 0) {
183 			pm_runtime_put_sync(dev);
184 			return ret;
185 		}
186 
187 		ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev);
188 		if (ret < 0) {
189 			pm_runtime_put_sync(dev);
190 			return ret;
191 		}
192 
193 		enable_irq(csiphy->irq);
194 
195 		csiphy->ops->reset(csiphy);
196 
197 		csiphy->ops->hw_version_read(csiphy, dev);
198 	} else {
199 		disable_irq(csiphy->irq);
200 
201 		camss_disable_clocks(csiphy->nclocks, csiphy->clock);
202 
203 		pm_runtime_put_sync(dev);
204 	}
205 
206 	return 0;
207 }
208 
209 /*
210  * csiphy_get_lane_mask - Calculate CSI2 lane mask configuration parameter
211  * @lane_cfg - CSI2 lane configuration
212  *
213  * Return lane mask
214  */
215 static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg)
216 {
217 	u8 lane_mask;
218 	int i;
219 
220 	lane_mask = 1 << lane_cfg->clk.pos;
221 
222 	for (i = 0; i < lane_cfg->num_data; i++)
223 		lane_mask |= 1 << lane_cfg->data[i].pos;
224 
225 	return lane_mask;
226 }
227 
228 /*
229  * csiphy_stream_on - Enable streaming on CSIPHY module
230  * @csiphy: CSIPHY device
231  *
232  * Helper function to enable streaming on CSIPHY module.
233  * Main configuration of CSIPHY module is also done here.
234  *
235  * Return 0 on success or a negative error code otherwise
236  */
237 static int csiphy_stream_on(struct csiphy_device *csiphy)
238 {
239 	struct csiphy_config *cfg = &csiphy->cfg;
240 	u32 pixel_clock;
241 	u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg);
242 	u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats,
243 				csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
244 	u8 val;
245 	int ret;
246 
247 	ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock);
248 	if (ret) {
249 		dev_err(csiphy->camss->dev,
250 			"Cannot get CSI2 transmitter's pixel clock\n");
251 		return -EINVAL;
252 	}
253 	if (!pixel_clock) {
254 		dev_err(csiphy->camss->dev,
255 			"Got pixel clock == 0, cannot continue\n");
256 		return -EINVAL;
257 	}
258 
259 	val = readl_relaxed(csiphy->base_clk_mux);
260 	if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) {
261 		val &= ~0xf0;
262 		val |= cfg->csid_id << 4;
263 	} else {
264 		val &= ~0xf;
265 		val |= cfg->csid_id;
266 	}
267 	writel_relaxed(val, csiphy->base_clk_mux);
268 	wmb();
269 
270 	csiphy->ops->lanes_enable(csiphy, cfg, pixel_clock, bpp, lane_mask);
271 
272 	return 0;
273 }
274 
275 /*
276  * csiphy_stream_off - Disable streaming on CSIPHY module
277  * @csiphy: CSIPHY device
278  *
279  * Helper function to disable streaming on CSIPHY module
280  */
281 static void csiphy_stream_off(struct csiphy_device *csiphy)
282 {
283 	csiphy->ops->lanes_disable(csiphy, &csiphy->cfg);
284 }
285 
286 
287 /*
288  * csiphy_set_stream - Enable/disable streaming on CSIPHY module
289  * @sd: CSIPHY V4L2 subdevice
290  * @enable: Requested streaming state
291  *
292  * Return 0 on success or a negative error code otherwise
293  */
294 static int csiphy_set_stream(struct v4l2_subdev *sd, int enable)
295 {
296 	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
297 	int ret = 0;
298 
299 	if (enable)
300 		ret = csiphy_stream_on(csiphy);
301 	else
302 		csiphy_stream_off(csiphy);
303 
304 	return ret;
305 }
306 
307 /*
308  * __csiphy_get_format - Get pointer to format structure
309  * @csiphy: CSIPHY device
310  * @cfg: V4L2 subdev pad configuration
311  * @pad: pad from which format is requested
312  * @which: TRY or ACTIVE format
313  *
314  * Return pointer to TRY or ACTIVE format structure
315  */
316 static struct v4l2_mbus_framefmt *
317 __csiphy_get_format(struct csiphy_device *csiphy,
318 		    struct v4l2_subdev_pad_config *cfg,
319 		    unsigned int pad,
320 		    enum v4l2_subdev_format_whence which)
321 {
322 	if (which == V4L2_SUBDEV_FORMAT_TRY)
323 		return v4l2_subdev_get_try_format(&csiphy->subdev, cfg, pad);
324 
325 	return &csiphy->fmt[pad];
326 }
327 
328 /*
329  * csiphy_try_format - Handle try format by pad subdev method
330  * @csiphy: CSIPHY device
331  * @cfg: V4L2 subdev pad configuration
332  * @pad: pad on which format is requested
333  * @fmt: pointer to v4l2 format structure
334  * @which: wanted subdev format
335  */
336 static void csiphy_try_format(struct csiphy_device *csiphy,
337 			      struct v4l2_subdev_pad_config *cfg,
338 			      unsigned int pad,
339 			      struct v4l2_mbus_framefmt *fmt,
340 			      enum v4l2_subdev_format_whence which)
341 {
342 	unsigned int i;
343 
344 	switch (pad) {
345 	case MSM_CSIPHY_PAD_SINK:
346 		/* Set format on sink pad */
347 
348 		for (i = 0; i < csiphy->nformats; i++)
349 			if (fmt->code == csiphy->formats[i].code)
350 				break;
351 
352 		/* If not found, use UYVY as default */
353 		if (i >= csiphy->nformats)
354 			fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
355 
356 		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
357 		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
358 
359 		fmt->field = V4L2_FIELD_NONE;
360 		fmt->colorspace = V4L2_COLORSPACE_SRGB;
361 
362 		break;
363 
364 	case MSM_CSIPHY_PAD_SRC:
365 		/* Set and return a format same as sink pad */
366 
367 		*fmt = *__csiphy_get_format(csiphy, cfg, MSM_CSID_PAD_SINK,
368 					    which);
369 
370 		break;
371 	}
372 }
373 
374 /*
375  * csiphy_enum_mbus_code - Handle pixel format enumeration
376  * @sd: CSIPHY V4L2 subdevice
377  * @cfg: V4L2 subdev pad configuration
378  * @code: pointer to v4l2_subdev_mbus_code_enum structure
379  * return -EINVAL or zero on success
380  */
381 static int csiphy_enum_mbus_code(struct v4l2_subdev *sd,
382 				 struct v4l2_subdev_pad_config *cfg,
383 				 struct v4l2_subdev_mbus_code_enum *code)
384 {
385 	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
386 	struct v4l2_mbus_framefmt *format;
387 
388 	if (code->pad == MSM_CSIPHY_PAD_SINK) {
389 		if (code->index >= csiphy->nformats)
390 			return -EINVAL;
391 
392 		code->code = csiphy->formats[code->index].code;
393 	} else {
394 		if (code->index > 0)
395 			return -EINVAL;
396 
397 		format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SINK,
398 					     code->which);
399 
400 		code->code = format->code;
401 	}
402 
403 	return 0;
404 }
405 
406 /*
407  * csiphy_enum_frame_size - Handle frame size enumeration
408  * @sd: CSIPHY V4L2 subdevice
409  * @cfg: V4L2 subdev pad configuration
410  * @fse: pointer to v4l2_subdev_frame_size_enum structure
411  * return -EINVAL or zero on success
412  */
413 static int csiphy_enum_frame_size(struct v4l2_subdev *sd,
414 				  struct v4l2_subdev_pad_config *cfg,
415 				  struct v4l2_subdev_frame_size_enum *fse)
416 {
417 	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
418 	struct v4l2_mbus_framefmt format;
419 
420 	if (fse->index != 0)
421 		return -EINVAL;
422 
423 	format.code = fse->code;
424 	format.width = 1;
425 	format.height = 1;
426 	csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which);
427 	fse->min_width = format.width;
428 	fse->min_height = format.height;
429 
430 	if (format.code != fse->code)
431 		return -EINVAL;
432 
433 	format.code = fse->code;
434 	format.width = -1;
435 	format.height = -1;
436 	csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which);
437 	fse->max_width = format.width;
438 	fse->max_height = format.height;
439 
440 	return 0;
441 }
442 
443 /*
444  * csiphy_get_format - Handle get format by pads subdev method
445  * @sd: CSIPHY V4L2 subdevice
446  * @cfg: V4L2 subdev pad configuration
447  * @fmt: pointer to v4l2 subdev format structure
448  *
449  * Return -EINVAL or zero on success
450  */
451 static int csiphy_get_format(struct v4l2_subdev *sd,
452 			     struct v4l2_subdev_pad_config *cfg,
453 			     struct v4l2_subdev_format *fmt)
454 {
455 	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
456 	struct v4l2_mbus_framefmt *format;
457 
458 	format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which);
459 	if (format == NULL)
460 		return -EINVAL;
461 
462 	fmt->format = *format;
463 
464 	return 0;
465 }
466 
467 /*
468  * csiphy_set_format - Handle set format by pads subdev method
469  * @sd: CSIPHY V4L2 subdevice
470  * @cfg: V4L2 subdev pad configuration
471  * @fmt: pointer to v4l2 subdev format structure
472  *
473  * Return -EINVAL or zero on success
474  */
475 static int csiphy_set_format(struct v4l2_subdev *sd,
476 			     struct v4l2_subdev_pad_config *cfg,
477 			     struct v4l2_subdev_format *fmt)
478 {
479 	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
480 	struct v4l2_mbus_framefmt *format;
481 
482 	format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which);
483 	if (format == NULL)
484 		return -EINVAL;
485 
486 	csiphy_try_format(csiphy, cfg, fmt->pad, &fmt->format, fmt->which);
487 	*format = fmt->format;
488 
489 	/* Propagate the format from sink to source */
490 	if (fmt->pad == MSM_CSIPHY_PAD_SINK) {
491 		format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC,
492 					     fmt->which);
493 
494 		*format = fmt->format;
495 		csiphy_try_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC, format,
496 				  fmt->which);
497 	}
498 
499 	return 0;
500 }
501 
502 /*
503  * csiphy_init_formats - Initialize formats on all pads
504  * @sd: CSIPHY V4L2 subdevice
505  * @fh: V4L2 subdev file handle
506  *
507  * Initialize all pad formats with default values.
508  *
509  * Return 0 on success or a negative error code otherwise
510  */
511 static int csiphy_init_formats(struct v4l2_subdev *sd,
512 			       struct v4l2_subdev_fh *fh)
513 {
514 	struct v4l2_subdev_format format = {
515 		.pad = MSM_CSIPHY_PAD_SINK,
516 		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
517 			      V4L2_SUBDEV_FORMAT_ACTIVE,
518 		.format = {
519 			.code = MEDIA_BUS_FMT_UYVY8_2X8,
520 			.width = 1920,
521 			.height = 1080
522 		}
523 	};
524 
525 	return csiphy_set_format(sd, fh ? fh->pad : NULL, &format);
526 }
527 
528 /*
529  * msm_csiphy_subdev_init - Initialize CSIPHY device structure and resources
530  * @csiphy: CSIPHY device
531  * @res: CSIPHY module resources table
532  * @id: CSIPHY module id
533  *
534  * Return 0 on success or a negative error code otherwise
535  */
536 int msm_csiphy_subdev_init(struct camss *camss,
537 			   struct csiphy_device *csiphy,
538 			   const struct resources *res, u8 id)
539 {
540 	struct device *dev = camss->dev;
541 	struct platform_device *pdev = to_platform_device(dev);
542 	struct resource *r;
543 	int i, j;
544 	int ret;
545 
546 	csiphy->camss = camss;
547 	csiphy->id = id;
548 	csiphy->cfg.combo_mode = 0;
549 
550 	if (camss->version == CAMSS_8x16) {
551 		csiphy->ops = &csiphy_ops_2ph_1_0;
552 		csiphy->formats = csiphy_formats_8x16;
553 		csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x16);
554 	} else if (camss->version == CAMSS_8x96) {
555 		csiphy->ops = &csiphy_ops_3ph_1_0;
556 		csiphy->formats = csiphy_formats_8x96;
557 		csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96);
558 	} else {
559 		return -EINVAL;
560 	}
561 
562 	/* Memory */
563 
564 	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
565 	csiphy->base = devm_ioremap_resource(dev, r);
566 	if (IS_ERR(csiphy->base)) {
567 		dev_err(dev, "could not map memory\n");
568 		return PTR_ERR(csiphy->base);
569 	}
570 
571 	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]);
572 	csiphy->base_clk_mux = devm_ioremap_resource(dev, r);
573 	if (IS_ERR(csiphy->base_clk_mux)) {
574 		dev_err(dev, "could not map memory\n");
575 		return PTR_ERR(csiphy->base_clk_mux);
576 	}
577 
578 	/* Interrupt */
579 
580 	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
581 					 res->interrupt[0]);
582 	if (!r) {
583 		dev_err(dev, "missing IRQ\n");
584 		return -EINVAL;
585 	}
586 
587 	csiphy->irq = r->start;
588 	snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d",
589 		 dev_name(dev), MSM_CSIPHY_NAME, csiphy->id);
590 
591 	ret = devm_request_irq(dev, csiphy->irq, csiphy->ops->isr,
592 			       IRQF_TRIGGER_RISING, csiphy->irq_name, csiphy);
593 	if (ret < 0) {
594 		dev_err(dev, "request_irq failed: %d\n", ret);
595 		return ret;
596 	}
597 
598 	disable_irq(csiphy->irq);
599 
600 	/* Clocks */
601 
602 	csiphy->nclocks = 0;
603 	while (res->clock[csiphy->nclocks])
604 		csiphy->nclocks++;
605 
606 	csiphy->clock = devm_kcalloc(dev,
607 				     csiphy->nclocks, sizeof(*csiphy->clock),
608 				     GFP_KERNEL);
609 	if (!csiphy->clock)
610 		return -ENOMEM;
611 
612 	for (i = 0; i < csiphy->nclocks; i++) {
613 		struct camss_clock *clock = &csiphy->clock[i];
614 
615 		clock->clk = devm_clk_get(dev, res->clock[i]);
616 		if (IS_ERR(clock->clk))
617 			return PTR_ERR(clock->clk);
618 
619 		clock->name = res->clock[i];
620 
621 		clock->nfreqs = 0;
622 		while (res->clock_rate[i][clock->nfreqs])
623 			clock->nfreqs++;
624 
625 		if (!clock->nfreqs) {
626 			clock->freq = NULL;
627 			continue;
628 		}
629 
630 		clock->freq = devm_kcalloc(dev,
631 					   clock->nfreqs,
632 					   sizeof(*clock->freq),
633 					   GFP_KERNEL);
634 		if (!clock->freq)
635 			return -ENOMEM;
636 
637 		for (j = 0; j < clock->nfreqs; j++)
638 			clock->freq[j] = res->clock_rate[i][j];
639 	}
640 
641 	return 0;
642 }
643 
644 /*
645  * csiphy_link_setup - Setup CSIPHY connections
646  * @entity: Pointer to media entity structure
647  * @local: Pointer to local pad
648  * @remote: Pointer to remote pad
649  * @flags: Link flags
650  *
651  * Rreturn 0 on success
652  */
653 static int csiphy_link_setup(struct media_entity *entity,
654 			     const struct media_pad *local,
655 			     const struct media_pad *remote, u32 flags)
656 {
657 	if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
658 	    (flags & MEDIA_LNK_FL_ENABLED)) {
659 		struct v4l2_subdev *sd;
660 		struct csiphy_device *csiphy;
661 		struct csid_device *csid;
662 
663 		if (media_entity_remote_pad(local))
664 			return -EBUSY;
665 
666 		sd = media_entity_to_v4l2_subdev(entity);
667 		csiphy = v4l2_get_subdevdata(sd);
668 
669 		sd = media_entity_to_v4l2_subdev(remote->entity);
670 		csid = v4l2_get_subdevdata(sd);
671 
672 		csiphy->cfg.csid_id = csid->id;
673 	}
674 
675 	return 0;
676 }
677 
678 static const struct v4l2_subdev_core_ops csiphy_core_ops = {
679 	.s_power = csiphy_set_power,
680 };
681 
682 static const struct v4l2_subdev_video_ops csiphy_video_ops = {
683 	.s_stream = csiphy_set_stream,
684 };
685 
686 static const struct v4l2_subdev_pad_ops csiphy_pad_ops = {
687 	.enum_mbus_code = csiphy_enum_mbus_code,
688 	.enum_frame_size = csiphy_enum_frame_size,
689 	.get_fmt = csiphy_get_format,
690 	.set_fmt = csiphy_set_format,
691 };
692 
693 static const struct v4l2_subdev_ops csiphy_v4l2_ops = {
694 	.core = &csiphy_core_ops,
695 	.video = &csiphy_video_ops,
696 	.pad = &csiphy_pad_ops,
697 };
698 
699 static const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops = {
700 	.open = csiphy_init_formats,
701 };
702 
703 static const struct media_entity_operations csiphy_media_ops = {
704 	.link_setup = csiphy_link_setup,
705 	.link_validate = v4l2_subdev_link_validate,
706 };
707 
708 /*
709  * msm_csiphy_register_entity - Register subdev node for CSIPHY module
710  * @csiphy: CSIPHY device
711  * @v4l2_dev: V4L2 device
712  *
713  * Return 0 on success or a negative error code otherwise
714  */
715 int msm_csiphy_register_entity(struct csiphy_device *csiphy,
716 			       struct v4l2_device *v4l2_dev)
717 {
718 	struct v4l2_subdev *sd = &csiphy->subdev;
719 	struct media_pad *pads = csiphy->pads;
720 	struct device *dev = csiphy->camss->dev;
721 	int ret;
722 
723 	v4l2_subdev_init(sd, &csiphy_v4l2_ops);
724 	sd->internal_ops = &csiphy_v4l2_internal_ops;
725 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
726 	snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
727 		 MSM_CSIPHY_NAME, csiphy->id);
728 	v4l2_set_subdevdata(sd, csiphy);
729 
730 	ret = csiphy_init_formats(sd, NULL);
731 	if (ret < 0) {
732 		dev_err(dev, "Failed to init format: %d\n", ret);
733 		return ret;
734 	}
735 
736 	pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
737 	pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
738 
739 	sd->entity.function = MEDIA_ENT_F_IO_V4L;
740 	sd->entity.ops = &csiphy_media_ops;
741 	ret = media_entity_pads_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads);
742 	if (ret < 0) {
743 		dev_err(dev, "Failed to init media entity: %d\n", ret);
744 		return ret;
745 	}
746 
747 	ret = v4l2_device_register_subdev(v4l2_dev, sd);
748 	if (ret < 0) {
749 		dev_err(dev, "Failed to register subdev: %d\n", ret);
750 		media_entity_cleanup(&sd->entity);
751 	}
752 
753 	return ret;
754 }
755 
756 /*
757  * msm_csiphy_unregister_entity - Unregister CSIPHY module subdev node
758  * @csiphy: CSIPHY device
759  */
760 void msm_csiphy_unregister_entity(struct csiphy_device *csiphy)
761 {
762 	v4l2_device_unregister_subdev(&csiphy->subdev);
763 	media_entity_cleanup(&csiphy->subdev.entity);
764 }
765