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