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