Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * V4L2 Capture IC Preprocess Subdev for Freescale i.MX5/6 SOC
0004  *
0005  * This subdevice handles capture of video frames from the CSI or VDIC,
0006  * which are routed directly to the Image Converter preprocess tasks,
0007  * for resizing, colorspace conversion, and rotation.
0008  *
0009  * Copyright (c) 2012-2017 Mentor Graphics Inc.
0010  */
0011 #include <linux/delay.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/module.h>
0014 #include <linux/sched.h>
0015 #include <linux/slab.h>
0016 #include <linux/spinlock.h>
0017 #include <linux/timer.h>
0018 #include <media/v4l2-ctrls.h>
0019 #include <media/v4l2-device.h>
0020 #include <media/v4l2-ioctl.h>
0021 #include <media/v4l2-subdev.h>
0022 #include <media/imx.h>
0023 #include "imx-media.h"
0024 #include "imx-ic.h"
0025 
0026 /*
0027  * Min/Max supported width and heights.
0028  */
0029 #define MIN_W        32
0030 #define MIN_H        32
0031 #define MAX_W      4096
0032 #define MAX_H      4096
0033 #define W_ALIGN    4 /* multiple of 16 pixels */
0034 #define H_ALIGN    1 /* multiple of 2 lines */
0035 #define S_ALIGN    1 /* multiple of 2 */
0036 
0037 struct prp_priv {
0038     struct imx_ic_priv *ic_priv;
0039     struct media_pad pad[PRP_NUM_PADS];
0040 
0041     /* lock to protect all members below */
0042     struct mutex lock;
0043 
0044     struct v4l2_subdev *src_sd;
0045     struct v4l2_subdev *sink_sd_prpenc;
0046     struct v4l2_subdev *sink_sd_prpvf;
0047 
0048     /* the CSI id at link validate */
0049     int csi_id;
0050 
0051     struct v4l2_mbus_framefmt format_mbus;
0052     struct v4l2_fract frame_interval;
0053 
0054     int stream_count;
0055 };
0056 
0057 static inline struct prp_priv *sd_to_priv(struct v4l2_subdev *sd)
0058 {
0059     struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
0060 
0061     return ic_priv->task_priv;
0062 }
0063 
0064 static int prp_start(struct prp_priv *priv)
0065 {
0066     struct imx_ic_priv *ic_priv = priv->ic_priv;
0067     bool src_is_vdic;
0068 
0069     /* set IC to receive from CSI or VDI depending on source */
0070     src_is_vdic = !!(priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC);
0071 
0072     ipu_set_ic_src_mux(ic_priv->ipu, priv->csi_id, src_is_vdic);
0073 
0074     return 0;
0075 }
0076 
0077 static void prp_stop(struct prp_priv *priv)
0078 {
0079 }
0080 
0081 static struct v4l2_mbus_framefmt *
0082 __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state,
0083           unsigned int pad, enum v4l2_subdev_format_whence which)
0084 {
0085     struct imx_ic_priv *ic_priv = priv->ic_priv;
0086 
0087     if (which == V4L2_SUBDEV_FORMAT_TRY)
0088         return v4l2_subdev_get_try_format(&ic_priv->sd, sd_state, pad);
0089     else
0090         return &priv->format_mbus;
0091 }
0092 
0093 /*
0094  * V4L2 subdev operations.
0095  */
0096 
0097 static int prp_enum_mbus_code(struct v4l2_subdev *sd,
0098                   struct v4l2_subdev_state *sd_state,
0099                   struct v4l2_subdev_mbus_code_enum *code)
0100 {
0101     struct prp_priv *priv = sd_to_priv(sd);
0102     struct v4l2_mbus_framefmt *infmt;
0103     int ret = 0;
0104 
0105     mutex_lock(&priv->lock);
0106 
0107     switch (code->pad) {
0108     case PRP_SINK_PAD:
0109         ret = imx_media_enum_ipu_formats(&code->code, code->index,
0110                          PIXFMT_SEL_YUV_RGB);
0111         break;
0112     case PRP_SRC_PAD_PRPENC:
0113     case PRP_SRC_PAD_PRPVF:
0114         if (code->index != 0) {
0115             ret = -EINVAL;
0116             goto out;
0117         }
0118         infmt = __prp_get_fmt(priv, sd_state, PRP_SINK_PAD,
0119                       code->which);
0120         code->code = infmt->code;
0121         break;
0122     default:
0123         ret = -EINVAL;
0124     }
0125 out:
0126     mutex_unlock(&priv->lock);
0127     return ret;
0128 }
0129 
0130 static int prp_get_fmt(struct v4l2_subdev *sd,
0131                struct v4l2_subdev_state *sd_state,
0132                struct v4l2_subdev_format *sdformat)
0133 {
0134     struct prp_priv *priv = sd_to_priv(sd);
0135     struct v4l2_mbus_framefmt *fmt;
0136     int ret = 0;
0137 
0138     if (sdformat->pad >= PRP_NUM_PADS)
0139         return -EINVAL;
0140 
0141     mutex_lock(&priv->lock);
0142 
0143     fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which);
0144     if (!fmt) {
0145         ret = -EINVAL;
0146         goto out;
0147     }
0148 
0149     sdformat->format = *fmt;
0150 out:
0151     mutex_unlock(&priv->lock);
0152     return ret;
0153 }
0154 
0155 static int prp_set_fmt(struct v4l2_subdev *sd,
0156                struct v4l2_subdev_state *sd_state,
0157                struct v4l2_subdev_format *sdformat)
0158 {
0159     struct prp_priv *priv = sd_to_priv(sd);
0160     struct v4l2_mbus_framefmt *fmt, *infmt;
0161     const struct imx_media_pixfmt *cc;
0162     int ret = 0;
0163     u32 code;
0164 
0165     if (sdformat->pad >= PRP_NUM_PADS)
0166         return -EINVAL;
0167 
0168     mutex_lock(&priv->lock);
0169 
0170     if (priv->stream_count > 0) {
0171         ret = -EBUSY;
0172         goto out;
0173     }
0174 
0175     infmt = __prp_get_fmt(priv, sd_state, PRP_SINK_PAD, sdformat->which);
0176 
0177     switch (sdformat->pad) {
0178     case PRP_SINK_PAD:
0179         v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W,
0180                       W_ALIGN, &sdformat->format.height,
0181                       MIN_H, MAX_H, H_ALIGN, S_ALIGN);
0182 
0183         cc = imx_media_find_ipu_format(sdformat->format.code,
0184                            PIXFMT_SEL_YUV_RGB);
0185         if (!cc) {
0186             imx_media_enum_ipu_formats(&code, 0,
0187                            PIXFMT_SEL_YUV_RGB);
0188             cc = imx_media_find_ipu_format(code,
0189                                PIXFMT_SEL_YUV_RGB);
0190             sdformat->format.code = cc->codes[0];
0191         }
0192 
0193         if (sdformat->format.field == V4L2_FIELD_ANY)
0194             sdformat->format.field = V4L2_FIELD_NONE;
0195         break;
0196     case PRP_SRC_PAD_PRPENC:
0197     case PRP_SRC_PAD_PRPVF:
0198         /* Output pads mirror input pad */
0199         sdformat->format = *infmt;
0200         break;
0201     }
0202 
0203     imx_media_try_colorimetry(&sdformat->format, true);
0204 
0205     fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which);
0206     *fmt = sdformat->format;
0207 out:
0208     mutex_unlock(&priv->lock);
0209     return ret;
0210 }
0211 
0212 static int prp_link_setup(struct media_entity *entity,
0213               const struct media_pad *local,
0214               const struct media_pad *remote, u32 flags)
0215 {
0216     struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
0217     struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
0218     struct prp_priv *priv = ic_priv->task_priv;
0219     struct v4l2_subdev *remote_sd;
0220     int ret = 0;
0221 
0222     dev_dbg(ic_priv->ipu_dev, "%s: link setup %s -> %s",
0223         ic_priv->sd.name, remote->entity->name, local->entity->name);
0224 
0225     remote_sd = media_entity_to_v4l2_subdev(remote->entity);
0226 
0227     mutex_lock(&priv->lock);
0228 
0229     if (local->flags & MEDIA_PAD_FL_SINK) {
0230         if (flags & MEDIA_LNK_FL_ENABLED) {
0231             if (priv->src_sd) {
0232                 ret = -EBUSY;
0233                 goto out;
0234             }
0235             if (priv->sink_sd_prpenc &&
0236                 (remote_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC)) {
0237                 ret = -EINVAL;
0238                 goto out;
0239             }
0240             priv->src_sd = remote_sd;
0241         } else {
0242             priv->src_sd = NULL;
0243         }
0244 
0245         goto out;
0246     }
0247 
0248     /* this is a source pad */
0249     if (flags & MEDIA_LNK_FL_ENABLED) {
0250         switch (local->index) {
0251         case PRP_SRC_PAD_PRPENC:
0252             if (priv->sink_sd_prpenc) {
0253                 ret = -EBUSY;
0254                 goto out;
0255             }
0256             if (priv->src_sd && (priv->src_sd->grp_id &
0257                          IMX_MEDIA_GRP_ID_IPU_VDIC)) {
0258                 ret = -EINVAL;
0259                 goto out;
0260             }
0261             priv->sink_sd_prpenc = remote_sd;
0262             break;
0263         case PRP_SRC_PAD_PRPVF:
0264             if (priv->sink_sd_prpvf) {
0265                 ret = -EBUSY;
0266                 goto out;
0267             }
0268             priv->sink_sd_prpvf = remote_sd;
0269             break;
0270         default:
0271             ret = -EINVAL;
0272         }
0273     } else {
0274         switch (local->index) {
0275         case PRP_SRC_PAD_PRPENC:
0276             priv->sink_sd_prpenc = NULL;
0277             break;
0278         case PRP_SRC_PAD_PRPVF:
0279             priv->sink_sd_prpvf = NULL;
0280             break;
0281         default:
0282             ret = -EINVAL;
0283         }
0284     }
0285 
0286 out:
0287     mutex_unlock(&priv->lock);
0288     return ret;
0289 }
0290 
0291 static int prp_link_validate(struct v4l2_subdev *sd,
0292                  struct media_link *link,
0293                  struct v4l2_subdev_format *source_fmt,
0294                  struct v4l2_subdev_format *sink_fmt)
0295 {
0296     struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
0297     struct prp_priv *priv = ic_priv->task_priv;
0298     struct v4l2_subdev *csi;
0299     int ret;
0300 
0301     ret = v4l2_subdev_link_validate_default(sd, link,
0302                         source_fmt, sink_fmt);
0303     if (ret)
0304         return ret;
0305 
0306     csi = imx_media_pipeline_subdev(&ic_priv->sd.entity,
0307                     IMX_MEDIA_GRP_ID_IPU_CSI, true);
0308     if (IS_ERR(csi))
0309         csi = NULL;
0310 
0311     mutex_lock(&priv->lock);
0312 
0313     if (priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC) {
0314         /*
0315          * the ->PRPENC link cannot be enabled if the source
0316          * is the VDIC
0317          */
0318         if (priv->sink_sd_prpenc) {
0319             ret = -EINVAL;
0320             goto out;
0321         }
0322     } else {
0323         /* the source is a CSI */
0324         if (!csi) {
0325             ret = -EINVAL;
0326             goto out;
0327         }
0328     }
0329 
0330     if (csi) {
0331         switch (csi->grp_id) {
0332         case IMX_MEDIA_GRP_ID_IPU_CSI0:
0333             priv->csi_id = 0;
0334             break;
0335         case IMX_MEDIA_GRP_ID_IPU_CSI1:
0336             priv->csi_id = 1;
0337             break;
0338         default:
0339             ret = -EINVAL;
0340         }
0341     } else {
0342         priv->csi_id = 0;
0343     }
0344 
0345 out:
0346     mutex_unlock(&priv->lock);
0347     return ret;
0348 }
0349 
0350 static int prp_s_stream(struct v4l2_subdev *sd, int enable)
0351 {
0352     struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
0353     struct prp_priv *priv = ic_priv->task_priv;
0354     int ret = 0;
0355 
0356     mutex_lock(&priv->lock);
0357 
0358     if (!priv->src_sd || (!priv->sink_sd_prpenc && !priv->sink_sd_prpvf)) {
0359         ret = -EPIPE;
0360         goto out;
0361     }
0362 
0363     /*
0364      * enable/disable streaming only if stream_count is
0365      * going from 0 to 1 / 1 to 0.
0366      */
0367     if (priv->stream_count != !enable)
0368         goto update_count;
0369 
0370     dev_dbg(ic_priv->ipu_dev, "%s: stream %s\n", sd->name,
0371         enable ? "ON" : "OFF");
0372 
0373     if (enable)
0374         ret = prp_start(priv);
0375     else
0376         prp_stop(priv);
0377     if (ret)
0378         goto out;
0379 
0380     /* start/stop upstream */
0381     ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable);
0382     ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
0383     if (ret) {
0384         if (enable)
0385             prp_stop(priv);
0386         goto out;
0387     }
0388 
0389 update_count:
0390     priv->stream_count += enable ? 1 : -1;
0391     if (priv->stream_count < 0)
0392         priv->stream_count = 0;
0393 out:
0394     mutex_unlock(&priv->lock);
0395     return ret;
0396 }
0397 
0398 static int prp_g_frame_interval(struct v4l2_subdev *sd,
0399                 struct v4l2_subdev_frame_interval *fi)
0400 {
0401     struct prp_priv *priv = sd_to_priv(sd);
0402 
0403     if (fi->pad >= PRP_NUM_PADS)
0404         return -EINVAL;
0405 
0406     mutex_lock(&priv->lock);
0407     fi->interval = priv->frame_interval;
0408     mutex_unlock(&priv->lock);
0409 
0410     return 0;
0411 }
0412 
0413 static int prp_s_frame_interval(struct v4l2_subdev *sd,
0414                 struct v4l2_subdev_frame_interval *fi)
0415 {
0416     struct prp_priv *priv = sd_to_priv(sd);
0417 
0418     if (fi->pad >= PRP_NUM_PADS)
0419         return -EINVAL;
0420 
0421     mutex_lock(&priv->lock);
0422 
0423     /* No limits on valid frame intervals */
0424     if (fi->interval.numerator == 0 || fi->interval.denominator == 0)
0425         fi->interval = priv->frame_interval;
0426     else
0427         priv->frame_interval = fi->interval;
0428 
0429     mutex_unlock(&priv->lock);
0430 
0431     return 0;
0432 }
0433 
0434 static int prp_registered(struct v4l2_subdev *sd)
0435 {
0436     struct prp_priv *priv = sd_to_priv(sd);
0437     u32 code;
0438 
0439     /* init default frame interval */
0440     priv->frame_interval.numerator = 1;
0441     priv->frame_interval.denominator = 30;
0442 
0443     /* set a default mbus format  */
0444     imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV);
0445 
0446     return imx_media_init_mbus_fmt(&priv->format_mbus,
0447                        IMX_MEDIA_DEF_PIX_WIDTH,
0448                        IMX_MEDIA_DEF_PIX_HEIGHT, code,
0449                        V4L2_FIELD_NONE, NULL);
0450 }
0451 
0452 static const struct v4l2_subdev_pad_ops prp_pad_ops = {
0453     .init_cfg = imx_media_init_cfg,
0454     .enum_mbus_code = prp_enum_mbus_code,
0455     .get_fmt = prp_get_fmt,
0456     .set_fmt = prp_set_fmt,
0457     .link_validate = prp_link_validate,
0458 };
0459 
0460 static const struct v4l2_subdev_video_ops prp_video_ops = {
0461     .g_frame_interval = prp_g_frame_interval,
0462     .s_frame_interval = prp_s_frame_interval,
0463     .s_stream = prp_s_stream,
0464 };
0465 
0466 static const struct media_entity_operations prp_entity_ops = {
0467     .link_setup = prp_link_setup,
0468     .link_validate = v4l2_subdev_link_validate,
0469 };
0470 
0471 static const struct v4l2_subdev_ops prp_subdev_ops = {
0472     .video = &prp_video_ops,
0473     .pad = &prp_pad_ops,
0474 };
0475 
0476 static const struct v4l2_subdev_internal_ops prp_internal_ops = {
0477     .registered = prp_registered,
0478 };
0479 
0480 static int prp_init(struct imx_ic_priv *ic_priv)
0481 {
0482     struct prp_priv *priv;
0483     int i;
0484 
0485     priv = devm_kzalloc(ic_priv->ipu_dev, sizeof(*priv), GFP_KERNEL);
0486     if (!priv)
0487         return -ENOMEM;
0488 
0489     mutex_init(&priv->lock);
0490     ic_priv->task_priv = priv;
0491     priv->ic_priv = ic_priv;
0492 
0493     for (i = 0; i < PRP_NUM_PADS; i++)
0494         priv->pad[i].flags = (i == PRP_SINK_PAD) ?
0495             MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
0496 
0497     return media_entity_pads_init(&ic_priv->sd.entity, PRP_NUM_PADS,
0498                       priv->pad);
0499 }
0500 
0501 static void prp_remove(struct imx_ic_priv *ic_priv)
0502 {
0503     struct prp_priv *priv = ic_priv->task_priv;
0504 
0505     mutex_destroy(&priv->lock);
0506 }
0507 
0508 struct imx_ic_ops imx_ic_prp_ops = {
0509     .subdev_ops = &prp_subdev_ops,
0510     .internal_ops = &prp_internal_ops,
0511     .entity_ops = &prp_entity_ops,
0512     .init = prp_init,
0513     .remove = prp_remove,
0514 };