1
2
3
4
5
6
7
8
9
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
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
34#define H_ALIGN 1
35#define S_ALIGN 1
36
37struct prp_priv {
38 struct imx_ic_priv *ic_priv;
39 struct media_pad pad[PRP_NUM_PADS];
40
41
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
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
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 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
95
96
97static 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 }
125out:
126 mutex_unlock(&priv->lock);
127 return ret;
128}
129
130static 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;
150out:
151 mutex_unlock(&priv->lock);
152 return ret;
153}
154
155static 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
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;
207out:
208 mutex_unlock(&priv->lock);
209 return ret;
210}
211
212static 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
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
286out:
287 mutex_unlock(&priv->lock);
288 return ret;
289}
290
291static 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
316
317
318 if (priv->sink_sd_prpenc) {
319 ret = -EINVAL;
320 goto out;
321 }
322 } else {
323
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
345out:
346 mutex_unlock(&priv->lock);
347 return ret;
348}
349
350static 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
365
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
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
389update_count:
390 priv->stream_count += enable ? 1 : -1;
391 if (priv->stream_count < 0)
392 priv->stream_count = 0;
393out:
394 mutex_unlock(&priv->lock);
395 return ret;
396}
397
398static 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
413static 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
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
434static int prp_registered(struct v4l2_subdev *sd)
435{
436 struct prp_priv *priv = sd_to_priv(sd);
437 u32 code;
438
439
440 priv->frame_interval.numerator = 1;
441 priv->frame_interval.denominator = 30;
442
443
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
452static 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
460static 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
466static const struct media_entity_operations prp_entity_ops = {
467 .link_setup = prp_link_setup,
468 .link_validate = v4l2_subdev_link_validate,
469};
470
471static const struct v4l2_subdev_ops prp_subdev_ops = {
472 .video = &prp_video_ops,
473 .pad = &prp_pad_ops,
474};
475
476static const struct v4l2_subdev_internal_ops prp_internal_ops = {
477 .registered = prp_registered,
478};
479
480static 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
501static 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
508struct 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};
515