linux/drivers/staging/media/imx/imx-ic-prp.c
<<
>>
Prefs
   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        32
  30#define MIN_H        32
  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
  37struct 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
  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        /* 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
  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 * V4L2 subdev operations.
  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                /* Output pads mirror input pad */
 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        /* this is a source pad */
 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                 * the ->PRPENC link cannot be enabled if the source
 315                 * is the VDIC
 316                 */
 317                if (priv->sink_sd_prpenc) {
 318                        ret = -EINVAL;
 319                        goto out;
 320                }
 321        } else {
 322                /* the source is a CSI */
 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         * enable/disable streaming only if stream_count is
 364         * going from 0 to 1 / 1 to 0.
 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        /* start/stop upstream */
 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        /* No limits on valid frame intervals */
 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        /* init default frame interval */
 439        priv->frame_interval.numerator = 1;
 440        priv->frame_interval.denominator = 30;
 441
 442        /* set a default mbus format  */
 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