xref: /linux/drivers/staging/media/imx/imx-ic-prp.c (revision 52338415)
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       176
30 #define MIN_H       144
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 
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 
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 
77 static void prp_stop(struct prp_priv *priv)
78 {
79 }
80 
81 static struct v4l2_mbus_framefmt *
82 __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_pad_config *cfg,
83 	      unsigned int pad, enum v4l2_subdev_format_whence which)
84 {
85 	struct imx_ic_priv *ic_priv = priv->ic_priv;
86 
87 	if (which == V4L2_SUBDEV_FORMAT_TRY)
88 		return v4l2_subdev_get_try_format(&ic_priv->sd, cfg, pad);
89 	else
90 		return &priv->format_mbus;
91 }
92 
93 /*
94  * V4L2 subdev operations.
95  */
96 
97 static int prp_enum_mbus_code(struct v4l2_subdev *sd,
98 			      struct v4l2_subdev_pad_config *cfg,
99 			      struct v4l2_subdev_mbus_code_enum *code)
100 {
101 	struct prp_priv *priv = sd_to_priv(sd);
102 	struct v4l2_mbus_framefmt *infmt;
103 	int ret = 0;
104 
105 	mutex_lock(&priv->lock);
106 
107 	switch (code->pad) {
108 	case PRP_SINK_PAD:
109 		ret = imx_media_enum_ipu_format(&code->code, code->index,
110 						CS_SEL_ANY);
111 		break;
112 	case PRP_SRC_PAD_PRPENC:
113 	case PRP_SRC_PAD_PRPVF:
114 		if (code->index != 0) {
115 			ret = -EINVAL;
116 			goto out;
117 		}
118 		infmt = __prp_get_fmt(priv, cfg, PRP_SINK_PAD, code->which);
119 		code->code = infmt->code;
120 		break;
121 	default:
122 		ret = -EINVAL;
123 	}
124 out:
125 	mutex_unlock(&priv->lock);
126 	return ret;
127 }
128 
129 static int prp_get_fmt(struct v4l2_subdev *sd,
130 		       struct v4l2_subdev_pad_config *cfg,
131 		       struct v4l2_subdev_format *sdformat)
132 {
133 	struct prp_priv *priv = sd_to_priv(sd);
134 	struct v4l2_mbus_framefmt *fmt;
135 	int ret = 0;
136 
137 	if (sdformat->pad >= PRP_NUM_PADS)
138 		return -EINVAL;
139 
140 	mutex_lock(&priv->lock);
141 
142 	fmt = __prp_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
143 	if (!fmt) {
144 		ret = -EINVAL;
145 		goto out;
146 	}
147 
148 	sdformat->format = *fmt;
149 out:
150 	mutex_unlock(&priv->lock);
151 	return ret;
152 }
153 
154 static int prp_set_fmt(struct v4l2_subdev *sd,
155 		       struct v4l2_subdev_pad_config *cfg,
156 		       struct v4l2_subdev_format *sdformat)
157 {
158 	struct prp_priv *priv = sd_to_priv(sd);
159 	struct v4l2_mbus_framefmt *fmt, *infmt;
160 	const struct imx_media_pixfmt *cc;
161 	int ret = 0;
162 	u32 code;
163 
164 	if (sdformat->pad >= PRP_NUM_PADS)
165 		return -EINVAL;
166 
167 	mutex_lock(&priv->lock);
168 
169 	if (priv->stream_count > 0) {
170 		ret = -EBUSY;
171 		goto out;
172 	}
173 
174 	infmt = __prp_get_fmt(priv, cfg, PRP_SINK_PAD, sdformat->which);
175 
176 	switch (sdformat->pad) {
177 	case PRP_SINK_PAD:
178 		v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W,
179 				      W_ALIGN, &sdformat->format.height,
180 				      MIN_H, MAX_H, H_ALIGN, S_ALIGN);
181 
182 		cc = imx_media_find_ipu_format(sdformat->format.code,
183 					       CS_SEL_ANY);
184 		if (!cc) {
185 			imx_media_enum_ipu_format(&code, 0, CS_SEL_ANY);
186 			cc = imx_media_find_ipu_format(code, CS_SEL_ANY);
187 			sdformat->format.code = cc->codes[0];
188 		}
189 
190 		if (sdformat->format.field == V4L2_FIELD_ANY)
191 			sdformat->format.field = V4L2_FIELD_NONE;
192 		break;
193 	case PRP_SRC_PAD_PRPENC:
194 	case PRP_SRC_PAD_PRPVF:
195 		/* Output pads mirror input pad */
196 		sdformat->format = *infmt;
197 		break;
198 	}
199 
200 	imx_media_try_colorimetry(&sdformat->format, true);
201 
202 	fmt = __prp_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
203 	*fmt = sdformat->format;
204 out:
205 	mutex_unlock(&priv->lock);
206 	return ret;
207 }
208 
209 static int prp_link_setup(struct media_entity *entity,
210 			  const struct media_pad *local,
211 			  const struct media_pad *remote, u32 flags)
212 {
213 	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
214 	struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
215 	struct prp_priv *priv = ic_priv->task_priv;
216 	struct v4l2_subdev *remote_sd;
217 	int ret = 0;
218 
219 	dev_dbg(ic_priv->ipu_dev, "%s: link setup %s -> %s",
220 		ic_priv->sd.name, remote->entity->name, local->entity->name);
221 
222 	remote_sd = media_entity_to_v4l2_subdev(remote->entity);
223 
224 	mutex_lock(&priv->lock);
225 
226 	if (local->flags & MEDIA_PAD_FL_SINK) {
227 		if (flags & MEDIA_LNK_FL_ENABLED) {
228 			if (priv->src_sd) {
229 				ret = -EBUSY;
230 				goto out;
231 			}
232 			if (priv->sink_sd_prpenc &&
233 			    (remote_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC)) {
234 				ret = -EINVAL;
235 				goto out;
236 			}
237 			priv->src_sd = remote_sd;
238 		} else {
239 			priv->src_sd = NULL;
240 		}
241 
242 		goto out;
243 	}
244 
245 	/* this is a source pad */
246 	if (flags & MEDIA_LNK_FL_ENABLED) {
247 		switch (local->index) {
248 		case PRP_SRC_PAD_PRPENC:
249 			if (priv->sink_sd_prpenc) {
250 				ret = -EBUSY;
251 				goto out;
252 			}
253 			if (priv->src_sd && (priv->src_sd->grp_id &
254 					     IMX_MEDIA_GRP_ID_IPU_VDIC)) {
255 				ret = -EINVAL;
256 				goto out;
257 			}
258 			priv->sink_sd_prpenc = remote_sd;
259 			break;
260 		case PRP_SRC_PAD_PRPVF:
261 			if (priv->sink_sd_prpvf) {
262 				ret = -EBUSY;
263 				goto out;
264 			}
265 			priv->sink_sd_prpvf = remote_sd;
266 			break;
267 		default:
268 			ret = -EINVAL;
269 		}
270 	} else {
271 		switch (local->index) {
272 		case PRP_SRC_PAD_PRPENC:
273 			priv->sink_sd_prpenc = NULL;
274 			break;
275 		case PRP_SRC_PAD_PRPVF:
276 			priv->sink_sd_prpvf = NULL;
277 			break;
278 		default:
279 			ret = -EINVAL;
280 		}
281 	}
282 
283 out:
284 	mutex_unlock(&priv->lock);
285 	return ret;
286 }
287 
288 static int prp_link_validate(struct v4l2_subdev *sd,
289 			     struct media_link *link,
290 			     struct v4l2_subdev_format *source_fmt,
291 			     struct v4l2_subdev_format *sink_fmt)
292 {
293 	struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
294 	struct prp_priv *priv = ic_priv->task_priv;
295 	struct v4l2_subdev *csi;
296 	int ret;
297 
298 	ret = v4l2_subdev_link_validate_default(sd, link,
299 						source_fmt, sink_fmt);
300 	if (ret)
301 		return ret;
302 
303 	csi = imx_media_pipeline_subdev(&ic_priv->sd.entity,
304 					IMX_MEDIA_GRP_ID_IPU_CSI, true);
305 	if (IS_ERR(csi))
306 		csi = NULL;
307 
308 	mutex_lock(&priv->lock);
309 
310 	if (priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC) {
311 		/*
312 		 * the ->PRPENC link cannot be enabled if the source
313 		 * is the VDIC
314 		 */
315 		if (priv->sink_sd_prpenc) {
316 			ret = -EINVAL;
317 			goto out;
318 		}
319 	} else {
320 		/* the source is a CSI */
321 		if (!csi) {
322 			ret = -EINVAL;
323 			goto out;
324 		}
325 	}
326 
327 	if (csi) {
328 		switch (csi->grp_id) {
329 		case IMX_MEDIA_GRP_ID_IPU_CSI0:
330 			priv->csi_id = 0;
331 			break;
332 		case IMX_MEDIA_GRP_ID_IPU_CSI1:
333 			priv->csi_id = 1;
334 			break;
335 		default:
336 			ret = -EINVAL;
337 		}
338 	} else {
339 		priv->csi_id = 0;
340 	}
341 
342 out:
343 	mutex_unlock(&priv->lock);
344 	return ret;
345 }
346 
347 static int prp_s_stream(struct v4l2_subdev *sd, int enable)
348 {
349 	struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
350 	struct prp_priv *priv = ic_priv->task_priv;
351 	int ret = 0;
352 
353 	mutex_lock(&priv->lock);
354 
355 	if (!priv->src_sd || (!priv->sink_sd_prpenc && !priv->sink_sd_prpvf)) {
356 		ret = -EPIPE;
357 		goto out;
358 	}
359 
360 	/*
361 	 * enable/disable streaming only if stream_count is
362 	 * going from 0 to 1 / 1 to 0.
363 	 */
364 	if (priv->stream_count != !enable)
365 		goto update_count;
366 
367 	dev_dbg(ic_priv->ipu_dev, "%s: stream %s\n", sd->name,
368 		enable ? "ON" : "OFF");
369 
370 	if (enable)
371 		ret = prp_start(priv);
372 	else
373 		prp_stop(priv);
374 	if (ret)
375 		goto out;
376 
377 	/* start/stop upstream */
378 	ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable);
379 	ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
380 	if (ret) {
381 		if (enable)
382 			prp_stop(priv);
383 		goto out;
384 	}
385 
386 update_count:
387 	priv->stream_count += enable ? 1 : -1;
388 	if (priv->stream_count < 0)
389 		priv->stream_count = 0;
390 out:
391 	mutex_unlock(&priv->lock);
392 	return ret;
393 }
394 
395 static int prp_g_frame_interval(struct v4l2_subdev *sd,
396 				struct v4l2_subdev_frame_interval *fi)
397 {
398 	struct prp_priv *priv = sd_to_priv(sd);
399 
400 	if (fi->pad >= PRP_NUM_PADS)
401 		return -EINVAL;
402 
403 	mutex_lock(&priv->lock);
404 	fi->interval = priv->frame_interval;
405 	mutex_unlock(&priv->lock);
406 
407 	return 0;
408 }
409 
410 static int prp_s_frame_interval(struct v4l2_subdev *sd,
411 				struct v4l2_subdev_frame_interval *fi)
412 {
413 	struct prp_priv *priv = sd_to_priv(sd);
414 
415 	if (fi->pad >= PRP_NUM_PADS)
416 		return -EINVAL;
417 
418 	mutex_lock(&priv->lock);
419 
420 	/* No limits on valid frame intervals */
421 	if (fi->interval.numerator == 0 || fi->interval.denominator == 0)
422 		fi->interval = priv->frame_interval;
423 	else
424 		priv->frame_interval = fi->interval;
425 
426 	mutex_unlock(&priv->lock);
427 
428 	return 0;
429 }
430 
431 /*
432  * retrieve our pads parsed from the OF graph by the media device
433  */
434 static int prp_registered(struct v4l2_subdev *sd)
435 {
436 	struct prp_priv *priv = sd_to_priv(sd);
437 	int i, ret;
438 	u32 code;
439 
440 	for (i = 0; i < PRP_NUM_PADS; i++) {
441 		priv->pad[i].flags = (i == PRP_SINK_PAD) ?
442 			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
443 	}
444 
445 	/* init default frame interval */
446 	priv->frame_interval.numerator = 1;
447 	priv->frame_interval.denominator = 30;
448 
449 	/* set a default mbus format  */
450 	imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
451 	ret = imx_media_init_mbus_fmt(&priv->format_mbus, 640, 480, code,
452 				      V4L2_FIELD_NONE, NULL);
453 	if (ret)
454 		return ret;
455 
456 	return media_entity_pads_init(&sd->entity, PRP_NUM_PADS, priv->pad);
457 }
458 
459 static const struct v4l2_subdev_pad_ops prp_pad_ops = {
460 	.init_cfg = imx_media_init_cfg,
461 	.enum_mbus_code = prp_enum_mbus_code,
462 	.get_fmt = prp_get_fmt,
463 	.set_fmt = prp_set_fmt,
464 	.link_validate = prp_link_validate,
465 };
466 
467 static const struct v4l2_subdev_video_ops prp_video_ops = {
468 	.g_frame_interval = prp_g_frame_interval,
469 	.s_frame_interval = prp_s_frame_interval,
470 	.s_stream = prp_s_stream,
471 };
472 
473 static const struct media_entity_operations prp_entity_ops = {
474 	.link_setup = prp_link_setup,
475 	.link_validate = v4l2_subdev_link_validate,
476 };
477 
478 static const struct v4l2_subdev_ops prp_subdev_ops = {
479 	.video = &prp_video_ops,
480 	.pad = &prp_pad_ops,
481 };
482 
483 static const struct v4l2_subdev_internal_ops prp_internal_ops = {
484 	.registered = prp_registered,
485 };
486 
487 static int prp_init(struct imx_ic_priv *ic_priv)
488 {
489 	struct prp_priv *priv;
490 
491 	priv = devm_kzalloc(ic_priv->ipu_dev, sizeof(*priv), GFP_KERNEL);
492 	if (!priv)
493 		return -ENOMEM;
494 
495 	mutex_init(&priv->lock);
496 	ic_priv->task_priv = priv;
497 	priv->ic_priv = ic_priv;
498 
499 	return 0;
500 }
501 
502 static void prp_remove(struct imx_ic_priv *ic_priv)
503 {
504 	struct prp_priv *priv = ic_priv->task_priv;
505 
506 	mutex_destroy(&priv->lock);
507 }
508 
509 struct imx_ic_ops imx_ic_prp_ops = {
510 	.subdev_ops = &prp_subdev_ops,
511 	.internal_ops = &prp_internal_ops,
512 	.entity_ops = &prp_entity_ops,
513 	.init = prp_init,
514 	.remove = prp_remove,
515 };
516