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 
21ec6859b2STodor Tomov struct fract {
22ec6859b2STodor Tomov 	u8 numerator;
23ec6859b2STodor Tomov 	u8 denominator;
24ec6859b2STodor Tomov };
25ec6859b2STodor Tomov 
26ec6859b2STodor Tomov /*
27ec6859b2STodor Tomov  * struct camss_format_info - ISP media bus format information
28ec6859b2STodor Tomov  * @code: V4L2 media bus format code
29ec6859b2STodor Tomov  * @pixelformat: V4L2 pixel format FCC identifier
30ec6859b2STodor Tomov  * @planes: Number of planes
31ec6859b2STodor Tomov  * @hsub: Horizontal subsampling (for each plane)
32ec6859b2STodor Tomov  * @vsub: Vertical subsampling (for each plane)
33ec6859b2STodor Tomov  * @bpp: Bits per pixel when stored in memory (for each plane)
34ec6859b2STodor Tomov  */
35ec6859b2STodor Tomov struct camss_format_info {
36ec6859b2STodor Tomov 	u32 code;
37ec6859b2STodor Tomov 	u32 pixelformat;
38ec6859b2STodor Tomov 	u8 planes;
39ec6859b2STodor Tomov 	struct fract hsub[3];
40ec6859b2STodor Tomov 	struct fract vsub[3];
41ec6859b2STodor Tomov 	unsigned int bpp[3];
42ec6859b2STodor Tomov };
43ec6859b2STodor Tomov 
44cba3819dSTodor Tomov static const struct camss_format_info formats_rdi_8x16[] = {
45ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
46ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
47ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
48ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
49ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
50ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
51ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1,
52ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
53ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
54ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
55ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
56ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
57ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
58ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
59ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
60ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
61ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1,
62ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
63ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1,
64ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
65ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1,
66ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
67ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1,
68ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
69ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1,
70ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
71ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1,
72ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
73ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1,
74ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
75ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
76ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
77cc8fe073STodor Tomov 	{ MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1,
78cc8fe073STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
79ec6859b2STodor Tomov };
80ec6859b2STodor Tomov 
81cba3819dSTodor Tomov static const struct camss_format_info formats_rdi_8x96[] = {
82cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
83cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
84cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
85cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
86cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
87cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
88cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1,
89cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
90cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
91cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
92cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
93cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
94cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
95cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
96cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
97cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
98cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1,
99cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
100cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1,
101cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
102cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1,
103cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
104cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1,
105cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
1065019d7c8STodor Tomov 	{ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1,
1075019d7c8STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
108cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1,
109cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
110cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1,
111cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
112cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1,
113cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
114cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
115cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
116f476fb56STodor Tomov 	{ MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1,
117f476fb56STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
118f476fb56STodor Tomov 	{ MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1,
119f476fb56STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
120f476fb56STodor Tomov 	{ MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1,
121f476fb56STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
122f476fb56STodor Tomov 	{ MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1,
123f476fb56STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
124cc8fe073STodor Tomov 	{ MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1,
125cc8fe073STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
126cc8fe073STodor Tomov 	{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1,
127cc8fe073STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
128cba3819dSTodor Tomov };
129cba3819dSTodor Tomov 
130cba3819dSTodor Tomov static const struct camss_format_info formats_pix_8x16[] = {
131cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1,
132cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
133cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1,
134cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
135cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
136cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
137cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
138cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
139cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1,
140cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
141cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1,
142cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
143cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
144cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
145cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
146cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
147cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1,
148cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
149cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1,
150cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
151cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1,
152cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
153cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1,
154cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
155cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1,
156cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
157cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1,
158cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
159cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1,
160cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
161cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1,
162cba3819dSTodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
163cba3819dSTodor Tomov };
164cba3819dSTodor Tomov 
165cba3819dSTodor Tomov static const struct camss_format_info formats_pix_8x96[] = {
166ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1,
167ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
168ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1,
169ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
170ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
171ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
172ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
173ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
174ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1,
175ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
176ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1,
177ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
178ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
179ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
180ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
181ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
182ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1,
183ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
184ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1,
185ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
186ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1,
187ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
188ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1,
189ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
190ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1,
191ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
192ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1,
193ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
194ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1,
195ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
196ec6859b2STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1,
197ec6859b2STodor Tomov 	  { { 1, 1 } }, { { 1, 2 } }, { 8 } },
198312e1c85STodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
199312e1c85STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
200312e1c85STodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
201312e1c85STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
202312e1c85STodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
203312e1c85STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
204312e1c85STodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1,
205312e1c85STodor Tomov 	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
206ec6859b2STodor Tomov };
207ec6859b2STodor Tomov 
208ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
209ec6859b2STodor Tomov  * Helper functions
210ec6859b2STodor Tomov  */
211ec6859b2STodor Tomov 
212ec6859b2STodor Tomov static int video_find_format(u32 code, u32 pixelformat,
213ec6859b2STodor Tomov 			     const struct camss_format_info *formats,
214ec6859b2STodor Tomov 			     unsigned int nformats)
215ec6859b2STodor Tomov {
216ec6859b2STodor Tomov 	int i;
217ec6859b2STodor Tomov 
218ec6859b2STodor Tomov 	for (i = 0; i < nformats; i++) {
219ec6859b2STodor Tomov 		if (formats[i].code == code &&
220ec6859b2STodor Tomov 		    formats[i].pixelformat == pixelformat)
221ec6859b2STodor Tomov 			return i;
222ec6859b2STodor Tomov 	}
223ec6859b2STodor Tomov 
224ec6859b2STodor Tomov 	for (i = 0; i < nformats; i++)
225ec6859b2STodor Tomov 		if (formats[i].code == code)
226ec6859b2STodor Tomov 			return i;
227ec6859b2STodor Tomov 
228ec6859b2STodor Tomov 	WARN_ON(1);
229ec6859b2STodor Tomov 
230ec6859b2STodor Tomov 	return -EINVAL;
231ec6859b2STodor Tomov }
232ec6859b2STodor Tomov 
233ec6859b2STodor Tomov /*
234ec6859b2STodor Tomov  * video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane
235ec6859b2STodor Tomov  * @mbus: v4l2_mbus_framefmt format (input)
236ec6859b2STodor Tomov  * @pix: v4l2_pix_format_mplane format (output)
237ec6859b2STodor Tomov  * @f: a pointer to formats array element to be used for the conversion
238ec6859b2STodor Tomov  * @alignment: bytesperline alignment value
239ec6859b2STodor Tomov  *
240ec6859b2STodor Tomov  * Fill the output pix structure with information from the input mbus format.
241ec6859b2STodor Tomov  *
242ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
243ec6859b2STodor Tomov  */
244ec6859b2STodor Tomov static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
245ec6859b2STodor Tomov 				struct v4l2_pix_format_mplane *pix,
246ec6859b2STodor Tomov 				const struct camss_format_info *f,
247ec6859b2STodor Tomov 				unsigned int alignment)
248ec6859b2STodor Tomov {
249ec6859b2STodor Tomov 	unsigned int i;
250ec6859b2STodor Tomov 	u32 bytesperline;
251ec6859b2STodor Tomov 
252ec6859b2STodor Tomov 	memset(pix, 0, sizeof(*pix));
253ec6859b2STodor Tomov 	v4l2_fill_pix_format_mplane(pix, mbus);
254ec6859b2STodor Tomov 	pix->pixelformat = f->pixelformat;
255ec6859b2STodor Tomov 	pix->num_planes = f->planes;
256ec6859b2STodor Tomov 	for (i = 0; i < pix->num_planes; i++) {
257ec6859b2STodor Tomov 		bytesperline = pix->width / f->hsub[i].numerator *
258ec6859b2STodor Tomov 			f->hsub[i].denominator * f->bpp[i] / 8;
259ec6859b2STodor Tomov 		bytesperline = ALIGN(bytesperline, alignment);
260ec6859b2STodor Tomov 		pix->plane_fmt[i].bytesperline = bytesperline;
261ec6859b2STodor Tomov 		pix->plane_fmt[i].sizeimage = pix->height /
262ec6859b2STodor Tomov 				f->vsub[i].numerator * f->vsub[i].denominator *
263ec6859b2STodor Tomov 				bytesperline;
264ec6859b2STodor Tomov 	}
265ec6859b2STodor Tomov 
266ec6859b2STodor Tomov 	return 0;
267ec6859b2STodor Tomov }
268ec6859b2STodor Tomov 
269ec6859b2STodor Tomov static struct v4l2_subdev *video_remote_subdev(struct camss_video *video,
270ec6859b2STodor Tomov 					       u32 *pad)
271ec6859b2STodor Tomov {
272ec6859b2STodor Tomov 	struct media_pad *remote;
273ec6859b2STodor Tomov 
274ec6859b2STodor Tomov 	remote = media_entity_remote_pad(&video->pad);
275ec6859b2STodor Tomov 
276ec6859b2STodor Tomov 	if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
277ec6859b2STodor Tomov 		return NULL;
278ec6859b2STodor Tomov 
279ec6859b2STodor Tomov 	if (pad)
280ec6859b2STodor Tomov 		*pad = remote->index;
281ec6859b2STodor Tomov 
282ec6859b2STodor Tomov 	return media_entity_to_v4l2_subdev(remote->entity);
283ec6859b2STodor Tomov }
284ec6859b2STodor Tomov 
285ec6859b2STodor Tomov static int video_get_subdev_format(struct camss_video *video,
286ec6859b2STodor Tomov 				   struct v4l2_format *format)
287ec6859b2STodor Tomov {
288ec6859b2STodor Tomov 	struct v4l2_subdev_format fmt;
289ec6859b2STodor Tomov 	struct v4l2_subdev *subdev;
290ec6859b2STodor Tomov 	u32 pad;
291ec6859b2STodor Tomov 	int ret;
292ec6859b2STodor Tomov 
293ec6859b2STodor Tomov 	subdev = video_remote_subdev(video, &pad);
294ec6859b2STodor Tomov 	if (subdev == NULL)
295ec6859b2STodor Tomov 		return -EPIPE;
296ec6859b2STodor Tomov 
297ec6859b2STodor Tomov 	fmt.pad = pad;
298ec6859b2STodor Tomov 	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
299ec6859b2STodor Tomov 
300ec6859b2STodor Tomov 	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
301ec6859b2STodor Tomov 	if (ret)
302ec6859b2STodor Tomov 		return ret;
303ec6859b2STodor Tomov 
304ec6859b2STodor Tomov 	ret = video_find_format(fmt.format.code,
305ec6859b2STodor Tomov 				format->fmt.pix_mp.pixelformat,
306ec6859b2STodor Tomov 				video->formats, video->nformats);
307ec6859b2STodor Tomov 	if (ret < 0)
308ec6859b2STodor Tomov 		return ret;
309ec6859b2STodor Tomov 
310ec6859b2STodor Tomov 	format->type = video->type;
311ec6859b2STodor Tomov 
312ec6859b2STodor Tomov 	return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp,
313ec6859b2STodor Tomov 				    &video->formats[ret], video->bpl_alignment);
314ec6859b2STodor Tomov }
315ec6859b2STodor Tomov 
316ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
317ec6859b2STodor Tomov  * Video queue operations
318ec6859b2STodor Tomov  */
319ec6859b2STodor Tomov 
320ec6859b2STodor Tomov static int video_queue_setup(struct vb2_queue *q,
321ec6859b2STodor Tomov 	unsigned int *num_buffers, unsigned int *num_planes,
322ec6859b2STodor Tomov 	unsigned int sizes[], struct device *alloc_devs[])
323ec6859b2STodor Tomov {
324ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(q);
325ec6859b2STodor Tomov 	const struct v4l2_pix_format_mplane *format =
326ec6859b2STodor Tomov 						&video->active_fmt.fmt.pix_mp;
327ec6859b2STodor Tomov 	unsigned int i;
328ec6859b2STodor Tomov 
329ec6859b2STodor Tomov 	if (*num_planes) {
330ec6859b2STodor Tomov 		if (*num_planes != format->num_planes)
331ec6859b2STodor Tomov 			return -EINVAL;
332ec6859b2STodor Tomov 
333ec6859b2STodor Tomov 		for (i = 0; i < *num_planes; i++)
334ec6859b2STodor Tomov 			if (sizes[i] < format->plane_fmt[i].sizeimage)
335ec6859b2STodor Tomov 				return -EINVAL;
336ec6859b2STodor Tomov 
337ec6859b2STodor Tomov 		return 0;
338ec6859b2STodor Tomov 	}
339ec6859b2STodor Tomov 
340ec6859b2STodor Tomov 	*num_planes = format->num_planes;
341ec6859b2STodor Tomov 
342ec6859b2STodor Tomov 	for (i = 0; i < *num_planes; i++)
343ec6859b2STodor Tomov 		sizes[i] = format->plane_fmt[i].sizeimage;
344ec6859b2STodor Tomov 
345ec6859b2STodor Tomov 	return 0;
346ec6859b2STodor Tomov }
347ec6859b2STodor Tomov 
348ec6859b2STodor Tomov static int video_buf_init(struct vb2_buffer *vb)
349ec6859b2STodor Tomov {
350ec6859b2STodor Tomov 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
351ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
352ec6859b2STodor Tomov 	struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer,
353ec6859b2STodor Tomov 						   vb);
354ec6859b2STodor Tomov 	const struct v4l2_pix_format_mplane *format =
355ec6859b2STodor Tomov 						&video->active_fmt.fmt.pix_mp;
356ec6859b2STodor Tomov 	struct sg_table *sgt;
357ec6859b2STodor Tomov 	unsigned int i;
358ec6859b2STodor Tomov 
359ec6859b2STodor Tomov 	for (i = 0; i < format->num_planes; i++) {
360ec6859b2STodor Tomov 		sgt = vb2_dma_sg_plane_desc(vb, i);
361ec6859b2STodor Tomov 		if (!sgt)
362ec6859b2STodor Tomov 			return -EFAULT;
363ec6859b2STodor Tomov 
364ec6859b2STodor Tomov 		buffer->addr[i] = sg_dma_address(sgt->sgl);
365ec6859b2STodor Tomov 	}
366ec6859b2STodor Tomov 
367ec6859b2STodor Tomov 	if (format->pixelformat == V4L2_PIX_FMT_NV12 ||
368ec6859b2STodor Tomov 			format->pixelformat == V4L2_PIX_FMT_NV21 ||
369ec6859b2STodor Tomov 			format->pixelformat == V4L2_PIX_FMT_NV16 ||
370ec6859b2STodor Tomov 			format->pixelformat == V4L2_PIX_FMT_NV61)
371ec6859b2STodor Tomov 		buffer->addr[1] = buffer->addr[0] +
372ec6859b2STodor Tomov 				format->plane_fmt[0].bytesperline *
373ec6859b2STodor Tomov 				format->height;
374ec6859b2STodor Tomov 
375ec6859b2STodor Tomov 	return 0;
376ec6859b2STodor Tomov }
377ec6859b2STodor Tomov 
378ec6859b2STodor Tomov static int video_buf_prepare(struct vb2_buffer *vb)
379ec6859b2STodor Tomov {
380ec6859b2STodor Tomov 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
381ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
382ec6859b2STodor Tomov 	const struct v4l2_pix_format_mplane *format =
383ec6859b2STodor Tomov 						&video->active_fmt.fmt.pix_mp;
384ec6859b2STodor Tomov 	unsigned int i;
385ec6859b2STodor Tomov 
386ec6859b2STodor Tomov 	for (i = 0; i < format->num_planes; i++) {
387ec6859b2STodor Tomov 		if (format->plane_fmt[i].sizeimage > vb2_plane_size(vb, i))
388ec6859b2STodor Tomov 			return -EINVAL;
389ec6859b2STodor Tomov 
390ec6859b2STodor Tomov 		vb2_set_plane_payload(vb, i, format->plane_fmt[i].sizeimage);
391ec6859b2STodor Tomov 	}
392ec6859b2STodor Tomov 
393ec6859b2STodor Tomov 	vbuf->field = V4L2_FIELD_NONE;
394ec6859b2STodor Tomov 
395ec6859b2STodor Tomov 	return 0;
396ec6859b2STodor Tomov }
397ec6859b2STodor Tomov 
398ec6859b2STodor Tomov static void video_buf_queue(struct vb2_buffer *vb)
399ec6859b2STodor Tomov {
400ec6859b2STodor Tomov 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
401ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
402ec6859b2STodor Tomov 	struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer,
403ec6859b2STodor Tomov 						   vb);
404ec6859b2STodor Tomov 
405ec6859b2STodor Tomov 	video->ops->queue_buffer(video, buffer);
406ec6859b2STodor Tomov }
407ec6859b2STodor Tomov 
408ec6859b2STodor Tomov static int video_check_format(struct camss_video *video)
409ec6859b2STodor Tomov {
410ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *pix = &video->active_fmt.fmt.pix_mp;
411ec6859b2STodor Tomov 	struct v4l2_format format;
412ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *sd_pix = &format.fmt.pix_mp;
413ec6859b2STodor Tomov 	int ret;
414ec6859b2STodor Tomov 
415ec6859b2STodor Tomov 	sd_pix->pixelformat = pix->pixelformat;
416ec6859b2STodor Tomov 	ret = video_get_subdev_format(video, &format);
417ec6859b2STodor Tomov 	if (ret < 0)
418ec6859b2STodor Tomov 		return ret;
419ec6859b2STodor Tomov 
420ec6859b2STodor Tomov 	if (pix->pixelformat != sd_pix->pixelformat ||
421ec6859b2STodor Tomov 	    pix->height != sd_pix->height ||
422ec6859b2STodor Tomov 	    pix->width != sd_pix->width ||
423ec6859b2STodor Tomov 	    pix->num_planes != sd_pix->num_planes ||
424ec6859b2STodor Tomov 	    pix->field != format.fmt.pix_mp.field)
425ec6859b2STodor Tomov 		return -EPIPE;
426ec6859b2STodor Tomov 
427ec6859b2STodor Tomov 	return 0;
428ec6859b2STodor Tomov }
429ec6859b2STodor Tomov 
430ec6859b2STodor Tomov static int video_start_streaming(struct vb2_queue *q, unsigned int count)
431ec6859b2STodor Tomov {
432ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(q);
433ec6859b2STodor Tomov 	struct video_device *vdev = &video->vdev;
434ec6859b2STodor Tomov 	struct media_entity *entity;
435ec6859b2STodor Tomov 	struct media_pad *pad;
436ec6859b2STodor Tomov 	struct v4l2_subdev *subdev;
437ec6859b2STodor Tomov 	int ret;
438ec6859b2STodor Tomov 
439ec6859b2STodor Tomov 	ret = media_pipeline_start(&vdev->entity, &video->pipe);
440ec6859b2STodor Tomov 	if (ret < 0)
441ec6859b2STodor Tomov 		return ret;
442ec6859b2STodor Tomov 
443ec6859b2STodor Tomov 	ret = video_check_format(video);
444ec6859b2STodor Tomov 	if (ret < 0)
445ec6859b2STodor Tomov 		goto error;
446ec6859b2STodor Tomov 
447ec6859b2STodor Tomov 	entity = &vdev->entity;
448ec6859b2STodor Tomov 	while (1) {
449ec6859b2STodor Tomov 		pad = &entity->pads[0];
450ec6859b2STodor Tomov 		if (!(pad->flags & MEDIA_PAD_FL_SINK))
451ec6859b2STodor Tomov 			break;
452ec6859b2STodor Tomov 
453ec6859b2STodor Tomov 		pad = media_entity_remote_pad(pad);
454ec6859b2STodor Tomov 		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
455ec6859b2STodor Tomov 			break;
456ec6859b2STodor Tomov 
457ec6859b2STodor Tomov 		entity = pad->entity;
458ec6859b2STodor Tomov 		subdev = media_entity_to_v4l2_subdev(entity);
459ec6859b2STodor Tomov 
460ec6859b2STodor Tomov 		ret = v4l2_subdev_call(subdev, video, s_stream, 1);
461ec6859b2STodor Tomov 		if (ret < 0 && ret != -ENOIOCTLCMD)
462ec6859b2STodor Tomov 			goto error;
463ec6859b2STodor Tomov 	}
464ec6859b2STodor Tomov 
465ec6859b2STodor Tomov 	return 0;
466ec6859b2STodor Tomov 
467ec6859b2STodor Tomov error:
468ec6859b2STodor Tomov 	media_pipeline_stop(&vdev->entity);
469ec6859b2STodor Tomov 
470ec6859b2STodor Tomov 	video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED);
471ec6859b2STodor Tomov 
472ec6859b2STodor Tomov 	return ret;
473ec6859b2STodor Tomov }
474ec6859b2STodor Tomov 
475ec6859b2STodor Tomov static void video_stop_streaming(struct vb2_queue *q)
476ec6859b2STodor Tomov {
477ec6859b2STodor Tomov 	struct camss_video *video = vb2_get_drv_priv(q);
478ec6859b2STodor Tomov 	struct video_device *vdev = &video->vdev;
479ec6859b2STodor Tomov 	struct media_entity *entity;
480ec6859b2STodor Tomov 	struct media_pad *pad;
481ec6859b2STodor Tomov 	struct v4l2_subdev *subdev;
482ec6859b2STodor Tomov 
483ec6859b2STodor Tomov 	entity = &vdev->entity;
484ec6859b2STodor Tomov 	while (1) {
485ec6859b2STodor Tomov 		pad = &entity->pads[0];
486ec6859b2STodor Tomov 		if (!(pad->flags & MEDIA_PAD_FL_SINK))
487ec6859b2STodor Tomov 			break;
488ec6859b2STodor Tomov 
489ec6859b2STodor Tomov 		pad = media_entity_remote_pad(pad);
490ec6859b2STodor Tomov 		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
491ec6859b2STodor Tomov 			break;
492ec6859b2STodor Tomov 
493ec6859b2STodor Tomov 		entity = pad->entity;
494ec6859b2STodor Tomov 		subdev = media_entity_to_v4l2_subdev(entity);
495ec6859b2STodor Tomov 
496ec6859b2STodor Tomov 		v4l2_subdev_call(subdev, video, s_stream, 0);
497ec6859b2STodor Tomov 	}
498ec6859b2STodor Tomov 
499ec6859b2STodor Tomov 	media_pipeline_stop(&vdev->entity);
500ec6859b2STodor Tomov 
501ec6859b2STodor Tomov 	video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
502ec6859b2STodor Tomov }
503ec6859b2STodor Tomov 
504ec6859b2STodor Tomov static const struct vb2_ops msm_video_vb2_q_ops = {
505ec6859b2STodor Tomov 	.queue_setup     = video_queue_setup,
506ec6859b2STodor Tomov 	.wait_prepare    = vb2_ops_wait_prepare,
507ec6859b2STodor Tomov 	.wait_finish     = vb2_ops_wait_finish,
508ec6859b2STodor Tomov 	.buf_init        = video_buf_init,
509ec6859b2STodor Tomov 	.buf_prepare     = video_buf_prepare,
510ec6859b2STodor Tomov 	.buf_queue       = video_buf_queue,
511ec6859b2STodor Tomov 	.start_streaming = video_start_streaming,
512ec6859b2STodor Tomov 	.stop_streaming  = video_stop_streaming,
513ec6859b2STodor Tomov };
514ec6859b2STodor Tomov 
515ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
516ec6859b2STodor Tomov  * V4L2 ioctls
517ec6859b2STodor Tomov  */
518ec6859b2STodor Tomov 
519ec6859b2STodor Tomov static int video_querycap(struct file *file, void *fh,
520ec6859b2STodor Tomov 			  struct v4l2_capability *cap)
521ec6859b2STodor Tomov {
522ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
523ec6859b2STodor Tomov 
524ec6859b2STodor Tomov 	strlcpy(cap->driver, "qcom-camss", sizeof(cap->driver));
525ec6859b2STodor Tomov 	strlcpy(cap->card, "Qualcomm Camera Subsystem", sizeof(cap->card));
526ec6859b2STodor Tomov 	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
527ec6859b2STodor Tomov 		 dev_name(video->camss->dev));
528ec6859b2STodor Tomov 
529ec6859b2STodor Tomov 	return 0;
530ec6859b2STodor Tomov }
531ec6859b2STodor Tomov 
532ec6859b2STodor Tomov static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
533ec6859b2STodor Tomov {
534ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
535ec6859b2STodor Tomov 	int i, j, k;
536ec6859b2STodor Tomov 
537ec6859b2STodor Tomov 	if (f->type != video->type)
538ec6859b2STodor Tomov 		return -EINVAL;
539ec6859b2STodor Tomov 
540ec6859b2STodor Tomov 	if (f->index >= video->nformats)
541ec6859b2STodor Tomov 		return -EINVAL;
542ec6859b2STodor Tomov 
543ec6859b2STodor Tomov 	/* find index "i" of "k"th unique pixelformat in formats array */
544ec6859b2STodor Tomov 	k = -1;
545ec6859b2STodor Tomov 	for (i = 0; i < video->nformats; i++) {
546ec6859b2STodor Tomov 		for (j = 0; j < i; j++) {
547ec6859b2STodor Tomov 			if (video->formats[i].pixelformat ==
548ec6859b2STodor Tomov 					video->formats[j].pixelformat)
549ec6859b2STodor Tomov 				break;
550ec6859b2STodor Tomov 		}
551ec6859b2STodor Tomov 
552ec6859b2STodor Tomov 		if (j == i)
553ec6859b2STodor Tomov 			k++;
554ec6859b2STodor Tomov 
555ec6859b2STodor Tomov 		if (k == f->index)
556ec6859b2STodor Tomov 			break;
557ec6859b2STodor Tomov 	}
558ec6859b2STodor Tomov 
559ec6859b2STodor Tomov 	if (k < f->index)
560ec6859b2STodor Tomov 		return -EINVAL;
561ec6859b2STodor Tomov 
562ec6859b2STodor Tomov 	f->pixelformat = video->formats[i].pixelformat;
563ec6859b2STodor Tomov 
564ec6859b2STodor Tomov 	return 0;
565ec6859b2STodor Tomov }
566ec6859b2STodor Tomov 
567ec6859b2STodor Tomov static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
568ec6859b2STodor Tomov {
569ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
570ec6859b2STodor Tomov 
571ec6859b2STodor Tomov 	*f = video->active_fmt;
572ec6859b2STodor Tomov 
573ec6859b2STodor Tomov 	return 0;
574ec6859b2STodor Tomov }
575ec6859b2STodor Tomov 
576ec6859b2STodor Tomov static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f)
577ec6859b2STodor Tomov {
578ec6859b2STodor Tomov 	struct v4l2_pix_format_mplane *pix_mp;
579ec6859b2STodor Tomov 	const struct camss_format_info *fi;
580ec6859b2STodor Tomov 	struct v4l2_plane_pix_format *p;
581ec6859b2STodor Tomov 	u32 bytesperline[3] = { 0 };
582ec6859b2STodor Tomov 	u32 sizeimage[3] = { 0 };
583ec6859b2STodor Tomov 	u32 width, height;
584ec6859b2STodor Tomov 	u32 bpl, lines;
585ec6859b2STodor Tomov 	int i, j;
586ec6859b2STodor Tomov 
587ec6859b2STodor Tomov 	pix_mp = &f->fmt.pix_mp;
588ec6859b2STodor Tomov 
589ec6859b2STodor Tomov 	if (video->line_based)
590ec6859b2STodor Tomov 		for (i = 0; i < pix_mp->num_planes && i < 3; i++) {
591ec6859b2STodor Tomov 			p = &pix_mp->plane_fmt[i];
592ec6859b2STodor Tomov 			bytesperline[i] = clamp_t(u32, p->bytesperline,
593ec6859b2STodor Tomov 						  1, 65528);
594ec6859b2STodor Tomov 			sizeimage[i] = clamp_t(u32, p->sizeimage,
595ec6859b2STodor Tomov 					       bytesperline[i],
596ec6859b2STodor Tomov 					       bytesperline[i] * 4096);
597ec6859b2STodor Tomov 		}
598ec6859b2STodor Tomov 
599ec6859b2STodor Tomov 	for (j = 0; j < video->nformats; j++)
600ec6859b2STodor Tomov 		if (pix_mp->pixelformat == video->formats[j].pixelformat)
601ec6859b2STodor Tomov 			break;
602ec6859b2STodor Tomov 
603ec6859b2STodor Tomov 	if (j == video->nformats)
604ec6859b2STodor Tomov 		j = 0; /* default format */
605ec6859b2STodor Tomov 
606ec6859b2STodor Tomov 	fi = &video->formats[j];
607ec6859b2STodor Tomov 	width = pix_mp->width;
608ec6859b2STodor Tomov 	height = pix_mp->height;
609ec6859b2STodor Tomov 
610ec6859b2STodor Tomov 	memset(pix_mp, 0, sizeof(*pix_mp));
611ec6859b2STodor Tomov 
612ec6859b2STodor Tomov 	pix_mp->pixelformat = fi->pixelformat;
613ec6859b2STodor Tomov 	pix_mp->width = clamp_t(u32, width, 1, 8191);
614ec6859b2STodor Tomov 	pix_mp->height = clamp_t(u32, height, 1, 8191);
615ec6859b2STodor Tomov 	pix_mp->num_planes = fi->planes;
616ec6859b2STodor Tomov 	for (i = 0; i < pix_mp->num_planes; i++) {
617ec6859b2STodor Tomov 		bpl = pix_mp->width / fi->hsub[i].numerator *
618ec6859b2STodor Tomov 			fi->hsub[i].denominator * fi->bpp[i] / 8;
619ec6859b2STodor Tomov 		bpl = ALIGN(bpl, video->bpl_alignment);
620ec6859b2STodor Tomov 		pix_mp->plane_fmt[i].bytesperline = bpl;
621ec6859b2STodor Tomov 		pix_mp->plane_fmt[i].sizeimage = pix_mp->height /
622ec6859b2STodor Tomov 			fi->vsub[i].numerator * fi->vsub[i].denominator * bpl;
623ec6859b2STodor Tomov 	}
624ec6859b2STodor Tomov 
625ec6859b2STodor Tomov 	pix_mp->field = V4L2_FIELD_NONE;
626ec6859b2STodor Tomov 	pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
627ec6859b2STodor Tomov 	pix_mp->flags = 0;
628ec6859b2STodor Tomov 	pix_mp->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace);
629ec6859b2STodor Tomov 	pix_mp->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
630ec6859b2STodor Tomov 					pix_mp->colorspace, pix_mp->ycbcr_enc);
631ec6859b2STodor Tomov 	pix_mp->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace);
632ec6859b2STodor Tomov 
633ec6859b2STodor Tomov 	if (video->line_based)
634ec6859b2STodor Tomov 		for (i = 0; i < pix_mp->num_planes; i++) {
635ec6859b2STodor Tomov 			p = &pix_mp->plane_fmt[i];
636ec6859b2STodor Tomov 			p->bytesperline = clamp_t(u32, p->bytesperline,
637ec6859b2STodor Tomov 						  1, 65528);
638ec6859b2STodor Tomov 			p->sizeimage = clamp_t(u32, p->sizeimage,
639ec6859b2STodor Tomov 					       p->bytesperline,
640ec6859b2STodor Tomov 					       p->bytesperline * 4096);
641ec6859b2STodor Tomov 			lines = p->sizeimage / p->bytesperline;
642ec6859b2STodor Tomov 
643ec6859b2STodor Tomov 			if (p->bytesperline < bytesperline[i])
644ec6859b2STodor Tomov 				p->bytesperline = ALIGN(bytesperline[i], 8);
645ec6859b2STodor Tomov 
646ec6859b2STodor Tomov 			if (p->sizeimage < p->bytesperline * lines)
647ec6859b2STodor Tomov 				p->sizeimage = p->bytesperline * lines;
648ec6859b2STodor Tomov 
649ec6859b2STodor Tomov 			if (p->sizeimage < sizeimage[i])
650ec6859b2STodor Tomov 				p->sizeimage = sizeimage[i];
651ec6859b2STodor Tomov 		}
652ec6859b2STodor Tomov 
653ec6859b2STodor Tomov 	return 0;
654ec6859b2STodor Tomov }
655ec6859b2STodor Tomov 
656ec6859b2STodor Tomov static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
657ec6859b2STodor Tomov {
658ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
659ec6859b2STodor Tomov 
660ec6859b2STodor Tomov 	return __video_try_fmt(video, f);
661ec6859b2STodor Tomov }
662ec6859b2STodor Tomov 
663ec6859b2STodor Tomov static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
664ec6859b2STodor Tomov {
665ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
666ec6859b2STodor Tomov 	int ret;
667ec6859b2STodor Tomov 
668ec6859b2STodor Tomov 	if (vb2_is_busy(&video->vb2_q))
669ec6859b2STodor Tomov 		return -EBUSY;
670ec6859b2STodor Tomov 
671ec6859b2STodor Tomov 	ret = __video_try_fmt(video, f);
672ec6859b2STodor Tomov 	if (ret < 0)
673ec6859b2STodor Tomov 		return ret;
674ec6859b2STodor Tomov 
675ec6859b2STodor Tomov 	video->active_fmt = *f;
676ec6859b2STodor Tomov 
677ec6859b2STodor Tomov 	return 0;
678ec6859b2STodor Tomov }
679ec6859b2STodor Tomov 
680ec6859b2STodor Tomov static int video_enum_input(struct file *file, void *fh,
681ec6859b2STodor Tomov 			    struct v4l2_input *input)
682ec6859b2STodor Tomov {
683ec6859b2STodor Tomov 	if (input->index > 0)
684ec6859b2STodor Tomov 		return -EINVAL;
685ec6859b2STodor Tomov 
686ec6859b2STodor Tomov 	strlcpy(input->name, "camera", sizeof(input->name));
687ec6859b2STodor Tomov 	input->type = V4L2_INPUT_TYPE_CAMERA;
688ec6859b2STodor Tomov 
689ec6859b2STodor Tomov 	return 0;
690ec6859b2STodor Tomov }
691ec6859b2STodor Tomov 
692ec6859b2STodor Tomov static int video_g_input(struct file *file, void *fh, unsigned int *input)
693ec6859b2STodor Tomov {
694ec6859b2STodor Tomov 	*input = 0;
695ec6859b2STodor Tomov 
696ec6859b2STodor Tomov 	return 0;
697ec6859b2STodor Tomov }
698ec6859b2STodor Tomov 
699ec6859b2STodor Tomov static int video_s_input(struct file *file, void *fh, unsigned int input)
700ec6859b2STodor Tomov {
701ec6859b2STodor Tomov 	return input == 0 ? 0 : -EINVAL;
702ec6859b2STodor Tomov }
703ec6859b2STodor Tomov 
704ec6859b2STodor Tomov static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = {
705ec6859b2STodor Tomov 	.vidioc_querycap		= video_querycap,
706ec6859b2STodor Tomov 	.vidioc_enum_fmt_vid_cap_mplane	= video_enum_fmt,
707ec6859b2STodor Tomov 	.vidioc_g_fmt_vid_cap_mplane	= video_g_fmt,
708ec6859b2STodor Tomov 	.vidioc_s_fmt_vid_cap_mplane	= video_s_fmt,
709ec6859b2STodor Tomov 	.vidioc_try_fmt_vid_cap_mplane	= video_try_fmt,
710ec6859b2STodor Tomov 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
711ec6859b2STodor Tomov 	.vidioc_querybuf		= vb2_ioctl_querybuf,
712ec6859b2STodor Tomov 	.vidioc_qbuf			= vb2_ioctl_qbuf,
713ec6859b2STodor Tomov 	.vidioc_expbuf			= vb2_ioctl_expbuf,
714ec6859b2STodor Tomov 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
715ec6859b2STodor Tomov 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
716ec6859b2STodor Tomov 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
717ec6859b2STodor Tomov 	.vidioc_streamon		= vb2_ioctl_streamon,
718ec6859b2STodor Tomov 	.vidioc_streamoff		= vb2_ioctl_streamoff,
719ec6859b2STodor Tomov 	.vidioc_enum_input		= video_enum_input,
720ec6859b2STodor Tomov 	.vidioc_g_input			= video_g_input,
721ec6859b2STodor Tomov 	.vidioc_s_input			= video_s_input,
722ec6859b2STodor Tomov };
723ec6859b2STodor Tomov 
724ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
725ec6859b2STodor Tomov  * V4L2 file operations
726ec6859b2STodor Tomov  */
727ec6859b2STodor Tomov 
728ec6859b2STodor Tomov static int video_open(struct file *file)
729ec6859b2STodor Tomov {
730ec6859b2STodor Tomov 	struct video_device *vdev = video_devdata(file);
731ec6859b2STodor Tomov 	struct camss_video *video = video_drvdata(file);
732ec6859b2STodor Tomov 	struct v4l2_fh *vfh;
733ec6859b2STodor Tomov 	int ret;
734ec6859b2STodor Tomov 
735ec6859b2STodor Tomov 	mutex_lock(&video->lock);
736ec6859b2STodor Tomov 
737ec6859b2STodor Tomov 	vfh = kzalloc(sizeof(*vfh), GFP_KERNEL);
738ec6859b2STodor Tomov 	if (vfh == NULL) {
739ec6859b2STodor Tomov 		ret = -ENOMEM;
740ec6859b2STodor Tomov 		goto error_alloc;
741ec6859b2STodor Tomov 	}
742ec6859b2STodor Tomov 
743ec6859b2STodor Tomov 	v4l2_fh_init(vfh, vdev);
744ec6859b2STodor Tomov 	v4l2_fh_add(vfh);
745ec6859b2STodor Tomov 
746ec6859b2STodor Tomov 	file->private_data = vfh;
747ec6859b2STodor Tomov 
748ec6859b2STodor Tomov 	ret = v4l2_pipeline_pm_use(&vdev->entity, 1);
749ec6859b2STodor Tomov 	if (ret < 0) {
750ec6859b2STodor Tomov 		dev_err(video->camss->dev, "Failed to power up pipeline: %d\n",
751ec6859b2STodor Tomov 			ret);
752ec6859b2STodor Tomov 		goto error_pm_use;
753ec6859b2STodor Tomov 	}
754ec6859b2STodor Tomov 
755ec6859b2STodor Tomov 	mutex_unlock(&video->lock);
756ec6859b2STodor Tomov 
757ec6859b2STodor Tomov 	return 0;
758ec6859b2STodor Tomov 
759ec6859b2STodor Tomov error_pm_use:
760ec6859b2STodor Tomov 	v4l2_fh_release(file);
761ec6859b2STodor Tomov 
762ec6859b2STodor Tomov error_alloc:
763ec6859b2STodor Tomov 	mutex_unlock(&video->lock);
764ec6859b2STodor Tomov 
765ec6859b2STodor Tomov 	return ret;
766ec6859b2STodor Tomov }
767ec6859b2STodor Tomov 
768ec6859b2STodor Tomov static int video_release(struct file *file)
769ec6859b2STodor Tomov {
770ec6859b2STodor Tomov 	struct video_device *vdev = video_devdata(file);
771ec6859b2STodor Tomov 
772ec6859b2STodor Tomov 	vb2_fop_release(file);
773ec6859b2STodor Tomov 
774ec6859b2STodor Tomov 	v4l2_pipeline_pm_use(&vdev->entity, 0);
775ec6859b2STodor Tomov 
776ec6859b2STodor Tomov 	file->private_data = NULL;
777ec6859b2STodor Tomov 
778ec6859b2STodor Tomov 	return 0;
779ec6859b2STodor Tomov }
780ec6859b2STodor Tomov 
781ec6859b2STodor Tomov static const struct v4l2_file_operations msm_vid_fops = {
782ec6859b2STodor Tomov 	.owner          = THIS_MODULE,
783ec6859b2STodor Tomov 	.unlocked_ioctl = video_ioctl2,
784ec6859b2STodor Tomov 	.open           = video_open,
785ec6859b2STodor Tomov 	.release        = video_release,
786ec6859b2STodor Tomov 	.poll           = vb2_fop_poll,
787ec6859b2STodor Tomov 	.mmap		= vb2_fop_mmap,
788ec6859b2STodor Tomov 	.read		= vb2_fop_read,
789ec6859b2STodor Tomov };
790ec6859b2STodor Tomov 
791ec6859b2STodor Tomov /* -----------------------------------------------------------------------------
792ec6859b2STodor Tomov  * CAMSS video core
793ec6859b2STodor Tomov  */
794ec6859b2STodor Tomov 
795ec6859b2STodor Tomov static void msm_video_release(struct video_device *vdev)
796ec6859b2STodor Tomov {
797ec6859b2STodor Tomov 	struct camss_video *video = video_get_drvdata(vdev);
798ec6859b2STodor Tomov 
799ec6859b2STodor Tomov 	media_entity_cleanup(&vdev->entity);
800ec6859b2STodor Tomov 
801ec6859b2STodor Tomov 	mutex_destroy(&video->q_lock);
802ec6859b2STodor Tomov 	mutex_destroy(&video->lock);
803ec6859b2STodor Tomov 
804ec6859b2STodor Tomov 	if (atomic_dec_and_test(&video->camss->ref_count))
805ec6859b2STodor Tomov 		camss_delete(video->camss);
806ec6859b2STodor Tomov }
807ec6859b2STodor Tomov 
808ec6859b2STodor Tomov /*
809ec6859b2STodor Tomov  * msm_video_init_format - Helper function to initialize format
810ec6859b2STodor Tomov  * @video: struct camss_video
811ec6859b2STodor Tomov  *
812ec6859b2STodor Tomov  * Initialize pad format with default value.
813ec6859b2STodor Tomov  *
814ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
815ec6859b2STodor Tomov  */
816ec6859b2STodor Tomov static int msm_video_init_format(struct camss_video *video)
817ec6859b2STodor Tomov {
818ec6859b2STodor Tomov 	int ret;
819ec6859b2STodor Tomov 	struct v4l2_format format = {
820ec6859b2STodor Tomov 		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
821ec6859b2STodor Tomov 		.fmt.pix_mp = {
822ec6859b2STodor Tomov 			.width = 1920,
823ec6859b2STodor Tomov 			.height = 1080,
824ec6859b2STodor Tomov 			.pixelformat = video->formats[0].pixelformat,
825ec6859b2STodor Tomov 		},
826ec6859b2STodor Tomov 	};
827ec6859b2STodor Tomov 
828ec6859b2STodor Tomov 	ret = __video_try_fmt(video, &format);
829ec6859b2STodor Tomov 	if (ret < 0)
830ec6859b2STodor Tomov 		return ret;
831ec6859b2STodor Tomov 
832ec6859b2STodor Tomov 	video->active_fmt = format;
833ec6859b2STodor Tomov 
834ec6859b2STodor Tomov 	return 0;
835ec6859b2STodor Tomov }
836ec6859b2STodor Tomov 
837ec6859b2STodor Tomov /*
838ec6859b2STodor Tomov  * msm_video_register - Register a video device node
839ec6859b2STodor Tomov  * @video: struct camss_video
840ec6859b2STodor Tomov  * @v4l2_dev: V4L2 device
841ec6859b2STodor Tomov  * @name: name to be used for the video device node
842ec6859b2STodor Tomov  *
843ec6859b2STodor Tomov  * Initialize and register a video device node to a V4L2 device. Also
844ec6859b2STodor Tomov  * initialize the vb2 queue.
845ec6859b2STodor Tomov  *
846ec6859b2STodor Tomov  * Return 0 on success or a negative error code otherwise
847ec6859b2STodor Tomov  */
848ec6859b2STodor Tomov 
849ec6859b2STodor Tomov int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
850ec6859b2STodor Tomov 		       const char *name, int is_pix)
851ec6859b2STodor Tomov {
852ec6859b2STodor Tomov 	struct media_pad *pad = &video->pad;
853ec6859b2STodor Tomov 	struct video_device *vdev;
854ec6859b2STodor Tomov 	struct vb2_queue *q;
855ec6859b2STodor Tomov 	int ret;
856ec6859b2STodor Tomov 
857ec6859b2STodor Tomov 	vdev = &video->vdev;
858ec6859b2STodor Tomov 
859ec6859b2STodor Tomov 	mutex_init(&video->q_lock);
860ec6859b2STodor Tomov 
861ec6859b2STodor Tomov 	q = &video->vb2_q;
862ec6859b2STodor Tomov 	q->drv_priv = video;
863ec6859b2STodor Tomov 	q->mem_ops = &vb2_dma_sg_memops;
864ec6859b2STodor Tomov 	q->ops = &msm_video_vb2_q_ops;
865ec6859b2STodor Tomov 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
866ec6859b2STodor Tomov 	q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ;
867ec6859b2STodor Tomov 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
868ec6859b2STodor Tomov 	q->buf_struct_size = sizeof(struct camss_buffer);
869ec6859b2STodor Tomov 	q->dev = video->camss->dev;
870ec6859b2STodor Tomov 	q->lock = &video->q_lock;
871ec6859b2STodor Tomov 	ret = vb2_queue_init(q);
872ec6859b2STodor Tomov 	if (ret < 0) {
873ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret);
874ec6859b2STodor Tomov 		goto error_vb2_init;
875ec6859b2STodor Tomov 	}
876ec6859b2STodor Tomov 
877ec6859b2STodor Tomov 	pad->flags = MEDIA_PAD_FL_SINK;
878ec6859b2STodor Tomov 	ret = media_entity_pads_init(&vdev->entity, 1, pad);
879ec6859b2STodor Tomov 	if (ret < 0) {
880ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n",
881ec6859b2STodor Tomov 			ret);
882ec6859b2STodor Tomov 		goto error_media_init;
883ec6859b2STodor Tomov 	}
884ec6859b2STodor Tomov 
885ec6859b2STodor Tomov 	mutex_init(&video->lock);
886ec6859b2STodor Tomov 
887cba3819dSTodor Tomov 	if (video->camss->version == CAMSS_8x16) {
888ec6859b2STodor Tomov 		if (is_pix) {
889cba3819dSTodor Tomov 			video->formats = formats_pix_8x16;
890cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_pix_8x16);
891cba3819dSTodor Tomov 		} else {
892cba3819dSTodor Tomov 			video->formats = formats_rdi_8x16;
893cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_rdi_8x16);
894cba3819dSTodor Tomov 		}
895cba3819dSTodor Tomov 	} else if (video->camss->version == CAMSS_8x96) {
896cba3819dSTodor Tomov 		if (is_pix) {
897cba3819dSTodor Tomov 			video->formats = formats_pix_8x96;
898cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_pix_8x96);
899cba3819dSTodor Tomov 		} else {
900cba3819dSTodor Tomov 			video->formats = formats_rdi_8x96;
901cba3819dSTodor Tomov 			video->nformats = ARRAY_SIZE(formats_rdi_8x96);
902cba3819dSTodor Tomov 		}
903cba3819dSTodor Tomov 	} else {
904cba3819dSTodor Tomov 		goto error_video_register;
905ec6859b2STodor Tomov 	}
906ec6859b2STodor Tomov 
907ec6859b2STodor Tomov 	ret = msm_video_init_format(video);
908ec6859b2STodor Tomov 	if (ret < 0) {
909ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret);
910ec6859b2STodor Tomov 		goto error_video_register;
911ec6859b2STodor Tomov 	}
912ec6859b2STodor Tomov 
913ec6859b2STodor Tomov 	vdev->fops = &msm_vid_fops;
914ec6859b2STodor Tomov 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING |
915ec6859b2STodor Tomov 							V4L2_CAP_READWRITE;
916ec6859b2STodor Tomov 	vdev->ioctl_ops = &msm_vid_ioctl_ops;
917ec6859b2STodor Tomov 	vdev->release = msm_video_release;
918ec6859b2STodor Tomov 	vdev->v4l2_dev = v4l2_dev;
919ec6859b2STodor Tomov 	vdev->vfl_dir = VFL_DIR_RX;
920ec6859b2STodor Tomov 	vdev->queue = &video->vb2_q;
921ec6859b2STodor Tomov 	vdev->lock = &video->lock;
922ec6859b2STodor Tomov 	strlcpy(vdev->name, name, sizeof(vdev->name));
923ec6859b2STodor Tomov 
924ec6859b2STodor Tomov 	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
925ec6859b2STodor Tomov 	if (ret < 0) {
926ec6859b2STodor Tomov 		dev_err(v4l2_dev->dev, "Failed to register video device: %d\n",
927ec6859b2STodor Tomov 			ret);
928ec6859b2STodor Tomov 		goto error_video_register;
929ec6859b2STodor Tomov 	}
930ec6859b2STodor Tomov 
931ec6859b2STodor Tomov 	video_set_drvdata(vdev, video);
932ec6859b2STodor Tomov 	atomic_inc(&video->camss->ref_count);
933ec6859b2STodor Tomov 
934ec6859b2STodor Tomov 	return 0;
935ec6859b2STodor Tomov 
936ec6859b2STodor Tomov error_video_register:
937ec6859b2STodor Tomov 	media_entity_cleanup(&vdev->entity);
938ec6859b2STodor Tomov 	mutex_destroy(&video->lock);
939ec6859b2STodor Tomov error_media_init:
940ec6859b2STodor Tomov 	vb2_queue_release(&video->vb2_q);
941ec6859b2STodor Tomov error_vb2_init:
942ec6859b2STodor Tomov 	mutex_destroy(&video->q_lock);
943ec6859b2STodor Tomov 
944ec6859b2STodor Tomov 	return ret;
945ec6859b2STodor Tomov }
946ec6859b2STodor Tomov 
947ec6859b2STodor Tomov void msm_video_stop_streaming(struct camss_video *video)
948ec6859b2STodor Tomov {
949ec6859b2STodor Tomov 	if (vb2_is_streaming(&video->vb2_q))
950ec6859b2STodor Tomov 		vb2_queue_release(&video->vb2_q);
951ec6859b2STodor Tomov }
952ec6859b2STodor Tomov 
953ec6859b2STodor Tomov void msm_video_unregister(struct camss_video *video)
954ec6859b2STodor Tomov {
955ec6859b2STodor Tomov 	atomic_inc(&video->camss->ref_count);
956ec6859b2STodor Tomov 	video_unregister_device(&video->vdev);
957ec6859b2STodor Tomov 	atomic_dec(&video->camss->ref_count);
958ec6859b2STodor Tomov }
959