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 
136cba3819dSTodor Tomov static const struct camss_format_info formats_pix_8x16[] = {
137cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1,
138cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
139cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1,
140cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
141cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
142cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
143cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
144cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
145cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1,
146cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
147cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1,
148cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
149cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
150cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
151cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
152cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
153cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1,
154cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
155cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1,
156cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
157cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1,
158cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
159cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1,
160cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
161cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1,
162cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
163cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1,
164cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
165cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1,
166cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
167cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1,
168cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
169cba3819dSTodor Tomov };
170cba3819dSTodor Tomov 
171cba3819dSTodor Tomov static const struct camss_format_info formats_pix_8x96[] = {
172ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1,
173ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
174ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1,
175ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
176ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
177ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
178ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
179ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
180ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1,
181ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
182ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1,
183ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
184ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
185ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
186ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
187ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
188ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1,
189ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
190ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1,
191ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
192ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1,
193ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
194ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1,
195ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
196ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1,
197ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
198ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1,
199ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
200ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1,
201ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
202ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1,
203ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
204312e1c85STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
205312e1c85STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
206312e1c85STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
207312e1c85STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
208312e1c85STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
209312e1c85STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
210312e1c85STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1,
211312e1c85STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
212ec6859b2STodor Tomov };
213ec6859b2STodor Tomov 
214ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
215ec6859b2STodor Tomov  * Helper functions
216ec6859b2STodor Tomov  */
217ec6859b2STodor Tomov 
218ec6859b2STodor Tomov static int video_find_format(u32 code, u32 pixelformat,
219ec6859b2STodor Tomov 			     const struct camss_format_info *formats,
220ec6859b2STodor Tomov 			     unsigned int nformats)
221ec6859b2STodor Tomov {
222ec6859b2STodor Tomov 	int i;
223ec6859b2STodor Tomov 
224ec6859b2STodor Tomov 	for (i = 0; i < nformats; i++) {
225ec6859b2STodor Tomov 		if (formats[i].code == code &&
226ec6859b2STodor Tomov 		    formats[i].pixelformat == pixelformat)
227ec6859b2STodor Tomov 			return i;
228ec6859b2STodor Tomov 	}
229ec6859b2STodor Tomov 
230ec6859b2STodor Tomov 	for (i = 0; i < nformats; i++)
231ec6859b2STodor Tomov 		if (formats[i].code == code)
232ec6859b2STodor Tomov 			return i;
233ec6859b2STodor Tomov 
234ec6859b2STodor Tomov 	WARN_ON(1);
235ec6859b2STodor Tomov 
236ec6859b2STodor Tomov 	return -EINVAL;
237ec6859b2STodor Tomov }
238ec6859b2STodor Tomov 
239ec6859b2STodor Tomov /*
240ec6859b2STodor Tomov  * video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane
241ec6859b2STodor Tomov  * @mbus: v4l2_mbus_framefmt format (input)
242ec6859b2STodor Tomov  * @pix: v4l2_pix_format_mplane format (output)
243ec6859b2STodor Tomov  * @f: a pointer to formats array element to be used for the conversion
244ec6859b2STodor Tomov  * @alignment: bytesperline alignment value
245ec6859b2STodor Tomov  *
246ec6859b2STodor Tomov  * Fill the output pix structure with information from the input mbus format.
247ec6859b2STodor Tomov  *
248ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
249ec6859b2STodor Tomov  */
250ec6859b2STodor Tomov static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
251ec6859b2STodor Tomov 				struct v4l2_pix_format_mplane *pix,
252ec6859b2STodor Tomov 				const struct camss_format_info *f,
253ec6859b2STodor Tomov 				unsigned int alignment)
254ec6859b2STodor Tomov {
255ec6859b2STodor Tomov 	unsigned int i;
256ec6859b2STodor Tomov 	u32 bytesperline;
257ec6859b2STodor Tomov 
258ec6859b2STodor Tomov 	memset(pix, 0, sizeof(*pix));
259ec6859b2STodor Tomov 	v4l2_fill_pix_format_mplane(pix, mbus);
260ec6859b2STodor Tomov 	pix->pixelformat = f->pixelformat;
261ec6859b2STodor Tomov 	pix->num_planes = f->planes;
262ec6859b2STodor Tomov 	for (i = 0; i < pix->num_planes; i++) {
263ec6859b2STodor Tomov 		bytesperline = pix->width / f->hsub[i].numerator *
264ec6859b2STodor Tomov 			f->hsub[i].denominator * f->bpp[i] / 8;
265ec6859b2STodor Tomov 		bytesperline = ALIGN(bytesperline, alignment);
266ec6859b2STodor Tomov 		pix->plane_fmt[i].bytesperline = bytesperline;
267ec6859b2STodor Tomov 		pix->plane_fmt[i].sizeimage = pix->height /
268ec6859b2STodor Tomov 				f->vsub[i].numerator * f->vsub[i].denominator *
269ec6859b2STodor Tomov 				bytesperline;
270ec6859b2STodor Tomov 	}
271ec6859b2STodor Tomov 
272ec6859b2STodor Tomov 	return 0;
273ec6859b2STodor Tomov }
274ec6859b2STodor Tomov 
275ec6859b2STodor Tomov static struct v4l2_subdev *video_remote_subdev(struct camss_video *video,
276ec6859b2STodor Tomov 					       u32 *pad)
277ec6859b2STodor Tomov {
278ec6859b2STodor Tomov 	struct media_pad *remote;
279ec6859b2STodor Tomov 
280ec6859b2STodor Tomov 	remote = media_entity_remote_pad(&video->pad);
281ec6859b2STodor Tomov 
282ec6859b2STodor Tomov 	if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
283ec6859b2STodor Tomov 		return NULL;
284ec6859b2STodor Tomov 
285ec6859b2STodor Tomov 	if (pad)
286ec6859b2STodor Tomov 		*pad = remote->index;
287ec6859b2STodor Tomov 
288ec6859b2STodor Tomov 	return media_entity_to_v4l2_subdev(remote->entity);
289ec6859b2STodor Tomov }
290ec6859b2STodor Tomov 
291ec6859b2STodor Tomov static int video_get_subdev_format(struct camss_video *video,
292ec6859b2STodor Tomov 				   struct v4l2_format *format)
293ec6859b2STodor Tomov {
294ec6859b2STodor Tomov 	struct v4l2_subdev_format fmt;
295ec6859b2STodor Tomov 	struct v4l2_subdev *subdev;
296ec6859b2STodor Tomov 	u32 pad;
297ec6859b2STodor Tomov 	int ret;
298ec6859b2STodor Tomov 
299ec6859b2STodor Tomov 	subdev = video_remote_subdev(video, &pad);
300ec6859b2STodor Tomov 	if (subdev == NULL)
301ec6859b2STodor Tomov 		return -EPIPE;
302ec6859b2STodor Tomov 
303ec6859b2STodor Tomov 	fmt.pad = pad;
304ec6859b2STodor Tomov 	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
305ec6859b2STodor Tomov 
306ec6859b2STodor Tomov 	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
307ec6859b2STodor Tomov 	if (ret)
308ec6859b2STodor Tomov 		return ret;
309ec6859b2STodor Tomov 
310ec6859b2STodor Tomov 	ret = video_find_format(fmt.format.code,
311ec6859b2STodor Tomov 				format->fmt.pix_mp.pixelformat,
312ec6859b2STodor Tomov 				video->formats, video->nformats);
313ec6859b2STodor Tomov 	if (ret < 0)
314ec6859b2STodor Tomov 		return ret;
315ec6859b2STodor Tomov 
316ec6859b2STodor Tomov 	format->type = video->type;
317ec6859b2STodor Tomov 
318ec6859b2STodor Tomov 	return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp,
319ec6859b2STodor Tomov 				    &video->formats[ret], video->bpl_alignment);
320ec6859b2STodor Tomov }
321ec6859b2STodor Tomov 
322ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
323ec6859b2STodor Tomov  * Video queue operations
324ec6859b2STodor Tomov  */
325ec6859b2STodor Tomov 
326ec6859b2STodor Tomov static int video_queue_setup(struct vb2_queue *q,
327ec6859b2STodor Tomov 	unsigned int *num_buffers, unsigned int *num_planes,
328ec6859b2STodor Tomov 	unsigned int sizes[], struct device *alloc_devs[])
329ec6859b2STodor Tomov {
330ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(q);
331ec6859b2STodor Tomov 	const struct v4l2_pix_format_mplane *format =
332ec6859b2STodor Tomov 						&video->active_fmt.fmt.pix_mp;
333ec6859b2STodor Tomov 	unsigned int i;
334ec6859b2STodor Tomov 
335ec6859b2STodor Tomov 	if (*num_planes) {
336ec6859b2STodor Tomov 		if (*num_planes != format->num_planes)
337ec6859b2STodor Tomov 			return -EINVAL;
338ec6859b2STodor Tomov 
339ec6859b2STodor Tomov 		for (i = 0; i < *num_planes; i++)
340ec6859b2STodor Tomov 			if (sizes[i] < format->plane_fmt[i].sizeimage)
341ec6859b2STodor Tomov 				return -EINVAL;
342ec6859b2STodor Tomov 
343ec6859b2STodor Tomov 		return 0;
344ec6859b2STodor Tomov 	}
345ec6859b2STodor Tomov 
346ec6859b2STodor Tomov 	*num_planes = format->num_planes;
347ec6859b2STodor Tomov 
348ec6859b2STodor Tomov 	for (i = 0; i < *num_planes; i++)
349ec6859b2STodor Tomov 		sizes[i] = format->plane_fmt[i].sizeimage;
350ec6859b2STodor Tomov 
351ec6859b2STodor Tomov 	return 0;
352ec6859b2STodor Tomov }
353ec6859b2STodor Tomov 
354ec6859b2STodor Tomov static int video_buf_init(struct vb2_buffer *vb)
355ec6859b2STodor Tomov {
356ec6859b2STodor Tomov 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
357ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
358ec6859b2STodor Tomov 	struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer,
359ec6859b2STodor Tomov 						   vb);
360ec6859b2STodor Tomov 	const struct v4l2_pix_format_mplane *format =
361ec6859b2STodor Tomov 						&video->active_fmt.fmt.pix_mp;
362ec6859b2STodor Tomov 	struct sg_table *sgt;
363ec6859b2STodor Tomov 	unsigned int i;
364ec6859b2STodor Tomov 
365ec6859b2STodor Tomov 	for (i = 0; i < format->num_planes; i++) {
366ec6859b2STodor Tomov 		sgt = vb2_dma_sg_plane_desc(vb, i);
367ec6859b2STodor Tomov 		if (!sgt)
368ec6859b2STodor Tomov 			return -EFAULT;
369ec6859b2STodor Tomov 
370ec6859b2STodor Tomov 		buffer->addr[i] = sg_dma_address(sgt->sgl);
371ec6859b2STodor Tomov 	}
372ec6859b2STodor Tomov 
373ec6859b2STodor Tomov 	if (format->pixelformat == V4L2_PIX_FMT_NV12 ||
374ec6859b2STodor Tomov 			format->pixelformat == V4L2_PIX_FMT_NV21 ||
375ec6859b2STodor Tomov 			format->pixelformat == V4L2_PIX_FMT_NV16 ||
376ec6859b2STodor Tomov 			format->pixelformat == V4L2_PIX_FMT_NV61)
377ec6859b2STodor Tomov 		buffer->addr[1] = buffer->addr[0] +
378ec6859b2STodor Tomov 				format->plane_fmt[0].bytesperline *
379ec6859b2STodor Tomov 				format->height;
380ec6859b2STodor Tomov 
381ec6859b2STodor Tomov 	return 0;
382ec6859b2STodor Tomov }
383ec6859b2STodor Tomov 
384ec6859b2STodor Tomov static int video_buf_prepare(struct vb2_buffer *vb)
385ec6859b2STodor Tomov {
386ec6859b2STodor Tomov 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
387ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
388ec6859b2STodor Tomov 	const struct v4l2_pix_format_mplane *format =
389ec6859b2STodor Tomov 						&video->active_fmt.fmt.pix_mp;
390ec6859b2STodor Tomov 	unsigned int i;
391ec6859b2STodor Tomov 
392ec6859b2STodor Tomov 	for (i = 0; i < format->num_planes; i++) {
393ec6859b2STodor Tomov 		if (format->plane_fmt[i].sizeimage > vb2_plane_size(vb, i))
394ec6859b2STodor Tomov 			return -EINVAL;
395ec6859b2STodor Tomov 
396ec6859b2STodor Tomov 		vb2_set_plane_payload(vb, i, format->plane_fmt[i].sizeimage);
397ec6859b2STodor Tomov 	}
398ec6859b2STodor Tomov 
399ec6859b2STodor Tomov 	vbuf->field = V4L2_FIELD_NONE;
400ec6859b2STodor Tomov 
401ec6859b2STodor Tomov 	return 0;
402ec6859b2STodor Tomov }
403ec6859b2STodor Tomov 
404ec6859b2STodor Tomov static void video_buf_queue(struct vb2_buffer *vb)
405ec6859b2STodor Tomov {
406ec6859b2STodor Tomov 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
407ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
408ec6859b2STodor Tomov 	struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer,
409ec6859b2STodor Tomov 						   vb);
410ec6859b2STodor Tomov 
411ec6859b2STodor Tomov 	video->ops->queue_buffer(video, buffer);
412ec6859b2STodor Tomov }
413ec6859b2STodor Tomov 
414ec6859b2STodor Tomov static int video_check_format(struct camss_video *video)
415ec6859b2STodor Tomov {
416ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *pix = &video->active_fmt.fmt.pix_mp;
417ec6859b2STodor Tomov 	struct v4l2_format format;
418ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *sd_pix = &format.fmt.pix_mp;
419ec6859b2STodor Tomov 	int ret;
420ec6859b2STodor Tomov 
421ec6859b2STodor Tomov 	sd_pix->pixelformat = pix->pixelformat;
422ec6859b2STodor Tomov 	ret = video_get_subdev_format(video, &format);
423ec6859b2STodor Tomov 	if (ret < 0)
424ec6859b2STodor Tomov 		return ret;
425ec6859b2STodor Tomov 
426ec6859b2STodor Tomov 	if (pix->pixelformat != sd_pix->pixelformat ||
427ec6859b2STodor Tomov 	    pix->height != sd_pix->height ||
428ec6859b2STodor Tomov 	    pix->width != sd_pix->width ||
429ec6859b2STodor Tomov 	    pix->num_planes != sd_pix->num_planes ||
430ec6859b2STodor Tomov 	    pix->field != format.fmt.pix_mp.field)
431ec6859b2STodor Tomov 		return -EPIPE;
432ec6859b2STodor Tomov 
433ec6859b2STodor Tomov 	return 0;
434ec6859b2STodor Tomov }
435ec6859b2STodor Tomov 
436ec6859b2STodor Tomov static int video_start_streaming(struct vb2_queue *q, unsigned int count)
437ec6859b2STodor Tomov {
438ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(q);
439ec6859b2STodor Tomov 	struct video_device *vdev = &video->vdev;
440ec6859b2STodor Tomov 	struct media_entity *entity;
441ec6859b2STodor Tomov 	struct media_pad *pad;
442ec6859b2STodor Tomov 	struct v4l2_subdev *subdev;
443ec6859b2STodor Tomov 	int ret;
444ec6859b2STodor Tomov 
445ec6859b2STodor Tomov 	ret = media_pipeline_start(&vdev->entity, &video->pipe);
446ec6859b2STodor Tomov 	if (ret < 0)
447ec6859b2STodor Tomov 		return ret;
448ec6859b2STodor Tomov 
449ec6859b2STodor Tomov 	ret = video_check_format(video);
450ec6859b2STodor Tomov 	if (ret < 0)
451ec6859b2STodor Tomov 		goto error;
452ec6859b2STodor Tomov 
453ec6859b2STodor Tomov 	entity = &vdev->entity;
454ec6859b2STodor Tomov 	while (1) {
455ec6859b2STodor Tomov 		pad = &entity->pads[0];
456ec6859b2STodor Tomov 		if (!(pad->flags & MEDIA_PAD_FL_SINK))
457ec6859b2STodor Tomov 			break;
458ec6859b2STodor Tomov 
459ec6859b2STodor Tomov 		pad = media_entity_remote_pad(pad);
460ec6859b2STodor Tomov 		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
461ec6859b2STodor Tomov 			break;
462ec6859b2STodor Tomov 
463ec6859b2STodor Tomov 		entity = pad->entity;
464ec6859b2STodor Tomov 		subdev = media_entity_to_v4l2_subdev(entity);
465ec6859b2STodor Tomov 
466ec6859b2STodor Tomov 		ret = v4l2_subdev_call(subdev, video, s_stream, 1);
467ec6859b2STodor Tomov 		if (ret < 0 && ret != -ENOIOCTLCMD)
468ec6859b2STodor Tomov 			goto error;
469ec6859b2STodor Tomov 	}
470ec6859b2STodor Tomov 
471ec6859b2STodor Tomov 	return 0;
472ec6859b2STodor Tomov 
473ec6859b2STodor Tomov error:
474ec6859b2STodor Tomov 	media_pipeline_stop(&vdev->entity);
475ec6859b2STodor Tomov 
476ec6859b2STodor Tomov 	video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED);
477ec6859b2STodor Tomov 
478ec6859b2STodor Tomov 	return ret;
479ec6859b2STodor Tomov }
480ec6859b2STodor Tomov 
481ec6859b2STodor Tomov static void video_stop_streaming(struct vb2_queue *q)
482ec6859b2STodor Tomov {
483ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(q);
484ec6859b2STodor Tomov 	struct video_device *vdev = &video->vdev;
485ec6859b2STodor Tomov 	struct media_entity *entity;
486ec6859b2STodor Tomov 	struct media_pad *pad;
487ec6859b2STodor Tomov 	struct v4l2_subdev *subdev;
488ec6859b2STodor Tomov 
489ec6859b2STodor Tomov 	entity = &vdev->entity;
490ec6859b2STodor Tomov 	while (1) {
491ec6859b2STodor Tomov 		pad = &entity->pads[0];
492ec6859b2STodor Tomov 		if (!(pad->flags & MEDIA_PAD_FL_SINK))
493ec6859b2STodor Tomov 			break;
494ec6859b2STodor Tomov 
495ec6859b2STodor Tomov 		pad = media_entity_remote_pad(pad);
496ec6859b2STodor Tomov 		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
497ec6859b2STodor Tomov 			break;
498ec6859b2STodor Tomov 
499ec6859b2STodor Tomov 		entity = pad->entity;
500ec6859b2STodor Tomov 		subdev = media_entity_to_v4l2_subdev(entity);
501ec6859b2STodor Tomov 
502ec6859b2STodor Tomov 		v4l2_subdev_call(subdev, video, s_stream, 0);
503ec6859b2STodor Tomov 	}
504ec6859b2STodor Tomov 
505ec6859b2STodor Tomov 	media_pipeline_stop(&vdev->entity);
506ec6859b2STodor Tomov 
507ec6859b2STodor Tomov 	video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
508ec6859b2STodor Tomov }
509ec6859b2STodor Tomov 
510ec6859b2STodor Tomov static const struct vb2_ops msm_video_vb2_q_ops = {
511ec6859b2STodor Tomov 	.queue_setup     = video_queue_setup,
512ec6859b2STodor Tomov 	.wait_prepare    = vb2_ops_wait_prepare,
513ec6859b2STodor Tomov 	.wait_finish     = vb2_ops_wait_finish,
514ec6859b2STodor Tomov 	.buf_init        = video_buf_init,
515ec6859b2STodor Tomov 	.buf_prepare     = video_buf_prepare,
516ec6859b2STodor Tomov 	.buf_queue       = video_buf_queue,
517ec6859b2STodor Tomov 	.start_streaming = video_start_streaming,
518ec6859b2STodor Tomov 	.stop_streaming  = video_stop_streaming,
519ec6859b2STodor Tomov };
520ec6859b2STodor Tomov 
521ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
522ec6859b2STodor Tomov  * V4L2 ioctls
523ec6859b2STodor Tomov  */
524ec6859b2STodor Tomov 
525ec6859b2STodor Tomov static int video_querycap(struct file *file, void *fh,
526ec6859b2STodor Tomov 			  struct v4l2_capability *cap)
527ec6859b2STodor Tomov {
528ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
529ec6859b2STodor Tomov 
530c0decac1SMauro Carvalho Chehab 	strscpy(cap->driver, "qcom-camss", sizeof(cap->driver));
531c0decac1SMauro Carvalho Chehab 	strscpy(cap->card, "Qualcomm Camera Subsystem", sizeof(cap->card));
532ec6859b2STodor Tomov 	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
533ec6859b2STodor Tomov 		 dev_name(video->camss->dev));
534ec6859b2STodor Tomov 
535ec6859b2STodor Tomov 	return 0;
536ec6859b2STodor Tomov }
537ec6859b2STodor Tomov 
538a3d412d4SAndrey Konovalov static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
539ec6859b2STodor Tomov {
540a3d412d4SAndrey Konovalov 	struct camss_video *video = video_drvdata(file);
541ec6859b2STodor Tomov 	int i, j, k;
542dfb5d328SAndrey Konovalov 	u32 mcode = f->mbus_code;
543ec6859b2STodor Tomov 
544a3d412d4SAndrey Konovalov 	if (f->type != video->type)
545a3d412d4SAndrey Konovalov 		return -EINVAL;
546a3d412d4SAndrey Konovalov 
547a3d412d4SAndrey Konovalov 	if (f->index >= video->nformats)
548a3d412d4SAndrey Konovalov 		return -EINVAL;
549a3d412d4SAndrey Konovalov 
550dfb5d328SAndrey Konovalov 	/*
551dfb5d328SAndrey Konovalov 	 * Find index "i" of "k"th unique pixelformat in formats array.
552dfb5d328SAndrey Konovalov 	 *
553dfb5d328SAndrey Konovalov 	 * If f->mbus_code passed to video_enum_fmt() is not zero, a device
554dfb5d328SAndrey Konovalov 	 * with V4L2_CAP_IO_MC capability restricts enumeration to only the
555dfb5d328SAndrey Konovalov 	 * pixel formats that can be produced from that media bus code.
556dfb5d328SAndrey Konovalov 	 * This is implemented by skipping video->formats[] entries with
557dfb5d328SAndrey Konovalov 	 * code != f->mbus_code (if f->mbus_code is not zero).
558dfb5d328SAndrey Konovalov 	 * If the f->mbus_code passed to video_enum_fmt() is not supported,
559dfb5d328SAndrey Konovalov 	 * -EINVAL is returned.
560dfb5d328SAndrey Konovalov 	 * If f->mbus_code is zero, all the pixel formats are enumerated.
561dfb5d328SAndrey Konovalov 	 */
562ec6859b2STodor Tomov 	k = -1;
563ec6859b2STodor Tomov 	for (i = 0; i < video->nformats; i++) {
564dfb5d328SAndrey Konovalov 		if (mcode != 0 && video->formats[i].code != mcode)
565dfb5d328SAndrey Konovalov 			continue;
566dfb5d328SAndrey Konovalov 
567ec6859b2STodor Tomov 		for (j = 0; j < i; j++) {
568dfb5d328SAndrey Konovalov 			if (mcode != 0 && video->formats[j].code != mcode)
569dfb5d328SAndrey Konovalov 				continue;
570ec6859b2STodor Tomov 			if (video->formats[i].pixelformat ==
571ec6859b2STodor Tomov 					video->formats[j].pixelformat)
572ec6859b2STodor Tomov 				break;
573ec6859b2STodor Tomov 		}
574ec6859b2STodor Tomov 
575ec6859b2STodor Tomov 		if (j == i)
576ec6859b2STodor Tomov 			k++;
577ec6859b2STodor Tomov 
578a3d412d4SAndrey Konovalov 		if (k == f->index)
579a3d412d4SAndrey Konovalov 			break;
580ec6859b2STodor Tomov 	}
581ec6859b2STodor Tomov 
582b00481bdSDan Carpenter 	if (k == -1 || k < f->index)
583dfb5d328SAndrey Konovalov 		/*
584dfb5d328SAndrey Konovalov 		 * All the unique pixel formats matching the arguments
585dfb5d328SAndrey Konovalov 		 * have been enumerated (k >= 0 and f->index > 0), or
586dfb5d328SAndrey Konovalov 		 * no pixel formats match the non-zero f->mbus_code (k == -1).
587dfb5d328SAndrey Konovalov 		 */
588ec6859b2STodor Tomov 		return -EINVAL;
589ec6859b2STodor Tomov 
590ec6859b2STodor Tomov 	f->pixelformat = video->formats[i].pixelformat;
591ec6859b2STodor Tomov 
592ec6859b2STodor Tomov 	return 0;
593ec6859b2STodor Tomov }
594ec6859b2STodor Tomov 
59535493d65SAndrey Konovalov static int video_enum_framesizes(struct file *file, void *fh,
59635493d65SAndrey Konovalov 				 struct v4l2_frmsizeenum *fsize)
59735493d65SAndrey Konovalov {
59835493d65SAndrey Konovalov 	struct camss_video *video = video_drvdata(file);
59935493d65SAndrey Konovalov 	int i;
60035493d65SAndrey Konovalov 
60135493d65SAndrey Konovalov 	if (fsize->index)
60235493d65SAndrey Konovalov 		return -EINVAL;
60335493d65SAndrey Konovalov 
60435493d65SAndrey Konovalov 	/* Only accept pixel format present in the formats[] table */
60535493d65SAndrey Konovalov 	for (i = 0; i < video->nformats; i++) {
60635493d65SAndrey Konovalov 		if (video->formats[i].pixelformat == fsize->pixel_format)
60735493d65SAndrey Konovalov 			break;
60835493d65SAndrey Konovalov 	}
60935493d65SAndrey Konovalov 
61035493d65SAndrey Konovalov 	if (i == video->nformats)
61135493d65SAndrey Konovalov 		return -EINVAL;
61235493d65SAndrey Konovalov 
61335493d65SAndrey Konovalov 	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
61435493d65SAndrey Konovalov 	fsize->stepwise.min_width = CAMSS_FRAME_MIN_WIDTH;
61535493d65SAndrey Konovalov 	fsize->stepwise.max_width = CAMSS_FRAME_MAX_WIDTH;
61635493d65SAndrey Konovalov 	fsize->stepwise.min_height = CAMSS_FRAME_MIN_HEIGHT;
61735493d65SAndrey Konovalov 	fsize->stepwise.max_height = (video->line_based) ?
61835493d65SAndrey Konovalov 		CAMSS_FRAME_MAX_HEIGHT_PIX : CAMSS_FRAME_MAX_HEIGHT_RDI;
61935493d65SAndrey Konovalov 	fsize->stepwise.step_width = 1;
62035493d65SAndrey Konovalov 	fsize->stepwise.step_height = 1;
62135493d65SAndrey Konovalov 
62235493d65SAndrey Konovalov 	return 0;
62335493d65SAndrey Konovalov }
62435493d65SAndrey Konovalov 
625ec6859b2STodor Tomov static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
626ec6859b2STodor Tomov {
627ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
628ec6859b2STodor Tomov 
629ec6859b2STodor Tomov 	*f = video->active_fmt;
630ec6859b2STodor Tomov 
631ec6859b2STodor Tomov 	return 0;
632ec6859b2STodor Tomov }
633ec6859b2STodor Tomov 
634ec6859b2STodor Tomov static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f)
635ec6859b2STodor Tomov {
636ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *pix_mp;
637ec6859b2STodor Tomov 	const struct camss_format_info *fi;
638ec6859b2STodor Tomov 	struct v4l2_plane_pix_format *p;
639ec6859b2STodor Tomov 	u32 bytesperline[3] = { 0 };
640ec6859b2STodor Tomov 	u32 sizeimage[3] = { 0 };
641ec6859b2STodor Tomov 	u32 width, height;
642ec6859b2STodor Tomov 	u32 bpl, lines;
643ec6859b2STodor Tomov 	int i, j;
644ec6859b2STodor Tomov 
645ec6859b2STodor Tomov 	pix_mp = &f->fmt.pix_mp;
646ec6859b2STodor Tomov 
647ec6859b2STodor Tomov 	if (video->line_based)
648ec6859b2STodor Tomov 		for (i = 0; i < pix_mp->num_planes && i < 3; i++) {
649ec6859b2STodor Tomov 			p = &pix_mp->plane_fmt[i];
650ec6859b2STodor Tomov 			bytesperline[i] = clamp_t(u32, p->bytesperline,
651ec6859b2STodor Tomov 						  1, 65528);
652ec6859b2STodor Tomov 			sizeimage[i] = clamp_t(u32, p->sizeimage,
653ec6859b2STodor Tomov 					       bytesperline[i],
654daf2298bSAndrey Konovalov 					       bytesperline[i] * CAMSS_FRAME_MAX_HEIGHT_PIX);
655ec6859b2STodor Tomov 		}
656ec6859b2STodor Tomov 
657ec6859b2STodor Tomov 	for (j = 0; j < video->nformats; j++)
658ec6859b2STodor Tomov 		if (pix_mp->pixelformat == video->formats[j].pixelformat)
659ec6859b2STodor Tomov 			break;
660ec6859b2STodor Tomov 
661ec6859b2STodor Tomov 	if (j == video->nformats)
662ec6859b2STodor Tomov 		j = 0; /* default format */
663ec6859b2STodor Tomov 
664ec6859b2STodor Tomov 	fi = &video->formats[j];
665ec6859b2STodor Tomov 	width = pix_mp->width;
666ec6859b2STodor Tomov 	height = pix_mp->height;
667ec6859b2STodor Tomov 
668ec6859b2STodor Tomov 	memset(pix_mp, 0, sizeof(*pix_mp));
669ec6859b2STodor Tomov 
670ec6859b2STodor Tomov 	pix_mp->pixelformat = fi->pixelformat;
671daf2298bSAndrey Konovalov 	pix_mp->width = clamp_t(u32, width, 1, CAMSS_FRAME_MAX_WIDTH);
672daf2298bSAndrey Konovalov 	pix_mp->height = clamp_t(u32, height, 1, CAMSS_FRAME_MAX_HEIGHT_RDI);
673ec6859b2STodor Tomov 	pix_mp->num_planes = fi->planes;
674ec6859b2STodor Tomov 	for (i = 0; i < pix_mp->num_planes; i++) {
675ec6859b2STodor Tomov 		bpl = pix_mp->width / fi->hsub[i].numerator *
676ec6859b2STodor Tomov 			fi->hsub[i].denominator * fi->bpp[i] / 8;
677ec6859b2STodor Tomov 		bpl = ALIGN(bpl, video->bpl_alignment);
678ec6859b2STodor Tomov 		pix_mp->plane_fmt[i].bytesperline = bpl;
679ec6859b2STodor Tomov 		pix_mp->plane_fmt[i].sizeimage = pix_mp->height /
680ec6859b2STodor Tomov 			fi->vsub[i].numerator * fi->vsub[i].denominator * bpl;
681ec6859b2STodor Tomov 	}
682ec6859b2STodor Tomov 
683ec6859b2STodor Tomov 	pix_mp->field = V4L2_FIELD_NONE;
684ec6859b2STodor Tomov 	pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
685ec6859b2STodor Tomov 	pix_mp->flags = 0;
686ec6859b2STodor Tomov 	pix_mp->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace);
687ec6859b2STodor Tomov 	pix_mp->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
688ec6859b2STodor Tomov 					pix_mp->colorspace, pix_mp->ycbcr_enc);
689ec6859b2STodor Tomov 	pix_mp->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace);
690ec6859b2STodor Tomov 
691ec6859b2STodor Tomov 	if (video->line_based)
692ec6859b2STodor Tomov 		for (i = 0; i < pix_mp->num_planes; i++) {
693ec6859b2STodor Tomov 			p = &pix_mp->plane_fmt[i];
694ec6859b2STodor Tomov 			p->bytesperline = clamp_t(u32, p->bytesperline,
695ec6859b2STodor Tomov 						  1, 65528);
696ec6859b2STodor Tomov 			p->sizeimage = clamp_t(u32, p->sizeimage,
697ec6859b2STodor Tomov 					       p->bytesperline,
698daf2298bSAndrey Konovalov 					       p->bytesperline * CAMSS_FRAME_MAX_HEIGHT_PIX);
699ec6859b2STodor Tomov 			lines = p->sizeimage / p->bytesperline;
700ec6859b2STodor Tomov 
701ec6859b2STodor Tomov 			if (p->bytesperline < bytesperline[i])
702ec6859b2STodor Tomov 				p->bytesperline = ALIGN(bytesperline[i], 8);
703ec6859b2STodor Tomov 
704ec6859b2STodor Tomov 			if (p->sizeimage < p->bytesperline * lines)
705ec6859b2STodor Tomov 				p->sizeimage = p->bytesperline * lines;
706ec6859b2STodor Tomov 
707ec6859b2STodor Tomov 			if (p->sizeimage < sizeimage[i])
708ec6859b2STodor Tomov 				p->sizeimage = sizeimage[i];
709ec6859b2STodor Tomov 		}
710ec6859b2STodor Tomov 
711ec6859b2STodor Tomov 	return 0;
712ec6859b2STodor Tomov }
713ec6859b2STodor Tomov 
714ec6859b2STodor Tomov static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
715ec6859b2STodor Tomov {
716ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
717ec6859b2STodor Tomov 
718ec6859b2STodor Tomov 	return __video_try_fmt(video, f);
719ec6859b2STodor Tomov }
720ec6859b2STodor Tomov 
721ec6859b2STodor Tomov static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
722ec6859b2STodor Tomov {
723ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
724ec6859b2STodor Tomov 	int ret;
725ec6859b2STodor Tomov 
726ec6859b2STodor Tomov 	if (vb2_is_busy(&video->vb2_q))
727ec6859b2STodor Tomov 		return -EBUSY;
728ec6859b2STodor Tomov 
729ec6859b2STodor Tomov 	ret = __video_try_fmt(video, f);
730ec6859b2STodor Tomov 	if (ret < 0)
731ec6859b2STodor Tomov 		return ret;
732ec6859b2STodor Tomov 
733ec6859b2STodor Tomov 	video->active_fmt = *f;
734ec6859b2STodor Tomov 
735ec6859b2STodor Tomov 	return 0;
736ec6859b2STodor Tomov }
737ec6859b2STodor Tomov 
738ec6859b2STodor Tomov static int video_enum_input(struct file *file, void *fh,
739ec6859b2STodor Tomov 			    struct v4l2_input *input)
740ec6859b2STodor Tomov {
741ec6859b2STodor Tomov 	if (input->index > 0)
742ec6859b2STodor Tomov 		return -EINVAL;
743ec6859b2STodor Tomov 
744c0decac1SMauro Carvalho Chehab 	strscpy(input->name, "camera", sizeof(input->name));
745ec6859b2STodor Tomov 	input->type = V4L2_INPUT_TYPE_CAMERA;
746ec6859b2STodor Tomov 
747ec6859b2STodor Tomov 	return 0;
748ec6859b2STodor Tomov }
749ec6859b2STodor Tomov 
750ec6859b2STodor Tomov static int video_g_input(struct file *file, void *fh, unsigned int *input)
751ec6859b2STodor Tomov {
752ec6859b2STodor Tomov 	*input = 0;
753ec6859b2STodor Tomov 
754ec6859b2STodor Tomov 	return 0;
755ec6859b2STodor Tomov }
756ec6859b2STodor Tomov 
757ec6859b2STodor Tomov static int video_s_input(struct file *file, void *fh, unsigned int input)
758ec6859b2STodor Tomov {
759ec6859b2STodor Tomov 	return input == 0 ? 0 : -EINVAL;
760ec6859b2STodor Tomov }
761ec6859b2STodor Tomov 
762ec6859b2STodor Tomov static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = {
763ec6859b2STodor Tomov 	.vidioc_querycap		= video_querycap,
7647e98b7b5SBoris Brezillon 	.vidioc_enum_fmt_vid_cap	= video_enum_fmt,
76535493d65SAndrey Konovalov 	.vidioc_enum_framesizes		= video_enum_framesizes,
766ec6859b2STodor Tomov 	.vidioc_g_fmt_vid_cap_mplane	= video_g_fmt,
767ec6859b2STodor Tomov 	.vidioc_s_fmt_vid_cap_mplane	= video_s_fmt,
768ec6859b2STodor Tomov 	.vidioc_try_fmt_vid_cap_mplane	= video_try_fmt,
769ec6859b2STodor Tomov 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
770ec6859b2STodor Tomov 	.vidioc_querybuf		= vb2_ioctl_querybuf,
771ec6859b2STodor Tomov 	.vidioc_qbuf			= vb2_ioctl_qbuf,
772ec6859b2STodor Tomov 	.vidioc_expbuf			= vb2_ioctl_expbuf,
773ec6859b2STodor Tomov 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
774ec6859b2STodor Tomov 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
775ec6859b2STodor Tomov 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
776ec6859b2STodor Tomov 	.vidioc_streamon		= vb2_ioctl_streamon,
777ec6859b2STodor Tomov 	.vidioc_streamoff		= vb2_ioctl_streamoff,
778ec6859b2STodor Tomov 	.vidioc_enum_input		= video_enum_input,
779ec6859b2STodor Tomov 	.vidioc_g_input			= video_g_input,
780ec6859b2STodor Tomov 	.vidioc_s_input			= video_s_input,
781ec6859b2STodor Tomov };
782ec6859b2STodor Tomov 
783ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
784ec6859b2STodor Tomov  * V4L2 file operations
785ec6859b2STodor Tomov  */
786ec6859b2STodor Tomov 
787ec6859b2STodor Tomov static int video_open(struct file *file)
788ec6859b2STodor Tomov {
789ec6859b2STodor Tomov 	struct video_device *vdev = video_devdata(file);
790ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
791ec6859b2STodor Tomov 	struct v4l2_fh *vfh;
792ec6859b2STodor Tomov 	int ret;
793ec6859b2STodor Tomov 
794ec6859b2STodor Tomov 	mutex_lock(&video->lock);
795ec6859b2STodor Tomov 
796ec6859b2STodor Tomov 	vfh = kzalloc(sizeof(*vfh), GFP_KERNEL);
797ec6859b2STodor Tomov 	if (vfh == NULL) {
798ec6859b2STodor Tomov 		ret = -ENOMEM;
799ec6859b2STodor Tomov 		goto error_alloc;
800ec6859b2STodor Tomov 	}
801ec6859b2STodor Tomov 
802ec6859b2STodor Tomov 	v4l2_fh_init(vfh, vdev);
803ec6859b2STodor Tomov 	v4l2_fh_add(vfh);
804ec6859b2STodor Tomov 
805ec6859b2STodor Tomov 	file->private_data = vfh;
806ec6859b2STodor Tomov 
8078fd390b8SEzequiel Garcia 	ret = v4l2_pipeline_pm_get(&vdev->entity);
808ec6859b2STodor Tomov 	if (ret < 0) {
809ec6859b2STodor Tomov 		dev_err(video->camss->dev, "Failed to power up pipeline: %d\n",
810ec6859b2STodor Tomov 			ret);
811ec6859b2STodor Tomov 		goto error_pm_use;
812ec6859b2STodor Tomov 	}
813ec6859b2STodor Tomov 
814ec6859b2STodor Tomov 	mutex_unlock(&video->lock);
815ec6859b2STodor Tomov 
816ec6859b2STodor Tomov 	return 0;
817ec6859b2STodor Tomov 
818ec6859b2STodor Tomov error_pm_use:
819ec6859b2STodor Tomov 	v4l2_fh_release(file);
820ec6859b2STodor Tomov 
821ec6859b2STodor Tomov error_alloc:
822ec6859b2STodor Tomov 	mutex_unlock(&video->lock);
823ec6859b2STodor Tomov 
824ec6859b2STodor Tomov 	return ret;
825ec6859b2STodor Tomov }
826ec6859b2STodor Tomov 
827ec6859b2STodor Tomov static int video_release(struct file *file)
828ec6859b2STodor Tomov {
829ec6859b2STodor Tomov 	struct video_device *vdev = video_devdata(file);
830ec6859b2STodor Tomov 
831ec6859b2STodor Tomov 	vb2_fop_release(file);
832ec6859b2STodor Tomov 
8338fd390b8SEzequiel Garcia 	v4l2_pipeline_pm_put(&vdev->entity);
834ec6859b2STodor Tomov 
835ec6859b2STodor Tomov 	file->private_data = NULL;
836ec6859b2STodor Tomov 
837ec6859b2STodor Tomov 	return 0;
838ec6859b2STodor Tomov }
839ec6859b2STodor Tomov 
840ec6859b2STodor Tomov static const struct v4l2_file_operations msm_vid_fops = {
841ec6859b2STodor Tomov 	.owner          = THIS_MODULE,
842ec6859b2STodor Tomov 	.unlocked_ioctl = video_ioctl2,
843ec6859b2STodor Tomov 	.open           = video_open,
844ec6859b2STodor Tomov 	.release        = video_release,
845ec6859b2STodor Tomov 	.poll           = vb2_fop_poll,
846ec6859b2STodor Tomov 	.mmap		= vb2_fop_mmap,
847ec6859b2STodor Tomov 	.read		= vb2_fop_read,
848ec6859b2STodor Tomov };
849ec6859b2STodor Tomov 
850ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
851ec6859b2STodor Tomov  * CAMSS video core
852ec6859b2STodor Tomov  */
853ec6859b2STodor Tomov 
854ec6859b2STodor Tomov static void msm_video_release(struct video_device *vdev)
855ec6859b2STodor Tomov {
856ec6859b2STodor Tomov 	struct camss_video *video = video_get_drvdata(vdev);
857ec6859b2STodor Tomov 
858ec6859b2STodor Tomov 	media_entity_cleanup(&vdev->entity);
859ec6859b2STodor Tomov 
860ec6859b2STodor Tomov 	mutex_destroy(&video->q_lock);
861ec6859b2STodor Tomov 	mutex_destroy(&video->lock);
862ec6859b2STodor Tomov 
863ec6859b2STodor Tomov 	if (atomic_dec_and_test(&video->camss->ref_count))
864ec6859b2STodor Tomov 		camss_delete(video->camss);
865ec6859b2STodor Tomov }
866ec6859b2STodor Tomov 
867ec6859b2STodor Tomov /*
868ec6859b2STodor Tomov  * msm_video_init_format - Helper function to initialize format
869ec6859b2STodor Tomov  * @video: struct camss_video
870ec6859b2STodor Tomov  *
871ec6859b2STodor Tomov  * Initialize pad format with default value.
872ec6859b2STodor Tomov  *
873ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
874ec6859b2STodor Tomov  */
875ec6859b2STodor Tomov static int msm_video_init_format(struct camss_video *video)
876ec6859b2STodor Tomov {
877ec6859b2STodor Tomov 	int ret;
878ec6859b2STodor Tomov 	struct v4l2_format format = {
879ec6859b2STodor Tomov 		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
880ec6859b2STodor Tomov 		.fmt.pix_mp = {
881ec6859b2STodor Tomov 			.width = 1920,
882ec6859b2STodor Tomov 			.height = 1080,
883ec6859b2STodor Tomov 			.pixelformat = video->formats[0].pixelformat,
884ec6859b2STodor Tomov 		},
885ec6859b2STodor Tomov 	};
886ec6859b2STodor Tomov 
887ec6859b2STodor Tomov 	ret = __video_try_fmt(video, &format);
888ec6859b2STodor Tomov 	if (ret < 0)
889ec6859b2STodor Tomov 		return ret;
890ec6859b2STodor Tomov 
891ec6859b2STodor Tomov 	video->active_fmt = format;
892ec6859b2STodor Tomov 
893ec6859b2STodor Tomov 	return 0;
894ec6859b2STodor Tomov }
895ec6859b2STodor Tomov 
896ec6859b2STodor Tomov /*
897ec6859b2STodor Tomov  * msm_video_register - Register a video device node
898ec6859b2STodor Tomov  * @video: struct camss_video
899ec6859b2STodor Tomov  * @v4l2_dev: V4L2 device
900ec6859b2STodor Tomov  * @name: name to be used for the video device node
901ec6859b2STodor Tomov  *
902ec6859b2STodor Tomov  * Initialize and register a video device node to a V4L2 device. Also
903ec6859b2STodor Tomov  * initialize the vb2 queue.
904ec6859b2STodor Tomov  *
905ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
906ec6859b2STodor Tomov  */
907ec6859b2STodor Tomov 
908ec6859b2STodor Tomov int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
909ec6859b2STodor Tomov 		       const char *name, int is_pix)
910ec6859b2STodor Tomov {
911ec6859b2STodor Tomov 	struct media_pad *pad = &video->pad;
912ec6859b2STodor Tomov 	struct video_device *vdev;
913ec6859b2STodor Tomov 	struct vb2_queue *q;
914ec6859b2STodor Tomov 	int ret;
915ec6859b2STodor Tomov 
916ec6859b2STodor Tomov 	vdev = &video->vdev;
917ec6859b2STodor Tomov 
918ec6859b2STodor Tomov 	mutex_init(&video->q_lock);
919ec6859b2STodor Tomov 
920ec6859b2STodor Tomov 	q = &video->vb2_q;
921ec6859b2STodor Tomov 	q->drv_priv = video;
922ec6859b2STodor Tomov 	q->mem_ops = &vb2_dma_sg_memops;
923ec6859b2STodor Tomov 	q->ops = &msm_video_vb2_q_ops;
924ec6859b2STodor Tomov 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
925ec6859b2STodor Tomov 	q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ;
926ec6859b2STodor Tomov 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
927ec6859b2STodor Tomov 	q->buf_struct_size = sizeof(struct camss_buffer);
928ec6859b2STodor Tomov 	q->dev = video->camss->dev;
929ec6859b2STodor Tomov 	q->lock = &video->q_lock;
930ec6859b2STodor Tomov 	ret = vb2_queue_init(q);
931ec6859b2STodor Tomov 	if (ret < 0) {
932ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret);
933ec6859b2STodor Tomov 		goto error_vb2_init;
934ec6859b2STodor Tomov 	}
935ec6859b2STodor Tomov 
936ec6859b2STodor Tomov 	pad->flags = MEDIA_PAD_FL_SINK;
937ec6859b2STodor Tomov 	ret = media_entity_pads_init(&vdev->entity, 1, pad);
938ec6859b2STodor Tomov 	if (ret < 0) {
939ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n",
940ec6859b2STodor Tomov 			ret);
941492abcd7SHans Verkuil 		goto error_vb2_init;
942ec6859b2STodor Tomov 	}
943ec6859b2STodor Tomov 
944ec6859b2STodor Tomov 	mutex_init(&video->lock);
945ec6859b2STodor Tomov 
946cba3819dSTodor Tomov 	if (video->camss->version == CAMSS_8x16) {
947ec6859b2STodor Tomov 		if (is_pix) {
948cba3819dSTodor Tomov 			video->formats = formats_pix_8x16;
949cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_pix_8x16);
950cba3819dSTodor Tomov 		} else {
951cba3819dSTodor Tomov 			video->formats = formats_rdi_8x16;
952cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_rdi_8x16);
953cba3819dSTodor Tomov 		}
9549e5d1581SAngeloGioacchino Del Regno 	} else if (video->camss->version == CAMSS_8x96 ||
9559e5d1581SAngeloGioacchino Del Regno 		   video->camss->version == CAMSS_660) {
956cba3819dSTodor Tomov 		if (is_pix) {
957cba3819dSTodor Tomov 			video->formats = formats_pix_8x96;
958cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_pix_8x96);
959cba3819dSTodor Tomov 		} else {
960cba3819dSTodor Tomov 			video->formats = formats_rdi_8x96;
961cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_rdi_8x96);
962cba3819dSTodor Tomov 		}
963cba3819dSTodor Tomov 	} else {
964*9c67ed2aSDan Carpenter 		ret = -EINVAL;
965cba3819dSTodor Tomov 		goto error_video_register;
966ec6859b2STodor Tomov 	}
967ec6859b2STodor Tomov 
968ec6859b2STodor Tomov 	ret = msm_video_init_format(video);
969ec6859b2STodor Tomov 	if (ret < 0) {
970ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret);
971ec6859b2STodor Tomov 		goto error_video_register;
972ec6859b2STodor Tomov 	}
973ec6859b2STodor Tomov 
974ec6859b2STodor Tomov 	vdev->fops = &msm_vid_fops;
975dfb5d328SAndrey Konovalov 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING
976dfb5d328SAndrey Konovalov 			  | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC;
977ec6859b2STodor Tomov 	vdev->ioctl_ops = &msm_vid_ioctl_ops;
978ec6859b2STodor Tomov 	vdev->release = msm_video_release;
979ec6859b2STodor Tomov 	vdev->v4l2_dev = v4l2_dev;
980ec6859b2STodor Tomov 	vdev->vfl_dir = VFL_DIR_RX;
981ec6859b2STodor Tomov 	vdev->queue = &video->vb2_q;
982ec6859b2STodor Tomov 	vdev->lock = &video->lock;
983c0decac1SMauro Carvalho Chehab 	strscpy(vdev->name, name, sizeof(vdev->name));
984ec6859b2STodor Tomov 
98570cad449SHans Verkuil 	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
986ec6859b2STodor Tomov 	if (ret < 0) {
987ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to register video device: %d\n",
988ec6859b2STodor Tomov 			ret);
989ec6859b2STodor Tomov 		goto error_video_register;
990ec6859b2STodor Tomov 	}
991ec6859b2STodor Tomov 
992ec6859b2STodor Tomov 	video_set_drvdata(vdev, video);
993ec6859b2STodor Tomov 	atomic_inc(&video->camss->ref_count);
994ec6859b2STodor Tomov 
995ec6859b2STodor Tomov 	return 0;
996ec6859b2STodor Tomov 
997ec6859b2STodor Tomov error_video_register:
998ec6859b2STodor Tomov 	media_entity_cleanup(&vdev->entity);
999ec6859b2STodor Tomov 	mutex_destroy(&video->lock);
1000ec6859b2STodor Tomov error_vb2_init:
1001ec6859b2STodor Tomov 	mutex_destroy(&video->q_lock);
1002ec6859b2STodor Tomov 
1003ec6859b2STodor Tomov 	return ret;
1004ec6859b2STodor Tomov }
1005ec6859b2STodor Tomov 
1006ec6859b2STodor Tomov void msm_video_unregister(struct camss_video *video)
1007ec6859b2STodor Tomov {
1008ec6859b2STodor Tomov 	atomic_inc(&video->camss->ref_count);
1009492abcd7SHans Verkuil 	vb2_video_unregister_device(&video->vdev);
1010ec6859b2STodor Tomov 	atomic_dec(&video->camss->ref_count);
1011ec6859b2STodor Tomov }
1012