2 * V4L2 Capture IC Preprocess Subdev for Freescale i.MX5/6 SOC
4 * This subdevice handles capture of video frames from the CSI or VDIC,
5 * which are routed directly to the Image Converter preprocess tasks,
6 * for resizing, colorspace conversion, and rotation.
8 * Copyright (c) 2012-2017 Mentor Graphics Inc.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 #include <linux/delay.h>
16 #include <linux/interrupt.h>
17 #include <linux/module.h>
18 #include <linux/sched.h>
19 #include <linux/slab.h>
20 #include <linux/spinlock.h>
21 #include <linux/timer.h>
22 #include <media/v4l2-ctrls.h>
23 #include <media/v4l2-device.h>
24 #include <media/v4l2-ioctl.h>
25 #include <media/v4l2-subdev.h>
26 #include <media/imx.h>
27 #include "imx-media.h"
31 * Min/Max supported width and heights.
37 #define W_ALIGN 4 /* multiple of 16 pixels */
38 #define H_ALIGN 1 /* multiple of 2 lines */
39 #define S_ALIGN 1 /* multiple of 2 */
42 struct imx_media_dev *md;
43 struct imx_ic_priv *ic_priv;
44 struct media_pad pad[PRP_NUM_PADS];
46 /* lock to protect all members below */
49 /* IPU units we require */
52 struct v4l2_subdev *src_sd;
53 struct v4l2_subdev *sink_sd_prpenc;
54 struct v4l2_subdev *sink_sd_prpvf;
56 /* the CSI id at link validate */
59 struct v4l2_mbus_framefmt format_mbus;
60 struct v4l2_fract frame_interval;
65 static inline struct prp_priv *sd_to_priv(struct v4l2_subdev *sd)
67 struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
69 return ic_priv->prp_priv;
72 static int prp_start(struct prp_priv *priv)
74 struct imx_ic_priv *ic_priv = priv->ic_priv;
77 priv->ipu = priv->md->ipu[ic_priv->ipu_id];
79 /* set IC to receive from CSI or VDI depending on source */
80 src_is_vdic = !!(priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_VDIC);
82 ipu_set_ic_src_mux(priv->ipu, priv->csi_id, src_is_vdic);
87 static void prp_stop(struct prp_priv *priv)
91 static struct v4l2_mbus_framefmt *
92 __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_pad_config *cfg,
93 unsigned int pad, enum v4l2_subdev_format_whence which)
95 struct imx_ic_priv *ic_priv = priv->ic_priv;
97 if (which == V4L2_SUBDEV_FORMAT_TRY)
98 return v4l2_subdev_get_try_format(&ic_priv->sd, cfg, pad);
100 return &priv->format_mbus;
104 * V4L2 subdev operations.
107 static int prp_enum_mbus_code(struct v4l2_subdev *sd,
108 struct v4l2_subdev_pad_config *cfg,
109 struct v4l2_subdev_mbus_code_enum *code)
111 struct prp_priv *priv = sd_to_priv(sd);
112 struct v4l2_mbus_framefmt *infmt;
115 mutex_lock(&priv->lock);
119 ret = imx_media_enum_ipu_format(&code->code, code->index,
122 case PRP_SRC_PAD_PRPENC:
123 case PRP_SRC_PAD_PRPVF:
124 if (code->index != 0) {
128 infmt = __prp_get_fmt(priv, cfg, PRP_SINK_PAD, code->which);
129 code->code = infmt->code;
135 mutex_unlock(&priv->lock);
139 static int prp_get_fmt(struct v4l2_subdev *sd,
140 struct v4l2_subdev_pad_config *cfg,
141 struct v4l2_subdev_format *sdformat)
143 struct prp_priv *priv = sd_to_priv(sd);
144 struct v4l2_mbus_framefmt *fmt;
147 if (sdformat->pad >= PRP_NUM_PADS)
150 mutex_lock(&priv->lock);
152 fmt = __prp_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
158 sdformat->format = *fmt;
160 mutex_unlock(&priv->lock);
164 static int prp_set_fmt(struct v4l2_subdev *sd,
165 struct v4l2_subdev_pad_config *cfg,
166 struct v4l2_subdev_format *sdformat)
168 struct prp_priv *priv = sd_to_priv(sd);
169 struct v4l2_mbus_framefmt *fmt, *infmt;
170 const struct imx_media_pixfmt *cc;
174 if (sdformat->pad >= PRP_NUM_PADS)
177 mutex_lock(&priv->lock);
179 if (priv->stream_count > 0) {
184 infmt = __prp_get_fmt(priv, cfg, PRP_SINK_PAD, sdformat->which);
186 switch (sdformat->pad) {
188 v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W,
189 W_ALIGN, &sdformat->format.height,
190 MIN_H, MAX_H, H_ALIGN, S_ALIGN);
192 cc = imx_media_find_ipu_format(sdformat->format.code,
195 imx_media_enum_ipu_format(&code, 0, CS_SEL_ANY);
196 cc = imx_media_find_ipu_format(code, CS_SEL_ANY);
197 sdformat->format.code = cc->codes[0];
200 imx_media_fill_default_mbus_fields(&sdformat->format, infmt,
203 case PRP_SRC_PAD_PRPENC:
204 case PRP_SRC_PAD_PRPVF:
205 /* Output pads mirror input pad */
206 sdformat->format = *infmt;
210 fmt = __prp_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
211 *fmt = sdformat->format;
213 mutex_unlock(&priv->lock);
217 static int prp_link_setup(struct media_entity *entity,
218 const struct media_pad *local,
219 const struct media_pad *remote, u32 flags)
221 struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
222 struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
223 struct prp_priv *priv = ic_priv->prp_priv;
224 struct v4l2_subdev *remote_sd;
227 dev_dbg(ic_priv->dev, "link setup %s -> %s", remote->entity->name,
228 local->entity->name);
230 remote_sd = media_entity_to_v4l2_subdev(remote->entity);
232 mutex_lock(&priv->lock);
234 if (local->flags & MEDIA_PAD_FL_SINK) {
235 if (flags & MEDIA_LNK_FL_ENABLED) {
240 if (priv->sink_sd_prpenc && (remote_sd->grp_id &
241 IMX_MEDIA_GRP_ID_VDIC)) {
245 priv->src_sd = remote_sd;
253 /* this is a source pad */
254 if (flags & MEDIA_LNK_FL_ENABLED) {
255 switch (local->index) {
256 case PRP_SRC_PAD_PRPENC:
257 if (priv->sink_sd_prpenc) {
261 if (priv->src_sd && (priv->src_sd->grp_id &
262 IMX_MEDIA_GRP_ID_VDIC)) {
266 priv->sink_sd_prpenc = remote_sd;
268 case PRP_SRC_PAD_PRPVF:
269 if (priv->sink_sd_prpvf) {
273 priv->sink_sd_prpvf = remote_sd;
279 switch (local->index) {
280 case PRP_SRC_PAD_PRPENC:
281 priv->sink_sd_prpenc = NULL;
283 case PRP_SRC_PAD_PRPVF:
284 priv->sink_sd_prpvf = NULL;
292 mutex_unlock(&priv->lock);
296 static int prp_link_validate(struct v4l2_subdev *sd,
297 struct media_link *link,
298 struct v4l2_subdev_format *source_fmt,
299 struct v4l2_subdev_format *sink_fmt)
301 struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
302 struct prp_priv *priv = ic_priv->prp_priv;
303 struct imx_media_subdev *csi;
306 ret = v4l2_subdev_link_validate_default(sd, link,
307 source_fmt, sink_fmt);
311 csi = imx_media_find_upstream_subdev(priv->md, &ic_priv->sd.entity,
312 IMX_MEDIA_GRP_ID_CSI);
316 mutex_lock(&priv->lock);
318 if (priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_VDIC) {
320 * the ->PRPENC link cannot be enabled if the source
323 if (priv->sink_sd_prpenc)
327 /* the source is a CSI */
335 switch (csi->sd->grp_id) {
336 case IMX_MEDIA_GRP_ID_CSI0:
339 case IMX_MEDIA_GRP_ID_CSI1:
350 mutex_unlock(&priv->lock);
354 static int prp_s_stream(struct v4l2_subdev *sd, int enable)
356 struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
357 struct prp_priv *priv = ic_priv->prp_priv;
360 mutex_lock(&priv->lock);
362 if (!priv->src_sd || (!priv->sink_sd_prpenc && !priv->sink_sd_prpvf)) {
368 * enable/disable streaming only if stream_count is
369 * going from 0 to 1 / 1 to 0.
371 if (priv->stream_count != !enable)
374 dev_dbg(ic_priv->dev, "stream %s\n", enable ? "ON" : "OFF");
377 ret = prp_start(priv);
383 /* start/stop upstream */
384 ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable);
385 ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
393 priv->stream_count += enable ? 1 : -1;
394 if (priv->stream_count < 0)
395 priv->stream_count = 0;
397 mutex_unlock(&priv->lock);
401 static int prp_g_frame_interval(struct v4l2_subdev *sd,
402 struct v4l2_subdev_frame_interval *fi)
404 struct prp_priv *priv = sd_to_priv(sd);
406 if (fi->pad >= PRP_NUM_PADS)
409 mutex_lock(&priv->lock);
410 fi->interval = priv->frame_interval;
411 mutex_unlock(&priv->lock);
416 static int prp_s_frame_interval(struct v4l2_subdev *sd,
417 struct v4l2_subdev_frame_interval *fi)
419 struct prp_priv *priv = sd_to_priv(sd);
421 if (fi->pad >= PRP_NUM_PADS)
424 /* No limits on frame interval */
425 mutex_lock(&priv->lock);
426 priv->frame_interval = fi->interval;
427 mutex_unlock(&priv->lock);
433 * retrieve our pads parsed from the OF graph by the media device
435 static int prp_registered(struct v4l2_subdev *sd)
437 struct prp_priv *priv = sd_to_priv(sd);
441 /* get media device */
442 priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
444 for (i = 0; i < PRP_NUM_PADS; i++) {
445 priv->pad[i].flags = (i == PRP_SINK_PAD) ?
446 MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
449 /* init default frame interval */
450 priv->frame_interval.numerator = 1;
451 priv->frame_interval.denominator = 30;
453 /* set a default mbus format */
454 imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
455 ret = imx_media_init_mbus_fmt(&priv->format_mbus, 640, 480, code,
456 V4L2_FIELD_NONE, NULL);
460 return media_entity_pads_init(&sd->entity, PRP_NUM_PADS, priv->pad);
463 static const struct v4l2_subdev_pad_ops prp_pad_ops = {
464 .enum_mbus_code = prp_enum_mbus_code,
465 .get_fmt = prp_get_fmt,
466 .set_fmt = prp_set_fmt,
467 .link_validate = prp_link_validate,
470 static const struct v4l2_subdev_video_ops prp_video_ops = {
471 .g_frame_interval = prp_g_frame_interval,
472 .s_frame_interval = prp_s_frame_interval,
473 .s_stream = prp_s_stream,
476 static const struct media_entity_operations prp_entity_ops = {
477 .link_setup = prp_link_setup,
478 .link_validate = v4l2_subdev_link_validate,
481 static const struct v4l2_subdev_ops prp_subdev_ops = {
482 .video = &prp_video_ops,
486 static const struct v4l2_subdev_internal_ops prp_internal_ops = {
487 .registered = prp_registered,
490 static int prp_init(struct imx_ic_priv *ic_priv)
492 struct prp_priv *priv;
494 priv = devm_kzalloc(ic_priv->dev, sizeof(*priv), GFP_KERNEL);
498 mutex_init(&priv->lock);
499 ic_priv->prp_priv = priv;
500 priv->ic_priv = ic_priv;
505 static void prp_remove(struct imx_ic_priv *ic_priv)
507 struct prp_priv *priv = ic_priv->prp_priv;
509 mutex_destroy(&priv->lock);
512 struct imx_ic_ops imx_ic_prp_ops = {
513 .subdev_ops = &prp_subdev_ops,
514 .internal_ops = &prp_internal_ops,
515 .entity_ops = &prp_entity_ops,
517 .remove = prp_remove,