diff options
Diffstat (limited to 'extras/recipes-kernel/linux/linux-omap/media/0043-OMAP3-ISP-driver.patch')
-rw-r--r-- | extras/recipes-kernel/linux/linux-omap/media/0043-OMAP3-ISP-driver.patch | 21513 |
1 files changed, 21513 insertions, 0 deletions
diff --git a/extras/recipes-kernel/linux/linux-omap/media/0043-OMAP3-ISP-driver.patch b/extras/recipes-kernel/linux/linux-omap/media/0043-OMAP3-ISP-driver.patch new file mode 100644 index 00000000..b4e97848 --- /dev/null +++ b/extras/recipes-kernel/linux/linux-omap/media/0043-OMAP3-ISP-driver.patch | |||
@@ -0,0 +1,21513 @@ | |||
1 | From f12978691d5189949c9296bceb43c5b272c9c03c Mon Sep 17 00:00:00 2001 | ||
2 | From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
3 | Date: Tue, 17 Feb 2009 09:23:45 -0600 | ||
4 | Subject: [PATCH 43/43] OMAP3 ISP driver | ||
5 | |||
6 | Last 10 commits from upstream are | ||
7 | |||
8 | omap3isp: Autoidle enabled for ISP | ||
9 | omap3isp: enable AUTOIDLE through module parameter | ||
10 | omap3isp: preview: Fix defect correct config function | ||
11 | omap3isp: video: Replace BUG with WARN_ON in case of buffer queue error | ||
12 | omap3isp: Add module device table | ||
13 | omap3isp: csi2: Print registers on stream on | ||
14 | v4l: OMAP3 ISP CCDC: Add support for 8bit greyscale sensors | ||
15 | omap3isp: ccdc: Set default DC subtract value to 0 | ||
16 | omap3isp: Prefix all public symbols with omap3isp_ | ||
17 | omap3isp: Fix dependencies and mark as experimental | ||
18 | |||
19 | Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
20 | Signed-off-by: Sakari Ailus <sakari.ailus@nokia.com> | ||
21 | Signed-off-by: David Cohen <david.cohen@nokia.com> | ||
22 | Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com> | ||
23 | Signed-off-by: Vimarsh Zutshi <vimarsh.zutshi@nokia.com> | ||
24 | Signed-off-by: Tuukka Toivonen <tuukka.o.toivonen@nokia.com> | ||
25 | Signed-off-by: Sergio Aguirre <saaguirre@ti.com> | ||
26 | Signed-off-by: Antti Koskipaa <antti.koskipaa@nokia.com> | ||
27 | Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> | ||
28 | Signed-off-by: RaniSuneela <r-m@ti.com> | ||
29 | Signed-off-by: Atanas Filipov <afilipov@mm-sol.com> | ||
30 | Signed-off-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> | ||
31 | Signed-off-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> | ||
32 | Signed-off-by: Nayden Kanchev <nkanchev@mm-sol.com> | ||
33 | Signed-off-by: Phil Carmody <ext-phil.2.carmody@nokia.com> | ||
34 | Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | ||
35 | Signed-off-by: Dominic Curran <dcurran@ti.com> | ||
36 | Signed-off-by: Ilkka Myllyperkio <ilkka.myllyperkio@sofica.fi> | ||
37 | Signed-off-by: Pallavi Kulkarni <p-kulkarni@ti.com> | ||
38 | Signed-off-by: Vaibhav Hiremath <hvaibhav@ti.com> | ||
39 | --- | ||
40 | drivers/media/video/Kconfig | 13 + | ||
41 | drivers/media/video/Makefile | 2 + | ||
42 | drivers/media/video/isp/Makefile | 13 + | ||
43 | drivers/media/video/isp/cfa_coef_table.h | 601 +++++++ | ||
44 | drivers/media/video/isp/gamma_table.h | 90 + | ||
45 | drivers/media/video/isp/isp.c | 2221 +++++++++++++++++++++++++ | ||
46 | drivers/media/video/isp/isp.h | 427 +++++ | ||
47 | drivers/media/video/isp/ispccdc.c | 2280 ++++++++++++++++++++++++++ | ||
48 | drivers/media/video/isp/ispccdc.h | 223 +++ | ||
49 | drivers/media/video/isp/ispccp2.c | 1189 ++++++++++++++ | ||
50 | drivers/media/video/isp/ispccp2.h | 101 ++ | ||
51 | drivers/media/video/isp/ispcsi2.c | 1332 +++++++++++++++ | ||
52 | drivers/media/video/isp/ispcsi2.h | 169 ++ | ||
53 | drivers/media/video/isp/ispcsiphy.c | 247 +++ | ||
54 | drivers/media/video/isp/ispcsiphy.h | 74 + | ||
55 | drivers/media/video/isp/isph3a.h | 117 ++ | ||
56 | drivers/media/video/isp/isph3a_aewb.c | 374 +++++ | ||
57 | drivers/media/video/isp/isph3a_af.c | 429 +++++ | ||
58 | drivers/media/video/isp/isphist.c | 520 ++++++ | ||
59 | drivers/media/video/isp/isphist.h | 40 + | ||
60 | drivers/media/video/isp/isppreview.c | 2120 ++++++++++++++++++++++++ | ||
61 | drivers/media/video/isp/isppreview.h | 214 +++ | ||
62 | drivers/media/video/isp/ispqueue.c | 1136 +++++++++++++ | ||
63 | drivers/media/video/isp/ispqueue.h | 185 +++ | ||
64 | drivers/media/video/isp/ispreg.h | 1589 ++++++++++++++++++ | ||
65 | drivers/media/video/isp/ispresizer.c | 1710 +++++++++++++++++++ | ||
66 | drivers/media/video/isp/ispresizer.h | 150 ++ | ||
67 | drivers/media/video/isp/ispstat.c | 1100 +++++++++++++ | ||
68 | drivers/media/video/isp/ispstat.h | 169 ++ | ||
69 | drivers/media/video/isp/ispvideo.c | 1264 ++++++++++++++ | ||
70 | drivers/media/video/isp/ispvideo.h | 202 +++ | ||
71 | drivers/media/video/isp/luma_enhance_table.h | 154 ++ | ||
72 | drivers/media/video/isp/noise_filter_table.h | 90 + | ||
73 | include/linux/Kbuild | 1 + | ||
74 | include/linux/omap3isp.h | 631 +++++++ | ||
75 | 35 files changed, 21177 insertions(+), 0 deletions(-) | ||
76 | create mode 100644 drivers/media/video/isp/Makefile | ||
77 | create mode 100644 drivers/media/video/isp/cfa_coef_table.h | ||
78 | create mode 100644 drivers/media/video/isp/gamma_table.h | ||
79 | create mode 100644 drivers/media/video/isp/isp.c | ||
80 | create mode 100644 drivers/media/video/isp/isp.h | ||
81 | create mode 100644 drivers/media/video/isp/ispccdc.c | ||
82 | create mode 100644 drivers/media/video/isp/ispccdc.h | ||
83 | create mode 100644 drivers/media/video/isp/ispccp2.c | ||
84 | create mode 100644 drivers/media/video/isp/ispccp2.h | ||
85 | create mode 100644 drivers/media/video/isp/ispcsi2.c | ||
86 | create mode 100644 drivers/media/video/isp/ispcsi2.h | ||
87 | create mode 100644 drivers/media/video/isp/ispcsiphy.c | ||
88 | create mode 100644 drivers/media/video/isp/ispcsiphy.h | ||
89 | create mode 100644 drivers/media/video/isp/isph3a.h | ||
90 | create mode 100644 drivers/media/video/isp/isph3a_aewb.c | ||
91 | create mode 100644 drivers/media/video/isp/isph3a_af.c | ||
92 | create mode 100644 drivers/media/video/isp/isphist.c | ||
93 | create mode 100644 drivers/media/video/isp/isphist.h | ||
94 | create mode 100644 drivers/media/video/isp/isppreview.c | ||
95 | create mode 100644 drivers/media/video/isp/isppreview.h | ||
96 | create mode 100644 drivers/media/video/isp/ispqueue.c | ||
97 | create mode 100644 drivers/media/video/isp/ispqueue.h | ||
98 | create mode 100644 drivers/media/video/isp/ispreg.h | ||
99 | create mode 100644 drivers/media/video/isp/ispresizer.c | ||
100 | create mode 100644 drivers/media/video/isp/ispresizer.h | ||
101 | create mode 100644 drivers/media/video/isp/ispstat.c | ||
102 | create mode 100644 drivers/media/video/isp/ispstat.h | ||
103 | create mode 100644 drivers/media/video/isp/ispvideo.c | ||
104 | create mode 100644 drivers/media/video/isp/ispvideo.h | ||
105 | create mode 100644 drivers/media/video/isp/luma_enhance_table.h | ||
106 | create mode 100644 drivers/media/video/isp/noise_filter_table.h | ||
107 | create mode 100644 include/linux/omap3isp.h | ||
108 | |||
109 | diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig | ||
110 | index 6830d28..60c2bf0 100644 | ||
111 | --- a/drivers/media/video/Kconfig | ||
112 | +++ b/drivers/media/video/Kconfig | ||
113 | @@ -722,6 +722,19 @@ config VIDEO_VIA_CAMERA | ||
114 | Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems | ||
115 | with ov7670 sensors. | ||
116 | |||
117 | +config VIDEO_OMAP3 | ||
118 | + tristate "OMAP 3 Camera support (EXPERIMENTAL)" | ||
119 | + select OMAP_IOMMU | ||
120 | + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL | ||
121 | + ---help--- | ||
122 | + Driver for an OMAP 3 camera controller. | ||
123 | + | ||
124 | +config VIDEO_OMAP3_DEBUG | ||
125 | + bool "OMAP 3 Camera debug messages" | ||
126 | + depends on VIDEO_OMAP3 | ||
127 | + ---help--- | ||
128 | + Enable debug messages on OMAP 3 camera controller driver. | ||
129 | + | ||
130 | config SOC_CAMERA | ||
131 | tristate "SoC camera support" | ||
132 | depends on VIDEO_V4L2 && HAS_DMA && I2C | ||
133 | diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile | ||
134 | index adc1bd5..bd2f556 100644 | ||
135 | --- a/drivers/media/video/Makefile | ||
136 | +++ b/drivers/media/video/Makefile | ||
137 | @@ -124,6 +124,8 @@ obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o | ||
138 | |||
139 | obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o | ||
140 | |||
141 | +obj-$(CONFIG_VIDEO_OMAP3) += isp/ | ||
142 | + | ||
143 | obj-$(CONFIG_USB_DABUSB) += dabusb.o | ||
144 | obj-$(CONFIG_USB_SE401) += se401.o | ||
145 | obj-$(CONFIG_USB_ZR364XX) += zr364xx.o | ||
146 | diff --git a/drivers/media/video/isp/Makefile b/drivers/media/video/isp/Makefile | ||
147 | new file mode 100644 | ||
148 | index 0000000..b1b3447 | ||
149 | --- /dev/null | ||
150 | +++ b/drivers/media/video/isp/Makefile | ||
151 | @@ -0,0 +1,13 @@ | ||
152 | +# Makefile for OMAP3 ISP driver | ||
153 | + | ||
154 | +ifdef CONFIG_VIDEO_OMAP3_DEBUG | ||
155 | +EXTRA_CFLAGS += -DDEBUG | ||
156 | +endif | ||
157 | + | ||
158 | +omap3-isp-objs += \ | ||
159 | + isp.o ispqueue.o ispvideo.o \ | ||
160 | + ispcsiphy.o ispccp2.o ispcsi2.o \ | ||
161 | + ispccdc.o isppreview.o ispresizer.o \ | ||
162 | + ispstat.o isph3a_aewb.o isph3a_af.o isphist.o | ||
163 | + | ||
164 | +obj-$(CONFIG_VIDEO_OMAP3) += omap3-isp.o | ||
165 | diff --git a/drivers/media/video/isp/cfa_coef_table.h b/drivers/media/video/isp/cfa_coef_table.h | ||
166 | new file mode 100644 | ||
167 | index 0000000..4ec3fff | ||
168 | --- /dev/null | ||
169 | +++ b/drivers/media/video/isp/cfa_coef_table.h | ||
170 | @@ -0,0 +1,601 @@ | ||
171 | +/* | ||
172 | + * cfa_coef_table.h | ||
173 | + * | ||
174 | + * TI OMAP3 ISP - CFA coefficients table | ||
175 | + * | ||
176 | + * Copyright (C) 2009-2010 Nokia Corporation | ||
177 | + * | ||
178 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
179 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
180 | + * | ||
181 | + * This program is free software; you can redistribute it and/or | ||
182 | + * modify it under the terms of the GNU General Public License | ||
183 | + * version 2 as published by the Free Software Foundation. | ||
184 | + * | ||
185 | + * This program is distributed in the hope that it will be useful, but | ||
186 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
187 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
188 | + * General Public License for more details. | ||
189 | + * | ||
190 | + * You should have received a copy of the GNU General Public License | ||
191 | + * along with this program; if not, write to the Free Software | ||
192 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
193 | + * 02110-1301 USA | ||
194 | + */ | ||
195 | + | ||
196 | +244, | ||
197 | +0, | ||
198 | +247, | ||
199 | +0, | ||
200 | +12, | ||
201 | +27, | ||
202 | +36, | ||
203 | +247, | ||
204 | +250, | ||
205 | +0, | ||
206 | +27, | ||
207 | +0, | ||
208 | +4, | ||
209 | +250, | ||
210 | +12, | ||
211 | +244, | ||
212 | +248, | ||
213 | +0, | ||
214 | +0, | ||
215 | +0, | ||
216 | +0, | ||
217 | +40, | ||
218 | +0, | ||
219 | +0, | ||
220 | +244, | ||
221 | +12, | ||
222 | +250, | ||
223 | +4, | ||
224 | +0, | ||
225 | +27, | ||
226 | +0, | ||
227 | +250, | ||
228 | +247, | ||
229 | +36, | ||
230 | +27, | ||
231 | +12, | ||
232 | +0, | ||
233 | +247, | ||
234 | +0, | ||
235 | +244, | ||
236 | +0, | ||
237 | +0, | ||
238 | +40, | ||
239 | +0, | ||
240 | +0, | ||
241 | +0, | ||
242 | +0, | ||
243 | +248, | ||
244 | +244, | ||
245 | +0, | ||
246 | +247, | ||
247 | +0, | ||
248 | +12, | ||
249 | +27, | ||
250 | +36, | ||
251 | +247, | ||
252 | +250, | ||
253 | +0, | ||
254 | +27, | ||
255 | +0, | ||
256 | +4, | ||
257 | +250, | ||
258 | +12, | ||
259 | +244, | ||
260 | +248, | ||
261 | +0, | ||
262 | +0, | ||
263 | +0, | ||
264 | +0, | ||
265 | +40, | ||
266 | +0, | ||
267 | +0, | ||
268 | +244, | ||
269 | +12, | ||
270 | +250, | ||
271 | +4, | ||
272 | +0, | ||
273 | +27, | ||
274 | +0, | ||
275 | +250, | ||
276 | +247, | ||
277 | +36, | ||
278 | +27, | ||
279 | +12, | ||
280 | +0, | ||
281 | +247, | ||
282 | +0, | ||
283 | +244, | ||
284 | +0, | ||
285 | +0, | ||
286 | +40, | ||
287 | +0, | ||
288 | +0, | ||
289 | +0, | ||
290 | +0, | ||
291 | +248, | ||
292 | +244, | ||
293 | +0, | ||
294 | +247, | ||
295 | +0, | ||
296 | +12, | ||
297 | +27, | ||
298 | +36, | ||
299 | +247, | ||
300 | +250, | ||
301 | +0, | ||
302 | +27, | ||
303 | +0, | ||
304 | +4, | ||
305 | +250, | ||
306 | +12, | ||
307 | +244, | ||
308 | +248, | ||
309 | +0, | ||
310 | +0, | ||
311 | +0, | ||
312 | +0, | ||
313 | +40, | ||
314 | +0, | ||
315 | +0, | ||
316 | +244, | ||
317 | +12, | ||
318 | +250, | ||
319 | +4, | ||
320 | +0, | ||
321 | +27, | ||
322 | +0, | ||
323 | +250, | ||
324 | +247, | ||
325 | +36, | ||
326 | +27, | ||
327 | +12, | ||
328 | +0, | ||
329 | +247, | ||
330 | +0, | ||
331 | +244, | ||
332 | +0, | ||
333 | +0, | ||
334 | +40, | ||
335 | +0, | ||
336 | +0, | ||
337 | +0, | ||
338 | +0, | ||
339 | +248, | ||
340 | +0, | ||
341 | +247, | ||
342 | +0, | ||
343 | +244, | ||
344 | +247, | ||
345 | +36, | ||
346 | +27, | ||
347 | +12, | ||
348 | +0, | ||
349 | +27, | ||
350 | +0, | ||
351 | +250, | ||
352 | +244, | ||
353 | +12, | ||
354 | +250, | ||
355 | +4, | ||
356 | +0, | ||
357 | +0, | ||
358 | +0, | ||
359 | +248, | ||
360 | +0, | ||
361 | +0, | ||
362 | +40, | ||
363 | +0, | ||
364 | +4, | ||
365 | +250, | ||
366 | +12, | ||
367 | +244, | ||
368 | +250, | ||
369 | +0, | ||
370 | +27, | ||
371 | +0, | ||
372 | +12, | ||
373 | +27, | ||
374 | +36, | ||
375 | +247, | ||
376 | +244, | ||
377 | +0, | ||
378 | +247, | ||
379 | +0, | ||
380 | +0, | ||
381 | +40, | ||
382 | +0, | ||
383 | +0, | ||
384 | +248, | ||
385 | +0, | ||
386 | +0, | ||
387 | +0, | ||
388 | +0, | ||
389 | +247, | ||
390 | +0, | ||
391 | +244, | ||
392 | +247, | ||
393 | +36, | ||
394 | +27, | ||
395 | +12, | ||
396 | +0, | ||
397 | +27, | ||
398 | +0, | ||
399 | +250, | ||
400 | +244, | ||
401 | +12, | ||
402 | +250, | ||
403 | +4, | ||
404 | +0, | ||
405 | +0, | ||
406 | +0, | ||
407 | +248, | ||
408 | +0, | ||
409 | +0, | ||
410 | +40, | ||
411 | +0, | ||
412 | +4, | ||
413 | +250, | ||
414 | +12, | ||
415 | +244, | ||
416 | +250, | ||
417 | +0, | ||
418 | +27, | ||
419 | +0, | ||
420 | +12, | ||
421 | +27, | ||
422 | +36, | ||
423 | +247, | ||
424 | +244, | ||
425 | +0, | ||
426 | +247, | ||
427 | +0, | ||
428 | +0, | ||
429 | +40, | ||
430 | +0, | ||
431 | +0, | ||
432 | +248, | ||
433 | +0, | ||
434 | +0, | ||
435 | +0, | ||
436 | +0, | ||
437 | +247, | ||
438 | +0, | ||
439 | +244, | ||
440 | +247, | ||
441 | +36, | ||
442 | +27, | ||
443 | +12, | ||
444 | +0, | ||
445 | +27, | ||
446 | +0, | ||
447 | +250, | ||
448 | +244, | ||
449 | +12, | ||
450 | +250, | ||
451 | +4, | ||
452 | +0, | ||
453 | +0, | ||
454 | +0, | ||
455 | +248, | ||
456 | +0, | ||
457 | +0, | ||
458 | +40, | ||
459 | +0, | ||
460 | +4, | ||
461 | +250, | ||
462 | +12, | ||
463 | +244, | ||
464 | +250, | ||
465 | +0, | ||
466 | +27, | ||
467 | +0, | ||
468 | +12, | ||
469 | +27, | ||
470 | +36, | ||
471 | +247, | ||
472 | +244, | ||
473 | +0, | ||
474 | +247, | ||
475 | +0, | ||
476 | +0, | ||
477 | +40, | ||
478 | +0, | ||
479 | +0, | ||
480 | +248, | ||
481 | +0, | ||
482 | +0, | ||
483 | +0, | ||
484 | +4, | ||
485 | +250, | ||
486 | +12, | ||
487 | +244, | ||
488 | +250, | ||
489 | +0, | ||
490 | +27, | ||
491 | +0, | ||
492 | +12, | ||
493 | +27, | ||
494 | +36, | ||
495 | +247, | ||
496 | +244, | ||
497 | +0, | ||
498 | +247, | ||
499 | +0, | ||
500 | +0, | ||
501 | +0, | ||
502 | +0, | ||
503 | +248, | ||
504 | +0, | ||
505 | +0, | ||
506 | +40, | ||
507 | +0, | ||
508 | +0, | ||
509 | +247, | ||
510 | +0, | ||
511 | +244, | ||
512 | +247, | ||
513 | +36, | ||
514 | +27, | ||
515 | +12, | ||
516 | +0, | ||
517 | +27, | ||
518 | +0, | ||
519 | +250, | ||
520 | +244, | ||
521 | +12, | ||
522 | +250, | ||
523 | +4, | ||
524 | +0, | ||
525 | +40, | ||
526 | +0, | ||
527 | +0, | ||
528 | +248, | ||
529 | +0, | ||
530 | +0, | ||
531 | +0, | ||
532 | +4, | ||
533 | +250, | ||
534 | +12, | ||
535 | +244, | ||
536 | +250, | ||
537 | +0, | ||
538 | +27, | ||
539 | +0, | ||
540 | +12, | ||
541 | +27, | ||
542 | +36, | ||
543 | +247, | ||
544 | +244, | ||
545 | +0, | ||
546 | +247, | ||
547 | +0, | ||
548 | +0, | ||
549 | +0, | ||
550 | +0, | ||
551 | +248, | ||
552 | +0, | ||
553 | +0, | ||
554 | +40, | ||
555 | +0, | ||
556 | +0, | ||
557 | +247, | ||
558 | +0, | ||
559 | +244, | ||
560 | +247, | ||
561 | +36, | ||
562 | +27, | ||
563 | +12, | ||
564 | +0, | ||
565 | +27, | ||
566 | +0, | ||
567 | +250, | ||
568 | +244, | ||
569 | +12, | ||
570 | +250, | ||
571 | +4, | ||
572 | +0, | ||
573 | +40, | ||
574 | +0, | ||
575 | +0, | ||
576 | +248, | ||
577 | +0, | ||
578 | +0, | ||
579 | +0, | ||
580 | +4, | ||
581 | +250, | ||
582 | +12, | ||
583 | +244, | ||
584 | +250, | ||
585 | +0, | ||
586 | +27, | ||
587 | +0, | ||
588 | +12, | ||
589 | +27, | ||
590 | +36, | ||
591 | +247, | ||
592 | +244, | ||
593 | +0, | ||
594 | +247, | ||
595 | +0, | ||
596 | +0, | ||
597 | +0, | ||
598 | +0, | ||
599 | +248, | ||
600 | +0, | ||
601 | +0, | ||
602 | +40, | ||
603 | +0, | ||
604 | +0, | ||
605 | +247, | ||
606 | +0, | ||
607 | +244, | ||
608 | +247, | ||
609 | +36, | ||
610 | +27, | ||
611 | +12, | ||
612 | +0, | ||
613 | +27, | ||
614 | +0, | ||
615 | +250, | ||
616 | +244, | ||
617 | +12, | ||
618 | +250, | ||
619 | +4, | ||
620 | +0, | ||
621 | +40, | ||
622 | +0, | ||
623 | +0, | ||
624 | +248, | ||
625 | +0, | ||
626 | +0, | ||
627 | +0, | ||
628 | +244, | ||
629 | +12, | ||
630 | +250, | ||
631 | +4, | ||
632 | +0, | ||
633 | +27, | ||
634 | +0, | ||
635 | +250, | ||
636 | +247, | ||
637 | +36, | ||
638 | +27, | ||
639 | +12, | ||
640 | +0, | ||
641 | +247, | ||
642 | +0, | ||
643 | +244, | ||
644 | +248, | ||
645 | +0, | ||
646 | +0, | ||
647 | +0, | ||
648 | +0, | ||
649 | +40, | ||
650 | +0, | ||
651 | +0, | ||
652 | +244, | ||
653 | +0, | ||
654 | +247, | ||
655 | +0, | ||
656 | +12, | ||
657 | +27, | ||
658 | +36, | ||
659 | +247, | ||
660 | +250, | ||
661 | +0, | ||
662 | +27, | ||
663 | +0, | ||
664 | +4, | ||
665 | +250, | ||
666 | +12, | ||
667 | +244, | ||
668 | +0, | ||
669 | +0, | ||
670 | +40, | ||
671 | +0, | ||
672 | +0, | ||
673 | +0, | ||
674 | +0, | ||
675 | +248, | ||
676 | +244, | ||
677 | +12, | ||
678 | +250, | ||
679 | +4, | ||
680 | +0, | ||
681 | +27, | ||
682 | +0, | ||
683 | +250, | ||
684 | +247, | ||
685 | +36, | ||
686 | +27, | ||
687 | +12, | ||
688 | +0, | ||
689 | +247, | ||
690 | +0, | ||
691 | +244, | ||
692 | +248, | ||
693 | +0, | ||
694 | +0, | ||
695 | +0, | ||
696 | +0, | ||
697 | +40, | ||
698 | +0, | ||
699 | +0, | ||
700 | +244, | ||
701 | +0, | ||
702 | +247, | ||
703 | +0, | ||
704 | +12, | ||
705 | +27, | ||
706 | +36, | ||
707 | +247, | ||
708 | +250, | ||
709 | +0, | ||
710 | +27, | ||
711 | +0, | ||
712 | +4, | ||
713 | +250, | ||
714 | +12, | ||
715 | +244, | ||
716 | +0, | ||
717 | +0, | ||
718 | +40, | ||
719 | +0, | ||
720 | +0, | ||
721 | +0, | ||
722 | +0, | ||
723 | +248, | ||
724 | +244, | ||
725 | +12, | ||
726 | +250, | ||
727 | +4, | ||
728 | +0, | ||
729 | +27, | ||
730 | +0, | ||
731 | +250, | ||
732 | +247, | ||
733 | +36, | ||
734 | +27, | ||
735 | +12, | ||
736 | +0, | ||
737 | +247, | ||
738 | +0, | ||
739 | +244, | ||
740 | +248, | ||
741 | +0, | ||
742 | +0, | ||
743 | +0, | ||
744 | +0, | ||
745 | +40, | ||
746 | +0, | ||
747 | +0, | ||
748 | +244, | ||
749 | +0, | ||
750 | +247, | ||
751 | +0, | ||
752 | +12, | ||
753 | +27, | ||
754 | +36, | ||
755 | +247, | ||
756 | +250, | ||
757 | +0, | ||
758 | +27, | ||
759 | +0, | ||
760 | +4, | ||
761 | +250, | ||
762 | +12, | ||
763 | +244, | ||
764 | +0, | ||
765 | +0, | ||
766 | +40, | ||
767 | +0, | ||
768 | +0, | ||
769 | +0, | ||
770 | +0, | ||
771 | +248 | ||
772 | diff --git a/drivers/media/video/isp/gamma_table.h b/drivers/media/video/isp/gamma_table.h | ||
773 | new file mode 100644 | ||
774 | index 0000000..c2f7ec1 | ||
775 | --- /dev/null | ||
776 | +++ b/drivers/media/video/isp/gamma_table.h | ||
777 | @@ -0,0 +1,90 @@ | ||
778 | +/* | ||
779 | + * gamma_table.h | ||
780 | + * | ||
781 | + * TI OMAP3 ISP - Default gamma table for all components | ||
782 | + * | ||
783 | + * Copyright (C) 2010 Nokia Corporation | ||
784 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
785 | + * | ||
786 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
787 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
788 | + * | ||
789 | + * This program is free software; you can redistribute it and/or modify | ||
790 | + * it under the terms of the GNU General Public License version 2 as | ||
791 | + * published by the Free Software Foundation. | ||
792 | + * | ||
793 | + * This program is distributed in the hope that it will be useful, but | ||
794 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
795 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
796 | + * General Public License for more details. | ||
797 | + * | ||
798 | + * You should have received a copy of the GNU General Public License | ||
799 | + * along with this program; if not, write to the Free Software | ||
800 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
801 | + * 02110-1301 USA | ||
802 | + */ | ||
803 | + | ||
804 | + 0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, | ||
805 | + 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 36, 37, 39, 40, 41, 42, | ||
806 | + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 52, 53, 54, 55, 56, 57, | ||
807 | + 58, 59, 60, 61, 62, 63, 63, 64, 65, 66, 66, 67, 68, 69, 69, 70, | ||
808 | + 71, 72, 72, 73, 74, 75, 75, 76, 77, 78, 78, 79, 80, 81, 81, 82, | ||
809 | + 83, 84, 84, 85, 86, 87, 88, 88, 89, 90, 91, 91, 92, 93, 94, 94, | ||
810 | + 95, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 103, 104, 104, | ||
811 | +105, 106, 107, 108, 108, 109, 110, 111, 111, 112, 113, 114, 114, 115, 116, 117, | ||
812 | +117, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, | ||
813 | +126, 126, 127, 127, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, | ||
814 | +134, 134, 135, 135, 136, 136, 137, 137, 138, 138, 139, 139, 140, 140, 141, 141, | ||
815 | +142, 142, 143, 143, 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, 149, 149, | ||
816 | +150, 150, 151, 151, 152, 152, 153, 153, 153, 153, 154, 154, 154, 154, 155, 155, | ||
817 | +156, 156, 157, 157, 158, 158, 158, 159, 159, 159, 160, 160, 160, 161, 161, 162, | ||
818 | +162, 163, 163, 164, 164, 164, 164, 165, 165, 165, 165, 166, 166, 167, 167, 168, | ||
819 | +168, 169, 169, 170, 170, 170, 170, 171, 171, 171, 171, 172, 172, 173, 173, 174, | ||
820 | +174, 175, 175, 176, 176, 176, 176, 177, 177, 177, 177, 178, 178, 178, 178, 179, | ||
821 | +179, 179, 179, 180, 180, 180, 180, 181, 181, 181, 181, 182, 182, 182, 182, 183, | ||
822 | +183, 183, 183, 184, 184, 184, 184, 185, 185, 185, 185, 186, 186, 186, 186, 187, | ||
823 | +187, 187, 187, 188, 188, 188, 188, 189, 189, 189, 189, 190, 190, 190, 190, 191, | ||
824 | +191, 191, 191, 192, 192, 192, 192, 193, 193, 193, 193, 194, 194, 194, 194, 195, | ||
825 | +195, 195, 195, 196, 196, 196, 196, 197, 197, 197, 197, 198, 198, 198, 198, 199, | ||
826 | +199, 199, 199, 200, 200, 200, 200, 201, 201, 201, 201, 202, 202, 202, 203, 203, | ||
827 | +203, 203, 204, 204, 204, 204, 205, 205, 205, 205, 206, 206, 206, 206, 207, 207, | ||
828 | +207, 207, 208, 208, 208, 208, 209, 209, 209, 209, 210, 210, 210, 210, 210, 210, | ||
829 | +210, 210, 210, 210, 210, 210, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, | ||
830 | +211, 212, 212, 212, 212, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, | ||
831 | +213, 214, 214, 214, 214, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, | ||
832 | +216, 216, 216, 216, 217, 217, 217, 217, 218, 218, 218, 218, 219, 219, 219, 219, | ||
833 | +219, 219, 219, 219, 219, 219, 219, 219, 220, 220, 220, 220, 221, 221, 221, 221, | ||
834 | +221, 221, 221, 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 223, | ||
835 | +223, 223, 223, 223, 223, 223, 223, 224, 224, 224, 224, 225, 225, 225, 225, 225, | ||
836 | +225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 226, 226, | ||
837 | +226, 226, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 228, 228, | ||
838 | +228, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 230, 230, 230, | ||
839 | +230, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 232, 232, 232, | ||
840 | +232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, | ||
841 | +233, 233, 233, 233, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 235, | ||
842 | +235, 235, 235, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, | ||
843 | +236, 236, 236, 236, 236, 236, 237, 237, 237, 237, 238, 238, 238, 238, 238, 238, | ||
844 | +238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, | ||
845 | +238, 238, 238, 238, 238, 239, 239, 239, 239, 240, 240, 240, 240, 240, 240, 240, | ||
846 | +240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, | ||
847 | +240, 240, 240, 240, 241, 241, 241, 241, 242, 242, 242, 242, 242, 242, 242, 242, | ||
848 | +242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, | ||
849 | +242, 242, 243, 243, 243, 243, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, | ||
850 | +244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, | ||
851 | +244, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, | ||
852 | +246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, | ||
853 | +246, 246, 246, 246, 246, 246, 246, 247, 247, 247, 247, 248, 248, 248, 248, 248, | ||
854 | +248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, | ||
855 | +248, 248, 248, 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 250, 250, | ||
856 | +250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, | ||
857 | +250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, | ||
858 | +250, 250, 250, 250, 251, 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252, | ||
859 | +252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, | ||
860 | +252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, | ||
861 | +252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, | ||
862 | +252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253, 253, | ||
863 | +253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, | ||
864 | +253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, | ||
865 | +253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, | ||
866 | +253, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||
867 | +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||
868 | diff --git a/drivers/media/video/isp/isp.c b/drivers/media/video/isp/isp.c | ||
869 | new file mode 100644 | ||
870 | index 0000000..6f8527c | ||
871 | --- /dev/null | ||
872 | +++ b/drivers/media/video/isp/isp.c | ||
873 | @@ -0,0 +1,2221 @@ | ||
874 | +/* | ||
875 | + * isp.c | ||
876 | + * | ||
877 | + * TI OMAP3 ISP - Core | ||
878 | + * | ||
879 | + * Copyright (C) 2006-2010 Nokia Corporation | ||
880 | + * Copyright (C) 2007-2009 Texas Instruments, Inc. | ||
881 | + * | ||
882 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
883 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
884 | + * | ||
885 | + * Contributors: | ||
886 | + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
887 | + * Sakari Ailus <sakari.ailus@nokia.com> | ||
888 | + * David Cohen <david.cohen@nokia.com> | ||
889 | + * Stanimir Varbanov <svarbanov@mm-sol.com> | ||
890 | + * Vimarsh Zutshi <vimarsh.zutshi@nokia.com> | ||
891 | + * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> | ||
892 | + * Sergio Aguirre <saaguirre@ti.com> | ||
893 | + * Antti Koskipaa <antti.koskipaa@nokia.com> | ||
894 | + * Ivan T. Ivanov <iivanov@mm-sol.com> | ||
895 | + * RaniSuneela <r-m@ti.com> | ||
896 | + * Atanas Filipov <afilipov@mm-sol.com> | ||
897 | + * Gjorgji Rosikopulos <grosikopulos@mm-sol.com> | ||
898 | + * Hiroshi DOYU <hiroshi.doyu@nokia.com> | ||
899 | + * Nayden Kanchev <nkanchev@mm-sol.com> | ||
900 | + * Phil Carmody <ext-phil.2.carmody@nokia.com> | ||
901 | + * Artem Bityutskiy <artem.bityutskiy@nokia.com> | ||
902 | + * Dominic Curran <dcurran@ti.com> | ||
903 | + * Ilkka Myllyperkio <ilkka.myllyperkio@sofica.fi> | ||
904 | + * Pallavi Kulkarni <p-kulkarni@ti.com> | ||
905 | + * Vaibhav Hiremath <hvaibhav@ti.com> | ||
906 | + * Mohit Jalori <mjalori@ti.com> | ||
907 | + * Sameer Venkatraman <sameerv@ti.com> | ||
908 | + * Senthilvadivu Guruswamy <svadivu@ti.com> | ||
909 | + * Thara Gopinath <thara@ti.com> | ||
910 | + * Toni Leinonen <toni.leinonen@nokia.com> | ||
911 | + * Troy Laramy <t-laramy@ti.com> | ||
912 | + * | ||
913 | + * This program is free software; you can redistribute it and/or modify | ||
914 | + * it under the terms of the GNU General Public License version 2 as | ||
915 | + * published by the Free Software Foundation. | ||
916 | + * | ||
917 | + * This program is distributed in the hope that it will be useful, but | ||
918 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
919 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
920 | + * General Public License for more details. | ||
921 | + * | ||
922 | + * You should have received a copy of the GNU General Public License | ||
923 | + * along with this program; if not, write to the Free Software | ||
924 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
925 | + * 02110-1301 USA | ||
926 | + */ | ||
927 | + | ||
928 | +#include <asm/cacheflush.h> | ||
929 | + | ||
930 | +#include <linux/clk.h> | ||
931 | +#include <linux/delay.h> | ||
932 | +#include <linux/device.h> | ||
933 | +#include <linux/dma-mapping.h> | ||
934 | +#include <linux/i2c.h> | ||
935 | +#include <linux/interrupt.h> | ||
936 | +#include <linux/module.h> | ||
937 | +#include <linux/platform_device.h> | ||
938 | +#include <linux/regulator/consumer.h> | ||
939 | +#include <linux/slab.h> | ||
940 | +#include <linux/sched.h> | ||
941 | +#include <linux/vmalloc.h> | ||
942 | + | ||
943 | +#include <media/v4l2-common.h> | ||
944 | +#include <media/v4l2-device.h> | ||
945 | + | ||
946 | +#include "isp.h" | ||
947 | +#include "ispreg.h" | ||
948 | +#include "ispccdc.h" | ||
949 | +#include "isppreview.h" | ||
950 | +#include "ispresizer.h" | ||
951 | +#include "ispcsi2.h" | ||
952 | +#include "ispccp2.h" | ||
953 | +#include "isph3a.h" | ||
954 | +#include "isphist.h" | ||
955 | + | ||
956 | +static unsigned int autoidle; | ||
957 | +module_param(autoidle, int, 0444); | ||
958 | +MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support"); | ||
959 | + | ||
960 | +static void isp_save_ctx(struct isp_device *isp); | ||
961 | + | ||
962 | +static void isp_restore_ctx(struct isp_device *isp); | ||
963 | + | ||
964 | +static const struct isp_res_mapping isp_res_maps[] = { | ||
965 | + { | ||
966 | + .isp_rev = ISP_REVISION_2_0, | ||
967 | + .map = 1 << OMAP3_ISP_IOMEM_MAIN | | ||
968 | + 1 << OMAP3_ISP_IOMEM_CCP2 | | ||
969 | + 1 << OMAP3_ISP_IOMEM_CCDC | | ||
970 | + 1 << OMAP3_ISP_IOMEM_HIST | | ||
971 | + 1 << OMAP3_ISP_IOMEM_H3A | | ||
972 | + 1 << OMAP3_ISP_IOMEM_PREV | | ||
973 | + 1 << OMAP3_ISP_IOMEM_RESZ | | ||
974 | + 1 << OMAP3_ISP_IOMEM_SBL | | ||
975 | + 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 | | ||
976 | + 1 << OMAP3_ISP_IOMEM_CSIPHY2, | ||
977 | + }, | ||
978 | + { | ||
979 | + .isp_rev = ISP_REVISION_15_0, | ||
980 | + .map = 1 << OMAP3_ISP_IOMEM_MAIN | | ||
981 | + 1 << OMAP3_ISP_IOMEM_CCP2 | | ||
982 | + 1 << OMAP3_ISP_IOMEM_CCDC | | ||
983 | + 1 << OMAP3_ISP_IOMEM_HIST | | ||
984 | + 1 << OMAP3_ISP_IOMEM_H3A | | ||
985 | + 1 << OMAP3_ISP_IOMEM_PREV | | ||
986 | + 1 << OMAP3_ISP_IOMEM_RESZ | | ||
987 | + 1 << OMAP3_ISP_IOMEM_SBL | | ||
988 | + 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 | | ||
989 | + 1 << OMAP3_ISP_IOMEM_CSIPHY2 | | ||
990 | + 1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 | | ||
991 | + 1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 | | ||
992 | + 1 << OMAP3_ISP_IOMEM_CSIPHY1 | | ||
993 | + 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2, | ||
994 | + }, | ||
995 | +}; | ||
996 | + | ||
997 | +/* Structure for saving/restoring ISP module registers */ | ||
998 | +static struct isp_reg isp_reg_list[] = { | ||
999 | + {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0}, | ||
1000 | + {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0}, | ||
1001 | + {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0}, | ||
1002 | + {0, ISP_TOK_TERM, 0} | ||
1003 | +}; | ||
1004 | + | ||
1005 | +/* | ||
1006 | + * omap3isp_flush - Post pending L3 bus writes by doing a register readback | ||
1007 | + * @isp: OMAP3 ISP device | ||
1008 | + * | ||
1009 | + * In order to force posting of pending writes, we need to write and | ||
1010 | + * readback the same register, in this case the revision register. | ||
1011 | + * | ||
1012 | + * See this link for reference: | ||
1013 | + * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html | ||
1014 | + */ | ||
1015 | +void omap3isp_flush(struct isp_device *isp) | ||
1016 | +{ | ||
1017 | + isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); | ||
1018 | + isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); | ||
1019 | +} | ||
1020 | + | ||
1021 | +/* | ||
1022 | + * isp_enable_interrupts - Enable ISP interrupts. | ||
1023 | + * @isp: OMAP3 ISP device | ||
1024 | + */ | ||
1025 | +static void isp_enable_interrupts(struct isp_device *isp) | ||
1026 | +{ | ||
1027 | + static const u32 irq = IRQ0ENABLE_CSIA_IRQ | ||
1028 | + | IRQ0ENABLE_CSIB_IRQ | ||
1029 | + | IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ | ||
1030 | + | IRQ0ENABLE_CCDC_LSC_DONE_IRQ | ||
1031 | + | IRQ0ENABLE_CCDC_VD0_IRQ | ||
1032 | + | IRQ0ENABLE_CCDC_VD1_IRQ | ||
1033 | + | IRQ0ENABLE_HS_VS_IRQ | ||
1034 | + | IRQ0ENABLE_HIST_DONE_IRQ | ||
1035 | + | IRQ0ENABLE_H3A_AWB_DONE_IRQ | ||
1036 | + | IRQ0ENABLE_H3A_AF_DONE_IRQ | ||
1037 | + | IRQ0ENABLE_PRV_DONE_IRQ | ||
1038 | + | IRQ0ENABLE_RSZ_DONE_IRQ; | ||
1039 | + | ||
1040 | + isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); | ||
1041 | + isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); | ||
1042 | +} | ||
1043 | + | ||
1044 | +/* | ||
1045 | + * isp_disable_interrupts - Disable ISP interrupts. | ||
1046 | + * @isp: OMAP3 ISP device | ||
1047 | + */ | ||
1048 | +static void isp_disable_interrupts(struct isp_device *isp) | ||
1049 | +{ | ||
1050 | + isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); | ||
1051 | +} | ||
1052 | + | ||
1053 | +/** | ||
1054 | + * isp_set_xclk - Configures the specified cam_xclk to the desired frequency. | ||
1055 | + * @isp: OMAP3 ISP device | ||
1056 | + * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high | ||
1057 | + * @xclksel: XCLK to configure (0 = A, 1 = B). | ||
1058 | + * | ||
1059 | + * Configures the specified MCLK divisor in the ISP timing control register | ||
1060 | + * (TCTRL_CTRL) to generate the desired xclk clock value. | ||
1061 | + * | ||
1062 | + * Divisor = cam_mclk_hz / xclk | ||
1063 | + * | ||
1064 | + * Returns the final frequency that is actually being generated | ||
1065 | + **/ | ||
1066 | +static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel) | ||
1067 | +{ | ||
1068 | + u32 divisor; | ||
1069 | + u32 currentxclk; | ||
1070 | + unsigned long mclk_hz; | ||
1071 | + | ||
1072 | + if (!omap3isp_get(isp)) | ||
1073 | + return 0; | ||
1074 | + | ||
1075 | + mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); | ||
1076 | + | ||
1077 | + if (xclk >= mclk_hz) { | ||
1078 | + divisor = ISPTCTRL_CTRL_DIV_BYPASS; | ||
1079 | + currentxclk = mclk_hz; | ||
1080 | + } else if (xclk >= 2) { | ||
1081 | + divisor = mclk_hz / xclk; | ||
1082 | + if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS) | ||
1083 | + divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1; | ||
1084 | + currentxclk = mclk_hz / divisor; | ||
1085 | + } else { | ||
1086 | + divisor = xclk; | ||
1087 | + currentxclk = 0; | ||
1088 | + } | ||
1089 | + | ||
1090 | + switch (xclksel) { | ||
1091 | + case 0: | ||
1092 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, | ||
1093 | + ISPTCTRL_CTRL_DIVA_MASK, | ||
1094 | + divisor << ISPTCTRL_CTRL_DIVA_SHIFT); | ||
1095 | + dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n", | ||
1096 | + currentxclk); | ||
1097 | + break; | ||
1098 | + case 1: | ||
1099 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, | ||
1100 | + ISPTCTRL_CTRL_DIVB_MASK, | ||
1101 | + divisor << ISPTCTRL_CTRL_DIVB_SHIFT); | ||
1102 | + dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n", | ||
1103 | + currentxclk); | ||
1104 | + break; | ||
1105 | + default: | ||
1106 | + omap3isp_put(isp); | ||
1107 | + dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested " | ||
1108 | + "xclk. Must be 0 (A) or 1 (B).\n"); | ||
1109 | + return -EINVAL; | ||
1110 | + } | ||
1111 | + | ||
1112 | + /* Do we go from stable whatever to clock? */ | ||
1113 | + if (divisor >= 2 && isp->xclk_divisor[xclksel] < 2) | ||
1114 | + omap3isp_get(isp); | ||
1115 | + /* Stopping the clock. */ | ||
1116 | + else if (divisor < 2 && isp->xclk_divisor[xclksel] >= 2) | ||
1117 | + omap3isp_put(isp); | ||
1118 | + | ||
1119 | + isp->xclk_divisor[xclksel] = divisor; | ||
1120 | + | ||
1121 | + omap3isp_put(isp); | ||
1122 | + | ||
1123 | + return currentxclk; | ||
1124 | +} | ||
1125 | + | ||
1126 | +/* | ||
1127 | + * isp_power_settings - Sysconfig settings, for Power Management. | ||
1128 | + * @isp: OMAP3 ISP device | ||
1129 | + * @idle: Consider idle state. | ||
1130 | + * | ||
1131 | + * Sets the power settings for the ISP, and SBL bus. | ||
1132 | + */ | ||
1133 | +static void isp_power_settings(struct isp_device *isp, int idle) | ||
1134 | +{ | ||
1135 | + isp_reg_writel(isp, | ||
1136 | + ((idle ? ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY : | ||
1137 | + ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY) << | ||
1138 | + ISP_SYSCONFIG_MIDLEMODE_SHIFT) | | ||
1139 | + ((isp->revision == ISP_REVISION_15_0) ? | ||
1140 | + ISP_SYSCONFIG_AUTOIDLE : 0), | ||
1141 | + OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); | ||
1142 | + | ||
1143 | + if (isp->autoidle) | ||
1144 | + isp_reg_writel(isp, ISPCTRL_SBL_AUTOIDLE, OMAP3_ISP_IOMEM_MAIN, | ||
1145 | + ISP_CTRL); | ||
1146 | +} | ||
1147 | + | ||
1148 | +/* | ||
1149 | + * Configure the bridge and lane shifter. Valid inputs are | ||
1150 | + * | ||
1151 | + * CCDC_INPUT_PARALLEL: Parallel interface | ||
1152 | + * CCDC_INPUT_CSI2A: CSI2a receiver | ||
1153 | + * CCDC_INPUT_CCP2B: CCP2b receiver | ||
1154 | + * CCDC_INPUT_CSI2C: CSI2c receiver | ||
1155 | + * | ||
1156 | + * The bridge and lane shifter are configured according to the selected input | ||
1157 | + * and the ISP platform data. | ||
1158 | + */ | ||
1159 | +void omap3isp_configure_bridge(struct isp_device *isp, | ||
1160 | + enum ccdc_input_entity input, | ||
1161 | + const struct isp_parallel_platform_data *pdata) | ||
1162 | +{ | ||
1163 | + u32 ispctrl_val; | ||
1164 | + | ||
1165 | + ispctrl_val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); | ||
1166 | + ispctrl_val &= ~ISPCTRL_SHIFT_MASK; | ||
1167 | + ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV; | ||
1168 | + ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK; | ||
1169 | + ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK; | ||
1170 | + | ||
1171 | + switch (input) { | ||
1172 | + case CCDC_INPUT_PARALLEL: | ||
1173 | + ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL; | ||
1174 | + ispctrl_val |= pdata->data_lane_shift << ISPCTRL_SHIFT_SHIFT; | ||
1175 | + ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT; | ||
1176 | + ispctrl_val |= pdata->bridge << ISPCTRL_PAR_BRIDGE_SHIFT; | ||
1177 | + break; | ||
1178 | + | ||
1179 | + case CCDC_INPUT_CSI2A: | ||
1180 | + ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA; | ||
1181 | + break; | ||
1182 | + | ||
1183 | + case CCDC_INPUT_CCP2B: | ||
1184 | + ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB; | ||
1185 | + break; | ||
1186 | + | ||
1187 | + case CCDC_INPUT_CSI2C: | ||
1188 | + ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIC; | ||
1189 | + break; | ||
1190 | + | ||
1191 | + default: | ||
1192 | + return; | ||
1193 | + } | ||
1194 | + | ||
1195 | + ispctrl_val &= ~ISPCTRL_SYNC_DETECT_MASK; | ||
1196 | + ispctrl_val |= ISPCTRL_SYNC_DETECT_VSRISE; | ||
1197 | + | ||
1198 | + isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); | ||
1199 | +} | ||
1200 | + | ||
1201 | +/** | ||
1202 | + * isp_set_pixel_clock - Configures the ISP pixel clock | ||
1203 | + * @isp: OMAP3 ISP device | ||
1204 | + * @pixelclk: Average pixel clock in Hz | ||
1205 | + * | ||
1206 | + * Set the average pixel clock required by the sensor. The ISP will use the | ||
1207 | + * lowest possible memory bandwidth settings compatible with the clock. | ||
1208 | + **/ | ||
1209 | +static void isp_set_pixel_clock(struct isp_device *isp, unsigned int pixelclk) | ||
1210 | +{ | ||
1211 | + isp->isp_ccdc.vpcfg.pixelclk = pixelclk; | ||
1212 | +} | ||
1213 | + | ||
1214 | +void omap3isp_hist_dma_done(struct isp_device *isp) | ||
1215 | +{ | ||
1216 | + if (omap3isp_ccdc_busy(&isp->isp_ccdc) || | ||
1217 | + omap3isp_stat_pcr_busy(&isp->isp_hist)) { | ||
1218 | + /* Histogram cannot be enabled in this frame anymore */ | ||
1219 | + atomic_set(&isp->isp_hist.buf_err, 1); | ||
1220 | + dev_dbg(isp->dev, "hist: Out of synchronization with " | ||
1221 | + "CCDC. Ignoring next buffer.\n"); | ||
1222 | + } | ||
1223 | +} | ||
1224 | + | ||
1225 | +static inline void isp_isr_dbg(struct isp_device *isp, u32 irqstatus) | ||
1226 | +{ | ||
1227 | + static const char *name[] = { | ||
1228 | + "CSIA_IRQ", | ||
1229 | + "res1", | ||
1230 | + "res2", | ||
1231 | + "CSIB_LCM_IRQ", | ||
1232 | + "CSIB_IRQ", | ||
1233 | + "res5", | ||
1234 | + "res6", | ||
1235 | + "res7", | ||
1236 | + "CCDC_VD0_IRQ", | ||
1237 | + "CCDC_VD1_IRQ", | ||
1238 | + "CCDC_VD2_IRQ", | ||
1239 | + "CCDC_ERR_IRQ", | ||
1240 | + "H3A_AF_DONE_IRQ", | ||
1241 | + "H3A_AWB_DONE_IRQ", | ||
1242 | + "res14", | ||
1243 | + "res15", | ||
1244 | + "HIST_DONE_IRQ", | ||
1245 | + "CCDC_LSC_DONE", | ||
1246 | + "CCDC_LSC_PREFETCH_COMPLETED", | ||
1247 | + "CCDC_LSC_PREFETCH_ERROR", | ||
1248 | + "PRV_DONE_IRQ", | ||
1249 | + "CBUFF_IRQ", | ||
1250 | + "res22", | ||
1251 | + "res23", | ||
1252 | + "RSZ_DONE_IRQ", | ||
1253 | + "OVF_IRQ", | ||
1254 | + "res26", | ||
1255 | + "res27", | ||
1256 | + "MMU_ERR_IRQ", | ||
1257 | + "OCP_ERR_IRQ", | ||
1258 | + "SEC_ERR_IRQ", | ||
1259 | + "HS_VS_IRQ", | ||
1260 | + }; | ||
1261 | + int i; | ||
1262 | + | ||
1263 | + dev_dbg(isp->dev, ""); | ||
1264 | + | ||
1265 | + for (i = 0; i < ARRAY_SIZE(name); i++) { | ||
1266 | + if ((1 << i) & irqstatus) | ||
1267 | + printk(KERN_CONT "%s ", name[i]); | ||
1268 | + } | ||
1269 | + printk(KERN_CONT "\n"); | ||
1270 | +} | ||
1271 | + | ||
1272 | +static void isp_isr_sbl(struct isp_device *isp) | ||
1273 | +{ | ||
1274 | + struct device *dev = isp->dev; | ||
1275 | + u32 sbl_pcr; | ||
1276 | + | ||
1277 | + /* | ||
1278 | + * Handle shared buffer logic overflows for video buffers. | ||
1279 | + * ISPSBL_PCR_CCDCPRV_2_RSZ_OVF can be safely ignored. | ||
1280 | + */ | ||
1281 | + sbl_pcr = isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); | ||
1282 | + isp_reg_writel(isp, sbl_pcr, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); | ||
1283 | + sbl_pcr &= ~ISPSBL_PCR_CCDCPRV_2_RSZ_OVF; | ||
1284 | + | ||
1285 | + if (sbl_pcr) | ||
1286 | + dev_dbg(dev, "SBL overflow (PCR = 0x%08x)\n", sbl_pcr); | ||
1287 | + | ||
1288 | + if (sbl_pcr & (ISPSBL_PCR_CCDC_WBL_OVF | ISPSBL_PCR_CSIA_WBL_OVF | ||
1289 | + | ISPSBL_PCR_CSIB_WBL_OVF)) { | ||
1290 | + isp->isp_ccdc.error = 1; | ||
1291 | + if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW) | ||
1292 | + isp->isp_prev.error = 1; | ||
1293 | + if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER) | ||
1294 | + isp->isp_res.error = 1; | ||
1295 | + } | ||
1296 | + | ||
1297 | + if (sbl_pcr & ISPSBL_PCR_PRV_WBL_OVF) { | ||
1298 | + isp->isp_prev.error = 1; | ||
1299 | + if (isp->isp_res.input == RESIZER_INPUT_VP && | ||
1300 | + !(isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER)) | ||
1301 | + isp->isp_res.error = 1; | ||
1302 | + } | ||
1303 | + | ||
1304 | + if (sbl_pcr & (ISPSBL_PCR_RSZ1_WBL_OVF | ||
1305 | + | ISPSBL_PCR_RSZ2_WBL_OVF | ||
1306 | + | ISPSBL_PCR_RSZ3_WBL_OVF | ||
1307 | + | ISPSBL_PCR_RSZ4_WBL_OVF)) | ||
1308 | + isp->isp_res.error = 1; | ||
1309 | + | ||
1310 | + if (sbl_pcr & ISPSBL_PCR_H3A_AF_WBL_OVF) | ||
1311 | + omap3isp_stat_sbl_overflow(&isp->isp_af); | ||
1312 | + | ||
1313 | + if (sbl_pcr & ISPSBL_PCR_H3A_AEAWB_WBL_OVF) | ||
1314 | + omap3isp_stat_sbl_overflow(&isp->isp_aewb); | ||
1315 | +} | ||
1316 | + | ||
1317 | +/* | ||
1318 | + * isp_isr - Interrupt Service Routine for Camera ISP module. | ||
1319 | + * @irq: Not used currently. | ||
1320 | + * @_isp: Pointer to the OMAP3 ISP device | ||
1321 | + * | ||
1322 | + * Handles the corresponding callback if plugged in. | ||
1323 | + * | ||
1324 | + * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the | ||
1325 | + * IRQ wasn't handled. | ||
1326 | + */ | ||
1327 | +static irqreturn_t isp_isr(int irq, void *_isp) | ||
1328 | +{ | ||
1329 | + static const u32 ccdc_events = IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ | | ||
1330 | + IRQ0STATUS_CCDC_LSC_DONE_IRQ | | ||
1331 | + IRQ0STATUS_CCDC_VD0_IRQ | | ||
1332 | + IRQ0STATUS_CCDC_VD1_IRQ | | ||
1333 | + IRQ0STATUS_HS_VS_IRQ; | ||
1334 | + struct isp_device *isp = _isp; | ||
1335 | + u32 irqstatus; | ||
1336 | + int ret; | ||
1337 | + | ||
1338 | + irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); | ||
1339 | + isp_reg_writel(isp, irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); | ||
1340 | + | ||
1341 | + isp_isr_sbl(isp); | ||
1342 | + | ||
1343 | + if (irqstatus & IRQ0STATUS_CSIA_IRQ) { | ||
1344 | + ret = omap3isp_csi2_isr(&isp->isp_csi2a); | ||
1345 | + if (ret) | ||
1346 | + isp->isp_ccdc.error = 1; | ||
1347 | + } | ||
1348 | + | ||
1349 | + if (irqstatus & IRQ0STATUS_CSIB_IRQ) { | ||
1350 | + ret = omap3isp_ccp2_isr(&isp->isp_ccp2); | ||
1351 | + if (ret) | ||
1352 | + isp->isp_ccdc.error = 1; | ||
1353 | + } | ||
1354 | + | ||
1355 | + if (irqstatus & IRQ0STATUS_CCDC_VD0_IRQ) { | ||
1356 | + if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW) | ||
1357 | + omap3isp_preview_isr_frame_sync(&isp->isp_prev); | ||
1358 | + if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER) | ||
1359 | + omap3isp_resizer_isr_frame_sync(&isp->isp_res); | ||
1360 | + omap3isp_stat_isr_frame_sync(&isp->isp_aewb); | ||
1361 | + omap3isp_stat_isr_frame_sync(&isp->isp_af); | ||
1362 | + omap3isp_stat_isr_frame_sync(&isp->isp_hist); | ||
1363 | + } | ||
1364 | + | ||
1365 | + if (irqstatus & ccdc_events) | ||
1366 | + omap3isp_ccdc_isr(&isp->isp_ccdc, irqstatus & ccdc_events); | ||
1367 | + | ||
1368 | + if (irqstatus & IRQ0STATUS_PRV_DONE_IRQ) { | ||
1369 | + if (isp->isp_prev.output & PREVIEW_OUTPUT_RESIZER) | ||
1370 | + omap3isp_resizer_isr_frame_sync(&isp->isp_res); | ||
1371 | + omap3isp_preview_isr(&isp->isp_prev); | ||
1372 | + } | ||
1373 | + | ||
1374 | + if (irqstatus & IRQ0STATUS_RSZ_DONE_IRQ) | ||
1375 | + omap3isp_resizer_isr(&isp->isp_res); | ||
1376 | + | ||
1377 | + if (irqstatus & IRQ0STATUS_H3A_AWB_DONE_IRQ) | ||
1378 | + omap3isp_stat_isr(&isp->isp_aewb); | ||
1379 | + | ||
1380 | + if (irqstatus & IRQ0STATUS_H3A_AF_DONE_IRQ) | ||
1381 | + omap3isp_stat_isr(&isp->isp_af); | ||
1382 | + | ||
1383 | + if (irqstatus & IRQ0STATUS_HIST_DONE_IRQ) | ||
1384 | + omap3isp_stat_isr(&isp->isp_hist); | ||
1385 | + | ||
1386 | + omap3isp_flush(isp); | ||
1387 | + | ||
1388 | +#if defined(DEBUG) && defined(ISP_ISR_DEBUG) | ||
1389 | + isp_isr_dbg(isp, irqstatus); | ||
1390 | +#endif | ||
1391 | + | ||
1392 | + return IRQ_HANDLED; | ||
1393 | +} | ||
1394 | + | ||
1395 | +/* ----------------------------------------------------------------------------- | ||
1396 | + * Pipeline power management | ||
1397 | + * | ||
1398 | + * Entities must be powered up when part of a pipeline that contains at least | ||
1399 | + * one open video device node. | ||
1400 | + * | ||
1401 | + * To achieve this use the entity use_count field to track the number of users. | ||
1402 | + * For entities corresponding to video device nodes the use_count field stores | ||
1403 | + * the users count of the node. For entities corresponding to subdevs the | ||
1404 | + * use_count field stores the total number of users of all video device nodes | ||
1405 | + * in the pipeline. | ||
1406 | + * | ||
1407 | + * The omap3isp_pipeline_pm_use() function must be called in the open() and | ||
1408 | + * close() handlers of video device nodes. It increments or decrements the use | ||
1409 | + * count of all subdev entities in the pipeline. | ||
1410 | + * | ||
1411 | + * To react to link management on powered pipelines, the link setup notification | ||
1412 | + * callback updates the use count of all entities in the source and sink sides | ||
1413 | + * of the link. | ||
1414 | + */ | ||
1415 | + | ||
1416 | +/* | ||
1417 | + * isp_pipeline_pm_use_count - Count the number of users of a pipeline | ||
1418 | + * @entity: The entity | ||
1419 | + * | ||
1420 | + * Return the total number of users of all video device nodes in the pipeline. | ||
1421 | + */ | ||
1422 | +static int isp_pipeline_pm_use_count(struct media_entity *entity) | ||
1423 | +{ | ||
1424 | + struct media_entity_graph graph; | ||
1425 | + int use = 0; | ||
1426 | + | ||
1427 | + media_entity_graph_walk_start(&graph, entity); | ||
1428 | + | ||
1429 | + while ((entity = media_entity_graph_walk_next(&graph))) { | ||
1430 | + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) | ||
1431 | + use += entity->use_count; | ||
1432 | + } | ||
1433 | + | ||
1434 | + return use; | ||
1435 | +} | ||
1436 | + | ||
1437 | +/* | ||
1438 | + * isp_pipeline_pm_power_one - Apply power change to an entity | ||
1439 | + * @entity: The entity | ||
1440 | + * @change: Use count change | ||
1441 | + * | ||
1442 | + * Change the entity use count by @change. If the entity is a subdev update its | ||
1443 | + * power state by calling the core::s_power operation when the use count goes | ||
1444 | + * from 0 to != 0 or from != 0 to 0. | ||
1445 | + * | ||
1446 | + * Return 0 on success or a negative error code on failure. | ||
1447 | + */ | ||
1448 | +static int isp_pipeline_pm_power_one(struct media_entity *entity, int change) | ||
1449 | +{ | ||
1450 | + struct v4l2_subdev *subdev; | ||
1451 | + int ret; | ||
1452 | + | ||
1453 | + subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV | ||
1454 | + ? media_entity_to_v4l2_subdev(entity) : NULL; | ||
1455 | + | ||
1456 | + if (entity->use_count == 0 && change > 0 && subdev != NULL) { | ||
1457 | + ret = v4l2_subdev_call(subdev, core, s_power, 1); | ||
1458 | + if (ret < 0 && ret != -ENOIOCTLCMD) | ||
1459 | + return ret; | ||
1460 | + } | ||
1461 | + | ||
1462 | + entity->use_count += change; | ||
1463 | + WARN_ON(entity->use_count < 0); | ||
1464 | + | ||
1465 | + if (entity->use_count == 0 && change < 0 && subdev != NULL) | ||
1466 | + v4l2_subdev_call(subdev, core, s_power, 0); | ||
1467 | + | ||
1468 | + return 0; | ||
1469 | +} | ||
1470 | + | ||
1471 | +/* | ||
1472 | + * isp_pipeline_pm_power - Apply power change to all entities in a pipeline | ||
1473 | + * @entity: The entity | ||
1474 | + * @change: Use count change | ||
1475 | + * | ||
1476 | + * Walk the pipeline to update the use count and the power state of all non-node | ||
1477 | + * entities. | ||
1478 | + * | ||
1479 | + * Return 0 on success or a negative error code on failure. | ||
1480 | + */ | ||
1481 | +static int isp_pipeline_pm_power(struct media_entity *entity, int change) | ||
1482 | +{ | ||
1483 | + struct media_entity_graph graph; | ||
1484 | + struct media_entity *first = entity; | ||
1485 | + int ret = 0; | ||
1486 | + | ||
1487 | + if (!change) | ||
1488 | + return 0; | ||
1489 | + | ||
1490 | + media_entity_graph_walk_start(&graph, entity); | ||
1491 | + | ||
1492 | + while (!ret && (entity = media_entity_graph_walk_next(&graph))) | ||
1493 | + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) | ||
1494 | + ret = isp_pipeline_pm_power_one(entity, change); | ||
1495 | + | ||
1496 | + if (!ret) | ||
1497 | + return 0; | ||
1498 | + | ||
1499 | + media_entity_graph_walk_start(&graph, first); | ||
1500 | + | ||
1501 | + while ((first = media_entity_graph_walk_next(&graph)) | ||
1502 | + && first != entity) | ||
1503 | + if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE) | ||
1504 | + isp_pipeline_pm_power_one(first, -change); | ||
1505 | + | ||
1506 | + return ret; | ||
1507 | +} | ||
1508 | + | ||
1509 | +/* | ||
1510 | + * omap3isp_pipeline_pm_use - Update the use count of an entity | ||
1511 | + * @entity: The entity | ||
1512 | + * @use: Use (1) or stop using (0) the entity | ||
1513 | + * | ||
1514 | + * Update the use count of all entities in the pipeline and power entities on or | ||
1515 | + * off accordingly. | ||
1516 | + * | ||
1517 | + * Return 0 on success or a negative error code on failure. Powering entities | ||
1518 | + * off is assumed to never fail. No failure can occur when the use parameter is | ||
1519 | + * set to 0. | ||
1520 | + */ | ||
1521 | +int omap3isp_pipeline_pm_use(struct media_entity *entity, int use) | ||
1522 | +{ | ||
1523 | + int change = use ? 1 : -1; | ||
1524 | + int ret; | ||
1525 | + | ||
1526 | + mutex_lock(&entity->parent->graph_mutex); | ||
1527 | + | ||
1528 | + /* Apply use count to node. */ | ||
1529 | + entity->use_count += change; | ||
1530 | + WARN_ON(entity->use_count < 0); | ||
1531 | + | ||
1532 | + /* Apply power change to connected non-nodes. */ | ||
1533 | + ret = isp_pipeline_pm_power(entity, change); | ||
1534 | + | ||
1535 | + mutex_unlock(&entity->parent->graph_mutex); | ||
1536 | + | ||
1537 | + return ret; | ||
1538 | +} | ||
1539 | + | ||
1540 | +/* | ||
1541 | + * isp_pipeline_link_notify - Link management notification callback | ||
1542 | + * @source: Pad at the start of the link | ||
1543 | + * @sink: Pad at the end of the link | ||
1544 | + * @flags: New link flags that will be applied | ||
1545 | + * | ||
1546 | + * React to link management on powered pipelines by updating the use count of | ||
1547 | + * all entities in the source and sink sides of the link. Entities are powered | ||
1548 | + * on or off accordingly. | ||
1549 | + * | ||
1550 | + * Return 0 on success or a negative error code on failure. Powering entities | ||
1551 | + * off is assumed to never fail. This function will not fail for disconnection | ||
1552 | + * events. | ||
1553 | + */ | ||
1554 | +static int isp_pipeline_link_notify(struct media_pad *source, | ||
1555 | + struct media_pad *sink, u32 flags) | ||
1556 | +{ | ||
1557 | + int source_use = isp_pipeline_pm_use_count(source->entity); | ||
1558 | + int sink_use = isp_pipeline_pm_use_count(sink->entity); | ||
1559 | + int ret; | ||
1560 | + | ||
1561 | + if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
1562 | + /* Powering off entities is assumed to never fail. */ | ||
1563 | + isp_pipeline_pm_power(source->entity, -sink_use); | ||
1564 | + isp_pipeline_pm_power(sink->entity, -source_use); | ||
1565 | + return 0; | ||
1566 | + } | ||
1567 | + | ||
1568 | + ret = isp_pipeline_pm_power(source->entity, sink_use); | ||
1569 | + if (ret < 0) | ||
1570 | + return ret; | ||
1571 | + | ||
1572 | + ret = isp_pipeline_pm_power(sink->entity, source_use); | ||
1573 | + if (ret < 0) | ||
1574 | + isp_pipeline_pm_power(source->entity, -sink_use); | ||
1575 | + | ||
1576 | + return ret; | ||
1577 | +} | ||
1578 | + | ||
1579 | +/* ----------------------------------------------------------------------------- | ||
1580 | + * Pipeline stream management | ||
1581 | + */ | ||
1582 | + | ||
1583 | +/* | ||
1584 | + * isp_pipeline_enable - Enable streaming on a pipeline | ||
1585 | + * @pipe: ISP pipeline | ||
1586 | + * @mode: Stream mode (single shot or continuous) | ||
1587 | + * | ||
1588 | + * Walk the entities chain starting at the pipeline output video node and start | ||
1589 | + * all modules in the chain in the given mode. | ||
1590 | + * | ||
1591 | + * Return 0 if successfull, or the return value of the failed video::s_stream | ||
1592 | + * operation otherwise. | ||
1593 | + */ | ||
1594 | +static int isp_pipeline_enable(struct isp_pipeline *pipe, | ||
1595 | + enum isp_pipeline_stream_state mode) | ||
1596 | +{ | ||
1597 | + struct isp_device *isp = pipe->output->isp; | ||
1598 | + struct media_entity *entity; | ||
1599 | + struct media_pad *pad; | ||
1600 | + struct v4l2_subdev *subdev; | ||
1601 | + unsigned long flags; | ||
1602 | + int ret = 0; | ||
1603 | + | ||
1604 | + spin_lock_irqsave(&pipe->lock, flags); | ||
1605 | + pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT); | ||
1606 | + spin_unlock_irqrestore(&pipe->lock, flags); | ||
1607 | + | ||
1608 | + pipe->do_propagation = false; | ||
1609 | + | ||
1610 | + entity = &pipe->output->video.entity; | ||
1611 | + while (1) { | ||
1612 | + pad = &entity->pads[0]; | ||
1613 | + if (!(pad->flags & MEDIA_PAD_FL_INPUT)) | ||
1614 | + break; | ||
1615 | + | ||
1616 | + pad = media_entity_remote_source(pad); | ||
1617 | + if (pad == NULL || | ||
1618 | + media_entity_type(pad->entity) != | ||
1619 | + MEDIA_ENT_T_V4L2_SUBDEV) | ||
1620 | + break; | ||
1621 | + | ||
1622 | + entity = pad->entity; | ||
1623 | + subdev = media_entity_to_v4l2_subdev(entity); | ||
1624 | + | ||
1625 | + ret = v4l2_subdev_call(subdev, video, s_stream, mode); | ||
1626 | + if (ret < 0 && ret != -ENOIOCTLCMD) | ||
1627 | + break; | ||
1628 | + | ||
1629 | + if (subdev == &isp->isp_ccdc.subdev) { | ||
1630 | + v4l2_subdev_call(&isp->isp_aewb.subdev, video, | ||
1631 | + s_stream, mode); | ||
1632 | + v4l2_subdev_call(&isp->isp_af.subdev, video, | ||
1633 | + s_stream, mode); | ||
1634 | + v4l2_subdev_call(&isp->isp_hist.subdev, video, | ||
1635 | + s_stream, mode); | ||
1636 | + pipe->do_propagation = true; | ||
1637 | + } | ||
1638 | + } | ||
1639 | + | ||
1640 | + /* Frame number propagation. In continuous streaming mode the number | ||
1641 | + * is incremented in the frame start ISR. In mem-to-mem mode | ||
1642 | + * singleshot is used and frame start IRQs are not available. | ||
1643 | + * Thus we have to increment the number here. | ||
1644 | + */ | ||
1645 | + if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT) | ||
1646 | + atomic_inc(&pipe->frame_number); | ||
1647 | + | ||
1648 | + return ret; | ||
1649 | +} | ||
1650 | + | ||
1651 | +static int isp_pipeline_wait_resizer(struct isp_device *isp) | ||
1652 | +{ | ||
1653 | + return omap3isp_resizer_busy(&isp->isp_res); | ||
1654 | +} | ||
1655 | + | ||
1656 | +static int isp_pipeline_wait_preview(struct isp_device *isp) | ||
1657 | +{ | ||
1658 | + return omap3isp_preview_busy(&isp->isp_prev); | ||
1659 | +} | ||
1660 | + | ||
1661 | +static int isp_pipeline_wait_ccdc(struct isp_device *isp) | ||
1662 | +{ | ||
1663 | + return omap3isp_stat_busy(&isp->isp_af) | ||
1664 | + || omap3isp_stat_busy(&isp->isp_aewb) | ||
1665 | + || omap3isp_stat_busy(&isp->isp_hist) | ||
1666 | + || omap3isp_ccdc_busy(&isp->isp_ccdc); | ||
1667 | +} | ||
1668 | + | ||
1669 | +#define ISP_STOP_TIMEOUT msecs_to_jiffies(1000) | ||
1670 | + | ||
1671 | +static int isp_pipeline_wait(struct isp_device *isp, | ||
1672 | + int(*busy)(struct isp_device *isp)) | ||
1673 | +{ | ||
1674 | + unsigned long timeout = jiffies + ISP_STOP_TIMEOUT; | ||
1675 | + | ||
1676 | + while (!time_after(jiffies, timeout)) { | ||
1677 | + if (!busy(isp)) | ||
1678 | + return 0; | ||
1679 | + } | ||
1680 | + | ||
1681 | + return 1; | ||
1682 | +} | ||
1683 | + | ||
1684 | +/* | ||
1685 | + * isp_pipeline_disable - Disable streaming on a pipeline | ||
1686 | + * @pipe: ISP pipeline | ||
1687 | + * | ||
1688 | + * Walk the entities chain starting at the pipeline output video node and stop | ||
1689 | + * all modules in the chain. Wait synchronously for the modules to be stopped if | ||
1690 | + * necessary. | ||
1691 | + * | ||
1692 | + * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module | ||
1693 | + * can't be stopped (in which case a software reset of the ISP is probably | ||
1694 | + * necessary). | ||
1695 | + */ | ||
1696 | +static int isp_pipeline_disable(struct isp_pipeline *pipe) | ||
1697 | +{ | ||
1698 | + struct isp_device *isp = pipe->output->isp; | ||
1699 | + struct media_entity *entity; | ||
1700 | + struct media_pad *pad; | ||
1701 | + struct v4l2_subdev *subdev; | ||
1702 | + int failure = 0; | ||
1703 | + int ret; | ||
1704 | + | ||
1705 | + /* | ||
1706 | + * We need to stop all the modules after CCDC first or they'll | ||
1707 | + * never stop since they may not get a full frame from CCDC. | ||
1708 | + */ | ||
1709 | + entity = &pipe->output->video.entity; | ||
1710 | + while (1) { | ||
1711 | + pad = &entity->pads[0]; | ||
1712 | + if (!(pad->flags & MEDIA_PAD_FL_INPUT)) | ||
1713 | + break; | ||
1714 | + | ||
1715 | + pad = media_entity_remote_source(pad); | ||
1716 | + if (pad == NULL || | ||
1717 | + media_entity_type(pad->entity) != | ||
1718 | + MEDIA_ENT_T_V4L2_SUBDEV) | ||
1719 | + break; | ||
1720 | + | ||
1721 | + entity = pad->entity; | ||
1722 | + subdev = media_entity_to_v4l2_subdev(entity); | ||
1723 | + | ||
1724 | + if (subdev == &isp->isp_ccdc.subdev) { | ||
1725 | + v4l2_subdev_call(&isp->isp_aewb.subdev, | ||
1726 | + video, s_stream, 0); | ||
1727 | + v4l2_subdev_call(&isp->isp_af.subdev, | ||
1728 | + video, s_stream, 0); | ||
1729 | + v4l2_subdev_call(&isp->isp_hist.subdev, | ||
1730 | + video, s_stream, 0); | ||
1731 | + } | ||
1732 | + | ||
1733 | + v4l2_subdev_call(subdev, video, s_stream, 0); | ||
1734 | + | ||
1735 | + if (subdev == &isp->isp_res.subdev) { | ||
1736 | + ret = isp_pipeline_wait(isp, isp_pipeline_wait_resizer); | ||
1737 | + } else if (subdev == &isp->isp_prev.subdev) { | ||
1738 | + ret = isp_pipeline_wait(isp, isp_pipeline_wait_preview); | ||
1739 | + } else if (subdev == &isp->isp_ccdc.subdev) { | ||
1740 | + ret = isp_pipeline_wait(isp, isp_pipeline_wait_ccdc); | ||
1741 | + } else { | ||
1742 | + ret = 0; | ||
1743 | + } | ||
1744 | + | ||
1745 | + if (ret) { | ||
1746 | + dev_info(isp->dev, "Unable to stop %s\n", subdev->name); | ||
1747 | + failure = -ETIMEDOUT; | ||
1748 | + } | ||
1749 | + } | ||
1750 | + | ||
1751 | + return failure; | ||
1752 | +} | ||
1753 | + | ||
1754 | +/* | ||
1755 | + * omap3isp_pipeline_set_stream - Enable/disable streaming on a pipeline | ||
1756 | + * @pipe: ISP pipeline | ||
1757 | + * @state: Stream state (stopped, single shot or continuous) | ||
1758 | + * | ||
1759 | + * Set the pipeline to the given stream state. Pipelines can be started in | ||
1760 | + * single-shot or continuous mode. | ||
1761 | + * | ||
1762 | + * Return 0 if successfull, or the return value of the failed video::s_stream | ||
1763 | + * operation otherwise. | ||
1764 | + */ | ||
1765 | +int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, | ||
1766 | + enum isp_pipeline_stream_state state) | ||
1767 | +{ | ||
1768 | + int ret; | ||
1769 | + | ||
1770 | + if (state == ISP_PIPELINE_STREAM_STOPPED) | ||
1771 | + ret = isp_pipeline_disable(pipe); | ||
1772 | + else | ||
1773 | + ret = isp_pipeline_enable(pipe, state); | ||
1774 | + pipe->stream_state = state; | ||
1775 | + | ||
1776 | + return ret; | ||
1777 | +} | ||
1778 | + | ||
1779 | +/* | ||
1780 | + * isp_pipeline_resume - Resume streaming on a pipeline | ||
1781 | + * @pipe: ISP pipeline | ||
1782 | + * | ||
1783 | + * Resume video output and input and re-enable pipeline. | ||
1784 | + */ | ||
1785 | +static void isp_pipeline_resume(struct isp_pipeline *pipe) | ||
1786 | +{ | ||
1787 | + int singleshot = pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT; | ||
1788 | + | ||
1789 | + omap3isp_video_resume(pipe->output, !singleshot); | ||
1790 | + if (singleshot) | ||
1791 | + omap3isp_video_resume(pipe->input, 0); | ||
1792 | + isp_pipeline_enable(pipe, pipe->stream_state); | ||
1793 | +} | ||
1794 | + | ||
1795 | +/* | ||
1796 | + * isp_pipeline_suspend - Suspend streaming on a pipeline | ||
1797 | + * @pipe: ISP pipeline | ||
1798 | + * | ||
1799 | + * Suspend pipeline. | ||
1800 | + */ | ||
1801 | +static void isp_pipeline_suspend(struct isp_pipeline *pipe) | ||
1802 | +{ | ||
1803 | + isp_pipeline_disable(pipe); | ||
1804 | +} | ||
1805 | + | ||
1806 | +/* | ||
1807 | + * isp_pipeline_is_last - Verify if entity has an enbled link to the output | ||
1808 | + * video node | ||
1809 | + * @me: ISP module's media entity | ||
1810 | + * | ||
1811 | + * Returns 1 if the entity has an enabled link to the output video node or 0 | ||
1812 | + * otherwise. It's true only while pipeline can have no more than one output | ||
1813 | + * node. | ||
1814 | + */ | ||
1815 | +static int isp_pipeline_is_last(struct media_entity *me) | ||
1816 | +{ | ||
1817 | + struct isp_pipeline *pipe; | ||
1818 | + struct media_pad *pad; | ||
1819 | + | ||
1820 | + if (!me->pipe) | ||
1821 | + return 0; | ||
1822 | + pipe = to_isp_pipeline(me); | ||
1823 | + if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED) | ||
1824 | + return 0; | ||
1825 | + pad = media_entity_remote_source(&pipe->output->pad); | ||
1826 | + return pad->entity == me; | ||
1827 | +} | ||
1828 | + | ||
1829 | +/* | ||
1830 | + * isp_suspend_module_pipeline - Suspend pipeline to which belongs the module | ||
1831 | + * @me: ISP module's media entity | ||
1832 | + * | ||
1833 | + * Suspend the whole pipeline if module's entity has an enabled link to the | ||
1834 | + * output video node. It works only while pipeline can have no more than one | ||
1835 | + * output node. | ||
1836 | + */ | ||
1837 | +static void isp_suspend_module_pipeline(struct media_entity *me) | ||
1838 | +{ | ||
1839 | + if (isp_pipeline_is_last(me)) | ||
1840 | + isp_pipeline_suspend(to_isp_pipeline(me)); | ||
1841 | +} | ||
1842 | + | ||
1843 | +/* | ||
1844 | + * isp_resume_module_pipeline - Resume pipeline to which belongs the module | ||
1845 | + * @me: ISP module's media entity | ||
1846 | + * | ||
1847 | + * Resume the whole pipeline if module's entity has an enabled link to the | ||
1848 | + * output video node. It works only while pipeline can have no more than one | ||
1849 | + * output node. | ||
1850 | + */ | ||
1851 | +static void isp_resume_module_pipeline(struct media_entity *me) | ||
1852 | +{ | ||
1853 | + if (isp_pipeline_is_last(me)) | ||
1854 | + isp_pipeline_resume(to_isp_pipeline(me)); | ||
1855 | +} | ||
1856 | + | ||
1857 | +/* | ||
1858 | + * isp_suspend_modules - Suspend ISP submodules. | ||
1859 | + * @isp: OMAP3 ISP device | ||
1860 | + * | ||
1861 | + * Returns 0 if suspend left in idle state all the submodules properly, | ||
1862 | + * or returns 1 if a general Reset is required to suspend the submodules. | ||
1863 | + */ | ||
1864 | +static int isp_suspend_modules(struct isp_device *isp) | ||
1865 | +{ | ||
1866 | + unsigned long timeout; | ||
1867 | + | ||
1868 | + omap3isp_stat_suspend(&isp->isp_aewb); | ||
1869 | + omap3isp_stat_suspend(&isp->isp_af); | ||
1870 | + omap3isp_stat_suspend(&isp->isp_hist); | ||
1871 | + isp_suspend_module_pipeline(&isp->isp_res.subdev.entity); | ||
1872 | + isp_suspend_module_pipeline(&isp->isp_prev.subdev.entity); | ||
1873 | + isp_suspend_module_pipeline(&isp->isp_ccdc.subdev.entity); | ||
1874 | + isp_suspend_module_pipeline(&isp->isp_csi2a.subdev.entity); | ||
1875 | + isp_suspend_module_pipeline(&isp->isp_ccp2.subdev.entity); | ||
1876 | + | ||
1877 | + timeout = jiffies + ISP_STOP_TIMEOUT; | ||
1878 | + while (omap3isp_stat_busy(&isp->isp_af) | ||
1879 | + || omap3isp_stat_busy(&isp->isp_aewb) | ||
1880 | + || omap3isp_stat_busy(&isp->isp_hist) | ||
1881 | + || omap3isp_preview_busy(&isp->isp_prev) | ||
1882 | + || omap3isp_resizer_busy(&isp->isp_res) | ||
1883 | + || omap3isp_ccdc_busy(&isp->isp_ccdc)) { | ||
1884 | + if (time_after(jiffies, timeout)) { | ||
1885 | + dev_info(isp->dev, "can't stop modules.\n"); | ||
1886 | + return 1; | ||
1887 | + } | ||
1888 | + msleep(1); | ||
1889 | + } | ||
1890 | + | ||
1891 | + return 0; | ||
1892 | +} | ||
1893 | + | ||
1894 | +/* | ||
1895 | + * isp_resume_modules - Resume ISP submodules. | ||
1896 | + * @isp: OMAP3 ISP device | ||
1897 | + */ | ||
1898 | +static void isp_resume_modules(struct isp_device *isp) | ||
1899 | +{ | ||
1900 | + omap3isp_stat_resume(&isp->isp_aewb); | ||
1901 | + omap3isp_stat_resume(&isp->isp_af); | ||
1902 | + omap3isp_stat_resume(&isp->isp_hist); | ||
1903 | + isp_resume_module_pipeline(&isp->isp_res.subdev.entity); | ||
1904 | + isp_resume_module_pipeline(&isp->isp_prev.subdev.entity); | ||
1905 | + isp_resume_module_pipeline(&isp->isp_ccdc.subdev.entity); | ||
1906 | + isp_resume_module_pipeline(&isp->isp_csi2a.subdev.entity); | ||
1907 | + isp_resume_module_pipeline(&isp->isp_ccp2.subdev.entity); | ||
1908 | +} | ||
1909 | + | ||
1910 | +/* | ||
1911 | + * isp_reset - Reset ISP with a timeout wait for idle. | ||
1912 | + * @isp: OMAP3 ISP device | ||
1913 | + */ | ||
1914 | +static int isp_reset(struct isp_device *isp) | ||
1915 | +{ | ||
1916 | + unsigned long timeout = 0; | ||
1917 | + | ||
1918 | + isp_reg_writel(isp, | ||
1919 | + isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG) | ||
1920 | + | ISP_SYSCONFIG_SOFTRESET, | ||
1921 | + OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); | ||
1922 | + while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, | ||
1923 | + ISP_SYSSTATUS) & 0x1)) { | ||
1924 | + if (timeout++ > 10000) { | ||
1925 | + dev_alert(isp->dev, "cannot reset ISP\n"); | ||
1926 | + return -ETIMEDOUT; | ||
1927 | + } | ||
1928 | + udelay(1); | ||
1929 | + } | ||
1930 | + | ||
1931 | + return 0; | ||
1932 | +} | ||
1933 | + | ||
1934 | +/* | ||
1935 | + * isp_save_context - Saves the values of the ISP module registers. | ||
1936 | + * @isp: OMAP3 ISP device | ||
1937 | + * @reg_list: Structure containing pairs of register address and value to | ||
1938 | + * modify on OMAP. | ||
1939 | + */ | ||
1940 | +static void | ||
1941 | +isp_save_context(struct isp_device *isp, struct isp_reg *reg_list) | ||
1942 | +{ | ||
1943 | + struct isp_reg *next = reg_list; | ||
1944 | + | ||
1945 | + for (; next->reg != ISP_TOK_TERM; next++) | ||
1946 | + next->val = isp_reg_readl(isp, next->mmio_range, next->reg); | ||
1947 | +} | ||
1948 | + | ||
1949 | +/* | ||
1950 | + * isp_restore_context - Restores the values of the ISP module registers. | ||
1951 | + * @isp: OMAP3 ISP device | ||
1952 | + * @reg_list: Structure containing pairs of register address and value to | ||
1953 | + * modify on OMAP. | ||
1954 | + */ | ||
1955 | +static void | ||
1956 | +isp_restore_context(struct isp_device *isp, struct isp_reg *reg_list) | ||
1957 | +{ | ||
1958 | + struct isp_reg *next = reg_list; | ||
1959 | + | ||
1960 | + for (; next->reg != ISP_TOK_TERM; next++) | ||
1961 | + isp_reg_writel(isp, next->val, next->mmio_range, next->reg); | ||
1962 | +} | ||
1963 | + | ||
1964 | +/* | ||
1965 | + * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. | ||
1966 | + * @isp: OMAP3 ISP device | ||
1967 | + * | ||
1968 | + * Routine for saving the context of each module in the ISP. | ||
1969 | + * CCDC, HIST, H3A, PREV, RESZ and MMU. | ||
1970 | + */ | ||
1971 | +static void isp_save_ctx(struct isp_device *isp) | ||
1972 | +{ | ||
1973 | + isp_save_context(isp, isp_reg_list); | ||
1974 | + if (isp->iommu) | ||
1975 | + iommu_save_ctx(isp->iommu); | ||
1976 | +} | ||
1977 | + | ||
1978 | +/* | ||
1979 | + * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. | ||
1980 | + * @isp: OMAP3 ISP device | ||
1981 | + * | ||
1982 | + * Routine for restoring the context of each module in the ISP. | ||
1983 | + * CCDC, HIST, H3A, PREV, RESZ and MMU. | ||
1984 | + */ | ||
1985 | +static void isp_restore_ctx(struct isp_device *isp) | ||
1986 | +{ | ||
1987 | + isp_restore_context(isp, isp_reg_list); | ||
1988 | + if (isp->iommu) | ||
1989 | + iommu_restore_ctx(isp->iommu); | ||
1990 | + omap3isp_ccdc_restore_context(isp); | ||
1991 | + omap3isp_preview_restore_context(isp); | ||
1992 | +} | ||
1993 | + | ||
1994 | +/* ----------------------------------------------------------------------------- | ||
1995 | + * SBL resources management | ||
1996 | + */ | ||
1997 | +#define OMAP3_ISP_SBL_READ (OMAP3_ISP_SBL_CSI1_READ | \ | ||
1998 | + OMAP3_ISP_SBL_CCDC_LSC_READ | \ | ||
1999 | + OMAP3_ISP_SBL_PREVIEW_READ | \ | ||
2000 | + OMAP3_ISP_SBL_RESIZER_READ) | ||
2001 | +#define OMAP3_ISP_SBL_WRITE (OMAP3_ISP_SBL_CSI1_WRITE | \ | ||
2002 | + OMAP3_ISP_SBL_CSI2A_WRITE | \ | ||
2003 | + OMAP3_ISP_SBL_CSI2C_WRITE | \ | ||
2004 | + OMAP3_ISP_SBL_CCDC_WRITE | \ | ||
2005 | + OMAP3_ISP_SBL_PREVIEW_WRITE) | ||
2006 | + | ||
2007 | +void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res) | ||
2008 | +{ | ||
2009 | + u32 sbl = 0; | ||
2010 | + | ||
2011 | + isp->sbl_resources |= res; | ||
2012 | + | ||
2013 | + if (isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ) | ||
2014 | + sbl |= ISPCTRL_SBL_SHARED_RPORTA; | ||
2015 | + | ||
2016 | + if (isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ) | ||
2017 | + sbl |= ISPCTRL_SBL_SHARED_RPORTB; | ||
2018 | + | ||
2019 | + if (isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE) | ||
2020 | + sbl |= ISPCTRL_SBL_SHARED_WPORTC; | ||
2021 | + | ||
2022 | + if (isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE) | ||
2023 | + sbl |= ISPCTRL_SBL_WR0_RAM_EN; | ||
2024 | + | ||
2025 | + if (isp->sbl_resources & OMAP3_ISP_SBL_WRITE) | ||
2026 | + sbl |= ISPCTRL_SBL_WR1_RAM_EN; | ||
2027 | + | ||
2028 | + if (isp->sbl_resources & OMAP3_ISP_SBL_READ) | ||
2029 | + sbl |= ISPCTRL_SBL_RD_RAM_EN; | ||
2030 | + | ||
2031 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl); | ||
2032 | +} | ||
2033 | + | ||
2034 | +void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res) | ||
2035 | +{ | ||
2036 | + u32 sbl = 0; | ||
2037 | + | ||
2038 | + isp->sbl_resources &= ~res; | ||
2039 | + | ||
2040 | + if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ)) | ||
2041 | + sbl |= ISPCTRL_SBL_SHARED_RPORTA; | ||
2042 | + | ||
2043 | + if (!(isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ)) | ||
2044 | + sbl |= ISPCTRL_SBL_SHARED_RPORTB; | ||
2045 | + | ||
2046 | + if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE)) | ||
2047 | + sbl |= ISPCTRL_SBL_SHARED_WPORTC; | ||
2048 | + | ||
2049 | + if (!(isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE)) | ||
2050 | + sbl |= ISPCTRL_SBL_WR0_RAM_EN; | ||
2051 | + | ||
2052 | + if (!(isp->sbl_resources & OMAP3_ISP_SBL_WRITE)) | ||
2053 | + sbl |= ISPCTRL_SBL_WR1_RAM_EN; | ||
2054 | + | ||
2055 | + if (!(isp->sbl_resources & OMAP3_ISP_SBL_READ)) | ||
2056 | + sbl |= ISPCTRL_SBL_RD_RAM_EN; | ||
2057 | + | ||
2058 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl); | ||
2059 | +} | ||
2060 | + | ||
2061 | +/* | ||
2062 | + * isp_module_sync_idle - Helper to sync module with its idle state | ||
2063 | + * @me: ISP submodule's media entity | ||
2064 | + * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization | ||
2065 | + * @stopping: flag which tells module wants to stop | ||
2066 | + * | ||
2067 | + * This function checks if ISP submodule needs to wait for next interrupt. If | ||
2068 | + * yes, makes the caller to sleep while waiting for such event. | ||
2069 | + */ | ||
2070 | +int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, | ||
2071 | + atomic_t *stopping) | ||
2072 | +{ | ||
2073 | + struct isp_pipeline *pipe = to_isp_pipeline(me); | ||
2074 | + | ||
2075 | + if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED || | ||
2076 | + (pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT && | ||
2077 | + !isp_pipeline_ready(pipe))) | ||
2078 | + return 0; | ||
2079 | + | ||
2080 | + /* | ||
2081 | + * atomic_set() doesn't include memory barrier on ARM platform for SMP | ||
2082 | + * scenario. We'll call it here to avoid race conditions. | ||
2083 | + */ | ||
2084 | + atomic_set(stopping, 1); | ||
2085 | + smp_mb(); | ||
2086 | + | ||
2087 | + /* | ||
2088 | + * If module is the last one, it's writing to memory. In this case, | ||
2089 | + * it's necessary to check if the module is already paused due to | ||
2090 | + * DMA queue underrun or if it has to wait for next interrupt to be | ||
2091 | + * idle. | ||
2092 | + * If it isn't the last one, the function won't sleep but *stopping | ||
2093 | + * will still be set to warn next submodule caller's interrupt the | ||
2094 | + * module wants to be idle. | ||
2095 | + */ | ||
2096 | + if (isp_pipeline_is_last(me)) { | ||
2097 | + struct isp_video *video = pipe->output; | ||
2098 | + unsigned long flags; | ||
2099 | + spin_lock_irqsave(&video->queue->irqlock, flags); | ||
2100 | + if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) { | ||
2101 | + spin_unlock_irqrestore(&video->queue->irqlock, flags); | ||
2102 | + atomic_set(stopping, 0); | ||
2103 | + smp_mb(); | ||
2104 | + return 0; | ||
2105 | + } | ||
2106 | + spin_unlock_irqrestore(&video->queue->irqlock, flags); | ||
2107 | + if (!wait_event_timeout(*wait, !atomic_read(stopping), | ||
2108 | + msecs_to_jiffies(1000))) { | ||
2109 | + atomic_set(stopping, 0); | ||
2110 | + smp_mb(); | ||
2111 | + return -ETIMEDOUT; | ||
2112 | + } | ||
2113 | + } | ||
2114 | + | ||
2115 | + return 0; | ||
2116 | +} | ||
2117 | + | ||
2118 | +/* | ||
2119 | + * omap3isp_module_sync_is_stopped - Helper to verify if module was stopping | ||
2120 | + * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization | ||
2121 | + * @stopping: flag which tells module wants to stop | ||
2122 | + * | ||
2123 | + * This function checks if ISP submodule was stopping. In case of yes, it | ||
2124 | + * notices the caller by setting stopping to 0 and waking up the wait queue. | ||
2125 | + * Returns 1 if it was stopping or 0 otherwise. | ||
2126 | + */ | ||
2127 | +int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait, | ||
2128 | + atomic_t *stopping) | ||
2129 | +{ | ||
2130 | + if (atomic_cmpxchg(stopping, 1, 0)) { | ||
2131 | + wake_up(wait); | ||
2132 | + return 1; | ||
2133 | + } | ||
2134 | + | ||
2135 | + return 0; | ||
2136 | +} | ||
2137 | + | ||
2138 | +/* -------------------------------------------------------------------------- | ||
2139 | + * Clock management | ||
2140 | + */ | ||
2141 | + | ||
2142 | +#define ISPCTRL_CLKS_MASK (ISPCTRL_H3A_CLK_EN | \ | ||
2143 | + ISPCTRL_HIST_CLK_EN | \ | ||
2144 | + ISPCTRL_RSZ_CLK_EN | \ | ||
2145 | + (ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN) | \ | ||
2146 | + (ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN)) | ||
2147 | + | ||
2148 | +static void __isp_subclk_update(struct isp_device *isp) | ||
2149 | +{ | ||
2150 | + u32 clk = 0; | ||
2151 | + | ||
2152 | + if (isp->subclk_resources & OMAP3_ISP_SUBCLK_H3A) | ||
2153 | + clk |= ISPCTRL_H3A_CLK_EN; | ||
2154 | + | ||
2155 | + if (isp->subclk_resources & OMAP3_ISP_SUBCLK_HIST) | ||
2156 | + clk |= ISPCTRL_HIST_CLK_EN; | ||
2157 | + | ||
2158 | + if (isp->subclk_resources & OMAP3_ISP_SUBCLK_RESIZER) | ||
2159 | + clk |= ISPCTRL_RSZ_CLK_EN; | ||
2160 | + | ||
2161 | + /* NOTE: For CCDC & Preview submodules, we need to affect internal | ||
2162 | + * RAM aswell. | ||
2163 | + */ | ||
2164 | + if (isp->subclk_resources & OMAP3_ISP_SUBCLK_CCDC) | ||
2165 | + clk |= ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN; | ||
2166 | + | ||
2167 | + if (isp->subclk_resources & OMAP3_ISP_SUBCLK_PREVIEW) | ||
2168 | + clk |= ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN; | ||
2169 | + | ||
2170 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
2171 | + ISPCTRL_CLKS_MASK, clk); | ||
2172 | +} | ||
2173 | + | ||
2174 | +void omap3isp_subclk_enable(struct isp_device *isp, | ||
2175 | + enum isp_subclk_resource res) | ||
2176 | +{ | ||
2177 | + isp->subclk_resources |= res; | ||
2178 | + | ||
2179 | + __isp_subclk_update(isp); | ||
2180 | +} | ||
2181 | + | ||
2182 | +void omap3isp_subclk_disable(struct isp_device *isp, | ||
2183 | + enum isp_subclk_resource res) | ||
2184 | +{ | ||
2185 | + isp->subclk_resources &= ~res; | ||
2186 | + | ||
2187 | + __isp_subclk_update(isp); | ||
2188 | +} | ||
2189 | + | ||
2190 | +/* | ||
2191 | + * isp_enable_clocks - Enable ISP clocks | ||
2192 | + * @isp: OMAP3 ISP device | ||
2193 | + * | ||
2194 | + * Return 0 if successful, or clk_enable return value if any of tthem fails. | ||
2195 | + */ | ||
2196 | +static int isp_enable_clocks(struct isp_device *isp) | ||
2197 | +{ | ||
2198 | + int r; | ||
2199 | + unsigned long rate; | ||
2200 | + int divisor; | ||
2201 | + | ||
2202 | + /* | ||
2203 | + * cam_mclk clock chain: | ||
2204 | + * dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk | ||
2205 | + * | ||
2206 | + * In OMAP3630 dpll4_m5x2 != 2 x dpll4_m5 but both are | ||
2207 | + * set to the same value. Hence the rate set for dpll4_m5 | ||
2208 | + * has to be twice of what is set on OMAP3430 to get | ||
2209 | + * the required value for cam_mclk | ||
2210 | + */ | ||
2211 | + if (cpu_is_omap3630()) | ||
2212 | + divisor = 1; | ||
2213 | + else | ||
2214 | + divisor = 2; | ||
2215 | + | ||
2216 | + r = clk_enable(isp->clock[ISP_CLK_CAM_ICK]); | ||
2217 | + if (r) { | ||
2218 | + dev_err(isp->dev, "clk_enable cam_ick failed\n"); | ||
2219 | + goto out_clk_enable_ick; | ||
2220 | + } | ||
2221 | + r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK], | ||
2222 | + CM_CAM_MCLK_HZ/divisor); | ||
2223 | + if (r) { | ||
2224 | + dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n"); | ||
2225 | + goto out_clk_enable_mclk; | ||
2226 | + } | ||
2227 | + r = clk_enable(isp->clock[ISP_CLK_CAM_MCLK]); | ||
2228 | + if (r) { | ||
2229 | + dev_err(isp->dev, "clk_enable cam_mclk failed\n"); | ||
2230 | + goto out_clk_enable_mclk; | ||
2231 | + } | ||
2232 | + rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); | ||
2233 | + if (rate != CM_CAM_MCLK_HZ) | ||
2234 | + dev_warn(isp->dev, "unexpected cam_mclk rate:\n" | ||
2235 | + " expected : %d\n" | ||
2236 | + " actual : %ld\n", CM_CAM_MCLK_HZ, rate); | ||
2237 | + r = clk_enable(isp->clock[ISP_CLK_CSI2_FCK]); | ||
2238 | + if (r) { | ||
2239 | + dev_err(isp->dev, "clk_enable csi2_fck failed\n"); | ||
2240 | + goto out_clk_enable_csi2_fclk; | ||
2241 | + } | ||
2242 | + return 0; | ||
2243 | + | ||
2244 | +out_clk_enable_csi2_fclk: | ||
2245 | + clk_disable(isp->clock[ISP_CLK_CAM_MCLK]); | ||
2246 | +out_clk_enable_mclk: | ||
2247 | + clk_disable(isp->clock[ISP_CLK_CAM_ICK]); | ||
2248 | +out_clk_enable_ick: | ||
2249 | + return r; | ||
2250 | +} | ||
2251 | + | ||
2252 | +/* | ||
2253 | + * isp_disable_clocks - Disable ISP clocks | ||
2254 | + * @isp: OMAP3 ISP device | ||
2255 | + */ | ||
2256 | +static void isp_disable_clocks(struct isp_device *isp) | ||
2257 | +{ | ||
2258 | + clk_disable(isp->clock[ISP_CLK_CAM_ICK]); | ||
2259 | + clk_disable(isp->clock[ISP_CLK_CAM_MCLK]); | ||
2260 | + clk_disable(isp->clock[ISP_CLK_CSI2_FCK]); | ||
2261 | +} | ||
2262 | + | ||
2263 | +static const char *isp_clocks[] = { | ||
2264 | + "cam_ick", | ||
2265 | + "cam_mclk", | ||
2266 | + "dpll4_m5_ck", | ||
2267 | + "csi2_96m_fck", | ||
2268 | + "l3_ick", | ||
2269 | +}; | ||
2270 | + | ||
2271 | +static void isp_put_clocks(struct isp_device *isp) | ||
2272 | +{ | ||
2273 | + unsigned int i; | ||
2274 | + | ||
2275 | + for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) { | ||
2276 | + if (isp->clock[i]) { | ||
2277 | + clk_put(isp->clock[i]); | ||
2278 | + isp->clock[i] = NULL; | ||
2279 | + } | ||
2280 | + } | ||
2281 | +} | ||
2282 | + | ||
2283 | +static int isp_get_clocks(struct isp_device *isp) | ||
2284 | +{ | ||
2285 | + struct clk *clk; | ||
2286 | + unsigned int i; | ||
2287 | + | ||
2288 | + for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) { | ||
2289 | + clk = clk_get(isp->dev, isp_clocks[i]); | ||
2290 | + if (IS_ERR(clk)) { | ||
2291 | + dev_err(isp->dev, "clk_get %s failed\n", isp_clocks[i]); | ||
2292 | + isp_put_clocks(isp); | ||
2293 | + return PTR_ERR(clk); | ||
2294 | + } | ||
2295 | + | ||
2296 | + isp->clock[i] = clk; | ||
2297 | + } | ||
2298 | + | ||
2299 | + return 0; | ||
2300 | +} | ||
2301 | + | ||
2302 | +/* | ||
2303 | + * omap3isp_get - Acquire the ISP resource. | ||
2304 | + * | ||
2305 | + * Initializes the clocks for the first acquire. | ||
2306 | + * | ||
2307 | + * Increment the reference count on the ISP. If the first reference is taken, | ||
2308 | + * enable clocks and power-up all submodules. | ||
2309 | + * | ||
2310 | + * Return a pointer to the ISP device structure, or NULL if an error occured. | ||
2311 | + */ | ||
2312 | +struct isp_device *omap3isp_get(struct isp_device *isp) | ||
2313 | +{ | ||
2314 | + struct isp_device *__isp = isp; | ||
2315 | + | ||
2316 | + if (isp == NULL) | ||
2317 | + return NULL; | ||
2318 | + | ||
2319 | + mutex_lock(&isp->isp_mutex); | ||
2320 | + if (isp->ref_count > 0) | ||
2321 | + goto out; | ||
2322 | + | ||
2323 | + if (isp_enable_clocks(isp) < 0) { | ||
2324 | + __isp = NULL; | ||
2325 | + goto out; | ||
2326 | + } | ||
2327 | + | ||
2328 | + /* We don't want to restore context before saving it! */ | ||
2329 | + if (isp->has_context) | ||
2330 | + isp_restore_ctx(isp); | ||
2331 | + else | ||
2332 | + isp->has_context = 1; | ||
2333 | + | ||
2334 | + isp_enable_interrupts(isp); | ||
2335 | + | ||
2336 | +out: | ||
2337 | + if (__isp != NULL) | ||
2338 | + isp->ref_count++; | ||
2339 | + mutex_unlock(&isp->isp_mutex); | ||
2340 | + | ||
2341 | + return __isp; | ||
2342 | +} | ||
2343 | + | ||
2344 | +/* | ||
2345 | + * omap3isp_put - Release the ISP | ||
2346 | + * | ||
2347 | + * Decrement the reference count on the ISP. If the last reference is released, | ||
2348 | + * power-down all submodules, disable clocks and free temporary buffers. | ||
2349 | + */ | ||
2350 | +void omap3isp_put(struct isp_device *isp) | ||
2351 | +{ | ||
2352 | + if (isp == NULL) | ||
2353 | + return; | ||
2354 | + | ||
2355 | + mutex_lock(&isp->isp_mutex); | ||
2356 | + BUG_ON(isp->ref_count == 0); | ||
2357 | + if (--isp->ref_count == 0) { | ||
2358 | + isp_disable_interrupts(isp); | ||
2359 | + isp_save_ctx(isp); | ||
2360 | + isp_disable_clocks(isp); | ||
2361 | + } | ||
2362 | + mutex_unlock(&isp->isp_mutex); | ||
2363 | +} | ||
2364 | + | ||
2365 | +/* -------------------------------------------------------------------------- | ||
2366 | + * Platform device driver | ||
2367 | + */ | ||
2368 | + | ||
2369 | +/* | ||
2370 | + * omap3isp_print_status - Prints the values of the ISP Control Module registers | ||
2371 | + * @isp: OMAP3 ISP device | ||
2372 | + */ | ||
2373 | +#define ISP_PRINT_REGISTER(isp, name)\ | ||
2374 | + dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \ | ||
2375 | + isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name)) | ||
2376 | +#define SBL_PRINT_REGISTER(isp, name)\ | ||
2377 | + dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \ | ||
2378 | + isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name)) | ||
2379 | + | ||
2380 | +void omap3isp_print_status(struct isp_device *isp) | ||
2381 | +{ | ||
2382 | + dev_dbg(isp->dev, "-------------ISP Register dump--------------\n"); | ||
2383 | + | ||
2384 | + ISP_PRINT_REGISTER(isp, SYSCONFIG); | ||
2385 | + ISP_PRINT_REGISTER(isp, SYSSTATUS); | ||
2386 | + ISP_PRINT_REGISTER(isp, IRQ0ENABLE); | ||
2387 | + ISP_PRINT_REGISTER(isp, IRQ0STATUS); | ||
2388 | + ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH); | ||
2389 | + ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY); | ||
2390 | + ISP_PRINT_REGISTER(isp, CTRL); | ||
2391 | + ISP_PRINT_REGISTER(isp, TCTRL_CTRL); | ||
2392 | + ISP_PRINT_REGISTER(isp, TCTRL_FRAME); | ||
2393 | + ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY); | ||
2394 | + ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY); | ||
2395 | + ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY); | ||
2396 | + ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH); | ||
2397 | + ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH); | ||
2398 | + ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH); | ||
2399 | + | ||
2400 | + SBL_PRINT_REGISTER(isp, PCR); | ||
2401 | + SBL_PRINT_REGISTER(isp, SDR_REQ_EXP); | ||
2402 | + | ||
2403 | + dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
2404 | +} | ||
2405 | + | ||
2406 | +#ifdef CONFIG_PM | ||
2407 | + | ||
2408 | +/* | ||
2409 | + * Power management support. | ||
2410 | + * | ||
2411 | + * As the ISP can't properly handle an input video stream interruption on a non | ||
2412 | + * frame boundary, the ISP pipelines need to be stopped before sensors get | ||
2413 | + * suspended. However, as suspending the sensors can require a running clock, | ||
2414 | + * which can be provided by the ISP, the ISP can't be completely suspended | ||
2415 | + * before the sensor. | ||
2416 | + * | ||
2417 | + * To solve this problem power management support is split into prepare/complete | ||
2418 | + * and suspend/resume operations. The pipelines are stopped in prepare() and the | ||
2419 | + * ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in | ||
2420 | + * resume(), and the the pipelines are restarted in complete(). | ||
2421 | + * | ||
2422 | + * TODO: PM dependencies between the ISP and sensors are not modeled explicitly | ||
2423 | + * yet. | ||
2424 | + */ | ||
2425 | +static int isp_pm_prepare(struct device *dev) | ||
2426 | +{ | ||
2427 | + struct isp_device *isp = dev_get_drvdata(dev); | ||
2428 | + int reset; | ||
2429 | + | ||
2430 | + WARN_ON(mutex_is_locked(&isp->isp_mutex)); | ||
2431 | + | ||
2432 | + if (isp->ref_count == 0) | ||
2433 | + return 0; | ||
2434 | + | ||
2435 | + reset = isp_suspend_modules(isp); | ||
2436 | + isp_disable_interrupts(isp); | ||
2437 | + isp_save_ctx(isp); | ||
2438 | + if (reset) | ||
2439 | + isp_reset(isp); | ||
2440 | + | ||
2441 | + return 0; | ||
2442 | +} | ||
2443 | + | ||
2444 | +static int isp_pm_suspend(struct device *dev) | ||
2445 | +{ | ||
2446 | + struct isp_device *isp = dev_get_drvdata(dev); | ||
2447 | + | ||
2448 | + WARN_ON(mutex_is_locked(&isp->isp_mutex)); | ||
2449 | + | ||
2450 | + if (isp->ref_count) | ||
2451 | + isp_disable_clocks(isp); | ||
2452 | + | ||
2453 | + return 0; | ||
2454 | +} | ||
2455 | + | ||
2456 | +static int isp_pm_resume(struct device *dev) | ||
2457 | +{ | ||
2458 | + struct isp_device *isp = dev_get_drvdata(dev); | ||
2459 | + | ||
2460 | + if (isp->ref_count == 0) | ||
2461 | + return 0; | ||
2462 | + | ||
2463 | + return isp_enable_clocks(isp); | ||
2464 | +} | ||
2465 | + | ||
2466 | +static void isp_pm_complete(struct device *dev) | ||
2467 | +{ | ||
2468 | + struct isp_device *isp = dev_get_drvdata(dev); | ||
2469 | + | ||
2470 | + if (isp->ref_count == 0) | ||
2471 | + return; | ||
2472 | + | ||
2473 | + isp_restore_ctx(isp); | ||
2474 | + isp_enable_interrupts(isp); | ||
2475 | + isp_resume_modules(isp); | ||
2476 | +} | ||
2477 | + | ||
2478 | +#else | ||
2479 | + | ||
2480 | +#define isp_pm_prepare NULL | ||
2481 | +#define isp_pm_suspend NULL | ||
2482 | +#define isp_pm_resume NULL | ||
2483 | +#define isp_pm_complete NULL | ||
2484 | + | ||
2485 | +#endif /* CONFIG_PM */ | ||
2486 | + | ||
2487 | +static void isp_unregister_entities(struct isp_device *isp) | ||
2488 | +{ | ||
2489 | + omap3isp_csi2_unregister_entities(&isp->isp_csi2a); | ||
2490 | + omap3isp_ccp2_unregister_entities(&isp->isp_ccp2); | ||
2491 | + omap3isp_ccdc_unregister_entities(&isp->isp_ccdc); | ||
2492 | + omap3isp_preview_unregister_entities(&isp->isp_prev); | ||
2493 | + omap3isp_resizer_unregister_entities(&isp->isp_res); | ||
2494 | + omap3isp_stat_unregister_entities(&isp->isp_aewb); | ||
2495 | + omap3isp_stat_unregister_entities(&isp->isp_af); | ||
2496 | + omap3isp_stat_unregister_entities(&isp->isp_hist); | ||
2497 | + | ||
2498 | + v4l2_device_unregister(&isp->v4l2_dev); | ||
2499 | + media_device_unregister(&isp->media_dev); | ||
2500 | +} | ||
2501 | + | ||
2502 | +/* | ||
2503 | + * isp_register_subdev_group - Register a group of subdevices | ||
2504 | + * @isp: OMAP3 ISP device | ||
2505 | + * @board_info: I2C subdevs board information array | ||
2506 | + * | ||
2507 | + * Register all I2C subdevices in the board_info array. The array must be | ||
2508 | + * terminated by a NULL entry, and the first entry must be the sensor. | ||
2509 | + * | ||
2510 | + * Return a pointer to the sensor media entity if it has been successfully | ||
2511 | + * registered, or NULL otherwise. | ||
2512 | + */ | ||
2513 | +static struct v4l2_subdev * | ||
2514 | +isp_register_subdev_group(struct isp_device *isp, | ||
2515 | + struct isp_subdev_i2c_board_info *board_info) | ||
2516 | +{ | ||
2517 | + struct v4l2_subdev *sensor = NULL; | ||
2518 | + unsigned int first; | ||
2519 | + | ||
2520 | + if (board_info->board_info == NULL) | ||
2521 | + return NULL; | ||
2522 | + | ||
2523 | + for (first = 1; board_info->board_info; ++board_info, first = 0) { | ||
2524 | + struct v4l2_subdev *subdev; | ||
2525 | + struct i2c_adapter *adapter; | ||
2526 | + | ||
2527 | + adapter = i2c_get_adapter(board_info->i2c_adapter_id); | ||
2528 | + if (adapter == NULL) { | ||
2529 | + printk(KERN_ERR "%s: Unable to get I2C adapter %d for " | ||
2530 | + "device %s\n", __func__, | ||
2531 | + board_info->i2c_adapter_id, | ||
2532 | + board_info->board_info->type); | ||
2533 | + continue; | ||
2534 | + } | ||
2535 | + | ||
2536 | + subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter, | ||
2537 | + board_info->board_info, NULL, 1); | ||
2538 | + if (subdev == NULL) { | ||
2539 | + printk(KERN_ERR "%s: Unable to register subdev %s\n", | ||
2540 | + __func__, board_info->board_info->type); | ||
2541 | + continue; | ||
2542 | + } | ||
2543 | + | ||
2544 | + if (first) | ||
2545 | + sensor = subdev; | ||
2546 | + } | ||
2547 | + | ||
2548 | + return sensor; | ||
2549 | +} | ||
2550 | + | ||
2551 | +static int isp_register_entities(struct isp_device *isp) | ||
2552 | +{ | ||
2553 | + struct isp_platform_data *pdata = isp->pdata; | ||
2554 | + struct isp_v4l2_subdevs_group *subdevs; | ||
2555 | + int ret; | ||
2556 | + | ||
2557 | + isp->media_dev.dev = isp->dev; | ||
2558 | + strlcpy(isp->media_dev.model, "TI OMAP3 ISP", | ||
2559 | + sizeof(isp->media_dev.model)); | ||
2560 | + isp->media_dev.link_notify = isp_pipeline_link_notify; | ||
2561 | + ret = media_device_register(&isp->media_dev); | ||
2562 | + if (ret < 0) { | ||
2563 | + printk(KERN_ERR "%s: Media device registration failed (%d)\n", | ||
2564 | + __func__, ret); | ||
2565 | + return ret; | ||
2566 | + } | ||
2567 | + | ||
2568 | + isp->v4l2_dev.mdev = &isp->media_dev; | ||
2569 | + ret = v4l2_device_register(isp->dev, &isp->v4l2_dev); | ||
2570 | + if (ret < 0) { | ||
2571 | + printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n", | ||
2572 | + __func__, ret); | ||
2573 | + goto done; | ||
2574 | + } | ||
2575 | + | ||
2576 | + /* Register internal entities */ | ||
2577 | + ret = omap3isp_ccp2_register_entities(&isp->isp_ccp2, &isp->v4l2_dev); | ||
2578 | + if (ret < 0) | ||
2579 | + goto done; | ||
2580 | + | ||
2581 | + ret = omap3isp_csi2_register_entities(&isp->isp_csi2a, &isp->v4l2_dev); | ||
2582 | + if (ret < 0) | ||
2583 | + goto done; | ||
2584 | + | ||
2585 | + ret = omap3isp_ccdc_register_entities(&isp->isp_ccdc, &isp->v4l2_dev); | ||
2586 | + if (ret < 0) | ||
2587 | + goto done; | ||
2588 | + | ||
2589 | + ret = omap3isp_preview_register_entities(&isp->isp_prev, | ||
2590 | + &isp->v4l2_dev); | ||
2591 | + if (ret < 0) | ||
2592 | + goto done; | ||
2593 | + | ||
2594 | + ret = omap3isp_resizer_register_entities(&isp->isp_res, &isp->v4l2_dev); | ||
2595 | + if (ret < 0) | ||
2596 | + goto done; | ||
2597 | + | ||
2598 | + ret = omap3isp_stat_register_entities(&isp->isp_aewb, &isp->v4l2_dev); | ||
2599 | + if (ret < 0) | ||
2600 | + goto done; | ||
2601 | + | ||
2602 | + ret = omap3isp_stat_register_entities(&isp->isp_af, &isp->v4l2_dev); | ||
2603 | + if (ret < 0) | ||
2604 | + goto done; | ||
2605 | + | ||
2606 | + ret = omap3isp_stat_register_entities(&isp->isp_hist, &isp->v4l2_dev); | ||
2607 | + if (ret < 0) | ||
2608 | + goto done; | ||
2609 | + | ||
2610 | + /* Register external entities */ | ||
2611 | + for (subdevs = pdata->subdevs; subdevs->subdevs; ++subdevs) { | ||
2612 | + struct v4l2_subdev *sensor; | ||
2613 | + struct media_entity *input; | ||
2614 | + unsigned int flags; | ||
2615 | + unsigned int pad; | ||
2616 | + | ||
2617 | + sensor = isp_register_subdev_group(isp, subdevs->subdevs); | ||
2618 | + if (sensor == NULL) | ||
2619 | + continue; | ||
2620 | + | ||
2621 | + sensor->host_priv = subdevs; | ||
2622 | + | ||
2623 | + /* Connect the sensor to the correct interface module. Parallel | ||
2624 | + * sensors are connected directly to the CCDC, while serial | ||
2625 | + * sensors are connected to the CSI2a, CCP2b or CSI2c receiver | ||
2626 | + * through CSIPHY1 or CSIPHY2. | ||
2627 | + */ | ||
2628 | + switch (subdevs->interface) { | ||
2629 | + case ISP_INTERFACE_PARALLEL: | ||
2630 | + input = &isp->isp_ccdc.subdev.entity; | ||
2631 | + pad = CCDC_PAD_SINK; | ||
2632 | + flags = 0; | ||
2633 | + break; | ||
2634 | + | ||
2635 | + case ISP_INTERFACE_CSI2A_PHY2: | ||
2636 | + input = &isp->isp_csi2a.subdev.entity; | ||
2637 | + pad = CSI2_PAD_SINK; | ||
2638 | + flags = MEDIA_LNK_FL_IMMUTABLE | ||
2639 | + | MEDIA_LNK_FL_ENABLED; | ||
2640 | + break; | ||
2641 | + | ||
2642 | + case ISP_INTERFACE_CCP2B_PHY1: | ||
2643 | + case ISP_INTERFACE_CCP2B_PHY2: | ||
2644 | + input = &isp->isp_ccp2.subdev.entity; | ||
2645 | + pad = CCP2_PAD_SINK; | ||
2646 | + flags = 0; | ||
2647 | + break; | ||
2648 | + | ||
2649 | + case ISP_INTERFACE_CSI2C_PHY1: | ||
2650 | + input = &isp->isp_csi2c.subdev.entity; | ||
2651 | + pad = CSI2_PAD_SINK; | ||
2652 | + flags = MEDIA_LNK_FL_IMMUTABLE | ||
2653 | + | MEDIA_LNK_FL_ENABLED; | ||
2654 | + break; | ||
2655 | + | ||
2656 | + default: | ||
2657 | + printk(KERN_ERR "%s: invalid interface type %u\n", | ||
2658 | + __func__, subdevs->interface); | ||
2659 | + ret = -EINVAL; | ||
2660 | + goto done; | ||
2661 | + } | ||
2662 | + | ||
2663 | + ret = media_entity_create_link(&sensor->entity, 0, input, pad, | ||
2664 | + flags); | ||
2665 | + if (ret < 0) | ||
2666 | + goto done; | ||
2667 | + } | ||
2668 | + | ||
2669 | +done: | ||
2670 | + if (ret < 0) | ||
2671 | + isp_unregister_entities(isp); | ||
2672 | + | ||
2673 | + return ret; | ||
2674 | +} | ||
2675 | + | ||
2676 | +static void isp_cleanup_modules(struct isp_device *isp) | ||
2677 | +{ | ||
2678 | + omap3isp_h3a_aewb_cleanup(isp); | ||
2679 | + omap3isp_h3a_af_cleanup(isp); | ||
2680 | + omap3isp_hist_cleanup(isp); | ||
2681 | + omap3isp_resizer_cleanup(isp); | ||
2682 | + omap3isp_preview_cleanup(isp); | ||
2683 | + omap3isp_ccdc_cleanup(isp); | ||
2684 | + omap3isp_ccp2_cleanup(isp); | ||
2685 | + omap3isp_csi2_cleanup(isp); | ||
2686 | +} | ||
2687 | + | ||
2688 | +static int isp_initialize_modules(struct isp_device *isp) | ||
2689 | +{ | ||
2690 | + int ret; | ||
2691 | + | ||
2692 | + ret = omap3isp_csiphy_init(isp); | ||
2693 | + if (ret < 0) { | ||
2694 | + dev_err(isp->dev, "CSI PHY initialization failed\n"); | ||
2695 | + goto error_csiphy; | ||
2696 | + } | ||
2697 | + | ||
2698 | + ret = omap3isp_csi2_init(isp); | ||
2699 | + if (ret < 0) { | ||
2700 | + dev_err(isp->dev, "CSI2 initialization failed\n"); | ||
2701 | + goto error_csi2; | ||
2702 | + } | ||
2703 | + | ||
2704 | + ret = omap3isp_ccp2_init(isp); | ||
2705 | + if (ret < 0) { | ||
2706 | + dev_err(isp->dev, "CCP2 initialization failed\n"); | ||
2707 | + goto error_ccp2; | ||
2708 | + } | ||
2709 | + | ||
2710 | + ret = omap3isp_ccdc_init(isp); | ||
2711 | + if (ret < 0) { | ||
2712 | + dev_err(isp->dev, "CCDC initialization failed\n"); | ||
2713 | + goto error_ccdc; | ||
2714 | + } | ||
2715 | + | ||
2716 | + ret = omap3isp_preview_init(isp); | ||
2717 | + if (ret < 0) { | ||
2718 | + dev_err(isp->dev, "Preview initialization failed\n"); | ||
2719 | + goto error_preview; | ||
2720 | + } | ||
2721 | + | ||
2722 | + ret = omap3isp_resizer_init(isp); | ||
2723 | + if (ret < 0) { | ||
2724 | + dev_err(isp->dev, "Resizer initialization failed\n"); | ||
2725 | + goto error_resizer; | ||
2726 | + } | ||
2727 | + | ||
2728 | + ret = omap3isp_hist_init(isp); | ||
2729 | + if (ret < 0) { | ||
2730 | + dev_err(isp->dev, "Histogram initialization failed\n"); | ||
2731 | + goto error_hist; | ||
2732 | + } | ||
2733 | + | ||
2734 | + ret = omap3isp_h3a_aewb_init(isp); | ||
2735 | + if (ret < 0) { | ||
2736 | + dev_err(isp->dev, "H3A AEWB initialization failed\n"); | ||
2737 | + goto error_h3a_aewb; | ||
2738 | + } | ||
2739 | + | ||
2740 | + ret = omap3isp_h3a_af_init(isp); | ||
2741 | + if (ret < 0) { | ||
2742 | + dev_err(isp->dev, "H3A AF initialization failed\n"); | ||
2743 | + goto error_h3a_af; | ||
2744 | + } | ||
2745 | + | ||
2746 | + /* Connect the submodules. */ | ||
2747 | + ret = media_entity_create_link( | ||
2748 | + &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, | ||
2749 | + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); | ||
2750 | + if (ret < 0) | ||
2751 | + goto error_link; | ||
2752 | + | ||
2753 | + ret = media_entity_create_link( | ||
2754 | + &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE, | ||
2755 | + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); | ||
2756 | + if (ret < 0) | ||
2757 | + goto error_link; | ||
2758 | + | ||
2759 | + ret = media_entity_create_link( | ||
2760 | + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, | ||
2761 | + &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); | ||
2762 | + if (ret < 0) | ||
2763 | + goto error_link; | ||
2764 | + | ||
2765 | + ret = media_entity_create_link( | ||
2766 | + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, | ||
2767 | + &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); | ||
2768 | + if (ret < 0) | ||
2769 | + goto error_link; | ||
2770 | + | ||
2771 | + ret = media_entity_create_link( | ||
2772 | + &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, | ||
2773 | + &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); | ||
2774 | + if (ret < 0) | ||
2775 | + goto error_link; | ||
2776 | + | ||
2777 | + ret = media_entity_create_link( | ||
2778 | + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, | ||
2779 | + &isp->isp_aewb.subdev.entity, 0, | ||
2780 | + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); | ||
2781 | + if (ret < 0) | ||
2782 | + goto error_link; | ||
2783 | + | ||
2784 | + ret = media_entity_create_link( | ||
2785 | + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, | ||
2786 | + &isp->isp_af.subdev.entity, 0, | ||
2787 | + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); | ||
2788 | + if (ret < 0) | ||
2789 | + goto error_link; | ||
2790 | + | ||
2791 | + ret = media_entity_create_link( | ||
2792 | + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, | ||
2793 | + &isp->isp_hist.subdev.entity, 0, | ||
2794 | + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); | ||
2795 | + if (ret < 0) | ||
2796 | + goto error_link; | ||
2797 | + | ||
2798 | + return 0; | ||
2799 | + | ||
2800 | +error_link: | ||
2801 | + omap3isp_h3a_af_cleanup(isp); | ||
2802 | +error_h3a_af: | ||
2803 | + omap3isp_h3a_aewb_cleanup(isp); | ||
2804 | +error_h3a_aewb: | ||
2805 | + omap3isp_hist_cleanup(isp); | ||
2806 | +error_hist: | ||
2807 | + omap3isp_resizer_cleanup(isp); | ||
2808 | +error_resizer: | ||
2809 | + omap3isp_preview_cleanup(isp); | ||
2810 | +error_preview: | ||
2811 | + omap3isp_ccdc_cleanup(isp); | ||
2812 | +error_ccdc: | ||
2813 | + omap3isp_ccp2_cleanup(isp); | ||
2814 | +error_ccp2: | ||
2815 | + omap3isp_csi2_cleanup(isp); | ||
2816 | +error_csi2: | ||
2817 | +error_csiphy: | ||
2818 | + return ret; | ||
2819 | +} | ||
2820 | + | ||
2821 | +/* | ||
2822 | + * isp_remove - Remove ISP platform device | ||
2823 | + * @pdev: Pointer to ISP platform device | ||
2824 | + * | ||
2825 | + * Always returns 0. | ||
2826 | + */ | ||
2827 | +static int isp_remove(struct platform_device *pdev) | ||
2828 | +{ | ||
2829 | + struct isp_device *isp = platform_get_drvdata(pdev); | ||
2830 | + int i; | ||
2831 | + | ||
2832 | + isp_unregister_entities(isp); | ||
2833 | + isp_cleanup_modules(isp); | ||
2834 | + | ||
2835 | + omap3isp_get(isp); | ||
2836 | + iommu_put(isp->iommu); | ||
2837 | + omap3isp_put(isp); | ||
2838 | + | ||
2839 | + free_irq(isp->irq_num, isp); | ||
2840 | + isp_put_clocks(isp); | ||
2841 | + | ||
2842 | + for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) { | ||
2843 | + if (isp->mmio_base[i]) { | ||
2844 | + iounmap(isp->mmio_base[i]); | ||
2845 | + isp->mmio_base[i] = NULL; | ||
2846 | + } | ||
2847 | + | ||
2848 | + if (isp->mmio_base_phys[i]) { | ||
2849 | + release_mem_region(isp->mmio_base_phys[i], | ||
2850 | + isp->mmio_size[i]); | ||
2851 | + isp->mmio_base_phys[i] = 0; | ||
2852 | + } | ||
2853 | + } | ||
2854 | + | ||
2855 | + regulator_put(isp->isp_csiphy1.vdd); | ||
2856 | + regulator_put(isp->isp_csiphy2.vdd); | ||
2857 | + kfree(isp); | ||
2858 | + | ||
2859 | + return 0; | ||
2860 | +} | ||
2861 | + | ||
2862 | +static int isp_map_mem_resource(struct platform_device *pdev, | ||
2863 | + struct isp_device *isp, | ||
2864 | + enum isp_mem_resources res) | ||
2865 | +{ | ||
2866 | + struct resource *mem; | ||
2867 | + | ||
2868 | + /* request the mem region for the camera registers */ | ||
2869 | + | ||
2870 | + mem = platform_get_resource(pdev, IORESOURCE_MEM, res); | ||
2871 | + if (!mem) { | ||
2872 | + dev_err(isp->dev, "no mem resource?\n"); | ||
2873 | + return -ENODEV; | ||
2874 | + } | ||
2875 | + | ||
2876 | + if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { | ||
2877 | + dev_err(isp->dev, | ||
2878 | + "cannot reserve camera register I/O region\n"); | ||
2879 | + return -ENODEV; | ||
2880 | + } | ||
2881 | + isp->mmio_base_phys[res] = mem->start; | ||
2882 | + isp->mmio_size[res] = resource_size(mem); | ||
2883 | + | ||
2884 | + /* map the region */ | ||
2885 | + isp->mmio_base[res] = ioremap_nocache(isp->mmio_base_phys[res], | ||
2886 | + isp->mmio_size[res]); | ||
2887 | + if (!isp->mmio_base[res]) { | ||
2888 | + dev_err(isp->dev, "cannot map camera register I/O region\n"); | ||
2889 | + return -ENODEV; | ||
2890 | + } | ||
2891 | + | ||
2892 | + return 0; | ||
2893 | +} | ||
2894 | + | ||
2895 | +/* | ||
2896 | + * isp_probe - Probe ISP platform device | ||
2897 | + * @pdev: Pointer to ISP platform device | ||
2898 | + * | ||
2899 | + * Returns 0 if successful, | ||
2900 | + * -ENOMEM if no memory available, | ||
2901 | + * -ENODEV if no platform device resources found | ||
2902 | + * or no space for remapping registers, | ||
2903 | + * -EINVAL if couldn't install ISR, | ||
2904 | + * or clk_get return error value. | ||
2905 | + */ | ||
2906 | +static int isp_probe(struct platform_device *pdev) | ||
2907 | +{ | ||
2908 | + struct isp_platform_data *pdata = pdev->dev.platform_data; | ||
2909 | + struct isp_device *isp; | ||
2910 | + int ret; | ||
2911 | + int i, m; | ||
2912 | + | ||
2913 | + if (pdata == NULL) | ||
2914 | + return -EINVAL; | ||
2915 | + | ||
2916 | + isp = kzalloc(sizeof(*isp), GFP_KERNEL); | ||
2917 | + if (!isp) { | ||
2918 | + dev_err(&pdev->dev, "could not allocate memory\n"); | ||
2919 | + return -ENOMEM; | ||
2920 | + } | ||
2921 | + | ||
2922 | + isp->autoidle = autoidle; | ||
2923 | + isp->platform_cb.set_xclk = isp_set_xclk; | ||
2924 | + isp->platform_cb.set_pixel_clock = isp_set_pixel_clock; | ||
2925 | + | ||
2926 | + mutex_init(&isp->isp_mutex); | ||
2927 | + spin_lock_init(&isp->stat_lock); | ||
2928 | + | ||
2929 | + isp->dev = &pdev->dev; | ||
2930 | + isp->pdata = pdata; | ||
2931 | + isp->ref_count = 0; | ||
2932 | + | ||
2933 | + isp->raw_dmamask = DMA_BIT_MASK(32); | ||
2934 | + isp->dev->dma_mask = &isp->raw_dmamask; | ||
2935 | + isp->dev->coherent_dma_mask = DMA_BIT_MASK(32); | ||
2936 | + | ||
2937 | + platform_set_drvdata(pdev, isp); | ||
2938 | + | ||
2939 | + /* Regulators */ | ||
2940 | + isp->isp_csiphy1.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY1"); | ||
2941 | + isp->isp_csiphy2.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY2"); | ||
2942 | + | ||
2943 | + /* Clocks */ | ||
2944 | + ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN); | ||
2945 | + if (ret < 0) | ||
2946 | + goto error; | ||
2947 | + | ||
2948 | + ret = isp_get_clocks(isp); | ||
2949 | + if (ret < 0) | ||
2950 | + goto error; | ||
2951 | + | ||
2952 | + if (omap3isp_get(isp) == NULL) | ||
2953 | + goto error; | ||
2954 | + | ||
2955 | + ret = isp_reset(isp); | ||
2956 | + if (ret < 0) | ||
2957 | + goto error_isp; | ||
2958 | + | ||
2959 | + /* Memory resources */ | ||
2960 | + isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); | ||
2961 | + dev_info(isp->dev, "Revision %d.%d found\n", | ||
2962 | + (isp->revision & 0xf0) >> 4, isp->revision & 0x0f); | ||
2963 | + | ||
2964 | + for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++) | ||
2965 | + if (isp->revision == isp_res_maps[m].isp_rev) | ||
2966 | + break; | ||
2967 | + | ||
2968 | + if (m == ARRAY_SIZE(isp_res_maps)) { | ||
2969 | + dev_err(isp->dev, "No resource map found for ISP rev %d.%d\n", | ||
2970 | + (isp->revision & 0xf0) >> 4, isp->revision & 0xf); | ||
2971 | + ret = -ENODEV; | ||
2972 | + goto error_isp; | ||
2973 | + } | ||
2974 | + | ||
2975 | + for (i = 1; i < OMAP3_ISP_IOMEM_LAST; i++) { | ||
2976 | + if (isp_res_maps[m].map & 1 << i) { | ||
2977 | + ret = isp_map_mem_resource(pdev, isp, i); | ||
2978 | + if (ret) | ||
2979 | + goto error_isp; | ||
2980 | + } | ||
2981 | + } | ||
2982 | + | ||
2983 | + /* IOMMU */ | ||
2984 | + isp->iommu = iommu_get("isp"); | ||
2985 | + if (IS_ERR_OR_NULL(isp->iommu)) { | ||
2986 | + isp->iommu = NULL; | ||
2987 | + ret = -ENODEV; | ||
2988 | + goto error_isp; | ||
2989 | + } | ||
2990 | + | ||
2991 | + /* Interrupt */ | ||
2992 | + isp->irq_num = platform_get_irq(pdev, 0); | ||
2993 | + if (isp->irq_num <= 0) { | ||
2994 | + dev_err(isp->dev, "No IRQ resource\n"); | ||
2995 | + ret = -ENODEV; | ||
2996 | + goto error_isp; | ||
2997 | + } | ||
2998 | + | ||
2999 | + if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) { | ||
3000 | + dev_err(isp->dev, "Unable to request IRQ\n"); | ||
3001 | + ret = -EINVAL; | ||
3002 | + goto error_isp; | ||
3003 | + } | ||
3004 | + | ||
3005 | + /* Entities */ | ||
3006 | + ret = isp_initialize_modules(isp); | ||
3007 | + if (ret < 0) | ||
3008 | + goto error_irq; | ||
3009 | + | ||
3010 | + ret = isp_register_entities(isp); | ||
3011 | + if (ret < 0) | ||
3012 | + goto error_modules; | ||
3013 | + | ||
3014 | + isp_power_settings(isp, 1); | ||
3015 | + omap3isp_put(isp); | ||
3016 | + | ||
3017 | + return 0; | ||
3018 | + | ||
3019 | +error_modules: | ||
3020 | + isp_cleanup_modules(isp); | ||
3021 | +error_irq: | ||
3022 | + free_irq(isp->irq_num, isp); | ||
3023 | +error_isp: | ||
3024 | + iommu_put(isp->iommu); | ||
3025 | + omap3isp_put(isp); | ||
3026 | +error: | ||
3027 | + isp_put_clocks(isp); | ||
3028 | + | ||
3029 | + for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) { | ||
3030 | + if (isp->mmio_base[i]) { | ||
3031 | + iounmap(isp->mmio_base[i]); | ||
3032 | + isp->mmio_base[i] = NULL; | ||
3033 | + } | ||
3034 | + | ||
3035 | + if (isp->mmio_base_phys[i]) { | ||
3036 | + release_mem_region(isp->mmio_base_phys[i], | ||
3037 | + isp->mmio_size[i]); | ||
3038 | + isp->mmio_base_phys[i] = 0; | ||
3039 | + } | ||
3040 | + } | ||
3041 | + regulator_put(isp->isp_csiphy2.vdd); | ||
3042 | + regulator_put(isp->isp_csiphy1.vdd); | ||
3043 | + platform_set_drvdata(pdev, NULL); | ||
3044 | + kfree(isp); | ||
3045 | + | ||
3046 | + return ret; | ||
3047 | +} | ||
3048 | + | ||
3049 | +static const struct dev_pm_ops omap3isp_pm_ops = { | ||
3050 | + .prepare = isp_pm_prepare, | ||
3051 | + .suspend = isp_pm_suspend, | ||
3052 | + .resume = isp_pm_resume, | ||
3053 | + .complete = isp_pm_complete, | ||
3054 | +}; | ||
3055 | + | ||
3056 | +static struct platform_device_id omap3isp_id_table[] = { | ||
3057 | + { "omap3isp", 0 }, | ||
3058 | + { }, | ||
3059 | +}; | ||
3060 | +MODULE_DEVICE_TABLE(platform, omap3isp_id_table); | ||
3061 | + | ||
3062 | +static struct platform_driver omap3isp_driver = { | ||
3063 | + .probe = isp_probe, | ||
3064 | + .remove = isp_remove, | ||
3065 | + .id_table = omap3isp_id_table, | ||
3066 | + .driver = { | ||
3067 | + .owner = THIS_MODULE, | ||
3068 | + .name = "omap3isp", | ||
3069 | + .pm = &omap3isp_pm_ops, | ||
3070 | + }, | ||
3071 | +}; | ||
3072 | + | ||
3073 | +/* | ||
3074 | + * isp_init - ISP module initialization. | ||
3075 | + */ | ||
3076 | +static int __init isp_init(void) | ||
3077 | +{ | ||
3078 | + return platform_driver_register(&omap3isp_driver); | ||
3079 | +} | ||
3080 | + | ||
3081 | +/* | ||
3082 | + * isp_cleanup - ISP module cleanup. | ||
3083 | + */ | ||
3084 | +static void __exit isp_cleanup(void) | ||
3085 | +{ | ||
3086 | + platform_driver_unregister(&omap3isp_driver); | ||
3087 | +} | ||
3088 | + | ||
3089 | +module_init(isp_init); | ||
3090 | +module_exit(isp_cleanup); | ||
3091 | + | ||
3092 | +MODULE_AUTHOR("Nokia Corporation"); | ||
3093 | +MODULE_DESCRIPTION("TI OMAP3 ISP driver"); | ||
3094 | +MODULE_LICENSE("GPL"); | ||
3095 | diff --git a/drivers/media/video/isp/isp.h b/drivers/media/video/isp/isp.h | ||
3096 | new file mode 100644 | ||
3097 | index 0000000..44590a5 | ||
3098 | --- /dev/null | ||
3099 | +++ b/drivers/media/video/isp/isp.h | ||
3100 | @@ -0,0 +1,427 @@ | ||
3101 | +/* | ||
3102 | + * isp.h | ||
3103 | + * | ||
3104 | + * TI OMAP3 ISP - Core | ||
3105 | + * | ||
3106 | + * Copyright (C) 2009-2010 Nokia Corporation | ||
3107 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
3108 | + * | ||
3109 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
3110 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
3111 | + * | ||
3112 | + * This program is free software; you can redistribute it and/or modify | ||
3113 | + * it under the terms of the GNU General Public License version 2 as | ||
3114 | + * published by the Free Software Foundation. | ||
3115 | + * | ||
3116 | + * This program is distributed in the hope that it will be useful, but | ||
3117 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3118 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
3119 | + * General Public License for more details. | ||
3120 | + * | ||
3121 | + * You should have received a copy of the GNU General Public License | ||
3122 | + * along with this program; if not, write to the Free Software | ||
3123 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
3124 | + * 02110-1301 USA | ||
3125 | + */ | ||
3126 | + | ||
3127 | +#ifndef OMAP3_ISP_CORE_H | ||
3128 | +#define OMAP3_ISP_CORE_H | ||
3129 | + | ||
3130 | +#include <media/v4l2-device.h> | ||
3131 | +#include <linux/device.h> | ||
3132 | +#include <linux/io.h> | ||
3133 | +#include <linux/platform_device.h> | ||
3134 | +#include <linux/wait.h> | ||
3135 | +#include <plat/iommu.h> | ||
3136 | +#include <plat/iovmm.h> | ||
3137 | + | ||
3138 | +#include "ispstat.h" | ||
3139 | +#include "ispccdc.h" | ||
3140 | +#include "ispreg.h" | ||
3141 | +#include "ispresizer.h" | ||
3142 | +#include "isppreview.h" | ||
3143 | +#include "ispcsiphy.h" | ||
3144 | +#include "ispcsi2.h" | ||
3145 | +#include "ispccp2.h" | ||
3146 | + | ||
3147 | +#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8) | ||
3148 | + | ||
3149 | +#define ISP_TOK_TERM 0xFFFFFFFF /* | ||
3150 | + * terminating token for ISP | ||
3151 | + * modules reg list | ||
3152 | + */ | ||
3153 | +#define to_isp_device(ptr_module) \ | ||
3154 | + container_of(ptr_module, struct isp_device, isp_##ptr_module) | ||
3155 | +#define to_device(ptr_module) \ | ||
3156 | + (to_isp_device(ptr_module)->dev) | ||
3157 | + | ||
3158 | +enum isp_mem_resources { | ||
3159 | + OMAP3_ISP_IOMEM_MAIN, | ||
3160 | + OMAP3_ISP_IOMEM_CCP2, | ||
3161 | + OMAP3_ISP_IOMEM_CCDC, | ||
3162 | + OMAP3_ISP_IOMEM_HIST, | ||
3163 | + OMAP3_ISP_IOMEM_H3A, | ||
3164 | + OMAP3_ISP_IOMEM_PREV, | ||
3165 | + OMAP3_ISP_IOMEM_RESZ, | ||
3166 | + OMAP3_ISP_IOMEM_SBL, | ||
3167 | + OMAP3_ISP_IOMEM_CSI2A_REGS1, | ||
3168 | + OMAP3_ISP_IOMEM_CSIPHY2, | ||
3169 | + OMAP3_ISP_IOMEM_CSI2A_REGS2, | ||
3170 | + OMAP3_ISP_IOMEM_CSI2C_REGS1, | ||
3171 | + OMAP3_ISP_IOMEM_CSIPHY1, | ||
3172 | + OMAP3_ISP_IOMEM_CSI2C_REGS2, | ||
3173 | + OMAP3_ISP_IOMEM_LAST | ||
3174 | +}; | ||
3175 | + | ||
3176 | +enum isp_sbl_resource { | ||
3177 | + OMAP3_ISP_SBL_CSI1_READ = 0x1, | ||
3178 | + OMAP3_ISP_SBL_CSI1_WRITE = 0x2, | ||
3179 | + OMAP3_ISP_SBL_CSI2A_WRITE = 0x4, | ||
3180 | + OMAP3_ISP_SBL_CSI2C_WRITE = 0x8, | ||
3181 | + OMAP3_ISP_SBL_CCDC_LSC_READ = 0x10, | ||
3182 | + OMAP3_ISP_SBL_CCDC_WRITE = 0x20, | ||
3183 | + OMAP3_ISP_SBL_PREVIEW_READ = 0x40, | ||
3184 | + OMAP3_ISP_SBL_PREVIEW_WRITE = 0x80, | ||
3185 | + OMAP3_ISP_SBL_RESIZER_READ = 0x100, | ||
3186 | + OMAP3_ISP_SBL_RESIZER_WRITE = 0x200, | ||
3187 | +}; | ||
3188 | + | ||
3189 | +enum isp_subclk_resource { | ||
3190 | + OMAP3_ISP_SUBCLK_CCDC = (1 << 0), | ||
3191 | + OMAP3_ISP_SUBCLK_H3A = (1 << 1), | ||
3192 | + OMAP3_ISP_SUBCLK_HIST = (1 << 2), | ||
3193 | + OMAP3_ISP_SUBCLK_PREVIEW = (1 << 3), | ||
3194 | + OMAP3_ISP_SUBCLK_RESIZER = (1 << 4), | ||
3195 | +}; | ||
3196 | + | ||
3197 | +enum isp_interface_type { | ||
3198 | + ISP_INTERFACE_PARALLEL, | ||
3199 | + ISP_INTERFACE_CSI2A_PHY2, | ||
3200 | + ISP_INTERFACE_CCP2B_PHY1, | ||
3201 | + ISP_INTERFACE_CCP2B_PHY2, | ||
3202 | + ISP_INTERFACE_CSI2C_PHY1, | ||
3203 | +}; | ||
3204 | + | ||
3205 | +#define ISP_REVISION_1_0 0x10 | ||
3206 | +#define ISP_REVISION_2_0 0x20 | ||
3207 | +#define ISP_REVISION_15_0 0xF0 | ||
3208 | + | ||
3209 | +/* | ||
3210 | + * struct isp_res_mapping - Map ISP io resources to ISP revision. | ||
3211 | + * @isp_rev: ISP_REVISION_x_x | ||
3212 | + * @map: bitmap for enum isp_mem_resources | ||
3213 | + */ | ||
3214 | +struct isp_res_mapping { | ||
3215 | + u32 isp_rev; | ||
3216 | + u32 map; | ||
3217 | +}; | ||
3218 | + | ||
3219 | +/* | ||
3220 | + * struct isp_reg - Structure for ISP register values. | ||
3221 | + * @reg: 32-bit Register address. | ||
3222 | + * @val: 32-bit Register value. | ||
3223 | + */ | ||
3224 | +struct isp_reg { | ||
3225 | + enum isp_mem_resources mmio_range; | ||
3226 | + u32 reg; | ||
3227 | + u32 val; | ||
3228 | +}; | ||
3229 | + | ||
3230 | +/** | ||
3231 | + * struct isp_parallel_platform_data - Parallel interface platform data | ||
3232 | + * @width: Parallel bus width in bits (8, 10, 11 or 12) | ||
3233 | + * @data_lane_shift: Data lane shifter | ||
3234 | + * 0 - CAMEXT[13:0] -> CAM[13:0] | ||
3235 | + * 1 - CAMEXT[13:2] -> CAM[11:0] | ||
3236 | + * 2 - CAMEXT[13:4] -> CAM[9:0] | ||
3237 | + * 3 - CAMEXT[13:6] -> CAM[7:0] | ||
3238 | + * @clk_pol: Pixel clock polarity | ||
3239 | + * 0 - Non Inverted, 1 - Inverted | ||
3240 | + * @bridge: CCDC Bridge input control | ||
3241 | + * ISPCTRL_PAR_BRIDGE_DISABLE - Disable | ||
3242 | + * ISPCTRL_PAR_BRIDGE_LENDIAN - Little endian | ||
3243 | + * ISPCTRL_PAR_BRIDGE_BENDIAN - Big endian | ||
3244 | + */ | ||
3245 | +struct isp_parallel_platform_data { | ||
3246 | + unsigned int width; | ||
3247 | + unsigned int data_lane_shift:2; | ||
3248 | + unsigned int clk_pol:1; | ||
3249 | + unsigned int bridge:4; | ||
3250 | +}; | ||
3251 | + | ||
3252 | +/** | ||
3253 | + * struct isp_ccp2_platform_data - CCP2 interface platform data | ||
3254 | + * @strobe_clk_pol: Strobe/clock polarity | ||
3255 | + * 0 - Non Inverted, 1 - Inverted | ||
3256 | + * @crc: Enable the cyclic redundancy check | ||
3257 | + * @ccp2_mode: Enable CCP2 compatibility mode | ||
3258 | + * 0 - MIPI-CSI1 mode, 1 - CCP2 mode | ||
3259 | + * @phy_layer: Physical layer selection | ||
3260 | + * ISPCCP2_CTRL_PHY_SEL_CLOCK - Data/clock physical layer | ||
3261 | + * ISPCCP2_CTRL_PHY_SEL_STROBE - Data/strobe physical layer | ||
3262 | + * @vpclk_div: Video port output clock control | ||
3263 | + */ | ||
3264 | +struct isp_ccp2_platform_data { | ||
3265 | + unsigned int strobe_clk_pol:1; | ||
3266 | + unsigned int crc:1; | ||
3267 | + unsigned int ccp2_mode:1; | ||
3268 | + unsigned int phy_layer:1; | ||
3269 | + unsigned int vpclk_div:2; | ||
3270 | +}; | ||
3271 | + | ||
3272 | +/** | ||
3273 | + * struct isp_csi2_platform_data - CSI2 interface platform data | ||
3274 | + * @crc: Enable the cyclic redundancy check | ||
3275 | + * @vpclk_div: Video port output clock control | ||
3276 | + */ | ||
3277 | +struct isp_csi2_platform_data { | ||
3278 | + unsigned crc:1; | ||
3279 | + unsigned vpclk_div:2; | ||
3280 | +}; | ||
3281 | + | ||
3282 | +struct isp_subdev_i2c_board_info { | ||
3283 | + struct i2c_board_info *board_info; | ||
3284 | + int i2c_adapter_id; | ||
3285 | +}; | ||
3286 | + | ||
3287 | +struct isp_v4l2_subdevs_group { | ||
3288 | + struct isp_subdev_i2c_board_info *subdevs; | ||
3289 | + enum isp_interface_type interface; | ||
3290 | + union { | ||
3291 | + struct isp_parallel_platform_data parallel; | ||
3292 | + struct isp_ccp2_platform_data ccp2; | ||
3293 | + struct isp_csi2_platform_data csi2; | ||
3294 | + } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ | ||
3295 | +}; | ||
3296 | + | ||
3297 | +struct isp_platform_data { | ||
3298 | + struct isp_v4l2_subdevs_group *subdevs; | ||
3299 | +}; | ||
3300 | + | ||
3301 | +struct isp_platform_callback { | ||
3302 | + u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel); | ||
3303 | + int (*csiphy_config)(struct isp_csiphy *phy, | ||
3304 | + struct isp_csiphy_dphy_cfg *dphy, | ||
3305 | + struct isp_csiphy_lanes_cfg *lanes); | ||
3306 | + void (*set_pixel_clock)(struct isp_device *isp, unsigned int pixelclk); | ||
3307 | +}; | ||
3308 | + | ||
3309 | +/* | ||
3310 | + * struct isp_device - ISP device structure. | ||
3311 | + * @dev: Device pointer specific to the OMAP3 ISP. | ||
3312 | + * @revision: Stores current ISP module revision. | ||
3313 | + * @irq_num: Currently used IRQ number. | ||
3314 | + * @mmio_base: Array with kernel base addresses for ioremapped ISP register | ||
3315 | + * regions. | ||
3316 | + * @mmio_base_phys: Array with physical L4 bus addresses for ISP register | ||
3317 | + * regions. | ||
3318 | + * @mmio_size: Array with ISP register regions size in bytes. | ||
3319 | + * @raw_dmamask: Raw DMA mask | ||
3320 | + * @stat_lock: Spinlock for handling statistics | ||
3321 | + * @isp_mutex: Mutex for serializing requests to ISP. | ||
3322 | + * @has_context: Context has been saved at least once and can be restored. | ||
3323 | + * @ref_count: Reference count for handling multiple ISP requests. | ||
3324 | + * @cam_ick: Pointer to camera interface clock structure. | ||
3325 | + * @cam_mclk: Pointer to camera functional clock structure. | ||
3326 | + * @dpll4_m5_ck: Pointer to DPLL4 M5 clock structure. | ||
3327 | + * @csi2_fck: Pointer to camera CSI2 complexIO clock structure. | ||
3328 | + * @l3_ick: Pointer to OMAP3 L3 bus interface clock. | ||
3329 | + * @irq: Currently attached ISP ISR callbacks information structure. | ||
3330 | + * @isp_af: Pointer to current settings for ISP AutoFocus SCM. | ||
3331 | + * @isp_hist: Pointer to current settings for ISP Histogram SCM. | ||
3332 | + * @isp_h3a: Pointer to current settings for ISP Auto Exposure and | ||
3333 | + * White Balance SCM. | ||
3334 | + * @isp_res: Pointer to current settings for ISP Resizer. | ||
3335 | + * @isp_prev: Pointer to current settings for ISP Preview. | ||
3336 | + * @isp_ccdc: Pointer to current settings for ISP CCDC. | ||
3337 | + * @iommu: Pointer to requested IOMMU instance for ISP. | ||
3338 | + * @platform_cb: ISP driver callback function pointers for platform code | ||
3339 | + * | ||
3340 | + * This structure is used to store the OMAP ISP Information. | ||
3341 | + */ | ||
3342 | +struct isp_device { | ||
3343 | + struct v4l2_device v4l2_dev; | ||
3344 | + struct media_device media_dev; | ||
3345 | + struct device *dev; | ||
3346 | + u32 revision; | ||
3347 | + | ||
3348 | + /* platform HW resources */ | ||
3349 | + struct isp_platform_data *pdata; | ||
3350 | + unsigned int irq_num; | ||
3351 | + | ||
3352 | + void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST]; | ||
3353 | + unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST]; | ||
3354 | + resource_size_t mmio_size[OMAP3_ISP_IOMEM_LAST]; | ||
3355 | + | ||
3356 | + u64 raw_dmamask; | ||
3357 | + | ||
3358 | + /* ISP Obj */ | ||
3359 | + spinlock_t stat_lock; /* common lock for statistic drivers */ | ||
3360 | + struct mutex isp_mutex; /* For handling ref_count field */ | ||
3361 | + int has_context; | ||
3362 | + int ref_count; | ||
3363 | + unsigned int autoidle; | ||
3364 | + u32 xclk_divisor[2]; /* Two clocks, a and b. */ | ||
3365 | +#define ISP_CLK_CAM_ICK 0 | ||
3366 | +#define ISP_CLK_CAM_MCLK 1 | ||
3367 | +#define ISP_CLK_DPLL4_M5_CK 2 | ||
3368 | +#define ISP_CLK_CSI2_FCK 3 | ||
3369 | +#define ISP_CLK_L3_ICK 4 | ||
3370 | + struct clk *clock[5]; | ||
3371 | + | ||
3372 | + /* ISP modules */ | ||
3373 | + struct ispstat isp_af; | ||
3374 | + struct ispstat isp_aewb; | ||
3375 | + struct ispstat isp_hist; | ||
3376 | + struct isp_res_device isp_res; | ||
3377 | + struct isp_prev_device isp_prev; | ||
3378 | + struct isp_ccdc_device isp_ccdc; | ||
3379 | + struct isp_csi2_device isp_csi2a; | ||
3380 | + struct isp_csi2_device isp_csi2c; | ||
3381 | + struct isp_ccp2_device isp_ccp2; | ||
3382 | + struct isp_csiphy isp_csiphy1; | ||
3383 | + struct isp_csiphy isp_csiphy2; | ||
3384 | + | ||
3385 | + unsigned int sbl_resources; | ||
3386 | + unsigned int subclk_resources; | ||
3387 | + | ||
3388 | + struct iommu *iommu; | ||
3389 | + | ||
3390 | + struct isp_platform_callback platform_cb; | ||
3391 | +}; | ||
3392 | + | ||
3393 | +#define v4l2_dev_to_isp_device(dev) \ | ||
3394 | + container_of(dev, struct isp_device, v4l2_dev) | ||
3395 | + | ||
3396 | +void omap3isp_hist_dma_done(struct isp_device *isp); | ||
3397 | + | ||
3398 | +void omap3isp_flush(struct isp_device *isp); | ||
3399 | + | ||
3400 | +int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, | ||
3401 | + atomic_t *stopping); | ||
3402 | + | ||
3403 | +int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait, | ||
3404 | + atomic_t *stopping); | ||
3405 | + | ||
3406 | +int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, | ||
3407 | + enum isp_pipeline_stream_state state); | ||
3408 | +void omap3isp_configure_bridge(struct isp_device *isp, | ||
3409 | + enum ccdc_input_entity input, | ||
3410 | + const struct isp_parallel_platform_data *pdata); | ||
3411 | + | ||
3412 | +#define ISP_XCLK_NONE -1 | ||
3413 | +#define ISP_XCLK_A 0 | ||
3414 | +#define ISP_XCLK_B 1 | ||
3415 | + | ||
3416 | +struct isp_device *omap3isp_get(struct isp_device *isp); | ||
3417 | +void omap3isp_put(struct isp_device *isp); | ||
3418 | + | ||
3419 | +void omap3isp_print_status(struct isp_device *isp); | ||
3420 | + | ||
3421 | +void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res); | ||
3422 | +void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res); | ||
3423 | + | ||
3424 | +void omap3isp_subclk_enable(struct isp_device *isp, | ||
3425 | + enum isp_subclk_resource res); | ||
3426 | +void omap3isp_subclk_disable(struct isp_device *isp, | ||
3427 | + enum isp_subclk_resource res); | ||
3428 | + | ||
3429 | +int omap3isp_pipeline_pm_use(struct media_entity *entity, int use); | ||
3430 | + | ||
3431 | +int omap3isp_register_entities(struct platform_device *pdev, | ||
3432 | + struct v4l2_device *v4l2_dev); | ||
3433 | +void omap3isp_unregister_entities(struct platform_device *pdev); | ||
3434 | + | ||
3435 | +/* | ||
3436 | + * isp_reg_readl - Read value of an OMAP3 ISP register | ||
3437 | + * @dev: Device pointer specific to the OMAP3 ISP. | ||
3438 | + * @isp_mmio_range: Range to which the register offset refers to. | ||
3439 | + * @reg_offset: Register offset to read from. | ||
3440 | + * | ||
3441 | + * Returns an unsigned 32 bit value with the required register contents. | ||
3442 | + */ | ||
3443 | +static inline | ||
3444 | +u32 isp_reg_readl(struct isp_device *isp, enum isp_mem_resources isp_mmio_range, | ||
3445 | + u32 reg_offset) | ||
3446 | +{ | ||
3447 | + return __raw_readl(isp->mmio_base[isp_mmio_range] + reg_offset); | ||
3448 | +} | ||
3449 | + | ||
3450 | +/* | ||
3451 | + * isp_reg_writel - Write value to an OMAP3 ISP register | ||
3452 | + * @dev: Device pointer specific to the OMAP3 ISP. | ||
3453 | + * @reg_value: 32 bit value to write to the register. | ||
3454 | + * @isp_mmio_range: Range to which the register offset refers to. | ||
3455 | + * @reg_offset: Register offset to write into. | ||
3456 | + */ | ||
3457 | +static inline | ||
3458 | +void isp_reg_writel(struct isp_device *isp, u32 reg_value, | ||
3459 | + enum isp_mem_resources isp_mmio_range, u32 reg_offset) | ||
3460 | +{ | ||
3461 | + __raw_writel(reg_value, isp->mmio_base[isp_mmio_range] + reg_offset); | ||
3462 | +} | ||
3463 | + | ||
3464 | +/* | ||
3465 | + * isp_reg_and - Clear individual bits in an OMAP3 ISP register | ||
3466 | + * @dev: Device pointer specific to the OMAP3 ISP. | ||
3467 | + * @mmio_range: Range to which the register offset refers to. | ||
3468 | + * @reg: Register offset to work on. | ||
3469 | + * @clr_bits: 32 bit value which would be cleared in the register. | ||
3470 | + */ | ||
3471 | +static inline | ||
3472 | +void isp_reg_clr(struct isp_device *isp, enum isp_mem_resources mmio_range, | ||
3473 | + u32 reg, u32 clr_bits) | ||
3474 | +{ | ||
3475 | + u32 v = isp_reg_readl(isp, mmio_range, reg); | ||
3476 | + | ||
3477 | + isp_reg_writel(isp, v & ~clr_bits, mmio_range, reg); | ||
3478 | +} | ||
3479 | + | ||
3480 | +/* | ||
3481 | + * isp_reg_set - Set individual bits in an OMAP3 ISP register | ||
3482 | + * @dev: Device pointer specific to the OMAP3 ISP. | ||
3483 | + * @mmio_range: Range to which the register offset refers to. | ||
3484 | + * @reg: Register offset to work on. | ||
3485 | + * @set_bits: 32 bit value which would be set in the register. | ||
3486 | + */ | ||
3487 | +static inline | ||
3488 | +void isp_reg_set(struct isp_device *isp, enum isp_mem_resources mmio_range, | ||
3489 | + u32 reg, u32 set_bits) | ||
3490 | +{ | ||
3491 | + u32 v = isp_reg_readl(isp, mmio_range, reg); | ||
3492 | + | ||
3493 | + isp_reg_writel(isp, v | set_bits, mmio_range, reg); | ||
3494 | +} | ||
3495 | + | ||
3496 | +/* | ||
3497 | + * isp_reg_clr_set - Clear and set invidial bits in an OMAP3 ISP register | ||
3498 | + * @dev: Device pointer specific to the OMAP3 ISP. | ||
3499 | + * @mmio_range: Range to which the register offset refers to. | ||
3500 | + * @reg: Register offset to work on. | ||
3501 | + * @clr_bits: 32 bit value which would be cleared in the register. | ||
3502 | + * @set_bits: 32 bit value which would be set in the register. | ||
3503 | + * | ||
3504 | + * The clear operation is done first, and then the set operation. | ||
3505 | + */ | ||
3506 | +static inline | ||
3507 | +void isp_reg_clr_set(struct isp_device *isp, enum isp_mem_resources mmio_range, | ||
3508 | + u32 reg, u32 clr_bits, u32 set_bits) | ||
3509 | +{ | ||
3510 | + u32 v = isp_reg_readl(isp, mmio_range, reg); | ||
3511 | + | ||
3512 | + isp_reg_writel(isp, (v & ~clr_bits) | set_bits, mmio_range, reg); | ||
3513 | +} | ||
3514 | + | ||
3515 | +static inline enum v4l2_buf_type | ||
3516 | +isp_pad_buffer_type(const struct v4l2_subdev *subdev, int pad) | ||
3517 | +{ | ||
3518 | + if (pad >= subdev->entity.num_pads) | ||
3519 | + return 0; | ||
3520 | + | ||
3521 | + if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_INPUT) | ||
3522 | + return V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
3523 | + else | ||
3524 | + return V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
3525 | +} | ||
3526 | + | ||
3527 | +#endif /* OMAP3_ISP_CORE_H */ | ||
3528 | diff --git a/drivers/media/video/isp/ispccdc.c b/drivers/media/video/isp/ispccdc.c | ||
3529 | new file mode 100644 | ||
3530 | index 0000000..cb71e4f | ||
3531 | --- /dev/null | ||
3532 | +++ b/drivers/media/video/isp/ispccdc.c | ||
3533 | @@ -0,0 +1,2280 @@ | ||
3534 | +/* | ||
3535 | + * ispccdc.c | ||
3536 | + * | ||
3537 | + * TI OMAP3 ISP - CCDC module | ||
3538 | + * | ||
3539 | + * Copyright (C) 2009-2010 Nokia Corporation | ||
3540 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
3541 | + * | ||
3542 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
3543 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
3544 | + * | ||
3545 | + * This program is free software; you can redistribute it and/or modify | ||
3546 | + * it under the terms of the GNU General Public License version 2 as | ||
3547 | + * published by the Free Software Foundation. | ||
3548 | + * | ||
3549 | + * This program is distributed in the hope that it will be useful, but | ||
3550 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3551 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
3552 | + * General Public License for more details. | ||
3553 | + * | ||
3554 | + * You should have received a copy of the GNU General Public License | ||
3555 | + * along with this program; if not, write to the Free Software | ||
3556 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
3557 | + * 02110-1301 USA | ||
3558 | + */ | ||
3559 | + | ||
3560 | +#include <linux/module.h> | ||
3561 | +#include <linux/uaccess.h> | ||
3562 | +#include <linux/delay.h> | ||
3563 | +#include <linux/device.h> | ||
3564 | +#include <linux/dma-mapping.h> | ||
3565 | +#include <linux/mm.h> | ||
3566 | +#include <linux/sched.h> | ||
3567 | +#include <media/v4l2-ctrls.h> | ||
3568 | +#include <media/v4l2-event.h> | ||
3569 | + | ||
3570 | +#include "isp.h" | ||
3571 | +#include "ispreg.h" | ||
3572 | +#include "ispccdc.h" | ||
3573 | + | ||
3574 | +static struct v4l2_mbus_framefmt * | ||
3575 | +__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | ||
3576 | + unsigned int pad, enum v4l2_subdev_format_whence which); | ||
3577 | + | ||
3578 | +static const unsigned int ccdc_fmts[] = { | ||
3579 | + V4L2_MBUS_FMT_Y8_1X8, | ||
3580 | + V4L2_MBUS_FMT_SGRBG10_1X10, | ||
3581 | + V4L2_MBUS_FMT_SRGGB10_1X10, | ||
3582 | + V4L2_MBUS_FMT_SBGGR10_1X10, | ||
3583 | + V4L2_MBUS_FMT_SGBRG10_1X10, | ||
3584 | + V4L2_MBUS_FMT_SGRBG12_1X12, | ||
3585 | + V4L2_MBUS_FMT_SRGGB12_1X12, | ||
3586 | + V4L2_MBUS_FMT_SBGGR12_1X12, | ||
3587 | + V4L2_MBUS_FMT_SGBRG12_1X12, | ||
3588 | +}; | ||
3589 | + | ||
3590 | +/* | ||
3591 | + * ccdc_print_status - Print current CCDC Module register values. | ||
3592 | + * @ccdc: Pointer to ISP CCDC device. | ||
3593 | + * | ||
3594 | + * Also prints other debug information stored in the CCDC module. | ||
3595 | + */ | ||
3596 | +#define CCDC_PRINT_REGISTER(isp, name)\ | ||
3597 | + dev_dbg(isp->dev, "###CCDC " #name "=0x%08x\n", \ | ||
3598 | + isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_##name)) | ||
3599 | + | ||
3600 | +static void ccdc_print_status(struct isp_ccdc_device *ccdc) | ||
3601 | +{ | ||
3602 | + struct isp_device *isp = to_isp_device(ccdc); | ||
3603 | + | ||
3604 | + dev_dbg(isp->dev, "-------------CCDC Register dump-------------\n"); | ||
3605 | + | ||
3606 | + CCDC_PRINT_REGISTER(isp, PCR); | ||
3607 | + CCDC_PRINT_REGISTER(isp, SYN_MODE); | ||
3608 | + CCDC_PRINT_REGISTER(isp, HD_VD_WID); | ||
3609 | + CCDC_PRINT_REGISTER(isp, PIX_LINES); | ||
3610 | + CCDC_PRINT_REGISTER(isp, HORZ_INFO); | ||
3611 | + CCDC_PRINT_REGISTER(isp, VERT_START); | ||
3612 | + CCDC_PRINT_REGISTER(isp, VERT_LINES); | ||
3613 | + CCDC_PRINT_REGISTER(isp, CULLING); | ||
3614 | + CCDC_PRINT_REGISTER(isp, HSIZE_OFF); | ||
3615 | + CCDC_PRINT_REGISTER(isp, SDOFST); | ||
3616 | + CCDC_PRINT_REGISTER(isp, SDR_ADDR); | ||
3617 | + CCDC_PRINT_REGISTER(isp, CLAMP); | ||
3618 | + CCDC_PRINT_REGISTER(isp, DCSUB); | ||
3619 | + CCDC_PRINT_REGISTER(isp, COLPTN); | ||
3620 | + CCDC_PRINT_REGISTER(isp, BLKCMP); | ||
3621 | + CCDC_PRINT_REGISTER(isp, FPC); | ||
3622 | + CCDC_PRINT_REGISTER(isp, FPC_ADDR); | ||
3623 | + CCDC_PRINT_REGISTER(isp, VDINT); | ||
3624 | + CCDC_PRINT_REGISTER(isp, ALAW); | ||
3625 | + CCDC_PRINT_REGISTER(isp, REC656IF); | ||
3626 | + CCDC_PRINT_REGISTER(isp, CFG); | ||
3627 | + CCDC_PRINT_REGISTER(isp, FMTCFG); | ||
3628 | + CCDC_PRINT_REGISTER(isp, FMT_HORZ); | ||
3629 | + CCDC_PRINT_REGISTER(isp, FMT_VERT); | ||
3630 | + CCDC_PRINT_REGISTER(isp, PRGEVEN0); | ||
3631 | + CCDC_PRINT_REGISTER(isp, PRGEVEN1); | ||
3632 | + CCDC_PRINT_REGISTER(isp, PRGODD0); | ||
3633 | + CCDC_PRINT_REGISTER(isp, PRGODD1); | ||
3634 | + CCDC_PRINT_REGISTER(isp, VP_OUT); | ||
3635 | + CCDC_PRINT_REGISTER(isp, LSC_CONFIG); | ||
3636 | + CCDC_PRINT_REGISTER(isp, LSC_INITIAL); | ||
3637 | + CCDC_PRINT_REGISTER(isp, LSC_TABLE_BASE); | ||
3638 | + CCDC_PRINT_REGISTER(isp, LSC_TABLE_OFFSET); | ||
3639 | + | ||
3640 | + dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
3641 | +} | ||
3642 | + | ||
3643 | +/* | ||
3644 | + * omap3isp_ccdc_busy - Get busy state of the CCDC. | ||
3645 | + * @ccdc: Pointer to ISP CCDC device. | ||
3646 | + */ | ||
3647 | +int omap3isp_ccdc_busy(struct isp_ccdc_device *ccdc) | ||
3648 | +{ | ||
3649 | + struct isp_device *isp = to_isp_device(ccdc); | ||
3650 | + | ||
3651 | + return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR) & | ||
3652 | + ISPCCDC_PCR_BUSY; | ||
3653 | +} | ||
3654 | + | ||
3655 | +/* ----------------------------------------------------------------------------- | ||
3656 | + * Lens Shading Compensation | ||
3657 | + */ | ||
3658 | + | ||
3659 | +/* | ||
3660 | + * ccdc_lsc_validate_config - Check that LSC configuration is valid. | ||
3661 | + * @ccdc: Pointer to ISP CCDC device. | ||
3662 | + * @lsc_cfg: the LSC configuration to check. | ||
3663 | + * | ||
3664 | + * Returns 0 if the LSC configuration is valid, or -EINVAL if invalid. | ||
3665 | + */ | ||
3666 | +static int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc, | ||
3667 | + struct omap3isp_ccdc_lsc_config *lsc_cfg) | ||
3668 | +{ | ||
3669 | + struct isp_device *isp = to_isp_device(ccdc); | ||
3670 | + struct v4l2_mbus_framefmt *format; | ||
3671 | + unsigned int paxel_width, paxel_height; | ||
3672 | + unsigned int paxel_shift_x, paxel_shift_y; | ||
3673 | + unsigned int min_width, min_height, min_size; | ||
3674 | + unsigned int input_width, input_height; | ||
3675 | + | ||
3676 | + paxel_shift_x = lsc_cfg->gain_mode_m; | ||
3677 | + paxel_shift_y = lsc_cfg->gain_mode_n; | ||
3678 | + | ||
3679 | + if ((paxel_shift_x < 2) || (paxel_shift_x > 6) || | ||
3680 | + (paxel_shift_y < 2) || (paxel_shift_y > 6)) { | ||
3681 | + dev_dbg(isp->dev, "CCDC: LSC: Invalid paxel size\n"); | ||
3682 | + return -EINVAL; | ||
3683 | + } | ||
3684 | + | ||
3685 | + if (lsc_cfg->offset & 3) { | ||
3686 | + dev_dbg(isp->dev, "CCDC: LSC: Offset must be a multiple of " | ||
3687 | + "4\n"); | ||
3688 | + return -EINVAL; | ||
3689 | + } | ||
3690 | + | ||
3691 | + if ((lsc_cfg->initial_x & 1) || (lsc_cfg->initial_y & 1)) { | ||
3692 | + dev_dbg(isp->dev, "CCDC: LSC: initial_x and y must be even\n"); | ||
3693 | + return -EINVAL; | ||
3694 | + } | ||
3695 | + | ||
3696 | + format = __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK, | ||
3697 | + V4L2_SUBDEV_FORMAT_ACTIVE); | ||
3698 | + input_width = format->width; | ||
3699 | + input_height = format->height; | ||
3700 | + | ||
3701 | + /* Calculate minimum bytesize for validation */ | ||
3702 | + paxel_width = 1 << paxel_shift_x; | ||
3703 | + min_width = ((input_width + lsc_cfg->initial_x + paxel_width - 1) | ||
3704 | + >> paxel_shift_x) + 1; | ||
3705 | + | ||
3706 | + paxel_height = 1 << paxel_shift_y; | ||
3707 | + min_height = ((input_height + lsc_cfg->initial_y + paxel_height - 1) | ||
3708 | + >> paxel_shift_y) + 1; | ||
3709 | + | ||
3710 | + min_size = 4 * min_width * min_height; | ||
3711 | + if (min_size > lsc_cfg->size) { | ||
3712 | + dev_dbg(isp->dev, "CCDC: LSC: too small table\n"); | ||
3713 | + return -EINVAL; | ||
3714 | + } | ||
3715 | + if (lsc_cfg->offset < (min_width * 4)) { | ||
3716 | + dev_dbg(isp->dev, "CCDC: LSC: Offset is too small\n"); | ||
3717 | + return -EINVAL; | ||
3718 | + } | ||
3719 | + if ((lsc_cfg->size / lsc_cfg->offset) < min_height) { | ||
3720 | + dev_dbg(isp->dev, "CCDC: LSC: Wrong size/offset combination\n"); | ||
3721 | + return -EINVAL; | ||
3722 | + } | ||
3723 | + return 0; | ||
3724 | +} | ||
3725 | + | ||
3726 | +/* | ||
3727 | + * ccdc_lsc_program_table - Program Lens Shading Compensation table address. | ||
3728 | + * @ccdc: Pointer to ISP CCDC device. | ||
3729 | + */ | ||
3730 | +static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr) | ||
3731 | +{ | ||
3732 | + isp_reg_writel(to_isp_device(ccdc), addr, | ||
3733 | + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE); | ||
3734 | +} | ||
3735 | + | ||
3736 | +/* | ||
3737 | + * ccdc_lsc_setup_regs - Configures the lens shading compensation module | ||
3738 | + * @ccdc: Pointer to ISP CCDC device. | ||
3739 | + */ | ||
3740 | +static void ccdc_lsc_setup_regs(struct isp_ccdc_device *ccdc, | ||
3741 | + struct omap3isp_ccdc_lsc_config *cfg) | ||
3742 | +{ | ||
3743 | + struct isp_device *isp = to_isp_device(ccdc); | ||
3744 | + int reg; | ||
3745 | + | ||
3746 | + isp_reg_writel(isp, cfg->offset, OMAP3_ISP_IOMEM_CCDC, | ||
3747 | + ISPCCDC_LSC_TABLE_OFFSET); | ||
3748 | + | ||
3749 | + reg = 0; | ||
3750 | + reg |= cfg->gain_mode_n << ISPCCDC_LSC_GAIN_MODE_N_SHIFT; | ||
3751 | + reg |= cfg->gain_mode_m << ISPCCDC_LSC_GAIN_MODE_M_SHIFT; | ||
3752 | + reg |= cfg->gain_format << ISPCCDC_LSC_GAIN_FORMAT_SHIFT; | ||
3753 | + isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG); | ||
3754 | + | ||
3755 | + reg = 0; | ||
3756 | + reg &= ~ISPCCDC_LSC_INITIAL_X_MASK; | ||
3757 | + reg |= cfg->initial_x << ISPCCDC_LSC_INITIAL_X_SHIFT; | ||
3758 | + reg &= ~ISPCCDC_LSC_INITIAL_Y_MASK; | ||
3759 | + reg |= cfg->initial_y << ISPCCDC_LSC_INITIAL_Y_SHIFT; | ||
3760 | + isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, | ||
3761 | + ISPCCDC_LSC_INITIAL); | ||
3762 | +} | ||
3763 | + | ||
3764 | +static int ccdc_lsc_wait_prefetch(struct isp_ccdc_device *ccdc) | ||
3765 | +{ | ||
3766 | + struct isp_device *isp = to_isp_device(ccdc); | ||
3767 | + unsigned int wait; | ||
3768 | + | ||
3769 | + isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ, | ||
3770 | + OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); | ||
3771 | + | ||
3772 | + /* timeout 1 ms */ | ||
3773 | + for (wait = 0; wait < 1000; wait++) { | ||
3774 | + if (isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS) & | ||
3775 | + IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ) { | ||
3776 | + isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ, | ||
3777 | + OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); | ||
3778 | + return 0; | ||
3779 | + } | ||
3780 | + | ||
3781 | + rmb(); | ||
3782 | + udelay(1); | ||
3783 | + } | ||
3784 | + | ||
3785 | + return -ETIMEDOUT; | ||
3786 | +} | ||
3787 | + | ||
3788 | +/* | ||
3789 | + * __ccdc_lsc_enable - Enables/Disables the Lens Shading Compensation module. | ||
3790 | + * @ccdc: Pointer to ISP CCDC device. | ||
3791 | + * @enable: 0 Disables LSC, 1 Enables LSC. | ||
3792 | + */ | ||
3793 | +static int __ccdc_lsc_enable(struct isp_ccdc_device *ccdc, int enable) | ||
3794 | +{ | ||
3795 | + struct isp_device *isp = to_isp_device(ccdc); | ||
3796 | + const struct v4l2_mbus_framefmt *format = | ||
3797 | + __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK, | ||
3798 | + V4L2_SUBDEV_FORMAT_ACTIVE); | ||
3799 | + | ||
3800 | + if ((format->code != V4L2_MBUS_FMT_SGRBG10_1X10) && | ||
3801 | + (format->code != V4L2_MBUS_FMT_SRGGB10_1X10) && | ||
3802 | + (format->code != V4L2_MBUS_FMT_SBGGR10_1X10) && | ||
3803 | + (format->code != V4L2_MBUS_FMT_SGBRG10_1X10)) | ||
3804 | + return -EINVAL; | ||
3805 | + | ||
3806 | + if (enable) | ||
3807 | + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_LSC_READ); | ||
3808 | + | ||
3809 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, | ||
3810 | + ISPCCDC_LSC_ENABLE, enable ? ISPCCDC_LSC_ENABLE : 0); | ||
3811 | + | ||
3812 | + if (enable) { | ||
3813 | + if (ccdc_lsc_wait_prefetch(ccdc) < 0) { | ||
3814 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, | ||
3815 | + ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE); | ||
3816 | + ccdc->lsc.state = LSC_STATE_STOPPED; | ||
3817 | + dev_warn(to_device(ccdc), "LSC prefecth timeout\n"); | ||
3818 | + return -ETIMEDOUT; | ||
3819 | + } | ||
3820 | + ccdc->lsc.state = LSC_STATE_RUNNING; | ||
3821 | + } else { | ||
3822 | + ccdc->lsc.state = LSC_STATE_STOPPING; | ||
3823 | + } | ||
3824 | + | ||
3825 | + return 0; | ||
3826 | +} | ||
3827 | + | ||
3828 | +static int ccdc_lsc_busy(struct isp_ccdc_device *ccdc) | ||
3829 | +{ | ||
3830 | + struct isp_device *isp = to_isp_device(ccdc); | ||
3831 | + | ||
3832 | + return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG) & | ||
3833 | + ISPCCDC_LSC_BUSY; | ||
3834 | +} | ||
3835 | + | ||
3836 | +/* __ccdc_lsc_configure - Apply a new configuration to the LSC engine | ||
3837 | + * @ccdc: Pointer to ISP CCDC device | ||
3838 | + * @req: New configuration request | ||
3839 | + * | ||
3840 | + * context: in_interrupt() | ||
3841 | + */ | ||
3842 | +static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc, | ||
3843 | + struct ispccdc_lsc_config_req *req) | ||
3844 | +{ | ||
3845 | + if (!req->enable) | ||
3846 | + return -EINVAL; | ||
3847 | + | ||
3848 | + if (ccdc_lsc_validate_config(ccdc, &req->config) < 0) { | ||
3849 | + dev_dbg(to_device(ccdc), "Discard LSC configuration\n"); | ||
3850 | + return -EINVAL; | ||
3851 | + } | ||
3852 | + | ||
3853 | + if (ccdc_lsc_busy(ccdc)) | ||
3854 | + return -EBUSY; | ||
3855 | + | ||
3856 | + ccdc_lsc_setup_regs(ccdc, &req->config); | ||
3857 | + ccdc_lsc_program_table(ccdc, req->table); | ||
3858 | + return 0; | ||
3859 | +} | ||
3860 | + | ||
3861 | +/* | ||
3862 | + * ccdc_lsc_error_handler - Handle LSC prefetch error scenario. | ||
3863 | + * @ccdc: Pointer to ISP CCDC device. | ||
3864 | + * | ||
3865 | + * Disables LSC, and defers enablement to shadow registers update time. | ||
3866 | + */ | ||
3867 | +static void ccdc_lsc_error_handler(struct isp_ccdc_device *ccdc) | ||
3868 | +{ | ||
3869 | + struct isp_device *isp = to_isp_device(ccdc); | ||
3870 | + /* | ||
3871 | + * From OMAP3 TRM: When this event is pending, the module | ||
3872 | + * goes into transparent mode (output =input). Normal | ||
3873 | + * operation can be resumed at the start of the next frame | ||
3874 | + * after: | ||
3875 | + * 1) Clearing this event | ||
3876 | + * 2) Disabling the LSC module | ||
3877 | + * 3) Enabling it | ||
3878 | + */ | ||
3879 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, | ||
3880 | + ISPCCDC_LSC_ENABLE); | ||
3881 | + ccdc->lsc.state = LSC_STATE_STOPPED; | ||
3882 | +} | ||
3883 | + | ||
3884 | +static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc, | ||
3885 | + struct ispccdc_lsc_config_req *req) | ||
3886 | +{ | ||
3887 | + struct isp_device *isp = to_isp_device(ccdc); | ||
3888 | + | ||
3889 | + if (req == NULL) | ||
3890 | + return; | ||
3891 | + | ||
3892 | + if (req->iovm) | ||
3893 | + dma_unmap_sg(isp->dev, req->iovm->sgt->sgl, | ||
3894 | + req->iovm->sgt->nents, DMA_TO_DEVICE); | ||
3895 | + if (req->table) | ||
3896 | + iommu_vfree(isp->iommu, req->table); | ||
3897 | + kfree(req); | ||
3898 | +} | ||
3899 | + | ||
3900 | +static void ccdc_lsc_free_queue(struct isp_ccdc_device *ccdc, | ||
3901 | + struct list_head *queue) | ||
3902 | +{ | ||
3903 | + struct ispccdc_lsc_config_req *req, *n; | ||
3904 | + unsigned long flags; | ||
3905 | + | ||
3906 | + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
3907 | + list_for_each_entry_safe(req, n, queue, list) { | ||
3908 | + list_del(&req->list); | ||
3909 | + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
3910 | + ccdc_lsc_free_request(ccdc, req); | ||
3911 | + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
3912 | + } | ||
3913 | + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
3914 | +} | ||
3915 | + | ||
3916 | +static void ccdc_lsc_free_table_work(struct work_struct *work) | ||
3917 | +{ | ||
3918 | + struct isp_ccdc_device *ccdc; | ||
3919 | + struct ispccdc_lsc *lsc; | ||
3920 | + | ||
3921 | + lsc = container_of(work, struct ispccdc_lsc, table_work); | ||
3922 | + ccdc = container_of(lsc, struct isp_ccdc_device, lsc); | ||
3923 | + | ||
3924 | + ccdc_lsc_free_queue(ccdc, &lsc->free_queue); | ||
3925 | +} | ||
3926 | + | ||
3927 | +/* | ||
3928 | + * ccdc_lsc_config - Configure the LSC module from a userspace request | ||
3929 | + * | ||
3930 | + * Store the request LSC configuration in the LSC engine request pointer. The | ||
3931 | + * configuration will be applied to the hardware when the CCDC will be enabled, | ||
3932 | + * or at the next LSC interrupt if the CCDC is already running. | ||
3933 | + */ | ||
3934 | +static int ccdc_lsc_config(struct isp_ccdc_device *ccdc, | ||
3935 | + struct omap3isp_ccdc_update_config *config) | ||
3936 | +{ | ||
3937 | + struct isp_device *isp = to_isp_device(ccdc); | ||
3938 | + struct ispccdc_lsc_config_req *req; | ||
3939 | + unsigned long flags; | ||
3940 | + void *table; | ||
3941 | + u16 update; | ||
3942 | + int ret; | ||
3943 | + | ||
3944 | + update = config->update & | ||
3945 | + (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC); | ||
3946 | + if (!update) | ||
3947 | + return 0; | ||
3948 | + | ||
3949 | + if (update != (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC)) { | ||
3950 | + dev_dbg(to_device(ccdc), "%s: Both LSC configuration and table " | ||
3951 | + "need to be supplied\n", __func__); | ||
3952 | + return -EINVAL; | ||
3953 | + } | ||
3954 | + | ||
3955 | + req = kzalloc(sizeof(*req), GFP_KERNEL); | ||
3956 | + if (req == NULL) | ||
3957 | + return -ENOMEM; | ||
3958 | + | ||
3959 | + if (config->flag & OMAP3ISP_CCDC_CONFIG_LSC) { | ||
3960 | + if (copy_from_user(&req->config, config->lsc_cfg, | ||
3961 | + sizeof(req->config))) { | ||
3962 | + ret = -EFAULT; | ||
3963 | + goto done; | ||
3964 | + } | ||
3965 | + | ||
3966 | + req->enable = 1; | ||
3967 | + | ||
3968 | + req->table = iommu_vmalloc(isp->iommu, 0, req->config.size, | ||
3969 | + IOMMU_FLAG); | ||
3970 | + if (IS_ERR_VALUE(req->table)) { | ||
3971 | + req->table = 0; | ||
3972 | + ret = -ENOMEM; | ||
3973 | + goto done; | ||
3974 | + } | ||
3975 | + | ||
3976 | + req->iovm = find_iovm_area(isp->iommu, req->table); | ||
3977 | + if (req->iovm == NULL) { | ||
3978 | + ret = -ENOMEM; | ||
3979 | + goto done; | ||
3980 | + } | ||
3981 | + | ||
3982 | + if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl, | ||
3983 | + req->iovm->sgt->nents, DMA_TO_DEVICE)) { | ||
3984 | + ret = -ENOMEM; | ||
3985 | + req->iovm = NULL; | ||
3986 | + goto done; | ||
3987 | + } | ||
3988 | + | ||
3989 | + dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl, | ||
3990 | + req->iovm->sgt->nents, DMA_TO_DEVICE); | ||
3991 | + | ||
3992 | + table = da_to_va(isp->iommu, req->table); | ||
3993 | + if (copy_from_user(table, config->lsc, req->config.size)) { | ||
3994 | + ret = -EFAULT; | ||
3995 | + goto done; | ||
3996 | + } | ||
3997 | + | ||
3998 | + dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl, | ||
3999 | + req->iovm->sgt->nents, DMA_TO_DEVICE); | ||
4000 | + } | ||
4001 | + | ||
4002 | + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
4003 | + if (ccdc->lsc.request) { | ||
4004 | + list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue); | ||
4005 | + schedule_work(&ccdc->lsc.table_work); | ||
4006 | + } | ||
4007 | + ccdc->lsc.request = req; | ||
4008 | + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
4009 | + | ||
4010 | + ret = 0; | ||
4011 | + | ||
4012 | +done: | ||
4013 | + if (ret < 0) | ||
4014 | + ccdc_lsc_free_request(ccdc, req); | ||
4015 | + | ||
4016 | + return ret; | ||
4017 | +} | ||
4018 | + | ||
4019 | +static inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc) | ||
4020 | +{ | ||
4021 | + unsigned long flags; | ||
4022 | + | ||
4023 | + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
4024 | + if (ccdc->lsc.active) { | ||
4025 | + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
4026 | + return 1; | ||
4027 | + } | ||
4028 | + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
4029 | + return 0; | ||
4030 | +} | ||
4031 | + | ||
4032 | +static int ccdc_lsc_enable(struct isp_ccdc_device *ccdc) | ||
4033 | +{ | ||
4034 | + struct ispccdc_lsc *lsc = &ccdc->lsc; | ||
4035 | + | ||
4036 | + if (lsc->state != LSC_STATE_STOPPED) | ||
4037 | + return -EINVAL; | ||
4038 | + | ||
4039 | + if (lsc->active) { | ||
4040 | + list_add_tail(&lsc->active->list, &lsc->free_queue); | ||
4041 | + lsc->active = NULL; | ||
4042 | + } | ||
4043 | + | ||
4044 | + if (__ccdc_lsc_configure(ccdc, lsc->request) < 0) { | ||
4045 | + omap3isp_sbl_disable(to_isp_device(ccdc), | ||
4046 | + OMAP3_ISP_SBL_CCDC_LSC_READ); | ||
4047 | + list_add_tail(&lsc->request->list, &lsc->free_queue); | ||
4048 | + lsc->request = NULL; | ||
4049 | + goto done; | ||
4050 | + } | ||
4051 | + | ||
4052 | + lsc->active = lsc->request; | ||
4053 | + lsc->request = NULL; | ||
4054 | + __ccdc_lsc_enable(ccdc, 1); | ||
4055 | + | ||
4056 | +done: | ||
4057 | + if (!list_empty(&lsc->free_queue)) | ||
4058 | + schedule_work(&lsc->table_work); | ||
4059 | + | ||
4060 | + return 0; | ||
4061 | +} | ||
4062 | + | ||
4063 | +/* ----------------------------------------------------------------------------- | ||
4064 | + * Parameters configuration | ||
4065 | + */ | ||
4066 | + | ||
4067 | +/* | ||
4068 | + * ccdc_configure_clamp - Configure optical-black or digital clamping | ||
4069 | + * @ccdc: Pointer to ISP CCDC device. | ||
4070 | + * | ||
4071 | + * The CCDC performs either optical-black or digital clamp. Configure and enable | ||
4072 | + * the selected clamp method. | ||
4073 | + */ | ||
4074 | +static void ccdc_configure_clamp(struct isp_ccdc_device *ccdc) | ||
4075 | +{ | ||
4076 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4077 | + u32 clamp; | ||
4078 | + | ||
4079 | + if (ccdc->obclamp) { | ||
4080 | + clamp = ccdc->clamp.obgain << ISPCCDC_CLAMP_OBGAIN_SHIFT; | ||
4081 | + clamp |= ccdc->clamp.oblen << ISPCCDC_CLAMP_OBSLEN_SHIFT; | ||
4082 | + clamp |= ccdc->clamp.oblines << ISPCCDC_CLAMP_OBSLN_SHIFT; | ||
4083 | + clamp |= ccdc->clamp.obstpixel << ISPCCDC_CLAMP_OBST_SHIFT; | ||
4084 | + isp_reg_writel(isp, clamp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP); | ||
4085 | + } else { | ||
4086 | + isp_reg_writel(isp, ccdc->clamp.dcsubval, | ||
4087 | + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB); | ||
4088 | + } | ||
4089 | + | ||
4090 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP, | ||
4091 | + ISPCCDC_CLAMP_CLAMPEN, | ||
4092 | + ccdc->obclamp ? ISPCCDC_CLAMP_CLAMPEN : 0); | ||
4093 | +} | ||
4094 | + | ||
4095 | +/* | ||
4096 | + * ccdc_configure_fpc - Configure Faulty Pixel Correction | ||
4097 | + * @ccdc: Pointer to ISP CCDC device. | ||
4098 | + */ | ||
4099 | +static void ccdc_configure_fpc(struct isp_ccdc_device *ccdc) | ||
4100 | +{ | ||
4101 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4102 | + | ||
4103 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, ISPCCDC_FPC_FPCEN); | ||
4104 | + | ||
4105 | + if (!ccdc->fpc_en) | ||
4106 | + return; | ||
4107 | + | ||
4108 | + isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC, | ||
4109 | + ISPCCDC_FPC_ADDR); | ||
4110 | + /* The FPNUM field must be set before enabling FPC. */ | ||
4111 | + isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT), | ||
4112 | + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); | ||
4113 | + isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT) | | ||
4114 | + ISPCCDC_FPC_FPCEN, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); | ||
4115 | +} | ||
4116 | + | ||
4117 | +/* | ||
4118 | + * ccdc_configure_black_comp - Configure Black Level Compensation. | ||
4119 | + * @ccdc: Pointer to ISP CCDC device. | ||
4120 | + */ | ||
4121 | +static void ccdc_configure_black_comp(struct isp_ccdc_device *ccdc) | ||
4122 | +{ | ||
4123 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4124 | + u32 blcomp; | ||
4125 | + | ||
4126 | + blcomp = ccdc->blcomp.b_mg << ISPCCDC_BLKCMP_B_MG_SHIFT; | ||
4127 | + blcomp |= ccdc->blcomp.gb_g << ISPCCDC_BLKCMP_GB_G_SHIFT; | ||
4128 | + blcomp |= ccdc->blcomp.gr_cy << ISPCCDC_BLKCMP_GR_CY_SHIFT; | ||
4129 | + blcomp |= ccdc->blcomp.r_ye << ISPCCDC_BLKCMP_R_YE_SHIFT; | ||
4130 | + | ||
4131 | + isp_reg_writel(isp, blcomp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP); | ||
4132 | +} | ||
4133 | + | ||
4134 | +/* | ||
4135 | + * ccdc_configure_lpf - Configure Low-Pass Filter (LPF). | ||
4136 | + * @ccdc: Pointer to ISP CCDC device. | ||
4137 | + */ | ||
4138 | +static void ccdc_configure_lpf(struct isp_ccdc_device *ccdc) | ||
4139 | +{ | ||
4140 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4141 | + | ||
4142 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE, | ||
4143 | + ISPCCDC_SYN_MODE_LPF, | ||
4144 | + ccdc->lpf ? ISPCCDC_SYN_MODE_LPF : 0); | ||
4145 | +} | ||
4146 | + | ||
4147 | +/* | ||
4148 | + * ccdc_configure_alaw - Configure A-law compression. | ||
4149 | + * @ccdc: Pointer to ISP CCDC device. | ||
4150 | + */ | ||
4151 | +static void ccdc_configure_alaw(struct isp_ccdc_device *ccdc) | ||
4152 | +{ | ||
4153 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4154 | + u32 alaw = 0; | ||
4155 | + | ||
4156 | + switch (ccdc->syncif.datsz) { | ||
4157 | + case 8: | ||
4158 | + return; | ||
4159 | + | ||
4160 | + case 10: | ||
4161 | + alaw = ISPCCDC_ALAW_GWDI_9_0; | ||
4162 | + break; | ||
4163 | + case 11: | ||
4164 | + alaw = ISPCCDC_ALAW_GWDI_10_1; | ||
4165 | + break; | ||
4166 | + case 12: | ||
4167 | + alaw = ISPCCDC_ALAW_GWDI_11_2; | ||
4168 | + break; | ||
4169 | + case 13: | ||
4170 | + alaw = ISPCCDC_ALAW_GWDI_12_3; | ||
4171 | + break; | ||
4172 | + } | ||
4173 | + | ||
4174 | + if (ccdc->alaw) | ||
4175 | + alaw |= ISPCCDC_ALAW_CCDTBL; | ||
4176 | + | ||
4177 | + isp_reg_writel(isp, alaw, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW); | ||
4178 | +} | ||
4179 | + | ||
4180 | +/* | ||
4181 | + * ccdc_config_imgattr - Configure sensor image specific attributes. | ||
4182 | + * @ccdc: Pointer to ISP CCDC device. | ||
4183 | + * @colptn: Color pattern of the sensor. | ||
4184 | + */ | ||
4185 | +static void ccdc_config_imgattr(struct isp_ccdc_device *ccdc, u32 colptn) | ||
4186 | +{ | ||
4187 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4188 | + | ||
4189 | + isp_reg_writel(isp, colptn, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN); | ||
4190 | +} | ||
4191 | + | ||
4192 | +/* | ||
4193 | + * ccdc_config - Set CCDC configuration from userspace | ||
4194 | + * @ccdc: Pointer to ISP CCDC device. | ||
4195 | + * @userspace_add: Structure containing CCDC configuration sent from userspace. | ||
4196 | + * | ||
4197 | + * Returns 0 if successful, -EINVAL if the pointer to the configuration | ||
4198 | + * structure is null, or the copy_from_user function fails to copy user space | ||
4199 | + * memory to kernel space memory. | ||
4200 | + */ | ||
4201 | +static int ccdc_config(struct isp_ccdc_device *ccdc, | ||
4202 | + struct omap3isp_ccdc_update_config *ccdc_struct) | ||
4203 | +{ | ||
4204 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4205 | + unsigned long flags; | ||
4206 | + | ||
4207 | + spin_lock_irqsave(&ccdc->lock, flags); | ||
4208 | + ccdc->shadow_update = 1; | ||
4209 | + spin_unlock_irqrestore(&ccdc->lock, flags); | ||
4210 | + | ||
4211 | + if (OMAP3ISP_CCDC_ALAW & ccdc_struct->update) { | ||
4212 | + ccdc->alaw = !!(OMAP3ISP_CCDC_ALAW & ccdc_struct->flag); | ||
4213 | + ccdc->update |= OMAP3ISP_CCDC_ALAW; | ||
4214 | + } | ||
4215 | + | ||
4216 | + if (OMAP3ISP_CCDC_LPF & ccdc_struct->update) { | ||
4217 | + ccdc->lpf = !!(OMAP3ISP_CCDC_LPF & ccdc_struct->flag); | ||
4218 | + ccdc->update |= OMAP3ISP_CCDC_LPF; | ||
4219 | + } | ||
4220 | + | ||
4221 | + if (OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->update) { | ||
4222 | + if (copy_from_user(&ccdc->clamp, ccdc_struct->bclamp, | ||
4223 | + sizeof(ccdc->clamp))) { | ||
4224 | + ccdc->shadow_update = 0; | ||
4225 | + return -EFAULT; | ||
4226 | + } | ||
4227 | + | ||
4228 | + ccdc->obclamp = !!(OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->flag); | ||
4229 | + ccdc->update |= OMAP3ISP_CCDC_BLCLAMP; | ||
4230 | + } | ||
4231 | + | ||
4232 | + if (OMAP3ISP_CCDC_BCOMP & ccdc_struct->update) { | ||
4233 | + if (copy_from_user(&ccdc->blcomp, ccdc_struct->blcomp, | ||
4234 | + sizeof(ccdc->blcomp))) { | ||
4235 | + ccdc->shadow_update = 0; | ||
4236 | + return -EFAULT; | ||
4237 | + } | ||
4238 | + | ||
4239 | + ccdc->update |= OMAP3ISP_CCDC_BCOMP; | ||
4240 | + } | ||
4241 | + | ||
4242 | + ccdc->shadow_update = 0; | ||
4243 | + | ||
4244 | + if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) { | ||
4245 | + u32 table_old = 0; | ||
4246 | + u32 table_new; | ||
4247 | + u32 size; | ||
4248 | + | ||
4249 | + if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED) | ||
4250 | + return -EBUSY; | ||
4251 | + | ||
4252 | + ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag); | ||
4253 | + | ||
4254 | + if (ccdc->fpc_en) { | ||
4255 | + if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc, | ||
4256 | + sizeof(ccdc->fpc))) | ||
4257 | + return -EFAULT; | ||
4258 | + | ||
4259 | + /* | ||
4260 | + * table_new must be 64-bytes aligned, but it's | ||
4261 | + * already done by iommu_vmalloc(). | ||
4262 | + */ | ||
4263 | + size = ccdc->fpc.fpnum * 4; | ||
4264 | + table_new = iommu_vmalloc(isp->iommu, 0, size, | ||
4265 | + IOMMU_FLAG); | ||
4266 | + if (IS_ERR_VALUE(table_new)) | ||
4267 | + return -ENOMEM; | ||
4268 | + | ||
4269 | + if (copy_from_user(da_to_va(isp->iommu, table_new), | ||
4270 | + (__force void __user *) | ||
4271 | + ccdc->fpc.fpcaddr, size)) { | ||
4272 | + iommu_vfree(isp->iommu, table_new); | ||
4273 | + return -EFAULT; | ||
4274 | + } | ||
4275 | + | ||
4276 | + table_old = ccdc->fpc.fpcaddr; | ||
4277 | + ccdc->fpc.fpcaddr = table_new; | ||
4278 | + } | ||
4279 | + | ||
4280 | + ccdc_configure_fpc(ccdc); | ||
4281 | + if (table_old != 0) | ||
4282 | + iommu_vfree(isp->iommu, table_old); | ||
4283 | + } | ||
4284 | + | ||
4285 | + return ccdc_lsc_config(ccdc, ccdc_struct); | ||
4286 | +} | ||
4287 | + | ||
4288 | +static void ccdc_apply_controls(struct isp_ccdc_device *ccdc) | ||
4289 | +{ | ||
4290 | + if (ccdc->update & OMAP3ISP_CCDC_ALAW) { | ||
4291 | + ccdc_configure_alaw(ccdc); | ||
4292 | + ccdc->update &= ~OMAP3ISP_CCDC_ALAW; | ||
4293 | + } | ||
4294 | + | ||
4295 | + if (ccdc->update & OMAP3ISP_CCDC_LPF) { | ||
4296 | + ccdc_configure_lpf(ccdc); | ||
4297 | + ccdc->update &= ~OMAP3ISP_CCDC_LPF; | ||
4298 | + } | ||
4299 | + | ||
4300 | + if (ccdc->update & OMAP3ISP_CCDC_BLCLAMP) { | ||
4301 | + ccdc_configure_clamp(ccdc); | ||
4302 | + ccdc->update &= ~OMAP3ISP_CCDC_BLCLAMP; | ||
4303 | + } | ||
4304 | + | ||
4305 | + if (ccdc->update & OMAP3ISP_CCDC_BCOMP) { | ||
4306 | + ccdc_configure_black_comp(ccdc); | ||
4307 | + ccdc->update &= ~OMAP3ISP_CCDC_BCOMP; | ||
4308 | + } | ||
4309 | +} | ||
4310 | + | ||
4311 | +/* | ||
4312 | + * omap3isp_ccdc_restore_context - Restore values of the CCDC module registers | ||
4313 | + * @dev: Pointer to ISP device | ||
4314 | + */ | ||
4315 | +void omap3isp_ccdc_restore_context(struct isp_device *isp) | ||
4316 | +{ | ||
4317 | + struct isp_ccdc_device *ccdc = &isp->isp_ccdc; | ||
4318 | + | ||
4319 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_VDLC); | ||
4320 | + | ||
4321 | + ccdc->update = OMAP3ISP_CCDC_ALAW | OMAP3ISP_CCDC_LPF | ||
4322 | + | OMAP3ISP_CCDC_BLCLAMP | OMAP3ISP_CCDC_BCOMP; | ||
4323 | + ccdc_apply_controls(ccdc); | ||
4324 | + ccdc_configure_fpc(ccdc); | ||
4325 | +} | ||
4326 | + | ||
4327 | +/* ----------------------------------------------------------------------------- | ||
4328 | + * Format- and pipeline-related configuration helpers | ||
4329 | + */ | ||
4330 | + | ||
4331 | +/* | ||
4332 | + * ccdc_config_vp - Configure the Video Port. | ||
4333 | + * @ccdc: Pointer to ISP CCDC device. | ||
4334 | + */ | ||
4335 | +static void ccdc_config_vp(struct isp_ccdc_device *ccdc) | ||
4336 | +{ | ||
4337 | + struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); | ||
4338 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4339 | + unsigned long l3_ick = pipe->l3_ick; | ||
4340 | + unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8; | ||
4341 | + unsigned int div = 0; | ||
4342 | + u32 fmtcfg_vp; | ||
4343 | + | ||
4344 | + fmtcfg_vp = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG) | ||
4345 | + & ~(ISPCCDC_FMTCFG_VPIN_MASK | ISPCCDC_FMTCFG_VPIF_FRQ_MASK); | ||
4346 | + | ||
4347 | + switch (ccdc->syncif.datsz) { | ||
4348 | + case 8: | ||
4349 | + case 10: | ||
4350 | + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0; | ||
4351 | + break; | ||
4352 | + case 11: | ||
4353 | + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1; | ||
4354 | + break; | ||
4355 | + case 12: | ||
4356 | + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2; | ||
4357 | + break; | ||
4358 | + case 13: | ||
4359 | + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3; | ||
4360 | + break; | ||
4361 | + }; | ||
4362 | + | ||
4363 | + if (pipe->input) | ||
4364 | + div = DIV_ROUND_UP(l3_ick, pipe->max_rate); | ||
4365 | + else if (ccdc->vpcfg.pixelclk) | ||
4366 | + div = l3_ick / ccdc->vpcfg.pixelclk; | ||
4367 | + | ||
4368 | + div = clamp(div, 2U, max_div); | ||
4369 | + fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; | ||
4370 | + | ||
4371 | + isp_reg_writel(isp, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); | ||
4372 | +} | ||
4373 | + | ||
4374 | +/* | ||
4375 | + * ccdc_enable_vp - Enable Video Port. | ||
4376 | + * @ccdc: Pointer to ISP CCDC device. | ||
4377 | + * @enable: 0 Disables VP, 1 Enables VP | ||
4378 | + * | ||
4379 | + * This is needed for outputting image to Preview, H3A and HIST ISP submodules. | ||
4380 | + */ | ||
4381 | +static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable) | ||
4382 | +{ | ||
4383 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4384 | + | ||
4385 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, | ||
4386 | + ISPCCDC_FMTCFG_VPEN, enable ? ISPCCDC_FMTCFG_VPEN : 0); | ||
4387 | +} | ||
4388 | + | ||
4389 | +/* | ||
4390 | + * ccdc_config_outlineoffset - Configure memory saving output line offset | ||
4391 | + * @ccdc: Pointer to ISP CCDC device. | ||
4392 | + * @offset: Address offset to start a new line. Must be twice the | ||
4393 | + * Output width and aligned on 32 byte boundary | ||
4394 | + * @oddeven: Specifies the odd/even line pattern to be chosen to store the | ||
4395 | + * output. | ||
4396 | + * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines. | ||
4397 | + * | ||
4398 | + * - Configures the output line offset when stored in memory | ||
4399 | + * - Sets the odd/even line pattern to store the output | ||
4400 | + * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4)) | ||
4401 | + * - Configures the number of even and odd line fields in case of rearranging | ||
4402 | + * the lines. | ||
4403 | + */ | ||
4404 | +static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc, | ||
4405 | + u32 offset, u8 oddeven, u8 numlines) | ||
4406 | +{ | ||
4407 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4408 | + | ||
4409 | + isp_reg_writel(isp, offset & 0xffff, | ||
4410 | + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF); | ||
4411 | + | ||
4412 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
4413 | + ISPCCDC_SDOFST_FINV); | ||
4414 | + | ||
4415 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
4416 | + ISPCCDC_SDOFST_FOFST_4L); | ||
4417 | + | ||
4418 | + switch (oddeven) { | ||
4419 | + case EVENEVEN: | ||
4420 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
4421 | + (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT); | ||
4422 | + break; | ||
4423 | + case ODDEVEN: | ||
4424 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
4425 | + (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT); | ||
4426 | + break; | ||
4427 | + case EVENODD: | ||
4428 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
4429 | + (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT); | ||
4430 | + break; | ||
4431 | + case ODDODD: | ||
4432 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
4433 | + (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT); | ||
4434 | + break; | ||
4435 | + default: | ||
4436 | + break; | ||
4437 | + } | ||
4438 | +} | ||
4439 | + | ||
4440 | +/* | ||
4441 | + * ccdc_set_outaddr - Set memory address to save output image | ||
4442 | + * @ccdc: Pointer to ISP CCDC device. | ||
4443 | + * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary. | ||
4444 | + * | ||
4445 | + * Sets the memory address where the output will be saved. | ||
4446 | + */ | ||
4447 | +static void ccdc_set_outaddr(struct isp_ccdc_device *ccdc, u32 addr) | ||
4448 | +{ | ||
4449 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4450 | + | ||
4451 | + isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR); | ||
4452 | +} | ||
4453 | + | ||
4454 | +/* | ||
4455 | + * omap3isp_ccdc_max_rate - Calculate maximum input data rate based on the input | ||
4456 | + * @ccdc: Pointer to ISP CCDC device. | ||
4457 | + * @max_rate: Maximum calculated data rate. | ||
4458 | + * | ||
4459 | + * Returns in *max_rate less value between calculated and passed | ||
4460 | + */ | ||
4461 | +void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc, | ||
4462 | + unsigned int *max_rate) | ||
4463 | +{ | ||
4464 | + struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); | ||
4465 | + unsigned int rate; | ||
4466 | + | ||
4467 | + if (pipe == NULL) | ||
4468 | + return; | ||
4469 | + | ||
4470 | + /* | ||
4471 | + * TRM says that for parallel sensors the maximum data rate | ||
4472 | + * should be 90% form L3/2 clock, otherwise just L3/2. | ||
4473 | + */ | ||
4474 | + if (ccdc->input == CCDC_INPUT_PARALLEL) | ||
4475 | + rate = pipe->l3_ick / 2 * 9 / 10; | ||
4476 | + else | ||
4477 | + rate = pipe->l3_ick / 2; | ||
4478 | + | ||
4479 | + *max_rate = min(*max_rate, rate); | ||
4480 | +} | ||
4481 | + | ||
4482 | +/* | ||
4483 | + * ccdc_config_sync_if - Set CCDC sync interface configuration | ||
4484 | + * @ccdc: Pointer to ISP CCDC device. | ||
4485 | + * @syncif: Structure containing the sync parameters like field state, CCDC in | ||
4486 | + * master/slave mode, raw/yuv data, polarity of data, field, hs, vs | ||
4487 | + * signals. | ||
4488 | + */ | ||
4489 | +static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, | ||
4490 | + struct ispccdc_syncif *syncif) | ||
4491 | +{ | ||
4492 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4493 | + u32 syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, | ||
4494 | + ISPCCDC_SYN_MODE); | ||
4495 | + | ||
4496 | + syn_mode |= ISPCCDC_SYN_MODE_VDHDEN; | ||
4497 | + | ||
4498 | + if (syncif->fldstat) | ||
4499 | + syn_mode |= ISPCCDC_SYN_MODE_FLDSTAT; | ||
4500 | + else | ||
4501 | + syn_mode &= ~ISPCCDC_SYN_MODE_FLDSTAT; | ||
4502 | + | ||
4503 | + syn_mode &= ~ISPCCDC_SYN_MODE_DATSIZ_MASK; | ||
4504 | + switch (syncif->datsz) { | ||
4505 | + case 8: | ||
4506 | + syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8; | ||
4507 | + break; | ||
4508 | + case 10: | ||
4509 | + syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10; | ||
4510 | + break; | ||
4511 | + case 11: | ||
4512 | + syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11; | ||
4513 | + break; | ||
4514 | + case 12: | ||
4515 | + syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12; | ||
4516 | + break; | ||
4517 | + }; | ||
4518 | + | ||
4519 | + if (syncif->fldmode) | ||
4520 | + syn_mode |= ISPCCDC_SYN_MODE_FLDMODE; | ||
4521 | + else | ||
4522 | + syn_mode &= ~ISPCCDC_SYN_MODE_FLDMODE; | ||
4523 | + | ||
4524 | + if (syncif->datapol) | ||
4525 | + syn_mode |= ISPCCDC_SYN_MODE_DATAPOL; | ||
4526 | + else | ||
4527 | + syn_mode &= ~ISPCCDC_SYN_MODE_DATAPOL; | ||
4528 | + | ||
4529 | + if (syncif->fldpol) | ||
4530 | + syn_mode |= ISPCCDC_SYN_MODE_FLDPOL; | ||
4531 | + else | ||
4532 | + syn_mode &= ~ISPCCDC_SYN_MODE_FLDPOL; | ||
4533 | + | ||
4534 | + if (syncif->hdpol) | ||
4535 | + syn_mode |= ISPCCDC_SYN_MODE_HDPOL; | ||
4536 | + else | ||
4537 | + syn_mode &= ~ISPCCDC_SYN_MODE_HDPOL; | ||
4538 | + | ||
4539 | + if (syncif->vdpol) | ||
4540 | + syn_mode |= ISPCCDC_SYN_MODE_VDPOL; | ||
4541 | + else | ||
4542 | + syn_mode &= ~ISPCCDC_SYN_MODE_VDPOL; | ||
4543 | + | ||
4544 | + if (syncif->ccdc_mastermode) { | ||
4545 | + syn_mode |= ISPCCDC_SYN_MODE_FLDOUT | ISPCCDC_SYN_MODE_VDHDOUT; | ||
4546 | + isp_reg_writel(isp, | ||
4547 | + syncif->hs_width << ISPCCDC_HD_VD_WID_HDW_SHIFT | ||
4548 | + | syncif->vs_width << ISPCCDC_HD_VD_WID_VDW_SHIFT, | ||
4549 | + OMAP3_ISP_IOMEM_CCDC, | ||
4550 | + ISPCCDC_HD_VD_WID); | ||
4551 | + | ||
4552 | + isp_reg_writel(isp, | ||
4553 | + syncif->ppln << ISPCCDC_PIX_LINES_PPLN_SHIFT | ||
4554 | + | syncif->hlprf << ISPCCDC_PIX_LINES_HLPRF_SHIFT, | ||
4555 | + OMAP3_ISP_IOMEM_CCDC, | ||
4556 | + ISPCCDC_PIX_LINES); | ||
4557 | + } else | ||
4558 | + syn_mode &= ~(ISPCCDC_SYN_MODE_FLDOUT | | ||
4559 | + ISPCCDC_SYN_MODE_VDHDOUT); | ||
4560 | + | ||
4561 | + isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); | ||
4562 | + | ||
4563 | + if (!syncif->bt_r656_en) | ||
4564 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, | ||
4565 | + ISPCCDC_REC656IF_R656ON); | ||
4566 | +} | ||
4567 | + | ||
4568 | +/* CCDC formats descriptions */ | ||
4569 | +static const u32 ccdc_sgrbg_pattern = | ||
4570 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC0_SHIFT | | ||
4571 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC1_SHIFT | | ||
4572 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC2_SHIFT | | ||
4573 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC3_SHIFT | | ||
4574 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC0_SHIFT | | ||
4575 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC1_SHIFT | | ||
4576 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC2_SHIFT | | ||
4577 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC3_SHIFT | | ||
4578 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC0_SHIFT | | ||
4579 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC1_SHIFT | | ||
4580 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC2_SHIFT | | ||
4581 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC3_SHIFT | | ||
4582 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC0_SHIFT | | ||
4583 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC1_SHIFT | | ||
4584 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC2_SHIFT | | ||
4585 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC3_SHIFT; | ||
4586 | + | ||
4587 | +static const u32 ccdc_srggb_pattern = | ||
4588 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC0_SHIFT | | ||
4589 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC1_SHIFT | | ||
4590 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC2_SHIFT | | ||
4591 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC3_SHIFT | | ||
4592 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC0_SHIFT | | ||
4593 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC1_SHIFT | | ||
4594 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC2_SHIFT | | ||
4595 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC3_SHIFT | | ||
4596 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC0_SHIFT | | ||
4597 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC1_SHIFT | | ||
4598 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC2_SHIFT | | ||
4599 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC3_SHIFT | | ||
4600 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC0_SHIFT | | ||
4601 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC1_SHIFT | | ||
4602 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC2_SHIFT | | ||
4603 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC3_SHIFT; | ||
4604 | + | ||
4605 | +static const u32 ccdc_sbggr_pattern = | ||
4606 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC0_SHIFT | | ||
4607 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC1_SHIFT | | ||
4608 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC2_SHIFT | | ||
4609 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC3_SHIFT | | ||
4610 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC0_SHIFT | | ||
4611 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC1_SHIFT | | ||
4612 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC2_SHIFT | | ||
4613 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC3_SHIFT | | ||
4614 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC0_SHIFT | | ||
4615 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC1_SHIFT | | ||
4616 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC2_SHIFT | | ||
4617 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC3_SHIFT | | ||
4618 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC0_SHIFT | | ||
4619 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC1_SHIFT | | ||
4620 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC2_SHIFT | | ||
4621 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC3_SHIFT; | ||
4622 | + | ||
4623 | +static const u32 ccdc_sgbrg_pattern = | ||
4624 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC0_SHIFT | | ||
4625 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC1_SHIFT | | ||
4626 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC2_SHIFT | | ||
4627 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC3_SHIFT | | ||
4628 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC0_SHIFT | | ||
4629 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC1_SHIFT | | ||
4630 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC2_SHIFT | | ||
4631 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC3_SHIFT | | ||
4632 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC0_SHIFT | | ||
4633 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC1_SHIFT | | ||
4634 | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC2_SHIFT | | ||
4635 | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC3_SHIFT | | ||
4636 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC0_SHIFT | | ||
4637 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC1_SHIFT | | ||
4638 | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC2_SHIFT | | ||
4639 | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC3_SHIFT; | ||
4640 | + | ||
4641 | +static void ccdc_configure(struct isp_ccdc_device *ccdc) | ||
4642 | +{ | ||
4643 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4644 | + struct isp_parallel_platform_data *pdata = NULL; | ||
4645 | + struct v4l2_subdev *sensor; | ||
4646 | + struct v4l2_mbus_framefmt *format; | ||
4647 | + struct media_pad *pad; | ||
4648 | + unsigned long flags; | ||
4649 | + u32 syn_mode; | ||
4650 | + u32 ccdc_pattern; | ||
4651 | + | ||
4652 | + if (ccdc->input == CCDC_INPUT_PARALLEL) { | ||
4653 | + pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]); | ||
4654 | + sensor = media_entity_to_v4l2_subdev(pad->entity); | ||
4655 | + pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv) | ||
4656 | + ->bus.parallel; | ||
4657 | + } | ||
4658 | + | ||
4659 | + omap3isp_configure_bridge(isp, ccdc->input, pdata); | ||
4660 | + | ||
4661 | + ccdc->syncif.datsz = pdata ? pdata->width : 10; | ||
4662 | + ccdc_config_sync_if(ccdc, &ccdc->syncif); | ||
4663 | + | ||
4664 | + /* CCDC_PAD_SINK */ | ||
4665 | + format = &ccdc->formats[CCDC_PAD_SINK]; | ||
4666 | + | ||
4667 | + syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); | ||
4668 | + | ||
4669 | + /* Use the raw, unprocessed data when writing to memory. The H3A and | ||
4670 | + * histogram modules are still fed with lens shading corrected data. | ||
4671 | + */ | ||
4672 | + syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; | ||
4673 | + | ||
4674 | + if (ccdc->output & CCDC_OUTPUT_MEMORY) | ||
4675 | + syn_mode |= ISPCCDC_SYN_MODE_WEN; | ||
4676 | + else | ||
4677 | + syn_mode &= ~ISPCCDC_SYN_MODE_WEN; | ||
4678 | + | ||
4679 | + if (ccdc->output & CCDC_OUTPUT_RESIZER) | ||
4680 | + syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ; | ||
4681 | + else | ||
4682 | + syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; | ||
4683 | + | ||
4684 | + /* Use PACK8 mode for 1byte per pixel formats. */ | ||
4685 | + if (omap3isp_video_format_info(format->code)->bpp <= 8) | ||
4686 | + syn_mode |= ISPCCDC_SYN_MODE_PACK8; | ||
4687 | + else | ||
4688 | + syn_mode &= ~ISPCCDC_SYN_MODE_PACK8; | ||
4689 | + | ||
4690 | + isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); | ||
4691 | + | ||
4692 | + /* Mosaic filter */ | ||
4693 | + switch (format->code) { | ||
4694 | + case V4L2_MBUS_FMT_SRGGB10_1X10: | ||
4695 | + case V4L2_MBUS_FMT_SRGGB12_1X12: | ||
4696 | + ccdc_pattern = ccdc_srggb_pattern; | ||
4697 | + break; | ||
4698 | + case V4L2_MBUS_FMT_SBGGR10_1X10: | ||
4699 | + case V4L2_MBUS_FMT_SBGGR12_1X12: | ||
4700 | + ccdc_pattern = ccdc_sbggr_pattern; | ||
4701 | + break; | ||
4702 | + case V4L2_MBUS_FMT_SGBRG10_1X10: | ||
4703 | + case V4L2_MBUS_FMT_SGBRG12_1X12: | ||
4704 | + ccdc_pattern = ccdc_sgbrg_pattern; | ||
4705 | + break; | ||
4706 | + default: | ||
4707 | + /* Use GRBG */ | ||
4708 | + ccdc_pattern = ccdc_sgrbg_pattern; | ||
4709 | + break; | ||
4710 | + } | ||
4711 | + ccdc_config_imgattr(ccdc, ccdc_pattern); | ||
4712 | + | ||
4713 | + /* Generate VD0 on the last line of the image and VD1 on the | ||
4714 | + * 2/3 height line. | ||
4715 | + */ | ||
4716 | + isp_reg_writel(isp, ((format->height - 2) << ISPCCDC_VDINT_0_SHIFT) | | ||
4717 | + ((format->height * 2 / 3) << ISPCCDC_VDINT_1_SHIFT), | ||
4718 | + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT); | ||
4719 | + | ||
4720 | + /* CCDC_PAD_SOURCE_OF */ | ||
4721 | + format = &ccdc->formats[CCDC_PAD_SOURCE_OF]; | ||
4722 | + | ||
4723 | + isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) | | ||
4724 | + ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), | ||
4725 | + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); | ||
4726 | + isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT, | ||
4727 | + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); | ||
4728 | + isp_reg_writel(isp, (format->height - 1) | ||
4729 | + << ISPCCDC_VERT_LINES_NLV_SHIFT, | ||
4730 | + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); | ||
4731 | + | ||
4732 | + ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0); | ||
4733 | + | ||
4734 | + /* CCDC_PAD_SOURCE_VP */ | ||
4735 | + format = &ccdc->formats[CCDC_PAD_SOURCE_VP]; | ||
4736 | + | ||
4737 | + isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | | ||
4738 | + (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), | ||
4739 | + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ); | ||
4740 | + isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | | ||
4741 | + ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT), | ||
4742 | + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT); | ||
4743 | + | ||
4744 | + isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | | ||
4745 | + (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT), | ||
4746 | + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT); | ||
4747 | + | ||
4748 | + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
4749 | + if (ccdc->lsc.request == NULL) | ||
4750 | + goto unlock; | ||
4751 | + | ||
4752 | + WARN_ON(ccdc->lsc.active); | ||
4753 | + | ||
4754 | + /* Get last good LSC configuration. If it is not supported for | ||
4755 | + * the current active resolution discard it. | ||
4756 | + */ | ||
4757 | + if (ccdc->lsc.active == NULL && | ||
4758 | + __ccdc_lsc_configure(ccdc, ccdc->lsc.request) == 0) { | ||
4759 | + ccdc->lsc.active = ccdc->lsc.request; | ||
4760 | + } else { | ||
4761 | + list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue); | ||
4762 | + schedule_work(&ccdc->lsc.table_work); | ||
4763 | + } | ||
4764 | + | ||
4765 | + ccdc->lsc.request = NULL; | ||
4766 | + | ||
4767 | +unlock: | ||
4768 | + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
4769 | + | ||
4770 | + ccdc_apply_controls(ccdc); | ||
4771 | +} | ||
4772 | + | ||
4773 | +static void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable) | ||
4774 | +{ | ||
4775 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4776 | + | ||
4777 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR, | ||
4778 | + ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0); | ||
4779 | +} | ||
4780 | + | ||
4781 | +static int ccdc_disable(struct isp_ccdc_device *ccdc) | ||
4782 | +{ | ||
4783 | + unsigned long flags; | ||
4784 | + int ret = 0; | ||
4785 | + | ||
4786 | + spin_lock_irqsave(&ccdc->lock, flags); | ||
4787 | + if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS) | ||
4788 | + ccdc->stopping = CCDC_STOP_REQUEST; | ||
4789 | + spin_unlock_irqrestore(&ccdc->lock, flags); | ||
4790 | + | ||
4791 | + ret = wait_event_timeout(ccdc->wait, | ||
4792 | + ccdc->stopping == CCDC_STOP_FINISHED, | ||
4793 | + msecs_to_jiffies(2000)); | ||
4794 | + if (ret == 0) { | ||
4795 | + ret = -ETIMEDOUT; | ||
4796 | + dev_warn(to_device(ccdc), "CCDC stop timeout!\n"); | ||
4797 | + } | ||
4798 | + | ||
4799 | + omap3isp_sbl_disable(to_isp_device(ccdc), OMAP3_ISP_SBL_CCDC_LSC_READ); | ||
4800 | + | ||
4801 | + mutex_lock(&ccdc->ioctl_lock); | ||
4802 | + ccdc_lsc_free_request(ccdc, ccdc->lsc.request); | ||
4803 | + ccdc->lsc.request = ccdc->lsc.active; | ||
4804 | + ccdc->lsc.active = NULL; | ||
4805 | + cancel_work_sync(&ccdc->lsc.table_work); | ||
4806 | + ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue); | ||
4807 | + mutex_unlock(&ccdc->ioctl_lock); | ||
4808 | + | ||
4809 | + ccdc->stopping = CCDC_STOP_NOT_REQUESTED; | ||
4810 | + | ||
4811 | + return ret > 0 ? 0 : ret; | ||
4812 | +} | ||
4813 | + | ||
4814 | +static void ccdc_enable(struct isp_ccdc_device *ccdc) | ||
4815 | +{ | ||
4816 | + if (ccdc_lsc_is_configured(ccdc)) | ||
4817 | + __ccdc_lsc_enable(ccdc, 1); | ||
4818 | + __ccdc_enable(ccdc, 1); | ||
4819 | +} | ||
4820 | + | ||
4821 | +/* ----------------------------------------------------------------------------- | ||
4822 | + * Interrupt handling | ||
4823 | + */ | ||
4824 | + | ||
4825 | +/* | ||
4826 | + * ccdc_sbl_busy - Poll idle state of CCDC and related SBL memory write bits | ||
4827 | + * @ccdc: Pointer to ISP CCDC device. | ||
4828 | + * | ||
4829 | + * Returns zero if the CCDC is idle and the image has been written to | ||
4830 | + * memory, too. | ||
4831 | + */ | ||
4832 | +static int ccdc_sbl_busy(struct isp_ccdc_device *ccdc) | ||
4833 | +{ | ||
4834 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4835 | + | ||
4836 | + return omap3isp_ccdc_busy(ccdc) | ||
4837 | + | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_0) & | ||
4838 | + ISPSBL_CCDC_WR_0_DATA_READY) | ||
4839 | + | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_1) & | ||
4840 | + ISPSBL_CCDC_WR_0_DATA_READY) | ||
4841 | + | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_2) & | ||
4842 | + ISPSBL_CCDC_WR_0_DATA_READY) | ||
4843 | + | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_3) & | ||
4844 | + ISPSBL_CCDC_WR_0_DATA_READY); | ||
4845 | +} | ||
4846 | + | ||
4847 | +/* | ||
4848 | + * ccdc_sbl_wait_idle - Wait until the CCDC and related SBL are idle | ||
4849 | + * @ccdc: Pointer to ISP CCDC device. | ||
4850 | + * @max_wait: Max retry count in us for wait for idle/busy transition. | ||
4851 | + */ | ||
4852 | +static int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc, | ||
4853 | + unsigned int max_wait) | ||
4854 | +{ | ||
4855 | + unsigned int wait = 0; | ||
4856 | + | ||
4857 | + if (max_wait == 0) | ||
4858 | + max_wait = 10000; /* 10 ms */ | ||
4859 | + | ||
4860 | + for (wait = 0; wait <= max_wait; wait++) { | ||
4861 | + if (!ccdc_sbl_busy(ccdc)) | ||
4862 | + return 0; | ||
4863 | + | ||
4864 | + rmb(); | ||
4865 | + udelay(1); | ||
4866 | + } | ||
4867 | + | ||
4868 | + return -EBUSY; | ||
4869 | +} | ||
4870 | + | ||
4871 | +/* __ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence | ||
4872 | + * @ccdc: Pointer to ISP CCDC device. | ||
4873 | + * @event: Pointing which event trigger handler | ||
4874 | + * | ||
4875 | + * Return 1 when the event and stopping request combination is satisfyied, | ||
4876 | + * zero otherwise. | ||
4877 | + */ | ||
4878 | +static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event) | ||
4879 | +{ | ||
4880 | + int rval = 0; | ||
4881 | + | ||
4882 | + switch ((ccdc->stopping & 3) | event) { | ||
4883 | + case CCDC_STOP_REQUEST | CCDC_EVENT_VD1: | ||
4884 | + if (ccdc->lsc.state != LSC_STATE_STOPPED) | ||
4885 | + __ccdc_lsc_enable(ccdc, 0); | ||
4886 | + __ccdc_enable(ccdc, 0); | ||
4887 | + ccdc->stopping = CCDC_STOP_EXECUTED; | ||
4888 | + return 1; | ||
4889 | + | ||
4890 | + case CCDC_STOP_EXECUTED | CCDC_EVENT_VD0: | ||
4891 | + ccdc->stopping |= CCDC_STOP_CCDC_FINISHED; | ||
4892 | + if (ccdc->lsc.state == LSC_STATE_STOPPED) | ||
4893 | + ccdc->stopping |= CCDC_STOP_LSC_FINISHED; | ||
4894 | + rval = 1; | ||
4895 | + break; | ||
4896 | + | ||
4897 | + case CCDC_STOP_EXECUTED | CCDC_EVENT_LSC_DONE: | ||
4898 | + ccdc->stopping |= CCDC_STOP_LSC_FINISHED; | ||
4899 | + rval = 1; | ||
4900 | + break; | ||
4901 | + | ||
4902 | + case CCDC_STOP_EXECUTED | CCDC_EVENT_VD1: | ||
4903 | + return 1; | ||
4904 | + } | ||
4905 | + | ||
4906 | + if (ccdc->stopping == CCDC_STOP_FINISHED) { | ||
4907 | + wake_up(&ccdc->wait); | ||
4908 | + rval = 1; | ||
4909 | + } | ||
4910 | + | ||
4911 | + return rval; | ||
4912 | +} | ||
4913 | + | ||
4914 | +static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc) | ||
4915 | +{ | ||
4916 | + struct video_device *vdev = &ccdc->subdev.devnode; | ||
4917 | + struct v4l2_event event; | ||
4918 | + | ||
4919 | + memset(&event, 0, sizeof(event)); | ||
4920 | + event.type = V4L2_EVENT_OMAP3ISP_HS_VS; | ||
4921 | + | ||
4922 | + v4l2_event_queue(vdev, &event); | ||
4923 | +} | ||
4924 | + | ||
4925 | +/* | ||
4926 | + * ccdc_lsc_isr - Handle LSC events | ||
4927 | + * @ccdc: Pointer to ISP CCDC device. | ||
4928 | + * @events: LSC events | ||
4929 | + */ | ||
4930 | +static void ccdc_lsc_isr(struct isp_ccdc_device *ccdc, u32 events) | ||
4931 | +{ | ||
4932 | + unsigned long flags; | ||
4933 | + | ||
4934 | + if (events & IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ) { | ||
4935 | + ccdc_lsc_error_handler(ccdc); | ||
4936 | + ccdc->error = 1; | ||
4937 | + dev_dbg(to_device(ccdc), "lsc prefetch error\n"); | ||
4938 | + } | ||
4939 | + | ||
4940 | + if (!(events & IRQ0STATUS_CCDC_LSC_DONE_IRQ)) | ||
4941 | + return; | ||
4942 | + | ||
4943 | + /* LSC_DONE interrupt occur, there are two cases | ||
4944 | + * 1. stopping for reconfiguration | ||
4945 | + * 2. stopping because of STREAM OFF command | ||
4946 | + */ | ||
4947 | + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
4948 | + | ||
4949 | + if (ccdc->lsc.state == LSC_STATE_STOPPING) | ||
4950 | + ccdc->lsc.state = LSC_STATE_STOPPED; | ||
4951 | + | ||
4952 | + if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE)) | ||
4953 | + goto done; | ||
4954 | + | ||
4955 | + if (ccdc->lsc.state != LSC_STATE_RECONFIG) | ||
4956 | + goto done; | ||
4957 | + | ||
4958 | + /* LSC is in STOPPING state, change to the new state */ | ||
4959 | + ccdc->lsc.state = LSC_STATE_STOPPED; | ||
4960 | + | ||
4961 | + /* This is an exception. Start of frame and LSC_DONE interrupt | ||
4962 | + * have been received on the same time. Skip this event and wait | ||
4963 | + * for better times. | ||
4964 | + */ | ||
4965 | + if (events & IRQ0STATUS_HS_VS_IRQ) | ||
4966 | + goto done; | ||
4967 | + | ||
4968 | + /* The LSC engine is stopped at this point. Enable it if there's a | ||
4969 | + * pending request. | ||
4970 | + */ | ||
4971 | + if (ccdc->lsc.request == NULL) | ||
4972 | + goto done; | ||
4973 | + | ||
4974 | + ccdc_lsc_enable(ccdc); | ||
4975 | + | ||
4976 | +done: | ||
4977 | + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
4978 | +} | ||
4979 | + | ||
4980 | +static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) | ||
4981 | +{ | ||
4982 | + struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); | ||
4983 | + struct isp_device *isp = to_isp_device(ccdc); | ||
4984 | + struct isp_buffer *buffer; | ||
4985 | + int restart = 0; | ||
4986 | + | ||
4987 | + /* The CCDC generates VD0 interrupts even when disabled (the datasheet | ||
4988 | + * doesn't explicitly state if that's supposed to happen or not, so it | ||
4989 | + * can be considered as a hardware bug or as a feature, but we have to | ||
4990 | + * deal with it anyway). Disabling the CCDC when no buffer is available | ||
4991 | + * would thus not be enough, we need to handle the situation explicitly. | ||
4992 | + */ | ||
4993 | + if (list_empty(&ccdc->video_out.dmaqueue)) | ||
4994 | + goto done; | ||
4995 | + | ||
4996 | + /* We're in continuous mode, and memory writes were disabled due to a | ||
4997 | + * buffer underrun. Reenable them now that we have a buffer. The buffer | ||
4998 | + * address has been set in ccdc_video_queue. | ||
4999 | + */ | ||
5000 | + if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) { | ||
5001 | + restart = 1; | ||
5002 | + ccdc->underrun = 0; | ||
5003 | + goto done; | ||
5004 | + } | ||
5005 | + | ||
5006 | + if (ccdc_sbl_wait_idle(ccdc, 1000)) { | ||
5007 | + dev_info(isp->dev, "CCDC won't become idle!\n"); | ||
5008 | + goto done; | ||
5009 | + } | ||
5010 | + | ||
5011 | + buffer = omap3isp_video_buffer_next(&ccdc->video_out, ccdc->error); | ||
5012 | + if (buffer != NULL) { | ||
5013 | + ccdc_set_outaddr(ccdc, buffer->isp_addr); | ||
5014 | + restart = 1; | ||
5015 | + } | ||
5016 | + | ||
5017 | + pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; | ||
5018 | + | ||
5019 | + if (ccdc->state == ISP_PIPELINE_STREAM_SINGLESHOT && | ||
5020 | + isp_pipeline_ready(pipe)) | ||
5021 | + omap3isp_pipeline_set_stream(pipe, | ||
5022 | + ISP_PIPELINE_STREAM_SINGLESHOT); | ||
5023 | + | ||
5024 | +done: | ||
5025 | + ccdc->error = 0; | ||
5026 | + return restart; | ||
5027 | +} | ||
5028 | + | ||
5029 | +/* | ||
5030 | + * ccdc_vd0_isr - Handle VD0 event | ||
5031 | + * @ccdc: Pointer to ISP CCDC device. | ||
5032 | + * | ||
5033 | + * Executes LSC deferred enablement before next frame starts. | ||
5034 | + */ | ||
5035 | +static void ccdc_vd0_isr(struct isp_ccdc_device *ccdc) | ||
5036 | +{ | ||
5037 | + unsigned long flags; | ||
5038 | + int restart = 0; | ||
5039 | + | ||
5040 | + if (ccdc->output & CCDC_OUTPUT_MEMORY) | ||
5041 | + restart = ccdc_isr_buffer(ccdc); | ||
5042 | + | ||
5043 | + spin_lock_irqsave(&ccdc->lock, flags); | ||
5044 | + if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) { | ||
5045 | + spin_unlock_irqrestore(&ccdc->lock, flags); | ||
5046 | + return; | ||
5047 | + } | ||
5048 | + | ||
5049 | + if (!ccdc->shadow_update) | ||
5050 | + ccdc_apply_controls(ccdc); | ||
5051 | + spin_unlock_irqrestore(&ccdc->lock, flags); | ||
5052 | + | ||
5053 | + if (restart) | ||
5054 | + ccdc_enable(ccdc); | ||
5055 | +} | ||
5056 | + | ||
5057 | +/* | ||
5058 | + * ccdc_vd1_isr - Handle VD1 event | ||
5059 | + * @ccdc: Pointer to ISP CCDC device. | ||
5060 | + */ | ||
5061 | +static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc) | ||
5062 | +{ | ||
5063 | + unsigned long flags; | ||
5064 | + | ||
5065 | + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
5066 | + | ||
5067 | + /* | ||
5068 | + * Depending on the CCDC pipeline state, CCDC stopping should be | ||
5069 | + * handled differently. In SINGLESHOT we emulate an internal CCDC | ||
5070 | + * stopping because the CCDC hw works only in continuous mode. | ||
5071 | + * When CONTINUOUS pipeline state is used and the CCDC writes it's | ||
5072 | + * data to memory the CCDC and LSC are stopped immediately but | ||
5073 | + * without change the CCDC stopping state machine. The CCDC | ||
5074 | + * stopping state machine should be used only when user request | ||
5075 | + * for stopping is received (SINGLESHOT is an exeption). | ||
5076 | + */ | ||
5077 | + switch (ccdc->state) { | ||
5078 | + case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
5079 | + ccdc->stopping = CCDC_STOP_REQUEST; | ||
5080 | + break; | ||
5081 | + | ||
5082 | + case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
5083 | + if (ccdc->output & CCDC_OUTPUT_MEMORY) { | ||
5084 | + if (ccdc->lsc.state != LSC_STATE_STOPPED) | ||
5085 | + __ccdc_lsc_enable(ccdc, 0); | ||
5086 | + __ccdc_enable(ccdc, 0); | ||
5087 | + } | ||
5088 | + break; | ||
5089 | + | ||
5090 | + case ISP_PIPELINE_STREAM_STOPPED: | ||
5091 | + break; | ||
5092 | + } | ||
5093 | + | ||
5094 | + if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1)) | ||
5095 | + goto done; | ||
5096 | + | ||
5097 | + if (ccdc->lsc.request == NULL) | ||
5098 | + goto done; | ||
5099 | + | ||
5100 | + /* | ||
5101 | + * LSC need to be reconfigured. Stop it here and on next LSC_DONE IRQ | ||
5102 | + * do the appropriate changes in registers | ||
5103 | + */ | ||
5104 | + if (ccdc->lsc.state == LSC_STATE_RUNNING) { | ||
5105 | + __ccdc_lsc_enable(ccdc, 0); | ||
5106 | + ccdc->lsc.state = LSC_STATE_RECONFIG; | ||
5107 | + goto done; | ||
5108 | + } | ||
5109 | + | ||
5110 | + /* LSC has been in STOPPED state, enable it */ | ||
5111 | + if (ccdc->lsc.state == LSC_STATE_STOPPED) | ||
5112 | + ccdc_lsc_enable(ccdc); | ||
5113 | + | ||
5114 | +done: | ||
5115 | + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
5116 | +} | ||
5117 | + | ||
5118 | +/* | ||
5119 | + * omap3isp_ccdc_isr - Configure CCDC during interframe time. | ||
5120 | + * @ccdc: Pointer to ISP CCDC device. | ||
5121 | + * @events: CCDC events | ||
5122 | + */ | ||
5123 | +int omap3isp_ccdc_isr(struct isp_ccdc_device *ccdc, u32 events) | ||
5124 | +{ | ||
5125 | + if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) | ||
5126 | + return 0; | ||
5127 | + | ||
5128 | + if (events & IRQ0STATUS_CCDC_VD1_IRQ) | ||
5129 | + ccdc_vd1_isr(ccdc); | ||
5130 | + | ||
5131 | + ccdc_lsc_isr(ccdc, events); | ||
5132 | + | ||
5133 | + if (events & IRQ0STATUS_CCDC_VD0_IRQ) | ||
5134 | + ccdc_vd0_isr(ccdc); | ||
5135 | + | ||
5136 | + if (events & IRQ0STATUS_HS_VS_IRQ) | ||
5137 | + ccdc_hs_vs_isr(ccdc); | ||
5138 | + | ||
5139 | + return 0; | ||
5140 | +} | ||
5141 | + | ||
5142 | +/* ----------------------------------------------------------------------------- | ||
5143 | + * ISP video operations | ||
5144 | + */ | ||
5145 | + | ||
5146 | +static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer) | ||
5147 | +{ | ||
5148 | + struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc; | ||
5149 | + | ||
5150 | + if (!(ccdc->output & CCDC_OUTPUT_MEMORY)) | ||
5151 | + return -ENODEV; | ||
5152 | + | ||
5153 | + ccdc_set_outaddr(ccdc, buffer->isp_addr); | ||
5154 | + | ||
5155 | + /* We now have a buffer queued on the output, restart the pipeline in | ||
5156 | + * on the next CCDC interrupt if running in continuous mode (or when | ||
5157 | + * starting the stream). | ||
5158 | + */ | ||
5159 | + ccdc->underrun = 1; | ||
5160 | + | ||
5161 | + return 0; | ||
5162 | +} | ||
5163 | + | ||
5164 | +static const struct isp_video_operations ccdc_video_ops = { | ||
5165 | + .queue = ccdc_video_queue, | ||
5166 | +}; | ||
5167 | + | ||
5168 | +/* ----------------------------------------------------------------------------- | ||
5169 | + * V4L2 subdev operations | ||
5170 | + */ | ||
5171 | + | ||
5172 | +/* | ||
5173 | + * ccdc_ioctl - CCDC module private ioctl's | ||
5174 | + * @sd: ISP CCDC V4L2 subdevice | ||
5175 | + * @cmd: ioctl command | ||
5176 | + * @arg: ioctl argument | ||
5177 | + * | ||
5178 | + * Return 0 on success or a negative error code otherwise. | ||
5179 | + */ | ||
5180 | +static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
5181 | +{ | ||
5182 | + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
5183 | + int ret; | ||
5184 | + | ||
5185 | + switch (cmd) { | ||
5186 | + case VIDIOC_OMAP3ISP_CCDC_CFG: | ||
5187 | + mutex_lock(&ccdc->ioctl_lock); | ||
5188 | + ret = ccdc_config(ccdc, arg); | ||
5189 | + mutex_unlock(&ccdc->ioctl_lock); | ||
5190 | + break; | ||
5191 | + | ||
5192 | + default: | ||
5193 | + return -ENOIOCTLCMD; | ||
5194 | + } | ||
5195 | + | ||
5196 | + return ret; | ||
5197 | +} | ||
5198 | + | ||
5199 | +static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, | ||
5200 | + struct v4l2_event_subscription *sub) | ||
5201 | +{ | ||
5202 | + if (sub->type != V4L2_EVENT_OMAP3ISP_HS_VS) | ||
5203 | + return -EINVAL; | ||
5204 | + | ||
5205 | + return v4l2_event_subscribe(fh, sub); | ||
5206 | +} | ||
5207 | + | ||
5208 | +static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, | ||
5209 | + struct v4l2_event_subscription *sub) | ||
5210 | +{ | ||
5211 | + return v4l2_event_unsubscribe(fh, sub); | ||
5212 | +} | ||
5213 | + | ||
5214 | +/* | ||
5215 | + * ccdc_set_stream - Enable/Disable streaming on the CCDC module | ||
5216 | + * @sd: ISP CCDC V4L2 subdevice | ||
5217 | + * @enable: Enable/disable stream | ||
5218 | + * | ||
5219 | + * When writing to memory, the CCDC hardware can't be enabled without a memory | ||
5220 | + * buffer to write to. As the s_stream operation is called in response to a | ||
5221 | + * STREAMON call without any buffer queued yet, just update the enabled field | ||
5222 | + * and return immediately. The CCDC will be enabled in ccdc_isr_buffer(). | ||
5223 | + * | ||
5224 | + * When not writing to memory enable the CCDC immediately. | ||
5225 | + */ | ||
5226 | +static int ccdc_set_stream(struct v4l2_subdev *sd, int enable) | ||
5227 | +{ | ||
5228 | + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
5229 | + struct isp_device *isp = to_isp_device(ccdc); | ||
5230 | + int ret = 0; | ||
5231 | + | ||
5232 | + if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) { | ||
5233 | + if (enable == ISP_PIPELINE_STREAM_STOPPED) | ||
5234 | + return 0; | ||
5235 | + | ||
5236 | + omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_CCDC); | ||
5237 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, | ||
5238 | + ISPCCDC_CFG_VDLC); | ||
5239 | + | ||
5240 | + ccdc_configure(ccdc); | ||
5241 | + | ||
5242 | + /* TODO: Don't configure the video port if all of its output | ||
5243 | + * links are inactive. | ||
5244 | + */ | ||
5245 | + ccdc_config_vp(ccdc); | ||
5246 | + ccdc_enable_vp(ccdc, 1); | ||
5247 | + ccdc->error = 0; | ||
5248 | + ccdc_print_status(ccdc); | ||
5249 | + } | ||
5250 | + | ||
5251 | + switch (enable) { | ||
5252 | + case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
5253 | + if (ccdc->output & CCDC_OUTPUT_MEMORY) | ||
5254 | + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE); | ||
5255 | + | ||
5256 | + if (ccdc->underrun || !(ccdc->output & CCDC_OUTPUT_MEMORY)) | ||
5257 | + ccdc_enable(ccdc); | ||
5258 | + | ||
5259 | + ccdc->underrun = 0; | ||
5260 | + break; | ||
5261 | + | ||
5262 | + case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
5263 | + if (ccdc->output & CCDC_OUTPUT_MEMORY && | ||
5264 | + ccdc->state != ISP_PIPELINE_STREAM_SINGLESHOT) | ||
5265 | + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE); | ||
5266 | + | ||
5267 | + ccdc_enable(ccdc); | ||
5268 | + break; | ||
5269 | + | ||
5270 | + case ISP_PIPELINE_STREAM_STOPPED: | ||
5271 | + ret = ccdc_disable(ccdc); | ||
5272 | + if (ccdc->output & CCDC_OUTPUT_MEMORY) | ||
5273 | + omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CCDC_WRITE); | ||
5274 | + omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_CCDC); | ||
5275 | + ccdc->underrun = 0; | ||
5276 | + break; | ||
5277 | + } | ||
5278 | + | ||
5279 | + ccdc->state = enable; | ||
5280 | + return ret; | ||
5281 | +} | ||
5282 | + | ||
5283 | +static struct v4l2_mbus_framefmt * | ||
5284 | +__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | ||
5285 | + unsigned int pad, enum v4l2_subdev_format_whence which) | ||
5286 | +{ | ||
5287 | + if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
5288 | + return v4l2_subdev_get_try_format(fh, pad); | ||
5289 | + else | ||
5290 | + return &ccdc->formats[pad]; | ||
5291 | +} | ||
5292 | + | ||
5293 | +/* | ||
5294 | + * ccdc_try_format - Try video format on a pad | ||
5295 | + * @ccdc: ISP CCDC device | ||
5296 | + * @fh : V4L2 subdev file handle | ||
5297 | + * @pad: Pad number | ||
5298 | + * @fmt: Format | ||
5299 | + */ | ||
5300 | +static void | ||
5301 | +ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | ||
5302 | + unsigned int pad, struct v4l2_mbus_framefmt *fmt, | ||
5303 | + enum v4l2_subdev_format_whence which) | ||
5304 | +{ | ||
5305 | + struct v4l2_mbus_framefmt *format; | ||
5306 | + const struct isp_format_info *info; | ||
5307 | + unsigned int width = fmt->width; | ||
5308 | + unsigned int height = fmt->height; | ||
5309 | + unsigned int i; | ||
5310 | + | ||
5311 | + switch (pad) { | ||
5312 | + case CCDC_PAD_SINK: | ||
5313 | + /* TODO: If the CCDC output formatter pad is connected directly | ||
5314 | + * to the resizer, only YUV formats can be used. | ||
5315 | + */ | ||
5316 | + for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) { | ||
5317 | + if (fmt->code == ccdc_fmts[i]) | ||
5318 | + break; | ||
5319 | + } | ||
5320 | + | ||
5321 | + /* If not found, use SGRBG10 as default */ | ||
5322 | + if (i >= ARRAY_SIZE(ccdc_fmts)) | ||
5323 | + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
5324 | + | ||
5325 | + /* Clamp the input size. */ | ||
5326 | + fmt->width = clamp_t(u32, width, 32, 4096); | ||
5327 | + fmt->height = clamp_t(u32, height, 32, 4096); | ||
5328 | + break; | ||
5329 | + | ||
5330 | + case CCDC_PAD_SOURCE_OF: | ||
5331 | + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); | ||
5332 | + memcpy(fmt, format, sizeof(*fmt)); | ||
5333 | + | ||
5334 | + /* The data formatter truncates the number of horizontal output | ||
5335 | + * pixels to a multiple of 16. To avoid clipping data, allow | ||
5336 | + * callers to request an output size bigger than the input size | ||
5337 | + * up to the nearest multiple of 16. | ||
5338 | + */ | ||
5339 | + fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15); | ||
5340 | + fmt->width &= ~15; | ||
5341 | + fmt->height = clamp_t(u32, height, 32, fmt->height); | ||
5342 | + break; | ||
5343 | + | ||
5344 | + case CCDC_PAD_SOURCE_VP: | ||
5345 | + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); | ||
5346 | + memcpy(fmt, format, sizeof(*fmt)); | ||
5347 | + | ||
5348 | + /* The video port interface truncates the data to 10 bits. */ | ||
5349 | + info = omap3isp_video_format_info(fmt->code); | ||
5350 | + fmt->code = info->truncated; | ||
5351 | + | ||
5352 | + /* The number of lines that can be clocked out from the video | ||
5353 | + * port output must be at least one line less than the number | ||
5354 | + * of input lines. | ||
5355 | + */ | ||
5356 | + fmt->width = clamp_t(u32, width, 32, fmt->width); | ||
5357 | + fmt->height = clamp_t(u32, height, 32, fmt->height - 1); | ||
5358 | + break; | ||
5359 | + } | ||
5360 | + | ||
5361 | + /* Data is written to memory unpacked, each 10-bit or 12-bit pixel is | ||
5362 | + * stored on 2 bytes. | ||
5363 | + */ | ||
5364 | + fmt->colorspace = V4L2_COLORSPACE_SRGB; | ||
5365 | + fmt->field = V4L2_FIELD_NONE; | ||
5366 | +} | ||
5367 | + | ||
5368 | +/* | ||
5369 | + * ccdc_enum_mbus_code - Handle pixel format enumeration | ||
5370 | + * @sd : pointer to v4l2 subdev structure | ||
5371 | + * @fh : V4L2 subdev file handle | ||
5372 | + * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
5373 | + * return -EINVAL or zero on success | ||
5374 | + */ | ||
5375 | +static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, | ||
5376 | + struct v4l2_subdev_fh *fh, | ||
5377 | + struct v4l2_subdev_mbus_code_enum *code) | ||
5378 | +{ | ||
5379 | + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
5380 | + struct v4l2_mbus_framefmt *format; | ||
5381 | + | ||
5382 | + switch (code->pad) { | ||
5383 | + case CCDC_PAD_SINK: | ||
5384 | + if (code->index >= ARRAY_SIZE(ccdc_fmts)) | ||
5385 | + return -EINVAL; | ||
5386 | + | ||
5387 | + code->code = ccdc_fmts[code->index]; | ||
5388 | + break; | ||
5389 | + | ||
5390 | + case CCDC_PAD_SOURCE_OF: | ||
5391 | + case CCDC_PAD_SOURCE_VP: | ||
5392 | + /* No format conversion inside CCDC */ | ||
5393 | + if (code->index != 0) | ||
5394 | + return -EINVAL; | ||
5395 | + | ||
5396 | + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, | ||
5397 | + V4L2_SUBDEV_FORMAT_TRY); | ||
5398 | + | ||
5399 | + code->code = format->code; | ||
5400 | + break; | ||
5401 | + | ||
5402 | + default: | ||
5403 | + return -EINVAL; | ||
5404 | + } | ||
5405 | + | ||
5406 | + return 0; | ||
5407 | +} | ||
5408 | + | ||
5409 | +static int ccdc_enum_frame_size(struct v4l2_subdev *sd, | ||
5410 | + struct v4l2_subdev_fh *fh, | ||
5411 | + struct v4l2_subdev_frame_size_enum *fse) | ||
5412 | +{ | ||
5413 | + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
5414 | + struct v4l2_mbus_framefmt format; | ||
5415 | + | ||
5416 | + if (fse->index != 0) | ||
5417 | + return -EINVAL; | ||
5418 | + | ||
5419 | + format.code = fse->code; | ||
5420 | + format.width = 1; | ||
5421 | + format.height = 1; | ||
5422 | + ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
5423 | + fse->min_width = format.width; | ||
5424 | + fse->min_height = format.height; | ||
5425 | + | ||
5426 | + if (format.code != fse->code) | ||
5427 | + return -EINVAL; | ||
5428 | + | ||
5429 | + format.code = fse->code; | ||
5430 | + format.width = -1; | ||
5431 | + format.height = -1; | ||
5432 | + ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
5433 | + fse->max_width = format.width; | ||
5434 | + fse->max_height = format.height; | ||
5435 | + | ||
5436 | + return 0; | ||
5437 | +} | ||
5438 | + | ||
5439 | +/* | ||
5440 | + * ccdc_get_format - Retrieve the video format on a pad | ||
5441 | + * @sd : ISP CCDC V4L2 subdevice | ||
5442 | + * @fh : V4L2 subdev file handle | ||
5443 | + * @fmt: Format | ||
5444 | + * | ||
5445 | + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond | ||
5446 | + * to the format type. | ||
5447 | + */ | ||
5448 | +static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
5449 | + struct v4l2_subdev_format *fmt) | ||
5450 | +{ | ||
5451 | + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
5452 | + struct v4l2_mbus_framefmt *format; | ||
5453 | + | ||
5454 | + format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); | ||
5455 | + if (format == NULL) | ||
5456 | + return -EINVAL; | ||
5457 | + | ||
5458 | + fmt->format = *format; | ||
5459 | + return 0; | ||
5460 | +} | ||
5461 | + | ||
5462 | +/* | ||
5463 | + * ccdc_set_format - Set the video format on a pad | ||
5464 | + * @sd : ISP CCDC V4L2 subdevice | ||
5465 | + * @fh : V4L2 subdev file handle | ||
5466 | + * @fmt: Format | ||
5467 | + * | ||
5468 | + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond | ||
5469 | + * to the format type. | ||
5470 | + */ | ||
5471 | +static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
5472 | + struct v4l2_subdev_format *fmt) | ||
5473 | +{ | ||
5474 | + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
5475 | + struct v4l2_mbus_framefmt *format; | ||
5476 | + | ||
5477 | + format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); | ||
5478 | + if (format == NULL) | ||
5479 | + return -EINVAL; | ||
5480 | + | ||
5481 | + ccdc_try_format(ccdc, fh, fmt->pad, &fmt->format, fmt->which); | ||
5482 | + *format = fmt->format; | ||
5483 | + | ||
5484 | + /* Propagate the format from sink to source */ | ||
5485 | + if (fmt->pad == CCDC_PAD_SINK) { | ||
5486 | + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, | ||
5487 | + fmt->which); | ||
5488 | + *format = fmt->format; | ||
5489 | + ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, | ||
5490 | + fmt->which); | ||
5491 | + | ||
5492 | + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_VP, | ||
5493 | + fmt->which); | ||
5494 | + *format = fmt->format; | ||
5495 | + ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_VP, format, | ||
5496 | + fmt->which); | ||
5497 | + } | ||
5498 | + | ||
5499 | + return 0; | ||
5500 | +} | ||
5501 | + | ||
5502 | +/* | ||
5503 | + * ccdc_init_formats - Initialize formats on all pads | ||
5504 | + * @sd: ISP CCDC V4L2 subdevice | ||
5505 | + * @fh: V4L2 subdev file handle | ||
5506 | + * | ||
5507 | + * Initialize all pad formats with default values. If fh is not NULL, try | ||
5508 | + * formats are initialized on the file handle. Otherwise active formats are | ||
5509 | + * initialized on the device. | ||
5510 | + */ | ||
5511 | +static int ccdc_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
5512 | +{ | ||
5513 | + struct v4l2_subdev_format format; | ||
5514 | + | ||
5515 | + memset(&format, 0, sizeof(format)); | ||
5516 | + format.pad = CCDC_PAD_SINK; | ||
5517 | + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
5518 | + format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
5519 | + format.format.width = 4096; | ||
5520 | + format.format.height = 4096; | ||
5521 | + ccdc_set_format(sd, fh, &format); | ||
5522 | + | ||
5523 | + return 0; | ||
5524 | +} | ||
5525 | + | ||
5526 | +/* V4L2 subdev core operations */ | ||
5527 | +static const struct v4l2_subdev_core_ops ccdc_v4l2_core_ops = { | ||
5528 | + .queryctrl = v4l2_subdev_queryctrl, | ||
5529 | + .querymenu = v4l2_subdev_querymenu, | ||
5530 | + .g_ctrl = v4l2_subdev_g_ctrl, | ||
5531 | + .s_ctrl = v4l2_subdev_s_ctrl, | ||
5532 | + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, | ||
5533 | + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, | ||
5534 | + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, | ||
5535 | + .ioctl = ccdc_ioctl, | ||
5536 | + .subscribe_event = ccdc_subscribe_event, | ||
5537 | + .unsubscribe_event = ccdc_unsubscribe_event, | ||
5538 | +}; | ||
5539 | + | ||
5540 | +/* V4L2 subdev file operations */ | ||
5541 | +static const struct v4l2_subdev_file_ops ccdc_v4l2_file_ops = { | ||
5542 | + .open = ccdc_init_formats, | ||
5543 | +}; | ||
5544 | + | ||
5545 | +/* V4L2 subdev video operations */ | ||
5546 | +static const struct v4l2_subdev_video_ops ccdc_v4l2_video_ops = { | ||
5547 | + .s_stream = ccdc_set_stream, | ||
5548 | +}; | ||
5549 | + | ||
5550 | +/* V4L2 subdev pad operations */ | ||
5551 | +static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { | ||
5552 | + .enum_mbus_code = ccdc_enum_mbus_code, | ||
5553 | + .enum_frame_size = ccdc_enum_frame_size, | ||
5554 | + .get_fmt = ccdc_get_format, | ||
5555 | + .set_fmt = ccdc_set_format, | ||
5556 | +}; | ||
5557 | + | ||
5558 | +/* V4L2 subdev operations */ | ||
5559 | +static const struct v4l2_subdev_ops ccdc_v4l2_ops = { | ||
5560 | + .core = &ccdc_v4l2_core_ops, | ||
5561 | + .file = &ccdc_v4l2_file_ops, | ||
5562 | + .video = &ccdc_v4l2_video_ops, | ||
5563 | + .pad = &ccdc_v4l2_pad_ops, | ||
5564 | +}; | ||
5565 | + | ||
5566 | +/* ----------------------------------------------------------------------------- | ||
5567 | + * Media entity operations | ||
5568 | + */ | ||
5569 | + | ||
5570 | +/* | ||
5571 | + * ccdc_link_setup - Setup CCDC connections | ||
5572 | + * @entity: CCDC media entity | ||
5573 | + * @local: Pad at the local end of the link | ||
5574 | + * @remote: Pad at the remote end of the link | ||
5575 | + * @flags: Link flags | ||
5576 | + * | ||
5577 | + * return -EINVAL or zero on success | ||
5578 | + */ | ||
5579 | +static int ccdc_link_setup(struct media_entity *entity, | ||
5580 | + const struct media_pad *local, | ||
5581 | + const struct media_pad *remote, u32 flags) | ||
5582 | +{ | ||
5583 | + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
5584 | + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
5585 | + struct isp_device *isp = to_isp_device(ccdc); | ||
5586 | + | ||
5587 | + switch (local->index | media_entity_type(remote->entity)) { | ||
5588 | + case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
5589 | + /* Read from the sensor (parallel interface), CCP2, CSI2a or | ||
5590 | + * CSI2c. | ||
5591 | + */ | ||
5592 | + if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
5593 | + ccdc->input = CCDC_INPUT_NONE; | ||
5594 | + break; | ||
5595 | + } | ||
5596 | + | ||
5597 | + if (ccdc->input != CCDC_INPUT_NONE) | ||
5598 | + return -EBUSY; | ||
5599 | + | ||
5600 | + if (remote->entity == &isp->isp_ccp2.subdev.entity) | ||
5601 | + ccdc->input = CCDC_INPUT_CCP2B; | ||
5602 | + else if (remote->entity == &isp->isp_csi2a.subdev.entity) | ||
5603 | + ccdc->input = CCDC_INPUT_CSI2A; | ||
5604 | + else if (remote->entity == &isp->isp_csi2c.subdev.entity) | ||
5605 | + ccdc->input = CCDC_INPUT_CSI2C; | ||
5606 | + else | ||
5607 | + ccdc->input = CCDC_INPUT_PARALLEL; | ||
5608 | + | ||
5609 | + break; | ||
5610 | + | ||
5611 | + /* | ||
5612 | + * The ISP core doesn't support pipelines with multiple video outputs. | ||
5613 | + * Revisit this when it will be implemented, and return -EBUSY for now. | ||
5614 | + */ | ||
5615 | + | ||
5616 | + case CCDC_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: | ||
5617 | + /* Write to preview engine, histogram and H3A. When none of | ||
5618 | + * those links are active, the video port can be disabled. | ||
5619 | + */ | ||
5620 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
5621 | + if (ccdc->output & ~CCDC_OUTPUT_PREVIEW) | ||
5622 | + return -EBUSY; | ||
5623 | + ccdc->output |= CCDC_OUTPUT_PREVIEW; | ||
5624 | + } else { | ||
5625 | + ccdc->output &= ~CCDC_OUTPUT_PREVIEW; | ||
5626 | + } | ||
5627 | + break; | ||
5628 | + | ||
5629 | + case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_DEVNODE: | ||
5630 | + /* Write to memory */ | ||
5631 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
5632 | + if (ccdc->output & ~CCDC_OUTPUT_MEMORY) | ||
5633 | + return -EBUSY; | ||
5634 | + ccdc->output |= CCDC_OUTPUT_MEMORY; | ||
5635 | + } else { | ||
5636 | + ccdc->output &= ~CCDC_OUTPUT_MEMORY; | ||
5637 | + } | ||
5638 | + break; | ||
5639 | + | ||
5640 | + case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_V4L2_SUBDEV: | ||
5641 | + /* Write to resizer */ | ||
5642 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
5643 | + if (ccdc->output & ~CCDC_OUTPUT_RESIZER) | ||
5644 | + return -EBUSY; | ||
5645 | + ccdc->output |= CCDC_OUTPUT_RESIZER; | ||
5646 | + } else { | ||
5647 | + ccdc->output &= ~CCDC_OUTPUT_RESIZER; | ||
5648 | + } | ||
5649 | + break; | ||
5650 | + | ||
5651 | + default: | ||
5652 | + return -EINVAL; | ||
5653 | + } | ||
5654 | + | ||
5655 | + return 0; | ||
5656 | +} | ||
5657 | + | ||
5658 | +/* media operations */ | ||
5659 | +static const struct media_entity_operations ccdc_media_ops = { | ||
5660 | + .link_setup = ccdc_link_setup, | ||
5661 | +}; | ||
5662 | + | ||
5663 | +/* | ||
5664 | + * ccdc_init_entities - Initialize V4L2 subdev and media entity | ||
5665 | + * @ccdc: ISP CCDC module | ||
5666 | + * | ||
5667 | + * Return 0 on success and a negative error code on failure. | ||
5668 | + */ | ||
5669 | +static int ccdc_init_entities(struct isp_ccdc_device *ccdc) | ||
5670 | +{ | ||
5671 | + struct v4l2_subdev *sd = &ccdc->subdev; | ||
5672 | + struct media_pad *pads = ccdc->pads; | ||
5673 | + struct media_entity *me = &sd->entity; | ||
5674 | + int ret; | ||
5675 | + | ||
5676 | + ccdc->input = CCDC_INPUT_NONE; | ||
5677 | + | ||
5678 | + v4l2_subdev_init(sd, &ccdc_v4l2_ops); | ||
5679 | + strlcpy(sd->name, "OMAP3 ISP CCDC", sizeof(sd->name)); | ||
5680 | + sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
5681 | + v4l2_set_subdevdata(sd, ccdc); | ||
5682 | + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
5683 | + sd->nevents = OMAP3ISP_CCDC_NEVENTS; | ||
5684 | + | ||
5685 | + v4l2_ctrl_handler_init(&ccdc->ctrls, 1); | ||
5686 | + sd->ctrl_handler = &ccdc->ctrls; | ||
5687 | + | ||
5688 | + pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; | ||
5689 | + pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_OUTPUT; | ||
5690 | + pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_OUTPUT; | ||
5691 | + | ||
5692 | + me->ops = &ccdc_media_ops; | ||
5693 | + ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0); | ||
5694 | + if (ret < 0) | ||
5695 | + return ret; | ||
5696 | + | ||
5697 | + ccdc_init_formats(sd, NULL); | ||
5698 | + | ||
5699 | + ccdc->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
5700 | + ccdc->video_out.ops = &ccdc_video_ops; | ||
5701 | + ccdc->video_out.isp = to_isp_device(ccdc); | ||
5702 | + ccdc->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; | ||
5703 | + ccdc->video_out.bpl_alignment = 32; | ||
5704 | + | ||
5705 | + ret = omap3isp_video_init(&ccdc->video_out, "CCDC"); | ||
5706 | + if (ret < 0) | ||
5707 | + return ret; | ||
5708 | + | ||
5709 | + /* Connect the CCDC subdev to the video node. */ | ||
5710 | + ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF, | ||
5711 | + &ccdc->video_out.video.entity, 0, 0); | ||
5712 | + if (ret < 0) | ||
5713 | + return ret; | ||
5714 | + | ||
5715 | + return 0; | ||
5716 | +} | ||
5717 | + | ||
5718 | +void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc) | ||
5719 | +{ | ||
5720 | + media_entity_cleanup(&ccdc->subdev.entity); | ||
5721 | + | ||
5722 | + v4l2_device_unregister_subdev(&ccdc->subdev); | ||
5723 | + v4l2_ctrl_handler_free(&ccdc->ctrls); | ||
5724 | + omap3isp_video_unregister(&ccdc->video_out); | ||
5725 | +} | ||
5726 | + | ||
5727 | +int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc, | ||
5728 | + struct v4l2_device *vdev) | ||
5729 | +{ | ||
5730 | + int ret; | ||
5731 | + | ||
5732 | + /* Register the subdev and video node. */ | ||
5733 | + ret = v4l2_device_register_subdev(vdev, &ccdc->subdev); | ||
5734 | + if (ret < 0) | ||
5735 | + goto error; | ||
5736 | + | ||
5737 | + ret = omap3isp_video_register(&ccdc->video_out, vdev); | ||
5738 | + if (ret < 0) | ||
5739 | + goto error; | ||
5740 | + | ||
5741 | + return 0; | ||
5742 | + | ||
5743 | +error: | ||
5744 | + omap3isp_ccdc_unregister_entities(ccdc); | ||
5745 | + return ret; | ||
5746 | +} | ||
5747 | + | ||
5748 | +/* ----------------------------------------------------------------------------- | ||
5749 | + * ISP CCDC initialisation and cleanup | ||
5750 | + */ | ||
5751 | + | ||
5752 | +/* | ||
5753 | + * omap3isp_ccdc_init - CCDC module initialization. | ||
5754 | + * @dev: Device pointer specific to the OMAP3 ISP. | ||
5755 | + * | ||
5756 | + * TODO: Get the initialisation values from platform data. | ||
5757 | + * | ||
5758 | + * Return 0 on success or a negative error code otherwise. | ||
5759 | + */ | ||
5760 | +int omap3isp_ccdc_init(struct isp_device *isp) | ||
5761 | +{ | ||
5762 | + struct isp_ccdc_device *ccdc = &isp->isp_ccdc; | ||
5763 | + | ||
5764 | + spin_lock_init(&ccdc->lock); | ||
5765 | + init_waitqueue_head(&ccdc->wait); | ||
5766 | + mutex_init(&ccdc->ioctl_lock); | ||
5767 | + | ||
5768 | + ccdc->stopping = CCDC_STOP_NOT_REQUESTED; | ||
5769 | + | ||
5770 | + INIT_WORK(&ccdc->lsc.table_work, ccdc_lsc_free_table_work); | ||
5771 | + ccdc->lsc.state = LSC_STATE_STOPPED; | ||
5772 | + INIT_LIST_HEAD(&ccdc->lsc.free_queue); | ||
5773 | + spin_lock_init(&ccdc->lsc.req_lock); | ||
5774 | + | ||
5775 | + ccdc->syncif.ccdc_mastermode = 0; | ||
5776 | + ccdc->syncif.datapol = 0; | ||
5777 | + ccdc->syncif.datsz = 0; | ||
5778 | + ccdc->syncif.fldmode = 0; | ||
5779 | + ccdc->syncif.fldout = 0; | ||
5780 | + ccdc->syncif.fldpol = 0; | ||
5781 | + ccdc->syncif.fldstat = 0; | ||
5782 | + ccdc->syncif.hdpol = 0; | ||
5783 | + ccdc->syncif.vdpol = 0; | ||
5784 | + | ||
5785 | + ccdc->clamp.oblen = 0; | ||
5786 | + ccdc->clamp.dcsubval = 0; | ||
5787 | + | ||
5788 | + ccdc->vpcfg.pixelclk = 0; | ||
5789 | + | ||
5790 | + ccdc->update = OMAP3ISP_CCDC_BLCLAMP; | ||
5791 | + ccdc_apply_controls(ccdc); | ||
5792 | + | ||
5793 | + return ccdc_init_entities(ccdc); | ||
5794 | +} | ||
5795 | + | ||
5796 | +/* | ||
5797 | + * omap3isp_ccdc_cleanup - CCDC module cleanup. | ||
5798 | + * @dev: Device pointer specific to the OMAP3 ISP. | ||
5799 | + */ | ||
5800 | +void omap3isp_ccdc_cleanup(struct isp_device *isp) | ||
5801 | +{ | ||
5802 | + struct isp_ccdc_device *ccdc = &isp->isp_ccdc; | ||
5803 | + | ||
5804 | + /* Free LSC requests. As the CCDC is stopped there's no active request, | ||
5805 | + * so only the pending request and the free queue need to be handled. | ||
5806 | + */ | ||
5807 | + ccdc_lsc_free_request(ccdc, ccdc->lsc.request); | ||
5808 | + cancel_work_sync(&ccdc->lsc.table_work); | ||
5809 | + ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue); | ||
5810 | + | ||
5811 | + if (ccdc->fpc.fpcaddr != 0) | ||
5812 | + iommu_vfree(isp->iommu, ccdc->fpc.fpcaddr); | ||
5813 | +} | ||
5814 | diff --git a/drivers/media/video/isp/ispccdc.h b/drivers/media/video/isp/ispccdc.h | ||
5815 | new file mode 100644 | ||
5816 | index 0000000..5c00e2c | ||
5817 | --- /dev/null | ||
5818 | +++ b/drivers/media/video/isp/ispccdc.h | ||
5819 | @@ -0,0 +1,223 @@ | ||
5820 | +/* | ||
5821 | + * ispccdc.h | ||
5822 | + * | ||
5823 | + * TI OMAP3 ISP - CCDC module | ||
5824 | + * | ||
5825 | + * Copyright (C) 2009-2010 Nokia Corporation | ||
5826 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
5827 | + * | ||
5828 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
5829 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
5830 | + * | ||
5831 | + * This program is free software; you can redistribute it and/or modify | ||
5832 | + * it under the terms of the GNU General Public License version 2 as | ||
5833 | + * published by the Free Software Foundation. | ||
5834 | + * | ||
5835 | + * This program is distributed in the hope that it will be useful, but | ||
5836 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
5837 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
5838 | + * General Public License for more details. | ||
5839 | + * | ||
5840 | + * You should have received a copy of the GNU General Public License | ||
5841 | + * along with this program; if not, write to the Free Software | ||
5842 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
5843 | + * 02110-1301 USA | ||
5844 | + */ | ||
5845 | + | ||
5846 | +#ifndef OMAP3_ISP_CCDC_H | ||
5847 | +#define OMAP3_ISP_CCDC_H | ||
5848 | + | ||
5849 | +#include <linux/omap3isp.h> | ||
5850 | +#include <linux/workqueue.h> | ||
5851 | +#include <media/v4l2-ctrls.h> | ||
5852 | + | ||
5853 | +#include "ispvideo.h" | ||
5854 | + | ||
5855 | +enum ccdc_input_entity { | ||
5856 | + CCDC_INPUT_NONE, | ||
5857 | + CCDC_INPUT_PARALLEL, | ||
5858 | + CCDC_INPUT_CSI2A, | ||
5859 | + CCDC_INPUT_CCP2B, | ||
5860 | + CCDC_INPUT_CSI2C | ||
5861 | +}; | ||
5862 | + | ||
5863 | +#define CCDC_OUTPUT_MEMORY (1 << 0) | ||
5864 | +#define CCDC_OUTPUT_PREVIEW (1 << 1) | ||
5865 | +#define CCDC_OUTPUT_RESIZER (1 << 2) | ||
5866 | + | ||
5867 | +#define OMAP3ISP_CCDC_NEVENTS 16 | ||
5868 | + | ||
5869 | +/* | ||
5870 | + * struct ispccdc_syncif - Structure for Sync Interface between sensor and CCDC | ||
5871 | + * @ccdc_mastermode: Master mode. 1 - Master, 0 - Slave. | ||
5872 | + * @fldstat: Field state. 0 - Odd Field, 1 - Even Field. | ||
5873 | + * @datsz: Data size. | ||
5874 | + * @fldmode: 0 - Progressive, 1 - Interlaced. | ||
5875 | + * @datapol: 0 - Positive, 1 - Negative. | ||
5876 | + * @fldpol: 0 - Positive, 1 - Negative. | ||
5877 | + * @hdpol: 0 - Positive, 1 - Negative. | ||
5878 | + * @vdpol: 0 - Positive, 1 - Negative. | ||
5879 | + * @fldout: 0 - Input, 1 - Output. | ||
5880 | + * @hs_width: Width of the Horizontal Sync pulse, used for HS/VS Output. | ||
5881 | + * @vs_width: Width of the Vertical Sync pulse, used for HS/VS Output. | ||
5882 | + * @ppln: Number of pixels per line, used for HS/VS Output. | ||
5883 | + * @hlprf: Number of half lines per frame, used for HS/VS Output. | ||
5884 | + * @bt_r656_en: 1 - Enable ITU-R BT656 mode, 0 - Sync mode. | ||
5885 | + */ | ||
5886 | +struct ispccdc_syncif { | ||
5887 | + u8 ccdc_mastermode; | ||
5888 | + u8 fldstat; | ||
5889 | + u8 datsz; | ||
5890 | + u8 fldmode; | ||
5891 | + u8 datapol; | ||
5892 | + u8 fldpol; | ||
5893 | + u8 hdpol; | ||
5894 | + u8 vdpol; | ||
5895 | + u8 fldout; | ||
5896 | + u8 hs_width; | ||
5897 | + u8 vs_width; | ||
5898 | + u8 ppln; | ||
5899 | + u8 hlprf; | ||
5900 | + u8 bt_r656_en; | ||
5901 | +}; | ||
5902 | + | ||
5903 | +/* | ||
5904 | + * struct ispccdc_vp - Structure for Video Port parameters | ||
5905 | + * @pixelclk: Input pixel clock in Hz | ||
5906 | + */ | ||
5907 | +struct ispccdc_vp { | ||
5908 | + unsigned int pixelclk; | ||
5909 | +}; | ||
5910 | + | ||
5911 | +enum ispccdc_lsc_state { | ||
5912 | + LSC_STATE_STOPPED = 0, | ||
5913 | + LSC_STATE_STOPPING = 1, | ||
5914 | + LSC_STATE_RUNNING = 2, | ||
5915 | + LSC_STATE_RECONFIG = 3, | ||
5916 | +}; | ||
5917 | + | ||
5918 | +struct ispccdc_lsc_config_req { | ||
5919 | + struct list_head list; | ||
5920 | + struct omap3isp_ccdc_lsc_config config; | ||
5921 | + unsigned char enable; | ||
5922 | + u32 table; | ||
5923 | + struct iovm_struct *iovm; | ||
5924 | +}; | ||
5925 | + | ||
5926 | +/* | ||
5927 | + * ispccdc_lsc - CCDC LSC parameters | ||
5928 | + * @update_config: Set when user changes config | ||
5929 | + * @request_enable: Whether LSC is requested to be enabled | ||
5930 | + * @config: LSC config set by user | ||
5931 | + * @update_table: Set when user provides a new LSC table to table_new | ||
5932 | + * @table_new: LSC table set by user, ISP address | ||
5933 | + * @table_inuse: LSC table currently in use, ISP address | ||
5934 | + */ | ||
5935 | +struct ispccdc_lsc { | ||
5936 | + enum ispccdc_lsc_state state; | ||
5937 | + struct work_struct table_work; | ||
5938 | + | ||
5939 | + /* LSC queue of configurations */ | ||
5940 | + spinlock_t req_lock; | ||
5941 | + struct ispccdc_lsc_config_req *request; /* requested configuration */ | ||
5942 | + struct ispccdc_lsc_config_req *active; /* active configuration */ | ||
5943 | + struct list_head free_queue; /* configurations for freeing */ | ||
5944 | +}; | ||
5945 | + | ||
5946 | +#define CCDC_STOP_NOT_REQUESTED 0x00 | ||
5947 | +#define CCDC_STOP_REQUEST 0x01 | ||
5948 | +#define CCDC_STOP_EXECUTED (0x02 | CCDC_STOP_REQUEST) | ||
5949 | +#define CCDC_STOP_CCDC_FINISHED 0x04 | ||
5950 | +#define CCDC_STOP_LSC_FINISHED 0x08 | ||
5951 | +#define CCDC_STOP_FINISHED \ | ||
5952 | + (CCDC_STOP_EXECUTED | CCDC_STOP_CCDC_FINISHED | CCDC_STOP_LSC_FINISHED) | ||
5953 | + | ||
5954 | +#define CCDC_EVENT_VD1 0x10 | ||
5955 | +#define CCDC_EVENT_VD0 0x20 | ||
5956 | +#define CCDC_EVENT_LSC_DONE 0x40 | ||
5957 | + | ||
5958 | +/* Sink and source CCDC pads */ | ||
5959 | +#define CCDC_PAD_SINK 0 | ||
5960 | +#define CCDC_PAD_SOURCE_OF 1 | ||
5961 | +#define CCDC_PAD_SOURCE_VP 2 | ||
5962 | +#define CCDC_PADS_NUM 3 | ||
5963 | + | ||
5964 | +/* | ||
5965 | + * struct isp_ccdc_device - Structure for the CCDC module to store its own | ||
5966 | + * information | ||
5967 | + * @subdev: V4L2 subdevice | ||
5968 | + * @pads: Sink and source media entity pads | ||
5969 | + * @formats: Active video formats | ||
5970 | + * @ctrls: V4L2 controls handler | ||
5971 | + * @input: Active input | ||
5972 | + * @output: Active outputs | ||
5973 | + * @video_out: Output video node | ||
5974 | + * @error: A hardware error occured during capture | ||
5975 | + * @alaw: A-law compression enabled (1) or disabled (0) | ||
5976 | + * @lpf: Low pass filter enabled (1) or disabled (0) | ||
5977 | + * @obclamp: Optical-black clamp enabled (1) or disabled (0) | ||
5978 | + * @fpc_en: Faulty pixels correction enabled (1) or disabled (0) | ||
5979 | + * @blcomp: Black level compensation configuration | ||
5980 | + * @clamp: Optical-black or digital clamp configuration | ||
5981 | + * @fpc: Faulty pixels correction configuration | ||
5982 | + * @lsc: Lens shading compensation configuration | ||
5983 | + * @update: Bitmask of controls to update during the next interrupt | ||
5984 | + * @shadow_update: Controls update in progress by userspace | ||
5985 | + * @syncif: Interface synchronization configuration | ||
5986 | + * @vpcfg: Video port configuration | ||
5987 | + * @underrun: A buffer underrun occured and a new buffer has been queued | ||
5988 | + * @state: Streaming state | ||
5989 | + * @lock: Serializes shadow_update with interrupt handler | ||
5990 | + * @wait: Wait queue used to stop the module | ||
5991 | + * @stopping: Stopping state | ||
5992 | + * @ioctl_lock: Serializes ioctl calls and LSC requests freeing | ||
5993 | + */ | ||
5994 | +struct isp_ccdc_device { | ||
5995 | + struct v4l2_subdev subdev; | ||
5996 | + struct media_pad pads[CCDC_PADS_NUM]; | ||
5997 | + struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM]; | ||
5998 | + | ||
5999 | + struct v4l2_ctrl_handler ctrls; | ||
6000 | + | ||
6001 | + enum ccdc_input_entity input; | ||
6002 | + unsigned int output; | ||
6003 | + struct isp_video video_out; | ||
6004 | + unsigned int error; | ||
6005 | + | ||
6006 | + unsigned int alaw:1, | ||
6007 | + lpf:1, | ||
6008 | + obclamp:1, | ||
6009 | + fpc_en:1; | ||
6010 | + struct omap3isp_ccdc_blcomp blcomp; | ||
6011 | + struct omap3isp_ccdc_bclamp clamp; | ||
6012 | + struct omap3isp_ccdc_fpc fpc; | ||
6013 | + struct ispccdc_lsc lsc; | ||
6014 | + unsigned int update; | ||
6015 | + unsigned int shadow_update; | ||
6016 | + | ||
6017 | + struct ispccdc_syncif syncif; | ||
6018 | + struct ispccdc_vp vpcfg; | ||
6019 | + | ||
6020 | + unsigned int underrun:1; | ||
6021 | + enum isp_pipeline_stream_state state; | ||
6022 | + spinlock_t lock; | ||
6023 | + wait_queue_head_t wait; | ||
6024 | + unsigned int stopping; | ||
6025 | + struct mutex ioctl_lock; | ||
6026 | +}; | ||
6027 | + | ||
6028 | +struct isp_device; | ||
6029 | + | ||
6030 | +int omap3isp_ccdc_init(struct isp_device *isp); | ||
6031 | +void omap3isp_ccdc_cleanup(struct isp_device *isp); | ||
6032 | +int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc, | ||
6033 | + struct v4l2_device *vdev); | ||
6034 | +void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc); | ||
6035 | + | ||
6036 | +int omap3isp_ccdc_busy(struct isp_ccdc_device *isp_ccdc); | ||
6037 | +int omap3isp_ccdc_isr(struct isp_ccdc_device *isp_ccdc, u32 events); | ||
6038 | +void omap3isp_ccdc_restore_context(struct isp_device *isp); | ||
6039 | +void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc, | ||
6040 | + unsigned int *max_rate); | ||
6041 | + | ||
6042 | +#endif /* OMAP3_ISP_CCDC_H */ | ||
6043 | diff --git a/drivers/media/video/isp/ispccp2.c b/drivers/media/video/isp/ispccp2.c | ||
6044 | new file mode 100644 | ||
6045 | index 0000000..efcf827 | ||
6046 | --- /dev/null | ||
6047 | +++ b/drivers/media/video/isp/ispccp2.c | ||
6048 | @@ -0,0 +1,1189 @@ | ||
6049 | +/* | ||
6050 | + * ispccp2.c | ||
6051 | + * | ||
6052 | + * TI OMAP3 ISP - CCP2 module | ||
6053 | + * | ||
6054 | + * Copyright (C) 2010 Nokia Corporation | ||
6055 | + * Copyright (C) 2010 Texas Instruments, Inc. | ||
6056 | + * | ||
6057 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
6058 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
6059 | + * | ||
6060 | + * This program is free software; you can redistribute it and/or modify | ||
6061 | + * it under the terms of the GNU General Public License version 2 as | ||
6062 | + * published by the Free Software Foundation. | ||
6063 | + * | ||
6064 | + * This program is distributed in the hope that it will be useful, but | ||
6065 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
6066 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
6067 | + * General Public License for more details. | ||
6068 | + * | ||
6069 | + * You should have received a copy of the GNU General Public License | ||
6070 | + * along with this program; if not, write to the Free Software | ||
6071 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
6072 | + * 02110-1301 USA | ||
6073 | + */ | ||
6074 | + | ||
6075 | +#include <linux/delay.h> | ||
6076 | +#include <linux/device.h> | ||
6077 | +#include <linux/mm.h> | ||
6078 | +#include <linux/module.h> | ||
6079 | +#include <linux/mutex.h> | ||
6080 | +#include <linux/uaccess.h> | ||
6081 | + | ||
6082 | +#include "isp.h" | ||
6083 | +#include "ispreg.h" | ||
6084 | +#include "ispccp2.h" | ||
6085 | + | ||
6086 | +/* Number of LCX channels */ | ||
6087 | +#define CCP2_LCx_CHANS_NUM 3 | ||
6088 | +/* Max/Min size for CCP2 video port */ | ||
6089 | +#define ISPCCP2_DAT_START_MIN 0 | ||
6090 | +#define ISPCCP2_DAT_START_MAX 4095 | ||
6091 | +#define ISPCCP2_DAT_SIZE_MIN 0 | ||
6092 | +#define ISPCCP2_DAT_SIZE_MAX 4095 | ||
6093 | +#define ISPCCP2_VPCLK_FRACDIV 65536 | ||
6094 | +#define ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP 0x12 | ||
6095 | +#define ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP 0x16 | ||
6096 | +/* Max/Min size for CCP2 memory channel */ | ||
6097 | +#define ISPCCP2_LCM_HSIZE_COUNT_MIN 16 | ||
6098 | +#define ISPCCP2_LCM_HSIZE_COUNT_MAX 8191 | ||
6099 | +#define ISPCCP2_LCM_HSIZE_SKIP_MIN 0 | ||
6100 | +#define ISPCCP2_LCM_HSIZE_SKIP_MAX 8191 | ||
6101 | +#define ISPCCP2_LCM_VSIZE_MIN 1 | ||
6102 | +#define ISPCCP2_LCM_VSIZE_MAX 8191 | ||
6103 | +#define ISPCCP2_LCM_HWORDS_MIN 1 | ||
6104 | +#define ISPCCP2_LCM_HWORDS_MAX 4095 | ||
6105 | +#define ISPCCP2_LCM_CTRL_BURST_SIZE_32X 5 | ||
6106 | +#define ISPCCP2_LCM_CTRL_READ_THROTTLE_FULL 0 | ||
6107 | +#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 2 | ||
6108 | +#define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 2 | ||
6109 | +#define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 3 | ||
6110 | +#define ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 3 | ||
6111 | +#define ISPCCP2_LCM_CTRL_DST_PORT_VP 0 | ||
6112 | +#define ISPCCP2_LCM_CTRL_DST_PORT_MEM 1 | ||
6113 | + | ||
6114 | +/* Set only the required bits */ | ||
6115 | +#define BIT_SET(var, shift, mask, val) \ | ||
6116 | + do { \ | ||
6117 | + var = ((var) & ~((mask) << (shift))) \ | ||
6118 | + | ((val) << (shift)); \ | ||
6119 | + } while (0) | ||
6120 | + | ||
6121 | +/* | ||
6122 | + * ccp2_print_status - Print current CCP2 module register values. | ||
6123 | + */ | ||
6124 | +#define CCP2_PRINT_REGISTER(isp, name)\ | ||
6125 | + dev_dbg(isp->dev, "###CCP2 " #name "=0x%08x\n", \ | ||
6126 | + isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_##name)) | ||
6127 | + | ||
6128 | +static void ccp2_print_status(struct isp_ccp2_device *ccp2) | ||
6129 | +{ | ||
6130 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6131 | + | ||
6132 | + dev_dbg(isp->dev, "-------------CCP2 Register dump-------------\n"); | ||
6133 | + | ||
6134 | + CCP2_PRINT_REGISTER(isp, SYSCONFIG); | ||
6135 | + CCP2_PRINT_REGISTER(isp, SYSSTATUS); | ||
6136 | + CCP2_PRINT_REGISTER(isp, LC01_IRQENABLE); | ||
6137 | + CCP2_PRINT_REGISTER(isp, LC01_IRQSTATUS); | ||
6138 | + CCP2_PRINT_REGISTER(isp, LC23_IRQENABLE); | ||
6139 | + CCP2_PRINT_REGISTER(isp, LC23_IRQSTATUS); | ||
6140 | + CCP2_PRINT_REGISTER(isp, LCM_IRQENABLE); | ||
6141 | + CCP2_PRINT_REGISTER(isp, LCM_IRQSTATUS); | ||
6142 | + CCP2_PRINT_REGISTER(isp, CTRL); | ||
6143 | + CCP2_PRINT_REGISTER(isp, LCx_CTRL(0)); | ||
6144 | + CCP2_PRINT_REGISTER(isp, LCx_CODE(0)); | ||
6145 | + CCP2_PRINT_REGISTER(isp, LCx_STAT_START(0)); | ||
6146 | + CCP2_PRINT_REGISTER(isp, LCx_STAT_SIZE(0)); | ||
6147 | + CCP2_PRINT_REGISTER(isp, LCx_SOF_ADDR(0)); | ||
6148 | + CCP2_PRINT_REGISTER(isp, LCx_EOF_ADDR(0)); | ||
6149 | + CCP2_PRINT_REGISTER(isp, LCx_DAT_START(0)); | ||
6150 | + CCP2_PRINT_REGISTER(isp, LCx_DAT_SIZE(0)); | ||
6151 | + CCP2_PRINT_REGISTER(isp, LCx_DAT_PING_ADDR(0)); | ||
6152 | + CCP2_PRINT_REGISTER(isp, LCx_DAT_PONG_ADDR(0)); | ||
6153 | + CCP2_PRINT_REGISTER(isp, LCx_DAT_OFST(0)); | ||
6154 | + CCP2_PRINT_REGISTER(isp, LCM_CTRL); | ||
6155 | + CCP2_PRINT_REGISTER(isp, LCM_VSIZE); | ||
6156 | + CCP2_PRINT_REGISTER(isp, LCM_HSIZE); | ||
6157 | + CCP2_PRINT_REGISTER(isp, LCM_PREFETCH); | ||
6158 | + CCP2_PRINT_REGISTER(isp, LCM_SRC_ADDR); | ||
6159 | + CCP2_PRINT_REGISTER(isp, LCM_SRC_OFST); | ||
6160 | + CCP2_PRINT_REGISTER(isp, LCM_DST_ADDR); | ||
6161 | + CCP2_PRINT_REGISTER(isp, LCM_DST_OFST); | ||
6162 | + | ||
6163 | + dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
6164 | +} | ||
6165 | + | ||
6166 | +/* | ||
6167 | + * ccp2_reset - Reset the CCP2 | ||
6168 | + * @ccp2: pointer to ISP CCP2 device | ||
6169 | + */ | ||
6170 | +static void ccp2_reset(struct isp_ccp2_device *ccp2) | ||
6171 | +{ | ||
6172 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6173 | + int i = 0; | ||
6174 | + | ||
6175 | + /* Reset the CSI1/CCP2B and wait for reset to complete */ | ||
6176 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSCONFIG, | ||
6177 | + ISPCCP2_SYSCONFIG_SOFT_RESET); | ||
6178 | + while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSSTATUS) & | ||
6179 | + ISPCCP2_SYSSTATUS_RESET_DONE)) { | ||
6180 | + udelay(10); | ||
6181 | + if (i++ > 10) { /* try read 10 times */ | ||
6182 | + dev_warn(isp->dev, | ||
6183 | + "omap3_isp: timeout waiting for ccp2 reset\n"); | ||
6184 | + break; | ||
6185 | + } | ||
6186 | + } | ||
6187 | +} | ||
6188 | + | ||
6189 | +/* | ||
6190 | + * ccp2_pwr_cfg - Configure the power mode settings | ||
6191 | + * @ccp2: pointer to ISP CCP2 device | ||
6192 | + */ | ||
6193 | +static void ccp2_pwr_cfg(struct isp_ccp2_device *ccp2) | ||
6194 | +{ | ||
6195 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6196 | + | ||
6197 | + isp_reg_writel(isp, ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SMART | | ||
6198 | + ((isp->revision == ISP_REVISION_15_0 && isp->autoidle) ? | ||
6199 | + ISPCCP2_SYSCONFIG_AUTO_IDLE : 0), | ||
6200 | + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSCONFIG); | ||
6201 | +} | ||
6202 | + | ||
6203 | +/* | ||
6204 | + * ccp2_if_enable - Enable CCP2 interface. | ||
6205 | + * @ccp2: pointer to ISP CCP2 device | ||
6206 | + * @enable: enable/disable flag | ||
6207 | + */ | ||
6208 | +static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) | ||
6209 | +{ | ||
6210 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6211 | + struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); | ||
6212 | + int i; | ||
6213 | + | ||
6214 | + /* Enable/Disable all the LCx channels */ | ||
6215 | + for (i = 0; i < CCP2_LCx_CHANS_NUM; i++) | ||
6216 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(i), | ||
6217 | + ISPCCP2_LCx_CTRL_CHAN_EN, | ||
6218 | + enable ? ISPCCP2_LCx_CTRL_CHAN_EN : 0); | ||
6219 | + | ||
6220 | + /* Enable/Disable ccp2 interface in ccp2 mode */ | ||
6221 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL, | ||
6222 | + ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN, | ||
6223 | + enable ? (ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN) : 0); | ||
6224 | + | ||
6225 | + /* For frame count propagation */ | ||
6226 | + if (pipe->do_propagation) { | ||
6227 | + /* We may want the Frame Start IRQ from LC0 */ | ||
6228 | + if (enable) | ||
6229 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, | ||
6230 | + ISPCCP2_LC01_IRQENABLE, | ||
6231 | + ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); | ||
6232 | + else | ||
6233 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCP2, | ||
6234 | + ISPCCP2_LC01_IRQENABLE, | ||
6235 | + ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); | ||
6236 | + } | ||
6237 | +} | ||
6238 | + | ||
6239 | +/* | ||
6240 | + * ccp2_mem_enable - Enable CCP2 memory interface. | ||
6241 | + * @ccp2: pointer to ISP CCP2 device | ||
6242 | + * @enable: enable/disable flag | ||
6243 | + */ | ||
6244 | +static void ccp2_mem_enable(struct isp_ccp2_device *ccp2, u8 enable) | ||
6245 | +{ | ||
6246 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6247 | + | ||
6248 | + if (enable) | ||
6249 | + ccp2_if_enable(ccp2, 0); | ||
6250 | + | ||
6251 | + /* Enable/Disable ccp2 interface in ccp2 mode */ | ||
6252 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL, | ||
6253 | + ISPCCP2_CTRL_MODE, enable ? ISPCCP2_CTRL_MODE : 0); | ||
6254 | + | ||
6255 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_CTRL, | ||
6256 | + ISPCCP2_LCM_CTRL_CHAN_EN, | ||
6257 | + enable ? ISPCCP2_LCM_CTRL_CHAN_EN : 0); | ||
6258 | +} | ||
6259 | + | ||
6260 | +/* | ||
6261 | + * ccp2_phyif_config - Initialize CCP2 phy interface config | ||
6262 | + * @ccp2: Pointer to ISP CCP2 device | ||
6263 | + * @config: CCP2 platform data | ||
6264 | + * | ||
6265 | + * Configure the CCP2 physical interface module from platform data. | ||
6266 | + * | ||
6267 | + * Returns -EIO if strobe is chosen in CSI1 mode, or 0 on success. | ||
6268 | + */ | ||
6269 | +static int ccp2_phyif_config(struct isp_ccp2_device *ccp2, | ||
6270 | + const struct isp_ccp2_platform_data *pdata) | ||
6271 | +{ | ||
6272 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6273 | + u32 val; | ||
6274 | + | ||
6275 | + /* CCP2B mode */ | ||
6276 | + val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL) | | ||
6277 | + ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE; | ||
6278 | + /* Data/strobe physical layer */ | ||
6279 | + BIT_SET(val, ISPCCP2_CTRL_PHY_SEL_SHIFT, ISPCCP2_CTRL_PHY_SEL_MASK, | ||
6280 | + pdata->phy_layer); | ||
6281 | + BIT_SET(val, ISPCCP2_CTRL_INV_SHIFT, ISPCCP2_CTRL_INV_MASK, | ||
6282 | + pdata->strobe_clk_pol); | ||
6283 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); | ||
6284 | + | ||
6285 | + val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); | ||
6286 | + if (!(val & ISPCCP2_CTRL_MODE)) { | ||
6287 | + if (pdata->ccp2_mode) | ||
6288 | + dev_warn(isp->dev, "OMAP3 CCP2 bus not available\n"); | ||
6289 | + if (pdata->phy_layer == ISPCCP2_CTRL_PHY_SEL_STROBE) | ||
6290 | + /* Strobe mode requires CCP2 */ | ||
6291 | + return -EIO; | ||
6292 | + } | ||
6293 | + | ||
6294 | + return 0; | ||
6295 | +} | ||
6296 | + | ||
6297 | +/* | ||
6298 | + * ccp2_vp_config - Initialize CCP2 video port interface. | ||
6299 | + * @ccp2: Pointer to ISP CCP2 device | ||
6300 | + * @vpclk_div: Video port divisor | ||
6301 | + * | ||
6302 | + * Configure the CCP2 video port with the given clock divisor. The valid divisor | ||
6303 | + * values depend on the ISP revision: | ||
6304 | + * | ||
6305 | + * - revision 1.0 and 2.0 1 to 4 | ||
6306 | + * - revision 15.0 1 to 65536 | ||
6307 | + * | ||
6308 | + * The exact divisor value used might differ from the requested value, as ISP | ||
6309 | + * revision 15.0 represent the divisor by 65536 divided by an integer. | ||
6310 | + */ | ||
6311 | +static void ccp2_vp_config(struct isp_ccp2_device *ccp2, | ||
6312 | + unsigned int vpclk_div) | ||
6313 | +{ | ||
6314 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6315 | + u32 val; | ||
6316 | + | ||
6317 | + /* ISPCCP2_CTRL Video port */ | ||
6318 | + val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); | ||
6319 | + val |= ISPCCP2_CTRL_VP_ONLY_EN; /* Disable the memory write port */ | ||
6320 | + | ||
6321 | + if (isp->revision == ISP_REVISION_15_0) { | ||
6322 | + vpclk_div = clamp_t(unsigned int, vpclk_div, 1, 65536); | ||
6323 | + vpclk_div = min(ISPCCP2_VPCLK_FRACDIV / vpclk_div, 65535U); | ||
6324 | + BIT_SET(val, ISPCCP2_CTRL_VPCLK_DIV_SHIFT, | ||
6325 | + ISPCCP2_CTRL_VPCLK_DIV_MASK, vpclk_div); | ||
6326 | + } else { | ||
6327 | + vpclk_div = clamp_t(unsigned int, vpclk_div, 1, 4); | ||
6328 | + BIT_SET(val, ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT, | ||
6329 | + ISPCCP2_CTRL_VP_OUT_CTRL_MASK, vpclk_div - 1); | ||
6330 | + } | ||
6331 | + | ||
6332 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); | ||
6333 | +} | ||
6334 | + | ||
6335 | +/* | ||
6336 | + * ccp2_lcx_config - Initialize CCP2 logical channel interface. | ||
6337 | + * @ccp2: Pointer to ISP CCP2 device | ||
6338 | + * @config: Pointer to ISP LCx config structure. | ||
6339 | + * | ||
6340 | + * This will analyze the parameters passed by the interface config | ||
6341 | + * and configure CSI1/CCP2 logical channel | ||
6342 | + * | ||
6343 | + */ | ||
6344 | +static void ccp2_lcx_config(struct isp_ccp2_device *ccp2, | ||
6345 | + struct isp_interface_lcx_config *config) | ||
6346 | +{ | ||
6347 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6348 | + u32 val, format; | ||
6349 | + | ||
6350 | + switch (config->format) { | ||
6351 | + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: | ||
6352 | + format = ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP; | ||
6353 | + break; | ||
6354 | + case V4L2_MBUS_FMT_SGRBG10_1X10: | ||
6355 | + default: | ||
6356 | + format = ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP; /* RAW10+VP */ | ||
6357 | + break; | ||
6358 | + } | ||
6359 | + /* ISPCCP2_LCx_CTRL logical channel #0 */ | ||
6360 | + val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(0)) | ||
6361 | + | (ISPCCP2_LCx_CTRL_REGION_EN); /* Region */ | ||
6362 | + | ||
6363 | + if (isp->revision == ISP_REVISION_15_0) { | ||
6364 | + /* CRC */ | ||
6365 | + BIT_SET(val, ISPCCP2_LCx_CTRL_CRC_SHIFT_15_0, | ||
6366 | + ISPCCP2_LCx_CTRL_CRC_MASK, | ||
6367 | + config->crc); | ||
6368 | + /* Format = RAW10+VP or RAW8+DPCM10+VP*/ | ||
6369 | + BIT_SET(val, ISPCCP2_LCx_CTRL_FORMAT_SHIFT_15_0, | ||
6370 | + ISPCCP2_LCx_CTRL_FORMAT_MASK_15_0, format); | ||
6371 | + } else { | ||
6372 | + BIT_SET(val, ISPCCP2_LCx_CTRL_CRC_SHIFT, | ||
6373 | + ISPCCP2_LCx_CTRL_CRC_MASK, | ||
6374 | + config->crc); | ||
6375 | + | ||
6376 | + BIT_SET(val, ISPCCP2_LCx_CTRL_FORMAT_SHIFT, | ||
6377 | + ISPCCP2_LCx_CTRL_FORMAT_MASK, format); | ||
6378 | + } | ||
6379 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(0)); | ||
6380 | + | ||
6381 | + /* ISPCCP2_DAT_START for logical channel #0 */ | ||
6382 | + isp_reg_writel(isp, config->data_start << ISPCCP2_LCx_DAT_SHIFT, | ||
6383 | + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_DAT_START(0)); | ||
6384 | + | ||
6385 | + /* ISPCCP2_DAT_SIZE for logical channel #0 */ | ||
6386 | + isp_reg_writel(isp, config->data_size << ISPCCP2_LCx_DAT_SHIFT, | ||
6387 | + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_DAT_SIZE(0)); | ||
6388 | + | ||
6389 | + /* Enable error IRQs for logical channel #0 */ | ||
6390 | + val = ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ | | ||
6391 | + ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ | | ||
6392 | + ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ | | ||
6393 | + ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ | | ||
6394 | + ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ | | ||
6395 | + ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ | | ||
6396 | + ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ; | ||
6397 | + | ||
6398 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LC01_IRQSTATUS); | ||
6399 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LC01_IRQENABLE, val); | ||
6400 | +} | ||
6401 | + | ||
6402 | +/* | ||
6403 | + * ccp2_if_configure - Configure ccp2 with data from sensor | ||
6404 | + * @ccp2: Pointer to ISP CCP2 device | ||
6405 | + * | ||
6406 | + * Return 0 on success or a negative error code | ||
6407 | + */ | ||
6408 | +static int ccp2_if_configure(struct isp_ccp2_device *ccp2) | ||
6409 | +{ | ||
6410 | + const struct isp_v4l2_subdevs_group *pdata; | ||
6411 | + struct v4l2_mbus_framefmt *format; | ||
6412 | + struct media_pad *pad; | ||
6413 | + struct v4l2_subdev *sensor; | ||
6414 | + u32 lines = 0; | ||
6415 | + int ret; | ||
6416 | + | ||
6417 | + ccp2_pwr_cfg(ccp2); | ||
6418 | + | ||
6419 | + pad = media_entity_remote_source(&ccp2->pads[CCP2_PAD_SINK]); | ||
6420 | + sensor = media_entity_to_v4l2_subdev(pad->entity); | ||
6421 | + pdata = sensor->host_priv; | ||
6422 | + | ||
6423 | + ret = ccp2_phyif_config(ccp2, &pdata->bus.ccp2); | ||
6424 | + if (ret < 0) | ||
6425 | + return ret; | ||
6426 | + | ||
6427 | + ccp2_vp_config(ccp2, pdata->bus.ccp2.vpclk_div + 1); | ||
6428 | + | ||
6429 | + v4l2_subdev_call(sensor, sensor, g_skip_top_lines, &lines); | ||
6430 | + | ||
6431 | + format = &ccp2->formats[CCP2_PAD_SINK]; | ||
6432 | + | ||
6433 | + ccp2->if_cfg.data_start = lines; | ||
6434 | + ccp2->if_cfg.crc = pdata->bus.ccp2.crc; | ||
6435 | + ccp2->if_cfg.format = format->code; | ||
6436 | + ccp2->if_cfg.data_size = format->height; | ||
6437 | + | ||
6438 | + ccp2_lcx_config(ccp2, &ccp2->if_cfg); | ||
6439 | + | ||
6440 | + return 0; | ||
6441 | +} | ||
6442 | + | ||
6443 | +static int ccp2_adjust_bandwidth(struct isp_ccp2_device *ccp2) | ||
6444 | +{ | ||
6445 | + struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); | ||
6446 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6447 | + const struct v4l2_mbus_framefmt *ofmt = &ccp2->formats[CCP2_PAD_SOURCE]; | ||
6448 | + unsigned long l3_ick = pipe->l3_ick; | ||
6449 | + struct v4l2_fract *timeperframe; | ||
6450 | + unsigned int vpclk_div = 2; | ||
6451 | + unsigned int value; | ||
6452 | + u64 bound; | ||
6453 | + u64 area; | ||
6454 | + | ||
6455 | + /* Compute the minimum clock divisor, based on the pipeline maximum | ||
6456 | + * data rate. This is an absolute lower bound if we don't want SBL | ||
6457 | + * overflows, so round the value up. | ||
6458 | + */ | ||
6459 | + vpclk_div = max_t(unsigned int, DIV_ROUND_UP(l3_ick, pipe->max_rate), | ||
6460 | + vpclk_div); | ||
6461 | + | ||
6462 | + /* Compute the maximum clock divisor, based on the requested frame rate. | ||
6463 | + * This is a soft lower bound to achieve a frame rate equal or higher | ||
6464 | + * than the requested value, so round the value down. | ||
6465 | + */ | ||
6466 | + timeperframe = &pipe->max_timeperframe; | ||
6467 | + | ||
6468 | + if (timeperframe->numerator) { | ||
6469 | + area = ofmt->width * ofmt->height; | ||
6470 | + bound = div_u64(area * timeperframe->denominator, | ||
6471 | + timeperframe->numerator); | ||
6472 | + value = min_t(u64, bound, l3_ick); | ||
6473 | + vpclk_div = max_t(unsigned int, l3_ick / value, vpclk_div); | ||
6474 | + } | ||
6475 | + | ||
6476 | + dev_dbg(isp->dev, "%s: minimum clock divisor = %u\n", __func__, | ||
6477 | + vpclk_div); | ||
6478 | + | ||
6479 | + return vpclk_div; | ||
6480 | +} | ||
6481 | + | ||
6482 | +/* | ||
6483 | + * ccp2_mem_configure - Initialize CCP2 memory input/output interface | ||
6484 | + * @ccp2: Pointer to ISP CCP2 device | ||
6485 | + * @config: Pointer to ISP mem interface config structure | ||
6486 | + * | ||
6487 | + * This will analyze the parameters passed by the interface config | ||
6488 | + * structure, and configure the respective registers for proper | ||
6489 | + * CSI1/CCP2 memory input. | ||
6490 | + */ | ||
6491 | +static void ccp2_mem_configure(struct isp_ccp2_device *ccp2, | ||
6492 | + struct isp_interface_mem_config *config) | ||
6493 | +{ | ||
6494 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6495 | + u32 sink_pixcode = ccp2->formats[CCP2_PAD_SINK].code; | ||
6496 | + u32 source_pixcode = ccp2->formats[CCP2_PAD_SOURCE].code; | ||
6497 | + unsigned int dpcm_decompress = 0; | ||
6498 | + u32 val, hwords; | ||
6499 | + | ||
6500 | + if (sink_pixcode != source_pixcode && | ||
6501 | + sink_pixcode == V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) | ||
6502 | + dpcm_decompress = 1; | ||
6503 | + | ||
6504 | + ccp2_pwr_cfg(ccp2); | ||
6505 | + | ||
6506 | + /* Hsize, Skip */ | ||
6507 | + isp_reg_writel(isp, ISPCCP2_LCM_HSIZE_SKIP_MIN | | ||
6508 | + (config->hsize_count << ISPCCP2_LCM_HSIZE_SHIFT), | ||
6509 | + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_HSIZE); | ||
6510 | + | ||
6511 | + /* Vsize, no. of lines */ | ||
6512 | + isp_reg_writel(isp, config->vsize_count << ISPCCP2_LCM_VSIZE_SHIFT, | ||
6513 | + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_VSIZE); | ||
6514 | + | ||
6515 | + if (ccp2->video_in.bpl_padding == 0) | ||
6516 | + config->src_ofst = 0; | ||
6517 | + else | ||
6518 | + config->src_ofst = ccp2->video_in.bpl_value; | ||
6519 | + | ||
6520 | + isp_reg_writel(isp, config->src_ofst, OMAP3_ISP_IOMEM_CCP2, | ||
6521 | + ISPCCP2_LCM_SRC_OFST); | ||
6522 | + | ||
6523 | + /* Source and Destination formats */ | ||
6524 | + val = ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 << | ||
6525 | + ISPCCP2_LCM_CTRL_DST_FORMAT_SHIFT; | ||
6526 | + | ||
6527 | + if (dpcm_decompress) { | ||
6528 | + /* source format is RAW8 */ | ||
6529 | + val |= ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 << | ||
6530 | + ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT; | ||
6531 | + | ||
6532 | + /* RAW8 + DPCM10 - simple predictor */ | ||
6533 | + val |= ISPCCP2_LCM_CTRL_SRC_DPCM_PRED; | ||
6534 | + | ||
6535 | + /* enable source DPCM decompression */ | ||
6536 | + val |= ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 << | ||
6537 | + ISPCCP2_LCM_CTRL_SRC_DECOMPR_SHIFT; | ||
6538 | + } else { | ||
6539 | + /* source format is RAW10 */ | ||
6540 | + val |= ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 << | ||
6541 | + ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT; | ||
6542 | + } | ||
6543 | + | ||
6544 | + /* Burst size to 32x64 */ | ||
6545 | + val |= ISPCCP2_LCM_CTRL_BURST_SIZE_32X << | ||
6546 | + ISPCCP2_LCM_CTRL_BURST_SIZE_SHIFT; | ||
6547 | + | ||
6548 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_CTRL); | ||
6549 | + | ||
6550 | + /* Prefetch setup */ | ||
6551 | + if (dpcm_decompress) | ||
6552 | + hwords = (ISPCCP2_LCM_HSIZE_SKIP_MIN + | ||
6553 | + config->hsize_count) >> 3; | ||
6554 | + else | ||
6555 | + hwords = (ISPCCP2_LCM_HSIZE_SKIP_MIN + | ||
6556 | + config->hsize_count) >> 2; | ||
6557 | + | ||
6558 | + isp_reg_writel(isp, hwords << ISPCCP2_LCM_PREFETCH_SHIFT, | ||
6559 | + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_PREFETCH); | ||
6560 | + | ||
6561 | + /* Video port */ | ||
6562 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL, | ||
6563 | + ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE); | ||
6564 | + ccp2_vp_config(ccp2, ccp2_adjust_bandwidth(ccp2)); | ||
6565 | + | ||
6566 | + /* Clear LCM interrupts */ | ||
6567 | + isp_reg_writel(isp, ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ | | ||
6568 | + ISPCCP2_LCM_IRQSTATUS_EOF_IRQ, | ||
6569 | + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQSTATUS); | ||
6570 | + | ||
6571 | + /* Enable LCM interupts */ | ||
6572 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQENABLE, | ||
6573 | + ISPCCP2_LCM_IRQSTATUS_EOF_IRQ | | ||
6574 | + ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ); | ||
6575 | +} | ||
6576 | + | ||
6577 | +/* | ||
6578 | + * ccp2_set_inaddr - Sets memory address of input frame. | ||
6579 | + * @ccp2: Pointer to ISP CCP2 device | ||
6580 | + * @addr: 32bit memory address aligned on 32byte boundary. | ||
6581 | + * | ||
6582 | + * Configures the memory address from which the input frame is to be read. | ||
6583 | + */ | ||
6584 | +static void ccp2_set_inaddr(struct isp_ccp2_device *ccp2, u32 addr) | ||
6585 | +{ | ||
6586 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6587 | + | ||
6588 | + isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_SRC_ADDR); | ||
6589 | +} | ||
6590 | + | ||
6591 | +/* ----------------------------------------------------------------------------- | ||
6592 | + * Interrupt handling | ||
6593 | + */ | ||
6594 | + | ||
6595 | +static void ccp2_isr_buffer(struct isp_ccp2_device *ccp2) | ||
6596 | +{ | ||
6597 | + struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); | ||
6598 | + struct isp_buffer *buffer; | ||
6599 | + | ||
6600 | + buffer = omap3isp_video_buffer_next(&ccp2->video_in, ccp2->error); | ||
6601 | + if (buffer != NULL) | ||
6602 | + ccp2_set_inaddr(ccp2, buffer->isp_addr); | ||
6603 | + | ||
6604 | + pipe->state |= ISP_PIPELINE_IDLE_INPUT; | ||
6605 | + | ||
6606 | + if (ccp2->state == ISP_PIPELINE_STREAM_SINGLESHOT) { | ||
6607 | + if (isp_pipeline_ready(pipe)) | ||
6608 | + omap3isp_pipeline_set_stream(pipe, | ||
6609 | + ISP_PIPELINE_STREAM_SINGLESHOT); | ||
6610 | + } | ||
6611 | + | ||
6612 | + ccp2->error = 0; | ||
6613 | +} | ||
6614 | + | ||
6615 | +/* | ||
6616 | + * omap3isp_ccp2_isr - Handle ISP CCP2 interrupts | ||
6617 | + * @ccp2: Pointer to ISP CCP2 device | ||
6618 | + * | ||
6619 | + * This will handle the CCP2 interrupts | ||
6620 | + * | ||
6621 | + * Returns -EIO in case of error, or 0 on success. | ||
6622 | + */ | ||
6623 | +int omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2) | ||
6624 | +{ | ||
6625 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6626 | + int ret = 0; | ||
6627 | + static const u32 ISPCCP2_LC01_ERROR = | ||
6628 | + ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ | | ||
6629 | + ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ | | ||
6630 | + ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ | | ||
6631 | + ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ | | ||
6632 | + ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ | | ||
6633 | + ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ; | ||
6634 | + u32 lcx_irqstatus, lcm_irqstatus; | ||
6635 | + | ||
6636 | + /* First clear the interrupts */ | ||
6637 | + lcx_irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, | ||
6638 | + ISPCCP2_LC01_IRQSTATUS); | ||
6639 | + isp_reg_writel(isp, lcx_irqstatus, OMAP3_ISP_IOMEM_CCP2, | ||
6640 | + ISPCCP2_LC01_IRQSTATUS); | ||
6641 | + | ||
6642 | + lcm_irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, | ||
6643 | + ISPCCP2_LCM_IRQSTATUS); | ||
6644 | + isp_reg_writel(isp, lcm_irqstatus, OMAP3_ISP_IOMEM_CCP2, | ||
6645 | + ISPCCP2_LCM_IRQSTATUS); | ||
6646 | + /* Errors */ | ||
6647 | + if (lcx_irqstatus & ISPCCP2_LC01_ERROR) { | ||
6648 | + ccp2->error = 1; | ||
6649 | + dev_dbg(isp->dev, "CCP2 err:%x\n", lcx_irqstatus); | ||
6650 | + return -EIO; | ||
6651 | + } | ||
6652 | + | ||
6653 | + if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ) { | ||
6654 | + ccp2->error = 1; | ||
6655 | + dev_dbg(isp->dev, "CCP2 OCP err:%x\n", lcm_irqstatus); | ||
6656 | + ret = -EIO; | ||
6657 | + } | ||
6658 | + | ||
6659 | + if (omap3isp_module_sync_is_stopping(&ccp2->wait, &ccp2->stopping)) | ||
6660 | + return 0; | ||
6661 | + | ||
6662 | + /* Frame number propagation */ | ||
6663 | + if (lcx_irqstatus & ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ) { | ||
6664 | + struct isp_pipeline *pipe = | ||
6665 | + to_isp_pipeline(&ccp2->subdev.entity); | ||
6666 | + if (pipe->do_propagation) | ||
6667 | + atomic_inc(&pipe->frame_number); | ||
6668 | + } | ||
6669 | + | ||
6670 | + /* Handle queued buffers on frame end interrupts */ | ||
6671 | + if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_EOF_IRQ) | ||
6672 | + ccp2_isr_buffer(ccp2); | ||
6673 | + | ||
6674 | + return ret; | ||
6675 | +} | ||
6676 | + | ||
6677 | +/* ----------------------------------------------------------------------------- | ||
6678 | + * V4L2 subdev operations | ||
6679 | + */ | ||
6680 | + | ||
6681 | +static const unsigned int ccp2_fmts[] = { | ||
6682 | + V4L2_MBUS_FMT_SGRBG10_1X10, | ||
6683 | + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, | ||
6684 | +}; | ||
6685 | + | ||
6686 | +/* | ||
6687 | + * __ccp2_get_format - helper function for getting ccp2 format | ||
6688 | + * @ccp2 : Pointer to ISP CCP2 device | ||
6689 | + * @fh : V4L2 subdev file handle | ||
6690 | + * @pad : pad number | ||
6691 | + * @which : wanted subdev format | ||
6692 | + * return format structure or NULL on error | ||
6693 | + */ | ||
6694 | +static struct v4l2_mbus_framefmt * | ||
6695 | +__ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_fh *fh, | ||
6696 | + unsigned int pad, enum v4l2_subdev_format_whence which) | ||
6697 | +{ | ||
6698 | + if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
6699 | + return v4l2_subdev_get_try_format(fh, pad); | ||
6700 | + else | ||
6701 | + return &ccp2->formats[pad]; | ||
6702 | +} | ||
6703 | + | ||
6704 | +/* | ||
6705 | + * ccp2_try_format - Handle try format by pad subdev method | ||
6706 | + * @ccp2 : Pointer to ISP CCP2 device | ||
6707 | + * @fh : V4L2 subdev file handle | ||
6708 | + * @pad : pad num | ||
6709 | + * @fmt : pointer to v4l2 mbus format structure | ||
6710 | + * @which : wanted subdev format | ||
6711 | + */ | ||
6712 | +static void ccp2_try_format(struct isp_ccp2_device *ccp2, | ||
6713 | + struct v4l2_subdev_fh *fh, unsigned int pad, | ||
6714 | + struct v4l2_mbus_framefmt *fmt, | ||
6715 | + enum v4l2_subdev_format_whence which) | ||
6716 | +{ | ||
6717 | + struct v4l2_mbus_framefmt *format; | ||
6718 | + | ||
6719 | + switch (pad) { | ||
6720 | + case CCP2_PAD_SINK: | ||
6721 | + if (fmt->code != V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) | ||
6722 | + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
6723 | + | ||
6724 | + if (ccp2->input == CCP2_INPUT_SENSOR) { | ||
6725 | + fmt->width = clamp_t(u32, fmt->width, | ||
6726 | + ISPCCP2_DAT_START_MIN, | ||
6727 | + ISPCCP2_DAT_START_MAX); | ||
6728 | + fmt->height = clamp_t(u32, fmt->height, | ||
6729 | + ISPCCP2_DAT_SIZE_MIN, | ||
6730 | + ISPCCP2_DAT_SIZE_MAX); | ||
6731 | + } else if (ccp2->input == CCP2_INPUT_MEMORY) { | ||
6732 | + fmt->width = clamp_t(u32, fmt->width, | ||
6733 | + ISPCCP2_LCM_HSIZE_COUNT_MIN, | ||
6734 | + ISPCCP2_LCM_HSIZE_COUNT_MAX); | ||
6735 | + fmt->height = clamp_t(u32, fmt->height, | ||
6736 | + ISPCCP2_LCM_VSIZE_MIN, | ||
6737 | + ISPCCP2_LCM_VSIZE_MAX); | ||
6738 | + } | ||
6739 | + break; | ||
6740 | + | ||
6741 | + case CCP2_PAD_SOURCE: | ||
6742 | + /* Source format - copy sink format and change pixel code | ||
6743 | + * to SGRBG10_1X10 as we don't support CCP2 write to memory. | ||
6744 | + * When CCP2 write to memory feature will be added this | ||
6745 | + * should be changed properly. | ||
6746 | + */ | ||
6747 | + format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, which); | ||
6748 | + memcpy(fmt, format, sizeof(*fmt)); | ||
6749 | + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
6750 | + break; | ||
6751 | + } | ||
6752 | + | ||
6753 | + fmt->field = V4L2_FIELD_NONE; | ||
6754 | + fmt->colorspace = V4L2_COLORSPACE_SRGB; | ||
6755 | +} | ||
6756 | + | ||
6757 | +/* | ||
6758 | + * ccp2_enum_mbus_code - Handle pixel format enumeration | ||
6759 | + * @sd : pointer to v4l2 subdev structure | ||
6760 | + * @fh : V4L2 subdev file handle | ||
6761 | + * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
6762 | + * return -EINVAL or zero on success | ||
6763 | + */ | ||
6764 | +static int ccp2_enum_mbus_code(struct v4l2_subdev *sd, | ||
6765 | + struct v4l2_subdev_fh *fh, | ||
6766 | + struct v4l2_subdev_mbus_code_enum *code) | ||
6767 | +{ | ||
6768 | + struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); | ||
6769 | + struct v4l2_mbus_framefmt *format; | ||
6770 | + | ||
6771 | + if (code->pad == CCP2_PAD_SINK) { | ||
6772 | + if (code->index >= ARRAY_SIZE(ccp2_fmts)) | ||
6773 | + return -EINVAL; | ||
6774 | + | ||
6775 | + code->code = ccp2_fmts[code->index]; | ||
6776 | + } else { | ||
6777 | + if (code->index != 0) | ||
6778 | + return -EINVAL; | ||
6779 | + | ||
6780 | + format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, | ||
6781 | + V4L2_SUBDEV_FORMAT_TRY); | ||
6782 | + code->code = format->code; | ||
6783 | + } | ||
6784 | + | ||
6785 | + return 0; | ||
6786 | +} | ||
6787 | + | ||
6788 | +static int ccp2_enum_frame_size(struct v4l2_subdev *sd, | ||
6789 | + struct v4l2_subdev_fh *fh, | ||
6790 | + struct v4l2_subdev_frame_size_enum *fse) | ||
6791 | +{ | ||
6792 | + struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); | ||
6793 | + struct v4l2_mbus_framefmt format; | ||
6794 | + | ||
6795 | + if (fse->index != 0) | ||
6796 | + return -EINVAL; | ||
6797 | + | ||
6798 | + format.code = fse->code; | ||
6799 | + format.width = 1; | ||
6800 | + format.height = 1; | ||
6801 | + ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
6802 | + fse->min_width = format.width; | ||
6803 | + fse->min_height = format.height; | ||
6804 | + | ||
6805 | + if (format.code != fse->code) | ||
6806 | + return -EINVAL; | ||
6807 | + | ||
6808 | + format.code = fse->code; | ||
6809 | + format.width = -1; | ||
6810 | + format.height = -1; | ||
6811 | + ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
6812 | + fse->max_width = format.width; | ||
6813 | + fse->max_height = format.height; | ||
6814 | + | ||
6815 | + return 0; | ||
6816 | +} | ||
6817 | + | ||
6818 | +/* | ||
6819 | + * ccp2_get_format - Handle get format by pads subdev method | ||
6820 | + * @sd : pointer to v4l2 subdev structure | ||
6821 | + * @fh : V4L2 subdev file handle | ||
6822 | + * @fmt : pointer to v4l2 subdev format structure | ||
6823 | + * return -EINVAL or zero on sucess | ||
6824 | + */ | ||
6825 | +static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
6826 | + struct v4l2_subdev_format *fmt) | ||
6827 | +{ | ||
6828 | + struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); | ||
6829 | + struct v4l2_mbus_framefmt *format; | ||
6830 | + | ||
6831 | + format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which); | ||
6832 | + if (format == NULL) | ||
6833 | + return -EINVAL; | ||
6834 | + | ||
6835 | + fmt->format = *format; | ||
6836 | + return 0; | ||
6837 | +} | ||
6838 | + | ||
6839 | +/* | ||
6840 | + * ccp2_set_format - Handle set format by pads subdev method | ||
6841 | + * @sd : pointer to v4l2 subdev structure | ||
6842 | + * @fh : V4L2 subdev file handle | ||
6843 | + * @fmt : pointer to v4l2 subdev format structure | ||
6844 | + * returns zero | ||
6845 | + */ | ||
6846 | +static int ccp2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
6847 | + struct v4l2_subdev_format *fmt) | ||
6848 | +{ | ||
6849 | + struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); | ||
6850 | + struct v4l2_mbus_framefmt *format; | ||
6851 | + | ||
6852 | + format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which); | ||
6853 | + if (format == NULL) | ||
6854 | + return -EINVAL; | ||
6855 | + | ||
6856 | + ccp2_try_format(ccp2, fh, fmt->pad, &fmt->format, fmt->which); | ||
6857 | + *format = fmt->format; | ||
6858 | + | ||
6859 | + /* Propagate the format from sink to source */ | ||
6860 | + if (fmt->pad == CCP2_PAD_SINK) { | ||
6861 | + format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SOURCE, | ||
6862 | + fmt->which); | ||
6863 | + *format = fmt->format; | ||
6864 | + ccp2_try_format(ccp2, fh, CCP2_PAD_SOURCE, format, fmt->which); | ||
6865 | + } | ||
6866 | + | ||
6867 | + return 0; | ||
6868 | +} | ||
6869 | + | ||
6870 | +/* | ||
6871 | + * ccp2_init_formats - Initialize formats on all pads | ||
6872 | + * @sd: ISP CCP2 V4L2 subdevice | ||
6873 | + * @fh: V4L2 subdev file handle | ||
6874 | + * | ||
6875 | + * Initialize all pad formats with default values. If fh is not NULL, try | ||
6876 | + * formats are initialized on the file handle. Otherwise active formats are | ||
6877 | + * initialized on the device. | ||
6878 | + */ | ||
6879 | +static int ccp2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
6880 | +{ | ||
6881 | + struct v4l2_subdev_format format; | ||
6882 | + | ||
6883 | + memset(&format, 0, sizeof(format)); | ||
6884 | + format.pad = CCP2_PAD_SINK; | ||
6885 | + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
6886 | + format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
6887 | + format.format.width = 4096; | ||
6888 | + format.format.height = 4096; | ||
6889 | + ccp2_set_format(sd, fh, &format); | ||
6890 | + | ||
6891 | + return 0; | ||
6892 | +} | ||
6893 | + | ||
6894 | +/* | ||
6895 | + * ccp2_s_stream - Enable/Disable streaming on ccp2 subdev | ||
6896 | + * @sd : pointer to v4l2 subdev structure | ||
6897 | + * @enable: 1 == Enable, 0 == Disable | ||
6898 | + * return zero | ||
6899 | + */ | ||
6900 | +static int ccp2_s_stream(struct v4l2_subdev *sd, int enable) | ||
6901 | +{ | ||
6902 | + struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); | ||
6903 | + struct isp_device *isp = to_isp_device(ccp2); | ||
6904 | + struct device *dev = to_device(ccp2); | ||
6905 | + int ret; | ||
6906 | + | ||
6907 | + if (ccp2->state == ISP_PIPELINE_STREAM_STOPPED) { | ||
6908 | + if (enable == ISP_PIPELINE_STREAM_STOPPED) | ||
6909 | + return 0; | ||
6910 | + atomic_set(&ccp2->stopping, 0); | ||
6911 | + ccp2->error = 0; | ||
6912 | + } | ||
6913 | + | ||
6914 | + switch (enable) { | ||
6915 | + case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
6916 | + if (ccp2->phy) { | ||
6917 | + ret = omap3isp_csiphy_acquire(ccp2->phy); | ||
6918 | + if (ret < 0) | ||
6919 | + return ret; | ||
6920 | + } | ||
6921 | + | ||
6922 | + ccp2_if_configure(ccp2); | ||
6923 | + ccp2_print_status(ccp2); | ||
6924 | + | ||
6925 | + /* Enable CSI1/CCP2 interface */ | ||
6926 | + ccp2_if_enable(ccp2, 1); | ||
6927 | + break; | ||
6928 | + | ||
6929 | + case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
6930 | + if (ccp2->state != ISP_PIPELINE_STREAM_SINGLESHOT) { | ||
6931 | + struct v4l2_mbus_framefmt *format; | ||
6932 | + | ||
6933 | + format = &ccp2->formats[CCP2_PAD_SINK]; | ||
6934 | + | ||
6935 | + ccp2->mem_cfg.hsize_count = format->width; | ||
6936 | + ccp2->mem_cfg.vsize_count = format->height; | ||
6937 | + ccp2->mem_cfg.src_ofst = 0; | ||
6938 | + | ||
6939 | + ccp2_mem_configure(ccp2, &ccp2->mem_cfg); | ||
6940 | + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI1_READ); | ||
6941 | + ccp2_print_status(ccp2); | ||
6942 | + } | ||
6943 | + ccp2_mem_enable(ccp2, 1); | ||
6944 | + break; | ||
6945 | + | ||
6946 | + case ISP_PIPELINE_STREAM_STOPPED: | ||
6947 | + if (omap3isp_module_sync_idle(&sd->entity, &ccp2->wait, | ||
6948 | + &ccp2->stopping)) | ||
6949 | + dev_dbg(dev, "%s: module stop timeout.\n", sd->name); | ||
6950 | + if (ccp2->input == CCP2_INPUT_MEMORY) { | ||
6951 | + ccp2_mem_enable(ccp2, 0); | ||
6952 | + omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI1_READ); | ||
6953 | + } else if (ccp2->input == CCP2_INPUT_SENSOR) { | ||
6954 | + /* Disable CSI1/CCP2 interface */ | ||
6955 | + ccp2_if_enable(ccp2, 0); | ||
6956 | + if (ccp2->phy) | ||
6957 | + omap3isp_csiphy_release(ccp2->phy); | ||
6958 | + } | ||
6959 | + break; | ||
6960 | + } | ||
6961 | + | ||
6962 | + ccp2->state = enable; | ||
6963 | + return 0; | ||
6964 | +} | ||
6965 | + | ||
6966 | +/* subdev core operations */ | ||
6967 | +static const struct v4l2_subdev_core_ops ccp2_sd_core_ops = { | ||
6968 | + .queryctrl = v4l2_subdev_queryctrl, | ||
6969 | + .querymenu = v4l2_subdev_querymenu, | ||
6970 | + .g_ctrl = v4l2_subdev_g_ctrl, | ||
6971 | + .s_ctrl = v4l2_subdev_s_ctrl, | ||
6972 | + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, | ||
6973 | + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, | ||
6974 | + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, | ||
6975 | +}; | ||
6976 | + | ||
6977 | +/* subdev file operations */ | ||
6978 | +static const struct v4l2_subdev_file_ops ccp2_sd_file_ops = { | ||
6979 | + .open = ccp2_init_formats, | ||
6980 | +}; | ||
6981 | + | ||
6982 | +/* subdev video operations */ | ||
6983 | +static const struct v4l2_subdev_video_ops ccp2_sd_video_ops = { | ||
6984 | + .s_stream = ccp2_s_stream, | ||
6985 | +}; | ||
6986 | + | ||
6987 | +/* subdev pad operations */ | ||
6988 | +static const struct v4l2_subdev_pad_ops ccp2_sd_pad_ops = { | ||
6989 | + .enum_mbus_code = ccp2_enum_mbus_code, | ||
6990 | + .enum_frame_size = ccp2_enum_frame_size, | ||
6991 | + .get_fmt = ccp2_get_format, | ||
6992 | + .set_fmt = ccp2_set_format, | ||
6993 | +}; | ||
6994 | + | ||
6995 | +/* subdev operations */ | ||
6996 | +static const struct v4l2_subdev_ops ccp2_sd_ops = { | ||
6997 | + .core = &ccp2_sd_core_ops, | ||
6998 | + .file = &ccp2_sd_file_ops, | ||
6999 | + .video = &ccp2_sd_video_ops, | ||
7000 | + .pad = &ccp2_sd_pad_ops, | ||
7001 | +}; | ||
7002 | + | ||
7003 | +/* -------------------------------------------------------------------------- | ||
7004 | + * ISP ccp2 video device node | ||
7005 | + */ | ||
7006 | + | ||
7007 | +/* | ||
7008 | + * ccp2_video_queue - Queue video buffer. | ||
7009 | + * @video : Pointer to isp video structure | ||
7010 | + * @buffer: Pointer to isp_buffer structure | ||
7011 | + * return -EIO or zero on success | ||
7012 | + */ | ||
7013 | +static int ccp2_video_queue(struct isp_video *video, struct isp_buffer *buffer) | ||
7014 | +{ | ||
7015 | + struct isp_ccp2_device *ccp2 = &video->isp->isp_ccp2; | ||
7016 | + | ||
7017 | + ccp2_set_inaddr(ccp2, buffer->isp_addr); | ||
7018 | + return 0; | ||
7019 | +} | ||
7020 | + | ||
7021 | +static const struct isp_video_operations ccp2_video_ops = { | ||
7022 | + .queue = ccp2_video_queue, | ||
7023 | +}; | ||
7024 | + | ||
7025 | +/* ----------------------------------------------------------------------------- | ||
7026 | + * Media entity operations | ||
7027 | + */ | ||
7028 | + | ||
7029 | +/* | ||
7030 | + * ccp2_link_setup - Setup ccp2 connections. | ||
7031 | + * @entity : Pointer to media entity structure | ||
7032 | + * @local : Pointer to local pad array | ||
7033 | + * @remote : Pointer to remote pad array | ||
7034 | + * @flags : Link flags | ||
7035 | + * return -EINVAL on error or zero on success | ||
7036 | + */ | ||
7037 | +static int ccp2_link_setup(struct media_entity *entity, | ||
7038 | + const struct media_pad *local, | ||
7039 | + const struct media_pad *remote, u32 flags) | ||
7040 | +{ | ||
7041 | + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
7042 | + struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); | ||
7043 | + | ||
7044 | + switch (local->index | media_entity_type(remote->entity)) { | ||
7045 | + case CCP2_PAD_SINK | MEDIA_ENT_T_DEVNODE: | ||
7046 | + /* read from memory */ | ||
7047 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
7048 | + if (ccp2->input == CCP2_INPUT_SENSOR) | ||
7049 | + return -EBUSY; | ||
7050 | + ccp2->input = CCP2_INPUT_MEMORY; | ||
7051 | + } else { | ||
7052 | + if (ccp2->input == CCP2_INPUT_MEMORY) | ||
7053 | + ccp2->input = CCP2_INPUT_NONE; | ||
7054 | + } | ||
7055 | + break; | ||
7056 | + | ||
7057 | + case CCP2_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
7058 | + /* read from sensor/phy */ | ||
7059 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
7060 | + if (ccp2->input == CCP2_INPUT_MEMORY) | ||
7061 | + return -EBUSY; | ||
7062 | + ccp2->input = CCP2_INPUT_SENSOR; | ||
7063 | + } else { | ||
7064 | + if (ccp2->input == CCP2_INPUT_SENSOR) | ||
7065 | + ccp2->input = CCP2_INPUT_NONE; | ||
7066 | + } break; | ||
7067 | + | ||
7068 | + case CCP2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: | ||
7069 | + /* write to video port/ccdc */ | ||
7070 | + if (flags & MEDIA_LNK_FL_ENABLED) | ||
7071 | + ccp2->output = CCP2_OUTPUT_CCDC; | ||
7072 | + else | ||
7073 | + ccp2->output = CCP2_OUTPUT_NONE; | ||
7074 | + break; | ||
7075 | + | ||
7076 | + default: | ||
7077 | + return -EINVAL; | ||
7078 | + } | ||
7079 | + | ||
7080 | + return 0; | ||
7081 | +} | ||
7082 | + | ||
7083 | +/* media operations */ | ||
7084 | +static const struct media_entity_operations ccp2_media_ops = { | ||
7085 | + .link_setup = ccp2_link_setup, | ||
7086 | +}; | ||
7087 | + | ||
7088 | +/* | ||
7089 | + * ccp2_init_entities - Initialize ccp2 subdev and media entity. | ||
7090 | + * @ccp2: Pointer to ISP CCP2 device | ||
7091 | + * return negative error code or zero on success | ||
7092 | + */ | ||
7093 | +static int ccp2_init_entities(struct isp_ccp2_device *ccp2) | ||
7094 | +{ | ||
7095 | + struct v4l2_subdev *sd = &ccp2->subdev; | ||
7096 | + struct media_pad *pads = ccp2->pads; | ||
7097 | + struct media_entity *me = &sd->entity; | ||
7098 | + int ret; | ||
7099 | + | ||
7100 | + ccp2->input = CCP2_INPUT_NONE; | ||
7101 | + ccp2->output = CCP2_OUTPUT_NONE; | ||
7102 | + | ||
7103 | + v4l2_subdev_init(sd, &ccp2_sd_ops); | ||
7104 | + strlcpy(sd->name, "OMAP3 ISP CCP2", sizeof(sd->name)); | ||
7105 | + sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
7106 | + v4l2_set_subdevdata(sd, ccp2); | ||
7107 | + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
7108 | + | ||
7109 | + v4l2_ctrl_handler_init(&ccp2->ctrls, 1); | ||
7110 | + sd->ctrl_handler = &ccp2->ctrls; | ||
7111 | + | ||
7112 | + pads[CCP2_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; | ||
7113 | + pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_OUTPUT; | ||
7114 | + | ||
7115 | + me->ops = &ccp2_media_ops; | ||
7116 | + ret = media_entity_init(me, CCP2_PADS_NUM, pads, 0); | ||
7117 | + if (ret < 0) | ||
7118 | + return ret; | ||
7119 | + | ||
7120 | + ccp2_init_formats(sd, NULL); | ||
7121 | + | ||
7122 | + /* | ||
7123 | + * The CCP2 has weird line alignment requirements, possibly caused by | ||
7124 | + * DPCM8 decompression. Line length for data read from memory must be a | ||
7125 | + * multiple of 128 bits (16 bytes) in continuous mode (when no padding | ||
7126 | + * is present at end of lines). Additionally, if padding is used, the | ||
7127 | + * padded line length must be a multiple of 32 bytes. To simplify the | ||
7128 | + * implementation we use a fixed 32 bytes alignment regardless of the | ||
7129 | + * input format and width. If strict 128 bits alignment support is | ||
7130 | + * required ispvideo will need to be made aware of this special dual | ||
7131 | + * alignement requirements. | ||
7132 | + */ | ||
7133 | + ccp2->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
7134 | + ccp2->video_in.bpl_alignment = 32; | ||
7135 | + ccp2->video_in.bpl_max = 0xffffffe0; | ||
7136 | + ccp2->video_in.isp = to_isp_device(ccp2); | ||
7137 | + ccp2->video_in.ops = &ccp2_video_ops; | ||
7138 | + ccp2->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; | ||
7139 | + | ||
7140 | + ret = omap3isp_video_init(&ccp2->video_in, "CCP2"); | ||
7141 | + if (ret < 0) | ||
7142 | + return ret; | ||
7143 | + | ||
7144 | + /* Connect the video node to the ccp2 subdev. */ | ||
7145 | + ret = media_entity_create_link(&ccp2->video_in.video.entity, 0, | ||
7146 | + &ccp2->subdev.entity, CCP2_PAD_SINK, 0); | ||
7147 | + if (ret < 0) | ||
7148 | + return ret; | ||
7149 | + | ||
7150 | + return 0; | ||
7151 | +} | ||
7152 | + | ||
7153 | +/* | ||
7154 | + * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev | ||
7155 | + * @ccp2: Pointer to ISP CCP2 device | ||
7156 | + */ | ||
7157 | +void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2) | ||
7158 | +{ | ||
7159 | + media_entity_cleanup(&ccp2->subdev.entity); | ||
7160 | + | ||
7161 | + v4l2_device_unregister_subdev(&ccp2->subdev); | ||
7162 | + v4l2_ctrl_handler_free(&ccp2->ctrls); | ||
7163 | + omap3isp_video_unregister(&ccp2->video_in); | ||
7164 | +} | ||
7165 | + | ||
7166 | +/* | ||
7167 | + * omap3isp_ccp2_register_entities - Register the subdev media entity | ||
7168 | + * @ccp2: Pointer to ISP CCP2 device | ||
7169 | + * @vdev: Pointer to v4l device | ||
7170 | + * return negative error code or zero on success | ||
7171 | + */ | ||
7172 | + | ||
7173 | +int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2, | ||
7174 | + struct v4l2_device *vdev) | ||
7175 | +{ | ||
7176 | + int ret; | ||
7177 | + | ||
7178 | + /* Register the subdev and video nodes. */ | ||
7179 | + ret = v4l2_device_register_subdev(vdev, &ccp2->subdev); | ||
7180 | + if (ret < 0) | ||
7181 | + goto error; | ||
7182 | + | ||
7183 | + ret = omap3isp_video_register(&ccp2->video_in, vdev); | ||
7184 | + if (ret < 0) | ||
7185 | + goto error; | ||
7186 | + | ||
7187 | + return 0; | ||
7188 | + | ||
7189 | +error: | ||
7190 | + omap3isp_ccp2_unregister_entities(ccp2); | ||
7191 | + return ret; | ||
7192 | +} | ||
7193 | + | ||
7194 | +/* ----------------------------------------------------------------------------- | ||
7195 | + * ISP ccp2 initialisation and cleanup | ||
7196 | + */ | ||
7197 | + | ||
7198 | +/* | ||
7199 | + * omap3isp_ccp2_cleanup - CCP2 un-initialization | ||
7200 | + * @isp : Pointer to ISP device | ||
7201 | + */ | ||
7202 | +void omap3isp_ccp2_cleanup(struct isp_device *isp) | ||
7203 | +{ | ||
7204 | +} | ||
7205 | + | ||
7206 | +/* | ||
7207 | + * omap3isp_ccp2_init - CCP2 initialization. | ||
7208 | + * @isp : Pointer to ISP device | ||
7209 | + * return negative error code or zero on success | ||
7210 | + */ | ||
7211 | +int omap3isp_ccp2_init(struct isp_device *isp) | ||
7212 | +{ | ||
7213 | + struct isp_ccp2_device *ccp2 = &isp->isp_ccp2; | ||
7214 | + int ret; | ||
7215 | + | ||
7216 | + init_waitqueue_head(&ccp2->wait); | ||
7217 | + | ||
7218 | + /* On the OMAP36xx, the CCP2 uses the CSI PHY1 or PHY2, shared with | ||
7219 | + * the CSI2c or CSI2a receivers. The PHY then needs to be explicitly | ||
7220 | + * configured. | ||
7221 | + * | ||
7222 | + * TODO: Don't hardcode the usage of PHY1 (shared with CSI2c). | ||
7223 | + */ | ||
7224 | + if (isp->revision == ISP_REVISION_15_0) | ||
7225 | + ccp2->phy = &isp->isp_csiphy1; | ||
7226 | + | ||
7227 | + ret = ccp2_init_entities(ccp2); | ||
7228 | + if (ret < 0) | ||
7229 | + goto out; | ||
7230 | + | ||
7231 | + ccp2_reset(ccp2); | ||
7232 | +out: | ||
7233 | + if (ret) | ||
7234 | + omap3isp_ccp2_cleanup(isp); | ||
7235 | + | ||
7236 | + return ret; | ||
7237 | +} | ||
7238 | diff --git a/drivers/media/video/isp/ispccp2.h b/drivers/media/video/isp/ispccp2.h | ||
7239 | new file mode 100644 | ||
7240 | index 0000000..1c1504e | ||
7241 | --- /dev/null | ||
7242 | +++ b/drivers/media/video/isp/ispccp2.h | ||
7243 | @@ -0,0 +1,101 @@ | ||
7244 | +/* | ||
7245 | + * ispccp2.h | ||
7246 | + * | ||
7247 | + * TI OMAP3 ISP - CCP2 module | ||
7248 | + * | ||
7249 | + * Copyright (C) 2010 Nokia Corporation | ||
7250 | + * Copyright (C) 2010 Texas Instruments, Inc. | ||
7251 | + * | ||
7252 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
7253 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
7254 | + * | ||
7255 | + * This program is free software; you can redistribute it and/or modify | ||
7256 | + * it under the terms of the GNU General Public License version 2 as | ||
7257 | + * published by the Free Software Foundation. | ||
7258 | + * | ||
7259 | + * This program is distributed in the hope that it will be useful, but | ||
7260 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
7261 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
7262 | + * General Public License for more details. | ||
7263 | + * | ||
7264 | + * You should have received a copy of the GNU General Public License | ||
7265 | + * along with this program; if not, write to the Free Software | ||
7266 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
7267 | + * 02110-1301 USA | ||
7268 | + */ | ||
7269 | + | ||
7270 | +#ifndef OMAP3_ISP_CCP2_H | ||
7271 | +#define OMAP3_ISP_CCP2_H | ||
7272 | + | ||
7273 | +#include <linux/videodev2.h> | ||
7274 | +#include <media/v4l2-ctrls.h> | ||
7275 | + | ||
7276 | +struct isp_device; | ||
7277 | +struct isp_csiphy; | ||
7278 | + | ||
7279 | +/* Sink and source ccp2 pads */ | ||
7280 | +#define CCP2_PAD_SINK 0 | ||
7281 | +#define CCP2_PAD_SOURCE 1 | ||
7282 | +#define CCP2_PADS_NUM 2 | ||
7283 | + | ||
7284 | +/* CCP2 input media entity */ | ||
7285 | +enum ccp2_input_entity { | ||
7286 | + CCP2_INPUT_NONE, | ||
7287 | + CCP2_INPUT_SENSOR, | ||
7288 | + CCP2_INPUT_MEMORY, | ||
7289 | +}; | ||
7290 | + | ||
7291 | +/* CCP2 output media entity */ | ||
7292 | +enum ccp2_output_entity { | ||
7293 | + CCP2_OUTPUT_NONE, | ||
7294 | + CCP2_OUTPUT_CCDC, | ||
7295 | + CCP2_OUTPUT_MEMORY, | ||
7296 | +}; | ||
7297 | + | ||
7298 | + | ||
7299 | +/* Logical channel configuration */ | ||
7300 | +struct isp_interface_lcx_config { | ||
7301 | + int crc; | ||
7302 | + u32 data_start; | ||
7303 | + u32 data_size; | ||
7304 | + u32 format; | ||
7305 | +}; | ||
7306 | + | ||
7307 | +/* Memory channel configuration */ | ||
7308 | +struct isp_interface_mem_config { | ||
7309 | + u32 dst_port; | ||
7310 | + u32 vsize_count; | ||
7311 | + u32 hsize_count; | ||
7312 | + u32 src_ofst; | ||
7313 | + u32 dst_ofst; | ||
7314 | +}; | ||
7315 | + | ||
7316 | +/* CCP2 device */ | ||
7317 | +struct isp_ccp2_device { | ||
7318 | + struct v4l2_subdev subdev; | ||
7319 | + struct v4l2_mbus_framefmt formats[CCP2_PADS_NUM]; | ||
7320 | + struct media_pad pads[CCP2_PADS_NUM]; | ||
7321 | + | ||
7322 | + struct v4l2_ctrl_handler ctrls; | ||
7323 | + | ||
7324 | + enum ccp2_input_entity input; | ||
7325 | + enum ccp2_output_entity output; | ||
7326 | + struct isp_interface_lcx_config if_cfg; | ||
7327 | + struct isp_interface_mem_config mem_cfg; | ||
7328 | + struct isp_video video_in; | ||
7329 | + struct isp_csiphy *phy; | ||
7330 | + unsigned int error; | ||
7331 | + enum isp_pipeline_stream_state state; | ||
7332 | + wait_queue_head_t wait; | ||
7333 | + atomic_t stopping; | ||
7334 | +}; | ||
7335 | + | ||
7336 | +/* Function declarations */ | ||
7337 | +int omap3isp_ccp2_init(struct isp_device *isp); | ||
7338 | +void omap3isp_ccp2_cleanup(struct isp_device *isp); | ||
7339 | +int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2, | ||
7340 | + struct v4l2_device *vdev); | ||
7341 | +void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2); | ||
7342 | +int omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2); | ||
7343 | + | ||
7344 | +#endif /* OMAP3_ISP_CCP2_H */ | ||
7345 | diff --git a/drivers/media/video/isp/ispcsi2.c b/drivers/media/video/isp/ispcsi2.c | ||
7346 | new file mode 100644 | ||
7347 | index 0000000..30ced95 | ||
7348 | --- /dev/null | ||
7349 | +++ b/drivers/media/video/isp/ispcsi2.c | ||
7350 | @@ -0,0 +1,1332 @@ | ||
7351 | +/* | ||
7352 | + * ispcsi2.c | ||
7353 | + * | ||
7354 | + * TI OMAP3 ISP - CSI2 module | ||
7355 | + * | ||
7356 | + * Copyright (C) 2010 Nokia Corporation | ||
7357 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
7358 | + * | ||
7359 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
7360 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
7361 | + * | ||
7362 | + * This program is free software; you can redistribute it and/or modify | ||
7363 | + * it under the terms of the GNU General Public License version 2 as | ||
7364 | + * published by the Free Software Foundation. | ||
7365 | + * | ||
7366 | + * This program is distributed in the hope that it will be useful, but | ||
7367 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
7368 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
7369 | + * General Public License for more details. | ||
7370 | + * | ||
7371 | + * You should have received a copy of the GNU General Public License | ||
7372 | + * along with this program; if not, write to the Free Software | ||
7373 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
7374 | + * 02110-1301 USA | ||
7375 | + */ | ||
7376 | +#include <linux/delay.h> | ||
7377 | +#include <media/v4l2-common.h> | ||
7378 | +#include <linux/v4l2-mediabus.h> | ||
7379 | +#include <linux/mm.h> | ||
7380 | + | ||
7381 | +#include "isp.h" | ||
7382 | +#include "ispreg.h" | ||
7383 | +#include "ispcsi2.h" | ||
7384 | + | ||
7385 | +/* | ||
7386 | + * csi2_if_enable - Enable CSI2 Receiver interface. | ||
7387 | + * @enable: enable flag | ||
7388 | + * | ||
7389 | + */ | ||
7390 | +static void csi2_if_enable(struct isp_device *isp, | ||
7391 | + struct isp_csi2_device *csi2, u8 enable) | ||
7392 | +{ | ||
7393 | + struct isp_csi2_ctrl_cfg *currctrl = &csi2->ctrl; | ||
7394 | + | ||
7395 | + isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_CTRL, ISPCSI2_CTRL_IF_EN, | ||
7396 | + enable ? ISPCSI2_CTRL_IF_EN : 0); | ||
7397 | + | ||
7398 | + currctrl->if_enable = enable; | ||
7399 | +} | ||
7400 | + | ||
7401 | +/* | ||
7402 | + * csi2_recv_config - CSI2 receiver module configuration. | ||
7403 | + * @currctrl: isp_csi2_ctrl_cfg structure | ||
7404 | + * | ||
7405 | + */ | ||
7406 | +static void csi2_recv_config(struct isp_device *isp, | ||
7407 | + struct isp_csi2_device *csi2, | ||
7408 | + struct isp_csi2_ctrl_cfg *currctrl) | ||
7409 | +{ | ||
7410 | + u32 reg; | ||
7411 | + | ||
7412 | + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTRL); | ||
7413 | + | ||
7414 | + if (currctrl->frame_mode) | ||
7415 | + reg |= ISPCSI2_CTRL_FRAME; | ||
7416 | + else | ||
7417 | + reg &= ~ISPCSI2_CTRL_FRAME; | ||
7418 | + | ||
7419 | + if (currctrl->vp_clk_enable) | ||
7420 | + reg |= ISPCSI2_CTRL_VP_CLK_EN; | ||
7421 | + else | ||
7422 | + reg &= ~ISPCSI2_CTRL_VP_CLK_EN; | ||
7423 | + | ||
7424 | + if (currctrl->vp_only_enable) | ||
7425 | + reg |= ISPCSI2_CTRL_VP_ONLY_EN; | ||
7426 | + else | ||
7427 | + reg &= ~ISPCSI2_CTRL_VP_ONLY_EN; | ||
7428 | + | ||
7429 | + reg &= ~ISPCSI2_CTRL_VP_OUT_CTRL_MASK; | ||
7430 | + reg |= currctrl->vp_out_ctrl << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT; | ||
7431 | + | ||
7432 | + if (currctrl->ecc_enable) | ||
7433 | + reg |= ISPCSI2_CTRL_ECC_EN; | ||
7434 | + else | ||
7435 | + reg &= ~ISPCSI2_CTRL_ECC_EN; | ||
7436 | + | ||
7437 | + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTRL); | ||
7438 | +} | ||
7439 | + | ||
7440 | +static const unsigned int csi2_input_fmts[] = { | ||
7441 | + V4L2_MBUS_FMT_SGRBG10_1X10, | ||
7442 | + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, | ||
7443 | + V4L2_MBUS_FMT_SRGGB10_1X10, | ||
7444 | + V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, | ||
7445 | + V4L2_MBUS_FMT_SBGGR10_1X10, | ||
7446 | + V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, | ||
7447 | + V4L2_MBUS_FMT_SGBRG10_1X10, | ||
7448 | + V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, | ||
7449 | +}; | ||
7450 | + | ||
7451 | +/* To set the format on the CSI2 requires a mapping function that takes | ||
7452 | + * the following inputs: | ||
7453 | + * - 2 different formats (at this time) | ||
7454 | + * - 2 destinations (mem, vp+mem) (vp only handled separately) | ||
7455 | + * - 2 decompression options (on, off) | ||
7456 | + * - 2 isp revisions (certain format must be handled differently on OMAP3630) | ||
7457 | + * Output should be CSI2 frame format code | ||
7458 | + * Array indices as follows: [format][dest][decompr][is_3630] | ||
7459 | + * Not all combinations are valid. 0 means invalid. | ||
7460 | + */ | ||
7461 | +static const u16 __csi2_fmt_map[2][2][2][2] = { | ||
7462 | + /* RAW10 formats */ | ||
7463 | + { | ||
7464 | + /* Output to memory */ | ||
7465 | + { | ||
7466 | + /* No DPCM decompression */ | ||
7467 | + { CSI2_PIX_FMT_RAW10_EXP16, CSI2_PIX_FMT_RAW10_EXP16 }, | ||
7468 | + /* DPCM decompression */ | ||
7469 | + { 0, 0 }, | ||
7470 | + }, | ||
7471 | + /* Output to both */ | ||
7472 | + { | ||
7473 | + /* No DPCM decompression */ | ||
7474 | + { CSI2_PIX_FMT_RAW10_EXP16_VP, | ||
7475 | + CSI2_PIX_FMT_RAW10_EXP16_VP }, | ||
7476 | + /* DPCM decompression */ | ||
7477 | + { 0, 0 }, | ||
7478 | + }, | ||
7479 | + }, | ||
7480 | + /* RAW10 DPCM8 formats */ | ||
7481 | + { | ||
7482 | + /* Output to memory */ | ||
7483 | + { | ||
7484 | + /* No DPCM decompression */ | ||
7485 | + { CSI2_PIX_FMT_RAW8, CSI2_USERDEF_8BIT_DATA1 }, | ||
7486 | + /* DPCM decompression */ | ||
7487 | + { CSI2_PIX_FMT_RAW8_DPCM10_EXP16, | ||
7488 | + CSI2_USERDEF_8BIT_DATA1_DPCM10 }, | ||
7489 | + }, | ||
7490 | + /* Output to both */ | ||
7491 | + { | ||
7492 | + /* No DPCM decompression */ | ||
7493 | + { CSI2_PIX_FMT_RAW8_VP, | ||
7494 | + CSI2_PIX_FMT_RAW8_VP }, | ||
7495 | + /* DPCM decompression */ | ||
7496 | + { CSI2_PIX_FMT_RAW8_DPCM10_VP, | ||
7497 | + CSI2_USERDEF_8BIT_DATA1_DPCM10_VP }, | ||
7498 | + }, | ||
7499 | + }, | ||
7500 | +}; | ||
7501 | + | ||
7502 | +/* | ||
7503 | + * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID | ||
7504 | + * @csi2: ISP CSI2 device | ||
7505 | + * | ||
7506 | + * Returns CSI2 physical format id | ||
7507 | + */ | ||
7508 | +static u16 csi2_ctx_map_format(struct isp_csi2_device *csi2) | ||
7509 | +{ | ||
7510 | + const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK]; | ||
7511 | + int fmtidx, destidx, is_3630; | ||
7512 | + | ||
7513 | + switch (fmt->code) { | ||
7514 | + case V4L2_MBUS_FMT_SGRBG10_1X10: | ||
7515 | + case V4L2_MBUS_FMT_SRGGB10_1X10: | ||
7516 | + case V4L2_MBUS_FMT_SBGGR10_1X10: | ||
7517 | + case V4L2_MBUS_FMT_SGBRG10_1X10: | ||
7518 | + fmtidx = 0; | ||
7519 | + break; | ||
7520 | + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: | ||
7521 | + case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8: | ||
7522 | + case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8: | ||
7523 | + case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8: | ||
7524 | + fmtidx = 1; | ||
7525 | + break; | ||
7526 | + default: | ||
7527 | + WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n", | ||
7528 | + fmt->code); | ||
7529 | + return 0; | ||
7530 | + } | ||
7531 | + | ||
7532 | + if (!(csi2->output & CSI2_OUTPUT_CCDC) && | ||
7533 | + !(csi2->output & CSI2_OUTPUT_MEMORY)) { | ||
7534 | + /* Neither output enabled is a valid combination */ | ||
7535 | + return CSI2_PIX_FMT_OTHERS; | ||
7536 | + } | ||
7537 | + | ||
7538 | + /* If we need to skip frames at the beginning of the stream disable the | ||
7539 | + * video port to avoid sending the skipped frames to the CCDC. | ||
7540 | + */ | ||
7541 | + destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_CCDC); | ||
7542 | + is_3630 = csi2->isp->revision == ISP_REVISION_15_0; | ||
7543 | + | ||
7544 | + return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress][is_3630]; | ||
7545 | +} | ||
7546 | + | ||
7547 | +/* | ||
7548 | + * csi2_set_outaddr - Set memory address to save output image | ||
7549 | + * @csi2: Pointer to ISP CSI2a device. | ||
7550 | + * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary. | ||
7551 | + * | ||
7552 | + * Sets the memory address where the output will be saved. | ||
7553 | + * | ||
7554 | + * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte | ||
7555 | + * boundary. | ||
7556 | + */ | ||
7557 | +static void csi2_set_outaddr(struct isp_csi2_device *csi2, u32 addr) | ||
7558 | +{ | ||
7559 | + struct isp_device *isp = csi2->isp; | ||
7560 | + struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[0]; | ||
7561 | + | ||
7562 | + ctx->ping_addr = ctx->pong_addr = addr; | ||
7563 | + isp_reg_writel(isp, ctx->ping_addr, | ||
7564 | + csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum)); | ||
7565 | + isp_reg_writel(isp, ctx->pong_addr, | ||
7566 | + csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum)); | ||
7567 | +} | ||
7568 | + | ||
7569 | +/* | ||
7570 | + * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should | ||
7571 | + * be enabled by CSI2. | ||
7572 | + * @format_id: mapped format id | ||
7573 | + * | ||
7574 | + */ | ||
7575 | +static inline int is_usr_def_mapping(u32 format_id) | ||
7576 | +{ | ||
7577 | + return (format_id & 0x40) ? 1 : 0; | ||
7578 | +} | ||
7579 | + | ||
7580 | +/* | ||
7581 | + * csi2_ctx_enable - Enable specified CSI2 context | ||
7582 | + * @ctxnum: Context number, valid between 0 and 7 values. | ||
7583 | + * @enable: enable | ||
7584 | + * | ||
7585 | + */ | ||
7586 | +static void csi2_ctx_enable(struct isp_device *isp, | ||
7587 | + struct isp_csi2_device *csi2, u8 ctxnum, u8 enable) | ||
7588 | +{ | ||
7589 | + struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum]; | ||
7590 | + unsigned int skip = 0; | ||
7591 | + u32 reg; | ||
7592 | + | ||
7593 | + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum)); | ||
7594 | + | ||
7595 | + if (enable) { | ||
7596 | + if (csi2->frame_skip) | ||
7597 | + skip = csi2->frame_skip; | ||
7598 | + else if (csi2->output & CSI2_OUTPUT_MEMORY) | ||
7599 | + skip = 1; | ||
7600 | + | ||
7601 | + reg &= ~ISPCSI2_CTX_CTRL1_COUNT_MASK; | ||
7602 | + reg |= ISPCSI2_CTX_CTRL1_COUNT_UNLOCK | ||
7603 | + | (skip << ISPCSI2_CTX_CTRL1_COUNT_SHIFT) | ||
7604 | + | ISPCSI2_CTX_CTRL1_CTX_EN; | ||
7605 | + } else { | ||
7606 | + reg &= ~ISPCSI2_CTX_CTRL1_CTX_EN; | ||
7607 | + } | ||
7608 | + | ||
7609 | + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum)); | ||
7610 | + ctx->enabled = enable; | ||
7611 | +} | ||
7612 | + | ||
7613 | +/* | ||
7614 | + * csi2_ctx_config - CSI2 context configuration. | ||
7615 | + * @ctx: context configuration | ||
7616 | + * | ||
7617 | + */ | ||
7618 | +static void csi2_ctx_config(struct isp_device *isp, | ||
7619 | + struct isp_csi2_device *csi2, | ||
7620 | + struct isp_csi2_ctx_cfg *ctx) | ||
7621 | +{ | ||
7622 | + u32 reg; | ||
7623 | + | ||
7624 | + /* Set up CSI2_CTx_CTRL1 */ | ||
7625 | + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum)); | ||
7626 | + | ||
7627 | + if (ctx->eof_enabled) | ||
7628 | + reg |= ISPCSI2_CTX_CTRL1_EOF_EN; | ||
7629 | + else | ||
7630 | + reg &= ~ISPCSI2_CTX_CTRL1_EOF_EN; | ||
7631 | + | ||
7632 | + if (ctx->eol_enabled) | ||
7633 | + reg |= ISPCSI2_CTX_CTRL1_EOL_EN; | ||
7634 | + else | ||
7635 | + reg &= ~ISPCSI2_CTX_CTRL1_EOL_EN; | ||
7636 | + | ||
7637 | + if (ctx->checksum_enabled) | ||
7638 | + reg |= ISPCSI2_CTX_CTRL1_CS_EN; | ||
7639 | + else | ||
7640 | + reg &= ~ISPCSI2_CTX_CTRL1_CS_EN; | ||
7641 | + | ||
7642 | + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum)); | ||
7643 | + | ||
7644 | + /* Set up CSI2_CTx_CTRL2 */ | ||
7645 | + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum)); | ||
7646 | + | ||
7647 | + reg &= ~(ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK); | ||
7648 | + reg |= ctx->virtual_id << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT; | ||
7649 | + | ||
7650 | + reg &= ~(ISPCSI2_CTX_CTRL2_FORMAT_MASK); | ||
7651 | + reg |= ctx->format_id << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT; | ||
7652 | + | ||
7653 | + if (ctx->dpcm_decompress) { | ||
7654 | + if (ctx->dpcm_predictor) | ||
7655 | + reg |= ISPCSI2_CTX_CTRL2_DPCM_PRED; | ||
7656 | + else | ||
7657 | + reg &= ~ISPCSI2_CTX_CTRL2_DPCM_PRED; | ||
7658 | + } | ||
7659 | + | ||
7660 | + if (is_usr_def_mapping(ctx->format_id)) { | ||
7661 | + reg &= ~ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK; | ||
7662 | + reg |= 2 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT; | ||
7663 | + } | ||
7664 | + | ||
7665 | + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum)); | ||
7666 | + | ||
7667 | + /* Set up CSI2_CTx_CTRL3 */ | ||
7668 | + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum)); | ||
7669 | + reg &= ~(ISPCSI2_CTX_CTRL3_ALPHA_MASK); | ||
7670 | + reg |= (ctx->alpha << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT); | ||
7671 | + | ||
7672 | + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum)); | ||
7673 | + | ||
7674 | + /* Set up CSI2_CTx_DAT_OFST */ | ||
7675 | + reg = isp_reg_readl(isp, csi2->regs1, | ||
7676 | + ISPCSI2_CTX_DAT_OFST(ctx->ctxnum)); | ||
7677 | + reg &= ~ISPCSI2_CTX_DAT_OFST_OFST_MASK; | ||
7678 | + reg |= ctx->data_offset << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT; | ||
7679 | + isp_reg_writel(isp, reg, csi2->regs1, | ||
7680 | + ISPCSI2_CTX_DAT_OFST(ctx->ctxnum)); | ||
7681 | + | ||
7682 | + isp_reg_writel(isp, ctx->ping_addr, | ||
7683 | + csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum)); | ||
7684 | + | ||
7685 | + isp_reg_writel(isp, ctx->pong_addr, | ||
7686 | + csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum)); | ||
7687 | +} | ||
7688 | + | ||
7689 | +/* | ||
7690 | + * csi2_timing_config - CSI2 timing configuration. | ||
7691 | + * @timing: csi2_timing_cfg structure | ||
7692 | + */ | ||
7693 | +static void csi2_timing_config(struct isp_device *isp, | ||
7694 | + struct isp_csi2_device *csi2, | ||
7695 | + struct isp_csi2_timing_cfg *timing) | ||
7696 | +{ | ||
7697 | + u32 reg; | ||
7698 | + | ||
7699 | + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_TIMING); | ||
7700 | + | ||
7701 | + if (timing->force_rx_mode) | ||
7702 | + reg |= ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum); | ||
7703 | + else | ||
7704 | + reg &= ~ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum); | ||
7705 | + | ||
7706 | + if (timing->stop_state_16x) | ||
7707 | + reg |= ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum); | ||
7708 | + else | ||
7709 | + reg &= ~ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum); | ||
7710 | + | ||
7711 | + if (timing->stop_state_4x) | ||
7712 | + reg |= ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum); | ||
7713 | + else | ||
7714 | + reg &= ~ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum); | ||
7715 | + | ||
7716 | + reg &= ~ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(timing->ionum); | ||
7717 | + reg |= timing->stop_state_counter << | ||
7718 | + ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(timing->ionum); | ||
7719 | + | ||
7720 | + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_TIMING); | ||
7721 | +} | ||
7722 | + | ||
7723 | +/* | ||
7724 | + * csi2_irq_ctx_set - Enables CSI2 Context IRQs. | ||
7725 | + * @enable: Enable/disable CSI2 Context interrupts | ||
7726 | + */ | ||
7727 | +static void csi2_irq_ctx_set(struct isp_device *isp, | ||
7728 | + struct isp_csi2_device *csi2, int enable) | ||
7729 | +{ | ||
7730 | + u32 reg = ISPCSI2_CTX_IRQSTATUS_FE_IRQ; | ||
7731 | + int i; | ||
7732 | + | ||
7733 | + if (csi2->use_fs_irq) | ||
7734 | + reg |= ISPCSI2_CTX_IRQSTATUS_FS_IRQ; | ||
7735 | + | ||
7736 | + for (i = 0; i < 8; i++) { | ||
7737 | + isp_reg_writel(isp, reg, csi2->regs1, | ||
7738 | + ISPCSI2_CTX_IRQSTATUS(i)); | ||
7739 | + if (enable) | ||
7740 | + isp_reg_set(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), | ||
7741 | + reg); | ||
7742 | + else | ||
7743 | + isp_reg_clr(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), | ||
7744 | + reg); | ||
7745 | + } | ||
7746 | +} | ||
7747 | + | ||
7748 | +/* | ||
7749 | + * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs. | ||
7750 | + * @enable: Enable/disable CSI2 ComplexIO #1 interrupts | ||
7751 | + */ | ||
7752 | +static void csi2_irq_complexio1_set(struct isp_device *isp, | ||
7753 | + struct isp_csi2_device *csi2, int enable) | ||
7754 | +{ | ||
7755 | + u32 reg; | ||
7756 | + reg = ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT | | ||
7757 | + ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER | | ||
7758 | + ISPCSI2_PHY_IRQENABLE_STATEULPM5 | | ||
7759 | + ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 | | ||
7760 | + ISPCSI2_PHY_IRQENABLE_ERRESC5 | | ||
7761 | + ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 | | ||
7762 | + ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 | | ||
7763 | + ISPCSI2_PHY_IRQENABLE_STATEULPM4 | | ||
7764 | + ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 | | ||
7765 | + ISPCSI2_PHY_IRQENABLE_ERRESC4 | | ||
7766 | + ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 | | ||
7767 | + ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 | | ||
7768 | + ISPCSI2_PHY_IRQENABLE_STATEULPM3 | | ||
7769 | + ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 | | ||
7770 | + ISPCSI2_PHY_IRQENABLE_ERRESC3 | | ||
7771 | + ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 | | ||
7772 | + ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 | | ||
7773 | + ISPCSI2_PHY_IRQENABLE_STATEULPM2 | | ||
7774 | + ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 | | ||
7775 | + ISPCSI2_PHY_IRQENABLE_ERRESC2 | | ||
7776 | + ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 | | ||
7777 | + ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 | | ||
7778 | + ISPCSI2_PHY_IRQENABLE_STATEULPM1 | | ||
7779 | + ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 | | ||
7780 | + ISPCSI2_PHY_IRQENABLE_ERRESC1 | | ||
7781 | + ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 | | ||
7782 | + ISPCSI2_PHY_IRQENABLE_ERRSOTHS1; | ||
7783 | + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQSTATUS); | ||
7784 | + if (enable) | ||
7785 | + reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_PHY_IRQENABLE); | ||
7786 | + else | ||
7787 | + reg = 0; | ||
7788 | + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQENABLE); | ||
7789 | +} | ||
7790 | + | ||
7791 | +/* | ||
7792 | + * csi2_irq_status_set - Enables CSI2 Status IRQs. | ||
7793 | + * @enable: Enable/disable CSI2 Status interrupts | ||
7794 | + */ | ||
7795 | +static void csi2_irq_status_set(struct isp_device *isp, | ||
7796 | + struct isp_csi2_device *csi2, int enable) | ||
7797 | +{ | ||
7798 | + u32 reg; | ||
7799 | + reg = ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | | ||
7800 | + ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | | ||
7801 | + ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ | | ||
7802 | + ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | | ||
7803 | + ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | | ||
7804 | + ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ | | ||
7805 | + ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ | | ||
7806 | + ISPCSI2_IRQSTATUS_CONTEXT(0); | ||
7807 | + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQSTATUS); | ||
7808 | + if (enable) | ||
7809 | + reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQENABLE); | ||
7810 | + else | ||
7811 | + reg = 0; | ||
7812 | + | ||
7813 | + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQENABLE); | ||
7814 | +} | ||
7815 | + | ||
7816 | +/* | ||
7817 | + * omap3isp_csi2_reset - Resets the CSI2 module. | ||
7818 | + * | ||
7819 | + * Must be called with the phy lock held. | ||
7820 | + * | ||
7821 | + * Returns 0 if successful, or -EBUSY if power command didn't respond. | ||
7822 | + */ | ||
7823 | +int omap3isp_csi2_reset(struct isp_csi2_device *csi2) | ||
7824 | +{ | ||
7825 | + struct isp_device *isp = csi2->isp; | ||
7826 | + u8 soft_reset_retries = 0; | ||
7827 | + u32 reg; | ||
7828 | + int i; | ||
7829 | + | ||
7830 | + if (!csi2->available) | ||
7831 | + return -ENODEV; | ||
7832 | + | ||
7833 | + if (csi2->phy->phy_in_use) | ||
7834 | + return -EBUSY; | ||
7835 | + | ||
7836 | + isp_reg_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, | ||
7837 | + ISPCSI2_SYSCONFIG_SOFT_RESET); | ||
7838 | + | ||
7839 | + do { | ||
7840 | + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_SYSSTATUS) & | ||
7841 | + ISPCSI2_SYSSTATUS_RESET_DONE; | ||
7842 | + if (reg == ISPCSI2_SYSSTATUS_RESET_DONE) | ||
7843 | + break; | ||
7844 | + soft_reset_retries++; | ||
7845 | + if (soft_reset_retries < 5) | ||
7846 | + udelay(100); | ||
7847 | + } while (soft_reset_retries < 5); | ||
7848 | + | ||
7849 | + if (soft_reset_retries == 5) { | ||
7850 | + printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n"); | ||
7851 | + return -EBUSY; | ||
7852 | + } | ||
7853 | + | ||
7854 | + if (isp->revision == ISP_REVISION_15_0) | ||
7855 | + isp_reg_set(isp, csi2->regs1, ISPCSI2_PHY_CFG, | ||
7856 | + ISPCSI2_PHY_CFG_RESET_CTRL); | ||
7857 | + | ||
7858 | + i = 100; | ||
7859 | + do { | ||
7860 | + reg = isp_reg_readl(isp, csi2->phy->phy_regs, ISPCSIPHY_REG1) | ||
7861 | + & ISPCSIPHY_REG1_RESET_DONE_CTRLCLK; | ||
7862 | + if (reg == ISPCSIPHY_REG1_RESET_DONE_CTRLCLK) | ||
7863 | + break; | ||
7864 | + udelay(100); | ||
7865 | + } while (--i > 0); | ||
7866 | + | ||
7867 | + if (i == 0) { | ||
7868 | + printk(KERN_ERR | ||
7869 | + "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n"); | ||
7870 | + return -EBUSY; | ||
7871 | + } | ||
7872 | + | ||
7873 | + if (isp->autoidle) | ||
7874 | + isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, | ||
7875 | + ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK | | ||
7876 | + ISPCSI2_SYSCONFIG_AUTO_IDLE, | ||
7877 | + ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART | | ||
7878 | + ((isp->revision == ISP_REVISION_15_0) ? | ||
7879 | + ISPCSI2_SYSCONFIG_AUTO_IDLE : 0)); | ||
7880 | + else | ||
7881 | + isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, | ||
7882 | + ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK | | ||
7883 | + ISPCSI2_SYSCONFIG_AUTO_IDLE, | ||
7884 | + ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO); | ||
7885 | + | ||
7886 | + return 0; | ||
7887 | +} | ||
7888 | + | ||
7889 | +static int csi2_configure(struct isp_csi2_device *csi2) | ||
7890 | +{ | ||
7891 | + const struct isp_v4l2_subdevs_group *pdata; | ||
7892 | + struct isp_device *isp = csi2->isp; | ||
7893 | + struct isp_csi2_timing_cfg *timing = &csi2->timing[0]; | ||
7894 | + struct v4l2_subdev *sensor; | ||
7895 | + struct media_pad *pad; | ||
7896 | + | ||
7897 | + /* | ||
7898 | + * CSI2 fields that can be updated while the context has | ||
7899 | + * been enabled or the interface has been enabled are not | ||
7900 | + * updated dynamically currently. So we do not allow to | ||
7901 | + * reconfigure if either has been enabled | ||
7902 | + */ | ||
7903 | + if (csi2->contexts[0].enabled || csi2->ctrl.if_enable) | ||
7904 | + return -EBUSY; | ||
7905 | + | ||
7906 | + pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]); | ||
7907 | + sensor = media_entity_to_v4l2_subdev(pad->entity); | ||
7908 | + pdata = sensor->host_priv; | ||
7909 | + | ||
7910 | + csi2->frame_skip = 0; | ||
7911 | + v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip); | ||
7912 | + | ||
7913 | + csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div; | ||
7914 | + csi2->ctrl.frame_mode = ISP_CSI2_FRAME_IMMEDIATE; | ||
7915 | + csi2->ctrl.ecc_enable = pdata->bus.csi2.crc; | ||
7916 | + | ||
7917 | + timing->ionum = 1; | ||
7918 | + timing->force_rx_mode = 1; | ||
7919 | + timing->stop_state_16x = 1; | ||
7920 | + timing->stop_state_4x = 1; | ||
7921 | + timing->stop_state_counter = 0x1FF; | ||
7922 | + | ||
7923 | + /* | ||
7924 | + * The CSI2 receiver can't do any format conversion except DPCM | ||
7925 | + * decompression, so every set_format call configures both pads | ||
7926 | + * and enables DPCM decompression as a special case: | ||
7927 | + */ | ||
7928 | + if (csi2->formats[CSI2_PAD_SINK].code != | ||
7929 | + csi2->formats[CSI2_PAD_SOURCE].code) | ||
7930 | + csi2->dpcm_decompress = true; | ||
7931 | + else | ||
7932 | + csi2->dpcm_decompress = false; | ||
7933 | + | ||
7934 | + csi2->contexts[0].format_id = csi2_ctx_map_format(csi2); | ||
7935 | + | ||
7936 | + if (csi2->video_out.bpl_padding == 0) | ||
7937 | + csi2->contexts[0].data_offset = 0; | ||
7938 | + else | ||
7939 | + csi2->contexts[0].data_offset = csi2->video_out.bpl_value; | ||
7940 | + | ||
7941 | + /* | ||
7942 | + * Enable end of frame and end of line signals generation for | ||
7943 | + * context 0. These signals are generated from CSI2 receiver to | ||
7944 | + * qualify the last pixel of a frame and the last pixel of a line. | ||
7945 | + * Without enabling the signals CSI2 receiver writes data to memory | ||
7946 | + * beyond buffer size and/or data line offset is not handled correctly. | ||
7947 | + */ | ||
7948 | + csi2->contexts[0].eof_enabled = 1; | ||
7949 | + csi2->contexts[0].eol_enabled = 1; | ||
7950 | + | ||
7951 | + csi2_irq_complexio1_set(isp, csi2, 1); | ||
7952 | + csi2_irq_ctx_set(isp, csi2, 1); | ||
7953 | + csi2_irq_status_set(isp, csi2, 1); | ||
7954 | + | ||
7955 | + /* Set configuration (timings, format and links) */ | ||
7956 | + csi2_timing_config(isp, csi2, timing); | ||
7957 | + csi2_recv_config(isp, csi2, &csi2->ctrl); | ||
7958 | + csi2_ctx_config(isp, csi2, &csi2->contexts[0]); | ||
7959 | + | ||
7960 | + return 0; | ||
7961 | +} | ||
7962 | + | ||
7963 | +/* | ||
7964 | + * csi2_print_status - Prints CSI2 debug information. | ||
7965 | + */ | ||
7966 | +#define CSI2_PRINT_REGISTER(isp, regs, name)\ | ||
7967 | + dev_dbg(isp->dev, "###CSI2 " #name "=0x%08x\n", \ | ||
7968 | + isp_reg_readl(isp, regs, ISPCSI2_##name)) | ||
7969 | + | ||
7970 | +static void csi2_print_status(struct isp_csi2_device *csi2) | ||
7971 | +{ | ||
7972 | + struct isp_device *isp = csi2->isp; | ||
7973 | + | ||
7974 | + if (!csi2->available) | ||
7975 | + return; | ||
7976 | + | ||
7977 | + dev_dbg(isp->dev, "-------------CSI2 Register dump-------------\n"); | ||
7978 | + | ||
7979 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSCONFIG); | ||
7980 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSSTATUS); | ||
7981 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQENABLE); | ||
7982 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQSTATUS); | ||
7983 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTRL); | ||
7984 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_H); | ||
7985 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, GNQ); | ||
7986 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_CFG); | ||
7987 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQSTATUS); | ||
7988 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, SHORT_PACKET); | ||
7989 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQENABLE); | ||
7990 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_P); | ||
7991 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, TIMING); | ||
7992 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL1(0)); | ||
7993 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL2(0)); | ||
7994 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_OFST(0)); | ||
7995 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PING_ADDR(0)); | ||
7996 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PONG_ADDR(0)); | ||
7997 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQENABLE(0)); | ||
7998 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQSTATUS(0)); | ||
7999 | + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL3(0)); | ||
8000 | + | ||
8001 | + dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
8002 | +} | ||
8003 | + | ||
8004 | +/* ----------------------------------------------------------------------------- | ||
8005 | + * Interrupt handling | ||
8006 | + */ | ||
8007 | + | ||
8008 | +/* | ||
8009 | + * csi2_isr_buffer - Does buffer handling at end-of-frame | ||
8010 | + * when writing to memory. | ||
8011 | + */ | ||
8012 | +static void csi2_isr_buffer(struct isp_csi2_device *csi2) | ||
8013 | +{ | ||
8014 | + struct isp_device *isp = csi2->isp; | ||
8015 | + struct isp_buffer *buffer; | ||
8016 | + | ||
8017 | + csi2_ctx_enable(isp, csi2, 0, 0); | ||
8018 | + | ||
8019 | + buffer = omap3isp_video_buffer_next(&csi2->video_out, 0); | ||
8020 | + | ||
8021 | + /* | ||
8022 | + * Let video queue operation restart engine if there is an underrun | ||
8023 | + * condition. | ||
8024 | + */ | ||
8025 | + if (buffer == NULL) | ||
8026 | + return; | ||
8027 | + | ||
8028 | + csi2_set_outaddr(csi2, buffer->isp_addr); | ||
8029 | + csi2_ctx_enable(isp, csi2, 0, 1); | ||
8030 | +} | ||
8031 | + | ||
8032 | +static void csi2_isr_ctx(struct isp_csi2_device *csi2, | ||
8033 | + struct isp_csi2_ctx_cfg *ctx) | ||
8034 | +{ | ||
8035 | + struct isp_device *isp = csi2->isp; | ||
8036 | + unsigned int n = ctx->ctxnum; | ||
8037 | + u32 status; | ||
8038 | + | ||
8039 | + status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); | ||
8040 | + isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); | ||
8041 | + | ||
8042 | + /* Propagate frame number */ | ||
8043 | + if (status & ISPCSI2_CTX_IRQSTATUS_FS_IRQ) { | ||
8044 | + struct isp_pipeline *pipe = | ||
8045 | + to_isp_pipeline(&csi2->subdev.entity); | ||
8046 | + if (pipe->do_propagation) | ||
8047 | + atomic_inc(&pipe->frame_number); | ||
8048 | + } | ||
8049 | + | ||
8050 | + if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ)) | ||
8051 | + return; | ||
8052 | + | ||
8053 | + /* Skip interrupts until we reach the frame skip count. The CSI2 will be | ||
8054 | + * automatically disabled, as the frame skip count has been programmed | ||
8055 | + * in the CSI2_CTx_CTRL1::COUNT field, so reenable it. | ||
8056 | + * | ||
8057 | + * It would have been nice to rely on the FRAME_NUMBER interrupt instead | ||
8058 | + * but it turned out that the interrupt is only generated when the CSI2 | ||
8059 | + * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased | ||
8060 | + * correctly and reaches 0 when data is forwarded to the video port only | ||
8061 | + * but no interrupt arrives). Maybe a CSI2 hardware bug. | ||
8062 | + */ | ||
8063 | + if (csi2->frame_skip) { | ||
8064 | + csi2->frame_skip--; | ||
8065 | + if (csi2->frame_skip == 0) { | ||
8066 | + ctx->format_id = csi2_ctx_map_format(csi2); | ||
8067 | + csi2_ctx_config(isp, csi2, ctx); | ||
8068 | + csi2_ctx_enable(isp, csi2, n, 1); | ||
8069 | + } | ||
8070 | + return; | ||
8071 | + } | ||
8072 | + | ||
8073 | + if (csi2->output & CSI2_OUTPUT_MEMORY) | ||
8074 | + csi2_isr_buffer(csi2); | ||
8075 | +} | ||
8076 | + | ||
8077 | +/* | ||
8078 | + * omap3isp_csi2_isr - CSI2 interrupt handling. | ||
8079 | + * | ||
8080 | + * Return -EIO on Transmission error | ||
8081 | + */ | ||
8082 | +int omap3isp_csi2_isr(struct isp_csi2_device *csi2) | ||
8083 | +{ | ||
8084 | + u32 csi2_irqstatus, cpxio1_irqstatus; | ||
8085 | + struct isp_device *isp = csi2->isp; | ||
8086 | + int retval = 0; | ||
8087 | + | ||
8088 | + if (!csi2->available) | ||
8089 | + return -ENODEV; | ||
8090 | + | ||
8091 | + csi2_irqstatus = isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQSTATUS); | ||
8092 | + isp_reg_writel(isp, csi2_irqstatus, csi2->regs1, ISPCSI2_IRQSTATUS); | ||
8093 | + | ||
8094 | + /* Failure Cases */ | ||
8095 | + if (csi2_irqstatus & ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ) { | ||
8096 | + cpxio1_irqstatus = isp_reg_readl(isp, csi2->regs1, | ||
8097 | + ISPCSI2_PHY_IRQSTATUS); | ||
8098 | + isp_reg_writel(isp, cpxio1_irqstatus, | ||
8099 | + csi2->regs1, ISPCSI2_PHY_IRQSTATUS); | ||
8100 | + dev_dbg(isp->dev, "CSI2: ComplexIO Error IRQ " | ||
8101 | + "%x\n", cpxio1_irqstatus); | ||
8102 | + retval = -EIO; | ||
8103 | + } | ||
8104 | + | ||
8105 | + if (csi2_irqstatus & (ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | | ||
8106 | + ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | | ||
8107 | + ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | | ||
8108 | + ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | | ||
8109 | + ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ)) { | ||
8110 | + dev_dbg(isp->dev, "CSI2 Err:" | ||
8111 | + " OCP:%d," | ||
8112 | + " Short_pack:%d," | ||
8113 | + " ECC:%d," | ||
8114 | + " CPXIO2:%d," | ||
8115 | + " FIFO_OVF:%d," | ||
8116 | + "\n", | ||
8117 | + (csi2_irqstatus & | ||
8118 | + ISPCSI2_IRQSTATUS_OCP_ERR_IRQ) ? 1 : 0, | ||
8119 | + (csi2_irqstatus & | ||
8120 | + ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ) ? 1 : 0, | ||
8121 | + (csi2_irqstatus & | ||
8122 | + ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ) ? 1 : 0, | ||
8123 | + (csi2_irqstatus & | ||
8124 | + ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ) ? 1 : 0, | ||
8125 | + (csi2_irqstatus & | ||
8126 | + ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ) ? 1 : 0); | ||
8127 | + retval = -EIO; | ||
8128 | + } | ||
8129 | + | ||
8130 | + if (omap3isp_module_sync_is_stopping(&csi2->wait, &csi2->stopping)) | ||
8131 | + return 0; | ||
8132 | + | ||
8133 | + /* Successful cases */ | ||
8134 | + if (csi2_irqstatus & ISPCSI2_IRQSTATUS_CONTEXT(0)) | ||
8135 | + csi2_isr_ctx(csi2, &csi2->contexts[0]); | ||
8136 | + | ||
8137 | + if (csi2_irqstatus & ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ) | ||
8138 | + dev_dbg(isp->dev, "CSI2: ECC correction done\n"); | ||
8139 | + | ||
8140 | + return retval; | ||
8141 | +} | ||
8142 | + | ||
8143 | +/* ----------------------------------------------------------------------------- | ||
8144 | + * ISP video operations | ||
8145 | + */ | ||
8146 | + | ||
8147 | +/* | ||
8148 | + * csi2_queue - Queues the first buffer when using memory output | ||
8149 | + * @video: The video node | ||
8150 | + * @buffer: buffer to queue | ||
8151 | + */ | ||
8152 | +static int csi2_queue(struct isp_video *video, struct isp_buffer *buffer) | ||
8153 | +{ | ||
8154 | + struct isp_device *isp = video->isp; | ||
8155 | + struct isp_csi2_device *csi2 = &isp->isp_csi2a; | ||
8156 | + | ||
8157 | + csi2_set_outaddr(csi2, buffer->isp_addr); | ||
8158 | + | ||
8159 | + /* | ||
8160 | + * If streaming was enabled before there was a buffer queued | ||
8161 | + * or underrun happened in the ISR, the hardware was not enabled | ||
8162 | + * and DMA queue flag ISP_VIDEO_DMAQUEUE_UNDERRUN is still set. | ||
8163 | + * Enable it now. | ||
8164 | + */ | ||
8165 | + if (csi2->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) { | ||
8166 | + /* Enable / disable context 0 and IRQs */ | ||
8167 | + csi2_if_enable(isp, csi2, 1); | ||
8168 | + csi2_ctx_enable(isp, csi2, 0, 1); | ||
8169 | + isp_video_dmaqueue_flags_clr(&csi2->video_out); | ||
8170 | + } | ||
8171 | + | ||
8172 | + return 0; | ||
8173 | +} | ||
8174 | + | ||
8175 | +static const struct isp_video_operations csi2_ispvideo_ops = { | ||
8176 | + .queue = csi2_queue, | ||
8177 | +}; | ||
8178 | + | ||
8179 | +/* ----------------------------------------------------------------------------- | ||
8180 | + * V4L2 subdev operations | ||
8181 | + */ | ||
8182 | + | ||
8183 | +static struct v4l2_mbus_framefmt * | ||
8184 | +__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, | ||
8185 | + unsigned int pad, enum v4l2_subdev_format_whence which) | ||
8186 | +{ | ||
8187 | + if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
8188 | + return v4l2_subdev_get_try_format(fh, pad); | ||
8189 | + else | ||
8190 | + return &csi2->formats[pad]; | ||
8191 | +} | ||
8192 | + | ||
8193 | +static void | ||
8194 | +csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, | ||
8195 | + unsigned int pad, struct v4l2_mbus_framefmt *fmt, | ||
8196 | + enum v4l2_subdev_format_whence which) | ||
8197 | +{ | ||
8198 | + enum v4l2_mbus_pixelcode pixelcode; | ||
8199 | + struct v4l2_mbus_framefmt *format; | ||
8200 | + const struct isp_format_info *info; | ||
8201 | + unsigned int i; | ||
8202 | + | ||
8203 | + switch (pad) { | ||
8204 | + case CSI2_PAD_SINK: | ||
8205 | + /* Clamp the width and height to valid range (1-8191). */ | ||
8206 | + for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) { | ||
8207 | + if (fmt->code == csi2_input_fmts[i]) | ||
8208 | + break; | ||
8209 | + } | ||
8210 | + | ||
8211 | + /* If not found, use SGRBG10 as default */ | ||
8212 | + if (i >= ARRAY_SIZE(csi2_input_fmts)) | ||
8213 | + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
8214 | + | ||
8215 | + fmt->width = clamp_t(u32, fmt->width, 1, 8191); | ||
8216 | + fmt->height = clamp_t(u32, fmt->height, 1, 8191); | ||
8217 | + break; | ||
8218 | + | ||
8219 | + case CSI2_PAD_SOURCE: | ||
8220 | + /* Source format same as sink format, except for DPCM | ||
8221 | + * compression. | ||
8222 | + */ | ||
8223 | + pixelcode = fmt->code; | ||
8224 | + format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which); | ||
8225 | + memcpy(fmt, format, sizeof(*fmt)); | ||
8226 | + | ||
8227 | + /* | ||
8228 | + * Only Allow DPCM decompression, and check that the | ||
8229 | + * pattern is preserved | ||
8230 | + */ | ||
8231 | + info = omap3isp_video_format_info(fmt->code); | ||
8232 | + if (info->uncompressed == pixelcode) | ||
8233 | + fmt->code = pixelcode; | ||
8234 | + break; | ||
8235 | + } | ||
8236 | + | ||
8237 | + /* RGB, non-interlaced */ | ||
8238 | + fmt->colorspace = V4L2_COLORSPACE_SRGB; | ||
8239 | + fmt->field = V4L2_FIELD_NONE; | ||
8240 | +} | ||
8241 | + | ||
8242 | +/* | ||
8243 | + * csi2_enum_mbus_code - Handle pixel format enumeration | ||
8244 | + * @sd : pointer to v4l2 subdev structure | ||
8245 | + * @fh : V4L2 subdev file handle | ||
8246 | + * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
8247 | + * return -EINVAL or zero on success | ||
8248 | + */ | ||
8249 | +static int csi2_enum_mbus_code(struct v4l2_subdev *sd, | ||
8250 | + struct v4l2_subdev_fh *fh, | ||
8251 | + struct v4l2_subdev_mbus_code_enum *code) | ||
8252 | +{ | ||
8253 | + struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); | ||
8254 | + struct v4l2_mbus_framefmt *format; | ||
8255 | + const struct isp_format_info *info; | ||
8256 | + | ||
8257 | + if (code->pad == CSI2_PAD_SINK) { | ||
8258 | + if (code->index >= ARRAY_SIZE(csi2_input_fmts)) | ||
8259 | + return -EINVAL; | ||
8260 | + | ||
8261 | + code->code = csi2_input_fmts[code->index]; | ||
8262 | + } else { | ||
8263 | + format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, | ||
8264 | + V4L2_SUBDEV_FORMAT_TRY); | ||
8265 | + switch (code->index) { | ||
8266 | + case 0: | ||
8267 | + /* Passthrough sink pad code */ | ||
8268 | + code->code = format->code; | ||
8269 | + break; | ||
8270 | + case 1: | ||
8271 | + /* Uncompressed code */ | ||
8272 | + info = omap3isp_video_format_info(format->code); | ||
8273 | + if (info->uncompressed == format->code) | ||
8274 | + return -EINVAL; | ||
8275 | + | ||
8276 | + code->code = info->uncompressed; | ||
8277 | + break; | ||
8278 | + default: | ||
8279 | + return -EINVAL; | ||
8280 | + } | ||
8281 | + } | ||
8282 | + | ||
8283 | + return 0; | ||
8284 | +} | ||
8285 | + | ||
8286 | +static int csi2_enum_frame_size(struct v4l2_subdev *sd, | ||
8287 | + struct v4l2_subdev_fh *fh, | ||
8288 | + struct v4l2_subdev_frame_size_enum *fse) | ||
8289 | +{ | ||
8290 | + struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); | ||
8291 | + struct v4l2_mbus_framefmt format; | ||
8292 | + | ||
8293 | + if (fse->index != 0) | ||
8294 | + return -EINVAL; | ||
8295 | + | ||
8296 | + format.code = fse->code; | ||
8297 | + format.width = 1; | ||
8298 | + format.height = 1; | ||
8299 | + csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
8300 | + fse->min_width = format.width; | ||
8301 | + fse->min_height = format.height; | ||
8302 | + | ||
8303 | + if (format.code != fse->code) | ||
8304 | + return -EINVAL; | ||
8305 | + | ||
8306 | + format.code = fse->code; | ||
8307 | + format.width = -1; | ||
8308 | + format.height = -1; | ||
8309 | + csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
8310 | + fse->max_width = format.width; | ||
8311 | + fse->max_height = format.height; | ||
8312 | + | ||
8313 | + return 0; | ||
8314 | +} | ||
8315 | + | ||
8316 | +/* | ||
8317 | + * csi2_get_format - Handle get format by pads subdev method | ||
8318 | + * @sd : pointer to v4l2 subdev structure | ||
8319 | + * @fh : V4L2 subdev file handle | ||
8320 | + * @fmt: pointer to v4l2 subdev format structure | ||
8321 | + * return -EINVAL or zero on sucess | ||
8322 | + */ | ||
8323 | +static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
8324 | + struct v4l2_subdev_format *fmt) | ||
8325 | +{ | ||
8326 | + struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); | ||
8327 | + struct v4l2_mbus_framefmt *format; | ||
8328 | + | ||
8329 | + format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); | ||
8330 | + if (format == NULL) | ||
8331 | + return -EINVAL; | ||
8332 | + | ||
8333 | + fmt->format = *format; | ||
8334 | + return 0; | ||
8335 | +} | ||
8336 | + | ||
8337 | +/* | ||
8338 | + * csi2_set_format - Handle set format by pads subdev method | ||
8339 | + * @sd : pointer to v4l2 subdev structure | ||
8340 | + * @fh : V4L2 subdev file handle | ||
8341 | + * @fmt: pointer to v4l2 subdev format structure | ||
8342 | + * return -EINVAL or zero on success | ||
8343 | + */ | ||
8344 | +static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
8345 | + struct v4l2_subdev_format *fmt) | ||
8346 | +{ | ||
8347 | + struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); | ||
8348 | + struct v4l2_mbus_framefmt *format; | ||
8349 | + | ||
8350 | + format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); | ||
8351 | + if (format == NULL) | ||
8352 | + return -EINVAL; | ||
8353 | + | ||
8354 | + csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which); | ||
8355 | + *format = fmt->format; | ||
8356 | + | ||
8357 | + /* Propagate the format from sink to source */ | ||
8358 | + if (fmt->pad == CSI2_PAD_SINK) { | ||
8359 | + format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE, | ||
8360 | + fmt->which); | ||
8361 | + *format = fmt->format; | ||
8362 | + csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which); | ||
8363 | + } | ||
8364 | + | ||
8365 | + return 0; | ||
8366 | +} | ||
8367 | + | ||
8368 | +/* | ||
8369 | + * csi2_init_formats - Initialize formats on all pads | ||
8370 | + * @sd: ISP CSI2 V4L2 subdevice | ||
8371 | + * @fh: V4L2 subdev file handle | ||
8372 | + * | ||
8373 | + * Initialize all pad formats with default values. If fh is not NULL, try | ||
8374 | + * formats are initialized on the file handle. Otherwise active formats are | ||
8375 | + * initialized on the device. | ||
8376 | + */ | ||
8377 | +static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
8378 | +{ | ||
8379 | + struct v4l2_subdev_format format; | ||
8380 | + | ||
8381 | + memset(&format, 0, sizeof(format)); | ||
8382 | + format.pad = CSI2_PAD_SINK; | ||
8383 | + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
8384 | + format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
8385 | + format.format.width = 4096; | ||
8386 | + format.format.height = 4096; | ||
8387 | + csi2_set_format(sd, fh, &format); | ||
8388 | + | ||
8389 | + return 0; | ||
8390 | +} | ||
8391 | + | ||
8392 | +/* | ||
8393 | + * csi2_set_stream - Enable/Disable streaming on the CSI2 module | ||
8394 | + * @sd: ISP CSI2 V4L2 subdevice | ||
8395 | + * @enable: ISP pipeline stream state | ||
8396 | + * | ||
8397 | + * Return 0 on success or a negative error code otherwise. | ||
8398 | + */ | ||
8399 | +static int csi2_set_stream(struct v4l2_subdev *sd, int enable) | ||
8400 | +{ | ||
8401 | + struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); | ||
8402 | + struct isp_device *isp = csi2->isp; | ||
8403 | + struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); | ||
8404 | + struct isp_video *video_out = &csi2->video_out; | ||
8405 | + | ||
8406 | + switch (enable) { | ||
8407 | + case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
8408 | + if (omap3isp_csiphy_acquire(csi2->phy) < 0) | ||
8409 | + return -ENODEV; | ||
8410 | + csi2->use_fs_irq = pipe->do_propagation; | ||
8411 | + if (csi2->output & CSI2_OUTPUT_MEMORY) | ||
8412 | + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); | ||
8413 | + csi2_configure(csi2); | ||
8414 | + csi2_print_status(csi2); | ||
8415 | + | ||
8416 | + /* | ||
8417 | + * When outputting to memory with no buffer available, let the | ||
8418 | + * buffer queue handler start the hardware. A DMA queue flag | ||
8419 | + * ISP_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is | ||
8420 | + * a buffer available. | ||
8421 | + */ | ||
8422 | + if (csi2->output & CSI2_OUTPUT_MEMORY && | ||
8423 | + !(video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED)) | ||
8424 | + break; | ||
8425 | + /* Enable context 0 and IRQs */ | ||
8426 | + atomic_set(&csi2->stopping, 0); | ||
8427 | + csi2_ctx_enable(isp, csi2, 0, 1); | ||
8428 | + csi2_if_enable(isp, csi2, 1); | ||
8429 | + isp_video_dmaqueue_flags_clr(video_out); | ||
8430 | + break; | ||
8431 | + | ||
8432 | + case ISP_PIPELINE_STREAM_STOPPED: | ||
8433 | + if (csi2->state == ISP_PIPELINE_STREAM_STOPPED) | ||
8434 | + return 0; | ||
8435 | + if (omap3isp_module_sync_idle(&sd->entity, &csi2->wait, | ||
8436 | + &csi2->stopping)) | ||
8437 | + dev_dbg(isp->dev, "%s: module stop timeout.\n", | ||
8438 | + sd->name); | ||
8439 | + csi2_ctx_enable(isp, csi2, 0, 0); | ||
8440 | + csi2_if_enable(isp, csi2, 0); | ||
8441 | + csi2_irq_ctx_set(isp, csi2, 0); | ||
8442 | + omap3isp_csiphy_release(csi2->phy); | ||
8443 | + isp_video_dmaqueue_flags_clr(video_out); | ||
8444 | + omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); | ||
8445 | + break; | ||
8446 | + } | ||
8447 | + | ||
8448 | + csi2->state = enable; | ||
8449 | + return 0; | ||
8450 | +} | ||
8451 | + | ||
8452 | +/* subdev core operations */ | ||
8453 | +static const struct v4l2_subdev_core_ops csi2_core_ops = { | ||
8454 | + .queryctrl = v4l2_subdev_queryctrl, | ||
8455 | + .querymenu = v4l2_subdev_querymenu, | ||
8456 | + .g_ctrl = v4l2_subdev_g_ctrl, | ||
8457 | + .s_ctrl = v4l2_subdev_s_ctrl, | ||
8458 | + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, | ||
8459 | + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, | ||
8460 | + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, | ||
8461 | +}; | ||
8462 | + | ||
8463 | +/* subdev file operations */ | ||
8464 | +static const struct v4l2_subdev_file_ops csi2_file_ops = { | ||
8465 | + .open = csi2_init_formats, | ||
8466 | +}; | ||
8467 | + | ||
8468 | +/* subdev video operations */ | ||
8469 | +static const struct v4l2_subdev_video_ops csi2_video_ops = { | ||
8470 | + .s_stream = csi2_set_stream, | ||
8471 | +}; | ||
8472 | + | ||
8473 | +/* subdev pad operations */ | ||
8474 | +static const struct v4l2_subdev_pad_ops csi2_pad_ops = { | ||
8475 | + .enum_mbus_code = csi2_enum_mbus_code, | ||
8476 | + .enum_frame_size = csi2_enum_frame_size, | ||
8477 | + .get_fmt = csi2_get_format, | ||
8478 | + .set_fmt = csi2_set_format, | ||
8479 | +}; | ||
8480 | + | ||
8481 | +/* subdev operations */ | ||
8482 | +static const struct v4l2_subdev_ops csi2_ops = { | ||
8483 | + .core = &csi2_core_ops, | ||
8484 | + .file = &csi2_file_ops, | ||
8485 | + .video = &csi2_video_ops, | ||
8486 | + .pad = &csi2_pad_ops, | ||
8487 | +}; | ||
8488 | + | ||
8489 | +/* ----------------------------------------------------------------------------- | ||
8490 | + * Media entity operations | ||
8491 | + */ | ||
8492 | + | ||
8493 | +/* | ||
8494 | + * csi2_link_setup - Setup CSI2 connections. | ||
8495 | + * @entity : Pointer to media entity structure | ||
8496 | + * @local : Pointer to local pad array | ||
8497 | + * @remote : Pointer to remote pad array | ||
8498 | + * @flags : Link flags | ||
8499 | + * return -EINVAL or zero on success | ||
8500 | + */ | ||
8501 | +static int csi2_link_setup(struct media_entity *entity, | ||
8502 | + const struct media_pad *local, | ||
8503 | + const struct media_pad *remote, u32 flags) | ||
8504 | +{ | ||
8505 | + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
8506 | + struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); | ||
8507 | + struct isp_csi2_ctrl_cfg *ctrl = &csi2->ctrl; | ||
8508 | + | ||
8509 | + /* | ||
8510 | + * The ISP core doesn't support pipelines with multiple video outputs. | ||
8511 | + * Revisit this when it will be implemented, and return -EBUSY for now. | ||
8512 | + */ | ||
8513 | + | ||
8514 | + switch (local->index | media_entity_type(remote->entity)) { | ||
8515 | + case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: | ||
8516 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
8517 | + if (csi2->output & ~CSI2_OUTPUT_MEMORY) | ||
8518 | + return -EBUSY; | ||
8519 | + csi2->output |= CSI2_OUTPUT_MEMORY; | ||
8520 | + } else { | ||
8521 | + csi2->output &= ~CSI2_OUTPUT_MEMORY; | ||
8522 | + } | ||
8523 | + break; | ||
8524 | + | ||
8525 | + case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: | ||
8526 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
8527 | + if (csi2->output & ~CSI2_OUTPUT_CCDC) | ||
8528 | + return -EBUSY; | ||
8529 | + csi2->output |= CSI2_OUTPUT_CCDC; | ||
8530 | + } else { | ||
8531 | + csi2->output &= ~CSI2_OUTPUT_CCDC; | ||
8532 | + } | ||
8533 | + break; | ||
8534 | + | ||
8535 | + default: | ||
8536 | + /* Link from camera to CSI2 is fixed... */ | ||
8537 | + return -EINVAL; | ||
8538 | + } | ||
8539 | + | ||
8540 | + ctrl->vp_only_enable = | ||
8541 | + (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true; | ||
8542 | + ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_CCDC); | ||
8543 | + | ||
8544 | + return 0; | ||
8545 | +} | ||
8546 | + | ||
8547 | +/* media operations */ | ||
8548 | +static const struct media_entity_operations csi2_media_ops = { | ||
8549 | + .link_setup = csi2_link_setup, | ||
8550 | +}; | ||
8551 | + | ||
8552 | +/* | ||
8553 | + * csi2_init_entities - Initialize subdev and media entity. | ||
8554 | + * @csi2: Pointer to csi2 structure. | ||
8555 | + * return -ENOMEM or zero on success | ||
8556 | + */ | ||
8557 | +static int csi2_init_entities(struct isp_csi2_device *csi2) | ||
8558 | +{ | ||
8559 | + struct v4l2_subdev *sd = &csi2->subdev; | ||
8560 | + struct media_pad *pads = csi2->pads; | ||
8561 | + struct media_entity *me = &sd->entity; | ||
8562 | + int ret; | ||
8563 | + | ||
8564 | + v4l2_subdev_init(sd, &csi2_ops); | ||
8565 | + strlcpy(sd->name, "OMAP3 ISP CSI2a", sizeof(sd->name)); | ||
8566 | + | ||
8567 | + sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
8568 | + v4l2_set_subdevdata(sd, csi2); | ||
8569 | + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
8570 | + | ||
8571 | + v4l2_ctrl_handler_init(&csi2->ctrls, 1); | ||
8572 | + sd->ctrl_handler = &csi2->ctrls; | ||
8573 | + | ||
8574 | + pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_OUTPUT; | ||
8575 | + pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; | ||
8576 | + | ||
8577 | + me->ops = &csi2_media_ops; | ||
8578 | + ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0); | ||
8579 | + if (ret < 0) | ||
8580 | + return ret; | ||
8581 | + | ||
8582 | + csi2_init_formats(sd, NULL); | ||
8583 | + | ||
8584 | + /* Video device node */ | ||
8585 | + csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
8586 | + csi2->video_out.ops = &csi2_ispvideo_ops; | ||
8587 | + csi2->video_out.bpl_alignment = 32; | ||
8588 | + csi2->video_out.bpl_zero_padding = 1; | ||
8589 | + csi2->video_out.bpl_max = 0x1ffe0; | ||
8590 | + csi2->video_out.isp = csi2->isp; | ||
8591 | + csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; | ||
8592 | + | ||
8593 | + ret = omap3isp_video_init(&csi2->video_out, "CSI2a"); | ||
8594 | + if (ret < 0) | ||
8595 | + return ret; | ||
8596 | + | ||
8597 | + /* Connect the CSI2 subdev to the video node. */ | ||
8598 | + ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE, | ||
8599 | + &csi2->video_out.video.entity, 0, 0); | ||
8600 | + if (ret < 0) | ||
8601 | + return ret; | ||
8602 | + | ||
8603 | + return 0; | ||
8604 | +} | ||
8605 | + | ||
8606 | +void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2) | ||
8607 | +{ | ||
8608 | + media_entity_cleanup(&csi2->subdev.entity); | ||
8609 | + | ||
8610 | + v4l2_device_unregister_subdev(&csi2->subdev); | ||
8611 | + v4l2_ctrl_handler_free(&csi2->ctrls); | ||
8612 | + omap3isp_video_unregister(&csi2->video_out); | ||
8613 | +} | ||
8614 | + | ||
8615 | +int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2, | ||
8616 | + struct v4l2_device *vdev) | ||
8617 | +{ | ||
8618 | + int ret; | ||
8619 | + | ||
8620 | + /* Register the subdev and video nodes. */ | ||
8621 | + ret = v4l2_device_register_subdev(vdev, &csi2->subdev); | ||
8622 | + if (ret < 0) | ||
8623 | + goto error; | ||
8624 | + | ||
8625 | + ret = omap3isp_video_register(&csi2->video_out, vdev); | ||
8626 | + if (ret < 0) | ||
8627 | + goto error; | ||
8628 | + | ||
8629 | + return 0; | ||
8630 | + | ||
8631 | +error: | ||
8632 | + omap3isp_csi2_unregister_entities(csi2); | ||
8633 | + return ret; | ||
8634 | +} | ||
8635 | + | ||
8636 | +/* ----------------------------------------------------------------------------- | ||
8637 | + * ISP CSI2 initialisation and cleanup | ||
8638 | + */ | ||
8639 | + | ||
8640 | +/* | ||
8641 | + * omap3isp_csi2_cleanup - Routine for module driver cleanup | ||
8642 | + */ | ||
8643 | +void omap3isp_csi2_cleanup(struct isp_device *isp) | ||
8644 | +{ | ||
8645 | +} | ||
8646 | + | ||
8647 | +/* | ||
8648 | + * omap3isp_csi2_init - Routine for module driver init | ||
8649 | + */ | ||
8650 | +int omap3isp_csi2_init(struct isp_device *isp) | ||
8651 | +{ | ||
8652 | + struct isp_csi2_device *csi2a = &isp->isp_csi2a; | ||
8653 | + struct isp_csi2_device *csi2c = &isp->isp_csi2c; | ||
8654 | + int ret; | ||
8655 | + | ||
8656 | + csi2a->isp = isp; | ||
8657 | + csi2a->available = 1; | ||
8658 | + csi2a->regs1 = OMAP3_ISP_IOMEM_CSI2A_REGS1; | ||
8659 | + csi2a->regs2 = OMAP3_ISP_IOMEM_CSI2A_REGS2; | ||
8660 | + csi2a->phy = &isp->isp_csiphy2; | ||
8661 | + csi2a->state = ISP_PIPELINE_STREAM_STOPPED; | ||
8662 | + init_waitqueue_head(&csi2a->wait); | ||
8663 | + | ||
8664 | + ret = csi2_init_entities(csi2a); | ||
8665 | + if (ret < 0) | ||
8666 | + goto fail; | ||
8667 | + | ||
8668 | + if (isp->revision == ISP_REVISION_15_0) { | ||
8669 | + csi2c->isp = isp; | ||
8670 | + csi2c->available = 1; | ||
8671 | + csi2c->regs1 = OMAP3_ISP_IOMEM_CSI2C_REGS1; | ||
8672 | + csi2c->regs2 = OMAP3_ISP_IOMEM_CSI2C_REGS2; | ||
8673 | + csi2c->phy = &isp->isp_csiphy1; | ||
8674 | + csi2c->state = ISP_PIPELINE_STREAM_STOPPED; | ||
8675 | + init_waitqueue_head(&csi2c->wait); | ||
8676 | + } | ||
8677 | + | ||
8678 | + return 0; | ||
8679 | +fail: | ||
8680 | + omap3isp_csi2_cleanup(isp); | ||
8681 | + return ret; | ||
8682 | +} | ||
8683 | diff --git a/drivers/media/video/isp/ispcsi2.h b/drivers/media/video/isp/ispcsi2.h | ||
8684 | new file mode 100644 | ||
8685 | index 0000000..b367326 | ||
8686 | --- /dev/null | ||
8687 | +++ b/drivers/media/video/isp/ispcsi2.h | ||
8688 | @@ -0,0 +1,169 @@ | ||
8689 | +/* | ||
8690 | + * ispcsi2.h | ||
8691 | + * | ||
8692 | + * TI OMAP3 ISP - CSI2 module | ||
8693 | + * | ||
8694 | + * Copyright (C) 2010 Nokia Corporation | ||
8695 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
8696 | + * | ||
8697 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
8698 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
8699 | + * | ||
8700 | + * This program is free software; you can redistribute it and/or modify | ||
8701 | + * it under the terms of the GNU General Public License version 2 as | ||
8702 | + * published by the Free Software Foundation. | ||
8703 | + * | ||
8704 | + * This program is distributed in the hope that it will be useful, but | ||
8705 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8706 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
8707 | + * General Public License for more details. | ||
8708 | + * | ||
8709 | + * You should have received a copy of the GNU General Public License | ||
8710 | + * along with this program; if not, write to the Free Software | ||
8711 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
8712 | + * 02110-1301 USA | ||
8713 | + */ | ||
8714 | + | ||
8715 | +#ifndef OMAP3_ISP_CSI2_H | ||
8716 | +#define OMAP3_ISP_CSI2_H | ||
8717 | + | ||
8718 | +#include <linux/types.h> | ||
8719 | +#include <linux/videodev2.h> | ||
8720 | +#include <media/v4l2-ctrls.h> | ||
8721 | + | ||
8722 | +struct isp_csiphy; | ||
8723 | + | ||
8724 | +/* This is not an exhaustive list */ | ||
8725 | +enum isp_csi2_pix_formats { | ||
8726 | + CSI2_PIX_FMT_OTHERS = 0, | ||
8727 | + CSI2_PIX_FMT_YUV422_8BIT = 0x1e, | ||
8728 | + CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e, | ||
8729 | + CSI2_PIX_FMT_RAW10_EXP16 = 0xab, | ||
8730 | + CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f, | ||
8731 | + CSI2_PIX_FMT_RAW8 = 0x2a, | ||
8732 | + CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa, | ||
8733 | + CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a, | ||
8734 | + CSI2_PIX_FMT_RAW8_VP = 0x12a, | ||
8735 | + CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340, | ||
8736 | + CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0, | ||
8737 | + CSI2_USERDEF_8BIT_DATA1 = 0x40, | ||
8738 | +}; | ||
8739 | + | ||
8740 | +enum isp_csi2_irqevents { | ||
8741 | + OCP_ERR_IRQ = 0x4000, | ||
8742 | + SHORT_PACKET_IRQ = 0x2000, | ||
8743 | + ECC_CORRECTION_IRQ = 0x1000, | ||
8744 | + ECC_NO_CORRECTION_IRQ = 0x800, | ||
8745 | + COMPLEXIO2_ERR_IRQ = 0x400, | ||
8746 | + COMPLEXIO1_ERR_IRQ = 0x200, | ||
8747 | + FIFO_OVF_IRQ = 0x100, | ||
8748 | + CONTEXT7 = 0x80, | ||
8749 | + CONTEXT6 = 0x40, | ||
8750 | + CONTEXT5 = 0x20, | ||
8751 | + CONTEXT4 = 0x10, | ||
8752 | + CONTEXT3 = 0x8, | ||
8753 | + CONTEXT2 = 0x4, | ||
8754 | + CONTEXT1 = 0x2, | ||
8755 | + CONTEXT0 = 0x1, | ||
8756 | +}; | ||
8757 | + | ||
8758 | +enum isp_csi2_ctx_irqevents { | ||
8759 | + CTX_ECC_CORRECTION = 0x100, | ||
8760 | + CTX_LINE_NUMBER = 0x80, | ||
8761 | + CTX_FRAME_NUMBER = 0x40, | ||
8762 | + CTX_CS = 0x20, | ||
8763 | + CTX_LE = 0x8, | ||
8764 | + CTX_LS = 0x4, | ||
8765 | + CTX_FE = 0x2, | ||
8766 | + CTX_FS = 0x1, | ||
8767 | +}; | ||
8768 | + | ||
8769 | +enum isp_csi2_frame_mode { | ||
8770 | + ISP_CSI2_FRAME_IMMEDIATE, | ||
8771 | + ISP_CSI2_FRAME_AFTERFEC, | ||
8772 | +}; | ||
8773 | + | ||
8774 | +#define ISP_CSI2_MAX_CTX_NUM 7 | ||
8775 | + | ||
8776 | +struct isp_csi2_ctx_cfg { | ||
8777 | + u8 ctxnum; /* context number 0 - 7 */ | ||
8778 | + u8 dpcm_decompress; | ||
8779 | + | ||
8780 | + /* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */ | ||
8781 | + u8 virtual_id; | ||
8782 | + u16 format_id; /* as in CSI2_CTx_CTRL2[9:0] */ | ||
8783 | + u8 dpcm_predictor; /* 1: simple, 0: advanced */ | ||
8784 | + | ||
8785 | + /* Fields in CSI2_CTx_CTRL1/3 - Shadowed */ | ||
8786 | + u16 alpha; | ||
8787 | + u16 data_offset; | ||
8788 | + u32 ping_addr; | ||
8789 | + u32 pong_addr; | ||
8790 | + u8 eof_enabled; | ||
8791 | + u8 eol_enabled; | ||
8792 | + u8 checksum_enabled; | ||
8793 | + u8 enabled; | ||
8794 | +}; | ||
8795 | + | ||
8796 | +struct isp_csi2_timing_cfg { | ||
8797 | + u8 ionum; /* IO1 or IO2 as in CSI2_TIMING */ | ||
8798 | + unsigned force_rx_mode:1; | ||
8799 | + unsigned stop_state_16x:1; | ||
8800 | + unsigned stop_state_4x:1; | ||
8801 | + u16 stop_state_counter; | ||
8802 | +}; | ||
8803 | + | ||
8804 | +struct isp_csi2_ctrl_cfg { | ||
8805 | + bool vp_clk_enable; | ||
8806 | + bool vp_only_enable; | ||
8807 | + u8 vp_out_ctrl; | ||
8808 | + enum isp_csi2_frame_mode frame_mode; | ||
8809 | + bool ecc_enable; | ||
8810 | + bool if_enable; | ||
8811 | +}; | ||
8812 | + | ||
8813 | +#define CSI2_PAD_SINK 0 | ||
8814 | +#define CSI2_PAD_SOURCE 1 | ||
8815 | +#define CSI2_PADS_NUM 2 | ||
8816 | + | ||
8817 | +#define CSI2_OUTPUT_CCDC (1 << 0) | ||
8818 | +#define CSI2_OUTPUT_MEMORY (1 << 1) | ||
8819 | + | ||
8820 | +struct isp_csi2_device { | ||
8821 | + struct v4l2_subdev subdev; | ||
8822 | + struct media_pad pads[CSI2_PADS_NUM]; | ||
8823 | + struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM]; | ||
8824 | + | ||
8825 | + struct v4l2_ctrl_handler ctrls; | ||
8826 | + | ||
8827 | + struct isp_video video_out; | ||
8828 | + struct isp_device *isp; | ||
8829 | + | ||
8830 | + u8 available; /* Is the IP present on the silicon? */ | ||
8831 | + | ||
8832 | + /* mem resources - enums as defined in enum isp_mem_resources */ | ||
8833 | + u8 regs1; | ||
8834 | + u8 regs2; | ||
8835 | + | ||
8836 | + u32 output; /* output to CCDC, memory or both? */ | ||
8837 | + bool dpcm_decompress; | ||
8838 | + unsigned int frame_skip; | ||
8839 | + bool use_fs_irq; | ||
8840 | + | ||
8841 | + struct isp_csiphy *phy; | ||
8842 | + struct isp_csi2_ctx_cfg contexts[ISP_CSI2_MAX_CTX_NUM + 1]; | ||
8843 | + struct isp_csi2_timing_cfg timing[2]; | ||
8844 | + struct isp_csi2_ctrl_cfg ctrl; | ||
8845 | + enum isp_pipeline_stream_state state; | ||
8846 | + wait_queue_head_t wait; | ||
8847 | + atomic_t stopping; | ||
8848 | +}; | ||
8849 | + | ||
8850 | +int omap3isp_csi2_isr(struct isp_csi2_device *csi2); | ||
8851 | +int omap3isp_csi2_reset(struct isp_csi2_device *csi2); | ||
8852 | +int omap3isp_csi2_init(struct isp_device *isp); | ||
8853 | +void omap3isp_csi2_cleanup(struct isp_device *isp); | ||
8854 | +void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2); | ||
8855 | +int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2, | ||
8856 | + struct v4l2_device *vdev); | ||
8857 | +#endif /* OMAP3_ISP_CSI2_H */ | ||
8858 | diff --git a/drivers/media/video/isp/ispcsiphy.c b/drivers/media/video/isp/ispcsiphy.c | ||
8859 | new file mode 100644 | ||
8860 | index 0000000..59cd477 | ||
8861 | --- /dev/null | ||
8862 | +++ b/drivers/media/video/isp/ispcsiphy.c | ||
8863 | @@ -0,0 +1,247 @@ | ||
8864 | +/* | ||
8865 | + * ispcsiphy.c | ||
8866 | + * | ||
8867 | + * TI OMAP3 ISP - CSI PHY module | ||
8868 | + * | ||
8869 | + * Copyright (C) 2010 Nokia Corporation | ||
8870 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
8871 | + * | ||
8872 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
8873 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
8874 | + * | ||
8875 | + * This program is free software; you can redistribute it and/or modify | ||
8876 | + * it under the terms of the GNU General Public License version 2 as | ||
8877 | + * published by the Free Software Foundation. | ||
8878 | + * | ||
8879 | + * This program is distributed in the hope that it will be useful, but | ||
8880 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8881 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
8882 | + * General Public License for more details. | ||
8883 | + * | ||
8884 | + * You should have received a copy of the GNU General Public License | ||
8885 | + * along with this program; if not, write to the Free Software | ||
8886 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
8887 | + * 02110-1301 USA | ||
8888 | + */ | ||
8889 | + | ||
8890 | +#include <linux/delay.h> | ||
8891 | +#include <linux/device.h> | ||
8892 | +#include <linux/regulator/consumer.h> | ||
8893 | + | ||
8894 | +#include "isp.h" | ||
8895 | +#include "ispreg.h" | ||
8896 | +#include "ispcsiphy.h" | ||
8897 | + | ||
8898 | +/* | ||
8899 | + * csiphy_lanes_config - Configuration of CSIPHY lanes. | ||
8900 | + * | ||
8901 | + * Updates HW configuration. | ||
8902 | + * Called with phy->mutex taken. | ||
8903 | + */ | ||
8904 | +static void csiphy_lanes_config(struct isp_csiphy *phy) | ||
8905 | +{ | ||
8906 | + unsigned int i; | ||
8907 | + u32 reg; | ||
8908 | + | ||
8909 | + reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG); | ||
8910 | + | ||
8911 | + for (i = 0; i < phy->num_data_lanes; i++) { | ||
8912 | + reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) | | ||
8913 | + ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1)); | ||
8914 | + reg |= (phy->lanes.data[i].pol << | ||
8915 | + ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1)); | ||
8916 | + reg |= (phy->lanes.data[i].pos << | ||
8917 | + ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1)); | ||
8918 | + } | ||
8919 | + | ||
8920 | + reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK | | ||
8921 | + ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK); | ||
8922 | + reg |= phy->lanes.clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT; | ||
8923 | + reg |= phy->lanes.clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT; | ||
8924 | + | ||
8925 | + isp_reg_writel(phy->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG); | ||
8926 | +} | ||
8927 | + | ||
8928 | +/* | ||
8929 | + * csiphy_power_autoswitch_enable | ||
8930 | + * @enable: Sets or clears the autoswitch function enable flag. | ||
8931 | + */ | ||
8932 | +static void csiphy_power_autoswitch_enable(struct isp_csiphy *phy, bool enable) | ||
8933 | +{ | ||
8934 | + isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, | ||
8935 | + ISPCSI2_PHY_CFG_PWR_AUTO, | ||
8936 | + enable ? ISPCSI2_PHY_CFG_PWR_AUTO : 0); | ||
8937 | +} | ||
8938 | + | ||
8939 | +/* | ||
8940 | + * csiphy_set_power | ||
8941 | + * @power: Power state to be set. | ||
8942 | + * | ||
8943 | + * Returns 0 if successful, or -EBUSY if the retry count is exceeded. | ||
8944 | + */ | ||
8945 | +static int csiphy_set_power(struct isp_csiphy *phy, u32 power) | ||
8946 | +{ | ||
8947 | + u32 reg; | ||
8948 | + u8 retry_count; | ||
8949 | + | ||
8950 | + isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, | ||
8951 | + ISPCSI2_PHY_CFG_PWR_CMD_MASK, power); | ||
8952 | + | ||
8953 | + retry_count = 0; | ||
8954 | + do { | ||
8955 | + udelay(50); | ||
8956 | + reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG) & | ||
8957 | + ISPCSI2_PHY_CFG_PWR_STATUS_MASK; | ||
8958 | + | ||
8959 | + if (reg != power >> 2) | ||
8960 | + retry_count++; | ||
8961 | + | ||
8962 | + } while ((reg != power >> 2) && (retry_count < 100)); | ||
8963 | + | ||
8964 | + if (retry_count == 100) { | ||
8965 | + printk(KERN_ERR "CSI2 CIO set power failed!\n"); | ||
8966 | + return -EBUSY; | ||
8967 | + } | ||
8968 | + | ||
8969 | + return 0; | ||
8970 | +} | ||
8971 | + | ||
8972 | +/* | ||
8973 | + * csiphy_dphy_config - Configure CSI2 D-PHY parameters. | ||
8974 | + * | ||
8975 | + * Called with phy->mutex taken. | ||
8976 | + */ | ||
8977 | +static void csiphy_dphy_config(struct isp_csiphy *phy) | ||
8978 | +{ | ||
8979 | + u32 reg; | ||
8980 | + | ||
8981 | + /* Set up ISPCSIPHY_REG0 */ | ||
8982 | + reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG0); | ||
8983 | + | ||
8984 | + reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK | | ||
8985 | + ISPCSIPHY_REG0_THS_SETTLE_MASK); | ||
8986 | + reg |= phy->dphy.ths_term << ISPCSIPHY_REG0_THS_TERM_SHIFT; | ||
8987 | + reg |= phy->dphy.ths_settle << ISPCSIPHY_REG0_THS_SETTLE_SHIFT; | ||
8988 | + | ||
8989 | + isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG0); | ||
8990 | + | ||
8991 | + /* Set up ISPCSIPHY_REG1 */ | ||
8992 | + reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG1); | ||
8993 | + | ||
8994 | + reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK | | ||
8995 | + ISPCSIPHY_REG1_TCLK_MISS_MASK | | ||
8996 | + ISPCSIPHY_REG1_TCLK_SETTLE_MASK); | ||
8997 | + reg |= phy->dphy.tclk_term << ISPCSIPHY_REG1_TCLK_TERM_SHIFT; | ||
8998 | + reg |= phy->dphy.tclk_miss << ISPCSIPHY_REG1_TCLK_MISS_SHIFT; | ||
8999 | + reg |= phy->dphy.tclk_settle << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT; | ||
9000 | + | ||
9001 | + isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG1); | ||
9002 | +} | ||
9003 | + | ||
9004 | +static int csiphy_config(struct isp_csiphy *phy, | ||
9005 | + struct isp_csiphy_dphy_cfg *dphy, | ||
9006 | + struct isp_csiphy_lanes_cfg *lanes) | ||
9007 | +{ | ||
9008 | + unsigned int used_lanes = 0; | ||
9009 | + unsigned int i; | ||
9010 | + | ||
9011 | + /* Clock and data lanes verification */ | ||
9012 | + for (i = 0; i < phy->num_data_lanes; i++) { | ||
9013 | + if (lanes->data[i].pol > 1 || lanes->data[i].pos > 3) | ||
9014 | + return -EINVAL; | ||
9015 | + | ||
9016 | + if (used_lanes & (1 << lanes->data[i].pos)) | ||
9017 | + return -EINVAL; | ||
9018 | + | ||
9019 | + used_lanes |= 1 << lanes->data[i].pos; | ||
9020 | + } | ||
9021 | + | ||
9022 | + if (lanes->clk.pol > 1 || lanes->clk.pos > 3) | ||
9023 | + return -EINVAL; | ||
9024 | + | ||
9025 | + if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos)) | ||
9026 | + return -EINVAL; | ||
9027 | + | ||
9028 | + mutex_lock(&phy->mutex); | ||
9029 | + phy->dphy = *dphy; | ||
9030 | + phy->lanes = *lanes; | ||
9031 | + mutex_unlock(&phy->mutex); | ||
9032 | + | ||
9033 | + return 0; | ||
9034 | +} | ||
9035 | + | ||
9036 | +int omap3isp_csiphy_acquire(struct isp_csiphy *phy) | ||
9037 | +{ | ||
9038 | + int rval; | ||
9039 | + | ||
9040 | + if (phy->vdd == NULL) { | ||
9041 | + dev_err(phy->isp->dev, "Power regulator for CSI PHY not " | ||
9042 | + "available\n"); | ||
9043 | + return -ENODEV; | ||
9044 | + } | ||
9045 | + | ||
9046 | + mutex_lock(&phy->mutex); | ||
9047 | + | ||
9048 | + rval = regulator_enable(phy->vdd); | ||
9049 | + if (rval < 0) | ||
9050 | + goto done; | ||
9051 | + | ||
9052 | + omap3isp_csi2_reset(phy->csi2); | ||
9053 | + | ||
9054 | + csiphy_dphy_config(phy); | ||
9055 | + csiphy_lanes_config(phy); | ||
9056 | + | ||
9057 | + rval = csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_ON); | ||
9058 | + if (rval) { | ||
9059 | + regulator_disable(phy->vdd); | ||
9060 | + goto done; | ||
9061 | + } | ||
9062 | + | ||
9063 | + csiphy_power_autoswitch_enable(phy, true); | ||
9064 | + phy->phy_in_use = 1; | ||
9065 | + | ||
9066 | +done: | ||
9067 | + mutex_unlock(&phy->mutex); | ||
9068 | + return rval; | ||
9069 | +} | ||
9070 | + | ||
9071 | +void omap3isp_csiphy_release(struct isp_csiphy *phy) | ||
9072 | +{ | ||
9073 | + mutex_lock(&phy->mutex); | ||
9074 | + if (phy->phy_in_use) { | ||
9075 | + csiphy_power_autoswitch_enable(phy, false); | ||
9076 | + csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF); | ||
9077 | + regulator_disable(phy->vdd); | ||
9078 | + phy->phy_in_use = 0; | ||
9079 | + } | ||
9080 | + mutex_unlock(&phy->mutex); | ||
9081 | +} | ||
9082 | + | ||
9083 | +/* | ||
9084 | + * omap3isp_csiphy_init - Initialize the CSI PHY frontends | ||
9085 | + */ | ||
9086 | +int omap3isp_csiphy_init(struct isp_device *isp) | ||
9087 | +{ | ||
9088 | + struct isp_csiphy *phy1 = &isp->isp_csiphy1; | ||
9089 | + struct isp_csiphy *phy2 = &isp->isp_csiphy2; | ||
9090 | + | ||
9091 | + isp->platform_cb.csiphy_config = csiphy_config; | ||
9092 | + | ||
9093 | + phy2->isp = isp; | ||
9094 | + phy2->csi2 = &isp->isp_csi2a; | ||
9095 | + phy2->num_data_lanes = ISP_CSIPHY2_NUM_DATA_LANES; | ||
9096 | + phy2->cfg_regs = OMAP3_ISP_IOMEM_CSI2A_REGS1; | ||
9097 | + phy2->phy_regs = OMAP3_ISP_IOMEM_CSIPHY2; | ||
9098 | + mutex_init(&phy2->mutex); | ||
9099 | + | ||
9100 | + if (isp->revision == ISP_REVISION_15_0) { | ||
9101 | + phy1->isp = isp; | ||
9102 | + phy1->csi2 = &isp->isp_csi2c; | ||
9103 | + phy1->num_data_lanes = ISP_CSIPHY1_NUM_DATA_LANES; | ||
9104 | + phy1->cfg_regs = OMAP3_ISP_IOMEM_CSI2C_REGS1; | ||
9105 | + phy1->phy_regs = OMAP3_ISP_IOMEM_CSIPHY1; | ||
9106 | + mutex_init(&phy1->mutex); | ||
9107 | + } | ||
9108 | + | ||
9109 | + return 0; | ||
9110 | +} | ||
9111 | diff --git a/drivers/media/video/isp/ispcsiphy.h b/drivers/media/video/isp/ispcsiphy.h | ||
9112 | new file mode 100644 | ||
9113 | index 0000000..39a7e6b | ||
9114 | --- /dev/null | ||
9115 | +++ b/drivers/media/video/isp/ispcsiphy.h | ||
9116 | @@ -0,0 +1,74 @@ | ||
9117 | +/* | ||
9118 | + * ispcsiphy.h | ||
9119 | + * | ||
9120 | + * TI OMAP3 ISP - CSI PHY module | ||
9121 | + * | ||
9122 | + * Copyright (C) 2010 Nokia Corporation | ||
9123 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
9124 | + * | ||
9125 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
9126 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
9127 | + * | ||
9128 | + * This program is free software; you can redistribute it and/or modify | ||
9129 | + * it under the terms of the GNU General Public License version 2 as | ||
9130 | + * published by the Free Software Foundation. | ||
9131 | + * | ||
9132 | + * This program is distributed in the hope that it will be useful, but | ||
9133 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9134 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
9135 | + * General Public License for more details. | ||
9136 | + * | ||
9137 | + * You should have received a copy of the GNU General Public License | ||
9138 | + * along with this program; if not, write to the Free Software | ||
9139 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
9140 | + * 02110-1301 USA | ||
9141 | + */ | ||
9142 | + | ||
9143 | +#ifndef OMAP3_ISP_CSI_PHY_H | ||
9144 | +#define OMAP3_ISP_CSI_PHY_H | ||
9145 | + | ||
9146 | +struct isp_csi2_device; | ||
9147 | +struct regulator; | ||
9148 | + | ||
9149 | +struct csiphy_lane { | ||
9150 | + u8 pos; | ||
9151 | + u8 pol; | ||
9152 | +}; | ||
9153 | + | ||
9154 | +#define ISP_CSIPHY2_NUM_DATA_LANES 2 | ||
9155 | +#define ISP_CSIPHY1_NUM_DATA_LANES 1 | ||
9156 | + | ||
9157 | +struct isp_csiphy_lanes_cfg { | ||
9158 | + struct csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; | ||
9159 | + struct csiphy_lane clk; | ||
9160 | +}; | ||
9161 | + | ||
9162 | +struct isp_csiphy_dphy_cfg { | ||
9163 | + u8 ths_term; | ||
9164 | + u8 ths_settle; | ||
9165 | + u8 tclk_term; | ||
9166 | + unsigned tclk_miss:1; | ||
9167 | + u8 tclk_settle; | ||
9168 | +}; | ||
9169 | + | ||
9170 | +struct isp_csiphy { | ||
9171 | + struct isp_device *isp; | ||
9172 | + struct mutex mutex; /* serialize csiphy configuration */ | ||
9173 | + u8 phy_in_use; | ||
9174 | + struct isp_csi2_device *csi2; | ||
9175 | + struct regulator *vdd; | ||
9176 | + | ||
9177 | + /* mem resources - enums as defined in enum isp_mem_resources */ | ||
9178 | + unsigned int cfg_regs; | ||
9179 | + unsigned int phy_regs; | ||
9180 | + | ||
9181 | + u8 num_data_lanes; /* number of CSI2 Data Lanes supported */ | ||
9182 | + struct isp_csiphy_lanes_cfg lanes; | ||
9183 | + struct isp_csiphy_dphy_cfg dphy; | ||
9184 | +}; | ||
9185 | + | ||
9186 | +int omap3isp_csiphy_acquire(struct isp_csiphy *phy); | ||
9187 | +void omap3isp_csiphy_release(struct isp_csiphy *phy); | ||
9188 | +int omap3isp_csiphy_init(struct isp_device *isp); | ||
9189 | + | ||
9190 | +#endif /* OMAP3_ISP_CSI_PHY_H */ | ||
9191 | diff --git a/drivers/media/video/isp/isph3a.h b/drivers/media/video/isp/isph3a.h | ||
9192 | new file mode 100644 | ||
9193 | index 0000000..6d43529 | ||
9194 | --- /dev/null | ||
9195 | +++ b/drivers/media/video/isp/isph3a.h | ||
9196 | @@ -0,0 +1,117 @@ | ||
9197 | +/* | ||
9198 | + * isph3a.h | ||
9199 | + * | ||
9200 | + * TI OMAP3 ISP - H3A AF module | ||
9201 | + * | ||
9202 | + * Copyright (C) 2010 Nokia Corporation | ||
9203 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
9204 | + * | ||
9205 | + * Contacts: David Cohen <david.cohen@nokia.com> | ||
9206 | + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
9207 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
9208 | + * | ||
9209 | + * This program is free software; you can redistribute it and/or modify | ||
9210 | + * it under the terms of the GNU General Public License version 2 as | ||
9211 | + * published by the Free Software Foundation. | ||
9212 | + * | ||
9213 | + * This program is distributed in the hope that it will be useful, but | ||
9214 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9215 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
9216 | + * General Public License for more details. | ||
9217 | + * | ||
9218 | + * You should have received a copy of the GNU General Public License | ||
9219 | + * along with this program; if not, write to the Free Software | ||
9220 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
9221 | + * 02110-1301 USA | ||
9222 | + */ | ||
9223 | + | ||
9224 | +#ifndef OMAP3_ISP_H3A_H | ||
9225 | +#define OMAP3_ISP_H3A_H | ||
9226 | + | ||
9227 | +#include <linux/omap3isp.h> | ||
9228 | + | ||
9229 | +/* | ||
9230 | + * ---------- | ||
9231 | + * -H3A AEWB- | ||
9232 | + * ---------- | ||
9233 | + */ | ||
9234 | + | ||
9235 | +#define AEWB_PACKET_SIZE 16 | ||
9236 | +#define AEWB_SATURATION_LIMIT 0x3ff | ||
9237 | + | ||
9238 | +/* Flags for changed registers */ | ||
9239 | +#define PCR_CHNG (1 << 0) | ||
9240 | +#define AEWWIN1_CHNG (1 << 1) | ||
9241 | +#define AEWINSTART_CHNG (1 << 2) | ||
9242 | +#define AEWINBLK_CHNG (1 << 3) | ||
9243 | +#define AEWSUBWIN_CHNG (1 << 4) | ||
9244 | +#define PRV_WBDGAIN_CHNG (1 << 5) | ||
9245 | +#define PRV_WBGAIN_CHNG (1 << 6) | ||
9246 | + | ||
9247 | +/* ISPH3A REGISTERS bits */ | ||
9248 | +#define ISPH3A_PCR_AF_EN (1 << 0) | ||
9249 | +#define ISPH3A_PCR_AF_ALAW_EN (1 << 1) | ||
9250 | +#define ISPH3A_PCR_AF_MED_EN (1 << 2) | ||
9251 | +#define ISPH3A_PCR_AF_BUSY (1 << 15) | ||
9252 | +#define ISPH3A_PCR_AEW_EN (1 << 16) | ||
9253 | +#define ISPH3A_PCR_AEW_ALAW_EN (1 << 17) | ||
9254 | +#define ISPH3A_PCR_AEW_BUSY (1 << 18) | ||
9255 | +#define ISPH3A_PCR_AEW_MASK (ISPH3A_PCR_AEW_ALAW_EN | \ | ||
9256 | + ISPH3A_PCR_AEW_AVE2LMT_MASK) | ||
9257 | + | ||
9258 | +/* | ||
9259 | + * -------- | ||
9260 | + * -H3A AF- | ||
9261 | + * -------- | ||
9262 | + */ | ||
9263 | + | ||
9264 | +/* Peripheral Revision */ | ||
9265 | +#define AFPID 0x0 | ||
9266 | + | ||
9267 | +#define AFCOEF_OFFSET 0x00000004 /* COEF base address */ | ||
9268 | + | ||
9269 | +/* PCR fields */ | ||
9270 | +#define AF_BUSYAF (1 << 15) | ||
9271 | +#define AF_FVMODE (1 << 14) | ||
9272 | +#define AF_RGBPOS (0x7 << 11) | ||
9273 | +#define AF_MED_TH (0xFF << 3) | ||
9274 | +#define AF_MED_EN (1 << 2) | ||
9275 | +#define AF_ALAW_EN (1 << 1) | ||
9276 | +#define AF_EN (1 << 0) | ||
9277 | +#define AF_PCR_MASK (AF_FVMODE | AF_RGBPOS | AF_MED_TH | \ | ||
9278 | + AF_MED_EN | AF_ALAW_EN) | ||
9279 | + | ||
9280 | +/* AFPAX1 fields */ | ||
9281 | +#define AF_PAXW (0x7F << 16) | ||
9282 | +#define AF_PAXH 0x7F | ||
9283 | + | ||
9284 | +/* AFPAX2 fields */ | ||
9285 | +#define AF_AFINCV (0xF << 13) | ||
9286 | +#define AF_PAXVC (0x7F << 6) | ||
9287 | +#define AF_PAXHC 0x3F | ||
9288 | + | ||
9289 | +/* AFPAXSTART fields */ | ||
9290 | +#define AF_PAXSH (0xFFF<<16) | ||
9291 | +#define AF_PAXSV 0xFFF | ||
9292 | + | ||
9293 | +/* COEFFICIENT MASK */ | ||
9294 | +#define AF_COEF_MASK0 0xFFF | ||
9295 | +#define AF_COEF_MASK1 (0xFFF<<16) | ||
9296 | + | ||
9297 | +/* BIT SHIFTS */ | ||
9298 | +#define AF_RGBPOS_SHIFT 11 | ||
9299 | +#define AF_MED_TH_SHIFT 3 | ||
9300 | +#define AF_PAXW_SHIFT 16 | ||
9301 | +#define AF_LINE_INCR_SHIFT 13 | ||
9302 | +#define AF_VT_COUNT_SHIFT 6 | ||
9303 | +#define AF_HZ_START_SHIFT 16 | ||
9304 | +#define AF_COEF_SHIFT 16 | ||
9305 | + | ||
9306 | +/* Init and cleanup functions */ | ||
9307 | +int omap3isp_h3a_aewb_init(struct isp_device *isp); | ||
9308 | +int omap3isp_h3a_af_init(struct isp_device *isp); | ||
9309 | + | ||
9310 | +void omap3isp_h3a_aewb_cleanup(struct isp_device *isp); | ||
9311 | +void omap3isp_h3a_af_cleanup(struct isp_device *isp); | ||
9312 | + | ||
9313 | +#endif /* OMAP3_ISP_H3A_H */ | ||
9314 | diff --git a/drivers/media/video/isp/isph3a_aewb.c b/drivers/media/video/isp/isph3a_aewb.c | ||
9315 | new file mode 100644 | ||
9316 | index 0000000..b4e97f2 | ||
9317 | --- /dev/null | ||
9318 | +++ b/drivers/media/video/isp/isph3a_aewb.c | ||
9319 | @@ -0,0 +1,374 @@ | ||
9320 | +/* | ||
9321 | + * isph3a.c | ||
9322 | + * | ||
9323 | + * TI OMAP3 ISP - H3A module | ||
9324 | + * | ||
9325 | + * Copyright (C) 2010 Nokia Corporation | ||
9326 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
9327 | + * | ||
9328 | + * Contacts: David Cohen <david.cohen@nokia.com> | ||
9329 | + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
9330 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
9331 | + * | ||
9332 | + * This program is free software; you can redistribute it and/or modify | ||
9333 | + * it under the terms of the GNU General Public License version 2 as | ||
9334 | + * published by the Free Software Foundation. | ||
9335 | + * | ||
9336 | + * This program is distributed in the hope that it will be useful, but | ||
9337 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9338 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
9339 | + * General Public License for more details. | ||
9340 | + * | ||
9341 | + * You should have received a copy of the GNU General Public License | ||
9342 | + * along with this program; if not, write to the Free Software | ||
9343 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
9344 | + * 02110-1301 USA | ||
9345 | + */ | ||
9346 | + | ||
9347 | +#include <linux/slab.h> | ||
9348 | +#include <linux/uaccess.h> | ||
9349 | + | ||
9350 | +#include "isp.h" | ||
9351 | +#include "isph3a.h" | ||
9352 | +#include "ispstat.h" | ||
9353 | + | ||
9354 | +/* | ||
9355 | + * h3a_aewb_update_regs - Helper function to update h3a registers. | ||
9356 | + */ | ||
9357 | +static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv) | ||
9358 | +{ | ||
9359 | + struct omap3isp_h3a_aewb_config *conf = priv; | ||
9360 | + u32 pcr; | ||
9361 | + u32 win1; | ||
9362 | + u32 start; | ||
9363 | + u32 blk; | ||
9364 | + u32 subwin; | ||
9365 | + | ||
9366 | + if (aewb->state == ISPSTAT_DISABLED) | ||
9367 | + return; | ||
9368 | + | ||
9369 | + isp_reg_writel(aewb->isp, aewb->active_buf->iommu_addr, | ||
9370 | + OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST); | ||
9371 | + | ||
9372 | + if (!aewb->update) | ||
9373 | + return; | ||
9374 | + | ||
9375 | + /* Converting config metadata into reg values */ | ||
9376 | + pcr = conf->saturation_limit << ISPH3A_PCR_AEW_AVE2LMT_SHIFT; | ||
9377 | + pcr |= !!conf->alaw_enable << ISPH3A_PCR_AEW_ALAW_EN_SHIFT; | ||
9378 | + | ||
9379 | + win1 = ((conf->win_height >> 1) - 1) << ISPH3A_AEWWIN1_WINH_SHIFT; | ||
9380 | + win1 |= ((conf->win_width >> 1) - 1) << ISPH3A_AEWWIN1_WINW_SHIFT; | ||
9381 | + win1 |= (conf->ver_win_count - 1) << ISPH3A_AEWWIN1_WINVC_SHIFT; | ||
9382 | + win1 |= (conf->hor_win_count - 1) << ISPH3A_AEWWIN1_WINHC_SHIFT; | ||
9383 | + | ||
9384 | + start = conf->hor_win_start << ISPH3A_AEWINSTART_WINSH_SHIFT; | ||
9385 | + start |= conf->ver_win_start << ISPH3A_AEWINSTART_WINSV_SHIFT; | ||
9386 | + | ||
9387 | + blk = conf->blk_ver_win_start << ISPH3A_AEWINBLK_WINSV_SHIFT; | ||
9388 | + blk |= ((conf->blk_win_height >> 1) - 1) << ISPH3A_AEWINBLK_WINH_SHIFT; | ||
9389 | + | ||
9390 | + subwin = ((conf->subsample_ver_inc >> 1) - 1) << | ||
9391 | + ISPH3A_AEWSUBWIN_AEWINCV_SHIFT; | ||
9392 | + subwin |= ((conf->subsample_hor_inc >> 1) - 1) << | ||
9393 | + ISPH3A_AEWSUBWIN_AEWINCH_SHIFT; | ||
9394 | + | ||
9395 | + isp_reg_writel(aewb->isp, win1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1); | ||
9396 | + isp_reg_writel(aewb->isp, start, OMAP3_ISP_IOMEM_H3A, | ||
9397 | + ISPH3A_AEWINSTART); | ||
9398 | + isp_reg_writel(aewb->isp, blk, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK); | ||
9399 | + isp_reg_writel(aewb->isp, subwin, OMAP3_ISP_IOMEM_H3A, | ||
9400 | + ISPH3A_AEWSUBWIN); | ||
9401 | + isp_reg_clr_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
9402 | + ISPH3A_PCR_AEW_MASK, pcr); | ||
9403 | + | ||
9404 | + aewb->update = 0; | ||
9405 | + aewb->config_counter += aewb->inc_config; | ||
9406 | + aewb->inc_config = 0; | ||
9407 | + aewb->buf_size = conf->buf_size; | ||
9408 | +} | ||
9409 | + | ||
9410 | +static void h3a_aewb_enable(struct ispstat *aewb, int enable) | ||
9411 | +{ | ||
9412 | + if (enable) { | ||
9413 | + isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
9414 | + ISPH3A_PCR_AEW_EN); | ||
9415 | + /* This bit is already set if AF is enabled */ | ||
9416 | + if (aewb->isp->isp_af.state != ISPSTAT_ENABLED) | ||
9417 | + isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
9418 | + ISPCTRL_H3A_CLK_EN); | ||
9419 | + } else { | ||
9420 | + isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
9421 | + ISPH3A_PCR_AEW_EN); | ||
9422 | + /* This bit can't be cleared if AF is enabled */ | ||
9423 | + if (aewb->isp->isp_af.state != ISPSTAT_ENABLED) | ||
9424 | + isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
9425 | + ISPCTRL_H3A_CLK_EN); | ||
9426 | + } | ||
9427 | +} | ||
9428 | + | ||
9429 | +static int h3a_aewb_busy(struct ispstat *aewb) | ||
9430 | +{ | ||
9431 | + return isp_reg_readl(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) | ||
9432 | + & ISPH3A_PCR_BUSYAEAWB; | ||
9433 | +} | ||
9434 | + | ||
9435 | +static u32 h3a_aewb_get_buf_size(struct omap3isp_h3a_aewb_config *conf) | ||
9436 | +{ | ||
9437 | + /* Number of configured windows + extra row for black data */ | ||
9438 | + u32 win_count = (conf->ver_win_count + 1) * conf->hor_win_count; | ||
9439 | + | ||
9440 | + /* | ||
9441 | + * Unsaturated block counts for each 8 windows. | ||
9442 | + * 1 extra for the last (win_count % 8) windows if win_count is not | ||
9443 | + * divisible by 8. | ||
9444 | + */ | ||
9445 | + win_count += (win_count + 7) / 8; | ||
9446 | + | ||
9447 | + return win_count * AEWB_PACKET_SIZE; | ||
9448 | +} | ||
9449 | + | ||
9450 | +static int h3a_aewb_validate_params(struct ispstat *aewb, void *new_conf) | ||
9451 | +{ | ||
9452 | + struct omap3isp_h3a_aewb_config *user_cfg = new_conf; | ||
9453 | + u32 buf_size; | ||
9454 | + | ||
9455 | + if (unlikely(user_cfg->saturation_limit > | ||
9456 | + OMAP3ISP_AEWB_MAX_SATURATION_LIM)) | ||
9457 | + return -EINVAL; | ||
9458 | + | ||
9459 | + if (unlikely(user_cfg->win_height < OMAP3ISP_AEWB_MIN_WIN_H || | ||
9460 | + user_cfg->win_height > OMAP3ISP_AEWB_MAX_WIN_H || | ||
9461 | + user_cfg->win_height & 0x01)) | ||
9462 | + return -EINVAL; | ||
9463 | + | ||
9464 | + if (unlikely(user_cfg->win_width < OMAP3ISP_AEWB_MIN_WIN_W || | ||
9465 | + user_cfg->win_width > OMAP3ISP_AEWB_MAX_WIN_W || | ||
9466 | + user_cfg->win_width & 0x01)) | ||
9467 | + return -EINVAL; | ||
9468 | + | ||
9469 | + if (unlikely(user_cfg->ver_win_count < OMAP3ISP_AEWB_MIN_WINVC || | ||
9470 | + user_cfg->ver_win_count > OMAP3ISP_AEWB_MAX_WINVC)) | ||
9471 | + return -EINVAL; | ||
9472 | + | ||
9473 | + if (unlikely(user_cfg->hor_win_count < OMAP3ISP_AEWB_MIN_WINHC || | ||
9474 | + user_cfg->hor_win_count > OMAP3ISP_AEWB_MAX_WINHC)) | ||
9475 | + return -EINVAL; | ||
9476 | + | ||
9477 | + if (unlikely(user_cfg->ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) | ||
9478 | + return -EINVAL; | ||
9479 | + | ||
9480 | + if (unlikely(user_cfg->hor_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) | ||
9481 | + return -EINVAL; | ||
9482 | + | ||
9483 | + if (unlikely(user_cfg->blk_ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) | ||
9484 | + return -EINVAL; | ||
9485 | + | ||
9486 | + if (unlikely(user_cfg->blk_win_height < OMAP3ISP_AEWB_MIN_WIN_H || | ||
9487 | + user_cfg->blk_win_height > OMAP3ISP_AEWB_MAX_WIN_H || | ||
9488 | + user_cfg->blk_win_height & 0x01)) | ||
9489 | + return -EINVAL; | ||
9490 | + | ||
9491 | + if (unlikely(user_cfg->subsample_ver_inc < OMAP3ISP_AEWB_MIN_SUB_INC || | ||
9492 | + user_cfg->subsample_ver_inc > OMAP3ISP_AEWB_MAX_SUB_INC || | ||
9493 | + user_cfg->subsample_ver_inc & 0x01)) | ||
9494 | + return -EINVAL; | ||
9495 | + | ||
9496 | + if (unlikely(user_cfg->subsample_hor_inc < OMAP3ISP_AEWB_MIN_SUB_INC || | ||
9497 | + user_cfg->subsample_hor_inc > OMAP3ISP_AEWB_MAX_SUB_INC || | ||
9498 | + user_cfg->subsample_hor_inc & 0x01)) | ||
9499 | + return -EINVAL; | ||
9500 | + | ||
9501 | + buf_size = h3a_aewb_get_buf_size(user_cfg); | ||
9502 | + if (buf_size > user_cfg->buf_size) | ||
9503 | + user_cfg->buf_size = buf_size; | ||
9504 | + else if (user_cfg->buf_size > OMAP3ISP_AEWB_MAX_BUF_SIZE) | ||
9505 | + user_cfg->buf_size = OMAP3ISP_AEWB_MAX_BUF_SIZE; | ||
9506 | + | ||
9507 | + return 0; | ||
9508 | +} | ||
9509 | + | ||
9510 | +/* | ||
9511 | + * h3a_aewb_set_params - Helper function to check & store user given params. | ||
9512 | + * @new_conf: Pointer to AE and AWB parameters struct. | ||
9513 | + * | ||
9514 | + * As most of them are busy-lock registers, need to wait until AEW_BUSY = 0 to | ||
9515 | + * program them during ISR. | ||
9516 | + */ | ||
9517 | +static void h3a_aewb_set_params(struct ispstat *aewb, void *new_conf) | ||
9518 | +{ | ||
9519 | + struct omap3isp_h3a_aewb_config *user_cfg = new_conf; | ||
9520 | + struct omap3isp_h3a_aewb_config *cur_cfg = aewb->priv; | ||
9521 | + int update = 0; | ||
9522 | + | ||
9523 | + if (cur_cfg->saturation_limit != user_cfg->saturation_limit) { | ||
9524 | + cur_cfg->saturation_limit = user_cfg->saturation_limit; | ||
9525 | + update = 1; | ||
9526 | + } | ||
9527 | + if (cur_cfg->alaw_enable != user_cfg->alaw_enable) { | ||
9528 | + cur_cfg->alaw_enable = user_cfg->alaw_enable; | ||
9529 | + update = 1; | ||
9530 | + } | ||
9531 | + if (cur_cfg->win_height != user_cfg->win_height) { | ||
9532 | + cur_cfg->win_height = user_cfg->win_height; | ||
9533 | + update = 1; | ||
9534 | + } | ||
9535 | + if (cur_cfg->win_width != user_cfg->win_width) { | ||
9536 | + cur_cfg->win_width = user_cfg->win_width; | ||
9537 | + update = 1; | ||
9538 | + } | ||
9539 | + if (cur_cfg->ver_win_count != user_cfg->ver_win_count) { | ||
9540 | + cur_cfg->ver_win_count = user_cfg->ver_win_count; | ||
9541 | + update = 1; | ||
9542 | + } | ||
9543 | + if (cur_cfg->hor_win_count != user_cfg->hor_win_count) { | ||
9544 | + cur_cfg->hor_win_count = user_cfg->hor_win_count; | ||
9545 | + update = 1; | ||
9546 | + } | ||
9547 | + if (cur_cfg->ver_win_start != user_cfg->ver_win_start) { | ||
9548 | + cur_cfg->ver_win_start = user_cfg->ver_win_start; | ||
9549 | + update = 1; | ||
9550 | + } | ||
9551 | + if (cur_cfg->hor_win_start != user_cfg->hor_win_start) { | ||
9552 | + cur_cfg->hor_win_start = user_cfg->hor_win_start; | ||
9553 | + update = 1; | ||
9554 | + } | ||
9555 | + if (cur_cfg->blk_ver_win_start != user_cfg->blk_ver_win_start) { | ||
9556 | + cur_cfg->blk_ver_win_start = user_cfg->blk_ver_win_start; | ||
9557 | + update = 1; | ||
9558 | + } | ||
9559 | + if (cur_cfg->blk_win_height != user_cfg->blk_win_height) { | ||
9560 | + cur_cfg->blk_win_height = user_cfg->blk_win_height; | ||
9561 | + update = 1; | ||
9562 | + } | ||
9563 | + if (cur_cfg->subsample_ver_inc != user_cfg->subsample_ver_inc) { | ||
9564 | + cur_cfg->subsample_ver_inc = user_cfg->subsample_ver_inc; | ||
9565 | + update = 1; | ||
9566 | + } | ||
9567 | + if (cur_cfg->subsample_hor_inc != user_cfg->subsample_hor_inc) { | ||
9568 | + cur_cfg->subsample_hor_inc = user_cfg->subsample_hor_inc; | ||
9569 | + update = 1; | ||
9570 | + } | ||
9571 | + | ||
9572 | + if (update || !aewb->configured) { | ||
9573 | + aewb->inc_config++; | ||
9574 | + aewb->update = 1; | ||
9575 | + cur_cfg->buf_size = h3a_aewb_get_buf_size(cur_cfg); | ||
9576 | + } | ||
9577 | +} | ||
9578 | + | ||
9579 | +static long h3a_aewb_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
9580 | +{ | ||
9581 | + struct ispstat *stat = v4l2_get_subdevdata(sd); | ||
9582 | + | ||
9583 | + switch (cmd) { | ||
9584 | + case VIDIOC_OMAP3ISP_AEWB_CFG: | ||
9585 | + return omap3isp_stat_config(stat, arg); | ||
9586 | + case VIDIOC_OMAP3ISP_STAT_REQ: | ||
9587 | + return omap3isp_stat_request_statistics(stat, arg); | ||
9588 | + case VIDIOC_OMAP3ISP_STAT_EN: { | ||
9589 | + unsigned long *en = arg; | ||
9590 | + return omap3isp_stat_enable(stat, !!*en); | ||
9591 | + } | ||
9592 | + } | ||
9593 | + | ||
9594 | + return -ENOIOCTLCMD; | ||
9595 | +} | ||
9596 | + | ||
9597 | +static const struct ispstat_ops h3a_aewb_ops = { | ||
9598 | + .validate_params = h3a_aewb_validate_params, | ||
9599 | + .set_params = h3a_aewb_set_params, | ||
9600 | + .setup_regs = h3a_aewb_setup_regs, | ||
9601 | + .enable = h3a_aewb_enable, | ||
9602 | + .busy = h3a_aewb_busy, | ||
9603 | +}; | ||
9604 | + | ||
9605 | +static const struct v4l2_subdev_core_ops h3a_aewb_subdev_core_ops = { | ||
9606 | + .ioctl = h3a_aewb_ioctl, | ||
9607 | + .subscribe_event = omap3isp_stat_subscribe_event, | ||
9608 | + .unsubscribe_event = omap3isp_stat_unsubscribe_event, | ||
9609 | +}; | ||
9610 | + | ||
9611 | +static const struct v4l2_subdev_video_ops h3a_aewb_subdev_video_ops = { | ||
9612 | + .s_stream = omap3isp_stat_s_stream, | ||
9613 | +}; | ||
9614 | + | ||
9615 | +static const struct v4l2_subdev_ops h3a_aewb_subdev_ops = { | ||
9616 | + .core = &h3a_aewb_subdev_core_ops, | ||
9617 | + .video = &h3a_aewb_subdev_video_ops, | ||
9618 | +}; | ||
9619 | + | ||
9620 | +/* | ||
9621 | + * omap3isp_h3a_aewb_init - Module Initialisation. | ||
9622 | + */ | ||
9623 | +int omap3isp_h3a_aewb_init(struct isp_device *isp) | ||
9624 | +{ | ||
9625 | + struct ispstat *aewb = &isp->isp_aewb; | ||
9626 | + struct omap3isp_h3a_aewb_config *aewb_cfg; | ||
9627 | + struct omap3isp_h3a_aewb_config *aewb_recover_cfg; | ||
9628 | + int ret; | ||
9629 | + | ||
9630 | + aewb_cfg = kzalloc(sizeof(*aewb_cfg), GFP_KERNEL); | ||
9631 | + if (!aewb_cfg) | ||
9632 | + return -ENOMEM; | ||
9633 | + | ||
9634 | + memset(aewb, 0, sizeof(*aewb)); | ||
9635 | + aewb->ops = &h3a_aewb_ops; | ||
9636 | + aewb->priv = aewb_cfg; | ||
9637 | + aewb->dma_ch = -1; | ||
9638 | + aewb->event_type = V4L2_EVENT_OMAP3ISP_AEWB; | ||
9639 | + aewb->isp = isp; | ||
9640 | + | ||
9641 | + /* Set recover state configuration */ | ||
9642 | + aewb_recover_cfg = kzalloc(sizeof(*aewb_recover_cfg), GFP_KERNEL); | ||
9643 | + if (!aewb_recover_cfg) { | ||
9644 | + dev_err(aewb->isp->dev, "AEWB: cannot allocate memory for " | ||
9645 | + "recover configuration.\n"); | ||
9646 | + ret = -ENOMEM; | ||
9647 | + goto err_recover_alloc; | ||
9648 | + } | ||
9649 | + | ||
9650 | + aewb_recover_cfg->saturation_limit = OMAP3ISP_AEWB_MAX_SATURATION_LIM; | ||
9651 | + aewb_recover_cfg->win_height = OMAP3ISP_AEWB_MIN_WIN_H; | ||
9652 | + aewb_recover_cfg->win_width = OMAP3ISP_AEWB_MIN_WIN_W; | ||
9653 | + aewb_recover_cfg->ver_win_count = OMAP3ISP_AEWB_MIN_WINVC; | ||
9654 | + aewb_recover_cfg->hor_win_count = OMAP3ISP_AEWB_MIN_WINHC; | ||
9655 | + aewb_recover_cfg->blk_ver_win_start = aewb_recover_cfg->ver_win_start + | ||
9656 | + aewb_recover_cfg->win_height * aewb_recover_cfg->ver_win_count; | ||
9657 | + aewb_recover_cfg->blk_win_height = OMAP3ISP_AEWB_MIN_WIN_H; | ||
9658 | + aewb_recover_cfg->subsample_ver_inc = OMAP3ISP_AEWB_MIN_SUB_INC; | ||
9659 | + aewb_recover_cfg->subsample_hor_inc = OMAP3ISP_AEWB_MIN_SUB_INC; | ||
9660 | + | ||
9661 | + if (h3a_aewb_validate_params(aewb, aewb_recover_cfg)) { | ||
9662 | + dev_err(aewb->isp->dev, "AEWB: recover configuration is " | ||
9663 | + "invalid.\n"); | ||
9664 | + ret = -EINVAL; | ||
9665 | + goto err_conf; | ||
9666 | + } | ||
9667 | + | ||
9668 | + aewb_recover_cfg->buf_size = h3a_aewb_get_buf_size(aewb_recover_cfg); | ||
9669 | + aewb->recover_priv = aewb_recover_cfg; | ||
9670 | + | ||
9671 | + ret = omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops); | ||
9672 | + if (ret) | ||
9673 | + goto err_conf; | ||
9674 | + | ||
9675 | + return 0; | ||
9676 | + | ||
9677 | +err_conf: | ||
9678 | + kfree(aewb_recover_cfg); | ||
9679 | +err_recover_alloc: | ||
9680 | + kfree(aewb_cfg); | ||
9681 | + | ||
9682 | + return ret; | ||
9683 | +} | ||
9684 | + | ||
9685 | +/* | ||
9686 | + * omap3isp_h3a_aewb_cleanup - Module exit. | ||
9687 | + */ | ||
9688 | +void omap3isp_h3a_aewb_cleanup(struct isp_device *isp) | ||
9689 | +{ | ||
9690 | + kfree(isp->isp_aewb.priv); | ||
9691 | + kfree(isp->isp_aewb.recover_priv); | ||
9692 | + omap3isp_stat_free(&isp->isp_aewb); | ||
9693 | +} | ||
9694 | diff --git a/drivers/media/video/isp/isph3a_af.c b/drivers/media/video/isp/isph3a_af.c | ||
9695 | new file mode 100644 | ||
9696 | index 0000000..c32a18e | ||
9697 | --- /dev/null | ||
9698 | +++ b/drivers/media/video/isp/isph3a_af.c | ||
9699 | @@ -0,0 +1,429 @@ | ||
9700 | +/* | ||
9701 | + * isph3a_af.c | ||
9702 | + * | ||
9703 | + * TI OMAP3 ISP - H3A AF module | ||
9704 | + * | ||
9705 | + * Copyright (C) 2010 Nokia Corporation | ||
9706 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
9707 | + * | ||
9708 | + * Contacts: David Cohen <david.cohen@nokia.com> | ||
9709 | + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
9710 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
9711 | + * | ||
9712 | + * This program is free software; you can redistribute it and/or modify | ||
9713 | + * it under the terms of the GNU General Public License version 2 as | ||
9714 | + * published by the Free Software Foundation. | ||
9715 | + * | ||
9716 | + * This program is distributed in the hope that it will be useful, but | ||
9717 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9718 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
9719 | + * General Public License for more details. | ||
9720 | + * | ||
9721 | + * You should have received a copy of the GNU General Public License | ||
9722 | + * along with this program; if not, write to the Free Software | ||
9723 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
9724 | + * 02110-1301 USA | ||
9725 | + */ | ||
9726 | + | ||
9727 | +/* Linux specific include files */ | ||
9728 | +#include <linux/device.h> | ||
9729 | +#include <linux/slab.h> | ||
9730 | + | ||
9731 | +#include "isp.h" | ||
9732 | +#include "isph3a.h" | ||
9733 | +#include "ispstat.h" | ||
9734 | + | ||
9735 | +#define IS_OUT_OF_BOUNDS(value, min, max) \ | ||
9736 | + (((value) < (min)) || ((value) > (max))) | ||
9737 | + | ||
9738 | +static void h3a_af_setup_regs(struct ispstat *af, void *priv) | ||
9739 | +{ | ||
9740 | + struct omap3isp_h3a_af_config *conf = priv; | ||
9741 | + u32 pcr; | ||
9742 | + u32 pax1; | ||
9743 | + u32 pax2; | ||
9744 | + u32 paxstart; | ||
9745 | + u32 coef; | ||
9746 | + u32 base_coef_set0; | ||
9747 | + u32 base_coef_set1; | ||
9748 | + int index; | ||
9749 | + | ||
9750 | + if (af->state == ISPSTAT_DISABLED) | ||
9751 | + return; | ||
9752 | + | ||
9753 | + isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A, | ||
9754 | + ISPH3A_AFBUFST); | ||
9755 | + | ||
9756 | + if (!af->update) | ||
9757 | + return; | ||
9758 | + | ||
9759 | + /* Configure Hardware Registers */ | ||
9760 | + pax1 = ((conf->paxel.width >> 1) - 1) << AF_PAXW_SHIFT; | ||
9761 | + /* Set height in AFPAX1 */ | ||
9762 | + pax1 |= (conf->paxel.height >> 1) - 1; | ||
9763 | + isp_reg_writel(af->isp, pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1); | ||
9764 | + | ||
9765 | + /* Configure AFPAX2 Register */ | ||
9766 | + /* Set Line Increment in AFPAX2 Register */ | ||
9767 | + pax2 = ((conf->paxel.line_inc >> 1) - 1) << AF_LINE_INCR_SHIFT; | ||
9768 | + /* Set Vertical Count */ | ||
9769 | + pax2 |= (conf->paxel.v_cnt - 1) << AF_VT_COUNT_SHIFT; | ||
9770 | + /* Set Horizontal Count */ | ||
9771 | + pax2 |= (conf->paxel.h_cnt - 1); | ||
9772 | + isp_reg_writel(af->isp, pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2); | ||
9773 | + | ||
9774 | + /* Configure PAXSTART Register */ | ||
9775 | + /*Configure Horizontal Start */ | ||
9776 | + paxstart = conf->paxel.h_start << AF_HZ_START_SHIFT; | ||
9777 | + /* Configure Vertical Start */ | ||
9778 | + paxstart |= conf->paxel.v_start; | ||
9779 | + isp_reg_writel(af->isp, paxstart, OMAP3_ISP_IOMEM_H3A, | ||
9780 | + ISPH3A_AFPAXSTART); | ||
9781 | + | ||
9782 | + /*SetIIRSH Register */ | ||
9783 | + isp_reg_writel(af->isp, conf->iir.h_start, | ||
9784 | + OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH); | ||
9785 | + | ||
9786 | + base_coef_set0 = ISPH3A_AFCOEF010; | ||
9787 | + base_coef_set1 = ISPH3A_AFCOEF110; | ||
9788 | + for (index = 0; index <= 8; index += 2) { | ||
9789 | + /*Set IIR Filter0 Coefficients */ | ||
9790 | + coef = 0; | ||
9791 | + coef |= conf->iir.coeff_set0[index]; | ||
9792 | + coef |= conf->iir.coeff_set0[index + 1] << | ||
9793 | + AF_COEF_SHIFT; | ||
9794 | + isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A, | ||
9795 | + base_coef_set0); | ||
9796 | + base_coef_set0 += AFCOEF_OFFSET; | ||
9797 | + | ||
9798 | + /*Set IIR Filter1 Coefficients */ | ||
9799 | + coef = 0; | ||
9800 | + coef |= conf->iir.coeff_set1[index]; | ||
9801 | + coef |= conf->iir.coeff_set1[index + 1] << | ||
9802 | + AF_COEF_SHIFT; | ||
9803 | + isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A, | ||
9804 | + base_coef_set1); | ||
9805 | + base_coef_set1 += AFCOEF_OFFSET; | ||
9806 | + } | ||
9807 | + /* set AFCOEF0010 Register */ | ||
9808 | + isp_reg_writel(af->isp, conf->iir.coeff_set0[10], | ||
9809 | + OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010); | ||
9810 | + /* set AFCOEF1010 Register */ | ||
9811 | + isp_reg_writel(af->isp, conf->iir.coeff_set1[10], | ||
9812 | + OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010); | ||
9813 | + | ||
9814 | + /* PCR Register */ | ||
9815 | + /* Set RGB Position */ | ||
9816 | + pcr = conf->rgb_pos << AF_RGBPOS_SHIFT; | ||
9817 | + /* Set Accumulator Mode */ | ||
9818 | + if (conf->fvmode == OMAP3ISP_AF_MODE_PEAK) | ||
9819 | + pcr |= AF_FVMODE; | ||
9820 | + /* Set A-law */ | ||
9821 | + if (conf->alaw_enable) | ||
9822 | + pcr |= AF_ALAW_EN; | ||
9823 | + /* HMF Configurations */ | ||
9824 | + if (conf->hmf.enable) { | ||
9825 | + /* Enable HMF */ | ||
9826 | + pcr |= AF_MED_EN; | ||
9827 | + /* Set Median Threshold */ | ||
9828 | + pcr |= conf->hmf.threshold << AF_MED_TH_SHIFT; | ||
9829 | + } | ||
9830 | + /* Set PCR Register */ | ||
9831 | + isp_reg_clr_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
9832 | + AF_PCR_MASK, pcr); | ||
9833 | + | ||
9834 | + af->update = 0; | ||
9835 | + af->config_counter += af->inc_config; | ||
9836 | + af->inc_config = 0; | ||
9837 | + af->buf_size = conf->buf_size; | ||
9838 | +} | ||
9839 | + | ||
9840 | +static void h3a_af_enable(struct ispstat *af, int enable) | ||
9841 | +{ | ||
9842 | + if (enable) { | ||
9843 | + isp_reg_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
9844 | + ISPH3A_PCR_AF_EN); | ||
9845 | + /* This bit is already set if AEWB is enabled */ | ||
9846 | + if (af->isp->isp_aewb.state != ISPSTAT_ENABLED) | ||
9847 | + isp_reg_set(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
9848 | + ISPCTRL_H3A_CLK_EN); | ||
9849 | + } else { | ||
9850 | + isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
9851 | + ISPH3A_PCR_AF_EN); | ||
9852 | + /* This bit can't be cleared if AEWB is enabled */ | ||
9853 | + if (af->isp->isp_aewb.state != ISPSTAT_ENABLED) | ||
9854 | + isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
9855 | + ISPCTRL_H3A_CLK_EN); | ||
9856 | + } | ||
9857 | +} | ||
9858 | + | ||
9859 | +static int h3a_af_busy(struct ispstat *af) | ||
9860 | +{ | ||
9861 | + return isp_reg_readl(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) | ||
9862 | + & ISPH3A_PCR_BUSYAF; | ||
9863 | +} | ||
9864 | + | ||
9865 | +static u32 h3a_af_get_buf_size(struct omap3isp_h3a_af_config *conf) | ||
9866 | +{ | ||
9867 | + return conf->paxel.h_cnt * conf->paxel.v_cnt * OMAP3ISP_AF_PAXEL_SIZE; | ||
9868 | +} | ||
9869 | + | ||
9870 | +/* Function to check paxel parameters */ | ||
9871 | +static int h3a_af_validate_params(struct ispstat *af, void *new_conf) | ||
9872 | +{ | ||
9873 | + struct omap3isp_h3a_af_config *user_cfg = new_conf; | ||
9874 | + struct omap3isp_h3a_af_paxel *paxel_cfg = &user_cfg->paxel; | ||
9875 | + struct omap3isp_h3a_af_iir *iir_cfg = &user_cfg->iir; | ||
9876 | + int index; | ||
9877 | + u32 buf_size; | ||
9878 | + | ||
9879 | + /* Check horizontal Count */ | ||
9880 | + if (IS_OUT_OF_BOUNDS(paxel_cfg->h_cnt, | ||
9881 | + OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN, | ||
9882 | + OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX)) | ||
9883 | + return -EINVAL; | ||
9884 | + | ||
9885 | + /* Check Vertical Count */ | ||
9886 | + if (IS_OUT_OF_BOUNDS(paxel_cfg->v_cnt, | ||
9887 | + OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN, | ||
9888 | + OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX)) | ||
9889 | + return -EINVAL; | ||
9890 | + | ||
9891 | + if (IS_OUT_OF_BOUNDS(paxel_cfg->height, OMAP3ISP_AF_PAXEL_HEIGHT_MIN, | ||
9892 | + OMAP3ISP_AF_PAXEL_HEIGHT_MAX) || | ||
9893 | + paxel_cfg->height % 2) | ||
9894 | + return -EINVAL; | ||
9895 | + | ||
9896 | + /* Check width */ | ||
9897 | + if (IS_OUT_OF_BOUNDS(paxel_cfg->width, OMAP3ISP_AF_PAXEL_WIDTH_MIN, | ||
9898 | + OMAP3ISP_AF_PAXEL_WIDTH_MAX) || | ||
9899 | + paxel_cfg->width % 2) | ||
9900 | + return -EINVAL; | ||
9901 | + | ||
9902 | + /* Check Line Increment */ | ||
9903 | + if (IS_OUT_OF_BOUNDS(paxel_cfg->line_inc, | ||
9904 | + OMAP3ISP_AF_PAXEL_INCREMENT_MIN, | ||
9905 | + OMAP3ISP_AF_PAXEL_INCREMENT_MAX) || | ||
9906 | + paxel_cfg->line_inc % 2) | ||
9907 | + return -EINVAL; | ||
9908 | + | ||
9909 | + /* Check Horizontal Start */ | ||
9910 | + if ((paxel_cfg->h_start < iir_cfg->h_start) || | ||
9911 | + IS_OUT_OF_BOUNDS(paxel_cfg->h_start, | ||
9912 | + OMAP3ISP_AF_PAXEL_HZSTART_MIN, | ||
9913 | + OMAP3ISP_AF_PAXEL_HZSTART_MAX)) | ||
9914 | + return -EINVAL; | ||
9915 | + | ||
9916 | + /* Check IIR */ | ||
9917 | + for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) { | ||
9918 | + if ((iir_cfg->coeff_set0[index]) > OMAP3ISP_AF_COEF_MAX) | ||
9919 | + return -EINVAL; | ||
9920 | + | ||
9921 | + if ((iir_cfg->coeff_set1[index]) > OMAP3ISP_AF_COEF_MAX) | ||
9922 | + return -EINVAL; | ||
9923 | + } | ||
9924 | + | ||
9925 | + if (IS_OUT_OF_BOUNDS(iir_cfg->h_start, OMAP3ISP_AF_IIRSH_MIN, | ||
9926 | + OMAP3ISP_AF_IIRSH_MAX)) | ||
9927 | + return -EINVAL; | ||
9928 | + | ||
9929 | + /* Hack: If paxel size is 12, the 10th AF window may be corrupted */ | ||
9930 | + if ((paxel_cfg->h_cnt * paxel_cfg->v_cnt > 9) && | ||
9931 | + (paxel_cfg->width * paxel_cfg->height == 12)) | ||
9932 | + return -EINVAL; | ||
9933 | + | ||
9934 | + buf_size = h3a_af_get_buf_size(user_cfg); | ||
9935 | + if (buf_size > user_cfg->buf_size) | ||
9936 | + /* User buf_size request wasn't enough */ | ||
9937 | + user_cfg->buf_size = buf_size; | ||
9938 | + else if (user_cfg->buf_size > OMAP3ISP_AF_MAX_BUF_SIZE) | ||
9939 | + user_cfg->buf_size = OMAP3ISP_AF_MAX_BUF_SIZE; | ||
9940 | + | ||
9941 | + return 0; | ||
9942 | +} | ||
9943 | + | ||
9944 | +/* Update local parameters */ | ||
9945 | +static void h3a_af_set_params(struct ispstat *af, void *new_conf) | ||
9946 | +{ | ||
9947 | + struct omap3isp_h3a_af_config *user_cfg = new_conf; | ||
9948 | + struct omap3isp_h3a_af_config *cur_cfg = af->priv; | ||
9949 | + int update = 0; | ||
9950 | + int index; | ||
9951 | + | ||
9952 | + /* alaw */ | ||
9953 | + if (cur_cfg->alaw_enable != user_cfg->alaw_enable) { | ||
9954 | + update = 1; | ||
9955 | + goto out; | ||
9956 | + } | ||
9957 | + | ||
9958 | + /* hmf */ | ||
9959 | + if (cur_cfg->hmf.enable != user_cfg->hmf.enable) { | ||
9960 | + update = 1; | ||
9961 | + goto out; | ||
9962 | + } | ||
9963 | + if (cur_cfg->hmf.threshold != user_cfg->hmf.threshold) { | ||
9964 | + update = 1; | ||
9965 | + goto out; | ||
9966 | + } | ||
9967 | + | ||
9968 | + /* rgbpos */ | ||
9969 | + if (cur_cfg->rgb_pos != user_cfg->rgb_pos) { | ||
9970 | + update = 1; | ||
9971 | + goto out; | ||
9972 | + } | ||
9973 | + | ||
9974 | + /* iir */ | ||
9975 | + if (cur_cfg->iir.h_start != user_cfg->iir.h_start) { | ||
9976 | + update = 1; | ||
9977 | + goto out; | ||
9978 | + } | ||
9979 | + for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) { | ||
9980 | + if (cur_cfg->iir.coeff_set0[index] != | ||
9981 | + user_cfg->iir.coeff_set0[index]) { | ||
9982 | + update = 1; | ||
9983 | + goto out; | ||
9984 | + } | ||
9985 | + if (cur_cfg->iir.coeff_set1[index] != | ||
9986 | + user_cfg->iir.coeff_set1[index]) { | ||
9987 | + update = 1; | ||
9988 | + goto out; | ||
9989 | + } | ||
9990 | + } | ||
9991 | + | ||
9992 | + /* paxel */ | ||
9993 | + if ((cur_cfg->paxel.width != user_cfg->paxel.width) || | ||
9994 | + (cur_cfg->paxel.height != user_cfg->paxel.height) || | ||
9995 | + (cur_cfg->paxel.h_start != user_cfg->paxel.h_start) || | ||
9996 | + (cur_cfg->paxel.v_start != user_cfg->paxel.v_start) || | ||
9997 | + (cur_cfg->paxel.h_cnt != user_cfg->paxel.h_cnt) || | ||
9998 | + (cur_cfg->paxel.v_cnt != user_cfg->paxel.v_cnt) || | ||
9999 | + (cur_cfg->paxel.line_inc != user_cfg->paxel.line_inc)) { | ||
10000 | + update = 1; | ||
10001 | + goto out; | ||
10002 | + } | ||
10003 | + | ||
10004 | + /* af_mode */ | ||
10005 | + if (cur_cfg->fvmode != user_cfg->fvmode) | ||
10006 | + update = 1; | ||
10007 | + | ||
10008 | +out: | ||
10009 | + if (update || !af->configured) { | ||
10010 | + memcpy(cur_cfg, user_cfg, sizeof(*cur_cfg)); | ||
10011 | + af->inc_config++; | ||
10012 | + af->update = 1; | ||
10013 | + /* | ||
10014 | + * User might be asked for a bigger buffer than necessary for | ||
10015 | + * this configuration. In order to return the right amount of | ||
10016 | + * data during buffer request, let's calculate the size here | ||
10017 | + * instead of stick with user_cfg->buf_size. | ||
10018 | + */ | ||
10019 | + cur_cfg->buf_size = h3a_af_get_buf_size(cur_cfg); | ||
10020 | + } | ||
10021 | +} | ||
10022 | + | ||
10023 | +static long h3a_af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
10024 | +{ | ||
10025 | + struct ispstat *stat = v4l2_get_subdevdata(sd); | ||
10026 | + | ||
10027 | + switch (cmd) { | ||
10028 | + case VIDIOC_OMAP3ISP_AF_CFG: | ||
10029 | + return omap3isp_stat_config(stat, arg); | ||
10030 | + case VIDIOC_OMAP3ISP_STAT_REQ: | ||
10031 | + return omap3isp_stat_request_statistics(stat, arg); | ||
10032 | + case VIDIOC_OMAP3ISP_STAT_EN: { | ||
10033 | + int *en = arg; | ||
10034 | + return omap3isp_stat_enable(stat, !!*en); | ||
10035 | + } | ||
10036 | + } | ||
10037 | + | ||
10038 | + return -ENOIOCTLCMD; | ||
10039 | + | ||
10040 | +} | ||
10041 | + | ||
10042 | +static const struct ispstat_ops h3a_af_ops = { | ||
10043 | + .validate_params = h3a_af_validate_params, | ||
10044 | + .set_params = h3a_af_set_params, | ||
10045 | + .setup_regs = h3a_af_setup_regs, | ||
10046 | + .enable = h3a_af_enable, | ||
10047 | + .busy = h3a_af_busy, | ||
10048 | +}; | ||
10049 | + | ||
10050 | +static const struct v4l2_subdev_core_ops h3a_af_subdev_core_ops = { | ||
10051 | + .ioctl = h3a_af_ioctl, | ||
10052 | + .subscribe_event = omap3isp_stat_subscribe_event, | ||
10053 | + .unsubscribe_event = omap3isp_stat_unsubscribe_event, | ||
10054 | +}; | ||
10055 | + | ||
10056 | +static const struct v4l2_subdev_video_ops h3a_af_subdev_video_ops = { | ||
10057 | + .s_stream = omap3isp_stat_s_stream, | ||
10058 | +}; | ||
10059 | + | ||
10060 | +static const struct v4l2_subdev_ops h3a_af_subdev_ops = { | ||
10061 | + .core = &h3a_af_subdev_core_ops, | ||
10062 | + .video = &h3a_af_subdev_video_ops, | ||
10063 | +}; | ||
10064 | + | ||
10065 | +/* Function to register the AF character device driver. */ | ||
10066 | +int omap3isp_h3a_af_init(struct isp_device *isp) | ||
10067 | +{ | ||
10068 | + struct ispstat *af = &isp->isp_af; | ||
10069 | + struct omap3isp_h3a_af_config *af_cfg; | ||
10070 | + struct omap3isp_h3a_af_config *af_recover_cfg; | ||
10071 | + int ret; | ||
10072 | + | ||
10073 | + af_cfg = kzalloc(sizeof(*af_cfg), GFP_KERNEL); | ||
10074 | + if (af_cfg == NULL) | ||
10075 | + return -ENOMEM; | ||
10076 | + | ||
10077 | + memset(af, 0, sizeof(*af)); | ||
10078 | + af->ops = &h3a_af_ops; | ||
10079 | + af->priv = af_cfg; | ||
10080 | + af->dma_ch = -1; | ||
10081 | + af->event_type = V4L2_EVENT_OMAP3ISP_AF; | ||
10082 | + af->isp = isp; | ||
10083 | + | ||
10084 | + /* Set recover state configuration */ | ||
10085 | + af_recover_cfg = kzalloc(sizeof(*af_recover_cfg), GFP_KERNEL); | ||
10086 | + if (!af_recover_cfg) { | ||
10087 | + dev_err(af->isp->dev, "AF: cannot allocate memory for recover " | ||
10088 | + "configuration.\n"); | ||
10089 | + ret = -ENOMEM; | ||
10090 | + goto err_recover_alloc; | ||
10091 | + } | ||
10092 | + | ||
10093 | + af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN; | ||
10094 | + af_recover_cfg->paxel.width = OMAP3ISP_AF_PAXEL_WIDTH_MIN; | ||
10095 | + af_recover_cfg->paxel.height = OMAP3ISP_AF_PAXEL_HEIGHT_MIN; | ||
10096 | + af_recover_cfg->paxel.h_cnt = OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN; | ||
10097 | + af_recover_cfg->paxel.v_cnt = OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN; | ||
10098 | + af_recover_cfg->paxel.line_inc = OMAP3ISP_AF_PAXEL_INCREMENT_MIN; | ||
10099 | + if (h3a_af_validate_params(af, af_recover_cfg)) { | ||
10100 | + dev_err(af->isp->dev, "AF: recover configuration is " | ||
10101 | + "invalid.\n"); | ||
10102 | + ret = -EINVAL; | ||
10103 | + goto err_conf; | ||
10104 | + } | ||
10105 | + | ||
10106 | + af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg); | ||
10107 | + af->recover_priv = af_recover_cfg; | ||
10108 | + | ||
10109 | + ret = omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops); | ||
10110 | + if (ret) | ||
10111 | + goto err_conf; | ||
10112 | + | ||
10113 | + return 0; | ||
10114 | + | ||
10115 | +err_conf: | ||
10116 | + kfree(af_recover_cfg); | ||
10117 | +err_recover_alloc: | ||
10118 | + kfree(af_cfg); | ||
10119 | + | ||
10120 | + return ret; | ||
10121 | +} | ||
10122 | + | ||
10123 | +void omap3isp_h3a_af_cleanup(struct isp_device *isp) | ||
10124 | +{ | ||
10125 | + kfree(isp->isp_af.priv); | ||
10126 | + kfree(isp->isp_af.recover_priv); | ||
10127 | + omap3isp_stat_free(&isp->isp_af); | ||
10128 | +} | ||
10129 | diff --git a/drivers/media/video/isp/isphist.c b/drivers/media/video/isp/isphist.c | ||
10130 | new file mode 100644 | ||
10131 | index 0000000..a43eb92 | ||
10132 | --- /dev/null | ||
10133 | +++ b/drivers/media/video/isp/isphist.c | ||
10134 | @@ -0,0 +1,520 @@ | ||
10135 | +/* | ||
10136 | + * isphist.c | ||
10137 | + * | ||
10138 | + * TI OMAP3 ISP - Histogram module | ||
10139 | + * | ||
10140 | + * Copyright (C) 2010 Nokia Corporation | ||
10141 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
10142 | + * | ||
10143 | + * Contacts: David Cohen <david.cohen@nokia.com> | ||
10144 | + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10145 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
10146 | + * | ||
10147 | + * This program is free software; you can redistribute it and/or modify | ||
10148 | + * it under the terms of the GNU General Public License version 2 as | ||
10149 | + * published by the Free Software Foundation. | ||
10150 | + * | ||
10151 | + * This program is distributed in the hope that it will be useful, but | ||
10152 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10153 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
10154 | + * General Public License for more details. | ||
10155 | + * | ||
10156 | + * You should have received a copy of the GNU General Public License | ||
10157 | + * along with this program; if not, write to the Free Software | ||
10158 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
10159 | + * 02110-1301 USA | ||
10160 | + */ | ||
10161 | + | ||
10162 | +#include <linux/delay.h> | ||
10163 | +#include <linux/slab.h> | ||
10164 | +#include <linux/uaccess.h> | ||
10165 | +#include <linux/device.h> | ||
10166 | + | ||
10167 | +#include "isp.h" | ||
10168 | +#include "ispreg.h" | ||
10169 | +#include "isphist.h" | ||
10170 | + | ||
10171 | +#define HIST_CONFIG_DMA 1 | ||
10172 | + | ||
10173 | +#define HIST_USING_DMA(hist) ((hist)->dma_ch >= 0) | ||
10174 | + | ||
10175 | +/* | ||
10176 | + * hist_reset_mem - clear Histogram memory before start stats engine. | ||
10177 | + */ | ||
10178 | +static void hist_reset_mem(struct ispstat *hist) | ||
10179 | +{ | ||
10180 | + struct isp_device *isp = hist->isp; | ||
10181 | + struct omap3isp_hist_config *conf = hist->priv; | ||
10182 | + unsigned int i; | ||
10183 | + | ||
10184 | + isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); | ||
10185 | + | ||
10186 | + /* | ||
10187 | + * By setting it, the histogram internal buffer is being cleared at the | ||
10188 | + * same time it's being read. This bit must be cleared afterwards. | ||
10189 | + */ | ||
10190 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); | ||
10191 | + | ||
10192 | + /* | ||
10193 | + * We'll clear 4 words at each iteration for optimization. It avoids | ||
10194 | + * 3/4 of the jumps. We also know HIST_MEM_SIZE is divisible by 4. | ||
10195 | + */ | ||
10196 | + for (i = OMAP3ISP_HIST_MEM_SIZE / 4; i > 0; i--) { | ||
10197 | + isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
10198 | + isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
10199 | + isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
10200 | + isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
10201 | + } | ||
10202 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); | ||
10203 | + | ||
10204 | + hist->wait_acc_frames = conf->num_acc_frames; | ||
10205 | +} | ||
10206 | + | ||
10207 | +static void hist_dma_config(struct ispstat *hist) | ||
10208 | +{ | ||
10209 | + hist->dma_config.data_type = OMAP_DMA_DATA_TYPE_S32; | ||
10210 | + hist->dma_config.sync_mode = OMAP_DMA_SYNC_ELEMENT; | ||
10211 | + hist->dma_config.frame_count = 1; | ||
10212 | + hist->dma_config.src_amode = OMAP_DMA_AMODE_CONSTANT; | ||
10213 | + hist->dma_config.src_start = OMAP3ISP_HIST_REG_BASE + ISPHIST_DATA; | ||
10214 | + hist->dma_config.dst_amode = OMAP_DMA_AMODE_POST_INC; | ||
10215 | + hist->dma_config.src_or_dst_synch = OMAP_DMA_SRC_SYNC; | ||
10216 | +} | ||
10217 | + | ||
10218 | +/* | ||
10219 | + * hist_setup_regs - Helper function to update Histogram registers. | ||
10220 | + */ | ||
10221 | +static void hist_setup_regs(struct ispstat *hist, void *priv) | ||
10222 | +{ | ||
10223 | + struct isp_device *isp = hist->isp; | ||
10224 | + struct omap3isp_hist_config *conf = priv; | ||
10225 | + int c; | ||
10226 | + u32 cnt; | ||
10227 | + u32 wb_gain; | ||
10228 | + u32 reg_hor[OMAP3ISP_HIST_MAX_REGIONS]; | ||
10229 | + u32 reg_ver[OMAP3ISP_HIST_MAX_REGIONS]; | ||
10230 | + | ||
10231 | + if (!hist->update || hist->state == ISPSTAT_DISABLED || | ||
10232 | + hist->state == ISPSTAT_DISABLING) | ||
10233 | + return; | ||
10234 | + | ||
10235 | + cnt = conf->cfa << ISPHIST_CNT_CFA_SHIFT; | ||
10236 | + | ||
10237 | + wb_gain = conf->wg[0] << ISPHIST_WB_GAIN_WG00_SHIFT; | ||
10238 | + wb_gain |= conf->wg[1] << ISPHIST_WB_GAIN_WG01_SHIFT; | ||
10239 | + wb_gain |= conf->wg[2] << ISPHIST_WB_GAIN_WG02_SHIFT; | ||
10240 | + if (conf->cfa == OMAP3ISP_HIST_CFA_BAYER) | ||
10241 | + wb_gain |= conf->wg[3] << ISPHIST_WB_GAIN_WG03_SHIFT; | ||
10242 | + | ||
10243 | + /* Regions size and position */ | ||
10244 | + for (c = 0; c < OMAP3ISP_HIST_MAX_REGIONS; c++) { | ||
10245 | + if (c < conf->num_regions) { | ||
10246 | + reg_hor[c] = conf->region[c].h_start << | ||
10247 | + ISPHIST_REG_START_SHIFT; | ||
10248 | + reg_hor[c] = conf->region[c].h_end << | ||
10249 | + ISPHIST_REG_END_SHIFT; | ||
10250 | + reg_ver[c] = conf->region[c].v_start << | ||
10251 | + ISPHIST_REG_START_SHIFT; | ||
10252 | + reg_ver[c] = conf->region[c].v_end << | ||
10253 | + ISPHIST_REG_END_SHIFT; | ||
10254 | + } else { | ||
10255 | + reg_hor[c] = 0; | ||
10256 | + reg_ver[c] = 0; | ||
10257 | + } | ||
10258 | + } | ||
10259 | + | ||
10260 | + cnt |= conf->hist_bins << ISPHIST_CNT_BINS_SHIFT; | ||
10261 | + switch (conf->hist_bins) { | ||
10262 | + case OMAP3ISP_HIST_BINS_256: | ||
10263 | + cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 8) << | ||
10264 | + ISPHIST_CNT_SHIFT_SHIFT; | ||
10265 | + break; | ||
10266 | + case OMAP3ISP_HIST_BINS_128: | ||
10267 | + cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 7) << | ||
10268 | + ISPHIST_CNT_SHIFT_SHIFT; | ||
10269 | + break; | ||
10270 | + case OMAP3ISP_HIST_BINS_64: | ||
10271 | + cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 6) << | ||
10272 | + ISPHIST_CNT_SHIFT_SHIFT; | ||
10273 | + break; | ||
10274 | + default: /* OMAP3ISP_HIST_BINS_32 */ | ||
10275 | + cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 5) << | ||
10276 | + ISPHIST_CNT_SHIFT_SHIFT; | ||
10277 | + break; | ||
10278 | + } | ||
10279 | + | ||
10280 | + hist_reset_mem(hist); | ||
10281 | + | ||
10282 | + isp_reg_writel(isp, cnt, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT); | ||
10283 | + isp_reg_writel(isp, wb_gain, OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN); | ||
10284 | + isp_reg_writel(isp, reg_hor[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_HORZ); | ||
10285 | + isp_reg_writel(isp, reg_ver[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_VERT); | ||
10286 | + isp_reg_writel(isp, reg_hor[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_HORZ); | ||
10287 | + isp_reg_writel(isp, reg_ver[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_VERT); | ||
10288 | + isp_reg_writel(isp, reg_hor[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_HORZ); | ||
10289 | + isp_reg_writel(isp, reg_ver[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_VERT); | ||
10290 | + isp_reg_writel(isp, reg_hor[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_HORZ); | ||
10291 | + isp_reg_writel(isp, reg_ver[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_VERT); | ||
10292 | + | ||
10293 | + hist->update = 0; | ||
10294 | + hist->config_counter += hist->inc_config; | ||
10295 | + hist->inc_config = 0; | ||
10296 | + hist->buf_size = conf->buf_size; | ||
10297 | +} | ||
10298 | + | ||
10299 | +static void hist_enable(struct ispstat *hist, int enable) | ||
10300 | +{ | ||
10301 | + if (enable) { | ||
10302 | + isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, | ||
10303 | + ISPHIST_PCR_ENABLE); | ||
10304 | + isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
10305 | + ISPCTRL_HIST_CLK_EN); | ||
10306 | + } else { | ||
10307 | + isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, | ||
10308 | + ISPHIST_PCR_ENABLE); | ||
10309 | + isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
10310 | + ISPCTRL_HIST_CLK_EN); | ||
10311 | + } | ||
10312 | +} | ||
10313 | + | ||
10314 | +static int hist_busy(struct ispstat *hist) | ||
10315 | +{ | ||
10316 | + return isp_reg_readl(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR) | ||
10317 | + & ISPHIST_PCR_BUSY; | ||
10318 | +} | ||
10319 | + | ||
10320 | +static void hist_dma_cb(int lch, u16 ch_status, void *data) | ||
10321 | +{ | ||
10322 | + struct ispstat *hist = data; | ||
10323 | + | ||
10324 | + if (ch_status & ~OMAP_DMA_BLOCK_IRQ) { | ||
10325 | + dev_dbg(hist->isp->dev, "hist: DMA error. status = 0x%04x\n", | ||
10326 | + ch_status); | ||
10327 | + omap_stop_dma(lch); | ||
10328 | + hist_reset_mem(hist); | ||
10329 | + atomic_set(&hist->buf_err, 1); | ||
10330 | + } | ||
10331 | + isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, | ||
10332 | + ISPHIST_CNT_CLEAR); | ||
10333 | + | ||
10334 | + omap3isp_stat_dma_isr(hist); | ||
10335 | + if (hist->state != ISPSTAT_DISABLED) | ||
10336 | + omap3isp_hist_dma_done(hist->isp); | ||
10337 | +} | ||
10338 | + | ||
10339 | +static int hist_buf_dma(struct ispstat *hist) | ||
10340 | +{ | ||
10341 | + dma_addr_t dma_addr = hist->active_buf->dma_addr; | ||
10342 | + | ||
10343 | + if (unlikely(!dma_addr)) { | ||
10344 | + dev_dbg(hist->isp->dev, "hist: invalid DMA buffer address\n"); | ||
10345 | + hist_reset_mem(hist); | ||
10346 | + return STAT_NO_BUF; | ||
10347 | + } | ||
10348 | + | ||
10349 | + isp_reg_writel(hist->isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); | ||
10350 | + isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, | ||
10351 | + ISPHIST_CNT_CLEAR); | ||
10352 | + omap3isp_flush(hist->isp); | ||
10353 | + hist->dma_config.dst_start = dma_addr; | ||
10354 | + hist->dma_config.elem_count = hist->buf_size / sizeof(u32); | ||
10355 | + omap_set_dma_params(hist->dma_ch, &hist->dma_config); | ||
10356 | + | ||
10357 | + omap_start_dma(hist->dma_ch); | ||
10358 | + | ||
10359 | + return STAT_BUF_WAITING_DMA; | ||
10360 | +} | ||
10361 | + | ||
10362 | +static int hist_buf_pio(struct ispstat *hist) | ||
10363 | +{ | ||
10364 | + struct isp_device *isp = hist->isp; | ||
10365 | + u32 *buf = hist->active_buf->virt_addr; | ||
10366 | + unsigned int i; | ||
10367 | + | ||
10368 | + if (!buf) { | ||
10369 | + dev_dbg(isp->dev, "hist: invalid PIO buffer address\n"); | ||
10370 | + hist_reset_mem(hist); | ||
10371 | + return STAT_NO_BUF; | ||
10372 | + } | ||
10373 | + | ||
10374 | + isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); | ||
10375 | + | ||
10376 | + /* | ||
10377 | + * By setting it, the histogram internal buffer is being cleared at the | ||
10378 | + * same time it's being read. This bit must be cleared just after all | ||
10379 | + * data is acquired. | ||
10380 | + */ | ||
10381 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); | ||
10382 | + | ||
10383 | + /* | ||
10384 | + * We'll read 4 times a 4-bytes-word at each iteration for | ||
10385 | + * optimization. It avoids 3/4 of the jumps. We also know buf_size is | ||
10386 | + * divisible by 16. | ||
10387 | + */ | ||
10388 | + for (i = hist->buf_size / 16; i > 0; i--) { | ||
10389 | + *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
10390 | + *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
10391 | + *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
10392 | + *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
10393 | + } | ||
10394 | + isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, | ||
10395 | + ISPHIST_CNT_CLEAR); | ||
10396 | + | ||
10397 | + return STAT_BUF_DONE; | ||
10398 | +} | ||
10399 | + | ||
10400 | +/* | ||
10401 | + * hist_buf_process - Callback from ISP driver for HIST interrupt. | ||
10402 | + */ | ||
10403 | +static int hist_buf_process(struct ispstat *hist) | ||
10404 | +{ | ||
10405 | + struct omap3isp_hist_config *user_cfg = hist->priv; | ||
10406 | + int ret; | ||
10407 | + | ||
10408 | + if (atomic_read(&hist->buf_err) || hist->state != ISPSTAT_ENABLED) { | ||
10409 | + hist_reset_mem(hist); | ||
10410 | + return STAT_NO_BUF; | ||
10411 | + } | ||
10412 | + | ||
10413 | + if (--(hist->wait_acc_frames)) | ||
10414 | + return STAT_NO_BUF; | ||
10415 | + | ||
10416 | + if (HIST_USING_DMA(hist)) | ||
10417 | + ret = hist_buf_dma(hist); | ||
10418 | + else | ||
10419 | + ret = hist_buf_pio(hist); | ||
10420 | + | ||
10421 | + hist->wait_acc_frames = user_cfg->num_acc_frames; | ||
10422 | + | ||
10423 | + return ret; | ||
10424 | +} | ||
10425 | + | ||
10426 | +static u32 hist_get_buf_size(struct omap3isp_hist_config *conf) | ||
10427 | +{ | ||
10428 | + return OMAP3ISP_HIST_MEM_SIZE_BINS(conf->hist_bins) * conf->num_regions; | ||
10429 | +} | ||
10430 | + | ||
10431 | +/* | ||
10432 | + * hist_validate_params - Helper function to check user given params. | ||
10433 | + * @user_cfg: Pointer to user configuration structure. | ||
10434 | + * | ||
10435 | + * Returns 0 on success configuration. | ||
10436 | + */ | ||
10437 | +static int hist_validate_params(struct ispstat *hist, void *new_conf) | ||
10438 | +{ | ||
10439 | + struct omap3isp_hist_config *user_cfg = new_conf; | ||
10440 | + int c; | ||
10441 | + u32 buf_size; | ||
10442 | + | ||
10443 | + if (user_cfg->cfa > OMAP3ISP_HIST_CFA_FOVEONX3) | ||
10444 | + return -EINVAL; | ||
10445 | + | ||
10446 | + /* Regions size and position */ | ||
10447 | + | ||
10448 | + if ((user_cfg->num_regions < OMAP3ISP_HIST_MIN_REGIONS) || | ||
10449 | + (user_cfg->num_regions > OMAP3ISP_HIST_MAX_REGIONS)) | ||
10450 | + return -EINVAL; | ||
10451 | + | ||
10452 | + /* Regions */ | ||
10453 | + for (c = 0; c < user_cfg->num_regions; c++) { | ||
10454 | + if (user_cfg->region[c].h_start & ~ISPHIST_REG_START_END_MASK) | ||
10455 | + return -EINVAL; | ||
10456 | + if (user_cfg->region[c].h_end & ~ISPHIST_REG_START_END_MASK) | ||
10457 | + return -EINVAL; | ||
10458 | + if (user_cfg->region[c].v_start & ~ISPHIST_REG_START_END_MASK) | ||
10459 | + return -EINVAL; | ||
10460 | + if (user_cfg->region[c].v_end & ~ISPHIST_REG_START_END_MASK) | ||
10461 | + return -EINVAL; | ||
10462 | + if (user_cfg->region[c].h_start > user_cfg->region[c].h_end) | ||
10463 | + return -EINVAL; | ||
10464 | + if (user_cfg->region[c].v_start > user_cfg->region[c].v_end) | ||
10465 | + return -EINVAL; | ||
10466 | + } | ||
10467 | + | ||
10468 | + switch (user_cfg->num_regions) { | ||
10469 | + case 1: | ||
10470 | + if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_256) | ||
10471 | + return -EINVAL; | ||
10472 | + break; | ||
10473 | + case 2: | ||
10474 | + if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_128) | ||
10475 | + return -EINVAL; | ||
10476 | + break; | ||
10477 | + default: /* 3 or 4 */ | ||
10478 | + if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_64) | ||
10479 | + return -EINVAL; | ||
10480 | + break; | ||
10481 | + } | ||
10482 | + | ||
10483 | + buf_size = hist_get_buf_size(user_cfg); | ||
10484 | + if (buf_size > user_cfg->buf_size) | ||
10485 | + /* User's buf_size request wasn't enoght */ | ||
10486 | + user_cfg->buf_size = buf_size; | ||
10487 | + else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE) | ||
10488 | + user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE; | ||
10489 | + | ||
10490 | + return 0; | ||
10491 | +} | ||
10492 | + | ||
10493 | +static int hist_comp_params(struct ispstat *hist, | ||
10494 | + struct omap3isp_hist_config *user_cfg) | ||
10495 | +{ | ||
10496 | + struct omap3isp_hist_config *cur_cfg = hist->priv; | ||
10497 | + int c; | ||
10498 | + | ||
10499 | + if (cur_cfg->cfa != user_cfg->cfa) | ||
10500 | + return 1; | ||
10501 | + | ||
10502 | + if (cur_cfg->num_acc_frames != user_cfg->num_acc_frames) | ||
10503 | + return 1; | ||
10504 | + | ||
10505 | + if (cur_cfg->hist_bins != user_cfg->hist_bins) | ||
10506 | + return 1; | ||
10507 | + | ||
10508 | + for (c = 0; c < OMAP3ISP_HIST_MAX_WG; c++) { | ||
10509 | + if (c == 3 && user_cfg->cfa == OMAP3ISP_HIST_CFA_FOVEONX3) | ||
10510 | + break; | ||
10511 | + else if (cur_cfg->wg[c] != user_cfg->wg[c]) | ||
10512 | + return 1; | ||
10513 | + } | ||
10514 | + | ||
10515 | + if (cur_cfg->num_regions != user_cfg->num_regions) | ||
10516 | + return 1; | ||
10517 | + | ||
10518 | + /* Regions */ | ||
10519 | + for (c = 0; c < user_cfg->num_regions; c++) { | ||
10520 | + if (cur_cfg->region[c].h_start != user_cfg->region[c].h_start) | ||
10521 | + return 1; | ||
10522 | + if (cur_cfg->region[c].h_end != user_cfg->region[c].h_end) | ||
10523 | + return 1; | ||
10524 | + if (cur_cfg->region[c].v_start != user_cfg->region[c].v_start) | ||
10525 | + return 1; | ||
10526 | + if (cur_cfg->region[c].v_end != user_cfg->region[c].v_end) | ||
10527 | + return 1; | ||
10528 | + } | ||
10529 | + | ||
10530 | + return 0; | ||
10531 | +} | ||
10532 | + | ||
10533 | +/* | ||
10534 | + * hist_update_params - Helper function to check and store user given params. | ||
10535 | + * @new_conf: Pointer to user configuration structure. | ||
10536 | + */ | ||
10537 | +static void hist_set_params(struct ispstat *hist, void *new_conf) | ||
10538 | +{ | ||
10539 | + struct omap3isp_hist_config *user_cfg = new_conf; | ||
10540 | + struct omap3isp_hist_config *cur_cfg = hist->priv; | ||
10541 | + | ||
10542 | + if (!hist->configured || hist_comp_params(hist, user_cfg)) { | ||
10543 | + memcpy(cur_cfg, user_cfg, sizeof(*user_cfg)); | ||
10544 | + if (user_cfg->num_acc_frames == 0) | ||
10545 | + user_cfg->num_acc_frames = 1; | ||
10546 | + hist->inc_config++; | ||
10547 | + hist->update = 1; | ||
10548 | + /* | ||
10549 | + * User might be asked for a bigger buffer than necessary for | ||
10550 | + * this configuration. In order to return the right amount of | ||
10551 | + * data during buffer request, let's calculate the size here | ||
10552 | + * instead of stick with user_cfg->buf_size. | ||
10553 | + */ | ||
10554 | + cur_cfg->buf_size = hist_get_buf_size(cur_cfg); | ||
10555 | + | ||
10556 | + } | ||
10557 | +} | ||
10558 | + | ||
10559 | +static long hist_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
10560 | +{ | ||
10561 | + struct ispstat *stat = v4l2_get_subdevdata(sd); | ||
10562 | + | ||
10563 | + switch (cmd) { | ||
10564 | + case VIDIOC_OMAP3ISP_HIST_CFG: | ||
10565 | + return omap3isp_stat_config(stat, arg); | ||
10566 | + case VIDIOC_OMAP3ISP_STAT_REQ: | ||
10567 | + return omap3isp_stat_request_statistics(stat, arg); | ||
10568 | + case VIDIOC_OMAP3ISP_STAT_EN: { | ||
10569 | + int *en = arg; | ||
10570 | + return omap3isp_stat_enable(stat, !!*en); | ||
10571 | + } | ||
10572 | + } | ||
10573 | + | ||
10574 | + return -ENOIOCTLCMD; | ||
10575 | + | ||
10576 | +} | ||
10577 | + | ||
10578 | +static const struct ispstat_ops hist_ops = { | ||
10579 | + .validate_params = hist_validate_params, | ||
10580 | + .set_params = hist_set_params, | ||
10581 | + .setup_regs = hist_setup_regs, | ||
10582 | + .enable = hist_enable, | ||
10583 | + .busy = hist_busy, | ||
10584 | + .buf_process = hist_buf_process, | ||
10585 | +}; | ||
10586 | + | ||
10587 | +static const struct v4l2_subdev_core_ops hist_subdev_core_ops = { | ||
10588 | + .ioctl = hist_ioctl, | ||
10589 | + .subscribe_event = omap3isp_stat_subscribe_event, | ||
10590 | + .unsubscribe_event = omap3isp_stat_unsubscribe_event, | ||
10591 | +}; | ||
10592 | + | ||
10593 | +static const struct v4l2_subdev_video_ops hist_subdev_video_ops = { | ||
10594 | + .s_stream = omap3isp_stat_s_stream, | ||
10595 | +}; | ||
10596 | + | ||
10597 | +static const struct v4l2_subdev_ops hist_subdev_ops = { | ||
10598 | + .core = &hist_subdev_core_ops, | ||
10599 | + .video = &hist_subdev_video_ops, | ||
10600 | +}; | ||
10601 | + | ||
10602 | +/* | ||
10603 | + * omap3isp_hist_init - Module Initialization. | ||
10604 | + */ | ||
10605 | +int omap3isp_hist_init(struct isp_device *isp) | ||
10606 | +{ | ||
10607 | + struct ispstat *hist = &isp->isp_hist; | ||
10608 | + struct omap3isp_hist_config *hist_cfg; | ||
10609 | + int ret = -1; | ||
10610 | + | ||
10611 | + hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL); | ||
10612 | + if (hist_cfg == NULL) | ||
10613 | + return -ENOMEM; | ||
10614 | + | ||
10615 | + memset(hist, 0, sizeof(*hist)); | ||
10616 | + if (HIST_CONFIG_DMA) | ||
10617 | + ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST", | ||
10618 | + hist_dma_cb, hist, &hist->dma_ch); | ||
10619 | + if (ret) { | ||
10620 | + if (HIST_CONFIG_DMA) | ||
10621 | + dev_warn(isp->dev, "hist: DMA request channel failed. " | ||
10622 | + "Using PIO only.\n"); | ||
10623 | + hist->dma_ch = -1; | ||
10624 | + } else { | ||
10625 | + dev_dbg(isp->dev, "hist: DMA channel = %d\n", hist->dma_ch); | ||
10626 | + hist_dma_config(hist); | ||
10627 | + omap_enable_dma_irq(hist->dma_ch, OMAP_DMA_BLOCK_IRQ); | ||
10628 | + } | ||
10629 | + | ||
10630 | + hist->ops = &hist_ops; | ||
10631 | + hist->priv = hist_cfg; | ||
10632 | + hist->event_type = V4L2_EVENT_OMAP3ISP_HIST; | ||
10633 | + hist->isp = isp; | ||
10634 | + | ||
10635 | + ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops); | ||
10636 | + if (ret) { | ||
10637 | + kfree(hist_cfg); | ||
10638 | + if (HIST_USING_DMA(hist)) | ||
10639 | + omap_free_dma(hist->dma_ch); | ||
10640 | + } | ||
10641 | + | ||
10642 | + return ret; | ||
10643 | +} | ||
10644 | + | ||
10645 | +/* | ||
10646 | + * omap3isp_hist_cleanup - Module cleanup. | ||
10647 | + */ | ||
10648 | +void omap3isp_hist_cleanup(struct isp_device *isp) | ||
10649 | +{ | ||
10650 | + if (HIST_USING_DMA(&isp->isp_hist)) | ||
10651 | + omap_free_dma(isp->isp_hist.dma_ch); | ||
10652 | + kfree(isp->isp_hist.priv); | ||
10653 | + omap3isp_stat_free(&isp->isp_hist); | ||
10654 | +} | ||
10655 | diff --git a/drivers/media/video/isp/isphist.h b/drivers/media/video/isp/isphist.h | ||
10656 | new file mode 100644 | ||
10657 | index 0000000..247192b | ||
10658 | --- /dev/null | ||
10659 | +++ b/drivers/media/video/isp/isphist.h | ||
10660 | @@ -0,0 +1,40 @@ | ||
10661 | +/* | ||
10662 | + * isphist.h | ||
10663 | + * | ||
10664 | + * TI OMAP3 ISP - Histogram module | ||
10665 | + * | ||
10666 | + * Copyright (C) 2010 Nokia Corporation | ||
10667 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
10668 | + * | ||
10669 | + * Contacts: David Cohen <david.cohen@nokia.com> | ||
10670 | + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10671 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
10672 | + * | ||
10673 | + * This program is free software; you can redistribute it and/or modify | ||
10674 | + * it under the terms of the GNU General Public License version 2 as | ||
10675 | + * published by the Free Software Foundation. | ||
10676 | + * | ||
10677 | + * This program is distributed in the hope that it will be useful, but | ||
10678 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10679 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
10680 | + * General Public License for more details. | ||
10681 | + * | ||
10682 | + * You should have received a copy of the GNU General Public License | ||
10683 | + * along with this program; if not, write to the Free Software | ||
10684 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
10685 | + * 02110-1301 USA | ||
10686 | + */ | ||
10687 | + | ||
10688 | +#ifndef OMAP3_ISP_HIST_H | ||
10689 | +#define OMAP3_ISP_HIST_H | ||
10690 | + | ||
10691 | +#include <linux/omap3isp.h> | ||
10692 | + | ||
10693 | +#define ISPHIST_IN_BIT_WIDTH_CCDC 10 | ||
10694 | + | ||
10695 | +struct isp_device; | ||
10696 | + | ||
10697 | +int omap3isp_hist_init(struct isp_device *isp); | ||
10698 | +void omap3isp_hist_cleanup(struct isp_device *isp); | ||
10699 | + | ||
10700 | +#endif /* OMAP3_ISP_HIST */ | ||
10701 | diff --git a/drivers/media/video/isp/isppreview.c b/drivers/media/video/isp/isppreview.c | ||
10702 | new file mode 100644 | ||
10703 | index 0000000..869583c | ||
10704 | --- /dev/null | ||
10705 | +++ b/drivers/media/video/isp/isppreview.c | ||
10706 | @@ -0,0 +1,2120 @@ | ||
10707 | +/* | ||
10708 | + * isppreview.c | ||
10709 | + * | ||
10710 | + * TI OMAP3 ISP driver - Preview module | ||
10711 | + * | ||
10712 | + * Copyright (C) 2010 Nokia Corporation | ||
10713 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
10714 | + * | ||
10715 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10716 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
10717 | + * | ||
10718 | + * This program is free software; you can redistribute it and/or modify | ||
10719 | + * it under the terms of the GNU General Public License version 2 as | ||
10720 | + * published by the Free Software Foundation. | ||
10721 | + * | ||
10722 | + * This program is distributed in the hope that it will be useful, but | ||
10723 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10724 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
10725 | + * General Public License for more details. | ||
10726 | + * | ||
10727 | + * You should have received a copy of the GNU General Public License | ||
10728 | + * along with this program; if not, write to the Free Software | ||
10729 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
10730 | + * 02110-1301 USA | ||
10731 | + */ | ||
10732 | + | ||
10733 | +#include <linux/device.h> | ||
10734 | +#include <linux/mm.h> | ||
10735 | +#include <linux/module.h> | ||
10736 | +#include <linux/mutex.h> | ||
10737 | +#include <linux/uaccess.h> | ||
10738 | + | ||
10739 | +#include "isp.h" | ||
10740 | +#include "ispreg.h" | ||
10741 | +#include "isppreview.h" | ||
10742 | + | ||
10743 | +/* Default values in Office Flourescent Light for RGBtoRGB Blending */ | ||
10744 | +static struct omap3isp_prev_rgbtorgb flr_rgb2rgb = { | ||
10745 | + { /* RGB-RGB Matrix */ | ||
10746 | + {0x01E2, 0x0F30, 0x0FEE}, | ||
10747 | + {0x0F9B, 0x01AC, 0x0FB9}, | ||
10748 | + {0x0FE0, 0x0EC0, 0x0260} | ||
10749 | + }, /* RGB Offset */ | ||
10750 | + {0x0000, 0x0000, 0x0000} | ||
10751 | +}; | ||
10752 | + | ||
10753 | +/* Default values in Office Flourescent Light for RGB to YUV Conversion*/ | ||
10754 | +static struct omap3isp_prev_csc flr_prev_csc = { | ||
10755 | + { /* CSC Coef Matrix */ | ||
10756 | + {66, 129, 25}, | ||
10757 | + {-38, -75, 112}, | ||
10758 | + {112, -94 , -18} | ||
10759 | + }, /* CSC Offset */ | ||
10760 | + {0x0, 0x0, 0x0} | ||
10761 | +}; | ||
10762 | + | ||
10763 | +/* Default values in Office Flourescent Light for CFA Gradient*/ | ||
10764 | +#define FLR_CFA_GRADTHRS_HORZ 0x28 | ||
10765 | +#define FLR_CFA_GRADTHRS_VERT 0x28 | ||
10766 | + | ||
10767 | +/* Default values in Office Flourescent Light for Chroma Suppression*/ | ||
10768 | +#define FLR_CSUP_GAIN 0x0D | ||
10769 | +#define FLR_CSUP_THRES 0xEB | ||
10770 | + | ||
10771 | +/* Default values in Office Flourescent Light for Noise Filter*/ | ||
10772 | +#define FLR_NF_STRGTH 0x03 | ||
10773 | + | ||
10774 | +/* Default values for White Balance */ | ||
10775 | +#define FLR_WBAL_DGAIN 0x100 | ||
10776 | +#define FLR_WBAL_COEF 0x20 | ||
10777 | + | ||
10778 | +/* Default values in Office Flourescent Light for Black Adjustment*/ | ||
10779 | +#define FLR_BLKADJ_BLUE 0x0 | ||
10780 | +#define FLR_BLKADJ_GREEN 0x0 | ||
10781 | +#define FLR_BLKADJ_RED 0x0 | ||
10782 | + | ||
10783 | +#define DEF_DETECT_CORRECT_VAL 0xe | ||
10784 | + | ||
10785 | +#define PREV_MIN_WIDTH 64 | ||
10786 | +#define PREV_MIN_HEIGHT 8 | ||
10787 | +#define PREV_MAX_HEIGHT 16384 | ||
10788 | + | ||
10789 | +/* | ||
10790 | + * Coeficient Tables for the submodules in Preview. | ||
10791 | + * Array is initialised with the values from.the tables text file. | ||
10792 | + */ | ||
10793 | + | ||
10794 | +/* | ||
10795 | + * CFA Filter Coefficient Table | ||
10796 | + * | ||
10797 | + */ | ||
10798 | +static u32 cfa_coef_table[] = { | ||
10799 | +#include "cfa_coef_table.h" | ||
10800 | +}; | ||
10801 | + | ||
10802 | +/* | ||
10803 | + * Default Gamma Correction Table - All components | ||
10804 | + */ | ||
10805 | +static u32 gamma_table[] = { | ||
10806 | +#include "gamma_table.h" | ||
10807 | +}; | ||
10808 | + | ||
10809 | +/* | ||
10810 | + * Noise Filter Threshold table | ||
10811 | + */ | ||
10812 | +static u32 noise_filter_table[] = { | ||
10813 | +#include "noise_filter_table.h" | ||
10814 | +}; | ||
10815 | + | ||
10816 | +/* | ||
10817 | + * Luminance Enhancement Table | ||
10818 | + */ | ||
10819 | +static u32 luma_enhance_table[] = { | ||
10820 | +#include "luma_enhance_table.h" | ||
10821 | +}; | ||
10822 | + | ||
10823 | +/* | ||
10824 | + * preview_enable_invalaw - Enable/Disable Inverse A-Law module in Preview. | ||
10825 | + * @enable: 1 - Reverse the A-Law done in CCDC. | ||
10826 | + */ | ||
10827 | +static void | ||
10828 | +preview_enable_invalaw(struct isp_prev_device *prev, u8 enable) | ||
10829 | +{ | ||
10830 | + struct isp_device *isp = to_isp_device(prev); | ||
10831 | + | ||
10832 | + if (enable) | ||
10833 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
10834 | + ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW); | ||
10835 | + else | ||
10836 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
10837 | + ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW); | ||
10838 | +} | ||
10839 | + | ||
10840 | +/* | ||
10841 | + * preview_enable_drkframe_capture - Enable/Disable of the darkframe capture. | ||
10842 | + * @prev - | ||
10843 | + * @enable: 1 - Enable, 0 - Disable | ||
10844 | + * | ||
10845 | + * NOTE: PRV_WSDR_ADDR and PRV_WADD_OFFSET must be set also | ||
10846 | + * The proccess is applied for each captured frame. | ||
10847 | + */ | ||
10848 | +static void | ||
10849 | +preview_enable_drkframe_capture(struct isp_prev_device *prev, u8 enable) | ||
10850 | +{ | ||
10851 | + struct isp_device *isp = to_isp_device(prev); | ||
10852 | + | ||
10853 | + if (enable) | ||
10854 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
10855 | + ISPPRV_PCR_DRKFCAP); | ||
10856 | + else | ||
10857 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
10858 | + ISPPRV_PCR_DRKFCAP); | ||
10859 | +} | ||
10860 | + | ||
10861 | +/* | ||
10862 | + * preview_enable_drkframe - Enable/Disable of the darkframe subtract. | ||
10863 | + * @enable: 1 - Acquires memory bandwidth since the pixels in each frame is | ||
10864 | + * subtracted with the pixels in the current frame. | ||
10865 | + * | ||
10866 | + * The proccess is applied for each captured frame. | ||
10867 | + */ | ||
10868 | +static void | ||
10869 | +preview_enable_drkframe(struct isp_prev_device *prev, u8 enable) | ||
10870 | +{ | ||
10871 | + struct isp_device *isp = to_isp_device(prev); | ||
10872 | + | ||
10873 | + if (enable) | ||
10874 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
10875 | + ISPPRV_PCR_DRKFEN); | ||
10876 | + else | ||
10877 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
10878 | + ISPPRV_PCR_DRKFEN); | ||
10879 | +} | ||
10880 | + | ||
10881 | +/* | ||
10882 | + * preview_config_drkf_shadcomp - Configures shift value in shading comp. | ||
10883 | + * @scomp_shtval: 3bit value of shift used in shading compensation. | ||
10884 | + */ | ||
10885 | +static void | ||
10886 | +preview_config_drkf_shadcomp(struct isp_prev_device *prev, | ||
10887 | + const void *scomp_shtval) | ||
10888 | +{ | ||
10889 | + struct isp_device *isp = to_isp_device(prev); | ||
10890 | + const u32 *shtval = scomp_shtval; | ||
10891 | + | ||
10892 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
10893 | + ISPPRV_PCR_SCOMP_SFT_MASK, | ||
10894 | + *shtval << ISPPRV_PCR_SCOMP_SFT_SHIFT); | ||
10895 | +} | ||
10896 | + | ||
10897 | +/* | ||
10898 | + * preview_enable_hmed - Enables/Disables of the Horizontal Median Filter. | ||
10899 | + * @enable: 1 - Enables Horizontal Median Filter. | ||
10900 | + */ | ||
10901 | +static void | ||
10902 | +preview_enable_hmed(struct isp_prev_device *prev, u8 enable) | ||
10903 | +{ | ||
10904 | + struct isp_device *isp = to_isp_device(prev); | ||
10905 | + | ||
10906 | + if (enable) | ||
10907 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
10908 | + ISPPRV_PCR_HMEDEN); | ||
10909 | + else | ||
10910 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
10911 | + ISPPRV_PCR_HMEDEN); | ||
10912 | +} | ||
10913 | + | ||
10914 | +/* | ||
10915 | + * preview_config_hmed - Configures the Horizontal Median Filter. | ||
10916 | + * @prev_hmed: Structure containing the odd and even distance between the | ||
10917 | + * pixels in the image along with the filter threshold. | ||
10918 | + */ | ||
10919 | +static void | ||
10920 | +preview_config_hmed(struct isp_prev_device *prev, const void *prev_hmed) | ||
10921 | +{ | ||
10922 | + struct isp_device *isp = to_isp_device(prev); | ||
10923 | + const struct omap3isp_prev_hmed *hmed = prev_hmed; | ||
10924 | + | ||
10925 | + isp_reg_writel(isp, (hmed->odddist == 1 ? 0 : ISPPRV_HMED_ODDDIST) | | ||
10926 | + (hmed->evendist == 1 ? 0 : ISPPRV_HMED_EVENDIST) | | ||
10927 | + (hmed->thres << ISPPRV_HMED_THRESHOLD_SHIFT), | ||
10928 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_HMED); | ||
10929 | +} | ||
10930 | + | ||
10931 | +/* | ||
10932 | + * preview_config_noisefilter - Configures the Noise Filter. | ||
10933 | + * @prev_nf: Structure containing the noisefilter table, strength to be used | ||
10934 | + * for the noise filter and the defect correction enable flag. | ||
10935 | + */ | ||
10936 | +static void | ||
10937 | +preview_config_noisefilter(struct isp_prev_device *prev, const void *prev_nf) | ||
10938 | +{ | ||
10939 | + struct isp_device *isp = to_isp_device(prev); | ||
10940 | + const struct omap3isp_prev_nf *nf = prev_nf; | ||
10941 | + unsigned int i; | ||
10942 | + | ||
10943 | + isp_reg_writel(isp, nf->spread, OMAP3_ISP_IOMEM_PREV, ISPPRV_NF); | ||
10944 | + isp_reg_writel(isp, ISPPRV_NF_TABLE_ADDR, | ||
10945 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); | ||
10946 | + for (i = 0; i < OMAP3ISP_PREV_NF_TBL_SIZE; i++) { | ||
10947 | + isp_reg_writel(isp, nf->table[i], | ||
10948 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); | ||
10949 | + } | ||
10950 | +} | ||
10951 | + | ||
10952 | +/* | ||
10953 | + * preview_config_dcor - Configures the defect correction | ||
10954 | + * @prev_dcor: Structure containing the defect correct thresholds | ||
10955 | + */ | ||
10956 | +static void | ||
10957 | +preview_config_dcor(struct isp_prev_device *prev, const void *prev_dcor) | ||
10958 | +{ | ||
10959 | + struct isp_device *isp = to_isp_device(prev); | ||
10960 | + const struct omap3isp_prev_dcor *dcor = prev_dcor; | ||
10961 | + | ||
10962 | + isp_reg_writel(isp, dcor->detect_correct[0], | ||
10963 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR0); | ||
10964 | + isp_reg_writel(isp, dcor->detect_correct[1], | ||
10965 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR1); | ||
10966 | + isp_reg_writel(isp, dcor->detect_correct[2], | ||
10967 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR2); | ||
10968 | + isp_reg_writel(isp, dcor->detect_correct[3], | ||
10969 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR3); | ||
10970 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
10971 | + ISPPRV_PCR_DCCOUP, | ||
10972 | + dcor->couplet_mode_en ? ISPPRV_PCR_DCCOUP : 0); | ||
10973 | +} | ||
10974 | + | ||
10975 | +/* | ||
10976 | + * preview_config_cfa - Configures the CFA Interpolation parameters. | ||
10977 | + * @prev_cfa: Structure containing the CFA interpolation table, CFA format | ||
10978 | + * in the image, vertical and horizontal gradient threshold. | ||
10979 | + */ | ||
10980 | +static void | ||
10981 | +preview_config_cfa(struct isp_prev_device *prev, const void *prev_cfa) | ||
10982 | +{ | ||
10983 | + struct isp_device *isp = to_isp_device(prev); | ||
10984 | + const struct omap3isp_prev_cfa *cfa = prev_cfa; | ||
10985 | + unsigned int i; | ||
10986 | + | ||
10987 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
10988 | + ISPPRV_PCR_CFAFMT_MASK, | ||
10989 | + cfa->format << ISPPRV_PCR_CFAFMT_SHIFT); | ||
10990 | + | ||
10991 | + isp_reg_writel(isp, | ||
10992 | + (cfa->gradthrs_vert << ISPPRV_CFA_GRADTH_VER_SHIFT) | | ||
10993 | + (cfa->gradthrs_horz << ISPPRV_CFA_GRADTH_HOR_SHIFT), | ||
10994 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_CFA); | ||
10995 | + | ||
10996 | + isp_reg_writel(isp, ISPPRV_CFA_TABLE_ADDR, | ||
10997 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); | ||
10998 | + | ||
10999 | + for (i = 0; i < OMAP3ISP_PREV_CFA_TBL_SIZE; i++) { | ||
11000 | + isp_reg_writel(isp, cfa->table[i], | ||
11001 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); | ||
11002 | + } | ||
11003 | +} | ||
11004 | + | ||
11005 | +/* | ||
11006 | + * preview_config_gammacorrn - Configures the Gamma Correction table values | ||
11007 | + * @gtable: Structure containing the table for red, blue, green gamma table. | ||
11008 | + */ | ||
11009 | +static void | ||
11010 | +preview_config_gammacorrn(struct isp_prev_device *prev, const void *gtable) | ||
11011 | +{ | ||
11012 | + struct isp_device *isp = to_isp_device(prev); | ||
11013 | + const struct omap3isp_prev_gtables *gt = gtable; | ||
11014 | + unsigned int i; | ||
11015 | + | ||
11016 | + isp_reg_writel(isp, ISPPRV_REDGAMMA_TABLE_ADDR, | ||
11017 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); | ||
11018 | + for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++) | ||
11019 | + isp_reg_writel(isp, gt->red[i], OMAP3_ISP_IOMEM_PREV, | ||
11020 | + ISPPRV_SET_TBL_DATA); | ||
11021 | + | ||
11022 | + isp_reg_writel(isp, ISPPRV_GREENGAMMA_TABLE_ADDR, | ||
11023 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); | ||
11024 | + for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++) | ||
11025 | + isp_reg_writel(isp, gt->green[i], OMAP3_ISP_IOMEM_PREV, | ||
11026 | + ISPPRV_SET_TBL_DATA); | ||
11027 | + | ||
11028 | + isp_reg_writel(isp, ISPPRV_BLUEGAMMA_TABLE_ADDR, | ||
11029 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); | ||
11030 | + for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++) | ||
11031 | + isp_reg_writel(isp, gt->blue[i], OMAP3_ISP_IOMEM_PREV, | ||
11032 | + ISPPRV_SET_TBL_DATA); | ||
11033 | +} | ||
11034 | + | ||
11035 | +/* | ||
11036 | + * preview_config_luma_enhancement - Sets the Luminance Enhancement table. | ||
11037 | + * @ytable: Structure containing the table for Luminance Enhancement table. | ||
11038 | + */ | ||
11039 | +static void | ||
11040 | +preview_config_luma_enhancement(struct isp_prev_device *prev, | ||
11041 | + const void *ytable) | ||
11042 | +{ | ||
11043 | + struct isp_device *isp = to_isp_device(prev); | ||
11044 | + const struct omap3isp_prev_luma *yt = ytable; | ||
11045 | + unsigned int i; | ||
11046 | + | ||
11047 | + isp_reg_writel(isp, ISPPRV_YENH_TABLE_ADDR, | ||
11048 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); | ||
11049 | + for (i = 0; i < OMAP3ISP_PREV_YENH_TBL_SIZE; i++) { | ||
11050 | + isp_reg_writel(isp, yt->table[i], | ||
11051 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); | ||
11052 | + } | ||
11053 | +} | ||
11054 | + | ||
11055 | +/* | ||
11056 | + * preview_config_chroma_suppression - Configures the Chroma Suppression. | ||
11057 | + * @csup: Structure containing the threshold value for suppression | ||
11058 | + * and the hypass filter enable flag. | ||
11059 | + */ | ||
11060 | +static void | ||
11061 | +preview_config_chroma_suppression(struct isp_prev_device *prev, | ||
11062 | + const void *csup) | ||
11063 | +{ | ||
11064 | + struct isp_device *isp = to_isp_device(prev); | ||
11065 | + const struct omap3isp_prev_csup *cs = csup; | ||
11066 | + | ||
11067 | + isp_reg_writel(isp, | ||
11068 | + cs->gain | (cs->thres << ISPPRV_CSUP_THRES_SHIFT) | | ||
11069 | + (cs->hypf_en << ISPPRV_CSUP_HPYF_SHIFT), | ||
11070 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_CSUP); | ||
11071 | +} | ||
11072 | + | ||
11073 | +/* | ||
11074 | + * preview_enable_noisefilter - Enables/Disables the Noise Filter. | ||
11075 | + * @enable: 1 - Enables the Noise Filter. | ||
11076 | + */ | ||
11077 | +static void | ||
11078 | +preview_enable_noisefilter(struct isp_prev_device *prev, u8 enable) | ||
11079 | +{ | ||
11080 | + struct isp_device *isp = to_isp_device(prev); | ||
11081 | + | ||
11082 | + if (enable) | ||
11083 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11084 | + ISPPRV_PCR_NFEN); | ||
11085 | + else | ||
11086 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11087 | + ISPPRV_PCR_NFEN); | ||
11088 | +} | ||
11089 | + | ||
11090 | +/* | ||
11091 | + * preview_enable_dcor - Enables/Disables the defect correction. | ||
11092 | + * @enable: 1 - Enables the defect correction. | ||
11093 | + */ | ||
11094 | +static void | ||
11095 | +preview_enable_dcor(struct isp_prev_device *prev, u8 enable) | ||
11096 | +{ | ||
11097 | + struct isp_device *isp = to_isp_device(prev); | ||
11098 | + | ||
11099 | + if (enable) | ||
11100 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11101 | + ISPPRV_PCR_DCOREN); | ||
11102 | + else | ||
11103 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11104 | + ISPPRV_PCR_DCOREN); | ||
11105 | +} | ||
11106 | + | ||
11107 | +/* | ||
11108 | + * preview_enable_cfa - Enable/Disable the CFA Interpolation. | ||
11109 | + * @enable: 1 - Enables the CFA. | ||
11110 | + */ | ||
11111 | +static void | ||
11112 | +preview_enable_cfa(struct isp_prev_device *prev, u8 enable) | ||
11113 | +{ | ||
11114 | + struct isp_device *isp = to_isp_device(prev); | ||
11115 | + | ||
11116 | + if (enable) | ||
11117 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11118 | + ISPPRV_PCR_CFAEN); | ||
11119 | + else | ||
11120 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11121 | + ISPPRV_PCR_CFAEN); | ||
11122 | +} | ||
11123 | + | ||
11124 | +/* | ||
11125 | + * preview_enable_gammabypass - Enables/Disables the GammaByPass | ||
11126 | + * @enable: 1 - Bypasses Gamma - 10bit input is cropped to 8MSB. | ||
11127 | + * 0 - Goes through Gamma Correction. input and output is 10bit. | ||
11128 | + */ | ||
11129 | +static void | ||
11130 | +preview_enable_gammabypass(struct isp_prev_device *prev, u8 enable) | ||
11131 | +{ | ||
11132 | + struct isp_device *isp = to_isp_device(prev); | ||
11133 | + | ||
11134 | + if (enable) | ||
11135 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11136 | + ISPPRV_PCR_GAMMA_BYPASS); | ||
11137 | + else | ||
11138 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11139 | + ISPPRV_PCR_GAMMA_BYPASS); | ||
11140 | +} | ||
11141 | + | ||
11142 | +/* | ||
11143 | + * preview_enable_luma_enhancement - Enables/Disables Luminance Enhancement | ||
11144 | + * @enable: 1 - Enable the Luminance Enhancement. | ||
11145 | + */ | ||
11146 | +static void | ||
11147 | +preview_enable_luma_enhancement(struct isp_prev_device *prev, u8 enable) | ||
11148 | +{ | ||
11149 | + struct isp_device *isp = to_isp_device(prev); | ||
11150 | + | ||
11151 | + if (enable) | ||
11152 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11153 | + ISPPRV_PCR_YNENHEN); | ||
11154 | + else | ||
11155 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11156 | + ISPPRV_PCR_YNENHEN); | ||
11157 | +} | ||
11158 | + | ||
11159 | +/* | ||
11160 | + * preview_enable_chroma_suppression - Enables/Disables Chrominance Suppr. | ||
11161 | + * @enable: 1 - Enable the Chrominance Suppression. | ||
11162 | + */ | ||
11163 | +static void | ||
11164 | +preview_enable_chroma_suppression(struct isp_prev_device *prev, u8 enable) | ||
11165 | +{ | ||
11166 | + struct isp_device *isp = to_isp_device(prev); | ||
11167 | + | ||
11168 | + if (enable) | ||
11169 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11170 | + ISPPRV_PCR_SUPEN); | ||
11171 | + else | ||
11172 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11173 | + ISPPRV_PCR_SUPEN); | ||
11174 | +} | ||
11175 | + | ||
11176 | +/* | ||
11177 | + * preview_config_whitebalance - Configures the White Balance parameters. | ||
11178 | + * @prev_wbal: Structure containing the digital gain and white balance | ||
11179 | + * coefficient. | ||
11180 | + * | ||
11181 | + * Coefficient matrix always with default values. | ||
11182 | + */ | ||
11183 | +static void | ||
11184 | +preview_config_whitebalance(struct isp_prev_device *prev, const void *prev_wbal) | ||
11185 | +{ | ||
11186 | + struct isp_device *isp = to_isp_device(prev); | ||
11187 | + const struct omap3isp_prev_wbal *wbal = prev_wbal; | ||
11188 | + u32 val; | ||
11189 | + | ||
11190 | + isp_reg_writel(isp, wbal->dgain, OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN); | ||
11191 | + | ||
11192 | + val = wbal->coef0 << ISPPRV_WBGAIN_COEF0_SHIFT; | ||
11193 | + val |= wbal->coef1 << ISPPRV_WBGAIN_COEF1_SHIFT; | ||
11194 | + val |= wbal->coef2 << ISPPRV_WBGAIN_COEF2_SHIFT; | ||
11195 | + val |= wbal->coef3 << ISPPRV_WBGAIN_COEF3_SHIFT; | ||
11196 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN); | ||
11197 | + | ||
11198 | + isp_reg_writel(isp, | ||
11199 | + ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_0_SHIFT | | ||
11200 | + ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_1_SHIFT | | ||
11201 | + ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_2_SHIFT | | ||
11202 | + ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_3_SHIFT | | ||
11203 | + ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_0_SHIFT | | ||
11204 | + ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_1_SHIFT | | ||
11205 | + ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_2_SHIFT | | ||
11206 | + ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_3_SHIFT | | ||
11207 | + ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_0_SHIFT | | ||
11208 | + ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_1_SHIFT | | ||
11209 | + ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_2_SHIFT | | ||
11210 | + ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_3_SHIFT | | ||
11211 | + ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_0_SHIFT | | ||
11212 | + ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_1_SHIFT | | ||
11213 | + ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_2_SHIFT | | ||
11214 | + ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_3_SHIFT, | ||
11215 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL); | ||
11216 | +} | ||
11217 | + | ||
11218 | +/* | ||
11219 | + * preview_config_blkadj - Configures the Black Adjustment parameters. | ||
11220 | + * @prev_blkadj: Structure containing the black adjustment towards red, green, | ||
11221 | + * blue. | ||
11222 | + */ | ||
11223 | +static void | ||
11224 | +preview_config_blkadj(struct isp_prev_device *prev, const void *prev_blkadj) | ||
11225 | +{ | ||
11226 | + struct isp_device *isp = to_isp_device(prev); | ||
11227 | + const struct omap3isp_prev_blkadj *blkadj = prev_blkadj; | ||
11228 | + | ||
11229 | + isp_reg_writel(isp, (blkadj->blue << ISPPRV_BLKADJOFF_B_SHIFT) | | ||
11230 | + (blkadj->green << ISPPRV_BLKADJOFF_G_SHIFT) | | ||
11231 | + (blkadj->red << ISPPRV_BLKADJOFF_R_SHIFT), | ||
11232 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_BLKADJOFF); | ||
11233 | +} | ||
11234 | + | ||
11235 | +/* | ||
11236 | + * preview_config_rgb_blending - Configures the RGB-RGB Blending matrix. | ||
11237 | + * @rgb2rgb: Structure containing the rgb to rgb blending matrix and the rgb | ||
11238 | + * offset. | ||
11239 | + */ | ||
11240 | +static void | ||
11241 | +preview_config_rgb_blending(struct isp_prev_device *prev, const void *rgb2rgb) | ||
11242 | +{ | ||
11243 | + struct isp_device *isp = to_isp_device(prev); | ||
11244 | + const struct omap3isp_prev_rgbtorgb *rgbrgb = rgb2rgb; | ||
11245 | + u32 val; | ||
11246 | + | ||
11247 | + val = (rgbrgb->matrix[0][0] & 0xfff) << ISPPRV_RGB_MAT1_MTX_RR_SHIFT; | ||
11248 | + val |= (rgbrgb->matrix[0][1] & 0xfff) << ISPPRV_RGB_MAT1_MTX_GR_SHIFT; | ||
11249 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT1); | ||
11250 | + | ||
11251 | + val = (rgbrgb->matrix[0][2] & 0xfff) << ISPPRV_RGB_MAT2_MTX_BR_SHIFT; | ||
11252 | + val |= (rgbrgb->matrix[1][0] & 0xfff) << ISPPRV_RGB_MAT2_MTX_RG_SHIFT; | ||
11253 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT2); | ||
11254 | + | ||
11255 | + val = (rgbrgb->matrix[1][1] & 0xfff) << ISPPRV_RGB_MAT3_MTX_GG_SHIFT; | ||
11256 | + val |= (rgbrgb->matrix[1][2] & 0xfff) << ISPPRV_RGB_MAT3_MTX_BG_SHIFT; | ||
11257 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT3); | ||
11258 | + | ||
11259 | + val = (rgbrgb->matrix[2][0] & 0xfff) << ISPPRV_RGB_MAT4_MTX_RB_SHIFT; | ||
11260 | + val |= (rgbrgb->matrix[2][1] & 0xfff) << ISPPRV_RGB_MAT4_MTX_GB_SHIFT; | ||
11261 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT4); | ||
11262 | + | ||
11263 | + val = (rgbrgb->matrix[2][2] & 0xfff) << ISPPRV_RGB_MAT5_MTX_BB_SHIFT; | ||
11264 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT5); | ||
11265 | + | ||
11266 | + val = (rgbrgb->offset[0] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT; | ||
11267 | + val |= (rgbrgb->offset[1] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT; | ||
11268 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF1); | ||
11269 | + | ||
11270 | + val = (rgbrgb->offset[2] & 0x3ff) << ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT; | ||
11271 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF2); | ||
11272 | +} | ||
11273 | + | ||
11274 | +/* | ||
11275 | + * Configures the RGB-YCbYCr conversion matrix | ||
11276 | + * @prev_csc: Structure containing the RGB to YCbYCr matrix and the | ||
11277 | + * YCbCr offset. | ||
11278 | + */ | ||
11279 | +static void | ||
11280 | +preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc) | ||
11281 | +{ | ||
11282 | + struct isp_device *isp = to_isp_device(prev); | ||
11283 | + const struct omap3isp_prev_csc *csc = prev_csc; | ||
11284 | + u32 val; | ||
11285 | + | ||
11286 | + val = (csc->matrix[0][0] & 0x3ff) << ISPPRV_CSC0_RY_SHIFT; | ||
11287 | + val |= (csc->matrix[0][1] & 0x3ff) << ISPPRV_CSC0_GY_SHIFT; | ||
11288 | + val |= (csc->matrix[0][2] & 0x3ff) << ISPPRV_CSC0_BY_SHIFT; | ||
11289 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC0); | ||
11290 | + | ||
11291 | + val = (csc->matrix[1][0] & 0x3ff) << ISPPRV_CSC1_RCB_SHIFT; | ||
11292 | + val |= (csc->matrix[1][1] & 0x3ff) << ISPPRV_CSC1_GCB_SHIFT; | ||
11293 | + val |= (csc->matrix[1][2] & 0x3ff) << ISPPRV_CSC1_BCB_SHIFT; | ||
11294 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC1); | ||
11295 | + | ||
11296 | + val = (csc->matrix[2][0] & 0x3ff) << ISPPRV_CSC2_RCR_SHIFT; | ||
11297 | + val |= (csc->matrix[2][1] & 0x3ff) << ISPPRV_CSC2_GCR_SHIFT; | ||
11298 | + val |= (csc->matrix[2][2] & 0x3ff) << ISPPRV_CSC2_BCR_SHIFT; | ||
11299 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC2); | ||
11300 | + | ||
11301 | + val = (csc->offset[0] & 0xff) << ISPPRV_CSC_OFFSET_Y_SHIFT; | ||
11302 | + val |= (csc->offset[1] & 0xff) << ISPPRV_CSC_OFFSET_CB_SHIFT; | ||
11303 | + val |= (csc->offset[2] & 0xff) << ISPPRV_CSC_OFFSET_CR_SHIFT; | ||
11304 | + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC_OFFSET); | ||
11305 | +} | ||
11306 | + | ||
11307 | +/* | ||
11308 | + * preview_update_contrast - Updates the contrast. | ||
11309 | + * @contrast: Pointer to hold the current programmed contrast value. | ||
11310 | + * | ||
11311 | + * Value should be programmed before enabling the module. | ||
11312 | + */ | ||
11313 | +static void | ||
11314 | +preview_update_contrast(struct isp_prev_device *prev, u8 contrast) | ||
11315 | +{ | ||
11316 | + struct prev_params *params = &prev->params; | ||
11317 | + | ||
11318 | + if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) { | ||
11319 | + params->contrast = contrast * ISPPRV_CONTRAST_UNITS; | ||
11320 | + prev->update |= PREV_CONTRAST; | ||
11321 | + } | ||
11322 | +} | ||
11323 | + | ||
11324 | +/* | ||
11325 | + * preview_config_contrast - Configures the Contrast. | ||
11326 | + * @params: Contrast value (u8 pointer, U8Q0 format). | ||
11327 | + * | ||
11328 | + * Value should be programmed before enabling the module. | ||
11329 | + */ | ||
11330 | +static void | ||
11331 | +preview_config_contrast(struct isp_prev_device *prev, const void *params) | ||
11332 | +{ | ||
11333 | + struct isp_device *isp = to_isp_device(prev); | ||
11334 | + | ||
11335 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT, | ||
11336 | + 0xff << ISPPRV_CNT_BRT_CNT_SHIFT, | ||
11337 | + *(u8 *)params << ISPPRV_CNT_BRT_CNT_SHIFT); | ||
11338 | +} | ||
11339 | + | ||
11340 | +/* | ||
11341 | + * preview_update_brightness - Updates the brightness in preview module. | ||
11342 | + * @brightness: Pointer to hold the current programmed brightness value. | ||
11343 | + * | ||
11344 | + */ | ||
11345 | +static void | ||
11346 | +preview_update_brightness(struct isp_prev_device *prev, u8 brightness) | ||
11347 | +{ | ||
11348 | + struct prev_params *params = &prev->params; | ||
11349 | + | ||
11350 | + if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) { | ||
11351 | + params->brightness = brightness * ISPPRV_BRIGHT_UNITS; | ||
11352 | + prev->update |= PREV_BRIGHTNESS; | ||
11353 | + } | ||
11354 | +} | ||
11355 | + | ||
11356 | +/* | ||
11357 | + * preview_config_brightness - Configures the brightness. | ||
11358 | + * @params: Brightness value (u8 pointer, U8Q0 format). | ||
11359 | + */ | ||
11360 | +static void | ||
11361 | +preview_config_brightness(struct isp_prev_device *prev, const void *params) | ||
11362 | +{ | ||
11363 | + struct isp_device *isp = to_isp_device(prev); | ||
11364 | + | ||
11365 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT, | ||
11366 | + 0xff << ISPPRV_CNT_BRT_BRT_SHIFT, | ||
11367 | + *(u8 *)params << ISPPRV_CNT_BRT_BRT_SHIFT); | ||
11368 | +} | ||
11369 | + | ||
11370 | +/* | ||
11371 | + * preview_config_yc_range - Configures the max and min Y and C values. | ||
11372 | + * @yclimit: Structure containing the range of Y and C values. | ||
11373 | + */ | ||
11374 | +static void | ||
11375 | +preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit) | ||
11376 | +{ | ||
11377 | + struct isp_device *isp = to_isp_device(prev); | ||
11378 | + const struct omap3isp_prev_yclimit *yc = yclimit; | ||
11379 | + | ||
11380 | + isp_reg_writel(isp, | ||
11381 | + yc->maxC << ISPPRV_SETUP_YC_MAXC_SHIFT | | ||
11382 | + yc->maxY << ISPPRV_SETUP_YC_MAXY_SHIFT | | ||
11383 | + yc->minC << ISPPRV_SETUP_YC_MINC_SHIFT | | ||
11384 | + yc->minY << ISPPRV_SETUP_YC_MINY_SHIFT, | ||
11385 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC); | ||
11386 | +} | ||
11387 | + | ||
11388 | +/* preview parameters update structure */ | ||
11389 | +struct preview_update { | ||
11390 | + int cfg_bit; | ||
11391 | + int feature_bit; | ||
11392 | + void (*config)(struct isp_prev_device *, const void *); | ||
11393 | + void (*enable)(struct isp_prev_device *, u8); | ||
11394 | +}; | ||
11395 | + | ||
11396 | +static struct preview_update update_attrs[] = { | ||
11397 | + {OMAP3ISP_PREV_LUMAENH, PREV_LUMA_ENHANCE, | ||
11398 | + preview_config_luma_enhancement, | ||
11399 | + preview_enable_luma_enhancement}, | ||
11400 | + {OMAP3ISP_PREV_INVALAW, PREV_INVERSE_ALAW, | ||
11401 | + NULL, | ||
11402 | + preview_enable_invalaw}, | ||
11403 | + {OMAP3ISP_PREV_HRZ_MED, PREV_HORZ_MEDIAN_FILTER, | ||
11404 | + preview_config_hmed, | ||
11405 | + preview_enable_hmed}, | ||
11406 | + {OMAP3ISP_PREV_CFA, PREV_CFA, | ||
11407 | + preview_config_cfa, | ||
11408 | + preview_enable_cfa}, | ||
11409 | + {OMAP3ISP_PREV_CHROMA_SUPP, PREV_CHROMA_SUPPRESS, | ||
11410 | + preview_config_chroma_suppression, | ||
11411 | + preview_enable_chroma_suppression}, | ||
11412 | + {OMAP3ISP_PREV_WB, PREV_WB, | ||
11413 | + preview_config_whitebalance, | ||
11414 | + NULL}, | ||
11415 | + {OMAP3ISP_PREV_BLKADJ, PREV_BLKADJ, | ||
11416 | + preview_config_blkadj, | ||
11417 | + NULL}, | ||
11418 | + {OMAP3ISP_PREV_RGB2RGB, PREV_RGB2RGB, | ||
11419 | + preview_config_rgb_blending, | ||
11420 | + NULL}, | ||
11421 | + {OMAP3ISP_PREV_COLOR_CONV, PREV_COLOR_CONV, | ||
11422 | + preview_config_rgb_to_ycbcr, | ||
11423 | + NULL}, | ||
11424 | + {OMAP3ISP_PREV_YC_LIMIT, PREV_YCLIMITS, | ||
11425 | + preview_config_yc_range, | ||
11426 | + NULL}, | ||
11427 | + {OMAP3ISP_PREV_DEFECT_COR, PREV_DEFECT_COR, | ||
11428 | + preview_config_dcor, | ||
11429 | + preview_enable_dcor}, | ||
11430 | + {OMAP3ISP_PREV_GAMMABYPASS, PREV_GAMMA_BYPASS, | ||
11431 | + NULL, | ||
11432 | + preview_enable_gammabypass}, | ||
11433 | + {OMAP3ISP_PREV_DRK_FRM_CAPTURE, PREV_DARK_FRAME_CAPTURE, | ||
11434 | + NULL, | ||
11435 | + preview_enable_drkframe_capture}, | ||
11436 | + {OMAP3ISP_PREV_DRK_FRM_SUBTRACT, PREV_DARK_FRAME_SUBTRACT, | ||
11437 | + NULL, | ||
11438 | + preview_enable_drkframe}, | ||
11439 | + {OMAP3ISP_PREV_LENS_SHADING, PREV_LENS_SHADING, | ||
11440 | + preview_config_drkf_shadcomp, | ||
11441 | + preview_enable_drkframe}, | ||
11442 | + {OMAP3ISP_PREV_NF, PREV_NOISE_FILTER, | ||
11443 | + preview_config_noisefilter, | ||
11444 | + preview_enable_noisefilter}, | ||
11445 | + {OMAP3ISP_PREV_GAMMA, PREV_GAMMA, | ||
11446 | + preview_config_gammacorrn, | ||
11447 | + NULL}, | ||
11448 | + {-1, PREV_CONTRAST, | ||
11449 | + preview_config_contrast, | ||
11450 | + NULL}, | ||
11451 | + {-1, PREV_BRIGHTNESS, | ||
11452 | + preview_config_brightness, | ||
11453 | + NULL}, | ||
11454 | +}; | ||
11455 | + | ||
11456 | +/* | ||
11457 | + * __preview_get_ptrs - helper function which return pointers to members | ||
11458 | + * of params and config structures. | ||
11459 | + * @params - pointer to preview_params structure. | ||
11460 | + * @param - return pointer to appropriate structure field. | ||
11461 | + * @configs - pointer to update config structure. | ||
11462 | + * @config - return pointer to appropriate structure field. | ||
11463 | + * @bit - for which feature to return pointers. | ||
11464 | + * Return size of coresponding prev_params member | ||
11465 | + */ | ||
11466 | +static u32 | ||
11467 | +__preview_get_ptrs(struct prev_params *params, void **param, | ||
11468 | + struct omap3isp_prev_update_config *configs, | ||
11469 | + void __user **config, u32 bit) | ||
11470 | +{ | ||
11471 | +#define CHKARG(cfgs, cfg, field) \ | ||
11472 | + if (cfgs && cfg) { \ | ||
11473 | + *(cfg) = (cfgs)->field; \ | ||
11474 | + } | ||
11475 | + | ||
11476 | + switch (bit) { | ||
11477 | + case PREV_HORZ_MEDIAN_FILTER: | ||
11478 | + *param = ¶ms->hmed; | ||
11479 | + CHKARG(configs, config, hmed) | ||
11480 | + return sizeof(params->hmed); | ||
11481 | + case PREV_NOISE_FILTER: | ||
11482 | + *param = ¶ms->nf; | ||
11483 | + CHKARG(configs, config, nf) | ||
11484 | + return sizeof(params->nf); | ||
11485 | + break; | ||
11486 | + case PREV_CFA: | ||
11487 | + *param = ¶ms->cfa; | ||
11488 | + CHKARG(configs, config, cfa) | ||
11489 | + return sizeof(params->cfa); | ||
11490 | + case PREV_LUMA_ENHANCE: | ||
11491 | + *param = ¶ms->luma; | ||
11492 | + CHKARG(configs, config, luma) | ||
11493 | + return sizeof(params->luma); | ||
11494 | + case PREV_CHROMA_SUPPRESS: | ||
11495 | + *param = ¶ms->csup; | ||
11496 | + CHKARG(configs, config, csup) | ||
11497 | + return sizeof(params->csup); | ||
11498 | + case PREV_DEFECT_COR: | ||
11499 | + *param = ¶ms->dcor; | ||
11500 | + CHKARG(configs, config, dcor) | ||
11501 | + return sizeof(params->dcor); | ||
11502 | + case PREV_BLKADJ: | ||
11503 | + *param = ¶ms->blk_adj; | ||
11504 | + CHKARG(configs, config, blkadj) | ||
11505 | + return sizeof(params->blk_adj); | ||
11506 | + case PREV_YCLIMITS: | ||
11507 | + *param = ¶ms->yclimit; | ||
11508 | + CHKARG(configs, config, yclimit) | ||
11509 | + return sizeof(params->yclimit); | ||
11510 | + case PREV_RGB2RGB: | ||
11511 | + *param = ¶ms->rgb2rgb; | ||
11512 | + CHKARG(configs, config, rgb2rgb) | ||
11513 | + return sizeof(params->rgb2rgb); | ||
11514 | + case PREV_COLOR_CONV: | ||
11515 | + *param = ¶ms->rgb2ycbcr; | ||
11516 | + CHKARG(configs, config, csc) | ||
11517 | + return sizeof(params->rgb2ycbcr); | ||
11518 | + case PREV_WB: | ||
11519 | + *param = ¶ms->wbal; | ||
11520 | + CHKARG(configs, config, wbal) | ||
11521 | + return sizeof(params->wbal); | ||
11522 | + case PREV_GAMMA: | ||
11523 | + *param = ¶ms->gamma; | ||
11524 | + CHKARG(configs, config, gamma) | ||
11525 | + return sizeof(params->gamma); | ||
11526 | + case PREV_CONTRAST: | ||
11527 | + *param = ¶ms->contrast; | ||
11528 | + return 0; | ||
11529 | + case PREV_BRIGHTNESS: | ||
11530 | + *param = ¶ms->brightness; | ||
11531 | + return 0; | ||
11532 | + default: | ||
11533 | + *param = NULL; | ||
11534 | + *config = NULL; | ||
11535 | + break; | ||
11536 | + } | ||
11537 | + return 0; | ||
11538 | +} | ||
11539 | + | ||
11540 | +/* | ||
11541 | + * preview_config - Copy and update local structure with userspace preview | ||
11542 | + * configuration. | ||
11543 | + * @prev: ISP preview engine | ||
11544 | + * @cfg: Configuration | ||
11545 | + * | ||
11546 | + * Return zero if success or -EFAULT if the configuration can't be copied from | ||
11547 | + * userspace. | ||
11548 | + */ | ||
11549 | +static int preview_config(struct isp_prev_device *prev, | ||
11550 | + struct omap3isp_prev_update_config *cfg) | ||
11551 | +{ | ||
11552 | + struct prev_params *params; | ||
11553 | + struct preview_update *attr; | ||
11554 | + int i, bit, rval = 0; | ||
11555 | + | ||
11556 | + params = &prev->params; | ||
11557 | + | ||
11558 | + if (prev->state != ISP_PIPELINE_STREAM_STOPPED) { | ||
11559 | + unsigned long flags; | ||
11560 | + | ||
11561 | + spin_lock_irqsave(&prev->lock, flags); | ||
11562 | + prev->shadow_update = 1; | ||
11563 | + spin_unlock_irqrestore(&prev->lock, flags); | ||
11564 | + } | ||
11565 | + | ||
11566 | + for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { | ||
11567 | + attr = &update_attrs[i]; | ||
11568 | + bit = 0; | ||
11569 | + | ||
11570 | + if (!(cfg->update & attr->cfg_bit)) | ||
11571 | + continue; | ||
11572 | + | ||
11573 | + bit = cfg->flag & attr->cfg_bit; | ||
11574 | + if (bit) { | ||
11575 | + void *to = NULL, __user *from = NULL; | ||
11576 | + unsigned long sz = 0; | ||
11577 | + | ||
11578 | + sz = __preview_get_ptrs(params, &to, cfg, &from, | ||
11579 | + bit); | ||
11580 | + if (to && from && sz) { | ||
11581 | + if (copy_from_user(to, from, sz)) { | ||
11582 | + rval = -EFAULT; | ||
11583 | + break; | ||
11584 | + } | ||
11585 | + } | ||
11586 | + params->features |= attr->feature_bit; | ||
11587 | + } else { | ||
11588 | + params->features &= ~attr->feature_bit; | ||
11589 | + } | ||
11590 | + | ||
11591 | + prev->update |= attr->feature_bit; | ||
11592 | + } | ||
11593 | + | ||
11594 | + prev->shadow_update = 0; | ||
11595 | + return rval; | ||
11596 | +} | ||
11597 | + | ||
11598 | +/* | ||
11599 | + * preview_setup_hw - Setup preview registers and/or internal memory | ||
11600 | + * @prev: pointer to preview private structure | ||
11601 | + * Note: can be called from interrupt context | ||
11602 | + * Return none | ||
11603 | + */ | ||
11604 | +static void preview_setup_hw(struct isp_prev_device *prev) | ||
11605 | +{ | ||
11606 | + struct prev_params *params = &prev->params; | ||
11607 | + struct preview_update *attr; | ||
11608 | + int i, bit; | ||
11609 | + void *param_ptr; | ||
11610 | + | ||
11611 | + for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { | ||
11612 | + attr = &update_attrs[i]; | ||
11613 | + | ||
11614 | + if (!(prev->update & attr->feature_bit)) | ||
11615 | + continue; | ||
11616 | + bit = params->features & attr->feature_bit; | ||
11617 | + if (bit) { | ||
11618 | + if (attr->config) { | ||
11619 | + __preview_get_ptrs(params, ¶m_ptr, NULL, | ||
11620 | + NULL, bit); | ||
11621 | + attr->config(prev, param_ptr); | ||
11622 | + } | ||
11623 | + if (attr->enable) | ||
11624 | + attr->enable(prev, 1); | ||
11625 | + } else | ||
11626 | + if (attr->enable) | ||
11627 | + attr->enable(prev, 0); | ||
11628 | + | ||
11629 | + prev->update &= ~attr->feature_bit; | ||
11630 | + } | ||
11631 | +} | ||
11632 | + | ||
11633 | +/* | ||
11634 | + * preview_config_ycpos - Configure byte layout of YUV image. | ||
11635 | + * @mode: Indicates the required byte layout. | ||
11636 | + */ | ||
11637 | +static void | ||
11638 | +preview_config_ycpos(struct isp_prev_device *prev, | ||
11639 | + enum v4l2_mbus_pixelcode pixelcode) | ||
11640 | +{ | ||
11641 | + struct isp_device *isp = to_isp_device(prev); | ||
11642 | + enum preview_ycpos_mode mode; | ||
11643 | + | ||
11644 | + switch (pixelcode) { | ||
11645 | + case V4L2_MBUS_FMT_YUYV8_1X16: | ||
11646 | + mode = YCPOS_CrYCbY; | ||
11647 | + break; | ||
11648 | + case V4L2_MBUS_FMT_UYVY8_1X16: | ||
11649 | + mode = YCPOS_YCrYCb; | ||
11650 | + break; | ||
11651 | + default: | ||
11652 | + return; | ||
11653 | + } | ||
11654 | + | ||
11655 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
11656 | + ISPPRV_PCR_YCPOS_CrYCbY, | ||
11657 | + mode << ISPPRV_PCR_YCPOS_SHIFT); | ||
11658 | +} | ||
11659 | + | ||
11660 | +/* | ||
11661 | + * preview_config_averager - Enable / disable / configure averager | ||
11662 | + * @average: Average value to be configured. | ||
11663 | + */ | ||
11664 | +static void preview_config_averager(struct isp_prev_device *prev, u8 average) | ||
11665 | +{ | ||
11666 | + struct isp_device *isp = to_isp_device(prev); | ||
11667 | + int reg = 0; | ||
11668 | + | ||
11669 | + if (prev->params.cfa.format == OMAP3ISP_CFAFMT_BAYER) | ||
11670 | + reg = ISPPRV_AVE_EVENDIST_2 << ISPPRV_AVE_EVENDIST_SHIFT | | ||
11671 | + ISPPRV_AVE_ODDDIST_2 << ISPPRV_AVE_ODDDIST_SHIFT | | ||
11672 | + average; | ||
11673 | + else if (prev->params.cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON) | ||
11674 | + reg = ISPPRV_AVE_EVENDIST_3 << ISPPRV_AVE_EVENDIST_SHIFT | | ||
11675 | + ISPPRV_AVE_ODDDIST_3 << ISPPRV_AVE_ODDDIST_SHIFT | | ||
11676 | + average; | ||
11677 | + isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE); | ||
11678 | +} | ||
11679 | + | ||
11680 | +/* | ||
11681 | + * preview_config_input_size - Configure the input frame size | ||
11682 | + * | ||
11683 | + * The preview engine crops several rows and columns internally depending on | ||
11684 | + * which processing blocks are enabled. The driver assumes all those blocks are | ||
11685 | + * enabled when reporting source pad formats to userspace. If this assumption is | ||
11686 | + * not true, rows and columns must be manually cropped at the preview engine | ||
11687 | + * input to avoid overflows at the end of lines and frames. | ||
11688 | + */ | ||
11689 | +static void preview_config_input_size(struct isp_prev_device *prev) | ||
11690 | +{ | ||
11691 | + struct isp_device *isp = to_isp_device(prev); | ||
11692 | + struct prev_params *params = &prev->params; | ||
11693 | + struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK]; | ||
11694 | + unsigned int sph = 0; | ||
11695 | + unsigned int eph = format->width - 1; | ||
11696 | + unsigned int slv = 0; | ||
11697 | + unsigned int elv = format->height - 1; | ||
11698 | + | ||
11699 | + if (prev->input == PREVIEW_INPUT_CCDC) { | ||
11700 | + sph += 2; | ||
11701 | + eph -= 2; | ||
11702 | + } | ||
11703 | + | ||
11704 | + /* | ||
11705 | + * Median filter 4 pixels | ||
11706 | + * Noise filter 4 pixels, 4 lines | ||
11707 | + * or faulty pixels correction | ||
11708 | + * CFA filter 4 pixels, 4 lines in Bayer mode | ||
11709 | + * 2 lines in other modes | ||
11710 | + * Color suppression 2 pixels | ||
11711 | + * or luma enhancement | ||
11712 | + * ------------------------------------------------------------- | ||
11713 | + * Maximum total 14 pixels, 8 lines | ||
11714 | + */ | ||
11715 | + | ||
11716 | + if (!(params->features & PREV_CFA)) { | ||
11717 | + sph += 2; | ||
11718 | + eph -= 2; | ||
11719 | + slv += 2; | ||
11720 | + elv -= 2; | ||
11721 | + } | ||
11722 | + if (!(params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER))) { | ||
11723 | + sph += 2; | ||
11724 | + eph -= 2; | ||
11725 | + slv += 2; | ||
11726 | + elv -= 2; | ||
11727 | + } | ||
11728 | + if (!(params->features & PREV_HORZ_MEDIAN_FILTER)) { | ||
11729 | + sph += 2; | ||
11730 | + eph -= 2; | ||
11731 | + } | ||
11732 | + if (!(params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))) | ||
11733 | + sph += 2; | ||
11734 | + | ||
11735 | + isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph, | ||
11736 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO); | ||
11737 | + isp_reg_writel(isp, (slv << ISPPRV_VERT_INFO_SLV_SHIFT) | elv, | ||
11738 | + OMAP3_ISP_IOMEM_PREV, ISPPRV_VERT_INFO); | ||
11739 | +} | ||
11740 | + | ||
11741 | +/* | ||
11742 | + * preview_config_inlineoffset - Configures the Read address line offset. | ||
11743 | + * @prev: Preview module | ||
11744 | + * @offset: Line offset | ||
11745 | + * | ||
11746 | + * According to the TRM, the line offset must be aligned on a 32 bytes boundary. | ||
11747 | + * However, a hardware bug requires the memory start address to be aligned on a | ||
11748 | + * 64 bytes boundary, so the offset probably should be aligned on 64 bytes as | ||
11749 | + * well. | ||
11750 | + */ | ||
11751 | +static void | ||
11752 | +preview_config_inlineoffset(struct isp_prev_device *prev, u32 offset) | ||
11753 | +{ | ||
11754 | + struct isp_device *isp = to_isp_device(prev); | ||
11755 | + | ||
11756 | + isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV, | ||
11757 | + ISPPRV_RADR_OFFSET); | ||
11758 | +} | ||
11759 | + | ||
11760 | +/* | ||
11761 | + * preview_set_inaddr - Sets memory address of input frame. | ||
11762 | + * @addr: 32bit memory address aligned on 32byte boundary. | ||
11763 | + * | ||
11764 | + * Configures the memory address from which the input frame is to be read. | ||
11765 | + */ | ||
11766 | +static void preview_set_inaddr(struct isp_prev_device *prev, u32 addr) | ||
11767 | +{ | ||
11768 | + struct isp_device *isp = to_isp_device(prev); | ||
11769 | + | ||
11770 | + isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_RSDR_ADDR); | ||
11771 | +} | ||
11772 | + | ||
11773 | +/* | ||
11774 | + * preview_config_outlineoffset - Configures the Write address line offset. | ||
11775 | + * @offset: Line Offset for the preview output. | ||
11776 | + * | ||
11777 | + * The offset must be a multiple of 32 bytes. | ||
11778 | + */ | ||
11779 | +static void preview_config_outlineoffset(struct isp_prev_device *prev, | ||
11780 | + u32 offset) | ||
11781 | +{ | ||
11782 | + struct isp_device *isp = to_isp_device(prev); | ||
11783 | + | ||
11784 | + isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV, | ||
11785 | + ISPPRV_WADD_OFFSET); | ||
11786 | +} | ||
11787 | + | ||
11788 | +/* | ||
11789 | + * preview_set_outaddr - Sets the memory address to store output frame | ||
11790 | + * @addr: 32bit memory address aligned on 32byte boundary. | ||
11791 | + * | ||
11792 | + * Configures the memory address to which the output frame is written. | ||
11793 | + */ | ||
11794 | +static void preview_set_outaddr(struct isp_prev_device *prev, u32 addr) | ||
11795 | +{ | ||
11796 | + struct isp_device *isp = to_isp_device(prev); | ||
11797 | + | ||
11798 | + isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_WSDR_ADDR); | ||
11799 | +} | ||
11800 | + | ||
11801 | +static void preview_adjust_bandwidth(struct isp_prev_device *prev) | ||
11802 | +{ | ||
11803 | + struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity); | ||
11804 | + struct isp_device *isp = to_isp_device(prev); | ||
11805 | + const struct v4l2_mbus_framefmt *ifmt = &prev->formats[PREV_PAD_SINK]; | ||
11806 | + unsigned long l3_ick = pipe->l3_ick; | ||
11807 | + struct v4l2_fract *timeperframe; | ||
11808 | + unsigned int cycles_per_frame; | ||
11809 | + unsigned int requests_per_frame; | ||
11810 | + unsigned int cycles_per_request; | ||
11811 | + unsigned int minimum; | ||
11812 | + unsigned int maximum; | ||
11813 | + unsigned int value; | ||
11814 | + | ||
11815 | + if (prev->input != PREVIEW_INPUT_MEMORY) { | ||
11816 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, | ||
11817 | + ISPSBL_SDR_REQ_PRV_EXP_MASK); | ||
11818 | + return; | ||
11819 | + } | ||
11820 | + | ||
11821 | + /* Compute the minimum number of cycles per request, based on the | ||
11822 | + * pipeline maximum data rate. This is an absolute lower bound if we | ||
11823 | + * don't want SBL overflows, so round the value up. | ||
11824 | + */ | ||
11825 | + cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1, | ||
11826 | + pipe->max_rate); | ||
11827 | + minimum = DIV_ROUND_UP(cycles_per_request, 32); | ||
11828 | + | ||
11829 | + /* Compute the maximum number of cycles per request, based on the | ||
11830 | + * requested frame rate. This is a soft upper bound to achieve a frame | ||
11831 | + * rate equal or higher than the requested value, so round the value | ||
11832 | + * down. | ||
11833 | + */ | ||
11834 | + timeperframe = &pipe->max_timeperframe; | ||
11835 | + | ||
11836 | + requests_per_frame = DIV_ROUND_UP(ifmt->width * 2, 256) * ifmt->height; | ||
11837 | + cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator, | ||
11838 | + timeperframe->denominator); | ||
11839 | + cycles_per_request = cycles_per_frame / requests_per_frame; | ||
11840 | + | ||
11841 | + maximum = cycles_per_request / 32; | ||
11842 | + | ||
11843 | + value = max(minimum, maximum); | ||
11844 | + | ||
11845 | + dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value); | ||
11846 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, | ||
11847 | + ISPSBL_SDR_REQ_PRV_EXP_MASK, | ||
11848 | + value << ISPSBL_SDR_REQ_PRV_EXP_SHIFT); | ||
11849 | +} | ||
11850 | + | ||
11851 | +/* | ||
11852 | + * omap3isp_preview_busy - Gets busy state of preview module. | ||
11853 | + */ | ||
11854 | +int omap3isp_preview_busy(struct isp_prev_device *prev) | ||
11855 | +{ | ||
11856 | + struct isp_device *isp = to_isp_device(prev); | ||
11857 | + | ||
11858 | + return isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR) | ||
11859 | + & ISPPRV_PCR_BUSY; | ||
11860 | +} | ||
11861 | + | ||
11862 | +/* | ||
11863 | + * omap3isp_preview_restore_context - Restores the values of preview registers | ||
11864 | + */ | ||
11865 | +void omap3isp_preview_restore_context(struct isp_device *isp) | ||
11866 | +{ | ||
11867 | + isp->isp_prev.update = PREV_FEATURES_END - 1; | ||
11868 | + preview_setup_hw(&isp->isp_prev); | ||
11869 | +} | ||
11870 | + | ||
11871 | +/* | ||
11872 | + * preview_print_status - Dump preview module registers to the kernel log | ||
11873 | + */ | ||
11874 | +#define PREV_PRINT_REGISTER(isp, name)\ | ||
11875 | + dev_dbg(isp->dev, "###PRV " #name "=0x%08x\n", \ | ||
11876 | + isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_##name)) | ||
11877 | + | ||
11878 | +static void preview_print_status(struct isp_prev_device *prev) | ||
11879 | +{ | ||
11880 | + struct isp_device *isp = to_isp_device(prev); | ||
11881 | + | ||
11882 | + dev_dbg(isp->dev, "-------------Preview Register dump----------\n"); | ||
11883 | + | ||
11884 | + PREV_PRINT_REGISTER(isp, PCR); | ||
11885 | + PREV_PRINT_REGISTER(isp, HORZ_INFO); | ||
11886 | + PREV_PRINT_REGISTER(isp, VERT_INFO); | ||
11887 | + PREV_PRINT_REGISTER(isp, RSDR_ADDR); | ||
11888 | + PREV_PRINT_REGISTER(isp, RADR_OFFSET); | ||
11889 | + PREV_PRINT_REGISTER(isp, DSDR_ADDR); | ||
11890 | + PREV_PRINT_REGISTER(isp, DRKF_OFFSET); | ||
11891 | + PREV_PRINT_REGISTER(isp, WSDR_ADDR); | ||
11892 | + PREV_PRINT_REGISTER(isp, WADD_OFFSET); | ||
11893 | + PREV_PRINT_REGISTER(isp, AVE); | ||
11894 | + PREV_PRINT_REGISTER(isp, HMED); | ||
11895 | + PREV_PRINT_REGISTER(isp, NF); | ||
11896 | + PREV_PRINT_REGISTER(isp, WB_DGAIN); | ||
11897 | + PREV_PRINT_REGISTER(isp, WBGAIN); | ||
11898 | + PREV_PRINT_REGISTER(isp, WBSEL); | ||
11899 | + PREV_PRINT_REGISTER(isp, CFA); | ||
11900 | + PREV_PRINT_REGISTER(isp, BLKADJOFF); | ||
11901 | + PREV_PRINT_REGISTER(isp, RGB_MAT1); | ||
11902 | + PREV_PRINT_REGISTER(isp, RGB_MAT2); | ||
11903 | + PREV_PRINT_REGISTER(isp, RGB_MAT3); | ||
11904 | + PREV_PRINT_REGISTER(isp, RGB_MAT4); | ||
11905 | + PREV_PRINT_REGISTER(isp, RGB_MAT5); | ||
11906 | + PREV_PRINT_REGISTER(isp, RGB_OFF1); | ||
11907 | + PREV_PRINT_REGISTER(isp, RGB_OFF2); | ||
11908 | + PREV_PRINT_REGISTER(isp, CSC0); | ||
11909 | + PREV_PRINT_REGISTER(isp, CSC1); | ||
11910 | + PREV_PRINT_REGISTER(isp, CSC2); | ||
11911 | + PREV_PRINT_REGISTER(isp, CSC_OFFSET); | ||
11912 | + PREV_PRINT_REGISTER(isp, CNT_BRT); | ||
11913 | + PREV_PRINT_REGISTER(isp, CSUP); | ||
11914 | + PREV_PRINT_REGISTER(isp, SETUP_YC); | ||
11915 | + PREV_PRINT_REGISTER(isp, SET_TBL_ADDR); | ||
11916 | + PREV_PRINT_REGISTER(isp, CDC_THR0); | ||
11917 | + PREV_PRINT_REGISTER(isp, CDC_THR1); | ||
11918 | + PREV_PRINT_REGISTER(isp, CDC_THR2); | ||
11919 | + PREV_PRINT_REGISTER(isp, CDC_THR3); | ||
11920 | + | ||
11921 | + dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
11922 | +} | ||
11923 | + | ||
11924 | +/* | ||
11925 | + * preview_init_params - init image processing parameters. | ||
11926 | + * @prev: pointer to previewer private structure | ||
11927 | + * return none | ||
11928 | + */ | ||
11929 | +static void preview_init_params(struct isp_prev_device *prev) | ||
11930 | +{ | ||
11931 | + struct prev_params *params = &prev->params; | ||
11932 | + int i = 0; | ||
11933 | + | ||
11934 | + /* Init values */ | ||
11935 | + params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS; | ||
11936 | + params->brightness = ISPPRV_BRIGHT_DEF * ISPPRV_BRIGHT_UNITS; | ||
11937 | + params->average = NO_AVE; | ||
11938 | + params->cfa.format = OMAP3ISP_CFAFMT_BAYER; | ||
11939 | + memcpy(params->cfa.table, cfa_coef_table, | ||
11940 | + sizeof(params->cfa.table)); | ||
11941 | + params->cfa.gradthrs_horz = FLR_CFA_GRADTHRS_HORZ; | ||
11942 | + params->cfa.gradthrs_vert = FLR_CFA_GRADTHRS_VERT; | ||
11943 | + params->csup.gain = FLR_CSUP_GAIN; | ||
11944 | + params->csup.thres = FLR_CSUP_THRES; | ||
11945 | + params->csup.hypf_en = 0; | ||
11946 | + memcpy(params->luma.table, luma_enhance_table, | ||
11947 | + sizeof(params->luma.table)); | ||
11948 | + params->nf.spread = FLR_NF_STRGTH; | ||
11949 | + memcpy(params->nf.table, noise_filter_table, sizeof(params->nf.table)); | ||
11950 | + params->dcor.couplet_mode_en = 1; | ||
11951 | + for (i = 0; i < OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS; i++) | ||
11952 | + params->dcor.detect_correct[i] = DEF_DETECT_CORRECT_VAL; | ||
11953 | + memcpy(params->gamma.blue, gamma_table, sizeof(params->gamma.blue)); | ||
11954 | + memcpy(params->gamma.green, gamma_table, sizeof(params->gamma.green)); | ||
11955 | + memcpy(params->gamma.red, gamma_table, sizeof(params->gamma.red)); | ||
11956 | + params->wbal.dgain = FLR_WBAL_DGAIN; | ||
11957 | + params->wbal.coef0 = FLR_WBAL_COEF; | ||
11958 | + params->wbal.coef1 = FLR_WBAL_COEF; | ||
11959 | + params->wbal.coef2 = FLR_WBAL_COEF; | ||
11960 | + params->wbal.coef3 = FLR_WBAL_COEF; | ||
11961 | + params->blk_adj.red = FLR_BLKADJ_RED; | ||
11962 | + params->blk_adj.green = FLR_BLKADJ_GREEN; | ||
11963 | + params->blk_adj.blue = FLR_BLKADJ_BLUE; | ||
11964 | + params->rgb2rgb = flr_rgb2rgb; | ||
11965 | + params->rgb2ycbcr = flr_prev_csc; | ||
11966 | + params->yclimit.minC = ISPPRV_YC_MIN; | ||
11967 | + params->yclimit.maxC = ISPPRV_YC_MAX; | ||
11968 | + params->yclimit.minY = ISPPRV_YC_MIN; | ||
11969 | + params->yclimit.maxY = ISPPRV_YC_MAX; | ||
11970 | + | ||
11971 | + params->features = PREV_CFA | PREV_DEFECT_COR | PREV_NOISE_FILTER | ||
11972 | + | PREV_GAMMA | PREV_BLKADJ | PREV_YCLIMITS | ||
11973 | + | PREV_RGB2RGB | PREV_COLOR_CONV | PREV_WB | ||
11974 | + | PREV_BRIGHTNESS | PREV_CONTRAST; | ||
11975 | + | ||
11976 | + prev->update = PREV_FEATURES_END - 1; | ||
11977 | +} | ||
11978 | + | ||
11979 | +/* | ||
11980 | + * preview_max_out_width - Handle previewer hardware ouput limitations | ||
11981 | + * @isp_revision : ISP revision | ||
11982 | + * returns maximum width output for current isp revision | ||
11983 | + */ | ||
11984 | +static unsigned int preview_max_out_width(struct isp_prev_device *prev) | ||
11985 | +{ | ||
11986 | + struct isp_device *isp = to_isp_device(prev); | ||
11987 | + | ||
11988 | + switch (isp->revision) { | ||
11989 | + case ISP_REVISION_1_0: | ||
11990 | + return ISPPRV_MAXOUTPUT_WIDTH; | ||
11991 | + | ||
11992 | + case ISP_REVISION_2_0: | ||
11993 | + default: | ||
11994 | + return ISPPRV_MAXOUTPUT_WIDTH_ES2; | ||
11995 | + | ||
11996 | + case ISP_REVISION_15_0: | ||
11997 | + return ISPPRV_MAXOUTPUT_WIDTH_3630; | ||
11998 | + } | ||
11999 | +} | ||
12000 | + | ||
12001 | +static void preview_configure(struct isp_prev_device *prev) | ||
12002 | +{ | ||
12003 | + struct isp_device *isp = to_isp_device(prev); | ||
12004 | + struct v4l2_mbus_framefmt *format; | ||
12005 | + unsigned int max_out_width; | ||
12006 | + unsigned int format_avg; | ||
12007 | + | ||
12008 | + preview_setup_hw(prev); | ||
12009 | + | ||
12010 | + if (prev->output & PREVIEW_OUTPUT_MEMORY) | ||
12011 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
12012 | + ISPPRV_PCR_SDRPORT); | ||
12013 | + else | ||
12014 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
12015 | + ISPPRV_PCR_SDRPORT); | ||
12016 | + | ||
12017 | + if (prev->output & PREVIEW_OUTPUT_RESIZER) | ||
12018 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
12019 | + ISPPRV_PCR_RSZPORT); | ||
12020 | + else | ||
12021 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
12022 | + ISPPRV_PCR_RSZPORT); | ||
12023 | + | ||
12024 | + /* PREV_PAD_SINK */ | ||
12025 | + format = &prev->formats[PREV_PAD_SINK]; | ||
12026 | + | ||
12027 | + preview_adjust_bandwidth(prev); | ||
12028 | + | ||
12029 | + preview_config_input_size(prev); | ||
12030 | + | ||
12031 | + if (prev->input == PREVIEW_INPUT_CCDC) | ||
12032 | + preview_config_inlineoffset(prev, 0); | ||
12033 | + else | ||
12034 | + preview_config_inlineoffset(prev, | ||
12035 | + ALIGN(format->width, 0x20) * 2); | ||
12036 | + | ||
12037 | + /* PREV_PAD_SOURCE */ | ||
12038 | + format = &prev->formats[PREV_PAD_SOURCE]; | ||
12039 | + | ||
12040 | + if (prev->output & PREVIEW_OUTPUT_MEMORY) | ||
12041 | + preview_config_outlineoffset(prev, | ||
12042 | + ALIGN(format->width, 0x10) * 2); | ||
12043 | + | ||
12044 | + max_out_width = preview_max_out_width(prev); | ||
12045 | + | ||
12046 | + format_avg = fls(DIV_ROUND_UP(format->width, max_out_width) - 1); | ||
12047 | + preview_config_averager(prev, format_avg); | ||
12048 | + preview_config_ycpos(prev, format->code); | ||
12049 | +} | ||
12050 | + | ||
12051 | +/* ----------------------------------------------------------------------------- | ||
12052 | + * Interrupt handling | ||
12053 | + */ | ||
12054 | + | ||
12055 | +static void preview_enable_oneshot(struct isp_prev_device *prev) | ||
12056 | +{ | ||
12057 | + struct isp_device *isp = to_isp_device(prev); | ||
12058 | + | ||
12059 | + /* The PCR.SOURCE bit is automatically reset to 0 when the PCR.ENABLE | ||
12060 | + * bit is set. As the preview engine is used in single-shot mode, we | ||
12061 | + * need to set PCR.SOURCE before enabling the preview engine. | ||
12062 | + */ | ||
12063 | + if (prev->input == PREVIEW_INPUT_MEMORY) | ||
12064 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
12065 | + ISPPRV_PCR_SOURCE); | ||
12066 | + | ||
12067 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
12068 | + ISPPRV_PCR_EN | ISPPRV_PCR_ONESHOT); | ||
12069 | +} | ||
12070 | + | ||
12071 | +void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev) | ||
12072 | +{ | ||
12073 | + /* | ||
12074 | + * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun | ||
12075 | + * condition, the module was paused and now we have a buffer queued | ||
12076 | + * on the output again. Restart the pipeline if running in continuous | ||
12077 | + * mode. | ||
12078 | + */ | ||
12079 | + if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS && | ||
12080 | + prev->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { | ||
12081 | + preview_enable_oneshot(prev); | ||
12082 | + isp_video_dmaqueue_flags_clr(&prev->video_out); | ||
12083 | + } | ||
12084 | +} | ||
12085 | + | ||
12086 | +static void preview_isr_buffer(struct isp_prev_device *prev) | ||
12087 | +{ | ||
12088 | + struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity); | ||
12089 | + struct isp_buffer *buffer; | ||
12090 | + int restart = 0; | ||
12091 | + | ||
12092 | + if (prev->input == PREVIEW_INPUT_MEMORY) { | ||
12093 | + buffer = omap3isp_video_buffer_next(&prev->video_in, | ||
12094 | + prev->error); | ||
12095 | + if (buffer != NULL) | ||
12096 | + preview_set_inaddr(prev, buffer->isp_addr); | ||
12097 | + pipe->state |= ISP_PIPELINE_IDLE_INPUT; | ||
12098 | + } | ||
12099 | + | ||
12100 | + if (prev->output & PREVIEW_OUTPUT_MEMORY) { | ||
12101 | + buffer = omap3isp_video_buffer_next(&prev->video_out, | ||
12102 | + prev->error); | ||
12103 | + if (buffer != NULL) { | ||
12104 | + preview_set_outaddr(prev, buffer->isp_addr); | ||
12105 | + restart = 1; | ||
12106 | + } | ||
12107 | + pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; | ||
12108 | + } | ||
12109 | + | ||
12110 | + switch (prev->state) { | ||
12111 | + case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
12112 | + if (isp_pipeline_ready(pipe)) | ||
12113 | + omap3isp_pipeline_set_stream(pipe, | ||
12114 | + ISP_PIPELINE_STREAM_SINGLESHOT); | ||
12115 | + break; | ||
12116 | + | ||
12117 | + case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
12118 | + /* If an underrun occurs, the video queue operation handler will | ||
12119 | + * restart the preview engine. Otherwise restart it immediately. | ||
12120 | + */ | ||
12121 | + if (restart) | ||
12122 | + preview_enable_oneshot(prev); | ||
12123 | + break; | ||
12124 | + | ||
12125 | + case ISP_PIPELINE_STREAM_STOPPED: | ||
12126 | + default: | ||
12127 | + return; | ||
12128 | + } | ||
12129 | + | ||
12130 | + prev->error = 0; | ||
12131 | +} | ||
12132 | + | ||
12133 | +/* | ||
12134 | + * omap3isp_preview_isr - ISP preview engine interrupt handler | ||
12135 | + * | ||
12136 | + * Manage the preview engine video buffers and configure shadowed registers. | ||
12137 | + */ | ||
12138 | +void omap3isp_preview_isr(struct isp_prev_device *prev) | ||
12139 | +{ | ||
12140 | + unsigned long flags; | ||
12141 | + | ||
12142 | + if (omap3isp_module_sync_is_stopping(&prev->wait, &prev->stopping)) | ||
12143 | + return; | ||
12144 | + | ||
12145 | + spin_lock_irqsave(&prev->lock, flags); | ||
12146 | + if (prev->shadow_update) | ||
12147 | + goto done; | ||
12148 | + | ||
12149 | + preview_setup_hw(prev); | ||
12150 | + preview_config_input_size(prev); | ||
12151 | + | ||
12152 | +done: | ||
12153 | + spin_unlock_irqrestore(&prev->lock, flags); | ||
12154 | + | ||
12155 | + if (prev->input == PREVIEW_INPUT_MEMORY || | ||
12156 | + prev->output & PREVIEW_OUTPUT_MEMORY) | ||
12157 | + preview_isr_buffer(prev); | ||
12158 | + else if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS) | ||
12159 | + preview_enable_oneshot(prev); | ||
12160 | +} | ||
12161 | + | ||
12162 | +/* ----------------------------------------------------------------------------- | ||
12163 | + * ISP video operations | ||
12164 | + */ | ||
12165 | + | ||
12166 | +static int preview_video_queue(struct isp_video *video, | ||
12167 | + struct isp_buffer *buffer) | ||
12168 | +{ | ||
12169 | + struct isp_prev_device *prev = &video->isp->isp_prev; | ||
12170 | + | ||
12171 | + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
12172 | + preview_set_inaddr(prev, buffer->isp_addr); | ||
12173 | + | ||
12174 | + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
12175 | + preview_set_outaddr(prev, buffer->isp_addr); | ||
12176 | + | ||
12177 | + return 0; | ||
12178 | +} | ||
12179 | + | ||
12180 | +static const struct isp_video_operations preview_video_ops = { | ||
12181 | + .queue = preview_video_queue, | ||
12182 | +}; | ||
12183 | + | ||
12184 | +/* ----------------------------------------------------------------------------- | ||
12185 | + * V4L2 subdev operations | ||
12186 | + */ | ||
12187 | + | ||
12188 | +/* | ||
12189 | + * preview_s_ctrl - Handle set control subdev method | ||
12190 | + * @ctrl: pointer to v4l2 control structure | ||
12191 | + */ | ||
12192 | +static int preview_s_ctrl(struct v4l2_ctrl *ctrl) | ||
12193 | +{ | ||
12194 | + struct isp_prev_device *prev = | ||
12195 | + container_of(ctrl->handler, struct isp_prev_device, ctrls); | ||
12196 | + | ||
12197 | + switch (ctrl->id) { | ||
12198 | + case V4L2_CID_BRIGHTNESS: | ||
12199 | + preview_update_brightness(prev, ctrl->val); | ||
12200 | + break; | ||
12201 | + case V4L2_CID_CONTRAST: | ||
12202 | + preview_update_contrast(prev, ctrl->val); | ||
12203 | + break; | ||
12204 | + } | ||
12205 | + | ||
12206 | + return 0; | ||
12207 | +} | ||
12208 | + | ||
12209 | +static const struct v4l2_ctrl_ops preview_ctrl_ops = { | ||
12210 | + .s_ctrl = preview_s_ctrl, | ||
12211 | +}; | ||
12212 | + | ||
12213 | +/* | ||
12214 | + * preview_ioctl - Handle preview module private ioctl's | ||
12215 | + * @prev: pointer to preview context structure | ||
12216 | + * @cmd: configuration command | ||
12217 | + * @arg: configuration argument | ||
12218 | + * return -EINVAL or zero on success | ||
12219 | + */ | ||
12220 | +static long preview_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
12221 | +{ | ||
12222 | + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); | ||
12223 | + | ||
12224 | + switch (cmd) { | ||
12225 | + case VIDIOC_OMAP3ISP_PRV_CFG: | ||
12226 | + return preview_config(prev, arg); | ||
12227 | + | ||
12228 | + default: | ||
12229 | + return -ENOIOCTLCMD; | ||
12230 | + } | ||
12231 | +} | ||
12232 | + | ||
12233 | +/* | ||
12234 | + * preview_set_stream - Enable/Disable streaming on preview subdev | ||
12235 | + * @sd : pointer to v4l2 subdev structure | ||
12236 | + * @enable: 1 == Enable, 0 == Disable | ||
12237 | + * return -EINVAL or zero on sucess | ||
12238 | + */ | ||
12239 | +static int preview_set_stream(struct v4l2_subdev *sd, int enable) | ||
12240 | +{ | ||
12241 | + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); | ||
12242 | + struct isp_video *video_out = &prev->video_out; | ||
12243 | + struct isp_device *isp = to_isp_device(prev); | ||
12244 | + struct device *dev = to_device(prev); | ||
12245 | + unsigned long flags; | ||
12246 | + | ||
12247 | + if (prev->state == ISP_PIPELINE_STREAM_STOPPED) { | ||
12248 | + if (enable == ISP_PIPELINE_STREAM_STOPPED) | ||
12249 | + return 0; | ||
12250 | + | ||
12251 | + omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_PREVIEW); | ||
12252 | + preview_configure(prev); | ||
12253 | + atomic_set(&prev->stopping, 0); | ||
12254 | + prev->error = 0; | ||
12255 | + preview_print_status(prev); | ||
12256 | + } | ||
12257 | + | ||
12258 | + switch (enable) { | ||
12259 | + case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
12260 | + if (prev->output & PREVIEW_OUTPUT_MEMORY) | ||
12261 | + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); | ||
12262 | + | ||
12263 | + if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED || | ||
12264 | + !(prev->output & PREVIEW_OUTPUT_MEMORY)) | ||
12265 | + preview_enable_oneshot(prev); | ||
12266 | + | ||
12267 | + isp_video_dmaqueue_flags_clr(video_out); | ||
12268 | + break; | ||
12269 | + | ||
12270 | + case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
12271 | + if (prev->input == PREVIEW_INPUT_MEMORY) | ||
12272 | + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_READ); | ||
12273 | + if (prev->output & PREVIEW_OUTPUT_MEMORY) | ||
12274 | + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); | ||
12275 | + | ||
12276 | + preview_enable_oneshot(prev); | ||
12277 | + break; | ||
12278 | + | ||
12279 | + case ISP_PIPELINE_STREAM_STOPPED: | ||
12280 | + if (omap3isp_module_sync_idle(&sd->entity, &prev->wait, | ||
12281 | + &prev->stopping)) | ||
12282 | + dev_dbg(dev, "%s: stop timeout.\n", sd->name); | ||
12283 | + spin_lock_irqsave(&prev->lock, flags); | ||
12284 | + omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_READ); | ||
12285 | + omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); | ||
12286 | + omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_PREVIEW); | ||
12287 | + spin_unlock_irqrestore(&prev->lock, flags); | ||
12288 | + isp_video_dmaqueue_flags_clr(video_out); | ||
12289 | + break; | ||
12290 | + } | ||
12291 | + | ||
12292 | + prev->state = enable; | ||
12293 | + return 0; | ||
12294 | +} | ||
12295 | + | ||
12296 | +static struct v4l2_mbus_framefmt * | ||
12297 | +__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, | ||
12298 | + unsigned int pad, enum v4l2_subdev_format_whence which) | ||
12299 | +{ | ||
12300 | + if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
12301 | + return v4l2_subdev_get_try_format(fh, pad); | ||
12302 | + else | ||
12303 | + return &prev->formats[pad]; | ||
12304 | +} | ||
12305 | + | ||
12306 | +/* previewer format descriptions */ | ||
12307 | +static const unsigned int preview_input_fmts[] = { | ||
12308 | + V4L2_MBUS_FMT_SGRBG10_1X10, | ||
12309 | + V4L2_MBUS_FMT_SRGGB10_1X10, | ||
12310 | + V4L2_MBUS_FMT_SBGGR10_1X10, | ||
12311 | + V4L2_MBUS_FMT_SGBRG10_1X10, | ||
12312 | +}; | ||
12313 | + | ||
12314 | +static const unsigned int preview_output_fmts[] = { | ||
12315 | + V4L2_MBUS_FMT_UYVY8_1X16, | ||
12316 | + V4L2_MBUS_FMT_YUYV8_1X16, | ||
12317 | +}; | ||
12318 | + | ||
12319 | +/* | ||
12320 | + * preview_try_format - Handle try format by pad subdev method | ||
12321 | + * @prev: ISP preview device | ||
12322 | + * @fh : V4L2 subdev file handle | ||
12323 | + * @pad: pad num | ||
12324 | + * @fmt: pointer to v4l2 format structure | ||
12325 | + */ | ||
12326 | +static void preview_try_format(struct isp_prev_device *prev, | ||
12327 | + struct v4l2_subdev_fh *fh, unsigned int pad, | ||
12328 | + struct v4l2_mbus_framefmt *fmt, | ||
12329 | + enum v4l2_subdev_format_whence which) | ||
12330 | +{ | ||
12331 | + struct v4l2_mbus_framefmt *format; | ||
12332 | + unsigned int max_out_width; | ||
12333 | + enum v4l2_mbus_pixelcode pixelcode; | ||
12334 | + unsigned int i; | ||
12335 | + | ||
12336 | + max_out_width = preview_max_out_width(prev); | ||
12337 | + | ||
12338 | + switch (pad) { | ||
12339 | + case PREV_PAD_SINK: | ||
12340 | + /* When reading data from the CCDC, the input size has already | ||
12341 | + * been mangled by the CCDC output pad so it can be accepted | ||
12342 | + * as-is. | ||
12343 | + * | ||
12344 | + * When reading data from memory, clamp the requested width and | ||
12345 | + * height. The TRM doesn't specify a minimum input height, make | ||
12346 | + * sure we got enough lines to enable the noise filter and color | ||
12347 | + * filter array interpolation. | ||
12348 | + */ | ||
12349 | + if (prev->input == PREVIEW_INPUT_MEMORY) { | ||
12350 | + fmt->width = clamp_t(u32, fmt->width, PREV_MIN_WIDTH, | ||
12351 | + max_out_width * 8); | ||
12352 | + fmt->height = clamp_t(u32, fmt->height, PREV_MIN_HEIGHT, | ||
12353 | + PREV_MAX_HEIGHT); | ||
12354 | + } | ||
12355 | + | ||
12356 | + fmt->colorspace = V4L2_COLORSPACE_SRGB; | ||
12357 | + | ||
12358 | + for (i = 0; i < ARRAY_SIZE(preview_input_fmts); i++) { | ||
12359 | + if (fmt->code == preview_input_fmts[i]) | ||
12360 | + break; | ||
12361 | + } | ||
12362 | + | ||
12363 | + /* If not found, use SGRBG10 as default */ | ||
12364 | + if (i >= ARRAY_SIZE(preview_input_fmts)) | ||
12365 | + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
12366 | + break; | ||
12367 | + | ||
12368 | + case PREV_PAD_SOURCE: | ||
12369 | + pixelcode = fmt->code; | ||
12370 | + format = __preview_get_format(prev, fh, PREV_PAD_SINK, which); | ||
12371 | + memcpy(fmt, format, sizeof(*fmt)); | ||
12372 | + | ||
12373 | + /* The preview module output size is configurable through the | ||
12374 | + * input interface (horizontal and vertical cropping) and the | ||
12375 | + * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). In | ||
12376 | + * spite of this, hardcode the output size to the biggest | ||
12377 | + * possible value for simplicity reasons. | ||
12378 | + */ | ||
12379 | + switch (pixelcode) { | ||
12380 | + case V4L2_MBUS_FMT_YUYV8_1X16: | ||
12381 | + case V4L2_MBUS_FMT_UYVY8_1X16: | ||
12382 | + fmt->code = pixelcode; | ||
12383 | + break; | ||
12384 | + | ||
12385 | + default: | ||
12386 | + fmt->code = V4L2_MBUS_FMT_YUYV8_1X16; | ||
12387 | + break; | ||
12388 | + } | ||
12389 | + | ||
12390 | + /* The TRM states (12.1.4.7.1.2) that 2 pixels must be cropped | ||
12391 | + * from the left and right sides when the input source is the | ||
12392 | + * CCDC. This seems not to be needed in practice, investigation | ||
12393 | + * is required. | ||
12394 | + */ | ||
12395 | + if (prev->input == PREVIEW_INPUT_CCDC) | ||
12396 | + fmt->width -= 4; | ||
12397 | + | ||
12398 | + /* The preview module can output a maximum of 3312 pixels | ||
12399 | + * horizontally due to fixed memory-line sizes. Compute the | ||
12400 | + * horizontal averaging factor accordingly. Note that the limit | ||
12401 | + * applies to the noise filter and CFA interpolation blocks, so | ||
12402 | + * it doesn't take cropping by further blocks into account. | ||
12403 | + * | ||
12404 | + * ES 1.0 hardware revision is limited to 1280 pixels | ||
12405 | + * horizontally. | ||
12406 | + */ | ||
12407 | + fmt->width >>= fls(DIV_ROUND_UP(fmt->width, max_out_width) - 1); | ||
12408 | + | ||
12409 | + /* Assume that all blocks are enabled and crop pixels and lines | ||
12410 | + * accordingly. See preview_config_input_size() for more | ||
12411 | + * information. | ||
12412 | + */ | ||
12413 | + fmt->width -= 14; | ||
12414 | + fmt->height -= 8; | ||
12415 | + | ||
12416 | + fmt->colorspace = V4L2_COLORSPACE_JPEG; | ||
12417 | + break; | ||
12418 | + } | ||
12419 | + | ||
12420 | + fmt->field = V4L2_FIELD_NONE; | ||
12421 | +} | ||
12422 | + | ||
12423 | +/* | ||
12424 | + * preview_enum_mbus_code - Handle pixel format enumeration | ||
12425 | + * @sd : pointer to v4l2 subdev structure | ||
12426 | + * @fh : V4L2 subdev file handle | ||
12427 | + * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
12428 | + * return -EINVAL or zero on success | ||
12429 | + */ | ||
12430 | +static int preview_enum_mbus_code(struct v4l2_subdev *sd, | ||
12431 | + struct v4l2_subdev_fh *fh, | ||
12432 | + struct v4l2_subdev_mbus_code_enum *code) | ||
12433 | +{ | ||
12434 | + switch (code->pad) { | ||
12435 | + case PREV_PAD_SINK: | ||
12436 | + if (code->index >= ARRAY_SIZE(preview_input_fmts)) | ||
12437 | + return -EINVAL; | ||
12438 | + | ||
12439 | + code->code = preview_input_fmts[code->index]; | ||
12440 | + break; | ||
12441 | + case PREV_PAD_SOURCE: | ||
12442 | + if (code->index >= ARRAY_SIZE(preview_output_fmts)) | ||
12443 | + return -EINVAL; | ||
12444 | + | ||
12445 | + code->code = preview_output_fmts[code->index]; | ||
12446 | + break; | ||
12447 | + default: | ||
12448 | + return -EINVAL; | ||
12449 | + } | ||
12450 | + | ||
12451 | + return 0; | ||
12452 | +} | ||
12453 | + | ||
12454 | +static int preview_enum_frame_size(struct v4l2_subdev *sd, | ||
12455 | + struct v4l2_subdev_fh *fh, | ||
12456 | + struct v4l2_subdev_frame_size_enum *fse) | ||
12457 | +{ | ||
12458 | + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); | ||
12459 | + struct v4l2_mbus_framefmt format; | ||
12460 | + | ||
12461 | + if (fse->index != 0) | ||
12462 | + return -EINVAL; | ||
12463 | + | ||
12464 | + format.code = fse->code; | ||
12465 | + format.width = 1; | ||
12466 | + format.height = 1; | ||
12467 | + preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
12468 | + fse->min_width = format.width; | ||
12469 | + fse->min_height = format.height; | ||
12470 | + | ||
12471 | + if (format.code != fse->code) | ||
12472 | + return -EINVAL; | ||
12473 | + | ||
12474 | + format.code = fse->code; | ||
12475 | + format.width = -1; | ||
12476 | + format.height = -1; | ||
12477 | + preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
12478 | + fse->max_width = format.width; | ||
12479 | + fse->max_height = format.height; | ||
12480 | + | ||
12481 | + return 0; | ||
12482 | +} | ||
12483 | + | ||
12484 | +/* | ||
12485 | + * preview_get_format - Handle get format by pads subdev method | ||
12486 | + * @sd : pointer to v4l2 subdev structure | ||
12487 | + * @fh : V4L2 subdev file handle | ||
12488 | + * @fmt: pointer to v4l2 subdev format structure | ||
12489 | + * return -EINVAL or zero on sucess | ||
12490 | + */ | ||
12491 | +static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
12492 | + struct v4l2_subdev_format *fmt) | ||
12493 | +{ | ||
12494 | + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); | ||
12495 | + struct v4l2_mbus_framefmt *format; | ||
12496 | + | ||
12497 | + format = __preview_get_format(prev, fh, fmt->pad, fmt->which); | ||
12498 | + if (format == NULL) | ||
12499 | + return -EINVAL; | ||
12500 | + | ||
12501 | + fmt->format = *format; | ||
12502 | + return 0; | ||
12503 | +} | ||
12504 | + | ||
12505 | +/* | ||
12506 | + * preview_set_format - Handle set format by pads subdev method | ||
12507 | + * @sd : pointer to v4l2 subdev structure | ||
12508 | + * @fh : V4L2 subdev file handle | ||
12509 | + * @fmt: pointer to v4l2 subdev format structure | ||
12510 | + * return -EINVAL or zero on success | ||
12511 | + */ | ||
12512 | +static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
12513 | + struct v4l2_subdev_format *fmt) | ||
12514 | +{ | ||
12515 | + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); | ||
12516 | + struct v4l2_mbus_framefmt *format; | ||
12517 | + | ||
12518 | + format = __preview_get_format(prev, fh, fmt->pad, fmt->which); | ||
12519 | + if (format == NULL) | ||
12520 | + return -EINVAL; | ||
12521 | + | ||
12522 | + preview_try_format(prev, fh, fmt->pad, &fmt->format, fmt->which); | ||
12523 | + *format = fmt->format; | ||
12524 | + | ||
12525 | + /* Propagate the format from sink to source */ | ||
12526 | + if (fmt->pad == PREV_PAD_SINK) { | ||
12527 | + format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, | ||
12528 | + fmt->which); | ||
12529 | + *format = fmt->format; | ||
12530 | + preview_try_format(prev, fh, PREV_PAD_SOURCE, format, | ||
12531 | + fmt->which); | ||
12532 | + } | ||
12533 | + | ||
12534 | + return 0; | ||
12535 | +} | ||
12536 | + | ||
12537 | +/* | ||
12538 | + * preview_init_formats - Initialize formats on all pads | ||
12539 | + * @sd: ISP preview V4L2 subdevice | ||
12540 | + * @fh: V4L2 subdev file handle | ||
12541 | + * | ||
12542 | + * Initialize all pad formats with default values. If fh is not NULL, try | ||
12543 | + * formats are initialized on the file handle. Otherwise active formats are | ||
12544 | + * initialized on the device. | ||
12545 | + */ | ||
12546 | +static int preview_init_formats(struct v4l2_subdev *sd, | ||
12547 | + struct v4l2_subdev_fh *fh) | ||
12548 | +{ | ||
12549 | + struct v4l2_subdev_format format; | ||
12550 | + | ||
12551 | + memset(&format, 0, sizeof(format)); | ||
12552 | + format.pad = PREV_PAD_SINK; | ||
12553 | + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
12554 | + format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
12555 | + format.format.width = 4096; | ||
12556 | + format.format.height = 4096; | ||
12557 | + preview_set_format(sd, fh, &format); | ||
12558 | + | ||
12559 | + return 0; | ||
12560 | +} | ||
12561 | + | ||
12562 | +/* subdev core operations */ | ||
12563 | +static const struct v4l2_subdev_core_ops preview_v4l2_core_ops = { | ||
12564 | + .queryctrl = v4l2_subdev_queryctrl, | ||
12565 | + .querymenu = v4l2_subdev_querymenu, | ||
12566 | + .g_ctrl = v4l2_subdev_g_ctrl, | ||
12567 | + .s_ctrl = v4l2_subdev_s_ctrl, | ||
12568 | + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, | ||
12569 | + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, | ||
12570 | + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, | ||
12571 | + .ioctl = preview_ioctl, | ||
12572 | +}; | ||
12573 | + | ||
12574 | +/* subdev file operations */ | ||
12575 | +static const struct v4l2_subdev_file_ops preview_v4l2_file_ops = { | ||
12576 | + .open = preview_init_formats, | ||
12577 | +}; | ||
12578 | + | ||
12579 | +/* subdev video operations */ | ||
12580 | +static const struct v4l2_subdev_video_ops preview_v4l2_video_ops = { | ||
12581 | + .s_stream = preview_set_stream, | ||
12582 | +}; | ||
12583 | + | ||
12584 | +/* subdev pad operations */ | ||
12585 | +static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = { | ||
12586 | + .enum_mbus_code = preview_enum_mbus_code, | ||
12587 | + .enum_frame_size = preview_enum_frame_size, | ||
12588 | + .get_fmt = preview_get_format, | ||
12589 | + .set_fmt = preview_set_format, | ||
12590 | +}; | ||
12591 | + | ||
12592 | +/* subdev operations */ | ||
12593 | +static const struct v4l2_subdev_ops preview_v4l2_ops = { | ||
12594 | + .core = &preview_v4l2_core_ops, | ||
12595 | + .file = &preview_v4l2_file_ops, | ||
12596 | + .video = &preview_v4l2_video_ops, | ||
12597 | + .pad = &preview_v4l2_pad_ops, | ||
12598 | +}; | ||
12599 | + | ||
12600 | +/* ----------------------------------------------------------------------------- | ||
12601 | + * Media entity operations | ||
12602 | + */ | ||
12603 | + | ||
12604 | +/* | ||
12605 | + * preview_link_setup - Setup previewer connections. | ||
12606 | + * @entity : Pointer to media entity structure | ||
12607 | + * @local : Pointer to local pad array | ||
12608 | + * @remote : Pointer to remote pad array | ||
12609 | + * @flags : Link flags | ||
12610 | + * return -EINVAL or zero on success | ||
12611 | + */ | ||
12612 | +static int preview_link_setup(struct media_entity *entity, | ||
12613 | + const struct media_pad *local, | ||
12614 | + const struct media_pad *remote, u32 flags) | ||
12615 | +{ | ||
12616 | + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
12617 | + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); | ||
12618 | + | ||
12619 | + switch (local->index | media_entity_type(remote->entity)) { | ||
12620 | + case PREV_PAD_SINK | MEDIA_ENT_T_DEVNODE: | ||
12621 | + /* read from memory */ | ||
12622 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
12623 | + if (prev->input == PREVIEW_INPUT_CCDC) | ||
12624 | + return -EBUSY; | ||
12625 | + prev->input = PREVIEW_INPUT_MEMORY; | ||
12626 | + } else { | ||
12627 | + if (prev->input == PREVIEW_INPUT_MEMORY) | ||
12628 | + prev->input = PREVIEW_INPUT_NONE; | ||
12629 | + } | ||
12630 | + break; | ||
12631 | + | ||
12632 | + case PREV_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
12633 | + /* read from ccdc */ | ||
12634 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
12635 | + if (prev->input == PREVIEW_INPUT_MEMORY) | ||
12636 | + return -EBUSY; | ||
12637 | + prev->input = PREVIEW_INPUT_CCDC; | ||
12638 | + } else { | ||
12639 | + if (prev->input == PREVIEW_INPUT_CCDC) | ||
12640 | + prev->input = PREVIEW_INPUT_NONE; | ||
12641 | + } | ||
12642 | + break; | ||
12643 | + | ||
12644 | + /* | ||
12645 | + * The ISP core doesn't support pipelines with multiple video outputs. | ||
12646 | + * Revisit this when it will be implemented, and return -EBUSY for now. | ||
12647 | + */ | ||
12648 | + | ||
12649 | + case PREV_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: | ||
12650 | + /* write to memory */ | ||
12651 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
12652 | + if (prev->output & ~PREVIEW_OUTPUT_MEMORY) | ||
12653 | + return -EBUSY; | ||
12654 | + prev->output |= PREVIEW_OUTPUT_MEMORY; | ||
12655 | + } else { | ||
12656 | + prev->output &= ~PREVIEW_OUTPUT_MEMORY; | ||
12657 | + } | ||
12658 | + break; | ||
12659 | + | ||
12660 | + case PREV_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: | ||
12661 | + /* write to resizer */ | ||
12662 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
12663 | + if (prev->output & ~PREVIEW_OUTPUT_RESIZER) | ||
12664 | + return -EBUSY; | ||
12665 | + prev->output |= PREVIEW_OUTPUT_RESIZER; | ||
12666 | + } else { | ||
12667 | + prev->output &= ~PREVIEW_OUTPUT_RESIZER; | ||
12668 | + } | ||
12669 | + break; | ||
12670 | + | ||
12671 | + default: | ||
12672 | + return -EINVAL; | ||
12673 | + } | ||
12674 | + | ||
12675 | + return 0; | ||
12676 | +} | ||
12677 | + | ||
12678 | +/* media operations */ | ||
12679 | +static const struct media_entity_operations preview_media_ops = { | ||
12680 | + .link_setup = preview_link_setup, | ||
12681 | +}; | ||
12682 | + | ||
12683 | +/* | ||
12684 | + * review_init_entities - Initialize subdev and media entity. | ||
12685 | + * @prev : Pointer to preview structure | ||
12686 | + * return -ENOMEM or zero on success | ||
12687 | + */ | ||
12688 | +static int preview_init_entities(struct isp_prev_device *prev) | ||
12689 | +{ | ||
12690 | + struct v4l2_subdev *sd = &prev->subdev; | ||
12691 | + struct media_pad *pads = prev->pads; | ||
12692 | + struct media_entity *me = &sd->entity; | ||
12693 | + int ret; | ||
12694 | + | ||
12695 | + prev->input = PREVIEW_INPUT_NONE; | ||
12696 | + | ||
12697 | + v4l2_subdev_init(sd, &preview_v4l2_ops); | ||
12698 | + strlcpy(sd->name, "OMAP3 ISP preview", sizeof(sd->name)); | ||
12699 | + sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
12700 | + v4l2_set_subdevdata(sd, prev); | ||
12701 | + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
12702 | + | ||
12703 | + v4l2_ctrl_handler_init(&prev->ctrls, 3); | ||
12704 | + v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_BRIGHTNESS, | ||
12705 | + ISPPRV_BRIGHT_LOW, ISPPRV_BRIGHT_HIGH, | ||
12706 | + ISPPRV_BRIGHT_STEP, ISPPRV_BRIGHT_DEF); | ||
12707 | + v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_CONTRAST, | ||
12708 | + ISPPRV_CONTRAST_LOW, ISPPRV_CONTRAST_HIGH, | ||
12709 | + ISPPRV_CONTRAST_STEP, ISPPRV_CONTRAST_DEF); | ||
12710 | + v4l2_ctrl_handler_setup(&prev->ctrls); | ||
12711 | + sd->ctrl_handler = &prev->ctrls; | ||
12712 | + | ||
12713 | + pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; | ||
12714 | + pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_OUTPUT; | ||
12715 | + | ||
12716 | + me->ops = &preview_media_ops; | ||
12717 | + ret = media_entity_init(me, PREV_PADS_NUM, pads, 0); | ||
12718 | + if (ret < 0) | ||
12719 | + return ret; | ||
12720 | + | ||
12721 | + preview_init_formats(sd, NULL); | ||
12722 | + | ||
12723 | + /* According to the OMAP34xx TRM, video buffers need to be aligned on a | ||
12724 | + * 32 bytes boundary. However, an undocumented hardware bug requires a | ||
12725 | + * 64 bytes boundary at the preview engine input. | ||
12726 | + */ | ||
12727 | + prev->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
12728 | + prev->video_in.ops = &preview_video_ops; | ||
12729 | + prev->video_in.isp = to_isp_device(prev); | ||
12730 | + prev->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; | ||
12731 | + prev->video_in.bpl_alignment = 64; | ||
12732 | + prev->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
12733 | + prev->video_out.ops = &preview_video_ops; | ||
12734 | + prev->video_out.isp = to_isp_device(prev); | ||
12735 | + prev->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; | ||
12736 | + prev->video_out.bpl_alignment = 32; | ||
12737 | + | ||
12738 | + ret = omap3isp_video_init(&prev->video_in, "preview"); | ||
12739 | + if (ret < 0) | ||
12740 | + return ret; | ||
12741 | + | ||
12742 | + ret = omap3isp_video_init(&prev->video_out, "preview"); | ||
12743 | + if (ret < 0) | ||
12744 | + return ret; | ||
12745 | + | ||
12746 | + /* Connect the video nodes to the previewer subdev. */ | ||
12747 | + ret = media_entity_create_link(&prev->video_in.video.entity, 0, | ||
12748 | + &prev->subdev.entity, PREV_PAD_SINK, 0); | ||
12749 | + if (ret < 0) | ||
12750 | + return ret; | ||
12751 | + | ||
12752 | + ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE, | ||
12753 | + &prev->video_out.video.entity, 0, 0); | ||
12754 | + if (ret < 0) | ||
12755 | + return ret; | ||
12756 | + | ||
12757 | + return 0; | ||
12758 | +} | ||
12759 | + | ||
12760 | +void omap3isp_preview_unregister_entities(struct isp_prev_device *prev) | ||
12761 | +{ | ||
12762 | + media_entity_cleanup(&prev->subdev.entity); | ||
12763 | + | ||
12764 | + v4l2_device_unregister_subdev(&prev->subdev); | ||
12765 | + v4l2_ctrl_handler_free(&prev->ctrls); | ||
12766 | + omap3isp_video_unregister(&prev->video_in); | ||
12767 | + omap3isp_video_unregister(&prev->video_out); | ||
12768 | +} | ||
12769 | + | ||
12770 | +int omap3isp_preview_register_entities(struct isp_prev_device *prev, | ||
12771 | + struct v4l2_device *vdev) | ||
12772 | +{ | ||
12773 | + int ret; | ||
12774 | + | ||
12775 | + /* Register the subdev and video nodes. */ | ||
12776 | + ret = v4l2_device_register_subdev(vdev, &prev->subdev); | ||
12777 | + if (ret < 0) | ||
12778 | + goto error; | ||
12779 | + | ||
12780 | + ret = omap3isp_video_register(&prev->video_in, vdev); | ||
12781 | + if (ret < 0) | ||
12782 | + goto error; | ||
12783 | + | ||
12784 | + ret = omap3isp_video_register(&prev->video_out, vdev); | ||
12785 | + if (ret < 0) | ||
12786 | + goto error; | ||
12787 | + | ||
12788 | + return 0; | ||
12789 | + | ||
12790 | +error: | ||
12791 | + omap3isp_preview_unregister_entities(prev); | ||
12792 | + return ret; | ||
12793 | +} | ||
12794 | + | ||
12795 | +/* ----------------------------------------------------------------------------- | ||
12796 | + * ISP previewer initialisation and cleanup | ||
12797 | + */ | ||
12798 | + | ||
12799 | +void omap3isp_preview_cleanup(struct isp_device *isp) | ||
12800 | +{ | ||
12801 | +} | ||
12802 | + | ||
12803 | +/* | ||
12804 | + * isp_preview_init - Previewer initialization. | ||
12805 | + * @dev : Pointer to ISP device | ||
12806 | + * return -ENOMEM or zero on success | ||
12807 | + */ | ||
12808 | +int omap3isp_preview_init(struct isp_device *isp) | ||
12809 | +{ | ||
12810 | + struct isp_prev_device *prev = &isp->isp_prev; | ||
12811 | + int ret; | ||
12812 | + | ||
12813 | + spin_lock_init(&prev->lock); | ||
12814 | + init_waitqueue_head(&prev->wait); | ||
12815 | + preview_init_params(prev); | ||
12816 | + | ||
12817 | + ret = preview_init_entities(prev); | ||
12818 | + if (ret < 0) | ||
12819 | + goto out; | ||
12820 | + | ||
12821 | +out: | ||
12822 | + if (ret) | ||
12823 | + omap3isp_preview_cleanup(isp); | ||
12824 | + | ||
12825 | + return ret; | ||
12826 | +} | ||
12827 | diff --git a/drivers/media/video/isp/isppreview.h b/drivers/media/video/isp/isppreview.h | ||
12828 | new file mode 100644 | ||
12829 | index 0000000..e20c7c6 | ||
12830 | --- /dev/null | ||
12831 | +++ b/drivers/media/video/isp/isppreview.h | ||
12832 | @@ -0,0 +1,214 @@ | ||
12833 | +/* | ||
12834 | + * isppreview.h | ||
12835 | + * | ||
12836 | + * TI OMAP3 ISP - Preview module | ||
12837 | + * | ||
12838 | + * Copyright (C) 2010 Nokia Corporation | ||
12839 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
12840 | + * | ||
12841 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
12842 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
12843 | + * | ||
12844 | + * This program is free software; you can redistribute it and/or modify | ||
12845 | + * it under the terms of the GNU General Public License version 2 as | ||
12846 | + * published by the Free Software Foundation. | ||
12847 | + * | ||
12848 | + * This program is distributed in the hope that it will be useful, but | ||
12849 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12850 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12851 | + * General Public License for more details. | ||
12852 | + * | ||
12853 | + * You should have received a copy of the GNU General Public License | ||
12854 | + * along with this program; if not, write to the Free Software | ||
12855 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
12856 | + * 02110-1301 USA | ||
12857 | + */ | ||
12858 | + | ||
12859 | +#ifndef OMAP3_ISP_PREVIEW_H | ||
12860 | +#define OMAP3_ISP_PREVIEW_H | ||
12861 | + | ||
12862 | +#include <linux/omap3isp.h> | ||
12863 | +#include <linux/types.h> | ||
12864 | +#include <media/v4l2-ctrls.h> | ||
12865 | + | ||
12866 | +#include "ispvideo.h" | ||
12867 | + | ||
12868 | +#define ISPPRV_BRIGHT_STEP 0x1 | ||
12869 | +#define ISPPRV_BRIGHT_DEF 0x0 | ||
12870 | +#define ISPPRV_BRIGHT_LOW 0x0 | ||
12871 | +#define ISPPRV_BRIGHT_HIGH 0xFF | ||
12872 | +#define ISPPRV_BRIGHT_UNITS 0x1 | ||
12873 | + | ||
12874 | +#define ISPPRV_CONTRAST_STEP 0x1 | ||
12875 | +#define ISPPRV_CONTRAST_DEF 0x10 | ||
12876 | +#define ISPPRV_CONTRAST_LOW 0x0 | ||
12877 | +#define ISPPRV_CONTRAST_HIGH 0xFF | ||
12878 | +#define ISPPRV_CONTRAST_UNITS 0x1 | ||
12879 | + | ||
12880 | +#define NO_AVE 0x0 | ||
12881 | +#define AVE_2_PIX 0x1 | ||
12882 | +#define AVE_4_PIX 0x2 | ||
12883 | +#define AVE_8_PIX 0x3 | ||
12884 | + | ||
12885 | +/* Features list */ | ||
12886 | +#define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH | ||
12887 | +#define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW | ||
12888 | +#define PREV_HORZ_MEDIAN_FILTER OMAP3ISP_PREV_HRZ_MED | ||
12889 | +#define PREV_CFA OMAP3ISP_PREV_CFA | ||
12890 | +#define PREV_CHROMA_SUPPRESS OMAP3ISP_PREV_CHROMA_SUPP | ||
12891 | +#define PREV_WB OMAP3ISP_PREV_WB | ||
12892 | +#define PREV_BLKADJ OMAP3ISP_PREV_BLKADJ | ||
12893 | +#define PREV_RGB2RGB OMAP3ISP_PREV_RGB2RGB | ||
12894 | +#define PREV_COLOR_CONV OMAP3ISP_PREV_COLOR_CONV | ||
12895 | +#define PREV_YCLIMITS OMAP3ISP_PREV_YC_LIMIT | ||
12896 | +#define PREV_DEFECT_COR OMAP3ISP_PREV_DEFECT_COR | ||
12897 | +#define PREV_GAMMA_BYPASS OMAP3ISP_PREV_GAMMABYPASS | ||
12898 | +#define PREV_DARK_FRAME_CAPTURE OMAP3ISP_PREV_DRK_FRM_CAPTURE | ||
12899 | +#define PREV_DARK_FRAME_SUBTRACT OMAP3ISP_PREV_DRK_FRM_SUBTRACT | ||
12900 | +#define PREV_LENS_SHADING OMAP3ISP_PREV_LENS_SHADING | ||
12901 | +#define PREV_NOISE_FILTER OMAP3ISP_PREV_NF | ||
12902 | +#define PREV_GAMMA OMAP3ISP_PREV_GAMMA | ||
12903 | + | ||
12904 | +#define PREV_CONTRAST (1 << 17) | ||
12905 | +#define PREV_BRIGHTNESS (1 << 18) | ||
12906 | +#define PREV_AVERAGER (1 << 19) | ||
12907 | +#define PREV_FEATURES_END (1 << 20) | ||
12908 | + | ||
12909 | +enum preview_input_entity { | ||
12910 | + PREVIEW_INPUT_NONE, | ||
12911 | + PREVIEW_INPUT_CCDC, | ||
12912 | + PREVIEW_INPUT_MEMORY, | ||
12913 | +}; | ||
12914 | + | ||
12915 | +#define PREVIEW_OUTPUT_RESIZER (1 << 1) | ||
12916 | +#define PREVIEW_OUTPUT_MEMORY (1 << 2) | ||
12917 | + | ||
12918 | +/* Configure byte layout of YUV image */ | ||
12919 | +enum preview_ycpos_mode { | ||
12920 | + YCPOS_YCrYCb = 0, | ||
12921 | + YCPOS_YCbYCr = 1, | ||
12922 | + YCPOS_CbYCrY = 2, | ||
12923 | + YCPOS_CrYCbY = 3 | ||
12924 | +}; | ||
12925 | + | ||
12926 | +/* | ||
12927 | + * struct prev_params - Structure for all configuration | ||
12928 | + * @features: Set of features enabled. | ||
12929 | + * @cfa: CFA coefficients. | ||
12930 | + * @csup: Chroma suppression coefficients. | ||
12931 | + * @luma: Luma enhancement coefficients. | ||
12932 | + * @nf: Noise filter coefficients. | ||
12933 | + * @dcor: Noise filter coefficients. | ||
12934 | + * @gamma: Gamma coefficients. | ||
12935 | + * @wbal: White Balance parameters. | ||
12936 | + * @blk_adj: Black adjustment parameters. | ||
12937 | + * @rgb2rgb: RGB blending parameters. | ||
12938 | + * @rgb2ycbcr: RGB to ycbcr parameters. | ||
12939 | + * @hmed: Horizontal median filter. | ||
12940 | + * @yclimit: YC limits parameters. | ||
12941 | + * @average: Downsampling rate for averager. | ||
12942 | + * @contrast: Contrast. | ||
12943 | + * @brightness: Brightness. | ||
12944 | + */ | ||
12945 | +struct prev_params { | ||
12946 | + u32 features; | ||
12947 | + struct omap3isp_prev_cfa cfa; | ||
12948 | + struct omap3isp_prev_csup csup; | ||
12949 | + struct omap3isp_prev_luma luma; | ||
12950 | + struct omap3isp_prev_nf nf; | ||
12951 | + struct omap3isp_prev_dcor dcor; | ||
12952 | + struct omap3isp_prev_gtables gamma; | ||
12953 | + struct omap3isp_prev_wbal wbal; | ||
12954 | + struct omap3isp_prev_blkadj blk_adj; | ||
12955 | + struct omap3isp_prev_rgbtorgb rgb2rgb; | ||
12956 | + struct omap3isp_prev_csc rgb2ycbcr; | ||
12957 | + struct omap3isp_prev_hmed hmed; | ||
12958 | + struct omap3isp_prev_yclimit yclimit; | ||
12959 | + u8 average; | ||
12960 | + u8 contrast; | ||
12961 | + u8 brightness; | ||
12962 | +}; | ||
12963 | + | ||
12964 | +/* | ||
12965 | + * struct isptables_update - Structure for Table Configuration. | ||
12966 | + * @update: Specifies which tables should be updated. | ||
12967 | + * @flag: Specifies which tables should be enabled. | ||
12968 | + * @nf: Pointer to structure for Noise Filter | ||
12969 | + * @lsc: Pointer to LSC gain table. (currently not used) | ||
12970 | + * @gamma: Pointer to gamma correction tables. | ||
12971 | + * @cfa: Pointer to color filter array configuration. | ||
12972 | + * @wbal: Pointer to colour and digital gain configuration. | ||
12973 | + */ | ||
12974 | +struct isptables_update { | ||
12975 | + u32 update; | ||
12976 | + u32 flag; | ||
12977 | + struct omap3isp_prev_nf *nf; | ||
12978 | + u32 *lsc; | ||
12979 | + struct omap3isp_prev_gtables *gamma; | ||
12980 | + struct omap3isp_prev_cfa *cfa; | ||
12981 | + struct omap3isp_prev_wbal *wbal; | ||
12982 | +}; | ||
12983 | + | ||
12984 | +/* Sink and source previewer pads */ | ||
12985 | +#define PREV_PAD_SINK 0 | ||
12986 | +#define PREV_PAD_SOURCE 1 | ||
12987 | +#define PREV_PADS_NUM 2 | ||
12988 | + | ||
12989 | +/* | ||
12990 | + * struct isp_prev_device - Structure for storing ISP Preview module information | ||
12991 | + * @subdev: V4L2 subdevice | ||
12992 | + * @pads: Media entity pads | ||
12993 | + * @formats: Active formats at the subdev pad | ||
12994 | + * @input: Module currently connected to the input pad | ||
12995 | + * @output: Bitmask of the active output | ||
12996 | + * @video_in: Input video entity | ||
12997 | + * @video_out: Output video entity | ||
12998 | + * @error: A hardware error occured during capture | ||
12999 | + * @params: Module configuration data | ||
13000 | + * @shadow_update: If set, update the hardware configured in the next interrupt | ||
13001 | + * @underrun: Whether the preview entity has queued buffers on the output | ||
13002 | + * @state: Current preview pipeline state | ||
13003 | + * @lock: Shadow update lock | ||
13004 | + * @update: Bitmask of the parameters to be updated | ||
13005 | + * | ||
13006 | + * This structure is used to store the OMAP ISP Preview module Information. | ||
13007 | + */ | ||
13008 | +struct isp_prev_device { | ||
13009 | + struct v4l2_subdev subdev; | ||
13010 | + struct media_pad pads[PREV_PADS_NUM]; | ||
13011 | + struct v4l2_mbus_framefmt formats[PREV_PADS_NUM]; | ||
13012 | + | ||
13013 | + struct v4l2_ctrl_handler ctrls; | ||
13014 | + | ||
13015 | + enum preview_input_entity input; | ||
13016 | + unsigned int output; | ||
13017 | + struct isp_video video_in; | ||
13018 | + struct isp_video video_out; | ||
13019 | + unsigned int error; | ||
13020 | + | ||
13021 | + struct prev_params params; | ||
13022 | + unsigned int shadow_update:1; | ||
13023 | + enum isp_pipeline_stream_state state; | ||
13024 | + wait_queue_head_t wait; | ||
13025 | + atomic_t stopping; | ||
13026 | + spinlock_t lock; | ||
13027 | + u32 update; | ||
13028 | +}; | ||
13029 | + | ||
13030 | +struct isp_device; | ||
13031 | + | ||
13032 | +int omap3isp_preview_init(struct isp_device *isp); | ||
13033 | +void omap3isp_preview_cleanup(struct isp_device *isp); | ||
13034 | + | ||
13035 | +int omap3isp_preview_register_entities(struct isp_prev_device *prv, | ||
13036 | + struct v4l2_device *vdev); | ||
13037 | +void omap3isp_preview_unregister_entities(struct isp_prev_device *prv); | ||
13038 | + | ||
13039 | +void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev); | ||
13040 | +void omap3isp_preview_isr(struct isp_prev_device *prev); | ||
13041 | + | ||
13042 | +int omap3isp_preview_busy(struct isp_prev_device *isp_prev); | ||
13043 | + | ||
13044 | +void omap3isp_preview_restore_context(struct isp_device *isp); | ||
13045 | + | ||
13046 | +#endif /* OMAP3_ISP_PREVIEW_H */ | ||
13047 | diff --git a/drivers/media/video/isp/ispqueue.c b/drivers/media/video/isp/ispqueue.c | ||
13048 | new file mode 100644 | ||
13049 | index 0000000..af78c19 | ||
13050 | --- /dev/null | ||
13051 | +++ b/drivers/media/video/isp/ispqueue.c | ||
13052 | @@ -0,0 +1,1136 @@ | ||
13053 | +/* | ||
13054 | + * ispqueue.c | ||
13055 | + * | ||
13056 | + * TI OMAP3 ISP - Video buffers queue handling | ||
13057 | + * | ||
13058 | + * Copyright (C) 2010 Nokia Corporation | ||
13059 | + * | ||
13060 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
13061 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
13062 | + * | ||
13063 | + * This program is free software; you can redistribute it and/or modify | ||
13064 | + * it under the terms of the GNU General Public License version 2 as | ||
13065 | + * published by the Free Software Foundation. | ||
13066 | + * | ||
13067 | + * This program is distributed in the hope that it will be useful, but | ||
13068 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13069 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13070 | + * General Public License for more details. | ||
13071 | + * | ||
13072 | + * You should have received a copy of the GNU General Public License | ||
13073 | + * along with this program; if not, write to the Free Software | ||
13074 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
13075 | + * 02110-1301 USA | ||
13076 | + */ | ||
13077 | + | ||
13078 | +#include <asm/cacheflush.h> | ||
13079 | +#include <linux/dma-mapping.h> | ||
13080 | +#include <linux/mm.h> | ||
13081 | +#include <linux/pagemap.h> | ||
13082 | +#include <linux/poll.h> | ||
13083 | +#include <linux/scatterlist.h> | ||
13084 | +#include <linux/sched.h> | ||
13085 | +#include <linux/slab.h> | ||
13086 | +#include <linux/vmalloc.h> | ||
13087 | + | ||
13088 | +#include "ispqueue.h" | ||
13089 | + | ||
13090 | +/* ----------------------------------------------------------------------------- | ||
13091 | + * Video buffers management | ||
13092 | + */ | ||
13093 | + | ||
13094 | +/* | ||
13095 | + * isp_video_buffer_cache_sync - Keep the buffers coherent between CPU and ISP | ||
13096 | + * | ||
13097 | + * The typical operation required here is Cache Invalidation across | ||
13098 | + * the (user space) buffer address range. And this _must_ be done | ||
13099 | + * at QBUF stage (and *only* at QBUF). | ||
13100 | + * | ||
13101 | + * We try to use optimal cache invalidation function: | ||
13102 | + * - dmac_map_area: | ||
13103 | + * - used when the number of pages are _low_. | ||
13104 | + * - it becomes quite slow as the number of pages increase. | ||
13105 | + * - for 648x492 viewfinder (150 pages) it takes 1.3 ms. | ||
13106 | + * - for 5 Mpix buffer (2491 pages) it takes between 25-50 ms. | ||
13107 | + * | ||
13108 | + * - flush_cache_all: | ||
13109 | + * - used when the number of pages are _high_. | ||
13110 | + * - time taken in the range of 500-900 us. | ||
13111 | + * - has a higher penalty but, as whole dcache + icache is invalidated | ||
13112 | + */ | ||
13113 | +/* | ||
13114 | + * FIXME: dmac_inv_range crashes randomly on the user space buffer | ||
13115 | + * address. Fall back to flush_cache_all for now. | ||
13116 | + */ | ||
13117 | +#define ISP_CACHE_FLUSH_PAGES_MAX 0 | ||
13118 | + | ||
13119 | +static void isp_video_buffer_cache_sync(struct isp_video_buffer *buf) | ||
13120 | +{ | ||
13121 | + if (buf->vbuf.m.userptr == 0 || buf->npages == 0 || | ||
13122 | + buf->npages > ISP_CACHE_FLUSH_PAGES_MAX) | ||
13123 | + flush_cache_all(); | ||
13124 | + else { | ||
13125 | + dmac_map_area((void *)buf->vbuf.m.userptr, buf->vbuf.length, | ||
13126 | + DMA_FROM_DEVICE); | ||
13127 | + outer_inv_range(buf->vbuf.m.userptr, | ||
13128 | + buf->vbuf.m.userptr + buf->vbuf.length); | ||
13129 | + } | ||
13130 | +} | ||
13131 | + | ||
13132 | +/* | ||
13133 | + * isp_video_buffer_lock_vma - Prevent VMAs from being unmapped | ||
13134 | + * | ||
13135 | + * Lock the VMAs underlying the given buffer into memory. This avoids the | ||
13136 | + * userspace buffer mapping from being swapped out, making VIPT cache handling | ||
13137 | + * easier. | ||
13138 | + * | ||
13139 | + * Note that the pages will not be freed as the buffers have been locked to | ||
13140 | + * memory using by a call to get_user_pages(), but the userspace mapping could | ||
13141 | + * still disappear if the VMAs are not locked. This is caused by the memory | ||
13142 | + * management code trying to be as lock-less as possible, which results in the | ||
13143 | + * userspace mapping manager not finding out that the pages are locked under | ||
13144 | + * some conditions. | ||
13145 | + */ | ||
13146 | +static int isp_video_buffer_lock_vma(struct isp_video_buffer *buf, int lock) | ||
13147 | +{ | ||
13148 | + struct vm_area_struct *vma; | ||
13149 | + unsigned long start; | ||
13150 | + unsigned long end; | ||
13151 | + int ret = 0; | ||
13152 | + | ||
13153 | + if (buf->vbuf.memory == V4L2_MEMORY_MMAP) | ||
13154 | + return 0; | ||
13155 | + | ||
13156 | + /* We can be called from workqueue context if the current task dies to | ||
13157 | + * unlock the VMAs. In that case there's no current memory management | ||
13158 | + * context so unlocking can't be performed, but the VMAs have been or | ||
13159 | + * are getting destroyed anyway so it doesn't really matter. | ||
13160 | + */ | ||
13161 | + if (!current || !current->mm) | ||
13162 | + return lock ? -EINVAL : 0; | ||
13163 | + | ||
13164 | + start = buf->vbuf.m.userptr; | ||
13165 | + end = buf->vbuf.m.userptr + buf->vbuf.length - 1; | ||
13166 | + | ||
13167 | + down_write(¤t->mm->mmap_sem); | ||
13168 | + spin_lock(¤t->mm->page_table_lock); | ||
13169 | + | ||
13170 | + do { | ||
13171 | + vma = find_vma(current->mm, start); | ||
13172 | + if (vma == NULL) { | ||
13173 | + ret = -EFAULT; | ||
13174 | + goto out; | ||
13175 | + } | ||
13176 | + | ||
13177 | + if (lock) | ||
13178 | + vma->vm_flags |= VM_LOCKED; | ||
13179 | + else | ||
13180 | + vma->vm_flags &= ~VM_LOCKED; | ||
13181 | + | ||
13182 | + start = vma->vm_end + 1; | ||
13183 | + } while (vma->vm_end < end); | ||
13184 | + | ||
13185 | + if (lock) | ||
13186 | + buf->vm_flags |= VM_LOCKED; | ||
13187 | + else | ||
13188 | + buf->vm_flags &= ~VM_LOCKED; | ||
13189 | + | ||
13190 | +out: | ||
13191 | + spin_unlock(¤t->mm->page_table_lock); | ||
13192 | + up_write(¤t->mm->mmap_sem); | ||
13193 | + return ret; | ||
13194 | +} | ||
13195 | + | ||
13196 | +/* | ||
13197 | + * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer | ||
13198 | + * | ||
13199 | + * Iterate over the vmalloc'ed area and create a scatter list entry for every | ||
13200 | + * page. | ||
13201 | + */ | ||
13202 | +static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf) | ||
13203 | +{ | ||
13204 | + struct scatterlist *sglist; | ||
13205 | + unsigned int npages; | ||
13206 | + unsigned int i; | ||
13207 | + void *addr; | ||
13208 | + | ||
13209 | + addr = buf->vaddr; | ||
13210 | + npages = PAGE_ALIGN(buf->vbuf.length) >> PAGE_SHIFT; | ||
13211 | + | ||
13212 | + sglist = vmalloc(npages * sizeof(*sglist)); | ||
13213 | + if (sglist == NULL) | ||
13214 | + return -ENOMEM; | ||
13215 | + | ||
13216 | + sg_init_table(sglist, npages); | ||
13217 | + | ||
13218 | + for (i = 0; i < npages; ++i, addr += PAGE_SIZE) { | ||
13219 | + struct page *page = vmalloc_to_page(addr); | ||
13220 | + | ||
13221 | + if (page == NULL || PageHighMem(page)) { | ||
13222 | + vfree(sglist); | ||
13223 | + return -EINVAL; | ||
13224 | + } | ||
13225 | + | ||
13226 | + sg_set_page(&sglist[i], page, PAGE_SIZE, 0); | ||
13227 | + } | ||
13228 | + | ||
13229 | + buf->sglen = npages; | ||
13230 | + buf->sglist = sglist; | ||
13231 | + | ||
13232 | + return 0; | ||
13233 | +} | ||
13234 | + | ||
13235 | +/* | ||
13236 | + * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer | ||
13237 | + * | ||
13238 | + * Walk the buffer pages list and create a 1:1 mapping to a scatter list. | ||
13239 | + */ | ||
13240 | +static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf) | ||
13241 | +{ | ||
13242 | + struct scatterlist *sglist; | ||
13243 | + unsigned int offset = buf->offset; | ||
13244 | + unsigned int i; | ||
13245 | + | ||
13246 | + sglist = vmalloc(buf->npages * sizeof(*sglist)); | ||
13247 | + if (sglist == NULL) | ||
13248 | + return -ENOMEM; | ||
13249 | + | ||
13250 | + sg_init_table(sglist, buf->npages); | ||
13251 | + | ||
13252 | + for (i = 0; i < buf->npages; ++i) { | ||
13253 | + if (PageHighMem(buf->pages[i])) { | ||
13254 | + vfree(sglist); | ||
13255 | + return -EINVAL; | ||
13256 | + } | ||
13257 | + | ||
13258 | + sg_set_page(&sglist[i], buf->pages[i], PAGE_SIZE - offset, | ||
13259 | + offset); | ||
13260 | + offset = 0; | ||
13261 | + } | ||
13262 | + | ||
13263 | + buf->sglen = buf->npages; | ||
13264 | + buf->sglist = sglist; | ||
13265 | + | ||
13266 | + return 0; | ||
13267 | +} | ||
13268 | + | ||
13269 | +/* | ||
13270 | + * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer | ||
13271 | + * | ||
13272 | + * Create a scatter list of physically contiguous pages starting at the buffer | ||
13273 | + * memory physical address. | ||
13274 | + */ | ||
13275 | +static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf) | ||
13276 | +{ | ||
13277 | + struct scatterlist *sglist; | ||
13278 | + unsigned int offset = buf->offset; | ||
13279 | + unsigned long pfn = buf->paddr >> PAGE_SHIFT; | ||
13280 | + unsigned int i; | ||
13281 | + | ||
13282 | + sglist = vmalloc(buf->npages * sizeof(*sglist)); | ||
13283 | + if (sglist == NULL) | ||
13284 | + return -ENOMEM; | ||
13285 | + | ||
13286 | + sg_init_table(sglist, buf->npages); | ||
13287 | + | ||
13288 | + for (i = 0; i < buf->npages; ++i, ++pfn) { | ||
13289 | + sg_set_page(&sglist[i], pfn_to_page(pfn), PAGE_SIZE - offset, | ||
13290 | + offset); | ||
13291 | + /* PFNMAP buffers will not get DMA-mapped, set the DMA address | ||
13292 | + * manually. | ||
13293 | + */ | ||
13294 | + sg_dma_address(&sglist[i]) = (pfn << PAGE_SHIFT) + offset; | ||
13295 | + offset = 0; | ||
13296 | + } | ||
13297 | + | ||
13298 | + buf->sglen = buf->npages; | ||
13299 | + buf->sglist = sglist; | ||
13300 | + | ||
13301 | + return 0; | ||
13302 | +} | ||
13303 | + | ||
13304 | +/* | ||
13305 | + * isp_video_buffer_cleanup - Release pages for a userspace VMA. | ||
13306 | + * | ||
13307 | + * Release pages locked by a call isp_video_buffer_prepare_user and free the | ||
13308 | + * pages table. | ||
13309 | + */ | ||
13310 | +static void isp_video_buffer_cleanup(struct isp_video_buffer *buf) | ||
13311 | +{ | ||
13312 | + enum dma_data_direction direction; | ||
13313 | + unsigned int i; | ||
13314 | + | ||
13315 | + if (buf->queue->ops->buffer_cleanup) | ||
13316 | + buf->queue->ops->buffer_cleanup(buf); | ||
13317 | + | ||
13318 | + if (!(buf->vm_flags & VM_PFNMAP)) { | ||
13319 | + direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE | ||
13320 | + ? DMA_FROM_DEVICE : DMA_TO_DEVICE; | ||
13321 | + dma_unmap_sg(buf->queue->dev, buf->sglist, buf->sglen, | ||
13322 | + direction); | ||
13323 | + } | ||
13324 | + | ||
13325 | + vfree(buf->sglist); | ||
13326 | + buf->sglist = NULL; | ||
13327 | + buf->sglen = 0; | ||
13328 | + | ||
13329 | + if (buf->pages != NULL) { | ||
13330 | + isp_video_buffer_lock_vma(buf, 0); | ||
13331 | + | ||
13332 | + for (i = 0; i < buf->npages; ++i) | ||
13333 | + page_cache_release(buf->pages[i]); | ||
13334 | + | ||
13335 | + vfree(buf->pages); | ||
13336 | + buf->pages = NULL; | ||
13337 | + } | ||
13338 | + | ||
13339 | + buf->npages = 0; | ||
13340 | +} | ||
13341 | + | ||
13342 | +/* | ||
13343 | + * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory. | ||
13344 | + * | ||
13345 | + * This function creates a list of pages for a userspace VMA. The number of | ||
13346 | + * pages is first computed based on the buffer size, and pages are then | ||
13347 | + * retrieved by a call to get_user_pages. | ||
13348 | + * | ||
13349 | + * Pages are pinned to memory by get_user_pages, making them available for DMA | ||
13350 | + * transfers. However, due to memory management optimization, it seems the | ||
13351 | + * get_user_pages doesn't guarantee that the pinned pages will not be written | ||
13352 | + * to swap and removed from the userspace mapping(s). When this happens, a page | ||
13353 | + * fault can be generated when accessing those unmapped pages. | ||
13354 | + * | ||
13355 | + * If the fault is triggered by a page table walk caused by VIPT cache | ||
13356 | + * management operations, the page fault handler might oops if the MM semaphore | ||
13357 | + * is held, as it can't handle kernel page faults in that case. To fix that, a | ||
13358 | + * fixup entry needs to be added to the cache management code, or the userspace | ||
13359 | + * VMA must be locked to avoid removing pages from the userspace mapping in the | ||
13360 | + * first place. | ||
13361 | + * | ||
13362 | + * If the number of pages retrieved is smaller than the number required by the | ||
13363 | + * buffer size, the function returns -EFAULT. | ||
13364 | + */ | ||
13365 | +static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf) | ||
13366 | +{ | ||
13367 | + unsigned long data; | ||
13368 | + unsigned int first; | ||
13369 | + unsigned int last; | ||
13370 | + int ret; | ||
13371 | + | ||
13372 | + data = buf->vbuf.m.userptr; | ||
13373 | + first = (data & PAGE_MASK) >> PAGE_SHIFT; | ||
13374 | + last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT; | ||
13375 | + | ||
13376 | + buf->offset = data & ~PAGE_MASK; | ||
13377 | + buf->npages = last - first + 1; | ||
13378 | + buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0])); | ||
13379 | + if (buf->pages == NULL) | ||
13380 | + return -ENOMEM; | ||
13381 | + | ||
13382 | + down_read(¤t->mm->mmap_sem); | ||
13383 | + ret = get_user_pages(current, current->mm, data & PAGE_MASK, | ||
13384 | + buf->npages, | ||
13385 | + buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, | ||
13386 | + buf->pages, NULL); | ||
13387 | + up_read(¤t->mm->mmap_sem); | ||
13388 | + | ||
13389 | + if (ret != buf->npages) { | ||
13390 | + buf->npages = ret; | ||
13391 | + isp_video_buffer_cleanup(buf); | ||
13392 | + return -EFAULT; | ||
13393 | + } | ||
13394 | + | ||
13395 | + ret = isp_video_buffer_lock_vma(buf, 1); | ||
13396 | + if (ret < 0) | ||
13397 | + isp_video_buffer_cleanup(buf); | ||
13398 | + | ||
13399 | + return ret; | ||
13400 | +} | ||
13401 | + | ||
13402 | +/* | ||
13403 | + * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer | ||
13404 | + * | ||
13405 | + * Userspace VM_PFNMAP buffers are supported only if they are contiguous in | ||
13406 | + * memory and if they span a single VMA. | ||
13407 | + * | ||
13408 | + * Return 0 if the buffer is valid, or -EFAULT otherwise. | ||
13409 | + */ | ||
13410 | +static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf) | ||
13411 | +{ | ||
13412 | + struct vm_area_struct *vma; | ||
13413 | + unsigned long prev_pfn; | ||
13414 | + unsigned long this_pfn; | ||
13415 | + unsigned long start; | ||
13416 | + unsigned long end; | ||
13417 | + dma_addr_t pa; | ||
13418 | + int ret = -EFAULT; | ||
13419 | + | ||
13420 | + start = buf->vbuf.m.userptr; | ||
13421 | + end = buf->vbuf.m.userptr + buf->vbuf.length - 1; | ||
13422 | + | ||
13423 | + buf->offset = start & ~PAGE_MASK; | ||
13424 | + buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1; | ||
13425 | + buf->pages = NULL; | ||
13426 | + | ||
13427 | + down_read(¤t->mm->mmap_sem); | ||
13428 | + vma = find_vma(current->mm, start); | ||
13429 | + if (vma == NULL || vma->vm_end < end) | ||
13430 | + goto done; | ||
13431 | + | ||
13432 | + for (prev_pfn = 0; start <= end; start += PAGE_SIZE) { | ||
13433 | + ret = follow_pfn(vma, start, &this_pfn); | ||
13434 | + if (ret) | ||
13435 | + goto done; | ||
13436 | + | ||
13437 | + if (prev_pfn == 0) | ||
13438 | + pa = this_pfn << PAGE_SHIFT; | ||
13439 | + else if (this_pfn != prev_pfn + 1) { | ||
13440 | + ret = -EFAULT; | ||
13441 | + goto done; | ||
13442 | + } | ||
13443 | + | ||
13444 | + prev_pfn = this_pfn; | ||
13445 | + } | ||
13446 | + | ||
13447 | + buf->paddr = pa + buf->offset; | ||
13448 | + ret = 0; | ||
13449 | + | ||
13450 | +done: | ||
13451 | + up_read(¤t->mm->mmap_sem); | ||
13452 | + return ret; | ||
13453 | +} | ||
13454 | + | ||
13455 | +/* | ||
13456 | + * isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address | ||
13457 | + * | ||
13458 | + * This function locates the VMAs for the buffer's userspace address and checks | ||
13459 | + * that their flags match. The onlflag that we need to care for at the moment is | ||
13460 | + * VM_PFNMAP. | ||
13461 | + * | ||
13462 | + * The buffer vm_flags field is set to the first VMA flags. | ||
13463 | + * | ||
13464 | + * Return -EFAULT if no VMA can be found for part of the buffer, or if the VMAs | ||
13465 | + * have incompatible flags. | ||
13466 | + */ | ||
13467 | +static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf) | ||
13468 | +{ | ||
13469 | + struct vm_area_struct *vma; | ||
13470 | + unsigned long start; | ||
13471 | + unsigned long end; | ||
13472 | + int ret = -EFAULT; | ||
13473 | + | ||
13474 | + start = buf->vbuf.m.userptr; | ||
13475 | + end = buf->vbuf.m.userptr + buf->vbuf.length - 1; | ||
13476 | + | ||
13477 | + down_read(¤t->mm->mmap_sem); | ||
13478 | + | ||
13479 | + do { | ||
13480 | + vma = find_vma(current->mm, start); | ||
13481 | + if (vma == NULL) | ||
13482 | + goto done; | ||
13483 | + | ||
13484 | + if (start == buf->vbuf.m.userptr) | ||
13485 | + buf->vm_flags = vma->vm_flags; | ||
13486 | + | ||
13487 | + if ((buf->vm_flags ^ vma->vm_flags) & VM_PFNMAP) | ||
13488 | + goto done; | ||
13489 | + | ||
13490 | + start = vma->vm_end + 1; | ||
13491 | + } while (vma->vm_end < end); | ||
13492 | + | ||
13493 | + ret = 0; | ||
13494 | + | ||
13495 | +done: | ||
13496 | + up_read(¤t->mm->mmap_sem); | ||
13497 | + return ret; | ||
13498 | +} | ||
13499 | + | ||
13500 | +/* | ||
13501 | + * isp_video_buffer_prepare - Make a buffer ready for operation | ||
13502 | + * | ||
13503 | + * Preparing a buffer involves: | ||
13504 | + * | ||
13505 | + * - validating VMAs (userspace buffers only) | ||
13506 | + * - locking pages and VMAs into memory (userspace buffers only) | ||
13507 | + * - building page and scatter-gather lists | ||
13508 | + * - mapping buffers for DMA operation | ||
13509 | + * - performing driver-specific preparation | ||
13510 | + * | ||
13511 | + * The function must be called in userspace context with a valid mm context | ||
13512 | + * (this excludes cleanup paths such as sys_close when the userspace process | ||
13513 | + * segfaults). | ||
13514 | + */ | ||
13515 | +static int isp_video_buffer_prepare(struct isp_video_buffer *buf) | ||
13516 | +{ | ||
13517 | + enum dma_data_direction direction; | ||
13518 | + int ret; | ||
13519 | + | ||
13520 | + switch (buf->vbuf.memory) { | ||
13521 | + case V4L2_MEMORY_MMAP: | ||
13522 | + ret = isp_video_buffer_sglist_kernel(buf); | ||
13523 | + break; | ||
13524 | + | ||
13525 | + case V4L2_MEMORY_USERPTR: | ||
13526 | + ret = isp_video_buffer_prepare_vm_flags(buf); | ||
13527 | + if (ret < 0) | ||
13528 | + return ret; | ||
13529 | + | ||
13530 | + if (buf->vm_flags & VM_PFNMAP) { | ||
13531 | + ret = isp_video_buffer_prepare_pfnmap(buf); | ||
13532 | + if (ret < 0) | ||
13533 | + return ret; | ||
13534 | + | ||
13535 | + ret = isp_video_buffer_sglist_pfnmap(buf); | ||
13536 | + } else { | ||
13537 | + ret = isp_video_buffer_prepare_user(buf); | ||
13538 | + if (ret < 0) | ||
13539 | + return ret; | ||
13540 | + | ||
13541 | + ret = isp_video_buffer_sglist_user(buf); | ||
13542 | + } | ||
13543 | + break; | ||
13544 | + | ||
13545 | + default: | ||
13546 | + return -EINVAL; | ||
13547 | + } | ||
13548 | + | ||
13549 | + if (ret < 0) | ||
13550 | + goto done; | ||
13551 | + | ||
13552 | + if (!(buf->vm_flags & VM_PFNMAP)) { | ||
13553 | + direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE | ||
13554 | + ? DMA_FROM_DEVICE : DMA_TO_DEVICE; | ||
13555 | + ret = dma_map_sg(buf->queue->dev, buf->sglist, buf->sglen, | ||
13556 | + direction); | ||
13557 | + if (ret != buf->sglen) { | ||
13558 | + ret = -EFAULT; | ||
13559 | + goto done; | ||
13560 | + } | ||
13561 | + } | ||
13562 | + | ||
13563 | + if (buf->queue->ops->buffer_prepare) | ||
13564 | + ret = buf->queue->ops->buffer_prepare(buf); | ||
13565 | + | ||
13566 | +done: | ||
13567 | + if (ret < 0) { | ||
13568 | + isp_video_buffer_cleanup(buf); | ||
13569 | + return ret; | ||
13570 | + } | ||
13571 | + | ||
13572 | + return ret; | ||
13573 | +} | ||
13574 | + | ||
13575 | +/* | ||
13576 | + * isp_video_queue_query - Query the status of a given buffer | ||
13577 | + * | ||
13578 | + * Locking: must be called with the queue lock held. | ||
13579 | + */ | ||
13580 | +static void isp_video_buffer_query(struct isp_video_buffer *buf, | ||
13581 | + struct v4l2_buffer *vbuf) | ||
13582 | +{ | ||
13583 | + memcpy(vbuf, &buf->vbuf, sizeof(*vbuf)); | ||
13584 | + | ||
13585 | + if (buf->vma_use_count) | ||
13586 | + vbuf->flags |= V4L2_BUF_FLAG_MAPPED; | ||
13587 | + | ||
13588 | + switch (buf->state) { | ||
13589 | + case ISP_BUF_STATE_ERROR: | ||
13590 | + vbuf->flags |= V4L2_BUF_FLAG_ERROR; | ||
13591 | + case ISP_BUF_STATE_DONE: | ||
13592 | + vbuf->flags |= V4L2_BUF_FLAG_DONE; | ||
13593 | + case ISP_BUF_STATE_QUEUED: | ||
13594 | + case ISP_BUF_STATE_ACTIVE: | ||
13595 | + vbuf->flags |= V4L2_BUF_FLAG_QUEUED; | ||
13596 | + break; | ||
13597 | + case ISP_BUF_STATE_IDLE: | ||
13598 | + default: | ||
13599 | + break; | ||
13600 | + } | ||
13601 | +} | ||
13602 | + | ||
13603 | +/* | ||
13604 | + * isp_video_buffer_wait - Wait for a buffer to be ready | ||
13605 | + * | ||
13606 | + * In non-blocking mode, return immediately with 0 if the buffer is ready or | ||
13607 | + * -EAGAIN if the buffer is in the QUEUED or ACTIVE state. | ||
13608 | + * | ||
13609 | + * In blocking mode, wait (interruptibly but with no timeout) on the buffer wait | ||
13610 | + * queue using the same condition. | ||
13611 | + */ | ||
13612 | +static int isp_video_buffer_wait(struct isp_video_buffer *buf, int nonblocking) | ||
13613 | +{ | ||
13614 | + if (nonblocking) { | ||
13615 | + return (buf->state != ISP_BUF_STATE_QUEUED && | ||
13616 | + buf->state != ISP_BUF_STATE_ACTIVE) | ||
13617 | + ? 0 : -EAGAIN; | ||
13618 | + } | ||
13619 | + | ||
13620 | + return wait_event_interruptible(buf->wait, | ||
13621 | + buf->state != ISP_BUF_STATE_QUEUED && | ||
13622 | + buf->state != ISP_BUF_STATE_ACTIVE); | ||
13623 | +} | ||
13624 | + | ||
13625 | +/* ----------------------------------------------------------------------------- | ||
13626 | + * Queue management | ||
13627 | + */ | ||
13628 | + | ||
13629 | +/* | ||
13630 | + * isp_video_queue_free - Free video buffers memory | ||
13631 | + * | ||
13632 | + * Buffers can only be freed if the queue isn't streaming and if no buffer is | ||
13633 | + * mapped to userspace. Return -EBUSY if those conditions aren't statisfied. | ||
13634 | + * | ||
13635 | + * This function must be called with the queue lock held. | ||
13636 | + */ | ||
13637 | +static int isp_video_queue_free(struct isp_video_queue *queue) | ||
13638 | +{ | ||
13639 | + unsigned int i; | ||
13640 | + | ||
13641 | + if (queue->streaming) | ||
13642 | + return -EBUSY; | ||
13643 | + | ||
13644 | + for (i = 0; i < queue->count; ++i) { | ||
13645 | + if (queue->buffers[i]->vma_use_count != 0) | ||
13646 | + return -EBUSY; | ||
13647 | + } | ||
13648 | + | ||
13649 | + for (i = 0; i < queue->count; ++i) { | ||
13650 | + struct isp_video_buffer *buf = queue->buffers[i]; | ||
13651 | + | ||
13652 | + isp_video_buffer_cleanup(buf); | ||
13653 | + | ||
13654 | + vfree(buf->vaddr); | ||
13655 | + buf->vaddr = NULL; | ||
13656 | + | ||
13657 | + kfree(buf); | ||
13658 | + queue->buffers[i] = NULL; | ||
13659 | + } | ||
13660 | + | ||
13661 | + INIT_LIST_HEAD(&queue->queue); | ||
13662 | + queue->count = 0; | ||
13663 | + return 0; | ||
13664 | +} | ||
13665 | + | ||
13666 | +/* | ||
13667 | + * isp_video_queue_alloc - Allocate video buffers memory | ||
13668 | + * | ||
13669 | + * This function must be called with the queue lock held. | ||
13670 | + */ | ||
13671 | +static int isp_video_queue_alloc(struct isp_video_queue *queue, | ||
13672 | + unsigned int nbuffers, | ||
13673 | + unsigned int size, enum v4l2_memory memory) | ||
13674 | +{ | ||
13675 | + struct isp_video_buffer *buf; | ||
13676 | + unsigned int i; | ||
13677 | + void *mem; | ||
13678 | + int ret; | ||
13679 | + | ||
13680 | + /* Start by freeing the buffers. */ | ||
13681 | + ret = isp_video_queue_free(queue); | ||
13682 | + if (ret < 0) | ||
13683 | + return ret; | ||
13684 | + | ||
13685 | + /* Bail out of no buffers should be allocated. */ | ||
13686 | + if (nbuffers == 0) | ||
13687 | + return 0; | ||
13688 | + | ||
13689 | + /* Initialize the allocated buffers. */ | ||
13690 | + for (i = 0; i < nbuffers; ++i) { | ||
13691 | + buf = kzalloc(queue->bufsize, GFP_KERNEL); | ||
13692 | + if (buf == NULL) | ||
13693 | + break; | ||
13694 | + | ||
13695 | + if (memory == V4L2_MEMORY_MMAP) { | ||
13696 | + /* Allocate video buffers memory for mmap mode. Align | ||
13697 | + * the size to the page size. | ||
13698 | + */ | ||
13699 | + mem = vmalloc_32_user(PAGE_ALIGN(size)); | ||
13700 | + if (mem == NULL) { | ||
13701 | + kfree(buf); | ||
13702 | + break; | ||
13703 | + } | ||
13704 | + | ||
13705 | + buf->vbuf.m.offset = i * PAGE_ALIGN(size); | ||
13706 | + buf->vaddr = mem; | ||
13707 | + } | ||
13708 | + | ||
13709 | + buf->vbuf.index = i; | ||
13710 | + buf->vbuf.length = size; | ||
13711 | + buf->vbuf.type = queue->type; | ||
13712 | + buf->vbuf.field = V4L2_FIELD_NONE; | ||
13713 | + buf->vbuf.memory = memory; | ||
13714 | + | ||
13715 | + buf->queue = queue; | ||
13716 | + init_waitqueue_head(&buf->wait); | ||
13717 | + | ||
13718 | + queue->buffers[i] = buf; | ||
13719 | + } | ||
13720 | + | ||
13721 | + if (i == 0) | ||
13722 | + return -ENOMEM; | ||
13723 | + | ||
13724 | + queue->count = i; | ||
13725 | + return nbuffers; | ||
13726 | +} | ||
13727 | + | ||
13728 | +/** | ||
13729 | + * omap3isp_video_queue_cleanup - Clean up the video buffers queue | ||
13730 | + * @queue: Video buffers queue | ||
13731 | + * | ||
13732 | + * Free all allocated resources and clean up the video buffers queue. The queue | ||
13733 | + * must not be busy (no ongoing video stream) and buffers must have been | ||
13734 | + * unmapped. | ||
13735 | + * | ||
13736 | + * Return 0 on success or -EBUSY if the queue is busy or buffers haven't been | ||
13737 | + * unmapped. | ||
13738 | + */ | ||
13739 | +int omap3isp_video_queue_cleanup(struct isp_video_queue *queue) | ||
13740 | +{ | ||
13741 | + return isp_video_queue_free(queue); | ||
13742 | +} | ||
13743 | + | ||
13744 | +/** | ||
13745 | + * omap3isp_video_queue_init - Initialize the video buffers queue | ||
13746 | + * @queue: Video buffers queue | ||
13747 | + * @type: V4L2 buffer type (capture or output) | ||
13748 | + * @ops: Driver-specific queue operations | ||
13749 | + * @dev: Device used for DMA operations | ||
13750 | + * @bufsize: Size of the driver-specific buffer structure | ||
13751 | + * | ||
13752 | + * Initialize the video buffers queue with the supplied parameters. | ||
13753 | + * | ||
13754 | + * The queue type must be one of V4L2_BUF_TYPE_VIDEO_CAPTURE or | ||
13755 | + * V4L2_BUF_TYPE_VIDEO_OUTPUT. Other buffer types are not supported yet. | ||
13756 | + * | ||
13757 | + * Buffer objects will be allocated using the given buffer size to allow room | ||
13758 | + * for driver-specific fields. Driver-specific buffer structures must start | ||
13759 | + * with a struct isp_video_buffer field. Drivers with no driver-specific buffer | ||
13760 | + * structure must pass the size of the isp_video_buffer structure in the bufsize | ||
13761 | + * parameter. | ||
13762 | + * | ||
13763 | + * Return 0 on success. | ||
13764 | + */ | ||
13765 | +int omap3isp_video_queue_init(struct isp_video_queue *queue, | ||
13766 | + enum v4l2_buf_type type, | ||
13767 | + const struct isp_video_queue_operations *ops, | ||
13768 | + struct device *dev, unsigned int bufsize) | ||
13769 | +{ | ||
13770 | + INIT_LIST_HEAD(&queue->queue); | ||
13771 | + mutex_init(&queue->lock); | ||
13772 | + spin_lock_init(&queue->irqlock); | ||
13773 | + | ||
13774 | + queue->type = type; | ||
13775 | + queue->ops = ops; | ||
13776 | + queue->dev = dev; | ||
13777 | + queue->bufsize = bufsize; | ||
13778 | + | ||
13779 | + return 0; | ||
13780 | +} | ||
13781 | + | ||
13782 | +/* ----------------------------------------------------------------------------- | ||
13783 | + * V4L2 operations | ||
13784 | + */ | ||
13785 | + | ||
13786 | +/** | ||
13787 | + * omap3isp_video_queue_reqbufs - Allocate video buffers memory | ||
13788 | + * | ||
13789 | + * This function is intended to be used as a VIDIOC_REQBUFS ioctl handler. It | ||
13790 | + * allocated video buffer objects and, for MMAP buffers, buffer memory. | ||
13791 | + * | ||
13792 | + * If the number of buffers is 0, all buffers are freed and the function returns | ||
13793 | + * without performing any allocation. | ||
13794 | + * | ||
13795 | + * If the number of buffers is not 0, currently allocated buffers (if any) are | ||
13796 | + * freed and the requested number of buffers are allocated. Depending on | ||
13797 | + * driver-specific requirements and on memory availability, a number of buffer | ||
13798 | + * smaller or bigger than requested can be allocated. This isn't considered as | ||
13799 | + * an error. | ||
13800 | + * | ||
13801 | + * Return 0 on success or one of the following error codes: | ||
13802 | + * | ||
13803 | + * -EINVAL if the buffer type or index are invalid | ||
13804 | + * -EBUSY if the queue is busy (streaming or buffers mapped) | ||
13805 | + * -ENOMEM if the buffers can't be allocated due to an out-of-memory condition | ||
13806 | + */ | ||
13807 | +int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue, | ||
13808 | + struct v4l2_requestbuffers *rb) | ||
13809 | +{ | ||
13810 | + unsigned int nbuffers = rb->count; | ||
13811 | + unsigned int size; | ||
13812 | + int ret; | ||
13813 | + | ||
13814 | + if (rb->type != queue->type) | ||
13815 | + return -EINVAL; | ||
13816 | + | ||
13817 | + queue->ops->queue_prepare(queue, &nbuffers, &size); | ||
13818 | + if (size == 0) | ||
13819 | + return -EINVAL; | ||
13820 | + | ||
13821 | + nbuffers = min_t(unsigned int, nbuffers, ISP_VIDEO_MAX_BUFFERS); | ||
13822 | + | ||
13823 | + mutex_lock(&queue->lock); | ||
13824 | + | ||
13825 | + ret = isp_video_queue_alloc(queue, nbuffers, size, rb->memory); | ||
13826 | + if (ret < 0) | ||
13827 | + goto done; | ||
13828 | + | ||
13829 | + rb->count = ret; | ||
13830 | + ret = 0; | ||
13831 | + | ||
13832 | +done: | ||
13833 | + mutex_unlock(&queue->lock); | ||
13834 | + return ret; | ||
13835 | +} | ||
13836 | + | ||
13837 | +/** | ||
13838 | + * omap3isp_video_queue_querybuf - Query the status of a buffer in a queue | ||
13839 | + * | ||
13840 | + * This function is intended to be used as a VIDIOC_QUERYBUF ioctl handler. It | ||
13841 | + * returns the status of a given video buffer. | ||
13842 | + * | ||
13843 | + * Return 0 on success or -EINVAL if the buffer type or index are invalid. | ||
13844 | + */ | ||
13845 | +int omap3isp_video_queue_querybuf(struct isp_video_queue *queue, | ||
13846 | + struct v4l2_buffer *vbuf) | ||
13847 | +{ | ||
13848 | + struct isp_video_buffer *buf; | ||
13849 | + int ret = 0; | ||
13850 | + | ||
13851 | + if (vbuf->type != queue->type) | ||
13852 | + return -EINVAL; | ||
13853 | + | ||
13854 | + mutex_lock(&queue->lock); | ||
13855 | + | ||
13856 | + if (vbuf->index >= queue->count) { | ||
13857 | + ret = -EINVAL; | ||
13858 | + goto done; | ||
13859 | + } | ||
13860 | + | ||
13861 | + buf = queue->buffers[vbuf->index]; | ||
13862 | + isp_video_buffer_query(buf, vbuf); | ||
13863 | + | ||
13864 | +done: | ||
13865 | + mutex_unlock(&queue->lock); | ||
13866 | + return ret; | ||
13867 | +} | ||
13868 | + | ||
13869 | +/** | ||
13870 | + * omap3isp_video_queue_qbuf - Queue a buffer | ||
13871 | + * | ||
13872 | + * This function is intended to be used as a VIDIOC_QBUF ioctl handler. | ||
13873 | + * | ||
13874 | + * The v4l2_buffer structure passed from userspace is first sanity tested. If | ||
13875 | + * sane, the buffer is then processed and added to the main queue and, if the | ||
13876 | + * queue is streaming, to the IRQ queue. | ||
13877 | + * | ||
13878 | + * Before being enqueued, USERPTR buffers are checked for address changes. If | ||
13879 | + * the buffer has a different userspace address, the old memory area is unlocked | ||
13880 | + * and the new memory area is locked. | ||
13881 | + */ | ||
13882 | +int omap3isp_video_queue_qbuf(struct isp_video_queue *queue, | ||
13883 | + struct v4l2_buffer *vbuf) | ||
13884 | +{ | ||
13885 | + struct isp_video_buffer *buf; | ||
13886 | + unsigned long flags; | ||
13887 | + int ret = -EINVAL; | ||
13888 | + | ||
13889 | + if (vbuf->type != queue->type) | ||
13890 | + goto done; | ||
13891 | + | ||
13892 | + mutex_lock(&queue->lock); | ||
13893 | + | ||
13894 | + if (vbuf->index >= queue->count) | ||
13895 | + goto done; | ||
13896 | + | ||
13897 | + buf = queue->buffers[vbuf->index]; | ||
13898 | + | ||
13899 | + if (vbuf->memory != buf->vbuf.memory) | ||
13900 | + goto done; | ||
13901 | + | ||
13902 | + if (buf->state != ISP_BUF_STATE_IDLE) | ||
13903 | + goto done; | ||
13904 | + | ||
13905 | + if (vbuf->memory == V4L2_MEMORY_USERPTR && | ||
13906 | + vbuf->m.userptr != buf->vbuf.m.userptr) { | ||
13907 | + isp_video_buffer_cleanup(buf); | ||
13908 | + buf->vbuf.m.userptr = vbuf->m.userptr; | ||
13909 | + buf->prepared = 0; | ||
13910 | + } | ||
13911 | + | ||
13912 | + if (!buf->prepared) { | ||
13913 | + ret = isp_video_buffer_prepare(buf); | ||
13914 | + if (ret < 0) | ||
13915 | + goto done; | ||
13916 | + buf->prepared = 1; | ||
13917 | + } | ||
13918 | + | ||
13919 | + isp_video_buffer_cache_sync(buf); | ||
13920 | + | ||
13921 | + buf->state = ISP_BUF_STATE_QUEUED; | ||
13922 | + list_add_tail(&buf->stream, &queue->queue); | ||
13923 | + | ||
13924 | + if (queue->streaming) { | ||
13925 | + spin_lock_irqsave(&queue->irqlock, flags); | ||
13926 | + queue->ops->buffer_queue(buf); | ||
13927 | + spin_unlock_irqrestore(&queue->irqlock, flags); | ||
13928 | + } | ||
13929 | + | ||
13930 | + ret = 0; | ||
13931 | + | ||
13932 | +done: | ||
13933 | + mutex_unlock(&queue->lock); | ||
13934 | + return ret; | ||
13935 | +} | ||
13936 | + | ||
13937 | +/** | ||
13938 | + * omap3isp_video_queue_dqbuf - Dequeue a buffer | ||
13939 | + * | ||
13940 | + * This function is intended to be used as a VIDIOC_DQBUF ioctl handler. | ||
13941 | + * | ||
13942 | + * The v4l2_buffer structure passed from userspace is first sanity tested. If | ||
13943 | + * sane, the buffer is then processed and added to the main queue and, if the | ||
13944 | + * queue is streaming, to the IRQ queue. | ||
13945 | + * | ||
13946 | + * Before being enqueued, USERPTR buffers are checked for address changes. If | ||
13947 | + * the buffer has a different userspace address, the old memory area is unlocked | ||
13948 | + * and the new memory area is locked. | ||
13949 | + */ | ||
13950 | +int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue, | ||
13951 | + struct v4l2_buffer *vbuf, int nonblocking) | ||
13952 | +{ | ||
13953 | + struct isp_video_buffer *buf; | ||
13954 | + int ret; | ||
13955 | + | ||
13956 | + if (vbuf->type != queue->type) | ||
13957 | + return -EINVAL; | ||
13958 | + | ||
13959 | + mutex_lock(&queue->lock); | ||
13960 | + | ||
13961 | + if (list_empty(&queue->queue)) { | ||
13962 | + ret = -EINVAL; | ||
13963 | + goto done; | ||
13964 | + } | ||
13965 | + | ||
13966 | + buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream); | ||
13967 | + ret = isp_video_buffer_wait(buf, nonblocking); | ||
13968 | + if (ret < 0) | ||
13969 | + goto done; | ||
13970 | + | ||
13971 | + list_del(&buf->stream); | ||
13972 | + | ||
13973 | + isp_video_buffer_query(buf, vbuf); | ||
13974 | + buf->state = ISP_BUF_STATE_IDLE; | ||
13975 | + vbuf->flags &= ~V4L2_BUF_FLAG_QUEUED; | ||
13976 | + | ||
13977 | +done: | ||
13978 | + mutex_unlock(&queue->lock); | ||
13979 | + return ret; | ||
13980 | +} | ||
13981 | + | ||
13982 | +/** | ||
13983 | + * omap3isp_video_queue_streamon - Start streaming | ||
13984 | + * | ||
13985 | + * This function is intended to be used as a VIDIOC_STREAMON ioctl handler. It | ||
13986 | + * starts streaming on the queue and calls the buffer_queue operation for all | ||
13987 | + * queued buffers. | ||
13988 | + * | ||
13989 | + * Return 0 on success. | ||
13990 | + */ | ||
13991 | +int omap3isp_video_queue_streamon(struct isp_video_queue *queue) | ||
13992 | +{ | ||
13993 | + struct isp_video_buffer *buf; | ||
13994 | + unsigned long flags; | ||
13995 | + | ||
13996 | + mutex_lock(&queue->lock); | ||
13997 | + | ||
13998 | + if (queue->streaming) | ||
13999 | + goto done; | ||
14000 | + | ||
14001 | + queue->streaming = 1; | ||
14002 | + | ||
14003 | + spin_lock_irqsave(&queue->irqlock, flags); | ||
14004 | + list_for_each_entry(buf, &queue->queue, stream) | ||
14005 | + queue->ops->buffer_queue(buf); | ||
14006 | + spin_unlock_irqrestore(&queue->irqlock, flags); | ||
14007 | + | ||
14008 | +done: | ||
14009 | + mutex_unlock(&queue->lock); | ||
14010 | + return 0; | ||
14011 | +} | ||
14012 | + | ||
14013 | +/** | ||
14014 | + * omap3isp_video_queue_streamoff - Stop streaming | ||
14015 | + * | ||
14016 | + * This function is intended to be used as a VIDIOC_STREAMOFF ioctl handler. It | ||
14017 | + * stops streaming on the queue and wakes up all the buffers. | ||
14018 | + * | ||
14019 | + * Drivers must stop the hardware and synchronize with interrupt handlers and/or | ||
14020 | + * delayed works before calling this function to make sure no buffer will be | ||
14021 | + * touched by the driver and/or hardware. | ||
14022 | + */ | ||
14023 | +void omap3isp_video_queue_streamoff(struct isp_video_queue *queue) | ||
14024 | +{ | ||
14025 | + struct isp_video_buffer *buf; | ||
14026 | + unsigned long flags; | ||
14027 | + unsigned int i; | ||
14028 | + | ||
14029 | + mutex_lock(&queue->lock); | ||
14030 | + | ||
14031 | + if (!queue->streaming) | ||
14032 | + goto done; | ||
14033 | + | ||
14034 | + queue->streaming = 0; | ||
14035 | + | ||
14036 | + spin_lock_irqsave(&queue->irqlock, flags); | ||
14037 | + for (i = 0; i < queue->count; ++i) { | ||
14038 | + buf = queue->buffers[i]; | ||
14039 | + | ||
14040 | + if (buf->state == ISP_BUF_STATE_ACTIVE) | ||
14041 | + wake_up(&buf->wait); | ||
14042 | + | ||
14043 | + buf->state = ISP_BUF_STATE_IDLE; | ||
14044 | + } | ||
14045 | + spin_unlock_irqrestore(&queue->irqlock, flags); | ||
14046 | + | ||
14047 | + INIT_LIST_HEAD(&queue->queue); | ||
14048 | + | ||
14049 | +done: | ||
14050 | + mutex_unlock(&queue->lock); | ||
14051 | +} | ||
14052 | + | ||
14053 | +/** | ||
14054 | + * omap3isp_video_queue_discard_done - Discard all buffers marked as DONE | ||
14055 | + * | ||
14056 | + * This function is intended to be used with suspend/resume operations. It | ||
14057 | + * discards all 'done' buffers as they would be too old to be requested after | ||
14058 | + * resume. | ||
14059 | + * | ||
14060 | + * Drivers must stop the hardware and synchronize with interrupt handlers and/or | ||
14061 | + * delayed works before calling this function to make sure no buffer will be | ||
14062 | + * touched by the driver and/or hardware. | ||
14063 | + */ | ||
14064 | +void omap3isp_video_queue_discard_done(struct isp_video_queue *queue) | ||
14065 | +{ | ||
14066 | + struct isp_video_buffer *buf; | ||
14067 | + unsigned int i; | ||
14068 | + | ||
14069 | + mutex_lock(&queue->lock); | ||
14070 | + | ||
14071 | + if (!queue->streaming) | ||
14072 | + goto done; | ||
14073 | + | ||
14074 | + for (i = 0; i < queue->count; ++i) { | ||
14075 | + buf = queue->buffers[i]; | ||
14076 | + | ||
14077 | + if (buf->state == ISP_BUF_STATE_DONE) | ||
14078 | + buf->state = ISP_BUF_STATE_ERROR; | ||
14079 | + } | ||
14080 | + | ||
14081 | +done: | ||
14082 | + mutex_unlock(&queue->lock); | ||
14083 | +} | ||
14084 | + | ||
14085 | +static void isp_video_queue_vm_open(struct vm_area_struct *vma) | ||
14086 | +{ | ||
14087 | + struct isp_video_buffer *buf = vma->vm_private_data; | ||
14088 | + | ||
14089 | + buf->vma_use_count++; | ||
14090 | +} | ||
14091 | + | ||
14092 | +static void isp_video_queue_vm_close(struct vm_area_struct *vma) | ||
14093 | +{ | ||
14094 | + struct isp_video_buffer *buf = vma->vm_private_data; | ||
14095 | + | ||
14096 | + buf->vma_use_count--; | ||
14097 | +} | ||
14098 | + | ||
14099 | +static const struct vm_operations_struct isp_video_queue_vm_ops = { | ||
14100 | + .open = isp_video_queue_vm_open, | ||
14101 | + .close = isp_video_queue_vm_close, | ||
14102 | +}; | ||
14103 | + | ||
14104 | +/** | ||
14105 | + * omap3isp_video_queue_mmap - Map buffers to userspace | ||
14106 | + * | ||
14107 | + * This function is intended to be used as an mmap() file operation handler. It | ||
14108 | + * maps a buffer to userspace based on the VMA offset. | ||
14109 | + * | ||
14110 | + * Only buffers of memory type MMAP are supported. | ||
14111 | + */ | ||
14112 | +int omap3isp_video_queue_mmap(struct isp_video_queue *queue, | ||
14113 | + struct vm_area_struct *vma) | ||
14114 | +{ | ||
14115 | + struct isp_video_buffer *uninitialized_var(buf); | ||
14116 | + unsigned long size; | ||
14117 | + unsigned int i; | ||
14118 | + int ret = 0; | ||
14119 | + | ||
14120 | + mutex_lock(&queue->lock); | ||
14121 | + | ||
14122 | + for (i = 0; i < queue->count; ++i) { | ||
14123 | + buf = queue->buffers[i]; | ||
14124 | + if ((buf->vbuf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) | ||
14125 | + break; | ||
14126 | + } | ||
14127 | + | ||
14128 | + if (i == queue->count) { | ||
14129 | + ret = -EINVAL; | ||
14130 | + goto done; | ||
14131 | + } | ||
14132 | + | ||
14133 | + size = vma->vm_end - vma->vm_start; | ||
14134 | + | ||
14135 | + if (buf->vbuf.memory != V4L2_MEMORY_MMAP || | ||
14136 | + size != PAGE_ALIGN(buf->vbuf.length)) { | ||
14137 | + ret = -EINVAL; | ||
14138 | + goto done; | ||
14139 | + } | ||
14140 | + | ||
14141 | + ret = remap_vmalloc_range(vma, buf->vaddr, 0); | ||
14142 | + if (ret < 0) | ||
14143 | + goto done; | ||
14144 | + | ||
14145 | + vma->vm_ops = &isp_video_queue_vm_ops; | ||
14146 | + vma->vm_private_data = buf; | ||
14147 | + isp_video_queue_vm_open(vma); | ||
14148 | + | ||
14149 | +done: | ||
14150 | + mutex_unlock(&queue->lock); | ||
14151 | + return ret; | ||
14152 | +} | ||
14153 | + | ||
14154 | +/** | ||
14155 | + * omap3isp_video_queue_poll - Poll video queue state | ||
14156 | + * | ||
14157 | + * This function is intended to be used as a poll() file operation handler. It | ||
14158 | + * polls the state of the video buffer at the front of the queue and returns an | ||
14159 | + * events mask. | ||
14160 | + * | ||
14161 | + * If no buffer is present at the front of the queue, POLLERR is returned. | ||
14162 | + */ | ||
14163 | +unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue, | ||
14164 | + struct file *file, poll_table *wait) | ||
14165 | +{ | ||
14166 | + struct isp_video_buffer *buf; | ||
14167 | + unsigned int mask = 0; | ||
14168 | + | ||
14169 | + mutex_lock(&queue->lock); | ||
14170 | + if (list_empty(&queue->queue)) { | ||
14171 | + mask |= POLLERR; | ||
14172 | + goto done; | ||
14173 | + } | ||
14174 | + buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream); | ||
14175 | + | ||
14176 | + poll_wait(file, &buf->wait, wait); | ||
14177 | + if (buf->state == ISP_BUF_STATE_DONE || | ||
14178 | + buf->state == ISP_BUF_STATE_ERROR) { | ||
14179 | + if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
14180 | + mask |= POLLIN | POLLRDNORM; | ||
14181 | + else | ||
14182 | + mask |= POLLOUT | POLLWRNORM; | ||
14183 | + } | ||
14184 | + | ||
14185 | +done: | ||
14186 | + mutex_unlock(&queue->lock); | ||
14187 | + return mask; | ||
14188 | +} | ||
14189 | diff --git a/drivers/media/video/isp/ispqueue.h b/drivers/media/video/isp/ispqueue.h | ||
14190 | new file mode 100644 | ||
14191 | index 0000000..f05aba3 | ||
14192 | --- /dev/null | ||
14193 | +++ b/drivers/media/video/isp/ispqueue.h | ||
14194 | @@ -0,0 +1,185 @@ | ||
14195 | +/* | ||
14196 | + * ispqueue.h | ||
14197 | + * | ||
14198 | + * TI OMAP3 ISP - Video buffers queue handling | ||
14199 | + * | ||
14200 | + * Copyright (C) 2010 Nokia Corporation | ||
14201 | + * | ||
14202 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
14203 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
14204 | + * | ||
14205 | + * This program is free software; you can redistribute it and/or modify | ||
14206 | + * it under the terms of the GNU General Public License version 2 as | ||
14207 | + * published by the Free Software Foundation. | ||
14208 | + * | ||
14209 | + * This program is distributed in the hope that it will be useful, but | ||
14210 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14211 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14212 | + * General Public License for more details. | ||
14213 | + * | ||
14214 | + * You should have received a copy of the GNU General Public License | ||
14215 | + * along with this program; if not, write to the Free Software | ||
14216 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
14217 | + * 02110-1301 USA | ||
14218 | + */ | ||
14219 | + | ||
14220 | +#ifndef OMAP3_ISP_QUEUE_H | ||
14221 | +#define OMAP3_ISP_QUEUE_H | ||
14222 | + | ||
14223 | +#include <linux/kernel.h> | ||
14224 | +#include <linux/list.h> | ||
14225 | +#include <linux/mutex.h> | ||
14226 | +#include <linux/videodev2.h> | ||
14227 | +#include <linux/wait.h> | ||
14228 | + | ||
14229 | +struct isp_video_queue; | ||
14230 | +struct page; | ||
14231 | +struct scatterlist; | ||
14232 | + | ||
14233 | +#define ISP_VIDEO_MAX_BUFFERS 16 | ||
14234 | + | ||
14235 | +/** | ||
14236 | + * enum isp_video_buffer_state - ISP video buffer state | ||
14237 | + * @ISP_BUF_STATE_IDLE: The buffer is under userspace control (dequeued | ||
14238 | + * or not queued yet). | ||
14239 | + * @ISP_BUF_STATE_QUEUED: The buffer has been queued but isn't used by the | ||
14240 | + * device yet. | ||
14241 | + * @ISP_BUF_STATE_ACTIVE: The buffer is in use for an active video transfer. | ||
14242 | + * @ISP_BUF_STATE_ERROR: The device is done with the buffer and an error | ||
14243 | + * occured. For capture device the buffer likely contains corrupted data or | ||
14244 | + * no data at all. | ||
14245 | + * @ISP_BUF_STATE_DONE: The device is done with the buffer and no error occured. | ||
14246 | + * For capture devices the buffer contains valid data. | ||
14247 | + */ | ||
14248 | +enum isp_video_buffer_state { | ||
14249 | + ISP_BUF_STATE_IDLE, | ||
14250 | + ISP_BUF_STATE_QUEUED, | ||
14251 | + ISP_BUF_STATE_ACTIVE, | ||
14252 | + ISP_BUF_STATE_ERROR, | ||
14253 | + ISP_BUF_STATE_DONE, | ||
14254 | +}; | ||
14255 | + | ||
14256 | +/** | ||
14257 | + * struct isp_video_buffer - ISP video buffer | ||
14258 | + * @vma_use_count: Number of times the buffer is mmap'ed to userspace | ||
14259 | + * @stream: List head for insertion into main queue | ||
14260 | + * @queue: ISP buffers queue this buffer belongs to | ||
14261 | + * @prepared: Whether the buffer has been prepared | ||
14262 | + * @vaddr: Memory virtual address (for kernel buffers) | ||
14263 | + * @vm_flags: Buffer VMA flags (for userspace buffers) | ||
14264 | + * @offset: Offset inside the first page (for userspace buffers) | ||
14265 | + * @npages: Number of pages (for userspace buffers) | ||
14266 | + * @pages: Pages table (for userspace non-VM_PFNMAP buffers) | ||
14267 | + * @paddr: Memory physical address (for userspace VM_PFNMAP buffers) | ||
14268 | + * @sglen: Number of elements in the scatter list (for non-VM_PFNMAP buffers) | ||
14269 | + * @sglist: Scatter list (for non-VM_PFNMAP buffers) | ||
14270 | + * @vbuf: V4L2 buffer | ||
14271 | + * @irqlist: List head for insertion into IRQ queue | ||
14272 | + * @state: Current buffer state | ||
14273 | + * @wait: Wait queue to signal buffer completion | ||
14274 | + */ | ||
14275 | +struct isp_video_buffer { | ||
14276 | + unsigned long vma_use_count; | ||
14277 | + struct list_head stream; | ||
14278 | + struct isp_video_queue *queue; | ||
14279 | + unsigned int prepared:1; | ||
14280 | + | ||
14281 | + /* For kernel buffers. */ | ||
14282 | + void *vaddr; | ||
14283 | + | ||
14284 | + /* For userspace buffers. */ | ||
14285 | + unsigned long vm_flags; | ||
14286 | + unsigned long offset; | ||
14287 | + unsigned int npages; | ||
14288 | + struct page **pages; | ||
14289 | + dma_addr_t paddr; | ||
14290 | + | ||
14291 | + /* For all buffers except VM_PFNMAP. */ | ||
14292 | + unsigned int sglen; | ||
14293 | + struct scatterlist *sglist; | ||
14294 | + | ||
14295 | + /* Touched by the interrupt handler. */ | ||
14296 | + struct v4l2_buffer vbuf; | ||
14297 | + struct list_head irqlist; | ||
14298 | + enum isp_video_buffer_state state; | ||
14299 | + wait_queue_head_t wait; | ||
14300 | +}; | ||
14301 | + | ||
14302 | +#define to_isp_video_buffer(vb) container_of(vb, struct isp_video_buffer, vb) | ||
14303 | + | ||
14304 | +/** | ||
14305 | + * struct isp_video_queue_operations - Driver-specific operations | ||
14306 | + * @queue_prepare: Called before allocating buffers. Drivers should clamp the | ||
14307 | + * number of buffers according to their requirements, and must return the | ||
14308 | + * buffer size in bytes. | ||
14309 | + * @buffer_prepare: Called the first time a buffer is queued, or after changing | ||
14310 | + * the userspace memory address for a USERPTR buffer, with the queue lock | ||
14311 | + * held. Drivers should perform device-specific buffer preparation (such as | ||
14312 | + * mapping the buffer memory in an IOMMU). This operation is optional. | ||
14313 | + * @buffer_queue: Called when a buffer is being added to the queue with the | ||
14314 | + * queue irqlock spinlock held. | ||
14315 | + * @buffer_cleanup: Called before freeing buffers, or before changing the | ||
14316 | + * userspace memory address for a USERPTR buffer, with the queue lock held. | ||
14317 | + * Drivers must perform cleanup operations required to undo the | ||
14318 | + * buffer_prepare call. This operation is optional. | ||
14319 | + */ | ||
14320 | +struct isp_video_queue_operations { | ||
14321 | + void (*queue_prepare)(struct isp_video_queue *queue, | ||
14322 | + unsigned int *nbuffers, unsigned int *size); | ||
14323 | + int (*buffer_prepare)(struct isp_video_buffer *buf); | ||
14324 | + void (*buffer_queue)(struct isp_video_buffer *buf); | ||
14325 | + void (*buffer_cleanup)(struct isp_video_buffer *buf); | ||
14326 | +}; | ||
14327 | + | ||
14328 | +/** | ||
14329 | + * struct isp_video_queue - ISP video buffers queue | ||
14330 | + * @type: Type of video buffers handled by this queue | ||
14331 | + * @ops: Queue operations | ||
14332 | + * @dev: Device used for DMA operations | ||
14333 | + * @bufsize: Size of a driver-specific buffer object | ||
14334 | + * @count: Number of currently allocated buffers | ||
14335 | + * @buffers: ISP video buffers | ||
14336 | + * @lock: Mutex to protect access to the buffers, main queue and state | ||
14337 | + * @irqlock: Spinlock to protect access to the IRQ queue | ||
14338 | + * @streaming: Queue state, indicates whether the queue is streaming | ||
14339 | + * @queue: List of all queued buffers | ||
14340 | + */ | ||
14341 | +struct isp_video_queue { | ||
14342 | + enum v4l2_buf_type type; | ||
14343 | + const struct isp_video_queue_operations *ops; | ||
14344 | + struct device *dev; | ||
14345 | + unsigned int bufsize; | ||
14346 | + | ||
14347 | + unsigned int count; | ||
14348 | + struct isp_video_buffer *buffers[ISP_VIDEO_MAX_BUFFERS]; | ||
14349 | + struct mutex lock; | ||
14350 | + spinlock_t irqlock; | ||
14351 | + | ||
14352 | + unsigned int streaming:1; | ||
14353 | + | ||
14354 | + struct list_head queue; | ||
14355 | +}; | ||
14356 | + | ||
14357 | +int omap3isp_video_queue_cleanup(struct isp_video_queue *queue); | ||
14358 | +int omap3isp_video_queue_init(struct isp_video_queue *queue, | ||
14359 | + enum v4l2_buf_type type, | ||
14360 | + const struct isp_video_queue_operations *ops, | ||
14361 | + struct device *dev, unsigned int bufsize); | ||
14362 | + | ||
14363 | +int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue, | ||
14364 | + struct v4l2_requestbuffers *rb); | ||
14365 | +int omap3isp_video_queue_querybuf(struct isp_video_queue *queue, | ||
14366 | + struct v4l2_buffer *vbuf); | ||
14367 | +int omap3isp_video_queue_qbuf(struct isp_video_queue *queue, | ||
14368 | + struct v4l2_buffer *vbuf); | ||
14369 | +int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue, | ||
14370 | + struct v4l2_buffer *vbuf, int nonblocking); | ||
14371 | +int omap3isp_video_queue_streamon(struct isp_video_queue *queue); | ||
14372 | +void omap3isp_video_queue_streamoff(struct isp_video_queue *queue); | ||
14373 | +void omap3isp_video_queue_discard_done(struct isp_video_queue *queue); | ||
14374 | +int omap3isp_video_queue_mmap(struct isp_video_queue *queue, | ||
14375 | + struct vm_area_struct *vma); | ||
14376 | +unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue, | ||
14377 | + struct file *file, poll_table *wait); | ||
14378 | + | ||
14379 | +#endif /* OMAP3_ISP_QUEUE_H */ | ||
14380 | diff --git a/drivers/media/video/isp/ispreg.h b/drivers/media/video/isp/ispreg.h | ||
14381 | new file mode 100644 | ||
14382 | index 0000000..e78c7e3 | ||
14383 | --- /dev/null | ||
14384 | +++ b/drivers/media/video/isp/ispreg.h | ||
14385 | @@ -0,0 +1,1589 @@ | ||
14386 | +/* | ||
14387 | + * ispreg.h | ||
14388 | + * | ||
14389 | + * TI OMAP3 ISP - Registers definitions | ||
14390 | + * | ||
14391 | + * Copyright (C) 2010 Nokia Corporation | ||
14392 | + * Copyright (C) 2009 Texas Instruments, Inc | ||
14393 | + * | ||
14394 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
14395 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
14396 | + * | ||
14397 | + * This program is free software; you can redistribute it and/or modify | ||
14398 | + * it under the terms of the GNU General Public License version 2 as | ||
14399 | + * published by the Free Software Foundation. | ||
14400 | + * | ||
14401 | + * This program is distributed in the hope that it will be useful, but | ||
14402 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14403 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14404 | + * General Public License for more details. | ||
14405 | + * | ||
14406 | + * You should have received a copy of the GNU General Public License | ||
14407 | + * along with this program; if not, write to the Free Software | ||
14408 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
14409 | + * 02110-1301 USA | ||
14410 | + */ | ||
14411 | + | ||
14412 | +#ifndef OMAP3_ISP_REG_H | ||
14413 | +#define OMAP3_ISP_REG_H | ||
14414 | + | ||
14415 | +#include <plat/omap34xx.h> | ||
14416 | + | ||
14417 | + | ||
14418 | +#define CM_CAM_MCLK_HZ 172800000 /* Hz */ | ||
14419 | + | ||
14420 | +/* ISP Submodules offset */ | ||
14421 | + | ||
14422 | +#define OMAP3ISP_REG_BASE OMAP3430_ISP_BASE | ||
14423 | +#define OMAP3ISP_REG(offset) (OMAP3ISP_REG_BASE + (offset)) | ||
14424 | + | ||
14425 | +#define OMAP3ISP_CCP2_REG_OFFSET 0x0400 | ||
14426 | +#define OMAP3ISP_CCP2_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14427 | + OMAP3ISP_CCP2_REG_OFFSET) | ||
14428 | +#define OMAP3ISP_CCP2_REG(offset) (OMAP3ISP_CCP2_REG_BASE + (offset)) | ||
14429 | + | ||
14430 | +#define OMAP3ISP_CCDC_REG_OFFSET 0x0600 | ||
14431 | +#define OMAP3ISP_CCDC_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14432 | + OMAP3ISP_CCDC_REG_OFFSET) | ||
14433 | +#define OMAP3ISP_CCDC_REG(offset) (OMAP3ISP_CCDC_REG_BASE + (offset)) | ||
14434 | + | ||
14435 | +#define OMAP3ISP_HIST_REG_OFFSET 0x0A00 | ||
14436 | +#define OMAP3ISP_HIST_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14437 | + OMAP3ISP_HIST_REG_OFFSET) | ||
14438 | +#define OMAP3ISP_HIST_REG(offset) (OMAP3ISP_HIST_REG_BASE + (offset)) | ||
14439 | + | ||
14440 | +#define OMAP3ISP_H3A_REG_OFFSET 0x0C00 | ||
14441 | +#define OMAP3ISP_H3A_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14442 | + OMAP3ISP_H3A_REG_OFFSET) | ||
14443 | +#define OMAP3ISP_H3A_REG(offset) (OMAP3ISP_H3A_REG_BASE + (offset)) | ||
14444 | + | ||
14445 | +#define OMAP3ISP_PREV_REG_OFFSET 0x0E00 | ||
14446 | +#define OMAP3ISP_PREV_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14447 | + OMAP3ISP_PREV_REG_OFFSET) | ||
14448 | +#define OMAP3ISP_PREV_REG(offset) (OMAP3ISP_PREV_REG_BASE + (offset)) | ||
14449 | + | ||
14450 | +#define OMAP3ISP_RESZ_REG_OFFSET 0x1000 | ||
14451 | +#define OMAP3ISP_RESZ_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14452 | + OMAP3ISP_RESZ_REG_OFFSET) | ||
14453 | +#define OMAP3ISP_RESZ_REG(offset) (OMAP3ISP_RESZ_REG_BASE + (offset)) | ||
14454 | + | ||
14455 | +#define OMAP3ISP_SBL_REG_OFFSET 0x1200 | ||
14456 | +#define OMAP3ISP_SBL_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14457 | + OMAP3ISP_SBL_REG_OFFSET) | ||
14458 | +#define OMAP3ISP_SBL_REG(offset) (OMAP3ISP_SBL_REG_BASE + (offset)) | ||
14459 | + | ||
14460 | +#define OMAP3ISP_CSI2A_REGS1_REG_OFFSET 0x1800 | ||
14461 | +#define OMAP3ISP_CSI2A_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14462 | + OMAP3ISP_CSI2A_REGS1_REG_OFFSET) | ||
14463 | +#define OMAP3ISP_CSI2A_REGS1_REG(offset) \ | ||
14464 | + (OMAP3ISP_CSI2A_REGS1_REG_BASE + (offset)) | ||
14465 | + | ||
14466 | +#define OMAP3ISP_CSIPHY2_REG_OFFSET 0x1970 | ||
14467 | +#define OMAP3ISP_CSIPHY2_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14468 | + OMAP3ISP_CSIPHY2_REG_OFFSET) | ||
14469 | +#define OMAP3ISP_CSIPHY2_REG(offset) (OMAP3ISP_CSIPHY2_REG_BASE + (offset)) | ||
14470 | + | ||
14471 | +#define OMAP3ISP_CSI2A_REGS2_REG_OFFSET 0x19C0 | ||
14472 | +#define OMAP3ISP_CSI2A_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14473 | + OMAP3ISP_CSI2A_REGS2_REG_OFFSET) | ||
14474 | +#define OMAP3ISP_CSI2A_REGS2_REG(offset) \ | ||
14475 | + (OMAP3ISP_CSI2A_REGS2_REG_BASE + (offset)) | ||
14476 | + | ||
14477 | +#define OMAP3ISP_CSI2C_REGS1_REG_OFFSET 0x1C00 | ||
14478 | +#define OMAP3ISP_CSI2C_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14479 | + OMAP3ISP_CSI2C_REGS1_REG_OFFSET) | ||
14480 | +#define OMAP3ISP_CSI2C_REGS1_REG(offset) \ | ||
14481 | + (OMAP3ISP_CSI2C_REGS1_REG_BASE + (offset)) | ||
14482 | + | ||
14483 | +#define OMAP3ISP_CSIPHY1_REG_OFFSET 0x1D70 | ||
14484 | +#define OMAP3ISP_CSIPHY1_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14485 | + OMAP3ISP_CSIPHY1_REG_OFFSET) | ||
14486 | +#define OMAP3ISP_CSIPHY1_REG(offset) (OMAP3ISP_CSIPHY1_REG_BASE + (offset)) | ||
14487 | + | ||
14488 | +#define OMAP3ISP_CSI2C_REGS2_REG_OFFSET 0x1DC0 | ||
14489 | +#define OMAP3ISP_CSI2C_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
14490 | + OMAP3ISP_CSI2C_REGS2_REG_OFFSET) | ||
14491 | +#define OMAP3ISP_CSI2C_REGS2_REG(offset) \ | ||
14492 | + (OMAP3ISP_CSI2C_REGS2_REG_BASE + (offset)) | ||
14493 | + | ||
14494 | +/* ISP module register offset */ | ||
14495 | + | ||
14496 | +#define ISP_REVISION (0x000) | ||
14497 | +#define ISP_SYSCONFIG (0x004) | ||
14498 | +#define ISP_SYSSTATUS (0x008) | ||
14499 | +#define ISP_IRQ0ENABLE (0x00C) | ||
14500 | +#define ISP_IRQ0STATUS (0x010) | ||
14501 | +#define ISP_IRQ1ENABLE (0x014) | ||
14502 | +#define ISP_IRQ1STATUS (0x018) | ||
14503 | +#define ISP_TCTRL_GRESET_LENGTH (0x030) | ||
14504 | +#define ISP_TCTRL_PSTRB_REPLAY (0x034) | ||
14505 | +#define ISP_CTRL (0x040) | ||
14506 | +#define ISP_SECURE (0x044) | ||
14507 | +#define ISP_TCTRL_CTRL (0x050) | ||
14508 | +#define ISP_TCTRL_FRAME (0x054) | ||
14509 | +#define ISP_TCTRL_PSTRB_DELAY (0x058) | ||
14510 | +#define ISP_TCTRL_STRB_DELAY (0x05C) | ||
14511 | +#define ISP_TCTRL_SHUT_DELAY (0x060) | ||
14512 | +#define ISP_TCTRL_PSTRB_LENGTH (0x064) | ||
14513 | +#define ISP_TCTRL_STRB_LENGTH (0x068) | ||
14514 | +#define ISP_TCTRL_SHUT_LENGTH (0x06C) | ||
14515 | +#define ISP_PING_PONG_ADDR (0x070) | ||
14516 | +#define ISP_PING_PONG_MEM_RANGE (0x074) | ||
14517 | +#define ISP_PING_PONG_BUF_SIZE (0x078) | ||
14518 | + | ||
14519 | +/* CCP2 receiver registers */ | ||
14520 | + | ||
14521 | +#define ISPCCP2_REVISION (0x000) | ||
14522 | +#define ISPCCP2_SYSCONFIG (0x004) | ||
14523 | +#define ISPCCP2_SYSCONFIG_SOFT_RESET (1 << 1) | ||
14524 | +#define ISPCCP2_SYSCONFIG_AUTO_IDLE 0x1 | ||
14525 | +#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT 12 | ||
14526 | +#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_FORCE \ | ||
14527 | + (0x0 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
14528 | +#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_NO \ | ||
14529 | + (0x1 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
14530 | +#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SMART \ | ||
14531 | + (0x2 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
14532 | +#define ISPCCP2_SYSSTATUS (0x008) | ||
14533 | +#define ISPCCP2_SYSSTATUS_RESET_DONE (1 << 0) | ||
14534 | +#define ISPCCP2_LC01_IRQENABLE (0x00C) | ||
14535 | +#define ISPCCP2_LC01_IRQSTATUS (0x010) | ||
14536 | +#define ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ (1 << 11) | ||
14537 | +#define ISPCCP2_LC01_IRQSTATUS_LC0_LE_IRQ (1 << 10) | ||
14538 | +#define ISPCCP2_LC01_IRQSTATUS_LC0_LS_IRQ (1 << 9) | ||
14539 | +#define ISPCCP2_LC01_IRQSTATUS_LC0_FE_IRQ (1 << 8) | ||
14540 | +#define ISPCCP2_LC01_IRQSTATUS_LC0_COUNT_IRQ (1 << 7) | ||
14541 | +#define ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ (1 << 5) | ||
14542 | +#define ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ (1 << 4) | ||
14543 | +#define ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ (1 << 3) | ||
14544 | +#define ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ (1 << 2) | ||
14545 | +#define ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ (1 << 1) | ||
14546 | +#define ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ (1 << 0) | ||
14547 | + | ||
14548 | +#define ISPCCP2_LC23_IRQENABLE (0x014) | ||
14549 | +#define ISPCCP2_LC23_IRQSTATUS (0x018) | ||
14550 | +#define ISPCCP2_LCM_IRQENABLE (0x02C) | ||
14551 | +#define ISPCCP2_LCM_IRQSTATUS_EOF_IRQ (1 << 0) | ||
14552 | +#define ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ (1 << 1) | ||
14553 | +#define ISPCCP2_LCM_IRQSTATUS (0x030) | ||
14554 | +#define ISPCCP2_CTRL (0x040) | ||
14555 | +#define ISPCCP2_CTRL_IF_EN (1 << 0) | ||
14556 | +#define ISPCCP2_CTRL_PHY_SEL (1 << 1) | ||
14557 | +#define ISPCCP2_CTRL_PHY_SEL_CLOCK (0 << 1) | ||
14558 | +#define ISPCCP2_CTRL_PHY_SEL_STROBE (1 << 1) | ||
14559 | +#define ISPCCP2_CTRL_PHY_SEL_MASK 0x1 | ||
14560 | +#define ISPCCP2_CTRL_PHY_SEL_SHIFT 1 | ||
14561 | +#define ISPCCP2_CTRL_IO_OUT_SEL (1 << 2) | ||
14562 | +#define ISPCCP2_CTRL_MODE (1 << 4) | ||
14563 | +#define ISPCCP2_CTRL_VP_CLK_FORCE_ON (1 << 9) | ||
14564 | +#define ISPCCP2_CTRL_INV (1 << 10) | ||
14565 | +#define ISPCCP2_CTRL_INV_MASK 0x1 | ||
14566 | +#define ISPCCP2_CTRL_INV_SHIFT 10 | ||
14567 | +#define ISPCCP2_CTRL_VP_ONLY_EN (1 << 11) | ||
14568 | +#define ISPCCP2_CTRL_VP_CLK_POL (1 << 12) | ||
14569 | +#define ISPCCP2_CTRL_VPCLK_DIV_SHIFT 15 | ||
14570 | +#define ISPCCP2_CTRL_VPCLK_DIV_MASK 0x1ffff /* [31:15] */ | ||
14571 | +#define ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT 8 /* 3430 bits */ | ||
14572 | +#define ISPCCP2_CTRL_VP_OUT_CTRL_MASK 0x3 /* 3430 bits */ | ||
14573 | +#define ISPCCP2_DBG (0x044) | ||
14574 | +#define ISPCCP2_GNQ (0x048) | ||
14575 | +#define ISPCCP2_LCx_CTRL(x) ((0x050)+0x30*(x)) | ||
14576 | +#define ISPCCP2_LCx_CTRL_CHAN_EN (1 << 0) | ||
14577 | +#define ISPCCP2_LCx_CTRL_CRC_EN (1 << 19) | ||
14578 | +#define ISPCCP2_LCx_CTRL_CRC_MASK 0x1 | ||
14579 | +#define ISPCCP2_LCx_CTRL_CRC_SHIFT 2 | ||
14580 | +#define ISPCCP2_LCx_CTRL_CRC_SHIFT_15_0 19 | ||
14581 | +#define ISPCCP2_LCx_CTRL_REGION_EN (1 << 1) | ||
14582 | +#define ISPCCP2_LCx_CTRL_REGION_MASK 0x1 | ||
14583 | +#define ISPCCP2_LCx_CTRL_REGION_SHIFT 1 | ||
14584 | +#define ISPCCP2_LCx_CTRL_FORMAT_MASK_15_0 0x3f | ||
14585 | +#define ISPCCP2_LCx_CTRL_FORMAT_SHIFT_15_0 0x2 | ||
14586 | +#define ISPCCP2_LCx_CTRL_FORMAT_MASK 0x1f | ||
14587 | +#define ISPCCP2_LCx_CTRL_FORMAT_SHIFT 0x3 | ||
14588 | +#define ISPCCP2_LCx_CODE(x) ((0x054)+0x30*(x)) | ||
14589 | +#define ISPCCP2_LCx_STAT_START(x) ((0x058)+0x30*(x)) | ||
14590 | +#define ISPCCP2_LCx_STAT_SIZE(x) ((0x05C)+0x30*(x)) | ||
14591 | +#define ISPCCP2_LCx_SOF_ADDR(x) ((0x060)+0x30*(x)) | ||
14592 | +#define ISPCCP2_LCx_EOF_ADDR(x) ((0x064)+0x30*(x)) | ||
14593 | +#define ISPCCP2_LCx_DAT_START(x) ((0x068)+0x30*(x)) | ||
14594 | +#define ISPCCP2_LCx_DAT_SIZE(x) ((0x06C)+0x30*(x)) | ||
14595 | +#define ISPCCP2_LCx_DAT_MASK 0xFFF | ||
14596 | +#define ISPCCP2_LCx_DAT_SHIFT 16 | ||
14597 | +#define ISPCCP2_LCx_DAT_PING_ADDR(x) ((0x070)+0x30*(x)) | ||
14598 | +#define ISPCCP2_LCx_DAT_PONG_ADDR(x) ((0x074)+0x30*(x)) | ||
14599 | +#define ISPCCP2_LCx_DAT_OFST(x) ((0x078)+0x30*(x)) | ||
14600 | +#define ISPCCP2_LCM_CTRL (0x1D0) | ||
14601 | +#define ISPCCP2_LCM_CTRL_CHAN_EN (1 << 0) | ||
14602 | +#define ISPCCP2_LCM_CTRL_DST_PORT (1 << 2) | ||
14603 | +#define ISPCCP2_LCM_CTRL_DST_PORT_SHIFT 2 | ||
14604 | +#define ISPCCP2_LCM_CTRL_READ_THROTTLE_SHIFT 3 | ||
14605 | +#define ISPCCP2_LCM_CTRL_READ_THROTTLE_MASK 0x11 | ||
14606 | +#define ISPCCP2_LCM_CTRL_BURST_SIZE_SHIFT 5 | ||
14607 | +#define ISPCCP2_LCM_CTRL_BURST_SIZE_MASK 0x7 | ||
14608 | +#define ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT 16 | ||
14609 | +#define ISPCCP2_LCM_CTRL_SRC_FORMAT_MASK 0x7 | ||
14610 | +#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_SHIFT 20 | ||
14611 | +#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_MASK 0x3 | ||
14612 | +#define ISPCCP2_LCM_CTRL_SRC_DPCM_PRED (1 << 22) | ||
14613 | +#define ISPCCP2_LCM_CTRL_SRC_PACK (1 << 23) | ||
14614 | +#define ISPCCP2_LCM_CTRL_DST_FORMAT_SHIFT 24 | ||
14615 | +#define ISPCCP2_LCM_CTRL_DST_FORMAT_MASK 0x7 | ||
14616 | +#define ISPCCP2_LCM_VSIZE (0x1D4) | ||
14617 | +#define ISPCCP2_LCM_VSIZE_SHIFT 16 | ||
14618 | +#define ISPCCP2_LCM_HSIZE (0x1D8) | ||
14619 | +#define ISPCCP2_LCM_HSIZE_SHIFT 16 | ||
14620 | +#define ISPCCP2_LCM_PREFETCH (0x1DC) | ||
14621 | +#define ISPCCP2_LCM_PREFETCH_SHIFT 3 | ||
14622 | +#define ISPCCP2_LCM_SRC_ADDR (0x1E0) | ||
14623 | +#define ISPCCP2_LCM_SRC_OFST (0x1E4) | ||
14624 | +#define ISPCCP2_LCM_DST_ADDR (0x1E8) | ||
14625 | +#define ISPCCP2_LCM_DST_OFST (0x1EC) | ||
14626 | + | ||
14627 | +/* CCDC module register offset */ | ||
14628 | + | ||
14629 | +#define ISPCCDC_PID (0x000) | ||
14630 | +#define ISPCCDC_PCR (0x004) | ||
14631 | +#define ISPCCDC_SYN_MODE (0x008) | ||
14632 | +#define ISPCCDC_HD_VD_WID (0x00C) | ||
14633 | +#define ISPCCDC_PIX_LINES (0x010) | ||
14634 | +#define ISPCCDC_HORZ_INFO (0x014) | ||
14635 | +#define ISPCCDC_VERT_START (0x018) | ||
14636 | +#define ISPCCDC_VERT_LINES (0x01C) | ||
14637 | +#define ISPCCDC_CULLING (0x020) | ||
14638 | +#define ISPCCDC_HSIZE_OFF (0x024) | ||
14639 | +#define ISPCCDC_SDOFST (0x028) | ||
14640 | +#define ISPCCDC_SDR_ADDR (0x02C) | ||
14641 | +#define ISPCCDC_CLAMP (0x030) | ||
14642 | +#define ISPCCDC_DCSUB (0x034) | ||
14643 | +#define ISPCCDC_COLPTN (0x038) | ||
14644 | +#define ISPCCDC_BLKCMP (0x03C) | ||
14645 | +#define ISPCCDC_FPC (0x040) | ||
14646 | +#define ISPCCDC_FPC_ADDR (0x044) | ||
14647 | +#define ISPCCDC_VDINT (0x048) | ||
14648 | +#define ISPCCDC_ALAW (0x04C) | ||
14649 | +#define ISPCCDC_REC656IF (0x050) | ||
14650 | +#define ISPCCDC_CFG (0x054) | ||
14651 | +#define ISPCCDC_FMTCFG (0x058) | ||
14652 | +#define ISPCCDC_FMT_HORZ (0x05C) | ||
14653 | +#define ISPCCDC_FMT_VERT (0x060) | ||
14654 | +#define ISPCCDC_FMT_ADDR0 (0x064) | ||
14655 | +#define ISPCCDC_FMT_ADDR1 (0x068) | ||
14656 | +#define ISPCCDC_FMT_ADDR2 (0x06C) | ||
14657 | +#define ISPCCDC_FMT_ADDR3 (0x070) | ||
14658 | +#define ISPCCDC_FMT_ADDR4 (0x074) | ||
14659 | +#define ISPCCDC_FMT_ADDR5 (0x078) | ||
14660 | +#define ISPCCDC_FMT_ADDR6 (0x07C) | ||
14661 | +#define ISPCCDC_FMT_ADDR7 (0x080) | ||
14662 | +#define ISPCCDC_PRGEVEN0 (0x084) | ||
14663 | +#define ISPCCDC_PRGEVEN1 (0x088) | ||
14664 | +#define ISPCCDC_PRGODD0 (0x08C) | ||
14665 | +#define ISPCCDC_PRGODD1 (0x090) | ||
14666 | +#define ISPCCDC_VP_OUT (0x094) | ||
14667 | + | ||
14668 | +#define ISPCCDC_LSC_CONFIG (0x098) | ||
14669 | +#define ISPCCDC_LSC_INITIAL (0x09C) | ||
14670 | +#define ISPCCDC_LSC_TABLE_BASE (0x0A0) | ||
14671 | +#define ISPCCDC_LSC_TABLE_OFFSET (0x0A4) | ||
14672 | + | ||
14673 | +/* SBL */ | ||
14674 | +#define ISPSBL_PCR 0x4 | ||
14675 | +#define ISPSBL_PCR_H3A_AEAWB_WBL_OVF (1 << 16) | ||
14676 | +#define ISPSBL_PCR_H3A_AF_WBL_OVF (1 << 17) | ||
14677 | +#define ISPSBL_PCR_RSZ4_WBL_OVF (1 << 18) | ||
14678 | +#define ISPSBL_PCR_RSZ3_WBL_OVF (1 << 19) | ||
14679 | +#define ISPSBL_PCR_RSZ2_WBL_OVF (1 << 20) | ||
14680 | +#define ISPSBL_PCR_RSZ1_WBL_OVF (1 << 21) | ||
14681 | +#define ISPSBL_PCR_PRV_WBL_OVF (1 << 22) | ||
14682 | +#define ISPSBL_PCR_CCDC_WBL_OVF (1 << 23) | ||
14683 | +#define ISPSBL_PCR_CCDCPRV_2_RSZ_OVF (1 << 24) | ||
14684 | +#define ISPSBL_PCR_CSIA_WBL_OVF (1 << 25) | ||
14685 | +#define ISPSBL_PCR_CSIB_WBL_OVF (1 << 26) | ||
14686 | +#define ISPSBL_CCDC_WR_0 (0x028) | ||
14687 | +#define ISPSBL_CCDC_WR_0_DATA_READY (1 << 21) | ||
14688 | +#define ISPSBL_CCDC_WR_1 (0x02C) | ||
14689 | +#define ISPSBL_CCDC_WR_2 (0x030) | ||
14690 | +#define ISPSBL_CCDC_WR_3 (0x034) | ||
14691 | + | ||
14692 | +#define ISPSBL_SDR_REQ_EXP 0xF8 | ||
14693 | +#define ISPSBL_SDR_REQ_HIST_EXP_SHIFT 0 | ||
14694 | +#define ISPSBL_SDR_REQ_HIST_EXP_MASK (0x3FF) | ||
14695 | +#define ISPSBL_SDR_REQ_RSZ_EXP_SHIFT 10 | ||
14696 | +#define ISPSBL_SDR_REQ_RSZ_EXP_MASK (0x3FF << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT) | ||
14697 | +#define ISPSBL_SDR_REQ_PRV_EXP_SHIFT 20 | ||
14698 | +#define ISPSBL_SDR_REQ_PRV_EXP_MASK (0x3FF << ISPSBL_SDR_REQ_PRV_EXP_SHIFT) | ||
14699 | + | ||
14700 | +/* Histogram registers */ | ||
14701 | +#define ISPHIST_PID (0x000) | ||
14702 | +#define ISPHIST_PCR (0x004) | ||
14703 | +#define ISPHIST_CNT (0x008) | ||
14704 | +#define ISPHIST_WB_GAIN (0x00C) | ||
14705 | +#define ISPHIST_R0_HORZ (0x010) | ||
14706 | +#define ISPHIST_R0_VERT (0x014) | ||
14707 | +#define ISPHIST_R1_HORZ (0x018) | ||
14708 | +#define ISPHIST_R1_VERT (0x01C) | ||
14709 | +#define ISPHIST_R2_HORZ (0x020) | ||
14710 | +#define ISPHIST_R2_VERT (0x024) | ||
14711 | +#define ISPHIST_R3_HORZ (0x028) | ||
14712 | +#define ISPHIST_R3_VERT (0x02C) | ||
14713 | +#define ISPHIST_ADDR (0x030) | ||
14714 | +#define ISPHIST_DATA (0x034) | ||
14715 | +#define ISPHIST_RADD (0x038) | ||
14716 | +#define ISPHIST_RADD_OFF (0x03C) | ||
14717 | +#define ISPHIST_H_V_INFO (0x040) | ||
14718 | + | ||
14719 | +/* H3A module registers */ | ||
14720 | +#define ISPH3A_PID (0x000) | ||
14721 | +#define ISPH3A_PCR (0x004) | ||
14722 | +#define ISPH3A_AEWWIN1 (0x04C) | ||
14723 | +#define ISPH3A_AEWINSTART (0x050) | ||
14724 | +#define ISPH3A_AEWINBLK (0x054) | ||
14725 | +#define ISPH3A_AEWSUBWIN (0x058) | ||
14726 | +#define ISPH3A_AEWBUFST (0x05C) | ||
14727 | +#define ISPH3A_AFPAX1 (0x008) | ||
14728 | +#define ISPH3A_AFPAX2 (0x00C) | ||
14729 | +#define ISPH3A_AFPAXSTART (0x010) | ||
14730 | +#define ISPH3A_AFIIRSH (0x014) | ||
14731 | +#define ISPH3A_AFBUFST (0x018) | ||
14732 | +#define ISPH3A_AFCOEF010 (0x01C) | ||
14733 | +#define ISPH3A_AFCOEF032 (0x020) | ||
14734 | +#define ISPH3A_AFCOEF054 (0x024) | ||
14735 | +#define ISPH3A_AFCOEF076 (0x028) | ||
14736 | +#define ISPH3A_AFCOEF098 (0x02C) | ||
14737 | +#define ISPH3A_AFCOEF0010 (0x030) | ||
14738 | +#define ISPH3A_AFCOEF110 (0x034) | ||
14739 | +#define ISPH3A_AFCOEF132 (0x038) | ||
14740 | +#define ISPH3A_AFCOEF154 (0x03C) | ||
14741 | +#define ISPH3A_AFCOEF176 (0x040) | ||
14742 | +#define ISPH3A_AFCOEF198 (0x044) | ||
14743 | +#define ISPH3A_AFCOEF1010 (0x048) | ||
14744 | + | ||
14745 | +#define ISPPRV_PCR (0x004) | ||
14746 | +#define ISPPRV_HORZ_INFO (0x008) | ||
14747 | +#define ISPPRV_VERT_INFO (0x00C) | ||
14748 | +#define ISPPRV_RSDR_ADDR (0x010) | ||
14749 | +#define ISPPRV_RADR_OFFSET (0x014) | ||
14750 | +#define ISPPRV_DSDR_ADDR (0x018) | ||
14751 | +#define ISPPRV_DRKF_OFFSET (0x01C) | ||
14752 | +#define ISPPRV_WSDR_ADDR (0x020) | ||
14753 | +#define ISPPRV_WADD_OFFSET (0x024) | ||
14754 | +#define ISPPRV_AVE (0x028) | ||
14755 | +#define ISPPRV_HMED (0x02C) | ||
14756 | +#define ISPPRV_NF (0x030) | ||
14757 | +#define ISPPRV_WB_DGAIN (0x034) | ||
14758 | +#define ISPPRV_WBGAIN (0x038) | ||
14759 | +#define ISPPRV_WBSEL (0x03C) | ||
14760 | +#define ISPPRV_CFA (0x040) | ||
14761 | +#define ISPPRV_BLKADJOFF (0x044) | ||
14762 | +#define ISPPRV_RGB_MAT1 (0x048) | ||
14763 | +#define ISPPRV_RGB_MAT2 (0x04C) | ||
14764 | +#define ISPPRV_RGB_MAT3 (0x050) | ||
14765 | +#define ISPPRV_RGB_MAT4 (0x054) | ||
14766 | +#define ISPPRV_RGB_MAT5 (0x058) | ||
14767 | +#define ISPPRV_RGB_OFF1 (0x05C) | ||
14768 | +#define ISPPRV_RGB_OFF2 (0x060) | ||
14769 | +#define ISPPRV_CSC0 (0x064) | ||
14770 | +#define ISPPRV_CSC1 (0x068) | ||
14771 | +#define ISPPRV_CSC2 (0x06C) | ||
14772 | +#define ISPPRV_CSC_OFFSET (0x070) | ||
14773 | +#define ISPPRV_CNT_BRT (0x074) | ||
14774 | +#define ISPPRV_CSUP (0x078) | ||
14775 | +#define ISPPRV_SETUP_YC (0x07C) | ||
14776 | +#define ISPPRV_SET_TBL_ADDR (0x080) | ||
14777 | +#define ISPPRV_SET_TBL_DATA (0x084) | ||
14778 | +#define ISPPRV_CDC_THR0 (0x090) | ||
14779 | +#define ISPPRV_CDC_THR1 (ISPPRV_CDC_THR0 + (0x4)) | ||
14780 | +#define ISPPRV_CDC_THR2 (ISPPRV_CDC_THR0 + (0x4) * 2) | ||
14781 | +#define ISPPRV_CDC_THR3 (ISPPRV_CDC_THR0 + (0x4) * 3) | ||
14782 | + | ||
14783 | +#define ISPPRV_REDGAMMA_TABLE_ADDR 0x0000 | ||
14784 | +#define ISPPRV_GREENGAMMA_TABLE_ADDR 0x0400 | ||
14785 | +#define ISPPRV_BLUEGAMMA_TABLE_ADDR 0x0800 | ||
14786 | +#define ISPPRV_NF_TABLE_ADDR 0x0C00 | ||
14787 | +#define ISPPRV_YENH_TABLE_ADDR 0x1000 | ||
14788 | +#define ISPPRV_CFA_TABLE_ADDR 0x1400 | ||
14789 | + | ||
14790 | +#define ISPPRV_MAXOUTPUT_WIDTH 1280 | ||
14791 | +#define ISPPRV_MAXOUTPUT_WIDTH_ES2 3300 | ||
14792 | +#define ISPPRV_MAXOUTPUT_WIDTH_3630 4096 | ||
14793 | +#define ISPRSZ_MIN_OUTPUT 64 | ||
14794 | +#define ISPRSZ_MAX_OUTPUT 3312 | ||
14795 | + | ||
14796 | +/* Resizer module register offset */ | ||
14797 | +#define ISPRSZ_PID (0x000) | ||
14798 | +#define ISPRSZ_PCR (0x004) | ||
14799 | +#define ISPRSZ_CNT (0x008) | ||
14800 | +#define ISPRSZ_OUT_SIZE (0x00C) | ||
14801 | +#define ISPRSZ_IN_START (0x010) | ||
14802 | +#define ISPRSZ_IN_SIZE (0x014) | ||
14803 | +#define ISPRSZ_SDR_INADD (0x018) | ||
14804 | +#define ISPRSZ_SDR_INOFF (0x01C) | ||
14805 | +#define ISPRSZ_SDR_OUTADD (0x020) | ||
14806 | +#define ISPRSZ_SDR_OUTOFF (0x024) | ||
14807 | +#define ISPRSZ_HFILT10 (0x028) | ||
14808 | +#define ISPRSZ_HFILT32 (0x02C) | ||
14809 | +#define ISPRSZ_HFILT54 (0x030) | ||
14810 | +#define ISPRSZ_HFILT76 (0x034) | ||
14811 | +#define ISPRSZ_HFILT98 (0x038) | ||
14812 | +#define ISPRSZ_HFILT1110 (0x03C) | ||
14813 | +#define ISPRSZ_HFILT1312 (0x040) | ||
14814 | +#define ISPRSZ_HFILT1514 (0x044) | ||
14815 | +#define ISPRSZ_HFILT1716 (0x048) | ||
14816 | +#define ISPRSZ_HFILT1918 (0x04C) | ||
14817 | +#define ISPRSZ_HFILT2120 (0x050) | ||
14818 | +#define ISPRSZ_HFILT2322 (0x054) | ||
14819 | +#define ISPRSZ_HFILT2524 (0x058) | ||
14820 | +#define ISPRSZ_HFILT2726 (0x05C) | ||
14821 | +#define ISPRSZ_HFILT2928 (0x060) | ||
14822 | +#define ISPRSZ_HFILT3130 (0x064) | ||
14823 | +#define ISPRSZ_VFILT10 (0x068) | ||
14824 | +#define ISPRSZ_VFILT32 (0x06C) | ||
14825 | +#define ISPRSZ_VFILT54 (0x070) | ||
14826 | +#define ISPRSZ_VFILT76 (0x074) | ||
14827 | +#define ISPRSZ_VFILT98 (0x078) | ||
14828 | +#define ISPRSZ_VFILT1110 (0x07C) | ||
14829 | +#define ISPRSZ_VFILT1312 (0x080) | ||
14830 | +#define ISPRSZ_VFILT1514 (0x084) | ||
14831 | +#define ISPRSZ_VFILT1716 (0x088) | ||
14832 | +#define ISPRSZ_VFILT1918 (0x08C) | ||
14833 | +#define ISPRSZ_VFILT2120 (0x090) | ||
14834 | +#define ISPRSZ_VFILT2322 (0x094) | ||
14835 | +#define ISPRSZ_VFILT2524 (0x098) | ||
14836 | +#define ISPRSZ_VFILT2726 (0x09C) | ||
14837 | +#define ISPRSZ_VFILT2928 (0x0A0) | ||
14838 | +#define ISPRSZ_VFILT3130 (0x0A4) | ||
14839 | +#define ISPRSZ_YENH (0x0A8) | ||
14840 | + | ||
14841 | +#define ISP_INT_CLR 0xFF113F11 | ||
14842 | +#define ISPPRV_PCR_EN 1 | ||
14843 | +#define ISPPRV_PCR_BUSY (1 << 1) | ||
14844 | +#define ISPPRV_PCR_SOURCE (1 << 2) | ||
14845 | +#define ISPPRV_PCR_ONESHOT (1 << 3) | ||
14846 | +#define ISPPRV_PCR_WIDTH (1 << 4) | ||
14847 | +#define ISPPRV_PCR_INVALAW (1 << 5) | ||
14848 | +#define ISPPRV_PCR_DRKFEN (1 << 6) | ||
14849 | +#define ISPPRV_PCR_DRKFCAP (1 << 7) | ||
14850 | +#define ISPPRV_PCR_HMEDEN (1 << 8) | ||
14851 | +#define ISPPRV_PCR_NFEN (1 << 9) | ||
14852 | +#define ISPPRV_PCR_CFAEN (1 << 10) | ||
14853 | +#define ISPPRV_PCR_CFAFMT_SHIFT 11 | ||
14854 | +#define ISPPRV_PCR_CFAFMT_MASK 0x7800 | ||
14855 | +#define ISPPRV_PCR_CFAFMT_BAYER (0 << 11) | ||
14856 | +#define ISPPRV_PCR_CFAFMT_SONYVGA (1 << 11) | ||
14857 | +#define ISPPRV_PCR_CFAFMT_RGBFOVEON (2 << 11) | ||
14858 | +#define ISPPRV_PCR_CFAFMT_DNSPL (3 << 11) | ||
14859 | +#define ISPPRV_PCR_CFAFMT_HONEYCOMB (4 << 11) | ||
14860 | +#define ISPPRV_PCR_CFAFMT_RRGGBBFOVEON (5 << 11) | ||
14861 | +#define ISPPRV_PCR_YNENHEN (1 << 15) | ||
14862 | +#define ISPPRV_PCR_SUPEN (1 << 16) | ||
14863 | +#define ISPPRV_PCR_YCPOS_SHIFT 17 | ||
14864 | +#define ISPPRV_PCR_YCPOS_YCrYCb (0 << 17) | ||
14865 | +#define ISPPRV_PCR_YCPOS_YCbYCr (1 << 17) | ||
14866 | +#define ISPPRV_PCR_YCPOS_CbYCrY (2 << 17) | ||
14867 | +#define ISPPRV_PCR_YCPOS_CrYCbY (3 << 17) | ||
14868 | +#define ISPPRV_PCR_RSZPORT (1 << 19) | ||
14869 | +#define ISPPRV_PCR_SDRPORT (1 << 20) | ||
14870 | +#define ISPPRV_PCR_SCOMP_EN (1 << 21) | ||
14871 | +#define ISPPRV_PCR_SCOMP_SFT_SHIFT (22) | ||
14872 | +#define ISPPRV_PCR_SCOMP_SFT_MASK (7 << 22) | ||
14873 | +#define ISPPRV_PCR_GAMMA_BYPASS (1 << 26) | ||
14874 | +#define ISPPRV_PCR_DCOREN (1 << 27) | ||
14875 | +#define ISPPRV_PCR_DCCOUP (1 << 28) | ||
14876 | +#define ISPPRV_PCR_DRK_FAIL (1 << 31) | ||
14877 | + | ||
14878 | +#define ISPPRV_HORZ_INFO_EPH_SHIFT 0 | ||
14879 | +#define ISPPRV_HORZ_INFO_EPH_MASK 0x3fff | ||
14880 | +#define ISPPRV_HORZ_INFO_SPH_SHIFT 16 | ||
14881 | +#define ISPPRV_HORZ_INFO_SPH_MASK 0x3fff0 | ||
14882 | + | ||
14883 | +#define ISPPRV_VERT_INFO_ELV_SHIFT 0 | ||
14884 | +#define ISPPRV_VERT_INFO_ELV_MASK 0x3fff | ||
14885 | +#define ISPPRV_VERT_INFO_SLV_SHIFT 16 | ||
14886 | +#define ISPPRV_VERT_INFO_SLV_MASK 0x3fff0 | ||
14887 | + | ||
14888 | +#define ISPPRV_AVE_EVENDIST_SHIFT 2 | ||
14889 | +#define ISPPRV_AVE_EVENDIST_1 0x0 | ||
14890 | +#define ISPPRV_AVE_EVENDIST_2 0x1 | ||
14891 | +#define ISPPRV_AVE_EVENDIST_3 0x2 | ||
14892 | +#define ISPPRV_AVE_EVENDIST_4 0x3 | ||
14893 | +#define ISPPRV_AVE_ODDDIST_SHIFT 4 | ||
14894 | +#define ISPPRV_AVE_ODDDIST_1 0x0 | ||
14895 | +#define ISPPRV_AVE_ODDDIST_2 0x1 | ||
14896 | +#define ISPPRV_AVE_ODDDIST_3 0x2 | ||
14897 | +#define ISPPRV_AVE_ODDDIST_4 0x3 | ||
14898 | + | ||
14899 | +#define ISPPRV_HMED_THRESHOLD_SHIFT 0 | ||
14900 | +#define ISPPRV_HMED_EVENDIST (1 << 8) | ||
14901 | +#define ISPPRV_HMED_ODDDIST (1 << 9) | ||
14902 | + | ||
14903 | +#define ISPPRV_WBGAIN_COEF0_SHIFT 0 | ||
14904 | +#define ISPPRV_WBGAIN_COEF1_SHIFT 8 | ||
14905 | +#define ISPPRV_WBGAIN_COEF2_SHIFT 16 | ||
14906 | +#define ISPPRV_WBGAIN_COEF3_SHIFT 24 | ||
14907 | + | ||
14908 | +#define ISPPRV_WBSEL_COEF0 0x0 | ||
14909 | +#define ISPPRV_WBSEL_COEF1 0x1 | ||
14910 | +#define ISPPRV_WBSEL_COEF2 0x2 | ||
14911 | +#define ISPPRV_WBSEL_COEF3 0x3 | ||
14912 | + | ||
14913 | +#define ISPPRV_WBSEL_N0_0_SHIFT 0 | ||
14914 | +#define ISPPRV_WBSEL_N0_1_SHIFT 2 | ||
14915 | +#define ISPPRV_WBSEL_N0_2_SHIFT 4 | ||
14916 | +#define ISPPRV_WBSEL_N0_3_SHIFT 6 | ||
14917 | +#define ISPPRV_WBSEL_N1_0_SHIFT 8 | ||
14918 | +#define ISPPRV_WBSEL_N1_1_SHIFT 10 | ||
14919 | +#define ISPPRV_WBSEL_N1_2_SHIFT 12 | ||
14920 | +#define ISPPRV_WBSEL_N1_3_SHIFT 14 | ||
14921 | +#define ISPPRV_WBSEL_N2_0_SHIFT 16 | ||
14922 | +#define ISPPRV_WBSEL_N2_1_SHIFT 18 | ||
14923 | +#define ISPPRV_WBSEL_N2_2_SHIFT 20 | ||
14924 | +#define ISPPRV_WBSEL_N2_3_SHIFT 22 | ||
14925 | +#define ISPPRV_WBSEL_N3_0_SHIFT 24 | ||
14926 | +#define ISPPRV_WBSEL_N3_1_SHIFT 26 | ||
14927 | +#define ISPPRV_WBSEL_N3_2_SHIFT 28 | ||
14928 | +#define ISPPRV_WBSEL_N3_3_SHIFT 30 | ||
14929 | + | ||
14930 | +#define ISPPRV_CFA_GRADTH_HOR_SHIFT 0 | ||
14931 | +#define ISPPRV_CFA_GRADTH_VER_SHIFT 8 | ||
14932 | + | ||
14933 | +#define ISPPRV_BLKADJOFF_B_SHIFT 0 | ||
14934 | +#define ISPPRV_BLKADJOFF_G_SHIFT 8 | ||
14935 | +#define ISPPRV_BLKADJOFF_R_SHIFT 16 | ||
14936 | + | ||
14937 | +#define ISPPRV_RGB_MAT1_MTX_RR_SHIFT 0 | ||
14938 | +#define ISPPRV_RGB_MAT1_MTX_GR_SHIFT 16 | ||
14939 | + | ||
14940 | +#define ISPPRV_RGB_MAT2_MTX_BR_SHIFT 0 | ||
14941 | +#define ISPPRV_RGB_MAT2_MTX_RG_SHIFT 16 | ||
14942 | + | ||
14943 | +#define ISPPRV_RGB_MAT3_MTX_GG_SHIFT 0 | ||
14944 | +#define ISPPRV_RGB_MAT3_MTX_BG_SHIFT 16 | ||
14945 | + | ||
14946 | +#define ISPPRV_RGB_MAT4_MTX_RB_SHIFT 0 | ||
14947 | +#define ISPPRV_RGB_MAT4_MTX_GB_SHIFT 16 | ||
14948 | + | ||
14949 | +#define ISPPRV_RGB_MAT5_MTX_BB_SHIFT 0 | ||
14950 | + | ||
14951 | +#define ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT 0 | ||
14952 | +#define ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT 16 | ||
14953 | + | ||
14954 | +#define ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT 0 | ||
14955 | + | ||
14956 | +#define ISPPRV_CSC0_RY_SHIFT 0 | ||
14957 | +#define ISPPRV_CSC0_GY_SHIFT 10 | ||
14958 | +#define ISPPRV_CSC0_BY_SHIFT 20 | ||
14959 | + | ||
14960 | +#define ISPPRV_CSC1_RCB_SHIFT 0 | ||
14961 | +#define ISPPRV_CSC1_GCB_SHIFT 10 | ||
14962 | +#define ISPPRV_CSC1_BCB_SHIFT 20 | ||
14963 | + | ||
14964 | +#define ISPPRV_CSC2_RCR_SHIFT 0 | ||
14965 | +#define ISPPRV_CSC2_GCR_SHIFT 10 | ||
14966 | +#define ISPPRV_CSC2_BCR_SHIFT 20 | ||
14967 | + | ||
14968 | +#define ISPPRV_CSC_OFFSET_CR_SHIFT 0 | ||
14969 | +#define ISPPRV_CSC_OFFSET_CB_SHIFT 8 | ||
14970 | +#define ISPPRV_CSC_OFFSET_Y_SHIFT 16 | ||
14971 | + | ||
14972 | +#define ISPPRV_CNT_BRT_BRT_SHIFT 0 | ||
14973 | +#define ISPPRV_CNT_BRT_CNT_SHIFT 8 | ||
14974 | + | ||
14975 | +#define ISPPRV_CONTRAST_MAX 0x10 | ||
14976 | +#define ISPPRV_CONTRAST_MIN 0xFF | ||
14977 | +#define ISPPRV_BRIGHT_MIN 0x00 | ||
14978 | +#define ISPPRV_BRIGHT_MAX 0xFF | ||
14979 | + | ||
14980 | +#define ISPPRV_CSUP_CSUPG_SHIFT 0 | ||
14981 | +#define ISPPRV_CSUP_THRES_SHIFT 8 | ||
14982 | +#define ISPPRV_CSUP_HPYF_SHIFT 16 | ||
14983 | + | ||
14984 | +#define ISPPRV_SETUP_YC_MINC_SHIFT 0 | ||
14985 | +#define ISPPRV_SETUP_YC_MAXC_SHIFT 8 | ||
14986 | +#define ISPPRV_SETUP_YC_MINY_SHIFT 16 | ||
14987 | +#define ISPPRV_SETUP_YC_MAXY_SHIFT 24 | ||
14988 | +#define ISPPRV_YC_MAX 0xFF | ||
14989 | +#define ISPPRV_YC_MIN 0x0 | ||
14990 | + | ||
14991 | +/* Define bit fields within selected registers */ | ||
14992 | +#define ISP_REVISION_SHIFT 0 | ||
14993 | + | ||
14994 | +#define ISP_SYSCONFIG_AUTOIDLE (1 << 0) | ||
14995 | +#define ISP_SYSCONFIG_SOFTRESET (1 << 1) | ||
14996 | +#define ISP_SYSCONFIG_MIDLEMODE_SHIFT 12 | ||
14997 | +#define ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY 0x0 | ||
14998 | +#define ISP_SYSCONFIG_MIDLEMODE_NOSTANBY 0x1 | ||
14999 | +#define ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY 0x2 | ||
15000 | + | ||
15001 | +#define ISP_SYSSTATUS_RESETDONE 0 | ||
15002 | + | ||
15003 | +#define IRQ0ENABLE_CSIA_IRQ (1 << 0) | ||
15004 | +#define IRQ0ENABLE_CSIC_IRQ (1 << 1) | ||
15005 | +#define IRQ0ENABLE_CCP2_LCM_IRQ (1 << 3) | ||
15006 | +#define IRQ0ENABLE_CCP2_LC0_IRQ (1 << 4) | ||
15007 | +#define IRQ0ENABLE_CCP2_LC1_IRQ (1 << 5) | ||
15008 | +#define IRQ0ENABLE_CCP2_LC2_IRQ (1 << 6) | ||
15009 | +#define IRQ0ENABLE_CCP2_LC3_IRQ (1 << 7) | ||
15010 | +#define IRQ0ENABLE_CSIB_IRQ (IRQ0ENABLE_CCP2_LCM_IRQ | \ | ||
15011 | + IRQ0ENABLE_CCP2_LC0_IRQ | \ | ||
15012 | + IRQ0ENABLE_CCP2_LC1_IRQ | \ | ||
15013 | + IRQ0ENABLE_CCP2_LC2_IRQ | \ | ||
15014 | + IRQ0ENABLE_CCP2_LC3_IRQ) | ||
15015 | + | ||
15016 | +#define IRQ0ENABLE_CCDC_VD0_IRQ (1 << 8) | ||
15017 | +#define IRQ0ENABLE_CCDC_VD1_IRQ (1 << 9) | ||
15018 | +#define IRQ0ENABLE_CCDC_VD2_IRQ (1 << 10) | ||
15019 | +#define IRQ0ENABLE_CCDC_ERR_IRQ (1 << 11) | ||
15020 | +#define IRQ0ENABLE_H3A_AF_DONE_IRQ (1 << 12) | ||
15021 | +#define IRQ0ENABLE_H3A_AWB_DONE_IRQ (1 << 13) | ||
15022 | +#define IRQ0ENABLE_HIST_DONE_IRQ (1 << 16) | ||
15023 | +#define IRQ0ENABLE_CCDC_LSC_DONE_IRQ (1 << 17) | ||
15024 | +#define IRQ0ENABLE_CCDC_LSC_PREF_COMP_IRQ (1 << 18) | ||
15025 | +#define IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ (1 << 19) | ||
15026 | +#define IRQ0ENABLE_PRV_DONE_IRQ (1 << 20) | ||
15027 | +#define IRQ0ENABLE_RSZ_DONE_IRQ (1 << 24) | ||
15028 | +#define IRQ0ENABLE_OVF_IRQ (1 << 25) | ||
15029 | +#define IRQ0ENABLE_PING_IRQ (1 << 26) | ||
15030 | +#define IRQ0ENABLE_PONG_IRQ (1 << 27) | ||
15031 | +#define IRQ0ENABLE_MMU_ERR_IRQ (1 << 28) | ||
15032 | +#define IRQ0ENABLE_OCP_ERR_IRQ (1 << 29) | ||
15033 | +#define IRQ0ENABLE_SEC_ERR_IRQ (1 << 30) | ||
15034 | +#define IRQ0ENABLE_HS_VS_IRQ (1 << 31) | ||
15035 | + | ||
15036 | +#define IRQ0STATUS_CSIA_IRQ (1 << 0) | ||
15037 | +#define IRQ0STATUS_CSI2C_IRQ (1 << 1) | ||
15038 | +#define IRQ0STATUS_CCP2_LCM_IRQ (1 << 3) | ||
15039 | +#define IRQ0STATUS_CCP2_LC0_IRQ (1 << 4) | ||
15040 | +#define IRQ0STATUS_CSIB_IRQ (IRQ0STATUS_CCP2_LCM_IRQ | \ | ||
15041 | + IRQ0STATUS_CCP2_LC0_IRQ) | ||
15042 | + | ||
15043 | +#define IRQ0STATUS_CSIB_LC1_IRQ (1 << 5) | ||
15044 | +#define IRQ0STATUS_CSIB_LC2_IRQ (1 << 6) | ||
15045 | +#define IRQ0STATUS_CSIB_LC3_IRQ (1 << 7) | ||
15046 | +#define IRQ0STATUS_CCDC_VD0_IRQ (1 << 8) | ||
15047 | +#define IRQ0STATUS_CCDC_VD1_IRQ (1 << 9) | ||
15048 | +#define IRQ0STATUS_CCDC_VD2_IRQ (1 << 10) | ||
15049 | +#define IRQ0STATUS_CCDC_ERR_IRQ (1 << 11) | ||
15050 | +#define IRQ0STATUS_H3A_AF_DONE_IRQ (1 << 12) | ||
15051 | +#define IRQ0STATUS_H3A_AWB_DONE_IRQ (1 << 13) | ||
15052 | +#define IRQ0STATUS_HIST_DONE_IRQ (1 << 16) | ||
15053 | +#define IRQ0STATUS_CCDC_LSC_DONE_IRQ (1 << 17) | ||
15054 | +#define IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ (1 << 18) | ||
15055 | +#define IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ (1 << 19) | ||
15056 | +#define IRQ0STATUS_PRV_DONE_IRQ (1 << 20) | ||
15057 | +#define IRQ0STATUS_RSZ_DONE_IRQ (1 << 24) | ||
15058 | +#define IRQ0STATUS_OVF_IRQ (1 << 25) | ||
15059 | +#define IRQ0STATUS_PING_IRQ (1 << 26) | ||
15060 | +#define IRQ0STATUS_PONG_IRQ (1 << 27) | ||
15061 | +#define IRQ0STATUS_MMU_ERR_IRQ (1 << 28) | ||
15062 | +#define IRQ0STATUS_OCP_ERR_IRQ (1 << 29) | ||
15063 | +#define IRQ0STATUS_SEC_ERR_IRQ (1 << 30) | ||
15064 | +#define IRQ0STATUS_HS_VS_IRQ (1 << 31) | ||
15065 | + | ||
15066 | +#define TCTRL_GRESET_LEN 0 | ||
15067 | + | ||
15068 | +#define TCTRL_PSTRB_REPLAY_DELAY 0 | ||
15069 | +#define TCTRL_PSTRB_REPLAY_COUNTER_SHIFT 25 | ||
15070 | + | ||
15071 | +#define ISPCTRL_PAR_SER_CLK_SEL_PARALLEL 0x0 | ||
15072 | +#define ISPCTRL_PAR_SER_CLK_SEL_CSIA 0x1 | ||
15073 | +#define ISPCTRL_PAR_SER_CLK_SEL_CSIB 0x2 | ||
15074 | +#define ISPCTRL_PAR_SER_CLK_SEL_CSIC 0x3 | ||
15075 | +#define ISPCTRL_PAR_SER_CLK_SEL_MASK 0x3 | ||
15076 | + | ||
15077 | +#define ISPCTRL_PAR_BRIDGE_SHIFT 2 | ||
15078 | +#define ISPCTRL_PAR_BRIDGE_DISABLE (0x0 << 2) | ||
15079 | +#define ISPCTRL_PAR_BRIDGE_LENDIAN (0x2 << 2) | ||
15080 | +#define ISPCTRL_PAR_BRIDGE_BENDIAN (0x3 << 2) | ||
15081 | +#define ISPCTRL_PAR_BRIDGE_MASK (0x3 << 2) | ||
15082 | + | ||
15083 | +#define ISPCTRL_PAR_CLK_POL_SHIFT 4 | ||
15084 | +#define ISPCTRL_PAR_CLK_POL_INV (1 << 4) | ||
15085 | +#define ISPCTRL_PING_PONG_EN (1 << 5) | ||
15086 | +#define ISPCTRL_SHIFT_SHIFT 6 | ||
15087 | +#define ISPCTRL_SHIFT_0 (0x0 << 6) | ||
15088 | +#define ISPCTRL_SHIFT_2 (0x1 << 6) | ||
15089 | +#define ISPCTRL_SHIFT_4 (0x2 << 6) | ||
15090 | +#define ISPCTRL_SHIFT_MASK (0x3 << 6) | ||
15091 | + | ||
15092 | +#define ISPCTRL_CCDC_CLK_EN (1 << 8) | ||
15093 | +#define ISPCTRL_SCMP_CLK_EN (1 << 9) | ||
15094 | +#define ISPCTRL_H3A_CLK_EN (1 << 10) | ||
15095 | +#define ISPCTRL_HIST_CLK_EN (1 << 11) | ||
15096 | +#define ISPCTRL_PREV_CLK_EN (1 << 12) | ||
15097 | +#define ISPCTRL_RSZ_CLK_EN (1 << 13) | ||
15098 | +#define ISPCTRL_SYNC_DETECT_SHIFT 14 | ||
15099 | +#define ISPCTRL_SYNC_DETECT_HSFALL (0x0 << ISPCTRL_SYNC_DETECT_SHIFT) | ||
15100 | +#define ISPCTRL_SYNC_DETECT_HSRISE (0x1 << ISPCTRL_SYNC_DETECT_SHIFT) | ||
15101 | +#define ISPCTRL_SYNC_DETECT_VSFALL (0x2 << ISPCTRL_SYNC_DETECT_SHIFT) | ||
15102 | +#define ISPCTRL_SYNC_DETECT_VSRISE (0x3 << ISPCTRL_SYNC_DETECT_SHIFT) | ||
15103 | +#define ISPCTRL_SYNC_DETECT_MASK (0x3 << ISPCTRL_SYNC_DETECT_SHIFT) | ||
15104 | + | ||
15105 | +#define ISPCTRL_CCDC_RAM_EN (1 << 16) | ||
15106 | +#define ISPCTRL_PREV_RAM_EN (1 << 17) | ||
15107 | +#define ISPCTRL_SBL_RD_RAM_EN (1 << 18) | ||
15108 | +#define ISPCTRL_SBL_WR1_RAM_EN (1 << 19) | ||
15109 | +#define ISPCTRL_SBL_WR0_RAM_EN (1 << 20) | ||
15110 | +#define ISPCTRL_SBL_AUTOIDLE (1 << 21) | ||
15111 | +#define ISPCTRL_SBL_SHARED_WPORTC (1 << 26) | ||
15112 | +#define ISPCTRL_SBL_SHARED_RPORTA (1 << 27) | ||
15113 | +#define ISPCTRL_SBL_SHARED_RPORTB (1 << 28) | ||
15114 | +#define ISPCTRL_JPEG_FLUSH (1 << 30) | ||
15115 | +#define ISPCTRL_CCDC_FLUSH (1 << 31) | ||
15116 | + | ||
15117 | +#define ISPSECURE_SECUREMODE 0 | ||
15118 | + | ||
15119 | +#define ISPTCTRL_CTRL_DIV_LOW 0x0 | ||
15120 | +#define ISPTCTRL_CTRL_DIV_HIGH 0x1 | ||
15121 | +#define ISPTCTRL_CTRL_DIV_BYPASS 0x1F | ||
15122 | + | ||
15123 | +#define ISPTCTRL_CTRL_DIVA_SHIFT 0 | ||
15124 | +#define ISPTCTRL_CTRL_DIVA_MASK (0x1F << ISPTCTRL_CTRL_DIVA_SHIFT) | ||
15125 | + | ||
15126 | +#define ISPTCTRL_CTRL_DIVB_SHIFT 5 | ||
15127 | +#define ISPTCTRL_CTRL_DIVB_MASK (0x1F << ISPTCTRL_CTRL_DIVB_SHIFT) | ||
15128 | + | ||
15129 | +#define ISPTCTRL_CTRL_DIVC_SHIFT 10 | ||
15130 | +#define ISPTCTRL_CTRL_DIVC_NOCLOCK (0x0 << 10) | ||
15131 | + | ||
15132 | +#define ISPTCTRL_CTRL_SHUTEN (1 << 21) | ||
15133 | +#define ISPTCTRL_CTRL_PSTRBEN (1 << 22) | ||
15134 | +#define ISPTCTRL_CTRL_STRBEN (1 << 23) | ||
15135 | +#define ISPTCTRL_CTRL_SHUTPOL (1 << 24) | ||
15136 | +#define ISPTCTRL_CTRL_STRBPSTRBPOL (1 << 26) | ||
15137 | + | ||
15138 | +#define ISPTCTRL_CTRL_INSEL_SHIFT 27 | ||
15139 | +#define ISPTCTRL_CTRL_INSEL_PARALLEL (0x0 << 27) | ||
15140 | +#define ISPTCTRL_CTRL_INSEL_CSIA (0x1 << 27) | ||
15141 | +#define ISPTCTRL_CTRL_INSEL_CSIB (0x2 << 27) | ||
15142 | + | ||
15143 | +#define ISPTCTRL_CTRL_GRESETEn (1 << 29) | ||
15144 | +#define ISPTCTRL_CTRL_GRESETPOL (1 << 30) | ||
15145 | +#define ISPTCTRL_CTRL_GRESETDIR (1 << 31) | ||
15146 | + | ||
15147 | +#define ISPTCTRL_FRAME_SHUT_SHIFT 0 | ||
15148 | +#define ISPTCTRL_FRAME_PSTRB_SHIFT 6 | ||
15149 | +#define ISPTCTRL_FRAME_STRB_SHIFT 12 | ||
15150 | + | ||
15151 | +#define ISPCCDC_PID_PREV_SHIFT 0 | ||
15152 | +#define ISPCCDC_PID_CID_SHIFT 8 | ||
15153 | +#define ISPCCDC_PID_TID_SHIFT 16 | ||
15154 | + | ||
15155 | +#define ISPCCDC_PCR_EN 1 | ||
15156 | +#define ISPCCDC_PCR_BUSY (1 << 1) | ||
15157 | + | ||
15158 | +#define ISPCCDC_SYN_MODE_VDHDOUT 0x1 | ||
15159 | +#define ISPCCDC_SYN_MODE_FLDOUT (1 << 1) | ||
15160 | +#define ISPCCDC_SYN_MODE_VDPOL (1 << 2) | ||
15161 | +#define ISPCCDC_SYN_MODE_HDPOL (1 << 3) | ||
15162 | +#define ISPCCDC_SYN_MODE_FLDPOL (1 << 4) | ||
15163 | +#define ISPCCDC_SYN_MODE_EXWEN (1 << 5) | ||
15164 | +#define ISPCCDC_SYN_MODE_DATAPOL (1 << 6) | ||
15165 | +#define ISPCCDC_SYN_MODE_FLDMODE (1 << 7) | ||
15166 | +#define ISPCCDC_SYN_MODE_DATSIZ_MASK (0x7 << 8) | ||
15167 | +#define ISPCCDC_SYN_MODE_DATSIZ_8_16 (0x0 << 8) | ||
15168 | +#define ISPCCDC_SYN_MODE_DATSIZ_12 (0x4 << 8) | ||
15169 | +#define ISPCCDC_SYN_MODE_DATSIZ_11 (0x5 << 8) | ||
15170 | +#define ISPCCDC_SYN_MODE_DATSIZ_10 (0x6 << 8) | ||
15171 | +#define ISPCCDC_SYN_MODE_DATSIZ_8 (0x7 << 8) | ||
15172 | +#define ISPCCDC_SYN_MODE_PACK8 (1 << 11) | ||
15173 | +#define ISPCCDC_SYN_MODE_INPMOD_MASK (3 << 12) | ||
15174 | +#define ISPCCDC_SYN_MODE_INPMOD_RAW (0 << 12) | ||
15175 | +#define ISPCCDC_SYN_MODE_INPMOD_YCBCR16 (1 << 12) | ||
15176 | +#define ISPCCDC_SYN_MODE_INPMOD_YCBCR8 (2 << 12) | ||
15177 | +#define ISPCCDC_SYN_MODE_LPF (1 << 14) | ||
15178 | +#define ISPCCDC_SYN_MODE_FLDSTAT (1 << 15) | ||
15179 | +#define ISPCCDC_SYN_MODE_VDHDEN (1 << 16) | ||
15180 | +#define ISPCCDC_SYN_MODE_WEN (1 << 17) | ||
15181 | +#define ISPCCDC_SYN_MODE_VP2SDR (1 << 18) | ||
15182 | +#define ISPCCDC_SYN_MODE_SDR2RSZ (1 << 19) | ||
15183 | + | ||
15184 | +#define ISPCCDC_HD_VD_WID_VDW_SHIFT 0 | ||
15185 | +#define ISPCCDC_HD_VD_WID_HDW_SHIFT 16 | ||
15186 | + | ||
15187 | +#define ISPCCDC_PIX_LINES_HLPRF_SHIFT 0 | ||
15188 | +#define ISPCCDC_PIX_LINES_PPLN_SHIFT 16 | ||
15189 | + | ||
15190 | +#define ISPCCDC_HORZ_INFO_NPH_SHIFT 0 | ||
15191 | +#define ISPCCDC_HORZ_INFO_NPH_MASK 0xFFFF8000 | ||
15192 | +#define ISPCCDC_HORZ_INFO_SPH_MASK 0x1000FFFF | ||
15193 | +#define ISPCCDC_HORZ_INFO_SPH_SHIFT 16 | ||
15194 | + | ||
15195 | +#define ISPCCDC_VERT_START_SLV0_SHIFT 16 | ||
15196 | +#define ISPCCDC_VERT_START_SLV0_MASK 0x1000FFFF | ||
15197 | +#define ISPCCDC_VERT_START_SLV1_SHIFT 0 | ||
15198 | + | ||
15199 | +#define ISPCCDC_VERT_LINES_NLV_MASK 0xFFFF8000 | ||
15200 | +#define ISPCCDC_VERT_LINES_NLV_SHIFT 0 | ||
15201 | + | ||
15202 | +#define ISPCCDC_CULLING_CULV_SHIFT 0 | ||
15203 | +#define ISPCCDC_CULLING_CULHODD_SHIFT 16 | ||
15204 | +#define ISPCCDC_CULLING_CULHEVN_SHIFT 24 | ||
15205 | + | ||
15206 | +#define ISPCCDC_HSIZE_OFF_SHIFT 0 | ||
15207 | + | ||
15208 | +#define ISPCCDC_SDOFST_FINV (1 << 14) | ||
15209 | +#define ISPCCDC_SDOFST_FOFST_1L 0 | ||
15210 | +#define ISPCCDC_SDOFST_FOFST_4L (3 << 12) | ||
15211 | +#define ISPCCDC_SDOFST_LOFST3_SHIFT 0 | ||
15212 | +#define ISPCCDC_SDOFST_LOFST2_SHIFT 3 | ||
15213 | +#define ISPCCDC_SDOFST_LOFST1_SHIFT 6 | ||
15214 | +#define ISPCCDC_SDOFST_LOFST0_SHIFT 9 | ||
15215 | +#define EVENEVEN 1 | ||
15216 | +#define ODDEVEN 2 | ||
15217 | +#define EVENODD 3 | ||
15218 | +#define ODDODD 4 | ||
15219 | + | ||
15220 | +#define ISPCCDC_CLAMP_OBGAIN_SHIFT 0 | ||
15221 | +#define ISPCCDC_CLAMP_OBST_SHIFT 10 | ||
15222 | +#define ISPCCDC_CLAMP_OBSLN_SHIFT 25 | ||
15223 | +#define ISPCCDC_CLAMP_OBSLEN_SHIFT 28 | ||
15224 | +#define ISPCCDC_CLAMP_CLAMPEN (1 << 31) | ||
15225 | + | ||
15226 | +#define ISPCCDC_COLPTN_R_Ye 0x0 | ||
15227 | +#define ISPCCDC_COLPTN_Gr_Cy 0x1 | ||
15228 | +#define ISPCCDC_COLPTN_Gb_G 0x2 | ||
15229 | +#define ISPCCDC_COLPTN_B_Mg 0x3 | ||
15230 | +#define ISPCCDC_COLPTN_CP0PLC0_SHIFT 0 | ||
15231 | +#define ISPCCDC_COLPTN_CP0PLC1_SHIFT 2 | ||
15232 | +#define ISPCCDC_COLPTN_CP0PLC2_SHIFT 4 | ||
15233 | +#define ISPCCDC_COLPTN_CP0PLC3_SHIFT 6 | ||
15234 | +#define ISPCCDC_COLPTN_CP1PLC0_SHIFT 8 | ||
15235 | +#define ISPCCDC_COLPTN_CP1PLC1_SHIFT 10 | ||
15236 | +#define ISPCCDC_COLPTN_CP1PLC2_SHIFT 12 | ||
15237 | +#define ISPCCDC_COLPTN_CP1PLC3_SHIFT 14 | ||
15238 | +#define ISPCCDC_COLPTN_CP2PLC0_SHIFT 16 | ||
15239 | +#define ISPCCDC_COLPTN_CP2PLC1_SHIFT 18 | ||
15240 | +#define ISPCCDC_COLPTN_CP2PLC2_SHIFT 20 | ||
15241 | +#define ISPCCDC_COLPTN_CP2PLC3_SHIFT 22 | ||
15242 | +#define ISPCCDC_COLPTN_CP3PLC0_SHIFT 24 | ||
15243 | +#define ISPCCDC_COLPTN_CP3PLC1_SHIFT 26 | ||
15244 | +#define ISPCCDC_COLPTN_CP3PLC2_SHIFT 28 | ||
15245 | +#define ISPCCDC_COLPTN_CP3PLC3_SHIFT 30 | ||
15246 | + | ||
15247 | +#define ISPCCDC_BLKCMP_B_MG_SHIFT 0 | ||
15248 | +#define ISPCCDC_BLKCMP_GB_G_SHIFT 8 | ||
15249 | +#define ISPCCDC_BLKCMP_GR_CY_SHIFT 16 | ||
15250 | +#define ISPCCDC_BLKCMP_R_YE_SHIFT 24 | ||
15251 | + | ||
15252 | +#define ISPCCDC_FPC_FPNUM_SHIFT 0 | ||
15253 | +#define ISPCCDC_FPC_FPCEN (1 << 15) | ||
15254 | +#define ISPCCDC_FPC_FPERR (1 << 16) | ||
15255 | + | ||
15256 | +#define ISPCCDC_VDINT_1_SHIFT 0 | ||
15257 | +#define ISPCCDC_VDINT_0_SHIFT 16 | ||
15258 | +#define ISPCCDC_VDINT_0_MASK 0x7FFF | ||
15259 | +#define ISPCCDC_VDINT_1_MASK 0x7FFF | ||
15260 | + | ||
15261 | +#define ISPCCDC_ALAW_GWDI_12_3 (0x3 << 0) | ||
15262 | +#define ISPCCDC_ALAW_GWDI_11_2 (0x4 << 0) | ||
15263 | +#define ISPCCDC_ALAW_GWDI_10_1 (0x5 << 0) | ||
15264 | +#define ISPCCDC_ALAW_GWDI_9_0 (0x6 << 0) | ||
15265 | +#define ISPCCDC_ALAW_CCDTBL (1 << 3) | ||
15266 | + | ||
15267 | +#define ISPCCDC_REC656IF_R656ON 1 | ||
15268 | +#define ISPCCDC_REC656IF_ECCFVH (1 << 1) | ||
15269 | + | ||
15270 | +#define ISPCCDC_CFG_BW656 (1 << 5) | ||
15271 | +#define ISPCCDC_CFG_FIDMD_SHIFT 6 | ||
15272 | +#define ISPCCDC_CFG_WENLOG (1 << 8) | ||
15273 | +#define ISPCCDC_CFG_WENLOG_AND (0 << 8) | ||
15274 | +#define ISPCCDC_CFG_WENLOG_OR (1 << 8) | ||
15275 | +#define ISPCCDC_CFG_Y8POS (1 << 11) | ||
15276 | +#define ISPCCDC_CFG_BSWD (1 << 12) | ||
15277 | +#define ISPCCDC_CFG_MSBINVI (1 << 13) | ||
15278 | +#define ISPCCDC_CFG_VDLC (1 << 15) | ||
15279 | + | ||
15280 | +#define ISPCCDC_FMTCFG_FMTEN 0x1 | ||
15281 | +#define ISPCCDC_FMTCFG_LNALT (1 << 1) | ||
15282 | +#define ISPCCDC_FMTCFG_LNUM_SHIFT 2 | ||
15283 | +#define ISPCCDC_FMTCFG_PLEN_ODD_SHIFT 4 | ||
15284 | +#define ISPCCDC_FMTCFG_PLEN_EVEN_SHIFT 8 | ||
15285 | +#define ISPCCDC_FMTCFG_VPIN_MASK 0x00007000 | ||
15286 | +#define ISPCCDC_FMTCFG_VPIN_12_3 (0x3 << 12) | ||
15287 | +#define ISPCCDC_FMTCFG_VPIN_11_2 (0x4 << 12) | ||
15288 | +#define ISPCCDC_FMTCFG_VPIN_10_1 (0x5 << 12) | ||
15289 | +#define ISPCCDC_FMTCFG_VPIN_9_0 (0x6 << 12) | ||
15290 | +#define ISPCCDC_FMTCFG_VPEN (1 << 15) | ||
15291 | + | ||
15292 | +#define ISPCCDC_FMTCFG_VPIF_FRQ_MASK 0x003f0000 | ||
15293 | +#define ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT 16 | ||
15294 | +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY2 (0x0 << 16) | ||
15295 | +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY3 (0x1 << 16) | ||
15296 | +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY4 (0x2 << 16) | ||
15297 | +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY5 (0x3 << 16) | ||
15298 | +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY6 (0x4 << 16) | ||
15299 | + | ||
15300 | +#define ISPCCDC_FMT_HORZ_FMTLNH_SHIFT 0 | ||
15301 | +#define ISPCCDC_FMT_HORZ_FMTSPH_SHIFT 16 | ||
15302 | + | ||
15303 | +#define ISPCCDC_FMT_VERT_FMTLNV_SHIFT 0 | ||
15304 | +#define ISPCCDC_FMT_VERT_FMTSLV_SHIFT 16 | ||
15305 | + | ||
15306 | +#define ISPCCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF0000 | ||
15307 | +#define ISPCCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF | ||
15308 | + | ||
15309 | +#define ISPCCDC_FMT_VERT_FMTSLV_MASK 0x1FFF0000 | ||
15310 | +#define ISPCCDC_FMT_VERT_FMTLNV_MASK 0x1FFF | ||
15311 | + | ||
15312 | +#define ISPCCDC_VP_OUT_HORZ_ST_SHIFT 0 | ||
15313 | +#define ISPCCDC_VP_OUT_HORZ_NUM_SHIFT 4 | ||
15314 | +#define ISPCCDC_VP_OUT_VERT_NUM_SHIFT 17 | ||
15315 | + | ||
15316 | +#define ISPRSZ_PID_PREV_SHIFT 0 | ||
15317 | +#define ISPRSZ_PID_CID_SHIFT 8 | ||
15318 | +#define ISPRSZ_PID_TID_SHIFT 16 | ||
15319 | + | ||
15320 | +#define ISPRSZ_PCR_ENABLE (1 << 0) | ||
15321 | +#define ISPRSZ_PCR_BUSY (1 << 1) | ||
15322 | +#define ISPRSZ_PCR_ONESHOT (1 << 2) | ||
15323 | + | ||
15324 | +#define ISPRSZ_CNT_HRSZ_SHIFT 0 | ||
15325 | +#define ISPRSZ_CNT_HRSZ_MASK \ | ||
15326 | + (0x3FF << ISPRSZ_CNT_HRSZ_SHIFT) | ||
15327 | +#define ISPRSZ_CNT_VRSZ_SHIFT 10 | ||
15328 | +#define ISPRSZ_CNT_VRSZ_MASK \ | ||
15329 | + (0x3FF << ISPRSZ_CNT_VRSZ_SHIFT) | ||
15330 | +#define ISPRSZ_CNT_HSTPH_SHIFT 20 | ||
15331 | +#define ISPRSZ_CNT_HSTPH_MASK (0x7 << ISPRSZ_CNT_HSTPH_SHIFT) | ||
15332 | +#define ISPRSZ_CNT_VSTPH_SHIFT 23 | ||
15333 | +#define ISPRSZ_CNT_VSTPH_MASK (0x7 << ISPRSZ_CNT_VSTPH_SHIFT) | ||
15334 | +#define ISPRSZ_CNT_YCPOS (1 << 26) | ||
15335 | +#define ISPRSZ_CNT_INPTYP (1 << 27) | ||
15336 | +#define ISPRSZ_CNT_INPSRC (1 << 28) | ||
15337 | +#define ISPRSZ_CNT_CBILIN (1 << 29) | ||
15338 | + | ||
15339 | +#define ISPRSZ_OUT_SIZE_HORZ_SHIFT 0 | ||
15340 | +#define ISPRSZ_OUT_SIZE_HORZ_MASK \ | ||
15341 | + (0xFFF << ISPRSZ_OUT_SIZE_HORZ_SHIFT) | ||
15342 | +#define ISPRSZ_OUT_SIZE_VERT_SHIFT 16 | ||
15343 | +#define ISPRSZ_OUT_SIZE_VERT_MASK \ | ||
15344 | + (0xFFF << ISPRSZ_OUT_SIZE_VERT_SHIFT) | ||
15345 | + | ||
15346 | +#define ISPRSZ_IN_START_HORZ_ST_SHIFT 0 | ||
15347 | +#define ISPRSZ_IN_START_HORZ_ST_MASK \ | ||
15348 | + (0x1FFF << ISPRSZ_IN_START_HORZ_ST_SHIFT) | ||
15349 | +#define ISPRSZ_IN_START_VERT_ST_SHIFT 16 | ||
15350 | +#define ISPRSZ_IN_START_VERT_ST_MASK \ | ||
15351 | + (0x1FFF << ISPRSZ_IN_START_VERT_ST_SHIFT) | ||
15352 | + | ||
15353 | +#define ISPRSZ_IN_SIZE_HORZ_SHIFT 0 | ||
15354 | +#define ISPRSZ_IN_SIZE_HORZ_MASK \ | ||
15355 | + (0x1FFF << ISPRSZ_IN_SIZE_HORZ_SHIFT) | ||
15356 | +#define ISPRSZ_IN_SIZE_VERT_SHIFT 16 | ||
15357 | +#define ISPRSZ_IN_SIZE_VERT_MASK \ | ||
15358 | + (0x1FFF << ISPRSZ_IN_SIZE_VERT_SHIFT) | ||
15359 | + | ||
15360 | +#define ISPRSZ_SDR_INADD_ADDR_SHIFT 0 | ||
15361 | +#define ISPRSZ_SDR_INADD_ADDR_MASK 0xFFFFFFFF | ||
15362 | + | ||
15363 | +#define ISPRSZ_SDR_INOFF_OFFSET_SHIFT 0 | ||
15364 | +#define ISPRSZ_SDR_INOFF_OFFSET_MASK \ | ||
15365 | + (0xFFFF << ISPRSZ_SDR_INOFF_OFFSET_SHIFT) | ||
15366 | + | ||
15367 | +#define ISPRSZ_SDR_OUTADD_ADDR_SHIFT 0 | ||
15368 | +#define ISPRSZ_SDR_OUTADD_ADDR_MASK 0xFFFFFFFF | ||
15369 | + | ||
15370 | + | ||
15371 | +#define ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT 0 | ||
15372 | +#define ISPRSZ_SDR_OUTOFF_OFFSET_MASK \ | ||
15373 | + (0xFFFF << ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT) | ||
15374 | + | ||
15375 | +#define ISPRSZ_HFILT_COEF0_SHIFT 0 | ||
15376 | +#define ISPRSZ_HFILT_COEF0_MASK \ | ||
15377 | + (0x3FF << ISPRSZ_HFILT_COEF0_SHIFT) | ||
15378 | +#define ISPRSZ_HFILT_COEF1_SHIFT 16 | ||
15379 | +#define ISPRSZ_HFILT_COEF1_MASK \ | ||
15380 | + (0x3FF << ISPRSZ_HFILT_COEF1_SHIFT) | ||
15381 | + | ||
15382 | +#define ISPRSZ_HFILT32_COEF2_SHIFT 0 | ||
15383 | +#define ISPRSZ_HFILT32_COEF2_MASK 0x3FF | ||
15384 | +#define ISPRSZ_HFILT32_COEF3_SHIFT 16 | ||
15385 | +#define ISPRSZ_HFILT32_COEF3_MASK 0x3FF0000 | ||
15386 | + | ||
15387 | +#define ISPRSZ_HFILT54_COEF4_SHIFT 0 | ||
15388 | +#define ISPRSZ_HFILT54_COEF4_MASK 0x3FF | ||
15389 | +#define ISPRSZ_HFILT54_COEF5_SHIFT 16 | ||
15390 | +#define ISPRSZ_HFILT54_COEF5_MASK 0x3FF0000 | ||
15391 | + | ||
15392 | +#define ISPRSZ_HFILT76_COEFF6_SHIFT 0 | ||
15393 | +#define ISPRSZ_HFILT76_COEFF6_MASK 0x3FF | ||
15394 | +#define ISPRSZ_HFILT76_COEFF7_SHIFT 16 | ||
15395 | +#define ISPRSZ_HFILT76_COEFF7_MASK 0x3FF0000 | ||
15396 | + | ||
15397 | +#define ISPRSZ_HFILT98_COEFF8_SHIFT 0 | ||
15398 | +#define ISPRSZ_HFILT98_COEFF8_MASK 0x3FF | ||
15399 | +#define ISPRSZ_HFILT98_COEFF9_SHIFT 16 | ||
15400 | +#define ISPRSZ_HFILT98_COEFF9_MASK 0x3FF0000 | ||
15401 | + | ||
15402 | +#define ISPRSZ_HFILT1110_COEF10_SHIFT 0 | ||
15403 | +#define ISPRSZ_HFILT1110_COEF10_MASK 0x3FF | ||
15404 | +#define ISPRSZ_HFILT1110_COEF11_SHIFT 16 | ||
15405 | +#define ISPRSZ_HFILT1110_COEF11_MASK 0x3FF0000 | ||
15406 | + | ||
15407 | +#define ISPRSZ_HFILT1312_COEFF12_SHIFT 0 | ||
15408 | +#define ISPRSZ_HFILT1312_COEFF12_MASK 0x3FF | ||
15409 | +#define ISPRSZ_HFILT1312_COEFF13_SHIFT 16 | ||
15410 | +#define ISPRSZ_HFILT1312_COEFF13_MASK 0x3FF0000 | ||
15411 | + | ||
15412 | +#define ISPRSZ_HFILT1514_COEFF14_SHIFT 0 | ||
15413 | +#define ISPRSZ_HFILT1514_COEFF14_MASK 0x3FF | ||
15414 | +#define ISPRSZ_HFILT1514_COEFF15_SHIFT 16 | ||
15415 | +#define ISPRSZ_HFILT1514_COEFF15_MASK 0x3FF0000 | ||
15416 | + | ||
15417 | +#define ISPRSZ_HFILT1716_COEF16_SHIFT 0 | ||
15418 | +#define ISPRSZ_HFILT1716_COEF16_MASK 0x3FF | ||
15419 | +#define ISPRSZ_HFILT1716_COEF17_SHIFT 16 | ||
15420 | +#define ISPRSZ_HFILT1716_COEF17_MASK 0x3FF0000 | ||
15421 | + | ||
15422 | +#define ISPRSZ_HFILT1918_COEF18_SHIFT 0 | ||
15423 | +#define ISPRSZ_HFILT1918_COEF18_MASK 0x3FF | ||
15424 | +#define ISPRSZ_HFILT1918_COEF19_SHIFT 16 | ||
15425 | +#define ISPRSZ_HFILT1918_COEF19_MASK 0x3FF0000 | ||
15426 | + | ||
15427 | +#define ISPRSZ_HFILT2120_COEF20_SHIFT 0 | ||
15428 | +#define ISPRSZ_HFILT2120_COEF20_MASK 0x3FF | ||
15429 | +#define ISPRSZ_HFILT2120_COEF21_SHIFT 16 | ||
15430 | +#define ISPRSZ_HFILT2120_COEF21_MASK 0x3FF0000 | ||
15431 | + | ||
15432 | +#define ISPRSZ_HFILT2322_COEF22_SHIFT 0 | ||
15433 | +#define ISPRSZ_HFILT2322_COEF22_MASK 0x3FF | ||
15434 | +#define ISPRSZ_HFILT2322_COEF23_SHIFT 16 | ||
15435 | +#define ISPRSZ_HFILT2322_COEF23_MASK 0x3FF0000 | ||
15436 | + | ||
15437 | +#define ISPRSZ_HFILT2524_COEF24_SHIFT 0 | ||
15438 | +#define ISPRSZ_HFILT2524_COEF24_MASK 0x3FF | ||
15439 | +#define ISPRSZ_HFILT2524_COEF25_SHIFT 16 | ||
15440 | +#define ISPRSZ_HFILT2524_COEF25_MASK 0x3FF0000 | ||
15441 | + | ||
15442 | +#define ISPRSZ_HFILT2726_COEF26_SHIFT 0 | ||
15443 | +#define ISPRSZ_HFILT2726_COEF26_MASK 0x3FF | ||
15444 | +#define ISPRSZ_HFILT2726_COEF27_SHIFT 16 | ||
15445 | +#define ISPRSZ_HFILT2726_COEF27_MASK 0x3FF0000 | ||
15446 | + | ||
15447 | +#define ISPRSZ_HFILT2928_COEF28_SHIFT 0 | ||
15448 | +#define ISPRSZ_HFILT2928_COEF28_MASK 0x3FF | ||
15449 | +#define ISPRSZ_HFILT2928_COEF29_SHIFT 16 | ||
15450 | +#define ISPRSZ_HFILT2928_COEF29_MASK 0x3FF0000 | ||
15451 | + | ||
15452 | +#define ISPRSZ_HFILT3130_COEF30_SHIFT 0 | ||
15453 | +#define ISPRSZ_HFILT3130_COEF30_MASK 0x3FF | ||
15454 | +#define ISPRSZ_HFILT3130_COEF31_SHIFT 16 | ||
15455 | +#define ISPRSZ_HFILT3130_COEF31_MASK 0x3FF0000 | ||
15456 | + | ||
15457 | +#define ISPRSZ_VFILT_COEF0_SHIFT 0 | ||
15458 | +#define ISPRSZ_VFILT_COEF0_MASK \ | ||
15459 | + (0x3FF << ISPRSZ_VFILT_COEF0_SHIFT) | ||
15460 | +#define ISPRSZ_VFILT_COEF1_SHIFT 16 | ||
15461 | +#define ISPRSZ_VFILT_COEF1_MASK \ | ||
15462 | + (0x3FF << ISPRSZ_VFILT_COEF1_SHIFT) | ||
15463 | + | ||
15464 | +#define ISPRSZ_VFILT10_COEF0_SHIFT 0 | ||
15465 | +#define ISPRSZ_VFILT10_COEF0_MASK 0x3FF | ||
15466 | +#define ISPRSZ_VFILT10_COEF1_SHIFT 16 | ||
15467 | +#define ISPRSZ_VFILT10_COEF1_MASK 0x3FF0000 | ||
15468 | + | ||
15469 | +#define ISPRSZ_VFILT32_COEF2_SHIFT 0 | ||
15470 | +#define ISPRSZ_VFILT32_COEF2_MASK 0x3FF | ||
15471 | +#define ISPRSZ_VFILT32_COEF3_SHIFT 16 | ||
15472 | +#define ISPRSZ_VFILT32_COEF3_MASK 0x3FF0000 | ||
15473 | + | ||
15474 | +#define ISPRSZ_VFILT54_COEF4_SHIFT 0 | ||
15475 | +#define ISPRSZ_VFILT54_COEF4_MASK 0x3FF | ||
15476 | +#define ISPRSZ_VFILT54_COEF5_SHIFT 16 | ||
15477 | +#define ISPRSZ_VFILT54_COEF5_MASK 0x3FF0000 | ||
15478 | + | ||
15479 | +#define ISPRSZ_VFILT76_COEFF6_SHIFT 0 | ||
15480 | +#define ISPRSZ_VFILT76_COEFF6_MASK 0x3FF | ||
15481 | +#define ISPRSZ_VFILT76_COEFF7_SHIFT 16 | ||
15482 | +#define ISPRSZ_VFILT76_COEFF7_MASK 0x3FF0000 | ||
15483 | + | ||
15484 | +#define ISPRSZ_VFILT98_COEFF8_SHIFT 0 | ||
15485 | +#define ISPRSZ_VFILT98_COEFF8_MASK 0x3FF | ||
15486 | +#define ISPRSZ_VFILT98_COEFF9_SHIFT 16 | ||
15487 | +#define ISPRSZ_VFILT98_COEFF9_MASK 0x3FF0000 | ||
15488 | + | ||
15489 | +#define ISPRSZ_VFILT1110_COEF10_SHIFT 0 | ||
15490 | +#define ISPRSZ_VFILT1110_COEF10_MASK 0x3FF | ||
15491 | +#define ISPRSZ_VFILT1110_COEF11_SHIFT 16 | ||
15492 | +#define ISPRSZ_VFILT1110_COEF11_MASK 0x3FF0000 | ||
15493 | + | ||
15494 | +#define ISPRSZ_VFILT1312_COEFF12_SHIFT 0 | ||
15495 | +#define ISPRSZ_VFILT1312_COEFF12_MASK 0x3FF | ||
15496 | +#define ISPRSZ_VFILT1312_COEFF13_SHIFT 16 | ||
15497 | +#define ISPRSZ_VFILT1312_COEFF13_MASK 0x3FF0000 | ||
15498 | + | ||
15499 | +#define ISPRSZ_VFILT1514_COEFF14_SHIFT 0 | ||
15500 | +#define ISPRSZ_VFILT1514_COEFF14_MASK 0x3FF | ||
15501 | +#define ISPRSZ_VFILT1514_COEFF15_SHIFT 16 | ||
15502 | +#define ISPRSZ_VFILT1514_COEFF15_MASK 0x3FF0000 | ||
15503 | + | ||
15504 | +#define ISPRSZ_VFILT1716_COEF16_SHIFT 0 | ||
15505 | +#define ISPRSZ_VFILT1716_COEF16_MASK 0x3FF | ||
15506 | +#define ISPRSZ_VFILT1716_COEF17_SHIFT 16 | ||
15507 | +#define ISPRSZ_VFILT1716_COEF17_MASK 0x3FF0000 | ||
15508 | + | ||
15509 | +#define ISPRSZ_VFILT1918_COEF18_SHIFT 0 | ||
15510 | +#define ISPRSZ_VFILT1918_COEF18_MASK 0x3FF | ||
15511 | +#define ISPRSZ_VFILT1918_COEF19_SHIFT 16 | ||
15512 | +#define ISPRSZ_VFILT1918_COEF19_MASK 0x3FF0000 | ||
15513 | + | ||
15514 | +#define ISPRSZ_VFILT2120_COEF20_SHIFT 0 | ||
15515 | +#define ISPRSZ_VFILT2120_COEF20_MASK 0x3FF | ||
15516 | +#define ISPRSZ_VFILT2120_COEF21_SHIFT 16 | ||
15517 | +#define ISPRSZ_VFILT2120_COEF21_MASK 0x3FF0000 | ||
15518 | + | ||
15519 | +#define ISPRSZ_VFILT2322_COEF22_SHIFT 0 | ||
15520 | +#define ISPRSZ_VFILT2322_COEF22_MASK 0x3FF | ||
15521 | +#define ISPRSZ_VFILT2322_COEF23_SHIFT 16 | ||
15522 | +#define ISPRSZ_VFILT2322_COEF23_MASK 0x3FF0000 | ||
15523 | + | ||
15524 | +#define ISPRSZ_VFILT2524_COEF24_SHIFT 0 | ||
15525 | +#define ISPRSZ_VFILT2524_COEF24_MASK 0x3FF | ||
15526 | +#define ISPRSZ_VFILT2524_COEF25_SHIFT 16 | ||
15527 | +#define ISPRSZ_VFILT2524_COEF25_MASK 0x3FF0000 | ||
15528 | + | ||
15529 | +#define ISPRSZ_VFILT2726_COEF26_SHIFT 0 | ||
15530 | +#define ISPRSZ_VFILT2726_COEF26_MASK 0x3FF | ||
15531 | +#define ISPRSZ_VFILT2726_COEF27_SHIFT 16 | ||
15532 | +#define ISPRSZ_VFILT2726_COEF27_MASK 0x3FF0000 | ||
15533 | + | ||
15534 | +#define ISPRSZ_VFILT2928_COEF28_SHIFT 0 | ||
15535 | +#define ISPRSZ_VFILT2928_COEF28_MASK 0x3FF | ||
15536 | +#define ISPRSZ_VFILT2928_COEF29_SHIFT 16 | ||
15537 | +#define ISPRSZ_VFILT2928_COEF29_MASK 0x3FF0000 | ||
15538 | + | ||
15539 | +#define ISPRSZ_VFILT3130_COEF30_SHIFT 0 | ||
15540 | +#define ISPRSZ_VFILT3130_COEF30_MASK 0x3FF | ||
15541 | +#define ISPRSZ_VFILT3130_COEF31_SHIFT 16 | ||
15542 | +#define ISPRSZ_VFILT3130_COEF31_MASK 0x3FF0000 | ||
15543 | + | ||
15544 | +#define ISPRSZ_YENH_CORE_SHIFT 0 | ||
15545 | +#define ISPRSZ_YENH_CORE_MASK \ | ||
15546 | + (0xFF << ISPRSZ_YENH_CORE_SHIFT) | ||
15547 | +#define ISPRSZ_YENH_SLOP_SHIFT 8 | ||
15548 | +#define ISPRSZ_YENH_SLOP_MASK \ | ||
15549 | + (0xF << ISPRSZ_YENH_SLOP_SHIFT) | ||
15550 | +#define ISPRSZ_YENH_GAIN_SHIFT 12 | ||
15551 | +#define ISPRSZ_YENH_GAIN_MASK \ | ||
15552 | + (0xF << ISPRSZ_YENH_GAIN_SHIFT) | ||
15553 | +#define ISPRSZ_YENH_ALGO_SHIFT 16 | ||
15554 | +#define ISPRSZ_YENH_ALGO_MASK \ | ||
15555 | + (0x3 << ISPRSZ_YENH_ALGO_SHIFT) | ||
15556 | + | ||
15557 | +#define ISPH3A_PCR_AEW_ALAW_EN_SHIFT 1 | ||
15558 | +#define ISPH3A_PCR_AF_MED_TH_SHIFT 3 | ||
15559 | +#define ISPH3A_PCR_AF_RGBPOS_SHIFT 11 | ||
15560 | +#define ISPH3A_PCR_AEW_AVE2LMT_SHIFT 22 | ||
15561 | +#define ISPH3A_PCR_AEW_AVE2LMT_MASK 0xFFC00000 | ||
15562 | +#define ISPH3A_PCR_BUSYAF (1 << 15) | ||
15563 | +#define ISPH3A_PCR_BUSYAEAWB (1 << 18) | ||
15564 | + | ||
15565 | +#define ISPH3A_AEWWIN1_WINHC_SHIFT 0 | ||
15566 | +#define ISPH3A_AEWWIN1_WINHC_MASK 0x3F | ||
15567 | +#define ISPH3A_AEWWIN1_WINVC_SHIFT 6 | ||
15568 | +#define ISPH3A_AEWWIN1_WINVC_MASK 0x1FC0 | ||
15569 | +#define ISPH3A_AEWWIN1_WINW_SHIFT 13 | ||
15570 | +#define ISPH3A_AEWWIN1_WINW_MASK 0xFE000 | ||
15571 | +#define ISPH3A_AEWWIN1_WINH_SHIFT 24 | ||
15572 | +#define ISPH3A_AEWWIN1_WINH_MASK 0x7F000000 | ||
15573 | + | ||
15574 | +#define ISPH3A_AEWINSTART_WINSH_SHIFT 0 | ||
15575 | +#define ISPH3A_AEWINSTART_WINSH_MASK 0x0FFF | ||
15576 | +#define ISPH3A_AEWINSTART_WINSV_SHIFT 16 | ||
15577 | +#define ISPH3A_AEWINSTART_WINSV_MASK 0x0FFF0000 | ||
15578 | + | ||
15579 | +#define ISPH3A_AEWINBLK_WINH_SHIFT 0 | ||
15580 | +#define ISPH3A_AEWINBLK_WINH_MASK 0x7F | ||
15581 | +#define ISPH3A_AEWINBLK_WINSV_SHIFT 16 | ||
15582 | +#define ISPH3A_AEWINBLK_WINSV_MASK 0x0FFF0000 | ||
15583 | + | ||
15584 | +#define ISPH3A_AEWSUBWIN_AEWINCH_SHIFT 0 | ||
15585 | +#define ISPH3A_AEWSUBWIN_AEWINCH_MASK 0x0F | ||
15586 | +#define ISPH3A_AEWSUBWIN_AEWINCV_SHIFT 8 | ||
15587 | +#define ISPH3A_AEWSUBWIN_AEWINCV_MASK 0x0F00 | ||
15588 | + | ||
15589 | +#define ISPHIST_PCR_ENABLE_SHIFT 0 | ||
15590 | +#define ISPHIST_PCR_ENABLE_MASK 0x01 | ||
15591 | +#define ISPHIST_PCR_ENABLE (1 << ISPHIST_PCR_ENABLE_SHIFT) | ||
15592 | +#define ISPHIST_PCR_BUSY 0x02 | ||
15593 | + | ||
15594 | +#define ISPHIST_CNT_DATASIZE_SHIFT 8 | ||
15595 | +#define ISPHIST_CNT_DATASIZE_MASK 0x0100 | ||
15596 | +#define ISPHIST_CNT_CLEAR_SHIFT 7 | ||
15597 | +#define ISPHIST_CNT_CLEAR_MASK 0x080 | ||
15598 | +#define ISPHIST_CNT_CLEAR (1 << ISPHIST_CNT_CLEAR_SHIFT) | ||
15599 | +#define ISPHIST_CNT_CFA_SHIFT 6 | ||
15600 | +#define ISPHIST_CNT_CFA_MASK 0x040 | ||
15601 | +#define ISPHIST_CNT_BINS_SHIFT 4 | ||
15602 | +#define ISPHIST_CNT_BINS_MASK 0x030 | ||
15603 | +#define ISPHIST_CNT_SOURCE_SHIFT 3 | ||
15604 | +#define ISPHIST_CNT_SOURCE_MASK 0x08 | ||
15605 | +#define ISPHIST_CNT_SHIFT_SHIFT 0 | ||
15606 | +#define ISPHIST_CNT_SHIFT_MASK 0x07 | ||
15607 | + | ||
15608 | +#define ISPHIST_WB_GAIN_WG00_SHIFT 24 | ||
15609 | +#define ISPHIST_WB_GAIN_WG00_MASK 0xFF000000 | ||
15610 | +#define ISPHIST_WB_GAIN_WG01_SHIFT 16 | ||
15611 | +#define ISPHIST_WB_GAIN_WG01_MASK 0xFF0000 | ||
15612 | +#define ISPHIST_WB_GAIN_WG02_SHIFT 8 | ||
15613 | +#define ISPHIST_WB_GAIN_WG02_MASK 0xFF00 | ||
15614 | +#define ISPHIST_WB_GAIN_WG03_SHIFT 0 | ||
15615 | +#define ISPHIST_WB_GAIN_WG03_MASK 0xFF | ||
15616 | + | ||
15617 | +#define ISPHIST_REG_START_END_MASK 0x3FFF | ||
15618 | +#define ISPHIST_REG_START_SHIFT 16 | ||
15619 | +#define ISPHIST_REG_END_SHIFT 0 | ||
15620 | +#define ISPHIST_REG_START_MASK (ISPHIST_REG_START_END_MASK << \ | ||
15621 | + ISPHIST_REG_START_SHIFT) | ||
15622 | +#define ISPHIST_REG_END_MASK (ISPHIST_REG_START_END_MASK << \ | ||
15623 | + ISPHIST_REG_END_SHIFT) | ||
15624 | + | ||
15625 | +#define ISPHIST_REG_MASK (ISPHIST_REG_START_MASK | \ | ||
15626 | + ISPHIST_REG_END_MASK) | ||
15627 | + | ||
15628 | +#define ISPHIST_ADDR_SHIFT 0 | ||
15629 | +#define ISPHIST_ADDR_MASK 0x3FF | ||
15630 | + | ||
15631 | +#define ISPHIST_DATA_SHIFT 0 | ||
15632 | +#define ISPHIST_DATA_MASK 0xFFFFF | ||
15633 | + | ||
15634 | +#define ISPHIST_RADD_SHIFT 0 | ||
15635 | +#define ISPHIST_RADD_MASK 0xFFFFFFFF | ||
15636 | + | ||
15637 | +#define ISPHIST_RADD_OFF_SHIFT 0 | ||
15638 | +#define ISPHIST_RADD_OFF_MASK 0xFFFF | ||
15639 | + | ||
15640 | +#define ISPHIST_HV_INFO_HSIZE_SHIFT 16 | ||
15641 | +#define ISPHIST_HV_INFO_HSIZE_MASK 0x3FFF0000 | ||
15642 | +#define ISPHIST_HV_INFO_VSIZE_SHIFT 0 | ||
15643 | +#define ISPHIST_HV_INFO_VSIZE_MASK 0x3FFF | ||
15644 | + | ||
15645 | +#define ISPHIST_HV_INFO_MASK 0x3FFF3FFF | ||
15646 | + | ||
15647 | +#define ISPCCDC_LSC_ENABLE 1 | ||
15648 | +#define ISPCCDC_LSC_BUSY (1 << 7) | ||
15649 | +#define ISPCCDC_LSC_GAIN_MODE_N_MASK 0x700 | ||
15650 | +#define ISPCCDC_LSC_GAIN_MODE_N_SHIFT 8 | ||
15651 | +#define ISPCCDC_LSC_GAIN_MODE_M_MASK 0x3800 | ||
15652 | +#define ISPCCDC_LSC_GAIN_MODE_M_SHIFT 12 | ||
15653 | +#define ISPCCDC_LSC_GAIN_FORMAT_MASK 0xE | ||
15654 | +#define ISPCCDC_LSC_GAIN_FORMAT_SHIFT 1 | ||
15655 | +#define ISPCCDC_LSC_AFTER_REFORMATTER_MASK (1<<6) | ||
15656 | + | ||
15657 | +#define ISPCCDC_LSC_INITIAL_X_MASK 0x3F | ||
15658 | +#define ISPCCDC_LSC_INITIAL_X_SHIFT 0 | ||
15659 | +#define ISPCCDC_LSC_INITIAL_Y_MASK 0x3F0000 | ||
15660 | +#define ISPCCDC_LSC_INITIAL_Y_SHIFT 16 | ||
15661 | + | ||
15662 | +/* ----------------------------------------------------------------------------- | ||
15663 | + * CSI2 receiver registers (ES2.0) | ||
15664 | + */ | ||
15665 | + | ||
15666 | +#define ISPCSI2_REVISION (0x000) | ||
15667 | +#define ISPCSI2_SYSCONFIG (0x010) | ||
15668 | +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT 12 | ||
15669 | +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK \ | ||
15670 | + (0x3 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
15671 | +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_FORCE \ | ||
15672 | + (0x0 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
15673 | +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO \ | ||
15674 | + (0x1 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
15675 | +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART \ | ||
15676 | + (0x2 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
15677 | +#define ISPCSI2_SYSCONFIG_SOFT_RESET (1 << 1) | ||
15678 | +#define ISPCSI2_SYSCONFIG_AUTO_IDLE (1 << 0) | ||
15679 | + | ||
15680 | +#define ISPCSI2_SYSSTATUS (0x014) | ||
15681 | +#define ISPCSI2_SYSSTATUS_RESET_DONE (1 << 0) | ||
15682 | + | ||
15683 | +#define ISPCSI2_IRQSTATUS (0x018) | ||
15684 | +#define ISPCSI2_IRQSTATUS_OCP_ERR_IRQ (1 << 14) | ||
15685 | +#define ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ (1 << 13) | ||
15686 | +#define ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 12) | ||
15687 | +#define ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ (1 << 11) | ||
15688 | +#define ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ (1 << 10) | ||
15689 | +#define ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ (1 << 9) | ||
15690 | +#define ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ (1 << 8) | ||
15691 | +#define ISPCSI2_IRQSTATUS_CONTEXT(n) (1 << (n)) | ||
15692 | + | ||
15693 | +#define ISPCSI2_IRQENABLE (0x01c) | ||
15694 | +#define ISPCSI2_CTRL (0x040) | ||
15695 | +#define ISPCSI2_CTRL_VP_CLK_EN (1 << 15) | ||
15696 | +#define ISPCSI2_CTRL_VP_ONLY_EN (1 << 11) | ||
15697 | +#define ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT 8 | ||
15698 | +#define ISPCSI2_CTRL_VP_OUT_CTRL_MASK \ | ||
15699 | + (3 << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) | ||
15700 | +#define ISPCSI2_CTRL_DBG_EN (1 << 7) | ||
15701 | +#define ISPCSI2_CTRL_BURST_SIZE_SHIFT 5 | ||
15702 | +#define ISPCSI2_CTRL_BURST_SIZE_MASK \ | ||
15703 | + (3 << ISPCSI2_CTRL_BURST_SIZE_SHIFT) | ||
15704 | +#define ISPCSI2_CTRL_FRAME (1 << 3) | ||
15705 | +#define ISPCSI2_CTRL_ECC_EN (1 << 2) | ||
15706 | +#define ISPCSI2_CTRL_SECURE (1 << 1) | ||
15707 | +#define ISPCSI2_CTRL_IF_EN (1 << 0) | ||
15708 | + | ||
15709 | +#define ISPCSI2_DBG_H (0x044) | ||
15710 | +#define ISPCSI2_GNQ (0x048) | ||
15711 | +#define ISPCSI2_PHY_CFG (0x050) | ||
15712 | +#define ISPCSI2_PHY_CFG_RESET_CTRL (1 << 30) | ||
15713 | +#define ISPCSI2_PHY_CFG_RESET_DONE (1 << 29) | ||
15714 | +#define ISPCSI2_PHY_CFG_PWR_CMD_SHIFT 27 | ||
15715 | +#define ISPCSI2_PHY_CFG_PWR_CMD_MASK \ | ||
15716 | + (0x3 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) | ||
15717 | +#define ISPCSI2_PHY_CFG_PWR_CMD_OFF \ | ||
15718 | + (0x0 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) | ||
15719 | +#define ISPCSI2_PHY_CFG_PWR_CMD_ON \ | ||
15720 | + (0x1 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) | ||
15721 | +#define ISPCSI2_PHY_CFG_PWR_CMD_ULPW \ | ||
15722 | + (0x2 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) | ||
15723 | +#define ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT 25 | ||
15724 | +#define ISPCSI2_PHY_CFG_PWR_STATUS_MASK \ | ||
15725 | + (0x3 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) | ||
15726 | +#define ISPCSI2_PHY_CFG_PWR_STATUS_OFF \ | ||
15727 | + (0x0 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) | ||
15728 | +#define ISPCSI2_PHY_CFG_PWR_STATUS_ON \ | ||
15729 | + (0x1 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) | ||
15730 | +#define ISPCSI2_PHY_CFG_PWR_STATUS_ULPW \ | ||
15731 | + (0x2 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) | ||
15732 | +#define ISPCSI2_PHY_CFG_PWR_AUTO (1 << 24) | ||
15733 | + | ||
15734 | +#define ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n) (3 + ((n) * 4)) | ||
15735 | +#define ISPCSI2_PHY_CFG_DATA_POL_MASK(n) \ | ||
15736 | + (0x1 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n)) | ||
15737 | +#define ISPCSI2_PHY_CFG_DATA_POL_PN(n) \ | ||
15738 | + (0x0 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n)) | ||
15739 | +#define ISPCSI2_PHY_CFG_DATA_POL_NP(n) \ | ||
15740 | + (0x1 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n)) | ||
15741 | + | ||
15742 | +#define ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n) ((n) * 4) | ||
15743 | +#define ISPCSI2_PHY_CFG_DATA_POSITION_MASK(n) \ | ||
15744 | + (0x7 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
15745 | +#define ISPCSI2_PHY_CFG_DATA_POSITION_NC(n) \ | ||
15746 | + (0x0 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
15747 | +#define ISPCSI2_PHY_CFG_DATA_POSITION_1(n) \ | ||
15748 | + (0x1 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
15749 | +#define ISPCSI2_PHY_CFG_DATA_POSITION_2(n) \ | ||
15750 | + (0x2 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
15751 | +#define ISPCSI2_PHY_CFG_DATA_POSITION_3(n) \ | ||
15752 | + (0x3 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
15753 | +#define ISPCSI2_PHY_CFG_DATA_POSITION_4(n) \ | ||
15754 | + (0x4 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
15755 | +#define ISPCSI2_PHY_CFG_DATA_POSITION_5(n) \ | ||
15756 | + (0x5 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
15757 | + | ||
15758 | +#define ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT 3 | ||
15759 | +#define ISPCSI2_PHY_CFG_CLOCK_POL_MASK \ | ||
15760 | + (0x1 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT) | ||
15761 | +#define ISPCSI2_PHY_CFG_CLOCK_POL_PN \ | ||
15762 | + (0x0 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT) | ||
15763 | +#define ISPCSI2_PHY_CFG_CLOCK_POL_NP \ | ||
15764 | + (0x1 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT) | ||
15765 | + | ||
15766 | +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT 0 | ||
15767 | +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK \ | ||
15768 | + (0x7 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) | ||
15769 | +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_1 \ | ||
15770 | + (0x1 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) | ||
15771 | +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_2 \ | ||
15772 | + (0x2 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) | ||
15773 | +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_3 \ | ||
15774 | + (0x3 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) | ||
15775 | +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_4 \ | ||
15776 | + (0x4 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) | ||
15777 | +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_5 \ | ||
15778 | + (0x5 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) | ||
15779 | + | ||
15780 | +#define ISPCSI2_PHY_IRQSTATUS (0x054) | ||
15781 | +#define ISPCSI2_PHY_IRQSTATUS_STATEALLULPMEXIT (1 << 26) | ||
15782 | +#define ISPCSI2_PHY_IRQSTATUS_STATEALLULPMENTER (1 << 25) | ||
15783 | +#define ISPCSI2_PHY_IRQSTATUS_STATEULPM5 (1 << 24) | ||
15784 | +#define ISPCSI2_PHY_IRQSTATUS_STATEULPM4 (1 << 23) | ||
15785 | +#define ISPCSI2_PHY_IRQSTATUS_STATEULPM3 (1 << 22) | ||
15786 | +#define ISPCSI2_PHY_IRQSTATUS_STATEULPM2 (1 << 21) | ||
15787 | +#define ISPCSI2_PHY_IRQSTATUS_STATEULPM1 (1 << 20) | ||
15788 | +#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL5 (1 << 19) | ||
15789 | +#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL4 (1 << 18) | ||
15790 | +#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL3 (1 << 17) | ||
15791 | +#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL2 (1 << 16) | ||
15792 | +#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL1 (1 << 15) | ||
15793 | +#define ISPCSI2_PHY_IRQSTATUS_ERRESC5 (1 << 14) | ||
15794 | +#define ISPCSI2_PHY_IRQSTATUS_ERRESC4 (1 << 13) | ||
15795 | +#define ISPCSI2_PHY_IRQSTATUS_ERRESC3 (1 << 12) | ||
15796 | +#define ISPCSI2_PHY_IRQSTATUS_ERRESC2 (1 << 11) | ||
15797 | +#define ISPCSI2_PHY_IRQSTATUS_ERRESC1 (1 << 10) | ||
15798 | +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS5 (1 << 9) | ||
15799 | +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS4 (1 << 8) | ||
15800 | +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS3 (1 << 7) | ||
15801 | +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS2 (1 << 6) | ||
15802 | +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS1 (1 << 5) | ||
15803 | +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS5 (1 << 4) | ||
15804 | +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS4 (1 << 3) | ||
15805 | +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS3 (1 << 2) | ||
15806 | +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS2 (1 << 1) | ||
15807 | +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS1 1 | ||
15808 | + | ||
15809 | +#define ISPCSI2_SHORT_PACKET (0x05c) | ||
15810 | +#define ISPCSI2_PHY_IRQENABLE (0x060) | ||
15811 | +#define ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT (1 << 26) | ||
15812 | +#define ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER (1 << 25) | ||
15813 | +#define ISPCSI2_PHY_IRQENABLE_STATEULPM5 (1 << 24) | ||
15814 | +#define ISPCSI2_PHY_IRQENABLE_STATEULPM4 (1 << 23) | ||
15815 | +#define ISPCSI2_PHY_IRQENABLE_STATEULPM3 (1 << 22) | ||
15816 | +#define ISPCSI2_PHY_IRQENABLE_STATEULPM2 (1 << 21) | ||
15817 | +#define ISPCSI2_PHY_IRQENABLE_STATEULPM1 (1 << 20) | ||
15818 | +#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 (1 << 19) | ||
15819 | +#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 (1 << 18) | ||
15820 | +#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 (1 << 17) | ||
15821 | +#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 (1 << 16) | ||
15822 | +#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 (1 << 15) | ||
15823 | +#define ISPCSI2_PHY_IRQENABLE_ERRESC5 (1 << 14) | ||
15824 | +#define ISPCSI2_PHY_IRQENABLE_ERRESC4 (1 << 13) | ||
15825 | +#define ISPCSI2_PHY_IRQENABLE_ERRESC3 (1 << 12) | ||
15826 | +#define ISPCSI2_PHY_IRQENABLE_ERRESC2 (1 << 11) | ||
15827 | +#define ISPCSI2_PHY_IRQENABLE_ERRESC1 (1 << 10) | ||
15828 | +#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 (1 << 9) | ||
15829 | +#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 (1 << 8) | ||
15830 | +#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 (1 << 7) | ||
15831 | +#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 (1 << 6) | ||
15832 | +#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 (1 << 5) | ||
15833 | +#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 (1 << 4) | ||
15834 | +#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 (1 << 3) | ||
15835 | +#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 (1 << 2) | ||
15836 | +#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 (1 << 1) | ||
15837 | +#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS1 (1 << 0) | ||
15838 | + | ||
15839 | +#define ISPCSI2_DBG_P (0x068) | ||
15840 | +#define ISPCSI2_TIMING (0x06c) | ||
15841 | +#define ISPCSI2_TIMING_FORCE_RX_MODE_IO(n) (1 << ((16 * ((n) - 1)) + 15)) | ||
15842 | +#define ISPCSI2_TIMING_STOP_STATE_X16_IO(n) (1 << ((16 * ((n) - 1)) + 14)) | ||
15843 | +#define ISPCSI2_TIMING_STOP_STATE_X4_IO(n) (1 << ((16 * ((n) - 1)) + 13)) | ||
15844 | +#define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n) (16 * ((n) - 1)) | ||
15845 | +#define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(n) \ | ||
15846 | + (0x1fff << ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n)) | ||
15847 | + | ||
15848 | +#define ISPCSI2_CTX_CTRL1(n) ((0x070) + 0x20 * (n)) | ||
15849 | +#define ISPCSI2_CTX_CTRL1_COUNT_SHIFT 8 | ||
15850 | +#define ISPCSI2_CTX_CTRL1_COUNT_MASK \ | ||
15851 | + (0xff << ISPCSI2_CTX_CTRL1_COUNT_SHIFT) | ||
15852 | +#define ISPCSI2_CTX_CTRL1_EOF_EN (1 << 7) | ||
15853 | +#define ISPCSI2_CTX_CTRL1_EOL_EN (1 << 6) | ||
15854 | +#define ISPCSI2_CTX_CTRL1_CS_EN (1 << 5) | ||
15855 | +#define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK (1 << 4) | ||
15856 | +#define ISPCSI2_CTX_CTRL1_PING_PONG (1 << 3) | ||
15857 | +#define ISPCSI2_CTX_CTRL1_CTX_EN (1 << 0) | ||
15858 | + | ||
15859 | +#define ISPCSI2_CTX_CTRL2(n) ((0x074) + 0x20 * (n)) | ||
15860 | +#define ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT 13 | ||
15861 | +#define ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK \ | ||
15862 | + (0x3 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT) | ||
15863 | +#define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT 11 | ||
15864 | +#define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK \ | ||
15865 | + (0x3 << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT) | ||
15866 | +#define ISPCSI2_CTX_CTRL2_DPCM_PRED (1 << 10) | ||
15867 | +#define ISPCSI2_CTX_CTRL2_FORMAT_SHIFT 0 | ||
15868 | +#define ISPCSI2_CTX_CTRL2_FORMAT_MASK \ | ||
15869 | + (0x3ff << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT) | ||
15870 | +#define ISPCSI2_CTX_CTRL2_FRAME_SHIFT 16 | ||
15871 | +#define ISPCSI2_CTX_CTRL2_FRAME_MASK \ | ||
15872 | + (0xffff << ISPCSI2_CTX_CTRL2_FRAME_SHIFT) | ||
15873 | + | ||
15874 | +#define ISPCSI2_CTX_DAT_OFST(n) ((0x078) + 0x20 * (n)) | ||
15875 | +#define ISPCSI2_CTX_DAT_OFST_OFST_SHIFT 0 | ||
15876 | +#define ISPCSI2_CTX_DAT_OFST_OFST_MASK \ | ||
15877 | + (0x1ffe0 << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT) | ||
15878 | + | ||
15879 | +#define ISPCSI2_CTX_DAT_PING_ADDR(n) ((0x07c) + 0x20 * (n)) | ||
15880 | +#define ISPCSI2_CTX_DAT_PONG_ADDR(n) ((0x080) + 0x20 * (n)) | ||
15881 | +#define ISPCSI2_CTX_IRQENABLE(n) ((0x084) + 0x20 * (n)) | ||
15882 | +#define ISPCSI2_CTX_IRQENABLE_ECC_CORRECTION_IRQ (1 << 8) | ||
15883 | +#define ISPCSI2_CTX_IRQENABLE_LINE_NUMBER_IRQ (1 << 7) | ||
15884 | +#define ISPCSI2_CTX_IRQENABLE_FRAME_NUMBER_IRQ (1 << 6) | ||
15885 | +#define ISPCSI2_CTX_IRQENABLE_CS_IRQ (1 << 5) | ||
15886 | +#define ISPCSI2_CTX_IRQENABLE_LE_IRQ (1 << 3) | ||
15887 | +#define ISPCSI2_CTX_IRQENABLE_LS_IRQ (1 << 2) | ||
15888 | +#define ISPCSI2_CTX_IRQENABLE_FE_IRQ (1 << 1) | ||
15889 | +#define ISPCSI2_CTX_IRQENABLE_FS_IRQ (1 << 0) | ||
15890 | + | ||
15891 | +#define ISPCSI2_CTX_IRQSTATUS(n) ((0x088) + 0x20 * (n)) | ||
15892 | +#define ISPCSI2_CTX_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 8) | ||
15893 | +#define ISPCSI2_CTX_IRQSTATUS_LINE_NUMBER_IRQ (1 << 7) | ||
15894 | +#define ISPCSI2_CTX_IRQSTATUS_FRAME_NUMBER_IRQ (1 << 6) | ||
15895 | +#define ISPCSI2_CTX_IRQSTATUS_CS_IRQ (1 << 5) | ||
15896 | +#define ISPCSI2_CTX_IRQSTATUS_LE_IRQ (1 << 3) | ||
15897 | +#define ISPCSI2_CTX_IRQSTATUS_LS_IRQ (1 << 2) | ||
15898 | +#define ISPCSI2_CTX_IRQSTATUS_FE_IRQ (1 << 1) | ||
15899 | +#define ISPCSI2_CTX_IRQSTATUS_FS_IRQ (1 << 0) | ||
15900 | + | ||
15901 | +#define ISPCSI2_CTX_CTRL3(n) ((0x08c) + 0x20 * (n)) | ||
15902 | +#define ISPCSI2_CTX_CTRL3_ALPHA_SHIFT 5 | ||
15903 | +#define ISPCSI2_CTX_CTRL3_ALPHA_MASK \ | ||
15904 | + (0x3fff << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT) | ||
15905 | + | ||
15906 | +/* This instance is for OMAP3630 only */ | ||
15907 | +#define ISPCSI2_CTX_TRANSCODEH(n) (0x000 + 0x8 * (n)) | ||
15908 | +#define ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT 16 | ||
15909 | +#define ISPCSI2_CTX_TRANSCODEH_HCOUNT_MASK \ | ||
15910 | + (0x1fff << ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT) | ||
15911 | +#define ISPCSI2_CTX_TRANSCODEH_HSKIP_SHIFT 0 | ||
15912 | +#define ISPCSI2_CTX_TRANSCODEH_HSKIP_MASK \ | ||
15913 | + (0x1fff << ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT) | ||
15914 | +#define ISPCSI2_CTX_TRANSCODEV(n) (0x004 + 0x8 * (n)) | ||
15915 | +#define ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT 16 | ||
15916 | +#define ISPCSI2_CTX_TRANSCODEV_VCOUNT_MASK \ | ||
15917 | + (0x1fff << ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT) | ||
15918 | +#define ISPCSI2_CTX_TRANSCODEV_VSKIP_SHIFT 0 | ||
15919 | +#define ISPCSI2_CTX_TRANSCODEV_VSKIP_MASK \ | ||
15920 | + (0x1fff << ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT) | ||
15921 | + | ||
15922 | +/* ----------------------------------------------------------------------------- | ||
15923 | + * CSI PHY registers | ||
15924 | + */ | ||
15925 | + | ||
15926 | +#define ISPCSIPHY_REG0 (0x000) | ||
15927 | +#define ISPCSIPHY_REG0_THS_TERM_SHIFT 8 | ||
15928 | +#define ISPCSIPHY_REG0_THS_TERM_MASK \ | ||
15929 | + (0xff << ISPCSIPHY_REG0_THS_TERM_SHIFT) | ||
15930 | +#define ISPCSIPHY_REG0_THS_SETTLE_SHIFT 0 | ||
15931 | +#define ISPCSIPHY_REG0_THS_SETTLE_MASK \ | ||
15932 | + (0xff << ISPCSIPHY_REG0_THS_SETTLE_SHIFT) | ||
15933 | + | ||
15934 | +#define ISPCSIPHY_REG1 (0x004) | ||
15935 | +#define ISPCSIPHY_REG1_RESET_DONE_CTRLCLK (1 << 29) | ||
15936 | +/* This field is for OMAP3630 only */ | ||
15937 | +#define ISPCSIPHY_REG1_CLOCK_MISS_DETECTOR_STATUS (1 << 25) | ||
15938 | +#define ISPCSIPHY_REG1_TCLK_TERM_SHIFT 18 | ||
15939 | +#define ISPCSIPHY_REG1_TCLK_TERM_MASK \ | ||
15940 | + (0x7f << ISPCSIPHY_REG1_TCLK_TERM_SHIFT) | ||
15941 | +#define ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN_SHIFT 10 | ||
15942 | +#define ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN_MASK \ | ||
15943 | + (0xff << ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN) | ||
15944 | +/* This field is for OMAP3430 only */ | ||
15945 | +#define ISPCSIPHY_REG1_TCLK_MISS_SHIFT 8 | ||
15946 | +#define ISPCSIPHY_REG1_TCLK_MISS_MASK \ | ||
15947 | + (0x3 << ISPCSIPHY_REG1_TCLK_MISS_SHIFT) | ||
15948 | +/* This field is for OMAP3630 only */ | ||
15949 | +#define ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT 8 | ||
15950 | +#define ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_MASK \ | ||
15951 | + (0x3 << ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT) | ||
15952 | +#define ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT 0 | ||
15953 | +#define ISPCSIPHY_REG1_TCLK_SETTLE_MASK \ | ||
15954 | + (0xff << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT) | ||
15955 | + | ||
15956 | +/* This register is for OMAP3630 only */ | ||
15957 | +#define ISPCSIPHY_REG2 (0x008) | ||
15958 | +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_SHIFT 30 | ||
15959 | +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_MASK \ | ||
15960 | + (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_SHIFT) | ||
15961 | +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_SHIFT 28 | ||
15962 | +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_MASK \ | ||
15963 | + (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_SHIFT) | ||
15964 | +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_SHIFT 26 | ||
15965 | +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_MASK \ | ||
15966 | + (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_SHIFT) | ||
15967 | +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_SHIFT 24 | ||
15968 | +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_MASK \ | ||
15969 | + (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_SHIFT) | ||
15970 | +#define ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_SHIFT 0 | ||
15971 | +#define ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_MASK \ | ||
15972 | + (0x7fffff << ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_SHIFT) | ||
15973 | + | ||
15974 | +#endif /* OMAP3_ISP_REG_H */ | ||
15975 | diff --git a/drivers/media/video/isp/ispresizer.c b/drivers/media/video/isp/ispresizer.c | ||
15976 | new file mode 100644 | ||
15977 | index 0000000..a696450 | ||
15978 | --- /dev/null | ||
15979 | +++ b/drivers/media/video/isp/ispresizer.c | ||
15980 | @@ -0,0 +1,1710 @@ | ||
15981 | +/* | ||
15982 | + * ispresizer.c | ||
15983 | + * | ||
15984 | + * TI OMAP3 ISP - Resizer module | ||
15985 | + * | ||
15986 | + * Copyright (C) 2010 Nokia Corporation | ||
15987 | + * Copyright (C) 2009 Texas Instruments, Inc | ||
15988 | + * | ||
15989 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
15990 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
15991 | + * | ||
15992 | + * This program is free software; you can redistribute it and/or modify | ||
15993 | + * it under the terms of the GNU General Public License version 2 as | ||
15994 | + * published by the Free Software Foundation. | ||
15995 | + * | ||
15996 | + * This program is distributed in the hope that it will be useful, but | ||
15997 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15998 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15999 | + * General Public License for more details. | ||
16000 | + * | ||
16001 | + * You should have received a copy of the GNU General Public License | ||
16002 | + * along with this program; if not, write to the Free Software | ||
16003 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
16004 | + * 02110-1301 USA | ||
16005 | + */ | ||
16006 | + | ||
16007 | +#include <linux/device.h> | ||
16008 | +#include <linux/mm.h> | ||
16009 | +#include <linux/module.h> | ||
16010 | + | ||
16011 | +#include "isp.h" | ||
16012 | +#include "ispreg.h" | ||
16013 | +#include "ispresizer.h" | ||
16014 | + | ||
16015 | +/* | ||
16016 | + * Resizer Constants | ||
16017 | + */ | ||
16018 | +#define MIN_RESIZE_VALUE 64 | ||
16019 | +#define MID_RESIZE_VALUE 512 | ||
16020 | +#define MAX_RESIZE_VALUE 1024 | ||
16021 | + | ||
16022 | +#define MIN_IN_WIDTH 32 | ||
16023 | +#define MIN_IN_HEIGHT 32 | ||
16024 | +#define MAX_IN_WIDTH_MEMORY_MODE 4095 | ||
16025 | +#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES1 1280 | ||
16026 | +#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES2 4095 | ||
16027 | +#define MAX_IN_HEIGHT 4095 | ||
16028 | + | ||
16029 | +#define MIN_OUT_WIDTH 16 | ||
16030 | +#define MIN_OUT_HEIGHT 2 | ||
16031 | +#define MAX_OUT_HEIGHT 4095 | ||
16032 | + | ||
16033 | +/* | ||
16034 | + * Resizer Use Constraints | ||
16035 | + * "TRM ES3.1, table 12-46" | ||
16036 | + */ | ||
16037 | +#define MAX_4TAP_OUT_WIDTH_ES1 1280 | ||
16038 | +#define MAX_7TAP_OUT_WIDTH_ES1 640 | ||
16039 | +#define MAX_4TAP_OUT_WIDTH_ES2 3312 | ||
16040 | +#define MAX_7TAP_OUT_WIDTH_ES2 1650 | ||
16041 | +#define MAX_4TAP_OUT_WIDTH_3630 4096 | ||
16042 | +#define MAX_7TAP_OUT_WIDTH_3630 2048 | ||
16043 | + | ||
16044 | +/* | ||
16045 | + * Constants for ratio calculation | ||
16046 | + */ | ||
16047 | +#define RESIZE_DIVISOR 256 | ||
16048 | +#define DEFAULT_PHASE 1 | ||
16049 | + | ||
16050 | +/* | ||
16051 | + * Default (and only) configuration of filter coefficients. | ||
16052 | + * 7-tap mode is for scale factors 0.25x to 0.5x. | ||
16053 | + * 4-tap mode is for scale factors 0.5x to 4.0x. | ||
16054 | + * There shouldn't be any reason to recalculate these, EVER. | ||
16055 | + */ | ||
16056 | +static const struct isprsz_coef filter_coefs = { | ||
16057 | + /* For 8-phase 4-tap horizontal filter: */ | ||
16058 | + { | ||
16059 | + 0x0000, 0x0100, 0x0000, 0x0000, | ||
16060 | + 0x03FA, 0x00F6, 0x0010, 0x0000, | ||
16061 | + 0x03F9, 0x00DB, 0x002C, 0x0000, | ||
16062 | + 0x03FB, 0x00B3, 0x0053, 0x03FF, | ||
16063 | + 0x03FD, 0x0082, 0x0084, 0x03FD, | ||
16064 | + 0x03FF, 0x0053, 0x00B3, 0x03FB, | ||
16065 | + 0x0000, 0x002C, 0x00DB, 0x03F9, | ||
16066 | + 0x0000, 0x0010, 0x00F6, 0x03FA | ||
16067 | + }, | ||
16068 | + /* For 8-phase 4-tap vertical filter: */ | ||
16069 | + { | ||
16070 | + 0x0000, 0x0100, 0x0000, 0x0000, | ||
16071 | + 0x03FA, 0x00F6, 0x0010, 0x0000, | ||
16072 | + 0x03F9, 0x00DB, 0x002C, 0x0000, | ||
16073 | + 0x03FB, 0x00B3, 0x0053, 0x03FF, | ||
16074 | + 0x03FD, 0x0082, 0x0084, 0x03FD, | ||
16075 | + 0x03FF, 0x0053, 0x00B3, 0x03FB, | ||
16076 | + 0x0000, 0x002C, 0x00DB, 0x03F9, | ||
16077 | + 0x0000, 0x0010, 0x00F6, 0x03FA | ||
16078 | + }, | ||
16079 | + /* For 4-phase 7-tap horizontal filter: */ | ||
16080 | + #define DUMMY 0 | ||
16081 | + { | ||
16082 | + 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY, | ||
16083 | + 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY, | ||
16084 | + 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY, | ||
16085 | + 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY | ||
16086 | + }, | ||
16087 | + /* For 4-phase 7-tap vertical filter: */ | ||
16088 | + { | ||
16089 | + 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY, | ||
16090 | + 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY, | ||
16091 | + 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY, | ||
16092 | + 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY | ||
16093 | + } | ||
16094 | + /* | ||
16095 | + * The dummy padding is required in 7-tap mode because of how the | ||
16096 | + * registers are arranged physically. | ||
16097 | + */ | ||
16098 | + #undef DUMMY | ||
16099 | +}; | ||
16100 | + | ||
16101 | +/* | ||
16102 | + * __resizer_get_format - helper function for getting resizer format | ||
16103 | + * @res : pointer to resizer private structure | ||
16104 | + * @pad : pad number | ||
16105 | + * @fh : V4L2 subdev file handle | ||
16106 | + * @which : wanted subdev format | ||
16107 | + * return zero | ||
16108 | + */ | ||
16109 | +static struct v4l2_mbus_framefmt * | ||
16110 | +__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh, | ||
16111 | + unsigned int pad, enum v4l2_subdev_format_whence which) | ||
16112 | +{ | ||
16113 | + if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
16114 | + return v4l2_subdev_get_try_format(fh, pad); | ||
16115 | + else | ||
16116 | + return &res->formats[pad]; | ||
16117 | +} | ||
16118 | + | ||
16119 | +/* | ||
16120 | + * __resizer_get_crop - helper function for getting resizer crop rectangle | ||
16121 | + * @res : pointer to resizer private structure | ||
16122 | + * @fh : V4L2 subdev file handle | ||
16123 | + * @which : wanted subdev crop rectangle | ||
16124 | + */ | ||
16125 | +static struct v4l2_rect * | ||
16126 | +__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_fh *fh, | ||
16127 | + enum v4l2_subdev_format_whence which) | ||
16128 | +{ | ||
16129 | + if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
16130 | + return v4l2_subdev_get_try_crop(fh, RESZ_PAD_SINK); | ||
16131 | + else | ||
16132 | + return &res->crop.request; | ||
16133 | +} | ||
16134 | + | ||
16135 | +/* | ||
16136 | + * resizer_set_filters - Set resizer filters | ||
16137 | + * @res: Device context. | ||
16138 | + * @h_coeff: horizontal coefficient | ||
16139 | + * @v_coeff: vertical coefficient | ||
16140 | + * Return none | ||
16141 | + */ | ||
16142 | +static void resizer_set_filters(struct isp_res_device *res, const u16 *h_coeff, | ||
16143 | + const u16 *v_coeff) | ||
16144 | +{ | ||
16145 | + struct isp_device *isp = to_isp_device(res); | ||
16146 | + u32 startaddr_h, startaddr_v, tmp_h, tmp_v; | ||
16147 | + int i; | ||
16148 | + | ||
16149 | + startaddr_h = ISPRSZ_HFILT10; | ||
16150 | + startaddr_v = ISPRSZ_VFILT10; | ||
16151 | + | ||
16152 | + for (i = 0; i < COEFF_CNT; i += 2) { | ||
16153 | + tmp_h = h_coeff[i] | | ||
16154 | + (h_coeff[i + 1] << ISPRSZ_HFILT_COEF1_SHIFT); | ||
16155 | + tmp_v = v_coeff[i] | | ||
16156 | + (v_coeff[i + 1] << ISPRSZ_VFILT_COEF1_SHIFT); | ||
16157 | + isp_reg_writel(isp, tmp_h, OMAP3_ISP_IOMEM_RESZ, startaddr_h); | ||
16158 | + isp_reg_writel(isp, tmp_v, OMAP3_ISP_IOMEM_RESZ, startaddr_v); | ||
16159 | + startaddr_h += 4; | ||
16160 | + startaddr_v += 4; | ||
16161 | + } | ||
16162 | +} | ||
16163 | + | ||
16164 | +/* | ||
16165 | + * resizer_set_bilinear - Chrominance horizontal algorithm select | ||
16166 | + * @res: Device context. | ||
16167 | + * @type: Filtering interpolation type. | ||
16168 | + * | ||
16169 | + * Filtering that is same as luminance processing is | ||
16170 | + * intended only for downsampling, and bilinear interpolation | ||
16171 | + * is intended only for upsampling. | ||
16172 | + */ | ||
16173 | +static void resizer_set_bilinear(struct isp_res_device *res, | ||
16174 | + enum resizer_chroma_algo type) | ||
16175 | +{ | ||
16176 | + struct isp_device *isp = to_isp_device(res); | ||
16177 | + | ||
16178 | + if (type == RSZ_BILINEAR) | ||
16179 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
16180 | + ISPRSZ_CNT_CBILIN); | ||
16181 | + else | ||
16182 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
16183 | + ISPRSZ_CNT_CBILIN); | ||
16184 | +} | ||
16185 | + | ||
16186 | +/* | ||
16187 | + * resizer_set_ycpos - Luminance and chrominance order | ||
16188 | + * @res: Device context. | ||
16189 | + * @order: order type. | ||
16190 | + */ | ||
16191 | +static void resizer_set_ycpos(struct isp_res_device *res, | ||
16192 | + enum v4l2_mbus_pixelcode pixelcode) | ||
16193 | +{ | ||
16194 | + struct isp_device *isp = to_isp_device(res); | ||
16195 | + | ||
16196 | + switch (pixelcode) { | ||
16197 | + case V4L2_MBUS_FMT_YUYV8_1X16: | ||
16198 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
16199 | + ISPRSZ_CNT_YCPOS); | ||
16200 | + break; | ||
16201 | + case V4L2_MBUS_FMT_UYVY8_1X16: | ||
16202 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
16203 | + ISPRSZ_CNT_YCPOS); | ||
16204 | + break; | ||
16205 | + default: | ||
16206 | + return; | ||
16207 | + } | ||
16208 | +} | ||
16209 | + | ||
16210 | +/* | ||
16211 | + * resizer_set_phase - Setup horizontal and vertical starting phase | ||
16212 | + * @res: Device context. | ||
16213 | + * @h_phase: horizontal phase parameters. | ||
16214 | + * @v_phase: vertical phase parameters. | ||
16215 | + * | ||
16216 | + * Horizontal and vertical phase range is 0 to 7 | ||
16217 | + */ | ||
16218 | +static void resizer_set_phase(struct isp_res_device *res, u32 h_phase, | ||
16219 | + u32 v_phase) | ||
16220 | +{ | ||
16221 | + struct isp_device *isp = to_isp_device(res); | ||
16222 | + u32 rgval = 0; | ||
16223 | + | ||
16224 | + rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & | ||
16225 | + ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK); | ||
16226 | + rgval |= (h_phase << ISPRSZ_CNT_HSTPH_SHIFT) & ISPRSZ_CNT_HSTPH_MASK; | ||
16227 | + rgval |= (v_phase << ISPRSZ_CNT_VSTPH_SHIFT) & ISPRSZ_CNT_VSTPH_MASK; | ||
16228 | + | ||
16229 | + isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT); | ||
16230 | +} | ||
16231 | + | ||
16232 | +/* | ||
16233 | + * resizer_set_luma - Setup luminance enhancer parameters | ||
16234 | + * @res: Device context. | ||
16235 | + * @luma: Structure for luminance enhancer parameters. | ||
16236 | + * | ||
16237 | + * Algorithm select: | ||
16238 | + * 0x0: Disable | ||
16239 | + * 0x1: [-1 2 -1]/2 high-pass filter | ||
16240 | + * 0x2: [-1 -2 6 -2 -1]/4 high-pass filter | ||
16241 | + * | ||
16242 | + * Maximum gain: | ||
16243 | + * The data is coded in U4Q4 representation. | ||
16244 | + * | ||
16245 | + * Slope: | ||
16246 | + * The data is coded in U4Q4 representation. | ||
16247 | + * | ||
16248 | + * Coring offset: | ||
16249 | + * The data is coded in U8Q0 representation. | ||
16250 | + * | ||
16251 | + * The new luminance value is computed as: | ||
16252 | + * Y += HPF(Y) x max(GAIN, (HPF(Y) - CORE) x SLOP + 8) >> 4. | ||
16253 | + */ | ||
16254 | +static void resizer_set_luma(struct isp_res_device *res, | ||
16255 | + struct resizer_luma_yenh *luma) | ||
16256 | +{ | ||
16257 | + struct isp_device *isp = to_isp_device(res); | ||
16258 | + u32 rgval = 0; | ||
16259 | + | ||
16260 | + rgval = (luma->algo << ISPRSZ_YENH_ALGO_SHIFT) | ||
16261 | + & ISPRSZ_YENH_ALGO_MASK; | ||
16262 | + rgval |= (luma->gain << ISPRSZ_YENH_GAIN_SHIFT) | ||
16263 | + & ISPRSZ_YENH_GAIN_MASK; | ||
16264 | + rgval |= (luma->slope << ISPRSZ_YENH_SLOP_SHIFT) | ||
16265 | + & ISPRSZ_YENH_SLOP_MASK; | ||
16266 | + rgval |= (luma->core << ISPRSZ_YENH_CORE_SHIFT) | ||
16267 | + & ISPRSZ_YENH_CORE_MASK; | ||
16268 | + | ||
16269 | + isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH); | ||
16270 | +} | ||
16271 | + | ||
16272 | +/* | ||
16273 | + * resizer_set_source - Input source select | ||
16274 | + * @res: Device context. | ||
16275 | + * @source: Input source type | ||
16276 | + * | ||
16277 | + * If this field is set to RESIZER_INPUT_VP, the resizer input is fed from | ||
16278 | + * Preview/CCDC engine, otherwise from memory. | ||
16279 | + */ | ||
16280 | +static void resizer_set_source(struct isp_res_device *res, | ||
16281 | + enum resizer_input_entity source) | ||
16282 | +{ | ||
16283 | + struct isp_device *isp = to_isp_device(res); | ||
16284 | + | ||
16285 | + if (source == RESIZER_INPUT_MEMORY) | ||
16286 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
16287 | + ISPRSZ_CNT_INPSRC); | ||
16288 | + else | ||
16289 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
16290 | + ISPRSZ_CNT_INPSRC); | ||
16291 | +} | ||
16292 | + | ||
16293 | +/* | ||
16294 | + * resizer_set_ratio - Setup horizontal and vertical resizing value | ||
16295 | + * @res: Device context. | ||
16296 | + * @ratio: Structure for ratio parameters. | ||
16297 | + * | ||
16298 | + * Resizing range from 64 to 1024 | ||
16299 | + */ | ||
16300 | +static void resizer_set_ratio(struct isp_res_device *res, | ||
16301 | + const struct resizer_ratio *ratio) | ||
16302 | +{ | ||
16303 | + struct isp_device *isp = to_isp_device(res); | ||
16304 | + const u16 *h_filter, *v_filter; | ||
16305 | + u32 rgval = 0; | ||
16306 | + | ||
16307 | + rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & | ||
16308 | + ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK); | ||
16309 | + rgval |= ((ratio->horz - 1) << ISPRSZ_CNT_HRSZ_SHIFT) | ||
16310 | + & ISPRSZ_CNT_HRSZ_MASK; | ||
16311 | + rgval |= ((ratio->vert - 1) << ISPRSZ_CNT_VRSZ_SHIFT) | ||
16312 | + & ISPRSZ_CNT_VRSZ_MASK; | ||
16313 | + isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT); | ||
16314 | + | ||
16315 | + /* prepare horizontal filter coefficients */ | ||
16316 | + if (ratio->horz > MID_RESIZE_VALUE) | ||
16317 | + h_filter = &filter_coefs.h_filter_coef_7tap[0]; | ||
16318 | + else | ||
16319 | + h_filter = &filter_coefs.h_filter_coef_4tap[0]; | ||
16320 | + | ||
16321 | + /* prepare vertical filter coefficients */ | ||
16322 | + if (ratio->vert > MID_RESIZE_VALUE) | ||
16323 | + v_filter = &filter_coefs.v_filter_coef_7tap[0]; | ||
16324 | + else | ||
16325 | + v_filter = &filter_coefs.v_filter_coef_4tap[0]; | ||
16326 | + | ||
16327 | + resizer_set_filters(res, h_filter, v_filter); | ||
16328 | +} | ||
16329 | + | ||
16330 | +/* | ||
16331 | + * resizer_set_dst_size - Setup the output height and width | ||
16332 | + * @res: Device context. | ||
16333 | + * @width: Output width. | ||
16334 | + * @height: Output height. | ||
16335 | + * | ||
16336 | + * Width : | ||
16337 | + * The value must be EVEN. | ||
16338 | + * | ||
16339 | + * Height: | ||
16340 | + * The number of bytes written to SDRAM must be | ||
16341 | + * a multiple of 16-bytes if the vertical resizing factor | ||
16342 | + * is greater than 1x (upsizing) | ||
16343 | + */ | ||
16344 | +static void resizer_set_output_size(struct isp_res_device *res, | ||
16345 | + u32 width, u32 height) | ||
16346 | +{ | ||
16347 | + struct isp_device *isp = to_isp_device(res); | ||
16348 | + u32 rgval = 0; | ||
16349 | + | ||
16350 | + dev_dbg(isp->dev, "Output size[w/h]: %dx%d\n", width, height); | ||
16351 | + rgval = (width << ISPRSZ_OUT_SIZE_HORZ_SHIFT) | ||
16352 | + & ISPRSZ_OUT_SIZE_HORZ_MASK; | ||
16353 | + rgval |= (height << ISPRSZ_OUT_SIZE_VERT_SHIFT) | ||
16354 | + & ISPRSZ_OUT_SIZE_VERT_MASK; | ||
16355 | + isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE); | ||
16356 | +} | ||
16357 | + | ||
16358 | +/* | ||
16359 | + * resizer_set_output_offset - Setup memory offset for the output lines. | ||
16360 | + * @res: Device context. | ||
16361 | + * @offset: Memory offset. | ||
16362 | + * | ||
16363 | + * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte | ||
16364 | + * boundary; the 5 LSBs are read-only. For optimal use of SDRAM bandwidth, | ||
16365 | + * the SDRAM line offset must be set on a 256-byte boundary | ||
16366 | + */ | ||
16367 | +static void resizer_set_output_offset(struct isp_res_device *res, u32 offset) | ||
16368 | +{ | ||
16369 | + struct isp_device *isp = to_isp_device(res); | ||
16370 | + | ||
16371 | + isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF); | ||
16372 | +} | ||
16373 | + | ||
16374 | +/* | ||
16375 | + * resizer_set_start - Setup vertical and horizontal start position | ||
16376 | + * @res: Device context. | ||
16377 | + * @left: Horizontal start position. | ||
16378 | + * @top: Vertical start position. | ||
16379 | + * | ||
16380 | + * Vertical start line: | ||
16381 | + * This field makes sense only when the resizer obtains its input | ||
16382 | + * from the preview engine/CCDC | ||
16383 | + * | ||
16384 | + * Horizontal start pixel: | ||
16385 | + * Pixels are coded on 16 bits for YUV and 8 bits for color separate data. | ||
16386 | + * When the resizer gets its input from SDRAM, this field must be set | ||
16387 | + * to <= 15 for YUV 16-bit data and <= 31 for 8-bit color separate data | ||
16388 | + */ | ||
16389 | +static void resizer_set_start(struct isp_res_device *res, u32 left, u32 top) | ||
16390 | +{ | ||
16391 | + struct isp_device *isp = to_isp_device(res); | ||
16392 | + u32 rgval = 0; | ||
16393 | + | ||
16394 | + rgval = (left << ISPRSZ_IN_START_HORZ_ST_SHIFT) | ||
16395 | + & ISPRSZ_IN_START_HORZ_ST_MASK; | ||
16396 | + rgval |= (top << ISPRSZ_IN_START_VERT_ST_SHIFT) | ||
16397 | + & ISPRSZ_IN_START_VERT_ST_MASK; | ||
16398 | + | ||
16399 | + isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START); | ||
16400 | +} | ||
16401 | + | ||
16402 | +/* | ||
16403 | + * resizer_set_input_size - Setup the input size | ||
16404 | + * @res: Device context. | ||
16405 | + * @width: The range is 0 to 4095 pixels | ||
16406 | + * @height: The range is 0 to 4095 lines | ||
16407 | + */ | ||
16408 | +static void resizer_set_input_size(struct isp_res_device *res, | ||
16409 | + u32 width, u32 height) | ||
16410 | +{ | ||
16411 | + struct isp_device *isp = to_isp_device(res); | ||
16412 | + u32 rgval = 0; | ||
16413 | + | ||
16414 | + dev_dbg(isp->dev, "Input size[w/h]: %dx%d\n", width, height); | ||
16415 | + | ||
16416 | + rgval = (width << ISPRSZ_IN_SIZE_HORZ_SHIFT) | ||
16417 | + & ISPRSZ_IN_SIZE_HORZ_MASK; | ||
16418 | + rgval |= (height << ISPRSZ_IN_SIZE_VERT_SHIFT) | ||
16419 | + & ISPRSZ_IN_SIZE_VERT_MASK; | ||
16420 | + | ||
16421 | + isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE); | ||
16422 | +} | ||
16423 | + | ||
16424 | +/* | ||
16425 | + * resizer_set_src_offs - Setup the memory offset for the input lines | ||
16426 | + * @res: Device context. | ||
16427 | + * @offset: Memory offset. | ||
16428 | + * | ||
16429 | + * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte | ||
16430 | + * boundary; the 5 LSBs are read-only. This field must be programmed to be | ||
16431 | + * 0x0 if the resizer input is from preview engine/CCDC. | ||
16432 | + */ | ||
16433 | +static void resizer_set_input_offset(struct isp_res_device *res, u32 offset) | ||
16434 | +{ | ||
16435 | + struct isp_device *isp = to_isp_device(res); | ||
16436 | + | ||
16437 | + isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF); | ||
16438 | +} | ||
16439 | + | ||
16440 | +/* | ||
16441 | + * resizer_set_intype - Input type select | ||
16442 | + * @res: Device context. | ||
16443 | + * @type: Pixel format type. | ||
16444 | + */ | ||
16445 | +static void resizer_set_intype(struct isp_res_device *res, | ||
16446 | + enum resizer_colors_type type) | ||
16447 | +{ | ||
16448 | + struct isp_device *isp = to_isp_device(res); | ||
16449 | + | ||
16450 | + if (type == RSZ_COLOR8) | ||
16451 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
16452 | + ISPRSZ_CNT_INPTYP); | ||
16453 | + else | ||
16454 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
16455 | + ISPRSZ_CNT_INPTYP); | ||
16456 | +} | ||
16457 | + | ||
16458 | +/* | ||
16459 | + * __resizer_set_inaddr - Helper function for set input address | ||
16460 | + * @res : pointer to resizer private data structure | ||
16461 | + * @addr: input address | ||
16462 | + * return none | ||
16463 | + */ | ||
16464 | +static void __resizer_set_inaddr(struct isp_res_device *res, u32 addr) | ||
16465 | +{ | ||
16466 | + struct isp_device *isp = to_isp_device(res); | ||
16467 | + | ||
16468 | + isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD); | ||
16469 | +} | ||
16470 | + | ||
16471 | +/* | ||
16472 | + * The data rate at the horizontal resizer output must not exceed half the | ||
16473 | + * functional clock or 100 MP/s, whichever is lower. According to the TRM | ||
16474 | + * there's no similar requirement for the vertical resizer output. However | ||
16475 | + * experience showed that vertical upscaling by 4 leads to SBL overflows (with | ||
16476 | + * data rates at the resizer output exceeding 300 MP/s). Limiting the resizer | ||
16477 | + * output data rate to the functional clock or 200 MP/s, whichever is lower, | ||
16478 | + * seems to get rid of SBL overflows. | ||
16479 | + * | ||
16480 | + * The maximum data rate at the output of the horizontal resizer can thus be | ||
16481 | + * computed with | ||
16482 | + * | ||
16483 | + * max intermediate rate <= L3 clock * input height / output height | ||
16484 | + * max intermediate rate <= L3 clock / 2 | ||
16485 | + * | ||
16486 | + * The maximum data rate at the resizer input is then | ||
16487 | + * | ||
16488 | + * max input rate <= max intermediate rate * input width / output width | ||
16489 | + * | ||
16490 | + * where the input width and height are the resizer input crop rectangle size. | ||
16491 | + * The TRM doesn't clearly explain if that's a maximum instant data rate or a | ||
16492 | + * maximum average data rate. | ||
16493 | + */ | ||
16494 | +void omap3isp_resizer_max_rate(struct isp_res_device *res, | ||
16495 | + unsigned int *max_rate) | ||
16496 | +{ | ||
16497 | + struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); | ||
16498 | + const struct v4l2_mbus_framefmt *ofmt = &res->formats[RESZ_PAD_SOURCE]; | ||
16499 | + unsigned long limit = min(pipe->l3_ick, 200000000UL); | ||
16500 | + unsigned long clock; | ||
16501 | + | ||
16502 | + clock = div_u64((u64)limit * res->crop.active.height, ofmt->height); | ||
16503 | + clock = min(clock, limit / 2); | ||
16504 | + *max_rate = div_u64((u64)clock * res->crop.active.width, ofmt->width); | ||
16505 | +} | ||
16506 | + | ||
16507 | +/* | ||
16508 | + * When the resizer processes images from memory, the driver must slow down read | ||
16509 | + * requests on the input to at least comply with the internal data rate | ||
16510 | + * requirements. If the application real-time requirements can cope with slower | ||
16511 | + * processing, the resizer can be slowed down even more to put less pressure on | ||
16512 | + * the overall system. | ||
16513 | + * | ||
16514 | + * When the resizer processes images on the fly (either from the CCDC or the | ||
16515 | + * preview module), the same data rate requirements apply but they can't be | ||
16516 | + * enforced at the resizer level. The image input module (sensor, CCP2 or | ||
16517 | + * preview module) must not provide image data faster than the resizer can | ||
16518 | + * process. | ||
16519 | + * | ||
16520 | + * For live image pipelines, the data rate is set by the frame format, size and | ||
16521 | + * rate. The sensor output frame rate must not exceed the maximum resizer data | ||
16522 | + * rate. | ||
16523 | + * | ||
16524 | + * The resizer slows down read requests by inserting wait cycles in the SBL | ||
16525 | + * requests. The maximum number of 256-byte requests per second can be computed | ||
16526 | + * as (the data rate is multiplied by 2 to convert from pixels per second to | ||
16527 | + * bytes per second) | ||
16528 | + * | ||
16529 | + * request per second = data rate * 2 / 256 | ||
16530 | + * cycles per request = cycles per second / requests per second | ||
16531 | + * | ||
16532 | + * The number of cycles per second is controlled by the L3 clock, leading to | ||
16533 | + * | ||
16534 | + * cycles per request = L3 frequency / 2 * 256 / data rate | ||
16535 | + */ | ||
16536 | +static void resizer_adjust_bandwidth(struct isp_res_device *res) | ||
16537 | +{ | ||
16538 | + struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); | ||
16539 | + struct isp_device *isp = to_isp_device(res); | ||
16540 | + unsigned long l3_ick = pipe->l3_ick; | ||
16541 | + struct v4l2_fract *timeperframe; | ||
16542 | + unsigned int cycles_per_frame; | ||
16543 | + unsigned int requests_per_frame; | ||
16544 | + unsigned int cycles_per_request; | ||
16545 | + unsigned int granularity; | ||
16546 | + unsigned int minimum; | ||
16547 | + unsigned int maximum; | ||
16548 | + unsigned int value; | ||
16549 | + | ||
16550 | + if (res->input != RESIZER_INPUT_MEMORY) { | ||
16551 | + isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, | ||
16552 | + ISPSBL_SDR_REQ_RSZ_EXP_MASK); | ||
16553 | + return; | ||
16554 | + } | ||
16555 | + | ||
16556 | + switch (isp->revision) { | ||
16557 | + case ISP_REVISION_1_0: | ||
16558 | + case ISP_REVISION_2_0: | ||
16559 | + default: | ||
16560 | + granularity = 1024; | ||
16561 | + break; | ||
16562 | + | ||
16563 | + case ISP_REVISION_15_0: | ||
16564 | + granularity = 32; | ||
16565 | + break; | ||
16566 | + } | ||
16567 | + | ||
16568 | + /* Compute the minimum number of cycles per request, based on the | ||
16569 | + * pipeline maximum data rate. This is an absolute lower bound if we | ||
16570 | + * don't want SBL overflows, so round the value up. | ||
16571 | + */ | ||
16572 | + cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1, | ||
16573 | + pipe->max_rate); | ||
16574 | + minimum = DIV_ROUND_UP(cycles_per_request, granularity); | ||
16575 | + | ||
16576 | + /* Compute the maximum number of cycles per request, based on the | ||
16577 | + * requested frame rate. This is a soft upper bound to achieve a frame | ||
16578 | + * rate equal or higher than the requested value, so round the value | ||
16579 | + * down. | ||
16580 | + */ | ||
16581 | + timeperframe = &pipe->max_timeperframe; | ||
16582 | + | ||
16583 | + requests_per_frame = DIV_ROUND_UP(res->crop.active.width * 2, 256) | ||
16584 | + * res->crop.active.height; | ||
16585 | + cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator, | ||
16586 | + timeperframe->denominator); | ||
16587 | + cycles_per_request = cycles_per_frame / requests_per_frame; | ||
16588 | + | ||
16589 | + maximum = cycles_per_request / granularity; | ||
16590 | + | ||
16591 | + value = max(minimum, maximum); | ||
16592 | + | ||
16593 | + dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value); | ||
16594 | + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, | ||
16595 | + ISPSBL_SDR_REQ_RSZ_EXP_MASK, | ||
16596 | + value << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT); | ||
16597 | +} | ||
16598 | + | ||
16599 | +/* | ||
16600 | + * omap3isp_resizer_busy - Checks if ISP resizer is busy. | ||
16601 | + * | ||
16602 | + * Returns busy field from ISPRSZ_PCR register. | ||
16603 | + */ | ||
16604 | +int omap3isp_resizer_busy(struct isp_res_device *res) | ||
16605 | +{ | ||
16606 | + struct isp_device *isp = to_isp_device(res); | ||
16607 | + | ||
16608 | + return isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & | ||
16609 | + ISPRSZ_PCR_BUSY; | ||
16610 | +} | ||
16611 | + | ||
16612 | +/* | ||
16613 | + * resizer_set_inaddr - Sets the memory address of the input frame. | ||
16614 | + * @addr: 32bit memory address aligned on 32byte boundary. | ||
16615 | + */ | ||
16616 | +static void resizer_set_inaddr(struct isp_res_device *res, u32 addr) | ||
16617 | +{ | ||
16618 | + res->addr_base = addr; | ||
16619 | + | ||
16620 | + /* This will handle crop settings in stream off state */ | ||
16621 | + if (res->crop_offset) | ||
16622 | + addr += res->crop_offset & ~0x1f; | ||
16623 | + | ||
16624 | + __resizer_set_inaddr(res, addr); | ||
16625 | +} | ||
16626 | + | ||
16627 | +/* | ||
16628 | + * Configures the memory address to which the output frame is written. | ||
16629 | + * @addr: 32bit memory address aligned on 32byte boundary. | ||
16630 | + * Note: For SBL efficiency reasons the address should be on a 256-byte | ||
16631 | + * boundary. | ||
16632 | + */ | ||
16633 | +static void resizer_set_outaddr(struct isp_res_device *res, u32 addr) | ||
16634 | +{ | ||
16635 | + struct isp_device *isp = to_isp_device(res); | ||
16636 | + | ||
16637 | + /* | ||
16638 | + * Set output address. This needs to be in its own function | ||
16639 | + * because it changes often. | ||
16640 | + */ | ||
16641 | + isp_reg_writel(isp, addr << ISPRSZ_SDR_OUTADD_ADDR_SHIFT, | ||
16642 | + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD); | ||
16643 | +} | ||
16644 | + | ||
16645 | +/* | ||
16646 | + * resizer_print_status - Prints the values of the resizer module registers. | ||
16647 | + */ | ||
16648 | +#define RSZ_PRINT_REGISTER(isp, name)\ | ||
16649 | + dev_dbg(isp->dev, "###RSZ " #name "=0x%08x\n", \ | ||
16650 | + isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_##name)) | ||
16651 | + | ||
16652 | +static void resizer_print_status(struct isp_res_device *res) | ||
16653 | +{ | ||
16654 | + struct isp_device *isp = to_isp_device(res); | ||
16655 | + | ||
16656 | + dev_dbg(isp->dev, "-------------Resizer Register dump----------\n"); | ||
16657 | + | ||
16658 | + RSZ_PRINT_REGISTER(isp, PCR); | ||
16659 | + RSZ_PRINT_REGISTER(isp, CNT); | ||
16660 | + RSZ_PRINT_REGISTER(isp, OUT_SIZE); | ||
16661 | + RSZ_PRINT_REGISTER(isp, IN_START); | ||
16662 | + RSZ_PRINT_REGISTER(isp, IN_SIZE); | ||
16663 | + RSZ_PRINT_REGISTER(isp, SDR_INADD); | ||
16664 | + RSZ_PRINT_REGISTER(isp, SDR_INOFF); | ||
16665 | + RSZ_PRINT_REGISTER(isp, SDR_OUTADD); | ||
16666 | + RSZ_PRINT_REGISTER(isp, SDR_OUTOFF); | ||
16667 | + RSZ_PRINT_REGISTER(isp, YENH); | ||
16668 | + | ||
16669 | + dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
16670 | +} | ||
16671 | + | ||
16672 | +/* | ||
16673 | + * resizer_calc_ratios - Helper function for calculate resizer ratios | ||
16674 | + * @res: pointer to resizer private data structure | ||
16675 | + * @input: input frame size | ||
16676 | + * @output: output frame size | ||
16677 | + * @ratio : return calculated ratios | ||
16678 | + * return none | ||
16679 | + * | ||
16680 | + * The resizer uses a polyphase sample rate converter. The upsampling filter | ||
16681 | + * has a fixed number of phases that depend on the resizing ratio. As the ratio | ||
16682 | + * computation depends on the number of phases, we need to compute a first | ||
16683 | + * approximation and then refine it. | ||
16684 | + * | ||
16685 | + * The input/output/ratio relationship is given by the OMAP34xx TRM: | ||
16686 | + * | ||
16687 | + * - 8-phase, 4-tap mode (RSZ = 64 ~ 512) | ||
16688 | + * iw = (32 * sph + (ow - 1) * hrsz + 16) >> 8 + 7 | ||
16689 | + * ih = (32 * spv + (oh - 1) * vrsz + 16) >> 8 + 4 | ||
16690 | + * - 4-phase, 7-tap mode (RSZ = 513 ~ 1024) | ||
16691 | + * iw = (64 * sph + (ow - 1) * hrsz + 32) >> 8 + 7 | ||
16692 | + * ih = (64 * spv + (oh - 1) * vrsz + 32) >> 8 + 7 | ||
16693 | + * | ||
16694 | + * iw and ih are the input width and height after cropping. Those equations need | ||
16695 | + * to be satisfied exactly for the resizer to work correctly. | ||
16696 | + * | ||
16697 | + * Reverting the equations, we can compute the resizing ratios with | ||
16698 | + * | ||
16699 | + * - 8-phase, 4-tap mode | ||
16700 | + * hrsz = ((iw - 7) * 256 - 16 - 32 * sph) / (ow - 1) | ||
16701 | + * vrsz = ((ih - 4) * 256 - 16 - 32 * spv) / (oh - 1) | ||
16702 | + * - 4-phase, 7-tap mode | ||
16703 | + * hrsz = ((iw - 7) * 256 - 32 - 64 * sph) / (ow - 1) | ||
16704 | + * vrsz = ((ih - 7) * 256 - 32 - 64 * spv) / (oh - 1) | ||
16705 | + * | ||
16706 | + * The ratios are integer values, and must be rounded down to ensure that the | ||
16707 | + * cropped input size is not bigger than the uncropped input size. As the ratio | ||
16708 | + * in 7-tap mode is always smaller than the ratio in 4-tap mode, we can use the | ||
16709 | + * 7-tap mode equations to compute a ratio approximation. | ||
16710 | + * | ||
16711 | + * We first clamp the output size according to the hardware capabilitie to avoid | ||
16712 | + * auto-cropping the input more than required to satisfy the TRM equations. The | ||
16713 | + * minimum output size is achieved with a scaling factor of 1024. It is thus | ||
16714 | + * computed using the 7-tap equations. | ||
16715 | + * | ||
16716 | + * min ow = ((iw - 7) * 256 - 32 - 64 * sph) / 1024 + 1 | ||
16717 | + * min oh = ((ih - 7) * 256 - 32 - 64 * spv) / 1024 + 1 | ||
16718 | + * | ||
16719 | + * Similarly, the maximum output size is achieved with a scaling factor of 64 | ||
16720 | + * and computed using the 4-tap equations. | ||
16721 | + * | ||
16722 | + * max ow = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / 64 + 1 | ||
16723 | + * max oh = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1 | ||
16724 | + * | ||
16725 | + * The additional +255 term compensates for the round down operation performed | ||
16726 | + * by the TRM equations when shifting the value right by 8 bits. | ||
16727 | + * | ||
16728 | + * We then compute and clamp the ratios (x1/4 ~ x4). Clamping the output size to | ||
16729 | + * the maximum value guarantees that the ratio value will never be smaller than | ||
16730 | + * the minimum, but it could still slightly exceed the maximum. Clamping the | ||
16731 | + * ratio will thus result in a resizing factor slightly larger than the | ||
16732 | + * requested value. | ||
16733 | + * | ||
16734 | + * To accomodate that, and make sure the TRM equations are satisfied exactly, we | ||
16735 | + * compute the input crop rectangle as the last step. | ||
16736 | + * | ||
16737 | + * As if the situation wasn't complex enough, the maximum output width depends | ||
16738 | + * on the vertical resizing ratio. Fortunately, the output height doesn't | ||
16739 | + * depend on the horizontal resizing ratio. We can then start by computing the | ||
16740 | + * output height and the vertical ratio, and then move to computing the output | ||
16741 | + * width and the horizontal ratio. | ||
16742 | + */ | ||
16743 | +static void resizer_calc_ratios(struct isp_res_device *res, | ||
16744 | + struct v4l2_rect *input, | ||
16745 | + struct v4l2_mbus_framefmt *output, | ||
16746 | + struct resizer_ratio *ratio) | ||
16747 | +{ | ||
16748 | + struct isp_device *isp = to_isp_device(res); | ||
16749 | + const unsigned int spv = DEFAULT_PHASE; | ||
16750 | + const unsigned int sph = DEFAULT_PHASE; | ||
16751 | + unsigned int upscaled_width; | ||
16752 | + unsigned int upscaled_height; | ||
16753 | + unsigned int min_width; | ||
16754 | + unsigned int min_height; | ||
16755 | + unsigned int max_width; | ||
16756 | + unsigned int max_height; | ||
16757 | + unsigned int width_alignment; | ||
16758 | + | ||
16759 | + /* | ||
16760 | + * Clamp the output height based on the hardware capabilities and | ||
16761 | + * compute the vertical resizing ratio. | ||
16762 | + */ | ||
16763 | + min_height = ((input->height - 7) * 256 - 32 - 64 * spv) / 1024 + 1; | ||
16764 | + min_height = max_t(unsigned int, min_height, MIN_OUT_HEIGHT); | ||
16765 | + max_height = ((input->height - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1; | ||
16766 | + max_height = min_t(unsigned int, max_height, MAX_OUT_HEIGHT); | ||
16767 | + output->height = clamp(output->height, min_height, max_height); | ||
16768 | + | ||
16769 | + ratio->vert = ((input->height - 7) * 256 - 32 - 64 * spv) | ||
16770 | + / (output->height - 1); | ||
16771 | + ratio->vert = clamp_t(unsigned int, ratio->vert, | ||
16772 | + MIN_RESIZE_VALUE, MAX_RESIZE_VALUE); | ||
16773 | + | ||
16774 | + if (ratio->vert <= MID_RESIZE_VALUE) { | ||
16775 | + upscaled_height = (output->height - 1) * ratio->vert | ||
16776 | + + 32 * spv + 16; | ||
16777 | + input->height = (upscaled_height >> 8) + 4; | ||
16778 | + } else { | ||
16779 | + upscaled_height = (output->height - 1) * ratio->vert | ||
16780 | + + 64 * spv + 32; | ||
16781 | + input->height = (upscaled_height >> 8) + 7; | ||
16782 | + } | ||
16783 | + | ||
16784 | + /* | ||
16785 | + * Compute the minimum and maximum output widths based on the hardware | ||
16786 | + * capabilities. The maximum depends on the vertical resizing ratio. | ||
16787 | + */ | ||
16788 | + min_width = ((input->width - 7) * 256 - 32 - 64 * sph) / 1024 + 1; | ||
16789 | + min_width = max_t(unsigned int, min_width, MIN_OUT_WIDTH); | ||
16790 | + | ||
16791 | + if (ratio->vert <= MID_RESIZE_VALUE) { | ||
16792 | + switch (isp->revision) { | ||
16793 | + case ISP_REVISION_1_0: | ||
16794 | + max_width = MAX_4TAP_OUT_WIDTH_ES1; | ||
16795 | + break; | ||
16796 | + | ||
16797 | + case ISP_REVISION_2_0: | ||
16798 | + default: | ||
16799 | + max_width = MAX_4TAP_OUT_WIDTH_ES2; | ||
16800 | + break; | ||
16801 | + | ||
16802 | + case ISP_REVISION_15_0: | ||
16803 | + max_width = MAX_4TAP_OUT_WIDTH_3630; | ||
16804 | + break; | ||
16805 | + } | ||
16806 | + } else { | ||
16807 | + switch (isp->revision) { | ||
16808 | + case ISP_REVISION_1_0: | ||
16809 | + max_width = MAX_7TAP_OUT_WIDTH_ES1; | ||
16810 | + break; | ||
16811 | + | ||
16812 | + case ISP_REVISION_2_0: | ||
16813 | + default: | ||
16814 | + max_width = MAX_7TAP_OUT_WIDTH_ES2; | ||
16815 | + break; | ||
16816 | + | ||
16817 | + case ISP_REVISION_15_0: | ||
16818 | + max_width = MAX_7TAP_OUT_WIDTH_3630; | ||
16819 | + break; | ||
16820 | + } | ||
16821 | + } | ||
16822 | + max_width = min(((input->width - 7) * 256 + 255 - 16 - 32 * sph) / 64 | ||
16823 | + + 1, max_width); | ||
16824 | + | ||
16825 | + /* | ||
16826 | + * The output width must be even, and must be a multiple of 16 bytes | ||
16827 | + * when upscaling vertically. Clamp the output width to the valid range. | ||
16828 | + * Take the alignment into account (the maximum width in 7-tap mode on | ||
16829 | + * ES2 isn't a multiple of 8) and align the result up to make sure it | ||
16830 | + * won't be smaller than the minimum. | ||
16831 | + */ | ||
16832 | + width_alignment = ratio->vert < 256 ? 8 : 2; | ||
16833 | + output->width = clamp(output->width, min_width, | ||
16834 | + max_width & ~(width_alignment - 1)); | ||
16835 | + output->width = ALIGN(output->width, width_alignment); | ||
16836 | + | ||
16837 | + ratio->horz = ((input->width - 7) * 256 - 32 - 64 * sph) | ||
16838 | + / (output->width - 1); | ||
16839 | + ratio->horz = clamp_t(unsigned int, ratio->horz, | ||
16840 | + MIN_RESIZE_VALUE, MAX_RESIZE_VALUE); | ||
16841 | + | ||
16842 | + if (ratio->horz <= MID_RESIZE_VALUE) { | ||
16843 | + upscaled_width = (output->width - 1) * ratio->horz | ||
16844 | + + 32 * sph + 16; | ||
16845 | + input->width = (upscaled_width >> 8) + 7; | ||
16846 | + } else { | ||
16847 | + upscaled_width = (output->width - 1) * ratio->horz | ||
16848 | + + 64 * sph + 32; | ||
16849 | + input->width = (upscaled_width >> 8) + 7; | ||
16850 | + } | ||
16851 | +} | ||
16852 | + | ||
16853 | +/* | ||
16854 | + * resizer_set_crop_params - Setup hardware with cropping parameters | ||
16855 | + * @res : resizer private structure | ||
16856 | + * @crop_rect : current crop rectangle | ||
16857 | + * @ratio : resizer ratios | ||
16858 | + * return none | ||
16859 | + */ | ||
16860 | +static void resizer_set_crop_params(struct isp_res_device *res, | ||
16861 | + const struct v4l2_mbus_framefmt *input, | ||
16862 | + const struct v4l2_mbus_framefmt *output) | ||
16863 | +{ | ||
16864 | + resizer_set_ratio(res, &res->ratio); | ||
16865 | + | ||
16866 | + /* Set chrominance horizontal algorithm */ | ||
16867 | + if (res->ratio.horz >= RESIZE_DIVISOR) | ||
16868 | + resizer_set_bilinear(res, RSZ_THE_SAME); | ||
16869 | + else | ||
16870 | + resizer_set_bilinear(res, RSZ_BILINEAR); | ||
16871 | + | ||
16872 | + resizer_adjust_bandwidth(res); | ||
16873 | + | ||
16874 | + if (res->input == RESIZER_INPUT_MEMORY) { | ||
16875 | + /* Calculate additional offset for crop */ | ||
16876 | + res->crop_offset = (res->crop.active.top * input->width + | ||
16877 | + res->crop.active.left) * 2; | ||
16878 | + /* | ||
16879 | + * Write lowest 4 bits of horizontal pixel offset (in pixels), | ||
16880 | + * vertical start must be 0. | ||
16881 | + */ | ||
16882 | + resizer_set_start(res, (res->crop_offset / 2) & 0xf, 0); | ||
16883 | + | ||
16884 | + /* | ||
16885 | + * Set start (read) address for cropping, in bytes. | ||
16886 | + * Lowest 5 bits must be zero. | ||
16887 | + */ | ||
16888 | + __resizer_set_inaddr(res, | ||
16889 | + res->addr_base + (res->crop_offset & ~0x1f)); | ||
16890 | + } else { | ||
16891 | + /* | ||
16892 | + * Set vertical start line and horizontal starting pixel. | ||
16893 | + * If the input is from CCDC/PREV, horizontal start field is | ||
16894 | + * in bytes (twice number of pixels). | ||
16895 | + */ | ||
16896 | + resizer_set_start(res, res->crop.active.left * 2, | ||
16897 | + res->crop.active.top); | ||
16898 | + /* Input address and offset must be 0 for preview/ccdc input */ | ||
16899 | + __resizer_set_inaddr(res, 0); | ||
16900 | + resizer_set_input_offset(res, 0); | ||
16901 | + } | ||
16902 | + | ||
16903 | + /* Set the input size */ | ||
16904 | + resizer_set_input_size(res, res->crop.active.width, | ||
16905 | + res->crop.active.height); | ||
16906 | +} | ||
16907 | + | ||
16908 | +static void resizer_configure(struct isp_res_device *res) | ||
16909 | +{ | ||
16910 | + struct v4l2_mbus_framefmt *informat, *outformat; | ||
16911 | + struct resizer_luma_yenh luma = {0, 0, 0, 0}; | ||
16912 | + | ||
16913 | + resizer_set_source(res, res->input); | ||
16914 | + | ||
16915 | + informat = &res->formats[RESZ_PAD_SINK]; | ||
16916 | + outformat = &res->formats[RESZ_PAD_SOURCE]; | ||
16917 | + | ||
16918 | + /* RESZ_PAD_SINK */ | ||
16919 | + if (res->input == RESIZER_INPUT_VP) | ||
16920 | + resizer_set_input_offset(res, 0); | ||
16921 | + else | ||
16922 | + resizer_set_input_offset(res, ALIGN(informat->width, 0x10) * 2); | ||
16923 | + | ||
16924 | + /* YUV422 interleaved, default phase, no luma enhancement */ | ||
16925 | + resizer_set_intype(res, RSZ_YUV422); | ||
16926 | + resizer_set_ycpos(res, informat->code); | ||
16927 | + resizer_set_phase(res, DEFAULT_PHASE, DEFAULT_PHASE); | ||
16928 | + resizer_set_luma(res, &luma); | ||
16929 | + | ||
16930 | + /* RESZ_PAD_SOURCE */ | ||
16931 | + resizer_set_output_offset(res, ALIGN(outformat->width * 2, 32)); | ||
16932 | + resizer_set_output_size(res, outformat->width, outformat->height); | ||
16933 | + | ||
16934 | + resizer_set_crop_params(res, informat, outformat); | ||
16935 | +} | ||
16936 | + | ||
16937 | +/* ----------------------------------------------------------------------------- | ||
16938 | + * Interrupt handling | ||
16939 | + */ | ||
16940 | + | ||
16941 | +static void resizer_enable_oneshot(struct isp_res_device *res) | ||
16942 | +{ | ||
16943 | + struct isp_device *isp = to_isp_device(res); | ||
16944 | + | ||
16945 | + isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR, | ||
16946 | + ISPRSZ_PCR_ENABLE | ISPRSZ_PCR_ONESHOT); | ||
16947 | +} | ||
16948 | + | ||
16949 | +void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res) | ||
16950 | +{ | ||
16951 | + /* | ||
16952 | + * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun | ||
16953 | + * condition, the module was paused and now we have a buffer queued | ||
16954 | + * on the output again. Restart the pipeline if running in continuous | ||
16955 | + * mode. | ||
16956 | + */ | ||
16957 | + if (res->state == ISP_PIPELINE_STREAM_CONTINUOUS && | ||
16958 | + res->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { | ||
16959 | + resizer_enable_oneshot(res); | ||
16960 | + isp_video_dmaqueue_flags_clr(&res->video_out); | ||
16961 | + } | ||
16962 | +} | ||
16963 | + | ||
16964 | +static void resizer_isr_buffer(struct isp_res_device *res) | ||
16965 | +{ | ||
16966 | + struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); | ||
16967 | + struct isp_buffer *buffer; | ||
16968 | + int restart = 0; | ||
16969 | + | ||
16970 | + if (res->state == ISP_PIPELINE_STREAM_STOPPED) | ||
16971 | + return; | ||
16972 | + | ||
16973 | + /* Complete the output buffer and, if reading from memory, the input | ||
16974 | + * buffer. | ||
16975 | + */ | ||
16976 | + buffer = omap3isp_video_buffer_next(&res->video_out, res->error); | ||
16977 | + if (buffer != NULL) { | ||
16978 | + resizer_set_outaddr(res, buffer->isp_addr); | ||
16979 | + restart = 1; | ||
16980 | + } | ||
16981 | + | ||
16982 | + pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; | ||
16983 | + | ||
16984 | + if (res->input == RESIZER_INPUT_MEMORY) { | ||
16985 | + buffer = omap3isp_video_buffer_next(&res->video_in, 0); | ||
16986 | + if (buffer != NULL) | ||
16987 | + resizer_set_inaddr(res, buffer->isp_addr); | ||
16988 | + pipe->state |= ISP_PIPELINE_IDLE_INPUT; | ||
16989 | + } | ||
16990 | + | ||
16991 | + if (res->state == ISP_PIPELINE_STREAM_SINGLESHOT) { | ||
16992 | + if (isp_pipeline_ready(pipe)) | ||
16993 | + omap3isp_pipeline_set_stream(pipe, | ||
16994 | + ISP_PIPELINE_STREAM_SINGLESHOT); | ||
16995 | + } else { | ||
16996 | + /* If an underrun occurs, the video queue operation handler will | ||
16997 | + * restart the resizer. Otherwise restart it immediately. | ||
16998 | + */ | ||
16999 | + if (restart) | ||
17000 | + resizer_enable_oneshot(res); | ||
17001 | + } | ||
17002 | + | ||
17003 | + res->error = 0; | ||
17004 | +} | ||
17005 | + | ||
17006 | +/* | ||
17007 | + * omap3isp_resizer_isr - ISP resizer interrupt handler | ||
17008 | + * | ||
17009 | + * Manage the resizer video buffers and configure shadowed and busy-locked | ||
17010 | + * registers. | ||
17011 | + */ | ||
17012 | +void omap3isp_resizer_isr(struct isp_res_device *res) | ||
17013 | +{ | ||
17014 | + struct v4l2_mbus_framefmt *informat, *outformat; | ||
17015 | + | ||
17016 | + if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping)) | ||
17017 | + return; | ||
17018 | + | ||
17019 | + if (res->applycrop) { | ||
17020 | + outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE, | ||
17021 | + V4L2_SUBDEV_FORMAT_ACTIVE); | ||
17022 | + informat = __resizer_get_format(res, NULL, RESZ_PAD_SINK, | ||
17023 | + V4L2_SUBDEV_FORMAT_ACTIVE); | ||
17024 | + resizer_set_crop_params(res, informat, outformat); | ||
17025 | + res->applycrop = 0; | ||
17026 | + } | ||
17027 | + | ||
17028 | + resizer_isr_buffer(res); | ||
17029 | +} | ||
17030 | + | ||
17031 | +/* ----------------------------------------------------------------------------- | ||
17032 | + * ISP video operations | ||
17033 | + */ | ||
17034 | + | ||
17035 | +static int resizer_video_queue(struct isp_video *video, | ||
17036 | + struct isp_buffer *buffer) | ||
17037 | +{ | ||
17038 | + struct isp_res_device *res = &video->isp->isp_res; | ||
17039 | + | ||
17040 | + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
17041 | + resizer_set_inaddr(res, buffer->isp_addr); | ||
17042 | + | ||
17043 | + /* | ||
17044 | + * We now have a buffer queued on the output. Despite what the | ||
17045 | + * TRM says, the resizer can't be restarted immediately. | ||
17046 | + * Enabling it in one shot mode in the middle of a frame (or at | ||
17047 | + * least asynchronously to the frame) results in the output | ||
17048 | + * being shifted randomly left/right and up/down, as if the | ||
17049 | + * hardware didn't synchronize itself to the beginning of the | ||
17050 | + * frame correctly. | ||
17051 | + * | ||
17052 | + * Restart the resizer on the next sync interrupt if running in | ||
17053 | + * continuous mode or when starting the stream. | ||
17054 | + */ | ||
17055 | + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
17056 | + resizer_set_outaddr(res, buffer->isp_addr); | ||
17057 | + | ||
17058 | + return 0; | ||
17059 | +} | ||
17060 | + | ||
17061 | +static const struct isp_video_operations resizer_video_ops = { | ||
17062 | + .queue = resizer_video_queue, | ||
17063 | +}; | ||
17064 | + | ||
17065 | +/* ----------------------------------------------------------------------------- | ||
17066 | + * V4L2 subdev operations | ||
17067 | + */ | ||
17068 | + | ||
17069 | +/* | ||
17070 | + * resizer_set_stream - Enable/Disable streaming on resizer subdev | ||
17071 | + * @sd: ISP resizer V4L2 subdev | ||
17072 | + * @enable: 1 == Enable, 0 == Disable | ||
17073 | + * | ||
17074 | + * The resizer hardware can't be enabled without a memory buffer to write to. | ||
17075 | + * As the s_stream operation is called in response to a STREAMON call without | ||
17076 | + * any buffer queued yet, just update the state field and return immediately. | ||
17077 | + * The resizer will be enabled in resizer_video_queue(). | ||
17078 | + */ | ||
17079 | +static int resizer_set_stream(struct v4l2_subdev *sd, int enable) | ||
17080 | +{ | ||
17081 | + struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
17082 | + struct isp_video *video_out = &res->video_out; | ||
17083 | + struct isp_device *isp = to_isp_device(res); | ||
17084 | + struct device *dev = to_device(res); | ||
17085 | + | ||
17086 | + if (res->state == ISP_PIPELINE_STREAM_STOPPED) { | ||
17087 | + if (enable == ISP_PIPELINE_STREAM_STOPPED) | ||
17088 | + return 0; | ||
17089 | + | ||
17090 | + omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_RESIZER); | ||
17091 | + resizer_configure(res); | ||
17092 | + res->error = 0; | ||
17093 | + resizer_print_status(res); | ||
17094 | + } | ||
17095 | + | ||
17096 | + switch (enable) { | ||
17097 | + case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
17098 | + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); | ||
17099 | + if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { | ||
17100 | + resizer_enable_oneshot(res); | ||
17101 | + isp_video_dmaqueue_flags_clr(video_out); | ||
17102 | + } | ||
17103 | + break; | ||
17104 | + | ||
17105 | + case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
17106 | + if (res->input == RESIZER_INPUT_MEMORY) | ||
17107 | + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_READ); | ||
17108 | + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); | ||
17109 | + | ||
17110 | + resizer_enable_oneshot(res); | ||
17111 | + break; | ||
17112 | + | ||
17113 | + case ISP_PIPELINE_STREAM_STOPPED: | ||
17114 | + if (omap3isp_module_sync_idle(&sd->entity, &res->wait, | ||
17115 | + &res->stopping)) | ||
17116 | + dev_dbg(dev, "%s: module stop timeout.\n", sd->name); | ||
17117 | + omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_RESIZER_READ | | ||
17118 | + OMAP3_ISP_SBL_RESIZER_WRITE); | ||
17119 | + omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_RESIZER); | ||
17120 | + isp_video_dmaqueue_flags_clr(video_out); | ||
17121 | + break; | ||
17122 | + } | ||
17123 | + | ||
17124 | + res->state = enable; | ||
17125 | + return 0; | ||
17126 | +} | ||
17127 | + | ||
17128 | +/* | ||
17129 | + * resizer_g_crop - handle get crop subdev operation | ||
17130 | + * @sd : pointer to v4l2 subdev structure | ||
17131 | + * @pad : subdev pad | ||
17132 | + * @crop : pointer to crop structure | ||
17133 | + * @which : active or try format | ||
17134 | + * return zero | ||
17135 | + */ | ||
17136 | +static int resizer_g_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
17137 | + struct v4l2_subdev_crop *crop) | ||
17138 | +{ | ||
17139 | + struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
17140 | + struct v4l2_mbus_framefmt *format; | ||
17141 | + struct resizer_ratio ratio; | ||
17142 | + | ||
17143 | + /* Only sink pad has crop capability */ | ||
17144 | + if (crop->pad != RESZ_PAD_SINK) | ||
17145 | + return -EINVAL; | ||
17146 | + | ||
17147 | + format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, crop->which); | ||
17148 | + crop->rect = *__resizer_get_crop(res, fh, crop->which); | ||
17149 | + resizer_calc_ratios(res, &crop->rect, format, &ratio); | ||
17150 | + | ||
17151 | + return 0; | ||
17152 | +} | ||
17153 | + | ||
17154 | +/* | ||
17155 | + * resizer_try_crop - mangles crop parameters. | ||
17156 | + */ | ||
17157 | +static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, | ||
17158 | + const struct v4l2_mbus_framefmt *source, | ||
17159 | + struct v4l2_rect *crop) | ||
17160 | +{ | ||
17161 | + const unsigned int spv = DEFAULT_PHASE; | ||
17162 | + const unsigned int sph = DEFAULT_PHASE; | ||
17163 | + | ||
17164 | + /* Crop rectangle is constrained to the output size so that zoom ratio | ||
17165 | + * cannot exceed +/-4.0. | ||
17166 | + */ | ||
17167 | + unsigned int min_width = | ||
17168 | + ((32 * sph + (source->width - 1) * 64 + 16) >> 8) + 7; | ||
17169 | + unsigned int min_height = | ||
17170 | + ((32 * spv + (source->height - 1) * 64 + 16) >> 8) + 4; | ||
17171 | + unsigned int max_width = | ||
17172 | + ((64 * sph + (source->width - 1) * 1024 + 32) >> 8) + 7; | ||
17173 | + unsigned int max_height = | ||
17174 | + ((64 * spv + (source->height - 1) * 1024 + 32) >> 8) + 7; | ||
17175 | + | ||
17176 | + crop->width = clamp_t(u32, crop->width, min_width, max_width); | ||
17177 | + crop->height = clamp_t(u32, crop->height, min_height, max_height); | ||
17178 | + | ||
17179 | + /* Crop can not go beyond of the input rectangle */ | ||
17180 | + crop->left = clamp_t(u32, crop->left, 0, sink->width - MIN_IN_WIDTH); | ||
17181 | + crop->width = clamp_t(u32, crop->width, MIN_IN_WIDTH, | ||
17182 | + sink->width - crop->left); | ||
17183 | + crop->top = clamp_t(u32, crop->top, 0, sink->height - MIN_IN_HEIGHT); | ||
17184 | + crop->height = clamp_t(u32, crop->height, MIN_IN_HEIGHT, | ||
17185 | + sink->height - crop->top); | ||
17186 | +} | ||
17187 | + | ||
17188 | +/* | ||
17189 | + * resizer_s_crop - handle set crop subdev operation | ||
17190 | + * @sd : pointer to v4l2 subdev structure | ||
17191 | + * @pad : subdev pad | ||
17192 | + * @crop : pointer to crop structure | ||
17193 | + * @which : active or try format | ||
17194 | + * return -EINVAL or zero when succeed | ||
17195 | + */ | ||
17196 | +static int resizer_s_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
17197 | + struct v4l2_subdev_crop *crop) | ||
17198 | +{ | ||
17199 | + struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
17200 | + struct isp_device *isp = to_isp_device(res); | ||
17201 | + struct v4l2_mbus_framefmt *format_sink, *format_source; | ||
17202 | + struct resizer_ratio ratio; | ||
17203 | + | ||
17204 | + /* Only sink pad has crop capability */ | ||
17205 | + if (crop->pad != RESZ_PAD_SINK) | ||
17206 | + return -EINVAL; | ||
17207 | + | ||
17208 | + format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, | ||
17209 | + crop->which); | ||
17210 | + format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, | ||
17211 | + crop->which); | ||
17212 | + | ||
17213 | + dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__, | ||
17214 | + crop->rect.left, crop->rect.top, crop->rect.width, | ||
17215 | + crop->rect.height, crop->which); | ||
17216 | + | ||
17217 | + dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__, | ||
17218 | + format_sink->width, format_sink->height, | ||
17219 | + format_source->width, format_source->height); | ||
17220 | + | ||
17221 | + resizer_try_crop(format_sink, format_source, &crop->rect); | ||
17222 | + *__resizer_get_crop(res, fh, crop->which) = crop->rect; | ||
17223 | + resizer_calc_ratios(res, &crop->rect, format_source, &ratio); | ||
17224 | + | ||
17225 | + if (crop->which == V4L2_SUBDEV_FORMAT_TRY) | ||
17226 | + return 0; | ||
17227 | + | ||
17228 | + res->ratio = ratio; | ||
17229 | + res->crop.active = crop->rect; | ||
17230 | + | ||
17231 | + /* | ||
17232 | + * s_crop can be called while streaming is on. In this case | ||
17233 | + * the crop values will be set in the next IRQ. | ||
17234 | + */ | ||
17235 | + if (res->state != ISP_PIPELINE_STREAM_STOPPED) | ||
17236 | + res->applycrop = 1; | ||
17237 | + | ||
17238 | + return 0; | ||
17239 | +} | ||
17240 | + | ||
17241 | +/* resizer pixel formats */ | ||
17242 | +static const unsigned int resizer_formats[] = { | ||
17243 | + V4L2_MBUS_FMT_UYVY8_1X16, | ||
17244 | + V4L2_MBUS_FMT_YUYV8_1X16, | ||
17245 | +}; | ||
17246 | + | ||
17247 | +static unsigned int resizer_max_in_width(struct isp_res_device *res) | ||
17248 | +{ | ||
17249 | + struct isp_device *isp = to_isp_device(res); | ||
17250 | + | ||
17251 | + if (res->input == RESIZER_INPUT_MEMORY) { | ||
17252 | + return MAX_IN_WIDTH_MEMORY_MODE; | ||
17253 | + } else { | ||
17254 | + if (isp->revision == ISP_REVISION_1_0) | ||
17255 | + return MAX_IN_WIDTH_ONTHEFLY_MODE_ES1; | ||
17256 | + else | ||
17257 | + return MAX_IN_WIDTH_ONTHEFLY_MODE_ES2; | ||
17258 | + } | ||
17259 | +} | ||
17260 | + | ||
17261 | +/* | ||
17262 | + * resizer_try_format - Handle try format by pad subdev method | ||
17263 | + * @res : ISP resizer device | ||
17264 | + * @fh : V4L2 subdev file handle | ||
17265 | + * @pad : pad num | ||
17266 | + * @fmt : pointer to v4l2 format structure | ||
17267 | + * @which : wanted subdev format | ||
17268 | + */ | ||
17269 | +static void resizer_try_format(struct isp_res_device *res, | ||
17270 | + struct v4l2_subdev_fh *fh, unsigned int pad, | ||
17271 | + struct v4l2_mbus_framefmt *fmt, | ||
17272 | + enum v4l2_subdev_format_whence which) | ||
17273 | +{ | ||
17274 | + struct v4l2_mbus_framefmt *format; | ||
17275 | + struct resizer_ratio ratio; | ||
17276 | + struct v4l2_rect crop; | ||
17277 | + | ||
17278 | + switch (pad) { | ||
17279 | + case RESZ_PAD_SINK: | ||
17280 | + if (fmt->code != V4L2_MBUS_FMT_YUYV8_1X16 && | ||
17281 | + fmt->code != V4L2_MBUS_FMT_UYVY8_1X16) | ||
17282 | + fmt->code = V4L2_MBUS_FMT_YUYV8_1X16; | ||
17283 | + | ||
17284 | + fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH, | ||
17285 | + resizer_max_in_width(res)); | ||
17286 | + fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT, | ||
17287 | + MAX_IN_HEIGHT); | ||
17288 | + break; | ||
17289 | + | ||
17290 | + case RESZ_PAD_SOURCE: | ||
17291 | + format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which); | ||
17292 | + fmt->code = format->code; | ||
17293 | + | ||
17294 | + crop = *__resizer_get_crop(res, fh, which); | ||
17295 | + resizer_calc_ratios(res, &crop, fmt, &ratio); | ||
17296 | + break; | ||
17297 | + } | ||
17298 | + | ||
17299 | + fmt->colorspace = V4L2_COLORSPACE_JPEG; | ||
17300 | + fmt->field = V4L2_FIELD_NONE; | ||
17301 | +} | ||
17302 | + | ||
17303 | +/* | ||
17304 | + * resizer_enum_mbus_code - Handle pixel format enumeration | ||
17305 | + * @sd : pointer to v4l2 subdev structure | ||
17306 | + * @fh : V4L2 subdev file handle | ||
17307 | + * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
17308 | + * return -EINVAL or zero on success | ||
17309 | + */ | ||
17310 | +static int resizer_enum_mbus_code(struct v4l2_subdev *sd, | ||
17311 | + struct v4l2_subdev_fh *fh, | ||
17312 | + struct v4l2_subdev_mbus_code_enum *code) | ||
17313 | +{ | ||
17314 | + struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
17315 | + struct v4l2_mbus_framefmt *format; | ||
17316 | + | ||
17317 | + if (code->pad == RESZ_PAD_SINK) { | ||
17318 | + if (code->index >= ARRAY_SIZE(resizer_formats)) | ||
17319 | + return -EINVAL; | ||
17320 | + | ||
17321 | + code->code = resizer_formats[code->index]; | ||
17322 | + } else { | ||
17323 | + if (code->index != 0) | ||
17324 | + return -EINVAL; | ||
17325 | + | ||
17326 | + format = __resizer_get_format(res, fh, RESZ_PAD_SINK, | ||
17327 | + V4L2_SUBDEV_FORMAT_TRY); | ||
17328 | + code->code = format->code; | ||
17329 | + } | ||
17330 | + | ||
17331 | + return 0; | ||
17332 | +} | ||
17333 | + | ||
17334 | +static int resizer_enum_frame_size(struct v4l2_subdev *sd, | ||
17335 | + struct v4l2_subdev_fh *fh, | ||
17336 | + struct v4l2_subdev_frame_size_enum *fse) | ||
17337 | +{ | ||
17338 | + struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
17339 | + struct v4l2_mbus_framefmt format; | ||
17340 | + | ||
17341 | + if (fse->index != 0) | ||
17342 | + return -EINVAL; | ||
17343 | + | ||
17344 | + format.code = fse->code; | ||
17345 | + format.width = 1; | ||
17346 | + format.height = 1; | ||
17347 | + resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
17348 | + fse->min_width = format.width; | ||
17349 | + fse->min_height = format.height; | ||
17350 | + | ||
17351 | + if (format.code != fse->code) | ||
17352 | + return -EINVAL; | ||
17353 | + | ||
17354 | + format.code = fse->code; | ||
17355 | + format.width = -1; | ||
17356 | + format.height = -1; | ||
17357 | + resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
17358 | + fse->max_width = format.width; | ||
17359 | + fse->max_height = format.height; | ||
17360 | + | ||
17361 | + return 0; | ||
17362 | +} | ||
17363 | + | ||
17364 | +/* | ||
17365 | + * resizer_get_format - Handle get format by pads subdev method | ||
17366 | + * @sd : pointer to v4l2 subdev structure | ||
17367 | + * @fh : V4L2 subdev file handle | ||
17368 | + * @fmt : pointer to v4l2 subdev format structure | ||
17369 | + * return -EINVAL or zero on sucess | ||
17370 | + */ | ||
17371 | +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
17372 | + struct v4l2_subdev_format *fmt) | ||
17373 | +{ | ||
17374 | + struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
17375 | + struct v4l2_mbus_framefmt *format; | ||
17376 | + | ||
17377 | + format = __resizer_get_format(res, fh, fmt->pad, fmt->which); | ||
17378 | + if (format == NULL) | ||
17379 | + return -EINVAL; | ||
17380 | + | ||
17381 | + fmt->format = *format; | ||
17382 | + return 0; | ||
17383 | +} | ||
17384 | + | ||
17385 | +/* | ||
17386 | + * resizer_set_format - Handle set format by pads subdev method | ||
17387 | + * @sd : pointer to v4l2 subdev structure | ||
17388 | + * @fh : V4L2 subdev file handle | ||
17389 | + * @fmt : pointer to v4l2 subdev format structure | ||
17390 | + * return -EINVAL or zero on success | ||
17391 | + */ | ||
17392 | +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
17393 | + struct v4l2_subdev_format *fmt) | ||
17394 | +{ | ||
17395 | + struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
17396 | + struct v4l2_mbus_framefmt *format; | ||
17397 | + struct v4l2_rect *crop; | ||
17398 | + | ||
17399 | + format = __resizer_get_format(res, fh, fmt->pad, fmt->which); | ||
17400 | + if (format == NULL) | ||
17401 | + return -EINVAL; | ||
17402 | + | ||
17403 | + resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which); | ||
17404 | + *format = fmt->format; | ||
17405 | + | ||
17406 | + if (fmt->pad == RESZ_PAD_SINK) { | ||
17407 | + /* reset crop rectangle */ | ||
17408 | + crop = __resizer_get_crop(res, fh, fmt->which); | ||
17409 | + crop->left = 0; | ||
17410 | + crop->top = 0; | ||
17411 | + crop->width = fmt->format.width; | ||
17412 | + crop->height = fmt->format.height; | ||
17413 | + | ||
17414 | + /* Propagate the format from sink to source */ | ||
17415 | + format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, | ||
17416 | + fmt->which); | ||
17417 | + *format = fmt->format; | ||
17418 | + resizer_try_format(res, fh, RESZ_PAD_SOURCE, format, | ||
17419 | + fmt->which); | ||
17420 | + } | ||
17421 | + | ||
17422 | + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { | ||
17423 | + /* Compute and store the active crop rectangle and resizer | ||
17424 | + * ratios. format already points to the source pad active | ||
17425 | + * format. | ||
17426 | + */ | ||
17427 | + res->crop.active = res->crop.request; | ||
17428 | + resizer_calc_ratios(res, &res->crop.active, format, | ||
17429 | + &res->ratio); | ||
17430 | + } | ||
17431 | + | ||
17432 | + return 0; | ||
17433 | +} | ||
17434 | + | ||
17435 | +/* | ||
17436 | + * resizer_init_formats - Initialize formats on all pads | ||
17437 | + * @sd: ISP resizer V4L2 subdevice | ||
17438 | + * @fh: V4L2 subdev file handle | ||
17439 | + * | ||
17440 | + * Initialize all pad formats with default values. If fh is not NULL, try | ||
17441 | + * formats are initialized on the file handle. Otherwise active formats are | ||
17442 | + * initialized on the device. | ||
17443 | + */ | ||
17444 | +static int resizer_init_formats(struct v4l2_subdev *sd, | ||
17445 | + struct v4l2_subdev_fh *fh) | ||
17446 | +{ | ||
17447 | + struct v4l2_subdev_format format; | ||
17448 | + | ||
17449 | + memset(&format, 0, sizeof(format)); | ||
17450 | + format.pad = RESZ_PAD_SINK; | ||
17451 | + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
17452 | + format.format.code = V4L2_MBUS_FMT_YUYV8_1X16; | ||
17453 | + format.format.width = 4096; | ||
17454 | + format.format.height = 4096; | ||
17455 | + resizer_set_format(sd, fh, &format); | ||
17456 | + | ||
17457 | + return 0; | ||
17458 | +} | ||
17459 | + | ||
17460 | +/* subdev core operations */ | ||
17461 | +static const struct v4l2_subdev_core_ops resizer_v4l2_core_ops = { | ||
17462 | + .queryctrl = v4l2_subdev_queryctrl, | ||
17463 | + .querymenu = v4l2_subdev_querymenu, | ||
17464 | + .g_ctrl = v4l2_subdev_g_ctrl, | ||
17465 | + .s_ctrl = v4l2_subdev_s_ctrl, | ||
17466 | + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, | ||
17467 | + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, | ||
17468 | + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, | ||
17469 | +}; | ||
17470 | + | ||
17471 | +/* subdev file operations */ | ||
17472 | +static const struct v4l2_subdev_file_ops resizer_v4l2_file_ops = { | ||
17473 | + .open = resizer_init_formats, | ||
17474 | +}; | ||
17475 | + | ||
17476 | +/* subdev video operations */ | ||
17477 | +static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { | ||
17478 | + .s_stream = resizer_set_stream, | ||
17479 | +}; | ||
17480 | + | ||
17481 | +/* subdev pad operations */ | ||
17482 | +static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { | ||
17483 | + .enum_mbus_code = resizer_enum_mbus_code, | ||
17484 | + .enum_frame_size = resizer_enum_frame_size, | ||
17485 | + .get_fmt = resizer_get_format, | ||
17486 | + .set_fmt = resizer_set_format, | ||
17487 | + .get_crop = resizer_g_crop, | ||
17488 | + .set_crop = resizer_s_crop, | ||
17489 | +}; | ||
17490 | + | ||
17491 | +/* subdev operations */ | ||
17492 | +static const struct v4l2_subdev_ops resizer_v4l2_ops = { | ||
17493 | + .core = &resizer_v4l2_core_ops, | ||
17494 | + .file = &resizer_v4l2_file_ops, | ||
17495 | + .video = &resizer_v4l2_video_ops, | ||
17496 | + .pad = &resizer_v4l2_pad_ops, | ||
17497 | +}; | ||
17498 | + | ||
17499 | + | ||
17500 | +/* ----------------------------------------------------------------------------- | ||
17501 | + * Media entity operations | ||
17502 | + */ | ||
17503 | + | ||
17504 | +/* | ||
17505 | + * resizer_link_setup - Setup resizer connections. | ||
17506 | + * @entity : Pointer to media entity structure | ||
17507 | + * @local : Pointer to local pad array | ||
17508 | + * @remote : Pointer to remote pad array | ||
17509 | + * @flags : Link flags | ||
17510 | + * return -EINVAL or zero on success | ||
17511 | + */ | ||
17512 | +static int resizer_link_setup(struct media_entity *entity, | ||
17513 | + const struct media_pad *local, | ||
17514 | + const struct media_pad *remote, u32 flags) | ||
17515 | +{ | ||
17516 | + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
17517 | + struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
17518 | + | ||
17519 | + switch (local->index | media_entity_type(remote->entity)) { | ||
17520 | + case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE: | ||
17521 | + /* read from memory */ | ||
17522 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
17523 | + if (res->input == RESIZER_INPUT_VP) | ||
17524 | + return -EBUSY; | ||
17525 | + res->input = RESIZER_INPUT_MEMORY; | ||
17526 | + } else { | ||
17527 | + if (res->input == RESIZER_INPUT_MEMORY) | ||
17528 | + res->input = RESIZER_INPUT_NONE; | ||
17529 | + } | ||
17530 | + break; | ||
17531 | + | ||
17532 | + case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
17533 | + /* read from ccdc or previewer */ | ||
17534 | + if (flags & MEDIA_LNK_FL_ENABLED) { | ||
17535 | + if (res->input == RESIZER_INPUT_MEMORY) | ||
17536 | + return -EBUSY; | ||
17537 | + res->input = RESIZER_INPUT_VP; | ||
17538 | + } else { | ||
17539 | + if (res->input == RESIZER_INPUT_VP) | ||
17540 | + res->input = RESIZER_INPUT_NONE; | ||
17541 | + } | ||
17542 | + break; | ||
17543 | + | ||
17544 | + case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: | ||
17545 | + /* resizer always write to memory */ | ||
17546 | + break; | ||
17547 | + | ||
17548 | + default: | ||
17549 | + return -EINVAL; | ||
17550 | + } | ||
17551 | + | ||
17552 | + return 0; | ||
17553 | +} | ||
17554 | + | ||
17555 | +/* media operations */ | ||
17556 | +static const struct media_entity_operations resizer_media_ops = { | ||
17557 | + .link_setup = resizer_link_setup, | ||
17558 | +}; | ||
17559 | + | ||
17560 | +/* | ||
17561 | + * resizer_init_entities - Initialize resizer subdev and media entity. | ||
17562 | + * @res : Pointer to resizer device structure | ||
17563 | + * return -ENOMEM or zero on success | ||
17564 | + */ | ||
17565 | +static int resizer_init_entities(struct isp_res_device *res) | ||
17566 | +{ | ||
17567 | + struct v4l2_subdev *sd = &res->subdev; | ||
17568 | + struct media_pad *pads = res->pads; | ||
17569 | + struct media_entity *me = &sd->entity; | ||
17570 | + int ret; | ||
17571 | + | ||
17572 | + res->input = RESIZER_INPUT_NONE; | ||
17573 | + | ||
17574 | + v4l2_subdev_init(sd, &resizer_v4l2_ops); | ||
17575 | + strlcpy(sd->name, "OMAP3 ISP resizer", sizeof(sd->name)); | ||
17576 | + sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
17577 | + v4l2_set_subdevdata(sd, res); | ||
17578 | + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
17579 | + | ||
17580 | + v4l2_ctrl_handler_init(&res->ctrls, 1); | ||
17581 | + sd->ctrl_handler = &res->ctrls; | ||
17582 | + | ||
17583 | + pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; | ||
17584 | + pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_OUTPUT; | ||
17585 | + | ||
17586 | + me->ops = &resizer_media_ops; | ||
17587 | + ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0); | ||
17588 | + if (ret < 0) | ||
17589 | + return ret; | ||
17590 | + | ||
17591 | + resizer_init_formats(sd, NULL); | ||
17592 | + | ||
17593 | + res->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
17594 | + res->video_in.ops = &resizer_video_ops; | ||
17595 | + res->video_in.isp = to_isp_device(res); | ||
17596 | + res->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; | ||
17597 | + res->video_in.bpl_alignment = 32; | ||
17598 | + res->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
17599 | + res->video_out.ops = &resizer_video_ops; | ||
17600 | + res->video_out.isp = to_isp_device(res); | ||
17601 | + res->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; | ||
17602 | + res->video_out.bpl_alignment = 32; | ||
17603 | + | ||
17604 | + ret = omap3isp_video_init(&res->video_in, "resizer"); | ||
17605 | + if (ret < 0) | ||
17606 | + return ret; | ||
17607 | + | ||
17608 | + ret = omap3isp_video_init(&res->video_out, "resizer"); | ||
17609 | + if (ret < 0) | ||
17610 | + return ret; | ||
17611 | + | ||
17612 | + /* Connect the video nodes to the resizer subdev. */ | ||
17613 | + ret = media_entity_create_link(&res->video_in.video.entity, 0, | ||
17614 | + &res->subdev.entity, RESZ_PAD_SINK, 0); | ||
17615 | + if (ret < 0) | ||
17616 | + return ret; | ||
17617 | + | ||
17618 | + ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE, | ||
17619 | + &res->video_out.video.entity, 0, 0); | ||
17620 | + if (ret < 0) | ||
17621 | + return ret; | ||
17622 | + | ||
17623 | + return 0; | ||
17624 | +} | ||
17625 | + | ||
17626 | +void omap3isp_resizer_unregister_entities(struct isp_res_device *res) | ||
17627 | +{ | ||
17628 | + media_entity_cleanup(&res->subdev.entity); | ||
17629 | + | ||
17630 | + v4l2_device_unregister_subdev(&res->subdev); | ||
17631 | + v4l2_ctrl_handler_free(&res->ctrls); | ||
17632 | + omap3isp_video_unregister(&res->video_in); | ||
17633 | + omap3isp_video_unregister(&res->video_out); | ||
17634 | +} | ||
17635 | + | ||
17636 | +int omap3isp_resizer_register_entities(struct isp_res_device *res, | ||
17637 | + struct v4l2_device *vdev) | ||
17638 | +{ | ||
17639 | + int ret; | ||
17640 | + | ||
17641 | + /* Register the subdev and video nodes. */ | ||
17642 | + ret = v4l2_device_register_subdev(vdev, &res->subdev); | ||
17643 | + if (ret < 0) | ||
17644 | + goto error; | ||
17645 | + | ||
17646 | + ret = omap3isp_video_register(&res->video_in, vdev); | ||
17647 | + if (ret < 0) | ||
17648 | + goto error; | ||
17649 | + | ||
17650 | + ret = omap3isp_video_register(&res->video_out, vdev); | ||
17651 | + if (ret < 0) | ||
17652 | + goto error; | ||
17653 | + | ||
17654 | + return 0; | ||
17655 | + | ||
17656 | +error: | ||
17657 | + omap3isp_resizer_unregister_entities(res); | ||
17658 | + return ret; | ||
17659 | +} | ||
17660 | + | ||
17661 | +/* ----------------------------------------------------------------------------- | ||
17662 | + * ISP resizer initialization and cleanup | ||
17663 | + */ | ||
17664 | + | ||
17665 | +void omap3isp_resizer_cleanup(struct isp_device *isp) | ||
17666 | +{ | ||
17667 | +} | ||
17668 | + | ||
17669 | +/* | ||
17670 | + * isp_resizer_init - Resizer initialization. | ||
17671 | + * @isp : Pointer to ISP device | ||
17672 | + * return -ENOMEM or zero on success | ||
17673 | + */ | ||
17674 | +int omap3isp_resizer_init(struct isp_device *isp) | ||
17675 | +{ | ||
17676 | + struct isp_res_device *res = &isp->isp_res; | ||
17677 | + int ret; | ||
17678 | + | ||
17679 | + init_waitqueue_head(&res->wait); | ||
17680 | + atomic_set(&res->stopping, 0); | ||
17681 | + ret = resizer_init_entities(res); | ||
17682 | + if (ret < 0) | ||
17683 | + goto out; | ||
17684 | + | ||
17685 | +out: | ||
17686 | + if (ret) | ||
17687 | + omap3isp_resizer_cleanup(isp); | ||
17688 | + | ||
17689 | + return ret; | ||
17690 | +} | ||
17691 | diff --git a/drivers/media/video/isp/ispresizer.h b/drivers/media/video/isp/ispresizer.h | ||
17692 | new file mode 100644 | ||
17693 | index 0000000..39d188f | ||
17694 | --- /dev/null | ||
17695 | +++ b/drivers/media/video/isp/ispresizer.h | ||
17696 | @@ -0,0 +1,150 @@ | ||
17697 | +/* | ||
17698 | + * ispresizer.h | ||
17699 | + * | ||
17700 | + * TI OMAP3 ISP - Resizer module | ||
17701 | + * | ||
17702 | + * Copyright (C) 2010 Nokia Corporation | ||
17703 | + * Copyright (C) 2009 Texas Instruments, Inc | ||
17704 | + * | ||
17705 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
17706 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
17707 | + * | ||
17708 | + * This program is free software; you can redistribute it and/or modify | ||
17709 | + * it under the terms of the GNU General Public License version 2 as | ||
17710 | + * published by the Free Software Foundation. | ||
17711 | + * | ||
17712 | + * This program is distributed in the hope that it will be useful, but | ||
17713 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17714 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17715 | + * General Public License for more details. | ||
17716 | + * | ||
17717 | + * You should have received a copy of the GNU General Public License | ||
17718 | + * along with this program; if not, write to the Free Software | ||
17719 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
17720 | + * 02110-1301 USA | ||
17721 | + */ | ||
17722 | + | ||
17723 | +#ifndef OMAP3_ISP_RESIZER_H | ||
17724 | +#define OMAP3_ISP_RESIZER_H | ||
17725 | + | ||
17726 | +#include <linux/types.h> | ||
17727 | +#include <media/v4l2-ctrls.h> | ||
17728 | + | ||
17729 | +/* | ||
17730 | + * Constants for filter coefficents count | ||
17731 | + */ | ||
17732 | +#define COEFF_CNT 32 | ||
17733 | + | ||
17734 | +/* | ||
17735 | + * struct isprsz_coef - Structure for resizer filter coeffcients. | ||
17736 | + * @h_filter_coef_4tap: Horizontal filter coefficients for 8-phase/4-tap | ||
17737 | + * mode (.5x-4x) | ||
17738 | + * @v_filter_coef_4tap: Vertical filter coefficients for 8-phase/4-tap | ||
17739 | + * mode (.5x-4x) | ||
17740 | + * @h_filter_coef_7tap: Horizontal filter coefficients for 4-phase/7-tap | ||
17741 | + * mode (.25x-.5x) | ||
17742 | + * @v_filter_coef_7tap: Vertical filter coefficients for 4-phase/7-tap | ||
17743 | + * mode (.25x-.5x) | ||
17744 | + */ | ||
17745 | +struct isprsz_coef { | ||
17746 | + u16 h_filter_coef_4tap[32]; | ||
17747 | + u16 v_filter_coef_4tap[32]; | ||
17748 | + /* Every 8th value is a dummy value in the following arrays: */ | ||
17749 | + u16 h_filter_coef_7tap[32]; | ||
17750 | + u16 v_filter_coef_7tap[32]; | ||
17751 | +}; | ||
17752 | + | ||
17753 | +/* Chrominance horizontal algorithm */ | ||
17754 | +enum resizer_chroma_algo { | ||
17755 | + RSZ_THE_SAME = 0, /* Chrominance the same as Luminance */ | ||
17756 | + RSZ_BILINEAR = 1, /* Chrominance uses bilinear interpolation */ | ||
17757 | +}; | ||
17758 | + | ||
17759 | +/* Resizer input type select */ | ||
17760 | +enum resizer_colors_type { | ||
17761 | + RSZ_YUV422 = 0, /* YUV422 color is interleaved */ | ||
17762 | + RSZ_COLOR8 = 1, /* Color separate data on 8 bits */ | ||
17763 | +}; | ||
17764 | + | ||
17765 | +/* | ||
17766 | + * Structure for horizontal and vertical resizing value | ||
17767 | + */ | ||
17768 | +struct resizer_ratio { | ||
17769 | + u32 horz; | ||
17770 | + u32 vert; | ||
17771 | +}; | ||
17772 | + | ||
17773 | +/* | ||
17774 | + * Structure for luminance enhancer parameters. | ||
17775 | + */ | ||
17776 | +struct resizer_luma_yenh { | ||
17777 | + u8 algo; /* algorithm select. */ | ||
17778 | + u8 gain; /* maximum gain. */ | ||
17779 | + u8 slope; /* slope. */ | ||
17780 | + u8 core; /* core offset. */ | ||
17781 | +}; | ||
17782 | + | ||
17783 | +enum resizer_input_entity { | ||
17784 | + RESIZER_INPUT_NONE, | ||
17785 | + RESIZER_INPUT_VP, /* input video port - prev or ccdc */ | ||
17786 | + RESIZER_INPUT_MEMORY, | ||
17787 | +}; | ||
17788 | + | ||
17789 | +/* Sink and source resizer pads */ | ||
17790 | +#define RESZ_PAD_SINK 0 | ||
17791 | +#define RESZ_PAD_SOURCE 1 | ||
17792 | +#define RESZ_PADS_NUM 2 | ||
17793 | + | ||
17794 | +/* | ||
17795 | + * struct isp_res_device - OMAP3 ISP resizer module | ||
17796 | + * @crop.request: Crop rectangle requested by the user | ||
17797 | + * @crop.active: Active crop rectangle (based on hardware requirements) | ||
17798 | + */ | ||
17799 | +struct isp_res_device { | ||
17800 | + struct v4l2_subdev subdev; | ||
17801 | + struct media_pad pads[RESZ_PADS_NUM]; | ||
17802 | + struct v4l2_mbus_framefmt formats[RESZ_PADS_NUM]; | ||
17803 | + | ||
17804 | + struct v4l2_ctrl_handler ctrls; | ||
17805 | + | ||
17806 | + enum resizer_input_entity input; | ||
17807 | + struct isp_video video_in; | ||
17808 | + struct isp_video video_out; | ||
17809 | + unsigned int error; | ||
17810 | + | ||
17811 | + u32 addr_base; /* stored source buffer address in memory mode */ | ||
17812 | + u32 crop_offset; /* additional offset for crop in memory mode */ | ||
17813 | + struct resizer_ratio ratio; | ||
17814 | + int pm_state; | ||
17815 | + unsigned int applycrop:1; | ||
17816 | + enum isp_pipeline_stream_state state; | ||
17817 | + wait_queue_head_t wait; | ||
17818 | + atomic_t stopping; | ||
17819 | + | ||
17820 | + struct { | ||
17821 | + struct v4l2_rect request; | ||
17822 | + struct v4l2_rect active; | ||
17823 | + } crop; | ||
17824 | +}; | ||
17825 | + | ||
17826 | +struct isp_device; | ||
17827 | + | ||
17828 | +int omap3isp_resizer_init(struct isp_device *isp); | ||
17829 | +void omap3isp_resizer_cleanup(struct isp_device *isp); | ||
17830 | + | ||
17831 | +int omap3isp_resizer_register_entities(struct isp_res_device *res, | ||
17832 | + struct v4l2_device *vdev); | ||
17833 | +void omap3isp_resizer_unregister_entities(struct isp_res_device *res); | ||
17834 | +void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res); | ||
17835 | +void omap3isp_resizer_isr(struct isp_res_device *isp_res); | ||
17836 | + | ||
17837 | +void omap3isp_resizer_max_rate(struct isp_res_device *res, | ||
17838 | + unsigned int *max_rate); | ||
17839 | + | ||
17840 | +void omap3isp_resizer_suspend(struct isp_res_device *isp_res); | ||
17841 | + | ||
17842 | +void omap3isp_resizer_resume(struct isp_res_device *isp_res); | ||
17843 | + | ||
17844 | +int omap3isp_resizer_busy(struct isp_res_device *isp_res); | ||
17845 | + | ||
17846 | +#endif /* OMAP3_ISP_RESIZER_H */ | ||
17847 | diff --git a/drivers/media/video/isp/ispstat.c b/drivers/media/video/isp/ispstat.c | ||
17848 | new file mode 100644 | ||
17849 | index 0000000..3406572 | ||
17850 | --- /dev/null | ||
17851 | +++ b/drivers/media/video/isp/ispstat.c | ||
17852 | @@ -0,0 +1,1100 @@ | ||
17853 | +/* | ||
17854 | + * ispstat.c | ||
17855 | + * | ||
17856 | + * TI OMAP3 ISP - Statistics core | ||
17857 | + * | ||
17858 | + * Copyright (C) 2010 Nokia Corporation | ||
17859 | + * Copyright (C) 2009 Texas Instruments, Inc | ||
17860 | + * | ||
17861 | + * Contacts: David Cohen <david.cohen@nokia.com> | ||
17862 | + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
17863 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
17864 | + * | ||
17865 | + * This program is free software; you can redistribute it and/or modify | ||
17866 | + * it under the terms of the GNU General Public License version 2 as | ||
17867 | + * published by the Free Software Foundation. | ||
17868 | + * | ||
17869 | + * This program is distributed in the hope that it will be useful, but | ||
17870 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17871 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17872 | + * General Public License for more details. | ||
17873 | + * | ||
17874 | + * You should have received a copy of the GNU General Public License | ||
17875 | + * along with this program; if not, write to the Free Software | ||
17876 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
17877 | + * 02110-1301 USA | ||
17878 | + */ | ||
17879 | + | ||
17880 | +#include <linux/dma-mapping.h> | ||
17881 | +#include <linux/slab.h> | ||
17882 | +#include <linux/uaccess.h> | ||
17883 | + | ||
17884 | +#include "isp.h" | ||
17885 | + | ||
17886 | +#define IS_COHERENT_BUF(stat) ((stat)->dma_ch >= 0) | ||
17887 | + | ||
17888 | +/* | ||
17889 | + * MAGIC_SIZE must always be the greatest common divisor of | ||
17890 | + * AEWB_PACKET_SIZE and AF_PAXEL_SIZE. | ||
17891 | + */ | ||
17892 | +#define MAGIC_SIZE 16 | ||
17893 | +#define MAGIC_NUM 0x55 | ||
17894 | + | ||
17895 | +/* HACK: AF module seems to be writing one more paxel data than it should. */ | ||
17896 | +#define AF_EXTRA_DATA OMAP3ISP_AF_PAXEL_SIZE | ||
17897 | + | ||
17898 | +/* | ||
17899 | + * HACK: H3A modules go to an invalid state after have a SBL overflow. It makes | ||
17900 | + * the next buffer to start to be written in the same point where the overflow | ||
17901 | + * occurred instead of the configured address. The only known way to make it to | ||
17902 | + * go back to a valid state is having a valid buffer processing. Of course it | ||
17903 | + * requires at least a doubled buffer size to avoid an access to invalid memory | ||
17904 | + * region. But it does not fix everything. It may happen more than one | ||
17905 | + * consecutive SBL overflows. In that case, it might be unpredictable how many | ||
17906 | + * buffers the allocated memory should fit. For that case, a recover | ||
17907 | + * configuration was created. It produces the minimum buffer size for each H3A | ||
17908 | + * module and decrease the change for more SBL overflows. This recover state | ||
17909 | + * will be enabled every time a SBL overflow occur. As the output buffer size | ||
17910 | + * isn't big, it's possible to have an extra size able to fit many recover | ||
17911 | + * buffers making it extreamily unlikely to have an access to invalid memory | ||
17912 | + * region. | ||
17913 | + */ | ||
17914 | +#define NUM_H3A_RECOVER_BUFS 10 | ||
17915 | + | ||
17916 | +/* | ||
17917 | + * HACK: Because of HW issues the generic layer sometimes need to have | ||
17918 | + * different behaviour for different statistic modules. | ||
17919 | + */ | ||
17920 | +#define IS_H3A_AF(stat) ((stat) == &(stat)->isp->isp_af) | ||
17921 | +#define IS_H3A_AEWB(stat) ((stat) == &(stat)->isp->isp_aewb) | ||
17922 | +#define IS_H3A(stat) (IS_H3A_AF(stat) || IS_H3A_AEWB(stat)) | ||
17923 | + | ||
17924 | +static void __isp_stat_buf_sync_magic(struct ispstat *stat, | ||
17925 | + struct ispstat_buffer *buf, | ||
17926 | + u32 buf_size, enum dma_data_direction dir, | ||
17927 | + void (*dma_sync)(struct device *, | ||
17928 | + dma_addr_t, unsigned long, size_t, | ||
17929 | + enum dma_data_direction)) | ||
17930 | +{ | ||
17931 | + struct device *dev = stat->isp->dev; | ||
17932 | + struct page *pg; | ||
17933 | + dma_addr_t dma_addr; | ||
17934 | + u32 offset; | ||
17935 | + | ||
17936 | + /* Initial magic words */ | ||
17937 | + pg = vmalloc_to_page(buf->virt_addr); | ||
17938 | + dma_addr = page_to_dma(dev, pg); | ||
17939 | + dma_sync(dev, dma_addr, 0, MAGIC_SIZE, dir); | ||
17940 | + | ||
17941 | + /* Final magic words */ | ||
17942 | + pg = vmalloc_to_page(buf->virt_addr + buf_size); | ||
17943 | + dma_addr = page_to_dma(dev, pg); | ||
17944 | + offset = ((u32)buf->virt_addr + buf_size) & ~PAGE_MASK; | ||
17945 | + dma_sync(dev, dma_addr, offset, MAGIC_SIZE, dir); | ||
17946 | +} | ||
17947 | + | ||
17948 | +static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat, | ||
17949 | + struct ispstat_buffer *buf, | ||
17950 | + u32 buf_size, | ||
17951 | + enum dma_data_direction dir) | ||
17952 | +{ | ||
17953 | + if (IS_COHERENT_BUF(stat)) | ||
17954 | + return; | ||
17955 | + | ||
17956 | + __isp_stat_buf_sync_magic(stat, buf, buf_size, dir, | ||
17957 | + dma_sync_single_range_for_device); | ||
17958 | +} | ||
17959 | + | ||
17960 | +static void isp_stat_buf_sync_magic_for_cpu(struct ispstat *stat, | ||
17961 | + struct ispstat_buffer *buf, | ||
17962 | + u32 buf_size, | ||
17963 | + enum dma_data_direction dir) | ||
17964 | +{ | ||
17965 | + if (IS_COHERENT_BUF(stat)) | ||
17966 | + return; | ||
17967 | + | ||
17968 | + __isp_stat_buf_sync_magic(stat, buf, buf_size, dir, | ||
17969 | + dma_sync_single_range_for_cpu); | ||
17970 | +} | ||
17971 | + | ||
17972 | +static int isp_stat_buf_check_magic(struct ispstat *stat, | ||
17973 | + struct ispstat_buffer *buf) | ||
17974 | +{ | ||
17975 | + const u32 buf_size = IS_H3A_AF(stat) ? | ||
17976 | + buf->buf_size + AF_EXTRA_DATA : buf->buf_size; | ||
17977 | + u8 *w; | ||
17978 | + u8 *end; | ||
17979 | + int ret = -EINVAL; | ||
17980 | + | ||
17981 | + isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE); | ||
17982 | + | ||
17983 | + /* Checking initial magic numbers. They shouldn't be here anymore. */ | ||
17984 | + for (w = buf->virt_addr, end = w + MAGIC_SIZE; w < end; w++) | ||
17985 | + if (likely(*w != MAGIC_NUM)) | ||
17986 | + ret = 0; | ||
17987 | + | ||
17988 | + if (ret) { | ||
17989 | + dev_dbg(stat->isp->dev, "%s: beginning magic check does not " | ||
17990 | + "match.\n", stat->subdev.name); | ||
17991 | + return ret; | ||
17992 | + } | ||
17993 | + | ||
17994 | + /* Checking magic numbers at the end. They must be still here. */ | ||
17995 | + for (w = buf->virt_addr + buf_size, end = w + MAGIC_SIZE; | ||
17996 | + w < end; w++) { | ||
17997 | + if (unlikely(*w != MAGIC_NUM)) { | ||
17998 | + dev_dbg(stat->isp->dev, "%s: endding magic check does " | ||
17999 | + "not match.\n", stat->subdev.name); | ||
18000 | + return -EINVAL; | ||
18001 | + } | ||
18002 | + } | ||
18003 | + | ||
18004 | + isp_stat_buf_sync_magic_for_device(stat, buf, buf_size, | ||
18005 | + DMA_FROM_DEVICE); | ||
18006 | + | ||
18007 | + return 0; | ||
18008 | +} | ||
18009 | + | ||
18010 | +static void isp_stat_buf_insert_magic(struct ispstat *stat, | ||
18011 | + struct ispstat_buffer *buf) | ||
18012 | +{ | ||
18013 | + const u32 buf_size = IS_H3A_AF(stat) ? | ||
18014 | + stat->buf_size + AF_EXTRA_DATA : stat->buf_size; | ||
18015 | + | ||
18016 | + isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE); | ||
18017 | + | ||
18018 | + /* | ||
18019 | + * Inserting MAGIC_NUM at the beginning and end of the buffer. | ||
18020 | + * buf->buf_size is set only after the buffer is queued. For now the | ||
18021 | + * right buf_size for the current configuration is pointed by | ||
18022 | + * stat->buf_size. | ||
18023 | + */ | ||
18024 | + memset(buf->virt_addr, MAGIC_NUM, MAGIC_SIZE); | ||
18025 | + memset(buf->virt_addr + buf_size, MAGIC_NUM, MAGIC_SIZE); | ||
18026 | + | ||
18027 | + isp_stat_buf_sync_magic_for_device(stat, buf, buf_size, | ||
18028 | + DMA_BIDIRECTIONAL); | ||
18029 | +} | ||
18030 | + | ||
18031 | +static void isp_stat_buf_sync_for_device(struct ispstat *stat, | ||
18032 | + struct ispstat_buffer *buf) | ||
18033 | +{ | ||
18034 | + if (IS_COHERENT_BUF(stat)) | ||
18035 | + return; | ||
18036 | + | ||
18037 | + dma_sync_sg_for_device(stat->isp->dev, buf->iovm->sgt->sgl, | ||
18038 | + buf->iovm->sgt->nents, DMA_FROM_DEVICE); | ||
18039 | +} | ||
18040 | + | ||
18041 | +static void isp_stat_buf_sync_for_cpu(struct ispstat *stat, | ||
18042 | + struct ispstat_buffer *buf) | ||
18043 | +{ | ||
18044 | + if (IS_COHERENT_BUF(stat)) | ||
18045 | + return; | ||
18046 | + | ||
18047 | + dma_sync_sg_for_cpu(stat->isp->dev, buf->iovm->sgt->sgl, | ||
18048 | + buf->iovm->sgt->nents, DMA_FROM_DEVICE); | ||
18049 | +} | ||
18050 | + | ||
18051 | +static void isp_stat_buf_clear(struct ispstat *stat) | ||
18052 | +{ | ||
18053 | + int i; | ||
18054 | + | ||
18055 | + for (i = 0; i < STAT_MAX_BUFS; i++) | ||
18056 | + stat->buf[i].empty = 1; | ||
18057 | +} | ||
18058 | + | ||
18059 | +static struct ispstat_buffer * | ||
18060 | +__isp_stat_buf_find(struct ispstat *stat, int look_empty) | ||
18061 | +{ | ||
18062 | + struct ispstat_buffer *found = NULL; | ||
18063 | + int i; | ||
18064 | + | ||
18065 | + for (i = 0; i < STAT_MAX_BUFS; i++) { | ||
18066 | + struct ispstat_buffer *curr = &stat->buf[i]; | ||
18067 | + | ||
18068 | + /* | ||
18069 | + * Don't select the buffer which is being copied to | ||
18070 | + * userspace or used by the module. | ||
18071 | + */ | ||
18072 | + if (curr == stat->locked_buf || curr == stat->active_buf) | ||
18073 | + continue; | ||
18074 | + | ||
18075 | + /* Don't select uninitialised buffers if it's not required */ | ||
18076 | + if (!look_empty && curr->empty) | ||
18077 | + continue; | ||
18078 | + | ||
18079 | + /* Pick uninitialised buffer over anything else if look_empty */ | ||
18080 | + if (curr->empty) { | ||
18081 | + found = curr; | ||
18082 | + break; | ||
18083 | + } | ||
18084 | + | ||
18085 | + /* Choose the oldest buffer */ | ||
18086 | + if (!found || | ||
18087 | + (s32)curr->frame_number - (s32)found->frame_number < 0) | ||
18088 | + found = curr; | ||
18089 | + } | ||
18090 | + | ||
18091 | + return found; | ||
18092 | +} | ||
18093 | + | ||
18094 | +static inline struct ispstat_buffer * | ||
18095 | +isp_stat_buf_find_oldest(struct ispstat *stat) | ||
18096 | +{ | ||
18097 | + return __isp_stat_buf_find(stat, 0); | ||
18098 | +} | ||
18099 | + | ||
18100 | +static inline struct ispstat_buffer * | ||
18101 | +isp_stat_buf_find_oldest_or_empty(struct ispstat *stat) | ||
18102 | +{ | ||
18103 | + return __isp_stat_buf_find(stat, 1); | ||
18104 | +} | ||
18105 | + | ||
18106 | +static int isp_stat_buf_queue(struct ispstat *stat) | ||
18107 | +{ | ||
18108 | + if (!stat->active_buf) | ||
18109 | + return STAT_NO_BUF; | ||
18110 | + | ||
18111 | + do_gettimeofday(&stat->active_buf->ts); | ||
18112 | + | ||
18113 | + stat->active_buf->buf_size = stat->buf_size; | ||
18114 | + if (isp_stat_buf_check_magic(stat, stat->active_buf)) { | ||
18115 | + dev_dbg(stat->isp->dev, "%s: data wasn't properly written.\n", | ||
18116 | + stat->subdev.name); | ||
18117 | + return STAT_NO_BUF; | ||
18118 | + } | ||
18119 | + stat->active_buf->config_counter = stat->config_counter; | ||
18120 | + stat->active_buf->frame_number = stat->frame_number; | ||
18121 | + stat->active_buf->empty = 0; | ||
18122 | + stat->active_buf = NULL; | ||
18123 | + | ||
18124 | + return STAT_BUF_DONE; | ||
18125 | +} | ||
18126 | + | ||
18127 | +/* Get next free buffer to write the statistics to and mark it active. */ | ||
18128 | +static void isp_stat_buf_next(struct ispstat *stat) | ||
18129 | +{ | ||
18130 | + if (unlikely(stat->active_buf)) | ||
18131 | + /* Overwriting unused active buffer */ | ||
18132 | + dev_dbg(stat->isp->dev, "%s: new buffer requested without " | ||
18133 | + "queuing active one.\n", | ||
18134 | + stat->subdev.name); | ||
18135 | + else | ||
18136 | + stat->active_buf = isp_stat_buf_find_oldest_or_empty(stat); | ||
18137 | +} | ||
18138 | + | ||
18139 | +static void isp_stat_buf_release(struct ispstat *stat) | ||
18140 | +{ | ||
18141 | + unsigned long flags; | ||
18142 | + | ||
18143 | + isp_stat_buf_sync_for_device(stat, stat->locked_buf); | ||
18144 | + spin_lock_irqsave(&stat->isp->stat_lock, flags); | ||
18145 | + stat->locked_buf = NULL; | ||
18146 | + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
18147 | +} | ||
18148 | + | ||
18149 | +/* Get buffer to userspace. */ | ||
18150 | +static struct ispstat_buffer *isp_stat_buf_get(struct ispstat *stat, | ||
18151 | + struct omap3isp_stat_data *data) | ||
18152 | +{ | ||
18153 | + int rval = 0; | ||
18154 | + unsigned long flags; | ||
18155 | + struct ispstat_buffer *buf; | ||
18156 | + | ||
18157 | + spin_lock_irqsave(&stat->isp->stat_lock, flags); | ||
18158 | + | ||
18159 | + while (1) { | ||
18160 | + buf = isp_stat_buf_find_oldest(stat); | ||
18161 | + if (!buf) { | ||
18162 | + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
18163 | + dev_dbg(stat->isp->dev, "%s: cannot find a buffer.\n", | ||
18164 | + stat->subdev.name); | ||
18165 | + return ERR_PTR(-EBUSY); | ||
18166 | + } | ||
18167 | + if (isp_stat_buf_check_magic(stat, buf)) { | ||
18168 | + dev_dbg(stat->isp->dev, "%s: current buffer has " | ||
18169 | + "corrupted data\n.", stat->subdev.name); | ||
18170 | + /* Mark empty because it doesn't have valid data. */ | ||
18171 | + buf->empty = 1; | ||
18172 | + } else { | ||
18173 | + /* Buffer isn't corrupted. */ | ||
18174 | + break; | ||
18175 | + } | ||
18176 | + } | ||
18177 | + | ||
18178 | + stat->locked_buf = buf; | ||
18179 | + | ||
18180 | + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
18181 | + | ||
18182 | + if (buf->buf_size > data->buf_size) { | ||
18183 | + dev_warn(stat->isp->dev, "%s: userspace's buffer size is " | ||
18184 | + "not enough.\n", stat->subdev.name); | ||
18185 | + isp_stat_buf_release(stat); | ||
18186 | + return ERR_PTR(-EINVAL); | ||
18187 | + } | ||
18188 | + | ||
18189 | + isp_stat_buf_sync_for_cpu(stat, buf); | ||
18190 | + | ||
18191 | + rval = copy_to_user(data->buf, | ||
18192 | + buf->virt_addr, | ||
18193 | + buf->buf_size); | ||
18194 | + | ||
18195 | + if (rval) { | ||
18196 | + dev_info(stat->isp->dev, | ||
18197 | + "%s: failed copying %d bytes of stat data\n", | ||
18198 | + stat->subdev.name, rval); | ||
18199 | + buf = ERR_PTR(-EFAULT); | ||
18200 | + isp_stat_buf_release(stat); | ||
18201 | + } | ||
18202 | + | ||
18203 | + return buf; | ||
18204 | +} | ||
18205 | + | ||
18206 | +static void isp_stat_bufs_free(struct ispstat *stat) | ||
18207 | +{ | ||
18208 | + struct isp_device *isp = stat->isp; | ||
18209 | + int i; | ||
18210 | + | ||
18211 | + for (i = 0; i < STAT_MAX_BUFS; i++) { | ||
18212 | + struct ispstat_buffer *buf = &stat->buf[i]; | ||
18213 | + | ||
18214 | + if (!IS_COHERENT_BUF(stat)) { | ||
18215 | + if (IS_ERR_OR_NULL((void *)buf->iommu_addr)) | ||
18216 | + continue; | ||
18217 | + if (buf->iovm) | ||
18218 | + dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl, | ||
18219 | + buf->iovm->sgt->nents, | ||
18220 | + DMA_FROM_DEVICE); | ||
18221 | + iommu_vfree(isp->iommu, buf->iommu_addr); | ||
18222 | + } else { | ||
18223 | + if (!buf->virt_addr) | ||
18224 | + continue; | ||
18225 | + dma_free_coherent(stat->isp->dev, stat->buf_alloc_size, | ||
18226 | + buf->virt_addr, buf->dma_addr); | ||
18227 | + } | ||
18228 | + buf->iommu_addr = 0; | ||
18229 | + buf->iovm = NULL; | ||
18230 | + buf->dma_addr = 0; | ||
18231 | + buf->virt_addr = NULL; | ||
18232 | + buf->empty = 1; | ||
18233 | + } | ||
18234 | + | ||
18235 | + dev_dbg(stat->isp->dev, "%s: all buffers were freed.\n", | ||
18236 | + stat->subdev.name); | ||
18237 | + | ||
18238 | + stat->buf_alloc_size = 0; | ||
18239 | + stat->active_buf = NULL; | ||
18240 | +} | ||
18241 | + | ||
18242 | +static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size) | ||
18243 | +{ | ||
18244 | + struct isp_device *isp = stat->isp; | ||
18245 | + int i; | ||
18246 | + | ||
18247 | + stat->buf_alloc_size = size; | ||
18248 | + | ||
18249 | + for (i = 0; i < STAT_MAX_BUFS; i++) { | ||
18250 | + struct ispstat_buffer *buf = &stat->buf[i]; | ||
18251 | + struct iovm_struct *iovm; | ||
18252 | + | ||
18253 | + WARN_ON(buf->dma_addr); | ||
18254 | + buf->iommu_addr = iommu_vmalloc(isp->iommu, 0, size, | ||
18255 | + IOMMU_FLAG); | ||
18256 | + if (IS_ERR((void *)buf->iommu_addr)) { | ||
18257 | + dev_err(stat->isp->dev, | ||
18258 | + "%s: Can't acquire memory for " | ||
18259 | + "buffer %d\n", stat->subdev.name, i); | ||
18260 | + isp_stat_bufs_free(stat); | ||
18261 | + return -ENOMEM; | ||
18262 | + } | ||
18263 | + | ||
18264 | + iovm = find_iovm_area(isp->iommu, buf->iommu_addr); | ||
18265 | + if (!iovm || | ||
18266 | + !dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents, | ||
18267 | + DMA_FROM_DEVICE)) { | ||
18268 | + isp_stat_bufs_free(stat); | ||
18269 | + return -ENOMEM; | ||
18270 | + } | ||
18271 | + buf->iovm = iovm; | ||
18272 | + | ||
18273 | + buf->virt_addr = da_to_va(stat->isp->iommu, | ||
18274 | + (u32)buf->iommu_addr); | ||
18275 | + buf->empty = 1; | ||
18276 | + dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated." | ||
18277 | + "iommu_addr=0x%08lx virt_addr=0x%08lx", | ||
18278 | + stat->subdev.name, i, buf->iommu_addr, | ||
18279 | + (unsigned long)buf->virt_addr); | ||
18280 | + } | ||
18281 | + | ||
18282 | + return 0; | ||
18283 | +} | ||
18284 | + | ||
18285 | +static int isp_stat_bufs_alloc_dma(struct ispstat *stat, unsigned int size) | ||
18286 | +{ | ||
18287 | + int i; | ||
18288 | + | ||
18289 | + stat->buf_alloc_size = size; | ||
18290 | + | ||
18291 | + for (i = 0; i < STAT_MAX_BUFS; i++) { | ||
18292 | + struct ispstat_buffer *buf = &stat->buf[i]; | ||
18293 | + | ||
18294 | + WARN_ON(buf->iommu_addr); | ||
18295 | + buf->virt_addr = dma_alloc_coherent(stat->isp->dev, size, | ||
18296 | + &buf->dma_addr, GFP_KERNEL | GFP_DMA); | ||
18297 | + | ||
18298 | + if (!buf->virt_addr || !buf->dma_addr) { | ||
18299 | + dev_info(stat->isp->dev, | ||
18300 | + "%s: Can't acquire memory for " | ||
18301 | + "DMA buffer %d\n", stat->subdev.name, i); | ||
18302 | + isp_stat_bufs_free(stat); | ||
18303 | + return -ENOMEM; | ||
18304 | + } | ||
18305 | + buf->empty = 1; | ||
18306 | + | ||
18307 | + dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated." | ||
18308 | + "dma_addr=0x%08lx virt_addr=0x%08lx\n", | ||
18309 | + stat->subdev.name, i, (unsigned long)buf->dma_addr, | ||
18310 | + (unsigned long)buf->virt_addr); | ||
18311 | + } | ||
18312 | + | ||
18313 | + return 0; | ||
18314 | +} | ||
18315 | + | ||
18316 | +static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size) | ||
18317 | +{ | ||
18318 | + unsigned long flags; | ||
18319 | + | ||
18320 | + spin_lock_irqsave(&stat->isp->stat_lock, flags); | ||
18321 | + | ||
18322 | + BUG_ON(stat->locked_buf != NULL); | ||
18323 | + | ||
18324 | + /* Are the old buffers big enough? */ | ||
18325 | + if (stat->buf_alloc_size >= size) { | ||
18326 | + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
18327 | + return 0; | ||
18328 | + } | ||
18329 | + | ||
18330 | + if (stat->state != ISPSTAT_DISABLED || stat->buf_processing) { | ||
18331 | + dev_info(stat->isp->dev, | ||
18332 | + "%s: trying to allocate memory when busy\n", | ||
18333 | + stat->subdev.name); | ||
18334 | + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
18335 | + return -EBUSY; | ||
18336 | + } | ||
18337 | + | ||
18338 | + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
18339 | + | ||
18340 | + isp_stat_bufs_free(stat); | ||
18341 | + | ||
18342 | + if (IS_COHERENT_BUF(stat)) | ||
18343 | + return isp_stat_bufs_alloc_dma(stat, size); | ||
18344 | + else | ||
18345 | + return isp_stat_bufs_alloc_iommu(stat, size); | ||
18346 | +} | ||
18347 | + | ||
18348 | +static void isp_stat_queue_event(struct ispstat *stat, int err) | ||
18349 | +{ | ||
18350 | + struct video_device *vdev = &stat->subdev.devnode; | ||
18351 | + struct v4l2_event event; | ||
18352 | + struct omap3isp_stat_event_status *status = (void *)event.u.data; | ||
18353 | + | ||
18354 | + memset(&event, 0, sizeof(event)); | ||
18355 | + if (!err) { | ||
18356 | + status->frame_number = stat->frame_number; | ||
18357 | + status->config_counter = stat->config_counter; | ||
18358 | + } else { | ||
18359 | + status->buf_err = 1; | ||
18360 | + } | ||
18361 | + event.type = stat->event_type; | ||
18362 | + v4l2_event_queue(vdev, &event); | ||
18363 | +} | ||
18364 | + | ||
18365 | + | ||
18366 | +/* | ||
18367 | + * omap3isp_stat_request_statistics - Request statistics. | ||
18368 | + * @data: Pointer to return statistics data. | ||
18369 | + * | ||
18370 | + * Returns 0 if successful. | ||
18371 | + */ | ||
18372 | +int omap3isp_stat_request_statistics(struct ispstat *stat, | ||
18373 | + struct omap3isp_stat_data *data) | ||
18374 | +{ | ||
18375 | + struct ispstat_buffer *buf; | ||
18376 | + | ||
18377 | + if (stat->state != ISPSTAT_ENABLED) { | ||
18378 | + dev_dbg(stat->isp->dev, "%s: engine not enabled.\n", | ||
18379 | + stat->subdev.name); | ||
18380 | + return -EINVAL; | ||
18381 | + } | ||
18382 | + | ||
18383 | + mutex_lock(&stat->ioctl_lock); | ||
18384 | + buf = isp_stat_buf_get(stat, data); | ||
18385 | + if (IS_ERR(buf)) { | ||
18386 | + mutex_unlock(&stat->ioctl_lock); | ||
18387 | + return PTR_ERR(buf); | ||
18388 | + } | ||
18389 | + | ||
18390 | + data->ts = buf->ts; | ||
18391 | + data->config_counter = buf->config_counter; | ||
18392 | + data->frame_number = buf->frame_number; | ||
18393 | + data->buf_size = buf->buf_size; | ||
18394 | + | ||
18395 | + /* | ||
18396 | + * Deprecated. Number of new buffers is always equal to number of | ||
18397 | + * queued events without error flag. By setting it to 0, userspace | ||
18398 | + * won't try to request new buffer without receiving new event. | ||
18399 | + * This field must go away in future. | ||
18400 | + */ | ||
18401 | + data->new_bufs = 0; | ||
18402 | + | ||
18403 | + buf->empty = 1; | ||
18404 | + isp_stat_buf_release(stat); | ||
18405 | + mutex_unlock(&stat->ioctl_lock); | ||
18406 | + | ||
18407 | + return 0; | ||
18408 | +} | ||
18409 | + | ||
18410 | +/* | ||
18411 | + * omap3isp_stat_config - Receives new statistic engine configuration. | ||
18412 | + * @new_conf: Pointer to config structure. | ||
18413 | + * | ||
18414 | + * Returns 0 if successful, -EINVAL if new_conf pointer is NULL, -ENOMEM if | ||
18415 | + * was unable to allocate memory for the buffer, or other errors if parameters | ||
18416 | + * are invalid. | ||
18417 | + */ | ||
18418 | +int omap3isp_stat_config(struct ispstat *stat, void *new_conf) | ||
18419 | +{ | ||
18420 | + int ret; | ||
18421 | + unsigned long irqflags; | ||
18422 | + struct ispstat_generic_config *user_cfg = new_conf; | ||
18423 | + u32 buf_size = user_cfg->buf_size; | ||
18424 | + | ||
18425 | + if (!new_conf) { | ||
18426 | + dev_dbg(stat->isp->dev, "%s: configuration is NULL\n", | ||
18427 | + stat->subdev.name); | ||
18428 | + return -EINVAL; | ||
18429 | + } | ||
18430 | + | ||
18431 | + mutex_lock(&stat->ioctl_lock); | ||
18432 | + | ||
18433 | + dev_dbg(stat->isp->dev, "%s: configuring module with buffer " | ||
18434 | + "size=0x%08lx\n", stat->subdev.name, (unsigned long)buf_size); | ||
18435 | + | ||
18436 | + ret = stat->ops->validate_params(stat, new_conf); | ||
18437 | + if (ret) { | ||
18438 | + mutex_unlock(&stat->ioctl_lock); | ||
18439 | + dev_dbg(stat->isp->dev, "%s: configuration values are " | ||
18440 | + "invalid.\n", stat->subdev.name); | ||
18441 | + return ret; | ||
18442 | + } | ||
18443 | + | ||
18444 | + if (buf_size != user_cfg->buf_size) | ||
18445 | + dev_dbg(stat->isp->dev, "%s: driver has corrected buffer size " | ||
18446 | + "request to 0x%08lx\n", stat->subdev.name, | ||
18447 | + (unsigned long)user_cfg->buf_size); | ||
18448 | + | ||
18449 | + /* | ||
18450 | + * Hack: H3A modules may need a doubled buffer size to avoid access | ||
18451 | + * to a invalid memory address after a SBL overflow. | ||
18452 | + * The buffer size is always PAGE_ALIGNED. | ||
18453 | + * Hack 2: MAGIC_SIZE is added to buf_size so a magic word can be | ||
18454 | + * inserted at the end to data integrity check purpose. | ||
18455 | + * Hack 3: AF module writes one paxel data more than it should, so | ||
18456 | + * the buffer allocation must consider it to avoid invalid memory | ||
18457 | + * access. | ||
18458 | + * Hack 4: H3A need to allocate extra space for the recover state. | ||
18459 | + */ | ||
18460 | + if (IS_H3A(stat)) { | ||
18461 | + buf_size = user_cfg->buf_size * 2 + MAGIC_SIZE; | ||
18462 | + if (IS_H3A_AF(stat)) | ||
18463 | + /* | ||
18464 | + * Adding one extra paxel data size for each recover | ||
18465 | + * buffer + 2 regular ones. | ||
18466 | + */ | ||
18467 | + buf_size += AF_EXTRA_DATA * (NUM_H3A_RECOVER_BUFS + 2); | ||
18468 | + if (stat->recover_priv) { | ||
18469 | + struct ispstat_generic_config *recover_cfg = | ||
18470 | + stat->recover_priv; | ||
18471 | + buf_size += recover_cfg->buf_size * | ||
18472 | + NUM_H3A_RECOVER_BUFS; | ||
18473 | + } | ||
18474 | + buf_size = PAGE_ALIGN(buf_size); | ||
18475 | + } else { /* Histogram */ | ||
18476 | + buf_size = PAGE_ALIGN(user_cfg->buf_size + MAGIC_SIZE); | ||
18477 | + } | ||
18478 | + | ||
18479 | + ret = isp_stat_bufs_alloc(stat, buf_size); | ||
18480 | + if (ret) { | ||
18481 | + mutex_unlock(&stat->ioctl_lock); | ||
18482 | + return ret; | ||
18483 | + } | ||
18484 | + | ||
18485 | + spin_lock_irqsave(&stat->isp->stat_lock, irqflags); | ||
18486 | + stat->ops->set_params(stat, new_conf); | ||
18487 | + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
18488 | + | ||
18489 | + /* | ||
18490 | + * Returning the right future config_counter for this setup, so | ||
18491 | + * userspace can *know* when it has been applied. | ||
18492 | + */ | ||
18493 | + user_cfg->config_counter = stat->config_counter + stat->inc_config; | ||
18494 | + | ||
18495 | + /* Module has a valid configuration. */ | ||
18496 | + stat->configured = 1; | ||
18497 | + dev_dbg(stat->isp->dev, "%s: module has been successfully " | ||
18498 | + "configured.\n", stat->subdev.name); | ||
18499 | + | ||
18500 | + mutex_unlock(&stat->ioctl_lock); | ||
18501 | + | ||
18502 | + return 0; | ||
18503 | +} | ||
18504 | + | ||
18505 | +/* | ||
18506 | + * isp_stat_buf_process - Process statistic buffers. | ||
18507 | + * @buf_state: points out if buffer is ready to be processed. It's necessary | ||
18508 | + * because histogram needs to copy the data from internal memory | ||
18509 | + * before be able to process the buffer. | ||
18510 | + */ | ||
18511 | +static int isp_stat_buf_process(struct ispstat *stat, int buf_state) | ||
18512 | +{ | ||
18513 | + int ret = STAT_NO_BUF; | ||
18514 | + | ||
18515 | + if (!atomic_add_unless(&stat->buf_err, -1, 0) && | ||
18516 | + buf_state == STAT_BUF_DONE && stat->state == ISPSTAT_ENABLED) { | ||
18517 | + ret = isp_stat_buf_queue(stat); | ||
18518 | + isp_stat_buf_next(stat); | ||
18519 | + } | ||
18520 | + | ||
18521 | + return ret; | ||
18522 | +} | ||
18523 | + | ||
18524 | +int omap3isp_stat_pcr_busy(struct ispstat *stat) | ||
18525 | +{ | ||
18526 | + return stat->ops->busy(stat); | ||
18527 | +} | ||
18528 | + | ||
18529 | +int omap3isp_stat_busy(struct ispstat *stat) | ||
18530 | +{ | ||
18531 | + return omap3isp_stat_pcr_busy(stat) | stat->buf_processing | | ||
18532 | + (stat->state != ISPSTAT_DISABLED); | ||
18533 | +} | ||
18534 | + | ||
18535 | +/* | ||
18536 | + * isp_stat_pcr_enable - Disables/Enables statistic engines. | ||
18537 | + * @pcr_enable: 0/1 - Disables/Enables the engine. | ||
18538 | + * | ||
18539 | + * Must be called from ISP driver when the module is idle and synchronized | ||
18540 | + * with CCDC. | ||
18541 | + */ | ||
18542 | +static void isp_stat_pcr_enable(struct ispstat *stat, u8 pcr_enable) | ||
18543 | +{ | ||
18544 | + if ((stat->state != ISPSTAT_ENABLING && | ||
18545 | + stat->state != ISPSTAT_ENABLED) && pcr_enable) | ||
18546 | + /* Userspace has disabled the module. Aborting. */ | ||
18547 | + return; | ||
18548 | + | ||
18549 | + stat->ops->enable(stat, pcr_enable); | ||
18550 | + if (stat->state == ISPSTAT_DISABLING && !pcr_enable) | ||
18551 | + stat->state = ISPSTAT_DISABLED; | ||
18552 | + else if (stat->state == ISPSTAT_ENABLING && pcr_enable) | ||
18553 | + stat->state = ISPSTAT_ENABLED; | ||
18554 | +} | ||
18555 | + | ||
18556 | +void omap3isp_stat_suspend(struct ispstat *stat) | ||
18557 | +{ | ||
18558 | + unsigned long flags; | ||
18559 | + | ||
18560 | + spin_lock_irqsave(&stat->isp->stat_lock, flags); | ||
18561 | + | ||
18562 | + if (stat->state != ISPSTAT_DISABLED) | ||
18563 | + stat->ops->enable(stat, 0); | ||
18564 | + if (stat->state == ISPSTAT_ENABLED) | ||
18565 | + stat->state = ISPSTAT_SUSPENDED; | ||
18566 | + | ||
18567 | + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
18568 | +} | ||
18569 | + | ||
18570 | +void omap3isp_stat_resume(struct ispstat *stat) | ||
18571 | +{ | ||
18572 | + /* Module will be re-enabled with its pipeline */ | ||
18573 | + if (stat->state == ISPSTAT_SUSPENDED) | ||
18574 | + stat->state = ISPSTAT_ENABLING; | ||
18575 | +} | ||
18576 | + | ||
18577 | +static void isp_stat_try_enable(struct ispstat *stat) | ||
18578 | +{ | ||
18579 | + unsigned long irqflags; | ||
18580 | + | ||
18581 | + if (stat->priv == NULL) | ||
18582 | + /* driver wasn't initialised */ | ||
18583 | + return; | ||
18584 | + | ||
18585 | + spin_lock_irqsave(&stat->isp->stat_lock, irqflags); | ||
18586 | + if (stat->state == ISPSTAT_ENABLING && !stat->buf_processing && | ||
18587 | + stat->buf_alloc_size) { | ||
18588 | + /* | ||
18589 | + * Userspace's requested to enable the engine but it wasn't yet. | ||
18590 | + * Let's do that now. | ||
18591 | + */ | ||
18592 | + stat->update = 1; | ||
18593 | + isp_stat_buf_next(stat); | ||
18594 | + stat->ops->setup_regs(stat, stat->priv); | ||
18595 | + isp_stat_buf_insert_magic(stat, stat->active_buf); | ||
18596 | + | ||
18597 | + /* | ||
18598 | + * H3A module has some hw issues which forces the driver to | ||
18599 | + * ignore next buffers even if it was disabled in the meantime. | ||
18600 | + * On the other hand, Histogram shouldn't ignore buffers anymore | ||
18601 | + * if it's being enabled. | ||
18602 | + */ | ||
18603 | + if (!IS_H3A(stat)) | ||
18604 | + atomic_set(&stat->buf_err, 0); | ||
18605 | + | ||
18606 | + isp_stat_pcr_enable(stat, 1); | ||
18607 | + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
18608 | + dev_dbg(stat->isp->dev, "%s: module is enabled.\n", | ||
18609 | + stat->subdev.name); | ||
18610 | + } else { | ||
18611 | + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
18612 | + } | ||
18613 | +} | ||
18614 | + | ||
18615 | +void omap3isp_stat_isr_frame_sync(struct ispstat *stat) | ||
18616 | +{ | ||
18617 | + isp_stat_try_enable(stat); | ||
18618 | +} | ||
18619 | + | ||
18620 | +void omap3isp_stat_sbl_overflow(struct ispstat *stat) | ||
18621 | +{ | ||
18622 | + unsigned long irqflags; | ||
18623 | + | ||
18624 | + spin_lock_irqsave(&stat->isp->stat_lock, irqflags); | ||
18625 | + /* | ||
18626 | + * Due to a H3A hw issue which prevents the next buffer to start from | ||
18627 | + * the correct memory address, 2 buffers must be ignored. | ||
18628 | + */ | ||
18629 | + atomic_set(&stat->buf_err, 2); | ||
18630 | + | ||
18631 | + /* | ||
18632 | + * If more than one SBL overflow happen in a row, H3A module may access | ||
18633 | + * invalid memory region. | ||
18634 | + * stat->sbl_ovl_recover is set to tell to the driver to temporarily use | ||
18635 | + * a soft configuration which helps to avoid consecutive overflows. | ||
18636 | + */ | ||
18637 | + if (stat->recover_priv) | ||
18638 | + stat->sbl_ovl_recover = 1; | ||
18639 | + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
18640 | +} | ||
18641 | + | ||
18642 | +/* | ||
18643 | + * omap3isp_stat_enable - Disable/Enable statistic engine as soon as possible | ||
18644 | + * @enable: 0/1 - Disables/Enables the engine. | ||
18645 | + * | ||
18646 | + * Client should configure all the module registers before this. | ||
18647 | + * This function can be called from a userspace request. | ||
18648 | + */ | ||
18649 | +int omap3isp_stat_enable(struct ispstat *stat, u8 enable) | ||
18650 | +{ | ||
18651 | + unsigned long irqflags; | ||
18652 | + | ||
18653 | + dev_dbg(stat->isp->dev, "%s: user wants to %s module.\n", | ||
18654 | + stat->subdev.name, enable ? "enable" : "disable"); | ||
18655 | + | ||
18656 | + /* Prevent enabling while configuring */ | ||
18657 | + mutex_lock(&stat->ioctl_lock); | ||
18658 | + | ||
18659 | + spin_lock_irqsave(&stat->isp->stat_lock, irqflags); | ||
18660 | + | ||
18661 | + if (!stat->configured && enable) { | ||
18662 | + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
18663 | + mutex_unlock(&stat->ioctl_lock); | ||
18664 | + dev_dbg(stat->isp->dev, "%s: cannot enable module as it's " | ||
18665 | + "never been successfully configured so far.\n", | ||
18666 | + stat->subdev.name); | ||
18667 | + return -EINVAL; | ||
18668 | + } | ||
18669 | + | ||
18670 | + if (enable) { | ||
18671 | + if (stat->state == ISPSTAT_DISABLING) | ||
18672 | + /* Previous disabling request wasn't done yet */ | ||
18673 | + stat->state = ISPSTAT_ENABLED; | ||
18674 | + else if (stat->state == ISPSTAT_DISABLED) | ||
18675 | + /* Module is now being enabled */ | ||
18676 | + stat->state = ISPSTAT_ENABLING; | ||
18677 | + } else { | ||
18678 | + if (stat->state == ISPSTAT_ENABLING) { | ||
18679 | + /* Previous enabling request wasn't done yet */ | ||
18680 | + stat->state = ISPSTAT_DISABLED; | ||
18681 | + } else if (stat->state == ISPSTAT_ENABLED) { | ||
18682 | + /* Module is now being disabled */ | ||
18683 | + stat->state = ISPSTAT_DISABLING; | ||
18684 | + isp_stat_buf_clear(stat); | ||
18685 | + } | ||
18686 | + } | ||
18687 | + | ||
18688 | + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
18689 | + mutex_unlock(&stat->ioctl_lock); | ||
18690 | + | ||
18691 | + return 0; | ||
18692 | +} | ||
18693 | + | ||
18694 | +int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable) | ||
18695 | +{ | ||
18696 | + struct ispstat *stat = v4l2_get_subdevdata(subdev); | ||
18697 | + | ||
18698 | + if (enable) { | ||
18699 | + /* | ||
18700 | + * Only set enable PCR bit if the module was previously | ||
18701 | + * enabled through ioct. | ||
18702 | + */ | ||
18703 | + isp_stat_try_enable(stat); | ||
18704 | + } else { | ||
18705 | + unsigned long flags; | ||
18706 | + /* Disable PCR bit and config enable field */ | ||
18707 | + omap3isp_stat_enable(stat, 0); | ||
18708 | + spin_lock_irqsave(&stat->isp->stat_lock, flags); | ||
18709 | + stat->ops->enable(stat, 0); | ||
18710 | + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
18711 | + | ||
18712 | + /* | ||
18713 | + * If module isn't busy, a new interrupt may come or not to | ||
18714 | + * set the state to DISABLED. As Histogram needs to read its | ||
18715 | + * internal memory to clear it, let interrupt handler | ||
18716 | + * responsible of changing state to DISABLED. If the last | ||
18717 | + * interrupt is coming, it's still safe as the handler will | ||
18718 | + * ignore the second time when state is already set to DISABLED. | ||
18719 | + * It's necessary to synchronize Histogram with streamoff, once | ||
18720 | + * the module may be considered idle before last SDMA transfer | ||
18721 | + * starts if we return here. | ||
18722 | + */ | ||
18723 | + if (!omap3isp_stat_pcr_busy(stat)) | ||
18724 | + omap3isp_stat_isr(stat); | ||
18725 | + | ||
18726 | + dev_dbg(stat->isp->dev, "%s: module is being disabled\n", | ||
18727 | + stat->subdev.name); | ||
18728 | + } | ||
18729 | + | ||
18730 | + return 0; | ||
18731 | +} | ||
18732 | + | ||
18733 | +/* | ||
18734 | + * __stat_isr - Interrupt handler for statistic drivers | ||
18735 | + */ | ||
18736 | +static void __stat_isr(struct ispstat *stat, int from_dma) | ||
18737 | +{ | ||
18738 | + int ret = STAT_BUF_DONE; | ||
18739 | + int buf_processing; | ||
18740 | + unsigned long irqflags; | ||
18741 | + struct isp_pipeline *pipe; | ||
18742 | + | ||
18743 | + /* | ||
18744 | + * stat->buf_processing must be set before disable module. It's | ||
18745 | + * necessary to not inform too early the buffers aren't busy in case | ||
18746 | + * of SDMA is going to be used. | ||
18747 | + */ | ||
18748 | + spin_lock_irqsave(&stat->isp->stat_lock, irqflags); | ||
18749 | + if (stat->state == ISPSTAT_DISABLED) { | ||
18750 | + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
18751 | + return; | ||
18752 | + } | ||
18753 | + buf_processing = stat->buf_processing; | ||
18754 | + stat->buf_processing = 1; | ||
18755 | + stat->ops->enable(stat, 0); | ||
18756 | + | ||
18757 | + if (buf_processing && !from_dma) { | ||
18758 | + if (stat->state == ISPSTAT_ENABLED) { | ||
18759 | + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
18760 | + dev_err(stat->isp->dev, | ||
18761 | + "%s: interrupt occurred when module was still " | ||
18762 | + "processing a buffer.\n", stat->subdev.name); | ||
18763 | + ret = STAT_NO_BUF; | ||
18764 | + goto out; | ||
18765 | + } else { | ||
18766 | + /* | ||
18767 | + * Interrupt handler was called from streamoff when | ||
18768 | + * the module wasn't busy anymore to ensure it is being | ||
18769 | + * disabled after process last buffer. If such buffer | ||
18770 | + * processing has already started, no need to do | ||
18771 | + * anything else. | ||
18772 | + */ | ||
18773 | + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
18774 | + return; | ||
18775 | + } | ||
18776 | + } | ||
18777 | + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
18778 | + | ||
18779 | + /* If it's busy we can't process this buffer anymore */ | ||
18780 | + if (!omap3isp_stat_pcr_busy(stat)) { | ||
18781 | + if (!from_dma && stat->ops->buf_process) | ||
18782 | + /* Module still need to copy data to buffer. */ | ||
18783 | + ret = stat->ops->buf_process(stat); | ||
18784 | + if (ret == STAT_BUF_WAITING_DMA) | ||
18785 | + /* Buffer is not ready yet */ | ||
18786 | + return; | ||
18787 | + | ||
18788 | + spin_lock_irqsave(&stat->isp->stat_lock, irqflags); | ||
18789 | + | ||
18790 | + /* | ||
18791 | + * Histogram needs to read its internal memory to clear it | ||
18792 | + * before be disabled. For that reason, common statistic layer | ||
18793 | + * can return only after call stat's buf_process() operator. | ||
18794 | + */ | ||
18795 | + if (stat->state == ISPSTAT_DISABLING) { | ||
18796 | + stat->state = ISPSTAT_DISABLED; | ||
18797 | + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
18798 | + stat->buf_processing = 0; | ||
18799 | + return; | ||
18800 | + } | ||
18801 | + pipe = to_isp_pipeline(&stat->subdev.entity); | ||
18802 | + stat->frame_number = atomic_read(&pipe->frame_number); | ||
18803 | + | ||
18804 | + /* | ||
18805 | + * Before this point, 'ret' stores the buffer's status if it's | ||
18806 | + * ready to be processed. Afterwards, it holds the status if | ||
18807 | + * it was processed successfully. | ||
18808 | + */ | ||
18809 | + ret = isp_stat_buf_process(stat, ret); | ||
18810 | + | ||
18811 | + if (likely(!stat->sbl_ovl_recover)) { | ||
18812 | + stat->ops->setup_regs(stat, stat->priv); | ||
18813 | + } else { | ||
18814 | + /* | ||
18815 | + * Using recover config to increase the chance to have | ||
18816 | + * a good buffer processing and make the H3A module to | ||
18817 | + * go back to a valid state. | ||
18818 | + */ | ||
18819 | + stat->update = 1; | ||
18820 | + stat->ops->setup_regs(stat, stat->recover_priv); | ||
18821 | + stat->sbl_ovl_recover = 0; | ||
18822 | + | ||
18823 | + /* | ||
18824 | + * Set 'update' in case of the module needs to use | ||
18825 | + * regular configuration after next buffer. | ||
18826 | + */ | ||
18827 | + stat->update = 1; | ||
18828 | + } | ||
18829 | + | ||
18830 | + isp_stat_buf_insert_magic(stat, stat->active_buf); | ||
18831 | + | ||
18832 | + /* | ||
18833 | + * Hack: H3A modules may access invalid memory address or send | ||
18834 | + * corrupted data to userspace if more than 1 SBL overflow | ||
18835 | + * happens in a row without re-writing its buffer's start memory | ||
18836 | + * address in the meantime. Such situation is avoided if the | ||
18837 | + * module is not immediately re-enabled when the ISR misses the | ||
18838 | + * timing to process the buffer and to setup the registers. | ||
18839 | + * Because of that, pcr_enable(1) was moved to inside this 'if' | ||
18840 | + * block. But the next interruption will still happen as during | ||
18841 | + * pcr_enable(0) the module was busy. | ||
18842 | + */ | ||
18843 | + isp_stat_pcr_enable(stat, 1); | ||
18844 | + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
18845 | + } else { | ||
18846 | + /* | ||
18847 | + * If a SBL overflow occurs and the H3A driver misses the timing | ||
18848 | + * to process the buffer, stat->buf_err is set and won't be | ||
18849 | + * cleared now. So the next buffer will be correctly ignored. | ||
18850 | + * It's necessary due to a hw issue which makes the next H3A | ||
18851 | + * buffer to start from the memory address where the previous | ||
18852 | + * one stopped, instead of start where it was configured to. | ||
18853 | + * Do not "stat->buf_err = 0" here. | ||
18854 | + */ | ||
18855 | + | ||
18856 | + if (stat->ops->buf_process) | ||
18857 | + /* | ||
18858 | + * Driver may need to erase current data prior to | ||
18859 | + * process a new buffer. If it misses the timing, the | ||
18860 | + * next buffer might be wrong. So should be ignored. | ||
18861 | + * It happens only for Histogram. | ||
18862 | + */ | ||
18863 | + atomic_set(&stat->buf_err, 1); | ||
18864 | + | ||
18865 | + ret = STAT_NO_BUF; | ||
18866 | + dev_dbg(stat->isp->dev, "%s: cannot process buffer, " | ||
18867 | + "device is busy.\n", stat->subdev.name); | ||
18868 | + } | ||
18869 | + | ||
18870 | +out: | ||
18871 | + stat->buf_processing = 0; | ||
18872 | + isp_stat_queue_event(stat, ret != STAT_BUF_DONE); | ||
18873 | +} | ||
18874 | + | ||
18875 | +void omap3isp_stat_isr(struct ispstat *stat) | ||
18876 | +{ | ||
18877 | + __stat_isr(stat, 0); | ||
18878 | +} | ||
18879 | + | ||
18880 | +void omap3isp_stat_dma_isr(struct ispstat *stat) | ||
18881 | +{ | ||
18882 | + __stat_isr(stat, 1); | ||
18883 | +} | ||
18884 | + | ||
18885 | +static int isp_stat_init_entities(struct ispstat *stat, const char *name, | ||
18886 | + const struct v4l2_subdev_ops *sd_ops) | ||
18887 | +{ | ||
18888 | + struct v4l2_subdev *subdev = &stat->subdev; | ||
18889 | + struct media_entity *me = &subdev->entity; | ||
18890 | + | ||
18891 | + v4l2_subdev_init(subdev, sd_ops); | ||
18892 | + snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name); | ||
18893 | + subdev->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
18894 | + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
18895 | + subdev->nevents = STAT_NEVENTS; | ||
18896 | + v4l2_set_subdevdata(subdev, stat); | ||
18897 | + | ||
18898 | + stat->pad.flags = MEDIA_PAD_FL_INPUT; | ||
18899 | + me->ops = NULL; | ||
18900 | + | ||
18901 | + return media_entity_init(me, 1, &stat->pad, 0); | ||
18902 | +} | ||
18903 | + | ||
18904 | +int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, | ||
18905 | + struct v4l2_fh *fh, | ||
18906 | + struct v4l2_event_subscription *sub) | ||
18907 | +{ | ||
18908 | + struct ispstat *stat = v4l2_get_subdevdata(subdev); | ||
18909 | + | ||
18910 | + if (sub->type != stat->event_type) | ||
18911 | + return -EINVAL; | ||
18912 | + | ||
18913 | + return v4l2_event_subscribe(fh, sub); | ||
18914 | +} | ||
18915 | + | ||
18916 | +int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, | ||
18917 | + struct v4l2_fh *fh, | ||
18918 | + struct v4l2_event_subscription *sub) | ||
18919 | +{ | ||
18920 | + return v4l2_event_unsubscribe(fh, sub); | ||
18921 | +} | ||
18922 | + | ||
18923 | +void omap3isp_stat_unregister_entities(struct ispstat *stat) | ||
18924 | +{ | ||
18925 | + media_entity_cleanup(&stat->subdev.entity); | ||
18926 | + v4l2_device_unregister_subdev(&stat->subdev); | ||
18927 | +} | ||
18928 | + | ||
18929 | +int omap3isp_stat_register_entities(struct ispstat *stat, | ||
18930 | + struct v4l2_device *vdev) | ||
18931 | +{ | ||
18932 | + return v4l2_device_register_subdev(vdev, &stat->subdev); | ||
18933 | +} | ||
18934 | + | ||
18935 | +int omap3isp_stat_init(struct ispstat *stat, const char *name, | ||
18936 | + const struct v4l2_subdev_ops *sd_ops) | ||
18937 | +{ | ||
18938 | + stat->buf = kcalloc(STAT_MAX_BUFS, sizeof(*stat->buf), GFP_KERNEL); | ||
18939 | + if (!stat->buf) | ||
18940 | + return -ENOMEM; | ||
18941 | + isp_stat_buf_clear(stat); | ||
18942 | + mutex_init(&stat->ioctl_lock); | ||
18943 | + atomic_set(&stat->buf_err, 0); | ||
18944 | + | ||
18945 | + return isp_stat_init_entities(stat, name, sd_ops); | ||
18946 | +} | ||
18947 | + | ||
18948 | +void omap3isp_stat_free(struct ispstat *stat) | ||
18949 | +{ | ||
18950 | + isp_stat_bufs_free(stat); | ||
18951 | + kfree(stat->buf); | ||
18952 | +} | ||
18953 | diff --git a/drivers/media/video/isp/ispstat.h b/drivers/media/video/isp/ispstat.h | ||
18954 | new file mode 100644 | ||
18955 | index 0000000..5298d33 | ||
18956 | --- /dev/null | ||
18957 | +++ b/drivers/media/video/isp/ispstat.h | ||
18958 | @@ -0,0 +1,169 @@ | ||
18959 | +/* | ||
18960 | + * ispstat.h | ||
18961 | + * | ||
18962 | + * TI OMAP3 ISP - Statistics core | ||
18963 | + * | ||
18964 | + * Copyright (C) 2010 Nokia Corporation | ||
18965 | + * Copyright (C) 2009 Texas Instruments, Inc | ||
18966 | + * | ||
18967 | + * Contacts: David Cohen <david.cohen@nokia.com> | ||
18968 | + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
18969 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
18970 | + * | ||
18971 | + * This program is free software; you can redistribute it and/or modify | ||
18972 | + * it under the terms of the GNU General Public License version 2 as | ||
18973 | + * published by the Free Software Foundation. | ||
18974 | + * | ||
18975 | + * This program is distributed in the hope that it will be useful, but | ||
18976 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18977 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18978 | + * General Public License for more details. | ||
18979 | + * | ||
18980 | + * You should have received a copy of the GNU General Public License | ||
18981 | + * along with this program; if not, write to the Free Software | ||
18982 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
18983 | + * 02110-1301 USA | ||
18984 | + */ | ||
18985 | + | ||
18986 | +#ifndef OMAP3_ISP_STAT_H | ||
18987 | +#define OMAP3_ISP_STAT_H | ||
18988 | + | ||
18989 | +#include <linux/types.h> | ||
18990 | +#include <linux/omap3isp.h> | ||
18991 | +#include <plat/dma.h> | ||
18992 | +#include <media/v4l2-event.h> | ||
18993 | + | ||
18994 | +#include "isp.h" | ||
18995 | +#include "ispvideo.h" | ||
18996 | + | ||
18997 | +#define STAT_MAX_BUFS 5 | ||
18998 | +#define STAT_NEVENTS 8 | ||
18999 | + | ||
19000 | +#define STAT_BUF_DONE 0 /* Buffer is ready */ | ||
19001 | +#define STAT_NO_BUF 1 /* An error has occurred */ | ||
19002 | +#define STAT_BUF_WAITING_DMA 2 /* Histogram only: DMA is running */ | ||
19003 | + | ||
19004 | +struct ispstat; | ||
19005 | + | ||
19006 | +struct ispstat_buffer { | ||
19007 | + unsigned long iommu_addr; | ||
19008 | + struct iovm_struct *iovm; | ||
19009 | + void *virt_addr; | ||
19010 | + dma_addr_t dma_addr; | ||
19011 | + struct timeval ts; | ||
19012 | + u32 buf_size; | ||
19013 | + u32 frame_number; | ||
19014 | + u16 config_counter; | ||
19015 | + u8 empty; | ||
19016 | +}; | ||
19017 | + | ||
19018 | +struct ispstat_ops { | ||
19019 | + /* | ||
19020 | + * Validate new params configuration. | ||
19021 | + * new_conf->buf_size value must be changed to the exact buffer size | ||
19022 | + * necessary for the new configuration if it's smaller. | ||
19023 | + */ | ||
19024 | + int (*validate_params)(struct ispstat *stat, void *new_conf); | ||
19025 | + | ||
19026 | + /* | ||
19027 | + * Save new params configuration. | ||
19028 | + * stat->priv->buf_size value must be set to the exact buffer size for | ||
19029 | + * the new configuration. | ||
19030 | + * stat->update is set to 1 if new configuration is different than | ||
19031 | + * current one. | ||
19032 | + */ | ||
19033 | + void (*set_params)(struct ispstat *stat, void *new_conf); | ||
19034 | + | ||
19035 | + /* Apply stored configuration. */ | ||
19036 | + void (*setup_regs)(struct ispstat *stat, void *priv); | ||
19037 | + | ||
19038 | + /* Enable/Disable module. */ | ||
19039 | + void (*enable)(struct ispstat *stat, int enable); | ||
19040 | + | ||
19041 | + /* Verify is module is busy. */ | ||
19042 | + int (*busy)(struct ispstat *stat); | ||
19043 | + | ||
19044 | + /* Used for specific operations during generic buf process task. */ | ||
19045 | + int (*buf_process)(struct ispstat *stat); | ||
19046 | +}; | ||
19047 | + | ||
19048 | +enum ispstat_state_t { | ||
19049 | + ISPSTAT_DISABLED = 0, | ||
19050 | + ISPSTAT_DISABLING, | ||
19051 | + ISPSTAT_ENABLED, | ||
19052 | + ISPSTAT_ENABLING, | ||
19053 | + ISPSTAT_SUSPENDED, | ||
19054 | +}; | ||
19055 | + | ||
19056 | +struct ispstat { | ||
19057 | + struct v4l2_subdev subdev; | ||
19058 | + struct media_pad pad; /* sink pad */ | ||
19059 | + | ||
19060 | + /* Control */ | ||
19061 | + unsigned configured:1; | ||
19062 | + unsigned update:1; | ||
19063 | + unsigned buf_processing:1; | ||
19064 | + unsigned sbl_ovl_recover:1; | ||
19065 | + u8 inc_config; | ||
19066 | + atomic_t buf_err; | ||
19067 | + enum ispstat_state_t state; /* enabling/disabling state */ | ||
19068 | + struct omap_dma_channel_params dma_config; | ||
19069 | + struct isp_device *isp; | ||
19070 | + void *priv; /* pointer to priv config struct */ | ||
19071 | + void *recover_priv; /* pointer to recover priv configuration */ | ||
19072 | + struct mutex ioctl_lock; /* serialize private ioctl */ | ||
19073 | + | ||
19074 | + const struct ispstat_ops *ops; | ||
19075 | + | ||
19076 | + /* Buffer */ | ||
19077 | + u8 wait_acc_frames; | ||
19078 | + u16 config_counter; | ||
19079 | + u32 frame_number; | ||
19080 | + u32 buf_size; | ||
19081 | + u32 buf_alloc_size; | ||
19082 | + int dma_ch; | ||
19083 | + unsigned long event_type; | ||
19084 | + struct ispstat_buffer *buf; | ||
19085 | + struct ispstat_buffer *active_buf; | ||
19086 | + struct ispstat_buffer *locked_buf; | ||
19087 | +}; | ||
19088 | + | ||
19089 | +struct ispstat_generic_config { | ||
19090 | + /* | ||
19091 | + * Fields must be in the same order as in: | ||
19092 | + * - isph3a_aewb_config | ||
19093 | + * - isph3a_af_config | ||
19094 | + * - isphist_config | ||
19095 | + */ | ||
19096 | + u32 buf_size; | ||
19097 | + u16 config_counter; | ||
19098 | +}; | ||
19099 | + | ||
19100 | +int omap3isp_stat_config(struct ispstat *stat, void *new_conf); | ||
19101 | +int omap3isp_stat_request_statistics(struct ispstat *stat, | ||
19102 | + struct omap3isp_stat_data *data); | ||
19103 | +int omap3isp_stat_init(struct ispstat *stat, const char *name, | ||
19104 | + const struct v4l2_subdev_ops *sd_ops); | ||
19105 | +void omap3isp_stat_free(struct ispstat *stat); | ||
19106 | +int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, | ||
19107 | + struct v4l2_fh *fh, | ||
19108 | + struct v4l2_event_subscription *sub); | ||
19109 | +int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, | ||
19110 | + struct v4l2_fh *fh, | ||
19111 | + struct v4l2_event_subscription *sub); | ||
19112 | +int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable); | ||
19113 | + | ||
19114 | +int omap3isp_stat_busy(struct ispstat *stat); | ||
19115 | +int omap3isp_stat_pcr_busy(struct ispstat *stat); | ||
19116 | +void omap3isp_stat_suspend(struct ispstat *stat); | ||
19117 | +void omap3isp_stat_resume(struct ispstat *stat); | ||
19118 | +int omap3isp_stat_enable(struct ispstat *stat, u8 enable); | ||
19119 | +void omap3isp_stat_sbl_overflow(struct ispstat *stat); | ||
19120 | +void omap3isp_stat_isr(struct ispstat *stat); | ||
19121 | +void omap3isp_stat_isr_frame_sync(struct ispstat *stat); | ||
19122 | +void omap3isp_stat_dma_isr(struct ispstat *stat); | ||
19123 | +int omap3isp_stat_register_entities(struct ispstat *stat, | ||
19124 | + struct v4l2_device *vdev); | ||
19125 | +void omap3isp_stat_unregister_entities(struct ispstat *stat); | ||
19126 | + | ||
19127 | +#endif /* OMAP3_ISP_STAT_H */ | ||
19128 | diff --git a/drivers/media/video/isp/ispvideo.c b/drivers/media/video/isp/ispvideo.c | ||
19129 | new file mode 100644 | ||
19130 | index 0000000..ef0adb0 | ||
19131 | --- /dev/null | ||
19132 | +++ b/drivers/media/video/isp/ispvideo.c | ||
19133 | @@ -0,0 +1,1264 @@ | ||
19134 | +/* | ||
19135 | + * ispvideo.c | ||
19136 | + * | ||
19137 | + * TI OMAP3 ISP - Generic video node | ||
19138 | + * | ||
19139 | + * Copyright (C) 2009-2010 Nokia Corporation | ||
19140 | + * | ||
19141 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
19142 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
19143 | + * | ||
19144 | + * This program is free software; you can redistribute it and/or modify | ||
19145 | + * it under the terms of the GNU General Public License version 2 as | ||
19146 | + * published by the Free Software Foundation. | ||
19147 | + * | ||
19148 | + * This program is distributed in the hope that it will be useful, but | ||
19149 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19150 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19151 | + * General Public License for more details. | ||
19152 | + * | ||
19153 | + * You should have received a copy of the GNU General Public License | ||
19154 | + * along with this program; if not, write to the Free Software | ||
19155 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
19156 | + * 02110-1301 USA | ||
19157 | + */ | ||
19158 | + | ||
19159 | +#include <asm/cacheflush.h> | ||
19160 | +#include <linux/clk.h> | ||
19161 | +#include <linux/mm.h> | ||
19162 | +#include <linux/pagemap.h> | ||
19163 | +#include <linux/scatterlist.h> | ||
19164 | +#include <linux/sched.h> | ||
19165 | +#include <linux/slab.h> | ||
19166 | +#include <linux/vmalloc.h> | ||
19167 | +#include <media/v4l2-dev.h> | ||
19168 | +#include <media/v4l2-ioctl.h> | ||
19169 | +#include <plat/iommu.h> | ||
19170 | +#include <plat/iovmm.h> | ||
19171 | +#include <plat/omap-pm.h> | ||
19172 | + | ||
19173 | +#include "ispvideo.h" | ||
19174 | +#include "isp.h" | ||
19175 | + | ||
19176 | + | ||
19177 | +/* ----------------------------------------------------------------------------- | ||
19178 | + * Helper functions | ||
19179 | + */ | ||
19180 | + | ||
19181 | +static struct isp_format_info formats[] = { | ||
19182 | + { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, | ||
19183 | + V4L2_MBUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 8, }, | ||
19184 | + { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, | ||
19185 | + V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10DPCM8, 8, }, | ||
19186 | + { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, | ||
19187 | + V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10, 10, }, | ||
19188 | + { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10, | ||
19189 | + V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10, 10, }, | ||
19190 | + { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10, | ||
19191 | + V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10, 10, }, | ||
19192 | + { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, | ||
19193 | + V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10, 10, }, | ||
19194 | + { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10, | ||
19195 | + V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12, 12, }, | ||
19196 | + { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10, | ||
19197 | + V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12, 12, }, | ||
19198 | + { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10, | ||
19199 | + V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12, 12, }, | ||
19200 | + { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10, | ||
19201 | + V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12, 12, }, | ||
19202 | + { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16, | ||
19203 | + V4L2_MBUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 16, }, | ||
19204 | + { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16, | ||
19205 | + V4L2_MBUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 16, }, | ||
19206 | +}; | ||
19207 | + | ||
19208 | +const struct isp_format_info * | ||
19209 | +omap3isp_video_format_info(enum v4l2_mbus_pixelcode code) | ||
19210 | +{ | ||
19211 | + unsigned int i; | ||
19212 | + | ||
19213 | + for (i = 0; i < ARRAY_SIZE(formats); ++i) { | ||
19214 | + if (formats[i].code == code) | ||
19215 | + return &formats[i]; | ||
19216 | + } | ||
19217 | + | ||
19218 | + return NULL; | ||
19219 | +} | ||
19220 | + | ||
19221 | +/* | ||
19222 | + * isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format | ||
19223 | + * @video: ISP video instance | ||
19224 | + * @mbus: v4l2_mbus_framefmt format (input) | ||
19225 | + * @pix: v4l2_pix_format format (output) | ||
19226 | + * | ||
19227 | + * Fill the output pix structure with information from the input mbus format. | ||
19228 | + * The bytesperline and sizeimage fields are computed from the requested bytes | ||
19229 | + * per line value in the pix format and information from the video instance. | ||
19230 | + * | ||
19231 | + * Return the number of padding bytes at end of line. | ||
19232 | + */ | ||
19233 | +static unsigned int isp_video_mbus_to_pix(const struct isp_video *video, | ||
19234 | + const struct v4l2_mbus_framefmt *mbus, | ||
19235 | + struct v4l2_pix_format *pix) | ||
19236 | +{ | ||
19237 | + unsigned int bpl = pix->bytesperline; | ||
19238 | + unsigned int min_bpl; | ||
19239 | + unsigned int i; | ||
19240 | + | ||
19241 | + memset(pix, 0, sizeof(*pix)); | ||
19242 | + pix->width = mbus->width; | ||
19243 | + pix->height = mbus->height; | ||
19244 | + | ||
19245 | + for (i = 0; i < ARRAY_SIZE(formats); ++i) { | ||
19246 | + if (formats[i].code == mbus->code) | ||
19247 | + break; | ||
19248 | + } | ||
19249 | + | ||
19250 | + if (WARN_ON(i == ARRAY_SIZE(formats))) | ||
19251 | + return 0; | ||
19252 | + | ||
19253 | + min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8; | ||
19254 | + | ||
19255 | + /* Clamp the requested bytes per line value. If the maximum bytes per | ||
19256 | + * line value is zero, the module doesn't support user configurable line | ||
19257 | + * sizes. Override the requested value with the minimum in that case. | ||
19258 | + */ | ||
19259 | + if (video->bpl_max) | ||
19260 | + bpl = clamp(bpl, min_bpl, video->bpl_max); | ||
19261 | + else | ||
19262 | + bpl = min_bpl; | ||
19263 | + | ||
19264 | + if (!video->bpl_zero_padding || bpl != min_bpl) | ||
19265 | + bpl = ALIGN(bpl, video->bpl_alignment); | ||
19266 | + | ||
19267 | + pix->pixelformat = formats[i].pixelformat; | ||
19268 | + pix->bytesperline = bpl; | ||
19269 | + pix->sizeimage = pix->bytesperline * pix->height; | ||
19270 | + pix->colorspace = mbus->colorspace; | ||
19271 | + pix->field = mbus->field; | ||
19272 | + | ||
19273 | + return bpl - min_bpl; | ||
19274 | +} | ||
19275 | + | ||
19276 | +static void isp_video_pix_to_mbus(const struct v4l2_pix_format *pix, | ||
19277 | + struct v4l2_mbus_framefmt *mbus) | ||
19278 | +{ | ||
19279 | + unsigned int i; | ||
19280 | + | ||
19281 | + memset(mbus, 0, sizeof(*mbus)); | ||
19282 | + mbus->width = pix->width; | ||
19283 | + mbus->height = pix->height; | ||
19284 | + | ||
19285 | + for (i = 0; i < ARRAY_SIZE(formats); ++i) { | ||
19286 | + if (formats[i].pixelformat == pix->pixelformat) | ||
19287 | + break; | ||
19288 | + } | ||
19289 | + | ||
19290 | + if (WARN_ON(i == ARRAY_SIZE(formats))) | ||
19291 | + return; | ||
19292 | + | ||
19293 | + mbus->code = formats[i].code; | ||
19294 | + mbus->colorspace = pix->colorspace; | ||
19295 | + mbus->field = pix->field; | ||
19296 | +} | ||
19297 | + | ||
19298 | +static struct v4l2_subdev * | ||
19299 | +isp_video_remote_subdev(struct isp_video *video, u32 *pad) | ||
19300 | +{ | ||
19301 | + struct media_pad *remote; | ||
19302 | + | ||
19303 | + remote = media_entity_remote_source(&video->pad); | ||
19304 | + | ||
19305 | + if (remote == NULL || | ||
19306 | + media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
19307 | + return NULL; | ||
19308 | + | ||
19309 | + if (pad) | ||
19310 | + *pad = remote->index; | ||
19311 | + | ||
19312 | + return media_entity_to_v4l2_subdev(remote->entity); | ||
19313 | +} | ||
19314 | + | ||
19315 | +/* Return a pointer to the ISP video instance at the far end of the pipeline. */ | ||
19316 | +static struct isp_video * | ||
19317 | +isp_video_far_end(struct isp_video *video) | ||
19318 | +{ | ||
19319 | + struct media_entity_graph graph; | ||
19320 | + struct media_entity *entity = &video->video.entity; | ||
19321 | + struct media_device *mdev = entity->parent; | ||
19322 | + struct isp_video *far_end = NULL; | ||
19323 | + | ||
19324 | + mutex_lock(&mdev->graph_mutex); | ||
19325 | + media_entity_graph_walk_start(&graph, entity); | ||
19326 | + | ||
19327 | + while ((entity = media_entity_graph_walk_next(&graph))) { | ||
19328 | + if (entity == &video->video.entity) | ||
19329 | + continue; | ||
19330 | + | ||
19331 | + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) | ||
19332 | + continue; | ||
19333 | + | ||
19334 | + far_end = to_isp_video(media_entity_to_video_device(entity)); | ||
19335 | + if (far_end->type != video->type) | ||
19336 | + break; | ||
19337 | + | ||
19338 | + far_end = NULL; | ||
19339 | + } | ||
19340 | + | ||
19341 | + mutex_unlock(&mdev->graph_mutex); | ||
19342 | + return far_end; | ||
19343 | +} | ||
19344 | + | ||
19345 | +/* | ||
19346 | + * Validate a pipeline by checking both ends of all links for format | ||
19347 | + * discrepancies. | ||
19348 | + * | ||
19349 | + * Compute the minimum time per frame value as the maximum of time per frame | ||
19350 | + * limits reported by every block in the pipeline. | ||
19351 | + * | ||
19352 | + * Return 0 if all formats match, or -EPIPE if at least one link is found with | ||
19353 | + * different formats on its two ends. | ||
19354 | + */ | ||
19355 | +static int isp_video_validate_pipeline(struct isp_pipeline *pipe) | ||
19356 | +{ | ||
19357 | + struct isp_device *isp = pipe->output->isp; | ||
19358 | + struct v4l2_subdev_format fmt_source; | ||
19359 | + struct v4l2_subdev_format fmt_sink; | ||
19360 | + struct media_pad *pad; | ||
19361 | + struct v4l2_subdev *subdev; | ||
19362 | + int ret; | ||
19363 | + | ||
19364 | + pipe->max_rate = pipe->l3_ick; | ||
19365 | + | ||
19366 | + subdev = isp_video_remote_subdev(pipe->output, NULL); | ||
19367 | + if (subdev == NULL) | ||
19368 | + return -EPIPE; | ||
19369 | + | ||
19370 | + while (1) { | ||
19371 | + /* Retrieve the sink format */ | ||
19372 | + pad = &subdev->entity.pads[0]; | ||
19373 | + if (!(pad->flags & MEDIA_PAD_FL_INPUT)) | ||
19374 | + break; | ||
19375 | + | ||
19376 | + fmt_sink.pad = pad->index; | ||
19377 | + fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
19378 | + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink); | ||
19379 | + if (ret < 0 && ret != -ENOIOCTLCMD) | ||
19380 | + return -EPIPE; | ||
19381 | + | ||
19382 | + /* Update the maximum frame rate */ | ||
19383 | + if (subdev == &isp->isp_res.subdev) | ||
19384 | + omap3isp_resizer_max_rate(&isp->isp_res, | ||
19385 | + &pipe->max_rate); | ||
19386 | + | ||
19387 | + /* Check ccdc maximum data rate when data comes from sensor | ||
19388 | + * TODO: Include ccdc rate in pipe->max_rate and compare the | ||
19389 | + * total pipe rate with the input data rate from sensor. | ||
19390 | + */ | ||
19391 | + if (subdev == &isp->isp_ccdc.subdev && pipe->input == NULL) { | ||
19392 | + unsigned int rate = UINT_MAX; | ||
19393 | + | ||
19394 | + omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); | ||
19395 | + if (isp->isp_ccdc.vpcfg.pixelclk > rate) | ||
19396 | + return -ENOSPC; | ||
19397 | + } | ||
19398 | + | ||
19399 | + /* Retrieve the source format */ | ||
19400 | + pad = media_entity_remote_source(pad); | ||
19401 | + if (pad == NULL || | ||
19402 | + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
19403 | + break; | ||
19404 | + | ||
19405 | + subdev = media_entity_to_v4l2_subdev(pad->entity); | ||
19406 | + | ||
19407 | + fmt_source.pad = pad->index; | ||
19408 | + fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
19409 | + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); | ||
19410 | + if (ret < 0 && ret != -ENOIOCTLCMD) | ||
19411 | + return -EPIPE; | ||
19412 | + | ||
19413 | + /* Check if the two ends match */ | ||
19414 | + if (fmt_source.format.code != fmt_sink.format.code || | ||
19415 | + fmt_source.format.width != fmt_sink.format.width || | ||
19416 | + fmt_source.format.height != fmt_sink.format.height) | ||
19417 | + return -EPIPE; | ||
19418 | + } | ||
19419 | + | ||
19420 | + return 0; | ||
19421 | +} | ||
19422 | + | ||
19423 | +static int | ||
19424 | +__isp_video_get_format(struct isp_video *video, struct v4l2_format *format) | ||
19425 | +{ | ||
19426 | + struct v4l2_subdev_format fmt; | ||
19427 | + struct v4l2_subdev *subdev; | ||
19428 | + u32 pad; | ||
19429 | + int ret; | ||
19430 | + | ||
19431 | + subdev = isp_video_remote_subdev(video, &pad); | ||
19432 | + if (subdev == NULL) | ||
19433 | + return -EINVAL; | ||
19434 | + | ||
19435 | + mutex_lock(&video->mutex); | ||
19436 | + | ||
19437 | + fmt.pad = pad; | ||
19438 | + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
19439 | + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); | ||
19440 | + if (ret == -ENOIOCTLCMD) | ||
19441 | + ret = -EINVAL; | ||
19442 | + | ||
19443 | + mutex_unlock(&video->mutex); | ||
19444 | + | ||
19445 | + if (ret) | ||
19446 | + return ret; | ||
19447 | + | ||
19448 | + format->type = video->type; | ||
19449 | + return isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); | ||
19450 | +} | ||
19451 | + | ||
19452 | +static int | ||
19453 | +isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh) | ||
19454 | +{ | ||
19455 | + struct v4l2_format format; | ||
19456 | + int ret; | ||
19457 | + | ||
19458 | + memcpy(&format, &vfh->format, sizeof(format)); | ||
19459 | + ret = __isp_video_get_format(video, &format); | ||
19460 | + if (ret < 0) | ||
19461 | + return ret; | ||
19462 | + | ||
19463 | + if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat || | ||
19464 | + vfh->format.fmt.pix.height != format.fmt.pix.height || | ||
19465 | + vfh->format.fmt.pix.width != format.fmt.pix.width || | ||
19466 | + vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline || | ||
19467 | + vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage) | ||
19468 | + return -EINVAL; | ||
19469 | + | ||
19470 | + return ret; | ||
19471 | +} | ||
19472 | + | ||
19473 | +/* ----------------------------------------------------------------------------- | ||
19474 | + * IOMMU management | ||
19475 | + */ | ||
19476 | + | ||
19477 | +#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8) | ||
19478 | + | ||
19479 | +/* | ||
19480 | + * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list | ||
19481 | + * @dev: Device pointer specific to the OMAP3 ISP. | ||
19482 | + * @sglist: Pointer to source Scatter gather list to allocate. | ||
19483 | + * @sglen: Number of elements of the scatter-gatter list. | ||
19484 | + * | ||
19485 | + * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if | ||
19486 | + * we ran out of memory. | ||
19487 | + */ | ||
19488 | +static dma_addr_t | ||
19489 | +ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen) | ||
19490 | +{ | ||
19491 | + struct sg_table *sgt; | ||
19492 | + u32 da; | ||
19493 | + | ||
19494 | + sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); | ||
19495 | + if (sgt == NULL) | ||
19496 | + return -ENOMEM; | ||
19497 | + | ||
19498 | + sgt->sgl = (struct scatterlist *)sglist; | ||
19499 | + sgt->nents = sglen; | ||
19500 | + sgt->orig_nents = sglen; | ||
19501 | + | ||
19502 | + da = iommu_vmap(isp->iommu, 0, sgt, IOMMU_FLAG); | ||
19503 | + if (IS_ERR_VALUE(da)) | ||
19504 | + kfree(sgt); | ||
19505 | + | ||
19506 | + return da; | ||
19507 | +} | ||
19508 | + | ||
19509 | +/* | ||
19510 | + * ispmmu_vunmap - Unmap a device address from the ISP MMU | ||
19511 | + * @dev: Device pointer specific to the OMAP3 ISP. | ||
19512 | + * @da: Device address generated from a ispmmu_vmap call. | ||
19513 | + */ | ||
19514 | +static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da) | ||
19515 | +{ | ||
19516 | + struct sg_table *sgt; | ||
19517 | + | ||
19518 | + sgt = iommu_vunmap(isp->iommu, (u32)da); | ||
19519 | + kfree(sgt); | ||
19520 | +} | ||
19521 | + | ||
19522 | +/* ----------------------------------------------------------------------------- | ||
19523 | + * Video queue operations | ||
19524 | + */ | ||
19525 | + | ||
19526 | +static void isp_video_queue_prepare(struct isp_video_queue *queue, | ||
19527 | + unsigned int *nbuffers, unsigned int *size) | ||
19528 | +{ | ||
19529 | + struct isp_video_fh *vfh = | ||
19530 | + container_of(queue, struct isp_video_fh, queue); | ||
19531 | + struct isp_video *video = vfh->video; | ||
19532 | + | ||
19533 | + *size = vfh->format.fmt.pix.sizeimage; | ||
19534 | + if (*size == 0) | ||
19535 | + return; | ||
19536 | + | ||
19537 | + *nbuffers = min(*nbuffers, video->capture_mem / PAGE_ALIGN(*size)); | ||
19538 | +} | ||
19539 | + | ||
19540 | +static void isp_video_buffer_cleanup(struct isp_video_buffer *buf) | ||
19541 | +{ | ||
19542 | + struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); | ||
19543 | + struct isp_buffer *buffer = to_isp_buffer(buf); | ||
19544 | + struct isp_video *video = vfh->video; | ||
19545 | + | ||
19546 | + if (buffer->isp_addr) { | ||
19547 | + ispmmu_vunmap(video->isp, buffer->isp_addr); | ||
19548 | + buffer->isp_addr = 0; | ||
19549 | + } | ||
19550 | +} | ||
19551 | + | ||
19552 | +static int isp_video_buffer_prepare(struct isp_video_buffer *buf) | ||
19553 | +{ | ||
19554 | + struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); | ||
19555 | + struct isp_buffer *buffer = to_isp_buffer(buf); | ||
19556 | + struct isp_video *video = vfh->video; | ||
19557 | + unsigned long addr; | ||
19558 | + | ||
19559 | + addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen); | ||
19560 | + if (IS_ERR_VALUE(addr)) | ||
19561 | + return -EIO; | ||
19562 | + | ||
19563 | + if (!IS_ALIGNED(addr, 32)) { | ||
19564 | + dev_dbg(video->isp->dev, "Buffer address must be " | ||
19565 | + "aligned to 32 bytes boundary.\n"); | ||
19566 | + ispmmu_vunmap(video->isp, buffer->isp_addr); | ||
19567 | + return -EINVAL; | ||
19568 | + } | ||
19569 | + | ||
19570 | + buf->vbuf.bytesused = vfh->format.fmt.pix.sizeimage; | ||
19571 | + buffer->isp_addr = addr; | ||
19572 | + return 0; | ||
19573 | +} | ||
19574 | + | ||
19575 | +/* | ||
19576 | + * isp_video_buffer_queue - Add buffer to streaming queue | ||
19577 | + * @buf: Video buffer | ||
19578 | + * | ||
19579 | + * In memory-to-memory mode, start streaming on the pipeline if buffers are | ||
19580 | + * queued on both the input and the output, if the pipeline isn't already busy. | ||
19581 | + * If the pipeline is busy, it will be restarted in the output module interrupt | ||
19582 | + * handler. | ||
19583 | + */ | ||
19584 | +static void isp_video_buffer_queue(struct isp_video_buffer *buf) | ||
19585 | +{ | ||
19586 | + struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); | ||
19587 | + struct isp_buffer *buffer = to_isp_buffer(buf); | ||
19588 | + struct isp_video *video = vfh->video; | ||
19589 | + struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); | ||
19590 | + enum isp_pipeline_state state; | ||
19591 | + unsigned long flags; | ||
19592 | + unsigned int empty; | ||
19593 | + unsigned int start; | ||
19594 | + | ||
19595 | + empty = list_empty(&video->dmaqueue); | ||
19596 | + list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue); | ||
19597 | + | ||
19598 | + if (empty) { | ||
19599 | + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
19600 | + state = ISP_PIPELINE_QUEUE_OUTPUT; | ||
19601 | + else | ||
19602 | + state = ISP_PIPELINE_QUEUE_INPUT; | ||
19603 | + | ||
19604 | + spin_lock_irqsave(&pipe->lock, flags); | ||
19605 | + pipe->state |= state; | ||
19606 | + video->ops->queue(video, buffer); | ||
19607 | + video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; | ||
19608 | + | ||
19609 | + start = isp_pipeline_ready(pipe); | ||
19610 | + if (start) | ||
19611 | + pipe->state |= ISP_PIPELINE_STREAM; | ||
19612 | + spin_unlock_irqrestore(&pipe->lock, flags); | ||
19613 | + | ||
19614 | + if (start) | ||
19615 | + omap3isp_pipeline_set_stream(pipe, | ||
19616 | + ISP_PIPELINE_STREAM_SINGLESHOT); | ||
19617 | + } | ||
19618 | +} | ||
19619 | + | ||
19620 | +static const struct isp_video_queue_operations isp_video_queue_ops = { | ||
19621 | + .queue_prepare = &isp_video_queue_prepare, | ||
19622 | + .buffer_prepare = &isp_video_buffer_prepare, | ||
19623 | + .buffer_queue = &isp_video_buffer_queue, | ||
19624 | + .buffer_cleanup = &isp_video_buffer_cleanup, | ||
19625 | +}; | ||
19626 | + | ||
19627 | +/* | ||
19628 | + * omap3isp_video_buffer_next - Complete the current buffer and return the next | ||
19629 | + * @video: ISP video object | ||
19630 | + * @error: Whether an error occured during capture | ||
19631 | + * | ||
19632 | + * Remove the current video buffer from the DMA queue and fill its timestamp, | ||
19633 | + * field count and state fields before waking up its completion handler. | ||
19634 | + * | ||
19635 | + * The buffer state is set to VIDEOBUF_DONE if no error occured (@error is 0) | ||
19636 | + * or VIDEOBUF_ERROR otherwise (@error is non-zero). | ||
19637 | + * | ||
19638 | + * The DMA queue is expected to contain at least one buffer. | ||
19639 | + * | ||
19640 | + * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is | ||
19641 | + * empty. | ||
19642 | + */ | ||
19643 | +struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video, | ||
19644 | + unsigned int error) | ||
19645 | +{ | ||
19646 | + struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); | ||
19647 | + struct isp_video_queue *queue = video->queue; | ||
19648 | + enum isp_pipeline_state state; | ||
19649 | + struct isp_video_buffer *buf; | ||
19650 | + unsigned long flags; | ||
19651 | + struct timespec ts; | ||
19652 | + | ||
19653 | + spin_lock_irqsave(&queue->irqlock, flags); | ||
19654 | + if (WARN_ON(list_empty(&video->dmaqueue))) { | ||
19655 | + spin_unlock_irqrestore(&queue->irqlock, flags); | ||
19656 | + return NULL; | ||
19657 | + } | ||
19658 | + | ||
19659 | + buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer, | ||
19660 | + irqlist); | ||
19661 | + list_del(&buf->irqlist); | ||
19662 | + spin_unlock_irqrestore(&queue->irqlock, flags); | ||
19663 | + | ||
19664 | + ktime_get_ts(&ts); | ||
19665 | + buf->vbuf.timestamp.tv_sec = ts.tv_sec; | ||
19666 | + buf->vbuf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; | ||
19667 | + | ||
19668 | + /* Do frame number propagation only if this is the output video node. | ||
19669 | + * Frame number either comes from the CSI receivers or it gets | ||
19670 | + * incremented here if H3A is not active. | ||
19671 | + * Note: There is no guarantee that the output buffer will finish | ||
19672 | + * first, so the input number might lag behind by 1 in some cases. | ||
19673 | + */ | ||
19674 | + if (video == pipe->output && !pipe->do_propagation) | ||
19675 | + buf->vbuf.sequence = atomic_inc_return(&pipe->frame_number); | ||
19676 | + else | ||
19677 | + buf->vbuf.sequence = atomic_read(&pipe->frame_number); | ||
19678 | + | ||
19679 | + buf->state = error ? ISP_BUF_STATE_ERROR : ISP_BUF_STATE_DONE; | ||
19680 | + | ||
19681 | + wake_up(&buf->wait); | ||
19682 | + | ||
19683 | + if (list_empty(&video->dmaqueue)) { | ||
19684 | + if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
19685 | + state = ISP_PIPELINE_QUEUE_OUTPUT | ||
19686 | + | ISP_PIPELINE_STREAM; | ||
19687 | + else | ||
19688 | + state = ISP_PIPELINE_QUEUE_INPUT | ||
19689 | + | ISP_PIPELINE_STREAM; | ||
19690 | + | ||
19691 | + spin_lock_irqsave(&pipe->lock, flags); | ||
19692 | + pipe->state &= ~state; | ||
19693 | + if (video->pipe.stream_state == ISP_PIPELINE_STREAM_CONTINUOUS) | ||
19694 | + video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; | ||
19695 | + spin_unlock_irqrestore(&pipe->lock, flags); | ||
19696 | + return NULL; | ||
19697 | + } | ||
19698 | + | ||
19699 | + if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { | ||
19700 | + spin_lock_irqsave(&pipe->lock, flags); | ||
19701 | + pipe->state &= ~ISP_PIPELINE_STREAM; | ||
19702 | + spin_unlock_irqrestore(&pipe->lock, flags); | ||
19703 | + } | ||
19704 | + | ||
19705 | + buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer, | ||
19706 | + irqlist); | ||
19707 | + buf->state = ISP_BUF_STATE_ACTIVE; | ||
19708 | + return to_isp_buffer(buf); | ||
19709 | +} | ||
19710 | + | ||
19711 | +/* | ||
19712 | + * omap3isp_video_resume - Perform resume operation on the buffers | ||
19713 | + * @video: ISP video object | ||
19714 | + * @continuous: Pipeline is in single shot mode if 0 or continous mode otherwise | ||
19715 | + * | ||
19716 | + * This function is intended to be used on suspend/resume scenario. It | ||
19717 | + * requests video queue layer to discard buffers marked as DONE if it's in | ||
19718 | + * continuous mode and requests ISP modules to queue again the ACTIVE buffer | ||
19719 | + * if there's any. | ||
19720 | + */ | ||
19721 | +void omap3isp_video_resume(struct isp_video *video, int continuous) | ||
19722 | +{ | ||
19723 | + struct isp_buffer *buf = NULL; | ||
19724 | + | ||
19725 | + if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
19726 | + omap3isp_video_queue_discard_done(video->queue); | ||
19727 | + | ||
19728 | + if (!list_empty(&video->dmaqueue)) { | ||
19729 | + buf = list_first_entry(&video->dmaqueue, | ||
19730 | + struct isp_buffer, buffer.irqlist); | ||
19731 | + video->ops->queue(video, buf); | ||
19732 | + video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; | ||
19733 | + } else { | ||
19734 | + if (continuous) | ||
19735 | + video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; | ||
19736 | + } | ||
19737 | +} | ||
19738 | + | ||
19739 | +/* ----------------------------------------------------------------------------- | ||
19740 | + * V4L2 ioctls | ||
19741 | + */ | ||
19742 | + | ||
19743 | +static int | ||
19744 | +isp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) | ||
19745 | +{ | ||
19746 | + struct isp_video *video = video_drvdata(file); | ||
19747 | + | ||
19748 | + strlcpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver)); | ||
19749 | + strlcpy(cap->card, video->video.name, sizeof(cap->card)); | ||
19750 | + strlcpy(cap->bus_info, "media", sizeof(cap->bus_info)); | ||
19751 | + cap->version = ISP_VIDEO_DRIVER_VERSION; | ||
19752 | + | ||
19753 | + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
19754 | + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; | ||
19755 | + else | ||
19756 | + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; | ||
19757 | + | ||
19758 | + return 0; | ||
19759 | +} | ||
19760 | + | ||
19761 | +static int | ||
19762 | +isp_video_get_format(struct file *file, void *fh, struct v4l2_format *format) | ||
19763 | +{ | ||
19764 | + struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
19765 | + struct isp_video *video = video_drvdata(file); | ||
19766 | + | ||
19767 | + if (format->type != video->type) | ||
19768 | + return -EINVAL; | ||
19769 | + | ||
19770 | + mutex_lock(&video->mutex); | ||
19771 | + *format = vfh->format; | ||
19772 | + mutex_unlock(&video->mutex); | ||
19773 | + | ||
19774 | + return 0; | ||
19775 | +} | ||
19776 | + | ||
19777 | +static int | ||
19778 | +isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) | ||
19779 | +{ | ||
19780 | + struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
19781 | + struct isp_video *video = video_drvdata(file); | ||
19782 | + struct v4l2_mbus_framefmt fmt; | ||
19783 | + | ||
19784 | + if (format->type != video->type) | ||
19785 | + return -EINVAL; | ||
19786 | + | ||
19787 | + mutex_lock(&video->mutex); | ||
19788 | + | ||
19789 | + /* Fill the bytesperline and sizeimage fields by converting to media bus | ||
19790 | + * format and back to pixel format. | ||
19791 | + */ | ||
19792 | + isp_video_pix_to_mbus(&format->fmt.pix, &fmt); | ||
19793 | + isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix); | ||
19794 | + | ||
19795 | + vfh->format = *format; | ||
19796 | + | ||
19797 | + mutex_unlock(&video->mutex); | ||
19798 | + return 0; | ||
19799 | +} | ||
19800 | + | ||
19801 | +static int | ||
19802 | +isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) | ||
19803 | +{ | ||
19804 | + struct isp_video *video = video_drvdata(file); | ||
19805 | + struct v4l2_subdev_format fmt; | ||
19806 | + struct v4l2_subdev *subdev; | ||
19807 | + u32 pad; | ||
19808 | + int ret; | ||
19809 | + | ||
19810 | + if (format->type != video->type) | ||
19811 | + return -EINVAL; | ||
19812 | + | ||
19813 | + subdev = isp_video_remote_subdev(video, &pad); | ||
19814 | + if (subdev == NULL) | ||
19815 | + return -EINVAL; | ||
19816 | + | ||
19817 | + isp_video_pix_to_mbus(&format->fmt.pix, &fmt.format); | ||
19818 | + | ||
19819 | + fmt.pad = pad; | ||
19820 | + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
19821 | + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); | ||
19822 | + if (ret) | ||
19823 | + return ret == -ENOIOCTLCMD ? -EINVAL : ret; | ||
19824 | + | ||
19825 | + isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); | ||
19826 | + return 0; | ||
19827 | +} | ||
19828 | + | ||
19829 | +static int | ||
19830 | +isp_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) | ||
19831 | +{ | ||
19832 | + struct isp_video *video = video_drvdata(file); | ||
19833 | + struct v4l2_subdev *subdev; | ||
19834 | + int ret; | ||
19835 | + | ||
19836 | + subdev = isp_video_remote_subdev(video, NULL); | ||
19837 | + if (subdev == NULL) | ||
19838 | + return -EINVAL; | ||
19839 | + | ||
19840 | + mutex_lock(&video->mutex); | ||
19841 | + ret = v4l2_subdev_call(subdev, video, cropcap, cropcap); | ||
19842 | + mutex_unlock(&video->mutex); | ||
19843 | + | ||
19844 | + return ret == -ENOIOCTLCMD ? -EINVAL : ret; | ||
19845 | +} | ||
19846 | + | ||
19847 | +static int | ||
19848 | +isp_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop) | ||
19849 | +{ | ||
19850 | + struct isp_video *video = video_drvdata(file); | ||
19851 | + struct v4l2_subdev_format format; | ||
19852 | + struct v4l2_subdev *subdev; | ||
19853 | + u32 pad; | ||
19854 | + int ret; | ||
19855 | + | ||
19856 | + subdev = isp_video_remote_subdev(video, &pad); | ||
19857 | + if (subdev == NULL) | ||
19858 | + return -EINVAL; | ||
19859 | + | ||
19860 | + /* Try the get crop operation first and fallback to get format if not | ||
19861 | + * implemented. | ||
19862 | + */ | ||
19863 | + ret = v4l2_subdev_call(subdev, video, g_crop, crop); | ||
19864 | + if (ret != -ENOIOCTLCMD) | ||
19865 | + return ret; | ||
19866 | + | ||
19867 | + format.pad = pad; | ||
19868 | + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
19869 | + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); | ||
19870 | + if (ret < 0) | ||
19871 | + return ret == -ENOIOCTLCMD ? -EINVAL : ret; | ||
19872 | + | ||
19873 | + crop->c.left = 0; | ||
19874 | + crop->c.top = 0; | ||
19875 | + crop->c.width = format.format.width; | ||
19876 | + crop->c.height = format.format.height; | ||
19877 | + | ||
19878 | + return 0; | ||
19879 | +} | ||
19880 | + | ||
19881 | +static int | ||
19882 | +isp_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop) | ||
19883 | +{ | ||
19884 | + struct isp_video *video = video_drvdata(file); | ||
19885 | + struct v4l2_subdev *subdev; | ||
19886 | + int ret; | ||
19887 | + | ||
19888 | + subdev = isp_video_remote_subdev(video, NULL); | ||
19889 | + if (subdev == NULL) | ||
19890 | + return -EINVAL; | ||
19891 | + | ||
19892 | + mutex_lock(&video->mutex); | ||
19893 | + ret = v4l2_subdev_call(subdev, video, s_crop, crop); | ||
19894 | + mutex_unlock(&video->mutex); | ||
19895 | + | ||
19896 | + return ret == -ENOIOCTLCMD ? -EINVAL : ret; | ||
19897 | +} | ||
19898 | + | ||
19899 | +static int | ||
19900 | +isp_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a) | ||
19901 | +{ | ||
19902 | + struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
19903 | + struct isp_video *video = video_drvdata(file); | ||
19904 | + | ||
19905 | + if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || | ||
19906 | + video->type != a->type) | ||
19907 | + return -EINVAL; | ||
19908 | + | ||
19909 | + memset(a, 0, sizeof(*a)); | ||
19910 | + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
19911 | + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; | ||
19912 | + a->parm.output.timeperframe = vfh->timeperframe; | ||
19913 | + | ||
19914 | + return 0; | ||
19915 | +} | ||
19916 | + | ||
19917 | +static int | ||
19918 | +isp_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a) | ||
19919 | +{ | ||
19920 | + struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
19921 | + struct isp_video *video = video_drvdata(file); | ||
19922 | + | ||
19923 | + if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || | ||
19924 | + video->type != a->type) | ||
19925 | + return -EINVAL; | ||
19926 | + | ||
19927 | + if (a->parm.output.timeperframe.denominator == 0) | ||
19928 | + a->parm.output.timeperframe.denominator = 1; | ||
19929 | + | ||
19930 | + vfh->timeperframe = a->parm.output.timeperframe; | ||
19931 | + | ||
19932 | + return 0; | ||
19933 | +} | ||
19934 | + | ||
19935 | +static int | ||
19936 | +isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) | ||
19937 | +{ | ||
19938 | + struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
19939 | + | ||
19940 | + return omap3isp_video_queue_reqbufs(&vfh->queue, rb); | ||
19941 | +} | ||
19942 | + | ||
19943 | +static int | ||
19944 | +isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) | ||
19945 | +{ | ||
19946 | + struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
19947 | + | ||
19948 | + return omap3isp_video_queue_querybuf(&vfh->queue, b); | ||
19949 | +} | ||
19950 | + | ||
19951 | +static int | ||
19952 | +isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) | ||
19953 | +{ | ||
19954 | + struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
19955 | + | ||
19956 | + return omap3isp_video_queue_qbuf(&vfh->queue, b); | ||
19957 | +} | ||
19958 | + | ||
19959 | +static int | ||
19960 | +isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) | ||
19961 | +{ | ||
19962 | + struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
19963 | + | ||
19964 | + return omap3isp_video_queue_dqbuf(&vfh->queue, b, | ||
19965 | + file->f_flags & O_NONBLOCK); | ||
19966 | +} | ||
19967 | + | ||
19968 | +/* | ||
19969 | + * Stream management | ||
19970 | + * | ||
19971 | + * Every ISP pipeline has a single input and a single output. The input can be | ||
19972 | + * either a sensor or a video node. The output is always a video node. | ||
19973 | + * | ||
19974 | + * As every pipeline has an output video node, the ISP video objects at the | ||
19975 | + * pipeline output stores the pipeline state. It tracks the streaming state of | ||
19976 | + * both the input and output, as well as the availability of buffers. | ||
19977 | + * | ||
19978 | + * In sensor-to-memory mode, frames are always available at the pipeline input. | ||
19979 | + * Starting the sensor usually requires I2C transfers and must be done in | ||
19980 | + * interruptible context. The pipeline is started and stopped synchronously | ||
19981 | + * to the stream on/off commands. All modules in the pipeline will get their | ||
19982 | + * subdev set stream handler called. The module at the end of the pipeline must | ||
19983 | + * delay starting the hardware until buffers are available at its output. | ||
19984 | + * | ||
19985 | + * In memory-to-memory mode, starting/stopping the stream requires | ||
19986 | + * synchronization between the input and output. ISP modules can't be stopped | ||
19987 | + * in the middle of a frame, and at least some of the modules seem to become | ||
19988 | + * busy as soon as they're started, even if they don't receive a frame start | ||
19989 | + * event. For that reason frames need to be processed in single-shot mode. The | ||
19990 | + * driver needs to wait until a frame is completely processed and written to | ||
19991 | + * memory before restarting the pipeline for the next frame. Pipelined | ||
19992 | + * processing might be possible but requires more testing. | ||
19993 | + * | ||
19994 | + * Stream start must be delayed until buffers are available at both the input | ||
19995 | + * and output. The pipeline must be started in the videobuf queue callback with | ||
19996 | + * the buffers queue spinlock held. The modules subdev set stream operation must | ||
19997 | + * not sleep. | ||
19998 | + */ | ||
19999 | +static int | ||
20000 | +isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) | ||
20001 | +{ | ||
20002 | + struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
20003 | + struct isp_video *video = video_drvdata(file); | ||
20004 | + enum isp_pipeline_state state; | ||
20005 | + struct isp_pipeline *pipe; | ||
20006 | + struct isp_video *far_end; | ||
20007 | + unsigned long flags; | ||
20008 | + int ret; | ||
20009 | + | ||
20010 | + if (type != video->type) | ||
20011 | + return -EINVAL; | ||
20012 | + | ||
20013 | + mutex_lock(&video->stream_lock); | ||
20014 | + | ||
20015 | + if (video->streaming) { | ||
20016 | + mutex_unlock(&video->stream_lock); | ||
20017 | + return -EBUSY; | ||
20018 | + } | ||
20019 | + | ||
20020 | + /* Start streaming on the pipeline. No link touching an entity in the | ||
20021 | + * pipeline can be activated or deactivated once streaming is started. | ||
20022 | + */ | ||
20023 | + pipe = video->video.entity.pipe | ||
20024 | + ? to_isp_pipeline(&video->video.entity) : &video->pipe; | ||
20025 | + media_entity_pipeline_start(&video->video.entity, &pipe->pipe); | ||
20026 | + | ||
20027 | + /* Verify that the currently configured format matches the output of | ||
20028 | + * the connected subdev. | ||
20029 | + */ | ||
20030 | + ret = isp_video_check_format(video, vfh); | ||
20031 | + if (ret < 0) | ||
20032 | + goto error; | ||
20033 | + | ||
20034 | + video->bpl_padding = ret; | ||
20035 | + video->bpl_value = vfh->format.fmt.pix.bytesperline; | ||
20036 | + | ||
20037 | + /* Find the ISP video node connected at the far end of the pipeline and | ||
20038 | + * update the pipeline. | ||
20039 | + */ | ||
20040 | + far_end = isp_video_far_end(video); | ||
20041 | + | ||
20042 | + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { | ||
20043 | + state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT; | ||
20044 | + pipe->input = far_end; | ||
20045 | + pipe->output = video; | ||
20046 | + } else { | ||
20047 | + if (far_end == NULL) { | ||
20048 | + ret = -EPIPE; | ||
20049 | + goto error; | ||
20050 | + } | ||
20051 | + | ||
20052 | + state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT; | ||
20053 | + pipe->input = video; | ||
20054 | + pipe->output = far_end; | ||
20055 | + } | ||
20056 | + | ||
20057 | + /* Make sure the interconnect clock runs fast enough. | ||
20058 | + * | ||
20059 | + * Formula from: resource34xx.c set_opp() | ||
20060 | + * If MPU freq is above 500MHz, make sure the interconnect | ||
20061 | + * is at 100Mhz or above. | ||
20062 | + * throughput in KiB/s for 100 Mhz = 100 * 1000 * 4. | ||
20063 | + * | ||
20064 | + * We want to be fast enough then set OCP clock to be max as | ||
20065 | + * possible, in that case 185Mhz then: | ||
20066 | + * throughput in KiB/s for 185Mhz = 185 * 1000 * 4 = 740000 KiB/s | ||
20067 | + */ | ||
20068 | + omap_pm_set_min_bus_tput(video->isp->dev, OCP_INITIATOR_AGENT, 740000); | ||
20069 | + pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); | ||
20070 | + | ||
20071 | + /* Validate the pipeline and update its state. */ | ||
20072 | + ret = isp_video_validate_pipeline(pipe); | ||
20073 | + if (ret < 0) | ||
20074 | + goto error; | ||
20075 | + | ||
20076 | + spin_lock_irqsave(&pipe->lock, flags); | ||
20077 | + pipe->state &= ~ISP_PIPELINE_STREAM; | ||
20078 | + pipe->state |= state; | ||
20079 | + spin_unlock_irqrestore(&pipe->lock, flags); | ||
20080 | + | ||
20081 | + /* Set the maximum time per frame as the value requested by userspace. | ||
20082 | + * This is a soft limit that can be overridden if the hardware doesn't | ||
20083 | + * support the request limit. | ||
20084 | + */ | ||
20085 | + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
20086 | + pipe->max_timeperframe = vfh->timeperframe; | ||
20087 | + | ||
20088 | + video->queue = &vfh->queue; | ||
20089 | + INIT_LIST_HEAD(&video->dmaqueue); | ||
20090 | + atomic_set(&pipe->frame_number, -1); | ||
20091 | + | ||
20092 | + ret = omap3isp_video_queue_streamon(&vfh->queue); | ||
20093 | + if (ret < 0) | ||
20094 | + goto error; | ||
20095 | + | ||
20096 | + /* In sensor-to-memory mode, the stream can be started synchronously | ||
20097 | + * to the stream on command. In memory-to-memory mode, it will be | ||
20098 | + * started when buffers are queued on both the input and output. | ||
20099 | + */ | ||
20100 | + if (pipe->input == NULL) { | ||
20101 | + ret = omap3isp_pipeline_set_stream(pipe, | ||
20102 | + ISP_PIPELINE_STREAM_CONTINUOUS); | ||
20103 | + if (ret < 0) | ||
20104 | + goto error; | ||
20105 | + spin_lock_irqsave(&video->queue->irqlock, flags); | ||
20106 | + if (list_empty(&video->dmaqueue)) | ||
20107 | + video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; | ||
20108 | + spin_unlock_irqrestore(&video->queue->irqlock, flags); | ||
20109 | + } | ||
20110 | + | ||
20111 | +error: | ||
20112 | + if (ret < 0) { | ||
20113 | + omap3isp_video_queue_streamoff(&vfh->queue); | ||
20114 | + omap_pm_set_min_bus_tput(video->isp->dev, | ||
20115 | + OCP_INITIATOR_AGENT, 0); | ||
20116 | + media_entity_pipeline_stop(&video->video.entity); | ||
20117 | + video->queue = NULL; | ||
20118 | + } | ||
20119 | + | ||
20120 | + if (!ret) | ||
20121 | + video->streaming = 1; | ||
20122 | + | ||
20123 | + mutex_unlock(&video->stream_lock); | ||
20124 | + return ret; | ||
20125 | +} | ||
20126 | + | ||
20127 | +static int | ||
20128 | +isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) | ||
20129 | +{ | ||
20130 | + struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
20131 | + struct isp_video *video = video_drvdata(file); | ||
20132 | + struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); | ||
20133 | + enum isp_pipeline_state state; | ||
20134 | + unsigned int streaming; | ||
20135 | + unsigned long flags; | ||
20136 | + | ||
20137 | + if (type != video->type) | ||
20138 | + return -EINVAL; | ||
20139 | + | ||
20140 | + mutex_lock(&video->stream_lock); | ||
20141 | + | ||
20142 | + /* Make sure we're not streaming yet. */ | ||
20143 | + mutex_lock(&vfh->queue.lock); | ||
20144 | + streaming = vfh->queue.streaming; | ||
20145 | + mutex_unlock(&vfh->queue.lock); | ||
20146 | + | ||
20147 | + if (!streaming) | ||
20148 | + goto done; | ||
20149 | + | ||
20150 | + /* Update the pipeline state. */ | ||
20151 | + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
20152 | + state = ISP_PIPELINE_STREAM_OUTPUT | ||
20153 | + | ISP_PIPELINE_QUEUE_OUTPUT; | ||
20154 | + else | ||
20155 | + state = ISP_PIPELINE_STREAM_INPUT | ||
20156 | + | ISP_PIPELINE_QUEUE_INPUT; | ||
20157 | + | ||
20158 | + spin_lock_irqsave(&pipe->lock, flags); | ||
20159 | + pipe->state &= ~state; | ||
20160 | + spin_unlock_irqrestore(&pipe->lock, flags); | ||
20161 | + | ||
20162 | + /* Stop the stream. */ | ||
20163 | + omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED); | ||
20164 | + omap3isp_video_queue_streamoff(&vfh->queue); | ||
20165 | + video->queue = NULL; | ||
20166 | + video->streaming = 0; | ||
20167 | + | ||
20168 | + omap_pm_set_min_bus_tput(video->isp->dev, OCP_INITIATOR_AGENT, 0); | ||
20169 | + media_entity_pipeline_stop(&video->video.entity); | ||
20170 | + | ||
20171 | +done: | ||
20172 | + mutex_unlock(&video->stream_lock); | ||
20173 | + return 0; | ||
20174 | +} | ||
20175 | + | ||
20176 | +static int | ||
20177 | +isp_video_enum_input(struct file *file, void *fh, struct v4l2_input *input) | ||
20178 | +{ | ||
20179 | + if (input->index > 0) | ||
20180 | + return -EINVAL; | ||
20181 | + | ||
20182 | + strlcpy(input->name, "camera", sizeof(input->name)); | ||
20183 | + input->type = V4L2_INPUT_TYPE_CAMERA; | ||
20184 | + | ||
20185 | + return 0; | ||
20186 | +} | ||
20187 | + | ||
20188 | +static int | ||
20189 | +isp_video_g_input(struct file *file, void *fh, unsigned int *input) | ||
20190 | +{ | ||
20191 | + *input = 0; | ||
20192 | + | ||
20193 | + return 0; | ||
20194 | +} | ||
20195 | + | ||
20196 | +static int | ||
20197 | +isp_video_s_input(struct file *file, void *fh, unsigned int input) | ||
20198 | +{ | ||
20199 | + return input == 0 ? 0 : -EINVAL; | ||
20200 | +} | ||
20201 | + | ||
20202 | +static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { | ||
20203 | + .vidioc_querycap = isp_video_querycap, | ||
20204 | + .vidioc_g_fmt_vid_cap = isp_video_get_format, | ||
20205 | + .vidioc_s_fmt_vid_cap = isp_video_set_format, | ||
20206 | + .vidioc_try_fmt_vid_cap = isp_video_try_format, | ||
20207 | + .vidioc_g_fmt_vid_out = isp_video_get_format, | ||
20208 | + .vidioc_s_fmt_vid_out = isp_video_set_format, | ||
20209 | + .vidioc_try_fmt_vid_out = isp_video_try_format, | ||
20210 | + .vidioc_cropcap = isp_video_cropcap, | ||
20211 | + .vidioc_g_crop = isp_video_get_crop, | ||
20212 | + .vidioc_s_crop = isp_video_set_crop, | ||
20213 | + .vidioc_g_parm = isp_video_get_param, | ||
20214 | + .vidioc_s_parm = isp_video_set_param, | ||
20215 | + .vidioc_reqbufs = isp_video_reqbufs, | ||
20216 | + .vidioc_querybuf = isp_video_querybuf, | ||
20217 | + .vidioc_qbuf = isp_video_qbuf, | ||
20218 | + .vidioc_dqbuf = isp_video_dqbuf, | ||
20219 | + .vidioc_streamon = isp_video_streamon, | ||
20220 | + .vidioc_streamoff = isp_video_streamoff, | ||
20221 | + .vidioc_enum_input = isp_video_enum_input, | ||
20222 | + .vidioc_g_input = isp_video_g_input, | ||
20223 | + .vidioc_s_input = isp_video_s_input, | ||
20224 | +}; | ||
20225 | + | ||
20226 | +/* ----------------------------------------------------------------------------- | ||
20227 | + * V4L2 file operations | ||
20228 | + */ | ||
20229 | + | ||
20230 | +static int isp_video_open(struct file *file) | ||
20231 | +{ | ||
20232 | + struct isp_video *video = video_drvdata(file); | ||
20233 | + struct isp_video_fh *handle; | ||
20234 | + int ret = 0; | ||
20235 | + | ||
20236 | + handle = kzalloc(sizeof(*handle), GFP_KERNEL); | ||
20237 | + if (handle == NULL) | ||
20238 | + return -ENOMEM; | ||
20239 | + | ||
20240 | + v4l2_fh_init(&handle->vfh, &video->video); | ||
20241 | + v4l2_fh_add(&handle->vfh); | ||
20242 | + | ||
20243 | + /* If this is the first user, initialise the pipeline. */ | ||
20244 | + if (omap3isp_get(video->isp) == NULL) { | ||
20245 | + ret = -EBUSY; | ||
20246 | + goto done; | ||
20247 | + } | ||
20248 | + | ||
20249 | + ret = omap3isp_pipeline_pm_use(&video->video.entity, 1); | ||
20250 | + if (ret < 0) { | ||
20251 | + omap3isp_put(video->isp); | ||
20252 | + goto done; | ||
20253 | + } | ||
20254 | + | ||
20255 | + omap3isp_video_queue_init(&handle->queue, video->type, | ||
20256 | + &isp_video_queue_ops, video->isp->dev, | ||
20257 | + sizeof(struct isp_buffer)); | ||
20258 | + | ||
20259 | + memset(&handle->format, 0, sizeof(handle->format)); | ||
20260 | + handle->format.type = video->type; | ||
20261 | + handle->timeperframe.denominator = 1; | ||
20262 | + | ||
20263 | + handle->video = video; | ||
20264 | + file->private_data = &handle->vfh; | ||
20265 | + | ||
20266 | +done: | ||
20267 | + if (ret < 0) { | ||
20268 | + v4l2_fh_del(&handle->vfh); | ||
20269 | + kfree(handle); | ||
20270 | + } | ||
20271 | + | ||
20272 | + return ret; | ||
20273 | +} | ||
20274 | + | ||
20275 | +static int isp_video_release(struct file *file) | ||
20276 | +{ | ||
20277 | + struct isp_video *video = video_drvdata(file); | ||
20278 | + struct v4l2_fh *vfh = file->private_data; | ||
20279 | + struct isp_video_fh *handle = to_isp_video_fh(vfh); | ||
20280 | + | ||
20281 | + /* Disable streaming and free the buffers queue resources. */ | ||
20282 | + isp_video_streamoff(file, vfh, video->type); | ||
20283 | + | ||
20284 | + mutex_lock(&handle->queue.lock); | ||
20285 | + omap3isp_video_queue_cleanup(&handle->queue); | ||
20286 | + mutex_unlock(&handle->queue.lock); | ||
20287 | + | ||
20288 | + omap3isp_pipeline_pm_use(&video->video.entity, 0); | ||
20289 | + | ||
20290 | + /* Release the file handle. */ | ||
20291 | + v4l2_fh_del(vfh); | ||
20292 | + kfree(handle); | ||
20293 | + file->private_data = NULL; | ||
20294 | + | ||
20295 | + omap3isp_put(video->isp); | ||
20296 | + | ||
20297 | + return 0; | ||
20298 | +} | ||
20299 | + | ||
20300 | +static unsigned int isp_video_poll(struct file *file, poll_table *wait) | ||
20301 | +{ | ||
20302 | + struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); | ||
20303 | + struct isp_video_queue *queue = &vfh->queue; | ||
20304 | + | ||
20305 | + return omap3isp_video_queue_poll(queue, file, wait); | ||
20306 | +} | ||
20307 | + | ||
20308 | +static int isp_video_mmap(struct file *file, struct vm_area_struct *vma) | ||
20309 | +{ | ||
20310 | + struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); | ||
20311 | + | ||
20312 | + return omap3isp_video_queue_mmap(&vfh->queue, vma); | ||
20313 | +} | ||
20314 | + | ||
20315 | +static struct v4l2_file_operations isp_video_fops = { | ||
20316 | + .owner = THIS_MODULE, | ||
20317 | + .unlocked_ioctl = video_ioctl2, | ||
20318 | + .open = isp_video_open, | ||
20319 | + .release = isp_video_release, | ||
20320 | + .poll = isp_video_poll, | ||
20321 | + .mmap = isp_video_mmap, | ||
20322 | +}; | ||
20323 | + | ||
20324 | +/* ----------------------------------------------------------------------------- | ||
20325 | + * ISP video core | ||
20326 | + */ | ||
20327 | + | ||
20328 | +static const struct isp_video_operations isp_video_dummy_ops = { | ||
20329 | +}; | ||
20330 | + | ||
20331 | +int omap3isp_video_init(struct isp_video *video, const char *name) | ||
20332 | +{ | ||
20333 | + const char *direction; | ||
20334 | + int ret; | ||
20335 | + | ||
20336 | + switch (video->type) { | ||
20337 | + case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
20338 | + direction = "output"; | ||
20339 | + video->pad.flags = MEDIA_PAD_FL_INPUT; | ||
20340 | + break; | ||
20341 | + case V4L2_BUF_TYPE_VIDEO_OUTPUT: | ||
20342 | + direction = "input"; | ||
20343 | + video->pad.flags = MEDIA_PAD_FL_OUTPUT; | ||
20344 | + break; | ||
20345 | + | ||
20346 | + default: | ||
20347 | + return -EINVAL; | ||
20348 | + } | ||
20349 | + | ||
20350 | + ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); | ||
20351 | + if (ret < 0) | ||
20352 | + return ret; | ||
20353 | + | ||
20354 | + mutex_init(&video->mutex); | ||
20355 | + atomic_set(&video->active, 0); | ||
20356 | + | ||
20357 | + spin_lock_init(&video->pipe.lock); | ||
20358 | + mutex_init(&video->stream_lock); | ||
20359 | + | ||
20360 | + /* Initialize the video device. */ | ||
20361 | + if (video->ops == NULL) | ||
20362 | + video->ops = &isp_video_dummy_ops; | ||
20363 | + | ||
20364 | + video->video.fops = &isp_video_fops; | ||
20365 | + snprintf(video->video.name, sizeof(video->video.name), | ||
20366 | + "OMAP3 ISP %s %s", name, direction); | ||
20367 | + video->video.vfl_type = VFL_TYPE_GRABBER; | ||
20368 | + video->video.release = video_device_release_empty; | ||
20369 | + video->video.ioctl_ops = &isp_video_ioctl_ops; | ||
20370 | + video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED; | ||
20371 | + | ||
20372 | + video_set_drvdata(&video->video, video); | ||
20373 | + | ||
20374 | + return 0; | ||
20375 | +} | ||
20376 | + | ||
20377 | +int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) | ||
20378 | +{ | ||
20379 | + int ret; | ||
20380 | + | ||
20381 | + video->video.v4l2_dev = vdev; | ||
20382 | + | ||
20383 | + ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); | ||
20384 | + if (ret < 0) | ||
20385 | + printk(KERN_ERR "%s: could not register video device (%d)\n", | ||
20386 | + __func__, ret); | ||
20387 | + | ||
20388 | + return ret; | ||
20389 | +} | ||
20390 | + | ||
20391 | +void omap3isp_video_unregister(struct isp_video *video) | ||
20392 | +{ | ||
20393 | + if (video_is_registered(&video->video)) { | ||
20394 | + media_entity_cleanup(&video->video.entity); | ||
20395 | + video_unregister_device(&video->video); | ||
20396 | + } | ||
20397 | +} | ||
20398 | diff --git a/drivers/media/video/isp/ispvideo.h b/drivers/media/video/isp/ispvideo.h | ||
20399 | new file mode 100644 | ||
20400 | index 0000000..41c8fb9 | ||
20401 | --- /dev/null | ||
20402 | +++ b/drivers/media/video/isp/ispvideo.h | ||
20403 | @@ -0,0 +1,202 @@ | ||
20404 | +/* | ||
20405 | + * ispvideo.h | ||
20406 | + * | ||
20407 | + * TI OMAP3 ISP - Generic video node | ||
20408 | + * | ||
20409 | + * Copyright (C) 2009-2010 Nokia Corporation | ||
20410 | + * | ||
20411 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
20412 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
20413 | + * | ||
20414 | + * This program is free software; you can redistribute it and/or modify | ||
20415 | + * it under the terms of the GNU General Public License version 2 as | ||
20416 | + * published by the Free Software Foundation. | ||
20417 | + * | ||
20418 | + * This program is distributed in the hope that it will be useful, but | ||
20419 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20420 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20421 | + * General Public License for more details. | ||
20422 | + * | ||
20423 | + * You should have received a copy of the GNU General Public License | ||
20424 | + * along with this program; if not, write to the Free Software | ||
20425 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20426 | + * 02110-1301 USA | ||
20427 | + */ | ||
20428 | + | ||
20429 | +#ifndef OMAP3_ISP_VIDEO_H | ||
20430 | +#define OMAP3_ISP_VIDEO_H | ||
20431 | + | ||
20432 | +#include <linux/v4l2-mediabus.h> | ||
20433 | +#include <linux/version.h> | ||
20434 | +#include <media/media-entity.h> | ||
20435 | +#include <media/v4l2-dev.h> | ||
20436 | +#include <media/v4l2-fh.h> | ||
20437 | + | ||
20438 | +#include "ispqueue.h" | ||
20439 | + | ||
20440 | +#define ISP_VIDEO_DRIVER_NAME "ispvideo" | ||
20441 | +#define ISP_VIDEO_DRIVER_VERSION KERNEL_VERSION(0, 0, 1) | ||
20442 | + | ||
20443 | +struct isp_device; | ||
20444 | +struct isp_video; | ||
20445 | +struct v4l2_mbus_framefmt; | ||
20446 | +struct v4l2_pix_format; | ||
20447 | + | ||
20448 | +/* | ||
20449 | + * struct isp_format_info - ISP media bus format information | ||
20450 | + * @code: V4L2 media bus format code | ||
20451 | + * @truncated: V4L2 media bus format code for the same format truncated to 10 | ||
20452 | + * bits. Identical to @code if the format is 10 bits wide or less. | ||
20453 | + * @uncompressed: V4L2 media bus format code for the corresponding uncompressed | ||
20454 | + * format. Identical to @code if the format is not DPCM compressed. | ||
20455 | + * @pixelformat: V4L2 pixel format FCC identifier | ||
20456 | + * @bpp: Bits per pixel | ||
20457 | + */ | ||
20458 | +struct isp_format_info { | ||
20459 | + enum v4l2_mbus_pixelcode code; | ||
20460 | + enum v4l2_mbus_pixelcode truncated; | ||
20461 | + enum v4l2_mbus_pixelcode uncompressed; | ||
20462 | + u32 pixelformat; | ||
20463 | + unsigned int bpp; | ||
20464 | +}; | ||
20465 | + | ||
20466 | +enum isp_pipeline_stream_state { | ||
20467 | + ISP_PIPELINE_STREAM_STOPPED = 0, | ||
20468 | + ISP_PIPELINE_STREAM_CONTINUOUS = 1, | ||
20469 | + ISP_PIPELINE_STREAM_SINGLESHOT = 2, | ||
20470 | +}; | ||
20471 | + | ||
20472 | +enum isp_pipeline_state { | ||
20473 | + /* The stream has been started on the input video node. */ | ||
20474 | + ISP_PIPELINE_STREAM_INPUT = 1, | ||
20475 | + /* The stream has been started on the output video node. */ | ||
20476 | + ISP_PIPELINE_STREAM_OUTPUT = 2, | ||
20477 | + /* At least one buffer is queued on the input video node. */ | ||
20478 | + ISP_PIPELINE_QUEUE_INPUT = 4, | ||
20479 | + /* At least one buffer is queued on the output video node. */ | ||
20480 | + ISP_PIPELINE_QUEUE_OUTPUT = 8, | ||
20481 | + /* The input entity is idle, ready to be started. */ | ||
20482 | + ISP_PIPELINE_IDLE_INPUT = 16, | ||
20483 | + /* The output entity is idle, ready to be started. */ | ||
20484 | + ISP_PIPELINE_IDLE_OUTPUT = 32, | ||
20485 | + /* The pipeline is currently streaming. */ | ||
20486 | + ISP_PIPELINE_STREAM = 64, | ||
20487 | +}; | ||
20488 | + | ||
20489 | +struct isp_pipeline { | ||
20490 | + struct media_pipeline pipe; | ||
20491 | + spinlock_t lock; | ||
20492 | + unsigned int state; | ||
20493 | + enum isp_pipeline_stream_state stream_state; | ||
20494 | + struct isp_video *input; | ||
20495 | + struct isp_video *output; | ||
20496 | + unsigned long l3_ick; | ||
20497 | + unsigned int max_rate; | ||
20498 | + atomic_t frame_number; | ||
20499 | + bool do_propagation; /* of frame number */ | ||
20500 | + struct v4l2_fract max_timeperframe; | ||
20501 | +}; | ||
20502 | + | ||
20503 | +#define to_isp_pipeline(__e) \ | ||
20504 | + container_of((__e)->pipe, struct isp_pipeline, pipe) | ||
20505 | + | ||
20506 | +static inline int isp_pipeline_ready(struct isp_pipeline *pipe) | ||
20507 | +{ | ||
20508 | + return pipe->state == (ISP_PIPELINE_STREAM_INPUT | | ||
20509 | + ISP_PIPELINE_STREAM_OUTPUT | | ||
20510 | + ISP_PIPELINE_QUEUE_INPUT | | ||
20511 | + ISP_PIPELINE_QUEUE_OUTPUT | | ||
20512 | + ISP_PIPELINE_IDLE_INPUT | | ||
20513 | + ISP_PIPELINE_IDLE_OUTPUT); | ||
20514 | +} | ||
20515 | + | ||
20516 | +/* | ||
20517 | + * struct isp_buffer - ISP buffer | ||
20518 | + * @buffer: ISP video buffer | ||
20519 | + * @isp_addr: MMU mapped address (a.k.a. device address) of the buffer. | ||
20520 | + */ | ||
20521 | +struct isp_buffer { | ||
20522 | + struct isp_video_buffer buffer; | ||
20523 | + dma_addr_t isp_addr; | ||
20524 | +}; | ||
20525 | + | ||
20526 | +#define to_isp_buffer(buf) container_of(buf, struct isp_buffer, buffer) | ||
20527 | + | ||
20528 | +enum isp_video_dmaqueue_flags { | ||
20529 | + /* Set if DMA queue becomes empty when ISP_PIPELINE_STREAM_CONTINUOUS */ | ||
20530 | + ISP_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0), | ||
20531 | + /* Set when queuing buffer to an empty DMA queue */ | ||
20532 | + ISP_VIDEO_DMAQUEUE_QUEUED = (1 << 1), | ||
20533 | +}; | ||
20534 | + | ||
20535 | +#define isp_video_dmaqueue_flags_clr(video) \ | ||
20536 | + ({ (video)->dmaqueue_flags = 0; }) | ||
20537 | + | ||
20538 | +/* | ||
20539 | + * struct isp_video_operations - ISP video operations | ||
20540 | + * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF | ||
20541 | + * if there was no buffer previously queued. | ||
20542 | + */ | ||
20543 | +struct isp_video_operations { | ||
20544 | + int(*queue)(struct isp_video *video, struct isp_buffer *buffer); | ||
20545 | +}; | ||
20546 | + | ||
20547 | +struct isp_video { | ||
20548 | + struct video_device video; | ||
20549 | + enum v4l2_buf_type type; | ||
20550 | + struct media_pad pad; | ||
20551 | + | ||
20552 | + struct mutex mutex; | ||
20553 | + atomic_t active; | ||
20554 | + | ||
20555 | + struct isp_device *isp; | ||
20556 | + | ||
20557 | + unsigned int capture_mem; | ||
20558 | + unsigned int bpl_alignment; /* alignment value */ | ||
20559 | + unsigned int bpl_zero_padding; /* whether the alignment is optional */ | ||
20560 | + unsigned int bpl_max; /* maximum bytes per line value */ | ||
20561 | + unsigned int bpl_value; /* bytes per line value */ | ||
20562 | + unsigned int bpl_padding; /* padding at end of line */ | ||
20563 | + | ||
20564 | + /* Entity video node streaming */ | ||
20565 | + unsigned int streaming:1; | ||
20566 | + | ||
20567 | + /* Pipeline state */ | ||
20568 | + struct isp_pipeline pipe; | ||
20569 | + struct mutex stream_lock; | ||
20570 | + | ||
20571 | + /* Video buffers queue */ | ||
20572 | + struct isp_video_queue *queue; | ||
20573 | + struct list_head dmaqueue; | ||
20574 | + enum isp_video_dmaqueue_flags dmaqueue_flags; | ||
20575 | + | ||
20576 | + const struct isp_video_operations *ops; | ||
20577 | +}; | ||
20578 | + | ||
20579 | +#define to_isp_video(vdev) container_of(vdev, struct isp_video, video) | ||
20580 | + | ||
20581 | +struct isp_video_fh { | ||
20582 | + struct v4l2_fh vfh; | ||
20583 | + struct isp_video *video; | ||
20584 | + struct isp_video_queue queue; | ||
20585 | + struct v4l2_format format; | ||
20586 | + struct v4l2_fract timeperframe; | ||
20587 | +}; | ||
20588 | + | ||
20589 | +#define to_isp_video_fh(fh) container_of(fh, struct isp_video_fh, vfh) | ||
20590 | +#define isp_video_queue_to_isp_video_fh(q) \ | ||
20591 | + container_of(q, struct isp_video_fh, queue) | ||
20592 | + | ||
20593 | +int omap3isp_video_init(struct isp_video *video, const char *name); | ||
20594 | +int omap3isp_video_register(struct isp_video *video, | ||
20595 | + struct v4l2_device *vdev); | ||
20596 | +void omap3isp_video_unregister(struct isp_video *video); | ||
20597 | +struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video, | ||
20598 | + unsigned int error); | ||
20599 | +void omap3isp_video_resume(struct isp_video *video, int continuous); | ||
20600 | +struct media_pad *omap3isp_video_remote_pad(struct isp_video *video); | ||
20601 | + | ||
20602 | +const struct isp_format_info * | ||
20603 | +omap3isp_video_format_info(enum v4l2_mbus_pixelcode code); | ||
20604 | + | ||
20605 | +#endif /* OMAP3_ISP_VIDEO_H */ | ||
20606 | diff --git a/drivers/media/video/isp/luma_enhance_table.h b/drivers/media/video/isp/luma_enhance_table.h | ||
20607 | new file mode 100644 | ||
20608 | index 0000000..56d93c2 | ||
20609 | --- /dev/null | ||
20610 | +++ b/drivers/media/video/isp/luma_enhance_table.h | ||
20611 | @@ -0,0 +1,154 @@ | ||
20612 | +/* | ||
20613 | + * luma_enhance_table.h | ||
20614 | + * | ||
20615 | + * TI OMAP3 ISP - Luminance enhancement table | ||
20616 | + * | ||
20617 | + * Copyright (C) 2010 Nokia Corporation | ||
20618 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
20619 | + * | ||
20620 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
20621 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
20622 | + * | ||
20623 | + * This program is free software; you can redistribute it and/or modify | ||
20624 | + * it under the terms of the GNU General Public License version 2 as | ||
20625 | + * published by the Free Software Foundation. | ||
20626 | + * | ||
20627 | + * This program is distributed in the hope that it will be useful, but | ||
20628 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20629 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20630 | + * General Public License for more details. | ||
20631 | + * | ||
20632 | + * You should have received a copy of the GNU General Public License | ||
20633 | + * along with this program; if not, write to the Free Software | ||
20634 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20635 | + * 02110-1301 USA | ||
20636 | + */ | ||
20637 | + | ||
20638 | +1047552, | ||
20639 | +1047552, | ||
20640 | +1047552, | ||
20641 | +1047552, | ||
20642 | +1047552, | ||
20643 | +1047552, | ||
20644 | +1047552, | ||
20645 | +1047552, | ||
20646 | +1047552, | ||
20647 | +1047552, | ||
20648 | +1047552, | ||
20649 | +1047552, | ||
20650 | +1047552, | ||
20651 | +1047552, | ||
20652 | +1047552, | ||
20653 | +1047552, | ||
20654 | +1047552, | ||
20655 | +1047552, | ||
20656 | +1047552, | ||
20657 | +1047552, | ||
20658 | +1047552, | ||
20659 | +1047552, | ||
20660 | +1047552, | ||
20661 | +1047552, | ||
20662 | +1047552, | ||
20663 | +1047552, | ||
20664 | +1047552, | ||
20665 | +1047552, | ||
20666 | +1048575, | ||
20667 | +1047551, | ||
20668 | +1046527, | ||
20669 | +1045503, | ||
20670 | +1044479, | ||
20671 | +1043455, | ||
20672 | +1042431, | ||
20673 | +1041407, | ||
20674 | +1040383, | ||
20675 | +1039359, | ||
20676 | +1038335, | ||
20677 | +1037311, | ||
20678 | +1036287, | ||
20679 | +1035263, | ||
20680 | +1034239, | ||
20681 | +1033215, | ||
20682 | +1032191, | ||
20683 | +1031167, | ||
20684 | +1030143, | ||
20685 | +1028096, | ||
20686 | +1028096, | ||
20687 | +1028096, | ||
20688 | +1028096, | ||
20689 | +1028096, | ||
20690 | +1028096, | ||
20691 | +1028096, | ||
20692 | +1028096, | ||
20693 | +1028096, | ||
20694 | +1028096, | ||
20695 | +1028100, | ||
20696 | +1032196, | ||
20697 | +1036292, | ||
20698 | +1040388, | ||
20699 | +1044484, | ||
20700 | +0, | ||
20701 | +0, | ||
20702 | +0, | ||
20703 | +5, | ||
20704 | +5125, | ||
20705 | +10245, | ||
20706 | +15365, | ||
20707 | +20485, | ||
20708 | +25605, | ||
20709 | +30720, | ||
20710 | +30720, | ||
20711 | +30720, | ||
20712 | +30720, | ||
20713 | +30720, | ||
20714 | +30720, | ||
20715 | +30720, | ||
20716 | +30720, | ||
20717 | +30720, | ||
20718 | +30720, | ||
20719 | +30720, | ||
20720 | +31743, | ||
20721 | +30719, | ||
20722 | +29695, | ||
20723 | +28671, | ||
20724 | +27647, | ||
20725 | +26623, | ||
20726 | +25599, | ||
20727 | +24575, | ||
20728 | +23551, | ||
20729 | +22527, | ||
20730 | +21503, | ||
20731 | +20479, | ||
20732 | +19455, | ||
20733 | +18431, | ||
20734 | +17407, | ||
20735 | +16383, | ||
20736 | +15359, | ||
20737 | +14335, | ||
20738 | +13311, | ||
20739 | +12287, | ||
20740 | +11263, | ||
20741 | +10239, | ||
20742 | +9215, | ||
20743 | +8191, | ||
20744 | +7167, | ||
20745 | +6143, | ||
20746 | +5119, | ||
20747 | +4095, | ||
20748 | +3071, | ||
20749 | +1024, | ||
20750 | +1024, | ||
20751 | +1024, | ||
20752 | +1024, | ||
20753 | +1024, | ||
20754 | +1024, | ||
20755 | +1024, | ||
20756 | +1024, | ||
20757 | +1024, | ||
20758 | +1024, | ||
20759 | +1024, | ||
20760 | +1024, | ||
20761 | +1024, | ||
20762 | +1024, | ||
20763 | +1024, | ||
20764 | +1024, | ||
20765 | +1024 | ||
20766 | diff --git a/drivers/media/video/isp/noise_filter_table.h b/drivers/media/video/isp/noise_filter_table.h | ||
20767 | new file mode 100644 | ||
20768 | index 0000000..4b4c085 | ||
20769 | --- /dev/null | ||
20770 | +++ b/drivers/media/video/isp/noise_filter_table.h | ||
20771 | @@ -0,0 +1,90 @@ | ||
20772 | +/* | ||
20773 | + * noise_filter_table.h | ||
20774 | + * | ||
20775 | + * TI OMAP3 ISP - Noise filter table | ||
20776 | + * | ||
20777 | + * Copyright (C) 2010 Nokia Corporation | ||
20778 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
20779 | + * | ||
20780 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
20781 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
20782 | + * | ||
20783 | + * This program is free software; you can redistribute it and/or modify | ||
20784 | + * it under the terms of the GNU General Public License version 2 as | ||
20785 | + * published by the Free Software Foundation. | ||
20786 | + * | ||
20787 | + * This program is distributed in the hope that it will be useful, but | ||
20788 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20789 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20790 | + * General Public License for more details. | ||
20791 | + * | ||
20792 | + * You should have received a copy of the GNU General Public License | ||
20793 | + * along with this program; if not, write to the Free Software | ||
20794 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20795 | + * 02110-1301 USA | ||
20796 | + */ | ||
20797 | + | ||
20798 | +16, | ||
20799 | +16, | ||
20800 | +16, | ||
20801 | +16, | ||
20802 | +16, | ||
20803 | +16, | ||
20804 | +16, | ||
20805 | +16, | ||
20806 | +16, | ||
20807 | +16, | ||
20808 | +16, | ||
20809 | +16, | ||
20810 | +16, | ||
20811 | +16, | ||
20812 | +16, | ||
20813 | +16, | ||
20814 | +16, | ||
20815 | +16, | ||
20816 | +16, | ||
20817 | +16, | ||
20818 | +16, | ||
20819 | +16, | ||
20820 | +16, | ||
20821 | +16, | ||
20822 | +16, | ||
20823 | +16, | ||
20824 | +16, | ||
20825 | +16, | ||
20826 | +16, | ||
20827 | +16, | ||
20828 | +16, | ||
20829 | +16, | ||
20830 | +31, | ||
20831 | +31, | ||
20832 | +31, | ||
20833 | +31, | ||
20834 | +31, | ||
20835 | +31, | ||
20836 | +31, | ||
20837 | +31, | ||
20838 | +31, | ||
20839 | +31, | ||
20840 | +31, | ||
20841 | +31, | ||
20842 | +31, | ||
20843 | +31, | ||
20844 | +31, | ||
20845 | +31, | ||
20846 | +31, | ||
20847 | +31, | ||
20848 | +31, | ||
20849 | +31, | ||
20850 | +31, | ||
20851 | +31, | ||
20852 | +31, | ||
20853 | +31, | ||
20854 | +31, | ||
20855 | +31, | ||
20856 | +31, | ||
20857 | +31, | ||
20858 | +31, | ||
20859 | +31, | ||
20860 | +31, | ||
20861 | +31 | ||
20862 | diff --git a/include/linux/Kbuild b/include/linux/Kbuild | ||
20863 | index c0db7f4..f65f612 100644 | ||
20864 | --- a/include/linux/Kbuild | ||
20865 | +++ b/include/linux/Kbuild | ||
20866 | @@ -272,6 +272,7 @@ header-y += nfsacl.h | ||
20867 | header-y += nl80211.h | ||
20868 | header-y += nubus.h | ||
20869 | header-y += nvram.h | ||
20870 | +header-y += omap3isp.h | ||
20871 | header-y += omapfb.h | ||
20872 | header-y += oom.h | ||
20873 | header-y += param.h | ||
20874 | diff --git a/include/linux/omap3isp.h b/include/linux/omap3isp.h | ||
20875 | new file mode 100644 | ||
20876 | index 0000000..ab249b2 | ||
20877 | --- /dev/null | ||
20878 | +++ b/include/linux/omap3isp.h | ||
20879 | @@ -0,0 +1,631 @@ | ||
20880 | +/* | ||
20881 | + * omap3isp.h | ||
20882 | + * | ||
20883 | + * TI OMAP3 ISP - User-space API | ||
20884 | + * | ||
20885 | + * Copyright (C) 2010 Nokia Corporation | ||
20886 | + * Copyright (C) 2009 Texas Instruments, Inc. | ||
20887 | + * | ||
20888 | + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
20889 | + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
20890 | + * | ||
20891 | + * This program is free software; you can redistribute it and/or modify | ||
20892 | + * it under the terms of the GNU General Public License version 2 as | ||
20893 | + * published by the Free Software Foundation. | ||
20894 | + * | ||
20895 | + * This program is distributed in the hope that it will be useful, but | ||
20896 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20897 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20898 | + * General Public License for more details. | ||
20899 | + * | ||
20900 | + * You should have received a copy of the GNU General Public License | ||
20901 | + * along with this program; if not, write to the Free Software | ||
20902 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20903 | + * 02110-1301 USA | ||
20904 | + */ | ||
20905 | + | ||
20906 | +#ifndef OMAP3_ISP_USER_H | ||
20907 | +#define OMAP3_ISP_USER_H | ||
20908 | + | ||
20909 | +#include <linux/types.h> | ||
20910 | + | ||
20911 | +/* Private IOCTLs */ | ||
20912 | + | ||
20913 | +#define VIDIOC_OMAP3ISP_CCDC_CFG \ | ||
20914 | + _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct omap3isp_ccdc_update_config) | ||
20915 | +#define VIDIOC_OMAP3ISP_PRV_CFG \ | ||
20916 | + _IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct omap3isp_prev_update_config) | ||
20917 | +#define VIDIOC_OMAP3ISP_AEWB_CFG \ | ||
20918 | + _IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct omap3isp_h3a_aewb_config) | ||
20919 | +#define VIDIOC_OMAP3ISP_HIST_CFG \ | ||
20920 | + _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct omap3isp_hist_config) | ||
20921 | +#define VIDIOC_OMAP3ISP_AF_CFG \ | ||
20922 | + _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct omap3isp_h3a_af_config) | ||
20923 | +#define VIDIOC_OMAP3ISP_STAT_REQ \ | ||
20924 | + _IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct omap3isp_stat_data) | ||
20925 | +#define VIDIOC_OMAP3ISP_STAT_EN \ | ||
20926 | + _IOWR('V', BASE_VIDIOC_PRIVATE + 7, unsigned long) | ||
20927 | + | ||
20928 | +/* Events */ | ||
20929 | + | ||
20930 | +#define V4L2_EVENT_OMAP3ISP_CLASS (V4L2_EVENT_PRIVATE_START | 0x100) | ||
20931 | +#define V4L2_EVENT_OMAP3ISP_AEWB (V4L2_EVENT_OMAP3ISP_CLASS | 0x1) | ||
20932 | +#define V4L2_EVENT_OMAP3ISP_AF (V4L2_EVENT_OMAP3ISP_CLASS | 0x2) | ||
20933 | +#define V4L2_EVENT_OMAP3ISP_HIST (V4L2_EVENT_OMAP3ISP_CLASS | 0x3) | ||
20934 | +#define V4L2_EVENT_OMAP3ISP_HS_VS (V4L2_EVENT_OMAP3ISP_CLASS | 0x4) | ||
20935 | + | ||
20936 | +struct omap3isp_stat_event_status { | ||
20937 | + __u32 frame_number; | ||
20938 | + __u16 config_counter; | ||
20939 | + __u8 buf_err; | ||
20940 | +}; | ||
20941 | + | ||
20942 | +/* AE/AWB related structures and flags*/ | ||
20943 | + | ||
20944 | +/* H3A Range Constants */ | ||
20945 | +#define OMAP3ISP_AEWB_MAX_SATURATION_LIM 1023 | ||
20946 | +#define OMAP3ISP_AEWB_MIN_WIN_H 2 | ||
20947 | +#define OMAP3ISP_AEWB_MAX_WIN_H 256 | ||
20948 | +#define OMAP3ISP_AEWB_MIN_WIN_W 6 | ||
20949 | +#define OMAP3ISP_AEWB_MAX_WIN_W 256 | ||
20950 | +#define OMAP3ISP_AEWB_MIN_WINVC 1 | ||
20951 | +#define OMAP3ISP_AEWB_MIN_WINHC 1 | ||
20952 | +#define OMAP3ISP_AEWB_MAX_WINVC 128 | ||
20953 | +#define OMAP3ISP_AEWB_MAX_WINHC 36 | ||
20954 | +#define OMAP3ISP_AEWB_MAX_WINSTART 4095 | ||
20955 | +#define OMAP3ISP_AEWB_MIN_SUB_INC 2 | ||
20956 | +#define OMAP3ISP_AEWB_MAX_SUB_INC 32 | ||
20957 | +#define OMAP3ISP_AEWB_MAX_BUF_SIZE 83600 | ||
20958 | + | ||
20959 | +#define OMAP3ISP_AF_IIRSH_MIN 0 | ||
20960 | +#define OMAP3ISP_AF_IIRSH_MAX 4095 | ||
20961 | +#define OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN 1 | ||
20962 | +#define OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX 36 | ||
20963 | +#define OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN 1 | ||
20964 | +#define OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX 128 | ||
20965 | +#define OMAP3ISP_AF_PAXEL_INCREMENT_MIN 2 | ||
20966 | +#define OMAP3ISP_AF_PAXEL_INCREMENT_MAX 32 | ||
20967 | +#define OMAP3ISP_AF_PAXEL_HEIGHT_MIN 2 | ||
20968 | +#define OMAP3ISP_AF_PAXEL_HEIGHT_MAX 256 | ||
20969 | +#define OMAP3ISP_AF_PAXEL_WIDTH_MIN 16 | ||
20970 | +#define OMAP3ISP_AF_PAXEL_WIDTH_MAX 256 | ||
20971 | +#define OMAP3ISP_AF_PAXEL_HZSTART_MIN 1 | ||
20972 | +#define OMAP3ISP_AF_PAXEL_HZSTART_MAX 4095 | ||
20973 | +#define OMAP3ISP_AF_PAXEL_VTSTART_MIN 0 | ||
20974 | +#define OMAP3ISP_AF_PAXEL_VTSTART_MAX 4095 | ||
20975 | +#define OMAP3ISP_AF_THRESHOLD_MAX 255 | ||
20976 | +#define OMAP3ISP_AF_COEF_MAX 4095 | ||
20977 | +#define OMAP3ISP_AF_PAXEL_SIZE 48 | ||
20978 | +#define OMAP3ISP_AF_MAX_BUF_SIZE 221184 | ||
20979 | + | ||
20980 | +/** | ||
20981 | + * struct omap3isp_h3a_aewb_config - AE AWB configuration reset values | ||
20982 | + * saturation_limit: Saturation limit. | ||
20983 | + * @win_height: Window Height. Range 2 - 256, even values only. | ||
20984 | + * @win_width: Window Width. Range 6 - 256, even values only. | ||
20985 | + * @ver_win_count: Vertical Window Count. Range 1 - 128. | ||
20986 | + * @hor_win_count: Horizontal Window Count. Range 1 - 36. | ||
20987 | + * @ver_win_start: Vertical Window Start. Range 0 - 4095. | ||
20988 | + * @hor_win_start: Horizontal Window Start. Range 0 - 4095. | ||
20989 | + * @blk_ver_win_start: Black Vertical Windows Start. Range 0 - 4095. | ||
20990 | + * @blk_win_height: Black Window Height. Range 2 - 256, even values only. | ||
20991 | + * @subsample_ver_inc: Subsample Vertical points increment Range 2 - 32, even | ||
20992 | + * values only. | ||
20993 | + * @subsample_hor_inc: Subsample Horizontal points increment Range 2 - 32, even | ||
20994 | + * values only. | ||
20995 | + * @alaw_enable: AEW ALAW EN flag. | ||
20996 | + * @aewb_enable: AE AWB stats generation EN flag. | ||
20997 | + */ | ||
20998 | +struct omap3isp_h3a_aewb_config { | ||
20999 | + /* | ||
21000 | + * Common fields. | ||
21001 | + * They should be the first ones and must be in the same order as in | ||
21002 | + * ispstat_generic_config struct. | ||
21003 | + */ | ||
21004 | + __u32 buf_size; | ||
21005 | + __u16 config_counter; | ||
21006 | + | ||
21007 | + /* Private fields */ | ||
21008 | + __u16 saturation_limit; | ||
21009 | + __u16 win_height; | ||
21010 | + __u16 win_width; | ||
21011 | + __u16 ver_win_count; | ||
21012 | + __u16 hor_win_count; | ||
21013 | + __u16 ver_win_start; | ||
21014 | + __u16 hor_win_start; | ||
21015 | + __u16 blk_ver_win_start; | ||
21016 | + __u16 blk_win_height; | ||
21017 | + __u16 subsample_ver_inc; | ||
21018 | + __u16 subsample_hor_inc; | ||
21019 | + __u8 alaw_enable; | ||
21020 | +}; | ||
21021 | + | ||
21022 | +/** | ||
21023 | + * struct omap3isp_stat_data - Statistic data sent to or received from user | ||
21024 | + * @buf: Pointer to pass to user. | ||
21025 | + * @frame_number: Frame number of requested stats. | ||
21026 | + * @cur_frame: Current frame number being processed. | ||
21027 | + * @buf_size: Buffer size requested and returned. | ||
21028 | + * @ts: Timestamp of returned framestats. | ||
21029 | + */ | ||
21030 | +struct omap3isp_stat_data { | ||
21031 | + struct timeval ts; | ||
21032 | + void __user *buf; | ||
21033 | + __u32 buf_size; | ||
21034 | + __u16 frame_number; | ||
21035 | + __u16 cur_frame; | ||
21036 | + __u16 config_counter; | ||
21037 | + __u16 new_bufs; /* Deprecated */ | ||
21038 | +}; | ||
21039 | + | ||
21040 | + | ||
21041 | +/* Histogram related structs */ | ||
21042 | + | ||
21043 | +/* Flags for number of bins */ | ||
21044 | +#define OMAP3ISP_HIST_BINS_32 0 | ||
21045 | +#define OMAP3ISP_HIST_BINS_64 1 | ||
21046 | +#define OMAP3ISP_HIST_BINS_128 2 | ||
21047 | +#define OMAP3ISP_HIST_BINS_256 3 | ||
21048 | + | ||
21049 | +/* Number of bins * 4 colors * 4-bytes word */ | ||
21050 | +#define OMAP3ISP_HIST_MEM_SIZE_BINS(n) ((1 << ((n)+5))*4*4) | ||
21051 | + | ||
21052 | +#define OMAP3ISP_HIST_MEM_SIZE 1024 | ||
21053 | +#define OMAP3ISP_HIST_MIN_REGIONS 1 | ||
21054 | +#define OMAP3ISP_HIST_MAX_REGIONS 4 | ||
21055 | +#define OMAP3ISP_HIST_MAX_WB_GAIN 255 | ||
21056 | +#define OMAP3ISP_HIST_MIN_WB_GAIN 0 | ||
21057 | +#define OMAP3ISP_HIST_MAX_BIT_WIDTH 14 | ||
21058 | +#define OMAP3ISP_HIST_MIN_BIT_WIDTH 8 | ||
21059 | +#define OMAP3ISP_HIST_MAX_WG 4 | ||
21060 | +#define OMAP3ISP_HIST_MAX_BUF_SIZE 4096 | ||
21061 | + | ||
21062 | +/* Source */ | ||
21063 | +#define OMAP3ISP_HIST_SOURCE_CCDC 0 | ||
21064 | +#define OMAP3ISP_HIST_SOURCE_MEM 1 | ||
21065 | + | ||
21066 | +/* CFA pattern */ | ||
21067 | +#define OMAP3ISP_HIST_CFA_BAYER 0 | ||
21068 | +#define OMAP3ISP_HIST_CFA_FOVEONX3 1 | ||
21069 | + | ||
21070 | +struct omap3isp_hist_region { | ||
21071 | + __u16 h_start; | ||
21072 | + __u16 h_end; | ||
21073 | + __u16 v_start; | ||
21074 | + __u16 v_end; | ||
21075 | +}; | ||
21076 | + | ||
21077 | +struct omap3isp_hist_config { | ||
21078 | + /* | ||
21079 | + * Common fields. | ||
21080 | + * They should be the first ones and must be in the same order as in | ||
21081 | + * ispstat_generic_config struct. | ||
21082 | + */ | ||
21083 | + __u32 buf_size; | ||
21084 | + __u16 config_counter; | ||
21085 | + | ||
21086 | + __u8 num_acc_frames; /* Num of image frames to be processed and | ||
21087 | + accumulated for each histogram frame */ | ||
21088 | + __u16 hist_bins; /* number of bins: 32, 64, 128, or 256 */ | ||
21089 | + __u8 cfa; /* BAYER or FOVEON X3 */ | ||
21090 | + __u8 wg[OMAP3ISP_HIST_MAX_WG]; /* White Balance Gain */ | ||
21091 | + __u8 num_regions; /* number of regions to be configured */ | ||
21092 | + struct omap3isp_hist_region region[OMAP3ISP_HIST_MAX_REGIONS]; | ||
21093 | +}; | ||
21094 | + | ||
21095 | +/* Auto Focus related structs */ | ||
21096 | + | ||
21097 | +#define OMAP3ISP_AF_NUM_COEF 11 | ||
21098 | + | ||
21099 | +enum omap3isp_h3a_af_fvmode { | ||
21100 | + OMAP3ISP_AF_MODE_SUMMED = 0, | ||
21101 | + OMAP3ISP_AF_MODE_PEAK = 1 | ||
21102 | +}; | ||
21103 | + | ||
21104 | +/* Red, Green, and blue pixel location in the AF windows */ | ||
21105 | +enum omap3isp_h3a_af_rgbpos { | ||
21106 | + OMAP3ISP_AF_GR_GB_BAYER = 0, /* GR and GB as Bayer pattern */ | ||
21107 | + OMAP3ISP_AF_RG_GB_BAYER = 1, /* RG and GB as Bayer pattern */ | ||
21108 | + OMAP3ISP_AF_GR_BG_BAYER = 2, /* GR and BG as Bayer pattern */ | ||
21109 | + OMAP3ISP_AF_RG_BG_BAYER = 3, /* RG and BG as Bayer pattern */ | ||
21110 | + OMAP3ISP_AF_GG_RB_CUSTOM = 4, /* GG and RB as custom pattern */ | ||
21111 | + OMAP3ISP_AF_RB_GG_CUSTOM = 5 /* RB and GG as custom pattern */ | ||
21112 | +}; | ||
21113 | + | ||
21114 | +/* Contains the information regarding the Horizontal Median Filter */ | ||
21115 | +struct omap3isp_h3a_af_hmf { | ||
21116 | + __u8 enable; /* Status of Horizontal Median Filter */ | ||
21117 | + __u8 threshold; /* Threshhold Value for Horizontal Median Filter */ | ||
21118 | +}; | ||
21119 | + | ||
21120 | +/* Contains the information regarding the IIR Filters */ | ||
21121 | +struct omap3isp_h3a_af_iir { | ||
21122 | + __u16 h_start; /* IIR horizontal start */ | ||
21123 | + __u16 coeff_set0[OMAP3ISP_AF_NUM_COEF]; /* Filter coefficient, set 0 */ | ||
21124 | + __u16 coeff_set1[OMAP3ISP_AF_NUM_COEF]; /* Filter coefficient, set 1 */ | ||
21125 | +}; | ||
21126 | + | ||
21127 | +/* Contains the information regarding the Paxels Structure in AF Engine */ | ||
21128 | +struct omap3isp_h3a_af_paxel { | ||
21129 | + __u16 h_start; /* Horizontal Start Position */ | ||
21130 | + __u16 v_start; /* Vertical Start Position */ | ||
21131 | + __u8 width; /* Width of the Paxel */ | ||
21132 | + __u8 height; /* Height of the Paxel */ | ||
21133 | + __u8 h_cnt; /* Horizontal Count */ | ||
21134 | + __u8 v_cnt; /* vertical Count */ | ||
21135 | + __u8 line_inc; /* Line Increment */ | ||
21136 | +}; | ||
21137 | + | ||
21138 | +/* Contains the parameters required for hardware set up of AF Engine */ | ||
21139 | +struct omap3isp_h3a_af_config { | ||
21140 | + /* | ||
21141 | + * Common fields. | ||
21142 | + * They should be the first ones and must be in the same order as in | ||
21143 | + * ispstat_generic_config struct. | ||
21144 | + */ | ||
21145 | + __u32 buf_size; | ||
21146 | + __u16 config_counter; | ||
21147 | + | ||
21148 | + struct omap3isp_h3a_af_hmf hmf; /* HMF configurations */ | ||
21149 | + struct omap3isp_h3a_af_iir iir; /* IIR filter configurations */ | ||
21150 | + struct omap3isp_h3a_af_paxel paxel; /* Paxel parameters */ | ||
21151 | + enum omap3isp_h3a_af_rgbpos rgb_pos; /* RGB Positions */ | ||
21152 | + enum omap3isp_h3a_af_fvmode fvmode; /* Accumulator mode */ | ||
21153 | + __u8 alaw_enable; /* AF ALAW status */ | ||
21154 | +}; | ||
21155 | + | ||
21156 | +/* ISP CCDC structs */ | ||
21157 | + | ||
21158 | +/* Abstraction layer CCDC configurations */ | ||
21159 | +#define OMAP3ISP_CCDC_ALAW (1 << 0) | ||
21160 | +#define OMAP3ISP_CCDC_LPF (1 << 1) | ||
21161 | +#define OMAP3ISP_CCDC_BLCLAMP (1 << 2) | ||
21162 | +#define OMAP3ISP_CCDC_BCOMP (1 << 3) | ||
21163 | +#define OMAP3ISP_CCDC_FPC (1 << 4) | ||
21164 | +#define OMAP3ISP_CCDC_CULL (1 << 5) | ||
21165 | +#define OMAP3ISP_CCDC_CONFIG_LSC (1 << 7) | ||
21166 | +#define OMAP3ISP_CCDC_TBL_LSC (1 << 8) | ||
21167 | + | ||
21168 | +#define OMAP3ISP_RGB_MAX 3 | ||
21169 | + | ||
21170 | +/* Enumeration constants for Alaw input width */ | ||
21171 | +enum omap3isp_alaw_ipwidth { | ||
21172 | + OMAP3ISP_ALAW_BIT12_3 = 0x3, | ||
21173 | + OMAP3ISP_ALAW_BIT11_2 = 0x4, | ||
21174 | + OMAP3ISP_ALAW_BIT10_1 = 0x5, | ||
21175 | + OMAP3ISP_ALAW_BIT9_0 = 0x6 | ||
21176 | +}; | ||
21177 | + | ||
21178 | +/** | ||
21179 | + * struct omap3isp_ccdc_lsc_config - LSC configuration | ||
21180 | + * @offset: Table Offset of the gain table. | ||
21181 | + * @gain_mode_n: Vertical dimension of a paxel in LSC configuration. | ||
21182 | + * @gain_mode_m: Horizontal dimension of a paxel in LSC configuration. | ||
21183 | + * @gain_format: Gain table format. | ||
21184 | + * @fmtsph: Start pixel horizontal from start of the HS sync pulse. | ||
21185 | + * @fmtlnh: Number of pixels in horizontal direction to use for the data | ||
21186 | + * reformatter. | ||
21187 | + * @fmtslv: Start line from start of VS sync pulse for the data reformatter. | ||
21188 | + * @fmtlnv: Number of lines in vertical direction for the data reformatter. | ||
21189 | + * @initial_x: X position, in pixels, of the first active pixel in reference | ||
21190 | + * to the first active paxel. Must be an even number. | ||
21191 | + * @initial_y: Y position, in pixels, of the first active pixel in reference | ||
21192 | + * to the first active paxel. Must be an even number. | ||
21193 | + * @size: Size of LSC gain table. Filled when loaded from userspace. | ||
21194 | + */ | ||
21195 | +struct omap3isp_ccdc_lsc_config { | ||
21196 | + __u16 offset; | ||
21197 | + __u8 gain_mode_n; | ||
21198 | + __u8 gain_mode_m; | ||
21199 | + __u8 gain_format; | ||
21200 | + __u16 fmtsph; | ||
21201 | + __u16 fmtlnh; | ||
21202 | + __u16 fmtslv; | ||
21203 | + __u16 fmtlnv; | ||
21204 | + __u8 initial_x; | ||
21205 | + __u8 initial_y; | ||
21206 | + __u32 size; | ||
21207 | +}; | ||
21208 | + | ||
21209 | +/** | ||
21210 | + * struct omap3isp_ccdc_bclamp - Optical & Digital black clamp subtract | ||
21211 | + * @obgain: Optical black average gain. | ||
21212 | + * @obstpixel: Start Pixel w.r.t. HS pulse in Optical black sample. | ||
21213 | + * @oblines: Optical Black Sample lines. | ||
21214 | + * @oblen: Optical Black Sample Length. | ||
21215 | + * @dcsubval: Digital Black Clamp subtract value. | ||
21216 | + */ | ||
21217 | +struct omap3isp_ccdc_bclamp { | ||
21218 | + __u8 obgain; | ||
21219 | + __u8 obstpixel; | ||
21220 | + __u8 oblines; | ||
21221 | + __u8 oblen; | ||
21222 | + __u16 dcsubval; | ||
21223 | +}; | ||
21224 | + | ||
21225 | +/** | ||
21226 | + * struct omap3isp_ccdc_fpc - Faulty Pixels Correction | ||
21227 | + * @fpnum: Number of faulty pixels to be corrected in the frame. | ||
21228 | + * @fpcaddr: Memory address of the FPC Table | ||
21229 | + */ | ||
21230 | +struct omap3isp_ccdc_fpc { | ||
21231 | + __u16 fpnum; | ||
21232 | + __u32 fpcaddr; | ||
21233 | +}; | ||
21234 | + | ||
21235 | +/** | ||
21236 | + * struct omap3isp_ccdc_blcomp - Black Level Compensation parameters | ||
21237 | + * @b_mg: B/Mg pixels. 2's complement. -128 to +127. | ||
21238 | + * @gb_g: Gb/G pixels. 2's complement. -128 to +127. | ||
21239 | + * @gr_cy: Gr/Cy pixels. 2's complement. -128 to +127. | ||
21240 | + * @r_ye: R/Ye pixels. 2's complement. -128 to +127. | ||
21241 | + */ | ||
21242 | +struct omap3isp_ccdc_blcomp { | ||
21243 | + __u8 b_mg; | ||
21244 | + __u8 gb_g; | ||
21245 | + __u8 gr_cy; | ||
21246 | + __u8 r_ye; | ||
21247 | +}; | ||
21248 | + | ||
21249 | +/** | ||
21250 | + * omap3isp_ccdc_culling - Culling parameters | ||
21251 | + * @v_pattern: Vertical culling pattern. | ||
21252 | + * @h_odd: Horizontal Culling pattern for odd lines. | ||
21253 | + * @h_even: Horizontal Culling pattern for even lines. | ||
21254 | + */ | ||
21255 | +struct omap3isp_ccdc_culling { | ||
21256 | + __u8 v_pattern; | ||
21257 | + __u16 h_odd; | ||
21258 | + __u16 h_even; | ||
21259 | +}; | ||
21260 | + | ||
21261 | +/** | ||
21262 | + * omap3isp_ccdc_update_config - CCDC configuration | ||
21263 | + * @update: Specifies which CCDC registers should be updated. | ||
21264 | + * @flag: Specifies which CCDC functions should be enabled. | ||
21265 | + * @alawip: Enable/Disable A-Law compression. | ||
21266 | + * @bclamp: Black clamp control register. | ||
21267 | + * @blcomp: Black level compensation value for RGrGbB Pixels. 2's complement. | ||
21268 | + * @fpc: Number of faulty pixels corrected in the frame, address of FPC table. | ||
21269 | + * @cull: Cull control register. | ||
21270 | + * @lsc: Pointer to LSC gain table. | ||
21271 | + */ | ||
21272 | +struct omap3isp_ccdc_update_config { | ||
21273 | + __u16 update; | ||
21274 | + __u16 flag; | ||
21275 | + enum omap3isp_alaw_ipwidth alawip; | ||
21276 | + struct omap3isp_ccdc_bclamp __user *bclamp; | ||
21277 | + struct omap3isp_ccdc_blcomp __user *blcomp; | ||
21278 | + struct omap3isp_ccdc_fpc __user *fpc; | ||
21279 | + struct omap3isp_ccdc_lsc_config __user *lsc_cfg; | ||
21280 | + struct omap3isp_ccdc_culling __user *cull; | ||
21281 | + __u8 __user *lsc; | ||
21282 | +}; | ||
21283 | + | ||
21284 | +/* Preview configurations */ | ||
21285 | +#define OMAP3ISP_PREV_LUMAENH (1 << 0) | ||
21286 | +#define OMAP3ISP_PREV_INVALAW (1 << 1) | ||
21287 | +#define OMAP3ISP_PREV_HRZ_MED (1 << 2) | ||
21288 | +#define OMAP3ISP_PREV_CFA (1 << 3) | ||
21289 | +#define OMAP3ISP_PREV_CHROMA_SUPP (1 << 4) | ||
21290 | +#define OMAP3ISP_PREV_WB (1 << 5) | ||
21291 | +#define OMAP3ISP_PREV_BLKADJ (1 << 6) | ||
21292 | +#define OMAP3ISP_PREV_RGB2RGB (1 << 7) | ||
21293 | +#define OMAP3ISP_PREV_COLOR_CONV (1 << 8) | ||
21294 | +#define OMAP3ISP_PREV_YC_LIMIT (1 << 9) | ||
21295 | +#define OMAP3ISP_PREV_DEFECT_COR (1 << 10) | ||
21296 | +#define OMAP3ISP_PREV_GAMMABYPASS (1 << 11) | ||
21297 | +#define OMAP3ISP_PREV_DRK_FRM_CAPTURE (1 << 12) | ||
21298 | +#define OMAP3ISP_PREV_DRK_FRM_SUBTRACT (1 << 13) | ||
21299 | +#define OMAP3ISP_PREV_LENS_SHADING (1 << 14) | ||
21300 | +#define OMAP3ISP_PREV_NF (1 << 15) | ||
21301 | +#define OMAP3ISP_PREV_GAMMA (1 << 16) | ||
21302 | + | ||
21303 | +#define OMAP3ISP_PREV_NF_TBL_SIZE 64 | ||
21304 | +#define OMAP3ISP_PREV_CFA_TBL_SIZE 576 | ||
21305 | +#define OMAP3ISP_PREV_GAMMA_TBL_SIZE 1024 | ||
21306 | +#define OMAP3ISP_PREV_YENH_TBL_SIZE 128 | ||
21307 | + | ||
21308 | +#define OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS 4 | ||
21309 | + | ||
21310 | +/** | ||
21311 | + * struct omap3isp_prev_hmed - Horizontal Median Filter | ||
21312 | + * @odddist: Distance between consecutive pixels of same color in the odd line. | ||
21313 | + * @evendist: Distance between consecutive pixels of same color in the even | ||
21314 | + * line. | ||
21315 | + * @thres: Horizontal median filter threshold. | ||
21316 | + */ | ||
21317 | +struct omap3isp_prev_hmed { | ||
21318 | + __u8 odddist; | ||
21319 | + __u8 evendist; | ||
21320 | + __u8 thres; | ||
21321 | +}; | ||
21322 | + | ||
21323 | +/* | ||
21324 | + * Enumeration for CFA Formats supported by preview | ||
21325 | + */ | ||
21326 | +enum omap3isp_cfa_fmt { | ||
21327 | + OMAP3ISP_CFAFMT_BAYER, | ||
21328 | + OMAP3ISP_CFAFMT_SONYVGA, | ||
21329 | + OMAP3ISP_CFAFMT_RGBFOVEON, | ||
21330 | + OMAP3ISP_CFAFMT_DNSPL, | ||
21331 | + OMAP3ISP_CFAFMT_HONEYCOMB, | ||
21332 | + OMAP3ISP_CFAFMT_RRGGBBFOVEON | ||
21333 | +}; | ||
21334 | + | ||
21335 | +/** | ||
21336 | + * struct omap3isp_prev_cfa - CFA Interpolation | ||
21337 | + * @format: CFA Format Enum value supported by preview. | ||
21338 | + * @gradthrs_vert: CFA Gradient Threshold - Vertical. | ||
21339 | + * @gradthrs_horz: CFA Gradient Threshold - Horizontal. | ||
21340 | + * @table: Pointer to the CFA table. | ||
21341 | + */ | ||
21342 | +struct omap3isp_prev_cfa { | ||
21343 | + enum omap3isp_cfa_fmt format; | ||
21344 | + __u8 gradthrs_vert; | ||
21345 | + __u8 gradthrs_horz; | ||
21346 | + __u32 table[OMAP3ISP_PREV_CFA_TBL_SIZE]; | ||
21347 | +}; | ||
21348 | + | ||
21349 | +/** | ||
21350 | + * struct omap3isp_prev_csup - Chrominance Suppression | ||
21351 | + * @gain: Gain. | ||
21352 | + * @thres: Threshold. | ||
21353 | + * @hypf_en: Flag to enable/disable the High Pass Filter. | ||
21354 | + */ | ||
21355 | +struct omap3isp_prev_csup { | ||
21356 | + __u8 gain; | ||
21357 | + __u8 thres; | ||
21358 | + __u8 hypf_en; | ||
21359 | +}; | ||
21360 | + | ||
21361 | +/** | ||
21362 | + * struct omap3isp_prev_wbal - White Balance | ||
21363 | + * @dgain: Digital gain (U10Q8). | ||
21364 | + * @coef3: White balance gain - COEF 3 (U8Q5). | ||
21365 | + * @coef2: White balance gain - COEF 2 (U8Q5). | ||
21366 | + * @coef1: White balance gain - COEF 1 (U8Q5). | ||
21367 | + * @coef0: White balance gain - COEF 0 (U8Q5). | ||
21368 | + */ | ||
21369 | +struct omap3isp_prev_wbal { | ||
21370 | + __u16 dgain; | ||
21371 | + __u8 coef3; | ||
21372 | + __u8 coef2; | ||
21373 | + __u8 coef1; | ||
21374 | + __u8 coef0; | ||
21375 | +}; | ||
21376 | + | ||
21377 | +/** | ||
21378 | + * struct omap3isp_prev_blkadj - Black Level Adjustment | ||
21379 | + * @red: Black level offset adjustment for Red in 2's complement format | ||
21380 | + * @green: Black level offset adjustment for Green in 2's complement format | ||
21381 | + * @blue: Black level offset adjustment for Blue in 2's complement format | ||
21382 | + */ | ||
21383 | +struct omap3isp_prev_blkadj { | ||
21384 | + /*Black level offset adjustment for Red in 2's complement format */ | ||
21385 | + __u8 red; | ||
21386 | + /*Black level offset adjustment for Green in 2's complement format */ | ||
21387 | + __u8 green; | ||
21388 | + /* Black level offset adjustment for Blue in 2's complement format */ | ||
21389 | + __u8 blue; | ||
21390 | +}; | ||
21391 | + | ||
21392 | +/** | ||
21393 | + * struct omap3isp_prev_rgbtorgb - RGB to RGB Blending | ||
21394 | + * @matrix: Blending values(S12Q8 format) | ||
21395 | + * [RR] [GR] [BR] | ||
21396 | + * [RG] [GG] [BG] | ||
21397 | + * [RB] [GB] [BB] | ||
21398 | + * @offset: Blending offset value for R,G,B in 2's complement integer format. | ||
21399 | + */ | ||
21400 | +struct omap3isp_prev_rgbtorgb { | ||
21401 | + __u16 matrix[OMAP3ISP_RGB_MAX][OMAP3ISP_RGB_MAX]; | ||
21402 | + __u16 offset[OMAP3ISP_RGB_MAX]; | ||
21403 | +}; | ||
21404 | + | ||
21405 | +/** | ||
21406 | + * struct omap3isp_prev_csc - Color Space Conversion from RGB-YCbYCr | ||
21407 | + * @matrix: Color space conversion coefficients(S10Q8) | ||
21408 | + * [CSCRY] [CSCGY] [CSCBY] | ||
21409 | + * [CSCRCB] [CSCGCB] [CSCBCB] | ||
21410 | + * [CSCRCR] [CSCGCR] [CSCBCR] | ||
21411 | + * @offset: CSC offset values for Y offset, CB offset and CR offset respectively | ||
21412 | + */ | ||
21413 | +struct omap3isp_prev_csc { | ||
21414 | + __u16 matrix[OMAP3ISP_RGB_MAX][OMAP3ISP_RGB_MAX]; | ||
21415 | + __s16 offset[OMAP3ISP_RGB_MAX]; | ||
21416 | +}; | ||
21417 | + | ||
21418 | +/** | ||
21419 | + * struct omap3isp_prev_yclimit - Y, C Value Limit | ||
21420 | + * @minC: Minimum C value | ||
21421 | + * @maxC: Maximum C value | ||
21422 | + * @minY: Minimum Y value | ||
21423 | + * @maxY: Maximum Y value | ||
21424 | + */ | ||
21425 | +struct omap3isp_prev_yclimit { | ||
21426 | + __u8 minC; | ||
21427 | + __u8 maxC; | ||
21428 | + __u8 minY; | ||
21429 | + __u8 maxY; | ||
21430 | +}; | ||
21431 | + | ||
21432 | +/** | ||
21433 | + * struct omap3isp_prev_dcor - Defect correction | ||
21434 | + * @couplet_mode_en: Flag to enable or disable the couplet dc Correction in NF | ||
21435 | + * @detect_correct: Thresholds for correction bit 0:10 detect 16:25 correct | ||
21436 | + */ | ||
21437 | +struct omap3isp_prev_dcor { | ||
21438 | + __u8 couplet_mode_en; | ||
21439 | + __u32 detect_correct[OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS]; | ||
21440 | +}; | ||
21441 | + | ||
21442 | +/** | ||
21443 | + * struct omap3isp_prev_nf - Noise Filter | ||
21444 | + * @spread: Spread value to be used in Noise Filter | ||
21445 | + * @table: Pointer to the Noise Filter table | ||
21446 | + */ | ||
21447 | +struct omap3isp_prev_nf { | ||
21448 | + __u8 spread; | ||
21449 | + __u32 table[OMAP3ISP_PREV_NF_TBL_SIZE]; | ||
21450 | +}; | ||
21451 | + | ||
21452 | +/** | ||
21453 | + * struct omap3isp_prev_gtables - Gamma correction tables | ||
21454 | + * @red: Array for red gamma table. | ||
21455 | + * @green: Array for green gamma table. | ||
21456 | + * @blue: Array for blue gamma table. | ||
21457 | + */ | ||
21458 | +struct omap3isp_prev_gtables { | ||
21459 | + __u32 red[OMAP3ISP_PREV_GAMMA_TBL_SIZE]; | ||
21460 | + __u32 green[OMAP3ISP_PREV_GAMMA_TBL_SIZE]; | ||
21461 | + __u32 blue[OMAP3ISP_PREV_GAMMA_TBL_SIZE]; | ||
21462 | +}; | ||
21463 | + | ||
21464 | +/** | ||
21465 | + * struct omap3isp_prev_luma - Luma enhancement | ||
21466 | + * @table: Array for luma enhancement table. | ||
21467 | + */ | ||
21468 | +struct omap3isp_prev_luma { | ||
21469 | + __u32 table[OMAP3ISP_PREV_YENH_TBL_SIZE]; | ||
21470 | +}; | ||
21471 | + | ||
21472 | +/** | ||
21473 | + * struct omap3isp_prev_update_config - Preview engine configuration (user) | ||
21474 | + * @update: Specifies which ISP Preview registers should be updated. | ||
21475 | + * @flag: Specifies which ISP Preview functions should be enabled. | ||
21476 | + * @shading_shift: 3bit value of shift used in shading compensation. | ||
21477 | + * @luma: Pointer to luma enhancement structure. | ||
21478 | + * @hmed: Pointer to structure containing the odd and even distance. | ||
21479 | + * between the pixels in the image along with the filter threshold. | ||
21480 | + * @cfa: Pointer to structure containing the CFA interpolation table, CFA. | ||
21481 | + * format in the image, vertical and horizontal gradient threshold. | ||
21482 | + * @csup: Pointer to Structure for Chrominance Suppression coefficients. | ||
21483 | + * @wbal: Pointer to structure for White Balance. | ||
21484 | + * @blkadj: Pointer to structure for Black Adjustment. | ||
21485 | + * @rgb2rgb: Pointer to structure for RGB to RGB Blending. | ||
21486 | + * @csc: Pointer to structure for Color Space Conversion from RGB-YCbYCr. | ||
21487 | + * @yclimit: Pointer to structure for Y, C Value Limit. | ||
21488 | + * @dcor: Pointer to structure for defect correction. | ||
21489 | + * @nf: Pointer to structure for Noise Filter | ||
21490 | + * @gamma: Pointer to gamma structure. | ||
21491 | + */ | ||
21492 | +struct omap3isp_prev_update_config { | ||
21493 | + __u32 update; | ||
21494 | + __u32 flag; | ||
21495 | + __u32 shading_shift; | ||
21496 | + struct omap3isp_prev_luma __user *luma; | ||
21497 | + struct omap3isp_prev_hmed __user *hmed; | ||
21498 | + struct omap3isp_prev_cfa __user *cfa; | ||
21499 | + struct omap3isp_prev_csup __user *csup; | ||
21500 | + struct omap3isp_prev_wbal __user *wbal; | ||
21501 | + struct omap3isp_prev_blkadj __user *blkadj; | ||
21502 | + struct omap3isp_prev_rgbtorgb __user *rgb2rgb; | ||
21503 | + struct omap3isp_prev_csc __user *csc; | ||
21504 | + struct omap3isp_prev_yclimit __user *yclimit; | ||
21505 | + struct omap3isp_prev_dcor __user *dcor; | ||
21506 | + struct omap3isp_prev_nf __user *nf; | ||
21507 | + struct omap3isp_prev_gtables __user *gamma; | ||
21508 | +}; | ||
21509 | + | ||
21510 | +#endif /* OMAP3_ISP_USER_H */ | ||
21511 | -- | ||
21512 | 1.6.6.1 | ||
21513 | |||