162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for Renesas R-Car VIN
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016 Renesas Electronics Corp.
662306a36Sopenharmony_ci * Copyright (C) 2011-2013 Renesas Solutions Corp.
762306a36Sopenharmony_ci * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
862306a36Sopenharmony_ci * Copyright (C) 2008 Magnus Damm
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Based on the soc-camera rcar_vin driver
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <media/v4l2-event.h>
1662306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
1762306a36Sopenharmony_ci#include <media/v4l2-mc.h>
1862306a36Sopenharmony_ci#include <media/v4l2-rect.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "rcar-vin.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define RVIN_DEFAULT_FORMAT	V4L2_PIX_FMT_YUYV
2362306a36Sopenharmony_ci#define RVIN_DEFAULT_WIDTH	800
2462306a36Sopenharmony_ci#define RVIN_DEFAULT_HEIGHT	600
2562306a36Sopenharmony_ci#define RVIN_DEFAULT_FIELD	V4L2_FIELD_NONE
2662306a36Sopenharmony_ci#define RVIN_DEFAULT_COLORSPACE	V4L2_COLORSPACE_SRGB
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
2962306a36Sopenharmony_ci * Format Conversions
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic const struct rvin_video_format rvin_formats[] = {
3362306a36Sopenharmony_ci	{
3462306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_NV12,
3562306a36Sopenharmony_ci		.bpp			= 1,
3662306a36Sopenharmony_ci	},
3762306a36Sopenharmony_ci	{
3862306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_NV16,
3962306a36Sopenharmony_ci		.bpp			= 1,
4062306a36Sopenharmony_ci	},
4162306a36Sopenharmony_ci	{
4262306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_YUYV,
4362306a36Sopenharmony_ci		.bpp			= 2,
4462306a36Sopenharmony_ci	},
4562306a36Sopenharmony_ci	{
4662306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_UYVY,
4762306a36Sopenharmony_ci		.bpp			= 2,
4862306a36Sopenharmony_ci	},
4962306a36Sopenharmony_ci	{
5062306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_RGB565,
5162306a36Sopenharmony_ci		.bpp			= 2,
5262306a36Sopenharmony_ci	},
5362306a36Sopenharmony_ci	{
5462306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_XRGB555,
5562306a36Sopenharmony_ci		.bpp			= 2,
5662306a36Sopenharmony_ci	},
5762306a36Sopenharmony_ci	{
5862306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_XBGR32,
5962306a36Sopenharmony_ci		.bpp			= 4,
6062306a36Sopenharmony_ci	},
6162306a36Sopenharmony_ci	{
6262306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_ARGB555,
6362306a36Sopenharmony_ci		.bpp			= 2,
6462306a36Sopenharmony_ci	},
6562306a36Sopenharmony_ci	{
6662306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_ABGR32,
6762306a36Sopenharmony_ci		.bpp			= 4,
6862306a36Sopenharmony_ci	},
6962306a36Sopenharmony_ci	{
7062306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_SBGGR8,
7162306a36Sopenharmony_ci		.bpp			= 1,
7262306a36Sopenharmony_ci	},
7362306a36Sopenharmony_ci	{
7462306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_SGBRG8,
7562306a36Sopenharmony_ci		.bpp			= 1,
7662306a36Sopenharmony_ci	},
7762306a36Sopenharmony_ci	{
7862306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_SGRBG8,
7962306a36Sopenharmony_ci		.bpp			= 1,
8062306a36Sopenharmony_ci	},
8162306a36Sopenharmony_ci	{
8262306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_SRGGB8,
8362306a36Sopenharmony_ci		.bpp			= 1,
8462306a36Sopenharmony_ci	},
8562306a36Sopenharmony_ci	{
8662306a36Sopenharmony_ci		.fourcc			= V4L2_PIX_FMT_GREY,
8762306a36Sopenharmony_ci		.bpp			= 1,
8862306a36Sopenharmony_ci	},
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciconst struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin,
9262306a36Sopenharmony_ci						       u32 pixelformat)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	int i;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	switch (pixelformat) {
9762306a36Sopenharmony_ci	case V4L2_PIX_FMT_XBGR32:
9862306a36Sopenharmony_ci		if (vin->info->model == RCAR_M1)
9962306a36Sopenharmony_ci			return NULL;
10062306a36Sopenharmony_ci		break;
10162306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
10262306a36Sopenharmony_ci		/*
10362306a36Sopenharmony_ci		 * If NV12 is supported it's only supported on channels 0, 1, 4,
10462306a36Sopenharmony_ci		 * 5, 8, 9, 12 and 13.
10562306a36Sopenharmony_ci		 */
10662306a36Sopenharmony_ci		if (!vin->info->nv12 || !(BIT(vin->id) & 0x3333))
10762306a36Sopenharmony_ci			return NULL;
10862306a36Sopenharmony_ci		break;
10962306a36Sopenharmony_ci	default:
11062306a36Sopenharmony_ci		break;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(rvin_formats); i++)
11462306a36Sopenharmony_ci		if (rvin_formats[i].fourcc == pixelformat)
11562306a36Sopenharmony_ci			return rvin_formats + i;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return NULL;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic u32 rvin_format_bytesperline(struct rvin_dev *vin,
12162306a36Sopenharmony_ci				    struct v4l2_pix_format *pix)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	const struct rvin_video_format *fmt;
12462306a36Sopenharmony_ci	u32 align;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	fmt = rvin_format_from_pixel(vin, pix->pixelformat);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (WARN_ON(!fmt))
12962306a36Sopenharmony_ci		return -EINVAL;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	switch (pix->pixelformat) {
13262306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
13362306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV16:
13462306a36Sopenharmony_ci		align = 0x20;
13562306a36Sopenharmony_ci		break;
13662306a36Sopenharmony_ci	default:
13762306a36Sopenharmony_ci		align = 0x10;
13862306a36Sopenharmony_ci		break;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (V4L2_FIELD_IS_SEQUENTIAL(pix->field))
14262306a36Sopenharmony_ci		align = 0x80;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return ALIGN(pix->width, align) * fmt->bpp;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic u32 rvin_format_sizeimage(struct v4l2_pix_format *pix)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	switch (pix->pixelformat) {
15062306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
15162306a36Sopenharmony_ci		return pix->bytesperline * pix->height * 3 / 2;
15262306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV16:
15362306a36Sopenharmony_ci		return pix->bytesperline * pix->height * 2;
15462306a36Sopenharmony_ci	default:
15562306a36Sopenharmony_ci		return pix->bytesperline * pix->height;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	u32 walign;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (!rvin_format_from_pixel(vin, pix->pixelformat))
16462306a36Sopenharmony_ci		pix->pixelformat = RVIN_DEFAULT_FORMAT;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	switch (pix->field) {
16762306a36Sopenharmony_ci	case V4L2_FIELD_TOP:
16862306a36Sopenharmony_ci	case V4L2_FIELD_BOTTOM:
16962306a36Sopenharmony_ci	case V4L2_FIELD_NONE:
17062306a36Sopenharmony_ci	case V4L2_FIELD_INTERLACED_TB:
17162306a36Sopenharmony_ci	case V4L2_FIELD_INTERLACED_BT:
17262306a36Sopenharmony_ci	case V4L2_FIELD_INTERLACED:
17362306a36Sopenharmony_ci	case V4L2_FIELD_ALTERNATE:
17462306a36Sopenharmony_ci	case V4L2_FIELD_SEQ_TB:
17562306a36Sopenharmony_ci	case V4L2_FIELD_SEQ_BT:
17662306a36Sopenharmony_ci		break;
17762306a36Sopenharmony_ci	default:
17862306a36Sopenharmony_ci		pix->field = RVIN_DEFAULT_FIELD;
17962306a36Sopenharmony_ci		break;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Hardware limits width alignment based on format. */
18362306a36Sopenharmony_ci	switch (pix->pixelformat) {
18462306a36Sopenharmony_ci	/* Multiple of 32 (2^5) for NV12/16. */
18562306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
18662306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV16:
18762306a36Sopenharmony_ci		walign = 5;
18862306a36Sopenharmony_ci		break;
18962306a36Sopenharmony_ci	/* Multiple of 2 (2^1) for YUV. */
19062306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUYV:
19162306a36Sopenharmony_ci	case V4L2_PIX_FMT_UYVY:
19262306a36Sopenharmony_ci		walign = 1;
19362306a36Sopenharmony_ci		break;
19462306a36Sopenharmony_ci	/* No multiple for RGB. */
19562306a36Sopenharmony_ci	default:
19662306a36Sopenharmony_ci		walign = 0;
19762306a36Sopenharmony_ci		break;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* Limit to VIN capabilities */
20162306a36Sopenharmony_ci	v4l_bound_align_image(&pix->width, 5, vin->info->max_width, walign,
20262306a36Sopenharmony_ci			      &pix->height, 2, vin->info->max_height, 0, 0);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	pix->bytesperline = rvin_format_bytesperline(vin, pix);
20562306a36Sopenharmony_ci	pix->sizeimage = rvin_format_sizeimage(pix);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	vin_dbg(vin, "Format %ux%u bpl: %u size: %u\n",
20862306a36Sopenharmony_ci		pix->width, pix->height, pix->bytesperline, pix->sizeimage);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
21262306a36Sopenharmony_ci * V4L2
21362306a36Sopenharmony_ci */
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int rvin_reset_format(struct rvin_dev *vin)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct v4l2_subdev_format fmt = {
21862306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
21962306a36Sopenharmony_ci		.pad = vin->parallel.source_pad,
22062306a36Sopenharmony_ci	};
22162306a36Sopenharmony_ci	int ret;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt);
22462306a36Sopenharmony_ci	if (ret)
22562306a36Sopenharmony_ci		return ret;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	v4l2_fill_pix_format(&vin->format, &fmt.format);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	vin->crop.top = 0;
23062306a36Sopenharmony_ci	vin->crop.left = 0;
23162306a36Sopenharmony_ci	vin->crop.width = vin->format.width;
23262306a36Sopenharmony_ci	vin->crop.height = vin->format.height;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/*  Make use of the hardware interlacer by default. */
23562306a36Sopenharmony_ci	if (vin->format.field == V4L2_FIELD_ALTERNATE) {
23662306a36Sopenharmony_ci		vin->format.field = V4L2_FIELD_INTERLACED;
23762306a36Sopenharmony_ci		vin->format.height *= 2;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	rvin_format_align(vin, &vin->format);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	vin->compose.top = 0;
24362306a36Sopenharmony_ci	vin->compose.left = 0;
24462306a36Sopenharmony_ci	vin->compose.width = vin->format.width;
24562306a36Sopenharmony_ci	vin->compose.height = vin->format.height;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return 0;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic int rvin_try_format(struct rvin_dev *vin, u32 which,
25162306a36Sopenharmony_ci			   struct v4l2_pix_format *pix,
25262306a36Sopenharmony_ci			   struct v4l2_rect *src_rect)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
25562306a36Sopenharmony_ci	struct v4l2_subdev_state *sd_state;
25662306a36Sopenharmony_ci	static struct lock_class_key key;
25762306a36Sopenharmony_ci	struct v4l2_subdev_format format = {
25862306a36Sopenharmony_ci		.which = which,
25962306a36Sopenharmony_ci		.pad = vin->parallel.source_pad,
26062306a36Sopenharmony_ci	};
26162306a36Sopenharmony_ci	enum v4l2_field field;
26262306a36Sopenharmony_ci	u32 width, height;
26362306a36Sopenharmony_ci	int ret;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/*
26662306a36Sopenharmony_ci	 * FIXME: Drop this call, drivers are not supposed to use
26762306a36Sopenharmony_ci	 * __v4l2_subdev_state_alloc().
26862306a36Sopenharmony_ci	 */
26962306a36Sopenharmony_ci	sd_state = __v4l2_subdev_state_alloc(sd, "rvin:state->lock", &key);
27062306a36Sopenharmony_ci	if (IS_ERR(sd_state))
27162306a36Sopenharmony_ci		return PTR_ERR(sd_state);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (!rvin_format_from_pixel(vin, pix->pixelformat))
27462306a36Sopenharmony_ci		pix->pixelformat = RVIN_DEFAULT_FORMAT;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	v4l2_fill_mbus_format(&format.format, pix, vin->mbus_code);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* Allow the video device to override field and to scale */
27962306a36Sopenharmony_ci	field = pix->field;
28062306a36Sopenharmony_ci	width = pix->width;
28162306a36Sopenharmony_ci	height = pix->height;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ret = v4l2_subdev_call(sd, pad, set_fmt, sd_state, &format);
28462306a36Sopenharmony_ci	if (ret < 0 && ret != -ENOIOCTLCMD)
28562306a36Sopenharmony_ci		goto done;
28662306a36Sopenharmony_ci	ret = 0;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	v4l2_fill_pix_format(pix, &format.format);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (src_rect) {
29162306a36Sopenharmony_ci		src_rect->top = 0;
29262306a36Sopenharmony_ci		src_rect->left = 0;
29362306a36Sopenharmony_ci		src_rect->width = pix->width;
29462306a36Sopenharmony_ci		src_rect->height = pix->height;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (field != V4L2_FIELD_ANY)
29862306a36Sopenharmony_ci		pix->field = field;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	pix->width = width;
30162306a36Sopenharmony_ci	pix->height = height;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	rvin_format_align(vin, pix);
30462306a36Sopenharmony_cidone:
30562306a36Sopenharmony_ci	__v4l2_subdev_state_free(sd_state);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return ret;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic int rvin_querycap(struct file *file, void *priv,
31162306a36Sopenharmony_ci			 struct v4l2_capability *cap)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
31462306a36Sopenharmony_ci	strscpy(cap->card, "R_Car_VIN", sizeof(cap->card));
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int rvin_try_fmt_vid_cap(struct file *file, void *priv,
31962306a36Sopenharmony_ci				struct v4l2_format *f)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	return rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, NULL);
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int rvin_s_fmt_vid_cap(struct file *file, void *priv,
32762306a36Sopenharmony_ci			      struct v4l2_format *f)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
33062306a36Sopenharmony_ci	struct v4l2_rect fmt_rect, src_rect;
33162306a36Sopenharmony_ci	int ret;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (vb2_is_busy(&vin->queue))
33462306a36Sopenharmony_ci		return -EBUSY;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	ret = rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix,
33762306a36Sopenharmony_ci			      &src_rect);
33862306a36Sopenharmony_ci	if (ret)
33962306a36Sopenharmony_ci		return ret;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	vin->format = f->fmt.pix;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	fmt_rect.top = 0;
34462306a36Sopenharmony_ci	fmt_rect.left = 0;
34562306a36Sopenharmony_ci	fmt_rect.width = vin->format.width;
34662306a36Sopenharmony_ci	fmt_rect.height = vin->format.height;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	v4l2_rect_map_inside(&vin->crop, &src_rect);
34962306a36Sopenharmony_ci	v4l2_rect_map_inside(&vin->compose, &fmt_rect);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return 0;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int rvin_g_fmt_vid_cap(struct file *file, void *priv,
35562306a36Sopenharmony_ci			      struct v4l2_format *f)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	f->fmt.pix = vin->format;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	return 0;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic int rvin_enum_fmt_vid_cap(struct file *file, void *priv,
36562306a36Sopenharmony_ci				 struct v4l2_fmtdesc *f)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
36862306a36Sopenharmony_ci	unsigned int i;
36962306a36Sopenharmony_ci	int matched;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/*
37262306a36Sopenharmony_ci	 * If mbus_code is set only enumerate supported pixel formats for that
37362306a36Sopenharmony_ci	 * bus code. Converting from YCbCr to RGB and RGB to YCbCr is possible
37462306a36Sopenharmony_ci	 * with VIN, so all supported YCbCr and RGB media bus codes can produce
37562306a36Sopenharmony_ci	 * all of the related pixel formats. If mbus_code is not set enumerate
37662306a36Sopenharmony_ci	 * all possible pixelformats.
37762306a36Sopenharmony_ci	 *
37862306a36Sopenharmony_ci	 * TODO: Once raw MEDIA_BUS_FMT_SRGGB12_1X12 format is added to the
37962306a36Sopenharmony_ci	 * driver this needs to be extended so raw media bus code only result in
38062306a36Sopenharmony_ci	 * raw pixel format.
38162306a36Sopenharmony_ci	 */
38262306a36Sopenharmony_ci	switch (f->mbus_code) {
38362306a36Sopenharmony_ci	case 0:
38462306a36Sopenharmony_ci	case MEDIA_BUS_FMT_YUYV8_1X16:
38562306a36Sopenharmony_ci	case MEDIA_BUS_FMT_UYVY8_1X16:
38662306a36Sopenharmony_ci	case MEDIA_BUS_FMT_UYVY8_2X8:
38762306a36Sopenharmony_ci	case MEDIA_BUS_FMT_UYVY10_2X10:
38862306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB888_1X24:
38962306a36Sopenharmony_ci		break;
39062306a36Sopenharmony_ci	case MEDIA_BUS_FMT_SBGGR8_1X8:
39162306a36Sopenharmony_ci		if (f->index)
39262306a36Sopenharmony_ci			return -EINVAL;
39362306a36Sopenharmony_ci		f->pixelformat = V4L2_PIX_FMT_SBGGR8;
39462306a36Sopenharmony_ci		return 0;
39562306a36Sopenharmony_ci	case MEDIA_BUS_FMT_SGBRG8_1X8:
39662306a36Sopenharmony_ci		if (f->index)
39762306a36Sopenharmony_ci			return -EINVAL;
39862306a36Sopenharmony_ci		f->pixelformat = V4L2_PIX_FMT_SGBRG8;
39962306a36Sopenharmony_ci		return 0;
40062306a36Sopenharmony_ci	case MEDIA_BUS_FMT_SGRBG8_1X8:
40162306a36Sopenharmony_ci		if (f->index)
40262306a36Sopenharmony_ci			return -EINVAL;
40362306a36Sopenharmony_ci		f->pixelformat = V4L2_PIX_FMT_SGRBG8;
40462306a36Sopenharmony_ci		return 0;
40562306a36Sopenharmony_ci	case MEDIA_BUS_FMT_SRGGB8_1X8:
40662306a36Sopenharmony_ci		if (f->index)
40762306a36Sopenharmony_ci			return -EINVAL;
40862306a36Sopenharmony_ci		f->pixelformat = V4L2_PIX_FMT_SRGGB8;
40962306a36Sopenharmony_ci		return 0;
41062306a36Sopenharmony_ci	default:
41162306a36Sopenharmony_ci		return -EINVAL;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	matched = -1;
41562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(rvin_formats); i++) {
41662306a36Sopenharmony_ci		if (rvin_format_from_pixel(vin, rvin_formats[i].fourcc))
41762306a36Sopenharmony_ci			matched++;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		if (matched == f->index) {
42062306a36Sopenharmony_ci			f->pixelformat = rvin_formats[i].fourcc;
42162306a36Sopenharmony_ci			return 0;
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return -EINVAL;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic int rvin_remote_rectangle(struct rvin_dev *vin, struct v4l2_rect *rect)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	struct v4l2_subdev_format fmt = {
43162306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
43262306a36Sopenharmony_ci	};
43362306a36Sopenharmony_ci	struct v4l2_subdev *sd;
43462306a36Sopenharmony_ci	unsigned int index;
43562306a36Sopenharmony_ci	int ret;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (vin->info->use_mc) {
43862306a36Sopenharmony_ci		struct media_pad *pad = media_pad_remote_pad_first(&vin->pad);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		if (!pad)
44162306a36Sopenharmony_ci			return -EINVAL;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		sd = media_entity_to_v4l2_subdev(pad->entity);
44462306a36Sopenharmony_ci		index = pad->index;
44562306a36Sopenharmony_ci	} else {
44662306a36Sopenharmony_ci		sd = vin_to_source(vin);
44762306a36Sopenharmony_ci		index = vin->parallel.source_pad;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	fmt.pad = index;
45162306a36Sopenharmony_ci	ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
45262306a36Sopenharmony_ci	if (ret)
45362306a36Sopenharmony_ci		return ret;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	rect->left = rect->top = 0;
45662306a36Sopenharmony_ci	rect->width = fmt.format.width;
45762306a36Sopenharmony_ci	rect->height = fmt.format.height;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (fmt.format.field == V4L2_FIELD_ALTERNATE) {
46062306a36Sopenharmony_ci		switch (vin->format.field) {
46162306a36Sopenharmony_ci		case V4L2_FIELD_INTERLACED_TB:
46262306a36Sopenharmony_ci		case V4L2_FIELD_INTERLACED_BT:
46362306a36Sopenharmony_ci		case V4L2_FIELD_INTERLACED:
46462306a36Sopenharmony_ci		case V4L2_FIELD_SEQ_TB:
46562306a36Sopenharmony_ci		case V4L2_FIELD_SEQ_BT:
46662306a36Sopenharmony_ci			rect->height *= 2;
46762306a36Sopenharmony_ci			break;
46862306a36Sopenharmony_ci		}
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	return 0;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic int rvin_g_selection(struct file *file, void *fh,
47562306a36Sopenharmony_ci			    struct v4l2_selection *s)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
47862306a36Sopenharmony_ci	int ret;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (!vin->scaler)
48162306a36Sopenharmony_ci		return -ENOIOCTLCMD;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
48462306a36Sopenharmony_ci		return -EINVAL;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	switch (s->target) {
48762306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
48862306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
48962306a36Sopenharmony_ci		ret = rvin_remote_rectangle(vin, &s->r);
49062306a36Sopenharmony_ci		if (ret)
49162306a36Sopenharmony_ci			return ret;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		break;
49462306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
49562306a36Sopenharmony_ci		s->r = vin->crop;
49662306a36Sopenharmony_ci		break;
49762306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
49862306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
49962306a36Sopenharmony_ci		s->r.left = s->r.top = 0;
50062306a36Sopenharmony_ci		s->r.width = vin->format.width;
50162306a36Sopenharmony_ci		s->r.height = vin->format.height;
50262306a36Sopenharmony_ci		break;
50362306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
50462306a36Sopenharmony_ci		s->r = vin->compose;
50562306a36Sopenharmony_ci		break;
50662306a36Sopenharmony_ci	default:
50762306a36Sopenharmony_ci		return -EINVAL;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	return 0;
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic int rvin_s_selection(struct file *file, void *fh,
51462306a36Sopenharmony_ci			    struct v4l2_selection *s)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
51762306a36Sopenharmony_ci	const struct rvin_video_format *fmt;
51862306a36Sopenharmony_ci	struct v4l2_rect r = s->r;
51962306a36Sopenharmony_ci	struct v4l2_rect max_rect;
52062306a36Sopenharmony_ci	struct v4l2_rect min_rect = {
52162306a36Sopenharmony_ci		.width = 6,
52262306a36Sopenharmony_ci		.height = 2,
52362306a36Sopenharmony_ci	};
52462306a36Sopenharmony_ci	int ret;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	if (!vin->scaler)
52762306a36Sopenharmony_ci		return -ENOIOCTLCMD;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
53062306a36Sopenharmony_ci		return -EINVAL;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	v4l2_rect_set_min_size(&r, &min_rect);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	switch (s->target) {
53562306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
53662306a36Sopenharmony_ci		/* Can't crop outside of source input */
53762306a36Sopenharmony_ci		ret = rvin_remote_rectangle(vin, &max_rect);
53862306a36Sopenharmony_ci		if (ret)
53962306a36Sopenharmony_ci			return ret;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		v4l2_rect_map_inside(&r, &max_rect);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci		v4l_bound_align_image(&r.width, 6, max_rect.width, 0,
54462306a36Sopenharmony_ci				      &r.height, 2, max_rect.height, 0, 0);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		r.top  = clamp_t(s32, r.top, 0, max_rect.height - r.height);
54762306a36Sopenharmony_ci		r.left = clamp_t(s32, r.left, 0, max_rect.width - r.width);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		vin->crop = s->r = r;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n",
55262306a36Sopenharmony_ci			r.width, r.height, r.left, r.top,
55362306a36Sopenharmony_ci			max_rect.width, max_rect.height);
55462306a36Sopenharmony_ci		break;
55562306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
55662306a36Sopenharmony_ci		/* Make sure compose rect fits inside output format */
55762306a36Sopenharmony_ci		max_rect.top = max_rect.left = 0;
55862306a36Sopenharmony_ci		max_rect.width = vin->format.width;
55962306a36Sopenharmony_ci		max_rect.height = vin->format.height;
56062306a36Sopenharmony_ci		v4l2_rect_map_inside(&r, &max_rect);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		/*
56362306a36Sopenharmony_ci		 * Composing is done by adding a offset to the buffer address,
56462306a36Sopenharmony_ci		 * the HW wants this address to be aligned to HW_BUFFER_MASK.
56562306a36Sopenharmony_ci		 * Make sure the top and left values meets this requirement.
56662306a36Sopenharmony_ci		 */
56762306a36Sopenharmony_ci		while ((r.top * vin->format.bytesperline) & HW_BUFFER_MASK)
56862306a36Sopenharmony_ci			r.top--;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci		fmt = rvin_format_from_pixel(vin, vin->format.pixelformat);
57162306a36Sopenharmony_ci		while ((r.left * fmt->bpp) & HW_BUFFER_MASK)
57262306a36Sopenharmony_ci			r.left--;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		vin->compose = s->r = r;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n",
57762306a36Sopenharmony_ci			r.width, r.height, r.left, r.top,
57862306a36Sopenharmony_ci			vin->format.width, vin->format.height);
57962306a36Sopenharmony_ci		break;
58062306a36Sopenharmony_ci	default:
58162306a36Sopenharmony_ci		return -EINVAL;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* HW supports modifying configuration while running */
58562306a36Sopenharmony_ci	rvin_crop_scale_comp(vin);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	return 0;
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic int rvin_g_parm(struct file *file, void *priv,
59162306a36Sopenharmony_ci		       struct v4l2_streamparm *parm)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
59462306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	return v4l2_g_parm_cap(&vin->vdev, sd, parm);
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic int rvin_s_parm(struct file *file, void *priv,
60062306a36Sopenharmony_ci		       struct v4l2_streamparm *parm)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
60362306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	return v4l2_s_parm_cap(&vin->vdev, sd, parm);
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic int rvin_g_pixelaspect(struct file *file, void *priv,
60962306a36Sopenharmony_ci			      int type, struct v4l2_fract *f)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
61262306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
61562306a36Sopenharmony_ci		return -EINVAL;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	return v4l2_subdev_call(sd, video, g_pixelaspect, f);
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic int rvin_enum_input(struct file *file, void *priv,
62162306a36Sopenharmony_ci			   struct v4l2_input *i)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
62462306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
62562306a36Sopenharmony_ci	int ret;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if (i->index != 0)
62862306a36Sopenharmony_ci		return -EINVAL;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	ret = v4l2_subdev_call(sd, video, g_input_status, &i->status);
63162306a36Sopenharmony_ci	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
63262306a36Sopenharmony_ci		return ret;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	i->type = V4L2_INPUT_TYPE_CAMERA;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if (v4l2_subdev_has_op(sd, pad, dv_timings_cap)) {
63762306a36Sopenharmony_ci		i->capabilities = V4L2_IN_CAP_DV_TIMINGS;
63862306a36Sopenharmony_ci		i->std = 0;
63962306a36Sopenharmony_ci	} else {
64062306a36Sopenharmony_ci		i->capabilities = V4L2_IN_CAP_STD;
64162306a36Sopenharmony_ci		i->std = vin->vdev.tvnorms;
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	strscpy(i->name, "Camera", sizeof(i->name));
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	return 0;
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic int rvin_g_input(struct file *file, void *priv, unsigned int *i)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	*i = 0;
65262306a36Sopenharmony_ci	return 0;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic int rvin_s_input(struct file *file, void *priv, unsigned int i)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	if (i > 0)
65862306a36Sopenharmony_ci		return -EINVAL;
65962306a36Sopenharmony_ci	return 0;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
66562306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	return v4l2_subdev_call(sd, video, querystd, a);
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_cistatic int rvin_s_std(struct file *file, void *priv, v4l2_std_id a)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
67362306a36Sopenharmony_ci	int ret;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	ret = v4l2_subdev_call(vin_to_source(vin), video, s_std, a);
67662306a36Sopenharmony_ci	if (ret < 0)
67762306a36Sopenharmony_ci		return ret;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	vin->std = a;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	/* Changing the standard will change the width/height */
68262306a36Sopenharmony_ci	return rvin_reset_format(vin);
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_cistatic int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (v4l2_subdev_has_op(vin_to_source(vin), pad, dv_timings_cap))
69062306a36Sopenharmony_ci		return -ENOIOCTLCMD;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	*a = vin->std;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	return 0;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic int rvin_subscribe_event(struct v4l2_fh *fh,
69862306a36Sopenharmony_ci				const struct v4l2_event_subscription *sub)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	switch (sub->type) {
70162306a36Sopenharmony_ci	case V4L2_EVENT_SOURCE_CHANGE:
70262306a36Sopenharmony_ci		return v4l2_event_subscribe(fh, sub, 4, NULL);
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci	return v4l2_ctrl_subscribe_event(fh, sub);
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic int rvin_enum_dv_timings(struct file *file, void *priv_fh,
70862306a36Sopenharmony_ci				struct v4l2_enum_dv_timings *timings)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
71162306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
71262306a36Sopenharmony_ci	int ret;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	if (timings->pad)
71562306a36Sopenharmony_ci		return -EINVAL;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	timings->pad = vin->parallel.sink_pad;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	timings->pad = 0;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	return ret;
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic int rvin_s_dv_timings(struct file *file, void *priv_fh,
72762306a36Sopenharmony_ci			     struct v4l2_dv_timings *timings)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
73062306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
73162306a36Sopenharmony_ci	int ret;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	ret = v4l2_subdev_call(sd, video, s_dv_timings, timings);
73462306a36Sopenharmony_ci	if (ret)
73562306a36Sopenharmony_ci		return ret;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	/* Changing the timings will change the width/height */
73862306a36Sopenharmony_ci	return rvin_reset_format(vin);
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_cistatic int rvin_g_dv_timings(struct file *file, void *priv_fh,
74262306a36Sopenharmony_ci			     struct v4l2_dv_timings *timings)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
74562306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	return v4l2_subdev_call(sd, video, g_dv_timings, timings);
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic int rvin_query_dv_timings(struct file *file, void *priv_fh,
75162306a36Sopenharmony_ci				 struct v4l2_dv_timings *timings)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
75462306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	return v4l2_subdev_call(sd, video, query_dv_timings, timings);
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic int rvin_dv_timings_cap(struct file *file, void *priv_fh,
76062306a36Sopenharmony_ci			       struct v4l2_dv_timings_cap *cap)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
76362306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
76462306a36Sopenharmony_ci	int ret;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (cap->pad)
76762306a36Sopenharmony_ci		return -EINVAL;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	cap->pad = vin->parallel.sink_pad;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	cap->pad = 0;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	return ret;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
78162306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
78262306a36Sopenharmony_ci	int ret;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (edid->pad)
78562306a36Sopenharmony_ci		return -EINVAL;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	edid->pad = vin->parallel.sink_pad;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	ret = v4l2_subdev_call(sd, pad, get_edid, edid);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	edid->pad = 0;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	return ret;
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
79962306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
80062306a36Sopenharmony_ci	int ret;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	if (edid->pad)
80362306a36Sopenharmony_ci		return -EINVAL;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	edid->pad = vin->parallel.sink_pad;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	ret = v4l2_subdev_call(sd, pad, set_edid, edid);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	edid->pad = 0;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	return ret;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops rvin_ioctl_ops = {
81562306a36Sopenharmony_ci	.vidioc_querycap		= rvin_querycap,
81662306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap		= rvin_try_fmt_vid_cap,
81762306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap		= rvin_g_fmt_vid_cap,
81862306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap		= rvin_s_fmt_vid_cap,
81962306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	= rvin_enum_fmt_vid_cap,
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	.vidioc_g_selection		= rvin_g_selection,
82262306a36Sopenharmony_ci	.vidioc_s_selection		= rvin_s_selection,
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	.vidioc_g_parm			= rvin_g_parm,
82562306a36Sopenharmony_ci	.vidioc_s_parm			= rvin_s_parm,
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	.vidioc_g_pixelaspect		= rvin_g_pixelaspect,
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	.vidioc_enum_input		= rvin_enum_input,
83062306a36Sopenharmony_ci	.vidioc_g_input			= rvin_g_input,
83162306a36Sopenharmony_ci	.vidioc_s_input			= rvin_s_input,
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	.vidioc_dv_timings_cap		= rvin_dv_timings_cap,
83462306a36Sopenharmony_ci	.vidioc_enum_dv_timings		= rvin_enum_dv_timings,
83562306a36Sopenharmony_ci	.vidioc_g_dv_timings		= rvin_g_dv_timings,
83662306a36Sopenharmony_ci	.vidioc_s_dv_timings		= rvin_s_dv_timings,
83762306a36Sopenharmony_ci	.vidioc_query_dv_timings	= rvin_query_dv_timings,
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	.vidioc_g_edid			= rvin_g_edid,
84062306a36Sopenharmony_ci	.vidioc_s_edid			= rvin_s_edid,
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	.vidioc_querystd		= rvin_querystd,
84362306a36Sopenharmony_ci	.vidioc_g_std			= rvin_g_std,
84462306a36Sopenharmony_ci	.vidioc_s_std			= rvin_s_std,
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
84762306a36Sopenharmony_ci	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
84862306a36Sopenharmony_ci	.vidioc_querybuf		= vb2_ioctl_querybuf,
84962306a36Sopenharmony_ci	.vidioc_qbuf			= vb2_ioctl_qbuf,
85062306a36Sopenharmony_ci	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
85162306a36Sopenharmony_ci	.vidioc_expbuf			= vb2_ioctl_expbuf,
85262306a36Sopenharmony_ci	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
85362306a36Sopenharmony_ci	.vidioc_streamon		= vb2_ioctl_streamon,
85462306a36Sopenharmony_ci	.vidioc_streamoff		= vb2_ioctl_streamoff,
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	.vidioc_log_status		= v4l2_ctrl_log_status,
85762306a36Sopenharmony_ci	.vidioc_subscribe_event		= rvin_subscribe_event,
85862306a36Sopenharmony_ci	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
85962306a36Sopenharmony_ci};
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
86262306a36Sopenharmony_ci * V4L2 Media Controller
86362306a36Sopenharmony_ci */
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_cistatic void rvin_mc_try_format(struct rvin_dev *vin,
86662306a36Sopenharmony_ci			       struct v4l2_pix_format *pix)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	/*
86962306a36Sopenharmony_ci	 * The V4L2 specification clearly documents the colorspace fields
87062306a36Sopenharmony_ci	 * as being set by drivers for capture devices. Using the values
87162306a36Sopenharmony_ci	 * supplied by userspace thus wouldn't comply with the API. Until
87262306a36Sopenharmony_ci	 * the API is updated force fixed values.
87362306a36Sopenharmony_ci	 */
87462306a36Sopenharmony_ci	pix->colorspace = RVIN_DEFAULT_COLORSPACE;
87562306a36Sopenharmony_ci	pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);
87662306a36Sopenharmony_ci	pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
87762306a36Sopenharmony_ci	pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, pix->colorspace,
87862306a36Sopenharmony_ci							  pix->ycbcr_enc);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	rvin_format_align(vin, pix);
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic int rvin_mc_try_fmt_vid_cap(struct file *file, void *priv,
88462306a36Sopenharmony_ci				   struct v4l2_format *f)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	rvin_mc_try_format(vin, &f->fmt.pix);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	return 0;
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_cistatic int rvin_mc_s_fmt_vid_cap(struct file *file, void *priv,
89462306a36Sopenharmony_ci				 struct v4l2_format *f)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (vb2_is_busy(&vin->queue))
89962306a36Sopenharmony_ci		return -EBUSY;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	rvin_mc_try_format(vin, &f->fmt.pix);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	vin->format = f->fmt.pix;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	vin->crop.top = 0;
90662306a36Sopenharmony_ci	vin->crop.left = 0;
90762306a36Sopenharmony_ci	vin->crop.width = vin->format.width;
90862306a36Sopenharmony_ci	vin->crop.height = vin->format.height;
90962306a36Sopenharmony_ci	vin->compose = vin->crop;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	return 0;
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops rvin_mc_ioctl_ops = {
91562306a36Sopenharmony_ci	.vidioc_querycap		= rvin_querycap,
91662306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap		= rvin_mc_try_fmt_vid_cap,
91762306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap		= rvin_g_fmt_vid_cap,
91862306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap		= rvin_mc_s_fmt_vid_cap,
91962306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	= rvin_enum_fmt_vid_cap,
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	.vidioc_g_selection		= rvin_g_selection,
92262306a36Sopenharmony_ci	.vidioc_s_selection		= rvin_s_selection,
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
92562306a36Sopenharmony_ci	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
92662306a36Sopenharmony_ci	.vidioc_querybuf		= vb2_ioctl_querybuf,
92762306a36Sopenharmony_ci	.vidioc_qbuf			= vb2_ioctl_qbuf,
92862306a36Sopenharmony_ci	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
92962306a36Sopenharmony_ci	.vidioc_expbuf			= vb2_ioctl_expbuf,
93062306a36Sopenharmony_ci	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
93162306a36Sopenharmony_ci	.vidioc_streamon		= vb2_ioctl_streamon,
93262306a36Sopenharmony_ci	.vidioc_streamoff		= vb2_ioctl_streamoff,
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	.vidioc_log_status		= v4l2_ctrl_log_status,
93562306a36Sopenharmony_ci	.vidioc_subscribe_event		= rvin_subscribe_event,
93662306a36Sopenharmony_ci	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
93762306a36Sopenharmony_ci};
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
94062306a36Sopenharmony_ci * File Operations
94162306a36Sopenharmony_ci */
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic int rvin_power_parallel(struct rvin_dev *vin, bool on)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	struct v4l2_subdev *sd = vin_to_source(vin);
94662306a36Sopenharmony_ci	int power = on ? 1 : 0;
94762306a36Sopenharmony_ci	int ret;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	ret = v4l2_subdev_call(sd, core, s_power, power);
95062306a36Sopenharmony_ci	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
95162306a36Sopenharmony_ci		return ret;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	return 0;
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic int rvin_open(struct file *file)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
95962306a36Sopenharmony_ci	int ret;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(vin->dev);
96262306a36Sopenharmony_ci	if (ret < 0)
96362306a36Sopenharmony_ci		return ret;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&vin->lock);
96662306a36Sopenharmony_ci	if (ret)
96762306a36Sopenharmony_ci		goto err_pm;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	file->private_data = vin;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	ret = v4l2_fh_open(file);
97262306a36Sopenharmony_ci	if (ret)
97362306a36Sopenharmony_ci		goto err_unlock;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (vin->info->use_mc)
97662306a36Sopenharmony_ci		ret = v4l2_pipeline_pm_get(&vin->vdev.entity);
97762306a36Sopenharmony_ci	else if (v4l2_fh_is_singular_file(file))
97862306a36Sopenharmony_ci		ret = rvin_power_parallel(vin, true);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	if (ret < 0)
98162306a36Sopenharmony_ci		goto err_open;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	ret = v4l2_ctrl_handler_setup(&vin->ctrl_handler);
98462306a36Sopenharmony_ci	if (ret)
98562306a36Sopenharmony_ci		goto err_power;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	mutex_unlock(&vin->lock);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	return 0;
99062306a36Sopenharmony_cierr_power:
99162306a36Sopenharmony_ci	if (vin->info->use_mc)
99262306a36Sopenharmony_ci		v4l2_pipeline_pm_put(&vin->vdev.entity);
99362306a36Sopenharmony_ci	else if (v4l2_fh_is_singular_file(file))
99462306a36Sopenharmony_ci		rvin_power_parallel(vin, false);
99562306a36Sopenharmony_cierr_open:
99662306a36Sopenharmony_ci	v4l2_fh_release(file);
99762306a36Sopenharmony_cierr_unlock:
99862306a36Sopenharmony_ci	mutex_unlock(&vin->lock);
99962306a36Sopenharmony_cierr_pm:
100062306a36Sopenharmony_ci	pm_runtime_put(vin->dev);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	return ret;
100362306a36Sopenharmony_ci}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_cistatic int rvin_release(struct file *file)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	struct rvin_dev *vin = video_drvdata(file);
100862306a36Sopenharmony_ci	bool fh_singular;
100962306a36Sopenharmony_ci	int ret;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	mutex_lock(&vin->lock);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	/* Save the singular status before we call the clean-up helper */
101462306a36Sopenharmony_ci	fh_singular = v4l2_fh_is_singular_file(file);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	/* the release helper will cleanup any on-going streaming */
101762306a36Sopenharmony_ci	ret = _vb2_fop_release(file, NULL);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (vin->info->use_mc) {
102062306a36Sopenharmony_ci		v4l2_pipeline_pm_put(&vin->vdev.entity);
102162306a36Sopenharmony_ci	} else {
102262306a36Sopenharmony_ci		if (fh_singular)
102362306a36Sopenharmony_ci			rvin_power_parallel(vin, false);
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	mutex_unlock(&vin->lock);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	pm_runtime_put(vin->dev);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	return ret;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic const struct v4l2_file_operations rvin_fops = {
103462306a36Sopenharmony_ci	.owner		= THIS_MODULE,
103562306a36Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
103662306a36Sopenharmony_ci	.open		= rvin_open,
103762306a36Sopenharmony_ci	.release	= rvin_release,
103862306a36Sopenharmony_ci	.poll		= vb2_fop_poll,
103962306a36Sopenharmony_ci	.mmap		= vb2_fop_mmap,
104062306a36Sopenharmony_ci	.read		= vb2_fop_read,
104162306a36Sopenharmony_ci};
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_civoid rvin_v4l2_unregister(struct rvin_dev *vin)
104462306a36Sopenharmony_ci{
104562306a36Sopenharmony_ci	if (!video_is_registered(&vin->vdev))
104662306a36Sopenharmony_ci		return;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	v4l2_info(&vin->v4l2_dev, "Removing %s\n",
104962306a36Sopenharmony_ci		  video_device_node_name(&vin->vdev));
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	/* Checks internally if vdev have been init or not */
105262306a36Sopenharmony_ci	video_unregister_device(&vin->vdev);
105362306a36Sopenharmony_ci}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_cistatic void rvin_notify_video_device(struct rvin_dev *vin,
105662306a36Sopenharmony_ci				     unsigned int notification, void *arg)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	switch (notification) {
105962306a36Sopenharmony_ci	case V4L2_DEVICE_NOTIFY_EVENT:
106062306a36Sopenharmony_ci		v4l2_event_queue(&vin->vdev, arg);
106162306a36Sopenharmony_ci		break;
106262306a36Sopenharmony_ci	default:
106362306a36Sopenharmony_ci		break;
106462306a36Sopenharmony_ci	}
106562306a36Sopenharmony_ci}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_cistatic void rvin_notify(struct v4l2_subdev *sd,
106862306a36Sopenharmony_ci			unsigned int notification, void *arg)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	struct v4l2_subdev *remote;
107162306a36Sopenharmony_ci	struct rvin_group *group;
107262306a36Sopenharmony_ci	struct media_pad *pad;
107362306a36Sopenharmony_ci	struct rvin_dev *vin =
107462306a36Sopenharmony_ci		container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev);
107562306a36Sopenharmony_ci	unsigned int i;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	/* If no media controller, no need to route the event. */
107862306a36Sopenharmony_ci	if (!vin->info->use_mc) {
107962306a36Sopenharmony_ci		rvin_notify_video_device(vin, notification, arg);
108062306a36Sopenharmony_ci		return;
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	group = vin->group;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	for (i = 0; i < RCAR_VIN_NUM; i++) {
108662306a36Sopenharmony_ci		vin = group->vin[i];
108762306a36Sopenharmony_ci		if (!vin)
108862306a36Sopenharmony_ci			continue;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		pad = media_pad_remote_pad_first(&vin->pad);
109162306a36Sopenharmony_ci		if (!pad)
109262306a36Sopenharmony_ci			continue;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci		remote = media_entity_to_v4l2_subdev(pad->entity);
109562306a36Sopenharmony_ci		if (remote != sd)
109662306a36Sopenharmony_ci			continue;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci		rvin_notify_video_device(vin, notification, arg);
109962306a36Sopenharmony_ci	}
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ciint rvin_v4l2_register(struct rvin_dev *vin)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	struct video_device *vdev = &vin->vdev;
110562306a36Sopenharmony_ci	int ret;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	vin->v4l2_dev.notify = rvin_notify;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	/* video node */
111062306a36Sopenharmony_ci	vdev->v4l2_dev = &vin->v4l2_dev;
111162306a36Sopenharmony_ci	vdev->queue = &vin->queue;
111262306a36Sopenharmony_ci	snprintf(vdev->name, sizeof(vdev->name), "VIN%u output", vin->id);
111362306a36Sopenharmony_ci	vdev->release = video_device_release_empty;
111462306a36Sopenharmony_ci	vdev->lock = &vin->lock;
111562306a36Sopenharmony_ci	vdev->fops = &rvin_fops;
111662306a36Sopenharmony_ci	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
111762306a36Sopenharmony_ci		V4L2_CAP_READWRITE;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	/* Set a default format */
112062306a36Sopenharmony_ci	vin->format.pixelformat	= RVIN_DEFAULT_FORMAT;
112162306a36Sopenharmony_ci	vin->format.width = RVIN_DEFAULT_WIDTH;
112262306a36Sopenharmony_ci	vin->format.height = RVIN_DEFAULT_HEIGHT;
112362306a36Sopenharmony_ci	vin->format.field = RVIN_DEFAULT_FIELD;
112462306a36Sopenharmony_ci	vin->format.colorspace = RVIN_DEFAULT_COLORSPACE;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	if (vin->info->use_mc) {
112762306a36Sopenharmony_ci		vdev->device_caps |= V4L2_CAP_IO_MC;
112862306a36Sopenharmony_ci		vdev->ioctl_ops = &rvin_mc_ioctl_ops;
112962306a36Sopenharmony_ci	} else {
113062306a36Sopenharmony_ci		vdev->ioctl_ops = &rvin_ioctl_ops;
113162306a36Sopenharmony_ci		rvin_reset_format(vin);
113262306a36Sopenharmony_ci	}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	rvin_format_align(vin, &vin->format);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	ret = video_register_device(&vin->vdev, VFL_TYPE_VIDEO, -1);
113762306a36Sopenharmony_ci	if (ret) {
113862306a36Sopenharmony_ci		vin_err(vin, "Failed to register video device\n");
113962306a36Sopenharmony_ci		return ret;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	video_set_drvdata(&vin->vdev, vin);
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	v4l2_info(&vin->v4l2_dev, "Device registered as %s\n",
114562306a36Sopenharmony_ci		  video_device_node_name(&vin->vdev));
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	return ret;
114862306a36Sopenharmony_ci}
1149