xref: /OK3568_Linux_fs/kernel/drivers/media/platform/rcar-vin/rcar-v4l2.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Driver for Renesas R-Car VIN
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2016 Renesas Electronics Corp.
6*4882a593Smuzhiyun  * Copyright (C) 2011-2013 Renesas Solutions Corp.
7*4882a593Smuzhiyun  * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
8*4882a593Smuzhiyun  * Copyright (C) 2008 Magnus Damm
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * Based on the soc-camera rcar_vin driver
11*4882a593Smuzhiyun  */
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <linux/pm_runtime.h>
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include <media/v4l2-event.h>
16*4882a593Smuzhiyun #include <media/v4l2-ioctl.h>
17*4882a593Smuzhiyun #include <media/v4l2-mc.h>
18*4882a593Smuzhiyun #include <media/v4l2-rect.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include "rcar-vin.h"
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #define RVIN_DEFAULT_FORMAT	V4L2_PIX_FMT_YUYV
23*4882a593Smuzhiyun #define RVIN_DEFAULT_WIDTH	800
24*4882a593Smuzhiyun #define RVIN_DEFAULT_HEIGHT	600
25*4882a593Smuzhiyun #define RVIN_DEFAULT_FIELD	V4L2_FIELD_NONE
26*4882a593Smuzhiyun #define RVIN_DEFAULT_COLORSPACE	V4L2_COLORSPACE_SRGB
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
29*4882a593Smuzhiyun  * Format Conversions
30*4882a593Smuzhiyun  */
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun static const struct rvin_video_format rvin_formats[] = {
33*4882a593Smuzhiyun 	{
34*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_NV12,
35*4882a593Smuzhiyun 		.bpp			= 1,
36*4882a593Smuzhiyun 	},
37*4882a593Smuzhiyun 	{
38*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_NV16,
39*4882a593Smuzhiyun 		.bpp			= 1,
40*4882a593Smuzhiyun 	},
41*4882a593Smuzhiyun 	{
42*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_YUYV,
43*4882a593Smuzhiyun 		.bpp			= 2,
44*4882a593Smuzhiyun 	},
45*4882a593Smuzhiyun 	{
46*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_UYVY,
47*4882a593Smuzhiyun 		.bpp			= 2,
48*4882a593Smuzhiyun 	},
49*4882a593Smuzhiyun 	{
50*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_RGB565,
51*4882a593Smuzhiyun 		.bpp			= 2,
52*4882a593Smuzhiyun 	},
53*4882a593Smuzhiyun 	{
54*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_XRGB555,
55*4882a593Smuzhiyun 		.bpp			= 2,
56*4882a593Smuzhiyun 	},
57*4882a593Smuzhiyun 	{
58*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_XBGR32,
59*4882a593Smuzhiyun 		.bpp			= 4,
60*4882a593Smuzhiyun 	},
61*4882a593Smuzhiyun 	{
62*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_ARGB555,
63*4882a593Smuzhiyun 		.bpp			= 2,
64*4882a593Smuzhiyun 	},
65*4882a593Smuzhiyun 	{
66*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_ABGR32,
67*4882a593Smuzhiyun 		.bpp			= 4,
68*4882a593Smuzhiyun 	},
69*4882a593Smuzhiyun 	{
70*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_SBGGR8,
71*4882a593Smuzhiyun 		.bpp			= 1,
72*4882a593Smuzhiyun 	},
73*4882a593Smuzhiyun 	{
74*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_SGBRG8,
75*4882a593Smuzhiyun 		.bpp			= 1,
76*4882a593Smuzhiyun 	},
77*4882a593Smuzhiyun 	{
78*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_SGRBG8,
79*4882a593Smuzhiyun 		.bpp			= 1,
80*4882a593Smuzhiyun 	},
81*4882a593Smuzhiyun 	{
82*4882a593Smuzhiyun 		.fourcc			= V4L2_PIX_FMT_SRGGB8,
83*4882a593Smuzhiyun 		.bpp			= 1,
84*4882a593Smuzhiyun 	},
85*4882a593Smuzhiyun };
86*4882a593Smuzhiyun 
rvin_format_from_pixel(struct rvin_dev * vin,u32 pixelformat)87*4882a593Smuzhiyun const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin,
88*4882a593Smuzhiyun 						       u32 pixelformat)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun 	int i;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	switch (pixelformat) {
93*4882a593Smuzhiyun 	case V4L2_PIX_FMT_XBGR32:
94*4882a593Smuzhiyun 		if (vin->info->model == RCAR_M1)
95*4882a593Smuzhiyun 			return NULL;
96*4882a593Smuzhiyun 		break;
97*4882a593Smuzhiyun 	case V4L2_PIX_FMT_NV12:
98*4882a593Smuzhiyun 		/*
99*4882a593Smuzhiyun 		 * If NV12 is supported it's only supported on channels 0, 1, 4,
100*4882a593Smuzhiyun 		 * 5, 8, 9, 12 and 13.
101*4882a593Smuzhiyun 		 */
102*4882a593Smuzhiyun 		if (!vin->info->nv12 || !(BIT(vin->id) & 0x3333))
103*4882a593Smuzhiyun 			return NULL;
104*4882a593Smuzhiyun 		break;
105*4882a593Smuzhiyun 	default:
106*4882a593Smuzhiyun 		break;
107*4882a593Smuzhiyun 	}
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(rvin_formats); i++)
110*4882a593Smuzhiyun 		if (rvin_formats[i].fourcc == pixelformat)
111*4882a593Smuzhiyun 			return rvin_formats + i;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	return NULL;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun 
rvin_format_bytesperline(struct rvin_dev * vin,struct v4l2_pix_format * pix)116*4882a593Smuzhiyun static u32 rvin_format_bytesperline(struct rvin_dev *vin,
117*4882a593Smuzhiyun 				    struct v4l2_pix_format *pix)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun 	const struct rvin_video_format *fmt;
120*4882a593Smuzhiyun 	u32 align;
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	fmt = rvin_format_from_pixel(vin, pix->pixelformat);
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	if (WARN_ON(!fmt))
125*4882a593Smuzhiyun 		return -EINVAL;
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	switch (pix->pixelformat) {
128*4882a593Smuzhiyun 	case V4L2_PIX_FMT_NV12:
129*4882a593Smuzhiyun 	case V4L2_PIX_FMT_NV16:
130*4882a593Smuzhiyun 		align = 0x20;
131*4882a593Smuzhiyun 		break;
132*4882a593Smuzhiyun 	default:
133*4882a593Smuzhiyun 		align = 0x10;
134*4882a593Smuzhiyun 		break;
135*4882a593Smuzhiyun 	}
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	if (V4L2_FIELD_IS_SEQUENTIAL(pix->field))
138*4882a593Smuzhiyun 		align = 0x80;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	return ALIGN(pix->width, align) * fmt->bpp;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun 
rvin_format_sizeimage(struct v4l2_pix_format * pix)143*4882a593Smuzhiyun static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun 	switch (pix->pixelformat) {
146*4882a593Smuzhiyun 	case V4L2_PIX_FMT_NV12:
147*4882a593Smuzhiyun 		return pix->bytesperline * pix->height * 3 / 2;
148*4882a593Smuzhiyun 	case V4L2_PIX_FMT_NV16:
149*4882a593Smuzhiyun 		return pix->bytesperline * pix->height * 2;
150*4882a593Smuzhiyun 	default:
151*4882a593Smuzhiyun 		return pix->bytesperline * pix->height;
152*4882a593Smuzhiyun 	}
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
rvin_format_align(struct rvin_dev * vin,struct v4l2_pix_format * pix)155*4882a593Smuzhiyun static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix)
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun 	u32 walign;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	if (!rvin_format_from_pixel(vin, pix->pixelformat))
160*4882a593Smuzhiyun 		pix->pixelformat = RVIN_DEFAULT_FORMAT;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	switch (pix->field) {
163*4882a593Smuzhiyun 	case V4L2_FIELD_TOP:
164*4882a593Smuzhiyun 	case V4L2_FIELD_BOTTOM:
165*4882a593Smuzhiyun 	case V4L2_FIELD_NONE:
166*4882a593Smuzhiyun 	case V4L2_FIELD_INTERLACED_TB:
167*4882a593Smuzhiyun 	case V4L2_FIELD_INTERLACED_BT:
168*4882a593Smuzhiyun 	case V4L2_FIELD_INTERLACED:
169*4882a593Smuzhiyun 	case V4L2_FIELD_ALTERNATE:
170*4882a593Smuzhiyun 	case V4L2_FIELD_SEQ_TB:
171*4882a593Smuzhiyun 	case V4L2_FIELD_SEQ_BT:
172*4882a593Smuzhiyun 		break;
173*4882a593Smuzhiyun 	default:
174*4882a593Smuzhiyun 		pix->field = RVIN_DEFAULT_FIELD;
175*4882a593Smuzhiyun 		break;
176*4882a593Smuzhiyun 	}
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	/* Hardware limits width alignment based on format. */
179*4882a593Smuzhiyun 	switch (pix->pixelformat) {
180*4882a593Smuzhiyun 	/* Multiple of 32 (2^5) for NV12/16. */
181*4882a593Smuzhiyun 	case V4L2_PIX_FMT_NV12:
182*4882a593Smuzhiyun 	case V4L2_PIX_FMT_NV16:
183*4882a593Smuzhiyun 		walign = 5;
184*4882a593Smuzhiyun 		break;
185*4882a593Smuzhiyun 	/* Multiple of 2 (2^1) for YUV. */
186*4882a593Smuzhiyun 	case V4L2_PIX_FMT_YUYV:
187*4882a593Smuzhiyun 	case V4L2_PIX_FMT_UYVY:
188*4882a593Smuzhiyun 		walign = 1;
189*4882a593Smuzhiyun 		break;
190*4882a593Smuzhiyun 	/* No multiple for RGB. */
191*4882a593Smuzhiyun 	default:
192*4882a593Smuzhiyun 		walign = 0;
193*4882a593Smuzhiyun 		break;
194*4882a593Smuzhiyun 	}
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	/* Limit to VIN capabilities */
197*4882a593Smuzhiyun 	v4l_bound_align_image(&pix->width, 5, vin->info->max_width, walign,
198*4882a593Smuzhiyun 			      &pix->height, 2, vin->info->max_height, 0, 0);
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	pix->bytesperline = rvin_format_bytesperline(vin, pix);
201*4882a593Smuzhiyun 	pix->sizeimage = rvin_format_sizeimage(pix);
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	vin_dbg(vin, "Format %ux%u bpl: %u size: %u\n",
204*4882a593Smuzhiyun 		pix->width, pix->height, pix->bytesperline, pix->sizeimage);
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
208*4882a593Smuzhiyun  * V4L2
209*4882a593Smuzhiyun  */
210*4882a593Smuzhiyun 
rvin_reset_format(struct rvin_dev * vin)211*4882a593Smuzhiyun static int rvin_reset_format(struct rvin_dev *vin)
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun 	struct v4l2_subdev_format fmt = {
214*4882a593Smuzhiyun 		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
215*4882a593Smuzhiyun 		.pad = vin->parallel->source_pad,
216*4882a593Smuzhiyun 	};
217*4882a593Smuzhiyun 	int ret;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt);
220*4882a593Smuzhiyun 	if (ret)
221*4882a593Smuzhiyun 		return ret;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	v4l2_fill_pix_format(&vin->format, &fmt.format);
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	vin->src_rect.top = 0;
226*4882a593Smuzhiyun 	vin->src_rect.left = 0;
227*4882a593Smuzhiyun 	vin->src_rect.width = vin->format.width;
228*4882a593Smuzhiyun 	vin->src_rect.height = vin->format.height;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	/*  Make use of the hardware interlacer by default. */
231*4882a593Smuzhiyun 	if (vin->format.field == V4L2_FIELD_ALTERNATE) {
232*4882a593Smuzhiyun 		vin->format.field = V4L2_FIELD_INTERLACED;
233*4882a593Smuzhiyun 		vin->format.height *= 2;
234*4882a593Smuzhiyun 	}
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	rvin_format_align(vin, &vin->format);
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	vin->crop = vin->src_rect;
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	vin->compose.top = 0;
241*4882a593Smuzhiyun 	vin->compose.left = 0;
242*4882a593Smuzhiyun 	vin->compose.width = vin->format.width;
243*4882a593Smuzhiyun 	vin->compose.height = vin->format.height;
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	return 0;
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun 
rvin_try_format(struct rvin_dev * vin,u32 which,struct v4l2_pix_format * pix,struct v4l2_rect * src_rect)248*4882a593Smuzhiyun static int rvin_try_format(struct rvin_dev *vin, u32 which,
249*4882a593Smuzhiyun 			   struct v4l2_pix_format *pix,
250*4882a593Smuzhiyun 			   struct v4l2_rect *src_rect)
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun 	struct v4l2_subdev *sd = vin_to_source(vin);
253*4882a593Smuzhiyun 	struct v4l2_subdev_pad_config *pad_cfg;
254*4882a593Smuzhiyun 	struct v4l2_subdev_format format = {
255*4882a593Smuzhiyun 		.which = which,
256*4882a593Smuzhiyun 		.pad = vin->parallel->source_pad,
257*4882a593Smuzhiyun 	};
258*4882a593Smuzhiyun 	enum v4l2_field field;
259*4882a593Smuzhiyun 	u32 width, height;
260*4882a593Smuzhiyun 	int ret;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	pad_cfg = v4l2_subdev_alloc_pad_config(sd);
263*4882a593Smuzhiyun 	if (pad_cfg == NULL)
264*4882a593Smuzhiyun 		return -ENOMEM;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	if (!rvin_format_from_pixel(vin, pix->pixelformat))
267*4882a593Smuzhiyun 		pix->pixelformat = RVIN_DEFAULT_FORMAT;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	v4l2_fill_mbus_format(&format.format, pix, vin->mbus_code);
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	/* Allow the video device to override field and to scale */
272*4882a593Smuzhiyun 	field = pix->field;
273*4882a593Smuzhiyun 	width = pix->width;
274*4882a593Smuzhiyun 	height = pix->height;
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	ret = v4l2_subdev_call(sd, pad, set_fmt, pad_cfg, &format);
277*4882a593Smuzhiyun 	if (ret < 0 && ret != -ENOIOCTLCMD)
278*4882a593Smuzhiyun 		goto done;
279*4882a593Smuzhiyun 	ret = 0;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	v4l2_fill_pix_format(pix, &format.format);
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	if (src_rect) {
284*4882a593Smuzhiyun 		src_rect->top = 0;
285*4882a593Smuzhiyun 		src_rect->left = 0;
286*4882a593Smuzhiyun 		src_rect->width = pix->width;
287*4882a593Smuzhiyun 		src_rect->height = pix->height;
288*4882a593Smuzhiyun 	}
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	if (field != V4L2_FIELD_ANY)
291*4882a593Smuzhiyun 		pix->field = field;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	pix->width = width;
294*4882a593Smuzhiyun 	pix->height = height;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	rvin_format_align(vin, pix);
297*4882a593Smuzhiyun done:
298*4882a593Smuzhiyun 	v4l2_subdev_free_pad_config(pad_cfg);
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	return ret;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun 
rvin_querycap(struct file * file,void * priv,struct v4l2_capability * cap)303*4882a593Smuzhiyun static int rvin_querycap(struct file *file, void *priv,
304*4882a593Smuzhiyun 			 struct v4l2_capability *cap)
305*4882a593Smuzhiyun {
306*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
309*4882a593Smuzhiyun 	strscpy(cap->card, "R_Car_VIN", sizeof(cap->card));
310*4882a593Smuzhiyun 	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
311*4882a593Smuzhiyun 		 dev_name(vin->dev));
312*4882a593Smuzhiyun 	return 0;
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun 
rvin_try_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)315*4882a593Smuzhiyun static int rvin_try_fmt_vid_cap(struct file *file, void *priv,
316*4882a593Smuzhiyun 				struct v4l2_format *f)
317*4882a593Smuzhiyun {
318*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	return rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, NULL);
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun 
rvin_s_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)323*4882a593Smuzhiyun static int rvin_s_fmt_vid_cap(struct file *file, void *priv,
324*4882a593Smuzhiyun 			      struct v4l2_format *f)
325*4882a593Smuzhiyun {
326*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
327*4882a593Smuzhiyun 	struct v4l2_rect fmt_rect, src_rect;
328*4882a593Smuzhiyun 	int ret;
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	if (vb2_is_busy(&vin->queue))
331*4882a593Smuzhiyun 		return -EBUSY;
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	ret = rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix,
334*4882a593Smuzhiyun 			      &src_rect);
335*4882a593Smuzhiyun 	if (ret)
336*4882a593Smuzhiyun 		return ret;
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	vin->format = f->fmt.pix;
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	fmt_rect.top = 0;
341*4882a593Smuzhiyun 	fmt_rect.left = 0;
342*4882a593Smuzhiyun 	fmt_rect.width = vin->format.width;
343*4882a593Smuzhiyun 	fmt_rect.height = vin->format.height;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	v4l2_rect_map_inside(&vin->crop, &src_rect);
346*4882a593Smuzhiyun 	v4l2_rect_map_inside(&vin->compose, &fmt_rect);
347*4882a593Smuzhiyun 	vin->src_rect = src_rect;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	return 0;
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun 
rvin_g_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)352*4882a593Smuzhiyun static int rvin_g_fmt_vid_cap(struct file *file, void *priv,
353*4882a593Smuzhiyun 			      struct v4l2_format *f)
354*4882a593Smuzhiyun {
355*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	f->fmt.pix = vin->format;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	return 0;
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun 
rvin_enum_fmt_vid_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)362*4882a593Smuzhiyun static int rvin_enum_fmt_vid_cap(struct file *file, void *priv,
363*4882a593Smuzhiyun 				 struct v4l2_fmtdesc *f)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
366*4882a593Smuzhiyun 	unsigned int i;
367*4882a593Smuzhiyun 	int matched;
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	/*
370*4882a593Smuzhiyun 	 * If mbus_code is set only enumerate supported pixel formats for that
371*4882a593Smuzhiyun 	 * bus code. Converting from YCbCr to RGB and RGB to YCbCr is possible
372*4882a593Smuzhiyun 	 * with VIN, so all supported YCbCr and RGB media bus codes can produce
373*4882a593Smuzhiyun 	 * all of the related pixel formats. If mbus_code is not set enumerate
374*4882a593Smuzhiyun 	 * all possible pixelformats.
375*4882a593Smuzhiyun 	 *
376*4882a593Smuzhiyun 	 * TODO: Once raw MEDIA_BUS_FMT_SRGGB12_1X12 format is added to the
377*4882a593Smuzhiyun 	 * driver this needs to be extended so raw media bus code only result in
378*4882a593Smuzhiyun 	 * raw pixel format.
379*4882a593Smuzhiyun 	 */
380*4882a593Smuzhiyun 	switch (f->mbus_code) {
381*4882a593Smuzhiyun 	case 0:
382*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_YUYV8_1X16:
383*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_UYVY8_1X16:
384*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_UYVY8_2X8:
385*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_UYVY10_2X10:
386*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_RGB888_1X24:
387*4882a593Smuzhiyun 		break;
388*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_SBGGR8_1X8:
389*4882a593Smuzhiyun 		if (f->index)
390*4882a593Smuzhiyun 			return -EINVAL;
391*4882a593Smuzhiyun 		f->pixelformat = V4L2_PIX_FMT_SBGGR8;
392*4882a593Smuzhiyun 		return 0;
393*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_SGBRG8_1X8:
394*4882a593Smuzhiyun 		if (f->index)
395*4882a593Smuzhiyun 			return -EINVAL;
396*4882a593Smuzhiyun 		f->pixelformat = V4L2_PIX_FMT_SGBRG8;
397*4882a593Smuzhiyun 		return 0;
398*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_SGRBG8_1X8:
399*4882a593Smuzhiyun 		if (f->index)
400*4882a593Smuzhiyun 			return -EINVAL;
401*4882a593Smuzhiyun 		f->pixelformat = V4L2_PIX_FMT_SGRBG8;
402*4882a593Smuzhiyun 		return 0;
403*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_SRGGB8_1X8:
404*4882a593Smuzhiyun 		if (f->index)
405*4882a593Smuzhiyun 			return -EINVAL;
406*4882a593Smuzhiyun 		f->pixelformat = V4L2_PIX_FMT_SRGGB8;
407*4882a593Smuzhiyun 		return 0;
408*4882a593Smuzhiyun 	default:
409*4882a593Smuzhiyun 		return -EINVAL;
410*4882a593Smuzhiyun 	}
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	matched = -1;
413*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(rvin_formats); i++) {
414*4882a593Smuzhiyun 		if (rvin_format_from_pixel(vin, rvin_formats[i].fourcc))
415*4882a593Smuzhiyun 			matched++;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 		if (matched == f->index) {
418*4882a593Smuzhiyun 			f->pixelformat = rvin_formats[i].fourcc;
419*4882a593Smuzhiyun 			return 0;
420*4882a593Smuzhiyun 		}
421*4882a593Smuzhiyun 	}
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 	return -EINVAL;
424*4882a593Smuzhiyun }
425*4882a593Smuzhiyun 
rvin_g_selection(struct file * file,void * fh,struct v4l2_selection * s)426*4882a593Smuzhiyun static int rvin_g_selection(struct file *file, void *fh,
427*4882a593Smuzhiyun 			    struct v4l2_selection *s)
428*4882a593Smuzhiyun {
429*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
432*4882a593Smuzhiyun 		return -EINVAL;
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 	switch (s->target) {
435*4882a593Smuzhiyun 	case V4L2_SEL_TGT_CROP_BOUNDS:
436*4882a593Smuzhiyun 	case V4L2_SEL_TGT_CROP_DEFAULT:
437*4882a593Smuzhiyun 		s->r.left = s->r.top = 0;
438*4882a593Smuzhiyun 		s->r.width = vin->src_rect.width;
439*4882a593Smuzhiyun 		s->r.height = vin->src_rect.height;
440*4882a593Smuzhiyun 		break;
441*4882a593Smuzhiyun 	case V4L2_SEL_TGT_CROP:
442*4882a593Smuzhiyun 		s->r = vin->crop;
443*4882a593Smuzhiyun 		break;
444*4882a593Smuzhiyun 	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
445*4882a593Smuzhiyun 	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
446*4882a593Smuzhiyun 		s->r.left = s->r.top = 0;
447*4882a593Smuzhiyun 		s->r.width = vin->format.width;
448*4882a593Smuzhiyun 		s->r.height = vin->format.height;
449*4882a593Smuzhiyun 		break;
450*4882a593Smuzhiyun 	case V4L2_SEL_TGT_COMPOSE:
451*4882a593Smuzhiyun 		s->r = vin->compose;
452*4882a593Smuzhiyun 		break;
453*4882a593Smuzhiyun 	default:
454*4882a593Smuzhiyun 		return -EINVAL;
455*4882a593Smuzhiyun 	}
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	return 0;
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun 
rvin_s_selection(struct file * file,void * fh,struct v4l2_selection * s)460*4882a593Smuzhiyun static int rvin_s_selection(struct file *file, void *fh,
461*4882a593Smuzhiyun 			    struct v4l2_selection *s)
462*4882a593Smuzhiyun {
463*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
464*4882a593Smuzhiyun 	const struct rvin_video_format *fmt;
465*4882a593Smuzhiyun 	struct v4l2_rect r = s->r;
466*4882a593Smuzhiyun 	struct v4l2_rect max_rect;
467*4882a593Smuzhiyun 	struct v4l2_rect min_rect = {
468*4882a593Smuzhiyun 		.width = 6,
469*4882a593Smuzhiyun 		.height = 2,
470*4882a593Smuzhiyun 	};
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
473*4882a593Smuzhiyun 		return -EINVAL;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	v4l2_rect_set_min_size(&r, &min_rect);
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 	switch (s->target) {
478*4882a593Smuzhiyun 	case V4L2_SEL_TGT_CROP:
479*4882a593Smuzhiyun 		/* Can't crop outside of source input */
480*4882a593Smuzhiyun 		max_rect.top = max_rect.left = 0;
481*4882a593Smuzhiyun 		max_rect.width = vin->src_rect.width;
482*4882a593Smuzhiyun 		max_rect.height = vin->src_rect.height;
483*4882a593Smuzhiyun 		v4l2_rect_map_inside(&r, &max_rect);
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 		v4l_bound_align_image(&r.width, 6, vin->src_rect.width, 0,
486*4882a593Smuzhiyun 				      &r.height, 2, vin->src_rect.height, 0, 0);
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 		r.top  = clamp_t(s32, r.top, 0,
489*4882a593Smuzhiyun 				 vin->src_rect.height - r.height);
490*4882a593Smuzhiyun 		r.left = clamp_t(s32, r.left, 0, vin->src_rect.width - r.width);
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun 		vin->crop = s->r = r;
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 		vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n",
495*4882a593Smuzhiyun 			r.width, r.height, r.left, r.top,
496*4882a593Smuzhiyun 			vin->src_rect.width, vin->src_rect.height);
497*4882a593Smuzhiyun 		break;
498*4882a593Smuzhiyun 	case V4L2_SEL_TGT_COMPOSE:
499*4882a593Smuzhiyun 		/* Make sure compose rect fits inside output format */
500*4882a593Smuzhiyun 		max_rect.top = max_rect.left = 0;
501*4882a593Smuzhiyun 		max_rect.width = vin->format.width;
502*4882a593Smuzhiyun 		max_rect.height = vin->format.height;
503*4882a593Smuzhiyun 		v4l2_rect_map_inside(&r, &max_rect);
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun 		/*
506*4882a593Smuzhiyun 		 * Composing is done by adding a offset to the buffer address,
507*4882a593Smuzhiyun 		 * the HW wants this address to be aligned to HW_BUFFER_MASK.
508*4882a593Smuzhiyun 		 * Make sure the top and left values meets this requirement.
509*4882a593Smuzhiyun 		 */
510*4882a593Smuzhiyun 		while ((r.top * vin->format.bytesperline) & HW_BUFFER_MASK)
511*4882a593Smuzhiyun 			r.top--;
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 		fmt = rvin_format_from_pixel(vin, vin->format.pixelformat);
514*4882a593Smuzhiyun 		while ((r.left * fmt->bpp) & HW_BUFFER_MASK)
515*4882a593Smuzhiyun 			r.left--;
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 		vin->compose = s->r = r;
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 		vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n",
520*4882a593Smuzhiyun 			r.width, r.height, r.left, r.top,
521*4882a593Smuzhiyun 			vin->format.width, vin->format.height);
522*4882a593Smuzhiyun 		break;
523*4882a593Smuzhiyun 	default:
524*4882a593Smuzhiyun 		return -EINVAL;
525*4882a593Smuzhiyun 	}
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun 	/* HW supports modifying configuration while running */
528*4882a593Smuzhiyun 	rvin_crop_scale_comp(vin);
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 	return 0;
531*4882a593Smuzhiyun }
532*4882a593Smuzhiyun 
rvin_g_pixelaspect(struct file * file,void * priv,int type,struct v4l2_fract * f)533*4882a593Smuzhiyun static int rvin_g_pixelaspect(struct file *file, void *priv,
534*4882a593Smuzhiyun 			      int type, struct v4l2_fract *f)
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
537*4882a593Smuzhiyun 	struct v4l2_subdev *sd = vin_to_source(vin);
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
540*4882a593Smuzhiyun 		return -EINVAL;
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	return v4l2_subdev_call(sd, video, g_pixelaspect, f);
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun 
rvin_enum_input(struct file * file,void * priv,struct v4l2_input * i)545*4882a593Smuzhiyun static int rvin_enum_input(struct file *file, void *priv,
546*4882a593Smuzhiyun 			   struct v4l2_input *i)
547*4882a593Smuzhiyun {
548*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
549*4882a593Smuzhiyun 	struct v4l2_subdev *sd = vin_to_source(vin);
550*4882a593Smuzhiyun 	int ret;
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	if (i->index != 0)
553*4882a593Smuzhiyun 		return -EINVAL;
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	ret = v4l2_subdev_call(sd, video, g_input_status, &i->status);
556*4882a593Smuzhiyun 	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
557*4882a593Smuzhiyun 		return ret;
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	i->type = V4L2_INPUT_TYPE_CAMERA;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	if (v4l2_subdev_has_op(sd, pad, dv_timings_cap)) {
562*4882a593Smuzhiyun 		i->capabilities = V4L2_IN_CAP_DV_TIMINGS;
563*4882a593Smuzhiyun 		i->std = 0;
564*4882a593Smuzhiyun 	} else {
565*4882a593Smuzhiyun 		i->capabilities = V4L2_IN_CAP_STD;
566*4882a593Smuzhiyun 		i->std = vin->vdev.tvnorms;
567*4882a593Smuzhiyun 	}
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	strscpy(i->name, "Camera", sizeof(i->name));
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 	return 0;
572*4882a593Smuzhiyun }
573*4882a593Smuzhiyun 
rvin_g_input(struct file * file,void * priv,unsigned int * i)574*4882a593Smuzhiyun static int rvin_g_input(struct file *file, void *priv, unsigned int *i)
575*4882a593Smuzhiyun {
576*4882a593Smuzhiyun 	*i = 0;
577*4882a593Smuzhiyun 	return 0;
578*4882a593Smuzhiyun }
579*4882a593Smuzhiyun 
rvin_s_input(struct file * file,void * priv,unsigned int i)580*4882a593Smuzhiyun static int rvin_s_input(struct file *file, void *priv, unsigned int i)
581*4882a593Smuzhiyun {
582*4882a593Smuzhiyun 	if (i > 0)
583*4882a593Smuzhiyun 		return -EINVAL;
584*4882a593Smuzhiyun 	return 0;
585*4882a593Smuzhiyun }
586*4882a593Smuzhiyun 
rvin_querystd(struct file * file,void * priv,v4l2_std_id * a)587*4882a593Smuzhiyun static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a)
588*4882a593Smuzhiyun {
589*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
590*4882a593Smuzhiyun 	struct v4l2_subdev *sd = vin_to_source(vin);
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 	return v4l2_subdev_call(sd, video, querystd, a);
593*4882a593Smuzhiyun }
594*4882a593Smuzhiyun 
rvin_s_std(struct file * file,void * priv,v4l2_std_id a)595*4882a593Smuzhiyun static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a)
596*4882a593Smuzhiyun {
597*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
598*4882a593Smuzhiyun 	int ret;
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun 	ret = v4l2_subdev_call(vin_to_source(vin), video, s_std, a);
601*4882a593Smuzhiyun 	if (ret < 0)
602*4882a593Smuzhiyun 		return ret;
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	vin->std = a;
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	/* Changing the standard will change the width/height */
607*4882a593Smuzhiyun 	return rvin_reset_format(vin);
608*4882a593Smuzhiyun }
609*4882a593Smuzhiyun 
rvin_g_std(struct file * file,void * priv,v4l2_std_id * a)610*4882a593Smuzhiyun static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a)
611*4882a593Smuzhiyun {
612*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
613*4882a593Smuzhiyun 
614*4882a593Smuzhiyun 	if (v4l2_subdev_has_op(vin_to_source(vin), pad, dv_timings_cap))
615*4882a593Smuzhiyun 		return -ENOIOCTLCMD;
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun 	*a = vin->std;
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	return 0;
620*4882a593Smuzhiyun }
621*4882a593Smuzhiyun 
rvin_subscribe_event(struct v4l2_fh * fh,const struct v4l2_event_subscription * sub)622*4882a593Smuzhiyun static int rvin_subscribe_event(struct v4l2_fh *fh,
623*4882a593Smuzhiyun 				const struct v4l2_event_subscription *sub)
624*4882a593Smuzhiyun {
625*4882a593Smuzhiyun 	switch (sub->type) {
626*4882a593Smuzhiyun 	case V4L2_EVENT_SOURCE_CHANGE:
627*4882a593Smuzhiyun 		return v4l2_event_subscribe(fh, sub, 4, NULL);
628*4882a593Smuzhiyun 	}
629*4882a593Smuzhiyun 	return v4l2_ctrl_subscribe_event(fh, sub);
630*4882a593Smuzhiyun }
631*4882a593Smuzhiyun 
rvin_enum_dv_timings(struct file * file,void * priv_fh,struct v4l2_enum_dv_timings * timings)632*4882a593Smuzhiyun static int rvin_enum_dv_timings(struct file *file, void *priv_fh,
633*4882a593Smuzhiyun 				struct v4l2_enum_dv_timings *timings)
634*4882a593Smuzhiyun {
635*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
636*4882a593Smuzhiyun 	struct v4l2_subdev *sd = vin_to_source(vin);
637*4882a593Smuzhiyun 	int ret;
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 	if (timings->pad)
640*4882a593Smuzhiyun 		return -EINVAL;
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun 	timings->pad = vin->parallel->sink_pad;
643*4882a593Smuzhiyun 
644*4882a593Smuzhiyun 	ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings);
645*4882a593Smuzhiyun 
646*4882a593Smuzhiyun 	timings->pad = 0;
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	return ret;
649*4882a593Smuzhiyun }
650*4882a593Smuzhiyun 
rvin_s_dv_timings(struct file * file,void * priv_fh,struct v4l2_dv_timings * timings)651*4882a593Smuzhiyun static int rvin_s_dv_timings(struct file *file, void *priv_fh,
652*4882a593Smuzhiyun 			     struct v4l2_dv_timings *timings)
653*4882a593Smuzhiyun {
654*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
655*4882a593Smuzhiyun 	struct v4l2_subdev *sd = vin_to_source(vin);
656*4882a593Smuzhiyun 	int ret;
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun 	ret = v4l2_subdev_call(sd, video, s_dv_timings, timings);
659*4882a593Smuzhiyun 	if (ret)
660*4882a593Smuzhiyun 		return ret;
661*4882a593Smuzhiyun 
662*4882a593Smuzhiyun 	/* Changing the timings will change the width/height */
663*4882a593Smuzhiyun 	return rvin_reset_format(vin);
664*4882a593Smuzhiyun }
665*4882a593Smuzhiyun 
rvin_g_dv_timings(struct file * file,void * priv_fh,struct v4l2_dv_timings * timings)666*4882a593Smuzhiyun static int rvin_g_dv_timings(struct file *file, void *priv_fh,
667*4882a593Smuzhiyun 			     struct v4l2_dv_timings *timings)
668*4882a593Smuzhiyun {
669*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
670*4882a593Smuzhiyun 	struct v4l2_subdev *sd = vin_to_source(vin);
671*4882a593Smuzhiyun 
672*4882a593Smuzhiyun 	return v4l2_subdev_call(sd, video, g_dv_timings, timings);
673*4882a593Smuzhiyun }
674*4882a593Smuzhiyun 
rvin_query_dv_timings(struct file * file,void * priv_fh,struct v4l2_dv_timings * timings)675*4882a593Smuzhiyun static int rvin_query_dv_timings(struct file *file, void *priv_fh,
676*4882a593Smuzhiyun 				 struct v4l2_dv_timings *timings)
677*4882a593Smuzhiyun {
678*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
679*4882a593Smuzhiyun 	struct v4l2_subdev *sd = vin_to_source(vin);
680*4882a593Smuzhiyun 
681*4882a593Smuzhiyun 	return v4l2_subdev_call(sd, video, query_dv_timings, timings);
682*4882a593Smuzhiyun }
683*4882a593Smuzhiyun 
rvin_dv_timings_cap(struct file * file,void * priv_fh,struct v4l2_dv_timings_cap * cap)684*4882a593Smuzhiyun static int rvin_dv_timings_cap(struct file *file, void *priv_fh,
685*4882a593Smuzhiyun 			       struct v4l2_dv_timings_cap *cap)
686*4882a593Smuzhiyun {
687*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
688*4882a593Smuzhiyun 	struct v4l2_subdev *sd = vin_to_source(vin);
689*4882a593Smuzhiyun 	int ret;
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	if (cap->pad)
692*4882a593Smuzhiyun 		return -EINVAL;
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun 	cap->pad = vin->parallel->sink_pad;
695*4882a593Smuzhiyun 
696*4882a593Smuzhiyun 	ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap);
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	cap->pad = 0;
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun 	return ret;
701*4882a593Smuzhiyun }
702*4882a593Smuzhiyun 
rvin_g_edid(struct file * file,void * fh,struct v4l2_edid * edid)703*4882a593Smuzhiyun static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
704*4882a593Smuzhiyun {
705*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
706*4882a593Smuzhiyun 	struct v4l2_subdev *sd = vin_to_source(vin);
707*4882a593Smuzhiyun 	int ret;
708*4882a593Smuzhiyun 
709*4882a593Smuzhiyun 	if (edid->pad)
710*4882a593Smuzhiyun 		return -EINVAL;
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun 	edid->pad = vin->parallel->sink_pad;
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 	ret = v4l2_subdev_call(sd, pad, get_edid, edid);
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 	edid->pad = 0;
717*4882a593Smuzhiyun 
718*4882a593Smuzhiyun 	return ret;
719*4882a593Smuzhiyun }
720*4882a593Smuzhiyun 
rvin_s_edid(struct file * file,void * fh,struct v4l2_edid * edid)721*4882a593Smuzhiyun static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
722*4882a593Smuzhiyun {
723*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
724*4882a593Smuzhiyun 	struct v4l2_subdev *sd = vin_to_source(vin);
725*4882a593Smuzhiyun 	int ret;
726*4882a593Smuzhiyun 
727*4882a593Smuzhiyun 	if (edid->pad)
728*4882a593Smuzhiyun 		return -EINVAL;
729*4882a593Smuzhiyun 
730*4882a593Smuzhiyun 	edid->pad = vin->parallel->sink_pad;
731*4882a593Smuzhiyun 
732*4882a593Smuzhiyun 	ret = v4l2_subdev_call(sd, pad, set_edid, edid);
733*4882a593Smuzhiyun 
734*4882a593Smuzhiyun 	edid->pad = 0;
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun 	return ret;
737*4882a593Smuzhiyun }
738*4882a593Smuzhiyun 
739*4882a593Smuzhiyun static const struct v4l2_ioctl_ops rvin_ioctl_ops = {
740*4882a593Smuzhiyun 	.vidioc_querycap		= rvin_querycap,
741*4882a593Smuzhiyun 	.vidioc_try_fmt_vid_cap		= rvin_try_fmt_vid_cap,
742*4882a593Smuzhiyun 	.vidioc_g_fmt_vid_cap		= rvin_g_fmt_vid_cap,
743*4882a593Smuzhiyun 	.vidioc_s_fmt_vid_cap		= rvin_s_fmt_vid_cap,
744*4882a593Smuzhiyun 	.vidioc_enum_fmt_vid_cap	= rvin_enum_fmt_vid_cap,
745*4882a593Smuzhiyun 
746*4882a593Smuzhiyun 	.vidioc_g_selection		= rvin_g_selection,
747*4882a593Smuzhiyun 	.vidioc_s_selection		= rvin_s_selection,
748*4882a593Smuzhiyun 
749*4882a593Smuzhiyun 	.vidioc_g_pixelaspect		= rvin_g_pixelaspect,
750*4882a593Smuzhiyun 
751*4882a593Smuzhiyun 	.vidioc_enum_input		= rvin_enum_input,
752*4882a593Smuzhiyun 	.vidioc_g_input			= rvin_g_input,
753*4882a593Smuzhiyun 	.vidioc_s_input			= rvin_s_input,
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun 	.vidioc_dv_timings_cap		= rvin_dv_timings_cap,
756*4882a593Smuzhiyun 	.vidioc_enum_dv_timings		= rvin_enum_dv_timings,
757*4882a593Smuzhiyun 	.vidioc_g_dv_timings		= rvin_g_dv_timings,
758*4882a593Smuzhiyun 	.vidioc_s_dv_timings		= rvin_s_dv_timings,
759*4882a593Smuzhiyun 	.vidioc_query_dv_timings	= rvin_query_dv_timings,
760*4882a593Smuzhiyun 
761*4882a593Smuzhiyun 	.vidioc_g_edid			= rvin_g_edid,
762*4882a593Smuzhiyun 	.vidioc_s_edid			= rvin_s_edid,
763*4882a593Smuzhiyun 
764*4882a593Smuzhiyun 	.vidioc_querystd		= rvin_querystd,
765*4882a593Smuzhiyun 	.vidioc_g_std			= rvin_g_std,
766*4882a593Smuzhiyun 	.vidioc_s_std			= rvin_s_std,
767*4882a593Smuzhiyun 
768*4882a593Smuzhiyun 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
769*4882a593Smuzhiyun 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
770*4882a593Smuzhiyun 	.vidioc_querybuf		= vb2_ioctl_querybuf,
771*4882a593Smuzhiyun 	.vidioc_qbuf			= vb2_ioctl_qbuf,
772*4882a593Smuzhiyun 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
773*4882a593Smuzhiyun 	.vidioc_expbuf			= vb2_ioctl_expbuf,
774*4882a593Smuzhiyun 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
775*4882a593Smuzhiyun 	.vidioc_streamon		= vb2_ioctl_streamon,
776*4882a593Smuzhiyun 	.vidioc_streamoff		= vb2_ioctl_streamoff,
777*4882a593Smuzhiyun 
778*4882a593Smuzhiyun 	.vidioc_log_status		= v4l2_ctrl_log_status,
779*4882a593Smuzhiyun 	.vidioc_subscribe_event		= rvin_subscribe_event,
780*4882a593Smuzhiyun 	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
781*4882a593Smuzhiyun };
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
784*4882a593Smuzhiyun  * V4L2 Media Controller
785*4882a593Smuzhiyun  */
786*4882a593Smuzhiyun 
rvin_mc_try_format(struct rvin_dev * vin,struct v4l2_pix_format * pix)787*4882a593Smuzhiyun static void rvin_mc_try_format(struct rvin_dev *vin,
788*4882a593Smuzhiyun 			       struct v4l2_pix_format *pix)
789*4882a593Smuzhiyun {
790*4882a593Smuzhiyun 	/*
791*4882a593Smuzhiyun 	 * The V4L2 specification clearly documents the colorspace fields
792*4882a593Smuzhiyun 	 * as being set by drivers for capture devices. Using the values
793*4882a593Smuzhiyun 	 * supplied by userspace thus wouldn't comply with the API. Until
794*4882a593Smuzhiyun 	 * the API is updated force fixed values.
795*4882a593Smuzhiyun 	 */
796*4882a593Smuzhiyun 	pix->colorspace = RVIN_DEFAULT_COLORSPACE;
797*4882a593Smuzhiyun 	pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);
798*4882a593Smuzhiyun 	pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
799*4882a593Smuzhiyun 	pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, pix->colorspace,
800*4882a593Smuzhiyun 							  pix->ycbcr_enc);
801*4882a593Smuzhiyun 
802*4882a593Smuzhiyun 	rvin_format_align(vin, pix);
803*4882a593Smuzhiyun }
804*4882a593Smuzhiyun 
rvin_mc_try_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)805*4882a593Smuzhiyun static int rvin_mc_try_fmt_vid_cap(struct file *file, void *priv,
806*4882a593Smuzhiyun 				   struct v4l2_format *f)
807*4882a593Smuzhiyun {
808*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun 	rvin_mc_try_format(vin, &f->fmt.pix);
811*4882a593Smuzhiyun 
812*4882a593Smuzhiyun 	return 0;
813*4882a593Smuzhiyun }
814*4882a593Smuzhiyun 
rvin_mc_s_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)815*4882a593Smuzhiyun static int rvin_mc_s_fmt_vid_cap(struct file *file, void *priv,
816*4882a593Smuzhiyun 				 struct v4l2_format *f)
817*4882a593Smuzhiyun {
818*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
819*4882a593Smuzhiyun 
820*4882a593Smuzhiyun 	if (vb2_is_busy(&vin->queue))
821*4882a593Smuzhiyun 		return -EBUSY;
822*4882a593Smuzhiyun 
823*4882a593Smuzhiyun 	rvin_mc_try_format(vin, &f->fmt.pix);
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 	vin->format = f->fmt.pix;
826*4882a593Smuzhiyun 
827*4882a593Smuzhiyun 	vin->crop.top = 0;
828*4882a593Smuzhiyun 	vin->crop.left = 0;
829*4882a593Smuzhiyun 	vin->crop.width = vin->format.width;
830*4882a593Smuzhiyun 	vin->crop.height = vin->format.height;
831*4882a593Smuzhiyun 	vin->compose = vin->crop;
832*4882a593Smuzhiyun 
833*4882a593Smuzhiyun 	return 0;
834*4882a593Smuzhiyun }
835*4882a593Smuzhiyun 
836*4882a593Smuzhiyun static const struct v4l2_ioctl_ops rvin_mc_ioctl_ops = {
837*4882a593Smuzhiyun 	.vidioc_querycap		= rvin_querycap,
838*4882a593Smuzhiyun 	.vidioc_try_fmt_vid_cap		= rvin_mc_try_fmt_vid_cap,
839*4882a593Smuzhiyun 	.vidioc_g_fmt_vid_cap		= rvin_g_fmt_vid_cap,
840*4882a593Smuzhiyun 	.vidioc_s_fmt_vid_cap		= rvin_mc_s_fmt_vid_cap,
841*4882a593Smuzhiyun 	.vidioc_enum_fmt_vid_cap	= rvin_enum_fmt_vid_cap,
842*4882a593Smuzhiyun 
843*4882a593Smuzhiyun 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
844*4882a593Smuzhiyun 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
845*4882a593Smuzhiyun 	.vidioc_querybuf		= vb2_ioctl_querybuf,
846*4882a593Smuzhiyun 	.vidioc_qbuf			= vb2_ioctl_qbuf,
847*4882a593Smuzhiyun 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
848*4882a593Smuzhiyun 	.vidioc_expbuf			= vb2_ioctl_expbuf,
849*4882a593Smuzhiyun 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
850*4882a593Smuzhiyun 	.vidioc_streamon		= vb2_ioctl_streamon,
851*4882a593Smuzhiyun 	.vidioc_streamoff		= vb2_ioctl_streamoff,
852*4882a593Smuzhiyun 
853*4882a593Smuzhiyun 	.vidioc_log_status		= v4l2_ctrl_log_status,
854*4882a593Smuzhiyun 	.vidioc_subscribe_event		= rvin_subscribe_event,
855*4882a593Smuzhiyun 	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
856*4882a593Smuzhiyun };
857*4882a593Smuzhiyun 
858*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
859*4882a593Smuzhiyun  * File Operations
860*4882a593Smuzhiyun  */
861*4882a593Smuzhiyun 
rvin_power_parallel(struct rvin_dev * vin,bool on)862*4882a593Smuzhiyun static int rvin_power_parallel(struct rvin_dev *vin, bool on)
863*4882a593Smuzhiyun {
864*4882a593Smuzhiyun 	struct v4l2_subdev *sd = vin_to_source(vin);
865*4882a593Smuzhiyun 	int power = on ? 1 : 0;
866*4882a593Smuzhiyun 	int ret;
867*4882a593Smuzhiyun 
868*4882a593Smuzhiyun 	ret = v4l2_subdev_call(sd, core, s_power, power);
869*4882a593Smuzhiyun 	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
870*4882a593Smuzhiyun 		return ret;
871*4882a593Smuzhiyun 
872*4882a593Smuzhiyun 	return 0;
873*4882a593Smuzhiyun }
874*4882a593Smuzhiyun 
rvin_open(struct file * file)875*4882a593Smuzhiyun static int rvin_open(struct file *file)
876*4882a593Smuzhiyun {
877*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
878*4882a593Smuzhiyun 	int ret;
879*4882a593Smuzhiyun 
880*4882a593Smuzhiyun 	ret = pm_runtime_get_sync(vin->dev);
881*4882a593Smuzhiyun 	if (ret < 0) {
882*4882a593Smuzhiyun 		pm_runtime_put_noidle(vin->dev);
883*4882a593Smuzhiyun 		return ret;
884*4882a593Smuzhiyun 	}
885*4882a593Smuzhiyun 
886*4882a593Smuzhiyun 	ret = mutex_lock_interruptible(&vin->lock);
887*4882a593Smuzhiyun 	if (ret)
888*4882a593Smuzhiyun 		goto err_pm;
889*4882a593Smuzhiyun 
890*4882a593Smuzhiyun 	file->private_data = vin;
891*4882a593Smuzhiyun 
892*4882a593Smuzhiyun 	ret = v4l2_fh_open(file);
893*4882a593Smuzhiyun 	if (ret)
894*4882a593Smuzhiyun 		goto err_unlock;
895*4882a593Smuzhiyun 
896*4882a593Smuzhiyun 	if (vin->info->use_mc)
897*4882a593Smuzhiyun 		ret = v4l2_pipeline_pm_get(&vin->vdev.entity);
898*4882a593Smuzhiyun 	else if (v4l2_fh_is_singular_file(file))
899*4882a593Smuzhiyun 		ret = rvin_power_parallel(vin, true);
900*4882a593Smuzhiyun 
901*4882a593Smuzhiyun 	if (ret < 0)
902*4882a593Smuzhiyun 		goto err_open;
903*4882a593Smuzhiyun 
904*4882a593Smuzhiyun 	ret = v4l2_ctrl_handler_setup(&vin->ctrl_handler);
905*4882a593Smuzhiyun 	if (ret)
906*4882a593Smuzhiyun 		goto err_power;
907*4882a593Smuzhiyun 
908*4882a593Smuzhiyun 	mutex_unlock(&vin->lock);
909*4882a593Smuzhiyun 
910*4882a593Smuzhiyun 	return 0;
911*4882a593Smuzhiyun err_power:
912*4882a593Smuzhiyun 	if (vin->info->use_mc)
913*4882a593Smuzhiyun 		v4l2_pipeline_pm_put(&vin->vdev.entity);
914*4882a593Smuzhiyun 	else if (v4l2_fh_is_singular_file(file))
915*4882a593Smuzhiyun 		rvin_power_parallel(vin, false);
916*4882a593Smuzhiyun err_open:
917*4882a593Smuzhiyun 	v4l2_fh_release(file);
918*4882a593Smuzhiyun err_unlock:
919*4882a593Smuzhiyun 	mutex_unlock(&vin->lock);
920*4882a593Smuzhiyun err_pm:
921*4882a593Smuzhiyun 	pm_runtime_put(vin->dev);
922*4882a593Smuzhiyun 
923*4882a593Smuzhiyun 	return ret;
924*4882a593Smuzhiyun }
925*4882a593Smuzhiyun 
rvin_release(struct file * file)926*4882a593Smuzhiyun static int rvin_release(struct file *file)
927*4882a593Smuzhiyun {
928*4882a593Smuzhiyun 	struct rvin_dev *vin = video_drvdata(file);
929*4882a593Smuzhiyun 	bool fh_singular;
930*4882a593Smuzhiyun 	int ret;
931*4882a593Smuzhiyun 
932*4882a593Smuzhiyun 	mutex_lock(&vin->lock);
933*4882a593Smuzhiyun 
934*4882a593Smuzhiyun 	/* Save the singular status before we call the clean-up helper */
935*4882a593Smuzhiyun 	fh_singular = v4l2_fh_is_singular_file(file);
936*4882a593Smuzhiyun 
937*4882a593Smuzhiyun 	/* the release helper will cleanup any on-going streaming */
938*4882a593Smuzhiyun 	ret = _vb2_fop_release(file, NULL);
939*4882a593Smuzhiyun 
940*4882a593Smuzhiyun 	if (vin->info->use_mc) {
941*4882a593Smuzhiyun 		v4l2_pipeline_pm_put(&vin->vdev.entity);
942*4882a593Smuzhiyun 	} else {
943*4882a593Smuzhiyun 		if (fh_singular)
944*4882a593Smuzhiyun 			rvin_power_parallel(vin, false);
945*4882a593Smuzhiyun 	}
946*4882a593Smuzhiyun 
947*4882a593Smuzhiyun 	mutex_unlock(&vin->lock);
948*4882a593Smuzhiyun 
949*4882a593Smuzhiyun 	pm_runtime_put(vin->dev);
950*4882a593Smuzhiyun 
951*4882a593Smuzhiyun 	return ret;
952*4882a593Smuzhiyun }
953*4882a593Smuzhiyun 
954*4882a593Smuzhiyun static const struct v4l2_file_operations rvin_fops = {
955*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
956*4882a593Smuzhiyun 	.unlocked_ioctl	= video_ioctl2,
957*4882a593Smuzhiyun 	.open		= rvin_open,
958*4882a593Smuzhiyun 	.release	= rvin_release,
959*4882a593Smuzhiyun 	.poll		= vb2_fop_poll,
960*4882a593Smuzhiyun 	.mmap		= vb2_fop_mmap,
961*4882a593Smuzhiyun 	.read		= vb2_fop_read,
962*4882a593Smuzhiyun };
963*4882a593Smuzhiyun 
rvin_v4l2_unregister(struct rvin_dev * vin)964*4882a593Smuzhiyun void rvin_v4l2_unregister(struct rvin_dev *vin)
965*4882a593Smuzhiyun {
966*4882a593Smuzhiyun 	if (!video_is_registered(&vin->vdev))
967*4882a593Smuzhiyun 		return;
968*4882a593Smuzhiyun 
969*4882a593Smuzhiyun 	v4l2_info(&vin->v4l2_dev, "Removing %s\n",
970*4882a593Smuzhiyun 		  video_device_node_name(&vin->vdev));
971*4882a593Smuzhiyun 
972*4882a593Smuzhiyun 	/* Checks internally if vdev have been init or not */
973*4882a593Smuzhiyun 	video_unregister_device(&vin->vdev);
974*4882a593Smuzhiyun }
975*4882a593Smuzhiyun 
rvin_notify(struct v4l2_subdev * sd,unsigned int notification,void * arg)976*4882a593Smuzhiyun static void rvin_notify(struct v4l2_subdev *sd,
977*4882a593Smuzhiyun 			unsigned int notification, void *arg)
978*4882a593Smuzhiyun {
979*4882a593Smuzhiyun 	struct rvin_dev *vin =
980*4882a593Smuzhiyun 		container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev);
981*4882a593Smuzhiyun 
982*4882a593Smuzhiyun 	switch (notification) {
983*4882a593Smuzhiyun 	case V4L2_DEVICE_NOTIFY_EVENT:
984*4882a593Smuzhiyun 		v4l2_event_queue(&vin->vdev, arg);
985*4882a593Smuzhiyun 		break;
986*4882a593Smuzhiyun 	default:
987*4882a593Smuzhiyun 		break;
988*4882a593Smuzhiyun 	}
989*4882a593Smuzhiyun }
990*4882a593Smuzhiyun 
rvin_v4l2_register(struct rvin_dev * vin)991*4882a593Smuzhiyun int rvin_v4l2_register(struct rvin_dev *vin)
992*4882a593Smuzhiyun {
993*4882a593Smuzhiyun 	struct video_device *vdev = &vin->vdev;
994*4882a593Smuzhiyun 	int ret;
995*4882a593Smuzhiyun 
996*4882a593Smuzhiyun 	vin->v4l2_dev.notify = rvin_notify;
997*4882a593Smuzhiyun 
998*4882a593Smuzhiyun 	/* video node */
999*4882a593Smuzhiyun 	vdev->v4l2_dev = &vin->v4l2_dev;
1000*4882a593Smuzhiyun 	vdev->queue = &vin->queue;
1001*4882a593Smuzhiyun 	snprintf(vdev->name, sizeof(vdev->name), "VIN%u output", vin->id);
1002*4882a593Smuzhiyun 	vdev->release = video_device_release_empty;
1003*4882a593Smuzhiyun 	vdev->lock = &vin->lock;
1004*4882a593Smuzhiyun 	vdev->fops = &rvin_fops;
1005*4882a593Smuzhiyun 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
1006*4882a593Smuzhiyun 		V4L2_CAP_READWRITE;
1007*4882a593Smuzhiyun 
1008*4882a593Smuzhiyun 	/* Set a default format */
1009*4882a593Smuzhiyun 	vin->format.pixelformat	= RVIN_DEFAULT_FORMAT;
1010*4882a593Smuzhiyun 	vin->format.width = RVIN_DEFAULT_WIDTH;
1011*4882a593Smuzhiyun 	vin->format.height = RVIN_DEFAULT_HEIGHT;
1012*4882a593Smuzhiyun 	vin->format.field = RVIN_DEFAULT_FIELD;
1013*4882a593Smuzhiyun 	vin->format.colorspace = RVIN_DEFAULT_COLORSPACE;
1014*4882a593Smuzhiyun 
1015*4882a593Smuzhiyun 	if (vin->info->use_mc) {
1016*4882a593Smuzhiyun 		vdev->device_caps |= V4L2_CAP_IO_MC;
1017*4882a593Smuzhiyun 		vdev->ioctl_ops = &rvin_mc_ioctl_ops;
1018*4882a593Smuzhiyun 	} else {
1019*4882a593Smuzhiyun 		vdev->ioctl_ops = &rvin_ioctl_ops;
1020*4882a593Smuzhiyun 		rvin_reset_format(vin);
1021*4882a593Smuzhiyun 	}
1022*4882a593Smuzhiyun 
1023*4882a593Smuzhiyun 	rvin_format_align(vin, &vin->format);
1024*4882a593Smuzhiyun 
1025*4882a593Smuzhiyun 	ret = video_register_device(&vin->vdev, VFL_TYPE_VIDEO, -1);
1026*4882a593Smuzhiyun 	if (ret) {
1027*4882a593Smuzhiyun 		vin_err(vin, "Failed to register video device\n");
1028*4882a593Smuzhiyun 		return ret;
1029*4882a593Smuzhiyun 	}
1030*4882a593Smuzhiyun 
1031*4882a593Smuzhiyun 	video_set_drvdata(&vin->vdev, vin);
1032*4882a593Smuzhiyun 
1033*4882a593Smuzhiyun 	v4l2_info(&vin->v4l2_dev, "Device registered as %s\n",
1034*4882a593Smuzhiyun 		  video_device_node_name(&vin->vdev));
1035*4882a593Smuzhiyun 
1036*4882a593Smuzhiyun 	return ret;
1037*4882a593Smuzhiyun }
1038