150e76151SPaul Kocialkowski // SPDX-License-Identifier: GPL-2.0
250e76151SPaul Kocialkowski /*
350e76151SPaul Kocialkowski * Cedrus VPU driver
450e76151SPaul Kocialkowski *
550e76151SPaul Kocialkowski * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
650e76151SPaul Kocialkowski * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
750e76151SPaul Kocialkowski * Copyright (C) 2018 Bootlin
850e76151SPaul Kocialkowski *
950e76151SPaul Kocialkowski * Based on the vim2m driver, that is:
1050e76151SPaul Kocialkowski *
1150e76151SPaul Kocialkowski * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
1250e76151SPaul Kocialkowski * Pawel Osciak, <pawel@osciak.com>
1350e76151SPaul Kocialkowski * Marek Szyprowski, <m.szyprowski@samsung.com>
1450e76151SPaul Kocialkowski */
1550e76151SPaul Kocialkowski
16d5aecd28SJernej Skrabec #include <linux/pm_runtime.h>
17d5aecd28SJernej Skrabec
1850e76151SPaul Kocialkowski #include <media/videobuf2-dma-contig.h>
1950e76151SPaul Kocialkowski #include <media/v4l2-device.h>
2050e76151SPaul Kocialkowski #include <media/v4l2-ioctl.h>
2150e76151SPaul Kocialkowski #include <media/v4l2-event.h>
2250e76151SPaul Kocialkowski #include <media/v4l2-mem2mem.h>
2350e76151SPaul Kocialkowski
2450e76151SPaul Kocialkowski #include "cedrus.h"
2550e76151SPaul Kocialkowski #include "cedrus_video.h"
2650e76151SPaul Kocialkowski #include "cedrus_dec.h"
2750e76151SPaul Kocialkowski #include "cedrus_hw.h"
2850e76151SPaul Kocialkowski
2950e76151SPaul Kocialkowski #define CEDRUS_DECODE_SRC BIT(0)
3050e76151SPaul Kocialkowski #define CEDRUS_DECODE_DST BIT(1)
3150e76151SPaul Kocialkowski
3250e76151SPaul Kocialkowski #define CEDRUS_MIN_WIDTH 16U
3350e76151SPaul Kocialkowski #define CEDRUS_MIN_HEIGHT 16U
340b3e5c15SJernej Skrabec #define CEDRUS_MAX_WIDTH 4096U
350b3e5c15SJernej Skrabec #define CEDRUS_MAX_HEIGHT 2304U
3650e76151SPaul Kocialkowski
3750e76151SPaul Kocialkowski static struct cedrus_format cedrus_formats[] = {
3850e76151SPaul Kocialkowski {
3950e76151SPaul Kocialkowski .pixelformat = V4L2_PIX_FMT_MPEG2_SLICE,
4050e76151SPaul Kocialkowski .directions = CEDRUS_DECODE_SRC,
41503dab0bSMartin Cerveny .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC,
4250e76151SPaul Kocialkowski },
4350e76151SPaul Kocialkowski {
447bb3c32aSEzequiel Garcia .pixelformat = V4L2_PIX_FMT_H264_SLICE,
456eb9b758SMaxime Ripard .directions = CEDRUS_DECODE_SRC,
46503dab0bSMartin Cerveny .capabilities = CEDRUS_CAPABILITY_H264_DEC,
476eb9b758SMaxime Ripard },
486eb9b758SMaxime Ripard {
4986caab29SPaul Kocialkowski .pixelformat = V4L2_PIX_FMT_HEVC_SLICE,
5086caab29SPaul Kocialkowski .directions = CEDRUS_DECODE_SRC,
5186caab29SPaul Kocialkowski .capabilities = CEDRUS_CAPABILITY_H265_DEC,
5286caab29SPaul Kocialkowski },
5386caab29SPaul Kocialkowski {
543c39a16dSJernej Skrabec .pixelformat = V4L2_PIX_FMT_VP8_FRAME,
553c39a16dSJernej Skrabec .directions = CEDRUS_DECODE_SRC,
5668b4a01fSJernej Skrabec .capabilities = CEDRUS_CAPABILITY_VP8_DEC,
573c39a16dSJernej Skrabec },
583c39a16dSJernej Skrabec {
5950e76151SPaul Kocialkowski .pixelformat = V4L2_PIX_FMT_NV12,
6050e76151SPaul Kocialkowski .directions = CEDRUS_DECODE_DST,
6150e76151SPaul Kocialkowski .capabilities = CEDRUS_CAPABILITY_UNTILED,
6250e76151SPaul Kocialkowski },
63e240c003SJernej Skrabec {
64e240c003SJernej Skrabec .pixelformat = V4L2_PIX_FMT_NV12_32L32,
65e240c003SJernej Skrabec .directions = CEDRUS_DECODE_DST,
66e240c003SJernej Skrabec },
6750e76151SPaul Kocialkowski };
6850e76151SPaul Kocialkowski
6950e76151SPaul Kocialkowski #define CEDRUS_FORMATS_COUNT ARRAY_SIZE(cedrus_formats)
7050e76151SPaul Kocialkowski
cedrus_file2ctx(struct file * file)7150e76151SPaul Kocialkowski static inline struct cedrus_ctx *cedrus_file2ctx(struct file *file)
7250e76151SPaul Kocialkowski {
7350e76151SPaul Kocialkowski return container_of(file->private_data, struct cedrus_ctx, fh);
7450e76151SPaul Kocialkowski }
7550e76151SPaul Kocialkowski
cedrus_find_format(struct cedrus_ctx * ctx,u32 pixelformat,u32 directions)76e7efb377SJernej Skrabec static struct cedrus_format *cedrus_find_format(struct cedrus_ctx *ctx,
77e7efb377SJernej Skrabec u32 pixelformat, u32 directions)
7850e76151SPaul Kocialkowski {
79dec55525SHans Verkuil struct cedrus_format *first_valid_fmt = NULL;
8050e76151SPaul Kocialkowski struct cedrus_format *fmt;
8150e76151SPaul Kocialkowski unsigned int i;
8250e76151SPaul Kocialkowski
8350e76151SPaul Kocialkowski for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) {
8450e76151SPaul Kocialkowski fmt = &cedrus_formats[i];
8550e76151SPaul Kocialkowski
86e7efb377SJernej Skrabec if (!cedrus_is_capable(ctx, fmt->capabilities) ||
87dec55525SHans Verkuil !(fmt->directions & directions))
8850e76151SPaul Kocialkowski continue;
8950e76151SPaul Kocialkowski
90dec55525SHans Verkuil if (fmt->pixelformat == pixelformat)
9150e76151SPaul Kocialkowski break;
92dec55525SHans Verkuil
93dec55525SHans Verkuil if (!first_valid_fmt)
94dec55525SHans Verkuil first_valid_fmt = fmt;
9550e76151SPaul Kocialkowski }
9650e76151SPaul Kocialkowski
9750e76151SPaul Kocialkowski if (i == CEDRUS_FORMATS_COUNT)
98dec55525SHans Verkuil return first_valid_fmt;
9950e76151SPaul Kocialkowski
10050e76151SPaul Kocialkowski return &cedrus_formats[i];
10150e76151SPaul Kocialkowski }
10250e76151SPaul Kocialkowski
cedrus_prepare_format(struct v4l2_pix_format * pix_fmt)103965c71e8SHans Verkuil void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt)
10450e76151SPaul Kocialkowski {
10550e76151SPaul Kocialkowski unsigned int width = pix_fmt->width;
10650e76151SPaul Kocialkowski unsigned int height = pix_fmt->height;
10750e76151SPaul Kocialkowski unsigned int sizeimage = pix_fmt->sizeimage;
10850e76151SPaul Kocialkowski unsigned int bytesperline = pix_fmt->bytesperline;
10950e76151SPaul Kocialkowski
11050e76151SPaul Kocialkowski pix_fmt->field = V4L2_FIELD_NONE;
11150e76151SPaul Kocialkowski
11250e76151SPaul Kocialkowski /* Limit to hardware min/max. */
11350e76151SPaul Kocialkowski width = clamp(width, CEDRUS_MIN_WIDTH, CEDRUS_MAX_WIDTH);
11450e76151SPaul Kocialkowski height = clamp(height, CEDRUS_MIN_HEIGHT, CEDRUS_MAX_HEIGHT);
11550e76151SPaul Kocialkowski
11650e76151SPaul Kocialkowski switch (pix_fmt->pixelformat) {
11750e76151SPaul Kocialkowski case V4L2_PIX_FMT_MPEG2_SLICE:
1187bb3c32aSEzequiel Garcia case V4L2_PIX_FMT_H264_SLICE:
11986caab29SPaul Kocialkowski case V4L2_PIX_FMT_HEVC_SLICE:
1203c39a16dSJernej Skrabec case V4L2_PIX_FMT_VP8_FRAME:
12150e76151SPaul Kocialkowski /* Zero bytes per line for encoded source. */
12250e76151SPaul Kocialkowski bytesperline = 0;
123965c71e8SHans Verkuil /* Choose some minimum size since this can't be 0 */
124965c71e8SHans Verkuil sizeimage = max_t(u32, SZ_1K, sizeimage);
12550e76151SPaul Kocialkowski break;
12650e76151SPaul Kocialkowski
127b84f60a3SEzequiel Garcia case V4L2_PIX_FMT_NV12_32L32:
12850e76151SPaul Kocialkowski /* 32-aligned stride. */
12950e76151SPaul Kocialkowski bytesperline = ALIGN(width, 32);
13050e76151SPaul Kocialkowski
13150e76151SPaul Kocialkowski /* 32-aligned height. */
13250e76151SPaul Kocialkowski height = ALIGN(height, 32);
13350e76151SPaul Kocialkowski
13450e76151SPaul Kocialkowski /* Luma plane size. */
13550e76151SPaul Kocialkowski sizeimage = bytesperline * height;
13650e76151SPaul Kocialkowski
13750e76151SPaul Kocialkowski /* Chroma plane size. */
138448ea5eeSNicolas Dufresne sizeimage += bytesperline * ALIGN(height, 64) / 2;
13950e76151SPaul Kocialkowski
14050e76151SPaul Kocialkowski break;
14150e76151SPaul Kocialkowski
14250e76151SPaul Kocialkowski case V4L2_PIX_FMT_NV12:
14350e76151SPaul Kocialkowski /* 16-aligned stride. */
14450e76151SPaul Kocialkowski bytesperline = ALIGN(width, 16);
14550e76151SPaul Kocialkowski
14650e76151SPaul Kocialkowski /* 16-aligned height. */
14750e76151SPaul Kocialkowski height = ALIGN(height, 16);
14850e76151SPaul Kocialkowski
14950e76151SPaul Kocialkowski /* Luma plane size. */
15050e76151SPaul Kocialkowski sizeimage = bytesperline * height;
15150e76151SPaul Kocialkowski
15250e76151SPaul Kocialkowski /* Chroma plane size. */
15350e76151SPaul Kocialkowski sizeimage += bytesperline * height / 2;
15450e76151SPaul Kocialkowski
15550e76151SPaul Kocialkowski break;
15650e76151SPaul Kocialkowski }
15750e76151SPaul Kocialkowski
15850e76151SPaul Kocialkowski pix_fmt->width = width;
15950e76151SPaul Kocialkowski pix_fmt->height = height;
16050e76151SPaul Kocialkowski
16150e76151SPaul Kocialkowski pix_fmt->bytesperline = bytesperline;
16250e76151SPaul Kocialkowski pix_fmt->sizeimage = sizeimage;
16350e76151SPaul Kocialkowski }
16450e76151SPaul Kocialkowski
cedrus_querycap(struct file * file,void * priv,struct v4l2_capability * cap)16550e76151SPaul Kocialkowski static int cedrus_querycap(struct file *file, void *priv,
16650e76151SPaul Kocialkowski struct v4l2_capability *cap)
16750e76151SPaul Kocialkowski {
16850e76151SPaul Kocialkowski strscpy(cap->driver, CEDRUS_NAME, sizeof(cap->driver));
16950e76151SPaul Kocialkowski strscpy(cap->card, CEDRUS_NAME, sizeof(cap->card));
17050e76151SPaul Kocialkowski snprintf(cap->bus_info, sizeof(cap->bus_info),
17150e76151SPaul Kocialkowski "platform:%s", CEDRUS_NAME);
17250e76151SPaul Kocialkowski
17350e76151SPaul Kocialkowski return 0;
17450e76151SPaul Kocialkowski }
17550e76151SPaul Kocialkowski
cedrus_enum_fmt(struct file * file,struct v4l2_fmtdesc * f,u32 direction)17650e76151SPaul Kocialkowski static int cedrus_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
17750e76151SPaul Kocialkowski u32 direction)
17850e76151SPaul Kocialkowski {
17950e76151SPaul Kocialkowski struct cedrus_ctx *ctx = cedrus_file2ctx(file);
18050e76151SPaul Kocialkowski unsigned int i, index;
18150e76151SPaul Kocialkowski
18250e76151SPaul Kocialkowski /* Index among formats that match the requested direction. */
18350e76151SPaul Kocialkowski index = 0;
18450e76151SPaul Kocialkowski
18550e76151SPaul Kocialkowski for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) {
186e7efb377SJernej Skrabec if (!cedrus_is_capable(ctx, cedrus_formats[i].capabilities))
18750e76151SPaul Kocialkowski continue;
18850e76151SPaul Kocialkowski
18950e76151SPaul Kocialkowski if (!(cedrus_formats[i].directions & direction))
19050e76151SPaul Kocialkowski continue;
19150e76151SPaul Kocialkowski
19250e76151SPaul Kocialkowski if (index == f->index)
19350e76151SPaul Kocialkowski break;
19450e76151SPaul Kocialkowski
19550e76151SPaul Kocialkowski index++;
19650e76151SPaul Kocialkowski }
19750e76151SPaul Kocialkowski
19850e76151SPaul Kocialkowski /* Matched format. */
19950e76151SPaul Kocialkowski if (i < CEDRUS_FORMATS_COUNT) {
20050e76151SPaul Kocialkowski f->pixelformat = cedrus_formats[i].pixelformat;
20150e76151SPaul Kocialkowski
20250e76151SPaul Kocialkowski return 0;
20350e76151SPaul Kocialkowski }
20450e76151SPaul Kocialkowski
20550e76151SPaul Kocialkowski return -EINVAL;
20650e76151SPaul Kocialkowski }
20750e76151SPaul Kocialkowski
cedrus_enum_fmt_vid_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)20850e76151SPaul Kocialkowski static int cedrus_enum_fmt_vid_cap(struct file *file, void *priv,
20950e76151SPaul Kocialkowski struct v4l2_fmtdesc *f)
21050e76151SPaul Kocialkowski {
21150e76151SPaul Kocialkowski return cedrus_enum_fmt(file, f, CEDRUS_DECODE_DST);
21250e76151SPaul Kocialkowski }
21350e76151SPaul Kocialkowski
cedrus_enum_fmt_vid_out(struct file * file,void * priv,struct v4l2_fmtdesc * f)21450e76151SPaul Kocialkowski static int cedrus_enum_fmt_vid_out(struct file *file, void *priv,
21550e76151SPaul Kocialkowski struct v4l2_fmtdesc *f)
21650e76151SPaul Kocialkowski {
21750e76151SPaul Kocialkowski return cedrus_enum_fmt(file, f, CEDRUS_DECODE_SRC);
21850e76151SPaul Kocialkowski }
21950e76151SPaul Kocialkowski
cedrus_g_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)22050e76151SPaul Kocialkowski static int cedrus_g_fmt_vid_cap(struct file *file, void *priv,
22150e76151SPaul Kocialkowski struct v4l2_format *f)
22250e76151SPaul Kocialkowski {
22350e76151SPaul Kocialkowski struct cedrus_ctx *ctx = cedrus_file2ctx(file);
22450e76151SPaul Kocialkowski
22550e76151SPaul Kocialkowski f->fmt.pix = ctx->dst_fmt;
22650e76151SPaul Kocialkowski return 0;
22750e76151SPaul Kocialkowski }
22850e76151SPaul Kocialkowski
cedrus_g_fmt_vid_out(struct file * file,void * priv,struct v4l2_format * f)22950e76151SPaul Kocialkowski static int cedrus_g_fmt_vid_out(struct file *file, void *priv,
23050e76151SPaul Kocialkowski struct v4l2_format *f)
23150e76151SPaul Kocialkowski {
23250e76151SPaul Kocialkowski struct cedrus_ctx *ctx = cedrus_file2ctx(file);
23350e76151SPaul Kocialkowski
23450e76151SPaul Kocialkowski f->fmt.pix = ctx->src_fmt;
23550e76151SPaul Kocialkowski return 0;
23650e76151SPaul Kocialkowski }
23750e76151SPaul Kocialkowski
cedrus_try_fmt_vid_cap_p(struct cedrus_ctx * ctx,struct v4l2_pix_format * pix_fmt)238b13ffeafSJernej Skrabec static int cedrus_try_fmt_vid_cap_p(struct cedrus_ctx *ctx,
239b13ffeafSJernej Skrabec struct v4l2_pix_format *pix_fmt)
24050e76151SPaul Kocialkowski {
241dec55525SHans Verkuil struct cedrus_format *fmt =
242e7efb377SJernej Skrabec cedrus_find_format(ctx, pix_fmt->pixelformat,
243e7efb377SJernej Skrabec CEDRUS_DECODE_DST);
24450e76151SPaul Kocialkowski
245dec55525SHans Verkuil if (!fmt)
24650e76151SPaul Kocialkowski return -EINVAL;
24750e76151SPaul Kocialkowski
248dec55525SHans Verkuil pix_fmt->pixelformat = fmt->pixelformat;
2498c608272SNicolas Dufresne pix_fmt->width = ctx->src_fmt.width;
2508c608272SNicolas Dufresne pix_fmt->height = ctx->src_fmt.height;
25150e76151SPaul Kocialkowski cedrus_prepare_format(pix_fmt);
25250e76151SPaul Kocialkowski
2533d273e81SJernej Skrabec if (ctx->current_codec->extra_cap_size)
2543d273e81SJernej Skrabec pix_fmt->sizeimage +=
2553d273e81SJernej Skrabec ctx->current_codec->extra_cap_size(ctx, pix_fmt);
2563d273e81SJernej Skrabec
25750e76151SPaul Kocialkowski return 0;
25850e76151SPaul Kocialkowski }
25950e76151SPaul Kocialkowski
cedrus_try_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)260b13ffeafSJernej Skrabec static int cedrus_try_fmt_vid_cap(struct file *file, void *priv,
26150e76151SPaul Kocialkowski struct v4l2_format *f)
26250e76151SPaul Kocialkowski {
263b13ffeafSJernej Skrabec return cedrus_try_fmt_vid_cap_p(cedrus_file2ctx(file), &f->fmt.pix);
264b13ffeafSJernej Skrabec }
265b13ffeafSJernej Skrabec
cedrus_try_fmt_vid_out_p(struct cedrus_ctx * ctx,struct v4l2_pix_format * pix_fmt)266b13ffeafSJernej Skrabec static int cedrus_try_fmt_vid_out_p(struct cedrus_ctx *ctx,
267b13ffeafSJernej Skrabec struct v4l2_pix_format *pix_fmt)
268b13ffeafSJernej Skrabec {
269dec55525SHans Verkuil struct cedrus_format *fmt =
270e7efb377SJernej Skrabec cedrus_find_format(ctx, pix_fmt->pixelformat,
271e7efb377SJernej Skrabec CEDRUS_DECODE_SRC);
27250e76151SPaul Kocialkowski
273dec55525SHans Verkuil if (!fmt)
27450e76151SPaul Kocialkowski return -EINVAL;
27550e76151SPaul Kocialkowski
276dec55525SHans Verkuil pix_fmt->pixelformat = fmt->pixelformat;
27750e76151SPaul Kocialkowski cedrus_prepare_format(pix_fmt);
27850e76151SPaul Kocialkowski
27950e76151SPaul Kocialkowski return 0;
28050e76151SPaul Kocialkowski }
28150e76151SPaul Kocialkowski
cedrus_try_fmt_vid_out(struct file * file,void * priv,struct v4l2_format * f)282b13ffeafSJernej Skrabec static int cedrus_try_fmt_vid_out(struct file *file, void *priv,
283b13ffeafSJernej Skrabec struct v4l2_format *f)
284b13ffeafSJernej Skrabec {
285b13ffeafSJernej Skrabec return cedrus_try_fmt_vid_out_p(cedrus_file2ctx(file), &f->fmt.pix);
286b13ffeafSJernej Skrabec }
287b13ffeafSJernej Skrabec
cedrus_s_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)28850e76151SPaul Kocialkowski static int cedrus_s_fmt_vid_cap(struct file *file, void *priv,
28950e76151SPaul Kocialkowski struct v4l2_format *f)
29050e76151SPaul Kocialkowski {
29150e76151SPaul Kocialkowski struct cedrus_ctx *ctx = cedrus_file2ctx(file);
2929dacde5eSPaul Kocialkowski struct vb2_queue *vq;
29350e76151SPaul Kocialkowski int ret;
29450e76151SPaul Kocialkowski
2959dacde5eSPaul Kocialkowski vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
2969dacde5eSPaul Kocialkowski if (vb2_is_busy(vq))
2979dacde5eSPaul Kocialkowski return -EBUSY;
2989dacde5eSPaul Kocialkowski
29950e76151SPaul Kocialkowski ret = cedrus_try_fmt_vid_cap(file, priv, f);
30050e76151SPaul Kocialkowski if (ret)
30150e76151SPaul Kocialkowski return ret;
30250e76151SPaul Kocialkowski
30350e76151SPaul Kocialkowski ctx->dst_fmt = f->fmt.pix;
30450e76151SPaul Kocialkowski
30550e76151SPaul Kocialkowski return 0;
30650e76151SPaul Kocialkowski }
30750e76151SPaul Kocialkowski
cedrus_reset_cap_format(struct cedrus_ctx * ctx)308b13ffeafSJernej Skrabec void cedrus_reset_cap_format(struct cedrus_ctx *ctx)
309b13ffeafSJernej Skrabec {
310b13ffeafSJernej Skrabec ctx->dst_fmt.pixelformat = 0;
311b13ffeafSJernej Skrabec cedrus_try_fmt_vid_cap_p(ctx, &ctx->dst_fmt);
312b13ffeafSJernej Skrabec }
313b13ffeafSJernej Skrabec
cedrus_s_fmt_vid_out_p(struct cedrus_ctx * ctx,struct v4l2_pix_format * pix_fmt)314b13ffeafSJernej Skrabec static int cedrus_s_fmt_vid_out_p(struct cedrus_ctx *ctx,
315b13ffeafSJernej Skrabec struct v4l2_pix_format *pix_fmt)
316b13ffeafSJernej Skrabec {
317b13ffeafSJernej Skrabec struct vb2_queue *vq;
318b13ffeafSJernej Skrabec int ret;
319b13ffeafSJernej Skrabec
320b13ffeafSJernej Skrabec ret = cedrus_try_fmt_vid_out_p(ctx, pix_fmt);
321b13ffeafSJernej Skrabec if (ret)
322b13ffeafSJernej Skrabec return ret;
323b13ffeafSJernej Skrabec
324b13ffeafSJernej Skrabec ctx->src_fmt = *pix_fmt;
325b13ffeafSJernej Skrabec
326b13ffeafSJernej Skrabec vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
327b13ffeafSJernej Skrabec
328b13ffeafSJernej Skrabec switch (ctx->src_fmt.pixelformat) {
329b13ffeafSJernej Skrabec case V4L2_PIX_FMT_H264_SLICE:
330b13ffeafSJernej Skrabec case V4L2_PIX_FMT_HEVC_SLICE:
331b13ffeafSJernej Skrabec vq->subsystem_flags |=
332b13ffeafSJernej Skrabec VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
333b13ffeafSJernej Skrabec break;
334b13ffeafSJernej Skrabec default:
335b13ffeafSJernej Skrabec vq->subsystem_flags &=
336b13ffeafSJernej Skrabec ~VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
337b13ffeafSJernej Skrabec break;
338b13ffeafSJernej Skrabec }
339b13ffeafSJernej Skrabec
3404e161728SJernej Skrabec switch (ctx->src_fmt.pixelformat) {
3414e161728SJernej Skrabec case V4L2_PIX_FMT_MPEG2_SLICE:
3424e161728SJernej Skrabec ctx->current_codec = &cedrus_dec_ops_mpeg2;
3434e161728SJernej Skrabec break;
3444e161728SJernej Skrabec case V4L2_PIX_FMT_H264_SLICE:
3454e161728SJernej Skrabec ctx->current_codec = &cedrus_dec_ops_h264;
3464e161728SJernej Skrabec break;
3474e161728SJernej Skrabec case V4L2_PIX_FMT_HEVC_SLICE:
3484e161728SJernej Skrabec ctx->current_codec = &cedrus_dec_ops_h265;
3494e161728SJernej Skrabec break;
3504e161728SJernej Skrabec case V4L2_PIX_FMT_VP8_FRAME:
3514e161728SJernej Skrabec ctx->current_codec = &cedrus_dec_ops_vp8;
3524e161728SJernej Skrabec break;
3534e161728SJernej Skrabec }
3544e161728SJernej Skrabec
355b13ffeafSJernej Skrabec /* Propagate format information to capture. */
356b13ffeafSJernej Skrabec ctx->dst_fmt.colorspace = pix_fmt->colorspace;
357b13ffeafSJernej Skrabec ctx->dst_fmt.xfer_func = pix_fmt->xfer_func;
358b13ffeafSJernej Skrabec ctx->dst_fmt.ycbcr_enc = pix_fmt->ycbcr_enc;
359b13ffeafSJernej Skrabec ctx->dst_fmt.quantization = pix_fmt->quantization;
360b13ffeafSJernej Skrabec cedrus_reset_cap_format(ctx);
361b13ffeafSJernej Skrabec
362b13ffeafSJernej Skrabec return 0;
363b13ffeafSJernej Skrabec }
364b13ffeafSJernej Skrabec
cedrus_reset_out_format(struct cedrus_ctx * ctx)365b13ffeafSJernej Skrabec void cedrus_reset_out_format(struct cedrus_ctx *ctx)
366b13ffeafSJernej Skrabec {
367b13ffeafSJernej Skrabec ctx->src_fmt.pixelformat = 0;
368b13ffeafSJernej Skrabec cedrus_s_fmt_vid_out_p(ctx, &ctx->src_fmt);
369b13ffeafSJernej Skrabec }
370b13ffeafSJernej Skrabec
cedrus_s_fmt_vid_out(struct file * file,void * priv,struct v4l2_format * f)37150e76151SPaul Kocialkowski static int cedrus_s_fmt_vid_out(struct file *file, void *priv,
37250e76151SPaul Kocialkowski struct v4l2_format *f)
37350e76151SPaul Kocialkowski {
37450e76151SPaul Kocialkowski struct cedrus_ctx *ctx = cedrus_file2ctx(file);
3759dacde5eSPaul Kocialkowski struct vb2_queue *vq;
3768c608272SNicolas Dufresne struct vb2_queue *peer_vq;
37750e76151SPaul Kocialkowski
3789dacde5eSPaul Kocialkowski vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
3798c608272SNicolas Dufresne /*
3808c608272SNicolas Dufresne * In order to support dynamic resolution change,
3818c608272SNicolas Dufresne * the decoder admits a resolution change, as long
3828c608272SNicolas Dufresne * as the pixelformat remains. Can't be done if streaming.
3838c608272SNicolas Dufresne */
3848c608272SNicolas Dufresne if (vb2_is_streaming(vq) || (vb2_is_busy(vq) &&
3858c608272SNicolas Dufresne f->fmt.pix.pixelformat != ctx->src_fmt.pixelformat))
3868c608272SNicolas Dufresne return -EBUSY;
3878c608272SNicolas Dufresne /*
3888c608272SNicolas Dufresne * Since format change on the OUTPUT queue will reset
3898c608272SNicolas Dufresne * the CAPTURE queue, we can't allow doing so
3908c608272SNicolas Dufresne * when the CAPTURE queue has buffers allocated.
3918c608272SNicolas Dufresne */
3928c608272SNicolas Dufresne peer_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
3938c608272SNicolas Dufresne V4L2_BUF_TYPE_VIDEO_CAPTURE);
3948c608272SNicolas Dufresne if (vb2_is_busy(peer_vq))
3959dacde5eSPaul Kocialkowski return -EBUSY;
3969dacde5eSPaul Kocialkowski
397b13ffeafSJernej Skrabec return cedrus_s_fmt_vid_out_p(cedrus_file2ctx(file), &f->fmt.pix);
39850e76151SPaul Kocialkowski }
39950e76151SPaul Kocialkowski
40050e76151SPaul Kocialkowski const struct v4l2_ioctl_ops cedrus_ioctl_ops = {
40150e76151SPaul Kocialkowski .vidioc_querycap = cedrus_querycap,
40250e76151SPaul Kocialkowski
40350e76151SPaul Kocialkowski .vidioc_enum_fmt_vid_cap = cedrus_enum_fmt_vid_cap,
40450e76151SPaul Kocialkowski .vidioc_g_fmt_vid_cap = cedrus_g_fmt_vid_cap,
40550e76151SPaul Kocialkowski .vidioc_try_fmt_vid_cap = cedrus_try_fmt_vid_cap,
40650e76151SPaul Kocialkowski .vidioc_s_fmt_vid_cap = cedrus_s_fmt_vid_cap,
40750e76151SPaul Kocialkowski
40850e76151SPaul Kocialkowski .vidioc_enum_fmt_vid_out = cedrus_enum_fmt_vid_out,
40950e76151SPaul Kocialkowski .vidioc_g_fmt_vid_out = cedrus_g_fmt_vid_out,
41050e76151SPaul Kocialkowski .vidioc_try_fmt_vid_out = cedrus_try_fmt_vid_out,
41150e76151SPaul Kocialkowski .vidioc_s_fmt_vid_out = cedrus_s_fmt_vid_out,
41250e76151SPaul Kocialkowski
41350e76151SPaul Kocialkowski .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
41450e76151SPaul Kocialkowski .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
41550e76151SPaul Kocialkowski .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
41650e76151SPaul Kocialkowski .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
41750e76151SPaul Kocialkowski .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
41850e76151SPaul Kocialkowski .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
41950e76151SPaul Kocialkowski .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
42050e76151SPaul Kocialkowski
42150e76151SPaul Kocialkowski .vidioc_streamon = v4l2_m2m_ioctl_streamon,
42250e76151SPaul Kocialkowski .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
42350e76151SPaul Kocialkowski
424eabf10e5SJernej Skrabec .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd,
425eabf10e5SJernej Skrabec .vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd,
426eabf10e5SJernej Skrabec
42750e76151SPaul Kocialkowski .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
42850e76151SPaul Kocialkowski .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
42950e76151SPaul Kocialkowski };
43050e76151SPaul Kocialkowski
cedrus_queue_setup(struct vb2_queue * vq,unsigned int * nbufs,unsigned int * nplanes,unsigned int sizes[],struct device * alloc_devs[])43150e76151SPaul Kocialkowski static int cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
43250e76151SPaul Kocialkowski unsigned int *nplanes, unsigned int sizes[],
43350e76151SPaul Kocialkowski struct device *alloc_devs[])
43450e76151SPaul Kocialkowski {
43550e76151SPaul Kocialkowski struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
43650e76151SPaul Kocialkowski struct v4l2_pix_format *pix_fmt;
43750e76151SPaul Kocialkowski
438dec55525SHans Verkuil if (V4L2_TYPE_IS_OUTPUT(vq->type))
43950e76151SPaul Kocialkowski pix_fmt = &ctx->src_fmt;
440dec55525SHans Verkuil else
44150e76151SPaul Kocialkowski pix_fmt = &ctx->dst_fmt;
44250e76151SPaul Kocialkowski
44350e76151SPaul Kocialkowski if (*nplanes) {
44450e76151SPaul Kocialkowski if (sizes[0] < pix_fmt->sizeimage)
44550e76151SPaul Kocialkowski return -EINVAL;
44650e76151SPaul Kocialkowski } else {
44750e76151SPaul Kocialkowski sizes[0] = pix_fmt->sizeimage;
44850e76151SPaul Kocialkowski *nplanes = 1;
44950e76151SPaul Kocialkowski }
45050e76151SPaul Kocialkowski
45150e76151SPaul Kocialkowski return 0;
45250e76151SPaul Kocialkowski }
45350e76151SPaul Kocialkowski
cedrus_queue_cleanup(struct vb2_queue * vq,u32 state)45450e76151SPaul Kocialkowski static void cedrus_queue_cleanup(struct vb2_queue *vq, u32 state)
45550e76151SPaul Kocialkowski {
45650e76151SPaul Kocialkowski struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
45750e76151SPaul Kocialkowski struct vb2_v4l2_buffer *vbuf;
45850e76151SPaul Kocialkowski
45950e76151SPaul Kocialkowski for (;;) {
46050e76151SPaul Kocialkowski if (V4L2_TYPE_IS_OUTPUT(vq->type))
46150e76151SPaul Kocialkowski vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
46250e76151SPaul Kocialkowski else
46350e76151SPaul Kocialkowski vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
46450e76151SPaul Kocialkowski
46550e76151SPaul Kocialkowski if (!vbuf)
46650e76151SPaul Kocialkowski return;
46750e76151SPaul Kocialkowski
46850e76151SPaul Kocialkowski v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
46950e76151SPaul Kocialkowski &ctx->hdl);
47050e76151SPaul Kocialkowski v4l2_m2m_buf_done(vbuf, state);
47150e76151SPaul Kocialkowski }
47250e76151SPaul Kocialkowski }
47350e76151SPaul Kocialkowski
cedrus_buf_out_validate(struct vb2_buffer * vb)4746b3e4c4cSHans Verkuil static int cedrus_buf_out_validate(struct vb2_buffer *vb)
4756b3e4c4cSHans Verkuil {
4766b3e4c4cSHans Verkuil struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
4776b3e4c4cSHans Verkuil
4786b3e4c4cSHans Verkuil vbuf->field = V4L2_FIELD_NONE;
4796b3e4c4cSHans Verkuil return 0;
4806b3e4c4cSHans Verkuil }
4816b3e4c4cSHans Verkuil
cedrus_buf_prepare(struct vb2_buffer * vb)48250e76151SPaul Kocialkowski static int cedrus_buf_prepare(struct vb2_buffer *vb)
48350e76151SPaul Kocialkowski {
48450e76151SPaul Kocialkowski struct vb2_queue *vq = vb->vb2_queue;
48550e76151SPaul Kocialkowski struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
48650e76151SPaul Kocialkowski struct v4l2_pix_format *pix_fmt;
48750e76151SPaul Kocialkowski
48850e76151SPaul Kocialkowski if (V4L2_TYPE_IS_OUTPUT(vq->type))
48950e76151SPaul Kocialkowski pix_fmt = &ctx->src_fmt;
49050e76151SPaul Kocialkowski else
49150e76151SPaul Kocialkowski pix_fmt = &ctx->dst_fmt;
49250e76151SPaul Kocialkowski
49350e76151SPaul Kocialkowski if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage)
49450e76151SPaul Kocialkowski return -EINVAL;
49550e76151SPaul Kocialkowski
496d84b9202SAndrzej Pietrasiewicz /*
497d84b9202SAndrzej Pietrasiewicz * Buffer's bytesused must be written by driver for CAPTURE buffers.
498d84b9202SAndrzej Pietrasiewicz * (for OUTPUT buffers, if userspace passes 0 bytesused, v4l2-core sets
499d84b9202SAndrzej Pietrasiewicz * it to buffer length).
500d84b9202SAndrzej Pietrasiewicz */
501d84b9202SAndrzej Pietrasiewicz if (V4L2_TYPE_IS_CAPTURE(vq->type))
50250e76151SPaul Kocialkowski vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage);
50350e76151SPaul Kocialkowski
50450e76151SPaul Kocialkowski return 0;
50550e76151SPaul Kocialkowski }
50650e76151SPaul Kocialkowski
cedrus_start_streaming(struct vb2_queue * vq,unsigned int count)50750e76151SPaul Kocialkowski static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count)
50850e76151SPaul Kocialkowski {
50950e76151SPaul Kocialkowski struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
51050e76151SPaul Kocialkowski struct cedrus_dev *dev = ctx->dev;
51150e76151SPaul Kocialkowski int ret = 0;
51250e76151SPaul Kocialkowski
513d5aecd28SJernej Skrabec if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
514e21e1e94SMauro Carvalho Chehab ret = pm_runtime_resume_and_get(dev->dev);
515e21e1e94SMauro Carvalho Chehab if (ret < 0)
516d5aecd28SJernej Skrabec goto err_cleanup;
51750e76151SPaul Kocialkowski
5184e161728SJernej Skrabec if (ctx->current_codec->start) {
5194e161728SJernej Skrabec ret = ctx->current_codec->start(ctx);
52050e76151SPaul Kocialkowski if (ret)
521d5aecd28SJernej Skrabec goto err_pm;
522d5aecd28SJernej Skrabec }
523d5aecd28SJernej Skrabec }
524d5aecd28SJernej Skrabec
525d5aecd28SJernej Skrabec return 0;
526d5aecd28SJernej Skrabec
527d5aecd28SJernej Skrabec err_pm:
528d5aecd28SJernej Skrabec pm_runtime_put(dev->dev);
529d5aecd28SJernej Skrabec err_cleanup:
53050e76151SPaul Kocialkowski cedrus_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
53150e76151SPaul Kocialkowski
53250e76151SPaul Kocialkowski return ret;
53350e76151SPaul Kocialkowski }
53450e76151SPaul Kocialkowski
cedrus_stop_streaming(struct vb2_queue * vq)53550e76151SPaul Kocialkowski static void cedrus_stop_streaming(struct vb2_queue *vq)
53650e76151SPaul Kocialkowski {
53750e76151SPaul Kocialkowski struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
53850e76151SPaul Kocialkowski struct cedrus_dev *dev = ctx->dev;
53950e76151SPaul Kocialkowski
540d5aecd28SJernej Skrabec if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
5414e161728SJernej Skrabec if (ctx->current_codec->stop)
5424e161728SJernej Skrabec ctx->current_codec->stop(ctx);
54350e76151SPaul Kocialkowski
544d5aecd28SJernej Skrabec pm_runtime_put(dev->dev);
545d5aecd28SJernej Skrabec }
546d5aecd28SJernej Skrabec
54750e76151SPaul Kocialkowski cedrus_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
54850e76151SPaul Kocialkowski }
54950e76151SPaul Kocialkowski
cedrus_buf_queue(struct vb2_buffer * vb)55050e76151SPaul Kocialkowski static void cedrus_buf_queue(struct vb2_buffer *vb)
55150e76151SPaul Kocialkowski {
55250e76151SPaul Kocialkowski struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
55350e76151SPaul Kocialkowski struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
55450e76151SPaul Kocialkowski
55550e76151SPaul Kocialkowski v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
55650e76151SPaul Kocialkowski }
55750e76151SPaul Kocialkowski
cedrus_buf_request_complete(struct vb2_buffer * vb)55850e76151SPaul Kocialkowski static void cedrus_buf_request_complete(struct vb2_buffer *vb)
55950e76151SPaul Kocialkowski {
56050e76151SPaul Kocialkowski struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
56150e76151SPaul Kocialkowski
56250e76151SPaul Kocialkowski v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
56350e76151SPaul Kocialkowski }
56450e76151SPaul Kocialkowski
565*b15a92f0SIan Cowan static const struct vb2_ops cedrus_qops = {
56650e76151SPaul Kocialkowski .queue_setup = cedrus_queue_setup,
56750e76151SPaul Kocialkowski .buf_prepare = cedrus_buf_prepare,
56850e76151SPaul Kocialkowski .buf_queue = cedrus_buf_queue,
5696b3e4c4cSHans Verkuil .buf_out_validate = cedrus_buf_out_validate,
57050e76151SPaul Kocialkowski .buf_request_complete = cedrus_buf_request_complete,
57150e76151SPaul Kocialkowski .start_streaming = cedrus_start_streaming,
57250e76151SPaul Kocialkowski .stop_streaming = cedrus_stop_streaming,
57350e76151SPaul Kocialkowski .wait_prepare = vb2_ops_wait_prepare,
57450e76151SPaul Kocialkowski .wait_finish = vb2_ops_wait_finish,
57550e76151SPaul Kocialkowski };
57650e76151SPaul Kocialkowski
cedrus_queue_init(void * priv,struct vb2_queue * src_vq,struct vb2_queue * dst_vq)57750e76151SPaul Kocialkowski int cedrus_queue_init(void *priv, struct vb2_queue *src_vq,
57850e76151SPaul Kocialkowski struct vb2_queue *dst_vq)
57950e76151SPaul Kocialkowski {
58050e76151SPaul Kocialkowski struct cedrus_ctx *ctx = priv;
58150e76151SPaul Kocialkowski int ret;
58250e76151SPaul Kocialkowski
58350e76151SPaul Kocialkowski src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
58450e76151SPaul Kocialkowski src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
58550e76151SPaul Kocialkowski src_vq->drv_priv = ctx;
58650e76151SPaul Kocialkowski src_vq->buf_struct_size = sizeof(struct cedrus_buffer);
58750e76151SPaul Kocialkowski src_vq->ops = &cedrus_qops;
58850e76151SPaul Kocialkowski src_vq->mem_ops = &vb2_dma_contig_memops;
58950e76151SPaul Kocialkowski src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
59050e76151SPaul Kocialkowski src_vq->lock = &ctx->dev->dev_mutex;
59150e76151SPaul Kocialkowski src_vq->dev = ctx->dev->dev;
59250e76151SPaul Kocialkowski src_vq->supports_requests = true;
593ca0d1bd4SHans Verkuil src_vq->requires_requests = true;
59450e76151SPaul Kocialkowski
59550e76151SPaul Kocialkowski ret = vb2_queue_init(src_vq);
59650e76151SPaul Kocialkowski if (ret)
59750e76151SPaul Kocialkowski return ret;
59850e76151SPaul Kocialkowski
59950e76151SPaul Kocialkowski dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
60050e76151SPaul Kocialkowski dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
60150e76151SPaul Kocialkowski dst_vq->drv_priv = ctx;
60250e76151SPaul Kocialkowski dst_vq->buf_struct_size = sizeof(struct cedrus_buffer);
60350e76151SPaul Kocialkowski dst_vq->ops = &cedrus_qops;
60450e76151SPaul Kocialkowski dst_vq->mem_ops = &vb2_dma_contig_memops;
60550e76151SPaul Kocialkowski dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
60650e76151SPaul Kocialkowski dst_vq->lock = &ctx->dev->dev_mutex;
60750e76151SPaul Kocialkowski dst_vq->dev = ctx->dev->dev;
60850e76151SPaul Kocialkowski
60950e76151SPaul Kocialkowski return vb2_queue_init(dst_vq);
61050e76151SPaul Kocialkowski }
611