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