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