GNU Linux-libre 6.8.7-gnu
[releases.git] / drivers / staging / media / imx / imx-ic-prp.c
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
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_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
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
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
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
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
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
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
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
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
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
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
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 };