1b873663bSTodor Tomov // SPDX-License-Identifier: GPL-2.0
2ec6859b2STodor Tomov /*
3ec6859b2STodor Tomov  * camss-video.c
4ec6859b2STodor Tomov  *
5ec6859b2STodor Tomov  * Qualcomm MSM Camera Subsystem - V4L2 device node
6ec6859b2STodor Tomov  *
7ec6859b2STodor Tomov  * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
8ec6859b2STodor Tomov  * Copyright (C) 2015-2018 Linaro Ltd.
9ec6859b2STodor Tomov  */
10ec6859b2STodor Tomov #include <linux/slab.h>
11ec6859b2STodor Tomov #include <media/media-entity.h>
12ec6859b2STodor Tomov #include <media/v4l2-dev.h>
13ec6859b2STodor Tomov #include <media/v4l2-device.h>
14ec6859b2STodor Tomov #include <media/v4l2-ioctl.h>
15ec6859b2STodor Tomov #include <media/v4l2-mc.h>
16ec6859b2STodor Tomov #include <media/videobuf2-dma-sg.h>
17ec6859b2STodor Tomov 
18ec6859b2STodor Tomov #include "camss-video.h"
19ec6859b2STodor Tomov #include "camss.h"
20ec6859b2STodor Tomov 
2135493d65SAndrey Konovalov #define CAMSS_FRAME_MIN_WIDTH		1
2235493d65SAndrey Konovalov #define CAMSS_FRAME_MAX_WIDTH		8191
2335493d65SAndrey Konovalov #define CAMSS_FRAME_MIN_HEIGHT		1
2435493d65SAndrey Konovalov #define CAMSS_FRAME_MAX_HEIGHT_RDI	8191
2535493d65SAndrey Konovalov #define CAMSS_FRAME_MAX_HEIGHT_PIX	4096
2635493d65SAndrey Konovalov 
27ec6859b2STodor Tomov struct fract {
28ec6859b2STodor Tomov 	u8 numerator;
29ec6859b2STodor Tomov 	u8 denominator;
30ec6859b2STodor Tomov };
31ec6859b2STodor Tomov 
32ec6859b2STodor Tomov /*
33ec6859b2STodor Tomov  * struct camss_format_info - ISP media bus format information
34ec6859b2STodor Tomov  * @code: V4L2 media bus format code
35ec6859b2STodor Tomov  * @pixelformat: V4L2 pixel format FCC identifier
36ec6859b2STodor Tomov  * @planes: Number of planes
37ec6859b2STodor Tomov  * @hsub: Horizontal subsampling (for each plane)
38ec6859b2STodor Tomov  * @vsub: Vertical subsampling (for each plane)
39ec6859b2STodor Tomov  * @bpp: Bits per pixel when stored in memory (for each plane)
40ec6859b2STodor Tomov  */
41ec6859b2STodor Tomov struct camss_format_info {
42ec6859b2STodor Tomov 	u32 code;
43ec6859b2STodor Tomov 	u32 pixelformat;
44ec6859b2STodor Tomov 	u8 planes;
45ec6859b2STodor Tomov 	struct fract hsub[3];
46ec6859b2STodor Tomov 	struct fract vsub[3];
47ec6859b2STodor Tomov 	unsigned int bpp[3];
48ec6859b2STodor Tomov };
49ec6859b2STodor Tomov 
50cba3819dSTodor Tomov static const struct camss_format_info formats_rdi_8x16[] = {
51ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
52ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
53ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
54ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
55ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
56ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
57ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1,
58ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
59ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
60ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
61ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
62ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
63ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
64ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
65ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
66ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
67ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1,
68ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
69ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1,
70ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
71ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1,
72ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
73ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1,
74ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
75ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1,
76ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
77ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1,
78ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
79ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1,
80ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
81ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
82ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
83cc8fe073STodor Tomov 	{ MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1,
84cc8fe073STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
85ec6859b2STodor Tomov };
86ec6859b2STodor Tomov 
87cba3819dSTodor Tomov static const struct camss_format_info formats_rdi_8x96[] = {
88cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
89cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
90cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
91cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
92cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
93cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
94cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1,
95cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
96cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
97cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
98cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
99cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
100cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
101cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
102cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
103cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
104cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1,
105cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
106cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1,
107cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
108cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1,
109cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
110cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1,
111cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
1125019d7c8STodor Tomov 	{ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1,
1135019d7c8STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
114cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1,
115cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
116cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1,
117cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
118cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1,
119cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
120cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
121cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
122f476fb56STodor Tomov 	{ MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1,
123f476fb56STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
124f476fb56STodor Tomov 	{ MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1,
125f476fb56STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
126f476fb56STodor Tomov 	{ MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1,
127f476fb56STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
128f476fb56STodor Tomov 	{ MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1,
129f476fb56STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
130cc8fe073STodor Tomov 	{ MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1,
131cc8fe073STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
132cc8fe073STodor Tomov 	{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1,
133cc8fe073STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
134cba3819dSTodor Tomov };
135cba3819dSTodor Tomov 
1367319cdf1SRobert Foss static const struct camss_format_info formats_rdi_845[] = {
1377319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
1387319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
1397319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
1407319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
1417319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
1427319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
1437319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1,
1447319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
1457319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
1467319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
1477319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
1487319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
1497319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
1507319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
1517319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
1527319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
1537319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1,
1547319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
1557319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1,
1567319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
1577319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1,
1587319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
1597319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1,
1607319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
1617319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1,
1627319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
1637319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1,
1647319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
1657319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1,
1667319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
1677319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1,
1687319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
1697319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
1707319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
1717319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1,
1727319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
1737319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1,
1747319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
1757319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1,
1767319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
1777319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1,
1787319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
179e53d6608SJonathan Marek 	{ MEDIA_BUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 1,
180e53d6608SJonathan Marek 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
1817319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1,
1827319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
1837319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1,
1847319cdf1SRobert Foss 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
1857319cdf1SRobert Foss };
1867319cdf1SRobert Foss 
187cba3819dSTodor Tomov static const struct camss_format_info formats_pix_8x16[] = {
188cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1,
189cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
190cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1,
191cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
192cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
193cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
194cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
195cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
196cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1,
197cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
198cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1,
199cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
200cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
201cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
202cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
203cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
204cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1,
205cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
206cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1,
207cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
208cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1,
209cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
210cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1,
211cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
212cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1,
213cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
214cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1,
215cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
216cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1,
217cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
218cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1,
219cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
220cba3819dSTodor Tomov };
221cba3819dSTodor Tomov 
222cba3819dSTodor Tomov static const struct camss_format_info formats_pix_8x96[] = {
223ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1,
224ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
225ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1,
226ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
227ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
228ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
229ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
230ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
231ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1,
232ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
233ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1,
234ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
235ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
236ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
237ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
238ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
239ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1,
240ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
241ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1,
242ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
243ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1,
244ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
245ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1,
246ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
247ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1,
248ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
249ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1,
250ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
251ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1,
252ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
253ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1,
254ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
255312e1c85STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
256312e1c85STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
257312e1c85STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
258312e1c85STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
259312e1c85STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
260312e1c85STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
261312e1c85STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1,
262312e1c85STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
263ec6859b2STodor Tomov };
264ec6859b2STodor Tomov 
265ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
266ec6859b2STodor Tomov  * Helper functions
267ec6859b2STodor Tomov  */
268ec6859b2STodor Tomov 
269ec6859b2STodor Tomov static int video_find_format(u32 code, u32 pixelformat,
270ec6859b2STodor Tomov 			     const struct camss_format_info *formats,
271ec6859b2STodor Tomov 			     unsigned int nformats)
272ec6859b2STodor Tomov {
273ec6859b2STodor Tomov 	int i;
274ec6859b2STodor Tomov 
275ec6859b2STodor Tomov 	for (i = 0; i < nformats; i++) {
276ec6859b2STodor Tomov 		if (formats[i].code == code &&
277ec6859b2STodor Tomov 		    formats[i].pixelformat == pixelformat)
278ec6859b2STodor Tomov 			return i;
279ec6859b2STodor Tomov 	}
280ec6859b2STodor Tomov 
281ec6859b2STodor Tomov 	for (i = 0; i < nformats; i++)
282ec6859b2STodor Tomov 		if (formats[i].code == code)
283ec6859b2STodor Tomov 			return i;
284ec6859b2STodor Tomov 
285ec6859b2STodor Tomov 	WARN_ON(1);
286ec6859b2STodor Tomov 
287ec6859b2STodor Tomov 	return -EINVAL;
288ec6859b2STodor Tomov }
289ec6859b2STodor Tomov 
290ec6859b2STodor Tomov /*
291ec6859b2STodor Tomov  * video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane
292ec6859b2STodor Tomov  * @mbus: v4l2_mbus_framefmt format (input)
293ec6859b2STodor Tomov  * @pix: v4l2_pix_format_mplane format (output)
294ec6859b2STodor Tomov  * @f: a pointer to formats array element to be used for the conversion
295ec6859b2STodor Tomov  * @alignment: bytesperline alignment value
296ec6859b2STodor Tomov  *
297ec6859b2STodor Tomov  * Fill the output pix structure with information from the input mbus format.
298ec6859b2STodor Tomov  *
299ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
300ec6859b2STodor Tomov  */
301ec6859b2STodor Tomov static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
302ec6859b2STodor Tomov 				struct v4l2_pix_format_mplane *pix,
303ec6859b2STodor Tomov 				const struct camss_format_info *f,
304ec6859b2STodor Tomov 				unsigned int alignment)
305ec6859b2STodor Tomov {
306ec6859b2STodor Tomov 	unsigned int i;
307ec6859b2STodor Tomov 	u32 bytesperline;
308ec6859b2STodor Tomov 
309ec6859b2STodor Tomov 	memset(pix, 0, sizeof(*pix));
310ec6859b2STodor Tomov 	v4l2_fill_pix_format_mplane(pix, mbus);
311ec6859b2STodor Tomov 	pix->pixelformat = f->pixelformat;
312ec6859b2STodor Tomov 	pix->num_planes = f->planes;
313ec6859b2STodor Tomov 	for (i = 0; i < pix->num_planes; i++) {
314ec6859b2STodor Tomov 		bytesperline = pix->width / f->hsub[i].numerator *
315ec6859b2STodor Tomov 			f->hsub[i].denominator * f->bpp[i] / 8;
316ec6859b2STodor Tomov 		bytesperline = ALIGN(bytesperline, alignment);
317ec6859b2STodor Tomov 		pix->plane_fmt[i].bytesperline = bytesperline;
318ec6859b2STodor Tomov 		pix->plane_fmt[i].sizeimage = pix->height /
319ec6859b2STodor Tomov 				f->vsub[i].numerator * f->vsub[i].denominator *
320ec6859b2STodor Tomov 				bytesperline;
321ec6859b2STodor Tomov 	}
322ec6859b2STodor Tomov 
323ec6859b2STodor Tomov 	return 0;
324ec6859b2STodor Tomov }
325ec6859b2STodor Tomov 
326ec6859b2STodor Tomov static struct v4l2_subdev *video_remote_subdev(struct camss_video *video,
327ec6859b2STodor Tomov 					       u32 *pad)
328ec6859b2STodor Tomov {
329ec6859b2STodor Tomov 	struct media_pad *remote;
330ec6859b2STodor Tomov 
331ec6859b2STodor Tomov 	remote = media_entity_remote_pad(&video->pad);
332ec6859b2STodor Tomov 
333ec6859b2STodor Tomov 	if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
334ec6859b2STodor Tomov 		return NULL;
335ec6859b2STodor Tomov 
336ec6859b2STodor Tomov 	if (pad)
337ec6859b2STodor Tomov 		*pad = remote->index;
338ec6859b2STodor Tomov 
339ec6859b2STodor Tomov 	return media_entity_to_v4l2_subdev(remote->entity);
340ec6859b2STodor Tomov }
341ec6859b2STodor Tomov 
342ec6859b2STodor Tomov static int video_get_subdev_format(struct camss_video *video,
343ec6859b2STodor Tomov 				   struct v4l2_format *format)
344ec6859b2STodor Tomov {
345ec6859b2STodor Tomov 	struct v4l2_subdev_format fmt;
346ec6859b2STodor Tomov 	struct v4l2_subdev *subdev;
347ec6859b2STodor Tomov 	u32 pad;
348ec6859b2STodor Tomov 	int ret;
349ec6859b2STodor Tomov 
350ec6859b2STodor Tomov 	subdev = video_remote_subdev(video, &pad);
351ec6859b2STodor Tomov 	if (subdev == NULL)
352ec6859b2STodor Tomov 		return -EPIPE;
353ec6859b2STodor Tomov 
354ec6859b2STodor Tomov 	fmt.pad = pad;
355ec6859b2STodor Tomov 	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
356ec6859b2STodor Tomov 
357ec6859b2STodor Tomov 	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
358ec6859b2STodor Tomov 	if (ret)
359ec6859b2STodor Tomov 		return ret;
360ec6859b2STodor Tomov 
361ec6859b2STodor Tomov 	ret = video_find_format(fmt.format.code,
362ec6859b2STodor Tomov 				format->fmt.pix_mp.pixelformat,
363ec6859b2STodor Tomov 				video->formats, video->nformats);
364ec6859b2STodor Tomov 	if (ret < 0)
365ec6859b2STodor Tomov 		return ret;
366ec6859b2STodor Tomov 
367ec6859b2STodor Tomov 	format->type = video->type;
368ec6859b2STodor Tomov 
369ec6859b2STodor Tomov 	return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp,
370ec6859b2STodor Tomov 				    &video->formats[ret], video->bpl_alignment);
371ec6859b2STodor Tomov }
372ec6859b2STodor Tomov 
373ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
374ec6859b2STodor Tomov  * Video queue operations
375ec6859b2STodor Tomov  */
376ec6859b2STodor Tomov 
377ec6859b2STodor Tomov static int video_queue_setup(struct vb2_queue *q,
378ec6859b2STodor Tomov 	unsigned int *num_buffers, unsigned int *num_planes,
379ec6859b2STodor Tomov 	unsigned int sizes[], struct device *alloc_devs[])
380ec6859b2STodor Tomov {
381ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(q);
382ec6859b2STodor Tomov 	const struct v4l2_pix_format_mplane *format =
383ec6859b2STodor Tomov 						&video->active_fmt.fmt.pix_mp;
384ec6859b2STodor Tomov 	unsigned int i;
385ec6859b2STodor Tomov 
386ec6859b2STodor Tomov 	if (*num_planes) {
387ec6859b2STodor Tomov 		if (*num_planes != format->num_planes)
388ec6859b2STodor Tomov 			return -EINVAL;
389ec6859b2STodor Tomov 
390ec6859b2STodor Tomov 		for (i = 0; i < *num_planes; i++)
391ec6859b2STodor Tomov 			if (sizes[i] < format->plane_fmt[i].sizeimage)
392ec6859b2STodor Tomov 				return -EINVAL;
393ec6859b2STodor Tomov 
394ec6859b2STodor Tomov 		return 0;
395ec6859b2STodor Tomov 	}
396ec6859b2STodor Tomov 
397ec6859b2STodor Tomov 	*num_planes = format->num_planes;
398ec6859b2STodor Tomov 
399ec6859b2STodor Tomov 	for (i = 0; i < *num_planes; i++)
400ec6859b2STodor Tomov 		sizes[i] = format->plane_fmt[i].sizeimage;
401ec6859b2STodor Tomov 
402ec6859b2STodor Tomov 	return 0;
403ec6859b2STodor Tomov }
404ec6859b2STodor Tomov 
405ec6859b2STodor Tomov static int video_buf_init(struct vb2_buffer *vb)
406ec6859b2STodor Tomov {
407ec6859b2STodor Tomov 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
408ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
409ec6859b2STodor Tomov 	struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer,
410ec6859b2STodor Tomov 						   vb);
411ec6859b2STodor Tomov 	const struct v4l2_pix_format_mplane *format =
412ec6859b2STodor Tomov 						&video->active_fmt.fmt.pix_mp;
413ec6859b2STodor Tomov 	struct sg_table *sgt;
414ec6859b2STodor Tomov 	unsigned int i;
415ec6859b2STodor Tomov 
416ec6859b2STodor Tomov 	for (i = 0; i < format->num_planes; i++) {
417ec6859b2STodor Tomov 		sgt = vb2_dma_sg_plane_desc(vb, i);
418ec6859b2STodor Tomov 		if (!sgt)
419ec6859b2STodor Tomov 			return -EFAULT;
420ec6859b2STodor Tomov 
421ec6859b2STodor Tomov 		buffer->addr[i] = sg_dma_address(sgt->sgl);
422ec6859b2STodor Tomov 	}
423ec6859b2STodor Tomov 
424ec6859b2STodor Tomov 	if (format->pixelformat == V4L2_PIX_FMT_NV12 ||
425ec6859b2STodor Tomov 			format->pixelformat == V4L2_PIX_FMT_NV21 ||
426ec6859b2STodor Tomov 			format->pixelformat == V4L2_PIX_FMT_NV16 ||
427ec6859b2STodor Tomov 			format->pixelformat == V4L2_PIX_FMT_NV61)
428ec6859b2STodor Tomov 		buffer->addr[1] = buffer->addr[0] +
429ec6859b2STodor Tomov 				format->plane_fmt[0].bytesperline *
430ec6859b2STodor Tomov 				format->height;
431ec6859b2STodor Tomov 
432ec6859b2STodor Tomov 	return 0;
433ec6859b2STodor Tomov }
434ec6859b2STodor Tomov 
435ec6859b2STodor Tomov static int video_buf_prepare(struct vb2_buffer *vb)
436ec6859b2STodor Tomov {
437ec6859b2STodor Tomov 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
438ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
439ec6859b2STodor Tomov 	const struct v4l2_pix_format_mplane *format =
440ec6859b2STodor Tomov 						&video->active_fmt.fmt.pix_mp;
441ec6859b2STodor Tomov 	unsigned int i;
442ec6859b2STodor Tomov 
443ec6859b2STodor Tomov 	for (i = 0; i < format->num_planes; i++) {
444ec6859b2STodor Tomov 		if (format->plane_fmt[i].sizeimage > vb2_plane_size(vb, i))
445ec6859b2STodor Tomov 			return -EINVAL;
446ec6859b2STodor Tomov 
447ec6859b2STodor Tomov 		vb2_set_plane_payload(vb, i, format->plane_fmt[i].sizeimage);
448ec6859b2STodor Tomov 	}
449ec6859b2STodor Tomov 
450ec6859b2STodor Tomov 	vbuf->field = V4L2_FIELD_NONE;
451ec6859b2STodor Tomov 
452ec6859b2STodor Tomov 	return 0;
453ec6859b2STodor Tomov }
454ec6859b2STodor Tomov 
455ec6859b2STodor Tomov static void video_buf_queue(struct vb2_buffer *vb)
456ec6859b2STodor Tomov {
457ec6859b2STodor Tomov 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
458ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
459ec6859b2STodor Tomov 	struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer,
460ec6859b2STodor Tomov 						   vb);
461ec6859b2STodor Tomov 
462ec6859b2STodor Tomov 	video->ops->queue_buffer(video, buffer);
463ec6859b2STodor Tomov }
464ec6859b2STodor Tomov 
465ec6859b2STodor Tomov static int video_check_format(struct camss_video *video)
466ec6859b2STodor Tomov {
467ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *pix = &video->active_fmt.fmt.pix_mp;
468ec6859b2STodor Tomov 	struct v4l2_format format;
469ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *sd_pix = &format.fmt.pix_mp;
470ec6859b2STodor Tomov 	int ret;
471ec6859b2STodor Tomov 
472ec6859b2STodor Tomov 	sd_pix->pixelformat = pix->pixelformat;
473ec6859b2STodor Tomov 	ret = video_get_subdev_format(video, &format);
474ec6859b2STodor Tomov 	if (ret < 0)
475ec6859b2STodor Tomov 		return ret;
476ec6859b2STodor Tomov 
477ec6859b2STodor Tomov 	if (pix->pixelformat != sd_pix->pixelformat ||
478ec6859b2STodor Tomov 	    pix->height != sd_pix->height ||
479ec6859b2STodor Tomov 	    pix->width != sd_pix->width ||
480ec6859b2STodor Tomov 	    pix->num_planes != sd_pix->num_planes ||
481ec6859b2STodor Tomov 	    pix->field != format.fmt.pix_mp.field)
482ec6859b2STodor Tomov 		return -EPIPE;
483ec6859b2STodor Tomov 
484ec6859b2STodor Tomov 	return 0;
485ec6859b2STodor Tomov }
486ec6859b2STodor Tomov 
487ec6859b2STodor Tomov static int video_start_streaming(struct vb2_queue *q, unsigned int count)
488ec6859b2STodor Tomov {
489ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(q);
490ec6859b2STodor Tomov 	struct video_device *vdev = &video->vdev;
491ec6859b2STodor Tomov 	struct media_entity *entity;
492ec6859b2STodor Tomov 	struct media_pad *pad;
493ec6859b2STodor Tomov 	struct v4l2_subdev *subdev;
494ec6859b2STodor Tomov 	int ret;
495ec6859b2STodor Tomov 
496ec6859b2STodor Tomov 	ret = media_pipeline_start(&vdev->entity, &video->pipe);
497ec6859b2STodor Tomov 	if (ret < 0)
498ec6859b2STodor Tomov 		return ret;
499ec6859b2STodor Tomov 
500ec6859b2STodor Tomov 	ret = video_check_format(video);
501ec6859b2STodor Tomov 	if (ret < 0)
502ec6859b2STodor Tomov 		goto error;
503ec6859b2STodor Tomov 
504ec6859b2STodor Tomov 	entity = &vdev->entity;
505ec6859b2STodor Tomov 	while (1) {
506ec6859b2STodor Tomov 		pad = &entity->pads[0];
507ec6859b2STodor Tomov 		if (!(pad->flags & MEDIA_PAD_FL_SINK))
508ec6859b2STodor Tomov 			break;
509ec6859b2STodor Tomov 
510ec6859b2STodor Tomov 		pad = media_entity_remote_pad(pad);
511ec6859b2STodor Tomov 		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
512ec6859b2STodor Tomov 			break;
513ec6859b2STodor Tomov 
514ec6859b2STodor Tomov 		entity = pad->entity;
515ec6859b2STodor Tomov 		subdev = media_entity_to_v4l2_subdev(entity);
516ec6859b2STodor Tomov 
517ec6859b2STodor Tomov 		ret = v4l2_subdev_call(subdev, video, s_stream, 1);
518ec6859b2STodor Tomov 		if (ret < 0 && ret != -ENOIOCTLCMD)
519ec6859b2STodor Tomov 			goto error;
520ec6859b2STodor Tomov 	}
521ec6859b2STodor Tomov 
522ec6859b2STodor Tomov 	return 0;
523ec6859b2STodor Tomov 
524ec6859b2STodor Tomov error:
525ec6859b2STodor Tomov 	media_pipeline_stop(&vdev->entity);
526ec6859b2STodor Tomov 
527ec6859b2STodor Tomov 	video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED);
528ec6859b2STodor Tomov 
529ec6859b2STodor Tomov 	return ret;
530ec6859b2STodor Tomov }
531ec6859b2STodor Tomov 
532ec6859b2STodor Tomov static void video_stop_streaming(struct vb2_queue *q)
533ec6859b2STodor Tomov {
534ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(q);
535ec6859b2STodor Tomov 	struct video_device *vdev = &video->vdev;
536ec6859b2STodor Tomov 	struct media_entity *entity;
537ec6859b2STodor Tomov 	struct media_pad *pad;
538ec6859b2STodor Tomov 	struct v4l2_subdev *subdev;
539ec6859b2STodor Tomov 
540ec6859b2STodor Tomov 	entity = &vdev->entity;
541ec6859b2STodor Tomov 	while (1) {
542ec6859b2STodor Tomov 		pad = &entity->pads[0];
543ec6859b2STodor Tomov 		if (!(pad->flags & MEDIA_PAD_FL_SINK))
544ec6859b2STodor Tomov 			break;
545ec6859b2STodor Tomov 
546ec6859b2STodor Tomov 		pad = media_entity_remote_pad(pad);
547ec6859b2STodor Tomov 		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
548ec6859b2STodor Tomov 			break;
549ec6859b2STodor Tomov 
550ec6859b2STodor Tomov 		entity = pad->entity;
551ec6859b2STodor Tomov 		subdev = media_entity_to_v4l2_subdev(entity);
552ec6859b2STodor Tomov 
553ec6859b2STodor Tomov 		v4l2_subdev_call(subdev, video, s_stream, 0);
554ec6859b2STodor Tomov 	}
555ec6859b2STodor Tomov 
556ec6859b2STodor Tomov 	media_pipeline_stop(&vdev->entity);
557ec6859b2STodor Tomov 
558ec6859b2STodor Tomov 	video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
559ec6859b2STodor Tomov }
560ec6859b2STodor Tomov 
561ec6859b2STodor Tomov static const struct vb2_ops msm_video_vb2_q_ops = {
562ec6859b2STodor Tomov 	.queue_setup     = video_queue_setup,
563ec6859b2STodor Tomov 	.wait_prepare    = vb2_ops_wait_prepare,
564ec6859b2STodor Tomov 	.wait_finish     = vb2_ops_wait_finish,
565ec6859b2STodor Tomov 	.buf_init        = video_buf_init,
566ec6859b2STodor Tomov 	.buf_prepare     = video_buf_prepare,
567ec6859b2STodor Tomov 	.buf_queue       = video_buf_queue,
568ec6859b2STodor Tomov 	.start_streaming = video_start_streaming,
569ec6859b2STodor Tomov 	.stop_streaming  = video_stop_streaming,
570ec6859b2STodor Tomov };
571ec6859b2STodor Tomov 
572ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
573ec6859b2STodor Tomov  * V4L2 ioctls
574ec6859b2STodor Tomov  */
575ec6859b2STodor Tomov 
576ec6859b2STodor Tomov static int video_querycap(struct file *file, void *fh,
577ec6859b2STodor Tomov 			  struct v4l2_capability *cap)
578ec6859b2STodor Tomov {
579ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
580ec6859b2STodor Tomov 
581c0decac1SMauro Carvalho Chehab 	strscpy(cap->driver, "qcom-camss", sizeof(cap->driver));
582c0decac1SMauro Carvalho Chehab 	strscpy(cap->card, "Qualcomm Camera Subsystem", sizeof(cap->card));
583ec6859b2STodor Tomov 	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
584ec6859b2STodor Tomov 		 dev_name(video->camss->dev));
585ec6859b2STodor Tomov 
586ec6859b2STodor Tomov 	return 0;
587ec6859b2STodor Tomov }
588ec6859b2STodor Tomov 
589a3d412d4SAndrey Konovalov static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
590ec6859b2STodor Tomov {
591a3d412d4SAndrey Konovalov 	struct camss_video *video = video_drvdata(file);
592ec6859b2STodor Tomov 	int i, j, k;
593dfb5d328SAndrey Konovalov 	u32 mcode = f->mbus_code;
594ec6859b2STodor Tomov 
595a3d412d4SAndrey Konovalov 	if (f->type != video->type)
596a3d412d4SAndrey Konovalov 		return -EINVAL;
597a3d412d4SAndrey Konovalov 
598a3d412d4SAndrey Konovalov 	if (f->index >= video->nformats)
599a3d412d4SAndrey Konovalov 		return -EINVAL;
600a3d412d4SAndrey Konovalov 
601dfb5d328SAndrey Konovalov 	/*
602dfb5d328SAndrey Konovalov 	 * Find index "i" of "k"th unique pixelformat in formats array.
603dfb5d328SAndrey Konovalov 	 *
604dfb5d328SAndrey Konovalov 	 * If f->mbus_code passed to video_enum_fmt() is not zero, a device
605dfb5d328SAndrey Konovalov 	 * with V4L2_CAP_IO_MC capability restricts enumeration to only the
606dfb5d328SAndrey Konovalov 	 * pixel formats that can be produced from that media bus code.
607dfb5d328SAndrey Konovalov 	 * This is implemented by skipping video->formats[] entries with
608dfb5d328SAndrey Konovalov 	 * code != f->mbus_code (if f->mbus_code is not zero).
609dfb5d328SAndrey Konovalov 	 * If the f->mbus_code passed to video_enum_fmt() is not supported,
610dfb5d328SAndrey Konovalov 	 * -EINVAL is returned.
611dfb5d328SAndrey Konovalov 	 * If f->mbus_code is zero, all the pixel formats are enumerated.
612dfb5d328SAndrey Konovalov 	 */
613ec6859b2STodor Tomov 	k = -1;
614ec6859b2STodor Tomov 	for (i = 0; i < video->nformats; i++) {
615dfb5d328SAndrey Konovalov 		if (mcode != 0 && video->formats[i].code != mcode)
616dfb5d328SAndrey Konovalov 			continue;
617dfb5d328SAndrey Konovalov 
618ec6859b2STodor Tomov 		for (j = 0; j < i; j++) {
619dfb5d328SAndrey Konovalov 			if (mcode != 0 && video->formats[j].code != mcode)
620dfb5d328SAndrey Konovalov 				continue;
621ec6859b2STodor Tomov 			if (video->formats[i].pixelformat ==
622ec6859b2STodor Tomov 					video->formats[j].pixelformat)
623ec6859b2STodor Tomov 				break;
624ec6859b2STodor Tomov 		}
625ec6859b2STodor Tomov 
626ec6859b2STodor Tomov 		if (j == i)
627ec6859b2STodor Tomov 			k++;
628ec6859b2STodor Tomov 
629a3d412d4SAndrey Konovalov 		if (k == f->index)
630a3d412d4SAndrey Konovalov 			break;
631ec6859b2STodor Tomov 	}
632ec6859b2STodor Tomov 
633b00481bdSDan Carpenter 	if (k == -1 || k < f->index)
634dfb5d328SAndrey Konovalov 		/*
635dfb5d328SAndrey Konovalov 		 * All the unique pixel formats matching the arguments
636dfb5d328SAndrey Konovalov 		 * have been enumerated (k >= 0 and f->index > 0), or
637dfb5d328SAndrey Konovalov 		 * no pixel formats match the non-zero f->mbus_code (k == -1).
638dfb5d328SAndrey Konovalov 		 */
639ec6859b2STodor Tomov 		return -EINVAL;
640ec6859b2STodor Tomov 
641ec6859b2STodor Tomov 	f->pixelformat = video->formats[i].pixelformat;
642ec6859b2STodor Tomov 
643ec6859b2STodor Tomov 	return 0;
644ec6859b2STodor Tomov }
645ec6859b2STodor Tomov 
64635493d65SAndrey Konovalov static int video_enum_framesizes(struct file *file, void *fh,
64735493d65SAndrey Konovalov 				 struct v4l2_frmsizeenum *fsize)
64835493d65SAndrey Konovalov {
64935493d65SAndrey Konovalov 	struct camss_video *video = video_drvdata(file);
65035493d65SAndrey Konovalov 	int i;
65135493d65SAndrey Konovalov 
65235493d65SAndrey Konovalov 	if (fsize->index)
65335493d65SAndrey Konovalov 		return -EINVAL;
65435493d65SAndrey Konovalov 
65535493d65SAndrey Konovalov 	/* Only accept pixel format present in the formats[] table */
65635493d65SAndrey Konovalov 	for (i = 0; i < video->nformats; i++) {
65735493d65SAndrey Konovalov 		if (video->formats[i].pixelformat == fsize->pixel_format)
65835493d65SAndrey Konovalov 			break;
65935493d65SAndrey Konovalov 	}
66035493d65SAndrey Konovalov 
66135493d65SAndrey Konovalov 	if (i == video->nformats)
66235493d65SAndrey Konovalov 		return -EINVAL;
66335493d65SAndrey Konovalov 
66435493d65SAndrey Konovalov 	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
66535493d65SAndrey Konovalov 	fsize->stepwise.min_width = CAMSS_FRAME_MIN_WIDTH;
66635493d65SAndrey Konovalov 	fsize->stepwise.max_width = CAMSS_FRAME_MAX_WIDTH;
66735493d65SAndrey Konovalov 	fsize->stepwise.min_height = CAMSS_FRAME_MIN_HEIGHT;
66835493d65SAndrey Konovalov 	fsize->stepwise.max_height = (video->line_based) ?
66935493d65SAndrey Konovalov 		CAMSS_FRAME_MAX_HEIGHT_PIX : CAMSS_FRAME_MAX_HEIGHT_RDI;
67035493d65SAndrey Konovalov 	fsize->stepwise.step_width = 1;
67135493d65SAndrey Konovalov 	fsize->stepwise.step_height = 1;
67235493d65SAndrey Konovalov 
67335493d65SAndrey Konovalov 	return 0;
67435493d65SAndrey Konovalov }
67535493d65SAndrey Konovalov 
676ec6859b2STodor Tomov static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
677ec6859b2STodor Tomov {
678ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
679ec6859b2STodor Tomov 
680ec6859b2STodor Tomov 	*f = video->active_fmt;
681ec6859b2STodor Tomov 
682ec6859b2STodor Tomov 	return 0;
683ec6859b2STodor Tomov }
684ec6859b2STodor Tomov 
685ec6859b2STodor Tomov static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f)
686ec6859b2STodor Tomov {
687ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *pix_mp;
688ec6859b2STodor Tomov 	const struct camss_format_info *fi;
689ec6859b2STodor Tomov 	struct v4l2_plane_pix_format *p;
690ec6859b2STodor Tomov 	u32 bytesperline[3] = { 0 };
691ec6859b2STodor Tomov 	u32 sizeimage[3] = { 0 };
692ec6859b2STodor Tomov 	u32 width, height;
693ec6859b2STodor Tomov 	u32 bpl, lines;
694ec6859b2STodor Tomov 	int i, j;
695ec6859b2STodor Tomov 
696ec6859b2STodor Tomov 	pix_mp = &f->fmt.pix_mp;
697ec6859b2STodor Tomov 
698ec6859b2STodor Tomov 	if (video->line_based)
699ec6859b2STodor Tomov 		for (i = 0; i < pix_mp->num_planes && i < 3; i++) {
700ec6859b2STodor Tomov 			p = &pix_mp->plane_fmt[i];
701ec6859b2STodor Tomov 			bytesperline[i] = clamp_t(u32, p->bytesperline,
702ec6859b2STodor Tomov 						  1, 65528);
703ec6859b2STodor Tomov 			sizeimage[i] = clamp_t(u32, p->sizeimage,
704ec6859b2STodor Tomov 					       bytesperline[i],
705daf2298bSAndrey Konovalov 					       bytesperline[i] * CAMSS_FRAME_MAX_HEIGHT_PIX);
706ec6859b2STodor Tomov 		}
707ec6859b2STodor Tomov 
708ec6859b2STodor Tomov 	for (j = 0; j < video->nformats; j++)
709ec6859b2STodor Tomov 		if (pix_mp->pixelformat == video->formats[j].pixelformat)
710ec6859b2STodor Tomov 			break;
711ec6859b2STodor Tomov 
712ec6859b2STodor Tomov 	if (j == video->nformats)
713ec6859b2STodor Tomov 		j = 0; /* default format */
714ec6859b2STodor Tomov 
715ec6859b2STodor Tomov 	fi = &video->formats[j];
716ec6859b2STodor Tomov 	width = pix_mp->width;
717ec6859b2STodor Tomov 	height = pix_mp->height;
718ec6859b2STodor Tomov 
719ec6859b2STodor Tomov 	memset(pix_mp, 0, sizeof(*pix_mp));
720ec6859b2STodor Tomov 
721ec6859b2STodor Tomov 	pix_mp->pixelformat = fi->pixelformat;
722daf2298bSAndrey Konovalov 	pix_mp->width = clamp_t(u32, width, 1, CAMSS_FRAME_MAX_WIDTH);
723daf2298bSAndrey Konovalov 	pix_mp->height = clamp_t(u32, height, 1, CAMSS_FRAME_MAX_HEIGHT_RDI);
724ec6859b2STodor Tomov 	pix_mp->num_planes = fi->planes;
725ec6859b2STodor Tomov 	for (i = 0; i < pix_mp->num_planes; i++) {
726ec6859b2STodor Tomov 		bpl = pix_mp->width / fi->hsub[i].numerator *
727ec6859b2STodor Tomov 			fi->hsub[i].denominator * fi->bpp[i] / 8;
728ec6859b2STodor Tomov 		bpl = ALIGN(bpl, video->bpl_alignment);
729ec6859b2STodor Tomov 		pix_mp->plane_fmt[i].bytesperline = bpl;
730ec6859b2STodor Tomov 		pix_mp->plane_fmt[i].sizeimage = pix_mp->height /
731ec6859b2STodor Tomov 			fi->vsub[i].numerator * fi->vsub[i].denominator * bpl;
732ec6859b2STodor Tomov 	}
733ec6859b2STodor Tomov 
734ec6859b2STodor Tomov 	pix_mp->field = V4L2_FIELD_NONE;
735ec6859b2STodor Tomov 	pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
736ec6859b2STodor Tomov 	pix_mp->flags = 0;
737ec6859b2STodor Tomov 	pix_mp->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace);
738ec6859b2STodor Tomov 	pix_mp->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
739ec6859b2STodor Tomov 					pix_mp->colorspace, pix_mp->ycbcr_enc);
740ec6859b2STodor Tomov 	pix_mp->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace);
741ec6859b2STodor Tomov 
742ec6859b2STodor Tomov 	if (video->line_based)
743ec6859b2STodor Tomov 		for (i = 0; i < pix_mp->num_planes; i++) {
744ec6859b2STodor Tomov 			p = &pix_mp->plane_fmt[i];
745ec6859b2STodor Tomov 			p->bytesperline = clamp_t(u32, p->bytesperline,
746ec6859b2STodor Tomov 						  1, 65528);
747ec6859b2STodor Tomov 			p->sizeimage = clamp_t(u32, p->sizeimage,
748ec6859b2STodor Tomov 					       p->bytesperline,
749daf2298bSAndrey Konovalov 					       p->bytesperline * CAMSS_FRAME_MAX_HEIGHT_PIX);
750ec6859b2STodor Tomov 			lines = p->sizeimage / p->bytesperline;
751ec6859b2STodor Tomov 
752ec6859b2STodor Tomov 			if (p->bytesperline < bytesperline[i])
753ec6859b2STodor Tomov 				p->bytesperline = ALIGN(bytesperline[i], 8);
754ec6859b2STodor Tomov 
755ec6859b2STodor Tomov 			if (p->sizeimage < p->bytesperline * lines)
756ec6859b2STodor Tomov 				p->sizeimage = p->bytesperline * lines;
757ec6859b2STodor Tomov 
758ec6859b2STodor Tomov 			if (p->sizeimage < sizeimage[i])
759ec6859b2STodor Tomov 				p->sizeimage = sizeimage[i];
760ec6859b2STodor Tomov 		}
761ec6859b2STodor Tomov 
762ec6859b2STodor Tomov 	return 0;
763ec6859b2STodor Tomov }
764ec6859b2STodor Tomov 
765ec6859b2STodor Tomov static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
766ec6859b2STodor Tomov {
767ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
768ec6859b2STodor Tomov 
769ec6859b2STodor Tomov 	return __video_try_fmt(video, f);
770ec6859b2STodor Tomov }
771ec6859b2STodor Tomov 
772ec6859b2STodor Tomov static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
773ec6859b2STodor Tomov {
774ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
775ec6859b2STodor Tomov 	int ret;
776ec6859b2STodor Tomov 
777ec6859b2STodor Tomov 	if (vb2_is_busy(&video->vb2_q))
778ec6859b2STodor Tomov 		return -EBUSY;
779ec6859b2STodor Tomov 
780ec6859b2STodor Tomov 	ret = __video_try_fmt(video, f);
781ec6859b2STodor Tomov 	if (ret < 0)
782ec6859b2STodor Tomov 		return ret;
783ec6859b2STodor Tomov 
784ec6859b2STodor Tomov 	video->active_fmt = *f;
785ec6859b2STodor Tomov 
786ec6859b2STodor Tomov 	return 0;
787ec6859b2STodor Tomov }
788ec6859b2STodor Tomov 
789ec6859b2STodor Tomov static int video_enum_input(struct file *file, void *fh,
790ec6859b2STodor Tomov 			    struct v4l2_input *input)
791ec6859b2STodor Tomov {
792ec6859b2STodor Tomov 	if (input->index > 0)
793ec6859b2STodor Tomov 		return -EINVAL;
794ec6859b2STodor Tomov 
795c0decac1SMauro Carvalho Chehab 	strscpy(input->name, "camera", sizeof(input->name));
796ec6859b2STodor Tomov 	input->type = V4L2_INPUT_TYPE_CAMERA;
797ec6859b2STodor Tomov 
798ec6859b2STodor Tomov 	return 0;
799ec6859b2STodor Tomov }
800ec6859b2STodor Tomov 
801ec6859b2STodor Tomov static int video_g_input(struct file *file, void *fh, unsigned int *input)
802ec6859b2STodor Tomov {
803ec6859b2STodor Tomov 	*input = 0;
804ec6859b2STodor Tomov 
805ec6859b2STodor Tomov 	return 0;
806ec6859b2STodor Tomov }
807ec6859b2STodor Tomov 
808ec6859b2STodor Tomov static int video_s_input(struct file *file, void *fh, unsigned int input)
809ec6859b2STodor Tomov {
810ec6859b2STodor Tomov 	return input == 0 ? 0 : -EINVAL;
811ec6859b2STodor Tomov }
812ec6859b2STodor Tomov 
813ec6859b2STodor Tomov static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = {
814ec6859b2STodor Tomov 	.vidioc_querycap		= video_querycap,
8157e98b7b5SBoris Brezillon 	.vidioc_enum_fmt_vid_cap	= video_enum_fmt,
81635493d65SAndrey Konovalov 	.vidioc_enum_framesizes		= video_enum_framesizes,
817ec6859b2STodor Tomov 	.vidioc_g_fmt_vid_cap_mplane	= video_g_fmt,
818ec6859b2STodor Tomov 	.vidioc_s_fmt_vid_cap_mplane	= video_s_fmt,
819ec6859b2STodor Tomov 	.vidioc_try_fmt_vid_cap_mplane	= video_try_fmt,
820ec6859b2STodor Tomov 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
821ec6859b2STodor Tomov 	.vidioc_querybuf		= vb2_ioctl_querybuf,
822ec6859b2STodor Tomov 	.vidioc_qbuf			= vb2_ioctl_qbuf,
823ec6859b2STodor Tomov 	.vidioc_expbuf			= vb2_ioctl_expbuf,
824ec6859b2STodor Tomov 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
825ec6859b2STodor Tomov 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
826ec6859b2STodor Tomov 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
827ec6859b2STodor Tomov 	.vidioc_streamon		= vb2_ioctl_streamon,
828ec6859b2STodor Tomov 	.vidioc_streamoff		= vb2_ioctl_streamoff,
829ec6859b2STodor Tomov 	.vidioc_enum_input		= video_enum_input,
830ec6859b2STodor Tomov 	.vidioc_g_input			= video_g_input,
831ec6859b2STodor Tomov 	.vidioc_s_input			= video_s_input,
832ec6859b2STodor Tomov };
833ec6859b2STodor Tomov 
834ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
835ec6859b2STodor Tomov  * V4L2 file operations
836ec6859b2STodor Tomov  */
837ec6859b2STodor Tomov 
838ec6859b2STodor Tomov static int video_open(struct file *file)
839ec6859b2STodor Tomov {
840ec6859b2STodor Tomov 	struct video_device *vdev = video_devdata(file);
841ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
842ec6859b2STodor Tomov 	struct v4l2_fh *vfh;
843ec6859b2STodor Tomov 	int ret;
844ec6859b2STodor Tomov 
845ec6859b2STodor Tomov 	mutex_lock(&video->lock);
846ec6859b2STodor Tomov 
847ec6859b2STodor Tomov 	vfh = kzalloc(sizeof(*vfh), GFP_KERNEL);
848ec6859b2STodor Tomov 	if (vfh == NULL) {
849ec6859b2STodor Tomov 		ret = -ENOMEM;
850ec6859b2STodor Tomov 		goto error_alloc;
851ec6859b2STodor Tomov 	}
852ec6859b2STodor Tomov 
853ec6859b2STodor Tomov 	v4l2_fh_init(vfh, vdev);
854ec6859b2STodor Tomov 	v4l2_fh_add(vfh);
855ec6859b2STodor Tomov 
856ec6859b2STodor Tomov 	file->private_data = vfh;
857ec6859b2STodor Tomov 
8588fd390b8SEzequiel Garcia 	ret = v4l2_pipeline_pm_get(&vdev->entity);
859ec6859b2STodor Tomov 	if (ret < 0) {
860ec6859b2STodor Tomov 		dev_err(video->camss->dev, "Failed to power up pipeline: %d\n",
861ec6859b2STodor Tomov 			ret);
862ec6859b2STodor Tomov 		goto error_pm_use;
863ec6859b2STodor Tomov 	}
864ec6859b2STodor Tomov 
865ec6859b2STodor Tomov 	mutex_unlock(&video->lock);
866ec6859b2STodor Tomov 
867ec6859b2STodor Tomov 	return 0;
868ec6859b2STodor Tomov 
869ec6859b2STodor Tomov error_pm_use:
870ec6859b2STodor Tomov 	v4l2_fh_release(file);
871ec6859b2STodor Tomov 
872ec6859b2STodor Tomov error_alloc:
873ec6859b2STodor Tomov 	mutex_unlock(&video->lock);
874ec6859b2STodor Tomov 
875ec6859b2STodor Tomov 	return ret;
876ec6859b2STodor Tomov }
877ec6859b2STodor Tomov 
878ec6859b2STodor Tomov static int video_release(struct file *file)
879ec6859b2STodor Tomov {
880ec6859b2STodor Tomov 	struct video_device *vdev = video_devdata(file);
881ec6859b2STodor Tomov 
882ec6859b2STodor Tomov 	vb2_fop_release(file);
883ec6859b2STodor Tomov 
8848fd390b8SEzequiel Garcia 	v4l2_pipeline_pm_put(&vdev->entity);
885ec6859b2STodor Tomov 
886ec6859b2STodor Tomov 	file->private_data = NULL;
887ec6859b2STodor Tomov 
888ec6859b2STodor Tomov 	return 0;
889ec6859b2STodor Tomov }
890ec6859b2STodor Tomov 
891ec6859b2STodor Tomov static const struct v4l2_file_operations msm_vid_fops = {
892ec6859b2STodor Tomov 	.owner          = THIS_MODULE,
893ec6859b2STodor Tomov 	.unlocked_ioctl = video_ioctl2,
894ec6859b2STodor Tomov 	.open           = video_open,
895ec6859b2STodor Tomov 	.release        = video_release,
896ec6859b2STodor Tomov 	.poll           = vb2_fop_poll,
897ec6859b2STodor Tomov 	.mmap		= vb2_fop_mmap,
898ec6859b2STodor Tomov 	.read		= vb2_fop_read,
899ec6859b2STodor Tomov };
900ec6859b2STodor Tomov 
901ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
902ec6859b2STodor Tomov  * CAMSS video core
903ec6859b2STodor Tomov  */
904ec6859b2STodor Tomov 
905ec6859b2STodor Tomov static void msm_video_release(struct video_device *vdev)
906ec6859b2STodor Tomov {
907ec6859b2STodor Tomov 	struct camss_video *video = video_get_drvdata(vdev);
908ec6859b2STodor Tomov 
909ec6859b2STodor Tomov 	media_entity_cleanup(&vdev->entity);
910ec6859b2STodor Tomov 
911ec6859b2STodor Tomov 	mutex_destroy(&video->q_lock);
912ec6859b2STodor Tomov 	mutex_destroy(&video->lock);
913ec6859b2STodor Tomov 
914ec6859b2STodor Tomov 	if (atomic_dec_and_test(&video->camss->ref_count))
915ec6859b2STodor Tomov 		camss_delete(video->camss);
916ec6859b2STodor Tomov }
917ec6859b2STodor Tomov 
918ec6859b2STodor Tomov /*
919ec6859b2STodor Tomov  * msm_video_init_format - Helper function to initialize format
920ec6859b2STodor Tomov  * @video: struct camss_video
921ec6859b2STodor Tomov  *
922ec6859b2STodor Tomov  * Initialize pad format with default value.
923ec6859b2STodor Tomov  *
924ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
925ec6859b2STodor Tomov  */
926ec6859b2STodor Tomov static int msm_video_init_format(struct camss_video *video)
927ec6859b2STodor Tomov {
928ec6859b2STodor Tomov 	int ret;
929ec6859b2STodor Tomov 	struct v4l2_format format = {
930ec6859b2STodor Tomov 		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
931ec6859b2STodor Tomov 		.fmt.pix_mp = {
932ec6859b2STodor Tomov 			.width = 1920,
933ec6859b2STodor Tomov 			.height = 1080,
934ec6859b2STodor Tomov 			.pixelformat = video->formats[0].pixelformat,
935ec6859b2STodor Tomov 		},
936ec6859b2STodor Tomov 	};
937ec6859b2STodor Tomov 
938ec6859b2STodor Tomov 	ret = __video_try_fmt(video, &format);
939ec6859b2STodor Tomov 	if (ret < 0)
940ec6859b2STodor Tomov 		return ret;
941ec6859b2STodor Tomov 
942ec6859b2STodor Tomov 	video->active_fmt = format;
943ec6859b2STodor Tomov 
944ec6859b2STodor Tomov 	return 0;
945ec6859b2STodor Tomov }
946ec6859b2STodor Tomov 
947ec6859b2STodor Tomov /*
948ec6859b2STodor Tomov  * msm_video_register - Register a video device node
949ec6859b2STodor Tomov  * @video: struct camss_video
950ec6859b2STodor Tomov  * @v4l2_dev: V4L2 device
951ec6859b2STodor Tomov  * @name: name to be used for the video device node
952ec6859b2STodor Tomov  *
953ec6859b2STodor Tomov  * Initialize and register a video device node to a V4L2 device. Also
954ec6859b2STodor Tomov  * initialize the vb2 queue.
955ec6859b2STodor Tomov  *
956ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
957ec6859b2STodor Tomov  */
958ec6859b2STodor Tomov 
959ec6859b2STodor Tomov int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
960ec6859b2STodor Tomov 		       const char *name, int is_pix)
961ec6859b2STodor Tomov {
962ec6859b2STodor Tomov 	struct media_pad *pad = &video->pad;
963ec6859b2STodor Tomov 	struct video_device *vdev;
964ec6859b2STodor Tomov 	struct vb2_queue *q;
965ec6859b2STodor Tomov 	int ret;
966ec6859b2STodor Tomov 
967ec6859b2STodor Tomov 	vdev = &video->vdev;
968ec6859b2STodor Tomov 
969ec6859b2STodor Tomov 	mutex_init(&video->q_lock);
970ec6859b2STodor Tomov 
971ec6859b2STodor Tomov 	q = &video->vb2_q;
972ec6859b2STodor Tomov 	q->drv_priv = video;
973ec6859b2STodor Tomov 	q->mem_ops = &vb2_dma_sg_memops;
974ec6859b2STodor Tomov 	q->ops = &msm_video_vb2_q_ops;
975ec6859b2STodor Tomov 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
976ec6859b2STodor Tomov 	q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ;
977ec6859b2STodor Tomov 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
978ec6859b2STodor Tomov 	q->buf_struct_size = sizeof(struct camss_buffer);
979ec6859b2STodor Tomov 	q->dev = video->camss->dev;
980ec6859b2STodor Tomov 	q->lock = &video->q_lock;
981ec6859b2STodor Tomov 	ret = vb2_queue_init(q);
982ec6859b2STodor Tomov 	if (ret < 0) {
983ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret);
984ec6859b2STodor Tomov 		goto error_vb2_init;
985ec6859b2STodor Tomov 	}
986ec6859b2STodor Tomov 
987ec6859b2STodor Tomov 	pad->flags = MEDIA_PAD_FL_SINK;
988ec6859b2STodor Tomov 	ret = media_entity_pads_init(&vdev->entity, 1, pad);
989ec6859b2STodor Tomov 	if (ret < 0) {
990ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n",
991ec6859b2STodor Tomov 			ret);
992492abcd7SHans Verkuil 		goto error_vb2_init;
993ec6859b2STodor Tomov 	}
994ec6859b2STodor Tomov 
995ec6859b2STodor Tomov 	mutex_init(&video->lock);
996ec6859b2STodor Tomov 
997cba3819dSTodor Tomov 	if (video->camss->version == CAMSS_8x16) {
998ec6859b2STodor Tomov 		if (is_pix) {
999cba3819dSTodor Tomov 			video->formats = formats_pix_8x16;
1000cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_pix_8x16);
1001cba3819dSTodor Tomov 		} else {
1002cba3819dSTodor Tomov 			video->formats = formats_rdi_8x16;
1003cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_rdi_8x16);
1004cba3819dSTodor Tomov 		}
10059e5d1581SAngeloGioacchino Del Regno 	} else if (video->camss->version == CAMSS_8x96 ||
10069e5d1581SAngeloGioacchino Del Regno 		   video->camss->version == CAMSS_660) {
1007cba3819dSTodor Tomov 		if (is_pix) {
1008cba3819dSTodor Tomov 			video->formats = formats_pix_8x96;
1009cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_pix_8x96);
1010cba3819dSTodor Tomov 		} else {
1011cba3819dSTodor Tomov 			video->formats = formats_rdi_8x96;
1012cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_rdi_8x96);
1013cba3819dSTodor Tomov 		}
1014*b4436a18SJonathan Marek 	}  else if (video->camss->version == CAMSS_845 ||
1015*b4436a18SJonathan Marek 		    video->camss->version == CAMSS_8250) {
10167319cdf1SRobert Foss 		video->formats = formats_rdi_845;
10177319cdf1SRobert Foss 		video->nformats = ARRAY_SIZE(formats_rdi_845);
1018cba3819dSTodor Tomov 	} else {
10199c67ed2aSDan Carpenter 		ret = -EINVAL;
1020cba3819dSTodor Tomov 		goto error_video_register;
1021ec6859b2STodor Tomov 	}
1022ec6859b2STodor Tomov 
1023ec6859b2STodor Tomov 	ret = msm_video_init_format(video);
1024ec6859b2STodor Tomov 	if (ret < 0) {
1025ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret);
1026ec6859b2STodor Tomov 		goto error_video_register;
1027ec6859b2STodor Tomov 	}
1028ec6859b2STodor Tomov 
1029ec6859b2STodor Tomov 	vdev->fops = &msm_vid_fops;
1030dfb5d328SAndrey Konovalov 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING
1031dfb5d328SAndrey Konovalov 			  | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC;
1032ec6859b2STodor Tomov 	vdev->ioctl_ops = &msm_vid_ioctl_ops;
1033ec6859b2STodor Tomov 	vdev->release = msm_video_release;
1034ec6859b2STodor Tomov 	vdev->v4l2_dev = v4l2_dev;
1035ec6859b2STodor Tomov 	vdev->vfl_dir = VFL_DIR_RX;
1036ec6859b2STodor Tomov 	vdev->queue = &video->vb2_q;
1037ec6859b2STodor Tomov 	vdev->lock = &video->lock;
1038c0decac1SMauro Carvalho Chehab 	strscpy(vdev->name, name, sizeof(vdev->name));
1039ec6859b2STodor Tomov 
104070cad449SHans Verkuil 	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
1041ec6859b2STodor Tomov 	if (ret < 0) {
1042ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to register video device: %d\n",
1043ec6859b2STodor Tomov 			ret);
1044ec6859b2STodor Tomov 		goto error_video_register;
1045ec6859b2STodor Tomov 	}
1046ec6859b2STodor Tomov 
1047ec6859b2STodor Tomov 	video_set_drvdata(vdev, video);
1048ec6859b2STodor Tomov 	atomic_inc(&video->camss->ref_count);
1049ec6859b2STodor Tomov 
1050ec6859b2STodor Tomov 	return 0;
1051ec6859b2STodor Tomov 
1052ec6859b2STodor Tomov error_video_register:
1053ec6859b2STodor Tomov 	media_entity_cleanup(&vdev->entity);
1054ec6859b2STodor Tomov 	mutex_destroy(&video->lock);
1055ec6859b2STodor Tomov error_vb2_init:
1056ec6859b2STodor Tomov 	mutex_destroy(&video->q_lock);
1057ec6859b2STodor Tomov 
1058ec6859b2STodor Tomov 	return ret;
1059ec6859b2STodor Tomov }
1060ec6859b2STodor Tomov 
1061ec6859b2STodor Tomov void msm_video_unregister(struct camss_video *video)
1062ec6859b2STodor Tomov {
1063ec6859b2STodor Tomov 	atomic_inc(&video->camss->ref_count);
1064492abcd7SHans Verkuil 	vb2_video_unregister_device(&video->vdev);
1065ec6859b2STodor Tomov 	atomic_dec(&video->camss->ref_count);
1066ec6859b2STodor Tomov }
1067