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
37struct 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
57static 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
64static 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
77static void prp_stop(struct prp_priv *priv)
78{
79}
80
81static struct v4l2_mbus_framefmt *
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
95static 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	}
123out:
124	mutex_unlock(&priv->lock);
125	return ret;
126}
127
128static 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;
148out:
149	mutex_unlock(&priv->lock);
150	return ret;
151}
152
153static 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;
205out:
206	mutex_unlock(&priv->lock);
207	return ret;
208}
209
210static 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
284out:
285	mutex_unlock(&priv->lock);
286	return ret;
287}
288
289static 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
343out:
344	mutex_unlock(&priv->lock);
345	return ret;
346}
347
348static 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
387update_count:
388	priv->stream_count += enable ? 1 : -1;
389	if (priv->stream_count < 0)
390		priv->stream_count = 0;
391out:
392	mutex_unlock(&priv->lock);
393	return ret;
394}
395
396static 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
419static 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
448static 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
466static 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
475static const struct v4l2_subdev_video_ops prp_video_ops = {
476	.s_stream = prp_s_stream,
477};
478
479static const struct media_entity_operations prp_entity_ops = {
480	.link_setup = prp_link_setup,
481	.link_validate = v4l2_subdev_link_validate,
482};
483
484static const struct v4l2_subdev_ops prp_subdev_ops = {
485	.video = &prp_video_ops,
486	.pad = &prp_pad_ops,
487};
488
489static const struct v4l2_subdev_internal_ops prp_internal_ops = {
490	.init_state = imx_media_init_state,
491	.registered = prp_registered,
492};
493
494static 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
515static 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
522struct 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