Commit | Line | Data |
---|---|---|
aa7b8278 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
f00add96 NS |
2 | /* |
3 | * Driver for Renesas R-Car VIN | |
4 | * | |
5 | * Copyright (C) 2016 Renesas Electronics Corp. | |
6 | * Copyright (C) 2011-2013 Renesas Solutions Corp. | |
7 | * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> | |
8 | * Copyright (C) 2008 Magnus Damm | |
9 | * | |
10 | * Based on the soc-camera rcar_vin driver | |
f00add96 NS |
11 | */ |
12 | ||
13 | #include <linux/delay.h> | |
14 | #include <linux/interrupt.h> | |
90dedce9 | 15 | #include <linux/pm_runtime.h> |
f00add96 NS |
16 | |
17 | #include <media/videobuf2-dma-contig.h> | |
18 | ||
19 | #include "rcar-vin.h" | |
20 | ||
21 | /* ----------------------------------------------------------------------------- | |
22 | * HW Functions | |
23 | */ | |
24 | ||
25 | /* Register offsets for R-Car VIN */ | |
26 | #define VNMC_REG 0x00 /* Video n Main Control Register */ | |
27 | #define VNMS_REG 0x04 /* Video n Module Status Register */ | |
28 | #define VNFC_REG 0x08 /* Video n Frame Capture Register */ | |
29 | #define VNSLPRC_REG 0x0C /* Video n Start Line Pre-Clip Register */ | |
30 | #define VNELPRC_REG 0x10 /* Video n End Line Pre-Clip Register */ | |
31 | #define VNSPPRC_REG 0x14 /* Video n Start Pixel Pre-Clip Register */ | |
32 | #define VNEPPRC_REG 0x18 /* Video n End Pixel Pre-Clip Register */ | |
f00add96 NS |
33 | #define VNIS_REG 0x2C /* Video n Image Stride Register */ |
34 | #define VNMB_REG(m) (0x30 + ((m) << 2)) /* Video n Memory Base m Register */ | |
35 | #define VNIE_REG 0x40 /* Video n Interrupt Enable Register */ | |
36 | #define VNINTS_REG 0x44 /* Video n Interrupt Status Register */ | |
37 | #define VNSI_REG 0x48 /* Video n Scanline Interrupt Register */ | |
38 | #define VNMTC_REG 0x4C /* Video n Memory Transfer Control Register */ | |
f00add96 NS |
39 | #define VNDMR_REG 0x58 /* Video n Data Mode Register */ |
40 | #define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */ | |
41 | #define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */ | |
4394eb24 NS |
42 | |
43 | /* Register offsets specific for Gen2 */ | |
44 | #define VNSLPOC_REG 0x1C /* Video n Start Line Post-Clip Register */ | |
45 | #define VNELPOC_REG 0x20 /* Video n End Line Post-Clip Register */ | |
46 | #define VNSPPOC_REG 0x24 /* Video n Start Pixel Post-Clip Register */ | |
47 | #define VNEPPOC_REG 0x28 /* Video n End Pixel Post-Clip Register */ | |
48 | #define VNYS_REG 0x50 /* Video n Y Scale Register */ | |
49 | #define VNXS_REG 0x54 /* Video n X Scale Register */ | |
f00add96 NS |
50 | #define VNC1A_REG 0x80 /* Video n Coefficient Set C1A Register */ |
51 | #define VNC1B_REG 0x84 /* Video n Coefficient Set C1B Register */ | |
52 | #define VNC1C_REG 0x88 /* Video n Coefficient Set C1C Register */ | |
53 | #define VNC2A_REG 0x90 /* Video n Coefficient Set C2A Register */ | |
54 | #define VNC2B_REG 0x94 /* Video n Coefficient Set C2B Register */ | |
55 | #define VNC2C_REG 0x98 /* Video n Coefficient Set C2C Register */ | |
56 | #define VNC3A_REG 0xA0 /* Video n Coefficient Set C3A Register */ | |
57 | #define VNC3B_REG 0xA4 /* Video n Coefficient Set C3B Register */ | |
58 | #define VNC3C_REG 0xA8 /* Video n Coefficient Set C3C Register */ | |
59 | #define VNC4A_REG 0xB0 /* Video n Coefficient Set C4A Register */ | |
60 | #define VNC4B_REG 0xB4 /* Video n Coefficient Set C4B Register */ | |
61 | #define VNC4C_REG 0xB8 /* Video n Coefficient Set C4C Register */ | |
62 | #define VNC5A_REG 0xC0 /* Video n Coefficient Set C5A Register */ | |
63 | #define VNC5B_REG 0xC4 /* Video n Coefficient Set C5B Register */ | |
64 | #define VNC5C_REG 0xC8 /* Video n Coefficient Set C5C Register */ | |
65 | #define VNC6A_REG 0xD0 /* Video n Coefficient Set C6A Register */ | |
66 | #define VNC6B_REG 0xD4 /* Video n Coefficient Set C6B Register */ | |
67 | #define VNC6C_REG 0xD8 /* Video n Coefficient Set C6C Register */ | |
68 | #define VNC7A_REG 0xE0 /* Video n Coefficient Set C7A Register */ | |
69 | #define VNC7B_REG 0xE4 /* Video n Coefficient Set C7B Register */ | |
70 | #define VNC7C_REG 0xE8 /* Video n Coefficient Set C7C Register */ | |
71 | #define VNC8A_REG 0xF0 /* Video n Coefficient Set C8A Register */ | |
72 | #define VNC8B_REG 0xF4 /* Video n Coefficient Set C8B Register */ | |
73 | #define VNC8C_REG 0xF8 /* Video n Coefficient Set C8C Register */ | |
74 | ||
4394eb24 NS |
75 | /* Register offsets specific for Gen3 */ |
76 | #define VNCSI_IFMD_REG 0x20 /* Video n CSI2 Interface Mode Register */ | |
928a6ea4 NS |
77 | #define VNUDS_CTRL_REG 0x80 /* Video n scaling control register */ |
78 | #define VNUDS_SCALE_REG 0x84 /* Video n scaling factor register */ | |
79 | #define VNUDS_PASS_BWIDTH_REG 0x90 /* Video n passband register */ | |
80 | #define VNUDS_CLIP_SIZE_REG 0xa4 /* Video n UDS output size clipping reg */ | |
f00add96 NS |
81 | |
82 | /* Register bit fields for R-Car VIN */ | |
83 | /* Video n Main Control Register bits */ | |
78b3f9d7 | 84 | #define VNMC_INF_MASK (7 << 16) |
4394eb24 NS |
85 | #define VNMC_DPINE (1 << 27) /* Gen3 specific */ |
86 | #define VNMC_SCLE (1 << 26) /* Gen3 specific */ | |
f00add96 NS |
87 | #define VNMC_FOC (1 << 21) |
88 | #define VNMC_YCAL (1 << 19) | |
89 | #define VNMC_INF_YUV8_BT656 (0 << 16) | |
90 | #define VNMC_INF_YUV8_BT601 (1 << 16) | |
91 | #define VNMC_INF_YUV10_BT656 (2 << 16) | |
92 | #define VNMC_INF_YUV10_BT601 (3 << 16) | |
e87c1a81 | 93 | #define VNMC_INF_RAW8 (4 << 16) |
f00add96 NS |
94 | #define VNMC_INF_YUV16 (5 << 16) |
95 | #define VNMC_INF_RGB888 (6 << 16) | |
78b3f9d7 | 96 | #define VNMC_INF_RGB666 (7 << 16) |
f00add96 NS |
97 | #define VNMC_VUP (1 << 10) |
98 | #define VNMC_IM_ODD (0 << 3) | |
99 | #define VNMC_IM_ODD_EVEN (1 << 3) | |
100 | #define VNMC_IM_EVEN (2 << 3) | |
101 | #define VNMC_IM_FULL (3 << 3) | |
102 | #define VNMC_BPS (1 << 1) | |
103 | #define VNMC_ME (1 << 0) | |
104 | ||
105 | /* Video n Module Status Register bits */ | |
106 | #define VNMS_FBS_MASK (3 << 3) | |
107 | #define VNMS_FBS_SHIFT 3 | |
b6f556cb | 108 | #define VNMS_FS (1 << 2) |
f00add96 NS |
109 | #define VNMS_AV (1 << 1) |
110 | #define VNMS_CA (1 << 0) | |
111 | ||
112 | /* Video n Frame Capture Register bits */ | |
113 | #define VNFC_C_FRAME (1 << 1) | |
114 | #define VNFC_S_FRAME (1 << 0) | |
115 | ||
116 | /* Video n Interrupt Enable Register bits */ | |
117 | #define VNIE_FIE (1 << 4) | |
118 | #define VNIE_EFE (1 << 1) | |
119 | ||
30334d3d NS |
120 | /* Video n Interrupt Status Register bits */ |
121 | #define VNINTS_FIS (1 << 4) | |
122 | ||
f00add96 | 123 | /* Video n Data Mode Register bits */ |
1d99e68c NS |
124 | #define VNDMR_A8BIT(n) (((n) & 0xff) << 24) |
125 | #define VNDMR_A8BIT_MASK (0xff << 24) | |
c93beb52 | 126 | #define VNDMR_YMODE_Y8 (1 << 12) |
f00add96 NS |
127 | #define VNDMR_EXRGB (1 << 8) |
128 | #define VNDMR_BPSM (1 << 4) | |
1d99e68c | 129 | #define VNDMR_ABIT (1 << 2) |
f00add96 | 130 | #define VNDMR_DTMD_YCSEP (1 << 1) |
19ab1f64 | 131 | #define VNDMR_DTMD_ARGB (1 << 0) |
9b744a3e | 132 | #define VNDMR_DTMD_YCSEP_420 (3 << 0) |
f00add96 NS |
133 | |
134 | /* Video n Data Mode Register 2 bits */ | |
135 | #define VNDMR2_VPS (1 << 30) | |
136 | #define VNDMR2_HPS (1 << 29) | |
53cf3100 | 137 | #define VNDMR2_CES (1 << 28) |
e8834943 | 138 | #define VNDMR2_YDS (1 << 22) |
f00add96 NS |
139 | #define VNDMR2_FTEV (1 << 17) |
140 | #define VNDMR2_VLV(n) ((n & 0xf) << 12) | |
141 | ||
4394eb24 NS |
142 | /* Video n CSI2 Interface Mode Register (Gen3) */ |
143 | #define VNCSI_IFMD_DES1 (1 << 26) | |
144 | #define VNCSI_IFMD_DES0 (1 << 25) | |
145 | #define VNCSI_IFMD_CSI_CHSEL(n) (((n) & 0xf) << 0) | |
4394eb24 | 146 | |
928a6ea4 NS |
147 | /* Video n scaling control register (Gen3) */ |
148 | #define VNUDS_CTRL_AMD (1 << 30) | |
149 | ||
9e921447 NS |
150 | struct rvin_buffer { |
151 | struct vb2_v4l2_buffer vb; | |
152 | struct list_head list; | |
153 | }; | |
154 | ||
155 | #define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \ | |
156 | struct rvin_buffer, \ | |
157 | vb)->list) | |
158 | ||
f00add96 NS |
159 | static void rvin_write(struct rvin_dev *vin, u32 value, u32 offset) |
160 | { | |
161 | iowrite32(value, vin->base + offset); | |
162 | } | |
163 | ||
164 | static u32 rvin_read(struct rvin_dev *vin, u32 offset) | |
165 | { | |
166 | return ioread32(vin->base + offset); | |
167 | } | |
168 | ||
f00add96 | 169 | /* ----------------------------------------------------------------------------- |
3ad69c61 | 170 | * Crop and Scaling |
f00add96 NS |
171 | */ |
172 | ||
3ad69c61 NS |
173 | static bool rvin_scaler_needed(const struct rvin_dev *vin) |
174 | { | |
175 | return !(vin->crop.width == vin->format.width && | |
176 | vin->compose.width == vin->format.width && | |
177 | vin->crop.height == vin->format.height && | |
178 | vin->compose.height == vin->format.height); | |
179 | } | |
180 | ||
f00add96 NS |
181 | struct vin_coeff { |
182 | unsigned short xs_value; | |
183 | u32 coeff_set[24]; | |
184 | }; | |
185 | ||
186 | static const struct vin_coeff vin_coeff_set[] = { | |
187 | { 0x0000, { | |
188 | 0x00000000, 0x00000000, 0x00000000, | |
189 | 0x00000000, 0x00000000, 0x00000000, | |
190 | 0x00000000, 0x00000000, 0x00000000, | |
191 | 0x00000000, 0x00000000, 0x00000000, | |
192 | 0x00000000, 0x00000000, 0x00000000, | |
193 | 0x00000000, 0x00000000, 0x00000000, | |
194 | 0x00000000, 0x00000000, 0x00000000, | |
195 | 0x00000000, 0x00000000, 0x00000000 }, | |
196 | }, | |
197 | { 0x1000, { | |
198 | 0x000fa400, 0x000fa400, 0x09625902, | |
199 | 0x000003f8, 0x00000403, 0x3de0d9f0, | |
200 | 0x001fffed, 0x00000804, 0x3cc1f9c3, | |
201 | 0x001003de, 0x00000c01, 0x3cb34d7f, | |
202 | 0x002003d2, 0x00000c00, 0x3d24a92d, | |
203 | 0x00200bca, 0x00000bff, 0x3df600d2, | |
204 | 0x002013cc, 0x000007ff, 0x3ed70c7e, | |
205 | 0x00100fde, 0x00000000, 0x3f87c036 }, | |
206 | }, | |
207 | { 0x1200, { | |
208 | 0x002ffff1, 0x002ffff1, 0x02a0a9c8, | |
209 | 0x002003e7, 0x001ffffa, 0x000185bc, | |
210 | 0x002007dc, 0x000003ff, 0x3e52859c, | |
211 | 0x00200bd4, 0x00000002, 0x3d53996b, | |
212 | 0x00100fd0, 0x00000403, 0x3d04ad2d, | |
213 | 0x00000bd5, 0x00000403, 0x3d35ace7, | |
214 | 0x3ff003e4, 0x00000801, 0x3dc674a1, | |
215 | 0x3fffe800, 0x00000800, 0x3e76f461 }, | |
216 | }, | |
217 | { 0x1400, { | |
218 | 0x00100be3, 0x00100be3, 0x04d1359a, | |
219 | 0x00000fdb, 0x002003ed, 0x0211fd93, | |
220 | 0x00000fd6, 0x002003f4, 0x0002d97b, | |
221 | 0x000007d6, 0x002ffffb, 0x3e93b956, | |
222 | 0x3ff003da, 0x001003ff, 0x3db49926, | |
223 | 0x3fffefe9, 0x00100001, 0x3d655cee, | |
224 | 0x3fffd400, 0x00000003, 0x3d65f4b6, | |
225 | 0x000fb421, 0x00000402, 0x3dc6547e }, | |
226 | }, | |
227 | { 0x1600, { | |
228 | 0x00000bdd, 0x00000bdd, 0x06519578, | |
229 | 0x3ff007da, 0x00000be3, 0x03c24973, | |
230 | 0x3ff003d9, 0x00000be9, 0x01b30d5f, | |
231 | 0x3ffff7df, 0x001003f1, 0x0003c542, | |
232 | 0x000fdfec, 0x001003f7, 0x3ec4711d, | |
233 | 0x000fc400, 0x002ffffd, 0x3df504f1, | |
234 | 0x001fa81a, 0x002ffc00, 0x3d957cc2, | |
235 | 0x002f8c3c, 0x00100000, 0x3db5c891 }, | |
236 | }, | |
237 | { 0x1800, { | |
238 | 0x3ff003dc, 0x3ff003dc, 0x0791e558, | |
239 | 0x000ff7dd, 0x3ff007de, 0x05328554, | |
240 | 0x000fe7e3, 0x3ff00be2, 0x03232546, | |
241 | 0x000fd7ee, 0x000007e9, 0x0143bd30, | |
242 | 0x001fb800, 0x000007ee, 0x00044511, | |
243 | 0x002fa015, 0x000007f4, 0x3ef4bcee, | |
244 | 0x002f8832, 0x001003f9, 0x3e4514c7, | |
245 | 0x001f7853, 0x001003fd, 0x3de54c9f }, | |
246 | }, | |
247 | { 0x1a00, { | |
248 | 0x000fefe0, 0x000fefe0, 0x08721d3c, | |
249 | 0x001fdbe7, 0x000ffbde, 0x0652a139, | |
250 | 0x001fcbf0, 0x000003df, 0x0463292e, | |
251 | 0x002fb3ff, 0x3ff007e3, 0x0293a91d, | |
252 | 0x002f9c12, 0x3ff00be7, 0x01241905, | |
253 | 0x001f8c29, 0x000007ed, 0x3fe470eb, | |
254 | 0x000f7c46, 0x000007f2, 0x3f04b8ca, | |
255 | 0x3fef7865, 0x000007f6, 0x3e74e4a8 }, | |
256 | }, | |
257 | { 0x1c00, { | |
258 | 0x001fd3e9, 0x001fd3e9, 0x08f23d26, | |
259 | 0x002fbff3, 0x001fe3e4, 0x0712ad23, | |
260 | 0x002fa800, 0x000ff3e0, 0x05631d1b, | |
261 | 0x001f9810, 0x000ffbe1, 0x03b3890d, | |
262 | 0x000f8c23, 0x000003e3, 0x0233e8fa, | |
263 | 0x3fef843b, 0x000003e7, 0x00f430e4, | |
264 | 0x3fbf8456, 0x3ff00bea, 0x00046cc8, | |
265 | 0x3f8f8c72, 0x3ff00bef, 0x3f3490ac }, | |
266 | }, | |
267 | { 0x1e00, { | |
268 | 0x001fbbf4, 0x001fbbf4, 0x09425112, | |
269 | 0x001fa800, 0x002fc7ed, 0x0792b110, | |
270 | 0x000f980e, 0x001fdbe6, 0x0613110a, | |
271 | 0x3fff8c20, 0x001fe7e3, 0x04a368fd, | |
272 | 0x3fcf8c33, 0x000ff7e2, 0x0343b8ed, | |
273 | 0x3f9f8c4a, 0x000fffe3, 0x0203f8da, | |
274 | 0x3f5f9c61, 0x000003e6, 0x00e428c5, | |
275 | 0x3f1fb07b, 0x000003eb, 0x3fe440af }, | |
276 | }, | |
277 | { 0x2000, { | |
278 | 0x000fa400, 0x000fa400, 0x09625902, | |
279 | 0x3fff980c, 0x001fb7f5, 0x0812b0ff, | |
280 | 0x3fdf901c, 0x001fc7ed, 0x06b2fcfa, | |
281 | 0x3faf902d, 0x001fd3e8, 0x055348f1, | |
282 | 0x3f7f983f, 0x001fe3e5, 0x04038ce3, | |
283 | 0x3f3fa454, 0x001fefe3, 0x02e3c8d1, | |
284 | 0x3f0fb86a, 0x001ff7e4, 0x01c3e8c0, | |
285 | 0x3ecfd880, 0x000fffe6, 0x00c404ac }, | |
286 | }, | |
287 | { 0x2200, { | |
288 | 0x3fdf9c0b, 0x3fdf9c0b, 0x09725cf4, | |
289 | 0x3fbf9818, 0x3fffa400, 0x0842a8f1, | |
290 | 0x3f8f9827, 0x000fb3f7, 0x0702f0ec, | |
291 | 0x3f5fa037, 0x000fc3ef, 0x05d330e4, | |
292 | 0x3f2fac49, 0x001fcfea, 0x04a364d9, | |
293 | 0x3effc05c, 0x001fdbe7, 0x038394ca, | |
294 | 0x3ecfdc6f, 0x001fe7e6, 0x0273b0bb, | |
295 | 0x3ea00083, 0x001fefe6, 0x0183c0a9 }, | |
296 | }, | |
297 | { 0x2400, { | |
298 | 0x3f9fa014, 0x3f9fa014, 0x098260e6, | |
299 | 0x3f7f9c23, 0x3fcf9c0a, 0x08629ce5, | |
300 | 0x3f4fa431, 0x3fefa400, 0x0742d8e1, | |
301 | 0x3f1fb440, 0x3fffb3f8, 0x062310d9, | |
302 | 0x3eefc850, 0x000fbbf2, 0x050340d0, | |
303 | 0x3ecfe062, 0x000fcbec, 0x041364c2, | |
304 | 0x3ea00073, 0x001fd3ea, 0x03037cb5, | |
305 | 0x3e902086, 0x001fdfe8, 0x022388a5 }, | |
306 | }, | |
307 | { 0x2600, { | |
308 | 0x3f5fa81e, 0x3f5fa81e, 0x096258da, | |
309 | 0x3f3fac2b, 0x3f8fa412, 0x088290d8, | |
310 | 0x3f0fbc38, 0x3fafa408, 0x0772c8d5, | |
311 | 0x3eefcc47, 0x3fcfa800, 0x0672f4ce, | |
312 | 0x3ecfe456, 0x3fefaffa, 0x05531cc6, | |
313 | 0x3eb00066, 0x3fffbbf3, 0x047334bb, | |
314 | 0x3ea01c77, 0x000fc7ee, 0x039348ae, | |
315 | 0x3ea04486, 0x000fd3eb, 0x02b350a1 }, | |
316 | }, | |
317 | { 0x2800, { | |
318 | 0x3f2fb426, 0x3f2fb426, 0x094250ce, | |
319 | 0x3f0fc032, 0x3f4fac1b, 0x086284cd, | |
320 | 0x3eefd040, 0x3f7fa811, 0x0782acc9, | |
321 | 0x3ecfe84c, 0x3f9fa807, 0x06a2d8c4, | |
322 | 0x3eb0005b, 0x3fbfac00, 0x05b2f4bc, | |
323 | 0x3eb0186a, 0x3fdfb3fa, 0x04c308b4, | |
324 | 0x3eb04077, 0x3fefbbf4, 0x03f31ca8, | |
325 | 0x3ec06884, 0x000fbff2, 0x03031c9e }, | |
326 | }, | |
327 | { 0x2a00, { | |
328 | 0x3f0fc42d, 0x3f0fc42d, 0x090240c4, | |
329 | 0x3eefd439, 0x3f2fb822, 0x08526cc2, | |
330 | 0x3edfe845, 0x3f4fb018, 0x078294bf, | |
331 | 0x3ec00051, 0x3f6fac0f, 0x06b2b4bb, | |
332 | 0x3ec0185f, 0x3f8fac07, 0x05e2ccb4, | |
333 | 0x3ec0386b, 0x3fafac00, 0x0502e8ac, | |
334 | 0x3ed05c77, 0x3fcfb3fb, 0x0432f0a3, | |
335 | 0x3ef08482, 0x3fdfbbf6, 0x0372f898 }, | |
336 | }, | |
337 | { 0x2c00, { | |
338 | 0x3eefdc31, 0x3eefdc31, 0x08e238b8, | |
339 | 0x3edfec3d, 0x3f0fc828, 0x082258b9, | |
340 | 0x3ed00049, 0x3f1fc01e, 0x077278b6, | |
341 | 0x3ed01455, 0x3f3fb815, 0x06c294b2, | |
342 | 0x3ed03460, 0x3f5fb40d, 0x0602acac, | |
343 | 0x3ef0506c, 0x3f7fb006, 0x0542c0a4, | |
344 | 0x3f107476, 0x3f9fb400, 0x0472c89d, | |
345 | 0x3f309c80, 0x3fbfb7fc, 0x03b2cc94 }, | |
346 | }, | |
347 | { 0x2e00, { | |
348 | 0x3eefec37, 0x3eefec37, 0x088220b0, | |
349 | 0x3ee00041, 0x3effdc2d, 0x07f244ae, | |
350 | 0x3ee0144c, 0x3f0fd023, 0x07625cad, | |
351 | 0x3ef02c57, 0x3f1fc81a, 0x06c274a9, | |
352 | 0x3f004861, 0x3f3fbc13, 0x060288a6, | |
353 | 0x3f20686b, 0x3f5fb80c, 0x05529c9e, | |
354 | 0x3f408c74, 0x3f6fb805, 0x04b2ac96, | |
355 | 0x3f80ac7e, 0x3f8fb800, 0x0402ac8e }, | |
356 | }, | |
357 | { 0x3000, { | |
358 | 0x3ef0003a, 0x3ef0003a, 0x084210a6, | |
359 | 0x3ef01045, 0x3effec32, 0x07b228a7, | |
360 | 0x3f00284e, 0x3f0fdc29, 0x073244a4, | |
361 | 0x3f104058, 0x3f0fd420, 0x06a258a2, | |
362 | 0x3f305c62, 0x3f2fc818, 0x0612689d, | |
363 | 0x3f508069, 0x3f3fc011, 0x05728496, | |
364 | 0x3f80a072, 0x3f4fc00a, 0x04d28c90, | |
365 | 0x3fc0c07b, 0x3f6fbc04, 0x04429088 }, | |
366 | }, | |
367 | { 0x3200, { | |
368 | 0x3f00103e, 0x3f00103e, 0x07f1fc9e, | |
369 | 0x3f102447, 0x3f000035, 0x0782149d, | |
370 | 0x3f203c4f, 0x3f0ff02c, 0x07122c9c, | |
371 | 0x3f405458, 0x3f0fe424, 0x06924099, | |
372 | 0x3f607061, 0x3f1fd41d, 0x06024c97, | |
373 | 0x3f909068, 0x3f2fcc16, 0x05726490, | |
374 | 0x3fc0b070, 0x3f3fc80f, 0x04f26c8a, | |
375 | 0x0000d077, 0x3f4fc409, 0x04627484 }, | |
376 | }, | |
377 | { 0x3400, { | |
378 | 0x3f202040, 0x3f202040, 0x07a1e898, | |
379 | 0x3f303449, 0x3f100c38, 0x0741fc98, | |
380 | 0x3f504c50, 0x3f10002f, 0x06e21495, | |
381 | 0x3f706459, 0x3f1ff028, 0x06722492, | |
382 | 0x3fa08060, 0x3f1fe421, 0x05f2348f, | |
383 | 0x3fd09c67, 0x3f1fdc19, 0x05824c89, | |
384 | 0x0000bc6e, 0x3f2fd014, 0x04f25086, | |
385 | 0x0040dc74, 0x3f3fcc0d, 0x04825c7f }, | |
386 | }, | |
387 | { 0x3600, { | |
388 | 0x3f403042, 0x3f403042, 0x0761d890, | |
389 | 0x3f504848, 0x3f301c3b, 0x0701f090, | |
390 | 0x3f805c50, 0x3f200c33, 0x06a2008f, | |
391 | 0x3fa07458, 0x3f10002b, 0x06520c8d, | |
392 | 0x3fd0905e, 0x3f1ff424, 0x05e22089, | |
393 | 0x0000ac65, 0x3f1fe81d, 0x05823483, | |
394 | 0x0030cc6a, 0x3f2fdc18, 0x04f23c81, | |
395 | 0x0080e871, 0x3f2fd412, 0x0482407c }, | |
396 | }, | |
397 | { 0x3800, { | |
398 | 0x3f604043, 0x3f604043, 0x0721c88a, | |
399 | 0x3f80544a, 0x3f502c3c, 0x06d1d88a, | |
400 | 0x3fb06851, 0x3f301c35, 0x0681e889, | |
401 | 0x3fd08456, 0x3f30082f, 0x0611fc88, | |
402 | 0x00009c5d, 0x3f200027, 0x05d20884, | |
403 | 0x0030b863, 0x3f2ff421, 0x05621880, | |
404 | 0x0070d468, 0x3f2fe81b, 0x0502247c, | |
405 | 0x00c0ec6f, 0x3f2fe015, 0x04a22877 }, | |
406 | }, | |
407 | { 0x3a00, { | |
408 | 0x3f904c44, 0x3f904c44, 0x06e1b884, | |
409 | 0x3fb0604a, 0x3f70383e, 0x0691c885, | |
410 | 0x3fe07451, 0x3f502c36, 0x0661d483, | |
411 | 0x00009055, 0x3f401831, 0x0601ec81, | |
412 | 0x0030a85b, 0x3f300c2a, 0x05b1f480, | |
413 | 0x0070c061, 0x3f300024, 0x0562047a, | |
414 | 0x00b0d867, 0x3f3ff41e, 0x05020c77, | |
415 | 0x00f0f46b, 0x3f2fec19, 0x04a21474 }, | |
416 | }, | |
417 | { 0x3c00, { | |
418 | 0x3fb05c43, 0x3fb05c43, 0x06c1b07e, | |
419 | 0x3fe06c4b, 0x3f902c3f, 0x0681c081, | |
420 | 0x0000844f, 0x3f703838, 0x0631cc7d, | |
421 | 0x00309855, 0x3f602433, 0x05d1d47e, | |
422 | 0x0060b459, 0x3f50142e, 0x0581e47b, | |
423 | 0x00a0c85f, 0x3f400828, 0x0531f078, | |
424 | 0x00e0e064, 0x3f300021, 0x0501fc73, | |
425 | 0x00b0fc6a, 0x3f3ff41d, 0x04a20873 }, | |
426 | }, | |
427 | { 0x3e00, { | |
428 | 0x3fe06444, 0x3fe06444, 0x0681a07a, | |
429 | 0x00007849, 0x3fc0503f, 0x0641b07a, | |
430 | 0x0020904d, 0x3fa0403a, 0x05f1c07a, | |
431 | 0x0060a453, 0x3f803034, 0x05c1c878, | |
432 | 0x0090b858, 0x3f70202f, 0x0571d477, | |
433 | 0x00d0d05d, 0x3f501829, 0x0531e073, | |
434 | 0x0110e462, 0x3f500825, 0x04e1e471, | |
435 | 0x01510065, 0x3f40001f, 0x04a1f06d }, | |
436 | }, | |
437 | { 0x4000, { | |
438 | 0x00007044, 0x00007044, 0x06519476, | |
439 | 0x00208448, 0x3fe05c3f, 0x0621a476, | |
440 | 0x0050984d, 0x3fc04c3a, 0x05e1b075, | |
441 | 0x0080ac52, 0x3fa03c35, 0x05a1b875, | |
442 | 0x00c0c056, 0x3f803030, 0x0561c473, | |
443 | 0x0100d45b, 0x3f70202b, 0x0521d46f, | |
444 | 0x0140e860, 0x3f601427, 0x04d1d46e, | |
445 | 0x01810064, 0x3f500822, 0x0491dc6b }, | |
446 | }, | |
447 | { 0x5000, { | |
448 | 0x0110a442, 0x0110a442, 0x0551545e, | |
449 | 0x0140b045, 0x00e0983f, 0x0531585f, | |
450 | 0x0160c047, 0x00c08c3c, 0x0511645e, | |
451 | 0x0190cc4a, 0x00908039, 0x04f1685f, | |
452 | 0x01c0dc4c, 0x00707436, 0x04d1705e, | |
453 | 0x0200e850, 0x00506833, 0x04b1785b, | |
454 | 0x0230f453, 0x00305c30, 0x0491805a, | |
455 | 0x02710056, 0x0010542d, 0x04718059 }, | |
456 | }, | |
457 | { 0x6000, { | |
458 | 0x01c0bc40, 0x01c0bc40, 0x04c13052, | |
459 | 0x01e0c841, 0x01a0b43d, 0x04c13851, | |
460 | 0x0210cc44, 0x0180a83c, 0x04a13453, | |
461 | 0x0230d845, 0x0160a03a, 0x04913c52, | |
462 | 0x0260e047, 0x01409838, 0x04714052, | |
463 | 0x0280ec49, 0x01208c37, 0x04514c50, | |
464 | 0x02b0f44b, 0x01008435, 0x04414c50, | |
465 | 0x02d1004c, 0x00e07c33, 0x0431544f }, | |
466 | }, | |
467 | { 0x7000, { | |
468 | 0x0230c83e, 0x0230c83e, 0x04711c4c, | |
469 | 0x0250d03f, 0x0210c43c, 0x0471204b, | |
470 | 0x0270d840, 0x0200b83c, 0x0451244b, | |
471 | 0x0290dc42, 0x01e0b43a, 0x0441244c, | |
472 | 0x02b0e443, 0x01c0b038, 0x0441284b, | |
473 | 0x02d0ec44, 0x01b0a438, 0x0421304a, | |
474 | 0x02f0f445, 0x0190a036, 0x04213449, | |
475 | 0x0310f847, 0x01709c34, 0x04213848 }, | |
476 | }, | |
477 | { 0x8000, { | |
478 | 0x0280d03d, 0x0280d03d, 0x04310c48, | |
479 | 0x02a0d43e, 0x0270c83c, 0x04311047, | |
480 | 0x02b0dc3e, 0x0250c83a, 0x04311447, | |
481 | 0x02d0e040, 0x0240c03a, 0x04211446, | |
482 | 0x02e0e840, 0x0220bc39, 0x04111847, | |
483 | 0x0300e842, 0x0210b438, 0x04012445, | |
484 | 0x0310f043, 0x0200b037, 0x04012045, | |
485 | 0x0330f444, 0x01e0ac36, 0x03f12445 }, | |
486 | }, | |
487 | { 0xefff, { | |
488 | 0x0340dc3a, 0x0340dc3a, 0x03b0ec40, | |
489 | 0x0340e03a, 0x0330e039, 0x03c0f03e, | |
490 | 0x0350e03b, 0x0330dc39, 0x03c0ec3e, | |
491 | 0x0350e43a, 0x0320dc38, 0x03c0f43e, | |
492 | 0x0360e43b, 0x0320d839, 0x03b0f03e, | |
493 | 0x0360e83b, 0x0310d838, 0x03c0fc3b, | |
494 | 0x0370e83b, 0x0310d439, 0x03a0f83d, | |
495 | 0x0370e83c, 0x0300d438, 0x03b0fc3c }, | |
496 | } | |
497 | }; | |
498 | ||
499 | static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs) | |
500 | { | |
501 | int i; | |
502 | const struct vin_coeff *p_prev_set = NULL; | |
503 | const struct vin_coeff *p_set = NULL; | |
504 | ||
0f4b3378 NS |
505 | /* Look for suitable coefficient values */ |
506 | for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) { | |
507 | p_prev_set = p_set; | |
508 | p_set = &vin_coeff_set[i]; | |
509 | ||
510 | if (xs < p_set->xs_value) | |
511 | break; | |
512 | } | |
513 | ||
514 | /* Use previous value if its XS value is closer */ | |
bf78f23a | 515 | if (p_prev_set && |
0f4b3378 NS |
516 | xs - p_prev_set->xs_value < p_set->xs_value - xs) |
517 | p_set = p_prev_set; | |
518 | ||
519 | /* Set coefficient registers */ | |
520 | rvin_write(vin, p_set->coeff_set[0], VNC1A_REG); | |
521 | rvin_write(vin, p_set->coeff_set[1], VNC1B_REG); | |
522 | rvin_write(vin, p_set->coeff_set[2], VNC1C_REG); | |
523 | ||
524 | rvin_write(vin, p_set->coeff_set[3], VNC2A_REG); | |
525 | rvin_write(vin, p_set->coeff_set[4], VNC2B_REG); | |
526 | rvin_write(vin, p_set->coeff_set[5], VNC2C_REG); | |
527 | ||
528 | rvin_write(vin, p_set->coeff_set[6], VNC3A_REG); | |
529 | rvin_write(vin, p_set->coeff_set[7], VNC3B_REG); | |
530 | rvin_write(vin, p_set->coeff_set[8], VNC3C_REG); | |
531 | ||
532 | rvin_write(vin, p_set->coeff_set[9], VNC4A_REG); | |
533 | rvin_write(vin, p_set->coeff_set[10], VNC4B_REG); | |
534 | rvin_write(vin, p_set->coeff_set[11], VNC4C_REG); | |
535 | ||
536 | rvin_write(vin, p_set->coeff_set[12], VNC5A_REG); | |
537 | rvin_write(vin, p_set->coeff_set[13], VNC5B_REG); | |
538 | rvin_write(vin, p_set->coeff_set[14], VNC5C_REG); | |
539 | ||
540 | rvin_write(vin, p_set->coeff_set[15], VNC6A_REG); | |
541 | rvin_write(vin, p_set->coeff_set[16], VNC6B_REG); | |
542 | rvin_write(vin, p_set->coeff_set[17], VNC6C_REG); | |
543 | ||
544 | rvin_write(vin, p_set->coeff_set[18], VNC7A_REG); | |
545 | rvin_write(vin, p_set->coeff_set[19], VNC7B_REG); | |
546 | rvin_write(vin, p_set->coeff_set[20], VNC7C_REG); | |
547 | ||
548 | rvin_write(vin, p_set->coeff_set[21], VNC8A_REG); | |
549 | rvin_write(vin, p_set->coeff_set[22], VNC8B_REG); | |
550 | rvin_write(vin, p_set->coeff_set[23], VNC8C_REG); | |
551 | } | |
552 | ||
3ad69c61 | 553 | void rvin_scaler_gen2(struct rvin_dev *vin) |
0f4b3378 | 554 | { |
08369321 | 555 | unsigned int crop_height; |
0f4b3378 NS |
556 | u32 xs, ys; |
557 | ||
0f4b3378 | 558 | /* Set scaling coefficient */ |
08369321 | 559 | crop_height = vin->crop.height; |
7e0cfdad | 560 | if (V4L2_FIELD_HAS_BOTH(vin->format.field)) |
08369321 NS |
561 | crop_height *= 2; |
562 | ||
0f4b3378 | 563 | ys = 0; |
08369321 NS |
564 | if (crop_height != vin->compose.height) |
565 | ys = (4096 * crop_height) / vin->compose.height; | |
0f4b3378 NS |
566 | rvin_write(vin, ys, VNYS_REG); |
567 | ||
568 | xs = 0; | |
569 | if (vin->crop.width != vin->compose.width) | |
570 | xs = (4096 * vin->crop.width) / vin->compose.width; | |
571 | ||
572 | /* Horizontal upscaling is up to double size */ | |
573 | if (xs > 0 && xs < 2048) | |
574 | xs = 2048; | |
575 | ||
576 | rvin_write(vin, xs, VNXS_REG); | |
577 | ||
578 | /* Horizontal upscaling is done out by scaling down from double size */ | |
579 | if (xs < 4096) | |
580 | xs *= 2; | |
581 | ||
582 | rvin_set_coeff(vin, xs); | |
583 | ||
584 | /* Set Start/End Pixel/Line Post-Clip */ | |
585 | rvin_write(vin, 0, VNSPPOC_REG); | |
586 | rvin_write(vin, 0, VNSLPOC_REG); | |
587 | rvin_write(vin, vin->format.width - 1, VNEPPOC_REG); | |
d23e12dc | 588 | |
7e0cfdad | 589 | if (V4L2_FIELD_HAS_BOTH(vin->format.field)) |
0f4b3378 | 590 | rvin_write(vin, vin->format.height / 2 - 1, VNELPOC_REG); |
d23e12dc | 591 | else |
0f4b3378 | 592 | rvin_write(vin, vin->format.height - 1, VNELPOC_REG); |
0f4b3378 | 593 | |
0f4b3378 NS |
594 | vin_dbg(vin, |
595 | "Pre-Clip: %ux%u@%u:%u YS: %d XS: %d Post-Clip: %ux%u@%u:%u\n", | |
596 | vin->crop.width, vin->crop.height, vin->crop.left, | |
597 | vin->crop.top, ys, xs, vin->format.width, vin->format.height, | |
598 | 0, 0); | |
599 | } | |
600 | ||
928a6ea4 NS |
601 | static unsigned int rvin_uds_scale_ratio(unsigned int in, unsigned int out) |
602 | { | |
603 | unsigned int ratio; | |
604 | ||
605 | ratio = in * 4096 / out; | |
606 | return ratio >= 0x10000 ? 0xffff : ratio; | |
607 | } | |
608 | ||
609 | static unsigned int rvin_uds_filter_width(unsigned int ratio) | |
610 | { | |
611 | if (ratio >= 0x1000) | |
612 | return 64 * (ratio & 0xf000) / ratio; | |
613 | ||
614 | return 64; | |
615 | } | |
616 | ||
617 | void rvin_scaler_gen3(struct rvin_dev *vin) | |
618 | { | |
619 | unsigned int ratio_h, ratio_v; | |
620 | unsigned int bwidth_h, bwidth_v; | |
621 | u32 vnmc, clip_size; | |
622 | ||
623 | vnmc = rvin_read(vin, VNMC_REG); | |
624 | ||
625 | /* Disable scaler if not needed. */ | |
626 | if (!rvin_scaler_needed(vin)) { | |
627 | rvin_write(vin, vnmc & ~VNMC_SCLE, VNMC_REG); | |
628 | return; | |
629 | } | |
630 | ||
631 | ratio_h = rvin_uds_scale_ratio(vin->crop.width, vin->compose.width); | |
632 | bwidth_h = rvin_uds_filter_width(ratio_h); | |
633 | ||
634 | ratio_v = rvin_uds_scale_ratio(vin->crop.height, vin->compose.height); | |
635 | bwidth_v = rvin_uds_filter_width(ratio_v); | |
636 | ||
637 | clip_size = vin->compose.width << 16; | |
638 | ||
639 | switch (vin->format.field) { | |
640 | case V4L2_FIELD_INTERLACED_TB: | |
641 | case V4L2_FIELD_INTERLACED_BT: | |
642 | case V4L2_FIELD_INTERLACED: | |
643 | case V4L2_FIELD_SEQ_TB: | |
644 | case V4L2_FIELD_SEQ_BT: | |
645 | clip_size |= vin->compose.height / 2; | |
646 | break; | |
647 | default: | |
648 | clip_size |= vin->compose.height; | |
649 | break; | |
650 | } | |
651 | ||
652 | rvin_write(vin, vnmc | VNMC_SCLE, VNMC_REG); | |
653 | rvin_write(vin, VNUDS_CTRL_AMD, VNUDS_CTRL_REG); | |
654 | rvin_write(vin, (ratio_h << 16) | ratio_v, VNUDS_SCALE_REG); | |
655 | rvin_write(vin, (bwidth_h << 16) | bwidth_v, VNUDS_PASS_BWIDTH_REG); | |
656 | rvin_write(vin, clip_size, VNUDS_CLIP_SIZE_REG); | |
657 | ||
658 | vin_dbg(vin, "Pre-Clip: %ux%u@%u:%u Post-Clip: %ux%u@%u:%u\n", | |
659 | vin->crop.width, vin->crop.height, vin->crop.left, | |
660 | vin->crop.top, vin->compose.width, vin->compose.height, | |
661 | vin->compose.left, vin->compose.top); | |
662 | } | |
663 | ||
4394eb24 NS |
664 | void rvin_crop_scale_comp(struct rvin_dev *vin) |
665 | { | |
84246ae3 NS |
666 | const struct rvin_video_format *fmt; |
667 | u32 stride; | |
668 | ||
4394eb24 NS |
669 | /* Set Start/End Pixel/Line Pre-Clip */ |
670 | rvin_write(vin, vin->crop.left, VNSPPRC_REG); | |
671 | rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG); | |
08369321 NS |
672 | rvin_write(vin, vin->crop.top, VNSLPRC_REG); |
673 | rvin_write(vin, vin->crop.top + vin->crop.height - 1, VNELPRC_REG); | |
4394eb24 | 674 | |
3ad69c61 NS |
675 | if (vin->scaler) |
676 | vin->scaler(vin); | |
4394eb24 | 677 | |
84246ae3 NS |
678 | fmt = rvin_format_from_pixel(vin, vin->format.pixelformat); |
679 | stride = vin->format.bytesperline / fmt->bpp; | |
e87c1a81 LP |
680 | |
681 | /* For RAW8 format bpp is 1, but the hardware process RAW8 | |
682 | * format in 2 pixel unit hence configure VNIS_REG as stride / 2. | |
683 | */ | |
8c3e0f67 | 684 | switch (vin->format.pixelformat) { |
811b8d66 LP |
685 | case V4L2_PIX_FMT_SBGGR8: |
686 | case V4L2_PIX_FMT_SGBRG8: | |
687 | case V4L2_PIX_FMT_SGRBG8: | |
688 | case V4L2_PIX_FMT_SRGGB8: | |
c93beb52 | 689 | case V4L2_PIX_FMT_GREY: |
e87c1a81 | 690 | stride /= 2; |
8c3e0f67 NS |
691 | break; |
692 | default: | |
693 | break; | |
694 | } | |
e87c1a81 | 695 | |
84246ae3 | 696 | rvin_write(vin, stride, VNIS_REG); |
4394eb24 NS |
697 | } |
698 | ||
0f4b3378 NS |
699 | /* ----------------------------------------------------------------------------- |
700 | * Hardware setup | |
701 | */ | |
702 | ||
703 | static int rvin_setup(struct rvin_dev *vin) | |
704 | { | |
d73c3357 | 705 | u32 vnmc, dmr, dmr2, interrupts; |
0f4b3378 NS |
706 | bool progressive = false, output_is_yuv = false, input_is_yuv = false; |
707 | ||
708 | switch (vin->format.field) { | |
709 | case V4L2_FIELD_TOP: | |
710 | vnmc = VNMC_IM_ODD; | |
711 | break; | |
712 | case V4L2_FIELD_BOTTOM: | |
713 | vnmc = VNMC_IM_EVEN; | |
714 | break; | |
715 | case V4L2_FIELD_INTERLACED: | |
716 | /* Default to TB */ | |
717 | vnmc = VNMC_IM_FULL; | |
718 | /* Use BT if video standard can be read and is 60 Hz format */ | |
5e7c6236 | 719 | if (!vin->info->use_mc && vin->std & V4L2_STD_525_60) |
4f554bde | 720 | vnmc = VNMC_IM_FULL | VNMC_FOC; |
0f4b3378 NS |
721 | break; |
722 | case V4L2_FIELD_INTERLACED_TB: | |
723 | vnmc = VNMC_IM_FULL; | |
724 | break; | |
725 | case V4L2_FIELD_INTERLACED_BT: | |
726 | vnmc = VNMC_IM_FULL | VNMC_FOC; | |
727 | break; | |
7e0cfdad NS |
728 | case V4L2_FIELD_SEQ_TB: |
729 | case V4L2_FIELD_SEQ_BT: | |
0f4b3378 NS |
730 | case V4L2_FIELD_NONE: |
731 | vnmc = VNMC_IM_ODD_EVEN; | |
732 | progressive = true; | |
733 | break; | |
08369321 NS |
734 | case V4L2_FIELD_ALTERNATE: |
735 | vnmc = VNMC_IM_ODD_EVEN; | |
736 | break; | |
0f4b3378 NS |
737 | default: |
738 | vnmc = VNMC_IM_ODD; | |
739 | break; | |
740 | } | |
741 | ||
742 | /* | |
743 | * Input interface | |
744 | */ | |
c65c99b4 | 745 | switch (vin->mbus_code) { |
0f4b3378 NS |
746 | case MEDIA_BUS_FMT_YUYV8_1X16: |
747 | /* BT.601/BT.1358 16bit YCbCr422 */ | |
748 | vnmc |= VNMC_INF_YUV16; | |
749 | input_is_yuv = true; | |
750 | break; | |
01d72e9d NS |
751 | case MEDIA_BUS_FMT_UYVY8_1X16: |
752 | vnmc |= VNMC_INF_YUV16 | VNMC_YCAL; | |
753 | input_is_yuv = true; | |
754 | break; | |
0f4b3378 NS |
755 | case MEDIA_BUS_FMT_UYVY8_2X8: |
756 | /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ | |
158e2a53 | 757 | if (!vin->is_csi && |
d7592b2e | 758 | vin->parallel.mbus_type == V4L2_MBUS_BT656) |
158e2a53 JM |
759 | vnmc |= VNMC_INF_YUV8_BT656; |
760 | else | |
761 | vnmc |= VNMC_INF_YUV8_BT601; | |
762 | ||
0f4b3378 NS |
763 | input_is_yuv = true; |
764 | break; | |
765 | case MEDIA_BUS_FMT_RGB888_1X24: | |
766 | vnmc |= VNMC_INF_RGB888; | |
767 | break; | |
768 | case MEDIA_BUS_FMT_UYVY10_2X10: | |
769 | /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ | |
158e2a53 | 770 | if (!vin->is_csi && |
d7592b2e | 771 | vin->parallel.mbus_type == V4L2_MBUS_BT656) |
158e2a53 JM |
772 | vnmc |= VNMC_INF_YUV10_BT656; |
773 | else | |
774 | vnmc |= VNMC_INF_YUV10_BT601; | |
775 | ||
0f4b3378 NS |
776 | input_is_yuv = true; |
777 | break; | |
8c3e0f67 NS |
778 | case MEDIA_BUS_FMT_SBGGR8_1X8: |
779 | case MEDIA_BUS_FMT_SGBRG8_1X8: | |
780 | case MEDIA_BUS_FMT_SGRBG8_1X8: | |
e87c1a81 | 781 | case MEDIA_BUS_FMT_SRGGB8_1X8: |
c93beb52 | 782 | case MEDIA_BUS_FMT_Y8_1X8: |
e87c1a81 LP |
783 | vnmc |= VNMC_INF_RAW8; |
784 | break; | |
0f4b3378 NS |
785 | default: |
786 | break; | |
787 | } | |
788 | ||
78b3f9d7 NS |
789 | /* Make sure input interface and input format is valid. */ |
790 | if (vin->info->model == RCAR_GEN3) { | |
791 | switch (vnmc & VNMC_INF_MASK) { | |
792 | case VNMC_INF_YUV8_BT656: | |
793 | case VNMC_INF_YUV10_BT656: | |
794 | case VNMC_INF_YUV16: | |
795 | case VNMC_INF_RGB666: | |
796 | if (vin->is_csi) { | |
797 | vin_err(vin, "Invalid setting in MIPI CSI2\n"); | |
798 | return -EINVAL; | |
799 | } | |
800 | break; | |
801 | case VNMC_INF_RAW8: | |
802 | if (!vin->is_csi) { | |
803 | vin_err(vin, "Invalid setting in Digital Pins\n"); | |
804 | return -EINVAL; | |
805 | } | |
806 | break; | |
807 | default: | |
808 | break; | |
809 | } | |
810 | } | |
811 | ||
8b72c18d | 812 | /* Enable VSYNC Field Toggle mode after one VSYNC input */ |
d73c3357 NS |
813 | if (vin->info->model == RCAR_GEN3) |
814 | dmr2 = VNDMR2_FTEV; | |
815 | else | |
4394eb24 | 816 | dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); |
0f4b3378 | 817 | |
158e2a53 JM |
818 | if (!vin->is_csi) { |
819 | /* Hsync Signal Polarity Select */ | |
d7592b2e | 820 | if (!(vin->parallel.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) |
158e2a53 | 821 | dmr2 |= VNDMR2_HPS; |
0f4b3378 | 822 | |
158e2a53 | 823 | /* Vsync Signal Polarity Select */ |
d7592b2e | 824 | if (!(vin->parallel.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) |
158e2a53 | 825 | dmr2 |= VNDMR2_VPS; |
53cf3100 JM |
826 | |
827 | /* Data Enable Polarity Select */ | |
d7592b2e | 828 | if (vin->parallel.bus.flags & V4L2_MBUS_DATA_ENABLE_LOW) |
53cf3100 | 829 | dmr2 |= VNDMR2_CES; |
e8834943 LP |
830 | |
831 | switch (vin->mbus_code) { | |
832 | case MEDIA_BUS_FMT_UYVY8_2X8: | |
d7592b2e NS |
833 | if (vin->parallel.bus.bus_width == 8 && |
834 | vin->parallel.bus.data_shift == 8) | |
e8834943 LP |
835 | dmr2 |= VNDMR2_YDS; |
836 | break; | |
837 | default: | |
838 | break; | |
839 | } | |
158e2a53 | 840 | } |
f00add96 | 841 | |
0f4b3378 NS |
842 | /* |
843 | * Output format | |
844 | */ | |
845 | switch (vin->format.pixelformat) { | |
9b744a3e | 846 | case V4L2_PIX_FMT_NV12: |
0f4b3378 NS |
847 | case V4L2_PIX_FMT_NV16: |
848 | rvin_write(vin, | |
5c9de1fa NS |
849 | ALIGN(vin->format.bytesperline * vin->format.height, |
850 | 0x80), VNUVAOF_REG); | |
9b744a3e NS |
851 | dmr = vin->format.pixelformat == V4L2_PIX_FMT_NV12 ? |
852 | VNDMR_DTMD_YCSEP_420 : VNDMR_DTMD_YCSEP; | |
0f4b3378 NS |
853 | output_is_yuv = true; |
854 | break; | |
855 | case V4L2_PIX_FMT_YUYV: | |
856 | dmr = VNDMR_BPSM; | |
857 | output_is_yuv = true; | |
858 | break; | |
859 | case V4L2_PIX_FMT_UYVY: | |
860 | dmr = 0; | |
861 | output_is_yuv = true; | |
862 | break; | |
863 | case V4L2_PIX_FMT_XRGB555: | |
19ab1f64 | 864 | dmr = VNDMR_DTMD_ARGB; |
0f4b3378 NS |
865 | break; |
866 | case V4L2_PIX_FMT_RGB565: | |
867 | dmr = 0; | |
868 | break; | |
869 | case V4L2_PIX_FMT_XBGR32: | |
870 | /* Note: not supported on M1 */ | |
871 | dmr = VNDMR_EXRGB; | |
872 | break; | |
1d99e68c NS |
873 | case V4L2_PIX_FMT_ARGB555: |
874 | dmr = (vin->alpha ? VNDMR_ABIT : 0) | VNDMR_DTMD_ARGB; | |
875 | break; | |
876 | case V4L2_PIX_FMT_ABGR32: | |
877 | dmr = VNDMR_A8BIT(vin->alpha) | VNDMR_EXRGB | VNDMR_DTMD_ARGB; | |
878 | break; | |
8c3e0f67 NS |
879 | case V4L2_PIX_FMT_SBGGR8: |
880 | case V4L2_PIX_FMT_SGBRG8: | |
881 | case V4L2_PIX_FMT_SGRBG8: | |
e87c1a81 LP |
882 | case V4L2_PIX_FMT_SRGGB8: |
883 | dmr = 0; | |
884 | break; | |
c93beb52 VB |
885 | case V4L2_PIX_FMT_GREY: |
886 | if (input_is_yuv) { | |
887 | dmr = VNDMR_DTMD_YCSEP | VNDMR_YMODE_Y8; | |
888 | output_is_yuv = true; | |
889 | } else { | |
890 | dmr = 0; | |
891 | } | |
892 | break; | |
0f4b3378 NS |
893 | default: |
894 | vin_err(vin, "Invalid pixelformat (0x%x)\n", | |
895 | vin->format.pixelformat); | |
896 | return -EINVAL; | |
f00add96 NS |
897 | } |
898 | ||
0f4b3378 NS |
899 | /* Always update on field change */ |
900 | vnmc |= VNMC_VUP; | |
f00add96 | 901 | |
406bb586 NS |
902 | if (!vin->info->use_isp) { |
903 | /* If input and output use the same colorspace, use bypass mode */ | |
904 | if (input_is_yuv == output_is_yuv) | |
905 | vnmc |= VNMC_BPS; | |
906 | ||
907 | if (vin->info->model == RCAR_GEN3) { | |
908 | /* Select between CSI-2 and parallel input */ | |
909 | if (vin->is_csi) | |
910 | vnmc &= ~VNMC_DPINE; | |
911 | else | |
912 | vnmc |= VNMC_DPINE; | |
913 | } | |
4394eb24 NS |
914 | } |
915 | ||
0f4b3378 NS |
916 | /* Progressive or interlaced mode */ |
917 | interrupts = progressive ? VNIE_FIE : VNIE_EFE; | |
f00add96 | 918 | |
0f4b3378 NS |
919 | /* Ack interrupts */ |
920 | rvin_write(vin, interrupts, VNINTS_REG); | |
921 | /* Enable interrupts */ | |
922 | rvin_write(vin, interrupts, VNIE_REG); | |
923 | /* Start capturing */ | |
924 | rvin_write(vin, dmr, VNDMR_REG); | |
925 | rvin_write(vin, dmr2, VNDMR2_REG); | |
f00add96 | 926 | |
0f4b3378 NS |
927 | /* Enable module */ |
928 | rvin_write(vin, vnmc | VNMC_ME, VNMC_REG); | |
f00add96 | 929 | |
0f4b3378 NS |
930 | return 0; |
931 | } | |
f00add96 | 932 | |
0f4b3378 NS |
933 | static void rvin_disable_interrupts(struct rvin_dev *vin) |
934 | { | |
935 | rvin_write(vin, 0, VNIE_REG); | |
936 | } | |
f00add96 | 937 | |
0f4b3378 NS |
938 | static u32 rvin_get_interrupt_status(struct rvin_dev *vin) |
939 | { | |
940 | return rvin_read(vin, VNINTS_REG); | |
941 | } | |
f00add96 | 942 | |
0f4b3378 NS |
943 | static void rvin_ack_interrupt(struct rvin_dev *vin) |
944 | { | |
945 | rvin_write(vin, rvin_read(vin, VNINTS_REG), VNINTS_REG); | |
f00add96 NS |
946 | } |
947 | ||
0f4b3378 | 948 | static bool rvin_capture_active(struct rvin_dev *vin) |
f00add96 | 949 | { |
0f4b3378 NS |
950 | return rvin_read(vin, VNMS_REG) & VNMS_CA; |
951 | } | |
f00add96 | 952 | |
08369321 NS |
953 | static enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms) |
954 | { | |
955 | if (vin->format.field == V4L2_FIELD_ALTERNATE) { | |
956 | /* If FS is set it is an Even field. */ | |
957 | if (vnms & VNMS_FS) | |
958 | return V4L2_FIELD_BOTTOM; | |
959 | return V4L2_FIELD_TOP; | |
960 | } | |
961 | ||
962 | return vin->format.field; | |
963 | } | |
964 | ||
0f4b3378 NS |
965 | static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr) |
966 | { | |
967 | const struct rvin_video_format *fmt; | |
968 | int offsetx, offsety; | |
969 | dma_addr_t offset; | |
f00add96 | 970 | |
21a816e7 | 971 | fmt = rvin_format_from_pixel(vin, vin->format.pixelformat); |
f00add96 | 972 | |
0f4b3378 NS |
973 | /* |
974 | * There is no HW support for composition do the beast we can | |
975 | * by modifying the buffer offset | |
976 | */ | |
977 | offsetx = vin->compose.left * fmt->bpp; | |
978 | offsety = vin->compose.top * vin->format.bytesperline; | |
979 | offset = addr + offsetx + offsety; | |
f00add96 | 980 | |
0f4b3378 NS |
981 | /* |
982 | * The address needs to be 128 bytes aligned. Driver should never accept | |
983 | * settings that do not satisfy this in the first place... | |
984 | */ | |
985 | if (WARN_ON((offsetx | offsety | offset) & HW_BUFFER_MASK)) | |
986 | return; | |
f00add96 | 987 | |
0f4b3378 NS |
988 | rvin_write(vin, offset, VNMB_REG(slot)); |
989 | } | |
f00add96 | 990 | |
0f4b3378 NS |
991 | /* |
992 | * Moves a buffer from the queue to the HW slot. If no buffer is | |
993 | * available use the scratch buffer. The scratch buffer is never | |
994 | * returned to userspace, its only function is to enable the capture | |
995 | * loop to keep running. | |
996 | */ | |
997 | static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) | |
998 | { | |
999 | struct rvin_buffer *buf; | |
1000 | struct vb2_v4l2_buffer *vbuf; | |
1001 | dma_addr_t phys_addr; | |
7e0cfdad | 1002 | int prev; |
0f4b3378 NS |
1003 | |
1004 | /* A already populated slot shall never be overwritten. */ | |
e72b7359 | 1005 | if (WARN_ON(vin->buf_hw[slot].buffer)) |
0f4b3378 NS |
1006 | return; |
1007 | ||
7e0cfdad | 1008 | prev = (slot == 0 ? HW_BUFFER_NUM : slot) - 1; |
0f4b3378 | 1009 | |
7e0cfdad NS |
1010 | if (vin->buf_hw[prev].type == HALF_TOP) { |
1011 | vbuf = vin->buf_hw[prev].buffer; | |
1012 | vin->buf_hw[slot].buffer = vbuf; | |
1013 | vin->buf_hw[slot].type = HALF_BOTTOM; | |
1014 | switch (vin->format.pixelformat) { | |
1015 | case V4L2_PIX_FMT_NV12: | |
1016 | case V4L2_PIX_FMT_NV16: | |
1017 | phys_addr = vin->buf_hw[prev].phys + | |
1018 | vin->format.sizeimage / 4; | |
1019 | break; | |
1020 | default: | |
1021 | phys_addr = vin->buf_hw[prev].phys + | |
1022 | vin->format.sizeimage / 2; | |
1023 | break; | |
1024 | } | |
a5991c4e NS |
1025 | } else if ((vin->state != STOPPED && vin->state != RUNNING) || |
1026 | list_empty(&vin->buf_list)) { | |
e72b7359 | 1027 | vin->buf_hw[slot].buffer = NULL; |
7e0cfdad | 1028 | vin->buf_hw[slot].type = FULL; |
0f4b3378 NS |
1029 | phys_addr = vin->scratch_phys; |
1030 | } else { | |
1031 | /* Keep track of buffer we give to HW */ | |
1032 | buf = list_entry(vin->buf_list.next, struct rvin_buffer, list); | |
1033 | vbuf = &buf->vb; | |
1034 | list_del_init(to_buf_list(vbuf)); | |
e72b7359 | 1035 | vin->buf_hw[slot].buffer = vbuf; |
0f4b3378 | 1036 | |
7e0cfdad NS |
1037 | vin->buf_hw[slot].type = |
1038 | V4L2_FIELD_IS_SEQUENTIAL(vin->format.field) ? | |
1039 | HALF_TOP : FULL; | |
1040 | ||
0f4b3378 NS |
1041 | /* Setup DMA */ |
1042 | phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); | |
f00add96 NS |
1043 | } |
1044 | ||
7e0cfdad NS |
1045 | vin_dbg(vin, "Filling HW slot: %d type: %d buffer: %p\n", |
1046 | slot, vin->buf_hw[slot].type, vin->buf_hw[slot].buffer); | |
1047 | ||
1048 | vin->buf_hw[slot].phys = phys_addr; | |
0f4b3378 NS |
1049 | rvin_set_slot_addr(vin, slot, phys_addr); |
1050 | } | |
f00add96 | 1051 | |
0f4b3378 NS |
1052 | static int rvin_capture_start(struct rvin_dev *vin) |
1053 | { | |
1054 | int slot, ret; | |
1055 | ||
7e0cfdad NS |
1056 | for (slot = 0; slot < HW_BUFFER_NUM; slot++) { |
1057 | vin->buf_hw[slot].buffer = NULL; | |
1058 | vin->buf_hw[slot].type = FULL; | |
1059 | } | |
1060 | ||
0f4b3378 NS |
1061 | for (slot = 0; slot < HW_BUFFER_NUM; slot++) |
1062 | rvin_fill_hw_slot(vin, slot); | |
1063 | ||
0f4b3378 NS |
1064 | ret = rvin_setup(vin); |
1065 | if (ret) | |
1066 | return ret; | |
1067 | ||
3ad69c61 NS |
1068 | rvin_crop_scale_comp(vin); |
1069 | ||
0f4b3378 NS |
1070 | vin_dbg(vin, "Starting to capture\n"); |
1071 | ||
1072 | /* Continuous Frame Capture Mode */ | |
1073 | rvin_write(vin, VNFC_C_FRAME, VNFC_REG); | |
1074 | ||
23689ab1 | 1075 | vin->state = STARTING; |
0f4b3378 NS |
1076 | |
1077 | return 0; | |
f00add96 NS |
1078 | } |
1079 | ||
0f4b3378 | 1080 | static void rvin_capture_stop(struct rvin_dev *vin) |
f00add96 | 1081 | { |
0f4b3378 NS |
1082 | /* Set continuous & single transfer off */ |
1083 | rvin_write(vin, 0, VNFC_REG); | |
1084 | ||
1085 | /* Disable module */ | |
1086 | rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG); | |
f00add96 NS |
1087 | } |
1088 | ||
1089 | /* ----------------------------------------------------------------------------- | |
1090 | * DMA Functions | |
1091 | */ | |
1092 | ||
1093 | #define RVIN_TIMEOUT_MS 100 | |
1094 | #define RVIN_RETRIES 10 | |
1095 | ||
f00add96 NS |
1096 | static irqreturn_t rvin_irq(int irq, void *data) |
1097 | { | |
1098 | struct rvin_dev *vin = data; | |
b6f556cb | 1099 | u32 int_status, vnms; |
f00add96 | 1100 | int slot; |
dc9aec79 | 1101 | unsigned int handled = 0; |
f00add96 NS |
1102 | unsigned long flags; |
1103 | ||
1104 | spin_lock_irqsave(&vin->qlock, flags); | |
1105 | ||
1106 | int_status = rvin_get_interrupt_status(vin); | |
1107 | if (!int_status) | |
1108 | goto done; | |
1109 | ||
1110 | rvin_ack_interrupt(vin); | |
1111 | handled = 1; | |
1112 | ||
30334d3d NS |
1113 | /* Nothing to do if nothing was captured. */ |
1114 | if (!(int_status & VNINTS_FIS)) | |
1115 | goto done; | |
1116 | ||
f00add96 NS |
1117 | /* Nothing to do if capture status is 'STOPPED' */ |
1118 | if (vin->state == STOPPED) { | |
1119 | vin_dbg(vin, "IRQ while state stopped\n"); | |
1120 | goto done; | |
1121 | } | |
1122 | ||
f00add96 | 1123 | /* Prepare for capture and update state */ |
b6f556cb | 1124 | vnms = rvin_read(vin, VNMS_REG); |
dc9aec79 | 1125 | slot = (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; |
f00add96 | 1126 | |
23689ab1 NS |
1127 | /* |
1128 | * To hand buffers back in a known order to userspace start | |
1129 | * to capture first from slot 0. | |
1130 | */ | |
1131 | if (vin->state == STARTING) { | |
1132 | if (slot != 0) { | |
1133 | vin_dbg(vin, "Starting sync slot: %d\n", slot); | |
1134 | goto done; | |
1135 | } | |
1136 | ||
1137 | vin_dbg(vin, "Capture start synced!\n"); | |
1138 | vin->state = RUNNING; | |
1139 | } | |
1140 | ||
f00add96 | 1141 | /* Capture frame */ |
e72b7359 | 1142 | if (vin->buf_hw[slot].buffer) { |
7e0cfdad NS |
1143 | /* |
1144 | * Nothing to do but refill the hardware slot if | |
1145 | * capture only filled first half of vb2 buffer. | |
1146 | */ | |
1147 | if (vin->buf_hw[slot].type == HALF_TOP) { | |
1148 | vin->buf_hw[slot].buffer = NULL; | |
1149 | rvin_fill_hw_slot(vin, slot); | |
1150 | goto done; | |
1151 | } | |
1152 | ||
e72b7359 NS |
1153 | vin->buf_hw[slot].buffer->field = |
1154 | rvin_get_active_field(vin, vnms); | |
1155 | vin->buf_hw[slot].buffer->sequence = vin->sequence; | |
1156 | vin->buf_hw[slot].buffer->vb2_buf.timestamp = ktime_get_ns(); | |
1157 | vb2_buffer_done(&vin->buf_hw[slot].buffer->vb2_buf, | |
dc9aec79 | 1158 | VB2_BUF_STATE_DONE); |
e72b7359 | 1159 | vin->buf_hw[slot].buffer = NULL; |
f00add96 | 1160 | } else { |
dc9aec79 NS |
1161 | /* Scratch buffer was used, dropping frame. */ |
1162 | vin_dbg(vin, "Dropping frame %u\n", vin->sequence); | |
f00add96 | 1163 | } |
dc9aec79 NS |
1164 | |
1165 | vin->sequence++; | |
1166 | ||
1167 | /* Prepare for next frame */ | |
1168 | rvin_fill_hw_slot(vin, slot); | |
f00add96 NS |
1169 | done: |
1170 | spin_unlock_irqrestore(&vin->qlock, flags); | |
1171 | ||
1172 | return IRQ_RETVAL(handled); | |
1173 | } | |
1174 | ||
b83a18cc NS |
1175 | static void return_unused_buffers(struct rvin_dev *vin, |
1176 | enum vb2_buffer_state state) | |
f00add96 NS |
1177 | { |
1178 | struct rvin_buffer *buf, *node; | |
63a71dd8 NS |
1179 | unsigned long flags; |
1180 | ||
1181 | spin_lock_irqsave(&vin->qlock, flags); | |
f00add96 NS |
1182 | |
1183 | list_for_each_entry_safe(buf, node, &vin->buf_list, list) { | |
1184 | vb2_buffer_done(&buf->vb.vb2_buf, state); | |
1185 | list_del(&buf->list); | |
1186 | } | |
63a71dd8 NS |
1187 | |
1188 | spin_unlock_irqrestore(&vin->qlock, flags); | |
f00add96 NS |
1189 | } |
1190 | ||
1191 | static int rvin_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, | |
1192 | unsigned int *nplanes, unsigned int sizes[], | |
36c0f8b3 | 1193 | struct device *alloc_devs[]) |
f00add96 NS |
1194 | |
1195 | { | |
1196 | struct rvin_dev *vin = vb2_get_drv_priv(vq); | |
1197 | ||
f00add96 NS |
1198 | /* Make sure the image size is large enough. */ |
1199 | if (*nplanes) | |
1200 | return sizes[0] < vin->format.sizeimage ? -EINVAL : 0; | |
1201 | ||
1202 | *nplanes = 1; | |
1203 | sizes[0] = vin->format.sizeimage; | |
1204 | ||
1205 | return 0; | |
1206 | }; | |
1207 | ||
1208 | static int rvin_buffer_prepare(struct vb2_buffer *vb) | |
1209 | { | |
1210 | struct rvin_dev *vin = vb2_get_drv_priv(vb->vb2_queue); | |
1211 | unsigned long size = vin->format.sizeimage; | |
1212 | ||
1213 | if (vb2_plane_size(vb, 0) < size) { | |
1214 | vin_err(vin, "buffer too small (%lu < %lu)\n", | |
1215 | vb2_plane_size(vb, 0), size); | |
1216 | return -EINVAL; | |
1217 | } | |
1218 | ||
1219 | vb2_set_plane_payload(vb, 0, size); | |
1220 | ||
1221 | return 0; | |
1222 | } | |
1223 | ||
1224 | static void rvin_buffer_queue(struct vb2_buffer *vb) | |
1225 | { | |
1226 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | |
1227 | struct rvin_dev *vin = vb2_get_drv_priv(vb->vb2_queue); | |
1228 | unsigned long flags; | |
1229 | ||
1230 | spin_lock_irqsave(&vin->qlock, flags); | |
1231 | ||
1232 | list_add_tail(to_buf_list(vbuf), &vin->buf_list); | |
1233 | ||
f00add96 NS |
1234 | spin_unlock_irqrestore(&vin->qlock, flags); |
1235 | } | |
1236 | ||
7b7eb115 NS |
1237 | static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, |
1238 | struct media_pad *pad) | |
1239 | { | |
1240 | struct v4l2_subdev_format fmt = { | |
1241 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, | |
1242 | }; | |
1243 | ||
1244 | fmt.pad = pad->index; | |
1245 | if (v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt)) | |
1246 | return -EPIPE; | |
1247 | ||
1248 | switch (fmt.format.code) { | |
1249 | case MEDIA_BUS_FMT_YUYV8_1X16: | |
01d72e9d | 1250 | case MEDIA_BUS_FMT_UYVY8_1X16: |
7b7eb115 NS |
1251 | case MEDIA_BUS_FMT_UYVY8_2X8: |
1252 | case MEDIA_BUS_FMT_UYVY10_2X10: | |
1253 | case MEDIA_BUS_FMT_RGB888_1X24: | |
fd22e8eb | 1254 | break; |
8c3e0f67 NS |
1255 | case MEDIA_BUS_FMT_SBGGR8_1X8: |
1256 | if (vin->format.pixelformat != V4L2_PIX_FMT_SBGGR8) | |
1257 | return -EPIPE; | |
1258 | break; | |
1259 | case MEDIA_BUS_FMT_SGBRG8_1X8: | |
1260 | if (vin->format.pixelformat != V4L2_PIX_FMT_SGBRG8) | |
1261 | return -EPIPE; | |
1262 | break; | |
1263 | case MEDIA_BUS_FMT_SGRBG8_1X8: | |
1264 | if (vin->format.pixelformat != V4L2_PIX_FMT_SGRBG8) | |
1265 | return -EPIPE; | |
1266 | break; | |
fd22e8eb LP |
1267 | case MEDIA_BUS_FMT_SRGGB8_1X8: |
1268 | if (vin->format.pixelformat != V4L2_PIX_FMT_SRGGB8) | |
1269 | return -EPIPE; | |
7b7eb115 | 1270 | break; |
c93beb52 VB |
1271 | case MEDIA_BUS_FMT_Y8_1X8: |
1272 | if (vin->format.pixelformat != V4L2_PIX_FMT_GREY) | |
1273 | return -EPIPE; | |
1274 | break; | |
7b7eb115 NS |
1275 | default: |
1276 | return -EPIPE; | |
1277 | } | |
fd22e8eb | 1278 | vin->mbus_code = fmt.format.code; |
7b7eb115 NS |
1279 | |
1280 | switch (fmt.format.field) { | |
1281 | case V4L2_FIELD_TOP: | |
1282 | case V4L2_FIELD_BOTTOM: | |
1283 | case V4L2_FIELD_NONE: | |
1284 | case V4L2_FIELD_INTERLACED_TB: | |
1285 | case V4L2_FIELD_INTERLACED_BT: | |
1286 | case V4L2_FIELD_INTERLACED: | |
1287 | case V4L2_FIELD_SEQ_TB: | |
1288 | case V4L2_FIELD_SEQ_BT: | |
1289 | /* Supported natively */ | |
1290 | break; | |
1291 | case V4L2_FIELD_ALTERNATE: | |
1292 | switch (vin->format.field) { | |
1293 | case V4L2_FIELD_TOP: | |
1294 | case V4L2_FIELD_BOTTOM: | |
1295 | case V4L2_FIELD_NONE: | |
08369321 | 1296 | case V4L2_FIELD_ALTERNATE: |
7b7eb115 NS |
1297 | break; |
1298 | case V4L2_FIELD_INTERLACED_TB: | |
1299 | case V4L2_FIELD_INTERLACED_BT: | |
1300 | case V4L2_FIELD_INTERLACED: | |
1301 | case V4L2_FIELD_SEQ_TB: | |
1302 | case V4L2_FIELD_SEQ_BT: | |
1303 | /* Use VIN hardware to combine the two fields */ | |
1304 | fmt.format.height *= 2; | |
1305 | break; | |
1306 | default: | |
1307 | return -EPIPE; | |
1308 | } | |
1309 | break; | |
1310 | default: | |
1311 | return -EPIPE; | |
1312 | } | |
1313 | ||
3ad69c61 | 1314 | if (rvin_scaler_needed(vin)) { |
879c5a45 NS |
1315 | /* Gen3 can't scale NV12 */ |
1316 | if (vin->info->model == RCAR_GEN3 && | |
1317 | vin->format.pixelformat == V4L2_PIX_FMT_NV12) | |
1318 | return -EPIPE; | |
1319 | ||
3ad69c61 NS |
1320 | if (!vin->scaler) |
1321 | return -EPIPE; | |
1322 | } else { | |
cb88d828 NS |
1323 | if (vin->format.pixelformat == V4L2_PIX_FMT_NV12) { |
1324 | if (ALIGN(fmt.format.width, 32) != vin->format.width || | |
1325 | ALIGN(fmt.format.height, 32) != vin->format.height) | |
1326 | return -EPIPE; | |
1327 | } else { | |
1328 | if (fmt.format.width != vin->format.width || | |
1329 | fmt.format.height != vin->format.height) | |
1330 | return -EPIPE; | |
1331 | } | |
3ad69c61 NS |
1332 | } |
1333 | ||
1334 | if (fmt.format.code != vin->mbus_code) | |
7b7eb115 NS |
1335 | return -EPIPE; |
1336 | ||
1337 | return 0; | |
1338 | } | |
1339 | ||
1340 | static int rvin_set_stream(struct rvin_dev *vin, int on) | |
1341 | { | |
7b7eb115 NS |
1342 | struct v4l2_subdev *sd; |
1343 | struct media_pad *pad; | |
1344 | int ret; | |
1345 | ||
1346 | /* No media controller used, simply pass operation to subdevice. */ | |
1347 | if (!vin->info->use_mc) { | |
d7592b2e | 1348 | ret = v4l2_subdev_call(vin->parallel.subdev, video, s_stream, |
7b7eb115 NS |
1349 | on); |
1350 | ||
1351 | return ret == -ENOIOCTLCMD ? 0 : ret; | |
1352 | } | |
1353 | ||
b2e44430 | 1354 | pad = media_pad_remote_pad_first(&vin->pad); |
7b7eb115 NS |
1355 | if (!pad) |
1356 | return -EPIPE; | |
1357 | ||
1358 | sd = media_entity_to_v4l2_subdev(pad->entity); | |
1359 | ||
1360 | if (!on) { | |
12cecbf9 | 1361 | video_device_pipeline_stop(&vin->vdev); |
7b7eb115 NS |
1362 | return v4l2_subdev_call(sd, video, s_stream, 0); |
1363 | } | |
1364 | ||
1365 | ret = rvin_mc_validate_format(vin, sd, pad); | |
1366 | if (ret) | |
1367 | return ret; | |
1368 | ||
6eaff06a | 1369 | ret = video_device_pipeline_alloc_start(&vin->vdev); |
7b7eb115 NS |
1370 | if (ret) |
1371 | return ret; | |
1372 | ||
1373 | ret = v4l2_subdev_call(sd, video, s_stream, 1); | |
1374 | if (ret == -ENOIOCTLCMD) | |
1375 | ret = 0; | |
1376 | if (ret) | |
12cecbf9 | 1377 | video_device_pipeline_stop(&vin->vdev); |
7b7eb115 NS |
1378 | |
1379 | return ret; | |
1380 | } | |
1381 | ||
63a71dd8 | 1382 | int rvin_start_streaming(struct rvin_dev *vin) |
f00add96 | 1383 | { |
f00add96 NS |
1384 | unsigned long flags; |
1385 | int ret; | |
1386 | ||
7b7eb115 | 1387 | ret = rvin_set_stream(vin, 1); |
63a71dd8 NS |
1388 | if (ret) |
1389 | return ret; | |
f00add96 NS |
1390 | |
1391 | spin_lock_irqsave(&vin->qlock, flags); | |
1392 | ||
f00add96 NS |
1393 | vin->sequence = 0; |
1394 | ||
f00add96 | 1395 | ret = rvin_capture_start(vin); |
63a71dd8 | 1396 | if (ret) |
7b7eb115 | 1397 | rvin_set_stream(vin, 0); |
f00add96 NS |
1398 | |
1399 | spin_unlock_irqrestore(&vin->qlock, flags); | |
6a8ffa8b | 1400 | |
f00add96 NS |
1401 | return ret; |
1402 | } | |
1403 | ||
63a71dd8 | 1404 | static int rvin_start_streaming_vq(struct vb2_queue *vq, unsigned int count) |
f00add96 NS |
1405 | { |
1406 | struct rvin_dev *vin = vb2_get_drv_priv(vq); | |
63a71dd8 NS |
1407 | int ret = -ENOMEM; |
1408 | ||
1409 | /* Allocate scratch buffer. */ | |
1410 | vin->scratch = dma_alloc_coherent(vin->dev, vin->format.sizeimage, | |
1411 | &vin->scratch_phys, GFP_KERNEL); | |
1412 | if (!vin->scratch) | |
1413 | goto err_scratch; | |
1414 | ||
1415 | ret = rvin_start_streaming(vin); | |
1416 | if (ret) | |
1417 | goto err_start; | |
1418 | ||
1419 | return 0; | |
1420 | err_start: | |
1421 | dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch, | |
1422 | vin->scratch_phys); | |
1423 | err_scratch: | |
1424 | return_unused_buffers(vin, VB2_BUF_STATE_QUEUED); | |
1425 | ||
1426 | return ret; | |
1427 | } | |
1428 | ||
1429 | void rvin_stop_streaming(struct rvin_dev *vin) | |
1430 | { | |
90ed5785 | 1431 | unsigned int i, retries; |
f00add96 | 1432 | unsigned long flags; |
90ed5785 | 1433 | bool buffersFreed; |
f00add96 NS |
1434 | |
1435 | spin_lock_irqsave(&vin->qlock, flags); | |
1436 | ||
c4f11535 NS |
1437 | if (vin->state == STOPPED) { |
1438 | spin_unlock_irqrestore(&vin->qlock, flags); | |
1439 | return; | |
1440 | } | |
1441 | ||
f00add96 NS |
1442 | vin->state = STOPPING; |
1443 | ||
90ed5785 NS |
1444 | /* Wait until only scratch buffer is used, max 3 interrupts. */ |
1445 | retries = 0; | |
1446 | while (retries++ < RVIN_RETRIES) { | |
1447 | buffersFreed = true; | |
1448 | for (i = 0; i < HW_BUFFER_NUM; i++) | |
1449 | if (vin->buf_hw[i].buffer) | |
1450 | buffersFreed = false; | |
1451 | ||
1452 | if (buffersFreed) | |
1453 | break; | |
1454 | ||
1455 | spin_unlock_irqrestore(&vin->qlock, flags); | |
1456 | msleep(RVIN_TIMEOUT_MS); | |
1457 | spin_lock_irqsave(&vin->qlock, flags); | |
1458 | } | |
1459 | ||
f00add96 | 1460 | /* Wait for streaming to stop */ |
90ed5785 | 1461 | retries = 0; |
f00add96 NS |
1462 | while (retries++ < RVIN_RETRIES) { |
1463 | ||
1464 | rvin_capture_stop(vin); | |
1465 | ||
1466 | /* Check if HW is stopped */ | |
1467 | if (!rvin_capture_active(vin)) { | |
1468 | vin->state = STOPPED; | |
1469 | break; | |
1470 | } | |
1471 | ||
1472 | spin_unlock_irqrestore(&vin->qlock, flags); | |
1473 | msleep(RVIN_TIMEOUT_MS); | |
1474 | spin_lock_irqsave(&vin->qlock, flags); | |
1475 | } | |
1476 | ||
90ed5785 | 1477 | if (!buffersFreed || vin->state != STOPPED) { |
f00add96 NS |
1478 | /* |
1479 | * If this happens something have gone horribly wrong. | |
1480 | * Set state to stopped to prevent the interrupt handler | |
1481 | * to make things worse... | |
1482 | */ | |
1483 | vin_err(vin, "Failed stop HW, something is seriously broken\n"); | |
1484 | vin->state = STOPPED; | |
1485 | } | |
1486 | ||
f00add96 NS |
1487 | spin_unlock_irqrestore(&vin->qlock, flags); |
1488 | ||
dca7cc1c NS |
1489 | /* If something went wrong, free buffers with an error. */ |
1490 | if (!buffersFreed) { | |
1491 | return_unused_buffers(vin, VB2_BUF_STATE_ERROR); | |
1492 | for (i = 0; i < HW_BUFFER_NUM; i++) { | |
1493 | if (vin->buf_hw[i].buffer) | |
1494 | vb2_buffer_done(&vin->buf_hw[i].buffer->vb2_buf, | |
1495 | VB2_BUF_STATE_ERROR); | |
1496 | } | |
1497 | } | |
1498 | ||
7b7eb115 | 1499 | rvin_set_stream(vin, 0); |
f00add96 NS |
1500 | |
1501 | /* disable interrupts */ | |
1502 | rvin_disable_interrupts(vin); | |
63a71dd8 NS |
1503 | } |
1504 | ||
1505 | static void rvin_stop_streaming_vq(struct vb2_queue *vq) | |
1506 | { | |
1507 | struct rvin_dev *vin = vb2_get_drv_priv(vq); | |
1508 | ||
1509 | rvin_stop_streaming(vin); | |
6a8ffa8b NS |
1510 | |
1511 | /* Free scratch buffer. */ | |
1512 | dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch, | |
1513 | vin->scratch_phys); | |
63a71dd8 NS |
1514 | |
1515 | return_unused_buffers(vin, VB2_BUF_STATE_ERROR); | |
f00add96 NS |
1516 | } |
1517 | ||
b7b361f0 | 1518 | static const struct vb2_ops rvin_qops = { |
f00add96 NS |
1519 | .queue_setup = rvin_queue_setup, |
1520 | .buf_prepare = rvin_buffer_prepare, | |
1521 | .buf_queue = rvin_buffer_queue, | |
63a71dd8 NS |
1522 | .start_streaming = rvin_start_streaming_vq, |
1523 | .stop_streaming = rvin_stop_streaming_vq, | |
f00add96 NS |
1524 | .wait_prepare = vb2_ops_wait_prepare, |
1525 | .wait_finish = vb2_ops_wait_finish, | |
1526 | }; | |
1527 | ||
d6ad012e | 1528 | void rvin_dma_unregister(struct rvin_dev *vin) |
f00add96 | 1529 | { |
f00add96 NS |
1530 | mutex_destroy(&vin->lock); |
1531 | ||
1532 | v4l2_device_unregister(&vin->v4l2_dev); | |
1533 | } | |
1534 | ||
d6ad012e | 1535 | int rvin_dma_register(struct rvin_dev *vin, int irq) |
f00add96 NS |
1536 | { |
1537 | struct vb2_queue *q = &vin->queue; | |
1538 | int i, ret; | |
1539 | ||
1540 | /* Initialize the top-level structure */ | |
1541 | ret = v4l2_device_register(vin->dev, &vin->v4l2_dev); | |
1542 | if (ret) | |
1543 | return ret; | |
1544 | ||
1545 | mutex_init(&vin->lock); | |
1546 | INIT_LIST_HEAD(&vin->buf_list); | |
1547 | ||
1548 | spin_lock_init(&vin->qlock); | |
1549 | ||
1550 | vin->state = STOPPED; | |
1551 | ||
1552 | for (i = 0; i < HW_BUFFER_NUM; i++) | |
e72b7359 | 1553 | vin->buf_hw[i].buffer = NULL; |
f00add96 NS |
1554 | |
1555 | /* buffer queue */ | |
f00add96 NS |
1556 | q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
1557 | q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; | |
1558 | q->lock = &vin->lock; | |
1559 | q->drv_priv = vin; | |
1560 | q->buf_struct_size = sizeof(struct rvin_buffer); | |
1561 | q->ops = &rvin_qops; | |
1562 | q->mem_ops = &vb2_dma_contig_memops; | |
1563 | q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; | |
dc9aec79 | 1564 | q->min_buffers_needed = 4; |
53ddcc68 | 1565 | q->dev = vin->dev; |
f00add96 NS |
1566 | |
1567 | ret = vb2_queue_init(q); | |
1568 | if (ret < 0) { | |
1569 | vin_err(vin, "failed to initialize VB2 queue\n"); | |
1570 | goto error; | |
1571 | } | |
1572 | ||
1573 | /* irq */ | |
1574 | ret = devm_request_irq(vin->dev, irq, rvin_irq, IRQF_SHARED, | |
1575 | KBUILD_MODNAME, vin); | |
1576 | if (ret) { | |
1577 | vin_err(vin, "failed to request irq\n"); | |
1578 | goto error; | |
1579 | } | |
1580 | ||
1581 | return 0; | |
1582 | error: | |
d6ad012e | 1583 | rvin_dma_unregister(vin); |
f00add96 NS |
1584 | |
1585 | return ret; | |
1586 | } | |
90dedce9 NS |
1587 | |
1588 | /* ----------------------------------------------------------------------------- | |
1589 | * Gen3 CHSEL manipulation | |
1590 | */ | |
1591 | ||
1592 | /* | |
1593 | * There is no need to have locking around changing the routing | |
1594 | * as it's only possible to do so when no VIN in the group is | |
1595 | * streaming so nothing can race with the VNMC register. | |
1596 | */ | |
1597 | int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel) | |
1598 | { | |
fb25ca37 JM |
1599 | const struct rvin_group_route *route; |
1600 | u32 ifmd = 0; | |
1601 | u32 vnmc; | |
90dedce9 NS |
1602 | int ret; |
1603 | ||
334fe327 MCC |
1604 | ret = pm_runtime_resume_and_get(vin->dev); |
1605 | if (ret < 0) | |
90dedce9 NS |
1606 | return ret; |
1607 | ||
1608 | /* Make register writes take effect immediately. */ | |
1609 | vnmc = rvin_read(vin, VNMC_REG); | |
1610 | rvin_write(vin, vnmc & ~VNMC_VUP, VNMC_REG); | |
1611 | ||
fb25ca37 JM |
1612 | /* |
1613 | * Set data expansion mode to "pad with 0s" by inspecting the routes | |
1614 | * table to find out which bit fields are available in the IFMD | |
1615 | * register. IFMD_DES1 controls data expansion mode for CSI20/21, | |
1616 | * IFMD_DES0 controls data expansion mode for CSI40/41. | |
1617 | */ | |
3e52419e | 1618 | for (route = vin->info->routes; route->chsel; route++) { |
fb25ca37 JM |
1619 | if (route->csi == RVIN_CSI20 || route->csi == RVIN_CSI21) |
1620 | ifmd |= VNCSI_IFMD_DES1; | |
1621 | else | |
1622 | ifmd |= VNCSI_IFMD_DES0; | |
90dedce9 | 1623 | |
fb25ca37 JM |
1624 | if (ifmd == (VNCSI_IFMD_DES0 | VNCSI_IFMD_DES1)) |
1625 | break; | |
1626 | } | |
1627 | ||
1628 | if (ifmd) { | |
1629 | ifmd |= VNCSI_IFMD_CSI_CHSEL(chsel); | |
1630 | rvin_write(vin, ifmd, VNCSI_IFMD_REG); | |
1631 | } | |
90dedce9 NS |
1632 | |
1633 | vin_dbg(vin, "Set IFMD 0x%x\n", ifmd); | |
1634 | ||
43e36a22 NS |
1635 | vin->chsel = chsel; |
1636 | ||
90dedce9 NS |
1637 | /* Restore VNMC. */ |
1638 | rvin_write(vin, vnmc, VNMC_REG); | |
1639 | ||
1640 | pm_runtime_put(vin->dev); | |
1641 | ||
8d19d5d0 | 1642 | return 0; |
90dedce9 | 1643 | } |
5720c733 NS |
1644 | |
1645 | void rvin_set_alpha(struct rvin_dev *vin, unsigned int alpha) | |
1646 | { | |
1d99e68c NS |
1647 | unsigned long flags; |
1648 | u32 dmr; | |
1649 | ||
1650 | spin_lock_irqsave(&vin->qlock, flags); | |
1651 | ||
5720c733 | 1652 | vin->alpha = alpha; |
1d99e68c NS |
1653 | |
1654 | if (vin->state == STOPPED) | |
1655 | goto out; | |
1656 | ||
1657 | switch (vin->format.pixelformat) { | |
1658 | case V4L2_PIX_FMT_ARGB555: | |
1659 | dmr = rvin_read(vin, VNDMR_REG) & ~VNDMR_ABIT; | |
1660 | if (vin->alpha) | |
1661 | dmr |= VNDMR_ABIT; | |
1662 | break; | |
1663 | case V4L2_PIX_FMT_ABGR32: | |
1664 | dmr = rvin_read(vin, VNDMR_REG) & ~VNDMR_A8BIT_MASK; | |
1665 | dmr |= VNDMR_A8BIT(vin->alpha); | |
1666 | break; | |
1667 | default: | |
1668 | goto out; | |
1669 | } | |
1670 | ||
1671 | rvin_write(vin, dmr, VNDMR_REG); | |
1672 | out: | |
1673 | spin_unlock_irqrestore(&vin->qlock, flags); | |
5720c733 | 1674 | } |