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