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