1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * V4L2 Capture IC Preprocess Subdev for Freescale i.MX5/6 SOC
4  *
5  * This subdevice handles capture of video frames from the CSI or VDIC,
6  * which are routed directly to the Image Converter preprocess tasks,
7  * for resizing, colorspace conversion, and rotation.
8  *
9  * Copyright (c) 2012-2017 Mentor Graphics Inc.
10  */
11 #include <linux/delay.h>
12 #include <linux/interrupt.h>
13 #include <linux/module.h>
14 #include <linux/sched.h>
15 #include <linux/slab.h>
16 #include <linux/spinlock.h>
17 #include <linux/timer.h>
18 #include <media/v4l2-ctrls.h>
19 #include <media/v4l2-device.h>
20 #include <media/v4l2-ioctl.h>
21 #include <media/v4l2-subdev.h>
22 #include <media/imx.h>
23 #include "imx-media.h"
24 #include "imx-ic.h"
25 
26 /*
27  * Min/Max supported width and heights.
28  */
29 #define MIN_W        32
30 #define MIN_H        32
31 #define MAX_W      4096
32 #define MAX_H      4096
33 #define W_ALIGN    4 /* multiple of 16 pixels */
34 #define H_ALIGN    1 /* multiple of 2 lines */
35 #define S_ALIGN    1 /* multiple of 2 */
36 
37 struct prp_priv {
38 	struct imx_ic_priv *ic_priv;
39 	struct media_pad pad[PRP_NUM_PADS];
40 
41 	/* lock to protect all members below */
42 	struct mutex lock;
43 
44 	struct v4l2_subdev *src_sd;
45 	struct v4l2_subdev *sink_sd_prpenc;
46 	struct v4l2_subdev *sink_sd_prpvf;
47 
48 	/* the CSI id at link validate */
49 	int csi_id;
50 
51 	struct v4l2_mbus_framefmt format_mbus;
52 	struct v4l2_fract frame_interval;
53 
54 	int stream_count;
55 };
56 
sd_to_priv(struct v4l2_subdev * sd)57 static inline struct prp_priv *sd_to_priv(struct v4l2_subdev *sd)
58 {
59 	struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
60 
61 	return ic_priv->task_priv;
62 }
63 
prp_start(struct prp_priv * priv)64 static int prp_start(struct prp_priv *priv)
65 {
66 	struct imx_ic_priv *ic_priv = priv->ic_priv;
67 	bool src_is_vdic;
68 
69 	/* set IC to receive from CSI or VDI depending on source */
70 	src_is_vdic = !!(priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC);
71 
72 	ipu_set_ic_src_mux(ic_priv->ipu, priv->csi_id, src_is_vdic);
73 
74 	return 0;
75 }
76 
prp_stop(struct prp_priv * priv)77 static void prp_stop(struct prp_priv *priv)
78 {
79 }
80 
81 static struct v4l2_mbus_framefmt *
__prp_get_fmt(struct prp_priv * priv,struct v4l2_subdev_state * sd_state,unsigned int pad,enum v4l2_subdev_format_whence which)82 __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state,
83 	      unsigned int pad, enum v4l2_subdev_format_whence which)
84 {
85 	if (which == V4L2_SUBDEV_FORMAT_TRY)
86 		return v4l2_subdev_state_get_format(sd_state, pad);
87 	else
88 		return &priv->format_mbus;
89 }
90 
91 /*
92  * V4L2 subdev operations.
93  */
94 
prp_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)95 static int prp_enum_mbus_code(struct v4l2_subdev *sd,
96 			      struct v4l2_subdev_state *sd_state,
97 			      struct v4l2_subdev_mbus_code_enum *code)
98 {
99 	struct prp_priv *priv = sd_to_priv(sd);
100 	struct v4l2_mbus_framefmt *infmt;
101 	int ret = 0;
102 
103 	mutex_lock(&priv->lock);
104 
105 	switch (code->pad) {
106 	case PRP_SINK_PAD:
107 		ret = imx_media_enum_ipu_formats(&code->code, code->index,
108 						 PIXFMT_SEL_YUV_RGB);
109 		break;
110 	case PRP_SRC_PAD_PRPENC:
111 	case PRP_SRC_PAD_PRPVF:
112 		if (code->index != 0) {
113 			ret = -EINVAL;
114 			goto out;
115 		}
116 		infmt = __prp_get_fmt(priv, sd_state, PRP_SINK_PAD,
117 				      code->which);
118 		code->code = infmt->code;
119 		break;
120 	default:
121 		ret = -EINVAL;
122 	}
123 out:
124 	mutex_unlock(&priv->lock);
125 	return ret;
126 }
127 
prp_get_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * sdformat)128 static int prp_get_fmt(struct v4l2_subdev *sd,
129 		       struct v4l2_subdev_state *sd_state,
130 		       struct v4l2_subdev_format *sdformat)
131 {
132 	struct prp_priv *priv = sd_to_priv(sd);
133 	struct v4l2_mbus_framefmt *fmt;
134 	int ret = 0;
135 
136 	if (sdformat->pad >= PRP_NUM_PADS)
137 		return -EINVAL;
138 
139 	mutex_lock(&priv->lock);
140 
141 	fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which);
142 	if (!fmt) {
143 		ret = -EINVAL;
144 		goto out;
145 	}
146 
147 	sdformat->format = *fmt;
148 out:
149 	mutex_unlock(&priv->lock);
150 	return ret;
151 }
152 
prp_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * sdformat)153 static int prp_set_fmt(struct v4l2_subdev *sd,
154 		       struct v4l2_subdev_state *sd_state,
155 		       struct v4l2_subdev_format *sdformat)
156 {
157 	struct prp_priv *priv = sd_to_priv(sd);
158 	struct v4l2_mbus_framefmt *fmt, *infmt;
159 	const struct imx_media_pixfmt *cc;
160 	int ret = 0;
161 	u32 code;
162 
163 	if (sdformat->pad >= PRP_NUM_PADS)
164 		return -EINVAL;
165 
166 	mutex_lock(&priv->lock);
167 
168 	if (priv->stream_count > 0) {
169 		ret = -EBUSY;
170 		goto out;
171 	}
172 
173 	infmt = __prp_get_fmt(priv, sd_state, PRP_SINK_PAD, sdformat->which);
174 
175 	switch (sdformat->pad) {
176 	case PRP_SINK_PAD:
177 		v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W,
178 				      W_ALIGN, &sdformat->format.height,
179 				      MIN_H, MAX_H, H_ALIGN, S_ALIGN);
180 
181 		cc = imx_media_find_ipu_format(sdformat->format.code,
182 					       PIXFMT_SEL_YUV_RGB);
183 		if (!cc) {
184 			imx_media_enum_ipu_formats(&code, 0,
185 						   PIXFMT_SEL_YUV_RGB);
186 			cc = imx_media_find_ipu_format(code,
187 						       PIXFMT_SEL_YUV_RGB);
188 			sdformat->format.code = cc->codes[0];
189 		}
190 
191 		if (sdformat->format.field == V4L2_FIELD_ANY)
192 			sdformat->format.field = V4L2_FIELD_NONE;
193 		break;
194 	case PRP_SRC_PAD_PRPENC:
195 	case PRP_SRC_PAD_PRPVF:
196 		/* Output pads mirror input pad */
197 		sdformat->format = *infmt;
198 		break;
199 	}
200 
201 	imx_media_try_colorimetry(&sdformat->format, true);
202 
203 	fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which);
204 	*fmt = sdformat->format;
205 out:
206 	mutex_unlock(&priv->lock);
207 	return ret;
208 }
209 
prp_link_setup(struct media_entity * entity,const struct media_pad * local,const struct media_pad * remote,u32 flags)210 static int prp_link_setup(struct media_entity *entity,
211 			  const struct media_pad *local,
212 			  const struct media_pad *remote, u32 flags)
213 {
214 	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
215 	struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
216 	struct prp_priv *priv = ic_priv->task_priv;
217 	struct v4l2_subdev *remote_sd;
218 	int ret = 0;
219 
220 	dev_dbg(ic_priv->ipu_dev, "%s: link setup %s -> %s",
221 		ic_priv->sd.name, remote->entity->name, local->entity->name);
222 
223 	remote_sd = media_entity_to_v4l2_subdev(remote->entity);
224 
225 	mutex_lock(&priv->lock);
226 
227 	if (local->flags & MEDIA_PAD_FL_SINK) {
228 		if (flags & MEDIA_LNK_FL_ENABLED) {
229 			if (priv->src_sd) {
230 				ret = -EBUSY;
231 				goto out;
232 			}
233 			if (priv->sink_sd_prpenc &&
234 			    (remote_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC)) {
235 				ret = -EINVAL;
236 				goto out;
237 			}
238 			priv->src_sd = remote_sd;
239 		} else {
240 			priv->src_sd = NULL;
241 		}
242 
243 		goto out;
244 	}
245 
246 	/* this is a source pad */
247 	if (flags & MEDIA_LNK_FL_ENABLED) {
248 		switch (local->index) {
249 		case PRP_SRC_PAD_PRPENC:
250 			if (priv->sink_sd_prpenc) {
251 				ret = -EBUSY;
252 				goto out;
253 			}
254 			if (priv->src_sd && (priv->src_sd->grp_id &
255 					     IMX_MEDIA_GRP_ID_IPU_VDIC)) {
256 				ret = -EINVAL;
257 				goto out;
258 			}
259 			priv->sink_sd_prpenc = remote_sd;
260 			break;
261 		case PRP_SRC_PAD_PRPVF:
262 			if (priv->sink_sd_prpvf) {
263 				ret = -EBUSY;
264 				goto out;
265 			}
266 			priv->sink_sd_prpvf = remote_sd;
267 			break;
268 		default:
269 			ret = -EINVAL;
270 		}
271 	} else {
272 		switch (local->index) {
273 		case PRP_SRC_PAD_PRPENC:
274 			priv->sink_sd_prpenc = NULL;
275 			break;
276 		case PRP_SRC_PAD_PRPVF:
277 			priv->sink_sd_prpvf = NULL;
278 			break;
279 		default:
280 			ret = -EINVAL;
281 		}
282 	}
283 
284 out:
285 	mutex_unlock(&priv->lock);
286 	return ret;
287 }
288 
prp_link_validate(struct v4l2_subdev * sd,struct media_link * link,struct v4l2_subdev_format * source_fmt,struct v4l2_subdev_format * sink_fmt)289 static int prp_link_validate(struct v4l2_subdev *sd,
290 			     struct media_link *link,
291 			     struct v4l2_subdev_format *source_fmt,
292 			     struct v4l2_subdev_format *sink_fmt)
293 {
294 	struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
295 	struct prp_priv *priv = ic_priv->task_priv;
296 	struct v4l2_subdev *csi;
297 	int ret;
298 
299 	ret = v4l2_subdev_link_validate_default(sd, link,
300 						source_fmt, sink_fmt);
301 	if (ret)
302 		return ret;
303 
304 	csi = imx_media_pipeline_subdev(&ic_priv->sd.entity,
305 					IMX_MEDIA_GRP_ID_IPU_CSI, true);
306 	if (IS_ERR(csi))
307 		csi = NULL;
308 
309 	mutex_lock(&priv->lock);
310 
311 	if (priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC) {
312 		/*
313 		 * the ->PRPENC link cannot be enabled if the source
314 		 * is the VDIC
315 		 */
316 		if (priv->sink_sd_prpenc) {
317 			ret = -EINVAL;
318 			goto out;
319 		}
320 	} else {
321 		/* the source is a CSI */
322 		if (!csi) {
323 			ret = -EINVAL;
324 			goto out;
325 		}
326 	}
327 
328 	if (csi) {
329 		switch (csi->grp_id) {
330 		case IMX_MEDIA_GRP_ID_IPU_CSI0:
331 			priv->csi_id = 0;
332 			break;
333 		case IMX_MEDIA_GRP_ID_IPU_CSI1:
334 			priv->csi_id = 1;
335 			break;
336 		default:
337 			ret = -EINVAL;
338 		}
339 	} else {
340 		priv->csi_id = 0;
341 	}
342 
343 out:
344 	mutex_unlock(&priv->lock);
345 	return ret;
346 }
347 
prp_s_stream(struct v4l2_subdev * sd,int enable)348 static int prp_s_stream(struct v4l2_subdev *sd, int enable)
349 {
350 	struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
351 	struct prp_priv *priv = ic_priv->task_priv;
352 	int ret = 0;
353 
354 	mutex_lock(&priv->lock);
355 
356 	if (!priv->src_sd || (!priv->sink_sd_prpenc && !priv->sink_sd_prpvf)) {
357 		ret = -EPIPE;
358 		goto out;
359 	}
360 
361 	/*
362 	 * enable/disable streaming only if stream_count is
363 	 * going from 0 to 1 / 1 to 0.
364 	 */
365 	if (priv->stream_count != !enable)
366 		goto update_count;
367 
368 	dev_dbg(ic_priv->ipu_dev, "%s: stream %s\n", sd->name,
369 		enable ? "ON" : "OFF");
370 
371 	if (enable)
372 		ret = prp_start(priv);
373 	else
374 		prp_stop(priv);
375 	if (ret)
376 		goto out;
377 
378 	/* start/stop upstream */
379 	ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable);
380 	ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
381 	if (ret) {
382 		if (enable)
383 			prp_stop(priv);
384 		goto out;
385 	}
386 
387 update_count:
388 	priv->stream_count += enable ? 1 : -1;
389 	if (priv->stream_count < 0)
390 		priv->stream_count = 0;
391 out:
392 	mutex_unlock(&priv->lock);
393 	return ret;
394 }
395 
prp_get_frame_interval(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_interval * fi)396 static int prp_get_frame_interval(struct v4l2_subdev *sd,
397 				  struct v4l2_subdev_state *sd_state,
398 				  struct v4l2_subdev_frame_interval *fi)
399 {
400 	struct prp_priv *priv = sd_to_priv(sd);
401 
402 	/*
403 	 * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
404 	 * subdev active state API.
405 	 */
406 	if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE)
407 		return -EINVAL;
408 
409 	if (fi->pad >= PRP_NUM_PADS)
410 		return -EINVAL;
411 
412 	mutex_lock(&priv->lock);
413 	fi->interval = priv->frame_interval;
414 	mutex_unlock(&priv->lock);
415 
416 	return 0;
417 }
418 
prp_set_frame_interval(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_interval * fi)419 static int prp_set_frame_interval(struct v4l2_subdev *sd,
420 				  struct v4l2_subdev_state *sd_state,
421 				  struct v4l2_subdev_frame_interval *fi)
422 {
423 	struct prp_priv *priv = sd_to_priv(sd);
424 
425 	/*
426 	 * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
427 	 * subdev active state API.
428 	 */
429 	if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE)
430 		return -EINVAL;
431 
432 	if (fi->pad >= PRP_NUM_PADS)
433 		return -EINVAL;
434 
435 	mutex_lock(&priv->lock);
436 
437 	/* No limits on valid frame intervals */
438 	if (fi->interval.numerator == 0 || fi->interval.denominator == 0)
439 		fi->interval = priv->frame_interval;
440 	else
441 		priv->frame_interval = fi->interval;
442 
443 	mutex_unlock(&priv->lock);
444 
445 	return 0;
446 }
447 
prp_registered(struct v4l2_subdev * sd)448 static int prp_registered(struct v4l2_subdev *sd)
449 {
450 	struct prp_priv *priv = sd_to_priv(sd);
451 	u32 code;
452 
453 	/* init default frame interval */
454 	priv->frame_interval.numerator = 1;
455 	priv->frame_interval.denominator = 30;
456 
457 	/* set a default mbus format  */
458 	imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV);
459 
460 	return imx_media_init_mbus_fmt(&priv->format_mbus,
461 				       IMX_MEDIA_DEF_PIX_WIDTH,
462 				       IMX_MEDIA_DEF_PIX_HEIGHT, code,
463 				       V4L2_FIELD_NONE, NULL);
464 }
465 
466 static const struct v4l2_subdev_pad_ops prp_pad_ops = {
467 	.enum_mbus_code = prp_enum_mbus_code,
468 	.get_fmt = prp_get_fmt,
469 	.set_fmt = prp_set_fmt,
470 	.get_frame_interval = prp_get_frame_interval,
471 	.set_frame_interval = prp_set_frame_interval,
472 	.link_validate = prp_link_validate,
473 };
474 
475 static const struct v4l2_subdev_video_ops prp_video_ops = {
476 	.s_stream = prp_s_stream,
477 };
478 
479 static const struct media_entity_operations prp_entity_ops = {
480 	.link_setup = prp_link_setup,
481 	.link_validate = v4l2_subdev_link_validate,
482 };
483 
484 static const struct v4l2_subdev_ops prp_subdev_ops = {
485 	.video = &prp_video_ops,
486 	.pad = &prp_pad_ops,
487 };
488 
489 static const struct v4l2_subdev_internal_ops prp_internal_ops = {
490 	.init_state = imx_media_init_state,
491 	.registered = prp_registered,
492 };
493 
prp_init(struct imx_ic_priv * ic_priv)494 static int prp_init(struct imx_ic_priv *ic_priv)
495 {
496 	struct prp_priv *priv;
497 	int i;
498 
499 	priv = devm_kzalloc(ic_priv->ipu_dev, sizeof(*priv), GFP_KERNEL);
500 	if (!priv)
501 		return -ENOMEM;
502 
503 	mutex_init(&priv->lock);
504 	ic_priv->task_priv = priv;
505 	priv->ic_priv = ic_priv;
506 
507 	for (i = 0; i < PRP_NUM_PADS; i++)
508 		priv->pad[i].flags = (i == PRP_SINK_PAD) ?
509 			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
510 
511 	return media_entity_pads_init(&ic_priv->sd.entity, PRP_NUM_PADS,
512 				      priv->pad);
513 }
514 
prp_remove(struct imx_ic_priv * ic_priv)515 static void prp_remove(struct imx_ic_priv *ic_priv)
516 {
517 	struct prp_priv *priv = ic_priv->task_priv;
518 
519 	mutex_destroy(&priv->lock);
520 }
521 
522 struct imx_ic_ops imx_ic_prp_ops = {
523 	.subdev_ops = &prp_subdev_ops,
524 	.internal_ops = &prp_internal_ops,
525 	.entity_ops = &prp_entity_ops,
526 	.init = prp_init,
527 	.remove = prp_remove,
528 };
529