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