root/drivers/staging/media/imx/imx-ic-prp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sd_to_priv
  2. prp_start
  3. prp_stop
  4. __prp_get_fmt
  5. prp_enum_mbus_code
  6. prp_get_fmt
  7. prp_set_fmt
  8. prp_link_setup
  9. prp_link_validate
  10. prp_s_stream
  11. prp_g_frame_interval
  12. prp_s_frame_interval
  13. prp_registered
  14. prp_init
  15. prp_remove

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

/* [<][>][^][v][top][bottom][index][help] */