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 
video_find_format(u32 code,u32 pixelformat,const struct camss_format_info * formats,unsigned int nformats)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  */
video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt * mbus,struct v4l2_pix_format_mplane * pix,const struct camss_format_info * f,unsigned int alignment)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 
video_remote_subdev(struct camss_video * video,u32 * pad)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 
331b2e44430SLaurent Pinchart 	remote = media_pad_remote_pad_first(&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 
video_get_subdev_format(struct camss_video * video,struct v4l2_format * format)342ec6859b2STodor Tomov static int video_get_subdev_format(struct camss_video *video,
343ec6859b2STodor Tomov 				   struct v4l2_format *format)
344ec6859b2STodor Tomov {
345*ecefa105SLaurent Pinchart 	struct v4l2_subdev_format fmt = {
346*ecefa105SLaurent Pinchart 		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
347*ecefa105SLaurent Pinchart 	};
348ec6859b2STodor Tomov 	struct v4l2_subdev *subdev;
349ec6859b2STodor Tomov 	u32 pad;
350ec6859b2STodor Tomov 	int ret;
351ec6859b2STodor Tomov 
352ec6859b2STodor Tomov 	subdev = video_remote_subdev(video, &pad);
353ec6859b2STodor Tomov 	if (subdev == NULL)
354ec6859b2STodor Tomov 		return -EPIPE;
355ec6859b2STodor Tomov 
356ec6859b2STodor Tomov 	fmt.pad = pad;
357ec6859b2STodor Tomov 
358ec6859b2STodor Tomov 	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
359ec6859b2STodor Tomov 	if (ret)
360ec6859b2STodor Tomov 		return ret;
361ec6859b2STodor Tomov 
362ec6859b2STodor Tomov 	ret = video_find_format(fmt.format.code,
363ec6859b2STodor Tomov 				format->fmt.pix_mp.pixelformat,
364ec6859b2STodor Tomov 				video->formats, video->nformats);
365ec6859b2STodor Tomov 	if (ret < 0)
366ec6859b2STodor Tomov 		return ret;
367ec6859b2STodor Tomov 
368ec6859b2STodor Tomov 	format->type = video->type;
369ec6859b2STodor Tomov 
370ec6859b2STodor Tomov 	return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp,
371ec6859b2STodor Tomov 				    &video->formats[ret], video->bpl_alignment);
372ec6859b2STodor Tomov }
373ec6859b2STodor Tomov 
374ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
375ec6859b2STodor Tomov  * Video queue operations
376ec6859b2STodor Tomov  */
377ec6859b2STodor Tomov 
video_queue_setup(struct vb2_queue * q,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])378ec6859b2STodor Tomov static int video_queue_setup(struct vb2_queue *q,
379ec6859b2STodor Tomov 	unsigned int *num_buffers, unsigned int *num_planes,
380ec6859b2STodor Tomov 	unsigned int sizes[], struct device *alloc_devs[])
381ec6859b2STodor Tomov {
382ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(q);
383ec6859b2STodor Tomov 	const struct v4l2_pix_format_mplane *format =
384ec6859b2STodor Tomov 						&video->active_fmt.fmt.pix_mp;
385ec6859b2STodor Tomov 	unsigned int i;
386ec6859b2STodor Tomov 
387ec6859b2STodor Tomov 	if (*num_planes) {
388ec6859b2STodor Tomov 		if (*num_planes != format->num_planes)
389ec6859b2STodor Tomov 			return -EINVAL;
390ec6859b2STodor Tomov 
391ec6859b2STodor Tomov 		for (i = 0; i < *num_planes; i++)
392ec6859b2STodor Tomov 			if (sizes[i] < format->plane_fmt[i].sizeimage)
393ec6859b2STodor Tomov 				return -EINVAL;
394ec6859b2STodor Tomov 
395ec6859b2STodor Tomov 		return 0;
396ec6859b2STodor Tomov 	}
397ec6859b2STodor Tomov 
398ec6859b2STodor Tomov 	*num_planes = format->num_planes;
399ec6859b2STodor Tomov 
400ec6859b2STodor Tomov 	for (i = 0; i < *num_planes; i++)
401ec6859b2STodor Tomov 		sizes[i] = format->plane_fmt[i].sizeimage;
402ec6859b2STodor Tomov 
403ec6859b2STodor Tomov 	return 0;
404ec6859b2STodor Tomov }
405ec6859b2STodor Tomov 
video_buf_init(struct vb2_buffer * vb)406ec6859b2STodor Tomov static int video_buf_init(struct vb2_buffer *vb)
407ec6859b2STodor Tomov {
408ec6859b2STodor Tomov 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
409ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
410ec6859b2STodor Tomov 	struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer,
411ec6859b2STodor Tomov 						   vb);
412ec6859b2STodor Tomov 	const struct v4l2_pix_format_mplane *format =
413ec6859b2STodor Tomov 						&video->active_fmt.fmt.pix_mp;
414ec6859b2STodor Tomov 	struct sg_table *sgt;
415ec6859b2STodor Tomov 	unsigned int i;
416ec6859b2STodor Tomov 
417ec6859b2STodor Tomov 	for (i = 0; i < format->num_planes; i++) {
418ec6859b2STodor Tomov 		sgt = vb2_dma_sg_plane_desc(vb, i);
419ec6859b2STodor Tomov 		if (!sgt)
420ec6859b2STodor Tomov 			return -EFAULT;
421ec6859b2STodor Tomov 
422ec6859b2STodor Tomov 		buffer->addr[i] = sg_dma_address(sgt->sgl);
423ec6859b2STodor Tomov 	}
424ec6859b2STodor Tomov 
425ec6859b2STodor Tomov 	if (format->pixelformat == V4L2_PIX_FMT_NV12 ||
426ec6859b2STodor Tomov 			format->pixelformat == V4L2_PIX_FMT_NV21 ||
427ec6859b2STodor Tomov 			format->pixelformat == V4L2_PIX_FMT_NV16 ||
428ec6859b2STodor Tomov 			format->pixelformat == V4L2_PIX_FMT_NV61)
429ec6859b2STodor Tomov 		buffer->addr[1] = buffer->addr[0] +
430ec6859b2STodor Tomov 				format->plane_fmt[0].bytesperline *
431ec6859b2STodor Tomov 				format->height;
432ec6859b2STodor Tomov 
433ec6859b2STodor Tomov 	return 0;
434ec6859b2STodor Tomov }
435ec6859b2STodor Tomov 
video_buf_prepare(struct vb2_buffer * vb)436ec6859b2STodor Tomov static int video_buf_prepare(struct vb2_buffer *vb)
437ec6859b2STodor Tomov {
438ec6859b2STodor Tomov 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
439ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
440ec6859b2STodor Tomov 	const struct v4l2_pix_format_mplane *format =
441ec6859b2STodor Tomov 						&video->active_fmt.fmt.pix_mp;
442ec6859b2STodor Tomov 	unsigned int i;
443ec6859b2STodor Tomov 
444ec6859b2STodor Tomov 	for (i = 0; i < format->num_planes; i++) {
445ec6859b2STodor Tomov 		if (format->plane_fmt[i].sizeimage > vb2_plane_size(vb, i))
446ec6859b2STodor Tomov 			return -EINVAL;
447ec6859b2STodor Tomov 
448ec6859b2STodor Tomov 		vb2_set_plane_payload(vb, i, format->plane_fmt[i].sizeimage);
449ec6859b2STodor Tomov 	}
450ec6859b2STodor Tomov 
451ec6859b2STodor Tomov 	vbuf->field = V4L2_FIELD_NONE;
452ec6859b2STodor Tomov 
453ec6859b2STodor Tomov 	return 0;
454ec6859b2STodor Tomov }
455ec6859b2STodor Tomov 
video_buf_queue(struct vb2_buffer * vb)456ec6859b2STodor Tomov static void video_buf_queue(struct vb2_buffer *vb)
457ec6859b2STodor Tomov {
458ec6859b2STodor Tomov 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
459ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
460ec6859b2STodor Tomov 	struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer,
461ec6859b2STodor Tomov 						   vb);
462ec6859b2STodor Tomov 
463ec6859b2STodor Tomov 	video->ops->queue_buffer(video, buffer);
464ec6859b2STodor Tomov }
465ec6859b2STodor Tomov 
video_check_format(struct camss_video * video)466ec6859b2STodor Tomov static int video_check_format(struct camss_video *video)
467ec6859b2STodor Tomov {
468ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *pix = &video->active_fmt.fmt.pix_mp;
469ec6859b2STodor Tomov 	struct v4l2_format format;
470ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *sd_pix = &format.fmt.pix_mp;
471ec6859b2STodor Tomov 	int ret;
472ec6859b2STodor Tomov 
473ec6859b2STodor Tomov 	sd_pix->pixelformat = pix->pixelformat;
474ec6859b2STodor Tomov 	ret = video_get_subdev_format(video, &format);
475ec6859b2STodor Tomov 	if (ret < 0)
476ec6859b2STodor Tomov 		return ret;
477ec6859b2STodor Tomov 
478ec6859b2STodor Tomov 	if (pix->pixelformat != sd_pix->pixelformat ||
479ec6859b2STodor Tomov 	    pix->height != sd_pix->height ||
480ec6859b2STodor Tomov 	    pix->width != sd_pix->width ||
481ec6859b2STodor Tomov 	    pix->num_planes != sd_pix->num_planes ||
482ec6859b2STodor Tomov 	    pix->field != format.fmt.pix_mp.field)
483ec6859b2STodor Tomov 		return -EPIPE;
484ec6859b2STodor Tomov 
485ec6859b2STodor Tomov 	return 0;
486ec6859b2STodor Tomov }
487ec6859b2STodor Tomov 
video_start_streaming(struct vb2_queue * q,unsigned int count)488ec6859b2STodor Tomov static int video_start_streaming(struct vb2_queue *q, unsigned int count)
489ec6859b2STodor Tomov {
490ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(q);
491ec6859b2STodor Tomov 	struct video_device *vdev = &video->vdev;
492ec6859b2STodor Tomov 	struct media_entity *entity;
493ec6859b2STodor Tomov 	struct media_pad *pad;
494ec6859b2STodor Tomov 	struct v4l2_subdev *subdev;
495ec6859b2STodor Tomov 	int ret;
496ec6859b2STodor Tomov 
49789013969SMilen Mitkov 	ret = video_device_pipeline_alloc_start(vdev);
49889013969SMilen Mitkov 	if (ret < 0) {
49989013969SMilen Mitkov 		dev_err(video->camss->dev, "Failed to start media pipeline: %d\n", ret);
500c8f35823SVladimir Zapolskiy 		goto flush_buffers;
50189013969SMilen Mitkov 	}
502ec6859b2STodor Tomov 
503ec6859b2STodor Tomov 	ret = video_check_format(video);
504ec6859b2STodor Tomov 	if (ret < 0)
505ec6859b2STodor Tomov 		goto error;
506ec6859b2STodor Tomov 
507ec6859b2STodor Tomov 	entity = &vdev->entity;
508ec6859b2STodor Tomov 	while (1) {
509ec6859b2STodor Tomov 		pad = &entity->pads[0];
510ec6859b2STodor Tomov 		if (!(pad->flags & MEDIA_PAD_FL_SINK))
511ec6859b2STodor Tomov 			break;
512ec6859b2STodor Tomov 
513b2e44430SLaurent Pinchart 		pad = media_pad_remote_pad_first(pad);
514ec6859b2STodor Tomov 		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
515ec6859b2STodor Tomov 			break;
516ec6859b2STodor Tomov 
517ec6859b2STodor Tomov 		entity = pad->entity;
518ec6859b2STodor Tomov 		subdev = media_entity_to_v4l2_subdev(entity);
519ec6859b2STodor Tomov 
520ec6859b2STodor Tomov 		ret = v4l2_subdev_call(subdev, video, s_stream, 1);
521ec6859b2STodor Tomov 		if (ret < 0 && ret != -ENOIOCTLCMD)
522ec6859b2STodor Tomov 			goto error;
523ec6859b2STodor Tomov 	}
524ec6859b2STodor Tomov 
525ec6859b2STodor Tomov 	return 0;
526ec6859b2STodor Tomov 
527ec6859b2STodor Tomov error:
52812cecbf9STomi Valkeinen 	video_device_pipeline_stop(vdev);
529ec6859b2STodor Tomov 
530c8f35823SVladimir Zapolskiy flush_buffers:
531ec6859b2STodor Tomov 	video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED);
532ec6859b2STodor Tomov 
533ec6859b2STodor Tomov 	return ret;
534ec6859b2STodor Tomov }
535ec6859b2STodor Tomov 
video_stop_streaming(struct vb2_queue * q)536ec6859b2STodor Tomov static void video_stop_streaming(struct vb2_queue *q)
537ec6859b2STodor Tomov {
538ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(q);
539ec6859b2STodor Tomov 	struct video_device *vdev = &video->vdev;
540ec6859b2STodor Tomov 	struct media_entity *entity;
541ec6859b2STodor Tomov 	struct media_pad *pad;
542ec6859b2STodor Tomov 	struct v4l2_subdev *subdev;
54389013969SMilen Mitkov 	int ret;
544ec6859b2STodor Tomov 
545ec6859b2STodor Tomov 	entity = &vdev->entity;
546ec6859b2STodor Tomov 	while (1) {
547ec6859b2STodor Tomov 		pad = &entity->pads[0];
548ec6859b2STodor Tomov 		if (!(pad->flags & MEDIA_PAD_FL_SINK))
549ec6859b2STodor Tomov 			break;
550ec6859b2STodor Tomov 
551b2e44430SLaurent Pinchart 		pad = media_pad_remote_pad_first(pad);
552ec6859b2STodor Tomov 		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
553ec6859b2STodor Tomov 			break;
554ec6859b2STodor Tomov 
555ec6859b2STodor Tomov 		entity = pad->entity;
556ec6859b2STodor Tomov 		subdev = media_entity_to_v4l2_subdev(entity);
557ec6859b2STodor Tomov 
55889013969SMilen Mitkov 		ret = v4l2_subdev_call(subdev, video, s_stream, 0);
55989013969SMilen Mitkov 
56089013969SMilen Mitkov 		if (entity->use_count > 1) {
56189013969SMilen Mitkov 			/* Don't stop if other instances of the pipeline are still running */
56289013969SMilen Mitkov 			dev_dbg(video->camss->dev, "Video pipeline still used, don't stop streaming.\n");
56389013969SMilen Mitkov 			return;
56489013969SMilen Mitkov 		}
56589013969SMilen Mitkov 
56689013969SMilen Mitkov 		if (ret) {
56789013969SMilen Mitkov 			dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret);
56889013969SMilen Mitkov 			return;
56989013969SMilen Mitkov 		}
570ec6859b2STodor Tomov 	}
571ec6859b2STodor Tomov 
57212cecbf9STomi Valkeinen 	video_device_pipeline_stop(vdev);
573ec6859b2STodor Tomov 
574ec6859b2STodor Tomov 	video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
575ec6859b2STodor Tomov }
576ec6859b2STodor Tomov 
577ec6859b2STodor Tomov static const struct vb2_ops msm_video_vb2_q_ops = {
578ec6859b2STodor Tomov 	.queue_setup     = video_queue_setup,
579ec6859b2STodor Tomov 	.wait_prepare    = vb2_ops_wait_prepare,
580ec6859b2STodor Tomov 	.wait_finish     = vb2_ops_wait_finish,
581ec6859b2STodor Tomov 	.buf_init        = video_buf_init,
582ec6859b2STodor Tomov 	.buf_prepare     = video_buf_prepare,
583ec6859b2STodor Tomov 	.buf_queue       = video_buf_queue,
584ec6859b2STodor Tomov 	.start_streaming = video_start_streaming,
585ec6859b2STodor Tomov 	.stop_streaming  = video_stop_streaming,
586ec6859b2STodor Tomov };
587ec6859b2STodor Tomov 
588ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
589ec6859b2STodor Tomov  * V4L2 ioctls
590ec6859b2STodor Tomov  */
591ec6859b2STodor Tomov 
video_querycap(struct file * file,void * fh,struct v4l2_capability * cap)592ec6859b2STodor Tomov static int video_querycap(struct file *file, void *fh,
593ec6859b2STodor Tomov 			  struct v4l2_capability *cap)
594ec6859b2STodor Tomov {
595c0decac1SMauro Carvalho Chehab 	strscpy(cap->driver, "qcom-camss", sizeof(cap->driver));
596c0decac1SMauro Carvalho Chehab 	strscpy(cap->card, "Qualcomm Camera Subsystem", sizeof(cap->card));
597ec6859b2STodor Tomov 
598ec6859b2STodor Tomov 	return 0;
599ec6859b2STodor Tomov }
600ec6859b2STodor Tomov 
video_enum_fmt(struct file * file,void * fh,struct v4l2_fmtdesc * f)601a3d412d4SAndrey Konovalov static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
602ec6859b2STodor Tomov {
603a3d412d4SAndrey Konovalov 	struct camss_video *video = video_drvdata(file);
604ec6859b2STodor Tomov 	int i, j, k;
605dfb5d328SAndrey Konovalov 	u32 mcode = f->mbus_code;
606ec6859b2STodor Tomov 
607a3d412d4SAndrey Konovalov 	if (f->type != video->type)
608a3d412d4SAndrey Konovalov 		return -EINVAL;
609a3d412d4SAndrey Konovalov 
610a3d412d4SAndrey Konovalov 	if (f->index >= video->nformats)
611a3d412d4SAndrey Konovalov 		return -EINVAL;
612a3d412d4SAndrey Konovalov 
613dfb5d328SAndrey Konovalov 	/*
614dfb5d328SAndrey Konovalov 	 * Find index "i" of "k"th unique pixelformat in formats array.
615dfb5d328SAndrey Konovalov 	 *
616dfb5d328SAndrey Konovalov 	 * If f->mbus_code passed to video_enum_fmt() is not zero, a device
617dfb5d328SAndrey Konovalov 	 * with V4L2_CAP_IO_MC capability restricts enumeration to only the
618dfb5d328SAndrey Konovalov 	 * pixel formats that can be produced from that media bus code.
619dfb5d328SAndrey Konovalov 	 * This is implemented by skipping video->formats[] entries with
620dfb5d328SAndrey Konovalov 	 * code != f->mbus_code (if f->mbus_code is not zero).
621dfb5d328SAndrey Konovalov 	 * If the f->mbus_code passed to video_enum_fmt() is not supported,
622dfb5d328SAndrey Konovalov 	 * -EINVAL is returned.
623dfb5d328SAndrey Konovalov 	 * If f->mbus_code is zero, all the pixel formats are enumerated.
624dfb5d328SAndrey Konovalov 	 */
625ec6859b2STodor Tomov 	k = -1;
626ec6859b2STodor Tomov 	for (i = 0; i < video->nformats; i++) {
627dfb5d328SAndrey Konovalov 		if (mcode != 0 && video->formats[i].code != mcode)
628dfb5d328SAndrey Konovalov 			continue;
629dfb5d328SAndrey Konovalov 
630ec6859b2STodor Tomov 		for (j = 0; j < i; j++) {
631dfb5d328SAndrey Konovalov 			if (mcode != 0 && video->formats[j].code != mcode)
632dfb5d328SAndrey Konovalov 				continue;
633ec6859b2STodor Tomov 			if (video->formats[i].pixelformat ==
634ec6859b2STodor Tomov 					video->formats[j].pixelformat)
635ec6859b2STodor Tomov 				break;
636ec6859b2STodor Tomov 		}
637ec6859b2STodor Tomov 
638ec6859b2STodor Tomov 		if (j == i)
639ec6859b2STodor Tomov 			k++;
640ec6859b2STodor Tomov 
641a3d412d4SAndrey Konovalov 		if (k == f->index)
642a3d412d4SAndrey Konovalov 			break;
643ec6859b2STodor Tomov 	}
644ec6859b2STodor Tomov 
645b00481bdSDan Carpenter 	if (k == -1 || k < f->index)
646dfb5d328SAndrey Konovalov 		/*
647dfb5d328SAndrey Konovalov 		 * All the unique pixel formats matching the arguments
648dfb5d328SAndrey Konovalov 		 * have been enumerated (k >= 0 and f->index > 0), or
649dfb5d328SAndrey Konovalov 		 * no pixel formats match the non-zero f->mbus_code (k == -1).
650dfb5d328SAndrey Konovalov 		 */
651ec6859b2STodor Tomov 		return -EINVAL;
652ec6859b2STodor Tomov 
653ec6859b2STodor Tomov 	f->pixelformat = video->formats[i].pixelformat;
654ec6859b2STodor Tomov 
655ec6859b2STodor Tomov 	return 0;
656ec6859b2STodor Tomov }
657ec6859b2STodor Tomov 
video_enum_framesizes(struct file * file,void * fh,struct v4l2_frmsizeenum * fsize)65835493d65SAndrey Konovalov static int video_enum_framesizes(struct file *file, void *fh,
65935493d65SAndrey Konovalov 				 struct v4l2_frmsizeenum *fsize)
66035493d65SAndrey Konovalov {
66135493d65SAndrey Konovalov 	struct camss_video *video = video_drvdata(file);
66235493d65SAndrey Konovalov 	int i;
66335493d65SAndrey Konovalov 
66435493d65SAndrey Konovalov 	if (fsize->index)
66535493d65SAndrey Konovalov 		return -EINVAL;
66635493d65SAndrey Konovalov 
66735493d65SAndrey Konovalov 	/* Only accept pixel format present in the formats[] table */
66835493d65SAndrey Konovalov 	for (i = 0; i < video->nformats; i++) {
66935493d65SAndrey Konovalov 		if (video->formats[i].pixelformat == fsize->pixel_format)
67035493d65SAndrey Konovalov 			break;
67135493d65SAndrey Konovalov 	}
67235493d65SAndrey Konovalov 
67335493d65SAndrey Konovalov 	if (i == video->nformats)
67435493d65SAndrey Konovalov 		return -EINVAL;
67535493d65SAndrey Konovalov 
67635493d65SAndrey Konovalov 	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
67735493d65SAndrey Konovalov 	fsize->stepwise.min_width = CAMSS_FRAME_MIN_WIDTH;
67835493d65SAndrey Konovalov 	fsize->stepwise.max_width = CAMSS_FRAME_MAX_WIDTH;
67935493d65SAndrey Konovalov 	fsize->stepwise.min_height = CAMSS_FRAME_MIN_HEIGHT;
68035493d65SAndrey Konovalov 	fsize->stepwise.max_height = (video->line_based) ?
68135493d65SAndrey Konovalov 		CAMSS_FRAME_MAX_HEIGHT_PIX : CAMSS_FRAME_MAX_HEIGHT_RDI;
68235493d65SAndrey Konovalov 	fsize->stepwise.step_width = 1;
68335493d65SAndrey Konovalov 	fsize->stepwise.step_height = 1;
68435493d65SAndrey Konovalov 
68535493d65SAndrey Konovalov 	return 0;
68635493d65SAndrey Konovalov }
68735493d65SAndrey Konovalov 
video_g_fmt(struct file * file,void * fh,struct v4l2_format * f)688ec6859b2STodor Tomov static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
689ec6859b2STodor Tomov {
690ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
691ec6859b2STodor Tomov 
692ec6859b2STodor Tomov 	*f = video->active_fmt;
693ec6859b2STodor Tomov 
694ec6859b2STodor Tomov 	return 0;
695ec6859b2STodor Tomov }
696ec6859b2STodor Tomov 
__video_try_fmt(struct camss_video * video,struct v4l2_format * f)697ec6859b2STodor Tomov static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f)
698ec6859b2STodor Tomov {
699ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *pix_mp;
700ec6859b2STodor Tomov 	const struct camss_format_info *fi;
701ec6859b2STodor Tomov 	struct v4l2_plane_pix_format *p;
702ec6859b2STodor Tomov 	u32 bytesperline[3] = { 0 };
703ec6859b2STodor Tomov 	u32 sizeimage[3] = { 0 };
704ec6859b2STodor Tomov 	u32 width, height;
705ec6859b2STodor Tomov 	u32 bpl, lines;
706ec6859b2STodor Tomov 	int i, j;
707ec6859b2STodor Tomov 
708ec6859b2STodor Tomov 	pix_mp = &f->fmt.pix_mp;
709ec6859b2STodor Tomov 
710ec6859b2STodor Tomov 	if (video->line_based)
711ec6859b2STodor Tomov 		for (i = 0; i < pix_mp->num_planes && i < 3; i++) {
712ec6859b2STodor Tomov 			p = &pix_mp->plane_fmt[i];
713ec6859b2STodor Tomov 			bytesperline[i] = clamp_t(u32, p->bytesperline,
714ec6859b2STodor Tomov 						  1, 65528);
715ec6859b2STodor Tomov 			sizeimage[i] = clamp_t(u32, p->sizeimage,
716ec6859b2STodor Tomov 					       bytesperline[i],
717daf2298bSAndrey Konovalov 					       bytesperline[i] * CAMSS_FRAME_MAX_HEIGHT_PIX);
718ec6859b2STodor Tomov 		}
719ec6859b2STodor Tomov 
720ec6859b2STodor Tomov 	for (j = 0; j < video->nformats; j++)
721ec6859b2STodor Tomov 		if (pix_mp->pixelformat == video->formats[j].pixelformat)
722ec6859b2STodor Tomov 			break;
723ec6859b2STodor Tomov 
724ec6859b2STodor Tomov 	if (j == video->nformats)
725ec6859b2STodor Tomov 		j = 0; /* default format */
726ec6859b2STodor Tomov 
727ec6859b2STodor Tomov 	fi = &video->formats[j];
728ec6859b2STodor Tomov 	width = pix_mp->width;
729ec6859b2STodor Tomov 	height = pix_mp->height;
730ec6859b2STodor Tomov 
731ec6859b2STodor Tomov 	memset(pix_mp, 0, sizeof(*pix_mp));
732ec6859b2STodor Tomov 
733ec6859b2STodor Tomov 	pix_mp->pixelformat = fi->pixelformat;
734daf2298bSAndrey Konovalov 	pix_mp->width = clamp_t(u32, width, 1, CAMSS_FRAME_MAX_WIDTH);
735daf2298bSAndrey Konovalov 	pix_mp->height = clamp_t(u32, height, 1, CAMSS_FRAME_MAX_HEIGHT_RDI);
736ec6859b2STodor Tomov 	pix_mp->num_planes = fi->planes;
737ec6859b2STodor Tomov 	for (i = 0; i < pix_mp->num_planes; i++) {
738ec6859b2STodor Tomov 		bpl = pix_mp->width / fi->hsub[i].numerator *
739ec6859b2STodor Tomov 			fi->hsub[i].denominator * fi->bpp[i] / 8;
740ec6859b2STodor Tomov 		bpl = ALIGN(bpl, video->bpl_alignment);
741ec6859b2STodor Tomov 		pix_mp->plane_fmt[i].bytesperline = bpl;
742ec6859b2STodor Tomov 		pix_mp->plane_fmt[i].sizeimage = pix_mp->height /
743ec6859b2STodor Tomov 			fi->vsub[i].numerator * fi->vsub[i].denominator * bpl;
744ec6859b2STodor Tomov 	}
745ec6859b2STodor Tomov 
746ec6859b2STodor Tomov 	pix_mp->field = V4L2_FIELD_NONE;
747ec6859b2STodor Tomov 	pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
748ec6859b2STodor Tomov 	pix_mp->flags = 0;
749ec6859b2STodor Tomov 	pix_mp->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace);
750ec6859b2STodor Tomov 	pix_mp->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
751ec6859b2STodor Tomov 					pix_mp->colorspace, pix_mp->ycbcr_enc);
752ec6859b2STodor Tomov 	pix_mp->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace);
753ec6859b2STodor Tomov 
754ec6859b2STodor Tomov 	if (video->line_based)
755ec6859b2STodor Tomov 		for (i = 0; i < pix_mp->num_planes; i++) {
756ec6859b2STodor Tomov 			p = &pix_mp->plane_fmt[i];
757ec6859b2STodor Tomov 			p->bytesperline = clamp_t(u32, p->bytesperline,
758ec6859b2STodor Tomov 						  1, 65528);
759ec6859b2STodor Tomov 			p->sizeimage = clamp_t(u32, p->sizeimage,
760ec6859b2STodor Tomov 					       p->bytesperline,
761daf2298bSAndrey Konovalov 					       p->bytesperline * CAMSS_FRAME_MAX_HEIGHT_PIX);
762ec6859b2STodor Tomov 			lines = p->sizeimage / p->bytesperline;
763ec6859b2STodor Tomov 
764ec6859b2STodor Tomov 			if (p->bytesperline < bytesperline[i])
765ec6859b2STodor Tomov 				p->bytesperline = ALIGN(bytesperline[i], 8);
766ec6859b2STodor Tomov 
767ec6859b2STodor Tomov 			if (p->sizeimage < p->bytesperline * lines)
768ec6859b2STodor Tomov 				p->sizeimage = p->bytesperline * lines;
769ec6859b2STodor Tomov 
770ec6859b2STodor Tomov 			if (p->sizeimage < sizeimage[i])
771ec6859b2STodor Tomov 				p->sizeimage = sizeimage[i];
772ec6859b2STodor Tomov 		}
773ec6859b2STodor Tomov 
774ec6859b2STodor Tomov 	return 0;
775ec6859b2STodor Tomov }
776ec6859b2STodor Tomov 
video_try_fmt(struct file * file,void * fh,struct v4l2_format * f)777ec6859b2STodor Tomov static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
778ec6859b2STodor Tomov {
779ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
780ec6859b2STodor Tomov 
781ec6859b2STodor Tomov 	return __video_try_fmt(video, f);
782ec6859b2STodor Tomov }
783ec6859b2STodor Tomov 
video_s_fmt(struct file * file,void * fh,struct v4l2_format * f)784ec6859b2STodor Tomov static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
785ec6859b2STodor Tomov {
786ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
787ec6859b2STodor Tomov 	int ret;
788ec6859b2STodor Tomov 
789ec6859b2STodor Tomov 	if (vb2_is_busy(&video->vb2_q))
790ec6859b2STodor Tomov 		return -EBUSY;
791ec6859b2STodor Tomov 
792ec6859b2STodor Tomov 	ret = __video_try_fmt(video, f);
793ec6859b2STodor Tomov 	if (ret < 0)
794ec6859b2STodor Tomov 		return ret;
795ec6859b2STodor Tomov 
796ec6859b2STodor Tomov 	video->active_fmt = *f;
797ec6859b2STodor Tomov 
798ec6859b2STodor Tomov 	return 0;
799ec6859b2STodor Tomov }
800ec6859b2STodor Tomov 
video_enum_input(struct file * file,void * fh,struct v4l2_input * input)801ec6859b2STodor Tomov static int video_enum_input(struct file *file, void *fh,
802ec6859b2STodor Tomov 			    struct v4l2_input *input)
803ec6859b2STodor Tomov {
804ec6859b2STodor Tomov 	if (input->index > 0)
805ec6859b2STodor Tomov 		return -EINVAL;
806ec6859b2STodor Tomov 
807c0decac1SMauro Carvalho Chehab 	strscpy(input->name, "camera", sizeof(input->name));
808ec6859b2STodor Tomov 	input->type = V4L2_INPUT_TYPE_CAMERA;
809ec6859b2STodor Tomov 
810ec6859b2STodor Tomov 	return 0;
811ec6859b2STodor Tomov }
812ec6859b2STodor Tomov 
video_g_input(struct file * file,void * fh,unsigned int * input)813ec6859b2STodor Tomov static int video_g_input(struct file *file, void *fh, unsigned int *input)
814ec6859b2STodor Tomov {
815ec6859b2STodor Tomov 	*input = 0;
816ec6859b2STodor Tomov 
817ec6859b2STodor Tomov 	return 0;
818ec6859b2STodor Tomov }
819ec6859b2STodor Tomov 
video_s_input(struct file * file,void * fh,unsigned int input)820ec6859b2STodor Tomov static int video_s_input(struct file *file, void *fh, unsigned int input)
821ec6859b2STodor Tomov {
822ec6859b2STodor Tomov 	return input == 0 ? 0 : -EINVAL;
823ec6859b2STodor Tomov }
824ec6859b2STodor Tomov 
825ec6859b2STodor Tomov static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = {
826ec6859b2STodor Tomov 	.vidioc_querycap		= video_querycap,
8277e98b7b5SBoris Brezillon 	.vidioc_enum_fmt_vid_cap	= video_enum_fmt,
82835493d65SAndrey Konovalov 	.vidioc_enum_framesizes		= video_enum_framesizes,
829ec6859b2STodor Tomov 	.vidioc_g_fmt_vid_cap_mplane	= video_g_fmt,
830ec6859b2STodor Tomov 	.vidioc_s_fmt_vid_cap_mplane	= video_s_fmt,
831ec6859b2STodor Tomov 	.vidioc_try_fmt_vid_cap_mplane	= video_try_fmt,
832ec6859b2STodor Tomov 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
833ec6859b2STodor Tomov 	.vidioc_querybuf		= vb2_ioctl_querybuf,
834ec6859b2STodor Tomov 	.vidioc_qbuf			= vb2_ioctl_qbuf,
835ec6859b2STodor Tomov 	.vidioc_expbuf			= vb2_ioctl_expbuf,
836ec6859b2STodor Tomov 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
837ec6859b2STodor Tomov 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
838ec6859b2STodor Tomov 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
839ec6859b2STodor Tomov 	.vidioc_streamon		= vb2_ioctl_streamon,
840ec6859b2STodor Tomov 	.vidioc_streamoff		= vb2_ioctl_streamoff,
841ec6859b2STodor Tomov 	.vidioc_enum_input		= video_enum_input,
842ec6859b2STodor Tomov 	.vidioc_g_input			= video_g_input,
843ec6859b2STodor Tomov 	.vidioc_s_input			= video_s_input,
844ec6859b2STodor Tomov };
845ec6859b2STodor Tomov 
846ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
847ec6859b2STodor Tomov  * V4L2 file operations
848ec6859b2STodor Tomov  */
849ec6859b2STodor Tomov 
video_open(struct file * file)850ec6859b2STodor Tomov static int video_open(struct file *file)
851ec6859b2STodor Tomov {
852ec6859b2STodor Tomov 	struct video_device *vdev = video_devdata(file);
853ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
854ec6859b2STodor Tomov 	struct v4l2_fh *vfh;
855ec6859b2STodor Tomov 	int ret;
856ec6859b2STodor Tomov 
857ec6859b2STodor Tomov 	mutex_lock(&video->lock);
858ec6859b2STodor Tomov 
859ec6859b2STodor Tomov 	vfh = kzalloc(sizeof(*vfh), GFP_KERNEL);
860ec6859b2STodor Tomov 	if (vfh == NULL) {
861ec6859b2STodor Tomov 		ret = -ENOMEM;
862ec6859b2STodor Tomov 		goto error_alloc;
863ec6859b2STodor Tomov 	}
864ec6859b2STodor Tomov 
865ec6859b2STodor Tomov 	v4l2_fh_init(vfh, vdev);
866ec6859b2STodor Tomov 	v4l2_fh_add(vfh);
867ec6859b2STodor Tomov 
868ec6859b2STodor Tomov 	file->private_data = vfh;
869ec6859b2STodor Tomov 
8708fd390b8SEzequiel Garcia 	ret = v4l2_pipeline_pm_get(&vdev->entity);
871ec6859b2STodor Tomov 	if (ret < 0) {
872ec6859b2STodor Tomov 		dev_err(video->camss->dev, "Failed to power up pipeline: %d\n",
873ec6859b2STodor Tomov 			ret);
874ec6859b2STodor Tomov 		goto error_pm_use;
875ec6859b2STodor Tomov 	}
876ec6859b2STodor Tomov 
877ec6859b2STodor Tomov 	mutex_unlock(&video->lock);
878ec6859b2STodor Tomov 
879ec6859b2STodor Tomov 	return 0;
880ec6859b2STodor Tomov 
881ec6859b2STodor Tomov error_pm_use:
882ec6859b2STodor Tomov 	v4l2_fh_release(file);
883ec6859b2STodor Tomov 
884ec6859b2STodor Tomov error_alloc:
885ec6859b2STodor Tomov 	mutex_unlock(&video->lock);
886ec6859b2STodor Tomov 
887ec6859b2STodor Tomov 	return ret;
888ec6859b2STodor Tomov }
889ec6859b2STodor Tomov 
video_release(struct file * file)890ec6859b2STodor Tomov static int video_release(struct file *file)
891ec6859b2STodor Tomov {
892ec6859b2STodor Tomov 	struct video_device *vdev = video_devdata(file);
893ec6859b2STodor Tomov 
894ec6859b2STodor Tomov 	vb2_fop_release(file);
895ec6859b2STodor Tomov 
8968fd390b8SEzequiel Garcia 	v4l2_pipeline_pm_put(&vdev->entity);
897ec6859b2STodor Tomov 
898ec6859b2STodor Tomov 	file->private_data = NULL;
899ec6859b2STodor Tomov 
900ec6859b2STodor Tomov 	return 0;
901ec6859b2STodor Tomov }
902ec6859b2STodor Tomov 
903ec6859b2STodor Tomov static const struct v4l2_file_operations msm_vid_fops = {
904ec6859b2STodor Tomov 	.owner          = THIS_MODULE,
905ec6859b2STodor Tomov 	.unlocked_ioctl = video_ioctl2,
906ec6859b2STodor Tomov 	.open           = video_open,
907ec6859b2STodor Tomov 	.release        = video_release,
908ec6859b2STodor Tomov 	.poll           = vb2_fop_poll,
909ec6859b2STodor Tomov 	.mmap		= vb2_fop_mmap,
910ec6859b2STodor Tomov 	.read		= vb2_fop_read,
911ec6859b2STodor Tomov };
912ec6859b2STodor Tomov 
913ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
914ec6859b2STodor Tomov  * CAMSS video core
915ec6859b2STodor Tomov  */
916ec6859b2STodor Tomov 
msm_video_release(struct video_device * vdev)917ec6859b2STodor Tomov static void msm_video_release(struct video_device *vdev)
918ec6859b2STodor Tomov {
919ec6859b2STodor Tomov 	struct camss_video *video = video_get_drvdata(vdev);
920ec6859b2STodor Tomov 
921ec6859b2STodor Tomov 	media_entity_cleanup(&vdev->entity);
922ec6859b2STodor Tomov 
923ec6859b2STodor Tomov 	mutex_destroy(&video->q_lock);
924ec6859b2STodor Tomov 	mutex_destroy(&video->lock);
925ec6859b2STodor Tomov 
926ec6859b2STodor Tomov 	if (atomic_dec_and_test(&video->camss->ref_count))
927ec6859b2STodor Tomov 		camss_delete(video->camss);
928ec6859b2STodor Tomov }
929ec6859b2STodor Tomov 
930ec6859b2STodor Tomov /*
931ec6859b2STodor Tomov  * msm_video_init_format - Helper function to initialize format
932ec6859b2STodor Tomov  * @video: struct camss_video
933ec6859b2STodor Tomov  *
934ec6859b2STodor Tomov  * Initialize pad format with default value.
935ec6859b2STodor Tomov  *
936ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
937ec6859b2STodor Tomov  */
msm_video_init_format(struct camss_video * video)938ec6859b2STodor Tomov static int msm_video_init_format(struct camss_video *video)
939ec6859b2STodor Tomov {
940ec6859b2STodor Tomov 	int ret;
941ec6859b2STodor Tomov 	struct v4l2_format format = {
942ec6859b2STodor Tomov 		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
943ec6859b2STodor Tomov 		.fmt.pix_mp = {
944ec6859b2STodor Tomov 			.width = 1920,
945ec6859b2STodor Tomov 			.height = 1080,
946ec6859b2STodor Tomov 			.pixelformat = video->formats[0].pixelformat,
947ec6859b2STodor Tomov 		},
948ec6859b2STodor Tomov 	};
949ec6859b2STodor Tomov 
950ec6859b2STodor Tomov 	ret = __video_try_fmt(video, &format);
951ec6859b2STodor Tomov 	if (ret < 0)
952ec6859b2STodor Tomov 		return ret;
953ec6859b2STodor Tomov 
954ec6859b2STodor Tomov 	video->active_fmt = format;
955ec6859b2STodor Tomov 
956ec6859b2STodor Tomov 	return 0;
957ec6859b2STodor Tomov }
958ec6859b2STodor Tomov 
959ec6859b2STodor Tomov /*
960ec6859b2STodor Tomov  * msm_video_register - Register a video device node
961ec6859b2STodor Tomov  * @video: struct camss_video
962ec6859b2STodor Tomov  * @v4l2_dev: V4L2 device
963ec6859b2STodor Tomov  * @name: name to be used for the video device node
964ec6859b2STodor Tomov  *
965ec6859b2STodor Tomov  * Initialize and register a video device node to a V4L2 device. Also
966ec6859b2STodor Tomov  * initialize the vb2 queue.
967ec6859b2STodor Tomov  *
968ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
969ec6859b2STodor Tomov  */
970ec6859b2STodor Tomov 
msm_video_register(struct camss_video * video,struct v4l2_device * v4l2_dev,const char * name,int is_pix)971ec6859b2STodor Tomov int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
972ec6859b2STodor Tomov 		       const char *name, int is_pix)
973ec6859b2STodor Tomov {
974ec6859b2STodor Tomov 	struct media_pad *pad = &video->pad;
975ec6859b2STodor Tomov 	struct video_device *vdev;
976ec6859b2STodor Tomov 	struct vb2_queue *q;
977ec6859b2STodor Tomov 	int ret;
978ec6859b2STodor Tomov 
979ec6859b2STodor Tomov 	vdev = &video->vdev;
980ec6859b2STodor Tomov 
981ec6859b2STodor Tomov 	mutex_init(&video->q_lock);
982ec6859b2STodor Tomov 
983ec6859b2STodor Tomov 	q = &video->vb2_q;
984ec6859b2STodor Tomov 	q->drv_priv = video;
985ec6859b2STodor Tomov 	q->mem_ops = &vb2_dma_sg_memops;
986ec6859b2STodor Tomov 	q->ops = &msm_video_vb2_q_ops;
987ec6859b2STodor Tomov 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
988ec6859b2STodor Tomov 	q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ;
989ec6859b2STodor Tomov 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
990ec6859b2STodor Tomov 	q->buf_struct_size = sizeof(struct camss_buffer);
991ec6859b2STodor Tomov 	q->dev = video->camss->dev;
992ec6859b2STodor Tomov 	q->lock = &video->q_lock;
993ec6859b2STodor Tomov 	ret = vb2_queue_init(q);
994ec6859b2STodor Tomov 	if (ret < 0) {
995ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret);
996ec6859b2STodor Tomov 		goto error_vb2_init;
997ec6859b2STodor Tomov 	}
998ec6859b2STodor Tomov 
999ec6859b2STodor Tomov 	pad->flags = MEDIA_PAD_FL_SINK;
1000ec6859b2STodor Tomov 	ret = media_entity_pads_init(&vdev->entity, 1, pad);
1001ec6859b2STodor Tomov 	if (ret < 0) {
1002ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n",
1003ec6859b2STodor Tomov 			ret);
1004492abcd7SHans Verkuil 		goto error_vb2_init;
1005ec6859b2STodor Tomov 	}
1006ec6859b2STodor Tomov 
1007ec6859b2STodor Tomov 	mutex_init(&video->lock);
1008ec6859b2STodor Tomov 
1009cba3819dSTodor Tomov 	if (video->camss->version == CAMSS_8x16) {
1010ec6859b2STodor Tomov 		if (is_pix) {
1011cba3819dSTodor Tomov 			video->formats = formats_pix_8x16;
1012cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_pix_8x16);
1013cba3819dSTodor Tomov 		} else {
1014cba3819dSTodor Tomov 			video->formats = formats_rdi_8x16;
1015cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_rdi_8x16);
1016cba3819dSTodor Tomov 		}
10179e5d1581SAngeloGioacchino Del Regno 	} else if (video->camss->version == CAMSS_8x96 ||
10189e5d1581SAngeloGioacchino Del Regno 		   video->camss->version == CAMSS_660) {
1019cba3819dSTodor Tomov 		if (is_pix) {
1020cba3819dSTodor Tomov 			video->formats = formats_pix_8x96;
1021cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_pix_8x96);
1022cba3819dSTodor Tomov 		} else {
1023cba3819dSTodor Tomov 			video->formats = formats_rdi_8x96;
1024cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_rdi_8x96);
1025cba3819dSTodor Tomov 		}
1026b4436a18SJonathan Marek 	}  else if (video->camss->version == CAMSS_845 ||
1027b4436a18SJonathan Marek 		    video->camss->version == CAMSS_8250) {
10287319cdf1SRobert Foss 		video->formats = formats_rdi_845;
10297319cdf1SRobert Foss 		video->nformats = ARRAY_SIZE(formats_rdi_845);
1030cba3819dSTodor Tomov 	} else {
10319c67ed2aSDan Carpenter 		ret = -EINVAL;
1032cba3819dSTodor Tomov 		goto error_video_register;
1033ec6859b2STodor Tomov 	}
1034ec6859b2STodor Tomov 
1035ec6859b2STodor Tomov 	ret = msm_video_init_format(video);
1036ec6859b2STodor Tomov 	if (ret < 0) {
1037ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret);
1038ec6859b2STodor Tomov 		goto error_video_register;
1039ec6859b2STodor Tomov 	}
1040ec6859b2STodor Tomov 
1041ec6859b2STodor Tomov 	vdev->fops = &msm_vid_fops;
1042dfb5d328SAndrey Konovalov 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING
1043dfb5d328SAndrey Konovalov 			  | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC;
1044ec6859b2STodor Tomov 	vdev->ioctl_ops = &msm_vid_ioctl_ops;
1045ec6859b2STodor Tomov 	vdev->release = msm_video_release;
1046ec6859b2STodor Tomov 	vdev->v4l2_dev = v4l2_dev;
1047ec6859b2STodor Tomov 	vdev->vfl_dir = VFL_DIR_RX;
1048ec6859b2STodor Tomov 	vdev->queue = &video->vb2_q;
1049ec6859b2STodor Tomov 	vdev->lock = &video->lock;
1050c0decac1SMauro Carvalho Chehab 	strscpy(vdev->name, name, sizeof(vdev->name));
1051ec6859b2STodor Tomov 
105270cad449SHans Verkuil 	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
1053ec6859b2STodor Tomov 	if (ret < 0) {
1054ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to register video device: %d\n",
1055ec6859b2STodor Tomov 			ret);
1056ec6859b2STodor Tomov 		goto error_video_register;
1057ec6859b2STodor Tomov 	}
1058ec6859b2STodor Tomov 
1059ec6859b2STodor Tomov 	video_set_drvdata(vdev, video);
1060ec6859b2STodor Tomov 	atomic_inc(&video->camss->ref_count);
1061ec6859b2STodor Tomov 
1062ec6859b2STodor Tomov 	return 0;
1063ec6859b2STodor Tomov 
1064ec6859b2STodor Tomov error_video_register:
1065ec6859b2STodor Tomov 	media_entity_cleanup(&vdev->entity);
1066ec6859b2STodor Tomov 	mutex_destroy(&video->lock);
1067ec6859b2STodor Tomov error_vb2_init:
1068ec6859b2STodor Tomov 	mutex_destroy(&video->q_lock);
1069ec6859b2STodor Tomov 
1070ec6859b2STodor Tomov 	return ret;
1071ec6859b2STodor Tomov }
1072ec6859b2STodor Tomov 
msm_video_unregister(struct camss_video * video)1073ec6859b2STodor Tomov void msm_video_unregister(struct camss_video *video)
1074ec6859b2STodor Tomov {
1075ec6859b2STodor Tomov 	atomic_inc(&video->camss->ref_count);
1076492abcd7SHans Verkuil 	vb2_video_unregister_device(&video->vdev);
1077ec6859b2STodor Tomov 	atomic_dec(&video->camss->ref_count);
1078ec6859b2STodor Tomov }
1079