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 
538c90f1178SAndrey Konovalov /*
539c90f1178SAndrey Konovalov  *  Returns the index in the video->formats[] array of the element which
540c90f1178SAndrey Konovalov  *  has the "ndx"th unique value of pixelformat field.
541c90f1178SAndrey Konovalov  *  If not found (no more unique pixelformat's) returns -EINVAL.
542c90f1178SAndrey Konovalov  */
543c90f1178SAndrey Konovalov static int video_get_unique_pixelformat_by_index(struct camss_video *video,
544c90f1178SAndrey Konovalov 						 int ndx)
545ec6859b2STodor Tomov {
546ec6859b2STodor Tomov 	int i, j, k;
547ec6859b2STodor Tomov 
548ec6859b2STodor Tomov 	/* find index "i" of "k"th unique pixelformat in formats array */
549ec6859b2STodor Tomov 	k = -1;
550ec6859b2STodor Tomov 	for (i = 0; i < video->nformats; i++) {
551ec6859b2STodor Tomov 		for (j = 0; j < i; j++) {
552ec6859b2STodor Tomov 			if (video->formats[i].pixelformat ==
553ec6859b2STodor Tomov 					video->formats[j].pixelformat)
554ec6859b2STodor Tomov 				break;
555ec6859b2STodor Tomov 		}
556ec6859b2STodor Tomov 
557ec6859b2STodor Tomov 		if (j == i)
558ec6859b2STodor Tomov 			k++;
559ec6859b2STodor Tomov 
560c90f1178SAndrey Konovalov 		if (k == ndx)
561c90f1178SAndrey Konovalov 			return i;
562ec6859b2STodor Tomov 	}
563ec6859b2STodor Tomov 
564c90f1178SAndrey Konovalov 	return -EINVAL;
565c90f1178SAndrey Konovalov }
566c90f1178SAndrey Konovalov 
567c90f1178SAndrey Konovalov /*
568c90f1178SAndrey Konovalov  *  Returns the index in the video->formats[] array of the element which
569c90f1178SAndrey Konovalov  *  has code equal to mcode.
570c90f1178SAndrey Konovalov  *  If not found returns -EINVAL.
571c90f1178SAndrey Konovalov  */
572c90f1178SAndrey Konovalov static int video_get_pixelformat_by_mbus_code(struct camss_video *video,
573c90f1178SAndrey Konovalov 					      u32 mcode)
574c90f1178SAndrey Konovalov {
575c90f1178SAndrey Konovalov 	int i;
576c90f1178SAndrey Konovalov 
577c90f1178SAndrey Konovalov 	for (i = 0; i < video->nformats; i++) {
578c90f1178SAndrey Konovalov 		if (video->formats[i].code == mcode)
579c90f1178SAndrey Konovalov 			return i;
580c90f1178SAndrey Konovalov 	}
581c90f1178SAndrey Konovalov 
582c90f1178SAndrey Konovalov 	return -EINVAL;
583c90f1178SAndrey Konovalov }
584c90f1178SAndrey Konovalov 
585c90f1178SAndrey Konovalov static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
586c90f1178SAndrey Konovalov {
587c90f1178SAndrey Konovalov 	struct camss_video *video = video_drvdata(file);
588c90f1178SAndrey Konovalov 	int i;
589c90f1178SAndrey Konovalov 
590c90f1178SAndrey Konovalov 	if (f->type != video->type)
591c90f1178SAndrey Konovalov 		return -EINVAL;
592c90f1178SAndrey Konovalov 
593c90f1178SAndrey Konovalov 	if (f->index >= video->nformats)
594c90f1178SAndrey Konovalov 		return -EINVAL;
595c90f1178SAndrey Konovalov 
596c90f1178SAndrey Konovalov 	if (f->mbus_code) {
597c90f1178SAndrey Konovalov 		/* Each entry in formats[] table has unique mbus_code */
598c90f1178SAndrey Konovalov 		if (f->index > 0)
599c90f1178SAndrey Konovalov 			return -EINVAL;
600c90f1178SAndrey Konovalov 
601c90f1178SAndrey Konovalov 		i = video_get_pixelformat_by_mbus_code(video, f->mbus_code);
602c90f1178SAndrey Konovalov 	} else {
603c90f1178SAndrey Konovalov 		i = video_get_unique_pixelformat_by_index(video, f->index);
604c90f1178SAndrey Konovalov 	}
605c90f1178SAndrey Konovalov 
606c90f1178SAndrey Konovalov 	if (i < 0)
607ec6859b2STodor Tomov 		return -EINVAL;
608ec6859b2STodor Tomov 
609ec6859b2STodor Tomov 	f->pixelformat = video->formats[i].pixelformat;
610ec6859b2STodor Tomov 
611ec6859b2STodor Tomov 	return 0;
612ec6859b2STodor Tomov }
613ec6859b2STodor Tomov 
61435493d65SAndrey Konovalov static int video_enum_framesizes(struct file *file, void *fh,
61535493d65SAndrey Konovalov 				 struct v4l2_frmsizeenum *fsize)
61635493d65SAndrey Konovalov {
61735493d65SAndrey Konovalov 	struct camss_video *video = video_drvdata(file);
61835493d65SAndrey Konovalov 	int i;
61935493d65SAndrey Konovalov 
62035493d65SAndrey Konovalov 	if (fsize->index)
62135493d65SAndrey Konovalov 		return -EINVAL;
62235493d65SAndrey Konovalov 
62335493d65SAndrey Konovalov 	/* Only accept pixel format present in the formats[] table */
62435493d65SAndrey Konovalov 	for (i = 0; i < video->nformats; i++) {
62535493d65SAndrey Konovalov 		if (video->formats[i].pixelformat == fsize->pixel_format)
62635493d65SAndrey Konovalov 			break;
62735493d65SAndrey Konovalov 	}
62835493d65SAndrey Konovalov 
62935493d65SAndrey Konovalov 	if (i == video->nformats)
63035493d65SAndrey Konovalov 		return -EINVAL;
63135493d65SAndrey Konovalov 
63235493d65SAndrey Konovalov 	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
63335493d65SAndrey Konovalov 	fsize->stepwise.min_width = CAMSS_FRAME_MIN_WIDTH;
63435493d65SAndrey Konovalov 	fsize->stepwise.max_width = CAMSS_FRAME_MAX_WIDTH;
63535493d65SAndrey Konovalov 	fsize->stepwise.min_height = CAMSS_FRAME_MIN_HEIGHT;
63635493d65SAndrey Konovalov 	fsize->stepwise.max_height = (video->line_based) ?
63735493d65SAndrey Konovalov 		CAMSS_FRAME_MAX_HEIGHT_PIX : CAMSS_FRAME_MAX_HEIGHT_RDI;
63835493d65SAndrey Konovalov 	fsize->stepwise.step_width = 1;
63935493d65SAndrey Konovalov 	fsize->stepwise.step_height = 1;
64035493d65SAndrey Konovalov 
64135493d65SAndrey Konovalov 	return 0;
64235493d65SAndrey Konovalov }
64335493d65SAndrey Konovalov 
644ec6859b2STodor Tomov static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
645ec6859b2STodor Tomov {
646ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
647ec6859b2STodor Tomov 
648ec6859b2STodor Tomov 	*f = video->active_fmt;
649ec6859b2STodor Tomov 
650ec6859b2STodor Tomov 	return 0;
651ec6859b2STodor Tomov }
652ec6859b2STodor Tomov 
653ec6859b2STodor Tomov static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f)
654ec6859b2STodor Tomov {
655ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *pix_mp;
656ec6859b2STodor Tomov 	const struct camss_format_info *fi;
657ec6859b2STodor Tomov 	struct v4l2_plane_pix_format *p;
658ec6859b2STodor Tomov 	u32 bytesperline[3] = { 0 };
659ec6859b2STodor Tomov 	u32 sizeimage[3] = { 0 };
660ec6859b2STodor Tomov 	u32 width, height;
661ec6859b2STodor Tomov 	u32 bpl, lines;
662ec6859b2STodor Tomov 	int i, j;
663ec6859b2STodor Tomov 
664ec6859b2STodor Tomov 	pix_mp = &f->fmt.pix_mp;
665ec6859b2STodor Tomov 
666ec6859b2STodor Tomov 	if (video->line_based)
667ec6859b2STodor Tomov 		for (i = 0; i < pix_mp->num_planes && i < 3; i++) {
668ec6859b2STodor Tomov 			p = &pix_mp->plane_fmt[i];
669ec6859b2STodor Tomov 			bytesperline[i] = clamp_t(u32, p->bytesperline,
670ec6859b2STodor Tomov 						  1, 65528);
671ec6859b2STodor Tomov 			sizeimage[i] = clamp_t(u32, p->sizeimage,
672ec6859b2STodor Tomov 					       bytesperline[i],
673ec6859b2STodor Tomov 					       bytesperline[i] * 4096);
674ec6859b2STodor Tomov 		}
675ec6859b2STodor Tomov 
676ec6859b2STodor Tomov 	for (j = 0; j < video->nformats; j++)
677ec6859b2STodor Tomov 		if (pix_mp->pixelformat == video->formats[j].pixelformat)
678ec6859b2STodor Tomov 			break;
679ec6859b2STodor Tomov 
680ec6859b2STodor Tomov 	if (j == video->nformats)
681ec6859b2STodor Tomov 		j = 0; /* default format */
682ec6859b2STodor Tomov 
683ec6859b2STodor Tomov 	fi = &video->formats[j];
684ec6859b2STodor Tomov 	width = pix_mp->width;
685ec6859b2STodor Tomov 	height = pix_mp->height;
686ec6859b2STodor Tomov 
687ec6859b2STodor Tomov 	memset(pix_mp, 0, sizeof(*pix_mp));
688ec6859b2STodor Tomov 
689ec6859b2STodor Tomov 	pix_mp->pixelformat = fi->pixelformat;
690ec6859b2STodor Tomov 	pix_mp->width = clamp_t(u32, width, 1, 8191);
691ec6859b2STodor Tomov 	pix_mp->height = clamp_t(u32, height, 1, 8191);
692ec6859b2STodor Tomov 	pix_mp->num_planes = fi->planes;
693ec6859b2STodor Tomov 	for (i = 0; i < pix_mp->num_planes; i++) {
694ec6859b2STodor Tomov 		bpl = pix_mp->width / fi->hsub[i].numerator *
695ec6859b2STodor Tomov 			fi->hsub[i].denominator * fi->bpp[i] / 8;
696ec6859b2STodor Tomov 		bpl = ALIGN(bpl, video->bpl_alignment);
697ec6859b2STodor Tomov 		pix_mp->plane_fmt[i].bytesperline = bpl;
698ec6859b2STodor Tomov 		pix_mp->plane_fmt[i].sizeimage = pix_mp->height /
699ec6859b2STodor Tomov 			fi->vsub[i].numerator * fi->vsub[i].denominator * bpl;
700ec6859b2STodor Tomov 	}
701ec6859b2STodor Tomov 
702ec6859b2STodor Tomov 	pix_mp->field = V4L2_FIELD_NONE;
703ec6859b2STodor Tomov 	pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
704ec6859b2STodor Tomov 	pix_mp->flags = 0;
705ec6859b2STodor Tomov 	pix_mp->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace);
706ec6859b2STodor Tomov 	pix_mp->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
707ec6859b2STodor Tomov 					pix_mp->colorspace, pix_mp->ycbcr_enc);
708ec6859b2STodor Tomov 	pix_mp->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace);
709ec6859b2STodor Tomov 
710ec6859b2STodor Tomov 	if (video->line_based)
711ec6859b2STodor Tomov 		for (i = 0; i < pix_mp->num_planes; i++) {
712ec6859b2STodor Tomov 			p = &pix_mp->plane_fmt[i];
713ec6859b2STodor Tomov 			p->bytesperline = clamp_t(u32, p->bytesperline,
714ec6859b2STodor Tomov 						  1, 65528);
715ec6859b2STodor Tomov 			p->sizeimage = clamp_t(u32, p->sizeimage,
716ec6859b2STodor Tomov 					       p->bytesperline,
717ec6859b2STodor Tomov 					       p->bytesperline * 4096);
718ec6859b2STodor Tomov 			lines = p->sizeimage / p->bytesperline;
719ec6859b2STodor Tomov 
720ec6859b2STodor Tomov 			if (p->bytesperline < bytesperline[i])
721ec6859b2STodor Tomov 				p->bytesperline = ALIGN(bytesperline[i], 8);
722ec6859b2STodor Tomov 
723ec6859b2STodor Tomov 			if (p->sizeimage < p->bytesperline * lines)
724ec6859b2STodor Tomov 				p->sizeimage = p->bytesperline * lines;
725ec6859b2STodor Tomov 
726ec6859b2STodor Tomov 			if (p->sizeimage < sizeimage[i])
727ec6859b2STodor Tomov 				p->sizeimage = sizeimage[i];
728ec6859b2STodor Tomov 		}
729ec6859b2STodor Tomov 
730ec6859b2STodor Tomov 	return 0;
731ec6859b2STodor Tomov }
732ec6859b2STodor Tomov 
733ec6859b2STodor Tomov static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
734ec6859b2STodor Tomov {
735ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
736ec6859b2STodor Tomov 
737ec6859b2STodor Tomov 	return __video_try_fmt(video, f);
738ec6859b2STodor Tomov }
739ec6859b2STodor Tomov 
740ec6859b2STodor Tomov static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
741ec6859b2STodor Tomov {
742ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
743ec6859b2STodor Tomov 	int ret;
744ec6859b2STodor Tomov 
745ec6859b2STodor Tomov 	if (vb2_is_busy(&video->vb2_q))
746ec6859b2STodor Tomov 		return -EBUSY;
747ec6859b2STodor Tomov 
748ec6859b2STodor Tomov 	ret = __video_try_fmt(video, f);
749ec6859b2STodor Tomov 	if (ret < 0)
750ec6859b2STodor Tomov 		return ret;
751ec6859b2STodor Tomov 
752ec6859b2STodor Tomov 	video->active_fmt = *f;
753ec6859b2STodor Tomov 
754ec6859b2STodor Tomov 	return 0;
755ec6859b2STodor Tomov }
756ec6859b2STodor Tomov 
757ec6859b2STodor Tomov static int video_enum_input(struct file *file, void *fh,
758ec6859b2STodor Tomov 			    struct v4l2_input *input)
759ec6859b2STodor Tomov {
760ec6859b2STodor Tomov 	if (input->index > 0)
761ec6859b2STodor Tomov 		return -EINVAL;
762ec6859b2STodor Tomov 
763c0decac1SMauro Carvalho Chehab 	strscpy(input->name, "camera", sizeof(input->name));
764ec6859b2STodor Tomov 	input->type = V4L2_INPUT_TYPE_CAMERA;
765ec6859b2STodor Tomov 
766ec6859b2STodor Tomov 	return 0;
767ec6859b2STodor Tomov }
768ec6859b2STodor Tomov 
769ec6859b2STodor Tomov static int video_g_input(struct file *file, void *fh, unsigned int *input)
770ec6859b2STodor Tomov {
771ec6859b2STodor Tomov 	*input = 0;
772ec6859b2STodor Tomov 
773ec6859b2STodor Tomov 	return 0;
774ec6859b2STodor Tomov }
775ec6859b2STodor Tomov 
776ec6859b2STodor Tomov static int video_s_input(struct file *file, void *fh, unsigned int input)
777ec6859b2STodor Tomov {
778ec6859b2STodor Tomov 	return input == 0 ? 0 : -EINVAL;
779ec6859b2STodor Tomov }
780ec6859b2STodor Tomov 
781ec6859b2STodor Tomov static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = {
782ec6859b2STodor Tomov 	.vidioc_querycap		= video_querycap,
7837e98b7b5SBoris Brezillon 	.vidioc_enum_fmt_vid_cap	= video_enum_fmt,
78435493d65SAndrey Konovalov 	.vidioc_enum_framesizes		= video_enum_framesizes,
785ec6859b2STodor Tomov 	.vidioc_g_fmt_vid_cap_mplane	= video_g_fmt,
786ec6859b2STodor Tomov 	.vidioc_s_fmt_vid_cap_mplane	= video_s_fmt,
787ec6859b2STodor Tomov 	.vidioc_try_fmt_vid_cap_mplane	= video_try_fmt,
788ec6859b2STodor Tomov 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
789ec6859b2STodor Tomov 	.vidioc_querybuf		= vb2_ioctl_querybuf,
790ec6859b2STodor Tomov 	.vidioc_qbuf			= vb2_ioctl_qbuf,
791ec6859b2STodor Tomov 	.vidioc_expbuf			= vb2_ioctl_expbuf,
792ec6859b2STodor Tomov 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
793ec6859b2STodor Tomov 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
794ec6859b2STodor Tomov 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
795ec6859b2STodor Tomov 	.vidioc_streamon		= vb2_ioctl_streamon,
796ec6859b2STodor Tomov 	.vidioc_streamoff		= vb2_ioctl_streamoff,
797ec6859b2STodor Tomov 	.vidioc_enum_input		= video_enum_input,
798ec6859b2STodor Tomov 	.vidioc_g_input			= video_g_input,
799ec6859b2STodor Tomov 	.vidioc_s_input			= video_s_input,
800ec6859b2STodor Tomov };
801ec6859b2STodor Tomov 
802ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
803ec6859b2STodor Tomov  * V4L2 file operations
804ec6859b2STodor Tomov  */
805ec6859b2STodor Tomov 
806ec6859b2STodor Tomov static int video_open(struct file *file)
807ec6859b2STodor Tomov {
808ec6859b2STodor Tomov 	struct video_device *vdev = video_devdata(file);
809ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
810ec6859b2STodor Tomov 	struct v4l2_fh *vfh;
811ec6859b2STodor Tomov 	int ret;
812ec6859b2STodor Tomov 
813ec6859b2STodor Tomov 	mutex_lock(&video->lock);
814ec6859b2STodor Tomov 
815ec6859b2STodor Tomov 	vfh = kzalloc(sizeof(*vfh), GFP_KERNEL);
816ec6859b2STodor Tomov 	if (vfh == NULL) {
817ec6859b2STodor Tomov 		ret = -ENOMEM;
818ec6859b2STodor Tomov 		goto error_alloc;
819ec6859b2STodor Tomov 	}
820ec6859b2STodor Tomov 
821ec6859b2STodor Tomov 	v4l2_fh_init(vfh, vdev);
822ec6859b2STodor Tomov 	v4l2_fh_add(vfh);
823ec6859b2STodor Tomov 
824ec6859b2STodor Tomov 	file->private_data = vfh;
825ec6859b2STodor Tomov 
8268fd390b8SEzequiel Garcia 	ret = v4l2_pipeline_pm_get(&vdev->entity);
827ec6859b2STodor Tomov 	if (ret < 0) {
828ec6859b2STodor Tomov 		dev_err(video->camss->dev, "Failed to power up pipeline: %d\n",
829ec6859b2STodor Tomov 			ret);
830ec6859b2STodor Tomov 		goto error_pm_use;
831ec6859b2STodor Tomov 	}
832ec6859b2STodor Tomov 
833ec6859b2STodor Tomov 	mutex_unlock(&video->lock);
834ec6859b2STodor Tomov 
835ec6859b2STodor Tomov 	return 0;
836ec6859b2STodor Tomov 
837ec6859b2STodor Tomov error_pm_use:
838ec6859b2STodor Tomov 	v4l2_fh_release(file);
839ec6859b2STodor Tomov 
840ec6859b2STodor Tomov error_alloc:
841ec6859b2STodor Tomov 	mutex_unlock(&video->lock);
842ec6859b2STodor Tomov 
843ec6859b2STodor Tomov 	return ret;
844ec6859b2STodor Tomov }
845ec6859b2STodor Tomov 
846ec6859b2STodor Tomov static int video_release(struct file *file)
847ec6859b2STodor Tomov {
848ec6859b2STodor Tomov 	struct video_device *vdev = video_devdata(file);
849ec6859b2STodor Tomov 
850ec6859b2STodor Tomov 	vb2_fop_release(file);
851ec6859b2STodor Tomov 
8528fd390b8SEzequiel Garcia 	v4l2_pipeline_pm_put(&vdev->entity);
853ec6859b2STodor Tomov 
854ec6859b2STodor Tomov 	file->private_data = NULL;
855ec6859b2STodor Tomov 
856ec6859b2STodor Tomov 	return 0;
857ec6859b2STodor Tomov }
858ec6859b2STodor Tomov 
859ec6859b2STodor Tomov static const struct v4l2_file_operations msm_vid_fops = {
860ec6859b2STodor Tomov 	.owner          = THIS_MODULE,
861ec6859b2STodor Tomov 	.unlocked_ioctl = video_ioctl2,
862ec6859b2STodor Tomov 	.open           = video_open,
863ec6859b2STodor Tomov 	.release        = video_release,
864ec6859b2STodor Tomov 	.poll           = vb2_fop_poll,
865ec6859b2STodor Tomov 	.mmap		= vb2_fop_mmap,
866ec6859b2STodor Tomov 	.read		= vb2_fop_read,
867ec6859b2STodor Tomov };
868ec6859b2STodor Tomov 
869ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
870ec6859b2STodor Tomov  * CAMSS video core
871ec6859b2STodor Tomov  */
872ec6859b2STodor Tomov 
873ec6859b2STodor Tomov static void msm_video_release(struct video_device *vdev)
874ec6859b2STodor Tomov {
875ec6859b2STodor Tomov 	struct camss_video *video = video_get_drvdata(vdev);
876ec6859b2STodor Tomov 
877ec6859b2STodor Tomov 	media_entity_cleanup(&vdev->entity);
878ec6859b2STodor Tomov 
879ec6859b2STodor Tomov 	mutex_destroy(&video->q_lock);
880ec6859b2STodor Tomov 	mutex_destroy(&video->lock);
881ec6859b2STodor Tomov 
882ec6859b2STodor Tomov 	if (atomic_dec_and_test(&video->camss->ref_count))
883ec6859b2STodor Tomov 		camss_delete(video->camss);
884ec6859b2STodor Tomov }
885ec6859b2STodor Tomov 
886ec6859b2STodor Tomov /*
887ec6859b2STodor Tomov  * msm_video_init_format - Helper function to initialize format
888ec6859b2STodor Tomov  * @video: struct camss_video
889ec6859b2STodor Tomov  *
890ec6859b2STodor Tomov  * Initialize pad format with default value.
891ec6859b2STodor Tomov  *
892ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
893ec6859b2STodor Tomov  */
894ec6859b2STodor Tomov static int msm_video_init_format(struct camss_video *video)
895ec6859b2STodor Tomov {
896ec6859b2STodor Tomov 	int ret;
897ec6859b2STodor Tomov 	struct v4l2_format format = {
898ec6859b2STodor Tomov 		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
899ec6859b2STodor Tomov 		.fmt.pix_mp = {
900ec6859b2STodor Tomov 			.width = 1920,
901ec6859b2STodor Tomov 			.height = 1080,
902ec6859b2STodor Tomov 			.pixelformat = video->formats[0].pixelformat,
903ec6859b2STodor Tomov 		},
904ec6859b2STodor Tomov 	};
905ec6859b2STodor Tomov 
906ec6859b2STodor Tomov 	ret = __video_try_fmt(video, &format);
907ec6859b2STodor Tomov 	if (ret < 0)
908ec6859b2STodor Tomov 		return ret;
909ec6859b2STodor Tomov 
910ec6859b2STodor Tomov 	video->active_fmt = format;
911ec6859b2STodor Tomov 
912ec6859b2STodor Tomov 	return 0;
913ec6859b2STodor Tomov }
914ec6859b2STodor Tomov 
915ec6859b2STodor Tomov /*
916ec6859b2STodor Tomov  * msm_video_register - Register a video device node
917ec6859b2STodor Tomov  * @video: struct camss_video
918ec6859b2STodor Tomov  * @v4l2_dev: V4L2 device
919ec6859b2STodor Tomov  * @name: name to be used for the video device node
920ec6859b2STodor Tomov  *
921ec6859b2STodor Tomov  * Initialize and register a video device node to a V4L2 device. Also
922ec6859b2STodor Tomov  * initialize the vb2 queue.
923ec6859b2STodor Tomov  *
924ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
925ec6859b2STodor Tomov  */
926ec6859b2STodor Tomov 
927ec6859b2STodor Tomov int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
928ec6859b2STodor Tomov 		       const char *name, int is_pix)
929ec6859b2STodor Tomov {
930ec6859b2STodor Tomov 	struct media_pad *pad = &video->pad;
931ec6859b2STodor Tomov 	struct video_device *vdev;
932ec6859b2STodor Tomov 	struct vb2_queue *q;
933ec6859b2STodor Tomov 	int ret;
934ec6859b2STodor Tomov 
935ec6859b2STodor Tomov 	vdev = &video->vdev;
936ec6859b2STodor Tomov 
937ec6859b2STodor Tomov 	mutex_init(&video->q_lock);
938ec6859b2STodor Tomov 
939ec6859b2STodor Tomov 	q = &video->vb2_q;
940ec6859b2STodor Tomov 	q->drv_priv = video;
941ec6859b2STodor Tomov 	q->mem_ops = &vb2_dma_sg_memops;
942ec6859b2STodor Tomov 	q->ops = &msm_video_vb2_q_ops;
943ec6859b2STodor Tomov 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
944ec6859b2STodor Tomov 	q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ;
945ec6859b2STodor Tomov 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
946ec6859b2STodor Tomov 	q->buf_struct_size = sizeof(struct camss_buffer);
947ec6859b2STodor Tomov 	q->dev = video->camss->dev;
948ec6859b2STodor Tomov 	q->lock = &video->q_lock;
949ec6859b2STodor Tomov 	ret = vb2_queue_init(q);
950ec6859b2STodor Tomov 	if (ret < 0) {
951ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret);
952ec6859b2STodor Tomov 		goto error_vb2_init;
953ec6859b2STodor Tomov 	}
954ec6859b2STodor Tomov 
955ec6859b2STodor Tomov 	pad->flags = MEDIA_PAD_FL_SINK;
956ec6859b2STodor Tomov 	ret = media_entity_pads_init(&vdev->entity, 1, pad);
957ec6859b2STodor Tomov 	if (ret < 0) {
958ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n",
959ec6859b2STodor Tomov 			ret);
960492abcd7SHans Verkuil 		goto error_vb2_init;
961ec6859b2STodor Tomov 	}
962ec6859b2STodor Tomov 
963ec6859b2STodor Tomov 	mutex_init(&video->lock);
964ec6859b2STodor Tomov 
965cba3819dSTodor Tomov 	if (video->camss->version == CAMSS_8x16) {
966ec6859b2STodor Tomov 		if (is_pix) {
967cba3819dSTodor Tomov 			video->formats = formats_pix_8x16;
968cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_pix_8x16);
969cba3819dSTodor Tomov 		} else {
970cba3819dSTodor Tomov 			video->formats = formats_rdi_8x16;
971cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_rdi_8x16);
972cba3819dSTodor Tomov 		}
973cba3819dSTodor Tomov 	} else if (video->camss->version == CAMSS_8x96) {
974cba3819dSTodor Tomov 		if (is_pix) {
975cba3819dSTodor Tomov 			video->formats = formats_pix_8x96;
976cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_pix_8x96);
977cba3819dSTodor Tomov 		} else {
978cba3819dSTodor Tomov 			video->formats = formats_rdi_8x96;
979cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_rdi_8x96);
980cba3819dSTodor Tomov 		}
981cba3819dSTodor Tomov 	} else {
982cba3819dSTodor Tomov 		goto error_video_register;
983ec6859b2STodor Tomov 	}
984ec6859b2STodor Tomov 
985ec6859b2STodor Tomov 	ret = msm_video_init_format(video);
986ec6859b2STodor Tomov 	if (ret < 0) {
987ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret);
988ec6859b2STodor Tomov 		goto error_video_register;
989ec6859b2STodor Tomov 	}
990ec6859b2STodor Tomov 
991ec6859b2STodor Tomov 	vdev->fops = &msm_vid_fops;
992c90f1178SAndrey Konovalov 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING
993c90f1178SAndrey Konovalov 			  | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC;
994ec6859b2STodor Tomov 	vdev->ioctl_ops = &msm_vid_ioctl_ops;
995ec6859b2STodor Tomov 	vdev->release = msm_video_release;
996ec6859b2STodor Tomov 	vdev->v4l2_dev = v4l2_dev;
997ec6859b2STodor Tomov 	vdev->vfl_dir = VFL_DIR_RX;
998ec6859b2STodor Tomov 	vdev->queue = &video->vb2_q;
999ec6859b2STodor Tomov 	vdev->lock = &video->lock;
1000c0decac1SMauro Carvalho Chehab 	strscpy(vdev->name, name, sizeof(vdev->name));
1001ec6859b2STodor Tomov 
100270cad449SHans Verkuil 	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
1003ec6859b2STodor Tomov 	if (ret < 0) {
1004ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to register video device: %d\n",
1005ec6859b2STodor Tomov 			ret);
1006ec6859b2STodor Tomov 		goto error_video_register;
1007ec6859b2STodor Tomov 	}
1008ec6859b2STodor Tomov 
1009ec6859b2STodor Tomov 	video_set_drvdata(vdev, video);
1010ec6859b2STodor Tomov 	atomic_inc(&video->camss->ref_count);
1011ec6859b2STodor Tomov 
1012ec6859b2STodor Tomov 	return 0;
1013ec6859b2STodor Tomov 
1014ec6859b2STodor Tomov error_video_register:
1015ec6859b2STodor Tomov 	media_entity_cleanup(&vdev->entity);
1016ec6859b2STodor Tomov 	mutex_destroy(&video->lock);
1017ec6859b2STodor Tomov error_vb2_init:
1018ec6859b2STodor Tomov 	mutex_destroy(&video->q_lock);
1019ec6859b2STodor Tomov 
1020ec6859b2STodor Tomov 	return ret;
1021ec6859b2STodor Tomov }
1022ec6859b2STodor Tomov 
1023ec6859b2STodor Tomov void msm_video_unregister(struct camss_video *video)
1024ec6859b2STodor Tomov {
1025ec6859b2STodor Tomov 	atomic_inc(&video->camss->ref_count);
1026492abcd7SHans Verkuil 	vb2_video_unregister_device(&video->vdev);
1027ec6859b2STodor Tomov 	atomic_dec(&video->camss->ref_count);
1028ec6859b2STodor Tomov }
1029