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