linux/drivers/staging/media/sunxi/cedrus/cedrus_video.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Cedrus VPU driver
   4 *
   5 * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
   6 * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
   7 * Copyright (C) 2018 Bootlin
   8 *
   9 * Based on the vim2m driver, that is:
  10 *
  11 * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
  12 * Pawel Osciak, <pawel@osciak.com>
  13 * Marek Szyprowski, <m.szyprowski@samsung.com>
  14 */
  15
  16#include <media/videobuf2-dma-contig.h>
  17#include <media/v4l2-device.h>
  18#include <media/v4l2-ioctl.h>
  19#include <media/v4l2-event.h>
  20#include <media/v4l2-mem2mem.h>
  21
  22#include "cedrus.h"
  23#include "cedrus_video.h"
  24#include "cedrus_dec.h"
  25#include "cedrus_hw.h"
  26
  27#define CEDRUS_DECODE_SRC       BIT(0)
  28#define CEDRUS_DECODE_DST       BIT(1)
  29
  30#define CEDRUS_MIN_WIDTH        16U
  31#define CEDRUS_MIN_HEIGHT       16U
  32#define CEDRUS_MAX_WIDTH        3840U
  33#define CEDRUS_MAX_HEIGHT       2160U
  34
  35static struct cedrus_format cedrus_formats[] = {
  36        {
  37                .pixelformat    = V4L2_PIX_FMT_MPEG2_SLICE,
  38                .directions     = CEDRUS_DECODE_SRC,
  39        },
  40        {
  41                .pixelformat    = V4L2_PIX_FMT_H264_SLICE,
  42                .directions     = CEDRUS_DECODE_SRC,
  43        },
  44        {
  45                .pixelformat    = V4L2_PIX_FMT_SUNXI_TILED_NV12,
  46                .directions     = CEDRUS_DECODE_DST,
  47        },
  48        {
  49                .pixelformat    = V4L2_PIX_FMT_NV12,
  50                .directions     = CEDRUS_DECODE_DST,
  51                .capabilities   = CEDRUS_CAPABILITY_UNTILED,
  52        },
  53};
  54
  55#define CEDRUS_FORMATS_COUNT    ARRAY_SIZE(cedrus_formats)
  56
  57static inline struct cedrus_ctx *cedrus_file2ctx(struct file *file)
  58{
  59        return container_of(file->private_data, struct cedrus_ctx, fh);
  60}
  61
  62static struct cedrus_format *cedrus_find_format(u32 pixelformat, u32 directions,
  63                                                unsigned int capabilities)
  64{
  65        struct cedrus_format *fmt;
  66        unsigned int i;
  67
  68        for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) {
  69                fmt = &cedrus_formats[i];
  70
  71                if (fmt->capabilities && (fmt->capabilities & capabilities) !=
  72                    fmt->capabilities)
  73                        continue;
  74
  75                if (fmt->pixelformat == pixelformat &&
  76                    (fmt->directions & directions) != 0)
  77                        break;
  78        }
  79
  80        if (i == CEDRUS_FORMATS_COUNT)
  81                return NULL;
  82
  83        return &cedrus_formats[i];
  84}
  85
  86static bool cedrus_check_format(u32 pixelformat, u32 directions,
  87                                unsigned int capabilities)
  88{
  89        return cedrus_find_format(pixelformat, directions, capabilities);
  90}
  91
  92static void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt)
  93{
  94        unsigned int width = pix_fmt->width;
  95        unsigned int height = pix_fmt->height;
  96        unsigned int sizeimage = pix_fmt->sizeimage;
  97        unsigned int bytesperline = pix_fmt->bytesperline;
  98
  99        pix_fmt->field = V4L2_FIELD_NONE;
 100
 101        /* Limit to hardware min/max. */
 102        width = clamp(width, CEDRUS_MIN_WIDTH, CEDRUS_MAX_WIDTH);
 103        height = clamp(height, CEDRUS_MIN_HEIGHT, CEDRUS_MAX_HEIGHT);
 104
 105        switch (pix_fmt->pixelformat) {
 106        case V4L2_PIX_FMT_MPEG2_SLICE:
 107        case V4L2_PIX_FMT_H264_SLICE:
 108                /* Zero bytes per line for encoded source. */
 109                bytesperline = 0;
 110
 111                break;
 112
 113        case V4L2_PIX_FMT_SUNXI_TILED_NV12:
 114                /* 32-aligned stride. */
 115                bytesperline = ALIGN(width, 32);
 116
 117                /* 32-aligned height. */
 118                height = ALIGN(height, 32);
 119
 120                /* Luma plane size. */
 121                sizeimage = bytesperline * height;
 122
 123                /* Chroma plane size. */
 124                sizeimage += bytesperline * height / 2;
 125
 126                break;
 127
 128        case V4L2_PIX_FMT_NV12:
 129                /* 16-aligned stride. */
 130                bytesperline = ALIGN(width, 16);
 131
 132                /* 16-aligned height. */
 133                height = ALIGN(height, 16);
 134
 135                /* Luma plane size. */
 136                sizeimage = bytesperline * height;
 137
 138                /* Chroma plane size. */
 139                sizeimage += bytesperline * height / 2;
 140
 141                break;
 142        }
 143
 144        pix_fmt->width = width;
 145        pix_fmt->height = height;
 146
 147        pix_fmt->bytesperline = bytesperline;
 148        pix_fmt->sizeimage = sizeimage;
 149}
 150
 151static int cedrus_querycap(struct file *file, void *priv,
 152                           struct v4l2_capability *cap)
 153{
 154        strscpy(cap->driver, CEDRUS_NAME, sizeof(cap->driver));
 155        strscpy(cap->card, CEDRUS_NAME, sizeof(cap->card));
 156        snprintf(cap->bus_info, sizeof(cap->bus_info),
 157                 "platform:%s", CEDRUS_NAME);
 158
 159        return 0;
 160}
 161
 162static int cedrus_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
 163                           u32 direction)
 164{
 165        struct cedrus_ctx *ctx = cedrus_file2ctx(file);
 166        struct cedrus_dev *dev = ctx->dev;
 167        unsigned int capabilities = dev->capabilities;
 168        struct cedrus_format *fmt;
 169        unsigned int i, index;
 170
 171        /* Index among formats that match the requested direction. */
 172        index = 0;
 173
 174        for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) {
 175                fmt = &cedrus_formats[i];
 176
 177                if (fmt->capabilities && (fmt->capabilities & capabilities) !=
 178                    fmt->capabilities)
 179                        continue;
 180
 181                if (!(cedrus_formats[i].directions & direction))
 182                        continue;
 183
 184                if (index == f->index)
 185                        break;
 186
 187                index++;
 188        }
 189
 190        /* Matched format. */
 191        if (i < CEDRUS_FORMATS_COUNT) {
 192                f->pixelformat = cedrus_formats[i].pixelformat;
 193
 194                return 0;
 195        }
 196
 197        return -EINVAL;
 198}
 199
 200static int cedrus_enum_fmt_vid_cap(struct file *file, void *priv,
 201                                   struct v4l2_fmtdesc *f)
 202{
 203        return cedrus_enum_fmt(file, f, CEDRUS_DECODE_DST);
 204}
 205
 206static int cedrus_enum_fmt_vid_out(struct file *file, void *priv,
 207                                   struct v4l2_fmtdesc *f)
 208{
 209        return cedrus_enum_fmt(file, f, CEDRUS_DECODE_SRC);
 210}
 211
 212static int cedrus_g_fmt_vid_cap(struct file *file, void *priv,
 213                                struct v4l2_format *f)
 214{
 215        struct cedrus_ctx *ctx = cedrus_file2ctx(file);
 216
 217        /* Fall back to dummy default by lack of hardware configuration. */
 218        if (!ctx->dst_fmt.width || !ctx->dst_fmt.height) {
 219                f->fmt.pix.pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12;
 220                cedrus_prepare_format(&f->fmt.pix);
 221
 222                return 0;
 223        }
 224
 225        f->fmt.pix = ctx->dst_fmt;
 226
 227        return 0;
 228}
 229
 230static int cedrus_g_fmt_vid_out(struct file *file, void *priv,
 231                                struct v4l2_format *f)
 232{
 233        struct cedrus_ctx *ctx = cedrus_file2ctx(file);
 234
 235        /* Fall back to dummy default by lack of hardware configuration. */
 236        if (!ctx->dst_fmt.width || !ctx->dst_fmt.height) {
 237                f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG2_SLICE;
 238                f->fmt.pix.sizeimage = SZ_1K;
 239                cedrus_prepare_format(&f->fmt.pix);
 240
 241                return 0;
 242        }
 243
 244        f->fmt.pix = ctx->src_fmt;
 245
 246        return 0;
 247}
 248
 249static int cedrus_try_fmt_vid_cap(struct file *file, void *priv,
 250                                  struct v4l2_format *f)
 251{
 252        struct cedrus_ctx *ctx = cedrus_file2ctx(file);
 253        struct cedrus_dev *dev = ctx->dev;
 254        struct v4l2_pix_format *pix_fmt = &f->fmt.pix;
 255
 256        if (!cedrus_check_format(pix_fmt->pixelformat, CEDRUS_DECODE_DST,
 257                                 dev->capabilities))
 258                return -EINVAL;
 259
 260        cedrus_prepare_format(pix_fmt);
 261
 262        return 0;
 263}
 264
 265static int cedrus_try_fmt_vid_out(struct file *file, void *priv,
 266                                  struct v4l2_format *f)
 267{
 268        struct cedrus_ctx *ctx = cedrus_file2ctx(file);
 269        struct cedrus_dev *dev = ctx->dev;
 270        struct v4l2_pix_format *pix_fmt = &f->fmt.pix;
 271
 272        if (!cedrus_check_format(pix_fmt->pixelformat, CEDRUS_DECODE_SRC,
 273                                 dev->capabilities))
 274                return -EINVAL;
 275
 276        /* Source image size has to be provided by userspace. */
 277        if (pix_fmt->sizeimage == 0)
 278                return -EINVAL;
 279
 280        cedrus_prepare_format(pix_fmt);
 281
 282        return 0;
 283}
 284
 285static int cedrus_s_fmt_vid_cap(struct file *file, void *priv,
 286                                struct v4l2_format *f)
 287{
 288        struct cedrus_ctx *ctx = cedrus_file2ctx(file);
 289        struct cedrus_dev *dev = ctx->dev;
 290        struct vb2_queue *vq;
 291        int ret;
 292
 293        vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
 294        if (vb2_is_busy(vq))
 295                return -EBUSY;
 296
 297        ret = cedrus_try_fmt_vid_cap(file, priv, f);
 298        if (ret)
 299                return ret;
 300
 301        ctx->dst_fmt = f->fmt.pix;
 302
 303        cedrus_dst_format_set(dev, &ctx->dst_fmt);
 304
 305        return 0;
 306}
 307
 308static int cedrus_s_fmt_vid_out(struct file *file, void *priv,
 309                                struct v4l2_format *f)
 310{
 311        struct cedrus_ctx *ctx = cedrus_file2ctx(file);
 312        struct vb2_queue *vq;
 313        int ret;
 314
 315        vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
 316        if (vb2_is_busy(vq))
 317                return -EBUSY;
 318
 319        ret = cedrus_try_fmt_vid_out(file, priv, f);
 320        if (ret)
 321                return ret;
 322
 323        ctx->src_fmt = f->fmt.pix;
 324
 325        /* Propagate colorspace information to capture. */
 326        ctx->dst_fmt.colorspace = f->fmt.pix.colorspace;
 327        ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func;
 328        ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
 329        ctx->dst_fmt.quantization = f->fmt.pix.quantization;
 330
 331        return 0;
 332}
 333
 334const struct v4l2_ioctl_ops cedrus_ioctl_ops = {
 335        .vidioc_querycap                = cedrus_querycap,
 336
 337        .vidioc_enum_fmt_vid_cap        = cedrus_enum_fmt_vid_cap,
 338        .vidioc_g_fmt_vid_cap           = cedrus_g_fmt_vid_cap,
 339        .vidioc_try_fmt_vid_cap         = cedrus_try_fmt_vid_cap,
 340        .vidioc_s_fmt_vid_cap           = cedrus_s_fmt_vid_cap,
 341
 342        .vidioc_enum_fmt_vid_out        = cedrus_enum_fmt_vid_out,
 343        .vidioc_g_fmt_vid_out           = cedrus_g_fmt_vid_out,
 344        .vidioc_try_fmt_vid_out         = cedrus_try_fmt_vid_out,
 345        .vidioc_s_fmt_vid_out           = cedrus_s_fmt_vid_out,
 346
 347        .vidioc_reqbufs                 = v4l2_m2m_ioctl_reqbufs,
 348        .vidioc_querybuf                = v4l2_m2m_ioctl_querybuf,
 349        .vidioc_qbuf                    = v4l2_m2m_ioctl_qbuf,
 350        .vidioc_dqbuf                   = v4l2_m2m_ioctl_dqbuf,
 351        .vidioc_prepare_buf             = v4l2_m2m_ioctl_prepare_buf,
 352        .vidioc_create_bufs             = v4l2_m2m_ioctl_create_bufs,
 353        .vidioc_expbuf                  = v4l2_m2m_ioctl_expbuf,
 354
 355        .vidioc_streamon                = v4l2_m2m_ioctl_streamon,
 356        .vidioc_streamoff               = v4l2_m2m_ioctl_streamoff,
 357
 358        .vidioc_subscribe_event         = v4l2_ctrl_subscribe_event,
 359        .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
 360};
 361
 362static int cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
 363                              unsigned int *nplanes, unsigned int sizes[],
 364                              struct device *alloc_devs[])
 365{
 366        struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
 367        struct cedrus_dev *dev = ctx->dev;
 368        struct v4l2_pix_format *pix_fmt;
 369        u32 directions;
 370
 371        if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
 372                directions = CEDRUS_DECODE_SRC;
 373                pix_fmt = &ctx->src_fmt;
 374        } else {
 375                directions = CEDRUS_DECODE_DST;
 376                pix_fmt = &ctx->dst_fmt;
 377        }
 378
 379        if (!cedrus_check_format(pix_fmt->pixelformat, directions,
 380                                 dev->capabilities))
 381                return -EINVAL;
 382
 383        if (*nplanes) {
 384                if (sizes[0] < pix_fmt->sizeimage)
 385                        return -EINVAL;
 386        } else {
 387                sizes[0] = pix_fmt->sizeimage;
 388                *nplanes = 1;
 389        }
 390
 391        return 0;
 392}
 393
 394static void cedrus_queue_cleanup(struct vb2_queue *vq, u32 state)
 395{
 396        struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
 397        struct vb2_v4l2_buffer *vbuf;
 398
 399        for (;;) {
 400                if (V4L2_TYPE_IS_OUTPUT(vq->type))
 401                        vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
 402                else
 403                        vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
 404
 405                if (!vbuf)
 406                        return;
 407
 408                v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
 409                                           &ctx->hdl);
 410                v4l2_m2m_buf_done(vbuf, state);
 411        }
 412}
 413
 414static int cedrus_buf_out_validate(struct vb2_buffer *vb)
 415{
 416        struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
 417
 418        vbuf->field = V4L2_FIELD_NONE;
 419        return 0;
 420}
 421
 422static int cedrus_buf_prepare(struct vb2_buffer *vb)
 423{
 424        struct vb2_queue *vq = vb->vb2_queue;
 425        struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
 426        struct v4l2_pix_format *pix_fmt;
 427
 428        if (V4L2_TYPE_IS_OUTPUT(vq->type))
 429                pix_fmt = &ctx->src_fmt;
 430        else
 431                pix_fmt = &ctx->dst_fmt;
 432
 433        if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage)
 434                return -EINVAL;
 435
 436        vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage);
 437
 438        return 0;
 439}
 440
 441static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count)
 442{
 443        struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
 444        struct cedrus_dev *dev = ctx->dev;
 445        int ret = 0;
 446
 447        switch (ctx->src_fmt.pixelformat) {
 448        case V4L2_PIX_FMT_MPEG2_SLICE:
 449                ctx->current_codec = CEDRUS_CODEC_MPEG2;
 450                break;
 451
 452        case V4L2_PIX_FMT_H264_SLICE:
 453                ctx->current_codec = CEDRUS_CODEC_H264;
 454                break;
 455
 456        default:
 457                return -EINVAL;
 458        }
 459
 460        if (V4L2_TYPE_IS_OUTPUT(vq->type) &&
 461            dev->dec_ops[ctx->current_codec]->start)
 462                ret = dev->dec_ops[ctx->current_codec]->start(ctx);
 463
 464        if (ret)
 465                cedrus_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
 466
 467        return ret;
 468}
 469
 470static void cedrus_stop_streaming(struct vb2_queue *vq)
 471{
 472        struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
 473        struct cedrus_dev *dev = ctx->dev;
 474
 475        if (V4L2_TYPE_IS_OUTPUT(vq->type) &&
 476            dev->dec_ops[ctx->current_codec]->stop)
 477                dev->dec_ops[ctx->current_codec]->stop(ctx);
 478
 479        cedrus_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
 480}
 481
 482static void cedrus_buf_queue(struct vb2_buffer *vb)
 483{
 484        struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
 485        struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
 486
 487        v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
 488}
 489
 490static void cedrus_buf_request_complete(struct vb2_buffer *vb)
 491{
 492        struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
 493
 494        v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
 495}
 496
 497static struct vb2_ops cedrus_qops = {
 498        .queue_setup            = cedrus_queue_setup,
 499        .buf_prepare            = cedrus_buf_prepare,
 500        .buf_queue              = cedrus_buf_queue,
 501        .buf_out_validate       = cedrus_buf_out_validate,
 502        .buf_request_complete   = cedrus_buf_request_complete,
 503        .start_streaming        = cedrus_start_streaming,
 504        .stop_streaming         = cedrus_stop_streaming,
 505        .wait_prepare           = vb2_ops_wait_prepare,
 506        .wait_finish            = vb2_ops_wait_finish,
 507};
 508
 509int cedrus_queue_init(void *priv, struct vb2_queue *src_vq,
 510                      struct vb2_queue *dst_vq)
 511{
 512        struct cedrus_ctx *ctx = priv;
 513        int ret;
 514
 515        src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
 516        src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
 517        src_vq->drv_priv = ctx;
 518        src_vq->buf_struct_size = sizeof(struct cedrus_buffer);
 519        src_vq->min_buffers_needed = 1;
 520        src_vq->ops = &cedrus_qops;
 521        src_vq->mem_ops = &vb2_dma_contig_memops;
 522        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
 523        src_vq->lock = &ctx->dev->dev_mutex;
 524        src_vq->dev = ctx->dev->dev;
 525        src_vq->supports_requests = true;
 526        src_vq->requires_requests = true;
 527
 528        ret = vb2_queue_init(src_vq);
 529        if (ret)
 530                return ret;
 531
 532        dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 533        dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
 534        dst_vq->drv_priv = ctx;
 535        dst_vq->buf_struct_size = sizeof(struct cedrus_buffer);
 536        dst_vq->min_buffers_needed = 1;
 537        dst_vq->ops = &cedrus_qops;
 538        dst_vq->mem_ops = &vb2_dma_contig_memops;
 539        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
 540        dst_vq->lock = &ctx->dev->dev_mutex;
 541        dst_vq->dev = ctx->dev->dev;
 542
 543        return vb2_queue_init(dst_vq);
 544}
 545