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