diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/media/video/omap3isp | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/media/video/omap3isp')
31 files changed, 19971 insertions, 0 deletions
diff --git a/drivers/media/video/omap3isp/Makefile b/drivers/media/video/omap3isp/Makefile new file mode 100644 index 00000000000..b1b344774ae --- /dev/null +++ b/drivers/media/video/omap3isp/Makefile | |||
@@ -0,0 +1,13 @@ | |||
1 | # Makefile for OMAP3 ISP driver | ||
2 | |||
3 | ifdef CONFIG_VIDEO_OMAP3_DEBUG | ||
4 | EXTRA_CFLAGS += -DDEBUG | ||
5 | endif | ||
6 | |||
7 | omap3-isp-objs += \ | ||
8 | isp.o ispqueue.o ispvideo.o \ | ||
9 | ispcsiphy.o ispccp2.o ispcsi2.o \ | ||
10 | ispccdc.o isppreview.o ispresizer.o \ | ||
11 | ispstat.o isph3a_aewb.o isph3a_af.o isphist.o | ||
12 | |||
13 | obj-$(CONFIG_VIDEO_OMAP3) += omap3-isp.o | ||
diff --git a/drivers/media/video/omap3isp/cfa_coef_table.h b/drivers/media/video/omap3isp/cfa_coef_table.h new file mode 100644 index 00000000000..c60df0ed075 --- /dev/null +++ b/drivers/media/video/omap3isp/cfa_coef_table.h | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * cfa_coef_table.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - CFA coefficients table | ||
5 | * | ||
6 | * Copyright (C) 2009-2010 Nokia Corporation | ||
7 | * | ||
8 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
9 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * version 2 as published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
23 | * 02110-1301 USA | ||
24 | */ | ||
25 | |||
26 | 244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244, | ||
27 | 248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250, | ||
28 | 247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248, | ||
29 | 244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244, | ||
30 | 248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250, | ||
31 | 247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248, | ||
32 | 244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244, | ||
33 | 248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250, | ||
34 | 247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248, | ||
35 | 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4, | ||
36 | 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0, | ||
37 | 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0, | ||
38 | 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4, | ||
39 | 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0, | ||
40 | 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0, | ||
41 | 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4, | ||
42 | 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0, | ||
43 | 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0, | ||
44 | 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0, | ||
45 | 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12, | ||
46 | 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0, | ||
47 | 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0, | ||
48 | 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12, | ||
49 | 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0, | ||
50 | 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0, | ||
51 | 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12, | ||
52 | 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0, | ||
53 | 244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244, | ||
54 | 248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247, | ||
55 | 250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248, | ||
56 | 244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244, | ||
57 | 248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247, | ||
58 | 250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248, | ||
59 | 244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244, | ||
60 | 248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247, | ||
61 | 250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248 | ||
diff --git a/drivers/media/video/omap3isp/gamma_table.h b/drivers/media/video/omap3isp/gamma_table.h new file mode 100644 index 00000000000..78deebf7d96 --- /dev/null +++ b/drivers/media/video/omap3isp/gamma_table.h | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * gamma_table.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - Default gamma table for all components | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | 0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, | ||
28 | 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 36, 37, 39, 40, 41, 42, | ||
29 | 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 52, 53, 54, 55, 56, 57, | ||
30 | 58, 59, 60, 61, 62, 63, 63, 64, 65, 66, 66, 67, 68, 69, 69, 70, | ||
31 | 71, 72, 72, 73, 74, 75, 75, 76, 77, 78, 78, 79, 80, 81, 81, 82, | ||
32 | 83, 84, 84, 85, 86, 87, 88, 88, 89, 90, 91, 91, 92, 93, 94, 94, | ||
33 | 95, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 103, 104, 104, | ||
34 | 105, 106, 107, 108, 108, 109, 110, 111, 111, 112, 113, 114, 114, 115, 116, 117, | ||
35 | 117, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, | ||
36 | 126, 126, 127, 127, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, | ||
37 | 134, 134, 135, 135, 136, 136, 137, 137, 138, 138, 139, 139, 140, 140, 141, 141, | ||
38 | 142, 142, 143, 143, 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, 149, 149, | ||
39 | 150, 150, 151, 151, 152, 152, 153, 153, 153, 153, 154, 154, 154, 154, 155, 155, | ||
40 | 156, 156, 157, 157, 158, 158, 158, 159, 159, 159, 160, 160, 160, 161, 161, 162, | ||
41 | 162, 163, 163, 164, 164, 164, 164, 165, 165, 165, 165, 166, 166, 167, 167, 168, | ||
42 | 168, 169, 169, 170, 170, 170, 170, 171, 171, 171, 171, 172, 172, 173, 173, 174, | ||
43 | 174, 175, 175, 176, 176, 176, 176, 177, 177, 177, 177, 178, 178, 178, 178, 179, | ||
44 | 179, 179, 179, 180, 180, 180, 180, 181, 181, 181, 181, 182, 182, 182, 182, 183, | ||
45 | 183, 183, 183, 184, 184, 184, 184, 185, 185, 185, 185, 186, 186, 186, 186, 187, | ||
46 | 187, 187, 187, 188, 188, 188, 188, 189, 189, 189, 189, 190, 190, 190, 190, 191, | ||
47 | 191, 191, 191, 192, 192, 192, 192, 193, 193, 193, 193, 194, 194, 194, 194, 195, | ||
48 | 195, 195, 195, 196, 196, 196, 196, 197, 197, 197, 197, 198, 198, 198, 198, 199, | ||
49 | 199, 199, 199, 200, 200, 200, 200, 201, 201, 201, 201, 202, 202, 202, 203, 203, | ||
50 | 203, 203, 204, 204, 204, 204, 205, 205, 205, 205, 206, 206, 206, 206, 207, 207, | ||
51 | 207, 207, 208, 208, 208, 208, 209, 209, 209, 209, 210, 210, 210, 210, 210, 210, | ||
52 | 210, 210, 210, 210, 210, 210, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, | ||
53 | 211, 212, 212, 212, 212, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, | ||
54 | 213, 214, 214, 214, 214, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, | ||
55 | 216, 216, 216, 216, 217, 217, 217, 217, 218, 218, 218, 218, 219, 219, 219, 219, | ||
56 | 219, 219, 219, 219, 219, 219, 219, 219, 220, 220, 220, 220, 221, 221, 221, 221, | ||
57 | 221, 221, 221, 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 223, | ||
58 | 223, 223, 223, 223, 223, 223, 223, 224, 224, 224, 224, 225, 225, 225, 225, 225, | ||
59 | 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 226, 226, | ||
60 | 226, 226, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 228, 228, | ||
61 | 228, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 230, 230, 230, | ||
62 | 230, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 232, 232, 232, | ||
63 | 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, | ||
64 | 233, 233, 233, 233, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 235, | ||
65 | 235, 235, 235, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, | ||
66 | 236, 236, 236, 236, 236, 236, 237, 237, 237, 237, 238, 238, 238, 238, 238, 238, | ||
67 | 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, | ||
68 | 238, 238, 238, 238, 238, 239, 239, 239, 239, 240, 240, 240, 240, 240, 240, 240, | ||
69 | 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, | ||
70 | 240, 240, 240, 240, 241, 241, 241, 241, 242, 242, 242, 242, 242, 242, 242, 242, | ||
71 | 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, | ||
72 | 242, 242, 243, 243, 243, 243, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, | ||
73 | 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, | ||
74 | 244, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, | ||
75 | 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, | ||
76 | 246, 246, 246, 246, 246, 246, 246, 247, 247, 247, 247, 248, 248, 248, 248, 248, | ||
77 | 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, | ||
78 | 248, 248, 248, 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 250, 250, | ||
79 | 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, | ||
80 | 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, | ||
81 | 250, 250, 250, 250, 251, 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252, | ||
82 | 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, | ||
83 | 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, | ||
84 | 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, | ||
85 | 252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253, 253, | ||
86 | 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, | ||
87 | 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, | ||
88 | 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, | ||
89 | 253, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||
90 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||
diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c new file mode 100644 index 00000000000..abc26854e29 --- /dev/null +++ b/drivers/media/video/omap3isp/isp.c | |||
@@ -0,0 +1,2244 @@ | |||
1 | /* | ||
2 | * isp.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - Core | ||
5 | * | ||
6 | * Copyright (C) 2006-2010 Nokia Corporation | ||
7 | * Copyright (C) 2007-2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * Contributors: | ||
13 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
14 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
15 | * David Cohen <dacohen@gmail.com> | ||
16 | * Stanimir Varbanov <svarbanov@mm-sol.com> | ||
17 | * Vimarsh Zutshi <vimarsh.zutshi@gmail.com> | ||
18 | * Tuukka Toivonen <tuukkat76@gmail.com> | ||
19 | * Sergio Aguirre <saaguirre@ti.com> | ||
20 | * Antti Koskipaa <akoskipa@gmail.com> | ||
21 | * Ivan T. Ivanov <iivanov@mm-sol.com> | ||
22 | * RaniSuneela <r-m@ti.com> | ||
23 | * Atanas Filipov <afilipov@mm-sol.com> | ||
24 | * Gjorgji Rosikopulos <grosikopulos@mm-sol.com> | ||
25 | * Hiroshi DOYU <hiroshi.doyu@nokia.com> | ||
26 | * Nayden Kanchev <nkanchev@mm-sol.com> | ||
27 | * Phil Carmody <ext-phil.2.carmody@nokia.com> | ||
28 | * Artem Bityutskiy <artem.bityutskiy@nokia.com> | ||
29 | * Dominic Curran <dcurran@ti.com> | ||
30 | * Ilkka Myllyperkio <ilkka.myllyperkio@sofica.fi> | ||
31 | * Pallavi Kulkarni <p-kulkarni@ti.com> | ||
32 | * Vaibhav Hiremath <hvaibhav@ti.com> | ||
33 | * Mohit Jalori <mjalori@ti.com> | ||
34 | * Sameer Venkatraman <sameerv@ti.com> | ||
35 | * Senthilvadivu Guruswamy <svadivu@ti.com> | ||
36 | * Thara Gopinath <thara@ti.com> | ||
37 | * Toni Leinonen <toni.leinonen@nokia.com> | ||
38 | * Troy Laramy <t-laramy@ti.com> | ||
39 | * | ||
40 | * This program is free software; you can redistribute it and/or modify | ||
41 | * it under the terms of the GNU General Public License version 2 as | ||
42 | * published by the Free Software Foundation. | ||
43 | * | ||
44 | * This program is distributed in the hope that it will be useful, but | ||
45 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
46 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
47 | * General Public License for more details. | ||
48 | * | ||
49 | * You should have received a copy of the GNU General Public License | ||
50 | * along with this program; if not, write to the Free Software | ||
51 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
52 | * 02110-1301 USA | ||
53 | */ | ||
54 | |||
55 | #include <asm/cacheflush.h> | ||
56 | |||
57 | #include <linux/clk.h> | ||
58 | #include <linux/delay.h> | ||
59 | #include <linux/device.h> | ||
60 | #include <linux/dma-mapping.h> | ||
61 | #include <linux/i2c.h> | ||
62 | #include <linux/interrupt.h> | ||
63 | #include <linux/module.h> | ||
64 | #include <linux/platform_device.h> | ||
65 | #include <linux/regulator/consumer.h> | ||
66 | #include <linux/slab.h> | ||
67 | #include <linux/sched.h> | ||
68 | #include <linux/vmalloc.h> | ||
69 | |||
70 | #include <media/v4l2-common.h> | ||
71 | #include <media/v4l2-device.h> | ||
72 | |||
73 | #include "isp.h" | ||
74 | #include "ispreg.h" | ||
75 | #include "ispccdc.h" | ||
76 | #include "isppreview.h" | ||
77 | #include "ispresizer.h" | ||
78 | #include "ispcsi2.h" | ||
79 | #include "ispccp2.h" | ||
80 | #include "isph3a.h" | ||
81 | #include "isphist.h" | ||
82 | |||
83 | static unsigned int autoidle; | ||
84 | module_param(autoidle, int, 0444); | ||
85 | MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support"); | ||
86 | |||
87 | static void isp_save_ctx(struct isp_device *isp); | ||
88 | |||
89 | static void isp_restore_ctx(struct isp_device *isp); | ||
90 | |||
91 | static const struct isp_res_mapping isp_res_maps[] = { | ||
92 | { | ||
93 | .isp_rev = ISP_REVISION_2_0, | ||
94 | .map = 1 << OMAP3_ISP_IOMEM_MAIN | | ||
95 | 1 << OMAP3_ISP_IOMEM_CCP2 | | ||
96 | 1 << OMAP3_ISP_IOMEM_CCDC | | ||
97 | 1 << OMAP3_ISP_IOMEM_HIST | | ||
98 | 1 << OMAP3_ISP_IOMEM_H3A | | ||
99 | 1 << OMAP3_ISP_IOMEM_PREV | | ||
100 | 1 << OMAP3_ISP_IOMEM_RESZ | | ||
101 | 1 << OMAP3_ISP_IOMEM_SBL | | ||
102 | 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 | | ||
103 | 1 << OMAP3_ISP_IOMEM_CSIPHY2, | ||
104 | }, | ||
105 | { | ||
106 | .isp_rev = ISP_REVISION_15_0, | ||
107 | .map = 1 << OMAP3_ISP_IOMEM_MAIN | | ||
108 | 1 << OMAP3_ISP_IOMEM_CCP2 | | ||
109 | 1 << OMAP3_ISP_IOMEM_CCDC | | ||
110 | 1 << OMAP3_ISP_IOMEM_HIST | | ||
111 | 1 << OMAP3_ISP_IOMEM_H3A | | ||
112 | 1 << OMAP3_ISP_IOMEM_PREV | | ||
113 | 1 << OMAP3_ISP_IOMEM_RESZ | | ||
114 | 1 << OMAP3_ISP_IOMEM_SBL | | ||
115 | 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 | | ||
116 | 1 << OMAP3_ISP_IOMEM_CSIPHY2 | | ||
117 | 1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 | | ||
118 | 1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 | | ||
119 | 1 << OMAP3_ISP_IOMEM_CSIPHY1 | | ||
120 | 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2, | ||
121 | }, | ||
122 | }; | ||
123 | |||
124 | /* Structure for saving/restoring ISP module registers */ | ||
125 | static struct isp_reg isp_reg_list[] = { | ||
126 | {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0}, | ||
127 | {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0}, | ||
128 | {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0}, | ||
129 | {0, ISP_TOK_TERM, 0} | ||
130 | }; | ||
131 | |||
132 | /* | ||
133 | * omap3isp_flush - Post pending L3 bus writes by doing a register readback | ||
134 | * @isp: OMAP3 ISP device | ||
135 | * | ||
136 | * In order to force posting of pending writes, we need to write and | ||
137 | * readback the same register, in this case the revision register. | ||
138 | * | ||
139 | * See this link for reference: | ||
140 | * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html | ||
141 | */ | ||
142 | void omap3isp_flush(struct isp_device *isp) | ||
143 | { | ||
144 | isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); | ||
145 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * isp_enable_interrupts - Enable ISP interrupts. | ||
150 | * @isp: OMAP3 ISP device | ||
151 | */ | ||
152 | static void isp_enable_interrupts(struct isp_device *isp) | ||
153 | { | ||
154 | static const u32 irq = IRQ0ENABLE_CSIA_IRQ | ||
155 | | IRQ0ENABLE_CSIB_IRQ | ||
156 | | IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ | ||
157 | | IRQ0ENABLE_CCDC_LSC_DONE_IRQ | ||
158 | | IRQ0ENABLE_CCDC_VD0_IRQ | ||
159 | | IRQ0ENABLE_CCDC_VD1_IRQ | ||
160 | | IRQ0ENABLE_HS_VS_IRQ | ||
161 | | IRQ0ENABLE_HIST_DONE_IRQ | ||
162 | | IRQ0ENABLE_H3A_AWB_DONE_IRQ | ||
163 | | IRQ0ENABLE_H3A_AF_DONE_IRQ | ||
164 | | IRQ0ENABLE_PRV_DONE_IRQ | ||
165 | | IRQ0ENABLE_RSZ_DONE_IRQ; | ||
166 | |||
167 | isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); | ||
168 | isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); | ||
169 | } | ||
170 | |||
171 | /* | ||
172 | * isp_disable_interrupts - Disable ISP interrupts. | ||
173 | * @isp: OMAP3 ISP device | ||
174 | */ | ||
175 | static void isp_disable_interrupts(struct isp_device *isp) | ||
176 | { | ||
177 | isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); | ||
178 | } | ||
179 | |||
180 | /** | ||
181 | * isp_set_xclk - Configures the specified cam_xclk to the desired frequency. | ||
182 | * @isp: OMAP3 ISP device | ||
183 | * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high | ||
184 | * @xclksel: XCLK to configure (0 = A, 1 = B). | ||
185 | * | ||
186 | * Configures the specified MCLK divisor in the ISP timing control register | ||
187 | * (TCTRL_CTRL) to generate the desired xclk clock value. | ||
188 | * | ||
189 | * Divisor = cam_mclk_hz / xclk | ||
190 | * | ||
191 | * Returns the final frequency that is actually being generated | ||
192 | **/ | ||
193 | static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel) | ||
194 | { | ||
195 | u32 divisor; | ||
196 | u32 currentxclk; | ||
197 | unsigned long mclk_hz; | ||
198 | |||
199 | if (!omap3isp_get(isp)) | ||
200 | return 0; | ||
201 | |||
202 | mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); | ||
203 | |||
204 | if (xclk >= mclk_hz) { | ||
205 | divisor = ISPTCTRL_CTRL_DIV_BYPASS; | ||
206 | currentxclk = mclk_hz; | ||
207 | } else if (xclk >= 2) { | ||
208 | divisor = mclk_hz / xclk; | ||
209 | if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS) | ||
210 | divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1; | ||
211 | currentxclk = mclk_hz / divisor; | ||
212 | } else { | ||
213 | divisor = xclk; | ||
214 | currentxclk = 0; | ||
215 | } | ||
216 | |||
217 | switch (xclksel) { | ||
218 | case ISP_XCLK_A: | ||
219 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, | ||
220 | ISPTCTRL_CTRL_DIVA_MASK, | ||
221 | divisor << ISPTCTRL_CTRL_DIVA_SHIFT); | ||
222 | dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n", | ||
223 | currentxclk); | ||
224 | break; | ||
225 | case ISP_XCLK_B: | ||
226 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, | ||
227 | ISPTCTRL_CTRL_DIVB_MASK, | ||
228 | divisor << ISPTCTRL_CTRL_DIVB_SHIFT); | ||
229 | dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n", | ||
230 | currentxclk); | ||
231 | break; | ||
232 | case ISP_XCLK_NONE: | ||
233 | default: | ||
234 | omap3isp_put(isp); | ||
235 | dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested " | ||
236 | "xclk. Must be 0 (A) or 1 (B).\n"); | ||
237 | return -EINVAL; | ||
238 | } | ||
239 | |||
240 | /* Do we go from stable whatever to clock? */ | ||
241 | if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2) | ||
242 | omap3isp_get(isp); | ||
243 | /* Stopping the clock. */ | ||
244 | else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2) | ||
245 | omap3isp_put(isp); | ||
246 | |||
247 | isp->xclk_divisor[xclksel - 1] = divisor; | ||
248 | |||
249 | omap3isp_put(isp); | ||
250 | |||
251 | return currentxclk; | ||
252 | } | ||
253 | |||
254 | /* | ||
255 | * isp_power_settings - Sysconfig settings, for Power Management. | ||
256 | * @isp: OMAP3 ISP device | ||
257 | * @idle: Consider idle state. | ||
258 | * | ||
259 | * Sets the power settings for the ISP, and SBL bus. | ||
260 | */ | ||
261 | static void isp_power_settings(struct isp_device *isp, int idle) | ||
262 | { | ||
263 | isp_reg_writel(isp, | ||
264 | ((idle ? ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY : | ||
265 | ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY) << | ||
266 | ISP_SYSCONFIG_MIDLEMODE_SHIFT) | | ||
267 | ((isp->revision == ISP_REVISION_15_0) ? | ||
268 | ISP_SYSCONFIG_AUTOIDLE : 0), | ||
269 | OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); | ||
270 | |||
271 | if (isp->autoidle) | ||
272 | isp_reg_writel(isp, ISPCTRL_SBL_AUTOIDLE, OMAP3_ISP_IOMEM_MAIN, | ||
273 | ISP_CTRL); | ||
274 | } | ||
275 | |||
276 | /* | ||
277 | * Configure the bridge and lane shifter. Valid inputs are | ||
278 | * | ||
279 | * CCDC_INPUT_PARALLEL: Parallel interface | ||
280 | * CCDC_INPUT_CSI2A: CSI2a receiver | ||
281 | * CCDC_INPUT_CCP2B: CCP2b receiver | ||
282 | * CCDC_INPUT_CSI2C: CSI2c receiver | ||
283 | * | ||
284 | * The bridge and lane shifter are configured according to the selected input | ||
285 | * and the ISP platform data. | ||
286 | */ | ||
287 | void omap3isp_configure_bridge(struct isp_device *isp, | ||
288 | enum ccdc_input_entity input, | ||
289 | const struct isp_parallel_platform_data *pdata, | ||
290 | unsigned int shift) | ||
291 | { | ||
292 | u32 ispctrl_val; | ||
293 | |||
294 | ispctrl_val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); | ||
295 | ispctrl_val &= ~ISPCTRL_SHIFT_MASK; | ||
296 | ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV; | ||
297 | ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK; | ||
298 | ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK; | ||
299 | |||
300 | switch (input) { | ||
301 | case CCDC_INPUT_PARALLEL: | ||
302 | ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL; | ||
303 | ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT; | ||
304 | ispctrl_val |= pdata->bridge << ISPCTRL_PAR_BRIDGE_SHIFT; | ||
305 | shift += pdata->data_lane_shift * 2; | ||
306 | break; | ||
307 | |||
308 | case CCDC_INPUT_CSI2A: | ||
309 | ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA; | ||
310 | break; | ||
311 | |||
312 | case CCDC_INPUT_CCP2B: | ||
313 | ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB; | ||
314 | break; | ||
315 | |||
316 | case CCDC_INPUT_CSI2C: | ||
317 | ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIC; | ||
318 | break; | ||
319 | |||
320 | default: | ||
321 | return; | ||
322 | } | ||
323 | |||
324 | ispctrl_val |= ((shift/2) << ISPCTRL_SHIFT_SHIFT) & ISPCTRL_SHIFT_MASK; | ||
325 | |||
326 | ispctrl_val &= ~ISPCTRL_SYNC_DETECT_MASK; | ||
327 | ispctrl_val |= ISPCTRL_SYNC_DETECT_VSRISE; | ||
328 | |||
329 | isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); | ||
330 | } | ||
331 | |||
332 | /** | ||
333 | * isp_set_pixel_clock - Configures the ISP pixel clock | ||
334 | * @isp: OMAP3 ISP device | ||
335 | * @pixelclk: Average pixel clock in Hz | ||
336 | * | ||
337 | * Set the average pixel clock required by the sensor. The ISP will use the | ||
338 | * lowest possible memory bandwidth settings compatible with the clock. | ||
339 | **/ | ||
340 | static void isp_set_pixel_clock(struct isp_device *isp, unsigned int pixelclk) | ||
341 | { | ||
342 | isp->isp_ccdc.vpcfg.pixelclk = pixelclk; | ||
343 | } | ||
344 | |||
345 | void omap3isp_hist_dma_done(struct isp_device *isp) | ||
346 | { | ||
347 | if (omap3isp_ccdc_busy(&isp->isp_ccdc) || | ||
348 | omap3isp_stat_pcr_busy(&isp->isp_hist)) { | ||
349 | /* Histogram cannot be enabled in this frame anymore */ | ||
350 | atomic_set(&isp->isp_hist.buf_err, 1); | ||
351 | dev_dbg(isp->dev, "hist: Out of synchronization with " | ||
352 | "CCDC. Ignoring next buffer.\n"); | ||
353 | } | ||
354 | } | ||
355 | |||
356 | static inline void isp_isr_dbg(struct isp_device *isp, u32 irqstatus) | ||
357 | { | ||
358 | static const char *name[] = { | ||
359 | "CSIA_IRQ", | ||
360 | "res1", | ||
361 | "res2", | ||
362 | "CSIB_LCM_IRQ", | ||
363 | "CSIB_IRQ", | ||
364 | "res5", | ||
365 | "res6", | ||
366 | "res7", | ||
367 | "CCDC_VD0_IRQ", | ||
368 | "CCDC_VD1_IRQ", | ||
369 | "CCDC_VD2_IRQ", | ||
370 | "CCDC_ERR_IRQ", | ||
371 | "H3A_AF_DONE_IRQ", | ||
372 | "H3A_AWB_DONE_IRQ", | ||
373 | "res14", | ||
374 | "res15", | ||
375 | "HIST_DONE_IRQ", | ||
376 | "CCDC_LSC_DONE", | ||
377 | "CCDC_LSC_PREFETCH_COMPLETED", | ||
378 | "CCDC_LSC_PREFETCH_ERROR", | ||
379 | "PRV_DONE_IRQ", | ||
380 | "CBUFF_IRQ", | ||
381 | "res22", | ||
382 | "res23", | ||
383 | "RSZ_DONE_IRQ", | ||
384 | "OVF_IRQ", | ||
385 | "res26", | ||
386 | "res27", | ||
387 | "MMU_ERR_IRQ", | ||
388 | "OCP_ERR_IRQ", | ||
389 | "SEC_ERR_IRQ", | ||
390 | "HS_VS_IRQ", | ||
391 | }; | ||
392 | int i; | ||
393 | |||
394 | dev_dbg(isp->dev, "ISP IRQ: "); | ||
395 | |||
396 | for (i = 0; i < ARRAY_SIZE(name); i++) { | ||
397 | if ((1 << i) & irqstatus) | ||
398 | printk(KERN_CONT "%s ", name[i]); | ||
399 | } | ||
400 | printk(KERN_CONT "\n"); | ||
401 | } | ||
402 | |||
403 | static void isp_isr_sbl(struct isp_device *isp) | ||
404 | { | ||
405 | struct device *dev = isp->dev; | ||
406 | u32 sbl_pcr; | ||
407 | |||
408 | /* | ||
409 | * Handle shared buffer logic overflows for video buffers. | ||
410 | * ISPSBL_PCR_CCDCPRV_2_RSZ_OVF can be safely ignored. | ||
411 | */ | ||
412 | sbl_pcr = isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); | ||
413 | isp_reg_writel(isp, sbl_pcr, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); | ||
414 | sbl_pcr &= ~ISPSBL_PCR_CCDCPRV_2_RSZ_OVF; | ||
415 | |||
416 | if (sbl_pcr) | ||
417 | dev_dbg(dev, "SBL overflow (PCR = 0x%08x)\n", sbl_pcr); | ||
418 | |||
419 | if (sbl_pcr & (ISPSBL_PCR_CCDC_WBL_OVF | ISPSBL_PCR_CSIA_WBL_OVF | ||
420 | | ISPSBL_PCR_CSIB_WBL_OVF)) { | ||
421 | isp->isp_ccdc.error = 1; | ||
422 | if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW) | ||
423 | isp->isp_prev.error = 1; | ||
424 | if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER) | ||
425 | isp->isp_res.error = 1; | ||
426 | } | ||
427 | |||
428 | if (sbl_pcr & ISPSBL_PCR_PRV_WBL_OVF) { | ||
429 | isp->isp_prev.error = 1; | ||
430 | if (isp->isp_res.input == RESIZER_INPUT_VP && | ||
431 | !(isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER)) | ||
432 | isp->isp_res.error = 1; | ||
433 | } | ||
434 | |||
435 | if (sbl_pcr & (ISPSBL_PCR_RSZ1_WBL_OVF | ||
436 | | ISPSBL_PCR_RSZ2_WBL_OVF | ||
437 | | ISPSBL_PCR_RSZ3_WBL_OVF | ||
438 | | ISPSBL_PCR_RSZ4_WBL_OVF)) | ||
439 | isp->isp_res.error = 1; | ||
440 | |||
441 | if (sbl_pcr & ISPSBL_PCR_H3A_AF_WBL_OVF) | ||
442 | omap3isp_stat_sbl_overflow(&isp->isp_af); | ||
443 | |||
444 | if (sbl_pcr & ISPSBL_PCR_H3A_AEAWB_WBL_OVF) | ||
445 | omap3isp_stat_sbl_overflow(&isp->isp_aewb); | ||
446 | } | ||
447 | |||
448 | /* | ||
449 | * isp_isr - Interrupt Service Routine for Camera ISP module. | ||
450 | * @irq: Not used currently. | ||
451 | * @_isp: Pointer to the OMAP3 ISP device | ||
452 | * | ||
453 | * Handles the corresponding callback if plugged in. | ||
454 | * | ||
455 | * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the | ||
456 | * IRQ wasn't handled. | ||
457 | */ | ||
458 | static irqreturn_t isp_isr(int irq, void *_isp) | ||
459 | { | ||
460 | static const u32 ccdc_events = IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ | | ||
461 | IRQ0STATUS_CCDC_LSC_DONE_IRQ | | ||
462 | IRQ0STATUS_CCDC_VD0_IRQ | | ||
463 | IRQ0STATUS_CCDC_VD1_IRQ | | ||
464 | IRQ0STATUS_HS_VS_IRQ; | ||
465 | struct isp_device *isp = _isp; | ||
466 | u32 irqstatus; | ||
467 | int ret; | ||
468 | |||
469 | irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); | ||
470 | isp_reg_writel(isp, irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); | ||
471 | |||
472 | isp_isr_sbl(isp); | ||
473 | |||
474 | if (irqstatus & IRQ0STATUS_CSIA_IRQ) { | ||
475 | ret = omap3isp_csi2_isr(&isp->isp_csi2a); | ||
476 | if (ret) | ||
477 | isp->isp_ccdc.error = 1; | ||
478 | } | ||
479 | |||
480 | if (irqstatus & IRQ0STATUS_CSIB_IRQ) { | ||
481 | ret = omap3isp_ccp2_isr(&isp->isp_ccp2); | ||
482 | if (ret) | ||
483 | isp->isp_ccdc.error = 1; | ||
484 | } | ||
485 | |||
486 | if (irqstatus & IRQ0STATUS_CCDC_VD0_IRQ) { | ||
487 | if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW) | ||
488 | omap3isp_preview_isr_frame_sync(&isp->isp_prev); | ||
489 | if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER) | ||
490 | omap3isp_resizer_isr_frame_sync(&isp->isp_res); | ||
491 | omap3isp_stat_isr_frame_sync(&isp->isp_aewb); | ||
492 | omap3isp_stat_isr_frame_sync(&isp->isp_af); | ||
493 | omap3isp_stat_isr_frame_sync(&isp->isp_hist); | ||
494 | } | ||
495 | |||
496 | if (irqstatus & ccdc_events) | ||
497 | omap3isp_ccdc_isr(&isp->isp_ccdc, irqstatus & ccdc_events); | ||
498 | |||
499 | if (irqstatus & IRQ0STATUS_PRV_DONE_IRQ) { | ||
500 | if (isp->isp_prev.output & PREVIEW_OUTPUT_RESIZER) | ||
501 | omap3isp_resizer_isr_frame_sync(&isp->isp_res); | ||
502 | omap3isp_preview_isr(&isp->isp_prev); | ||
503 | } | ||
504 | |||
505 | if (irqstatus & IRQ0STATUS_RSZ_DONE_IRQ) | ||
506 | omap3isp_resizer_isr(&isp->isp_res); | ||
507 | |||
508 | if (irqstatus & IRQ0STATUS_H3A_AWB_DONE_IRQ) | ||
509 | omap3isp_stat_isr(&isp->isp_aewb); | ||
510 | |||
511 | if (irqstatus & IRQ0STATUS_H3A_AF_DONE_IRQ) | ||
512 | omap3isp_stat_isr(&isp->isp_af); | ||
513 | |||
514 | if (irqstatus & IRQ0STATUS_HIST_DONE_IRQ) | ||
515 | omap3isp_stat_isr(&isp->isp_hist); | ||
516 | |||
517 | omap3isp_flush(isp); | ||
518 | |||
519 | #if defined(DEBUG) && defined(ISP_ISR_DEBUG) | ||
520 | isp_isr_dbg(isp, irqstatus); | ||
521 | #endif | ||
522 | |||
523 | return IRQ_HANDLED; | ||
524 | } | ||
525 | |||
526 | /* ----------------------------------------------------------------------------- | ||
527 | * Pipeline power management | ||
528 | * | ||
529 | * Entities must be powered up when part of a pipeline that contains at least | ||
530 | * one open video device node. | ||
531 | * | ||
532 | * To achieve this use the entity use_count field to track the number of users. | ||
533 | * For entities corresponding to video device nodes the use_count field stores | ||
534 | * the users count of the node. For entities corresponding to subdevs the | ||
535 | * use_count field stores the total number of users of all video device nodes | ||
536 | * in the pipeline. | ||
537 | * | ||
538 | * The omap3isp_pipeline_pm_use() function must be called in the open() and | ||
539 | * close() handlers of video device nodes. It increments or decrements the use | ||
540 | * count of all subdev entities in the pipeline. | ||
541 | * | ||
542 | * To react to link management on powered pipelines, the link setup notification | ||
543 | * callback updates the use count of all entities in the source and sink sides | ||
544 | * of the link. | ||
545 | */ | ||
546 | |||
547 | /* | ||
548 | * isp_pipeline_pm_use_count - Count the number of users of a pipeline | ||
549 | * @entity: The entity | ||
550 | * | ||
551 | * Return the total number of users of all video device nodes in the pipeline. | ||
552 | */ | ||
553 | static int isp_pipeline_pm_use_count(struct media_entity *entity) | ||
554 | { | ||
555 | struct media_entity_graph graph; | ||
556 | int use = 0; | ||
557 | |||
558 | media_entity_graph_walk_start(&graph, entity); | ||
559 | |||
560 | while ((entity = media_entity_graph_walk_next(&graph))) { | ||
561 | if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) | ||
562 | use += entity->use_count; | ||
563 | } | ||
564 | |||
565 | return use; | ||
566 | } | ||
567 | |||
568 | /* | ||
569 | * isp_pipeline_pm_power_one - Apply power change to an entity | ||
570 | * @entity: The entity | ||
571 | * @change: Use count change | ||
572 | * | ||
573 | * Change the entity use count by @change. If the entity is a subdev update its | ||
574 | * power state by calling the core::s_power operation when the use count goes | ||
575 | * from 0 to != 0 or from != 0 to 0. | ||
576 | * | ||
577 | * Return 0 on success or a negative error code on failure. | ||
578 | */ | ||
579 | static int isp_pipeline_pm_power_one(struct media_entity *entity, int change) | ||
580 | { | ||
581 | struct v4l2_subdev *subdev; | ||
582 | int ret; | ||
583 | |||
584 | subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV | ||
585 | ? media_entity_to_v4l2_subdev(entity) : NULL; | ||
586 | |||
587 | if (entity->use_count == 0 && change > 0 && subdev != NULL) { | ||
588 | ret = v4l2_subdev_call(subdev, core, s_power, 1); | ||
589 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
590 | return ret; | ||
591 | } | ||
592 | |||
593 | entity->use_count += change; | ||
594 | WARN_ON(entity->use_count < 0); | ||
595 | |||
596 | if (entity->use_count == 0 && change < 0 && subdev != NULL) | ||
597 | v4l2_subdev_call(subdev, core, s_power, 0); | ||
598 | |||
599 | return 0; | ||
600 | } | ||
601 | |||
602 | /* | ||
603 | * isp_pipeline_pm_power - Apply power change to all entities in a pipeline | ||
604 | * @entity: The entity | ||
605 | * @change: Use count change | ||
606 | * | ||
607 | * Walk the pipeline to update the use count and the power state of all non-node | ||
608 | * entities. | ||
609 | * | ||
610 | * Return 0 on success or a negative error code on failure. | ||
611 | */ | ||
612 | static int isp_pipeline_pm_power(struct media_entity *entity, int change) | ||
613 | { | ||
614 | struct media_entity_graph graph; | ||
615 | struct media_entity *first = entity; | ||
616 | int ret = 0; | ||
617 | |||
618 | if (!change) | ||
619 | return 0; | ||
620 | |||
621 | media_entity_graph_walk_start(&graph, entity); | ||
622 | |||
623 | while (!ret && (entity = media_entity_graph_walk_next(&graph))) | ||
624 | if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) | ||
625 | ret = isp_pipeline_pm_power_one(entity, change); | ||
626 | |||
627 | if (!ret) | ||
628 | return 0; | ||
629 | |||
630 | media_entity_graph_walk_start(&graph, first); | ||
631 | |||
632 | while ((first = media_entity_graph_walk_next(&graph)) | ||
633 | && first != entity) | ||
634 | if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE) | ||
635 | isp_pipeline_pm_power_one(first, -change); | ||
636 | |||
637 | return ret; | ||
638 | } | ||
639 | |||
640 | /* | ||
641 | * omap3isp_pipeline_pm_use - Update the use count of an entity | ||
642 | * @entity: The entity | ||
643 | * @use: Use (1) or stop using (0) the entity | ||
644 | * | ||
645 | * Update the use count of all entities in the pipeline and power entities on or | ||
646 | * off accordingly. | ||
647 | * | ||
648 | * Return 0 on success or a negative error code on failure. Powering entities | ||
649 | * off is assumed to never fail. No failure can occur when the use parameter is | ||
650 | * set to 0. | ||
651 | */ | ||
652 | int omap3isp_pipeline_pm_use(struct media_entity *entity, int use) | ||
653 | { | ||
654 | int change = use ? 1 : -1; | ||
655 | int ret; | ||
656 | |||
657 | mutex_lock(&entity->parent->graph_mutex); | ||
658 | |||
659 | /* Apply use count to node. */ | ||
660 | entity->use_count += change; | ||
661 | WARN_ON(entity->use_count < 0); | ||
662 | |||
663 | /* Apply power change to connected non-nodes. */ | ||
664 | ret = isp_pipeline_pm_power(entity, change); | ||
665 | if (ret < 0) | ||
666 | entity->use_count -= change; | ||
667 | |||
668 | mutex_unlock(&entity->parent->graph_mutex); | ||
669 | |||
670 | return ret; | ||
671 | } | ||
672 | |||
673 | /* | ||
674 | * isp_pipeline_link_notify - Link management notification callback | ||
675 | * @source: Pad at the start of the link | ||
676 | * @sink: Pad at the end of the link | ||
677 | * @flags: New link flags that will be applied | ||
678 | * | ||
679 | * React to link management on powered pipelines by updating the use count of | ||
680 | * all entities in the source and sink sides of the link. Entities are powered | ||
681 | * on or off accordingly. | ||
682 | * | ||
683 | * Return 0 on success or a negative error code on failure. Powering entities | ||
684 | * off is assumed to never fail. This function will not fail for disconnection | ||
685 | * events. | ||
686 | */ | ||
687 | static int isp_pipeline_link_notify(struct media_pad *source, | ||
688 | struct media_pad *sink, u32 flags) | ||
689 | { | ||
690 | int source_use = isp_pipeline_pm_use_count(source->entity); | ||
691 | int sink_use = isp_pipeline_pm_use_count(sink->entity); | ||
692 | int ret; | ||
693 | |||
694 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
695 | /* Powering off entities is assumed to never fail. */ | ||
696 | isp_pipeline_pm_power(source->entity, -sink_use); | ||
697 | isp_pipeline_pm_power(sink->entity, -source_use); | ||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | ret = isp_pipeline_pm_power(source->entity, sink_use); | ||
702 | if (ret < 0) | ||
703 | return ret; | ||
704 | |||
705 | ret = isp_pipeline_pm_power(sink->entity, source_use); | ||
706 | if (ret < 0) | ||
707 | isp_pipeline_pm_power(source->entity, -sink_use); | ||
708 | |||
709 | return ret; | ||
710 | } | ||
711 | |||
712 | /* ----------------------------------------------------------------------------- | ||
713 | * Pipeline stream management | ||
714 | */ | ||
715 | |||
716 | /* | ||
717 | * isp_pipeline_enable - Enable streaming on a pipeline | ||
718 | * @pipe: ISP pipeline | ||
719 | * @mode: Stream mode (single shot or continuous) | ||
720 | * | ||
721 | * Walk the entities chain starting at the pipeline output video node and start | ||
722 | * all modules in the chain in the given mode. | ||
723 | * | ||
724 | * Return 0 if successful, or the return value of the failed video::s_stream | ||
725 | * operation otherwise. | ||
726 | */ | ||
727 | static int isp_pipeline_enable(struct isp_pipeline *pipe, | ||
728 | enum isp_pipeline_stream_state mode) | ||
729 | { | ||
730 | struct isp_device *isp = pipe->output->isp; | ||
731 | struct media_entity *entity; | ||
732 | struct media_pad *pad; | ||
733 | struct v4l2_subdev *subdev; | ||
734 | unsigned long flags; | ||
735 | int ret = 0; | ||
736 | |||
737 | spin_lock_irqsave(&pipe->lock, flags); | ||
738 | pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT); | ||
739 | spin_unlock_irqrestore(&pipe->lock, flags); | ||
740 | |||
741 | pipe->do_propagation = false; | ||
742 | |||
743 | entity = &pipe->output->video.entity; | ||
744 | while (1) { | ||
745 | pad = &entity->pads[0]; | ||
746 | if (!(pad->flags & MEDIA_PAD_FL_SINK)) | ||
747 | break; | ||
748 | |||
749 | pad = media_entity_remote_source(pad); | ||
750 | if (pad == NULL || | ||
751 | media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
752 | break; | ||
753 | |||
754 | entity = pad->entity; | ||
755 | subdev = media_entity_to_v4l2_subdev(entity); | ||
756 | |||
757 | ret = v4l2_subdev_call(subdev, video, s_stream, mode); | ||
758 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
759 | break; | ||
760 | |||
761 | if (subdev == &isp->isp_ccdc.subdev) { | ||
762 | v4l2_subdev_call(&isp->isp_aewb.subdev, video, | ||
763 | s_stream, mode); | ||
764 | v4l2_subdev_call(&isp->isp_af.subdev, video, | ||
765 | s_stream, mode); | ||
766 | v4l2_subdev_call(&isp->isp_hist.subdev, video, | ||
767 | s_stream, mode); | ||
768 | pipe->do_propagation = true; | ||
769 | } | ||
770 | } | ||
771 | |||
772 | /* Frame number propagation. In continuous streaming mode the number | ||
773 | * is incremented in the frame start ISR. In mem-to-mem mode | ||
774 | * singleshot is used and frame start IRQs are not available. | ||
775 | * Thus we have to increment the number here. | ||
776 | */ | ||
777 | if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT) | ||
778 | atomic_inc(&pipe->frame_number); | ||
779 | |||
780 | return ret; | ||
781 | } | ||
782 | |||
783 | static int isp_pipeline_wait_resizer(struct isp_device *isp) | ||
784 | { | ||
785 | return omap3isp_resizer_busy(&isp->isp_res); | ||
786 | } | ||
787 | |||
788 | static int isp_pipeline_wait_preview(struct isp_device *isp) | ||
789 | { | ||
790 | return omap3isp_preview_busy(&isp->isp_prev); | ||
791 | } | ||
792 | |||
793 | static int isp_pipeline_wait_ccdc(struct isp_device *isp) | ||
794 | { | ||
795 | return omap3isp_stat_busy(&isp->isp_af) | ||
796 | || omap3isp_stat_busy(&isp->isp_aewb) | ||
797 | || omap3isp_stat_busy(&isp->isp_hist) | ||
798 | || omap3isp_ccdc_busy(&isp->isp_ccdc); | ||
799 | } | ||
800 | |||
801 | #define ISP_STOP_TIMEOUT msecs_to_jiffies(1000) | ||
802 | |||
803 | static int isp_pipeline_wait(struct isp_device *isp, | ||
804 | int(*busy)(struct isp_device *isp)) | ||
805 | { | ||
806 | unsigned long timeout = jiffies + ISP_STOP_TIMEOUT; | ||
807 | |||
808 | while (!time_after(jiffies, timeout)) { | ||
809 | if (!busy(isp)) | ||
810 | return 0; | ||
811 | } | ||
812 | |||
813 | return 1; | ||
814 | } | ||
815 | |||
816 | /* | ||
817 | * isp_pipeline_disable - Disable streaming on a pipeline | ||
818 | * @pipe: ISP pipeline | ||
819 | * | ||
820 | * Walk the entities chain starting at the pipeline output video node and stop | ||
821 | * all modules in the chain. Wait synchronously for the modules to be stopped if | ||
822 | * necessary. | ||
823 | * | ||
824 | * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module | ||
825 | * can't be stopped (in which case a software reset of the ISP is probably | ||
826 | * necessary). | ||
827 | */ | ||
828 | static int isp_pipeline_disable(struct isp_pipeline *pipe) | ||
829 | { | ||
830 | struct isp_device *isp = pipe->output->isp; | ||
831 | struct media_entity *entity; | ||
832 | struct media_pad *pad; | ||
833 | struct v4l2_subdev *subdev; | ||
834 | int failure = 0; | ||
835 | int ret; | ||
836 | |||
837 | /* | ||
838 | * We need to stop all the modules after CCDC first or they'll | ||
839 | * never stop since they may not get a full frame from CCDC. | ||
840 | */ | ||
841 | entity = &pipe->output->video.entity; | ||
842 | while (1) { | ||
843 | pad = &entity->pads[0]; | ||
844 | if (!(pad->flags & MEDIA_PAD_FL_SINK)) | ||
845 | break; | ||
846 | |||
847 | pad = media_entity_remote_source(pad); | ||
848 | if (pad == NULL || | ||
849 | media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
850 | break; | ||
851 | |||
852 | entity = pad->entity; | ||
853 | subdev = media_entity_to_v4l2_subdev(entity); | ||
854 | |||
855 | if (subdev == &isp->isp_ccdc.subdev) { | ||
856 | v4l2_subdev_call(&isp->isp_aewb.subdev, | ||
857 | video, s_stream, 0); | ||
858 | v4l2_subdev_call(&isp->isp_af.subdev, | ||
859 | video, s_stream, 0); | ||
860 | v4l2_subdev_call(&isp->isp_hist.subdev, | ||
861 | video, s_stream, 0); | ||
862 | } | ||
863 | |||
864 | v4l2_subdev_call(subdev, video, s_stream, 0); | ||
865 | |||
866 | if (subdev == &isp->isp_res.subdev) | ||
867 | ret = isp_pipeline_wait(isp, isp_pipeline_wait_resizer); | ||
868 | else if (subdev == &isp->isp_prev.subdev) | ||
869 | ret = isp_pipeline_wait(isp, isp_pipeline_wait_preview); | ||
870 | else if (subdev == &isp->isp_ccdc.subdev) | ||
871 | ret = isp_pipeline_wait(isp, isp_pipeline_wait_ccdc); | ||
872 | else | ||
873 | ret = 0; | ||
874 | |||
875 | if (ret) { | ||
876 | dev_info(isp->dev, "Unable to stop %s\n", subdev->name); | ||
877 | failure = -ETIMEDOUT; | ||
878 | } | ||
879 | } | ||
880 | |||
881 | if (failure < 0) | ||
882 | isp->needs_reset = true; | ||
883 | |||
884 | return failure; | ||
885 | } | ||
886 | |||
887 | /* | ||
888 | * omap3isp_pipeline_set_stream - Enable/disable streaming on a pipeline | ||
889 | * @pipe: ISP pipeline | ||
890 | * @state: Stream state (stopped, single shot or continuous) | ||
891 | * | ||
892 | * Set the pipeline to the given stream state. Pipelines can be started in | ||
893 | * single-shot or continuous mode. | ||
894 | * | ||
895 | * Return 0 if successful, or the return value of the failed video::s_stream | ||
896 | * operation otherwise. The pipeline state is not updated when the operation | ||
897 | * fails, except when stopping the pipeline. | ||
898 | */ | ||
899 | int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, | ||
900 | enum isp_pipeline_stream_state state) | ||
901 | { | ||
902 | int ret; | ||
903 | |||
904 | if (state == ISP_PIPELINE_STREAM_STOPPED) | ||
905 | ret = isp_pipeline_disable(pipe); | ||
906 | else | ||
907 | ret = isp_pipeline_enable(pipe, state); | ||
908 | |||
909 | if (ret == 0 || state == ISP_PIPELINE_STREAM_STOPPED) | ||
910 | pipe->stream_state = state; | ||
911 | |||
912 | return ret; | ||
913 | } | ||
914 | |||
915 | /* | ||
916 | * isp_pipeline_resume - Resume streaming on a pipeline | ||
917 | * @pipe: ISP pipeline | ||
918 | * | ||
919 | * Resume video output and input and re-enable pipeline. | ||
920 | */ | ||
921 | static void isp_pipeline_resume(struct isp_pipeline *pipe) | ||
922 | { | ||
923 | int singleshot = pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT; | ||
924 | |||
925 | omap3isp_video_resume(pipe->output, !singleshot); | ||
926 | if (singleshot) | ||
927 | omap3isp_video_resume(pipe->input, 0); | ||
928 | isp_pipeline_enable(pipe, pipe->stream_state); | ||
929 | } | ||
930 | |||
931 | /* | ||
932 | * isp_pipeline_suspend - Suspend streaming on a pipeline | ||
933 | * @pipe: ISP pipeline | ||
934 | * | ||
935 | * Suspend pipeline. | ||
936 | */ | ||
937 | static void isp_pipeline_suspend(struct isp_pipeline *pipe) | ||
938 | { | ||
939 | isp_pipeline_disable(pipe); | ||
940 | } | ||
941 | |||
942 | /* | ||
943 | * isp_pipeline_is_last - Verify if entity has an enabled link to the output | ||
944 | * video node | ||
945 | * @me: ISP module's media entity | ||
946 | * | ||
947 | * Returns 1 if the entity has an enabled link to the output video node or 0 | ||
948 | * otherwise. It's true only while pipeline can have no more than one output | ||
949 | * node. | ||
950 | */ | ||
951 | static int isp_pipeline_is_last(struct media_entity *me) | ||
952 | { | ||
953 | struct isp_pipeline *pipe; | ||
954 | struct media_pad *pad; | ||
955 | |||
956 | if (!me->pipe) | ||
957 | return 0; | ||
958 | pipe = to_isp_pipeline(me); | ||
959 | if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED) | ||
960 | return 0; | ||
961 | pad = media_entity_remote_source(&pipe->output->pad); | ||
962 | return pad->entity == me; | ||
963 | } | ||
964 | |||
965 | /* | ||
966 | * isp_suspend_module_pipeline - Suspend pipeline to which belongs the module | ||
967 | * @me: ISP module's media entity | ||
968 | * | ||
969 | * Suspend the whole pipeline if module's entity has an enabled link to the | ||
970 | * output video node. It works only while pipeline can have no more than one | ||
971 | * output node. | ||
972 | */ | ||
973 | static void isp_suspend_module_pipeline(struct media_entity *me) | ||
974 | { | ||
975 | if (isp_pipeline_is_last(me)) | ||
976 | isp_pipeline_suspend(to_isp_pipeline(me)); | ||
977 | } | ||
978 | |||
979 | /* | ||
980 | * isp_resume_module_pipeline - Resume pipeline to which belongs the module | ||
981 | * @me: ISP module's media entity | ||
982 | * | ||
983 | * Resume the whole pipeline if module's entity has an enabled link to the | ||
984 | * output video node. It works only while pipeline can have no more than one | ||
985 | * output node. | ||
986 | */ | ||
987 | static void isp_resume_module_pipeline(struct media_entity *me) | ||
988 | { | ||
989 | if (isp_pipeline_is_last(me)) | ||
990 | isp_pipeline_resume(to_isp_pipeline(me)); | ||
991 | } | ||
992 | |||
993 | /* | ||
994 | * isp_suspend_modules - Suspend ISP submodules. | ||
995 | * @isp: OMAP3 ISP device | ||
996 | * | ||
997 | * Returns 0 if suspend left in idle state all the submodules properly, | ||
998 | * or returns 1 if a general Reset is required to suspend the submodules. | ||
999 | */ | ||
1000 | static int isp_suspend_modules(struct isp_device *isp) | ||
1001 | { | ||
1002 | unsigned long timeout; | ||
1003 | |||
1004 | omap3isp_stat_suspend(&isp->isp_aewb); | ||
1005 | omap3isp_stat_suspend(&isp->isp_af); | ||
1006 | omap3isp_stat_suspend(&isp->isp_hist); | ||
1007 | isp_suspend_module_pipeline(&isp->isp_res.subdev.entity); | ||
1008 | isp_suspend_module_pipeline(&isp->isp_prev.subdev.entity); | ||
1009 | isp_suspend_module_pipeline(&isp->isp_ccdc.subdev.entity); | ||
1010 | isp_suspend_module_pipeline(&isp->isp_csi2a.subdev.entity); | ||
1011 | isp_suspend_module_pipeline(&isp->isp_ccp2.subdev.entity); | ||
1012 | |||
1013 | timeout = jiffies + ISP_STOP_TIMEOUT; | ||
1014 | while (omap3isp_stat_busy(&isp->isp_af) | ||
1015 | || omap3isp_stat_busy(&isp->isp_aewb) | ||
1016 | || omap3isp_stat_busy(&isp->isp_hist) | ||
1017 | || omap3isp_preview_busy(&isp->isp_prev) | ||
1018 | || omap3isp_resizer_busy(&isp->isp_res) | ||
1019 | || omap3isp_ccdc_busy(&isp->isp_ccdc)) { | ||
1020 | if (time_after(jiffies, timeout)) { | ||
1021 | dev_info(isp->dev, "can't stop modules.\n"); | ||
1022 | return 1; | ||
1023 | } | ||
1024 | msleep(1); | ||
1025 | } | ||
1026 | |||
1027 | return 0; | ||
1028 | } | ||
1029 | |||
1030 | /* | ||
1031 | * isp_resume_modules - Resume ISP submodules. | ||
1032 | * @isp: OMAP3 ISP device | ||
1033 | */ | ||
1034 | static void isp_resume_modules(struct isp_device *isp) | ||
1035 | { | ||
1036 | omap3isp_stat_resume(&isp->isp_aewb); | ||
1037 | omap3isp_stat_resume(&isp->isp_af); | ||
1038 | omap3isp_stat_resume(&isp->isp_hist); | ||
1039 | isp_resume_module_pipeline(&isp->isp_res.subdev.entity); | ||
1040 | isp_resume_module_pipeline(&isp->isp_prev.subdev.entity); | ||
1041 | isp_resume_module_pipeline(&isp->isp_ccdc.subdev.entity); | ||
1042 | isp_resume_module_pipeline(&isp->isp_csi2a.subdev.entity); | ||
1043 | isp_resume_module_pipeline(&isp->isp_ccp2.subdev.entity); | ||
1044 | } | ||
1045 | |||
1046 | /* | ||
1047 | * isp_reset - Reset ISP with a timeout wait for idle. | ||
1048 | * @isp: OMAP3 ISP device | ||
1049 | */ | ||
1050 | static int isp_reset(struct isp_device *isp) | ||
1051 | { | ||
1052 | unsigned long timeout = 0; | ||
1053 | |||
1054 | isp_reg_writel(isp, | ||
1055 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG) | ||
1056 | | ISP_SYSCONFIG_SOFTRESET, | ||
1057 | OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); | ||
1058 | while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, | ||
1059 | ISP_SYSSTATUS) & 0x1)) { | ||
1060 | if (timeout++ > 10000) { | ||
1061 | dev_alert(isp->dev, "cannot reset ISP\n"); | ||
1062 | return -ETIMEDOUT; | ||
1063 | } | ||
1064 | udelay(1); | ||
1065 | } | ||
1066 | |||
1067 | return 0; | ||
1068 | } | ||
1069 | |||
1070 | /* | ||
1071 | * isp_save_context - Saves the values of the ISP module registers. | ||
1072 | * @isp: OMAP3 ISP device | ||
1073 | * @reg_list: Structure containing pairs of register address and value to | ||
1074 | * modify on OMAP. | ||
1075 | */ | ||
1076 | static void | ||
1077 | isp_save_context(struct isp_device *isp, struct isp_reg *reg_list) | ||
1078 | { | ||
1079 | struct isp_reg *next = reg_list; | ||
1080 | |||
1081 | for (; next->reg != ISP_TOK_TERM; next++) | ||
1082 | next->val = isp_reg_readl(isp, next->mmio_range, next->reg); | ||
1083 | } | ||
1084 | |||
1085 | /* | ||
1086 | * isp_restore_context - Restores the values of the ISP module registers. | ||
1087 | * @isp: OMAP3 ISP device | ||
1088 | * @reg_list: Structure containing pairs of register address and value to | ||
1089 | * modify on OMAP. | ||
1090 | */ | ||
1091 | static void | ||
1092 | isp_restore_context(struct isp_device *isp, struct isp_reg *reg_list) | ||
1093 | { | ||
1094 | struct isp_reg *next = reg_list; | ||
1095 | |||
1096 | for (; next->reg != ISP_TOK_TERM; next++) | ||
1097 | isp_reg_writel(isp, next->val, next->mmio_range, next->reg); | ||
1098 | } | ||
1099 | |||
1100 | /* | ||
1101 | * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. | ||
1102 | * @isp: OMAP3 ISP device | ||
1103 | * | ||
1104 | * Routine for saving the context of each module in the ISP. | ||
1105 | * CCDC, HIST, H3A, PREV, RESZ and MMU. | ||
1106 | */ | ||
1107 | static void isp_save_ctx(struct isp_device *isp) | ||
1108 | { | ||
1109 | isp_save_context(isp, isp_reg_list); | ||
1110 | omap_iommu_save_ctx(isp->dev); | ||
1111 | } | ||
1112 | |||
1113 | /* | ||
1114 | * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. | ||
1115 | * @isp: OMAP3 ISP device | ||
1116 | * | ||
1117 | * Routine for restoring the context of each module in the ISP. | ||
1118 | * CCDC, HIST, H3A, PREV, RESZ and MMU. | ||
1119 | */ | ||
1120 | static void isp_restore_ctx(struct isp_device *isp) | ||
1121 | { | ||
1122 | isp_restore_context(isp, isp_reg_list); | ||
1123 | omap_iommu_restore_ctx(isp->dev); | ||
1124 | omap3isp_ccdc_restore_context(isp); | ||
1125 | omap3isp_preview_restore_context(isp); | ||
1126 | } | ||
1127 | |||
1128 | /* ----------------------------------------------------------------------------- | ||
1129 | * SBL resources management | ||
1130 | */ | ||
1131 | #define OMAP3_ISP_SBL_READ (OMAP3_ISP_SBL_CSI1_READ | \ | ||
1132 | OMAP3_ISP_SBL_CCDC_LSC_READ | \ | ||
1133 | OMAP3_ISP_SBL_PREVIEW_READ | \ | ||
1134 | OMAP3_ISP_SBL_RESIZER_READ) | ||
1135 | #define OMAP3_ISP_SBL_WRITE (OMAP3_ISP_SBL_CSI1_WRITE | \ | ||
1136 | OMAP3_ISP_SBL_CSI2A_WRITE | \ | ||
1137 | OMAP3_ISP_SBL_CSI2C_WRITE | \ | ||
1138 | OMAP3_ISP_SBL_CCDC_WRITE | \ | ||
1139 | OMAP3_ISP_SBL_PREVIEW_WRITE) | ||
1140 | |||
1141 | void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res) | ||
1142 | { | ||
1143 | u32 sbl = 0; | ||
1144 | |||
1145 | isp->sbl_resources |= res; | ||
1146 | |||
1147 | if (isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ) | ||
1148 | sbl |= ISPCTRL_SBL_SHARED_RPORTA; | ||
1149 | |||
1150 | if (isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ) | ||
1151 | sbl |= ISPCTRL_SBL_SHARED_RPORTB; | ||
1152 | |||
1153 | if (isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE) | ||
1154 | sbl |= ISPCTRL_SBL_SHARED_WPORTC; | ||
1155 | |||
1156 | if (isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE) | ||
1157 | sbl |= ISPCTRL_SBL_WR0_RAM_EN; | ||
1158 | |||
1159 | if (isp->sbl_resources & OMAP3_ISP_SBL_WRITE) | ||
1160 | sbl |= ISPCTRL_SBL_WR1_RAM_EN; | ||
1161 | |||
1162 | if (isp->sbl_resources & OMAP3_ISP_SBL_READ) | ||
1163 | sbl |= ISPCTRL_SBL_RD_RAM_EN; | ||
1164 | |||
1165 | isp_reg_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl); | ||
1166 | } | ||
1167 | |||
1168 | void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res) | ||
1169 | { | ||
1170 | u32 sbl = 0; | ||
1171 | |||
1172 | isp->sbl_resources &= ~res; | ||
1173 | |||
1174 | if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ)) | ||
1175 | sbl |= ISPCTRL_SBL_SHARED_RPORTA; | ||
1176 | |||
1177 | if (!(isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ)) | ||
1178 | sbl |= ISPCTRL_SBL_SHARED_RPORTB; | ||
1179 | |||
1180 | if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE)) | ||
1181 | sbl |= ISPCTRL_SBL_SHARED_WPORTC; | ||
1182 | |||
1183 | if (!(isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE)) | ||
1184 | sbl |= ISPCTRL_SBL_WR0_RAM_EN; | ||
1185 | |||
1186 | if (!(isp->sbl_resources & OMAP3_ISP_SBL_WRITE)) | ||
1187 | sbl |= ISPCTRL_SBL_WR1_RAM_EN; | ||
1188 | |||
1189 | if (!(isp->sbl_resources & OMAP3_ISP_SBL_READ)) | ||
1190 | sbl |= ISPCTRL_SBL_RD_RAM_EN; | ||
1191 | |||
1192 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl); | ||
1193 | } | ||
1194 | |||
1195 | /* | ||
1196 | * isp_module_sync_idle - Helper to sync module with its idle state | ||
1197 | * @me: ISP submodule's media entity | ||
1198 | * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization | ||
1199 | * @stopping: flag which tells module wants to stop | ||
1200 | * | ||
1201 | * This function checks if ISP submodule needs to wait for next interrupt. If | ||
1202 | * yes, makes the caller to sleep while waiting for such event. | ||
1203 | */ | ||
1204 | int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, | ||
1205 | atomic_t *stopping) | ||
1206 | { | ||
1207 | struct isp_pipeline *pipe = to_isp_pipeline(me); | ||
1208 | |||
1209 | if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED || | ||
1210 | (pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT && | ||
1211 | !isp_pipeline_ready(pipe))) | ||
1212 | return 0; | ||
1213 | |||
1214 | /* | ||
1215 | * atomic_set() doesn't include memory barrier on ARM platform for SMP | ||
1216 | * scenario. We'll call it here to avoid race conditions. | ||
1217 | */ | ||
1218 | atomic_set(stopping, 1); | ||
1219 | smp_mb(); | ||
1220 | |||
1221 | /* | ||
1222 | * If module is the last one, it's writing to memory. In this case, | ||
1223 | * it's necessary to check if the module is already paused due to | ||
1224 | * DMA queue underrun or if it has to wait for next interrupt to be | ||
1225 | * idle. | ||
1226 | * If it isn't the last one, the function won't sleep but *stopping | ||
1227 | * will still be set to warn next submodule caller's interrupt the | ||
1228 | * module wants to be idle. | ||
1229 | */ | ||
1230 | if (isp_pipeline_is_last(me)) { | ||
1231 | struct isp_video *video = pipe->output; | ||
1232 | unsigned long flags; | ||
1233 | spin_lock_irqsave(&video->queue->irqlock, flags); | ||
1234 | if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) { | ||
1235 | spin_unlock_irqrestore(&video->queue->irqlock, flags); | ||
1236 | atomic_set(stopping, 0); | ||
1237 | smp_mb(); | ||
1238 | return 0; | ||
1239 | } | ||
1240 | spin_unlock_irqrestore(&video->queue->irqlock, flags); | ||
1241 | if (!wait_event_timeout(*wait, !atomic_read(stopping), | ||
1242 | msecs_to_jiffies(1000))) { | ||
1243 | atomic_set(stopping, 0); | ||
1244 | smp_mb(); | ||
1245 | return -ETIMEDOUT; | ||
1246 | } | ||
1247 | } | ||
1248 | |||
1249 | return 0; | ||
1250 | } | ||
1251 | |||
1252 | /* | ||
1253 | * omap3isp_module_sync_is_stopped - Helper to verify if module was stopping | ||
1254 | * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization | ||
1255 | * @stopping: flag which tells module wants to stop | ||
1256 | * | ||
1257 | * This function checks if ISP submodule was stopping. In case of yes, it | ||
1258 | * notices the caller by setting stopping to 0 and waking up the wait queue. | ||
1259 | * Returns 1 if it was stopping or 0 otherwise. | ||
1260 | */ | ||
1261 | int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait, | ||
1262 | atomic_t *stopping) | ||
1263 | { | ||
1264 | if (atomic_cmpxchg(stopping, 1, 0)) { | ||
1265 | wake_up(wait); | ||
1266 | return 1; | ||
1267 | } | ||
1268 | |||
1269 | return 0; | ||
1270 | } | ||
1271 | |||
1272 | /* -------------------------------------------------------------------------- | ||
1273 | * Clock management | ||
1274 | */ | ||
1275 | |||
1276 | #define ISPCTRL_CLKS_MASK (ISPCTRL_H3A_CLK_EN | \ | ||
1277 | ISPCTRL_HIST_CLK_EN | \ | ||
1278 | ISPCTRL_RSZ_CLK_EN | \ | ||
1279 | (ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN) | \ | ||
1280 | (ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN)) | ||
1281 | |||
1282 | static void __isp_subclk_update(struct isp_device *isp) | ||
1283 | { | ||
1284 | u32 clk = 0; | ||
1285 | |||
1286 | if (isp->subclk_resources & OMAP3_ISP_SUBCLK_H3A) | ||
1287 | clk |= ISPCTRL_H3A_CLK_EN; | ||
1288 | |||
1289 | if (isp->subclk_resources & OMAP3_ISP_SUBCLK_HIST) | ||
1290 | clk |= ISPCTRL_HIST_CLK_EN; | ||
1291 | |||
1292 | if (isp->subclk_resources & OMAP3_ISP_SUBCLK_RESIZER) | ||
1293 | clk |= ISPCTRL_RSZ_CLK_EN; | ||
1294 | |||
1295 | /* NOTE: For CCDC & Preview submodules, we need to affect internal | ||
1296 | * RAM as well. | ||
1297 | */ | ||
1298 | if (isp->subclk_resources & OMAP3_ISP_SUBCLK_CCDC) | ||
1299 | clk |= ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN; | ||
1300 | |||
1301 | if (isp->subclk_resources & OMAP3_ISP_SUBCLK_PREVIEW) | ||
1302 | clk |= ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN; | ||
1303 | |||
1304 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
1305 | ISPCTRL_CLKS_MASK, clk); | ||
1306 | } | ||
1307 | |||
1308 | void omap3isp_subclk_enable(struct isp_device *isp, | ||
1309 | enum isp_subclk_resource res) | ||
1310 | { | ||
1311 | isp->subclk_resources |= res; | ||
1312 | |||
1313 | __isp_subclk_update(isp); | ||
1314 | } | ||
1315 | |||
1316 | void omap3isp_subclk_disable(struct isp_device *isp, | ||
1317 | enum isp_subclk_resource res) | ||
1318 | { | ||
1319 | isp->subclk_resources &= ~res; | ||
1320 | |||
1321 | __isp_subclk_update(isp); | ||
1322 | } | ||
1323 | |||
1324 | /* | ||
1325 | * isp_enable_clocks - Enable ISP clocks | ||
1326 | * @isp: OMAP3 ISP device | ||
1327 | * | ||
1328 | * Return 0 if successful, or clk_enable return value if any of tthem fails. | ||
1329 | */ | ||
1330 | static int isp_enable_clocks(struct isp_device *isp) | ||
1331 | { | ||
1332 | int r; | ||
1333 | unsigned long rate; | ||
1334 | int divisor; | ||
1335 | |||
1336 | /* | ||
1337 | * cam_mclk clock chain: | ||
1338 | * dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk | ||
1339 | * | ||
1340 | * In OMAP3630 dpll4_m5x2 != 2 x dpll4_m5 but both are | ||
1341 | * set to the same value. Hence the rate set for dpll4_m5 | ||
1342 | * has to be twice of what is set on OMAP3430 to get | ||
1343 | * the required value for cam_mclk | ||
1344 | */ | ||
1345 | if (cpu_is_omap3630()) | ||
1346 | divisor = 1; | ||
1347 | else | ||
1348 | divisor = 2; | ||
1349 | |||
1350 | r = clk_enable(isp->clock[ISP_CLK_CAM_ICK]); | ||
1351 | if (r) { | ||
1352 | dev_err(isp->dev, "clk_enable cam_ick failed\n"); | ||
1353 | goto out_clk_enable_ick; | ||
1354 | } | ||
1355 | r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK], | ||
1356 | CM_CAM_MCLK_HZ/divisor); | ||
1357 | if (r) { | ||
1358 | dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n"); | ||
1359 | goto out_clk_enable_mclk; | ||
1360 | } | ||
1361 | r = clk_enable(isp->clock[ISP_CLK_CAM_MCLK]); | ||
1362 | if (r) { | ||
1363 | dev_err(isp->dev, "clk_enable cam_mclk failed\n"); | ||
1364 | goto out_clk_enable_mclk; | ||
1365 | } | ||
1366 | rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); | ||
1367 | if (rate != CM_CAM_MCLK_HZ) | ||
1368 | dev_warn(isp->dev, "unexpected cam_mclk rate:\n" | ||
1369 | " expected : %d\n" | ||
1370 | " actual : %ld\n", CM_CAM_MCLK_HZ, rate); | ||
1371 | r = clk_enable(isp->clock[ISP_CLK_CSI2_FCK]); | ||
1372 | if (r) { | ||
1373 | dev_err(isp->dev, "clk_enable csi2_fck failed\n"); | ||
1374 | goto out_clk_enable_csi2_fclk; | ||
1375 | } | ||
1376 | return 0; | ||
1377 | |||
1378 | out_clk_enable_csi2_fclk: | ||
1379 | clk_disable(isp->clock[ISP_CLK_CAM_MCLK]); | ||
1380 | out_clk_enable_mclk: | ||
1381 | clk_disable(isp->clock[ISP_CLK_CAM_ICK]); | ||
1382 | out_clk_enable_ick: | ||
1383 | return r; | ||
1384 | } | ||
1385 | |||
1386 | /* | ||
1387 | * isp_disable_clocks - Disable ISP clocks | ||
1388 | * @isp: OMAP3 ISP device | ||
1389 | */ | ||
1390 | static void isp_disable_clocks(struct isp_device *isp) | ||
1391 | { | ||
1392 | clk_disable(isp->clock[ISP_CLK_CAM_ICK]); | ||
1393 | clk_disable(isp->clock[ISP_CLK_CAM_MCLK]); | ||
1394 | clk_disable(isp->clock[ISP_CLK_CSI2_FCK]); | ||
1395 | } | ||
1396 | |||
1397 | static const char *isp_clocks[] = { | ||
1398 | "cam_ick", | ||
1399 | "cam_mclk", | ||
1400 | "dpll4_m5_ck", | ||
1401 | "csi2_96m_fck", | ||
1402 | "l3_ick", | ||
1403 | }; | ||
1404 | |||
1405 | static void isp_put_clocks(struct isp_device *isp) | ||
1406 | { | ||
1407 | unsigned int i; | ||
1408 | |||
1409 | for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) { | ||
1410 | if (isp->clock[i]) { | ||
1411 | clk_put(isp->clock[i]); | ||
1412 | isp->clock[i] = NULL; | ||
1413 | } | ||
1414 | } | ||
1415 | } | ||
1416 | |||
1417 | static int isp_get_clocks(struct isp_device *isp) | ||
1418 | { | ||
1419 | struct clk *clk; | ||
1420 | unsigned int i; | ||
1421 | |||
1422 | for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) { | ||
1423 | clk = clk_get(isp->dev, isp_clocks[i]); | ||
1424 | if (IS_ERR(clk)) { | ||
1425 | dev_err(isp->dev, "clk_get %s failed\n", isp_clocks[i]); | ||
1426 | isp_put_clocks(isp); | ||
1427 | return PTR_ERR(clk); | ||
1428 | } | ||
1429 | |||
1430 | isp->clock[i] = clk; | ||
1431 | } | ||
1432 | |||
1433 | return 0; | ||
1434 | } | ||
1435 | |||
1436 | /* | ||
1437 | * omap3isp_get - Acquire the ISP resource. | ||
1438 | * | ||
1439 | * Initializes the clocks for the first acquire. | ||
1440 | * | ||
1441 | * Increment the reference count on the ISP. If the first reference is taken, | ||
1442 | * enable clocks and power-up all submodules. | ||
1443 | * | ||
1444 | * Return a pointer to the ISP device structure, or NULL if an error occurred. | ||
1445 | */ | ||
1446 | struct isp_device *omap3isp_get(struct isp_device *isp) | ||
1447 | { | ||
1448 | struct isp_device *__isp = isp; | ||
1449 | |||
1450 | if (isp == NULL) | ||
1451 | return NULL; | ||
1452 | |||
1453 | mutex_lock(&isp->isp_mutex); | ||
1454 | if (isp->ref_count > 0) | ||
1455 | goto out; | ||
1456 | |||
1457 | if (isp_enable_clocks(isp) < 0) { | ||
1458 | __isp = NULL; | ||
1459 | goto out; | ||
1460 | } | ||
1461 | |||
1462 | /* We don't want to restore context before saving it! */ | ||
1463 | if (isp->has_context) | ||
1464 | isp_restore_ctx(isp); | ||
1465 | else | ||
1466 | isp->has_context = 1; | ||
1467 | |||
1468 | isp_enable_interrupts(isp); | ||
1469 | |||
1470 | out: | ||
1471 | if (__isp != NULL) | ||
1472 | isp->ref_count++; | ||
1473 | mutex_unlock(&isp->isp_mutex); | ||
1474 | |||
1475 | return __isp; | ||
1476 | } | ||
1477 | |||
1478 | /* | ||
1479 | * omap3isp_put - Release the ISP | ||
1480 | * | ||
1481 | * Decrement the reference count on the ISP. If the last reference is released, | ||
1482 | * power-down all submodules, disable clocks and free temporary buffers. | ||
1483 | */ | ||
1484 | void omap3isp_put(struct isp_device *isp) | ||
1485 | { | ||
1486 | if (isp == NULL) | ||
1487 | return; | ||
1488 | |||
1489 | mutex_lock(&isp->isp_mutex); | ||
1490 | BUG_ON(isp->ref_count == 0); | ||
1491 | if (--isp->ref_count == 0) { | ||
1492 | isp_disable_interrupts(isp); | ||
1493 | isp_save_ctx(isp); | ||
1494 | if (isp->needs_reset) { | ||
1495 | isp_reset(isp); | ||
1496 | isp->needs_reset = false; | ||
1497 | } | ||
1498 | isp_disable_clocks(isp); | ||
1499 | } | ||
1500 | mutex_unlock(&isp->isp_mutex); | ||
1501 | } | ||
1502 | |||
1503 | /* -------------------------------------------------------------------------- | ||
1504 | * Platform device driver | ||
1505 | */ | ||
1506 | |||
1507 | /* | ||
1508 | * omap3isp_print_status - Prints the values of the ISP Control Module registers | ||
1509 | * @isp: OMAP3 ISP device | ||
1510 | */ | ||
1511 | #define ISP_PRINT_REGISTER(isp, name)\ | ||
1512 | dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \ | ||
1513 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name)) | ||
1514 | #define SBL_PRINT_REGISTER(isp, name)\ | ||
1515 | dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \ | ||
1516 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name)) | ||
1517 | |||
1518 | void omap3isp_print_status(struct isp_device *isp) | ||
1519 | { | ||
1520 | dev_dbg(isp->dev, "-------------ISP Register dump--------------\n"); | ||
1521 | |||
1522 | ISP_PRINT_REGISTER(isp, SYSCONFIG); | ||
1523 | ISP_PRINT_REGISTER(isp, SYSSTATUS); | ||
1524 | ISP_PRINT_REGISTER(isp, IRQ0ENABLE); | ||
1525 | ISP_PRINT_REGISTER(isp, IRQ0STATUS); | ||
1526 | ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH); | ||
1527 | ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY); | ||
1528 | ISP_PRINT_REGISTER(isp, CTRL); | ||
1529 | ISP_PRINT_REGISTER(isp, TCTRL_CTRL); | ||
1530 | ISP_PRINT_REGISTER(isp, TCTRL_FRAME); | ||
1531 | ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY); | ||
1532 | ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY); | ||
1533 | ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY); | ||
1534 | ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH); | ||
1535 | ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH); | ||
1536 | ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH); | ||
1537 | |||
1538 | SBL_PRINT_REGISTER(isp, PCR); | ||
1539 | SBL_PRINT_REGISTER(isp, SDR_REQ_EXP); | ||
1540 | |||
1541 | dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
1542 | } | ||
1543 | |||
1544 | #ifdef CONFIG_PM | ||
1545 | |||
1546 | /* | ||
1547 | * Power management support. | ||
1548 | * | ||
1549 | * As the ISP can't properly handle an input video stream interruption on a non | ||
1550 | * frame boundary, the ISP pipelines need to be stopped before sensors get | ||
1551 | * suspended. However, as suspending the sensors can require a running clock, | ||
1552 | * which can be provided by the ISP, the ISP can't be completely suspended | ||
1553 | * before the sensor. | ||
1554 | * | ||
1555 | * To solve this problem power management support is split into prepare/complete | ||
1556 | * and suspend/resume operations. The pipelines are stopped in prepare() and the | ||
1557 | * ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in | ||
1558 | * resume(), and the the pipelines are restarted in complete(). | ||
1559 | * | ||
1560 | * TODO: PM dependencies between the ISP and sensors are not modeled explicitly | ||
1561 | * yet. | ||
1562 | */ | ||
1563 | static int isp_pm_prepare(struct device *dev) | ||
1564 | { | ||
1565 | struct isp_device *isp = dev_get_drvdata(dev); | ||
1566 | int reset; | ||
1567 | |||
1568 | WARN_ON(mutex_is_locked(&isp->isp_mutex)); | ||
1569 | |||
1570 | if (isp->ref_count == 0) | ||
1571 | return 0; | ||
1572 | |||
1573 | reset = isp_suspend_modules(isp); | ||
1574 | isp_disable_interrupts(isp); | ||
1575 | isp_save_ctx(isp); | ||
1576 | if (reset) | ||
1577 | isp_reset(isp); | ||
1578 | |||
1579 | return 0; | ||
1580 | } | ||
1581 | |||
1582 | static int isp_pm_suspend(struct device *dev) | ||
1583 | { | ||
1584 | struct isp_device *isp = dev_get_drvdata(dev); | ||
1585 | |||
1586 | WARN_ON(mutex_is_locked(&isp->isp_mutex)); | ||
1587 | |||
1588 | if (isp->ref_count) | ||
1589 | isp_disable_clocks(isp); | ||
1590 | |||
1591 | return 0; | ||
1592 | } | ||
1593 | |||
1594 | static int isp_pm_resume(struct device *dev) | ||
1595 | { | ||
1596 | struct isp_device *isp = dev_get_drvdata(dev); | ||
1597 | |||
1598 | if (isp->ref_count == 0) | ||
1599 | return 0; | ||
1600 | |||
1601 | return isp_enable_clocks(isp); | ||
1602 | } | ||
1603 | |||
1604 | static void isp_pm_complete(struct device *dev) | ||
1605 | { | ||
1606 | struct isp_device *isp = dev_get_drvdata(dev); | ||
1607 | |||
1608 | if (isp->ref_count == 0) | ||
1609 | return; | ||
1610 | |||
1611 | isp_restore_ctx(isp); | ||
1612 | isp_enable_interrupts(isp); | ||
1613 | isp_resume_modules(isp); | ||
1614 | } | ||
1615 | |||
1616 | #else | ||
1617 | |||
1618 | #define isp_pm_prepare NULL | ||
1619 | #define isp_pm_suspend NULL | ||
1620 | #define isp_pm_resume NULL | ||
1621 | #define isp_pm_complete NULL | ||
1622 | |||
1623 | #endif /* CONFIG_PM */ | ||
1624 | |||
1625 | static void isp_unregister_entities(struct isp_device *isp) | ||
1626 | { | ||
1627 | omap3isp_csi2_unregister_entities(&isp->isp_csi2a); | ||
1628 | omap3isp_ccp2_unregister_entities(&isp->isp_ccp2); | ||
1629 | omap3isp_ccdc_unregister_entities(&isp->isp_ccdc); | ||
1630 | omap3isp_preview_unregister_entities(&isp->isp_prev); | ||
1631 | omap3isp_resizer_unregister_entities(&isp->isp_res); | ||
1632 | omap3isp_stat_unregister_entities(&isp->isp_aewb); | ||
1633 | omap3isp_stat_unregister_entities(&isp->isp_af); | ||
1634 | omap3isp_stat_unregister_entities(&isp->isp_hist); | ||
1635 | |||
1636 | v4l2_device_unregister(&isp->v4l2_dev); | ||
1637 | media_device_unregister(&isp->media_dev); | ||
1638 | } | ||
1639 | |||
1640 | /* | ||
1641 | * isp_register_subdev_group - Register a group of subdevices | ||
1642 | * @isp: OMAP3 ISP device | ||
1643 | * @board_info: I2C subdevs board information array | ||
1644 | * | ||
1645 | * Register all I2C subdevices in the board_info array. The array must be | ||
1646 | * terminated by a NULL entry, and the first entry must be the sensor. | ||
1647 | * | ||
1648 | * Return a pointer to the sensor media entity if it has been successfully | ||
1649 | * registered, or NULL otherwise. | ||
1650 | */ | ||
1651 | static struct v4l2_subdev * | ||
1652 | isp_register_subdev_group(struct isp_device *isp, | ||
1653 | struct isp_subdev_i2c_board_info *board_info) | ||
1654 | { | ||
1655 | struct v4l2_subdev *sensor = NULL; | ||
1656 | unsigned int first; | ||
1657 | |||
1658 | if (board_info->board_info == NULL) | ||
1659 | return NULL; | ||
1660 | |||
1661 | for (first = 1; board_info->board_info; ++board_info, first = 0) { | ||
1662 | struct v4l2_subdev *subdev; | ||
1663 | struct i2c_adapter *adapter; | ||
1664 | |||
1665 | adapter = i2c_get_adapter(board_info->i2c_adapter_id); | ||
1666 | if (adapter == NULL) { | ||
1667 | printk(KERN_ERR "%s: Unable to get I2C adapter %d for " | ||
1668 | "device %s\n", __func__, | ||
1669 | board_info->i2c_adapter_id, | ||
1670 | board_info->board_info->type); | ||
1671 | continue; | ||
1672 | } | ||
1673 | |||
1674 | subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter, | ||
1675 | board_info->board_info, NULL); | ||
1676 | if (subdev == NULL) { | ||
1677 | printk(KERN_ERR "%s: Unable to register subdev %s\n", | ||
1678 | __func__, board_info->board_info->type); | ||
1679 | continue; | ||
1680 | } | ||
1681 | |||
1682 | if (first) | ||
1683 | sensor = subdev; | ||
1684 | } | ||
1685 | |||
1686 | return sensor; | ||
1687 | } | ||
1688 | |||
1689 | static int isp_register_entities(struct isp_device *isp) | ||
1690 | { | ||
1691 | struct isp_platform_data *pdata = isp->pdata; | ||
1692 | struct isp_v4l2_subdevs_group *subdevs; | ||
1693 | int ret; | ||
1694 | |||
1695 | isp->media_dev.dev = isp->dev; | ||
1696 | strlcpy(isp->media_dev.model, "TI OMAP3 ISP", | ||
1697 | sizeof(isp->media_dev.model)); | ||
1698 | isp->media_dev.link_notify = isp_pipeline_link_notify; | ||
1699 | ret = media_device_register(&isp->media_dev); | ||
1700 | if (ret < 0) { | ||
1701 | printk(KERN_ERR "%s: Media device registration failed (%d)\n", | ||
1702 | __func__, ret); | ||
1703 | return ret; | ||
1704 | } | ||
1705 | |||
1706 | isp->v4l2_dev.mdev = &isp->media_dev; | ||
1707 | ret = v4l2_device_register(isp->dev, &isp->v4l2_dev); | ||
1708 | if (ret < 0) { | ||
1709 | printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n", | ||
1710 | __func__, ret); | ||
1711 | goto done; | ||
1712 | } | ||
1713 | |||
1714 | /* Register internal entities */ | ||
1715 | ret = omap3isp_ccp2_register_entities(&isp->isp_ccp2, &isp->v4l2_dev); | ||
1716 | if (ret < 0) | ||
1717 | goto done; | ||
1718 | |||
1719 | ret = omap3isp_csi2_register_entities(&isp->isp_csi2a, &isp->v4l2_dev); | ||
1720 | if (ret < 0) | ||
1721 | goto done; | ||
1722 | |||
1723 | ret = omap3isp_ccdc_register_entities(&isp->isp_ccdc, &isp->v4l2_dev); | ||
1724 | if (ret < 0) | ||
1725 | goto done; | ||
1726 | |||
1727 | ret = omap3isp_preview_register_entities(&isp->isp_prev, | ||
1728 | &isp->v4l2_dev); | ||
1729 | if (ret < 0) | ||
1730 | goto done; | ||
1731 | |||
1732 | ret = omap3isp_resizer_register_entities(&isp->isp_res, &isp->v4l2_dev); | ||
1733 | if (ret < 0) | ||
1734 | goto done; | ||
1735 | |||
1736 | ret = omap3isp_stat_register_entities(&isp->isp_aewb, &isp->v4l2_dev); | ||
1737 | if (ret < 0) | ||
1738 | goto done; | ||
1739 | |||
1740 | ret = omap3isp_stat_register_entities(&isp->isp_af, &isp->v4l2_dev); | ||
1741 | if (ret < 0) | ||
1742 | goto done; | ||
1743 | |||
1744 | ret = omap3isp_stat_register_entities(&isp->isp_hist, &isp->v4l2_dev); | ||
1745 | if (ret < 0) | ||
1746 | goto done; | ||
1747 | |||
1748 | /* Register external entities */ | ||
1749 | for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) { | ||
1750 | struct v4l2_subdev *sensor; | ||
1751 | struct media_entity *input; | ||
1752 | unsigned int flags; | ||
1753 | unsigned int pad; | ||
1754 | |||
1755 | sensor = isp_register_subdev_group(isp, subdevs->subdevs); | ||
1756 | if (sensor == NULL) | ||
1757 | continue; | ||
1758 | |||
1759 | sensor->host_priv = subdevs; | ||
1760 | |||
1761 | /* Connect the sensor to the correct interface module. Parallel | ||
1762 | * sensors are connected directly to the CCDC, while serial | ||
1763 | * sensors are connected to the CSI2a, CCP2b or CSI2c receiver | ||
1764 | * through CSIPHY1 or CSIPHY2. | ||
1765 | */ | ||
1766 | switch (subdevs->interface) { | ||
1767 | case ISP_INTERFACE_PARALLEL: | ||
1768 | input = &isp->isp_ccdc.subdev.entity; | ||
1769 | pad = CCDC_PAD_SINK; | ||
1770 | flags = 0; | ||
1771 | break; | ||
1772 | |||
1773 | case ISP_INTERFACE_CSI2A_PHY2: | ||
1774 | input = &isp->isp_csi2a.subdev.entity; | ||
1775 | pad = CSI2_PAD_SINK; | ||
1776 | flags = MEDIA_LNK_FL_IMMUTABLE | ||
1777 | | MEDIA_LNK_FL_ENABLED; | ||
1778 | break; | ||
1779 | |||
1780 | case ISP_INTERFACE_CCP2B_PHY1: | ||
1781 | case ISP_INTERFACE_CCP2B_PHY2: | ||
1782 | input = &isp->isp_ccp2.subdev.entity; | ||
1783 | pad = CCP2_PAD_SINK; | ||
1784 | flags = 0; | ||
1785 | break; | ||
1786 | |||
1787 | case ISP_INTERFACE_CSI2C_PHY1: | ||
1788 | input = &isp->isp_csi2c.subdev.entity; | ||
1789 | pad = CSI2_PAD_SINK; | ||
1790 | flags = MEDIA_LNK_FL_IMMUTABLE | ||
1791 | | MEDIA_LNK_FL_ENABLED; | ||
1792 | break; | ||
1793 | |||
1794 | default: | ||
1795 | printk(KERN_ERR "%s: invalid interface type %u\n", | ||
1796 | __func__, subdevs->interface); | ||
1797 | ret = -EINVAL; | ||
1798 | goto done; | ||
1799 | } | ||
1800 | |||
1801 | ret = media_entity_create_link(&sensor->entity, 0, input, pad, | ||
1802 | flags); | ||
1803 | if (ret < 0) | ||
1804 | goto done; | ||
1805 | } | ||
1806 | |||
1807 | ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); | ||
1808 | |||
1809 | done: | ||
1810 | if (ret < 0) | ||
1811 | isp_unregister_entities(isp); | ||
1812 | |||
1813 | return ret; | ||
1814 | } | ||
1815 | |||
1816 | static void isp_cleanup_modules(struct isp_device *isp) | ||
1817 | { | ||
1818 | omap3isp_h3a_aewb_cleanup(isp); | ||
1819 | omap3isp_h3a_af_cleanup(isp); | ||
1820 | omap3isp_hist_cleanup(isp); | ||
1821 | omap3isp_resizer_cleanup(isp); | ||
1822 | omap3isp_preview_cleanup(isp); | ||
1823 | omap3isp_ccdc_cleanup(isp); | ||
1824 | omap3isp_ccp2_cleanup(isp); | ||
1825 | omap3isp_csi2_cleanup(isp); | ||
1826 | } | ||
1827 | |||
1828 | static int isp_initialize_modules(struct isp_device *isp) | ||
1829 | { | ||
1830 | int ret; | ||
1831 | |||
1832 | ret = omap3isp_csiphy_init(isp); | ||
1833 | if (ret < 0) { | ||
1834 | dev_err(isp->dev, "CSI PHY initialization failed\n"); | ||
1835 | goto error_csiphy; | ||
1836 | } | ||
1837 | |||
1838 | ret = omap3isp_csi2_init(isp); | ||
1839 | if (ret < 0) { | ||
1840 | dev_err(isp->dev, "CSI2 initialization failed\n"); | ||
1841 | goto error_csi2; | ||
1842 | } | ||
1843 | |||
1844 | ret = omap3isp_ccp2_init(isp); | ||
1845 | if (ret < 0) { | ||
1846 | dev_err(isp->dev, "CCP2 initialization failed\n"); | ||
1847 | goto error_ccp2; | ||
1848 | } | ||
1849 | |||
1850 | ret = omap3isp_ccdc_init(isp); | ||
1851 | if (ret < 0) { | ||
1852 | dev_err(isp->dev, "CCDC initialization failed\n"); | ||
1853 | goto error_ccdc; | ||
1854 | } | ||
1855 | |||
1856 | ret = omap3isp_preview_init(isp); | ||
1857 | if (ret < 0) { | ||
1858 | dev_err(isp->dev, "Preview initialization failed\n"); | ||
1859 | goto error_preview; | ||
1860 | } | ||
1861 | |||
1862 | ret = omap3isp_resizer_init(isp); | ||
1863 | if (ret < 0) { | ||
1864 | dev_err(isp->dev, "Resizer initialization failed\n"); | ||
1865 | goto error_resizer; | ||
1866 | } | ||
1867 | |||
1868 | ret = omap3isp_hist_init(isp); | ||
1869 | if (ret < 0) { | ||
1870 | dev_err(isp->dev, "Histogram initialization failed\n"); | ||
1871 | goto error_hist; | ||
1872 | } | ||
1873 | |||
1874 | ret = omap3isp_h3a_aewb_init(isp); | ||
1875 | if (ret < 0) { | ||
1876 | dev_err(isp->dev, "H3A AEWB initialization failed\n"); | ||
1877 | goto error_h3a_aewb; | ||
1878 | } | ||
1879 | |||
1880 | ret = omap3isp_h3a_af_init(isp); | ||
1881 | if (ret < 0) { | ||
1882 | dev_err(isp->dev, "H3A AF initialization failed\n"); | ||
1883 | goto error_h3a_af; | ||
1884 | } | ||
1885 | |||
1886 | /* Connect the submodules. */ | ||
1887 | ret = media_entity_create_link( | ||
1888 | &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, | ||
1889 | &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); | ||
1890 | if (ret < 0) | ||
1891 | goto error_link; | ||
1892 | |||
1893 | ret = media_entity_create_link( | ||
1894 | &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE, | ||
1895 | &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); | ||
1896 | if (ret < 0) | ||
1897 | goto error_link; | ||
1898 | |||
1899 | ret = media_entity_create_link( | ||
1900 | &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, | ||
1901 | &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); | ||
1902 | if (ret < 0) | ||
1903 | goto error_link; | ||
1904 | |||
1905 | ret = media_entity_create_link( | ||
1906 | &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, | ||
1907 | &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); | ||
1908 | if (ret < 0) | ||
1909 | goto error_link; | ||
1910 | |||
1911 | ret = media_entity_create_link( | ||
1912 | &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, | ||
1913 | &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); | ||
1914 | if (ret < 0) | ||
1915 | goto error_link; | ||
1916 | |||
1917 | ret = media_entity_create_link( | ||
1918 | &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, | ||
1919 | &isp->isp_aewb.subdev.entity, 0, | ||
1920 | MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); | ||
1921 | if (ret < 0) | ||
1922 | goto error_link; | ||
1923 | |||
1924 | ret = media_entity_create_link( | ||
1925 | &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, | ||
1926 | &isp->isp_af.subdev.entity, 0, | ||
1927 | MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); | ||
1928 | if (ret < 0) | ||
1929 | goto error_link; | ||
1930 | |||
1931 | ret = media_entity_create_link( | ||
1932 | &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, | ||
1933 | &isp->isp_hist.subdev.entity, 0, | ||
1934 | MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); | ||
1935 | if (ret < 0) | ||
1936 | goto error_link; | ||
1937 | |||
1938 | return 0; | ||
1939 | |||
1940 | error_link: | ||
1941 | omap3isp_h3a_af_cleanup(isp); | ||
1942 | error_h3a_af: | ||
1943 | omap3isp_h3a_aewb_cleanup(isp); | ||
1944 | error_h3a_aewb: | ||
1945 | omap3isp_hist_cleanup(isp); | ||
1946 | error_hist: | ||
1947 | omap3isp_resizer_cleanup(isp); | ||
1948 | error_resizer: | ||
1949 | omap3isp_preview_cleanup(isp); | ||
1950 | error_preview: | ||
1951 | omap3isp_ccdc_cleanup(isp); | ||
1952 | error_ccdc: | ||
1953 | omap3isp_ccp2_cleanup(isp); | ||
1954 | error_ccp2: | ||
1955 | omap3isp_csi2_cleanup(isp); | ||
1956 | error_csi2: | ||
1957 | error_csiphy: | ||
1958 | return ret; | ||
1959 | } | ||
1960 | |||
1961 | /* | ||
1962 | * isp_remove - Remove ISP platform device | ||
1963 | * @pdev: Pointer to ISP platform device | ||
1964 | * | ||
1965 | * Always returns 0. | ||
1966 | */ | ||
1967 | static int isp_remove(struct platform_device *pdev) | ||
1968 | { | ||
1969 | struct isp_device *isp = platform_get_drvdata(pdev); | ||
1970 | int i; | ||
1971 | |||
1972 | isp_unregister_entities(isp); | ||
1973 | isp_cleanup_modules(isp); | ||
1974 | |||
1975 | omap3isp_get(isp); | ||
1976 | iommu_detach_device(isp->domain, &pdev->dev); | ||
1977 | iommu_domain_free(isp->domain); | ||
1978 | omap3isp_put(isp); | ||
1979 | |||
1980 | free_irq(isp->irq_num, isp); | ||
1981 | isp_put_clocks(isp); | ||
1982 | |||
1983 | for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) { | ||
1984 | if (isp->mmio_base[i]) { | ||
1985 | iounmap(isp->mmio_base[i]); | ||
1986 | isp->mmio_base[i] = NULL; | ||
1987 | } | ||
1988 | |||
1989 | if (isp->mmio_base_phys[i]) { | ||
1990 | release_mem_region(isp->mmio_base_phys[i], | ||
1991 | isp->mmio_size[i]); | ||
1992 | isp->mmio_base_phys[i] = 0; | ||
1993 | } | ||
1994 | } | ||
1995 | |||
1996 | regulator_put(isp->isp_csiphy1.vdd); | ||
1997 | regulator_put(isp->isp_csiphy2.vdd); | ||
1998 | kfree(isp); | ||
1999 | |||
2000 | return 0; | ||
2001 | } | ||
2002 | |||
2003 | static int isp_map_mem_resource(struct platform_device *pdev, | ||
2004 | struct isp_device *isp, | ||
2005 | enum isp_mem_resources res) | ||
2006 | { | ||
2007 | struct resource *mem; | ||
2008 | |||
2009 | /* request the mem region for the camera registers */ | ||
2010 | |||
2011 | mem = platform_get_resource(pdev, IORESOURCE_MEM, res); | ||
2012 | if (!mem) { | ||
2013 | dev_err(isp->dev, "no mem resource?\n"); | ||
2014 | return -ENODEV; | ||
2015 | } | ||
2016 | |||
2017 | if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { | ||
2018 | dev_err(isp->dev, | ||
2019 | "cannot reserve camera register I/O region\n"); | ||
2020 | return -ENODEV; | ||
2021 | } | ||
2022 | isp->mmio_base_phys[res] = mem->start; | ||
2023 | isp->mmio_size[res] = resource_size(mem); | ||
2024 | |||
2025 | /* map the region */ | ||
2026 | isp->mmio_base[res] = ioremap_nocache(isp->mmio_base_phys[res], | ||
2027 | isp->mmio_size[res]); | ||
2028 | if (!isp->mmio_base[res]) { | ||
2029 | dev_err(isp->dev, "cannot map camera register I/O region\n"); | ||
2030 | return -ENODEV; | ||
2031 | } | ||
2032 | |||
2033 | return 0; | ||
2034 | } | ||
2035 | |||
2036 | /* | ||
2037 | * isp_probe - Probe ISP platform device | ||
2038 | * @pdev: Pointer to ISP platform device | ||
2039 | * | ||
2040 | * Returns 0 if successful, | ||
2041 | * -ENOMEM if no memory available, | ||
2042 | * -ENODEV if no platform device resources found | ||
2043 | * or no space for remapping registers, | ||
2044 | * -EINVAL if couldn't install ISR, | ||
2045 | * or clk_get return error value. | ||
2046 | */ | ||
2047 | static int isp_probe(struct platform_device *pdev) | ||
2048 | { | ||
2049 | struct isp_platform_data *pdata = pdev->dev.platform_data; | ||
2050 | struct isp_device *isp; | ||
2051 | int ret; | ||
2052 | int i, m; | ||
2053 | |||
2054 | if (pdata == NULL) | ||
2055 | return -EINVAL; | ||
2056 | |||
2057 | isp = kzalloc(sizeof(*isp), GFP_KERNEL); | ||
2058 | if (!isp) { | ||
2059 | dev_err(&pdev->dev, "could not allocate memory\n"); | ||
2060 | return -ENOMEM; | ||
2061 | } | ||
2062 | |||
2063 | isp->autoidle = autoidle; | ||
2064 | isp->platform_cb.set_xclk = isp_set_xclk; | ||
2065 | isp->platform_cb.set_pixel_clock = isp_set_pixel_clock; | ||
2066 | |||
2067 | mutex_init(&isp->isp_mutex); | ||
2068 | spin_lock_init(&isp->stat_lock); | ||
2069 | |||
2070 | isp->dev = &pdev->dev; | ||
2071 | isp->pdata = pdata; | ||
2072 | isp->ref_count = 0; | ||
2073 | |||
2074 | isp->raw_dmamask = DMA_BIT_MASK(32); | ||
2075 | isp->dev->dma_mask = &isp->raw_dmamask; | ||
2076 | isp->dev->coherent_dma_mask = DMA_BIT_MASK(32); | ||
2077 | |||
2078 | platform_set_drvdata(pdev, isp); | ||
2079 | |||
2080 | /* Regulators */ | ||
2081 | isp->isp_csiphy1.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY1"); | ||
2082 | isp->isp_csiphy2.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY2"); | ||
2083 | |||
2084 | /* Clocks */ | ||
2085 | ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN); | ||
2086 | if (ret < 0) | ||
2087 | goto error; | ||
2088 | |||
2089 | ret = isp_get_clocks(isp); | ||
2090 | if (ret < 0) | ||
2091 | goto error; | ||
2092 | |||
2093 | if (omap3isp_get(isp) == NULL) | ||
2094 | goto error; | ||
2095 | |||
2096 | ret = isp_reset(isp); | ||
2097 | if (ret < 0) | ||
2098 | goto error_isp; | ||
2099 | |||
2100 | /* Memory resources */ | ||
2101 | isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); | ||
2102 | dev_info(isp->dev, "Revision %d.%d found\n", | ||
2103 | (isp->revision & 0xf0) >> 4, isp->revision & 0x0f); | ||
2104 | |||
2105 | for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++) | ||
2106 | if (isp->revision == isp_res_maps[m].isp_rev) | ||
2107 | break; | ||
2108 | |||
2109 | if (m == ARRAY_SIZE(isp_res_maps)) { | ||
2110 | dev_err(isp->dev, "No resource map found for ISP rev %d.%d\n", | ||
2111 | (isp->revision & 0xf0) >> 4, isp->revision & 0xf); | ||
2112 | ret = -ENODEV; | ||
2113 | goto error_isp; | ||
2114 | } | ||
2115 | |||
2116 | for (i = 1; i < OMAP3_ISP_IOMEM_LAST; i++) { | ||
2117 | if (isp_res_maps[m].map & 1 << i) { | ||
2118 | ret = isp_map_mem_resource(pdev, isp, i); | ||
2119 | if (ret) | ||
2120 | goto error_isp; | ||
2121 | } | ||
2122 | } | ||
2123 | |||
2124 | isp->domain = iommu_domain_alloc(pdev->dev.bus); | ||
2125 | if (!isp->domain) { | ||
2126 | dev_err(isp->dev, "can't alloc iommu domain\n"); | ||
2127 | ret = -ENOMEM; | ||
2128 | goto error_isp; | ||
2129 | } | ||
2130 | |||
2131 | ret = iommu_attach_device(isp->domain, &pdev->dev); | ||
2132 | if (ret) { | ||
2133 | dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret); | ||
2134 | goto free_domain; | ||
2135 | } | ||
2136 | |||
2137 | /* Interrupt */ | ||
2138 | isp->irq_num = platform_get_irq(pdev, 0); | ||
2139 | if (isp->irq_num <= 0) { | ||
2140 | dev_err(isp->dev, "No IRQ resource\n"); | ||
2141 | ret = -ENODEV; | ||
2142 | goto detach_dev; | ||
2143 | } | ||
2144 | |||
2145 | if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) { | ||
2146 | dev_err(isp->dev, "Unable to request IRQ\n"); | ||
2147 | ret = -EINVAL; | ||
2148 | goto detach_dev; | ||
2149 | } | ||
2150 | |||
2151 | /* Entities */ | ||
2152 | ret = isp_initialize_modules(isp); | ||
2153 | if (ret < 0) | ||
2154 | goto error_irq; | ||
2155 | |||
2156 | ret = isp_register_entities(isp); | ||
2157 | if (ret < 0) | ||
2158 | goto error_modules; | ||
2159 | |||
2160 | isp_power_settings(isp, 1); | ||
2161 | omap3isp_put(isp); | ||
2162 | |||
2163 | return 0; | ||
2164 | |||
2165 | error_modules: | ||
2166 | isp_cleanup_modules(isp); | ||
2167 | error_irq: | ||
2168 | free_irq(isp->irq_num, isp); | ||
2169 | detach_dev: | ||
2170 | iommu_detach_device(isp->domain, &pdev->dev); | ||
2171 | free_domain: | ||
2172 | iommu_domain_free(isp->domain); | ||
2173 | error_isp: | ||
2174 | omap3isp_put(isp); | ||
2175 | error: | ||
2176 | isp_put_clocks(isp); | ||
2177 | |||
2178 | for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) { | ||
2179 | if (isp->mmio_base[i]) { | ||
2180 | iounmap(isp->mmio_base[i]); | ||
2181 | isp->mmio_base[i] = NULL; | ||
2182 | } | ||
2183 | |||
2184 | if (isp->mmio_base_phys[i]) { | ||
2185 | release_mem_region(isp->mmio_base_phys[i], | ||
2186 | isp->mmio_size[i]); | ||
2187 | isp->mmio_base_phys[i] = 0; | ||
2188 | } | ||
2189 | } | ||
2190 | regulator_put(isp->isp_csiphy2.vdd); | ||
2191 | regulator_put(isp->isp_csiphy1.vdd); | ||
2192 | platform_set_drvdata(pdev, NULL); | ||
2193 | kfree(isp); | ||
2194 | |||
2195 | return ret; | ||
2196 | } | ||
2197 | |||
2198 | static const struct dev_pm_ops omap3isp_pm_ops = { | ||
2199 | .prepare = isp_pm_prepare, | ||
2200 | .suspend = isp_pm_suspend, | ||
2201 | .resume = isp_pm_resume, | ||
2202 | .complete = isp_pm_complete, | ||
2203 | }; | ||
2204 | |||
2205 | static struct platform_device_id omap3isp_id_table[] = { | ||
2206 | { "omap3isp", 0 }, | ||
2207 | { }, | ||
2208 | }; | ||
2209 | MODULE_DEVICE_TABLE(platform, omap3isp_id_table); | ||
2210 | |||
2211 | static struct platform_driver omap3isp_driver = { | ||
2212 | .probe = isp_probe, | ||
2213 | .remove = isp_remove, | ||
2214 | .id_table = omap3isp_id_table, | ||
2215 | .driver = { | ||
2216 | .owner = THIS_MODULE, | ||
2217 | .name = "omap3isp", | ||
2218 | .pm = &omap3isp_pm_ops, | ||
2219 | }, | ||
2220 | }; | ||
2221 | |||
2222 | /* | ||
2223 | * isp_init - ISP module initialization. | ||
2224 | */ | ||
2225 | static int __init isp_init(void) | ||
2226 | { | ||
2227 | return platform_driver_register(&omap3isp_driver); | ||
2228 | } | ||
2229 | |||
2230 | /* | ||
2231 | * isp_cleanup - ISP module cleanup. | ||
2232 | */ | ||
2233 | static void __exit isp_cleanup(void) | ||
2234 | { | ||
2235 | platform_driver_unregister(&omap3isp_driver); | ||
2236 | } | ||
2237 | |||
2238 | module_init(isp_init); | ||
2239 | module_exit(isp_cleanup); | ||
2240 | |||
2241 | MODULE_AUTHOR("Nokia Corporation"); | ||
2242 | MODULE_DESCRIPTION("TI OMAP3 ISP driver"); | ||
2243 | MODULE_LICENSE("GPL"); | ||
2244 | MODULE_VERSION(ISP_VIDEO_DRIVER_VERSION); | ||
diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h new file mode 100644 index 00000000000..60e0e29e3ed --- /dev/null +++ b/drivers/media/video/omap3isp/isp.h | |||
@@ -0,0 +1,438 @@ | |||
1 | /* | ||
2 | * isp.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - Core | ||
5 | * | ||
6 | * Copyright (C) 2009-2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #ifndef OMAP3_ISP_CORE_H | ||
28 | #define OMAP3_ISP_CORE_H | ||
29 | |||
30 | #include <media/v4l2-device.h> | ||
31 | #include <linux/device.h> | ||
32 | #include <linux/io.h> | ||
33 | #include <linux/platform_device.h> | ||
34 | #include <linux/wait.h> | ||
35 | #include <linux/iommu.h> | ||
36 | #include <plat/iommu.h> | ||
37 | #include <plat/iovmm.h> | ||
38 | |||
39 | #include "ispstat.h" | ||
40 | #include "ispccdc.h" | ||
41 | #include "ispreg.h" | ||
42 | #include "ispresizer.h" | ||
43 | #include "isppreview.h" | ||
44 | #include "ispcsiphy.h" | ||
45 | #include "ispcsi2.h" | ||
46 | #include "ispccp2.h" | ||
47 | |||
48 | #define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8) | ||
49 | |||
50 | #define ISP_TOK_TERM 0xFFFFFFFF /* | ||
51 | * terminating token for ISP | ||
52 | * modules reg list | ||
53 | */ | ||
54 | #define to_isp_device(ptr_module) \ | ||
55 | container_of(ptr_module, struct isp_device, isp_##ptr_module) | ||
56 | #define to_device(ptr_module) \ | ||
57 | (to_isp_device(ptr_module)->dev) | ||
58 | |||
59 | enum isp_mem_resources { | ||
60 | OMAP3_ISP_IOMEM_MAIN, | ||
61 | OMAP3_ISP_IOMEM_CCP2, | ||
62 | OMAP3_ISP_IOMEM_CCDC, | ||
63 | OMAP3_ISP_IOMEM_HIST, | ||
64 | OMAP3_ISP_IOMEM_H3A, | ||
65 | OMAP3_ISP_IOMEM_PREV, | ||
66 | OMAP3_ISP_IOMEM_RESZ, | ||
67 | OMAP3_ISP_IOMEM_SBL, | ||
68 | OMAP3_ISP_IOMEM_CSI2A_REGS1, | ||
69 | OMAP3_ISP_IOMEM_CSIPHY2, | ||
70 | OMAP3_ISP_IOMEM_CSI2A_REGS2, | ||
71 | OMAP3_ISP_IOMEM_CSI2C_REGS1, | ||
72 | OMAP3_ISP_IOMEM_CSIPHY1, | ||
73 | OMAP3_ISP_IOMEM_CSI2C_REGS2, | ||
74 | OMAP3_ISP_IOMEM_LAST | ||
75 | }; | ||
76 | |||
77 | enum isp_sbl_resource { | ||
78 | OMAP3_ISP_SBL_CSI1_READ = 0x1, | ||
79 | OMAP3_ISP_SBL_CSI1_WRITE = 0x2, | ||
80 | OMAP3_ISP_SBL_CSI2A_WRITE = 0x4, | ||
81 | OMAP3_ISP_SBL_CSI2C_WRITE = 0x8, | ||
82 | OMAP3_ISP_SBL_CCDC_LSC_READ = 0x10, | ||
83 | OMAP3_ISP_SBL_CCDC_WRITE = 0x20, | ||
84 | OMAP3_ISP_SBL_PREVIEW_READ = 0x40, | ||
85 | OMAP3_ISP_SBL_PREVIEW_WRITE = 0x80, | ||
86 | OMAP3_ISP_SBL_RESIZER_READ = 0x100, | ||
87 | OMAP3_ISP_SBL_RESIZER_WRITE = 0x200, | ||
88 | }; | ||
89 | |||
90 | enum isp_subclk_resource { | ||
91 | OMAP3_ISP_SUBCLK_CCDC = (1 << 0), | ||
92 | OMAP3_ISP_SUBCLK_H3A = (1 << 1), | ||
93 | OMAP3_ISP_SUBCLK_HIST = (1 << 2), | ||
94 | OMAP3_ISP_SUBCLK_PREVIEW = (1 << 3), | ||
95 | OMAP3_ISP_SUBCLK_RESIZER = (1 << 4), | ||
96 | }; | ||
97 | |||
98 | enum isp_interface_type { | ||
99 | ISP_INTERFACE_PARALLEL, | ||
100 | ISP_INTERFACE_CSI2A_PHY2, | ||
101 | ISP_INTERFACE_CCP2B_PHY1, | ||
102 | ISP_INTERFACE_CCP2B_PHY2, | ||
103 | ISP_INTERFACE_CSI2C_PHY1, | ||
104 | }; | ||
105 | |||
106 | /* ISP: OMAP 34xx ES 1.0 */ | ||
107 | #define ISP_REVISION_1_0 0x10 | ||
108 | /* ISP2: OMAP 34xx ES 2.0, 2.1 and 3.0 */ | ||
109 | #define ISP_REVISION_2_0 0x20 | ||
110 | /* ISP2P: OMAP 36xx */ | ||
111 | #define ISP_REVISION_15_0 0xF0 | ||
112 | |||
113 | /* | ||
114 | * struct isp_res_mapping - Map ISP io resources to ISP revision. | ||
115 | * @isp_rev: ISP_REVISION_x_x | ||
116 | * @map: bitmap for enum isp_mem_resources | ||
117 | */ | ||
118 | struct isp_res_mapping { | ||
119 | u32 isp_rev; | ||
120 | u32 map; | ||
121 | }; | ||
122 | |||
123 | /* | ||
124 | * struct isp_reg - Structure for ISP register values. | ||
125 | * @reg: 32-bit Register address. | ||
126 | * @val: 32-bit Register value. | ||
127 | */ | ||
128 | struct isp_reg { | ||
129 | enum isp_mem_resources mmio_range; | ||
130 | u32 reg; | ||
131 | u32 val; | ||
132 | }; | ||
133 | |||
134 | /** | ||
135 | * struct isp_parallel_platform_data - Parallel interface platform data | ||
136 | * @data_lane_shift: Data lane shifter | ||
137 | * 0 - CAMEXT[13:0] -> CAM[13:0] | ||
138 | * 1 - CAMEXT[13:2] -> CAM[11:0] | ||
139 | * 2 - CAMEXT[13:4] -> CAM[9:0] | ||
140 | * 3 - CAMEXT[13:6] -> CAM[7:0] | ||
141 | * @clk_pol: Pixel clock polarity | ||
142 | * 0 - Non Inverted, 1 - Inverted | ||
143 | * @hs_pol: Horizontal synchronization polarity | ||
144 | * 0 - Active high, 1 - Active low | ||
145 | * @vs_pol: Vertical synchronization polarity | ||
146 | * 0 - Active high, 1 - Active low | ||
147 | * @bridge: CCDC Bridge input control | ||
148 | * ISPCTRL_PAR_BRIDGE_DISABLE - Disable | ||
149 | * ISPCTRL_PAR_BRIDGE_LENDIAN - Little endian | ||
150 | * ISPCTRL_PAR_BRIDGE_BENDIAN - Big endian | ||
151 | */ | ||
152 | struct isp_parallel_platform_data { | ||
153 | unsigned int data_lane_shift:2; | ||
154 | unsigned int clk_pol:1; | ||
155 | unsigned int hs_pol:1; | ||
156 | unsigned int vs_pol:1; | ||
157 | unsigned int bridge:4; | ||
158 | }; | ||
159 | |||
160 | /** | ||
161 | * struct isp_ccp2_platform_data - CCP2 interface platform data | ||
162 | * @strobe_clk_pol: Strobe/clock polarity | ||
163 | * 0 - Non Inverted, 1 - Inverted | ||
164 | * @crc: Enable the cyclic redundancy check | ||
165 | * @ccp2_mode: Enable CCP2 compatibility mode | ||
166 | * 0 - MIPI-CSI1 mode, 1 - CCP2 mode | ||
167 | * @phy_layer: Physical layer selection | ||
168 | * ISPCCP2_CTRL_PHY_SEL_CLOCK - Data/clock physical layer | ||
169 | * ISPCCP2_CTRL_PHY_SEL_STROBE - Data/strobe physical layer | ||
170 | * @vpclk_div: Video port output clock control | ||
171 | */ | ||
172 | struct isp_ccp2_platform_data { | ||
173 | unsigned int strobe_clk_pol:1; | ||
174 | unsigned int crc:1; | ||
175 | unsigned int ccp2_mode:1; | ||
176 | unsigned int phy_layer:1; | ||
177 | unsigned int vpclk_div:2; | ||
178 | }; | ||
179 | |||
180 | /** | ||
181 | * struct isp_csi2_platform_data - CSI2 interface platform data | ||
182 | * @crc: Enable the cyclic redundancy check | ||
183 | * @vpclk_div: Video port output clock control | ||
184 | */ | ||
185 | struct isp_csi2_platform_data { | ||
186 | unsigned crc:1; | ||
187 | unsigned vpclk_div:2; | ||
188 | }; | ||
189 | |||
190 | struct isp_subdev_i2c_board_info { | ||
191 | struct i2c_board_info *board_info; | ||
192 | int i2c_adapter_id; | ||
193 | }; | ||
194 | |||
195 | struct isp_v4l2_subdevs_group { | ||
196 | struct isp_subdev_i2c_board_info *subdevs; | ||
197 | enum isp_interface_type interface; | ||
198 | union { | ||
199 | struct isp_parallel_platform_data parallel; | ||
200 | struct isp_ccp2_platform_data ccp2; | ||
201 | struct isp_csi2_platform_data csi2; | ||
202 | } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ | ||
203 | }; | ||
204 | |||
205 | struct isp_platform_data { | ||
206 | struct isp_v4l2_subdevs_group *subdevs; | ||
207 | void (*set_constraints)(struct isp_device *isp, bool enable); | ||
208 | }; | ||
209 | |||
210 | struct isp_platform_callback { | ||
211 | u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel); | ||
212 | int (*csiphy_config)(struct isp_csiphy *phy, | ||
213 | struct isp_csiphy_dphy_cfg *dphy, | ||
214 | struct isp_csiphy_lanes_cfg *lanes); | ||
215 | void (*set_pixel_clock)(struct isp_device *isp, unsigned int pixelclk); | ||
216 | }; | ||
217 | |||
218 | /* | ||
219 | * struct isp_device - ISP device structure. | ||
220 | * @dev: Device pointer specific to the OMAP3 ISP. | ||
221 | * @revision: Stores current ISP module revision. | ||
222 | * @irq_num: Currently used IRQ number. | ||
223 | * @mmio_base: Array with kernel base addresses for ioremapped ISP register | ||
224 | * regions. | ||
225 | * @mmio_base_phys: Array with physical L4 bus addresses for ISP register | ||
226 | * regions. | ||
227 | * @mmio_size: Array with ISP register regions size in bytes. | ||
228 | * @raw_dmamask: Raw DMA mask | ||
229 | * @stat_lock: Spinlock for handling statistics | ||
230 | * @isp_mutex: Mutex for serializing requests to ISP. | ||
231 | * @has_context: Context has been saved at least once and can be restored. | ||
232 | * @ref_count: Reference count for handling multiple ISP requests. | ||
233 | * @cam_ick: Pointer to camera interface clock structure. | ||
234 | * @cam_mclk: Pointer to camera functional clock structure. | ||
235 | * @dpll4_m5_ck: Pointer to DPLL4 M5 clock structure. | ||
236 | * @csi2_fck: Pointer to camera CSI2 complexIO clock structure. | ||
237 | * @l3_ick: Pointer to OMAP3 L3 bus interface clock. | ||
238 | * @irq: Currently attached ISP ISR callbacks information structure. | ||
239 | * @isp_af: Pointer to current settings for ISP AutoFocus SCM. | ||
240 | * @isp_hist: Pointer to current settings for ISP Histogram SCM. | ||
241 | * @isp_h3a: Pointer to current settings for ISP Auto Exposure and | ||
242 | * White Balance SCM. | ||
243 | * @isp_res: Pointer to current settings for ISP Resizer. | ||
244 | * @isp_prev: Pointer to current settings for ISP Preview. | ||
245 | * @isp_ccdc: Pointer to current settings for ISP CCDC. | ||
246 | * @iommu: Pointer to requested IOMMU instance for ISP. | ||
247 | * @platform_cb: ISP driver callback function pointers for platform code | ||
248 | * | ||
249 | * This structure is used to store the OMAP ISP Information. | ||
250 | */ | ||
251 | struct isp_device { | ||
252 | struct v4l2_device v4l2_dev; | ||
253 | struct media_device media_dev; | ||
254 | struct device *dev; | ||
255 | u32 revision; | ||
256 | |||
257 | /* platform HW resources */ | ||
258 | struct isp_platform_data *pdata; | ||
259 | unsigned int irq_num; | ||
260 | |||
261 | void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST]; | ||
262 | unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST]; | ||
263 | resource_size_t mmio_size[OMAP3_ISP_IOMEM_LAST]; | ||
264 | |||
265 | u64 raw_dmamask; | ||
266 | |||
267 | /* ISP Obj */ | ||
268 | spinlock_t stat_lock; /* common lock for statistic drivers */ | ||
269 | struct mutex isp_mutex; /* For handling ref_count field */ | ||
270 | bool needs_reset; | ||
271 | int has_context; | ||
272 | int ref_count; | ||
273 | unsigned int autoidle; | ||
274 | u32 xclk_divisor[2]; /* Two clocks, a and b. */ | ||
275 | #define ISP_CLK_CAM_ICK 0 | ||
276 | #define ISP_CLK_CAM_MCLK 1 | ||
277 | #define ISP_CLK_DPLL4_M5_CK 2 | ||
278 | #define ISP_CLK_CSI2_FCK 3 | ||
279 | #define ISP_CLK_L3_ICK 4 | ||
280 | struct clk *clock[5]; | ||
281 | |||
282 | /* ISP modules */ | ||
283 | struct ispstat isp_af; | ||
284 | struct ispstat isp_aewb; | ||
285 | struct ispstat isp_hist; | ||
286 | struct isp_res_device isp_res; | ||
287 | struct isp_prev_device isp_prev; | ||
288 | struct isp_ccdc_device isp_ccdc; | ||
289 | struct isp_csi2_device isp_csi2a; | ||
290 | struct isp_csi2_device isp_csi2c; | ||
291 | struct isp_ccp2_device isp_ccp2; | ||
292 | struct isp_csiphy isp_csiphy1; | ||
293 | struct isp_csiphy isp_csiphy2; | ||
294 | |||
295 | unsigned int sbl_resources; | ||
296 | unsigned int subclk_resources; | ||
297 | |||
298 | struct iommu_domain *domain; | ||
299 | |||
300 | struct isp_platform_callback platform_cb; | ||
301 | }; | ||
302 | |||
303 | #define v4l2_dev_to_isp_device(dev) \ | ||
304 | container_of(dev, struct isp_device, v4l2_dev) | ||
305 | |||
306 | void omap3isp_hist_dma_done(struct isp_device *isp); | ||
307 | |||
308 | void omap3isp_flush(struct isp_device *isp); | ||
309 | |||
310 | int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, | ||
311 | atomic_t *stopping); | ||
312 | |||
313 | int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait, | ||
314 | atomic_t *stopping); | ||
315 | |||
316 | int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, | ||
317 | enum isp_pipeline_stream_state state); | ||
318 | void omap3isp_configure_bridge(struct isp_device *isp, | ||
319 | enum ccdc_input_entity input, | ||
320 | const struct isp_parallel_platform_data *pdata, | ||
321 | unsigned int shift); | ||
322 | |||
323 | #define ISP_XCLK_NONE 0 | ||
324 | #define ISP_XCLK_A 1 | ||
325 | #define ISP_XCLK_B 2 | ||
326 | |||
327 | struct isp_device *omap3isp_get(struct isp_device *isp); | ||
328 | void omap3isp_put(struct isp_device *isp); | ||
329 | |||
330 | void omap3isp_print_status(struct isp_device *isp); | ||
331 | |||
332 | void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res); | ||
333 | void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res); | ||
334 | |||
335 | void omap3isp_subclk_enable(struct isp_device *isp, | ||
336 | enum isp_subclk_resource res); | ||
337 | void omap3isp_subclk_disable(struct isp_device *isp, | ||
338 | enum isp_subclk_resource res); | ||
339 | |||
340 | int omap3isp_pipeline_pm_use(struct media_entity *entity, int use); | ||
341 | |||
342 | int omap3isp_register_entities(struct platform_device *pdev, | ||
343 | struct v4l2_device *v4l2_dev); | ||
344 | void omap3isp_unregister_entities(struct platform_device *pdev); | ||
345 | |||
346 | /* | ||
347 | * isp_reg_readl - Read value of an OMAP3 ISP register | ||
348 | * @dev: Device pointer specific to the OMAP3 ISP. | ||
349 | * @isp_mmio_range: Range to which the register offset refers to. | ||
350 | * @reg_offset: Register offset to read from. | ||
351 | * | ||
352 | * Returns an unsigned 32 bit value with the required register contents. | ||
353 | */ | ||
354 | static inline | ||
355 | u32 isp_reg_readl(struct isp_device *isp, enum isp_mem_resources isp_mmio_range, | ||
356 | u32 reg_offset) | ||
357 | { | ||
358 | return __raw_readl(isp->mmio_base[isp_mmio_range] + reg_offset); | ||
359 | } | ||
360 | |||
361 | /* | ||
362 | * isp_reg_writel - Write value to an OMAP3 ISP register | ||
363 | * @dev: Device pointer specific to the OMAP3 ISP. | ||
364 | * @reg_value: 32 bit value to write to the register. | ||
365 | * @isp_mmio_range: Range to which the register offset refers to. | ||
366 | * @reg_offset: Register offset to write into. | ||
367 | */ | ||
368 | static inline | ||
369 | void isp_reg_writel(struct isp_device *isp, u32 reg_value, | ||
370 | enum isp_mem_resources isp_mmio_range, u32 reg_offset) | ||
371 | { | ||
372 | __raw_writel(reg_value, isp->mmio_base[isp_mmio_range] + reg_offset); | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | * isp_reg_and - Clear individual bits in an OMAP3 ISP register | ||
377 | * @dev: Device pointer specific to the OMAP3 ISP. | ||
378 | * @mmio_range: Range to which the register offset refers to. | ||
379 | * @reg: Register offset to work on. | ||
380 | * @clr_bits: 32 bit value which would be cleared in the register. | ||
381 | */ | ||
382 | static inline | ||
383 | void isp_reg_clr(struct isp_device *isp, enum isp_mem_resources mmio_range, | ||
384 | u32 reg, u32 clr_bits) | ||
385 | { | ||
386 | u32 v = isp_reg_readl(isp, mmio_range, reg); | ||
387 | |||
388 | isp_reg_writel(isp, v & ~clr_bits, mmio_range, reg); | ||
389 | } | ||
390 | |||
391 | /* | ||
392 | * isp_reg_set - Set individual bits in an OMAP3 ISP register | ||
393 | * @dev: Device pointer specific to the OMAP3 ISP. | ||
394 | * @mmio_range: Range to which the register offset refers to. | ||
395 | * @reg: Register offset to work on. | ||
396 | * @set_bits: 32 bit value which would be set in the register. | ||
397 | */ | ||
398 | static inline | ||
399 | void isp_reg_set(struct isp_device *isp, enum isp_mem_resources mmio_range, | ||
400 | u32 reg, u32 set_bits) | ||
401 | { | ||
402 | u32 v = isp_reg_readl(isp, mmio_range, reg); | ||
403 | |||
404 | isp_reg_writel(isp, v | set_bits, mmio_range, reg); | ||
405 | } | ||
406 | |||
407 | /* | ||
408 | * isp_reg_clr_set - Clear and set invidial bits in an OMAP3 ISP register | ||
409 | * @dev: Device pointer specific to the OMAP3 ISP. | ||
410 | * @mmio_range: Range to which the register offset refers to. | ||
411 | * @reg: Register offset to work on. | ||
412 | * @clr_bits: 32 bit value which would be cleared in the register. | ||
413 | * @set_bits: 32 bit value which would be set in the register. | ||
414 | * | ||
415 | * The clear operation is done first, and then the set operation. | ||
416 | */ | ||
417 | static inline | ||
418 | void isp_reg_clr_set(struct isp_device *isp, enum isp_mem_resources mmio_range, | ||
419 | u32 reg, u32 clr_bits, u32 set_bits) | ||
420 | { | ||
421 | u32 v = isp_reg_readl(isp, mmio_range, reg); | ||
422 | |||
423 | isp_reg_writel(isp, (v & ~clr_bits) | set_bits, mmio_range, reg); | ||
424 | } | ||
425 | |||
426 | static inline enum v4l2_buf_type | ||
427 | isp_pad_buffer_type(const struct v4l2_subdev *subdev, int pad) | ||
428 | { | ||
429 | if (pad >= subdev->entity.num_pads) | ||
430 | return 0; | ||
431 | |||
432 | if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SINK) | ||
433 | return V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
434 | else | ||
435 | return V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
436 | } | ||
437 | |||
438 | #endif /* OMAP3_ISP_CORE_H */ | ||
diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c new file mode 100644 index 00000000000..8748e0855c6 --- /dev/null +++ b/drivers/media/video/omap3isp/ispccdc.c | |||
@@ -0,0 +1,2315 @@ | |||
1 | /* | ||
2 | * ispccdc.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - CCDC module | ||
5 | * | ||
6 | * Copyright (C) 2009-2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/uaccess.h> | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/device.h> | ||
31 | #include <linux/dma-mapping.h> | ||
32 | #include <linux/mm.h> | ||
33 | #include <linux/sched.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <media/v4l2-event.h> | ||
36 | |||
37 | #include "isp.h" | ||
38 | #include "ispreg.h" | ||
39 | #include "ispccdc.h" | ||
40 | |||
41 | static struct v4l2_mbus_framefmt * | ||
42 | __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | ||
43 | unsigned int pad, enum v4l2_subdev_format_whence which); | ||
44 | |||
45 | static const unsigned int ccdc_fmts[] = { | ||
46 | V4L2_MBUS_FMT_Y8_1X8, | ||
47 | V4L2_MBUS_FMT_Y10_1X10, | ||
48 | V4L2_MBUS_FMT_Y12_1X12, | ||
49 | V4L2_MBUS_FMT_SGRBG8_1X8, | ||
50 | V4L2_MBUS_FMT_SRGGB8_1X8, | ||
51 | V4L2_MBUS_FMT_SBGGR8_1X8, | ||
52 | V4L2_MBUS_FMT_SGBRG8_1X8, | ||
53 | V4L2_MBUS_FMT_SGRBG10_1X10, | ||
54 | V4L2_MBUS_FMT_SRGGB10_1X10, | ||
55 | V4L2_MBUS_FMT_SBGGR10_1X10, | ||
56 | V4L2_MBUS_FMT_SGBRG10_1X10, | ||
57 | V4L2_MBUS_FMT_SGRBG12_1X12, | ||
58 | V4L2_MBUS_FMT_SRGGB12_1X12, | ||
59 | V4L2_MBUS_FMT_SBGGR12_1X12, | ||
60 | V4L2_MBUS_FMT_SGBRG12_1X12, | ||
61 | }; | ||
62 | |||
63 | /* | ||
64 | * ccdc_print_status - Print current CCDC Module register values. | ||
65 | * @ccdc: Pointer to ISP CCDC device. | ||
66 | * | ||
67 | * Also prints other debug information stored in the CCDC module. | ||
68 | */ | ||
69 | #define CCDC_PRINT_REGISTER(isp, name)\ | ||
70 | dev_dbg(isp->dev, "###CCDC " #name "=0x%08x\n", \ | ||
71 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_##name)) | ||
72 | |||
73 | static void ccdc_print_status(struct isp_ccdc_device *ccdc) | ||
74 | { | ||
75 | struct isp_device *isp = to_isp_device(ccdc); | ||
76 | |||
77 | dev_dbg(isp->dev, "-------------CCDC Register dump-------------\n"); | ||
78 | |||
79 | CCDC_PRINT_REGISTER(isp, PCR); | ||
80 | CCDC_PRINT_REGISTER(isp, SYN_MODE); | ||
81 | CCDC_PRINT_REGISTER(isp, HD_VD_WID); | ||
82 | CCDC_PRINT_REGISTER(isp, PIX_LINES); | ||
83 | CCDC_PRINT_REGISTER(isp, HORZ_INFO); | ||
84 | CCDC_PRINT_REGISTER(isp, VERT_START); | ||
85 | CCDC_PRINT_REGISTER(isp, VERT_LINES); | ||
86 | CCDC_PRINT_REGISTER(isp, CULLING); | ||
87 | CCDC_PRINT_REGISTER(isp, HSIZE_OFF); | ||
88 | CCDC_PRINT_REGISTER(isp, SDOFST); | ||
89 | CCDC_PRINT_REGISTER(isp, SDR_ADDR); | ||
90 | CCDC_PRINT_REGISTER(isp, CLAMP); | ||
91 | CCDC_PRINT_REGISTER(isp, DCSUB); | ||
92 | CCDC_PRINT_REGISTER(isp, COLPTN); | ||
93 | CCDC_PRINT_REGISTER(isp, BLKCMP); | ||
94 | CCDC_PRINT_REGISTER(isp, FPC); | ||
95 | CCDC_PRINT_REGISTER(isp, FPC_ADDR); | ||
96 | CCDC_PRINT_REGISTER(isp, VDINT); | ||
97 | CCDC_PRINT_REGISTER(isp, ALAW); | ||
98 | CCDC_PRINT_REGISTER(isp, REC656IF); | ||
99 | CCDC_PRINT_REGISTER(isp, CFG); | ||
100 | CCDC_PRINT_REGISTER(isp, FMTCFG); | ||
101 | CCDC_PRINT_REGISTER(isp, FMT_HORZ); | ||
102 | CCDC_PRINT_REGISTER(isp, FMT_VERT); | ||
103 | CCDC_PRINT_REGISTER(isp, PRGEVEN0); | ||
104 | CCDC_PRINT_REGISTER(isp, PRGEVEN1); | ||
105 | CCDC_PRINT_REGISTER(isp, PRGODD0); | ||
106 | CCDC_PRINT_REGISTER(isp, PRGODD1); | ||
107 | CCDC_PRINT_REGISTER(isp, VP_OUT); | ||
108 | CCDC_PRINT_REGISTER(isp, LSC_CONFIG); | ||
109 | CCDC_PRINT_REGISTER(isp, LSC_INITIAL); | ||
110 | CCDC_PRINT_REGISTER(isp, LSC_TABLE_BASE); | ||
111 | CCDC_PRINT_REGISTER(isp, LSC_TABLE_OFFSET); | ||
112 | |||
113 | dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
114 | } | ||
115 | |||
116 | /* | ||
117 | * omap3isp_ccdc_busy - Get busy state of the CCDC. | ||
118 | * @ccdc: Pointer to ISP CCDC device. | ||
119 | */ | ||
120 | int omap3isp_ccdc_busy(struct isp_ccdc_device *ccdc) | ||
121 | { | ||
122 | struct isp_device *isp = to_isp_device(ccdc); | ||
123 | |||
124 | return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR) & | ||
125 | ISPCCDC_PCR_BUSY; | ||
126 | } | ||
127 | |||
128 | /* ----------------------------------------------------------------------------- | ||
129 | * Lens Shading Compensation | ||
130 | */ | ||
131 | |||
132 | /* | ||
133 | * ccdc_lsc_validate_config - Check that LSC configuration is valid. | ||
134 | * @ccdc: Pointer to ISP CCDC device. | ||
135 | * @lsc_cfg: the LSC configuration to check. | ||
136 | * | ||
137 | * Returns 0 if the LSC configuration is valid, or -EINVAL if invalid. | ||
138 | */ | ||
139 | static int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc, | ||
140 | struct omap3isp_ccdc_lsc_config *lsc_cfg) | ||
141 | { | ||
142 | struct isp_device *isp = to_isp_device(ccdc); | ||
143 | struct v4l2_mbus_framefmt *format; | ||
144 | unsigned int paxel_width, paxel_height; | ||
145 | unsigned int paxel_shift_x, paxel_shift_y; | ||
146 | unsigned int min_width, min_height, min_size; | ||
147 | unsigned int input_width, input_height; | ||
148 | |||
149 | paxel_shift_x = lsc_cfg->gain_mode_m; | ||
150 | paxel_shift_y = lsc_cfg->gain_mode_n; | ||
151 | |||
152 | if ((paxel_shift_x < 2) || (paxel_shift_x > 6) || | ||
153 | (paxel_shift_y < 2) || (paxel_shift_y > 6)) { | ||
154 | dev_dbg(isp->dev, "CCDC: LSC: Invalid paxel size\n"); | ||
155 | return -EINVAL; | ||
156 | } | ||
157 | |||
158 | if (lsc_cfg->offset & 3) { | ||
159 | dev_dbg(isp->dev, "CCDC: LSC: Offset must be a multiple of " | ||
160 | "4\n"); | ||
161 | return -EINVAL; | ||
162 | } | ||
163 | |||
164 | if ((lsc_cfg->initial_x & 1) || (lsc_cfg->initial_y & 1)) { | ||
165 | dev_dbg(isp->dev, "CCDC: LSC: initial_x and y must be even\n"); | ||
166 | return -EINVAL; | ||
167 | } | ||
168 | |||
169 | format = __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK, | ||
170 | V4L2_SUBDEV_FORMAT_ACTIVE); | ||
171 | input_width = format->width; | ||
172 | input_height = format->height; | ||
173 | |||
174 | /* Calculate minimum bytesize for validation */ | ||
175 | paxel_width = 1 << paxel_shift_x; | ||
176 | min_width = ((input_width + lsc_cfg->initial_x + paxel_width - 1) | ||
177 | >> paxel_shift_x) + 1; | ||
178 | |||
179 | paxel_height = 1 << paxel_shift_y; | ||
180 | min_height = ((input_height + lsc_cfg->initial_y + paxel_height - 1) | ||
181 | >> paxel_shift_y) + 1; | ||
182 | |||
183 | min_size = 4 * min_width * min_height; | ||
184 | if (min_size > lsc_cfg->size) { | ||
185 | dev_dbg(isp->dev, "CCDC: LSC: too small table\n"); | ||
186 | return -EINVAL; | ||
187 | } | ||
188 | if (lsc_cfg->offset < (min_width * 4)) { | ||
189 | dev_dbg(isp->dev, "CCDC: LSC: Offset is too small\n"); | ||
190 | return -EINVAL; | ||
191 | } | ||
192 | if ((lsc_cfg->size / lsc_cfg->offset) < min_height) { | ||
193 | dev_dbg(isp->dev, "CCDC: LSC: Wrong size/offset combination\n"); | ||
194 | return -EINVAL; | ||
195 | } | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | /* | ||
200 | * ccdc_lsc_program_table - Program Lens Shading Compensation table address. | ||
201 | * @ccdc: Pointer to ISP CCDC device. | ||
202 | */ | ||
203 | static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr) | ||
204 | { | ||
205 | isp_reg_writel(to_isp_device(ccdc), addr, | ||
206 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE); | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | * ccdc_lsc_setup_regs - Configures the lens shading compensation module | ||
211 | * @ccdc: Pointer to ISP CCDC device. | ||
212 | */ | ||
213 | static void ccdc_lsc_setup_regs(struct isp_ccdc_device *ccdc, | ||
214 | struct omap3isp_ccdc_lsc_config *cfg) | ||
215 | { | ||
216 | struct isp_device *isp = to_isp_device(ccdc); | ||
217 | int reg; | ||
218 | |||
219 | isp_reg_writel(isp, cfg->offset, OMAP3_ISP_IOMEM_CCDC, | ||
220 | ISPCCDC_LSC_TABLE_OFFSET); | ||
221 | |||
222 | reg = 0; | ||
223 | reg |= cfg->gain_mode_n << ISPCCDC_LSC_GAIN_MODE_N_SHIFT; | ||
224 | reg |= cfg->gain_mode_m << ISPCCDC_LSC_GAIN_MODE_M_SHIFT; | ||
225 | reg |= cfg->gain_format << ISPCCDC_LSC_GAIN_FORMAT_SHIFT; | ||
226 | isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG); | ||
227 | |||
228 | reg = 0; | ||
229 | reg &= ~ISPCCDC_LSC_INITIAL_X_MASK; | ||
230 | reg |= cfg->initial_x << ISPCCDC_LSC_INITIAL_X_SHIFT; | ||
231 | reg &= ~ISPCCDC_LSC_INITIAL_Y_MASK; | ||
232 | reg |= cfg->initial_y << ISPCCDC_LSC_INITIAL_Y_SHIFT; | ||
233 | isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, | ||
234 | ISPCCDC_LSC_INITIAL); | ||
235 | } | ||
236 | |||
237 | static int ccdc_lsc_wait_prefetch(struct isp_ccdc_device *ccdc) | ||
238 | { | ||
239 | struct isp_device *isp = to_isp_device(ccdc); | ||
240 | unsigned int wait; | ||
241 | |||
242 | isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ, | ||
243 | OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); | ||
244 | |||
245 | /* timeout 1 ms */ | ||
246 | for (wait = 0; wait < 1000; wait++) { | ||
247 | if (isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS) & | ||
248 | IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ) { | ||
249 | isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ, | ||
250 | OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | rmb(); | ||
255 | udelay(1); | ||
256 | } | ||
257 | |||
258 | return -ETIMEDOUT; | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * __ccdc_lsc_enable - Enables/Disables the Lens Shading Compensation module. | ||
263 | * @ccdc: Pointer to ISP CCDC device. | ||
264 | * @enable: 0 Disables LSC, 1 Enables LSC. | ||
265 | */ | ||
266 | static int __ccdc_lsc_enable(struct isp_ccdc_device *ccdc, int enable) | ||
267 | { | ||
268 | struct isp_device *isp = to_isp_device(ccdc); | ||
269 | const struct v4l2_mbus_framefmt *format = | ||
270 | __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK, | ||
271 | V4L2_SUBDEV_FORMAT_ACTIVE); | ||
272 | |||
273 | if ((format->code != V4L2_MBUS_FMT_SGRBG10_1X10) && | ||
274 | (format->code != V4L2_MBUS_FMT_SRGGB10_1X10) && | ||
275 | (format->code != V4L2_MBUS_FMT_SBGGR10_1X10) && | ||
276 | (format->code != V4L2_MBUS_FMT_SGBRG10_1X10)) | ||
277 | return -EINVAL; | ||
278 | |||
279 | if (enable) | ||
280 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_LSC_READ); | ||
281 | |||
282 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, | ||
283 | ISPCCDC_LSC_ENABLE, enable ? ISPCCDC_LSC_ENABLE : 0); | ||
284 | |||
285 | if (enable) { | ||
286 | if (ccdc_lsc_wait_prefetch(ccdc) < 0) { | ||
287 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, | ||
288 | ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE); | ||
289 | ccdc->lsc.state = LSC_STATE_STOPPED; | ||
290 | dev_warn(to_device(ccdc), "LSC prefecth timeout\n"); | ||
291 | return -ETIMEDOUT; | ||
292 | } | ||
293 | ccdc->lsc.state = LSC_STATE_RUNNING; | ||
294 | } else { | ||
295 | ccdc->lsc.state = LSC_STATE_STOPPING; | ||
296 | } | ||
297 | |||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | static int ccdc_lsc_busy(struct isp_ccdc_device *ccdc) | ||
302 | { | ||
303 | struct isp_device *isp = to_isp_device(ccdc); | ||
304 | |||
305 | return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG) & | ||
306 | ISPCCDC_LSC_BUSY; | ||
307 | } | ||
308 | |||
309 | /* __ccdc_lsc_configure - Apply a new configuration to the LSC engine | ||
310 | * @ccdc: Pointer to ISP CCDC device | ||
311 | * @req: New configuration request | ||
312 | * | ||
313 | * context: in_interrupt() | ||
314 | */ | ||
315 | static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc, | ||
316 | struct ispccdc_lsc_config_req *req) | ||
317 | { | ||
318 | if (!req->enable) | ||
319 | return -EINVAL; | ||
320 | |||
321 | if (ccdc_lsc_validate_config(ccdc, &req->config) < 0) { | ||
322 | dev_dbg(to_device(ccdc), "Discard LSC configuration\n"); | ||
323 | return -EINVAL; | ||
324 | } | ||
325 | |||
326 | if (ccdc_lsc_busy(ccdc)) | ||
327 | return -EBUSY; | ||
328 | |||
329 | ccdc_lsc_setup_regs(ccdc, &req->config); | ||
330 | ccdc_lsc_program_table(ccdc, req->table); | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | /* | ||
335 | * ccdc_lsc_error_handler - Handle LSC prefetch error scenario. | ||
336 | * @ccdc: Pointer to ISP CCDC device. | ||
337 | * | ||
338 | * Disables LSC, and defers enablement to shadow registers update time. | ||
339 | */ | ||
340 | static void ccdc_lsc_error_handler(struct isp_ccdc_device *ccdc) | ||
341 | { | ||
342 | struct isp_device *isp = to_isp_device(ccdc); | ||
343 | /* | ||
344 | * From OMAP3 TRM: When this event is pending, the module | ||
345 | * goes into transparent mode (output =input). Normal | ||
346 | * operation can be resumed at the start of the next frame | ||
347 | * after: | ||
348 | * 1) Clearing this event | ||
349 | * 2) Disabling the LSC module | ||
350 | * 3) Enabling it | ||
351 | */ | ||
352 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, | ||
353 | ISPCCDC_LSC_ENABLE); | ||
354 | ccdc->lsc.state = LSC_STATE_STOPPED; | ||
355 | } | ||
356 | |||
357 | static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc, | ||
358 | struct ispccdc_lsc_config_req *req) | ||
359 | { | ||
360 | struct isp_device *isp = to_isp_device(ccdc); | ||
361 | |||
362 | if (req == NULL) | ||
363 | return; | ||
364 | |||
365 | if (req->iovm) | ||
366 | dma_unmap_sg(isp->dev, req->iovm->sgt->sgl, | ||
367 | req->iovm->sgt->nents, DMA_TO_DEVICE); | ||
368 | if (req->table) | ||
369 | omap_iommu_vfree(isp->domain, isp->dev, req->table); | ||
370 | kfree(req); | ||
371 | } | ||
372 | |||
373 | static void ccdc_lsc_free_queue(struct isp_ccdc_device *ccdc, | ||
374 | struct list_head *queue) | ||
375 | { | ||
376 | struct ispccdc_lsc_config_req *req, *n; | ||
377 | unsigned long flags; | ||
378 | |||
379 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
380 | list_for_each_entry_safe(req, n, queue, list) { | ||
381 | list_del(&req->list); | ||
382 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
383 | ccdc_lsc_free_request(ccdc, req); | ||
384 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
385 | } | ||
386 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
387 | } | ||
388 | |||
389 | static void ccdc_lsc_free_table_work(struct work_struct *work) | ||
390 | { | ||
391 | struct isp_ccdc_device *ccdc; | ||
392 | struct ispccdc_lsc *lsc; | ||
393 | |||
394 | lsc = container_of(work, struct ispccdc_lsc, table_work); | ||
395 | ccdc = container_of(lsc, struct isp_ccdc_device, lsc); | ||
396 | |||
397 | ccdc_lsc_free_queue(ccdc, &lsc->free_queue); | ||
398 | } | ||
399 | |||
400 | /* | ||
401 | * ccdc_lsc_config - Configure the LSC module from a userspace request | ||
402 | * | ||
403 | * Store the request LSC configuration in the LSC engine request pointer. The | ||
404 | * configuration will be applied to the hardware when the CCDC will be enabled, | ||
405 | * or at the next LSC interrupt if the CCDC is already running. | ||
406 | */ | ||
407 | static int ccdc_lsc_config(struct isp_ccdc_device *ccdc, | ||
408 | struct omap3isp_ccdc_update_config *config) | ||
409 | { | ||
410 | struct isp_device *isp = to_isp_device(ccdc); | ||
411 | struct ispccdc_lsc_config_req *req; | ||
412 | unsigned long flags; | ||
413 | void *table; | ||
414 | u16 update; | ||
415 | int ret; | ||
416 | |||
417 | update = config->update & | ||
418 | (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC); | ||
419 | if (!update) | ||
420 | return 0; | ||
421 | |||
422 | if (update != (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC)) { | ||
423 | dev_dbg(to_device(ccdc), "%s: Both LSC configuration and table " | ||
424 | "need to be supplied\n", __func__); | ||
425 | return -EINVAL; | ||
426 | } | ||
427 | |||
428 | req = kzalloc(sizeof(*req), GFP_KERNEL); | ||
429 | if (req == NULL) | ||
430 | return -ENOMEM; | ||
431 | |||
432 | if (config->flag & OMAP3ISP_CCDC_CONFIG_LSC) { | ||
433 | if (copy_from_user(&req->config, config->lsc_cfg, | ||
434 | sizeof(req->config))) { | ||
435 | ret = -EFAULT; | ||
436 | goto done; | ||
437 | } | ||
438 | |||
439 | req->enable = 1; | ||
440 | |||
441 | req->table = omap_iommu_vmalloc(isp->domain, isp->dev, 0, | ||
442 | req->config.size, IOMMU_FLAG); | ||
443 | if (IS_ERR_VALUE(req->table)) { | ||
444 | req->table = 0; | ||
445 | ret = -ENOMEM; | ||
446 | goto done; | ||
447 | } | ||
448 | |||
449 | req->iovm = omap_find_iovm_area(isp->dev, req->table); | ||
450 | if (req->iovm == NULL) { | ||
451 | ret = -ENOMEM; | ||
452 | goto done; | ||
453 | } | ||
454 | |||
455 | if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl, | ||
456 | req->iovm->sgt->nents, DMA_TO_DEVICE)) { | ||
457 | ret = -ENOMEM; | ||
458 | req->iovm = NULL; | ||
459 | goto done; | ||
460 | } | ||
461 | |||
462 | dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl, | ||
463 | req->iovm->sgt->nents, DMA_TO_DEVICE); | ||
464 | |||
465 | table = omap_da_to_va(isp->dev, req->table); | ||
466 | if (copy_from_user(table, config->lsc, req->config.size)) { | ||
467 | ret = -EFAULT; | ||
468 | goto done; | ||
469 | } | ||
470 | |||
471 | dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl, | ||
472 | req->iovm->sgt->nents, DMA_TO_DEVICE); | ||
473 | } | ||
474 | |||
475 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
476 | if (ccdc->lsc.request) { | ||
477 | list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue); | ||
478 | schedule_work(&ccdc->lsc.table_work); | ||
479 | } | ||
480 | ccdc->lsc.request = req; | ||
481 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
482 | |||
483 | ret = 0; | ||
484 | |||
485 | done: | ||
486 | if (ret < 0) | ||
487 | ccdc_lsc_free_request(ccdc, req); | ||
488 | |||
489 | return ret; | ||
490 | } | ||
491 | |||
492 | static inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc) | ||
493 | { | ||
494 | unsigned long flags; | ||
495 | |||
496 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
497 | if (ccdc->lsc.active) { | ||
498 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
499 | return 1; | ||
500 | } | ||
501 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | static int ccdc_lsc_enable(struct isp_ccdc_device *ccdc) | ||
506 | { | ||
507 | struct ispccdc_lsc *lsc = &ccdc->lsc; | ||
508 | |||
509 | if (lsc->state != LSC_STATE_STOPPED) | ||
510 | return -EINVAL; | ||
511 | |||
512 | if (lsc->active) { | ||
513 | list_add_tail(&lsc->active->list, &lsc->free_queue); | ||
514 | lsc->active = NULL; | ||
515 | } | ||
516 | |||
517 | if (__ccdc_lsc_configure(ccdc, lsc->request) < 0) { | ||
518 | omap3isp_sbl_disable(to_isp_device(ccdc), | ||
519 | OMAP3_ISP_SBL_CCDC_LSC_READ); | ||
520 | list_add_tail(&lsc->request->list, &lsc->free_queue); | ||
521 | lsc->request = NULL; | ||
522 | goto done; | ||
523 | } | ||
524 | |||
525 | lsc->active = lsc->request; | ||
526 | lsc->request = NULL; | ||
527 | __ccdc_lsc_enable(ccdc, 1); | ||
528 | |||
529 | done: | ||
530 | if (!list_empty(&lsc->free_queue)) | ||
531 | schedule_work(&lsc->table_work); | ||
532 | |||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | /* ----------------------------------------------------------------------------- | ||
537 | * Parameters configuration | ||
538 | */ | ||
539 | |||
540 | /* | ||
541 | * ccdc_configure_clamp - Configure optical-black or digital clamping | ||
542 | * @ccdc: Pointer to ISP CCDC device. | ||
543 | * | ||
544 | * The CCDC performs either optical-black or digital clamp. Configure and enable | ||
545 | * the selected clamp method. | ||
546 | */ | ||
547 | static void ccdc_configure_clamp(struct isp_ccdc_device *ccdc) | ||
548 | { | ||
549 | struct isp_device *isp = to_isp_device(ccdc); | ||
550 | u32 clamp; | ||
551 | |||
552 | if (ccdc->obclamp) { | ||
553 | clamp = ccdc->clamp.obgain << ISPCCDC_CLAMP_OBGAIN_SHIFT; | ||
554 | clamp |= ccdc->clamp.oblen << ISPCCDC_CLAMP_OBSLEN_SHIFT; | ||
555 | clamp |= ccdc->clamp.oblines << ISPCCDC_CLAMP_OBSLN_SHIFT; | ||
556 | clamp |= ccdc->clamp.obstpixel << ISPCCDC_CLAMP_OBST_SHIFT; | ||
557 | isp_reg_writel(isp, clamp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP); | ||
558 | } else { | ||
559 | isp_reg_writel(isp, ccdc->clamp.dcsubval, | ||
560 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB); | ||
561 | } | ||
562 | |||
563 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP, | ||
564 | ISPCCDC_CLAMP_CLAMPEN, | ||
565 | ccdc->obclamp ? ISPCCDC_CLAMP_CLAMPEN : 0); | ||
566 | } | ||
567 | |||
568 | /* | ||
569 | * ccdc_configure_fpc - Configure Faulty Pixel Correction | ||
570 | * @ccdc: Pointer to ISP CCDC device. | ||
571 | */ | ||
572 | static void ccdc_configure_fpc(struct isp_ccdc_device *ccdc) | ||
573 | { | ||
574 | struct isp_device *isp = to_isp_device(ccdc); | ||
575 | |||
576 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, ISPCCDC_FPC_FPCEN); | ||
577 | |||
578 | if (!ccdc->fpc_en) | ||
579 | return; | ||
580 | |||
581 | isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC, | ||
582 | ISPCCDC_FPC_ADDR); | ||
583 | /* The FPNUM field must be set before enabling FPC. */ | ||
584 | isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT), | ||
585 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); | ||
586 | isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT) | | ||
587 | ISPCCDC_FPC_FPCEN, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); | ||
588 | } | ||
589 | |||
590 | /* | ||
591 | * ccdc_configure_black_comp - Configure Black Level Compensation. | ||
592 | * @ccdc: Pointer to ISP CCDC device. | ||
593 | */ | ||
594 | static void ccdc_configure_black_comp(struct isp_ccdc_device *ccdc) | ||
595 | { | ||
596 | struct isp_device *isp = to_isp_device(ccdc); | ||
597 | u32 blcomp; | ||
598 | |||
599 | blcomp = ccdc->blcomp.b_mg << ISPCCDC_BLKCMP_B_MG_SHIFT; | ||
600 | blcomp |= ccdc->blcomp.gb_g << ISPCCDC_BLKCMP_GB_G_SHIFT; | ||
601 | blcomp |= ccdc->blcomp.gr_cy << ISPCCDC_BLKCMP_GR_CY_SHIFT; | ||
602 | blcomp |= ccdc->blcomp.r_ye << ISPCCDC_BLKCMP_R_YE_SHIFT; | ||
603 | |||
604 | isp_reg_writel(isp, blcomp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP); | ||
605 | } | ||
606 | |||
607 | /* | ||
608 | * ccdc_configure_lpf - Configure Low-Pass Filter (LPF). | ||
609 | * @ccdc: Pointer to ISP CCDC device. | ||
610 | */ | ||
611 | static void ccdc_configure_lpf(struct isp_ccdc_device *ccdc) | ||
612 | { | ||
613 | struct isp_device *isp = to_isp_device(ccdc); | ||
614 | |||
615 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE, | ||
616 | ISPCCDC_SYN_MODE_LPF, | ||
617 | ccdc->lpf ? ISPCCDC_SYN_MODE_LPF : 0); | ||
618 | } | ||
619 | |||
620 | /* | ||
621 | * ccdc_configure_alaw - Configure A-law compression. | ||
622 | * @ccdc: Pointer to ISP CCDC device. | ||
623 | */ | ||
624 | static void ccdc_configure_alaw(struct isp_ccdc_device *ccdc) | ||
625 | { | ||
626 | struct isp_device *isp = to_isp_device(ccdc); | ||
627 | u32 alaw = 0; | ||
628 | |||
629 | switch (ccdc->syncif.datsz) { | ||
630 | case 8: | ||
631 | return; | ||
632 | |||
633 | case 10: | ||
634 | alaw = ISPCCDC_ALAW_GWDI_9_0; | ||
635 | break; | ||
636 | case 11: | ||
637 | alaw = ISPCCDC_ALAW_GWDI_10_1; | ||
638 | break; | ||
639 | case 12: | ||
640 | alaw = ISPCCDC_ALAW_GWDI_11_2; | ||
641 | break; | ||
642 | case 13: | ||
643 | alaw = ISPCCDC_ALAW_GWDI_12_3; | ||
644 | break; | ||
645 | } | ||
646 | |||
647 | if (ccdc->alaw) | ||
648 | alaw |= ISPCCDC_ALAW_CCDTBL; | ||
649 | |||
650 | isp_reg_writel(isp, alaw, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW); | ||
651 | } | ||
652 | |||
653 | /* | ||
654 | * ccdc_config_imgattr - Configure sensor image specific attributes. | ||
655 | * @ccdc: Pointer to ISP CCDC device. | ||
656 | * @colptn: Color pattern of the sensor. | ||
657 | */ | ||
658 | static void ccdc_config_imgattr(struct isp_ccdc_device *ccdc, u32 colptn) | ||
659 | { | ||
660 | struct isp_device *isp = to_isp_device(ccdc); | ||
661 | |||
662 | isp_reg_writel(isp, colptn, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN); | ||
663 | } | ||
664 | |||
665 | /* | ||
666 | * ccdc_config - Set CCDC configuration from userspace | ||
667 | * @ccdc: Pointer to ISP CCDC device. | ||
668 | * @userspace_add: Structure containing CCDC configuration sent from userspace. | ||
669 | * | ||
670 | * Returns 0 if successful, -EINVAL if the pointer to the configuration | ||
671 | * structure is null, or the copy_from_user function fails to copy user space | ||
672 | * memory to kernel space memory. | ||
673 | */ | ||
674 | static int ccdc_config(struct isp_ccdc_device *ccdc, | ||
675 | struct omap3isp_ccdc_update_config *ccdc_struct) | ||
676 | { | ||
677 | struct isp_device *isp = to_isp_device(ccdc); | ||
678 | unsigned long flags; | ||
679 | |||
680 | spin_lock_irqsave(&ccdc->lock, flags); | ||
681 | ccdc->shadow_update = 1; | ||
682 | spin_unlock_irqrestore(&ccdc->lock, flags); | ||
683 | |||
684 | if (OMAP3ISP_CCDC_ALAW & ccdc_struct->update) { | ||
685 | ccdc->alaw = !!(OMAP3ISP_CCDC_ALAW & ccdc_struct->flag); | ||
686 | ccdc->update |= OMAP3ISP_CCDC_ALAW; | ||
687 | } | ||
688 | |||
689 | if (OMAP3ISP_CCDC_LPF & ccdc_struct->update) { | ||
690 | ccdc->lpf = !!(OMAP3ISP_CCDC_LPF & ccdc_struct->flag); | ||
691 | ccdc->update |= OMAP3ISP_CCDC_LPF; | ||
692 | } | ||
693 | |||
694 | if (OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->update) { | ||
695 | if (copy_from_user(&ccdc->clamp, ccdc_struct->bclamp, | ||
696 | sizeof(ccdc->clamp))) { | ||
697 | ccdc->shadow_update = 0; | ||
698 | return -EFAULT; | ||
699 | } | ||
700 | |||
701 | ccdc->obclamp = !!(OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->flag); | ||
702 | ccdc->update |= OMAP3ISP_CCDC_BLCLAMP; | ||
703 | } | ||
704 | |||
705 | if (OMAP3ISP_CCDC_BCOMP & ccdc_struct->update) { | ||
706 | if (copy_from_user(&ccdc->blcomp, ccdc_struct->blcomp, | ||
707 | sizeof(ccdc->blcomp))) { | ||
708 | ccdc->shadow_update = 0; | ||
709 | return -EFAULT; | ||
710 | } | ||
711 | |||
712 | ccdc->update |= OMAP3ISP_CCDC_BCOMP; | ||
713 | } | ||
714 | |||
715 | ccdc->shadow_update = 0; | ||
716 | |||
717 | if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) { | ||
718 | u32 table_old = 0; | ||
719 | u32 table_new; | ||
720 | u32 size; | ||
721 | |||
722 | if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED) | ||
723 | return -EBUSY; | ||
724 | |||
725 | ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag); | ||
726 | |||
727 | if (ccdc->fpc_en) { | ||
728 | if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc, | ||
729 | sizeof(ccdc->fpc))) | ||
730 | return -EFAULT; | ||
731 | |||
732 | /* | ||
733 | * table_new must be 64-bytes aligned, but it's | ||
734 | * already done by omap_iommu_vmalloc(). | ||
735 | */ | ||
736 | size = ccdc->fpc.fpnum * 4; | ||
737 | table_new = omap_iommu_vmalloc(isp->domain, isp->dev, | ||
738 | 0, size, IOMMU_FLAG); | ||
739 | if (IS_ERR_VALUE(table_new)) | ||
740 | return -ENOMEM; | ||
741 | |||
742 | if (copy_from_user(omap_da_to_va(isp->dev, table_new), | ||
743 | (__force void __user *) | ||
744 | ccdc->fpc.fpcaddr, size)) { | ||
745 | omap_iommu_vfree(isp->domain, isp->dev, | ||
746 | table_new); | ||
747 | return -EFAULT; | ||
748 | } | ||
749 | |||
750 | table_old = ccdc->fpc.fpcaddr; | ||
751 | ccdc->fpc.fpcaddr = table_new; | ||
752 | } | ||
753 | |||
754 | ccdc_configure_fpc(ccdc); | ||
755 | if (table_old != 0) | ||
756 | omap_iommu_vfree(isp->domain, isp->dev, table_old); | ||
757 | } | ||
758 | |||
759 | return ccdc_lsc_config(ccdc, ccdc_struct); | ||
760 | } | ||
761 | |||
762 | static void ccdc_apply_controls(struct isp_ccdc_device *ccdc) | ||
763 | { | ||
764 | if (ccdc->update & OMAP3ISP_CCDC_ALAW) { | ||
765 | ccdc_configure_alaw(ccdc); | ||
766 | ccdc->update &= ~OMAP3ISP_CCDC_ALAW; | ||
767 | } | ||
768 | |||
769 | if (ccdc->update & OMAP3ISP_CCDC_LPF) { | ||
770 | ccdc_configure_lpf(ccdc); | ||
771 | ccdc->update &= ~OMAP3ISP_CCDC_LPF; | ||
772 | } | ||
773 | |||
774 | if (ccdc->update & OMAP3ISP_CCDC_BLCLAMP) { | ||
775 | ccdc_configure_clamp(ccdc); | ||
776 | ccdc->update &= ~OMAP3ISP_CCDC_BLCLAMP; | ||
777 | } | ||
778 | |||
779 | if (ccdc->update & OMAP3ISP_CCDC_BCOMP) { | ||
780 | ccdc_configure_black_comp(ccdc); | ||
781 | ccdc->update &= ~OMAP3ISP_CCDC_BCOMP; | ||
782 | } | ||
783 | } | ||
784 | |||
785 | /* | ||
786 | * omap3isp_ccdc_restore_context - Restore values of the CCDC module registers | ||
787 | * @dev: Pointer to ISP device | ||
788 | */ | ||
789 | void omap3isp_ccdc_restore_context(struct isp_device *isp) | ||
790 | { | ||
791 | struct isp_ccdc_device *ccdc = &isp->isp_ccdc; | ||
792 | |||
793 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_VDLC); | ||
794 | |||
795 | ccdc->update = OMAP3ISP_CCDC_ALAW | OMAP3ISP_CCDC_LPF | ||
796 | | OMAP3ISP_CCDC_BLCLAMP | OMAP3ISP_CCDC_BCOMP; | ||
797 | ccdc_apply_controls(ccdc); | ||
798 | ccdc_configure_fpc(ccdc); | ||
799 | } | ||
800 | |||
801 | /* ----------------------------------------------------------------------------- | ||
802 | * Format- and pipeline-related configuration helpers | ||
803 | */ | ||
804 | |||
805 | /* | ||
806 | * ccdc_config_vp - Configure the Video Port. | ||
807 | * @ccdc: Pointer to ISP CCDC device. | ||
808 | */ | ||
809 | static void ccdc_config_vp(struct isp_ccdc_device *ccdc) | ||
810 | { | ||
811 | struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); | ||
812 | struct isp_device *isp = to_isp_device(ccdc); | ||
813 | unsigned long l3_ick = pipe->l3_ick; | ||
814 | unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8; | ||
815 | unsigned int div = 0; | ||
816 | u32 fmtcfg_vp; | ||
817 | |||
818 | fmtcfg_vp = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG) | ||
819 | & ~(ISPCCDC_FMTCFG_VPIN_MASK | ISPCCDC_FMTCFG_VPIF_FRQ_MASK); | ||
820 | |||
821 | switch (ccdc->syncif.datsz) { | ||
822 | case 8: | ||
823 | case 10: | ||
824 | fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0; | ||
825 | break; | ||
826 | case 11: | ||
827 | fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1; | ||
828 | break; | ||
829 | case 12: | ||
830 | fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2; | ||
831 | break; | ||
832 | case 13: | ||
833 | fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3; | ||
834 | break; | ||
835 | }; | ||
836 | |||
837 | if (pipe->input) | ||
838 | div = DIV_ROUND_UP(l3_ick, pipe->max_rate); | ||
839 | else if (ccdc->vpcfg.pixelclk) | ||
840 | div = l3_ick / ccdc->vpcfg.pixelclk; | ||
841 | |||
842 | div = clamp(div, 2U, max_div); | ||
843 | fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; | ||
844 | |||
845 | isp_reg_writel(isp, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); | ||
846 | } | ||
847 | |||
848 | /* | ||
849 | * ccdc_enable_vp - Enable Video Port. | ||
850 | * @ccdc: Pointer to ISP CCDC device. | ||
851 | * @enable: 0 Disables VP, 1 Enables VP | ||
852 | * | ||
853 | * This is needed for outputting image to Preview, H3A and HIST ISP submodules. | ||
854 | */ | ||
855 | static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable) | ||
856 | { | ||
857 | struct isp_device *isp = to_isp_device(ccdc); | ||
858 | |||
859 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, | ||
860 | ISPCCDC_FMTCFG_VPEN, enable ? ISPCCDC_FMTCFG_VPEN : 0); | ||
861 | } | ||
862 | |||
863 | /* | ||
864 | * ccdc_config_outlineoffset - Configure memory saving output line offset | ||
865 | * @ccdc: Pointer to ISP CCDC device. | ||
866 | * @offset: Address offset to start a new line. Must be twice the | ||
867 | * Output width and aligned on 32 byte boundary | ||
868 | * @oddeven: Specifies the odd/even line pattern to be chosen to store the | ||
869 | * output. | ||
870 | * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines. | ||
871 | * | ||
872 | * - Configures the output line offset when stored in memory | ||
873 | * - Sets the odd/even line pattern to store the output | ||
874 | * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4)) | ||
875 | * - Configures the number of even and odd line fields in case of rearranging | ||
876 | * the lines. | ||
877 | */ | ||
878 | static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc, | ||
879 | u32 offset, u8 oddeven, u8 numlines) | ||
880 | { | ||
881 | struct isp_device *isp = to_isp_device(ccdc); | ||
882 | |||
883 | isp_reg_writel(isp, offset & 0xffff, | ||
884 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF); | ||
885 | |||
886 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
887 | ISPCCDC_SDOFST_FINV); | ||
888 | |||
889 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
890 | ISPCCDC_SDOFST_FOFST_4L); | ||
891 | |||
892 | switch (oddeven) { | ||
893 | case EVENEVEN: | ||
894 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
895 | (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT); | ||
896 | break; | ||
897 | case ODDEVEN: | ||
898 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
899 | (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT); | ||
900 | break; | ||
901 | case EVENODD: | ||
902 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
903 | (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT); | ||
904 | break; | ||
905 | case ODDODD: | ||
906 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
907 | (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT); | ||
908 | break; | ||
909 | default: | ||
910 | break; | ||
911 | } | ||
912 | } | ||
913 | |||
914 | /* | ||
915 | * ccdc_set_outaddr - Set memory address to save output image | ||
916 | * @ccdc: Pointer to ISP CCDC device. | ||
917 | * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary. | ||
918 | * | ||
919 | * Sets the memory address where the output will be saved. | ||
920 | */ | ||
921 | static void ccdc_set_outaddr(struct isp_ccdc_device *ccdc, u32 addr) | ||
922 | { | ||
923 | struct isp_device *isp = to_isp_device(ccdc); | ||
924 | |||
925 | isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR); | ||
926 | } | ||
927 | |||
928 | /* | ||
929 | * omap3isp_ccdc_max_rate - Calculate maximum input data rate based on the input | ||
930 | * @ccdc: Pointer to ISP CCDC device. | ||
931 | * @max_rate: Maximum calculated data rate. | ||
932 | * | ||
933 | * Returns in *max_rate less value between calculated and passed | ||
934 | */ | ||
935 | void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc, | ||
936 | unsigned int *max_rate) | ||
937 | { | ||
938 | struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); | ||
939 | unsigned int rate; | ||
940 | |||
941 | if (pipe == NULL) | ||
942 | return; | ||
943 | |||
944 | /* | ||
945 | * TRM says that for parallel sensors the maximum data rate | ||
946 | * should be 90% form L3/2 clock, otherwise just L3/2. | ||
947 | */ | ||
948 | if (ccdc->input == CCDC_INPUT_PARALLEL) | ||
949 | rate = pipe->l3_ick / 2 * 9 / 10; | ||
950 | else | ||
951 | rate = pipe->l3_ick / 2; | ||
952 | |||
953 | *max_rate = min(*max_rate, rate); | ||
954 | } | ||
955 | |||
956 | /* | ||
957 | * ccdc_config_sync_if - Set CCDC sync interface configuration | ||
958 | * @ccdc: Pointer to ISP CCDC device. | ||
959 | * @syncif: Structure containing the sync parameters like field state, CCDC in | ||
960 | * master/slave mode, raw/yuv data, polarity of data, field, hs, vs | ||
961 | * signals. | ||
962 | */ | ||
963 | static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, | ||
964 | struct ispccdc_syncif *syncif) | ||
965 | { | ||
966 | struct isp_device *isp = to_isp_device(ccdc); | ||
967 | u32 syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, | ||
968 | ISPCCDC_SYN_MODE); | ||
969 | |||
970 | syn_mode |= ISPCCDC_SYN_MODE_VDHDEN; | ||
971 | |||
972 | if (syncif->fldstat) | ||
973 | syn_mode |= ISPCCDC_SYN_MODE_FLDSTAT; | ||
974 | else | ||
975 | syn_mode &= ~ISPCCDC_SYN_MODE_FLDSTAT; | ||
976 | |||
977 | syn_mode &= ~ISPCCDC_SYN_MODE_DATSIZ_MASK; | ||
978 | switch (syncif->datsz) { | ||
979 | case 8: | ||
980 | syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8; | ||
981 | break; | ||
982 | case 10: | ||
983 | syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10; | ||
984 | break; | ||
985 | case 11: | ||
986 | syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11; | ||
987 | break; | ||
988 | case 12: | ||
989 | syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12; | ||
990 | break; | ||
991 | }; | ||
992 | |||
993 | if (syncif->fldmode) | ||
994 | syn_mode |= ISPCCDC_SYN_MODE_FLDMODE; | ||
995 | else | ||
996 | syn_mode &= ~ISPCCDC_SYN_MODE_FLDMODE; | ||
997 | |||
998 | if (syncif->datapol) | ||
999 | syn_mode |= ISPCCDC_SYN_MODE_DATAPOL; | ||
1000 | else | ||
1001 | syn_mode &= ~ISPCCDC_SYN_MODE_DATAPOL; | ||
1002 | |||
1003 | if (syncif->fldpol) | ||
1004 | syn_mode |= ISPCCDC_SYN_MODE_FLDPOL; | ||
1005 | else | ||
1006 | syn_mode &= ~ISPCCDC_SYN_MODE_FLDPOL; | ||
1007 | |||
1008 | if (syncif->hdpol) | ||
1009 | syn_mode |= ISPCCDC_SYN_MODE_HDPOL; | ||
1010 | else | ||
1011 | syn_mode &= ~ISPCCDC_SYN_MODE_HDPOL; | ||
1012 | |||
1013 | if (syncif->vdpol) | ||
1014 | syn_mode |= ISPCCDC_SYN_MODE_VDPOL; | ||
1015 | else | ||
1016 | syn_mode &= ~ISPCCDC_SYN_MODE_VDPOL; | ||
1017 | |||
1018 | if (syncif->ccdc_mastermode) { | ||
1019 | syn_mode |= ISPCCDC_SYN_MODE_FLDOUT | ISPCCDC_SYN_MODE_VDHDOUT; | ||
1020 | isp_reg_writel(isp, | ||
1021 | syncif->hs_width << ISPCCDC_HD_VD_WID_HDW_SHIFT | ||
1022 | | syncif->vs_width << ISPCCDC_HD_VD_WID_VDW_SHIFT, | ||
1023 | OMAP3_ISP_IOMEM_CCDC, | ||
1024 | ISPCCDC_HD_VD_WID); | ||
1025 | |||
1026 | isp_reg_writel(isp, | ||
1027 | syncif->ppln << ISPCCDC_PIX_LINES_PPLN_SHIFT | ||
1028 | | syncif->hlprf << ISPCCDC_PIX_LINES_HLPRF_SHIFT, | ||
1029 | OMAP3_ISP_IOMEM_CCDC, | ||
1030 | ISPCCDC_PIX_LINES); | ||
1031 | } else | ||
1032 | syn_mode &= ~(ISPCCDC_SYN_MODE_FLDOUT | | ||
1033 | ISPCCDC_SYN_MODE_VDHDOUT); | ||
1034 | |||
1035 | isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); | ||
1036 | |||
1037 | if (!syncif->bt_r656_en) | ||
1038 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, | ||
1039 | ISPCCDC_REC656IF_R656ON); | ||
1040 | } | ||
1041 | |||
1042 | /* CCDC formats descriptions */ | ||
1043 | static const u32 ccdc_sgrbg_pattern = | ||
1044 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC0_SHIFT | | ||
1045 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC1_SHIFT | | ||
1046 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC2_SHIFT | | ||
1047 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC3_SHIFT | | ||
1048 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC0_SHIFT | | ||
1049 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC1_SHIFT | | ||
1050 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC2_SHIFT | | ||
1051 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC3_SHIFT | | ||
1052 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC0_SHIFT | | ||
1053 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC1_SHIFT | | ||
1054 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC2_SHIFT | | ||
1055 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC3_SHIFT | | ||
1056 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC0_SHIFT | | ||
1057 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC1_SHIFT | | ||
1058 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC2_SHIFT | | ||
1059 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC3_SHIFT; | ||
1060 | |||
1061 | static const u32 ccdc_srggb_pattern = | ||
1062 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC0_SHIFT | | ||
1063 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC1_SHIFT | | ||
1064 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC2_SHIFT | | ||
1065 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC3_SHIFT | | ||
1066 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC0_SHIFT | | ||
1067 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC1_SHIFT | | ||
1068 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC2_SHIFT | | ||
1069 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC3_SHIFT | | ||
1070 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC0_SHIFT | | ||
1071 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC1_SHIFT | | ||
1072 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC2_SHIFT | | ||
1073 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC3_SHIFT | | ||
1074 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC0_SHIFT | | ||
1075 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC1_SHIFT | | ||
1076 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC2_SHIFT | | ||
1077 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC3_SHIFT; | ||
1078 | |||
1079 | static const u32 ccdc_sbggr_pattern = | ||
1080 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC0_SHIFT | | ||
1081 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC1_SHIFT | | ||
1082 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC2_SHIFT | | ||
1083 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC3_SHIFT | | ||
1084 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC0_SHIFT | | ||
1085 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC1_SHIFT | | ||
1086 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC2_SHIFT | | ||
1087 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC3_SHIFT | | ||
1088 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC0_SHIFT | | ||
1089 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC1_SHIFT | | ||
1090 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC2_SHIFT | | ||
1091 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC3_SHIFT | | ||
1092 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC0_SHIFT | | ||
1093 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC1_SHIFT | | ||
1094 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC2_SHIFT | | ||
1095 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC3_SHIFT; | ||
1096 | |||
1097 | static const u32 ccdc_sgbrg_pattern = | ||
1098 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC0_SHIFT | | ||
1099 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC1_SHIFT | | ||
1100 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC2_SHIFT | | ||
1101 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC3_SHIFT | | ||
1102 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC0_SHIFT | | ||
1103 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC1_SHIFT | | ||
1104 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC2_SHIFT | | ||
1105 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC3_SHIFT | | ||
1106 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC0_SHIFT | | ||
1107 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC1_SHIFT | | ||
1108 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC2_SHIFT | | ||
1109 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC3_SHIFT | | ||
1110 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC0_SHIFT | | ||
1111 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC1_SHIFT | | ||
1112 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC2_SHIFT | | ||
1113 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC3_SHIFT; | ||
1114 | |||
1115 | static void ccdc_configure(struct isp_ccdc_device *ccdc) | ||
1116 | { | ||
1117 | struct isp_device *isp = to_isp_device(ccdc); | ||
1118 | struct isp_parallel_platform_data *pdata = NULL; | ||
1119 | struct v4l2_subdev *sensor; | ||
1120 | struct v4l2_mbus_framefmt *format; | ||
1121 | const struct isp_format_info *fmt_info; | ||
1122 | struct v4l2_subdev_format fmt_src; | ||
1123 | unsigned int depth_out; | ||
1124 | unsigned int depth_in = 0; | ||
1125 | struct media_pad *pad; | ||
1126 | unsigned long flags; | ||
1127 | unsigned int shift; | ||
1128 | u32 syn_mode; | ||
1129 | u32 ccdc_pattern; | ||
1130 | |||
1131 | pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]); | ||
1132 | sensor = media_entity_to_v4l2_subdev(pad->entity); | ||
1133 | if (ccdc->input == CCDC_INPUT_PARALLEL) | ||
1134 | pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv) | ||
1135 | ->bus.parallel; | ||
1136 | |||
1137 | /* Compute shift value for lane shifter to configure the bridge. */ | ||
1138 | fmt_src.pad = pad->index; | ||
1139 | fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
1140 | if (!v4l2_subdev_call(sensor, pad, get_fmt, NULL, &fmt_src)) { | ||
1141 | fmt_info = omap3isp_video_format_info(fmt_src.format.code); | ||
1142 | depth_in = fmt_info->bpp; | ||
1143 | } | ||
1144 | |||
1145 | fmt_info = omap3isp_video_format_info | ||
1146 | (isp->isp_ccdc.formats[CCDC_PAD_SINK].code); | ||
1147 | depth_out = fmt_info->bpp; | ||
1148 | |||
1149 | shift = depth_in - depth_out; | ||
1150 | omap3isp_configure_bridge(isp, ccdc->input, pdata, shift); | ||
1151 | |||
1152 | ccdc->syncif.datsz = depth_out; | ||
1153 | ccdc->syncif.hdpol = pdata ? pdata->hs_pol : 0; | ||
1154 | ccdc->syncif.vdpol = pdata ? pdata->vs_pol : 0; | ||
1155 | ccdc_config_sync_if(ccdc, &ccdc->syncif); | ||
1156 | |||
1157 | /* CCDC_PAD_SINK */ | ||
1158 | format = &ccdc->formats[CCDC_PAD_SINK]; | ||
1159 | |||
1160 | syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); | ||
1161 | |||
1162 | /* Use the raw, unprocessed data when writing to memory. The H3A and | ||
1163 | * histogram modules are still fed with lens shading corrected data. | ||
1164 | */ | ||
1165 | syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; | ||
1166 | |||
1167 | if (ccdc->output & CCDC_OUTPUT_MEMORY) | ||
1168 | syn_mode |= ISPCCDC_SYN_MODE_WEN; | ||
1169 | else | ||
1170 | syn_mode &= ~ISPCCDC_SYN_MODE_WEN; | ||
1171 | |||
1172 | if (ccdc->output & CCDC_OUTPUT_RESIZER) | ||
1173 | syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ; | ||
1174 | else | ||
1175 | syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; | ||
1176 | |||
1177 | /* Use PACK8 mode for 1byte per pixel formats. */ | ||
1178 | if (omap3isp_video_format_info(format->code)->bpp <= 8) | ||
1179 | syn_mode |= ISPCCDC_SYN_MODE_PACK8; | ||
1180 | else | ||
1181 | syn_mode &= ~ISPCCDC_SYN_MODE_PACK8; | ||
1182 | |||
1183 | isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); | ||
1184 | |||
1185 | /* Mosaic filter */ | ||
1186 | switch (format->code) { | ||
1187 | case V4L2_MBUS_FMT_SRGGB10_1X10: | ||
1188 | case V4L2_MBUS_FMT_SRGGB12_1X12: | ||
1189 | ccdc_pattern = ccdc_srggb_pattern; | ||
1190 | break; | ||
1191 | case V4L2_MBUS_FMT_SBGGR10_1X10: | ||
1192 | case V4L2_MBUS_FMT_SBGGR12_1X12: | ||
1193 | ccdc_pattern = ccdc_sbggr_pattern; | ||
1194 | break; | ||
1195 | case V4L2_MBUS_FMT_SGBRG10_1X10: | ||
1196 | case V4L2_MBUS_FMT_SGBRG12_1X12: | ||
1197 | ccdc_pattern = ccdc_sgbrg_pattern; | ||
1198 | break; | ||
1199 | default: | ||
1200 | /* Use GRBG */ | ||
1201 | ccdc_pattern = ccdc_sgrbg_pattern; | ||
1202 | break; | ||
1203 | } | ||
1204 | ccdc_config_imgattr(ccdc, ccdc_pattern); | ||
1205 | |||
1206 | /* Generate VD0 on the last line of the image and VD1 on the | ||
1207 | * 2/3 height line. | ||
1208 | */ | ||
1209 | isp_reg_writel(isp, ((format->height - 2) << ISPCCDC_VDINT_0_SHIFT) | | ||
1210 | ((format->height * 2 / 3) << ISPCCDC_VDINT_1_SHIFT), | ||
1211 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT); | ||
1212 | |||
1213 | /* CCDC_PAD_SOURCE_OF */ | ||
1214 | format = &ccdc->formats[CCDC_PAD_SOURCE_OF]; | ||
1215 | |||
1216 | isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) | | ||
1217 | ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), | ||
1218 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); | ||
1219 | isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT, | ||
1220 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); | ||
1221 | isp_reg_writel(isp, (format->height - 1) | ||
1222 | << ISPCCDC_VERT_LINES_NLV_SHIFT, | ||
1223 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); | ||
1224 | |||
1225 | ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0); | ||
1226 | |||
1227 | /* CCDC_PAD_SOURCE_VP */ | ||
1228 | format = &ccdc->formats[CCDC_PAD_SOURCE_VP]; | ||
1229 | |||
1230 | isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | | ||
1231 | (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), | ||
1232 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ); | ||
1233 | isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | | ||
1234 | ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT), | ||
1235 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT); | ||
1236 | |||
1237 | isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | | ||
1238 | (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT), | ||
1239 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT); | ||
1240 | |||
1241 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
1242 | if (ccdc->lsc.request == NULL) | ||
1243 | goto unlock; | ||
1244 | |||
1245 | WARN_ON(ccdc->lsc.active); | ||
1246 | |||
1247 | /* Get last good LSC configuration. If it is not supported for | ||
1248 | * the current active resolution discard it. | ||
1249 | */ | ||
1250 | if (ccdc->lsc.active == NULL && | ||
1251 | __ccdc_lsc_configure(ccdc, ccdc->lsc.request) == 0) { | ||
1252 | ccdc->lsc.active = ccdc->lsc.request; | ||
1253 | } else { | ||
1254 | list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue); | ||
1255 | schedule_work(&ccdc->lsc.table_work); | ||
1256 | } | ||
1257 | |||
1258 | ccdc->lsc.request = NULL; | ||
1259 | |||
1260 | unlock: | ||
1261 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
1262 | |||
1263 | ccdc_apply_controls(ccdc); | ||
1264 | } | ||
1265 | |||
1266 | static void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable) | ||
1267 | { | ||
1268 | struct isp_device *isp = to_isp_device(ccdc); | ||
1269 | |||
1270 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR, | ||
1271 | ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0); | ||
1272 | } | ||
1273 | |||
1274 | static int ccdc_disable(struct isp_ccdc_device *ccdc) | ||
1275 | { | ||
1276 | unsigned long flags; | ||
1277 | int ret = 0; | ||
1278 | |||
1279 | spin_lock_irqsave(&ccdc->lock, flags); | ||
1280 | if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS) | ||
1281 | ccdc->stopping = CCDC_STOP_REQUEST; | ||
1282 | spin_unlock_irqrestore(&ccdc->lock, flags); | ||
1283 | |||
1284 | ret = wait_event_timeout(ccdc->wait, | ||
1285 | ccdc->stopping == CCDC_STOP_FINISHED, | ||
1286 | msecs_to_jiffies(2000)); | ||
1287 | if (ret == 0) { | ||
1288 | ret = -ETIMEDOUT; | ||
1289 | dev_warn(to_device(ccdc), "CCDC stop timeout!\n"); | ||
1290 | } | ||
1291 | |||
1292 | omap3isp_sbl_disable(to_isp_device(ccdc), OMAP3_ISP_SBL_CCDC_LSC_READ); | ||
1293 | |||
1294 | mutex_lock(&ccdc->ioctl_lock); | ||
1295 | ccdc_lsc_free_request(ccdc, ccdc->lsc.request); | ||
1296 | ccdc->lsc.request = ccdc->lsc.active; | ||
1297 | ccdc->lsc.active = NULL; | ||
1298 | cancel_work_sync(&ccdc->lsc.table_work); | ||
1299 | ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue); | ||
1300 | mutex_unlock(&ccdc->ioctl_lock); | ||
1301 | |||
1302 | ccdc->stopping = CCDC_STOP_NOT_REQUESTED; | ||
1303 | |||
1304 | return ret > 0 ? 0 : ret; | ||
1305 | } | ||
1306 | |||
1307 | static void ccdc_enable(struct isp_ccdc_device *ccdc) | ||
1308 | { | ||
1309 | if (ccdc_lsc_is_configured(ccdc)) | ||
1310 | __ccdc_lsc_enable(ccdc, 1); | ||
1311 | __ccdc_enable(ccdc, 1); | ||
1312 | } | ||
1313 | |||
1314 | /* ----------------------------------------------------------------------------- | ||
1315 | * Interrupt handling | ||
1316 | */ | ||
1317 | |||
1318 | /* | ||
1319 | * ccdc_sbl_busy - Poll idle state of CCDC and related SBL memory write bits | ||
1320 | * @ccdc: Pointer to ISP CCDC device. | ||
1321 | * | ||
1322 | * Returns zero if the CCDC is idle and the image has been written to | ||
1323 | * memory, too. | ||
1324 | */ | ||
1325 | static int ccdc_sbl_busy(struct isp_ccdc_device *ccdc) | ||
1326 | { | ||
1327 | struct isp_device *isp = to_isp_device(ccdc); | ||
1328 | |||
1329 | return omap3isp_ccdc_busy(ccdc) | ||
1330 | | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_0) & | ||
1331 | ISPSBL_CCDC_WR_0_DATA_READY) | ||
1332 | | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_1) & | ||
1333 | ISPSBL_CCDC_WR_0_DATA_READY) | ||
1334 | | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_2) & | ||
1335 | ISPSBL_CCDC_WR_0_DATA_READY) | ||
1336 | | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_3) & | ||
1337 | ISPSBL_CCDC_WR_0_DATA_READY); | ||
1338 | } | ||
1339 | |||
1340 | /* | ||
1341 | * ccdc_sbl_wait_idle - Wait until the CCDC and related SBL are idle | ||
1342 | * @ccdc: Pointer to ISP CCDC device. | ||
1343 | * @max_wait: Max retry count in us for wait for idle/busy transition. | ||
1344 | */ | ||
1345 | static int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc, | ||
1346 | unsigned int max_wait) | ||
1347 | { | ||
1348 | unsigned int wait = 0; | ||
1349 | |||
1350 | if (max_wait == 0) | ||
1351 | max_wait = 10000; /* 10 ms */ | ||
1352 | |||
1353 | for (wait = 0; wait <= max_wait; wait++) { | ||
1354 | if (!ccdc_sbl_busy(ccdc)) | ||
1355 | return 0; | ||
1356 | |||
1357 | rmb(); | ||
1358 | udelay(1); | ||
1359 | } | ||
1360 | |||
1361 | return -EBUSY; | ||
1362 | } | ||
1363 | |||
1364 | /* __ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence | ||
1365 | * @ccdc: Pointer to ISP CCDC device. | ||
1366 | * @event: Pointing which event trigger handler | ||
1367 | * | ||
1368 | * Return 1 when the event and stopping request combination is satisfied, | ||
1369 | * zero otherwise. | ||
1370 | */ | ||
1371 | static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event) | ||
1372 | { | ||
1373 | int rval = 0; | ||
1374 | |||
1375 | switch ((ccdc->stopping & 3) | event) { | ||
1376 | case CCDC_STOP_REQUEST | CCDC_EVENT_VD1: | ||
1377 | if (ccdc->lsc.state != LSC_STATE_STOPPED) | ||
1378 | __ccdc_lsc_enable(ccdc, 0); | ||
1379 | __ccdc_enable(ccdc, 0); | ||
1380 | ccdc->stopping = CCDC_STOP_EXECUTED; | ||
1381 | return 1; | ||
1382 | |||
1383 | case CCDC_STOP_EXECUTED | CCDC_EVENT_VD0: | ||
1384 | ccdc->stopping |= CCDC_STOP_CCDC_FINISHED; | ||
1385 | if (ccdc->lsc.state == LSC_STATE_STOPPED) | ||
1386 | ccdc->stopping |= CCDC_STOP_LSC_FINISHED; | ||
1387 | rval = 1; | ||
1388 | break; | ||
1389 | |||
1390 | case CCDC_STOP_EXECUTED | CCDC_EVENT_LSC_DONE: | ||
1391 | ccdc->stopping |= CCDC_STOP_LSC_FINISHED; | ||
1392 | rval = 1; | ||
1393 | break; | ||
1394 | |||
1395 | case CCDC_STOP_EXECUTED | CCDC_EVENT_VD1: | ||
1396 | return 1; | ||
1397 | } | ||
1398 | |||
1399 | if (ccdc->stopping == CCDC_STOP_FINISHED) { | ||
1400 | wake_up(&ccdc->wait); | ||
1401 | rval = 1; | ||
1402 | } | ||
1403 | |||
1404 | return rval; | ||
1405 | } | ||
1406 | |||
1407 | static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc) | ||
1408 | { | ||
1409 | struct isp_pipeline *pipe = | ||
1410 | to_isp_pipeline(&ccdc->video_out.video.entity); | ||
1411 | struct video_device *vdev = &ccdc->subdev.devnode; | ||
1412 | struct v4l2_event event; | ||
1413 | |||
1414 | memset(&event, 0, sizeof(event)); | ||
1415 | event.type = V4L2_EVENT_FRAME_SYNC; | ||
1416 | event.u.frame_sync.frame_sequence = atomic_read(&pipe->frame_number); | ||
1417 | |||
1418 | v4l2_event_queue(vdev, &event); | ||
1419 | } | ||
1420 | |||
1421 | /* | ||
1422 | * ccdc_lsc_isr - Handle LSC events | ||
1423 | * @ccdc: Pointer to ISP CCDC device. | ||
1424 | * @events: LSC events | ||
1425 | */ | ||
1426 | static void ccdc_lsc_isr(struct isp_ccdc_device *ccdc, u32 events) | ||
1427 | { | ||
1428 | unsigned long flags; | ||
1429 | |||
1430 | if (events & IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ) { | ||
1431 | ccdc_lsc_error_handler(ccdc); | ||
1432 | ccdc->error = 1; | ||
1433 | dev_dbg(to_device(ccdc), "lsc prefetch error\n"); | ||
1434 | } | ||
1435 | |||
1436 | if (!(events & IRQ0STATUS_CCDC_LSC_DONE_IRQ)) | ||
1437 | return; | ||
1438 | |||
1439 | /* LSC_DONE interrupt occur, there are two cases | ||
1440 | * 1. stopping for reconfiguration | ||
1441 | * 2. stopping because of STREAM OFF command | ||
1442 | */ | ||
1443 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
1444 | |||
1445 | if (ccdc->lsc.state == LSC_STATE_STOPPING) | ||
1446 | ccdc->lsc.state = LSC_STATE_STOPPED; | ||
1447 | |||
1448 | if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE)) | ||
1449 | goto done; | ||
1450 | |||
1451 | if (ccdc->lsc.state != LSC_STATE_RECONFIG) | ||
1452 | goto done; | ||
1453 | |||
1454 | /* LSC is in STOPPING state, change to the new state */ | ||
1455 | ccdc->lsc.state = LSC_STATE_STOPPED; | ||
1456 | |||
1457 | /* This is an exception. Start of frame and LSC_DONE interrupt | ||
1458 | * have been received on the same time. Skip this event and wait | ||
1459 | * for better times. | ||
1460 | */ | ||
1461 | if (events & IRQ0STATUS_HS_VS_IRQ) | ||
1462 | goto done; | ||
1463 | |||
1464 | /* The LSC engine is stopped at this point. Enable it if there's a | ||
1465 | * pending request. | ||
1466 | */ | ||
1467 | if (ccdc->lsc.request == NULL) | ||
1468 | goto done; | ||
1469 | |||
1470 | ccdc_lsc_enable(ccdc); | ||
1471 | |||
1472 | done: | ||
1473 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
1474 | } | ||
1475 | |||
1476 | static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) | ||
1477 | { | ||
1478 | struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); | ||
1479 | struct isp_device *isp = to_isp_device(ccdc); | ||
1480 | struct isp_buffer *buffer; | ||
1481 | int restart = 0; | ||
1482 | |||
1483 | /* The CCDC generates VD0 interrupts even when disabled (the datasheet | ||
1484 | * doesn't explicitly state if that's supposed to happen or not, so it | ||
1485 | * can be considered as a hardware bug or as a feature, but we have to | ||
1486 | * deal with it anyway). Disabling the CCDC when no buffer is available | ||
1487 | * would thus not be enough, we need to handle the situation explicitly. | ||
1488 | */ | ||
1489 | if (list_empty(&ccdc->video_out.dmaqueue)) | ||
1490 | goto done; | ||
1491 | |||
1492 | /* We're in continuous mode, and memory writes were disabled due to a | ||
1493 | * buffer underrun. Reenable them now that we have a buffer. The buffer | ||
1494 | * address has been set in ccdc_video_queue. | ||
1495 | */ | ||
1496 | if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) { | ||
1497 | restart = 1; | ||
1498 | ccdc->underrun = 0; | ||
1499 | goto done; | ||
1500 | } | ||
1501 | |||
1502 | if (ccdc_sbl_wait_idle(ccdc, 1000)) { | ||
1503 | dev_info(isp->dev, "CCDC won't become idle!\n"); | ||
1504 | goto done; | ||
1505 | } | ||
1506 | |||
1507 | buffer = omap3isp_video_buffer_next(&ccdc->video_out, ccdc->error); | ||
1508 | if (buffer != NULL) { | ||
1509 | ccdc_set_outaddr(ccdc, buffer->isp_addr); | ||
1510 | restart = 1; | ||
1511 | } | ||
1512 | |||
1513 | pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; | ||
1514 | |||
1515 | if (ccdc->state == ISP_PIPELINE_STREAM_SINGLESHOT && | ||
1516 | isp_pipeline_ready(pipe)) | ||
1517 | omap3isp_pipeline_set_stream(pipe, | ||
1518 | ISP_PIPELINE_STREAM_SINGLESHOT); | ||
1519 | |||
1520 | done: | ||
1521 | ccdc->error = 0; | ||
1522 | return restart; | ||
1523 | } | ||
1524 | |||
1525 | /* | ||
1526 | * ccdc_vd0_isr - Handle VD0 event | ||
1527 | * @ccdc: Pointer to ISP CCDC device. | ||
1528 | * | ||
1529 | * Executes LSC deferred enablement before next frame starts. | ||
1530 | */ | ||
1531 | static void ccdc_vd0_isr(struct isp_ccdc_device *ccdc) | ||
1532 | { | ||
1533 | unsigned long flags; | ||
1534 | int restart = 0; | ||
1535 | |||
1536 | if (ccdc->output & CCDC_OUTPUT_MEMORY) | ||
1537 | restart = ccdc_isr_buffer(ccdc); | ||
1538 | |||
1539 | spin_lock_irqsave(&ccdc->lock, flags); | ||
1540 | if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) { | ||
1541 | spin_unlock_irqrestore(&ccdc->lock, flags); | ||
1542 | return; | ||
1543 | } | ||
1544 | |||
1545 | if (!ccdc->shadow_update) | ||
1546 | ccdc_apply_controls(ccdc); | ||
1547 | spin_unlock_irqrestore(&ccdc->lock, flags); | ||
1548 | |||
1549 | if (restart) | ||
1550 | ccdc_enable(ccdc); | ||
1551 | } | ||
1552 | |||
1553 | /* | ||
1554 | * ccdc_vd1_isr - Handle VD1 event | ||
1555 | * @ccdc: Pointer to ISP CCDC device. | ||
1556 | */ | ||
1557 | static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc) | ||
1558 | { | ||
1559 | unsigned long flags; | ||
1560 | |||
1561 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
1562 | |||
1563 | /* | ||
1564 | * Depending on the CCDC pipeline state, CCDC stopping should be | ||
1565 | * handled differently. In SINGLESHOT we emulate an internal CCDC | ||
1566 | * stopping because the CCDC hw works only in continuous mode. | ||
1567 | * When CONTINUOUS pipeline state is used and the CCDC writes it's | ||
1568 | * data to memory the CCDC and LSC are stopped immediately but | ||
1569 | * without change the CCDC stopping state machine. The CCDC | ||
1570 | * stopping state machine should be used only when user request | ||
1571 | * for stopping is received (SINGLESHOT is an exeption). | ||
1572 | */ | ||
1573 | switch (ccdc->state) { | ||
1574 | case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
1575 | ccdc->stopping = CCDC_STOP_REQUEST; | ||
1576 | break; | ||
1577 | |||
1578 | case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
1579 | if (ccdc->output & CCDC_OUTPUT_MEMORY) { | ||
1580 | if (ccdc->lsc.state != LSC_STATE_STOPPED) | ||
1581 | __ccdc_lsc_enable(ccdc, 0); | ||
1582 | __ccdc_enable(ccdc, 0); | ||
1583 | } | ||
1584 | break; | ||
1585 | |||
1586 | case ISP_PIPELINE_STREAM_STOPPED: | ||
1587 | break; | ||
1588 | } | ||
1589 | |||
1590 | if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1)) | ||
1591 | goto done; | ||
1592 | |||
1593 | if (ccdc->lsc.request == NULL) | ||
1594 | goto done; | ||
1595 | |||
1596 | /* | ||
1597 | * LSC need to be reconfigured. Stop it here and on next LSC_DONE IRQ | ||
1598 | * do the appropriate changes in registers | ||
1599 | */ | ||
1600 | if (ccdc->lsc.state == LSC_STATE_RUNNING) { | ||
1601 | __ccdc_lsc_enable(ccdc, 0); | ||
1602 | ccdc->lsc.state = LSC_STATE_RECONFIG; | ||
1603 | goto done; | ||
1604 | } | ||
1605 | |||
1606 | /* LSC has been in STOPPED state, enable it */ | ||
1607 | if (ccdc->lsc.state == LSC_STATE_STOPPED) | ||
1608 | ccdc_lsc_enable(ccdc); | ||
1609 | |||
1610 | done: | ||
1611 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
1612 | } | ||
1613 | |||
1614 | /* | ||
1615 | * omap3isp_ccdc_isr - Configure CCDC during interframe time. | ||
1616 | * @ccdc: Pointer to ISP CCDC device. | ||
1617 | * @events: CCDC events | ||
1618 | */ | ||
1619 | int omap3isp_ccdc_isr(struct isp_ccdc_device *ccdc, u32 events) | ||
1620 | { | ||
1621 | if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) | ||
1622 | return 0; | ||
1623 | |||
1624 | if (events & IRQ0STATUS_CCDC_VD1_IRQ) | ||
1625 | ccdc_vd1_isr(ccdc); | ||
1626 | |||
1627 | ccdc_lsc_isr(ccdc, events); | ||
1628 | |||
1629 | if (events & IRQ0STATUS_CCDC_VD0_IRQ) | ||
1630 | ccdc_vd0_isr(ccdc); | ||
1631 | |||
1632 | if (events & IRQ0STATUS_HS_VS_IRQ) | ||
1633 | ccdc_hs_vs_isr(ccdc); | ||
1634 | |||
1635 | return 0; | ||
1636 | } | ||
1637 | |||
1638 | /* ----------------------------------------------------------------------------- | ||
1639 | * ISP video operations | ||
1640 | */ | ||
1641 | |||
1642 | static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer) | ||
1643 | { | ||
1644 | struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc; | ||
1645 | |||
1646 | if (!(ccdc->output & CCDC_OUTPUT_MEMORY)) | ||
1647 | return -ENODEV; | ||
1648 | |||
1649 | ccdc_set_outaddr(ccdc, buffer->isp_addr); | ||
1650 | |||
1651 | /* We now have a buffer queued on the output, restart the pipeline | ||
1652 | * on the next CCDC interrupt if running in continuous mode (or when | ||
1653 | * starting the stream). | ||
1654 | */ | ||
1655 | ccdc->underrun = 1; | ||
1656 | |||
1657 | return 0; | ||
1658 | } | ||
1659 | |||
1660 | static const struct isp_video_operations ccdc_video_ops = { | ||
1661 | .queue = ccdc_video_queue, | ||
1662 | }; | ||
1663 | |||
1664 | /* ----------------------------------------------------------------------------- | ||
1665 | * V4L2 subdev operations | ||
1666 | */ | ||
1667 | |||
1668 | /* | ||
1669 | * ccdc_ioctl - CCDC module private ioctl's | ||
1670 | * @sd: ISP CCDC V4L2 subdevice | ||
1671 | * @cmd: ioctl command | ||
1672 | * @arg: ioctl argument | ||
1673 | * | ||
1674 | * Return 0 on success or a negative error code otherwise. | ||
1675 | */ | ||
1676 | static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
1677 | { | ||
1678 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
1679 | int ret; | ||
1680 | |||
1681 | switch (cmd) { | ||
1682 | case VIDIOC_OMAP3ISP_CCDC_CFG: | ||
1683 | mutex_lock(&ccdc->ioctl_lock); | ||
1684 | ret = ccdc_config(ccdc, arg); | ||
1685 | mutex_unlock(&ccdc->ioctl_lock); | ||
1686 | break; | ||
1687 | |||
1688 | default: | ||
1689 | return -ENOIOCTLCMD; | ||
1690 | } | ||
1691 | |||
1692 | return ret; | ||
1693 | } | ||
1694 | |||
1695 | static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, | ||
1696 | struct v4l2_event_subscription *sub) | ||
1697 | { | ||
1698 | if (sub->type != V4L2_EVENT_FRAME_SYNC) | ||
1699 | return -EINVAL; | ||
1700 | |||
1701 | /* line number is zero at frame start */ | ||
1702 | if (sub->id != 0) | ||
1703 | return -EINVAL; | ||
1704 | |||
1705 | return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS); | ||
1706 | } | ||
1707 | |||
1708 | static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, | ||
1709 | struct v4l2_event_subscription *sub) | ||
1710 | { | ||
1711 | return v4l2_event_unsubscribe(fh, sub); | ||
1712 | } | ||
1713 | |||
1714 | /* | ||
1715 | * ccdc_set_stream - Enable/Disable streaming on the CCDC module | ||
1716 | * @sd: ISP CCDC V4L2 subdevice | ||
1717 | * @enable: Enable/disable stream | ||
1718 | * | ||
1719 | * When writing to memory, the CCDC hardware can't be enabled without a memory | ||
1720 | * buffer to write to. As the s_stream operation is called in response to a | ||
1721 | * STREAMON call without any buffer queued yet, just update the enabled field | ||
1722 | * and return immediately. The CCDC will be enabled in ccdc_isr_buffer(). | ||
1723 | * | ||
1724 | * When not writing to memory enable the CCDC immediately. | ||
1725 | */ | ||
1726 | static int ccdc_set_stream(struct v4l2_subdev *sd, int enable) | ||
1727 | { | ||
1728 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
1729 | struct isp_device *isp = to_isp_device(ccdc); | ||
1730 | int ret = 0; | ||
1731 | |||
1732 | if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) { | ||
1733 | if (enable == ISP_PIPELINE_STREAM_STOPPED) | ||
1734 | return 0; | ||
1735 | |||
1736 | omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_CCDC); | ||
1737 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, | ||
1738 | ISPCCDC_CFG_VDLC); | ||
1739 | |||
1740 | ccdc_configure(ccdc); | ||
1741 | |||
1742 | /* TODO: Don't configure the video port if all of its output | ||
1743 | * links are inactive. | ||
1744 | */ | ||
1745 | ccdc_config_vp(ccdc); | ||
1746 | ccdc_enable_vp(ccdc, 1); | ||
1747 | ccdc->error = 0; | ||
1748 | ccdc_print_status(ccdc); | ||
1749 | } | ||
1750 | |||
1751 | switch (enable) { | ||
1752 | case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
1753 | if (ccdc->output & CCDC_OUTPUT_MEMORY) | ||
1754 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE); | ||
1755 | |||
1756 | if (ccdc->underrun || !(ccdc->output & CCDC_OUTPUT_MEMORY)) | ||
1757 | ccdc_enable(ccdc); | ||
1758 | |||
1759 | ccdc->underrun = 0; | ||
1760 | break; | ||
1761 | |||
1762 | case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
1763 | if (ccdc->output & CCDC_OUTPUT_MEMORY && | ||
1764 | ccdc->state != ISP_PIPELINE_STREAM_SINGLESHOT) | ||
1765 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE); | ||
1766 | |||
1767 | ccdc_enable(ccdc); | ||
1768 | break; | ||
1769 | |||
1770 | case ISP_PIPELINE_STREAM_STOPPED: | ||
1771 | ret = ccdc_disable(ccdc); | ||
1772 | if (ccdc->output & CCDC_OUTPUT_MEMORY) | ||
1773 | omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CCDC_WRITE); | ||
1774 | omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_CCDC); | ||
1775 | ccdc->underrun = 0; | ||
1776 | break; | ||
1777 | } | ||
1778 | |||
1779 | ccdc->state = enable; | ||
1780 | return ret; | ||
1781 | } | ||
1782 | |||
1783 | static struct v4l2_mbus_framefmt * | ||
1784 | __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | ||
1785 | unsigned int pad, enum v4l2_subdev_format_whence which) | ||
1786 | { | ||
1787 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
1788 | return v4l2_subdev_get_try_format(fh, pad); | ||
1789 | else | ||
1790 | return &ccdc->formats[pad]; | ||
1791 | } | ||
1792 | |||
1793 | /* | ||
1794 | * ccdc_try_format - Try video format on a pad | ||
1795 | * @ccdc: ISP CCDC device | ||
1796 | * @fh : V4L2 subdev file handle | ||
1797 | * @pad: Pad number | ||
1798 | * @fmt: Format | ||
1799 | */ | ||
1800 | static void | ||
1801 | ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | ||
1802 | unsigned int pad, struct v4l2_mbus_framefmt *fmt, | ||
1803 | enum v4l2_subdev_format_whence which) | ||
1804 | { | ||
1805 | struct v4l2_mbus_framefmt *format; | ||
1806 | const struct isp_format_info *info; | ||
1807 | unsigned int width = fmt->width; | ||
1808 | unsigned int height = fmt->height; | ||
1809 | unsigned int i; | ||
1810 | |||
1811 | switch (pad) { | ||
1812 | case CCDC_PAD_SINK: | ||
1813 | /* TODO: If the CCDC output formatter pad is connected directly | ||
1814 | * to the resizer, only YUV formats can be used. | ||
1815 | */ | ||
1816 | for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) { | ||
1817 | if (fmt->code == ccdc_fmts[i]) | ||
1818 | break; | ||
1819 | } | ||
1820 | |||
1821 | /* If not found, use SGRBG10 as default */ | ||
1822 | if (i >= ARRAY_SIZE(ccdc_fmts)) | ||
1823 | fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
1824 | |||
1825 | /* Clamp the input size. */ | ||
1826 | fmt->width = clamp_t(u32, width, 32, 4096); | ||
1827 | fmt->height = clamp_t(u32, height, 32, 4096); | ||
1828 | break; | ||
1829 | |||
1830 | case CCDC_PAD_SOURCE_OF: | ||
1831 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); | ||
1832 | memcpy(fmt, format, sizeof(*fmt)); | ||
1833 | |||
1834 | /* The data formatter truncates the number of horizontal output | ||
1835 | * pixels to a multiple of 16. To avoid clipping data, allow | ||
1836 | * callers to request an output size bigger than the input size | ||
1837 | * up to the nearest multiple of 16. | ||
1838 | */ | ||
1839 | fmt->width = clamp_t(u32, width, 32, fmt->width + 15); | ||
1840 | fmt->width &= ~15; | ||
1841 | fmt->height = clamp_t(u32, height, 32, fmt->height); | ||
1842 | break; | ||
1843 | |||
1844 | case CCDC_PAD_SOURCE_VP: | ||
1845 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); | ||
1846 | memcpy(fmt, format, sizeof(*fmt)); | ||
1847 | |||
1848 | /* The video port interface truncates the data to 10 bits. */ | ||
1849 | info = omap3isp_video_format_info(fmt->code); | ||
1850 | fmt->code = info->truncated; | ||
1851 | |||
1852 | /* The number of lines that can be clocked out from the video | ||
1853 | * port output must be at least one line less than the number | ||
1854 | * of input lines. | ||
1855 | */ | ||
1856 | fmt->width = clamp_t(u32, width, 32, fmt->width); | ||
1857 | fmt->height = clamp_t(u32, height, 32, fmt->height - 1); | ||
1858 | break; | ||
1859 | } | ||
1860 | |||
1861 | /* Data is written to memory unpacked, each 10-bit or 12-bit pixel is | ||
1862 | * stored on 2 bytes. | ||
1863 | */ | ||
1864 | fmt->colorspace = V4L2_COLORSPACE_SRGB; | ||
1865 | fmt->field = V4L2_FIELD_NONE; | ||
1866 | } | ||
1867 | |||
1868 | /* | ||
1869 | * ccdc_enum_mbus_code - Handle pixel format enumeration | ||
1870 | * @sd : pointer to v4l2 subdev structure | ||
1871 | * @fh : V4L2 subdev file handle | ||
1872 | * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
1873 | * return -EINVAL or zero on success | ||
1874 | */ | ||
1875 | static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, | ||
1876 | struct v4l2_subdev_fh *fh, | ||
1877 | struct v4l2_subdev_mbus_code_enum *code) | ||
1878 | { | ||
1879 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
1880 | struct v4l2_mbus_framefmt *format; | ||
1881 | |||
1882 | switch (code->pad) { | ||
1883 | case CCDC_PAD_SINK: | ||
1884 | if (code->index >= ARRAY_SIZE(ccdc_fmts)) | ||
1885 | return -EINVAL; | ||
1886 | |||
1887 | code->code = ccdc_fmts[code->index]; | ||
1888 | break; | ||
1889 | |||
1890 | case CCDC_PAD_SOURCE_OF: | ||
1891 | case CCDC_PAD_SOURCE_VP: | ||
1892 | /* No format conversion inside CCDC */ | ||
1893 | if (code->index != 0) | ||
1894 | return -EINVAL; | ||
1895 | |||
1896 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, | ||
1897 | V4L2_SUBDEV_FORMAT_TRY); | ||
1898 | |||
1899 | code->code = format->code; | ||
1900 | break; | ||
1901 | |||
1902 | default: | ||
1903 | return -EINVAL; | ||
1904 | } | ||
1905 | |||
1906 | return 0; | ||
1907 | } | ||
1908 | |||
1909 | static int ccdc_enum_frame_size(struct v4l2_subdev *sd, | ||
1910 | struct v4l2_subdev_fh *fh, | ||
1911 | struct v4l2_subdev_frame_size_enum *fse) | ||
1912 | { | ||
1913 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
1914 | struct v4l2_mbus_framefmt format; | ||
1915 | |||
1916 | if (fse->index != 0) | ||
1917 | return -EINVAL; | ||
1918 | |||
1919 | format.code = fse->code; | ||
1920 | format.width = 1; | ||
1921 | format.height = 1; | ||
1922 | ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
1923 | fse->min_width = format.width; | ||
1924 | fse->min_height = format.height; | ||
1925 | |||
1926 | if (format.code != fse->code) | ||
1927 | return -EINVAL; | ||
1928 | |||
1929 | format.code = fse->code; | ||
1930 | format.width = -1; | ||
1931 | format.height = -1; | ||
1932 | ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
1933 | fse->max_width = format.width; | ||
1934 | fse->max_height = format.height; | ||
1935 | |||
1936 | return 0; | ||
1937 | } | ||
1938 | |||
1939 | /* | ||
1940 | * ccdc_get_format - Retrieve the video format on a pad | ||
1941 | * @sd : ISP CCDC V4L2 subdevice | ||
1942 | * @fh : V4L2 subdev file handle | ||
1943 | * @fmt: Format | ||
1944 | * | ||
1945 | * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond | ||
1946 | * to the format type. | ||
1947 | */ | ||
1948 | static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1949 | struct v4l2_subdev_format *fmt) | ||
1950 | { | ||
1951 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
1952 | struct v4l2_mbus_framefmt *format; | ||
1953 | |||
1954 | format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); | ||
1955 | if (format == NULL) | ||
1956 | return -EINVAL; | ||
1957 | |||
1958 | fmt->format = *format; | ||
1959 | return 0; | ||
1960 | } | ||
1961 | |||
1962 | /* | ||
1963 | * ccdc_set_format - Set the video format on a pad | ||
1964 | * @sd : ISP CCDC V4L2 subdevice | ||
1965 | * @fh : V4L2 subdev file handle | ||
1966 | * @fmt: Format | ||
1967 | * | ||
1968 | * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond | ||
1969 | * to the format type. | ||
1970 | */ | ||
1971 | static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1972 | struct v4l2_subdev_format *fmt) | ||
1973 | { | ||
1974 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
1975 | struct v4l2_mbus_framefmt *format; | ||
1976 | |||
1977 | format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); | ||
1978 | if (format == NULL) | ||
1979 | return -EINVAL; | ||
1980 | |||
1981 | ccdc_try_format(ccdc, fh, fmt->pad, &fmt->format, fmt->which); | ||
1982 | *format = fmt->format; | ||
1983 | |||
1984 | /* Propagate the format from sink to source */ | ||
1985 | if (fmt->pad == CCDC_PAD_SINK) { | ||
1986 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, | ||
1987 | fmt->which); | ||
1988 | *format = fmt->format; | ||
1989 | ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, | ||
1990 | fmt->which); | ||
1991 | |||
1992 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_VP, | ||
1993 | fmt->which); | ||
1994 | *format = fmt->format; | ||
1995 | ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_VP, format, | ||
1996 | fmt->which); | ||
1997 | } | ||
1998 | |||
1999 | return 0; | ||
2000 | } | ||
2001 | |||
2002 | /* | ||
2003 | * ccdc_init_formats - Initialize formats on all pads | ||
2004 | * @sd: ISP CCDC V4L2 subdevice | ||
2005 | * @fh: V4L2 subdev file handle | ||
2006 | * | ||
2007 | * Initialize all pad formats with default values. If fh is not NULL, try | ||
2008 | * formats are initialized on the file handle. Otherwise active formats are | ||
2009 | * initialized on the device. | ||
2010 | */ | ||
2011 | static int ccdc_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
2012 | { | ||
2013 | struct v4l2_subdev_format format; | ||
2014 | |||
2015 | memset(&format, 0, sizeof(format)); | ||
2016 | format.pad = CCDC_PAD_SINK; | ||
2017 | format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
2018 | format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
2019 | format.format.width = 4096; | ||
2020 | format.format.height = 4096; | ||
2021 | ccdc_set_format(sd, fh, &format); | ||
2022 | |||
2023 | return 0; | ||
2024 | } | ||
2025 | |||
2026 | /* V4L2 subdev core operations */ | ||
2027 | static const struct v4l2_subdev_core_ops ccdc_v4l2_core_ops = { | ||
2028 | .ioctl = ccdc_ioctl, | ||
2029 | .subscribe_event = ccdc_subscribe_event, | ||
2030 | .unsubscribe_event = ccdc_unsubscribe_event, | ||
2031 | }; | ||
2032 | |||
2033 | /* V4L2 subdev video operations */ | ||
2034 | static const struct v4l2_subdev_video_ops ccdc_v4l2_video_ops = { | ||
2035 | .s_stream = ccdc_set_stream, | ||
2036 | }; | ||
2037 | |||
2038 | /* V4L2 subdev pad operations */ | ||
2039 | static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { | ||
2040 | .enum_mbus_code = ccdc_enum_mbus_code, | ||
2041 | .enum_frame_size = ccdc_enum_frame_size, | ||
2042 | .get_fmt = ccdc_get_format, | ||
2043 | .set_fmt = ccdc_set_format, | ||
2044 | }; | ||
2045 | |||
2046 | /* V4L2 subdev operations */ | ||
2047 | static const struct v4l2_subdev_ops ccdc_v4l2_ops = { | ||
2048 | .core = &ccdc_v4l2_core_ops, | ||
2049 | .video = &ccdc_v4l2_video_ops, | ||
2050 | .pad = &ccdc_v4l2_pad_ops, | ||
2051 | }; | ||
2052 | |||
2053 | /* V4L2 subdev internal operations */ | ||
2054 | static const struct v4l2_subdev_internal_ops ccdc_v4l2_internal_ops = { | ||
2055 | .open = ccdc_init_formats, | ||
2056 | }; | ||
2057 | |||
2058 | /* ----------------------------------------------------------------------------- | ||
2059 | * Media entity operations | ||
2060 | */ | ||
2061 | |||
2062 | /* | ||
2063 | * ccdc_link_setup - Setup CCDC connections | ||
2064 | * @entity: CCDC media entity | ||
2065 | * @local: Pad at the local end of the link | ||
2066 | * @remote: Pad at the remote end of the link | ||
2067 | * @flags: Link flags | ||
2068 | * | ||
2069 | * return -EINVAL or zero on success | ||
2070 | */ | ||
2071 | static int ccdc_link_setup(struct media_entity *entity, | ||
2072 | const struct media_pad *local, | ||
2073 | const struct media_pad *remote, u32 flags) | ||
2074 | { | ||
2075 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
2076 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
2077 | struct isp_device *isp = to_isp_device(ccdc); | ||
2078 | |||
2079 | switch (local->index | media_entity_type(remote->entity)) { | ||
2080 | case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
2081 | /* Read from the sensor (parallel interface), CCP2, CSI2a or | ||
2082 | * CSI2c. | ||
2083 | */ | ||
2084 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
2085 | ccdc->input = CCDC_INPUT_NONE; | ||
2086 | break; | ||
2087 | } | ||
2088 | |||
2089 | if (ccdc->input != CCDC_INPUT_NONE) | ||
2090 | return -EBUSY; | ||
2091 | |||
2092 | if (remote->entity == &isp->isp_ccp2.subdev.entity) | ||
2093 | ccdc->input = CCDC_INPUT_CCP2B; | ||
2094 | else if (remote->entity == &isp->isp_csi2a.subdev.entity) | ||
2095 | ccdc->input = CCDC_INPUT_CSI2A; | ||
2096 | else if (remote->entity == &isp->isp_csi2c.subdev.entity) | ||
2097 | ccdc->input = CCDC_INPUT_CSI2C; | ||
2098 | else | ||
2099 | ccdc->input = CCDC_INPUT_PARALLEL; | ||
2100 | |||
2101 | break; | ||
2102 | |||
2103 | /* | ||
2104 | * The ISP core doesn't support pipelines with multiple video outputs. | ||
2105 | * Revisit this when it will be implemented, and return -EBUSY for now. | ||
2106 | */ | ||
2107 | |||
2108 | case CCDC_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: | ||
2109 | /* Write to preview engine, histogram and H3A. When none of | ||
2110 | * those links are active, the video port can be disabled. | ||
2111 | */ | ||
2112 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
2113 | if (ccdc->output & ~CCDC_OUTPUT_PREVIEW) | ||
2114 | return -EBUSY; | ||
2115 | ccdc->output |= CCDC_OUTPUT_PREVIEW; | ||
2116 | } else { | ||
2117 | ccdc->output &= ~CCDC_OUTPUT_PREVIEW; | ||
2118 | } | ||
2119 | break; | ||
2120 | |||
2121 | case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_DEVNODE: | ||
2122 | /* Write to memory */ | ||
2123 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
2124 | if (ccdc->output & ~CCDC_OUTPUT_MEMORY) | ||
2125 | return -EBUSY; | ||
2126 | ccdc->output |= CCDC_OUTPUT_MEMORY; | ||
2127 | } else { | ||
2128 | ccdc->output &= ~CCDC_OUTPUT_MEMORY; | ||
2129 | } | ||
2130 | break; | ||
2131 | |||
2132 | case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_V4L2_SUBDEV: | ||
2133 | /* Write to resizer */ | ||
2134 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
2135 | if (ccdc->output & ~CCDC_OUTPUT_RESIZER) | ||
2136 | return -EBUSY; | ||
2137 | ccdc->output |= CCDC_OUTPUT_RESIZER; | ||
2138 | } else { | ||
2139 | ccdc->output &= ~CCDC_OUTPUT_RESIZER; | ||
2140 | } | ||
2141 | break; | ||
2142 | |||
2143 | default: | ||
2144 | return -EINVAL; | ||
2145 | } | ||
2146 | |||
2147 | return 0; | ||
2148 | } | ||
2149 | |||
2150 | /* media operations */ | ||
2151 | static const struct media_entity_operations ccdc_media_ops = { | ||
2152 | .link_setup = ccdc_link_setup, | ||
2153 | }; | ||
2154 | |||
2155 | void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc) | ||
2156 | { | ||
2157 | v4l2_device_unregister_subdev(&ccdc->subdev); | ||
2158 | omap3isp_video_unregister(&ccdc->video_out); | ||
2159 | } | ||
2160 | |||
2161 | int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc, | ||
2162 | struct v4l2_device *vdev) | ||
2163 | { | ||
2164 | int ret; | ||
2165 | |||
2166 | /* Register the subdev and video node. */ | ||
2167 | ret = v4l2_device_register_subdev(vdev, &ccdc->subdev); | ||
2168 | if (ret < 0) | ||
2169 | goto error; | ||
2170 | |||
2171 | ret = omap3isp_video_register(&ccdc->video_out, vdev); | ||
2172 | if (ret < 0) | ||
2173 | goto error; | ||
2174 | |||
2175 | return 0; | ||
2176 | |||
2177 | error: | ||
2178 | omap3isp_ccdc_unregister_entities(ccdc); | ||
2179 | return ret; | ||
2180 | } | ||
2181 | |||
2182 | /* ----------------------------------------------------------------------------- | ||
2183 | * ISP CCDC initialisation and cleanup | ||
2184 | */ | ||
2185 | |||
2186 | /* | ||
2187 | * ccdc_init_entities - Initialize V4L2 subdev and media entity | ||
2188 | * @ccdc: ISP CCDC module | ||
2189 | * | ||
2190 | * Return 0 on success and a negative error code on failure. | ||
2191 | */ | ||
2192 | static int ccdc_init_entities(struct isp_ccdc_device *ccdc) | ||
2193 | { | ||
2194 | struct v4l2_subdev *sd = &ccdc->subdev; | ||
2195 | struct media_pad *pads = ccdc->pads; | ||
2196 | struct media_entity *me = &sd->entity; | ||
2197 | int ret; | ||
2198 | |||
2199 | ccdc->input = CCDC_INPUT_NONE; | ||
2200 | |||
2201 | v4l2_subdev_init(sd, &ccdc_v4l2_ops); | ||
2202 | sd->internal_ops = &ccdc_v4l2_internal_ops; | ||
2203 | strlcpy(sd->name, "OMAP3 ISP CCDC", sizeof(sd->name)); | ||
2204 | sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
2205 | v4l2_set_subdevdata(sd, ccdc); | ||
2206 | sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
2207 | |||
2208 | pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
2209 | pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE; | ||
2210 | pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE; | ||
2211 | |||
2212 | me->ops = &ccdc_media_ops; | ||
2213 | ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0); | ||
2214 | if (ret < 0) | ||
2215 | return ret; | ||
2216 | |||
2217 | ccdc_init_formats(sd, NULL); | ||
2218 | |||
2219 | ccdc->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
2220 | ccdc->video_out.ops = &ccdc_video_ops; | ||
2221 | ccdc->video_out.isp = to_isp_device(ccdc); | ||
2222 | ccdc->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; | ||
2223 | ccdc->video_out.bpl_alignment = 32; | ||
2224 | |||
2225 | ret = omap3isp_video_init(&ccdc->video_out, "CCDC"); | ||
2226 | if (ret < 0) | ||
2227 | goto error_video; | ||
2228 | |||
2229 | /* Connect the CCDC subdev to the video node. */ | ||
2230 | ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF, | ||
2231 | &ccdc->video_out.video.entity, 0, 0); | ||
2232 | if (ret < 0) | ||
2233 | goto error_link; | ||
2234 | |||
2235 | return 0; | ||
2236 | |||
2237 | error_link: | ||
2238 | omap3isp_video_cleanup(&ccdc->video_out); | ||
2239 | error_video: | ||
2240 | media_entity_cleanup(me); | ||
2241 | return ret; | ||
2242 | } | ||
2243 | |||
2244 | /* | ||
2245 | * omap3isp_ccdc_init - CCDC module initialization. | ||
2246 | * @dev: Device pointer specific to the OMAP3 ISP. | ||
2247 | * | ||
2248 | * TODO: Get the initialisation values from platform data. | ||
2249 | * | ||
2250 | * Return 0 on success or a negative error code otherwise. | ||
2251 | */ | ||
2252 | int omap3isp_ccdc_init(struct isp_device *isp) | ||
2253 | { | ||
2254 | struct isp_ccdc_device *ccdc = &isp->isp_ccdc; | ||
2255 | int ret; | ||
2256 | |||
2257 | spin_lock_init(&ccdc->lock); | ||
2258 | init_waitqueue_head(&ccdc->wait); | ||
2259 | mutex_init(&ccdc->ioctl_lock); | ||
2260 | |||
2261 | ccdc->stopping = CCDC_STOP_NOT_REQUESTED; | ||
2262 | |||
2263 | INIT_WORK(&ccdc->lsc.table_work, ccdc_lsc_free_table_work); | ||
2264 | ccdc->lsc.state = LSC_STATE_STOPPED; | ||
2265 | INIT_LIST_HEAD(&ccdc->lsc.free_queue); | ||
2266 | spin_lock_init(&ccdc->lsc.req_lock); | ||
2267 | |||
2268 | ccdc->syncif.ccdc_mastermode = 0; | ||
2269 | ccdc->syncif.datapol = 0; | ||
2270 | ccdc->syncif.datsz = 0; | ||
2271 | ccdc->syncif.fldmode = 0; | ||
2272 | ccdc->syncif.fldout = 0; | ||
2273 | ccdc->syncif.fldpol = 0; | ||
2274 | ccdc->syncif.fldstat = 0; | ||
2275 | |||
2276 | ccdc->clamp.oblen = 0; | ||
2277 | ccdc->clamp.dcsubval = 0; | ||
2278 | |||
2279 | ccdc->vpcfg.pixelclk = 0; | ||
2280 | |||
2281 | ccdc->update = OMAP3ISP_CCDC_BLCLAMP; | ||
2282 | ccdc_apply_controls(ccdc); | ||
2283 | |||
2284 | ret = ccdc_init_entities(ccdc); | ||
2285 | if (ret < 0) { | ||
2286 | mutex_destroy(&ccdc->ioctl_lock); | ||
2287 | return ret; | ||
2288 | } | ||
2289 | |||
2290 | return 0; | ||
2291 | } | ||
2292 | |||
2293 | /* | ||
2294 | * omap3isp_ccdc_cleanup - CCDC module cleanup. | ||
2295 | * @dev: Device pointer specific to the OMAP3 ISP. | ||
2296 | */ | ||
2297 | void omap3isp_ccdc_cleanup(struct isp_device *isp) | ||
2298 | { | ||
2299 | struct isp_ccdc_device *ccdc = &isp->isp_ccdc; | ||
2300 | |||
2301 | omap3isp_video_cleanup(&ccdc->video_out); | ||
2302 | media_entity_cleanup(&ccdc->subdev.entity); | ||
2303 | |||
2304 | /* Free LSC requests. As the CCDC is stopped there's no active request, | ||
2305 | * so only the pending request and the free queue need to be handled. | ||
2306 | */ | ||
2307 | ccdc_lsc_free_request(ccdc, ccdc->lsc.request); | ||
2308 | cancel_work_sync(&ccdc->lsc.table_work); | ||
2309 | ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue); | ||
2310 | |||
2311 | if (ccdc->fpc.fpcaddr != 0) | ||
2312 | omap_iommu_vfree(isp->domain, isp->dev, ccdc->fpc.fpcaddr); | ||
2313 | |||
2314 | mutex_destroy(&ccdc->ioctl_lock); | ||
2315 | } | ||
diff --git a/drivers/media/video/omap3isp/ispccdc.h b/drivers/media/video/omap3isp/ispccdc.h new file mode 100644 index 00000000000..483a19cac1a --- /dev/null +++ b/drivers/media/video/omap3isp/ispccdc.h | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * ispccdc.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - CCDC module | ||
5 | * | ||
6 | * Copyright (C) 2009-2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #ifndef OMAP3_ISP_CCDC_H | ||
28 | #define OMAP3_ISP_CCDC_H | ||
29 | |||
30 | #include <linux/omap3isp.h> | ||
31 | #include <linux/workqueue.h> | ||
32 | |||
33 | #include "ispvideo.h" | ||
34 | |||
35 | enum ccdc_input_entity { | ||
36 | CCDC_INPUT_NONE, | ||
37 | CCDC_INPUT_PARALLEL, | ||
38 | CCDC_INPUT_CSI2A, | ||
39 | CCDC_INPUT_CCP2B, | ||
40 | CCDC_INPUT_CSI2C | ||
41 | }; | ||
42 | |||
43 | #define CCDC_OUTPUT_MEMORY (1 << 0) | ||
44 | #define CCDC_OUTPUT_PREVIEW (1 << 1) | ||
45 | #define CCDC_OUTPUT_RESIZER (1 << 2) | ||
46 | |||
47 | #define OMAP3ISP_CCDC_NEVENTS 16 | ||
48 | |||
49 | /* | ||
50 | * struct ispccdc_syncif - Structure for Sync Interface between sensor and CCDC | ||
51 | * @ccdc_mastermode: Master mode. 1 - Master, 0 - Slave. | ||
52 | * @fldstat: Field state. 0 - Odd Field, 1 - Even Field. | ||
53 | * @datsz: Data size. | ||
54 | * @fldmode: 0 - Progressive, 1 - Interlaced. | ||
55 | * @datapol: 0 - Positive, 1 - Negative. | ||
56 | * @fldpol: 0 - Positive, 1 - Negative. | ||
57 | * @hdpol: 0 - Positive, 1 - Negative. | ||
58 | * @vdpol: 0 - Positive, 1 - Negative. | ||
59 | * @fldout: 0 - Input, 1 - Output. | ||
60 | * @hs_width: Width of the Horizontal Sync pulse, used for HS/VS Output. | ||
61 | * @vs_width: Width of the Vertical Sync pulse, used for HS/VS Output. | ||
62 | * @ppln: Number of pixels per line, used for HS/VS Output. | ||
63 | * @hlprf: Number of half lines per frame, used for HS/VS Output. | ||
64 | * @bt_r656_en: 1 - Enable ITU-R BT656 mode, 0 - Sync mode. | ||
65 | */ | ||
66 | struct ispccdc_syncif { | ||
67 | u8 ccdc_mastermode; | ||
68 | u8 fldstat; | ||
69 | u8 datsz; | ||
70 | u8 fldmode; | ||
71 | u8 datapol; | ||
72 | u8 fldpol; | ||
73 | u8 hdpol; | ||
74 | u8 vdpol; | ||
75 | u8 fldout; | ||
76 | u8 hs_width; | ||
77 | u8 vs_width; | ||
78 | u8 ppln; | ||
79 | u8 hlprf; | ||
80 | u8 bt_r656_en; | ||
81 | }; | ||
82 | |||
83 | /* | ||
84 | * struct ispccdc_vp - Structure for Video Port parameters | ||
85 | * @pixelclk: Input pixel clock in Hz | ||
86 | */ | ||
87 | struct ispccdc_vp { | ||
88 | unsigned int pixelclk; | ||
89 | }; | ||
90 | |||
91 | enum ispccdc_lsc_state { | ||
92 | LSC_STATE_STOPPED = 0, | ||
93 | LSC_STATE_STOPPING = 1, | ||
94 | LSC_STATE_RUNNING = 2, | ||
95 | LSC_STATE_RECONFIG = 3, | ||
96 | }; | ||
97 | |||
98 | struct ispccdc_lsc_config_req { | ||
99 | struct list_head list; | ||
100 | struct omap3isp_ccdc_lsc_config config; | ||
101 | unsigned char enable; | ||
102 | u32 table; | ||
103 | struct iovm_struct *iovm; | ||
104 | }; | ||
105 | |||
106 | /* | ||
107 | * ispccdc_lsc - CCDC LSC parameters | ||
108 | * @update_config: Set when user changes config | ||
109 | * @request_enable: Whether LSC is requested to be enabled | ||
110 | * @config: LSC config set by user | ||
111 | * @update_table: Set when user provides a new LSC table to table_new | ||
112 | * @table_new: LSC table set by user, ISP address | ||
113 | * @table_inuse: LSC table currently in use, ISP address | ||
114 | */ | ||
115 | struct ispccdc_lsc { | ||
116 | enum ispccdc_lsc_state state; | ||
117 | struct work_struct table_work; | ||
118 | |||
119 | /* LSC queue of configurations */ | ||
120 | spinlock_t req_lock; | ||
121 | struct ispccdc_lsc_config_req *request; /* requested configuration */ | ||
122 | struct ispccdc_lsc_config_req *active; /* active configuration */ | ||
123 | struct list_head free_queue; /* configurations for freeing */ | ||
124 | }; | ||
125 | |||
126 | #define CCDC_STOP_NOT_REQUESTED 0x00 | ||
127 | #define CCDC_STOP_REQUEST 0x01 | ||
128 | #define CCDC_STOP_EXECUTED (0x02 | CCDC_STOP_REQUEST) | ||
129 | #define CCDC_STOP_CCDC_FINISHED 0x04 | ||
130 | #define CCDC_STOP_LSC_FINISHED 0x08 | ||
131 | #define CCDC_STOP_FINISHED \ | ||
132 | (CCDC_STOP_EXECUTED | CCDC_STOP_CCDC_FINISHED | CCDC_STOP_LSC_FINISHED) | ||
133 | |||
134 | #define CCDC_EVENT_VD1 0x10 | ||
135 | #define CCDC_EVENT_VD0 0x20 | ||
136 | #define CCDC_EVENT_LSC_DONE 0x40 | ||
137 | |||
138 | /* Sink and source CCDC pads */ | ||
139 | #define CCDC_PAD_SINK 0 | ||
140 | #define CCDC_PAD_SOURCE_OF 1 | ||
141 | #define CCDC_PAD_SOURCE_VP 2 | ||
142 | #define CCDC_PADS_NUM 3 | ||
143 | |||
144 | /* | ||
145 | * struct isp_ccdc_device - Structure for the CCDC module to store its own | ||
146 | * information | ||
147 | * @subdev: V4L2 subdevice | ||
148 | * @pads: Sink and source media entity pads | ||
149 | * @formats: Active video formats | ||
150 | * @input: Active input | ||
151 | * @output: Active outputs | ||
152 | * @video_out: Output video node | ||
153 | * @error: A hardware error occurred during capture | ||
154 | * @alaw: A-law compression enabled (1) or disabled (0) | ||
155 | * @lpf: Low pass filter enabled (1) or disabled (0) | ||
156 | * @obclamp: Optical-black clamp enabled (1) or disabled (0) | ||
157 | * @fpc_en: Faulty pixels correction enabled (1) or disabled (0) | ||
158 | * @blcomp: Black level compensation configuration | ||
159 | * @clamp: Optical-black or digital clamp configuration | ||
160 | * @fpc: Faulty pixels correction configuration | ||
161 | * @lsc: Lens shading compensation configuration | ||
162 | * @update: Bitmask of controls to update during the next interrupt | ||
163 | * @shadow_update: Controls update in progress by userspace | ||
164 | * @syncif: Interface synchronization configuration | ||
165 | * @vpcfg: Video port configuration | ||
166 | * @underrun: A buffer underrun occurred and a new buffer has been queued | ||
167 | * @state: Streaming state | ||
168 | * @lock: Serializes shadow_update with interrupt handler | ||
169 | * @wait: Wait queue used to stop the module | ||
170 | * @stopping: Stopping state | ||
171 | * @ioctl_lock: Serializes ioctl calls and LSC requests freeing | ||
172 | */ | ||
173 | struct isp_ccdc_device { | ||
174 | struct v4l2_subdev subdev; | ||
175 | struct media_pad pads[CCDC_PADS_NUM]; | ||
176 | struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM]; | ||
177 | |||
178 | enum ccdc_input_entity input; | ||
179 | unsigned int output; | ||
180 | struct isp_video video_out; | ||
181 | unsigned int error; | ||
182 | |||
183 | unsigned int alaw:1, | ||
184 | lpf:1, | ||
185 | obclamp:1, | ||
186 | fpc_en:1; | ||
187 | struct omap3isp_ccdc_blcomp blcomp; | ||
188 | struct omap3isp_ccdc_bclamp clamp; | ||
189 | struct omap3isp_ccdc_fpc fpc; | ||
190 | struct ispccdc_lsc lsc; | ||
191 | unsigned int update; | ||
192 | unsigned int shadow_update; | ||
193 | |||
194 | struct ispccdc_syncif syncif; | ||
195 | struct ispccdc_vp vpcfg; | ||
196 | |||
197 | unsigned int underrun:1; | ||
198 | enum isp_pipeline_stream_state state; | ||
199 | spinlock_t lock; | ||
200 | wait_queue_head_t wait; | ||
201 | unsigned int stopping; | ||
202 | struct mutex ioctl_lock; | ||
203 | }; | ||
204 | |||
205 | struct isp_device; | ||
206 | |||
207 | int omap3isp_ccdc_init(struct isp_device *isp); | ||
208 | void omap3isp_ccdc_cleanup(struct isp_device *isp); | ||
209 | int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc, | ||
210 | struct v4l2_device *vdev); | ||
211 | void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc); | ||
212 | |||
213 | int omap3isp_ccdc_busy(struct isp_ccdc_device *isp_ccdc); | ||
214 | int omap3isp_ccdc_isr(struct isp_ccdc_device *isp_ccdc, u32 events); | ||
215 | void omap3isp_ccdc_restore_context(struct isp_device *isp); | ||
216 | void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc, | ||
217 | unsigned int *max_rate); | ||
218 | |||
219 | #endif /* OMAP3_ISP_CCDC_H */ | ||
diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c new file mode 100644 index 00000000000..ec9e395f333 --- /dev/null +++ b/drivers/media/video/omap3isp/ispccp2.c | |||
@@ -0,0 +1,1196 @@ | |||
1 | /* | ||
2 | * ispccp2.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - CCP2 module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2010 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #include <linux/delay.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <linux/mm.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/mutex.h> | ||
32 | #include <linux/uaccess.h> | ||
33 | #include <linux/regulator/consumer.h> | ||
34 | |||
35 | #include "isp.h" | ||
36 | #include "ispreg.h" | ||
37 | #include "ispccp2.h" | ||
38 | |||
39 | /* Number of LCX channels */ | ||
40 | #define CCP2_LCx_CHANS_NUM 3 | ||
41 | /* Max/Min size for CCP2 video port */ | ||
42 | #define ISPCCP2_DAT_START_MIN 0 | ||
43 | #define ISPCCP2_DAT_START_MAX 4095 | ||
44 | #define ISPCCP2_DAT_SIZE_MIN 0 | ||
45 | #define ISPCCP2_DAT_SIZE_MAX 4095 | ||
46 | #define ISPCCP2_VPCLK_FRACDIV 65536 | ||
47 | #define ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP 0x12 | ||
48 | #define ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP 0x16 | ||
49 | /* Max/Min size for CCP2 memory channel */ | ||
50 | #define ISPCCP2_LCM_HSIZE_COUNT_MIN 16 | ||
51 | #define ISPCCP2_LCM_HSIZE_COUNT_MAX 8191 | ||
52 | #define ISPCCP2_LCM_HSIZE_SKIP_MIN 0 | ||
53 | #define ISPCCP2_LCM_HSIZE_SKIP_MAX 8191 | ||
54 | #define ISPCCP2_LCM_VSIZE_MIN 1 | ||
55 | #define ISPCCP2_LCM_VSIZE_MAX 8191 | ||
56 | #define ISPCCP2_LCM_HWORDS_MIN 1 | ||
57 | #define ISPCCP2_LCM_HWORDS_MAX 4095 | ||
58 | #define ISPCCP2_LCM_CTRL_BURST_SIZE_32X 5 | ||
59 | #define ISPCCP2_LCM_CTRL_READ_THROTTLE_FULL 0 | ||
60 | #define ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 2 | ||
61 | #define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 2 | ||
62 | #define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 3 | ||
63 | #define ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 3 | ||
64 | #define ISPCCP2_LCM_CTRL_DST_PORT_VP 0 | ||
65 | #define ISPCCP2_LCM_CTRL_DST_PORT_MEM 1 | ||
66 | |||
67 | /* Set only the required bits */ | ||
68 | #define BIT_SET(var, shift, mask, val) \ | ||
69 | do { \ | ||
70 | var = ((var) & ~((mask) << (shift))) \ | ||
71 | | ((val) << (shift)); \ | ||
72 | } while (0) | ||
73 | |||
74 | /* | ||
75 | * ccp2_print_status - Print current CCP2 module register values. | ||
76 | */ | ||
77 | #define CCP2_PRINT_REGISTER(isp, name)\ | ||
78 | dev_dbg(isp->dev, "###CCP2 " #name "=0x%08x\n", \ | ||
79 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_##name)) | ||
80 | |||
81 | static void ccp2_print_status(struct isp_ccp2_device *ccp2) | ||
82 | { | ||
83 | struct isp_device *isp = to_isp_device(ccp2); | ||
84 | |||
85 | dev_dbg(isp->dev, "-------------CCP2 Register dump-------------\n"); | ||
86 | |||
87 | CCP2_PRINT_REGISTER(isp, SYSCONFIG); | ||
88 | CCP2_PRINT_REGISTER(isp, SYSSTATUS); | ||
89 | CCP2_PRINT_REGISTER(isp, LC01_IRQENABLE); | ||
90 | CCP2_PRINT_REGISTER(isp, LC01_IRQSTATUS); | ||
91 | CCP2_PRINT_REGISTER(isp, LC23_IRQENABLE); | ||
92 | CCP2_PRINT_REGISTER(isp, LC23_IRQSTATUS); | ||
93 | CCP2_PRINT_REGISTER(isp, LCM_IRQENABLE); | ||
94 | CCP2_PRINT_REGISTER(isp, LCM_IRQSTATUS); | ||
95 | CCP2_PRINT_REGISTER(isp, CTRL); | ||
96 | CCP2_PRINT_REGISTER(isp, LCx_CTRL(0)); | ||
97 | CCP2_PRINT_REGISTER(isp, LCx_CODE(0)); | ||
98 | CCP2_PRINT_REGISTER(isp, LCx_STAT_START(0)); | ||
99 | CCP2_PRINT_REGISTER(isp, LCx_STAT_SIZE(0)); | ||
100 | CCP2_PRINT_REGISTER(isp, LCx_SOF_ADDR(0)); | ||
101 | CCP2_PRINT_REGISTER(isp, LCx_EOF_ADDR(0)); | ||
102 | CCP2_PRINT_REGISTER(isp, LCx_DAT_START(0)); | ||
103 | CCP2_PRINT_REGISTER(isp, LCx_DAT_SIZE(0)); | ||
104 | CCP2_PRINT_REGISTER(isp, LCx_DAT_PING_ADDR(0)); | ||
105 | CCP2_PRINT_REGISTER(isp, LCx_DAT_PONG_ADDR(0)); | ||
106 | CCP2_PRINT_REGISTER(isp, LCx_DAT_OFST(0)); | ||
107 | CCP2_PRINT_REGISTER(isp, LCM_CTRL); | ||
108 | CCP2_PRINT_REGISTER(isp, LCM_VSIZE); | ||
109 | CCP2_PRINT_REGISTER(isp, LCM_HSIZE); | ||
110 | CCP2_PRINT_REGISTER(isp, LCM_PREFETCH); | ||
111 | CCP2_PRINT_REGISTER(isp, LCM_SRC_ADDR); | ||
112 | CCP2_PRINT_REGISTER(isp, LCM_SRC_OFST); | ||
113 | CCP2_PRINT_REGISTER(isp, LCM_DST_ADDR); | ||
114 | CCP2_PRINT_REGISTER(isp, LCM_DST_OFST); | ||
115 | |||
116 | dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * ccp2_reset - Reset the CCP2 | ||
121 | * @ccp2: pointer to ISP CCP2 device | ||
122 | */ | ||
123 | static void ccp2_reset(struct isp_ccp2_device *ccp2) | ||
124 | { | ||
125 | struct isp_device *isp = to_isp_device(ccp2); | ||
126 | int i = 0; | ||
127 | |||
128 | /* Reset the CSI1/CCP2B and wait for reset to complete */ | ||
129 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSCONFIG, | ||
130 | ISPCCP2_SYSCONFIG_SOFT_RESET); | ||
131 | while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSSTATUS) & | ||
132 | ISPCCP2_SYSSTATUS_RESET_DONE)) { | ||
133 | udelay(10); | ||
134 | if (i++ > 10) { /* try read 10 times */ | ||
135 | dev_warn(isp->dev, | ||
136 | "omap3_isp: timeout waiting for ccp2 reset\n"); | ||
137 | break; | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * ccp2_pwr_cfg - Configure the power mode settings | ||
144 | * @ccp2: pointer to ISP CCP2 device | ||
145 | */ | ||
146 | static void ccp2_pwr_cfg(struct isp_ccp2_device *ccp2) | ||
147 | { | ||
148 | struct isp_device *isp = to_isp_device(ccp2); | ||
149 | |||
150 | isp_reg_writel(isp, ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SMART | | ||
151 | ((isp->revision == ISP_REVISION_15_0 && isp->autoidle) ? | ||
152 | ISPCCP2_SYSCONFIG_AUTO_IDLE : 0), | ||
153 | OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSCONFIG); | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * ccp2_if_enable - Enable CCP2 interface. | ||
158 | * @ccp2: pointer to ISP CCP2 device | ||
159 | * @enable: enable/disable flag | ||
160 | */ | ||
161 | static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) | ||
162 | { | ||
163 | struct isp_device *isp = to_isp_device(ccp2); | ||
164 | struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); | ||
165 | int i; | ||
166 | |||
167 | if (enable && ccp2->vdds_csib) | ||
168 | regulator_enable(ccp2->vdds_csib); | ||
169 | |||
170 | /* Enable/Disable all the LCx channels */ | ||
171 | for (i = 0; i < CCP2_LCx_CHANS_NUM; i++) | ||
172 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(i), | ||
173 | ISPCCP2_LCx_CTRL_CHAN_EN, | ||
174 | enable ? ISPCCP2_LCx_CTRL_CHAN_EN : 0); | ||
175 | |||
176 | /* Enable/Disable ccp2 interface in ccp2 mode */ | ||
177 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL, | ||
178 | ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN, | ||
179 | enable ? (ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN) : 0); | ||
180 | |||
181 | /* For frame count propagation */ | ||
182 | if (pipe->do_propagation) { | ||
183 | /* We may want the Frame Start IRQ from LC0 */ | ||
184 | if (enable) | ||
185 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, | ||
186 | ISPCCP2_LC01_IRQENABLE, | ||
187 | ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); | ||
188 | else | ||
189 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCP2, | ||
190 | ISPCCP2_LC01_IRQENABLE, | ||
191 | ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); | ||
192 | } | ||
193 | |||
194 | if (!enable && ccp2->vdds_csib) | ||
195 | regulator_disable(ccp2->vdds_csib); | ||
196 | } | ||
197 | |||
198 | /* | ||
199 | * ccp2_mem_enable - Enable CCP2 memory interface. | ||
200 | * @ccp2: pointer to ISP CCP2 device | ||
201 | * @enable: enable/disable flag | ||
202 | */ | ||
203 | static void ccp2_mem_enable(struct isp_ccp2_device *ccp2, u8 enable) | ||
204 | { | ||
205 | struct isp_device *isp = to_isp_device(ccp2); | ||
206 | |||
207 | if (enable) | ||
208 | ccp2_if_enable(ccp2, 0); | ||
209 | |||
210 | /* Enable/Disable ccp2 interface in ccp2 mode */ | ||
211 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL, | ||
212 | ISPCCP2_CTRL_MODE, enable ? ISPCCP2_CTRL_MODE : 0); | ||
213 | |||
214 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_CTRL, | ||
215 | ISPCCP2_LCM_CTRL_CHAN_EN, | ||
216 | enable ? ISPCCP2_LCM_CTRL_CHAN_EN : 0); | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * ccp2_phyif_config - Initialize CCP2 phy interface config | ||
221 | * @ccp2: Pointer to ISP CCP2 device | ||
222 | * @config: CCP2 platform data | ||
223 | * | ||
224 | * Configure the CCP2 physical interface module from platform data. | ||
225 | * | ||
226 | * Returns -EIO if strobe is chosen in CSI1 mode, or 0 on success. | ||
227 | */ | ||
228 | static int ccp2_phyif_config(struct isp_ccp2_device *ccp2, | ||
229 | const struct isp_ccp2_platform_data *pdata) | ||
230 | { | ||
231 | struct isp_device *isp = to_isp_device(ccp2); | ||
232 | u32 val; | ||
233 | |||
234 | /* CCP2B mode */ | ||
235 | val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL) | | ||
236 | ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE; | ||
237 | /* Data/strobe physical layer */ | ||
238 | BIT_SET(val, ISPCCP2_CTRL_PHY_SEL_SHIFT, ISPCCP2_CTRL_PHY_SEL_MASK, | ||
239 | pdata->phy_layer); | ||
240 | BIT_SET(val, ISPCCP2_CTRL_INV_SHIFT, ISPCCP2_CTRL_INV_MASK, | ||
241 | pdata->strobe_clk_pol); | ||
242 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); | ||
243 | |||
244 | val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); | ||
245 | if (!(val & ISPCCP2_CTRL_MODE)) { | ||
246 | if (pdata->ccp2_mode) | ||
247 | dev_warn(isp->dev, "OMAP3 CCP2 bus not available\n"); | ||
248 | if (pdata->phy_layer == ISPCCP2_CTRL_PHY_SEL_STROBE) | ||
249 | /* Strobe mode requires CCP2 */ | ||
250 | return -EIO; | ||
251 | } | ||
252 | |||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * ccp2_vp_config - Initialize CCP2 video port interface. | ||
258 | * @ccp2: Pointer to ISP CCP2 device | ||
259 | * @vpclk_div: Video port divisor | ||
260 | * | ||
261 | * Configure the CCP2 video port with the given clock divisor. The valid divisor | ||
262 | * values depend on the ISP revision: | ||
263 | * | ||
264 | * - revision 1.0 and 2.0 1 to 4 | ||
265 | * - revision 15.0 1 to 65536 | ||
266 | * | ||
267 | * The exact divisor value used might differ from the requested value, as ISP | ||
268 | * revision 15.0 represent the divisor by 65536 divided by an integer. | ||
269 | */ | ||
270 | static void ccp2_vp_config(struct isp_ccp2_device *ccp2, | ||
271 | unsigned int vpclk_div) | ||
272 | { | ||
273 | struct isp_device *isp = to_isp_device(ccp2); | ||
274 | u32 val; | ||
275 | |||
276 | /* ISPCCP2_CTRL Video port */ | ||
277 | val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); | ||
278 | val |= ISPCCP2_CTRL_VP_ONLY_EN; /* Disable the memory write port */ | ||
279 | |||
280 | if (isp->revision == ISP_REVISION_15_0) { | ||
281 | vpclk_div = clamp_t(unsigned int, vpclk_div, 1, 65536); | ||
282 | vpclk_div = min(ISPCCP2_VPCLK_FRACDIV / vpclk_div, 65535U); | ||
283 | BIT_SET(val, ISPCCP2_CTRL_VPCLK_DIV_SHIFT, | ||
284 | ISPCCP2_CTRL_VPCLK_DIV_MASK, vpclk_div); | ||
285 | } else { | ||
286 | vpclk_div = clamp_t(unsigned int, vpclk_div, 1, 4); | ||
287 | BIT_SET(val, ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT, | ||
288 | ISPCCP2_CTRL_VP_OUT_CTRL_MASK, vpclk_div - 1); | ||
289 | } | ||
290 | |||
291 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * ccp2_lcx_config - Initialize CCP2 logical channel interface. | ||
296 | * @ccp2: Pointer to ISP CCP2 device | ||
297 | * @config: Pointer to ISP LCx config structure. | ||
298 | * | ||
299 | * This will analyze the parameters passed by the interface config | ||
300 | * and configure CSI1/CCP2 logical channel | ||
301 | * | ||
302 | */ | ||
303 | static void ccp2_lcx_config(struct isp_ccp2_device *ccp2, | ||
304 | struct isp_interface_lcx_config *config) | ||
305 | { | ||
306 | struct isp_device *isp = to_isp_device(ccp2); | ||
307 | u32 val, format; | ||
308 | |||
309 | switch (config->format) { | ||
310 | case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: | ||
311 | format = ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP; | ||
312 | break; | ||
313 | case V4L2_MBUS_FMT_SGRBG10_1X10: | ||
314 | default: | ||
315 | format = ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP; /* RAW10+VP */ | ||
316 | break; | ||
317 | } | ||
318 | /* ISPCCP2_LCx_CTRL logical channel #0 */ | ||
319 | val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(0)) | ||
320 | | (ISPCCP2_LCx_CTRL_REGION_EN); /* Region */ | ||
321 | |||
322 | if (isp->revision == ISP_REVISION_15_0) { | ||
323 | /* CRC */ | ||
324 | BIT_SET(val, ISPCCP2_LCx_CTRL_CRC_SHIFT_15_0, | ||
325 | ISPCCP2_LCx_CTRL_CRC_MASK, | ||
326 | config->crc); | ||
327 | /* Format = RAW10+VP or RAW8+DPCM10+VP*/ | ||
328 | BIT_SET(val, ISPCCP2_LCx_CTRL_FORMAT_SHIFT_15_0, | ||
329 | ISPCCP2_LCx_CTRL_FORMAT_MASK_15_0, format); | ||
330 | } else { | ||
331 | BIT_SET(val, ISPCCP2_LCx_CTRL_CRC_SHIFT, | ||
332 | ISPCCP2_LCx_CTRL_CRC_MASK, | ||
333 | config->crc); | ||
334 | |||
335 | BIT_SET(val, ISPCCP2_LCx_CTRL_FORMAT_SHIFT, | ||
336 | ISPCCP2_LCx_CTRL_FORMAT_MASK, format); | ||
337 | } | ||
338 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(0)); | ||
339 | |||
340 | /* ISPCCP2_DAT_START for logical channel #0 */ | ||
341 | isp_reg_writel(isp, config->data_start << ISPCCP2_LCx_DAT_SHIFT, | ||
342 | OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_DAT_START(0)); | ||
343 | |||
344 | /* ISPCCP2_DAT_SIZE for logical channel #0 */ | ||
345 | isp_reg_writel(isp, config->data_size << ISPCCP2_LCx_DAT_SHIFT, | ||
346 | OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_DAT_SIZE(0)); | ||
347 | |||
348 | /* Enable error IRQs for logical channel #0 */ | ||
349 | val = ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ | | ||
350 | ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ | | ||
351 | ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ | | ||
352 | ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ | | ||
353 | ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ | | ||
354 | ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ | | ||
355 | ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ; | ||
356 | |||
357 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LC01_IRQSTATUS); | ||
358 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LC01_IRQENABLE, val); | ||
359 | } | ||
360 | |||
361 | /* | ||
362 | * ccp2_if_configure - Configure ccp2 with data from sensor | ||
363 | * @ccp2: Pointer to ISP CCP2 device | ||
364 | * | ||
365 | * Return 0 on success or a negative error code | ||
366 | */ | ||
367 | static int ccp2_if_configure(struct isp_ccp2_device *ccp2) | ||
368 | { | ||
369 | const struct isp_v4l2_subdevs_group *pdata; | ||
370 | struct v4l2_mbus_framefmt *format; | ||
371 | struct media_pad *pad; | ||
372 | struct v4l2_subdev *sensor; | ||
373 | u32 lines = 0; | ||
374 | int ret; | ||
375 | |||
376 | ccp2_pwr_cfg(ccp2); | ||
377 | |||
378 | pad = media_entity_remote_source(&ccp2->pads[CCP2_PAD_SINK]); | ||
379 | sensor = media_entity_to_v4l2_subdev(pad->entity); | ||
380 | pdata = sensor->host_priv; | ||
381 | |||
382 | ret = ccp2_phyif_config(ccp2, &pdata->bus.ccp2); | ||
383 | if (ret < 0) | ||
384 | return ret; | ||
385 | |||
386 | ccp2_vp_config(ccp2, pdata->bus.ccp2.vpclk_div + 1); | ||
387 | |||
388 | v4l2_subdev_call(sensor, sensor, g_skip_top_lines, &lines); | ||
389 | |||
390 | format = &ccp2->formats[CCP2_PAD_SINK]; | ||
391 | |||
392 | ccp2->if_cfg.data_start = lines; | ||
393 | ccp2->if_cfg.crc = pdata->bus.ccp2.crc; | ||
394 | ccp2->if_cfg.format = format->code; | ||
395 | ccp2->if_cfg.data_size = format->height; | ||
396 | |||
397 | ccp2_lcx_config(ccp2, &ccp2->if_cfg); | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static int ccp2_adjust_bandwidth(struct isp_ccp2_device *ccp2) | ||
403 | { | ||
404 | struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); | ||
405 | struct isp_device *isp = to_isp_device(ccp2); | ||
406 | const struct v4l2_mbus_framefmt *ofmt = &ccp2->formats[CCP2_PAD_SOURCE]; | ||
407 | unsigned long l3_ick = pipe->l3_ick; | ||
408 | struct v4l2_fract *timeperframe; | ||
409 | unsigned int vpclk_div = 2; | ||
410 | unsigned int value; | ||
411 | u64 bound; | ||
412 | u64 area; | ||
413 | |||
414 | /* Compute the minimum clock divisor, based on the pipeline maximum | ||
415 | * data rate. This is an absolute lower bound if we don't want SBL | ||
416 | * overflows, so round the value up. | ||
417 | */ | ||
418 | vpclk_div = max_t(unsigned int, DIV_ROUND_UP(l3_ick, pipe->max_rate), | ||
419 | vpclk_div); | ||
420 | |||
421 | /* Compute the maximum clock divisor, based on the requested frame rate. | ||
422 | * This is a soft lower bound to achieve a frame rate equal or higher | ||
423 | * than the requested value, so round the value down. | ||
424 | */ | ||
425 | timeperframe = &pipe->max_timeperframe; | ||
426 | |||
427 | if (timeperframe->numerator) { | ||
428 | area = ofmt->width * ofmt->height; | ||
429 | bound = div_u64(area * timeperframe->denominator, | ||
430 | timeperframe->numerator); | ||
431 | value = min_t(u64, bound, l3_ick); | ||
432 | vpclk_div = max_t(unsigned int, l3_ick / value, vpclk_div); | ||
433 | } | ||
434 | |||
435 | dev_dbg(isp->dev, "%s: minimum clock divisor = %u\n", __func__, | ||
436 | vpclk_div); | ||
437 | |||
438 | return vpclk_div; | ||
439 | } | ||
440 | |||
441 | /* | ||
442 | * ccp2_mem_configure - Initialize CCP2 memory input/output interface | ||
443 | * @ccp2: Pointer to ISP CCP2 device | ||
444 | * @config: Pointer to ISP mem interface config structure | ||
445 | * | ||
446 | * This will analyze the parameters passed by the interface config | ||
447 | * structure, and configure the respective registers for proper | ||
448 | * CSI1/CCP2 memory input. | ||
449 | */ | ||
450 | static void ccp2_mem_configure(struct isp_ccp2_device *ccp2, | ||
451 | struct isp_interface_mem_config *config) | ||
452 | { | ||
453 | struct isp_device *isp = to_isp_device(ccp2); | ||
454 | u32 sink_pixcode = ccp2->formats[CCP2_PAD_SINK].code; | ||
455 | u32 source_pixcode = ccp2->formats[CCP2_PAD_SOURCE].code; | ||
456 | unsigned int dpcm_decompress = 0; | ||
457 | u32 val, hwords; | ||
458 | |||
459 | if (sink_pixcode != source_pixcode && | ||
460 | sink_pixcode == V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) | ||
461 | dpcm_decompress = 1; | ||
462 | |||
463 | ccp2_pwr_cfg(ccp2); | ||
464 | |||
465 | /* Hsize, Skip */ | ||
466 | isp_reg_writel(isp, ISPCCP2_LCM_HSIZE_SKIP_MIN | | ||
467 | (config->hsize_count << ISPCCP2_LCM_HSIZE_SHIFT), | ||
468 | OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_HSIZE); | ||
469 | |||
470 | /* Vsize, no. of lines */ | ||
471 | isp_reg_writel(isp, config->vsize_count << ISPCCP2_LCM_VSIZE_SHIFT, | ||
472 | OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_VSIZE); | ||
473 | |||
474 | if (ccp2->video_in.bpl_padding == 0) | ||
475 | config->src_ofst = 0; | ||
476 | else | ||
477 | config->src_ofst = ccp2->video_in.bpl_value; | ||
478 | |||
479 | isp_reg_writel(isp, config->src_ofst, OMAP3_ISP_IOMEM_CCP2, | ||
480 | ISPCCP2_LCM_SRC_OFST); | ||
481 | |||
482 | /* Source and Destination formats */ | ||
483 | val = ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 << | ||
484 | ISPCCP2_LCM_CTRL_DST_FORMAT_SHIFT; | ||
485 | |||
486 | if (dpcm_decompress) { | ||
487 | /* source format is RAW8 */ | ||
488 | val |= ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 << | ||
489 | ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT; | ||
490 | |||
491 | /* RAW8 + DPCM10 - simple predictor */ | ||
492 | val |= ISPCCP2_LCM_CTRL_SRC_DPCM_PRED; | ||
493 | |||
494 | /* enable source DPCM decompression */ | ||
495 | val |= ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 << | ||
496 | ISPCCP2_LCM_CTRL_SRC_DECOMPR_SHIFT; | ||
497 | } else { | ||
498 | /* source format is RAW10 */ | ||
499 | val |= ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 << | ||
500 | ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT; | ||
501 | } | ||
502 | |||
503 | /* Burst size to 32x64 */ | ||
504 | val |= ISPCCP2_LCM_CTRL_BURST_SIZE_32X << | ||
505 | ISPCCP2_LCM_CTRL_BURST_SIZE_SHIFT; | ||
506 | |||
507 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_CTRL); | ||
508 | |||
509 | /* Prefetch setup */ | ||
510 | if (dpcm_decompress) | ||
511 | hwords = (ISPCCP2_LCM_HSIZE_SKIP_MIN + | ||
512 | config->hsize_count) >> 3; | ||
513 | else | ||
514 | hwords = (ISPCCP2_LCM_HSIZE_SKIP_MIN + | ||
515 | config->hsize_count) >> 2; | ||
516 | |||
517 | isp_reg_writel(isp, hwords << ISPCCP2_LCM_PREFETCH_SHIFT, | ||
518 | OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_PREFETCH); | ||
519 | |||
520 | /* Video port */ | ||
521 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL, | ||
522 | ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE); | ||
523 | ccp2_vp_config(ccp2, ccp2_adjust_bandwidth(ccp2)); | ||
524 | |||
525 | /* Clear LCM interrupts */ | ||
526 | isp_reg_writel(isp, ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ | | ||
527 | ISPCCP2_LCM_IRQSTATUS_EOF_IRQ, | ||
528 | OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQSTATUS); | ||
529 | |||
530 | /* Enable LCM interupts */ | ||
531 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQENABLE, | ||
532 | ISPCCP2_LCM_IRQSTATUS_EOF_IRQ | | ||
533 | ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ); | ||
534 | } | ||
535 | |||
536 | /* | ||
537 | * ccp2_set_inaddr - Sets memory address of input frame. | ||
538 | * @ccp2: Pointer to ISP CCP2 device | ||
539 | * @addr: 32bit memory address aligned on 32byte boundary. | ||
540 | * | ||
541 | * Configures the memory address from which the input frame is to be read. | ||
542 | */ | ||
543 | static void ccp2_set_inaddr(struct isp_ccp2_device *ccp2, u32 addr) | ||
544 | { | ||
545 | struct isp_device *isp = to_isp_device(ccp2); | ||
546 | |||
547 | isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_SRC_ADDR); | ||
548 | } | ||
549 | |||
550 | /* ----------------------------------------------------------------------------- | ||
551 | * Interrupt handling | ||
552 | */ | ||
553 | |||
554 | static void ccp2_isr_buffer(struct isp_ccp2_device *ccp2) | ||
555 | { | ||
556 | struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); | ||
557 | struct isp_buffer *buffer; | ||
558 | |||
559 | buffer = omap3isp_video_buffer_next(&ccp2->video_in, ccp2->error); | ||
560 | if (buffer != NULL) | ||
561 | ccp2_set_inaddr(ccp2, buffer->isp_addr); | ||
562 | |||
563 | pipe->state |= ISP_PIPELINE_IDLE_INPUT; | ||
564 | |||
565 | if (ccp2->state == ISP_PIPELINE_STREAM_SINGLESHOT) { | ||
566 | if (isp_pipeline_ready(pipe)) | ||
567 | omap3isp_pipeline_set_stream(pipe, | ||
568 | ISP_PIPELINE_STREAM_SINGLESHOT); | ||
569 | } | ||
570 | |||
571 | ccp2->error = 0; | ||
572 | } | ||
573 | |||
574 | /* | ||
575 | * omap3isp_ccp2_isr - Handle ISP CCP2 interrupts | ||
576 | * @ccp2: Pointer to ISP CCP2 device | ||
577 | * | ||
578 | * This will handle the CCP2 interrupts | ||
579 | * | ||
580 | * Returns -EIO in case of error, or 0 on success. | ||
581 | */ | ||
582 | int omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2) | ||
583 | { | ||
584 | struct isp_device *isp = to_isp_device(ccp2); | ||
585 | int ret = 0; | ||
586 | static const u32 ISPCCP2_LC01_ERROR = | ||
587 | ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ | | ||
588 | ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ | | ||
589 | ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ | | ||
590 | ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ | | ||
591 | ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ | | ||
592 | ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ; | ||
593 | u32 lcx_irqstatus, lcm_irqstatus; | ||
594 | |||
595 | /* First clear the interrupts */ | ||
596 | lcx_irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, | ||
597 | ISPCCP2_LC01_IRQSTATUS); | ||
598 | isp_reg_writel(isp, lcx_irqstatus, OMAP3_ISP_IOMEM_CCP2, | ||
599 | ISPCCP2_LC01_IRQSTATUS); | ||
600 | |||
601 | lcm_irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, | ||
602 | ISPCCP2_LCM_IRQSTATUS); | ||
603 | isp_reg_writel(isp, lcm_irqstatus, OMAP3_ISP_IOMEM_CCP2, | ||
604 | ISPCCP2_LCM_IRQSTATUS); | ||
605 | /* Errors */ | ||
606 | if (lcx_irqstatus & ISPCCP2_LC01_ERROR) { | ||
607 | ccp2->error = 1; | ||
608 | dev_dbg(isp->dev, "CCP2 err:%x\n", lcx_irqstatus); | ||
609 | return -EIO; | ||
610 | } | ||
611 | |||
612 | if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ) { | ||
613 | ccp2->error = 1; | ||
614 | dev_dbg(isp->dev, "CCP2 OCP err:%x\n", lcm_irqstatus); | ||
615 | ret = -EIO; | ||
616 | } | ||
617 | |||
618 | if (omap3isp_module_sync_is_stopping(&ccp2->wait, &ccp2->stopping)) | ||
619 | return 0; | ||
620 | |||
621 | /* Frame number propagation */ | ||
622 | if (lcx_irqstatus & ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ) { | ||
623 | struct isp_pipeline *pipe = | ||
624 | to_isp_pipeline(&ccp2->subdev.entity); | ||
625 | if (pipe->do_propagation) | ||
626 | atomic_inc(&pipe->frame_number); | ||
627 | } | ||
628 | |||
629 | /* Handle queued buffers on frame end interrupts */ | ||
630 | if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_EOF_IRQ) | ||
631 | ccp2_isr_buffer(ccp2); | ||
632 | |||
633 | return ret; | ||
634 | } | ||
635 | |||
636 | /* ----------------------------------------------------------------------------- | ||
637 | * V4L2 subdev operations | ||
638 | */ | ||
639 | |||
640 | static const unsigned int ccp2_fmts[] = { | ||
641 | V4L2_MBUS_FMT_SGRBG10_1X10, | ||
642 | V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, | ||
643 | }; | ||
644 | |||
645 | /* | ||
646 | * __ccp2_get_format - helper function for getting ccp2 format | ||
647 | * @ccp2 : Pointer to ISP CCP2 device | ||
648 | * @fh : V4L2 subdev file handle | ||
649 | * @pad : pad number | ||
650 | * @which : wanted subdev format | ||
651 | * return format structure or NULL on error | ||
652 | */ | ||
653 | static struct v4l2_mbus_framefmt * | ||
654 | __ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_fh *fh, | ||
655 | unsigned int pad, enum v4l2_subdev_format_whence which) | ||
656 | { | ||
657 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
658 | return v4l2_subdev_get_try_format(fh, pad); | ||
659 | else | ||
660 | return &ccp2->formats[pad]; | ||
661 | } | ||
662 | |||
663 | /* | ||
664 | * ccp2_try_format - Handle try format by pad subdev method | ||
665 | * @ccp2 : Pointer to ISP CCP2 device | ||
666 | * @fh : V4L2 subdev file handle | ||
667 | * @pad : pad num | ||
668 | * @fmt : pointer to v4l2 mbus format structure | ||
669 | * @which : wanted subdev format | ||
670 | */ | ||
671 | static void ccp2_try_format(struct isp_ccp2_device *ccp2, | ||
672 | struct v4l2_subdev_fh *fh, unsigned int pad, | ||
673 | struct v4l2_mbus_framefmt *fmt, | ||
674 | enum v4l2_subdev_format_whence which) | ||
675 | { | ||
676 | struct v4l2_mbus_framefmt *format; | ||
677 | |||
678 | switch (pad) { | ||
679 | case CCP2_PAD_SINK: | ||
680 | if (fmt->code != V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) | ||
681 | fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
682 | |||
683 | if (ccp2->input == CCP2_INPUT_SENSOR) { | ||
684 | fmt->width = clamp_t(u32, fmt->width, | ||
685 | ISPCCP2_DAT_START_MIN, | ||
686 | ISPCCP2_DAT_START_MAX); | ||
687 | fmt->height = clamp_t(u32, fmt->height, | ||
688 | ISPCCP2_DAT_SIZE_MIN, | ||
689 | ISPCCP2_DAT_SIZE_MAX); | ||
690 | } else if (ccp2->input == CCP2_INPUT_MEMORY) { | ||
691 | fmt->width = clamp_t(u32, fmt->width, | ||
692 | ISPCCP2_LCM_HSIZE_COUNT_MIN, | ||
693 | ISPCCP2_LCM_HSIZE_COUNT_MAX); | ||
694 | fmt->height = clamp_t(u32, fmt->height, | ||
695 | ISPCCP2_LCM_VSIZE_MIN, | ||
696 | ISPCCP2_LCM_VSIZE_MAX); | ||
697 | } | ||
698 | break; | ||
699 | |||
700 | case CCP2_PAD_SOURCE: | ||
701 | /* Source format - copy sink format and change pixel code | ||
702 | * to SGRBG10_1X10 as we don't support CCP2 write to memory. | ||
703 | * When CCP2 write to memory feature will be added this | ||
704 | * should be changed properly. | ||
705 | */ | ||
706 | format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, which); | ||
707 | memcpy(fmt, format, sizeof(*fmt)); | ||
708 | fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
709 | break; | ||
710 | } | ||
711 | |||
712 | fmt->field = V4L2_FIELD_NONE; | ||
713 | fmt->colorspace = V4L2_COLORSPACE_SRGB; | ||
714 | } | ||
715 | |||
716 | /* | ||
717 | * ccp2_enum_mbus_code - Handle pixel format enumeration | ||
718 | * @sd : pointer to v4l2 subdev structure | ||
719 | * @fh : V4L2 subdev file handle | ||
720 | * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
721 | * return -EINVAL or zero on success | ||
722 | */ | ||
723 | static int ccp2_enum_mbus_code(struct v4l2_subdev *sd, | ||
724 | struct v4l2_subdev_fh *fh, | ||
725 | struct v4l2_subdev_mbus_code_enum *code) | ||
726 | { | ||
727 | struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); | ||
728 | struct v4l2_mbus_framefmt *format; | ||
729 | |||
730 | if (code->pad == CCP2_PAD_SINK) { | ||
731 | if (code->index >= ARRAY_SIZE(ccp2_fmts)) | ||
732 | return -EINVAL; | ||
733 | |||
734 | code->code = ccp2_fmts[code->index]; | ||
735 | } else { | ||
736 | if (code->index != 0) | ||
737 | return -EINVAL; | ||
738 | |||
739 | format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, | ||
740 | V4L2_SUBDEV_FORMAT_TRY); | ||
741 | code->code = format->code; | ||
742 | } | ||
743 | |||
744 | return 0; | ||
745 | } | ||
746 | |||
747 | static int ccp2_enum_frame_size(struct v4l2_subdev *sd, | ||
748 | struct v4l2_subdev_fh *fh, | ||
749 | struct v4l2_subdev_frame_size_enum *fse) | ||
750 | { | ||
751 | struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); | ||
752 | struct v4l2_mbus_framefmt format; | ||
753 | |||
754 | if (fse->index != 0) | ||
755 | return -EINVAL; | ||
756 | |||
757 | format.code = fse->code; | ||
758 | format.width = 1; | ||
759 | format.height = 1; | ||
760 | ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
761 | fse->min_width = format.width; | ||
762 | fse->min_height = format.height; | ||
763 | |||
764 | if (format.code != fse->code) | ||
765 | return -EINVAL; | ||
766 | |||
767 | format.code = fse->code; | ||
768 | format.width = -1; | ||
769 | format.height = -1; | ||
770 | ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
771 | fse->max_width = format.width; | ||
772 | fse->max_height = format.height; | ||
773 | |||
774 | return 0; | ||
775 | } | ||
776 | |||
777 | /* | ||
778 | * ccp2_get_format - Handle get format by pads subdev method | ||
779 | * @sd : pointer to v4l2 subdev structure | ||
780 | * @fh : V4L2 subdev file handle | ||
781 | * @fmt : pointer to v4l2 subdev format structure | ||
782 | * return -EINVAL or zero on success | ||
783 | */ | ||
784 | static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
785 | struct v4l2_subdev_format *fmt) | ||
786 | { | ||
787 | struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); | ||
788 | struct v4l2_mbus_framefmt *format; | ||
789 | |||
790 | format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which); | ||
791 | if (format == NULL) | ||
792 | return -EINVAL; | ||
793 | |||
794 | fmt->format = *format; | ||
795 | return 0; | ||
796 | } | ||
797 | |||
798 | /* | ||
799 | * ccp2_set_format - Handle set format by pads subdev method | ||
800 | * @sd : pointer to v4l2 subdev structure | ||
801 | * @fh : V4L2 subdev file handle | ||
802 | * @fmt : pointer to v4l2 subdev format structure | ||
803 | * returns zero | ||
804 | */ | ||
805 | static int ccp2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
806 | struct v4l2_subdev_format *fmt) | ||
807 | { | ||
808 | struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); | ||
809 | struct v4l2_mbus_framefmt *format; | ||
810 | |||
811 | format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which); | ||
812 | if (format == NULL) | ||
813 | return -EINVAL; | ||
814 | |||
815 | ccp2_try_format(ccp2, fh, fmt->pad, &fmt->format, fmt->which); | ||
816 | *format = fmt->format; | ||
817 | |||
818 | /* Propagate the format from sink to source */ | ||
819 | if (fmt->pad == CCP2_PAD_SINK) { | ||
820 | format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SOURCE, | ||
821 | fmt->which); | ||
822 | *format = fmt->format; | ||
823 | ccp2_try_format(ccp2, fh, CCP2_PAD_SOURCE, format, fmt->which); | ||
824 | } | ||
825 | |||
826 | return 0; | ||
827 | } | ||
828 | |||
829 | /* | ||
830 | * ccp2_init_formats - Initialize formats on all pads | ||
831 | * @sd: ISP CCP2 V4L2 subdevice | ||
832 | * @fh: V4L2 subdev file handle | ||
833 | * | ||
834 | * Initialize all pad formats with default values. If fh is not NULL, try | ||
835 | * formats are initialized on the file handle. Otherwise active formats are | ||
836 | * initialized on the device. | ||
837 | */ | ||
838 | static int ccp2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
839 | { | ||
840 | struct v4l2_subdev_format format; | ||
841 | |||
842 | memset(&format, 0, sizeof(format)); | ||
843 | format.pad = CCP2_PAD_SINK; | ||
844 | format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
845 | format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
846 | format.format.width = 4096; | ||
847 | format.format.height = 4096; | ||
848 | ccp2_set_format(sd, fh, &format); | ||
849 | |||
850 | return 0; | ||
851 | } | ||
852 | |||
853 | /* | ||
854 | * ccp2_s_stream - Enable/Disable streaming on ccp2 subdev | ||
855 | * @sd : pointer to v4l2 subdev structure | ||
856 | * @enable: 1 == Enable, 0 == Disable | ||
857 | * return zero | ||
858 | */ | ||
859 | static int ccp2_s_stream(struct v4l2_subdev *sd, int enable) | ||
860 | { | ||
861 | struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); | ||
862 | struct isp_device *isp = to_isp_device(ccp2); | ||
863 | struct device *dev = to_device(ccp2); | ||
864 | int ret; | ||
865 | |||
866 | if (ccp2->state == ISP_PIPELINE_STREAM_STOPPED) { | ||
867 | if (enable == ISP_PIPELINE_STREAM_STOPPED) | ||
868 | return 0; | ||
869 | atomic_set(&ccp2->stopping, 0); | ||
870 | ccp2->error = 0; | ||
871 | } | ||
872 | |||
873 | switch (enable) { | ||
874 | case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
875 | if (ccp2->phy) { | ||
876 | ret = omap3isp_csiphy_acquire(ccp2->phy); | ||
877 | if (ret < 0) | ||
878 | return ret; | ||
879 | } | ||
880 | |||
881 | ccp2_if_configure(ccp2); | ||
882 | ccp2_print_status(ccp2); | ||
883 | |||
884 | /* Enable CSI1/CCP2 interface */ | ||
885 | ccp2_if_enable(ccp2, 1); | ||
886 | break; | ||
887 | |||
888 | case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
889 | if (ccp2->state != ISP_PIPELINE_STREAM_SINGLESHOT) { | ||
890 | struct v4l2_mbus_framefmt *format; | ||
891 | |||
892 | format = &ccp2->formats[CCP2_PAD_SINK]; | ||
893 | |||
894 | ccp2->mem_cfg.hsize_count = format->width; | ||
895 | ccp2->mem_cfg.vsize_count = format->height; | ||
896 | ccp2->mem_cfg.src_ofst = 0; | ||
897 | |||
898 | ccp2_mem_configure(ccp2, &ccp2->mem_cfg); | ||
899 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI1_READ); | ||
900 | ccp2_print_status(ccp2); | ||
901 | } | ||
902 | ccp2_mem_enable(ccp2, 1); | ||
903 | break; | ||
904 | |||
905 | case ISP_PIPELINE_STREAM_STOPPED: | ||
906 | if (omap3isp_module_sync_idle(&sd->entity, &ccp2->wait, | ||
907 | &ccp2->stopping)) | ||
908 | dev_dbg(dev, "%s: module stop timeout.\n", sd->name); | ||
909 | if (ccp2->input == CCP2_INPUT_MEMORY) { | ||
910 | ccp2_mem_enable(ccp2, 0); | ||
911 | omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI1_READ); | ||
912 | } else if (ccp2->input == CCP2_INPUT_SENSOR) { | ||
913 | /* Disable CSI1/CCP2 interface */ | ||
914 | ccp2_if_enable(ccp2, 0); | ||
915 | if (ccp2->phy) | ||
916 | omap3isp_csiphy_release(ccp2->phy); | ||
917 | } | ||
918 | break; | ||
919 | } | ||
920 | |||
921 | ccp2->state = enable; | ||
922 | return 0; | ||
923 | } | ||
924 | |||
925 | /* subdev video operations */ | ||
926 | static const struct v4l2_subdev_video_ops ccp2_sd_video_ops = { | ||
927 | .s_stream = ccp2_s_stream, | ||
928 | }; | ||
929 | |||
930 | /* subdev pad operations */ | ||
931 | static const struct v4l2_subdev_pad_ops ccp2_sd_pad_ops = { | ||
932 | .enum_mbus_code = ccp2_enum_mbus_code, | ||
933 | .enum_frame_size = ccp2_enum_frame_size, | ||
934 | .get_fmt = ccp2_get_format, | ||
935 | .set_fmt = ccp2_set_format, | ||
936 | }; | ||
937 | |||
938 | /* subdev operations */ | ||
939 | static const struct v4l2_subdev_ops ccp2_sd_ops = { | ||
940 | .video = &ccp2_sd_video_ops, | ||
941 | .pad = &ccp2_sd_pad_ops, | ||
942 | }; | ||
943 | |||
944 | /* subdev internal operations */ | ||
945 | static const struct v4l2_subdev_internal_ops ccp2_sd_internal_ops = { | ||
946 | .open = ccp2_init_formats, | ||
947 | }; | ||
948 | |||
949 | /* -------------------------------------------------------------------------- | ||
950 | * ISP ccp2 video device node | ||
951 | */ | ||
952 | |||
953 | /* | ||
954 | * ccp2_video_queue - Queue video buffer. | ||
955 | * @video : Pointer to isp video structure | ||
956 | * @buffer: Pointer to isp_buffer structure | ||
957 | * return -EIO or zero on success | ||
958 | */ | ||
959 | static int ccp2_video_queue(struct isp_video *video, struct isp_buffer *buffer) | ||
960 | { | ||
961 | struct isp_ccp2_device *ccp2 = &video->isp->isp_ccp2; | ||
962 | |||
963 | ccp2_set_inaddr(ccp2, buffer->isp_addr); | ||
964 | return 0; | ||
965 | } | ||
966 | |||
967 | static const struct isp_video_operations ccp2_video_ops = { | ||
968 | .queue = ccp2_video_queue, | ||
969 | }; | ||
970 | |||
971 | /* ----------------------------------------------------------------------------- | ||
972 | * Media entity operations | ||
973 | */ | ||
974 | |||
975 | /* | ||
976 | * ccp2_link_setup - Setup ccp2 connections. | ||
977 | * @entity : Pointer to media entity structure | ||
978 | * @local : Pointer to local pad array | ||
979 | * @remote : Pointer to remote pad array | ||
980 | * @flags : Link flags | ||
981 | * return -EINVAL on error or zero on success | ||
982 | */ | ||
983 | static int ccp2_link_setup(struct media_entity *entity, | ||
984 | const struct media_pad *local, | ||
985 | const struct media_pad *remote, u32 flags) | ||
986 | { | ||
987 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
988 | struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); | ||
989 | |||
990 | switch (local->index | media_entity_type(remote->entity)) { | ||
991 | case CCP2_PAD_SINK | MEDIA_ENT_T_DEVNODE: | ||
992 | /* read from memory */ | ||
993 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
994 | if (ccp2->input == CCP2_INPUT_SENSOR) | ||
995 | return -EBUSY; | ||
996 | ccp2->input = CCP2_INPUT_MEMORY; | ||
997 | } else { | ||
998 | if (ccp2->input == CCP2_INPUT_MEMORY) | ||
999 | ccp2->input = CCP2_INPUT_NONE; | ||
1000 | } | ||
1001 | break; | ||
1002 | |||
1003 | case CCP2_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1004 | /* read from sensor/phy */ | ||
1005 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1006 | if (ccp2->input == CCP2_INPUT_MEMORY) | ||
1007 | return -EBUSY; | ||
1008 | ccp2->input = CCP2_INPUT_SENSOR; | ||
1009 | } else { | ||
1010 | if (ccp2->input == CCP2_INPUT_SENSOR) | ||
1011 | ccp2->input = CCP2_INPUT_NONE; | ||
1012 | } break; | ||
1013 | |||
1014 | case CCP2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1015 | /* write to video port/ccdc */ | ||
1016 | if (flags & MEDIA_LNK_FL_ENABLED) | ||
1017 | ccp2->output = CCP2_OUTPUT_CCDC; | ||
1018 | else | ||
1019 | ccp2->output = CCP2_OUTPUT_NONE; | ||
1020 | break; | ||
1021 | |||
1022 | default: | ||
1023 | return -EINVAL; | ||
1024 | } | ||
1025 | |||
1026 | return 0; | ||
1027 | } | ||
1028 | |||
1029 | /* media operations */ | ||
1030 | static const struct media_entity_operations ccp2_media_ops = { | ||
1031 | .link_setup = ccp2_link_setup, | ||
1032 | }; | ||
1033 | |||
1034 | /* | ||
1035 | * ccp2_init_entities - Initialize ccp2 subdev and media entity. | ||
1036 | * @ccp2: Pointer to ISP CCP2 device | ||
1037 | * return negative error code or zero on success | ||
1038 | */ | ||
1039 | static int ccp2_init_entities(struct isp_ccp2_device *ccp2) | ||
1040 | { | ||
1041 | struct v4l2_subdev *sd = &ccp2->subdev; | ||
1042 | struct media_pad *pads = ccp2->pads; | ||
1043 | struct media_entity *me = &sd->entity; | ||
1044 | int ret; | ||
1045 | |||
1046 | ccp2->input = CCP2_INPUT_NONE; | ||
1047 | ccp2->output = CCP2_OUTPUT_NONE; | ||
1048 | |||
1049 | v4l2_subdev_init(sd, &ccp2_sd_ops); | ||
1050 | sd->internal_ops = &ccp2_sd_internal_ops; | ||
1051 | strlcpy(sd->name, "OMAP3 ISP CCP2", sizeof(sd->name)); | ||
1052 | sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
1053 | v4l2_set_subdevdata(sd, ccp2); | ||
1054 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1055 | |||
1056 | pads[CCP2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
1057 | pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | ||
1058 | |||
1059 | me->ops = &ccp2_media_ops; | ||
1060 | ret = media_entity_init(me, CCP2_PADS_NUM, pads, 0); | ||
1061 | if (ret < 0) | ||
1062 | return ret; | ||
1063 | |||
1064 | ccp2_init_formats(sd, NULL); | ||
1065 | |||
1066 | /* | ||
1067 | * The CCP2 has weird line alignment requirements, possibly caused by | ||
1068 | * DPCM8 decompression. Line length for data read from memory must be a | ||
1069 | * multiple of 128 bits (16 bytes) in continuous mode (when no padding | ||
1070 | * is present at end of lines). Additionally, if padding is used, the | ||
1071 | * padded line length must be a multiple of 32 bytes. To simplify the | ||
1072 | * implementation we use a fixed 32 bytes alignment regardless of the | ||
1073 | * input format and width. If strict 128 bits alignment support is | ||
1074 | * required ispvideo will need to be made aware of this special dual | ||
1075 | * alignement requirements. | ||
1076 | */ | ||
1077 | ccp2->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
1078 | ccp2->video_in.bpl_alignment = 32; | ||
1079 | ccp2->video_in.bpl_max = 0xffffffe0; | ||
1080 | ccp2->video_in.isp = to_isp_device(ccp2); | ||
1081 | ccp2->video_in.ops = &ccp2_video_ops; | ||
1082 | ccp2->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; | ||
1083 | |||
1084 | ret = omap3isp_video_init(&ccp2->video_in, "CCP2"); | ||
1085 | if (ret < 0) | ||
1086 | return ret; | ||
1087 | |||
1088 | /* Connect the video node to the ccp2 subdev. */ | ||
1089 | ret = media_entity_create_link(&ccp2->video_in.video.entity, 0, | ||
1090 | &ccp2->subdev.entity, CCP2_PAD_SINK, 0); | ||
1091 | if (ret < 0) | ||
1092 | return ret; | ||
1093 | |||
1094 | return 0; | ||
1095 | } | ||
1096 | |||
1097 | /* | ||
1098 | * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev | ||
1099 | * @ccp2: Pointer to ISP CCP2 device | ||
1100 | */ | ||
1101 | void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2) | ||
1102 | { | ||
1103 | media_entity_cleanup(&ccp2->subdev.entity); | ||
1104 | |||
1105 | v4l2_device_unregister_subdev(&ccp2->subdev); | ||
1106 | omap3isp_video_unregister(&ccp2->video_in); | ||
1107 | } | ||
1108 | |||
1109 | /* | ||
1110 | * omap3isp_ccp2_register_entities - Register the subdev media entity | ||
1111 | * @ccp2: Pointer to ISP CCP2 device | ||
1112 | * @vdev: Pointer to v4l device | ||
1113 | * return negative error code or zero on success | ||
1114 | */ | ||
1115 | |||
1116 | int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2, | ||
1117 | struct v4l2_device *vdev) | ||
1118 | { | ||
1119 | int ret; | ||
1120 | |||
1121 | /* Register the subdev and video nodes. */ | ||
1122 | ret = v4l2_device_register_subdev(vdev, &ccp2->subdev); | ||
1123 | if (ret < 0) | ||
1124 | goto error; | ||
1125 | |||
1126 | ret = omap3isp_video_register(&ccp2->video_in, vdev); | ||
1127 | if (ret < 0) | ||
1128 | goto error; | ||
1129 | |||
1130 | return 0; | ||
1131 | |||
1132 | error: | ||
1133 | omap3isp_ccp2_unregister_entities(ccp2); | ||
1134 | return ret; | ||
1135 | } | ||
1136 | |||
1137 | /* ----------------------------------------------------------------------------- | ||
1138 | * ISP ccp2 initialisation and cleanup | ||
1139 | */ | ||
1140 | |||
1141 | /* | ||
1142 | * omap3isp_ccp2_cleanup - CCP2 un-initialization | ||
1143 | * @isp : Pointer to ISP device | ||
1144 | */ | ||
1145 | void omap3isp_ccp2_cleanup(struct isp_device *isp) | ||
1146 | { | ||
1147 | struct isp_ccp2_device *ccp2 = &isp->isp_ccp2; | ||
1148 | |||
1149 | regulator_put(ccp2->vdds_csib); | ||
1150 | } | ||
1151 | |||
1152 | /* | ||
1153 | * omap3isp_ccp2_init - CCP2 initialization. | ||
1154 | * @isp : Pointer to ISP device | ||
1155 | * return negative error code or zero on success | ||
1156 | */ | ||
1157 | int omap3isp_ccp2_init(struct isp_device *isp) | ||
1158 | { | ||
1159 | struct isp_ccp2_device *ccp2 = &isp->isp_ccp2; | ||
1160 | int ret; | ||
1161 | |||
1162 | init_waitqueue_head(&ccp2->wait); | ||
1163 | |||
1164 | /* | ||
1165 | * On the OMAP34xx the CSI1 receiver is operated in the CSIb IO | ||
1166 | * complex, which is powered by vdds_csib power rail. Hence the | ||
1167 | * request for the regulator. | ||
1168 | * | ||
1169 | * On the OMAP36xx, the CCP2 uses the CSI PHY1 or PHY2, shared with | ||
1170 | * the CSI2c or CSI2a receivers. The PHY then needs to be explicitly | ||
1171 | * configured. | ||
1172 | * | ||
1173 | * TODO: Don't hardcode the usage of PHY1 (shared with CSI2c). | ||
1174 | */ | ||
1175 | if (isp->revision == ISP_REVISION_2_0) { | ||
1176 | ccp2->vdds_csib = regulator_get(isp->dev, "vdds_csib"); | ||
1177 | if (IS_ERR(ccp2->vdds_csib)) { | ||
1178 | dev_dbg(isp->dev, | ||
1179 | "Could not get regulator vdds_csib\n"); | ||
1180 | ccp2->vdds_csib = NULL; | ||
1181 | } | ||
1182 | } else if (isp->revision == ISP_REVISION_15_0) { | ||
1183 | ccp2->phy = &isp->isp_csiphy1; | ||
1184 | } | ||
1185 | |||
1186 | ret = ccp2_init_entities(ccp2); | ||
1187 | if (ret < 0) | ||
1188 | goto out; | ||
1189 | |||
1190 | ccp2_reset(ccp2); | ||
1191 | out: | ||
1192 | if (ret) | ||
1193 | omap3isp_ccp2_cleanup(isp); | ||
1194 | |||
1195 | return ret; | ||
1196 | } | ||
diff --git a/drivers/media/video/omap3isp/ispccp2.h b/drivers/media/video/omap3isp/ispccp2.h new file mode 100644 index 00000000000..6674e9de2cd --- /dev/null +++ b/drivers/media/video/omap3isp/ispccp2.h | |||
@@ -0,0 +1,99 @@ | |||
1 | /* | ||
2 | * ispccp2.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - CCP2 module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2010 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #ifndef OMAP3_ISP_CCP2_H | ||
28 | #define OMAP3_ISP_CCP2_H | ||
29 | |||
30 | #include <linux/videodev2.h> | ||
31 | |||
32 | struct isp_device; | ||
33 | struct isp_csiphy; | ||
34 | |||
35 | /* Sink and source ccp2 pads */ | ||
36 | #define CCP2_PAD_SINK 0 | ||
37 | #define CCP2_PAD_SOURCE 1 | ||
38 | #define CCP2_PADS_NUM 2 | ||
39 | |||
40 | /* CCP2 input media entity */ | ||
41 | enum ccp2_input_entity { | ||
42 | CCP2_INPUT_NONE, | ||
43 | CCP2_INPUT_SENSOR, | ||
44 | CCP2_INPUT_MEMORY, | ||
45 | }; | ||
46 | |||
47 | /* CCP2 output media entity */ | ||
48 | enum ccp2_output_entity { | ||
49 | CCP2_OUTPUT_NONE, | ||
50 | CCP2_OUTPUT_CCDC, | ||
51 | CCP2_OUTPUT_MEMORY, | ||
52 | }; | ||
53 | |||
54 | |||
55 | /* Logical channel configuration */ | ||
56 | struct isp_interface_lcx_config { | ||
57 | int crc; | ||
58 | u32 data_start; | ||
59 | u32 data_size; | ||
60 | u32 format; | ||
61 | }; | ||
62 | |||
63 | /* Memory channel configuration */ | ||
64 | struct isp_interface_mem_config { | ||
65 | u32 dst_port; | ||
66 | u32 vsize_count; | ||
67 | u32 hsize_count; | ||
68 | u32 src_ofst; | ||
69 | u32 dst_ofst; | ||
70 | }; | ||
71 | |||
72 | /* CCP2 device */ | ||
73 | struct isp_ccp2_device { | ||
74 | struct v4l2_subdev subdev; | ||
75 | struct v4l2_mbus_framefmt formats[CCP2_PADS_NUM]; | ||
76 | struct media_pad pads[CCP2_PADS_NUM]; | ||
77 | |||
78 | enum ccp2_input_entity input; | ||
79 | enum ccp2_output_entity output; | ||
80 | struct isp_interface_lcx_config if_cfg; | ||
81 | struct isp_interface_mem_config mem_cfg; | ||
82 | struct isp_video video_in; | ||
83 | struct isp_csiphy *phy; | ||
84 | struct regulator *vdds_csib; | ||
85 | unsigned int error; | ||
86 | enum isp_pipeline_stream_state state; | ||
87 | wait_queue_head_t wait; | ||
88 | atomic_t stopping; | ||
89 | }; | ||
90 | |||
91 | /* Function declarations */ | ||
92 | int omap3isp_ccp2_init(struct isp_device *isp); | ||
93 | void omap3isp_ccp2_cleanup(struct isp_device *isp); | ||
94 | int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2, | ||
95 | struct v4l2_device *vdev); | ||
96 | void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2); | ||
97 | int omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2); | ||
98 | |||
99 | #endif /* OMAP3_ISP_CCP2_H */ | ||
diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c new file mode 100644 index 00000000000..69161a682b3 --- /dev/null +++ b/drivers/media/video/omap3isp/ispcsi2.c | |||
@@ -0,0 +1,1317 @@ | |||
1 | /* | ||
2 | * ispcsi2.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - CSI2 module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | #include <linux/delay.h> | ||
27 | #include <media/v4l2-common.h> | ||
28 | #include <linux/v4l2-mediabus.h> | ||
29 | #include <linux/mm.h> | ||
30 | |||
31 | #include "isp.h" | ||
32 | #include "ispreg.h" | ||
33 | #include "ispcsi2.h" | ||
34 | |||
35 | /* | ||
36 | * csi2_if_enable - Enable CSI2 Receiver interface. | ||
37 | * @enable: enable flag | ||
38 | * | ||
39 | */ | ||
40 | static void csi2_if_enable(struct isp_device *isp, | ||
41 | struct isp_csi2_device *csi2, u8 enable) | ||
42 | { | ||
43 | struct isp_csi2_ctrl_cfg *currctrl = &csi2->ctrl; | ||
44 | |||
45 | isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_CTRL, ISPCSI2_CTRL_IF_EN, | ||
46 | enable ? ISPCSI2_CTRL_IF_EN : 0); | ||
47 | |||
48 | currctrl->if_enable = enable; | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * csi2_recv_config - CSI2 receiver module configuration. | ||
53 | * @currctrl: isp_csi2_ctrl_cfg structure | ||
54 | * | ||
55 | */ | ||
56 | static void csi2_recv_config(struct isp_device *isp, | ||
57 | struct isp_csi2_device *csi2, | ||
58 | struct isp_csi2_ctrl_cfg *currctrl) | ||
59 | { | ||
60 | u32 reg; | ||
61 | |||
62 | reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTRL); | ||
63 | |||
64 | if (currctrl->frame_mode) | ||
65 | reg |= ISPCSI2_CTRL_FRAME; | ||
66 | else | ||
67 | reg &= ~ISPCSI2_CTRL_FRAME; | ||
68 | |||
69 | if (currctrl->vp_clk_enable) | ||
70 | reg |= ISPCSI2_CTRL_VP_CLK_EN; | ||
71 | else | ||
72 | reg &= ~ISPCSI2_CTRL_VP_CLK_EN; | ||
73 | |||
74 | if (currctrl->vp_only_enable) | ||
75 | reg |= ISPCSI2_CTRL_VP_ONLY_EN; | ||
76 | else | ||
77 | reg &= ~ISPCSI2_CTRL_VP_ONLY_EN; | ||
78 | |||
79 | reg &= ~ISPCSI2_CTRL_VP_OUT_CTRL_MASK; | ||
80 | reg |= currctrl->vp_out_ctrl << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT; | ||
81 | |||
82 | if (currctrl->ecc_enable) | ||
83 | reg |= ISPCSI2_CTRL_ECC_EN; | ||
84 | else | ||
85 | reg &= ~ISPCSI2_CTRL_ECC_EN; | ||
86 | |||
87 | isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTRL); | ||
88 | } | ||
89 | |||
90 | static const unsigned int csi2_input_fmts[] = { | ||
91 | V4L2_MBUS_FMT_SGRBG10_1X10, | ||
92 | V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, | ||
93 | V4L2_MBUS_FMT_SRGGB10_1X10, | ||
94 | V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, | ||
95 | V4L2_MBUS_FMT_SBGGR10_1X10, | ||
96 | V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, | ||
97 | V4L2_MBUS_FMT_SGBRG10_1X10, | ||
98 | V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, | ||
99 | }; | ||
100 | |||
101 | /* To set the format on the CSI2 requires a mapping function that takes | ||
102 | * the following inputs: | ||
103 | * - 2 different formats (at this time) | ||
104 | * - 2 destinations (mem, vp+mem) (vp only handled separately) | ||
105 | * - 2 decompression options (on, off) | ||
106 | * - 2 isp revisions (certain format must be handled differently on OMAP3630) | ||
107 | * Output should be CSI2 frame format code | ||
108 | * Array indices as follows: [format][dest][decompr][is_3630] | ||
109 | * Not all combinations are valid. 0 means invalid. | ||
110 | */ | ||
111 | static const u16 __csi2_fmt_map[2][2][2][2] = { | ||
112 | /* RAW10 formats */ | ||
113 | { | ||
114 | /* Output to memory */ | ||
115 | { | ||
116 | /* No DPCM decompression */ | ||
117 | { CSI2_PIX_FMT_RAW10_EXP16, CSI2_PIX_FMT_RAW10_EXP16 }, | ||
118 | /* DPCM decompression */ | ||
119 | { 0, 0 }, | ||
120 | }, | ||
121 | /* Output to both */ | ||
122 | { | ||
123 | /* No DPCM decompression */ | ||
124 | { CSI2_PIX_FMT_RAW10_EXP16_VP, | ||
125 | CSI2_PIX_FMT_RAW10_EXP16_VP }, | ||
126 | /* DPCM decompression */ | ||
127 | { 0, 0 }, | ||
128 | }, | ||
129 | }, | ||
130 | /* RAW10 DPCM8 formats */ | ||
131 | { | ||
132 | /* Output to memory */ | ||
133 | { | ||
134 | /* No DPCM decompression */ | ||
135 | { CSI2_PIX_FMT_RAW8, CSI2_USERDEF_8BIT_DATA1 }, | ||
136 | /* DPCM decompression */ | ||
137 | { CSI2_PIX_FMT_RAW8_DPCM10_EXP16, | ||
138 | CSI2_USERDEF_8BIT_DATA1_DPCM10 }, | ||
139 | }, | ||
140 | /* Output to both */ | ||
141 | { | ||
142 | /* No DPCM decompression */ | ||
143 | { CSI2_PIX_FMT_RAW8_VP, | ||
144 | CSI2_PIX_FMT_RAW8_VP }, | ||
145 | /* DPCM decompression */ | ||
146 | { CSI2_PIX_FMT_RAW8_DPCM10_VP, | ||
147 | CSI2_USERDEF_8BIT_DATA1_DPCM10_VP }, | ||
148 | }, | ||
149 | }, | ||
150 | }; | ||
151 | |||
152 | /* | ||
153 | * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID | ||
154 | * @csi2: ISP CSI2 device | ||
155 | * | ||
156 | * Returns CSI2 physical format id | ||
157 | */ | ||
158 | static u16 csi2_ctx_map_format(struct isp_csi2_device *csi2) | ||
159 | { | ||
160 | const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK]; | ||
161 | int fmtidx, destidx, is_3630; | ||
162 | |||
163 | switch (fmt->code) { | ||
164 | case V4L2_MBUS_FMT_SGRBG10_1X10: | ||
165 | case V4L2_MBUS_FMT_SRGGB10_1X10: | ||
166 | case V4L2_MBUS_FMT_SBGGR10_1X10: | ||
167 | case V4L2_MBUS_FMT_SGBRG10_1X10: | ||
168 | fmtidx = 0; | ||
169 | break; | ||
170 | case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: | ||
171 | case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8: | ||
172 | case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8: | ||
173 | case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8: | ||
174 | fmtidx = 1; | ||
175 | break; | ||
176 | default: | ||
177 | WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n", | ||
178 | fmt->code); | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | if (!(csi2->output & CSI2_OUTPUT_CCDC) && | ||
183 | !(csi2->output & CSI2_OUTPUT_MEMORY)) { | ||
184 | /* Neither output enabled is a valid combination */ | ||
185 | return CSI2_PIX_FMT_OTHERS; | ||
186 | } | ||
187 | |||
188 | /* If we need to skip frames at the beginning of the stream disable the | ||
189 | * video port to avoid sending the skipped frames to the CCDC. | ||
190 | */ | ||
191 | destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_CCDC); | ||
192 | is_3630 = csi2->isp->revision == ISP_REVISION_15_0; | ||
193 | |||
194 | return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress][is_3630]; | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * csi2_set_outaddr - Set memory address to save output image | ||
199 | * @csi2: Pointer to ISP CSI2a device. | ||
200 | * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary. | ||
201 | * | ||
202 | * Sets the memory address where the output will be saved. | ||
203 | * | ||
204 | * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte | ||
205 | * boundary. | ||
206 | */ | ||
207 | static void csi2_set_outaddr(struct isp_csi2_device *csi2, u32 addr) | ||
208 | { | ||
209 | struct isp_device *isp = csi2->isp; | ||
210 | struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[0]; | ||
211 | |||
212 | ctx->ping_addr = addr; | ||
213 | ctx->pong_addr = addr; | ||
214 | isp_reg_writel(isp, ctx->ping_addr, | ||
215 | csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum)); | ||
216 | isp_reg_writel(isp, ctx->pong_addr, | ||
217 | csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum)); | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should | ||
222 | * be enabled by CSI2. | ||
223 | * @format_id: mapped format id | ||
224 | * | ||
225 | */ | ||
226 | static inline int is_usr_def_mapping(u32 format_id) | ||
227 | { | ||
228 | return (format_id & 0x40) ? 1 : 0; | ||
229 | } | ||
230 | |||
231 | /* | ||
232 | * csi2_ctx_enable - Enable specified CSI2 context | ||
233 | * @ctxnum: Context number, valid between 0 and 7 values. | ||
234 | * @enable: enable | ||
235 | * | ||
236 | */ | ||
237 | static void csi2_ctx_enable(struct isp_device *isp, | ||
238 | struct isp_csi2_device *csi2, u8 ctxnum, u8 enable) | ||
239 | { | ||
240 | struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum]; | ||
241 | unsigned int skip = 0; | ||
242 | u32 reg; | ||
243 | |||
244 | reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum)); | ||
245 | |||
246 | if (enable) { | ||
247 | if (csi2->frame_skip) | ||
248 | skip = csi2->frame_skip; | ||
249 | else if (csi2->output & CSI2_OUTPUT_MEMORY) | ||
250 | skip = 1; | ||
251 | |||
252 | reg &= ~ISPCSI2_CTX_CTRL1_COUNT_MASK; | ||
253 | reg |= ISPCSI2_CTX_CTRL1_COUNT_UNLOCK | ||
254 | | (skip << ISPCSI2_CTX_CTRL1_COUNT_SHIFT) | ||
255 | | ISPCSI2_CTX_CTRL1_CTX_EN; | ||
256 | } else { | ||
257 | reg &= ~ISPCSI2_CTX_CTRL1_CTX_EN; | ||
258 | } | ||
259 | |||
260 | isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum)); | ||
261 | ctx->enabled = enable; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * csi2_ctx_config - CSI2 context configuration. | ||
266 | * @ctx: context configuration | ||
267 | * | ||
268 | */ | ||
269 | static void csi2_ctx_config(struct isp_device *isp, | ||
270 | struct isp_csi2_device *csi2, | ||
271 | struct isp_csi2_ctx_cfg *ctx) | ||
272 | { | ||
273 | u32 reg; | ||
274 | |||
275 | /* Set up CSI2_CTx_CTRL1 */ | ||
276 | reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum)); | ||
277 | |||
278 | if (ctx->eof_enabled) | ||
279 | reg |= ISPCSI2_CTX_CTRL1_EOF_EN; | ||
280 | else | ||
281 | reg &= ~ISPCSI2_CTX_CTRL1_EOF_EN; | ||
282 | |||
283 | if (ctx->eol_enabled) | ||
284 | reg |= ISPCSI2_CTX_CTRL1_EOL_EN; | ||
285 | else | ||
286 | reg &= ~ISPCSI2_CTX_CTRL1_EOL_EN; | ||
287 | |||
288 | if (ctx->checksum_enabled) | ||
289 | reg |= ISPCSI2_CTX_CTRL1_CS_EN; | ||
290 | else | ||
291 | reg &= ~ISPCSI2_CTX_CTRL1_CS_EN; | ||
292 | |||
293 | isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum)); | ||
294 | |||
295 | /* Set up CSI2_CTx_CTRL2 */ | ||
296 | reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum)); | ||
297 | |||
298 | reg &= ~(ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK); | ||
299 | reg |= ctx->virtual_id << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT; | ||
300 | |||
301 | reg &= ~(ISPCSI2_CTX_CTRL2_FORMAT_MASK); | ||
302 | reg |= ctx->format_id << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT; | ||
303 | |||
304 | if (ctx->dpcm_decompress) { | ||
305 | if (ctx->dpcm_predictor) | ||
306 | reg |= ISPCSI2_CTX_CTRL2_DPCM_PRED; | ||
307 | else | ||
308 | reg &= ~ISPCSI2_CTX_CTRL2_DPCM_PRED; | ||
309 | } | ||
310 | |||
311 | if (is_usr_def_mapping(ctx->format_id)) { | ||
312 | reg &= ~ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK; | ||
313 | reg |= 2 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT; | ||
314 | } | ||
315 | |||
316 | isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum)); | ||
317 | |||
318 | /* Set up CSI2_CTx_CTRL3 */ | ||
319 | reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum)); | ||
320 | reg &= ~(ISPCSI2_CTX_CTRL3_ALPHA_MASK); | ||
321 | reg |= (ctx->alpha << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT); | ||
322 | |||
323 | isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum)); | ||
324 | |||
325 | /* Set up CSI2_CTx_DAT_OFST */ | ||
326 | reg = isp_reg_readl(isp, csi2->regs1, | ||
327 | ISPCSI2_CTX_DAT_OFST(ctx->ctxnum)); | ||
328 | reg &= ~ISPCSI2_CTX_DAT_OFST_OFST_MASK; | ||
329 | reg |= ctx->data_offset << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT; | ||
330 | isp_reg_writel(isp, reg, csi2->regs1, | ||
331 | ISPCSI2_CTX_DAT_OFST(ctx->ctxnum)); | ||
332 | |||
333 | isp_reg_writel(isp, ctx->ping_addr, | ||
334 | csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum)); | ||
335 | |||
336 | isp_reg_writel(isp, ctx->pong_addr, | ||
337 | csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum)); | ||
338 | } | ||
339 | |||
340 | /* | ||
341 | * csi2_timing_config - CSI2 timing configuration. | ||
342 | * @timing: csi2_timing_cfg structure | ||
343 | */ | ||
344 | static void csi2_timing_config(struct isp_device *isp, | ||
345 | struct isp_csi2_device *csi2, | ||
346 | struct isp_csi2_timing_cfg *timing) | ||
347 | { | ||
348 | u32 reg; | ||
349 | |||
350 | reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_TIMING); | ||
351 | |||
352 | if (timing->force_rx_mode) | ||
353 | reg |= ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum); | ||
354 | else | ||
355 | reg &= ~ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum); | ||
356 | |||
357 | if (timing->stop_state_16x) | ||
358 | reg |= ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum); | ||
359 | else | ||
360 | reg &= ~ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum); | ||
361 | |||
362 | if (timing->stop_state_4x) | ||
363 | reg |= ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum); | ||
364 | else | ||
365 | reg &= ~ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum); | ||
366 | |||
367 | reg &= ~ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(timing->ionum); | ||
368 | reg |= timing->stop_state_counter << | ||
369 | ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(timing->ionum); | ||
370 | |||
371 | isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_TIMING); | ||
372 | } | ||
373 | |||
374 | /* | ||
375 | * csi2_irq_ctx_set - Enables CSI2 Context IRQs. | ||
376 | * @enable: Enable/disable CSI2 Context interrupts | ||
377 | */ | ||
378 | static void csi2_irq_ctx_set(struct isp_device *isp, | ||
379 | struct isp_csi2_device *csi2, int enable) | ||
380 | { | ||
381 | u32 reg = ISPCSI2_CTX_IRQSTATUS_FE_IRQ; | ||
382 | int i; | ||
383 | |||
384 | if (csi2->use_fs_irq) | ||
385 | reg |= ISPCSI2_CTX_IRQSTATUS_FS_IRQ; | ||
386 | |||
387 | for (i = 0; i < 8; i++) { | ||
388 | isp_reg_writel(isp, reg, csi2->regs1, | ||
389 | ISPCSI2_CTX_IRQSTATUS(i)); | ||
390 | if (enable) | ||
391 | isp_reg_set(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), | ||
392 | reg); | ||
393 | else | ||
394 | isp_reg_clr(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), | ||
395 | reg); | ||
396 | } | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs. | ||
401 | * @enable: Enable/disable CSI2 ComplexIO #1 interrupts | ||
402 | */ | ||
403 | static void csi2_irq_complexio1_set(struct isp_device *isp, | ||
404 | struct isp_csi2_device *csi2, int enable) | ||
405 | { | ||
406 | u32 reg; | ||
407 | reg = ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT | | ||
408 | ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER | | ||
409 | ISPCSI2_PHY_IRQENABLE_STATEULPM5 | | ||
410 | ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 | | ||
411 | ISPCSI2_PHY_IRQENABLE_ERRESC5 | | ||
412 | ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 | | ||
413 | ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 | | ||
414 | ISPCSI2_PHY_IRQENABLE_STATEULPM4 | | ||
415 | ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 | | ||
416 | ISPCSI2_PHY_IRQENABLE_ERRESC4 | | ||
417 | ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 | | ||
418 | ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 | | ||
419 | ISPCSI2_PHY_IRQENABLE_STATEULPM3 | | ||
420 | ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 | | ||
421 | ISPCSI2_PHY_IRQENABLE_ERRESC3 | | ||
422 | ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 | | ||
423 | ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 | | ||
424 | ISPCSI2_PHY_IRQENABLE_STATEULPM2 | | ||
425 | ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 | | ||
426 | ISPCSI2_PHY_IRQENABLE_ERRESC2 | | ||
427 | ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 | | ||
428 | ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 | | ||
429 | ISPCSI2_PHY_IRQENABLE_STATEULPM1 | | ||
430 | ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 | | ||
431 | ISPCSI2_PHY_IRQENABLE_ERRESC1 | | ||
432 | ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 | | ||
433 | ISPCSI2_PHY_IRQENABLE_ERRSOTHS1; | ||
434 | isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQSTATUS); | ||
435 | if (enable) | ||
436 | reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_PHY_IRQENABLE); | ||
437 | else | ||
438 | reg = 0; | ||
439 | isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQENABLE); | ||
440 | } | ||
441 | |||
442 | /* | ||
443 | * csi2_irq_status_set - Enables CSI2 Status IRQs. | ||
444 | * @enable: Enable/disable CSI2 Status interrupts | ||
445 | */ | ||
446 | static void csi2_irq_status_set(struct isp_device *isp, | ||
447 | struct isp_csi2_device *csi2, int enable) | ||
448 | { | ||
449 | u32 reg; | ||
450 | reg = ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | | ||
451 | ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | | ||
452 | ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ | | ||
453 | ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | | ||
454 | ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | | ||
455 | ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ | | ||
456 | ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ | | ||
457 | ISPCSI2_IRQSTATUS_CONTEXT(0); | ||
458 | isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQSTATUS); | ||
459 | if (enable) | ||
460 | reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQENABLE); | ||
461 | else | ||
462 | reg = 0; | ||
463 | |||
464 | isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQENABLE); | ||
465 | } | ||
466 | |||
467 | /* | ||
468 | * omap3isp_csi2_reset - Resets the CSI2 module. | ||
469 | * | ||
470 | * Must be called with the phy lock held. | ||
471 | * | ||
472 | * Returns 0 if successful, or -EBUSY if power command didn't respond. | ||
473 | */ | ||
474 | int omap3isp_csi2_reset(struct isp_csi2_device *csi2) | ||
475 | { | ||
476 | struct isp_device *isp = csi2->isp; | ||
477 | u8 soft_reset_retries = 0; | ||
478 | u32 reg; | ||
479 | int i; | ||
480 | |||
481 | if (!csi2->available) | ||
482 | return -ENODEV; | ||
483 | |||
484 | if (csi2->phy->phy_in_use) | ||
485 | return -EBUSY; | ||
486 | |||
487 | isp_reg_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, | ||
488 | ISPCSI2_SYSCONFIG_SOFT_RESET); | ||
489 | |||
490 | do { | ||
491 | reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_SYSSTATUS) & | ||
492 | ISPCSI2_SYSSTATUS_RESET_DONE; | ||
493 | if (reg == ISPCSI2_SYSSTATUS_RESET_DONE) | ||
494 | break; | ||
495 | soft_reset_retries++; | ||
496 | if (soft_reset_retries < 5) | ||
497 | udelay(100); | ||
498 | } while (soft_reset_retries < 5); | ||
499 | |||
500 | if (soft_reset_retries == 5) { | ||
501 | printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n"); | ||
502 | return -EBUSY; | ||
503 | } | ||
504 | |||
505 | if (isp->revision == ISP_REVISION_15_0) | ||
506 | isp_reg_set(isp, csi2->regs1, ISPCSI2_PHY_CFG, | ||
507 | ISPCSI2_PHY_CFG_RESET_CTRL); | ||
508 | |||
509 | i = 100; | ||
510 | do { | ||
511 | reg = isp_reg_readl(isp, csi2->phy->phy_regs, ISPCSIPHY_REG1) | ||
512 | & ISPCSIPHY_REG1_RESET_DONE_CTRLCLK; | ||
513 | if (reg == ISPCSIPHY_REG1_RESET_DONE_CTRLCLK) | ||
514 | break; | ||
515 | udelay(100); | ||
516 | } while (--i > 0); | ||
517 | |||
518 | if (i == 0) { | ||
519 | printk(KERN_ERR | ||
520 | "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n"); | ||
521 | return -EBUSY; | ||
522 | } | ||
523 | |||
524 | if (isp->autoidle) | ||
525 | isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, | ||
526 | ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK | | ||
527 | ISPCSI2_SYSCONFIG_AUTO_IDLE, | ||
528 | ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART | | ||
529 | ((isp->revision == ISP_REVISION_15_0) ? | ||
530 | ISPCSI2_SYSCONFIG_AUTO_IDLE : 0)); | ||
531 | else | ||
532 | isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, | ||
533 | ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK | | ||
534 | ISPCSI2_SYSCONFIG_AUTO_IDLE, | ||
535 | ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO); | ||
536 | |||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static int csi2_configure(struct isp_csi2_device *csi2) | ||
541 | { | ||
542 | const struct isp_v4l2_subdevs_group *pdata; | ||
543 | struct isp_device *isp = csi2->isp; | ||
544 | struct isp_csi2_timing_cfg *timing = &csi2->timing[0]; | ||
545 | struct v4l2_subdev *sensor; | ||
546 | struct media_pad *pad; | ||
547 | |||
548 | /* | ||
549 | * CSI2 fields that can be updated while the context has | ||
550 | * been enabled or the interface has been enabled are not | ||
551 | * updated dynamically currently. So we do not allow to | ||
552 | * reconfigure if either has been enabled | ||
553 | */ | ||
554 | if (csi2->contexts[0].enabled || csi2->ctrl.if_enable) | ||
555 | return -EBUSY; | ||
556 | |||
557 | pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]); | ||
558 | sensor = media_entity_to_v4l2_subdev(pad->entity); | ||
559 | pdata = sensor->host_priv; | ||
560 | |||
561 | csi2->frame_skip = 0; | ||
562 | v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip); | ||
563 | |||
564 | csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div; | ||
565 | csi2->ctrl.frame_mode = ISP_CSI2_FRAME_IMMEDIATE; | ||
566 | csi2->ctrl.ecc_enable = pdata->bus.csi2.crc; | ||
567 | |||
568 | timing->ionum = 1; | ||
569 | timing->force_rx_mode = 1; | ||
570 | timing->stop_state_16x = 1; | ||
571 | timing->stop_state_4x = 1; | ||
572 | timing->stop_state_counter = 0x1FF; | ||
573 | |||
574 | /* | ||
575 | * The CSI2 receiver can't do any format conversion except DPCM | ||
576 | * decompression, so every set_format call configures both pads | ||
577 | * and enables DPCM decompression as a special case: | ||
578 | */ | ||
579 | if (csi2->formats[CSI2_PAD_SINK].code != | ||
580 | csi2->formats[CSI2_PAD_SOURCE].code) | ||
581 | csi2->dpcm_decompress = true; | ||
582 | else | ||
583 | csi2->dpcm_decompress = false; | ||
584 | |||
585 | csi2->contexts[0].format_id = csi2_ctx_map_format(csi2); | ||
586 | |||
587 | if (csi2->video_out.bpl_padding == 0) | ||
588 | csi2->contexts[0].data_offset = 0; | ||
589 | else | ||
590 | csi2->contexts[0].data_offset = csi2->video_out.bpl_value; | ||
591 | |||
592 | /* | ||
593 | * Enable end of frame and end of line signals generation for | ||
594 | * context 0. These signals are generated from CSI2 receiver to | ||
595 | * qualify the last pixel of a frame and the last pixel of a line. | ||
596 | * Without enabling the signals CSI2 receiver writes data to memory | ||
597 | * beyond buffer size and/or data line offset is not handled correctly. | ||
598 | */ | ||
599 | csi2->contexts[0].eof_enabled = 1; | ||
600 | csi2->contexts[0].eol_enabled = 1; | ||
601 | |||
602 | csi2_irq_complexio1_set(isp, csi2, 1); | ||
603 | csi2_irq_ctx_set(isp, csi2, 1); | ||
604 | csi2_irq_status_set(isp, csi2, 1); | ||
605 | |||
606 | /* Set configuration (timings, format and links) */ | ||
607 | csi2_timing_config(isp, csi2, timing); | ||
608 | csi2_recv_config(isp, csi2, &csi2->ctrl); | ||
609 | csi2_ctx_config(isp, csi2, &csi2->contexts[0]); | ||
610 | |||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | /* | ||
615 | * csi2_print_status - Prints CSI2 debug information. | ||
616 | */ | ||
617 | #define CSI2_PRINT_REGISTER(isp, regs, name)\ | ||
618 | dev_dbg(isp->dev, "###CSI2 " #name "=0x%08x\n", \ | ||
619 | isp_reg_readl(isp, regs, ISPCSI2_##name)) | ||
620 | |||
621 | static void csi2_print_status(struct isp_csi2_device *csi2) | ||
622 | { | ||
623 | struct isp_device *isp = csi2->isp; | ||
624 | |||
625 | if (!csi2->available) | ||
626 | return; | ||
627 | |||
628 | dev_dbg(isp->dev, "-------------CSI2 Register dump-------------\n"); | ||
629 | |||
630 | CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSCONFIG); | ||
631 | CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSSTATUS); | ||
632 | CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQENABLE); | ||
633 | CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQSTATUS); | ||
634 | CSI2_PRINT_REGISTER(isp, csi2->regs1, CTRL); | ||
635 | CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_H); | ||
636 | CSI2_PRINT_REGISTER(isp, csi2->regs1, GNQ); | ||
637 | CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_CFG); | ||
638 | CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQSTATUS); | ||
639 | CSI2_PRINT_REGISTER(isp, csi2->regs1, SHORT_PACKET); | ||
640 | CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQENABLE); | ||
641 | CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_P); | ||
642 | CSI2_PRINT_REGISTER(isp, csi2->regs1, TIMING); | ||
643 | CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL1(0)); | ||
644 | CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL2(0)); | ||
645 | CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_OFST(0)); | ||
646 | CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PING_ADDR(0)); | ||
647 | CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PONG_ADDR(0)); | ||
648 | CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQENABLE(0)); | ||
649 | CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQSTATUS(0)); | ||
650 | CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL3(0)); | ||
651 | |||
652 | dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
653 | } | ||
654 | |||
655 | /* ----------------------------------------------------------------------------- | ||
656 | * Interrupt handling | ||
657 | */ | ||
658 | |||
659 | /* | ||
660 | * csi2_isr_buffer - Does buffer handling at end-of-frame | ||
661 | * when writing to memory. | ||
662 | */ | ||
663 | static void csi2_isr_buffer(struct isp_csi2_device *csi2) | ||
664 | { | ||
665 | struct isp_device *isp = csi2->isp; | ||
666 | struct isp_buffer *buffer; | ||
667 | |||
668 | csi2_ctx_enable(isp, csi2, 0, 0); | ||
669 | |||
670 | buffer = omap3isp_video_buffer_next(&csi2->video_out, 0); | ||
671 | |||
672 | /* | ||
673 | * Let video queue operation restart engine if there is an underrun | ||
674 | * condition. | ||
675 | */ | ||
676 | if (buffer == NULL) | ||
677 | return; | ||
678 | |||
679 | csi2_set_outaddr(csi2, buffer->isp_addr); | ||
680 | csi2_ctx_enable(isp, csi2, 0, 1); | ||
681 | } | ||
682 | |||
683 | static void csi2_isr_ctx(struct isp_csi2_device *csi2, | ||
684 | struct isp_csi2_ctx_cfg *ctx) | ||
685 | { | ||
686 | struct isp_device *isp = csi2->isp; | ||
687 | unsigned int n = ctx->ctxnum; | ||
688 | u32 status; | ||
689 | |||
690 | status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); | ||
691 | isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); | ||
692 | |||
693 | /* Propagate frame number */ | ||
694 | if (status & ISPCSI2_CTX_IRQSTATUS_FS_IRQ) { | ||
695 | struct isp_pipeline *pipe = | ||
696 | to_isp_pipeline(&csi2->subdev.entity); | ||
697 | if (pipe->do_propagation) | ||
698 | atomic_inc(&pipe->frame_number); | ||
699 | } | ||
700 | |||
701 | if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ)) | ||
702 | return; | ||
703 | |||
704 | /* Skip interrupts until we reach the frame skip count. The CSI2 will be | ||
705 | * automatically disabled, as the frame skip count has been programmed | ||
706 | * in the CSI2_CTx_CTRL1::COUNT field, so reenable it. | ||
707 | * | ||
708 | * It would have been nice to rely on the FRAME_NUMBER interrupt instead | ||
709 | * but it turned out that the interrupt is only generated when the CSI2 | ||
710 | * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased | ||
711 | * correctly and reaches 0 when data is forwarded to the video port only | ||
712 | * but no interrupt arrives). Maybe a CSI2 hardware bug. | ||
713 | */ | ||
714 | if (csi2->frame_skip) { | ||
715 | csi2->frame_skip--; | ||
716 | if (csi2->frame_skip == 0) { | ||
717 | ctx->format_id = csi2_ctx_map_format(csi2); | ||
718 | csi2_ctx_config(isp, csi2, ctx); | ||
719 | csi2_ctx_enable(isp, csi2, n, 1); | ||
720 | } | ||
721 | return; | ||
722 | } | ||
723 | |||
724 | if (csi2->output & CSI2_OUTPUT_MEMORY) | ||
725 | csi2_isr_buffer(csi2); | ||
726 | } | ||
727 | |||
728 | /* | ||
729 | * omap3isp_csi2_isr - CSI2 interrupt handling. | ||
730 | * | ||
731 | * Return -EIO on Transmission error | ||
732 | */ | ||
733 | int omap3isp_csi2_isr(struct isp_csi2_device *csi2) | ||
734 | { | ||
735 | u32 csi2_irqstatus, cpxio1_irqstatus; | ||
736 | struct isp_device *isp = csi2->isp; | ||
737 | int retval = 0; | ||
738 | |||
739 | if (!csi2->available) | ||
740 | return -ENODEV; | ||
741 | |||
742 | csi2_irqstatus = isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQSTATUS); | ||
743 | isp_reg_writel(isp, csi2_irqstatus, csi2->regs1, ISPCSI2_IRQSTATUS); | ||
744 | |||
745 | /* Failure Cases */ | ||
746 | if (csi2_irqstatus & ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ) { | ||
747 | cpxio1_irqstatus = isp_reg_readl(isp, csi2->regs1, | ||
748 | ISPCSI2_PHY_IRQSTATUS); | ||
749 | isp_reg_writel(isp, cpxio1_irqstatus, | ||
750 | csi2->regs1, ISPCSI2_PHY_IRQSTATUS); | ||
751 | dev_dbg(isp->dev, "CSI2: ComplexIO Error IRQ " | ||
752 | "%x\n", cpxio1_irqstatus); | ||
753 | retval = -EIO; | ||
754 | } | ||
755 | |||
756 | if (csi2_irqstatus & (ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | | ||
757 | ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | | ||
758 | ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | | ||
759 | ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | | ||
760 | ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ)) { | ||
761 | dev_dbg(isp->dev, "CSI2 Err:" | ||
762 | " OCP:%d," | ||
763 | " Short_pack:%d," | ||
764 | " ECC:%d," | ||
765 | " CPXIO2:%d," | ||
766 | " FIFO_OVF:%d," | ||
767 | "\n", | ||
768 | (csi2_irqstatus & | ||
769 | ISPCSI2_IRQSTATUS_OCP_ERR_IRQ) ? 1 : 0, | ||
770 | (csi2_irqstatus & | ||
771 | ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ) ? 1 : 0, | ||
772 | (csi2_irqstatus & | ||
773 | ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ) ? 1 : 0, | ||
774 | (csi2_irqstatus & | ||
775 | ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ) ? 1 : 0, | ||
776 | (csi2_irqstatus & | ||
777 | ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ) ? 1 : 0); | ||
778 | retval = -EIO; | ||
779 | } | ||
780 | |||
781 | if (omap3isp_module_sync_is_stopping(&csi2->wait, &csi2->stopping)) | ||
782 | return 0; | ||
783 | |||
784 | /* Successful cases */ | ||
785 | if (csi2_irqstatus & ISPCSI2_IRQSTATUS_CONTEXT(0)) | ||
786 | csi2_isr_ctx(csi2, &csi2->contexts[0]); | ||
787 | |||
788 | if (csi2_irqstatus & ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ) | ||
789 | dev_dbg(isp->dev, "CSI2: ECC correction done\n"); | ||
790 | |||
791 | return retval; | ||
792 | } | ||
793 | |||
794 | /* ----------------------------------------------------------------------------- | ||
795 | * ISP video operations | ||
796 | */ | ||
797 | |||
798 | /* | ||
799 | * csi2_queue - Queues the first buffer when using memory output | ||
800 | * @video: The video node | ||
801 | * @buffer: buffer to queue | ||
802 | */ | ||
803 | static int csi2_queue(struct isp_video *video, struct isp_buffer *buffer) | ||
804 | { | ||
805 | struct isp_device *isp = video->isp; | ||
806 | struct isp_csi2_device *csi2 = &isp->isp_csi2a; | ||
807 | |||
808 | csi2_set_outaddr(csi2, buffer->isp_addr); | ||
809 | |||
810 | /* | ||
811 | * If streaming was enabled before there was a buffer queued | ||
812 | * or underrun happened in the ISR, the hardware was not enabled | ||
813 | * and DMA queue flag ISP_VIDEO_DMAQUEUE_UNDERRUN is still set. | ||
814 | * Enable it now. | ||
815 | */ | ||
816 | if (csi2->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) { | ||
817 | /* Enable / disable context 0 and IRQs */ | ||
818 | csi2_if_enable(isp, csi2, 1); | ||
819 | csi2_ctx_enable(isp, csi2, 0, 1); | ||
820 | isp_video_dmaqueue_flags_clr(&csi2->video_out); | ||
821 | } | ||
822 | |||
823 | return 0; | ||
824 | } | ||
825 | |||
826 | static const struct isp_video_operations csi2_ispvideo_ops = { | ||
827 | .queue = csi2_queue, | ||
828 | }; | ||
829 | |||
830 | /* ----------------------------------------------------------------------------- | ||
831 | * V4L2 subdev operations | ||
832 | */ | ||
833 | |||
834 | static struct v4l2_mbus_framefmt * | ||
835 | __csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, | ||
836 | unsigned int pad, enum v4l2_subdev_format_whence which) | ||
837 | { | ||
838 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
839 | return v4l2_subdev_get_try_format(fh, pad); | ||
840 | else | ||
841 | return &csi2->formats[pad]; | ||
842 | } | ||
843 | |||
844 | static void | ||
845 | csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, | ||
846 | unsigned int pad, struct v4l2_mbus_framefmt *fmt, | ||
847 | enum v4l2_subdev_format_whence which) | ||
848 | { | ||
849 | enum v4l2_mbus_pixelcode pixelcode; | ||
850 | struct v4l2_mbus_framefmt *format; | ||
851 | const struct isp_format_info *info; | ||
852 | unsigned int i; | ||
853 | |||
854 | switch (pad) { | ||
855 | case CSI2_PAD_SINK: | ||
856 | /* Clamp the width and height to valid range (1-8191). */ | ||
857 | for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) { | ||
858 | if (fmt->code == csi2_input_fmts[i]) | ||
859 | break; | ||
860 | } | ||
861 | |||
862 | /* If not found, use SGRBG10 as default */ | ||
863 | if (i >= ARRAY_SIZE(csi2_input_fmts)) | ||
864 | fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
865 | |||
866 | fmt->width = clamp_t(u32, fmt->width, 1, 8191); | ||
867 | fmt->height = clamp_t(u32, fmt->height, 1, 8191); | ||
868 | break; | ||
869 | |||
870 | case CSI2_PAD_SOURCE: | ||
871 | /* Source format same as sink format, except for DPCM | ||
872 | * compression. | ||
873 | */ | ||
874 | pixelcode = fmt->code; | ||
875 | format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which); | ||
876 | memcpy(fmt, format, sizeof(*fmt)); | ||
877 | |||
878 | /* | ||
879 | * Only Allow DPCM decompression, and check that the | ||
880 | * pattern is preserved | ||
881 | */ | ||
882 | info = omap3isp_video_format_info(fmt->code); | ||
883 | if (info->uncompressed == pixelcode) | ||
884 | fmt->code = pixelcode; | ||
885 | break; | ||
886 | } | ||
887 | |||
888 | /* RGB, non-interlaced */ | ||
889 | fmt->colorspace = V4L2_COLORSPACE_SRGB; | ||
890 | fmt->field = V4L2_FIELD_NONE; | ||
891 | } | ||
892 | |||
893 | /* | ||
894 | * csi2_enum_mbus_code - Handle pixel format enumeration | ||
895 | * @sd : pointer to v4l2 subdev structure | ||
896 | * @fh : V4L2 subdev file handle | ||
897 | * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
898 | * return -EINVAL or zero on success | ||
899 | */ | ||
900 | static int csi2_enum_mbus_code(struct v4l2_subdev *sd, | ||
901 | struct v4l2_subdev_fh *fh, | ||
902 | struct v4l2_subdev_mbus_code_enum *code) | ||
903 | { | ||
904 | struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); | ||
905 | struct v4l2_mbus_framefmt *format; | ||
906 | const struct isp_format_info *info; | ||
907 | |||
908 | if (code->pad == CSI2_PAD_SINK) { | ||
909 | if (code->index >= ARRAY_SIZE(csi2_input_fmts)) | ||
910 | return -EINVAL; | ||
911 | |||
912 | code->code = csi2_input_fmts[code->index]; | ||
913 | } else { | ||
914 | format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, | ||
915 | V4L2_SUBDEV_FORMAT_TRY); | ||
916 | switch (code->index) { | ||
917 | case 0: | ||
918 | /* Passthrough sink pad code */ | ||
919 | code->code = format->code; | ||
920 | break; | ||
921 | case 1: | ||
922 | /* Uncompressed code */ | ||
923 | info = omap3isp_video_format_info(format->code); | ||
924 | if (info->uncompressed == format->code) | ||
925 | return -EINVAL; | ||
926 | |||
927 | code->code = info->uncompressed; | ||
928 | break; | ||
929 | default: | ||
930 | return -EINVAL; | ||
931 | } | ||
932 | } | ||
933 | |||
934 | return 0; | ||
935 | } | ||
936 | |||
937 | static int csi2_enum_frame_size(struct v4l2_subdev *sd, | ||
938 | struct v4l2_subdev_fh *fh, | ||
939 | struct v4l2_subdev_frame_size_enum *fse) | ||
940 | { | ||
941 | struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); | ||
942 | struct v4l2_mbus_framefmt format; | ||
943 | |||
944 | if (fse->index != 0) | ||
945 | return -EINVAL; | ||
946 | |||
947 | format.code = fse->code; | ||
948 | format.width = 1; | ||
949 | format.height = 1; | ||
950 | csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
951 | fse->min_width = format.width; | ||
952 | fse->min_height = format.height; | ||
953 | |||
954 | if (format.code != fse->code) | ||
955 | return -EINVAL; | ||
956 | |||
957 | format.code = fse->code; | ||
958 | format.width = -1; | ||
959 | format.height = -1; | ||
960 | csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
961 | fse->max_width = format.width; | ||
962 | fse->max_height = format.height; | ||
963 | |||
964 | return 0; | ||
965 | } | ||
966 | |||
967 | /* | ||
968 | * csi2_get_format - Handle get format by pads subdev method | ||
969 | * @sd : pointer to v4l2 subdev structure | ||
970 | * @fh : V4L2 subdev file handle | ||
971 | * @fmt: pointer to v4l2 subdev format structure | ||
972 | * return -EINVAL or zero on success | ||
973 | */ | ||
974 | static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
975 | struct v4l2_subdev_format *fmt) | ||
976 | { | ||
977 | struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); | ||
978 | struct v4l2_mbus_framefmt *format; | ||
979 | |||
980 | format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); | ||
981 | if (format == NULL) | ||
982 | return -EINVAL; | ||
983 | |||
984 | fmt->format = *format; | ||
985 | return 0; | ||
986 | } | ||
987 | |||
988 | /* | ||
989 | * csi2_set_format - Handle set format by pads subdev method | ||
990 | * @sd : pointer to v4l2 subdev structure | ||
991 | * @fh : V4L2 subdev file handle | ||
992 | * @fmt: pointer to v4l2 subdev format structure | ||
993 | * return -EINVAL or zero on success | ||
994 | */ | ||
995 | static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
996 | struct v4l2_subdev_format *fmt) | ||
997 | { | ||
998 | struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); | ||
999 | struct v4l2_mbus_framefmt *format; | ||
1000 | |||
1001 | format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); | ||
1002 | if (format == NULL) | ||
1003 | return -EINVAL; | ||
1004 | |||
1005 | csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which); | ||
1006 | *format = fmt->format; | ||
1007 | |||
1008 | /* Propagate the format from sink to source */ | ||
1009 | if (fmt->pad == CSI2_PAD_SINK) { | ||
1010 | format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE, | ||
1011 | fmt->which); | ||
1012 | *format = fmt->format; | ||
1013 | csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which); | ||
1014 | } | ||
1015 | |||
1016 | return 0; | ||
1017 | } | ||
1018 | |||
1019 | /* | ||
1020 | * csi2_init_formats - Initialize formats on all pads | ||
1021 | * @sd: ISP CSI2 V4L2 subdevice | ||
1022 | * @fh: V4L2 subdev file handle | ||
1023 | * | ||
1024 | * Initialize all pad formats with default values. If fh is not NULL, try | ||
1025 | * formats are initialized on the file handle. Otherwise active formats are | ||
1026 | * initialized on the device. | ||
1027 | */ | ||
1028 | static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
1029 | { | ||
1030 | struct v4l2_subdev_format format; | ||
1031 | |||
1032 | memset(&format, 0, sizeof(format)); | ||
1033 | format.pad = CSI2_PAD_SINK; | ||
1034 | format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
1035 | format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
1036 | format.format.width = 4096; | ||
1037 | format.format.height = 4096; | ||
1038 | csi2_set_format(sd, fh, &format); | ||
1039 | |||
1040 | return 0; | ||
1041 | } | ||
1042 | |||
1043 | /* | ||
1044 | * csi2_set_stream - Enable/Disable streaming on the CSI2 module | ||
1045 | * @sd: ISP CSI2 V4L2 subdevice | ||
1046 | * @enable: ISP pipeline stream state | ||
1047 | * | ||
1048 | * Return 0 on success or a negative error code otherwise. | ||
1049 | */ | ||
1050 | static int csi2_set_stream(struct v4l2_subdev *sd, int enable) | ||
1051 | { | ||
1052 | struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); | ||
1053 | struct isp_device *isp = csi2->isp; | ||
1054 | struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); | ||
1055 | struct isp_video *video_out = &csi2->video_out; | ||
1056 | |||
1057 | switch (enable) { | ||
1058 | case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
1059 | if (omap3isp_csiphy_acquire(csi2->phy) < 0) | ||
1060 | return -ENODEV; | ||
1061 | csi2->use_fs_irq = pipe->do_propagation; | ||
1062 | if (csi2->output & CSI2_OUTPUT_MEMORY) | ||
1063 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); | ||
1064 | csi2_configure(csi2); | ||
1065 | csi2_print_status(csi2); | ||
1066 | |||
1067 | /* | ||
1068 | * When outputting to memory with no buffer available, let the | ||
1069 | * buffer queue handler start the hardware. A DMA queue flag | ||
1070 | * ISP_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is | ||
1071 | * a buffer available. | ||
1072 | */ | ||
1073 | if (csi2->output & CSI2_OUTPUT_MEMORY && | ||
1074 | !(video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED)) | ||
1075 | break; | ||
1076 | /* Enable context 0 and IRQs */ | ||
1077 | atomic_set(&csi2->stopping, 0); | ||
1078 | csi2_ctx_enable(isp, csi2, 0, 1); | ||
1079 | csi2_if_enable(isp, csi2, 1); | ||
1080 | isp_video_dmaqueue_flags_clr(video_out); | ||
1081 | break; | ||
1082 | |||
1083 | case ISP_PIPELINE_STREAM_STOPPED: | ||
1084 | if (csi2->state == ISP_PIPELINE_STREAM_STOPPED) | ||
1085 | return 0; | ||
1086 | if (omap3isp_module_sync_idle(&sd->entity, &csi2->wait, | ||
1087 | &csi2->stopping)) | ||
1088 | dev_dbg(isp->dev, "%s: module stop timeout.\n", | ||
1089 | sd->name); | ||
1090 | csi2_ctx_enable(isp, csi2, 0, 0); | ||
1091 | csi2_if_enable(isp, csi2, 0); | ||
1092 | csi2_irq_ctx_set(isp, csi2, 0); | ||
1093 | omap3isp_csiphy_release(csi2->phy); | ||
1094 | isp_video_dmaqueue_flags_clr(video_out); | ||
1095 | omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); | ||
1096 | break; | ||
1097 | } | ||
1098 | |||
1099 | csi2->state = enable; | ||
1100 | return 0; | ||
1101 | } | ||
1102 | |||
1103 | /* subdev video operations */ | ||
1104 | static const struct v4l2_subdev_video_ops csi2_video_ops = { | ||
1105 | .s_stream = csi2_set_stream, | ||
1106 | }; | ||
1107 | |||
1108 | /* subdev pad operations */ | ||
1109 | static const struct v4l2_subdev_pad_ops csi2_pad_ops = { | ||
1110 | .enum_mbus_code = csi2_enum_mbus_code, | ||
1111 | .enum_frame_size = csi2_enum_frame_size, | ||
1112 | .get_fmt = csi2_get_format, | ||
1113 | .set_fmt = csi2_set_format, | ||
1114 | }; | ||
1115 | |||
1116 | /* subdev operations */ | ||
1117 | static const struct v4l2_subdev_ops csi2_ops = { | ||
1118 | .video = &csi2_video_ops, | ||
1119 | .pad = &csi2_pad_ops, | ||
1120 | }; | ||
1121 | |||
1122 | /* subdev internal operations */ | ||
1123 | static const struct v4l2_subdev_internal_ops csi2_internal_ops = { | ||
1124 | .open = csi2_init_formats, | ||
1125 | }; | ||
1126 | |||
1127 | /* ----------------------------------------------------------------------------- | ||
1128 | * Media entity operations | ||
1129 | */ | ||
1130 | |||
1131 | /* | ||
1132 | * csi2_link_setup - Setup CSI2 connections. | ||
1133 | * @entity : Pointer to media entity structure | ||
1134 | * @local : Pointer to local pad array | ||
1135 | * @remote : Pointer to remote pad array | ||
1136 | * @flags : Link flags | ||
1137 | * return -EINVAL or zero on success | ||
1138 | */ | ||
1139 | static int csi2_link_setup(struct media_entity *entity, | ||
1140 | const struct media_pad *local, | ||
1141 | const struct media_pad *remote, u32 flags) | ||
1142 | { | ||
1143 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
1144 | struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); | ||
1145 | struct isp_csi2_ctrl_cfg *ctrl = &csi2->ctrl; | ||
1146 | |||
1147 | /* | ||
1148 | * The ISP core doesn't support pipelines with multiple video outputs. | ||
1149 | * Revisit this when it will be implemented, and return -EBUSY for now. | ||
1150 | */ | ||
1151 | |||
1152 | switch (local->index | media_entity_type(remote->entity)) { | ||
1153 | case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: | ||
1154 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1155 | if (csi2->output & ~CSI2_OUTPUT_MEMORY) | ||
1156 | return -EBUSY; | ||
1157 | csi2->output |= CSI2_OUTPUT_MEMORY; | ||
1158 | } else { | ||
1159 | csi2->output &= ~CSI2_OUTPUT_MEMORY; | ||
1160 | } | ||
1161 | break; | ||
1162 | |||
1163 | case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1164 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1165 | if (csi2->output & ~CSI2_OUTPUT_CCDC) | ||
1166 | return -EBUSY; | ||
1167 | csi2->output |= CSI2_OUTPUT_CCDC; | ||
1168 | } else { | ||
1169 | csi2->output &= ~CSI2_OUTPUT_CCDC; | ||
1170 | } | ||
1171 | break; | ||
1172 | |||
1173 | default: | ||
1174 | /* Link from camera to CSI2 is fixed... */ | ||
1175 | return -EINVAL; | ||
1176 | } | ||
1177 | |||
1178 | ctrl->vp_only_enable = | ||
1179 | (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true; | ||
1180 | ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_CCDC); | ||
1181 | |||
1182 | return 0; | ||
1183 | } | ||
1184 | |||
1185 | /* media operations */ | ||
1186 | static const struct media_entity_operations csi2_media_ops = { | ||
1187 | .link_setup = csi2_link_setup, | ||
1188 | }; | ||
1189 | |||
1190 | /* | ||
1191 | * csi2_init_entities - Initialize subdev and media entity. | ||
1192 | * @csi2: Pointer to csi2 structure. | ||
1193 | * return -ENOMEM or zero on success | ||
1194 | */ | ||
1195 | static int csi2_init_entities(struct isp_csi2_device *csi2) | ||
1196 | { | ||
1197 | struct v4l2_subdev *sd = &csi2->subdev; | ||
1198 | struct media_pad *pads = csi2->pads; | ||
1199 | struct media_entity *me = &sd->entity; | ||
1200 | int ret; | ||
1201 | |||
1202 | v4l2_subdev_init(sd, &csi2_ops); | ||
1203 | sd->internal_ops = &csi2_internal_ops; | ||
1204 | strlcpy(sd->name, "OMAP3 ISP CSI2a", sizeof(sd->name)); | ||
1205 | |||
1206 | sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
1207 | v4l2_set_subdevdata(sd, csi2); | ||
1208 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1209 | |||
1210 | pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | ||
1211 | pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
1212 | |||
1213 | me->ops = &csi2_media_ops; | ||
1214 | ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0); | ||
1215 | if (ret < 0) | ||
1216 | return ret; | ||
1217 | |||
1218 | csi2_init_formats(sd, NULL); | ||
1219 | |||
1220 | /* Video device node */ | ||
1221 | csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1222 | csi2->video_out.ops = &csi2_ispvideo_ops; | ||
1223 | csi2->video_out.bpl_alignment = 32; | ||
1224 | csi2->video_out.bpl_zero_padding = 1; | ||
1225 | csi2->video_out.bpl_max = 0x1ffe0; | ||
1226 | csi2->video_out.isp = csi2->isp; | ||
1227 | csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; | ||
1228 | |||
1229 | ret = omap3isp_video_init(&csi2->video_out, "CSI2a"); | ||
1230 | if (ret < 0) | ||
1231 | return ret; | ||
1232 | |||
1233 | /* Connect the CSI2 subdev to the video node. */ | ||
1234 | ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE, | ||
1235 | &csi2->video_out.video.entity, 0, 0); | ||
1236 | if (ret < 0) | ||
1237 | return ret; | ||
1238 | |||
1239 | return 0; | ||
1240 | } | ||
1241 | |||
1242 | void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2) | ||
1243 | { | ||
1244 | media_entity_cleanup(&csi2->subdev.entity); | ||
1245 | |||
1246 | v4l2_device_unregister_subdev(&csi2->subdev); | ||
1247 | omap3isp_video_unregister(&csi2->video_out); | ||
1248 | } | ||
1249 | |||
1250 | int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2, | ||
1251 | struct v4l2_device *vdev) | ||
1252 | { | ||
1253 | int ret; | ||
1254 | |||
1255 | /* Register the subdev and video nodes. */ | ||
1256 | ret = v4l2_device_register_subdev(vdev, &csi2->subdev); | ||
1257 | if (ret < 0) | ||
1258 | goto error; | ||
1259 | |||
1260 | ret = omap3isp_video_register(&csi2->video_out, vdev); | ||
1261 | if (ret < 0) | ||
1262 | goto error; | ||
1263 | |||
1264 | return 0; | ||
1265 | |||
1266 | error: | ||
1267 | omap3isp_csi2_unregister_entities(csi2); | ||
1268 | return ret; | ||
1269 | } | ||
1270 | |||
1271 | /* ----------------------------------------------------------------------------- | ||
1272 | * ISP CSI2 initialisation and cleanup | ||
1273 | */ | ||
1274 | |||
1275 | /* | ||
1276 | * omap3isp_csi2_cleanup - Routine for module driver cleanup | ||
1277 | */ | ||
1278 | void omap3isp_csi2_cleanup(struct isp_device *isp) | ||
1279 | { | ||
1280 | } | ||
1281 | |||
1282 | /* | ||
1283 | * omap3isp_csi2_init - Routine for module driver init | ||
1284 | */ | ||
1285 | int omap3isp_csi2_init(struct isp_device *isp) | ||
1286 | { | ||
1287 | struct isp_csi2_device *csi2a = &isp->isp_csi2a; | ||
1288 | struct isp_csi2_device *csi2c = &isp->isp_csi2c; | ||
1289 | int ret; | ||
1290 | |||
1291 | csi2a->isp = isp; | ||
1292 | csi2a->available = 1; | ||
1293 | csi2a->regs1 = OMAP3_ISP_IOMEM_CSI2A_REGS1; | ||
1294 | csi2a->regs2 = OMAP3_ISP_IOMEM_CSI2A_REGS2; | ||
1295 | csi2a->phy = &isp->isp_csiphy2; | ||
1296 | csi2a->state = ISP_PIPELINE_STREAM_STOPPED; | ||
1297 | init_waitqueue_head(&csi2a->wait); | ||
1298 | |||
1299 | ret = csi2_init_entities(csi2a); | ||
1300 | if (ret < 0) | ||
1301 | goto fail; | ||
1302 | |||
1303 | if (isp->revision == ISP_REVISION_15_0) { | ||
1304 | csi2c->isp = isp; | ||
1305 | csi2c->available = 1; | ||
1306 | csi2c->regs1 = OMAP3_ISP_IOMEM_CSI2C_REGS1; | ||
1307 | csi2c->regs2 = OMAP3_ISP_IOMEM_CSI2C_REGS2; | ||
1308 | csi2c->phy = &isp->isp_csiphy1; | ||
1309 | csi2c->state = ISP_PIPELINE_STREAM_STOPPED; | ||
1310 | init_waitqueue_head(&csi2c->wait); | ||
1311 | } | ||
1312 | |||
1313 | return 0; | ||
1314 | fail: | ||
1315 | omap3isp_csi2_cleanup(isp); | ||
1316 | return ret; | ||
1317 | } | ||
diff --git a/drivers/media/video/omap3isp/ispcsi2.h b/drivers/media/video/omap3isp/ispcsi2.h new file mode 100644 index 00000000000..456fb7fb8a0 --- /dev/null +++ b/drivers/media/video/omap3isp/ispcsi2.h | |||
@@ -0,0 +1,166 @@ | |||
1 | /* | ||
2 | * ispcsi2.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - CSI2 module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #ifndef OMAP3_ISP_CSI2_H | ||
28 | #define OMAP3_ISP_CSI2_H | ||
29 | |||
30 | #include <linux/types.h> | ||
31 | #include <linux/videodev2.h> | ||
32 | |||
33 | struct isp_csiphy; | ||
34 | |||
35 | /* This is not an exhaustive list */ | ||
36 | enum isp_csi2_pix_formats { | ||
37 | CSI2_PIX_FMT_OTHERS = 0, | ||
38 | CSI2_PIX_FMT_YUV422_8BIT = 0x1e, | ||
39 | CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e, | ||
40 | CSI2_PIX_FMT_RAW10_EXP16 = 0xab, | ||
41 | CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f, | ||
42 | CSI2_PIX_FMT_RAW8 = 0x2a, | ||
43 | CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa, | ||
44 | CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a, | ||
45 | CSI2_PIX_FMT_RAW8_VP = 0x12a, | ||
46 | CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340, | ||
47 | CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0, | ||
48 | CSI2_USERDEF_8BIT_DATA1 = 0x40, | ||
49 | }; | ||
50 | |||
51 | enum isp_csi2_irqevents { | ||
52 | OCP_ERR_IRQ = 0x4000, | ||
53 | SHORT_PACKET_IRQ = 0x2000, | ||
54 | ECC_CORRECTION_IRQ = 0x1000, | ||
55 | ECC_NO_CORRECTION_IRQ = 0x800, | ||
56 | COMPLEXIO2_ERR_IRQ = 0x400, | ||
57 | COMPLEXIO1_ERR_IRQ = 0x200, | ||
58 | FIFO_OVF_IRQ = 0x100, | ||
59 | CONTEXT7 = 0x80, | ||
60 | CONTEXT6 = 0x40, | ||
61 | CONTEXT5 = 0x20, | ||
62 | CONTEXT4 = 0x10, | ||
63 | CONTEXT3 = 0x8, | ||
64 | CONTEXT2 = 0x4, | ||
65 | CONTEXT1 = 0x2, | ||
66 | CONTEXT0 = 0x1, | ||
67 | }; | ||
68 | |||
69 | enum isp_csi2_ctx_irqevents { | ||
70 | CTX_ECC_CORRECTION = 0x100, | ||
71 | CTX_LINE_NUMBER = 0x80, | ||
72 | CTX_FRAME_NUMBER = 0x40, | ||
73 | CTX_CS = 0x20, | ||
74 | CTX_LE = 0x8, | ||
75 | CTX_LS = 0x4, | ||
76 | CTX_FE = 0x2, | ||
77 | CTX_FS = 0x1, | ||
78 | }; | ||
79 | |||
80 | enum isp_csi2_frame_mode { | ||
81 | ISP_CSI2_FRAME_IMMEDIATE, | ||
82 | ISP_CSI2_FRAME_AFTERFEC, | ||
83 | }; | ||
84 | |||
85 | #define ISP_CSI2_MAX_CTX_NUM 7 | ||
86 | |||
87 | struct isp_csi2_ctx_cfg { | ||
88 | u8 ctxnum; /* context number 0 - 7 */ | ||
89 | u8 dpcm_decompress; | ||
90 | |||
91 | /* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */ | ||
92 | u8 virtual_id; | ||
93 | u16 format_id; /* as in CSI2_CTx_CTRL2[9:0] */ | ||
94 | u8 dpcm_predictor; /* 1: simple, 0: advanced */ | ||
95 | |||
96 | /* Fields in CSI2_CTx_CTRL1/3 - Shadowed */ | ||
97 | u16 alpha; | ||
98 | u16 data_offset; | ||
99 | u32 ping_addr; | ||
100 | u32 pong_addr; | ||
101 | u8 eof_enabled; | ||
102 | u8 eol_enabled; | ||
103 | u8 checksum_enabled; | ||
104 | u8 enabled; | ||
105 | }; | ||
106 | |||
107 | struct isp_csi2_timing_cfg { | ||
108 | u8 ionum; /* IO1 or IO2 as in CSI2_TIMING */ | ||
109 | unsigned force_rx_mode:1; | ||
110 | unsigned stop_state_16x:1; | ||
111 | unsigned stop_state_4x:1; | ||
112 | u16 stop_state_counter; | ||
113 | }; | ||
114 | |||
115 | struct isp_csi2_ctrl_cfg { | ||
116 | bool vp_clk_enable; | ||
117 | bool vp_only_enable; | ||
118 | u8 vp_out_ctrl; | ||
119 | enum isp_csi2_frame_mode frame_mode; | ||
120 | bool ecc_enable; | ||
121 | bool if_enable; | ||
122 | }; | ||
123 | |||
124 | #define CSI2_PAD_SINK 0 | ||
125 | #define CSI2_PAD_SOURCE 1 | ||
126 | #define CSI2_PADS_NUM 2 | ||
127 | |||
128 | #define CSI2_OUTPUT_CCDC (1 << 0) | ||
129 | #define CSI2_OUTPUT_MEMORY (1 << 1) | ||
130 | |||
131 | struct isp_csi2_device { | ||
132 | struct v4l2_subdev subdev; | ||
133 | struct media_pad pads[CSI2_PADS_NUM]; | ||
134 | struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM]; | ||
135 | |||
136 | struct isp_video video_out; | ||
137 | struct isp_device *isp; | ||
138 | |||
139 | u8 available; /* Is the IP present on the silicon? */ | ||
140 | |||
141 | /* mem resources - enums as defined in enum isp_mem_resources */ | ||
142 | u8 regs1; | ||
143 | u8 regs2; | ||
144 | |||
145 | u32 output; /* output to CCDC, memory or both? */ | ||
146 | bool dpcm_decompress; | ||
147 | unsigned int frame_skip; | ||
148 | bool use_fs_irq; | ||
149 | |||
150 | struct isp_csiphy *phy; | ||
151 | struct isp_csi2_ctx_cfg contexts[ISP_CSI2_MAX_CTX_NUM + 1]; | ||
152 | struct isp_csi2_timing_cfg timing[2]; | ||
153 | struct isp_csi2_ctrl_cfg ctrl; | ||
154 | enum isp_pipeline_stream_state state; | ||
155 | wait_queue_head_t wait; | ||
156 | atomic_t stopping; | ||
157 | }; | ||
158 | |||
159 | int omap3isp_csi2_isr(struct isp_csi2_device *csi2); | ||
160 | int omap3isp_csi2_reset(struct isp_csi2_device *csi2); | ||
161 | int omap3isp_csi2_init(struct isp_device *isp); | ||
162 | void omap3isp_csi2_cleanup(struct isp_device *isp); | ||
163 | void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2); | ||
164 | int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2, | ||
165 | struct v4l2_device *vdev); | ||
166 | #endif /* OMAP3_ISP_CSI2_H */ | ||
diff --git a/drivers/media/video/omap3isp/ispcsiphy.c b/drivers/media/video/omap3isp/ispcsiphy.c new file mode 100644 index 00000000000..5be37ce7d0c --- /dev/null +++ b/drivers/media/video/omap3isp/ispcsiphy.c | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * ispcsiphy.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - CSI PHY module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #include <linux/delay.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <linux/regulator/consumer.h> | ||
30 | |||
31 | #include "isp.h" | ||
32 | #include "ispreg.h" | ||
33 | #include "ispcsiphy.h" | ||
34 | |||
35 | /* | ||
36 | * csiphy_lanes_config - Configuration of CSIPHY lanes. | ||
37 | * | ||
38 | * Updates HW configuration. | ||
39 | * Called with phy->mutex taken. | ||
40 | */ | ||
41 | static void csiphy_lanes_config(struct isp_csiphy *phy) | ||
42 | { | ||
43 | unsigned int i; | ||
44 | u32 reg; | ||
45 | |||
46 | reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG); | ||
47 | |||
48 | for (i = 0; i < phy->num_data_lanes; i++) { | ||
49 | reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) | | ||
50 | ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1)); | ||
51 | reg |= (phy->lanes.data[i].pol << | ||
52 | ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1)); | ||
53 | reg |= (phy->lanes.data[i].pos << | ||
54 | ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1)); | ||
55 | } | ||
56 | |||
57 | reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK | | ||
58 | ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK); | ||
59 | reg |= phy->lanes.clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT; | ||
60 | reg |= phy->lanes.clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT; | ||
61 | |||
62 | isp_reg_writel(phy->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG); | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * csiphy_power_autoswitch_enable | ||
67 | * @enable: Sets or clears the autoswitch function enable flag. | ||
68 | */ | ||
69 | static void csiphy_power_autoswitch_enable(struct isp_csiphy *phy, bool enable) | ||
70 | { | ||
71 | isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, | ||
72 | ISPCSI2_PHY_CFG_PWR_AUTO, | ||
73 | enable ? ISPCSI2_PHY_CFG_PWR_AUTO : 0); | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * csiphy_set_power | ||
78 | * @power: Power state to be set. | ||
79 | * | ||
80 | * Returns 0 if successful, or -EBUSY if the retry count is exceeded. | ||
81 | */ | ||
82 | static int csiphy_set_power(struct isp_csiphy *phy, u32 power) | ||
83 | { | ||
84 | u32 reg; | ||
85 | u8 retry_count; | ||
86 | |||
87 | isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, | ||
88 | ISPCSI2_PHY_CFG_PWR_CMD_MASK, power); | ||
89 | |||
90 | retry_count = 0; | ||
91 | do { | ||
92 | udelay(50); | ||
93 | reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG) & | ||
94 | ISPCSI2_PHY_CFG_PWR_STATUS_MASK; | ||
95 | |||
96 | if (reg != power >> 2) | ||
97 | retry_count++; | ||
98 | |||
99 | } while ((reg != power >> 2) && (retry_count < 100)); | ||
100 | |||
101 | if (retry_count == 100) { | ||
102 | printk(KERN_ERR "CSI2 CIO set power failed!\n"); | ||
103 | return -EBUSY; | ||
104 | } | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * csiphy_dphy_config - Configure CSI2 D-PHY parameters. | ||
111 | * | ||
112 | * Called with phy->mutex taken. | ||
113 | */ | ||
114 | static void csiphy_dphy_config(struct isp_csiphy *phy) | ||
115 | { | ||
116 | u32 reg; | ||
117 | |||
118 | /* Set up ISPCSIPHY_REG0 */ | ||
119 | reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG0); | ||
120 | |||
121 | reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK | | ||
122 | ISPCSIPHY_REG0_THS_SETTLE_MASK); | ||
123 | reg |= phy->dphy.ths_term << ISPCSIPHY_REG0_THS_TERM_SHIFT; | ||
124 | reg |= phy->dphy.ths_settle << ISPCSIPHY_REG0_THS_SETTLE_SHIFT; | ||
125 | |||
126 | isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG0); | ||
127 | |||
128 | /* Set up ISPCSIPHY_REG1 */ | ||
129 | reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG1); | ||
130 | |||
131 | reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK | | ||
132 | ISPCSIPHY_REG1_TCLK_MISS_MASK | | ||
133 | ISPCSIPHY_REG1_TCLK_SETTLE_MASK); | ||
134 | reg |= phy->dphy.tclk_term << ISPCSIPHY_REG1_TCLK_TERM_SHIFT; | ||
135 | reg |= phy->dphy.tclk_miss << ISPCSIPHY_REG1_TCLK_MISS_SHIFT; | ||
136 | reg |= phy->dphy.tclk_settle << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT; | ||
137 | |||
138 | isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG1); | ||
139 | } | ||
140 | |||
141 | static int csiphy_config(struct isp_csiphy *phy, | ||
142 | struct isp_csiphy_dphy_cfg *dphy, | ||
143 | struct isp_csiphy_lanes_cfg *lanes) | ||
144 | { | ||
145 | unsigned int used_lanes = 0; | ||
146 | unsigned int i; | ||
147 | |||
148 | /* Clock and data lanes verification */ | ||
149 | for (i = 0; i < phy->num_data_lanes; i++) { | ||
150 | if (lanes->data[i].pol > 1 || lanes->data[i].pos > 3) | ||
151 | return -EINVAL; | ||
152 | |||
153 | if (used_lanes & (1 << lanes->data[i].pos)) | ||
154 | return -EINVAL; | ||
155 | |||
156 | used_lanes |= 1 << lanes->data[i].pos; | ||
157 | } | ||
158 | |||
159 | if (lanes->clk.pol > 1 || lanes->clk.pos > 3) | ||
160 | return -EINVAL; | ||
161 | |||
162 | if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos)) | ||
163 | return -EINVAL; | ||
164 | |||
165 | mutex_lock(&phy->mutex); | ||
166 | phy->dphy = *dphy; | ||
167 | phy->lanes = *lanes; | ||
168 | mutex_unlock(&phy->mutex); | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | int omap3isp_csiphy_acquire(struct isp_csiphy *phy) | ||
174 | { | ||
175 | int rval; | ||
176 | |||
177 | if (phy->vdd == NULL) { | ||
178 | dev_err(phy->isp->dev, "Power regulator for CSI PHY not " | ||
179 | "available\n"); | ||
180 | return -ENODEV; | ||
181 | } | ||
182 | |||
183 | mutex_lock(&phy->mutex); | ||
184 | |||
185 | rval = regulator_enable(phy->vdd); | ||
186 | if (rval < 0) | ||
187 | goto done; | ||
188 | |||
189 | omap3isp_csi2_reset(phy->csi2); | ||
190 | |||
191 | csiphy_dphy_config(phy); | ||
192 | csiphy_lanes_config(phy); | ||
193 | |||
194 | rval = csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_ON); | ||
195 | if (rval) { | ||
196 | regulator_disable(phy->vdd); | ||
197 | goto done; | ||
198 | } | ||
199 | |||
200 | csiphy_power_autoswitch_enable(phy, true); | ||
201 | phy->phy_in_use = 1; | ||
202 | |||
203 | done: | ||
204 | mutex_unlock(&phy->mutex); | ||
205 | return rval; | ||
206 | } | ||
207 | |||
208 | void omap3isp_csiphy_release(struct isp_csiphy *phy) | ||
209 | { | ||
210 | mutex_lock(&phy->mutex); | ||
211 | if (phy->phy_in_use) { | ||
212 | csiphy_power_autoswitch_enable(phy, false); | ||
213 | csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF); | ||
214 | regulator_disable(phy->vdd); | ||
215 | phy->phy_in_use = 0; | ||
216 | } | ||
217 | mutex_unlock(&phy->mutex); | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * omap3isp_csiphy_init - Initialize the CSI PHY frontends | ||
222 | */ | ||
223 | int omap3isp_csiphy_init(struct isp_device *isp) | ||
224 | { | ||
225 | struct isp_csiphy *phy1 = &isp->isp_csiphy1; | ||
226 | struct isp_csiphy *phy2 = &isp->isp_csiphy2; | ||
227 | |||
228 | isp->platform_cb.csiphy_config = csiphy_config; | ||
229 | |||
230 | phy2->isp = isp; | ||
231 | phy2->csi2 = &isp->isp_csi2a; | ||
232 | phy2->num_data_lanes = ISP_CSIPHY2_NUM_DATA_LANES; | ||
233 | phy2->cfg_regs = OMAP3_ISP_IOMEM_CSI2A_REGS1; | ||
234 | phy2->phy_regs = OMAP3_ISP_IOMEM_CSIPHY2; | ||
235 | mutex_init(&phy2->mutex); | ||
236 | |||
237 | if (isp->revision == ISP_REVISION_15_0) { | ||
238 | phy1->isp = isp; | ||
239 | phy1->csi2 = &isp->isp_csi2c; | ||
240 | phy1->num_data_lanes = ISP_CSIPHY1_NUM_DATA_LANES; | ||
241 | phy1->cfg_regs = OMAP3_ISP_IOMEM_CSI2C_REGS1; | ||
242 | phy1->phy_regs = OMAP3_ISP_IOMEM_CSIPHY1; | ||
243 | mutex_init(&phy1->mutex); | ||
244 | } | ||
245 | |||
246 | return 0; | ||
247 | } | ||
diff --git a/drivers/media/video/omap3isp/ispcsiphy.h b/drivers/media/video/omap3isp/ispcsiphy.h new file mode 100644 index 00000000000..9596dc6830a --- /dev/null +++ b/drivers/media/video/omap3isp/ispcsiphy.h | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * ispcsiphy.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - CSI PHY module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #ifndef OMAP3_ISP_CSI_PHY_H | ||
28 | #define OMAP3_ISP_CSI_PHY_H | ||
29 | |||
30 | struct isp_csi2_device; | ||
31 | struct regulator; | ||
32 | |||
33 | struct csiphy_lane { | ||
34 | u8 pos; | ||
35 | u8 pol; | ||
36 | }; | ||
37 | |||
38 | #define ISP_CSIPHY2_NUM_DATA_LANES 2 | ||
39 | #define ISP_CSIPHY1_NUM_DATA_LANES 1 | ||
40 | |||
41 | struct isp_csiphy_lanes_cfg { | ||
42 | struct csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; | ||
43 | struct csiphy_lane clk; | ||
44 | }; | ||
45 | |||
46 | struct isp_csiphy_dphy_cfg { | ||
47 | u8 ths_term; | ||
48 | u8 ths_settle; | ||
49 | u8 tclk_term; | ||
50 | unsigned tclk_miss:1; | ||
51 | u8 tclk_settle; | ||
52 | }; | ||
53 | |||
54 | struct isp_csiphy { | ||
55 | struct isp_device *isp; | ||
56 | struct mutex mutex; /* serialize csiphy configuration */ | ||
57 | u8 phy_in_use; | ||
58 | struct isp_csi2_device *csi2; | ||
59 | struct regulator *vdd; | ||
60 | |||
61 | /* mem resources - enums as defined in enum isp_mem_resources */ | ||
62 | unsigned int cfg_regs; | ||
63 | unsigned int phy_regs; | ||
64 | |||
65 | u8 num_data_lanes; /* number of CSI2 Data Lanes supported */ | ||
66 | struct isp_csiphy_lanes_cfg lanes; | ||
67 | struct isp_csiphy_dphy_cfg dphy; | ||
68 | }; | ||
69 | |||
70 | int omap3isp_csiphy_acquire(struct isp_csiphy *phy); | ||
71 | void omap3isp_csiphy_release(struct isp_csiphy *phy); | ||
72 | int omap3isp_csiphy_init(struct isp_device *isp); | ||
73 | |||
74 | #endif /* OMAP3_ISP_CSI_PHY_H */ | ||
diff --git a/drivers/media/video/omap3isp/isph3a.h b/drivers/media/video/omap3isp/isph3a.h new file mode 100644 index 00000000000..fb09fd4ca75 --- /dev/null +++ b/drivers/media/video/omap3isp/isph3a.h | |||
@@ -0,0 +1,117 @@ | |||
1 | /* | ||
2 | * isph3a.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - H3A AF module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: David Cohen <dacohen@gmail.com> | ||
10 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
11 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License version 2 as | ||
15 | * published by the Free Software Foundation. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, but | ||
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20 | * General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
25 | * 02110-1301 USA | ||
26 | */ | ||
27 | |||
28 | #ifndef OMAP3_ISP_H3A_H | ||
29 | #define OMAP3_ISP_H3A_H | ||
30 | |||
31 | #include <linux/omap3isp.h> | ||
32 | |||
33 | /* | ||
34 | * ---------- | ||
35 | * -H3A AEWB- | ||
36 | * ---------- | ||
37 | */ | ||
38 | |||
39 | #define AEWB_PACKET_SIZE 16 | ||
40 | #define AEWB_SATURATION_LIMIT 0x3ff | ||
41 | |||
42 | /* Flags for changed registers */ | ||
43 | #define PCR_CHNG (1 << 0) | ||
44 | #define AEWWIN1_CHNG (1 << 1) | ||
45 | #define AEWINSTART_CHNG (1 << 2) | ||
46 | #define AEWINBLK_CHNG (1 << 3) | ||
47 | #define AEWSUBWIN_CHNG (1 << 4) | ||
48 | #define PRV_WBDGAIN_CHNG (1 << 5) | ||
49 | #define PRV_WBGAIN_CHNG (1 << 6) | ||
50 | |||
51 | /* ISPH3A REGISTERS bits */ | ||
52 | #define ISPH3A_PCR_AF_EN (1 << 0) | ||
53 | #define ISPH3A_PCR_AF_ALAW_EN (1 << 1) | ||
54 | #define ISPH3A_PCR_AF_MED_EN (1 << 2) | ||
55 | #define ISPH3A_PCR_AF_BUSY (1 << 15) | ||
56 | #define ISPH3A_PCR_AEW_EN (1 << 16) | ||
57 | #define ISPH3A_PCR_AEW_ALAW_EN (1 << 17) | ||
58 | #define ISPH3A_PCR_AEW_BUSY (1 << 18) | ||
59 | #define ISPH3A_PCR_AEW_MASK (ISPH3A_PCR_AEW_ALAW_EN | \ | ||
60 | ISPH3A_PCR_AEW_AVE2LMT_MASK) | ||
61 | |||
62 | /* | ||
63 | * -------- | ||
64 | * -H3A AF- | ||
65 | * -------- | ||
66 | */ | ||
67 | |||
68 | /* Peripheral Revision */ | ||
69 | #define AFPID 0x0 | ||
70 | |||
71 | #define AFCOEF_OFFSET 0x00000004 /* COEF base address */ | ||
72 | |||
73 | /* PCR fields */ | ||
74 | #define AF_BUSYAF (1 << 15) | ||
75 | #define AF_FVMODE (1 << 14) | ||
76 | #define AF_RGBPOS (0x7 << 11) | ||
77 | #define AF_MED_TH (0xFF << 3) | ||
78 | #define AF_MED_EN (1 << 2) | ||
79 | #define AF_ALAW_EN (1 << 1) | ||
80 | #define AF_EN (1 << 0) | ||
81 | #define AF_PCR_MASK (AF_FVMODE | AF_RGBPOS | AF_MED_TH | \ | ||
82 | AF_MED_EN | AF_ALAW_EN) | ||
83 | |||
84 | /* AFPAX1 fields */ | ||
85 | #define AF_PAXW (0x7F << 16) | ||
86 | #define AF_PAXH 0x7F | ||
87 | |||
88 | /* AFPAX2 fields */ | ||
89 | #define AF_AFINCV (0xF << 13) | ||
90 | #define AF_PAXVC (0x7F << 6) | ||
91 | #define AF_PAXHC 0x3F | ||
92 | |||
93 | /* AFPAXSTART fields */ | ||
94 | #define AF_PAXSH (0xFFF<<16) | ||
95 | #define AF_PAXSV 0xFFF | ||
96 | |||
97 | /* COEFFICIENT MASK */ | ||
98 | #define AF_COEF_MASK0 0xFFF | ||
99 | #define AF_COEF_MASK1 (0xFFF<<16) | ||
100 | |||
101 | /* BIT SHIFTS */ | ||
102 | #define AF_RGBPOS_SHIFT 11 | ||
103 | #define AF_MED_TH_SHIFT 3 | ||
104 | #define AF_PAXW_SHIFT 16 | ||
105 | #define AF_LINE_INCR_SHIFT 13 | ||
106 | #define AF_VT_COUNT_SHIFT 6 | ||
107 | #define AF_HZ_START_SHIFT 16 | ||
108 | #define AF_COEF_SHIFT 16 | ||
109 | |||
110 | /* Init and cleanup functions */ | ||
111 | int omap3isp_h3a_aewb_init(struct isp_device *isp); | ||
112 | int omap3isp_h3a_af_init(struct isp_device *isp); | ||
113 | |||
114 | void omap3isp_h3a_aewb_cleanup(struct isp_device *isp); | ||
115 | void omap3isp_h3a_af_cleanup(struct isp_device *isp); | ||
116 | |||
117 | #endif /* OMAP3_ISP_H3A_H */ | ||
diff --git a/drivers/media/video/omap3isp/isph3a_aewb.c b/drivers/media/video/omap3isp/isph3a_aewb.c new file mode 100644 index 00000000000..8068cefd8d8 --- /dev/null +++ b/drivers/media/video/omap3isp/isph3a_aewb.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /* | ||
2 | * isph3a.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - H3A module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: David Cohen <dacohen@gmail.com> | ||
10 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
11 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License version 2 as | ||
15 | * published by the Free Software Foundation. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, but | ||
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20 | * General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
25 | * 02110-1301 USA | ||
26 | */ | ||
27 | |||
28 | #include <linux/slab.h> | ||
29 | #include <linux/uaccess.h> | ||
30 | |||
31 | #include "isp.h" | ||
32 | #include "isph3a.h" | ||
33 | #include "ispstat.h" | ||
34 | |||
35 | /* | ||
36 | * h3a_aewb_update_regs - Helper function to update h3a registers. | ||
37 | */ | ||
38 | static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv) | ||
39 | { | ||
40 | struct omap3isp_h3a_aewb_config *conf = priv; | ||
41 | u32 pcr; | ||
42 | u32 win1; | ||
43 | u32 start; | ||
44 | u32 blk; | ||
45 | u32 subwin; | ||
46 | |||
47 | if (aewb->state == ISPSTAT_DISABLED) | ||
48 | return; | ||
49 | |||
50 | isp_reg_writel(aewb->isp, aewb->active_buf->iommu_addr, | ||
51 | OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST); | ||
52 | |||
53 | if (!aewb->update) | ||
54 | return; | ||
55 | |||
56 | /* Converting config metadata into reg values */ | ||
57 | pcr = conf->saturation_limit << ISPH3A_PCR_AEW_AVE2LMT_SHIFT; | ||
58 | pcr |= !!conf->alaw_enable << ISPH3A_PCR_AEW_ALAW_EN_SHIFT; | ||
59 | |||
60 | win1 = ((conf->win_height >> 1) - 1) << ISPH3A_AEWWIN1_WINH_SHIFT; | ||
61 | win1 |= ((conf->win_width >> 1) - 1) << ISPH3A_AEWWIN1_WINW_SHIFT; | ||
62 | win1 |= (conf->ver_win_count - 1) << ISPH3A_AEWWIN1_WINVC_SHIFT; | ||
63 | win1 |= (conf->hor_win_count - 1) << ISPH3A_AEWWIN1_WINHC_SHIFT; | ||
64 | |||
65 | start = conf->hor_win_start << ISPH3A_AEWINSTART_WINSH_SHIFT; | ||
66 | start |= conf->ver_win_start << ISPH3A_AEWINSTART_WINSV_SHIFT; | ||
67 | |||
68 | blk = conf->blk_ver_win_start << ISPH3A_AEWINBLK_WINSV_SHIFT; | ||
69 | blk |= ((conf->blk_win_height >> 1) - 1) << ISPH3A_AEWINBLK_WINH_SHIFT; | ||
70 | |||
71 | subwin = ((conf->subsample_ver_inc >> 1) - 1) << | ||
72 | ISPH3A_AEWSUBWIN_AEWINCV_SHIFT; | ||
73 | subwin |= ((conf->subsample_hor_inc >> 1) - 1) << | ||
74 | ISPH3A_AEWSUBWIN_AEWINCH_SHIFT; | ||
75 | |||
76 | isp_reg_writel(aewb->isp, win1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1); | ||
77 | isp_reg_writel(aewb->isp, start, OMAP3_ISP_IOMEM_H3A, | ||
78 | ISPH3A_AEWINSTART); | ||
79 | isp_reg_writel(aewb->isp, blk, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK); | ||
80 | isp_reg_writel(aewb->isp, subwin, OMAP3_ISP_IOMEM_H3A, | ||
81 | ISPH3A_AEWSUBWIN); | ||
82 | isp_reg_clr_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
83 | ISPH3A_PCR_AEW_MASK, pcr); | ||
84 | |||
85 | aewb->update = 0; | ||
86 | aewb->config_counter += aewb->inc_config; | ||
87 | aewb->inc_config = 0; | ||
88 | aewb->buf_size = conf->buf_size; | ||
89 | } | ||
90 | |||
91 | static void h3a_aewb_enable(struct ispstat *aewb, int enable) | ||
92 | { | ||
93 | if (enable) { | ||
94 | isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
95 | ISPH3A_PCR_AEW_EN); | ||
96 | /* This bit is already set if AF is enabled */ | ||
97 | if (aewb->isp->isp_af.state != ISPSTAT_ENABLED) | ||
98 | isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
99 | ISPCTRL_H3A_CLK_EN); | ||
100 | } else { | ||
101 | isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
102 | ISPH3A_PCR_AEW_EN); | ||
103 | /* This bit can't be cleared if AF is enabled */ | ||
104 | if (aewb->isp->isp_af.state != ISPSTAT_ENABLED) | ||
105 | isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
106 | ISPCTRL_H3A_CLK_EN); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | static int h3a_aewb_busy(struct ispstat *aewb) | ||
111 | { | ||
112 | return isp_reg_readl(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) | ||
113 | & ISPH3A_PCR_BUSYAEAWB; | ||
114 | } | ||
115 | |||
116 | static u32 h3a_aewb_get_buf_size(struct omap3isp_h3a_aewb_config *conf) | ||
117 | { | ||
118 | /* Number of configured windows + extra row for black data */ | ||
119 | u32 win_count = (conf->ver_win_count + 1) * conf->hor_win_count; | ||
120 | |||
121 | /* | ||
122 | * Unsaturated block counts for each 8 windows. | ||
123 | * 1 extra for the last (win_count % 8) windows if win_count is not | ||
124 | * divisible by 8. | ||
125 | */ | ||
126 | win_count += (win_count + 7) / 8; | ||
127 | |||
128 | return win_count * AEWB_PACKET_SIZE; | ||
129 | } | ||
130 | |||
131 | static int h3a_aewb_validate_params(struct ispstat *aewb, void *new_conf) | ||
132 | { | ||
133 | struct omap3isp_h3a_aewb_config *user_cfg = new_conf; | ||
134 | u32 buf_size; | ||
135 | |||
136 | if (unlikely(user_cfg->saturation_limit > | ||
137 | OMAP3ISP_AEWB_MAX_SATURATION_LIM)) | ||
138 | return -EINVAL; | ||
139 | |||
140 | if (unlikely(user_cfg->win_height < OMAP3ISP_AEWB_MIN_WIN_H || | ||
141 | user_cfg->win_height > OMAP3ISP_AEWB_MAX_WIN_H || | ||
142 | user_cfg->win_height & 0x01)) | ||
143 | return -EINVAL; | ||
144 | |||
145 | if (unlikely(user_cfg->win_width < OMAP3ISP_AEWB_MIN_WIN_W || | ||
146 | user_cfg->win_width > OMAP3ISP_AEWB_MAX_WIN_W || | ||
147 | user_cfg->win_width & 0x01)) | ||
148 | return -EINVAL; | ||
149 | |||
150 | if (unlikely(user_cfg->ver_win_count < OMAP3ISP_AEWB_MIN_WINVC || | ||
151 | user_cfg->ver_win_count > OMAP3ISP_AEWB_MAX_WINVC)) | ||
152 | return -EINVAL; | ||
153 | |||
154 | if (unlikely(user_cfg->hor_win_count < OMAP3ISP_AEWB_MIN_WINHC || | ||
155 | user_cfg->hor_win_count > OMAP3ISP_AEWB_MAX_WINHC)) | ||
156 | return -EINVAL; | ||
157 | |||
158 | if (unlikely(user_cfg->ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) | ||
159 | return -EINVAL; | ||
160 | |||
161 | if (unlikely(user_cfg->hor_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) | ||
162 | return -EINVAL; | ||
163 | |||
164 | if (unlikely(user_cfg->blk_ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) | ||
165 | return -EINVAL; | ||
166 | |||
167 | if (unlikely(user_cfg->blk_win_height < OMAP3ISP_AEWB_MIN_WIN_H || | ||
168 | user_cfg->blk_win_height > OMAP3ISP_AEWB_MAX_WIN_H || | ||
169 | user_cfg->blk_win_height & 0x01)) | ||
170 | return -EINVAL; | ||
171 | |||
172 | if (unlikely(user_cfg->subsample_ver_inc < OMAP3ISP_AEWB_MIN_SUB_INC || | ||
173 | user_cfg->subsample_ver_inc > OMAP3ISP_AEWB_MAX_SUB_INC || | ||
174 | user_cfg->subsample_ver_inc & 0x01)) | ||
175 | return -EINVAL; | ||
176 | |||
177 | if (unlikely(user_cfg->subsample_hor_inc < OMAP3ISP_AEWB_MIN_SUB_INC || | ||
178 | user_cfg->subsample_hor_inc > OMAP3ISP_AEWB_MAX_SUB_INC || | ||
179 | user_cfg->subsample_hor_inc & 0x01)) | ||
180 | return -EINVAL; | ||
181 | |||
182 | buf_size = h3a_aewb_get_buf_size(user_cfg); | ||
183 | if (buf_size > user_cfg->buf_size) | ||
184 | user_cfg->buf_size = buf_size; | ||
185 | else if (user_cfg->buf_size > OMAP3ISP_AEWB_MAX_BUF_SIZE) | ||
186 | user_cfg->buf_size = OMAP3ISP_AEWB_MAX_BUF_SIZE; | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * h3a_aewb_set_params - Helper function to check & store user given params. | ||
193 | * @new_conf: Pointer to AE and AWB parameters struct. | ||
194 | * | ||
195 | * As most of them are busy-lock registers, need to wait until AEW_BUSY = 0 to | ||
196 | * program them during ISR. | ||
197 | */ | ||
198 | static void h3a_aewb_set_params(struct ispstat *aewb, void *new_conf) | ||
199 | { | ||
200 | struct omap3isp_h3a_aewb_config *user_cfg = new_conf; | ||
201 | struct omap3isp_h3a_aewb_config *cur_cfg = aewb->priv; | ||
202 | int update = 0; | ||
203 | |||
204 | if (cur_cfg->saturation_limit != user_cfg->saturation_limit) { | ||
205 | cur_cfg->saturation_limit = user_cfg->saturation_limit; | ||
206 | update = 1; | ||
207 | } | ||
208 | if (cur_cfg->alaw_enable != user_cfg->alaw_enable) { | ||
209 | cur_cfg->alaw_enable = user_cfg->alaw_enable; | ||
210 | update = 1; | ||
211 | } | ||
212 | if (cur_cfg->win_height != user_cfg->win_height) { | ||
213 | cur_cfg->win_height = user_cfg->win_height; | ||
214 | update = 1; | ||
215 | } | ||
216 | if (cur_cfg->win_width != user_cfg->win_width) { | ||
217 | cur_cfg->win_width = user_cfg->win_width; | ||
218 | update = 1; | ||
219 | } | ||
220 | if (cur_cfg->ver_win_count != user_cfg->ver_win_count) { | ||
221 | cur_cfg->ver_win_count = user_cfg->ver_win_count; | ||
222 | update = 1; | ||
223 | } | ||
224 | if (cur_cfg->hor_win_count != user_cfg->hor_win_count) { | ||
225 | cur_cfg->hor_win_count = user_cfg->hor_win_count; | ||
226 | update = 1; | ||
227 | } | ||
228 | if (cur_cfg->ver_win_start != user_cfg->ver_win_start) { | ||
229 | cur_cfg->ver_win_start = user_cfg->ver_win_start; | ||
230 | update = 1; | ||
231 | } | ||
232 | if (cur_cfg->hor_win_start != user_cfg->hor_win_start) { | ||
233 | cur_cfg->hor_win_start = user_cfg->hor_win_start; | ||
234 | update = 1; | ||
235 | } | ||
236 | if (cur_cfg->blk_ver_win_start != user_cfg->blk_ver_win_start) { | ||
237 | cur_cfg->blk_ver_win_start = user_cfg->blk_ver_win_start; | ||
238 | update = 1; | ||
239 | } | ||
240 | if (cur_cfg->blk_win_height != user_cfg->blk_win_height) { | ||
241 | cur_cfg->blk_win_height = user_cfg->blk_win_height; | ||
242 | update = 1; | ||
243 | } | ||
244 | if (cur_cfg->subsample_ver_inc != user_cfg->subsample_ver_inc) { | ||
245 | cur_cfg->subsample_ver_inc = user_cfg->subsample_ver_inc; | ||
246 | update = 1; | ||
247 | } | ||
248 | if (cur_cfg->subsample_hor_inc != user_cfg->subsample_hor_inc) { | ||
249 | cur_cfg->subsample_hor_inc = user_cfg->subsample_hor_inc; | ||
250 | update = 1; | ||
251 | } | ||
252 | |||
253 | if (update || !aewb->configured) { | ||
254 | aewb->inc_config++; | ||
255 | aewb->update = 1; | ||
256 | cur_cfg->buf_size = h3a_aewb_get_buf_size(cur_cfg); | ||
257 | } | ||
258 | } | ||
259 | |||
260 | static long h3a_aewb_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
261 | { | ||
262 | struct ispstat *stat = v4l2_get_subdevdata(sd); | ||
263 | |||
264 | switch (cmd) { | ||
265 | case VIDIOC_OMAP3ISP_AEWB_CFG: | ||
266 | return omap3isp_stat_config(stat, arg); | ||
267 | case VIDIOC_OMAP3ISP_STAT_REQ: | ||
268 | return omap3isp_stat_request_statistics(stat, arg); | ||
269 | case VIDIOC_OMAP3ISP_STAT_EN: { | ||
270 | unsigned long *en = arg; | ||
271 | return omap3isp_stat_enable(stat, !!*en); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | return -ENOIOCTLCMD; | ||
276 | } | ||
277 | |||
278 | static const struct ispstat_ops h3a_aewb_ops = { | ||
279 | .validate_params = h3a_aewb_validate_params, | ||
280 | .set_params = h3a_aewb_set_params, | ||
281 | .setup_regs = h3a_aewb_setup_regs, | ||
282 | .enable = h3a_aewb_enable, | ||
283 | .busy = h3a_aewb_busy, | ||
284 | }; | ||
285 | |||
286 | static const struct v4l2_subdev_core_ops h3a_aewb_subdev_core_ops = { | ||
287 | .ioctl = h3a_aewb_ioctl, | ||
288 | .subscribe_event = omap3isp_stat_subscribe_event, | ||
289 | .unsubscribe_event = omap3isp_stat_unsubscribe_event, | ||
290 | }; | ||
291 | |||
292 | static const struct v4l2_subdev_video_ops h3a_aewb_subdev_video_ops = { | ||
293 | .s_stream = omap3isp_stat_s_stream, | ||
294 | }; | ||
295 | |||
296 | static const struct v4l2_subdev_ops h3a_aewb_subdev_ops = { | ||
297 | .core = &h3a_aewb_subdev_core_ops, | ||
298 | .video = &h3a_aewb_subdev_video_ops, | ||
299 | }; | ||
300 | |||
301 | /* | ||
302 | * omap3isp_h3a_aewb_init - Module Initialisation. | ||
303 | */ | ||
304 | int omap3isp_h3a_aewb_init(struct isp_device *isp) | ||
305 | { | ||
306 | struct ispstat *aewb = &isp->isp_aewb; | ||
307 | struct omap3isp_h3a_aewb_config *aewb_cfg; | ||
308 | struct omap3isp_h3a_aewb_config *aewb_recover_cfg; | ||
309 | int ret; | ||
310 | |||
311 | aewb_cfg = kzalloc(sizeof(*aewb_cfg), GFP_KERNEL); | ||
312 | if (!aewb_cfg) | ||
313 | return -ENOMEM; | ||
314 | |||
315 | memset(aewb, 0, sizeof(*aewb)); | ||
316 | aewb->ops = &h3a_aewb_ops; | ||
317 | aewb->priv = aewb_cfg; | ||
318 | aewb->dma_ch = -1; | ||
319 | aewb->event_type = V4L2_EVENT_OMAP3ISP_AEWB; | ||
320 | aewb->isp = isp; | ||
321 | |||
322 | /* Set recover state configuration */ | ||
323 | aewb_recover_cfg = kzalloc(sizeof(*aewb_recover_cfg), GFP_KERNEL); | ||
324 | if (!aewb_recover_cfg) { | ||
325 | dev_err(aewb->isp->dev, "AEWB: cannot allocate memory for " | ||
326 | "recover configuration.\n"); | ||
327 | ret = -ENOMEM; | ||
328 | goto err_recover_alloc; | ||
329 | } | ||
330 | |||
331 | aewb_recover_cfg->saturation_limit = OMAP3ISP_AEWB_MAX_SATURATION_LIM; | ||
332 | aewb_recover_cfg->win_height = OMAP3ISP_AEWB_MIN_WIN_H; | ||
333 | aewb_recover_cfg->win_width = OMAP3ISP_AEWB_MIN_WIN_W; | ||
334 | aewb_recover_cfg->ver_win_count = OMAP3ISP_AEWB_MIN_WINVC; | ||
335 | aewb_recover_cfg->hor_win_count = OMAP3ISP_AEWB_MIN_WINHC; | ||
336 | aewb_recover_cfg->blk_ver_win_start = aewb_recover_cfg->ver_win_start + | ||
337 | aewb_recover_cfg->win_height * aewb_recover_cfg->ver_win_count; | ||
338 | aewb_recover_cfg->blk_win_height = OMAP3ISP_AEWB_MIN_WIN_H; | ||
339 | aewb_recover_cfg->subsample_ver_inc = OMAP3ISP_AEWB_MIN_SUB_INC; | ||
340 | aewb_recover_cfg->subsample_hor_inc = OMAP3ISP_AEWB_MIN_SUB_INC; | ||
341 | |||
342 | if (h3a_aewb_validate_params(aewb, aewb_recover_cfg)) { | ||
343 | dev_err(aewb->isp->dev, "AEWB: recover configuration is " | ||
344 | "invalid.\n"); | ||
345 | ret = -EINVAL; | ||
346 | goto err_conf; | ||
347 | } | ||
348 | |||
349 | aewb_recover_cfg->buf_size = h3a_aewb_get_buf_size(aewb_recover_cfg); | ||
350 | aewb->recover_priv = aewb_recover_cfg; | ||
351 | |||
352 | ret = omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops); | ||
353 | if (ret) | ||
354 | goto err_conf; | ||
355 | |||
356 | return 0; | ||
357 | |||
358 | err_conf: | ||
359 | kfree(aewb_recover_cfg); | ||
360 | err_recover_alloc: | ||
361 | kfree(aewb_cfg); | ||
362 | |||
363 | return ret; | ||
364 | } | ||
365 | |||
366 | /* | ||
367 | * omap3isp_h3a_aewb_cleanup - Module exit. | ||
368 | */ | ||
369 | void omap3isp_h3a_aewb_cleanup(struct isp_device *isp) | ||
370 | { | ||
371 | kfree(isp->isp_aewb.priv); | ||
372 | kfree(isp->isp_aewb.recover_priv); | ||
373 | omap3isp_stat_free(&isp->isp_aewb); | ||
374 | } | ||
diff --git a/drivers/media/video/omap3isp/isph3a_af.c b/drivers/media/video/omap3isp/isph3a_af.c new file mode 100644 index 00000000000..ba54d0acdec --- /dev/null +++ b/drivers/media/video/omap3isp/isph3a_af.c | |||
@@ -0,0 +1,429 @@ | |||
1 | /* | ||
2 | * isph3a_af.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - H3A AF module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: David Cohen <dacohen@gmail.com> | ||
10 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
11 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License version 2 as | ||
15 | * published by the Free Software Foundation. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, but | ||
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20 | * General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
25 | * 02110-1301 USA | ||
26 | */ | ||
27 | |||
28 | /* Linux specific include files */ | ||
29 | #include <linux/device.h> | ||
30 | #include <linux/slab.h> | ||
31 | |||
32 | #include "isp.h" | ||
33 | #include "isph3a.h" | ||
34 | #include "ispstat.h" | ||
35 | |||
36 | #define IS_OUT_OF_BOUNDS(value, min, max) \ | ||
37 | (((value) < (min)) || ((value) > (max))) | ||
38 | |||
39 | static void h3a_af_setup_regs(struct ispstat *af, void *priv) | ||
40 | { | ||
41 | struct omap3isp_h3a_af_config *conf = priv; | ||
42 | u32 pcr; | ||
43 | u32 pax1; | ||
44 | u32 pax2; | ||
45 | u32 paxstart; | ||
46 | u32 coef; | ||
47 | u32 base_coef_set0; | ||
48 | u32 base_coef_set1; | ||
49 | int index; | ||
50 | |||
51 | if (af->state == ISPSTAT_DISABLED) | ||
52 | return; | ||
53 | |||
54 | isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A, | ||
55 | ISPH3A_AFBUFST); | ||
56 | |||
57 | if (!af->update) | ||
58 | return; | ||
59 | |||
60 | /* Configure Hardware Registers */ | ||
61 | pax1 = ((conf->paxel.width >> 1) - 1) << AF_PAXW_SHIFT; | ||
62 | /* Set height in AFPAX1 */ | ||
63 | pax1 |= (conf->paxel.height >> 1) - 1; | ||
64 | isp_reg_writel(af->isp, pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1); | ||
65 | |||
66 | /* Configure AFPAX2 Register */ | ||
67 | /* Set Line Increment in AFPAX2 Register */ | ||
68 | pax2 = ((conf->paxel.line_inc >> 1) - 1) << AF_LINE_INCR_SHIFT; | ||
69 | /* Set Vertical Count */ | ||
70 | pax2 |= (conf->paxel.v_cnt - 1) << AF_VT_COUNT_SHIFT; | ||
71 | /* Set Horizontal Count */ | ||
72 | pax2 |= (conf->paxel.h_cnt - 1); | ||
73 | isp_reg_writel(af->isp, pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2); | ||
74 | |||
75 | /* Configure PAXSTART Register */ | ||
76 | /*Configure Horizontal Start */ | ||
77 | paxstart = conf->paxel.h_start << AF_HZ_START_SHIFT; | ||
78 | /* Configure Vertical Start */ | ||
79 | paxstart |= conf->paxel.v_start; | ||
80 | isp_reg_writel(af->isp, paxstart, OMAP3_ISP_IOMEM_H3A, | ||
81 | ISPH3A_AFPAXSTART); | ||
82 | |||
83 | /*SetIIRSH Register */ | ||
84 | isp_reg_writel(af->isp, conf->iir.h_start, | ||
85 | OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH); | ||
86 | |||
87 | base_coef_set0 = ISPH3A_AFCOEF010; | ||
88 | base_coef_set1 = ISPH3A_AFCOEF110; | ||
89 | for (index = 0; index <= 8; index += 2) { | ||
90 | /*Set IIR Filter0 Coefficients */ | ||
91 | coef = 0; | ||
92 | coef |= conf->iir.coeff_set0[index]; | ||
93 | coef |= conf->iir.coeff_set0[index + 1] << | ||
94 | AF_COEF_SHIFT; | ||
95 | isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A, | ||
96 | base_coef_set0); | ||
97 | base_coef_set0 += AFCOEF_OFFSET; | ||
98 | |||
99 | /*Set IIR Filter1 Coefficients */ | ||
100 | coef = 0; | ||
101 | coef |= conf->iir.coeff_set1[index]; | ||
102 | coef |= conf->iir.coeff_set1[index + 1] << | ||
103 | AF_COEF_SHIFT; | ||
104 | isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A, | ||
105 | base_coef_set1); | ||
106 | base_coef_set1 += AFCOEF_OFFSET; | ||
107 | } | ||
108 | /* set AFCOEF0010 Register */ | ||
109 | isp_reg_writel(af->isp, conf->iir.coeff_set0[10], | ||
110 | OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010); | ||
111 | /* set AFCOEF1010 Register */ | ||
112 | isp_reg_writel(af->isp, conf->iir.coeff_set1[10], | ||
113 | OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010); | ||
114 | |||
115 | /* PCR Register */ | ||
116 | /* Set RGB Position */ | ||
117 | pcr = conf->rgb_pos << AF_RGBPOS_SHIFT; | ||
118 | /* Set Accumulator Mode */ | ||
119 | if (conf->fvmode == OMAP3ISP_AF_MODE_PEAK) | ||
120 | pcr |= AF_FVMODE; | ||
121 | /* Set A-law */ | ||
122 | if (conf->alaw_enable) | ||
123 | pcr |= AF_ALAW_EN; | ||
124 | /* HMF Configurations */ | ||
125 | if (conf->hmf.enable) { | ||
126 | /* Enable HMF */ | ||
127 | pcr |= AF_MED_EN; | ||
128 | /* Set Median Threshold */ | ||
129 | pcr |= conf->hmf.threshold << AF_MED_TH_SHIFT; | ||
130 | } | ||
131 | /* Set PCR Register */ | ||
132 | isp_reg_clr_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
133 | AF_PCR_MASK, pcr); | ||
134 | |||
135 | af->update = 0; | ||
136 | af->config_counter += af->inc_config; | ||
137 | af->inc_config = 0; | ||
138 | af->buf_size = conf->buf_size; | ||
139 | } | ||
140 | |||
141 | static void h3a_af_enable(struct ispstat *af, int enable) | ||
142 | { | ||
143 | if (enable) { | ||
144 | isp_reg_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
145 | ISPH3A_PCR_AF_EN); | ||
146 | /* This bit is already set if AEWB is enabled */ | ||
147 | if (af->isp->isp_aewb.state != ISPSTAT_ENABLED) | ||
148 | isp_reg_set(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
149 | ISPCTRL_H3A_CLK_EN); | ||
150 | } else { | ||
151 | isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
152 | ISPH3A_PCR_AF_EN); | ||
153 | /* This bit can't be cleared if AEWB is enabled */ | ||
154 | if (af->isp->isp_aewb.state != ISPSTAT_ENABLED) | ||
155 | isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
156 | ISPCTRL_H3A_CLK_EN); | ||
157 | } | ||
158 | } | ||
159 | |||
160 | static int h3a_af_busy(struct ispstat *af) | ||
161 | { | ||
162 | return isp_reg_readl(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) | ||
163 | & ISPH3A_PCR_BUSYAF; | ||
164 | } | ||
165 | |||
166 | static u32 h3a_af_get_buf_size(struct omap3isp_h3a_af_config *conf) | ||
167 | { | ||
168 | return conf->paxel.h_cnt * conf->paxel.v_cnt * OMAP3ISP_AF_PAXEL_SIZE; | ||
169 | } | ||
170 | |||
171 | /* Function to check paxel parameters */ | ||
172 | static int h3a_af_validate_params(struct ispstat *af, void *new_conf) | ||
173 | { | ||
174 | struct omap3isp_h3a_af_config *user_cfg = new_conf; | ||
175 | struct omap3isp_h3a_af_paxel *paxel_cfg = &user_cfg->paxel; | ||
176 | struct omap3isp_h3a_af_iir *iir_cfg = &user_cfg->iir; | ||
177 | int index; | ||
178 | u32 buf_size; | ||
179 | |||
180 | /* Check horizontal Count */ | ||
181 | if (IS_OUT_OF_BOUNDS(paxel_cfg->h_cnt, | ||
182 | OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN, | ||
183 | OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX)) | ||
184 | return -EINVAL; | ||
185 | |||
186 | /* Check Vertical Count */ | ||
187 | if (IS_OUT_OF_BOUNDS(paxel_cfg->v_cnt, | ||
188 | OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN, | ||
189 | OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX)) | ||
190 | return -EINVAL; | ||
191 | |||
192 | if (IS_OUT_OF_BOUNDS(paxel_cfg->height, OMAP3ISP_AF_PAXEL_HEIGHT_MIN, | ||
193 | OMAP3ISP_AF_PAXEL_HEIGHT_MAX) || | ||
194 | paxel_cfg->height % 2) | ||
195 | return -EINVAL; | ||
196 | |||
197 | /* Check width */ | ||
198 | if (IS_OUT_OF_BOUNDS(paxel_cfg->width, OMAP3ISP_AF_PAXEL_WIDTH_MIN, | ||
199 | OMAP3ISP_AF_PAXEL_WIDTH_MAX) || | ||
200 | paxel_cfg->width % 2) | ||
201 | return -EINVAL; | ||
202 | |||
203 | /* Check Line Increment */ | ||
204 | if (IS_OUT_OF_BOUNDS(paxel_cfg->line_inc, | ||
205 | OMAP3ISP_AF_PAXEL_INCREMENT_MIN, | ||
206 | OMAP3ISP_AF_PAXEL_INCREMENT_MAX) || | ||
207 | paxel_cfg->line_inc % 2) | ||
208 | return -EINVAL; | ||
209 | |||
210 | /* Check Horizontal Start */ | ||
211 | if ((paxel_cfg->h_start < iir_cfg->h_start) || | ||
212 | IS_OUT_OF_BOUNDS(paxel_cfg->h_start, | ||
213 | OMAP3ISP_AF_PAXEL_HZSTART_MIN, | ||
214 | OMAP3ISP_AF_PAXEL_HZSTART_MAX)) | ||
215 | return -EINVAL; | ||
216 | |||
217 | /* Check IIR */ | ||
218 | for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) { | ||
219 | if ((iir_cfg->coeff_set0[index]) > OMAP3ISP_AF_COEF_MAX) | ||
220 | return -EINVAL; | ||
221 | |||
222 | if ((iir_cfg->coeff_set1[index]) > OMAP3ISP_AF_COEF_MAX) | ||
223 | return -EINVAL; | ||
224 | } | ||
225 | |||
226 | if (IS_OUT_OF_BOUNDS(iir_cfg->h_start, OMAP3ISP_AF_IIRSH_MIN, | ||
227 | OMAP3ISP_AF_IIRSH_MAX)) | ||
228 | return -EINVAL; | ||
229 | |||
230 | /* Hack: If paxel size is 12, the 10th AF window may be corrupted */ | ||
231 | if ((paxel_cfg->h_cnt * paxel_cfg->v_cnt > 9) && | ||
232 | (paxel_cfg->width * paxel_cfg->height == 12)) | ||
233 | return -EINVAL; | ||
234 | |||
235 | buf_size = h3a_af_get_buf_size(user_cfg); | ||
236 | if (buf_size > user_cfg->buf_size) | ||
237 | /* User buf_size request wasn't enough */ | ||
238 | user_cfg->buf_size = buf_size; | ||
239 | else if (user_cfg->buf_size > OMAP3ISP_AF_MAX_BUF_SIZE) | ||
240 | user_cfg->buf_size = OMAP3ISP_AF_MAX_BUF_SIZE; | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | /* Update local parameters */ | ||
246 | static void h3a_af_set_params(struct ispstat *af, void *new_conf) | ||
247 | { | ||
248 | struct omap3isp_h3a_af_config *user_cfg = new_conf; | ||
249 | struct omap3isp_h3a_af_config *cur_cfg = af->priv; | ||
250 | int update = 0; | ||
251 | int index; | ||
252 | |||
253 | /* alaw */ | ||
254 | if (cur_cfg->alaw_enable != user_cfg->alaw_enable) { | ||
255 | update = 1; | ||
256 | goto out; | ||
257 | } | ||
258 | |||
259 | /* hmf */ | ||
260 | if (cur_cfg->hmf.enable != user_cfg->hmf.enable) { | ||
261 | update = 1; | ||
262 | goto out; | ||
263 | } | ||
264 | if (cur_cfg->hmf.threshold != user_cfg->hmf.threshold) { | ||
265 | update = 1; | ||
266 | goto out; | ||
267 | } | ||
268 | |||
269 | /* rgbpos */ | ||
270 | if (cur_cfg->rgb_pos != user_cfg->rgb_pos) { | ||
271 | update = 1; | ||
272 | goto out; | ||
273 | } | ||
274 | |||
275 | /* iir */ | ||
276 | if (cur_cfg->iir.h_start != user_cfg->iir.h_start) { | ||
277 | update = 1; | ||
278 | goto out; | ||
279 | } | ||
280 | for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) { | ||
281 | if (cur_cfg->iir.coeff_set0[index] != | ||
282 | user_cfg->iir.coeff_set0[index]) { | ||
283 | update = 1; | ||
284 | goto out; | ||
285 | } | ||
286 | if (cur_cfg->iir.coeff_set1[index] != | ||
287 | user_cfg->iir.coeff_set1[index]) { | ||
288 | update = 1; | ||
289 | goto out; | ||
290 | } | ||
291 | } | ||
292 | |||
293 | /* paxel */ | ||
294 | if ((cur_cfg->paxel.width != user_cfg->paxel.width) || | ||
295 | (cur_cfg->paxel.height != user_cfg->paxel.height) || | ||
296 | (cur_cfg->paxel.h_start != user_cfg->paxel.h_start) || | ||
297 | (cur_cfg->paxel.v_start != user_cfg->paxel.v_start) || | ||
298 | (cur_cfg->paxel.h_cnt != user_cfg->paxel.h_cnt) || | ||
299 | (cur_cfg->paxel.v_cnt != user_cfg->paxel.v_cnt) || | ||
300 | (cur_cfg->paxel.line_inc != user_cfg->paxel.line_inc)) { | ||
301 | update = 1; | ||
302 | goto out; | ||
303 | } | ||
304 | |||
305 | /* af_mode */ | ||
306 | if (cur_cfg->fvmode != user_cfg->fvmode) | ||
307 | update = 1; | ||
308 | |||
309 | out: | ||
310 | if (update || !af->configured) { | ||
311 | memcpy(cur_cfg, user_cfg, sizeof(*cur_cfg)); | ||
312 | af->inc_config++; | ||
313 | af->update = 1; | ||
314 | /* | ||
315 | * User might be asked for a bigger buffer than necessary for | ||
316 | * this configuration. In order to return the right amount of | ||
317 | * data during buffer request, let's calculate the size here | ||
318 | * instead of stick with user_cfg->buf_size. | ||
319 | */ | ||
320 | cur_cfg->buf_size = h3a_af_get_buf_size(cur_cfg); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | static long h3a_af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
325 | { | ||
326 | struct ispstat *stat = v4l2_get_subdevdata(sd); | ||
327 | |||
328 | switch (cmd) { | ||
329 | case VIDIOC_OMAP3ISP_AF_CFG: | ||
330 | return omap3isp_stat_config(stat, arg); | ||
331 | case VIDIOC_OMAP3ISP_STAT_REQ: | ||
332 | return omap3isp_stat_request_statistics(stat, arg); | ||
333 | case VIDIOC_OMAP3ISP_STAT_EN: { | ||
334 | int *en = arg; | ||
335 | return omap3isp_stat_enable(stat, !!*en); | ||
336 | } | ||
337 | } | ||
338 | |||
339 | return -ENOIOCTLCMD; | ||
340 | |||
341 | } | ||
342 | |||
343 | static const struct ispstat_ops h3a_af_ops = { | ||
344 | .validate_params = h3a_af_validate_params, | ||
345 | .set_params = h3a_af_set_params, | ||
346 | .setup_regs = h3a_af_setup_regs, | ||
347 | .enable = h3a_af_enable, | ||
348 | .busy = h3a_af_busy, | ||
349 | }; | ||
350 | |||
351 | static const struct v4l2_subdev_core_ops h3a_af_subdev_core_ops = { | ||
352 | .ioctl = h3a_af_ioctl, | ||
353 | .subscribe_event = omap3isp_stat_subscribe_event, | ||
354 | .unsubscribe_event = omap3isp_stat_unsubscribe_event, | ||
355 | }; | ||
356 | |||
357 | static const struct v4l2_subdev_video_ops h3a_af_subdev_video_ops = { | ||
358 | .s_stream = omap3isp_stat_s_stream, | ||
359 | }; | ||
360 | |||
361 | static const struct v4l2_subdev_ops h3a_af_subdev_ops = { | ||
362 | .core = &h3a_af_subdev_core_ops, | ||
363 | .video = &h3a_af_subdev_video_ops, | ||
364 | }; | ||
365 | |||
366 | /* Function to register the AF character device driver. */ | ||
367 | int omap3isp_h3a_af_init(struct isp_device *isp) | ||
368 | { | ||
369 | struct ispstat *af = &isp->isp_af; | ||
370 | struct omap3isp_h3a_af_config *af_cfg; | ||
371 | struct omap3isp_h3a_af_config *af_recover_cfg; | ||
372 | int ret; | ||
373 | |||
374 | af_cfg = kzalloc(sizeof(*af_cfg), GFP_KERNEL); | ||
375 | if (af_cfg == NULL) | ||
376 | return -ENOMEM; | ||
377 | |||
378 | memset(af, 0, sizeof(*af)); | ||
379 | af->ops = &h3a_af_ops; | ||
380 | af->priv = af_cfg; | ||
381 | af->dma_ch = -1; | ||
382 | af->event_type = V4L2_EVENT_OMAP3ISP_AF; | ||
383 | af->isp = isp; | ||
384 | |||
385 | /* Set recover state configuration */ | ||
386 | af_recover_cfg = kzalloc(sizeof(*af_recover_cfg), GFP_KERNEL); | ||
387 | if (!af_recover_cfg) { | ||
388 | dev_err(af->isp->dev, "AF: cannot allocate memory for recover " | ||
389 | "configuration.\n"); | ||
390 | ret = -ENOMEM; | ||
391 | goto err_recover_alloc; | ||
392 | } | ||
393 | |||
394 | af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN; | ||
395 | af_recover_cfg->paxel.width = OMAP3ISP_AF_PAXEL_WIDTH_MIN; | ||
396 | af_recover_cfg->paxel.height = OMAP3ISP_AF_PAXEL_HEIGHT_MIN; | ||
397 | af_recover_cfg->paxel.h_cnt = OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN; | ||
398 | af_recover_cfg->paxel.v_cnt = OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN; | ||
399 | af_recover_cfg->paxel.line_inc = OMAP3ISP_AF_PAXEL_INCREMENT_MIN; | ||
400 | if (h3a_af_validate_params(af, af_recover_cfg)) { | ||
401 | dev_err(af->isp->dev, "AF: recover configuration is " | ||
402 | "invalid.\n"); | ||
403 | ret = -EINVAL; | ||
404 | goto err_conf; | ||
405 | } | ||
406 | |||
407 | af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg); | ||
408 | af->recover_priv = af_recover_cfg; | ||
409 | |||
410 | ret = omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops); | ||
411 | if (ret) | ||
412 | goto err_conf; | ||
413 | |||
414 | return 0; | ||
415 | |||
416 | err_conf: | ||
417 | kfree(af_recover_cfg); | ||
418 | err_recover_alloc: | ||
419 | kfree(af_cfg); | ||
420 | |||
421 | return ret; | ||
422 | } | ||
423 | |||
424 | void omap3isp_h3a_af_cleanup(struct isp_device *isp) | ||
425 | { | ||
426 | kfree(isp->isp_af.priv); | ||
427 | kfree(isp->isp_af.recover_priv); | ||
428 | omap3isp_stat_free(&isp->isp_af); | ||
429 | } | ||
diff --git a/drivers/media/video/omap3isp/isphist.c b/drivers/media/video/omap3isp/isphist.c new file mode 100644 index 00000000000..1743856b30d --- /dev/null +++ b/drivers/media/video/omap3isp/isphist.c | |||
@@ -0,0 +1,520 @@ | |||
1 | /* | ||
2 | * isphist.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - Histogram module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: David Cohen <dacohen@gmail.com> | ||
10 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
11 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License version 2 as | ||
15 | * published by the Free Software Foundation. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, but | ||
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20 | * General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
25 | * 02110-1301 USA | ||
26 | */ | ||
27 | |||
28 | #include <linux/delay.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/uaccess.h> | ||
31 | #include <linux/device.h> | ||
32 | |||
33 | #include "isp.h" | ||
34 | #include "ispreg.h" | ||
35 | #include "isphist.h" | ||
36 | |||
37 | #define HIST_CONFIG_DMA 1 | ||
38 | |||
39 | #define HIST_USING_DMA(hist) ((hist)->dma_ch >= 0) | ||
40 | |||
41 | /* | ||
42 | * hist_reset_mem - clear Histogram memory before start stats engine. | ||
43 | */ | ||
44 | static void hist_reset_mem(struct ispstat *hist) | ||
45 | { | ||
46 | struct isp_device *isp = hist->isp; | ||
47 | struct omap3isp_hist_config *conf = hist->priv; | ||
48 | unsigned int i; | ||
49 | |||
50 | isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); | ||
51 | |||
52 | /* | ||
53 | * By setting it, the histogram internal buffer is being cleared at the | ||
54 | * same time it's being read. This bit must be cleared afterwards. | ||
55 | */ | ||
56 | isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); | ||
57 | |||
58 | /* | ||
59 | * We'll clear 4 words at each iteration for optimization. It avoids | ||
60 | * 3/4 of the jumps. We also know HIST_MEM_SIZE is divisible by 4. | ||
61 | */ | ||
62 | for (i = OMAP3ISP_HIST_MEM_SIZE / 4; i > 0; i--) { | ||
63 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
64 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
65 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
66 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
67 | } | ||
68 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); | ||
69 | |||
70 | hist->wait_acc_frames = conf->num_acc_frames; | ||
71 | } | ||
72 | |||
73 | static void hist_dma_config(struct ispstat *hist) | ||
74 | { | ||
75 | hist->dma_config.data_type = OMAP_DMA_DATA_TYPE_S32; | ||
76 | hist->dma_config.sync_mode = OMAP_DMA_SYNC_ELEMENT; | ||
77 | hist->dma_config.frame_count = 1; | ||
78 | hist->dma_config.src_amode = OMAP_DMA_AMODE_CONSTANT; | ||
79 | hist->dma_config.src_start = OMAP3ISP_HIST_REG_BASE + ISPHIST_DATA; | ||
80 | hist->dma_config.dst_amode = OMAP_DMA_AMODE_POST_INC; | ||
81 | hist->dma_config.src_or_dst_synch = OMAP_DMA_SRC_SYNC; | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * hist_setup_regs - Helper function to update Histogram registers. | ||
86 | */ | ||
87 | static void hist_setup_regs(struct ispstat *hist, void *priv) | ||
88 | { | ||
89 | struct isp_device *isp = hist->isp; | ||
90 | struct omap3isp_hist_config *conf = priv; | ||
91 | int c; | ||
92 | u32 cnt; | ||
93 | u32 wb_gain; | ||
94 | u32 reg_hor[OMAP3ISP_HIST_MAX_REGIONS]; | ||
95 | u32 reg_ver[OMAP3ISP_HIST_MAX_REGIONS]; | ||
96 | |||
97 | if (!hist->update || hist->state == ISPSTAT_DISABLED || | ||
98 | hist->state == ISPSTAT_DISABLING) | ||
99 | return; | ||
100 | |||
101 | cnt = conf->cfa << ISPHIST_CNT_CFA_SHIFT; | ||
102 | |||
103 | wb_gain = conf->wg[0] << ISPHIST_WB_GAIN_WG00_SHIFT; | ||
104 | wb_gain |= conf->wg[1] << ISPHIST_WB_GAIN_WG01_SHIFT; | ||
105 | wb_gain |= conf->wg[2] << ISPHIST_WB_GAIN_WG02_SHIFT; | ||
106 | if (conf->cfa == OMAP3ISP_HIST_CFA_BAYER) | ||
107 | wb_gain |= conf->wg[3] << ISPHIST_WB_GAIN_WG03_SHIFT; | ||
108 | |||
109 | /* Regions size and position */ | ||
110 | for (c = 0; c < OMAP3ISP_HIST_MAX_REGIONS; c++) { | ||
111 | if (c < conf->num_regions) { | ||
112 | reg_hor[c] = conf->region[c].h_start << | ||
113 | ISPHIST_REG_START_SHIFT; | ||
114 | reg_hor[c] = conf->region[c].h_end << | ||
115 | ISPHIST_REG_END_SHIFT; | ||
116 | reg_ver[c] = conf->region[c].v_start << | ||
117 | ISPHIST_REG_START_SHIFT; | ||
118 | reg_ver[c] = conf->region[c].v_end << | ||
119 | ISPHIST_REG_END_SHIFT; | ||
120 | } else { | ||
121 | reg_hor[c] = 0; | ||
122 | reg_ver[c] = 0; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | cnt |= conf->hist_bins << ISPHIST_CNT_BINS_SHIFT; | ||
127 | switch (conf->hist_bins) { | ||
128 | case OMAP3ISP_HIST_BINS_256: | ||
129 | cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 8) << | ||
130 | ISPHIST_CNT_SHIFT_SHIFT; | ||
131 | break; | ||
132 | case OMAP3ISP_HIST_BINS_128: | ||
133 | cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 7) << | ||
134 | ISPHIST_CNT_SHIFT_SHIFT; | ||
135 | break; | ||
136 | case OMAP3ISP_HIST_BINS_64: | ||
137 | cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 6) << | ||
138 | ISPHIST_CNT_SHIFT_SHIFT; | ||
139 | break; | ||
140 | default: /* OMAP3ISP_HIST_BINS_32 */ | ||
141 | cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 5) << | ||
142 | ISPHIST_CNT_SHIFT_SHIFT; | ||
143 | break; | ||
144 | } | ||
145 | |||
146 | hist_reset_mem(hist); | ||
147 | |||
148 | isp_reg_writel(isp, cnt, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT); | ||
149 | isp_reg_writel(isp, wb_gain, OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN); | ||
150 | isp_reg_writel(isp, reg_hor[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_HORZ); | ||
151 | isp_reg_writel(isp, reg_ver[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_VERT); | ||
152 | isp_reg_writel(isp, reg_hor[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_HORZ); | ||
153 | isp_reg_writel(isp, reg_ver[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_VERT); | ||
154 | isp_reg_writel(isp, reg_hor[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_HORZ); | ||
155 | isp_reg_writel(isp, reg_ver[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_VERT); | ||
156 | isp_reg_writel(isp, reg_hor[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_HORZ); | ||
157 | isp_reg_writel(isp, reg_ver[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_VERT); | ||
158 | |||
159 | hist->update = 0; | ||
160 | hist->config_counter += hist->inc_config; | ||
161 | hist->inc_config = 0; | ||
162 | hist->buf_size = conf->buf_size; | ||
163 | } | ||
164 | |||
165 | static void hist_enable(struct ispstat *hist, int enable) | ||
166 | { | ||
167 | if (enable) { | ||
168 | isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, | ||
169 | ISPHIST_PCR_ENABLE); | ||
170 | isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
171 | ISPCTRL_HIST_CLK_EN); | ||
172 | } else { | ||
173 | isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, | ||
174 | ISPHIST_PCR_ENABLE); | ||
175 | isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
176 | ISPCTRL_HIST_CLK_EN); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | static int hist_busy(struct ispstat *hist) | ||
181 | { | ||
182 | return isp_reg_readl(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR) | ||
183 | & ISPHIST_PCR_BUSY; | ||
184 | } | ||
185 | |||
186 | static void hist_dma_cb(int lch, u16 ch_status, void *data) | ||
187 | { | ||
188 | struct ispstat *hist = data; | ||
189 | |||
190 | if (ch_status & ~OMAP_DMA_BLOCK_IRQ) { | ||
191 | dev_dbg(hist->isp->dev, "hist: DMA error. status = 0x%04x\n", | ||
192 | ch_status); | ||
193 | omap_stop_dma(lch); | ||
194 | hist_reset_mem(hist); | ||
195 | atomic_set(&hist->buf_err, 1); | ||
196 | } | ||
197 | isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, | ||
198 | ISPHIST_CNT_CLEAR); | ||
199 | |||
200 | omap3isp_stat_dma_isr(hist); | ||
201 | if (hist->state != ISPSTAT_DISABLED) | ||
202 | omap3isp_hist_dma_done(hist->isp); | ||
203 | } | ||
204 | |||
205 | static int hist_buf_dma(struct ispstat *hist) | ||
206 | { | ||
207 | dma_addr_t dma_addr = hist->active_buf->dma_addr; | ||
208 | |||
209 | if (unlikely(!dma_addr)) { | ||
210 | dev_dbg(hist->isp->dev, "hist: invalid DMA buffer address\n"); | ||
211 | hist_reset_mem(hist); | ||
212 | return STAT_NO_BUF; | ||
213 | } | ||
214 | |||
215 | isp_reg_writel(hist->isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); | ||
216 | isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, | ||
217 | ISPHIST_CNT_CLEAR); | ||
218 | omap3isp_flush(hist->isp); | ||
219 | hist->dma_config.dst_start = dma_addr; | ||
220 | hist->dma_config.elem_count = hist->buf_size / sizeof(u32); | ||
221 | omap_set_dma_params(hist->dma_ch, &hist->dma_config); | ||
222 | |||
223 | omap_start_dma(hist->dma_ch); | ||
224 | |||
225 | return STAT_BUF_WAITING_DMA; | ||
226 | } | ||
227 | |||
228 | static int hist_buf_pio(struct ispstat *hist) | ||
229 | { | ||
230 | struct isp_device *isp = hist->isp; | ||
231 | u32 *buf = hist->active_buf->virt_addr; | ||
232 | unsigned int i; | ||
233 | |||
234 | if (!buf) { | ||
235 | dev_dbg(isp->dev, "hist: invalid PIO buffer address\n"); | ||
236 | hist_reset_mem(hist); | ||
237 | return STAT_NO_BUF; | ||
238 | } | ||
239 | |||
240 | isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); | ||
241 | |||
242 | /* | ||
243 | * By setting it, the histogram internal buffer is being cleared at the | ||
244 | * same time it's being read. This bit must be cleared just after all | ||
245 | * data is acquired. | ||
246 | */ | ||
247 | isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); | ||
248 | |||
249 | /* | ||
250 | * We'll read 4 times a 4-bytes-word at each iteration for | ||
251 | * optimization. It avoids 3/4 of the jumps. We also know buf_size is | ||
252 | * divisible by 16. | ||
253 | */ | ||
254 | for (i = hist->buf_size / 16; i > 0; i--) { | ||
255 | *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
256 | *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
257 | *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
258 | *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
259 | } | ||
260 | isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, | ||
261 | ISPHIST_CNT_CLEAR); | ||
262 | |||
263 | return STAT_BUF_DONE; | ||
264 | } | ||
265 | |||
266 | /* | ||
267 | * hist_buf_process - Callback from ISP driver for HIST interrupt. | ||
268 | */ | ||
269 | static int hist_buf_process(struct ispstat *hist) | ||
270 | { | ||
271 | struct omap3isp_hist_config *user_cfg = hist->priv; | ||
272 | int ret; | ||
273 | |||
274 | if (atomic_read(&hist->buf_err) || hist->state != ISPSTAT_ENABLED) { | ||
275 | hist_reset_mem(hist); | ||
276 | return STAT_NO_BUF; | ||
277 | } | ||
278 | |||
279 | if (--(hist->wait_acc_frames)) | ||
280 | return STAT_NO_BUF; | ||
281 | |||
282 | if (HIST_USING_DMA(hist)) | ||
283 | ret = hist_buf_dma(hist); | ||
284 | else | ||
285 | ret = hist_buf_pio(hist); | ||
286 | |||
287 | hist->wait_acc_frames = user_cfg->num_acc_frames; | ||
288 | |||
289 | return ret; | ||
290 | } | ||
291 | |||
292 | static u32 hist_get_buf_size(struct omap3isp_hist_config *conf) | ||
293 | { | ||
294 | return OMAP3ISP_HIST_MEM_SIZE_BINS(conf->hist_bins) * conf->num_regions; | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * hist_validate_params - Helper function to check user given params. | ||
299 | * @user_cfg: Pointer to user configuration structure. | ||
300 | * | ||
301 | * Returns 0 on success configuration. | ||
302 | */ | ||
303 | static int hist_validate_params(struct ispstat *hist, void *new_conf) | ||
304 | { | ||
305 | struct omap3isp_hist_config *user_cfg = new_conf; | ||
306 | int c; | ||
307 | u32 buf_size; | ||
308 | |||
309 | if (user_cfg->cfa > OMAP3ISP_HIST_CFA_FOVEONX3) | ||
310 | return -EINVAL; | ||
311 | |||
312 | /* Regions size and position */ | ||
313 | |||
314 | if ((user_cfg->num_regions < OMAP3ISP_HIST_MIN_REGIONS) || | ||
315 | (user_cfg->num_regions > OMAP3ISP_HIST_MAX_REGIONS)) | ||
316 | return -EINVAL; | ||
317 | |||
318 | /* Regions */ | ||
319 | for (c = 0; c < user_cfg->num_regions; c++) { | ||
320 | if (user_cfg->region[c].h_start & ~ISPHIST_REG_START_END_MASK) | ||
321 | return -EINVAL; | ||
322 | if (user_cfg->region[c].h_end & ~ISPHIST_REG_START_END_MASK) | ||
323 | return -EINVAL; | ||
324 | if (user_cfg->region[c].v_start & ~ISPHIST_REG_START_END_MASK) | ||
325 | return -EINVAL; | ||
326 | if (user_cfg->region[c].v_end & ~ISPHIST_REG_START_END_MASK) | ||
327 | return -EINVAL; | ||
328 | if (user_cfg->region[c].h_start > user_cfg->region[c].h_end) | ||
329 | return -EINVAL; | ||
330 | if (user_cfg->region[c].v_start > user_cfg->region[c].v_end) | ||
331 | return -EINVAL; | ||
332 | } | ||
333 | |||
334 | switch (user_cfg->num_regions) { | ||
335 | case 1: | ||
336 | if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_256) | ||
337 | return -EINVAL; | ||
338 | break; | ||
339 | case 2: | ||
340 | if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_128) | ||
341 | return -EINVAL; | ||
342 | break; | ||
343 | default: /* 3 or 4 */ | ||
344 | if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_64) | ||
345 | return -EINVAL; | ||
346 | break; | ||
347 | } | ||
348 | |||
349 | buf_size = hist_get_buf_size(user_cfg); | ||
350 | if (buf_size > user_cfg->buf_size) | ||
351 | /* User's buf_size request wasn't enoght */ | ||
352 | user_cfg->buf_size = buf_size; | ||
353 | else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE) | ||
354 | user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE; | ||
355 | |||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | static int hist_comp_params(struct ispstat *hist, | ||
360 | struct omap3isp_hist_config *user_cfg) | ||
361 | { | ||
362 | struct omap3isp_hist_config *cur_cfg = hist->priv; | ||
363 | int c; | ||
364 | |||
365 | if (cur_cfg->cfa != user_cfg->cfa) | ||
366 | return 1; | ||
367 | |||
368 | if (cur_cfg->num_acc_frames != user_cfg->num_acc_frames) | ||
369 | return 1; | ||
370 | |||
371 | if (cur_cfg->hist_bins != user_cfg->hist_bins) | ||
372 | return 1; | ||
373 | |||
374 | for (c = 0; c < OMAP3ISP_HIST_MAX_WG; c++) { | ||
375 | if (c == 3 && user_cfg->cfa == OMAP3ISP_HIST_CFA_FOVEONX3) | ||
376 | break; | ||
377 | else if (cur_cfg->wg[c] != user_cfg->wg[c]) | ||
378 | return 1; | ||
379 | } | ||
380 | |||
381 | if (cur_cfg->num_regions != user_cfg->num_regions) | ||
382 | return 1; | ||
383 | |||
384 | /* Regions */ | ||
385 | for (c = 0; c < user_cfg->num_regions; c++) { | ||
386 | if (cur_cfg->region[c].h_start != user_cfg->region[c].h_start) | ||
387 | return 1; | ||
388 | if (cur_cfg->region[c].h_end != user_cfg->region[c].h_end) | ||
389 | return 1; | ||
390 | if (cur_cfg->region[c].v_start != user_cfg->region[c].v_start) | ||
391 | return 1; | ||
392 | if (cur_cfg->region[c].v_end != user_cfg->region[c].v_end) | ||
393 | return 1; | ||
394 | } | ||
395 | |||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * hist_update_params - Helper function to check and store user given params. | ||
401 | * @new_conf: Pointer to user configuration structure. | ||
402 | */ | ||
403 | static void hist_set_params(struct ispstat *hist, void *new_conf) | ||
404 | { | ||
405 | struct omap3isp_hist_config *user_cfg = new_conf; | ||
406 | struct omap3isp_hist_config *cur_cfg = hist->priv; | ||
407 | |||
408 | if (!hist->configured || hist_comp_params(hist, user_cfg)) { | ||
409 | memcpy(cur_cfg, user_cfg, sizeof(*user_cfg)); | ||
410 | if (user_cfg->num_acc_frames == 0) | ||
411 | user_cfg->num_acc_frames = 1; | ||
412 | hist->inc_config++; | ||
413 | hist->update = 1; | ||
414 | /* | ||
415 | * User might be asked for a bigger buffer than necessary for | ||
416 | * this configuration. In order to return the right amount of | ||
417 | * data during buffer request, let's calculate the size here | ||
418 | * instead of stick with user_cfg->buf_size. | ||
419 | */ | ||
420 | cur_cfg->buf_size = hist_get_buf_size(cur_cfg); | ||
421 | |||
422 | } | ||
423 | } | ||
424 | |||
425 | static long hist_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
426 | { | ||
427 | struct ispstat *stat = v4l2_get_subdevdata(sd); | ||
428 | |||
429 | switch (cmd) { | ||
430 | case VIDIOC_OMAP3ISP_HIST_CFG: | ||
431 | return omap3isp_stat_config(stat, arg); | ||
432 | case VIDIOC_OMAP3ISP_STAT_REQ: | ||
433 | return omap3isp_stat_request_statistics(stat, arg); | ||
434 | case VIDIOC_OMAP3ISP_STAT_EN: { | ||
435 | int *en = arg; | ||
436 | return omap3isp_stat_enable(stat, !!*en); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | return -ENOIOCTLCMD; | ||
441 | |||
442 | } | ||
443 | |||
444 | static const struct ispstat_ops hist_ops = { | ||
445 | .validate_params = hist_validate_params, | ||
446 | .set_params = hist_set_params, | ||
447 | .setup_regs = hist_setup_regs, | ||
448 | .enable = hist_enable, | ||
449 | .busy = hist_busy, | ||
450 | .buf_process = hist_buf_process, | ||
451 | }; | ||
452 | |||
453 | static const struct v4l2_subdev_core_ops hist_subdev_core_ops = { | ||
454 | .ioctl = hist_ioctl, | ||
455 | .subscribe_event = omap3isp_stat_subscribe_event, | ||
456 | .unsubscribe_event = omap3isp_stat_unsubscribe_event, | ||
457 | }; | ||
458 | |||
459 | static const struct v4l2_subdev_video_ops hist_subdev_video_ops = { | ||
460 | .s_stream = omap3isp_stat_s_stream, | ||
461 | }; | ||
462 | |||
463 | static const struct v4l2_subdev_ops hist_subdev_ops = { | ||
464 | .core = &hist_subdev_core_ops, | ||
465 | .video = &hist_subdev_video_ops, | ||
466 | }; | ||
467 | |||
468 | /* | ||
469 | * omap3isp_hist_init - Module Initialization. | ||
470 | */ | ||
471 | int omap3isp_hist_init(struct isp_device *isp) | ||
472 | { | ||
473 | struct ispstat *hist = &isp->isp_hist; | ||
474 | struct omap3isp_hist_config *hist_cfg; | ||
475 | int ret = -1; | ||
476 | |||
477 | hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL); | ||
478 | if (hist_cfg == NULL) | ||
479 | return -ENOMEM; | ||
480 | |||
481 | memset(hist, 0, sizeof(*hist)); | ||
482 | if (HIST_CONFIG_DMA) | ||
483 | ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST", | ||
484 | hist_dma_cb, hist, &hist->dma_ch); | ||
485 | if (ret) { | ||
486 | if (HIST_CONFIG_DMA) | ||
487 | dev_warn(isp->dev, "hist: DMA request channel failed. " | ||
488 | "Using PIO only.\n"); | ||
489 | hist->dma_ch = -1; | ||
490 | } else { | ||
491 | dev_dbg(isp->dev, "hist: DMA channel = %d\n", hist->dma_ch); | ||
492 | hist_dma_config(hist); | ||
493 | omap_enable_dma_irq(hist->dma_ch, OMAP_DMA_BLOCK_IRQ); | ||
494 | } | ||
495 | |||
496 | hist->ops = &hist_ops; | ||
497 | hist->priv = hist_cfg; | ||
498 | hist->event_type = V4L2_EVENT_OMAP3ISP_HIST; | ||
499 | hist->isp = isp; | ||
500 | |||
501 | ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops); | ||
502 | if (ret) { | ||
503 | kfree(hist_cfg); | ||
504 | if (HIST_USING_DMA(hist)) | ||
505 | omap_free_dma(hist->dma_ch); | ||
506 | } | ||
507 | |||
508 | return ret; | ||
509 | } | ||
510 | |||
511 | /* | ||
512 | * omap3isp_hist_cleanup - Module cleanup. | ||
513 | */ | ||
514 | void omap3isp_hist_cleanup(struct isp_device *isp) | ||
515 | { | ||
516 | if (HIST_USING_DMA(&isp->isp_hist)) | ||
517 | omap_free_dma(isp->isp_hist.dma_ch); | ||
518 | kfree(isp->isp_hist.priv); | ||
519 | omap3isp_stat_free(&isp->isp_hist); | ||
520 | } | ||
diff --git a/drivers/media/video/omap3isp/isphist.h b/drivers/media/video/omap3isp/isphist.h new file mode 100644 index 00000000000..0b2a38ec94c --- /dev/null +++ b/drivers/media/video/omap3isp/isphist.h | |||
@@ -0,0 +1,40 @@ | |||
1 | /* | ||
2 | * isphist.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - Histogram module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: David Cohen <dacohen@gmail.com> | ||
10 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
11 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License version 2 as | ||
15 | * published by the Free Software Foundation. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, but | ||
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20 | * General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
25 | * 02110-1301 USA | ||
26 | */ | ||
27 | |||
28 | #ifndef OMAP3_ISP_HIST_H | ||
29 | #define OMAP3_ISP_HIST_H | ||
30 | |||
31 | #include <linux/omap3isp.h> | ||
32 | |||
33 | #define ISPHIST_IN_BIT_WIDTH_CCDC 10 | ||
34 | |||
35 | struct isp_device; | ||
36 | |||
37 | int omap3isp_hist_init(struct isp_device *isp); | ||
38 | void omap3isp_hist_cleanup(struct isp_device *isp); | ||
39 | |||
40 | #endif /* OMAP3_ISP_HIST */ | ||
diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c new file mode 100644 index 00000000000..aba537af87e --- /dev/null +++ b/drivers/media/video/omap3isp/isppreview.c | |||
@@ -0,0 +1,2113 @@ | |||
1 | /* | ||
2 | * isppreview.c | ||
3 | * | ||
4 | * TI OMAP3 ISP driver - Preview module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #include <linux/device.h> | ||
28 | #include <linux/mm.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/mutex.h> | ||
31 | #include <linux/uaccess.h> | ||
32 | |||
33 | #include "isp.h" | ||
34 | #include "ispreg.h" | ||
35 | #include "isppreview.h" | ||
36 | |||
37 | /* Default values in Office Fluorescent Light for RGBtoRGB Blending */ | ||
38 | static struct omap3isp_prev_rgbtorgb flr_rgb2rgb = { | ||
39 | { /* RGB-RGB Matrix */ | ||
40 | {0x01E2, 0x0F30, 0x0FEE}, | ||
41 | {0x0F9B, 0x01AC, 0x0FB9}, | ||
42 | {0x0FE0, 0x0EC0, 0x0260} | ||
43 | }, /* RGB Offset */ | ||
44 | {0x0000, 0x0000, 0x0000} | ||
45 | }; | ||
46 | |||
47 | /* Default values in Office Fluorescent Light for RGB to YUV Conversion*/ | ||
48 | static struct omap3isp_prev_csc flr_prev_csc = { | ||
49 | { /* CSC Coef Matrix */ | ||
50 | {66, 129, 25}, | ||
51 | {-38, -75, 112}, | ||
52 | {112, -94 , -18} | ||
53 | }, /* CSC Offset */ | ||
54 | {0x0, 0x0, 0x0} | ||
55 | }; | ||
56 | |||
57 | /* Default values in Office Fluorescent Light for CFA Gradient*/ | ||
58 | #define FLR_CFA_GRADTHRS_HORZ 0x28 | ||
59 | #define FLR_CFA_GRADTHRS_VERT 0x28 | ||
60 | |||
61 | /* Default values in Office Fluorescent Light for Chroma Suppression*/ | ||
62 | #define FLR_CSUP_GAIN 0x0D | ||
63 | #define FLR_CSUP_THRES 0xEB | ||
64 | |||
65 | /* Default values in Office Fluorescent Light for Noise Filter*/ | ||
66 | #define FLR_NF_STRGTH 0x03 | ||
67 | |||
68 | /* Default values for White Balance */ | ||
69 | #define FLR_WBAL_DGAIN 0x100 | ||
70 | #define FLR_WBAL_COEF 0x20 | ||
71 | |||
72 | /* Default values in Office Fluorescent Light for Black Adjustment*/ | ||
73 | #define FLR_BLKADJ_BLUE 0x0 | ||
74 | #define FLR_BLKADJ_GREEN 0x0 | ||
75 | #define FLR_BLKADJ_RED 0x0 | ||
76 | |||
77 | #define DEF_DETECT_CORRECT_VAL 0xe | ||
78 | |||
79 | #define PREV_MIN_WIDTH 64 | ||
80 | #define PREV_MIN_HEIGHT 8 | ||
81 | #define PREV_MAX_HEIGHT 16384 | ||
82 | |||
83 | /* | ||
84 | * Coeficient Tables for the submodules in Preview. | ||
85 | * Array is initialised with the values from.the tables text file. | ||
86 | */ | ||
87 | |||
88 | /* | ||
89 | * CFA Filter Coefficient Table | ||
90 | * | ||
91 | */ | ||
92 | static u32 cfa_coef_table[] = { | ||
93 | #include "cfa_coef_table.h" | ||
94 | }; | ||
95 | |||
96 | /* | ||
97 | * Default Gamma Correction Table - All components | ||
98 | */ | ||
99 | static u32 gamma_table[] = { | ||
100 | #include "gamma_table.h" | ||
101 | }; | ||
102 | |||
103 | /* | ||
104 | * Noise Filter Threshold table | ||
105 | */ | ||
106 | static u32 noise_filter_table[] = { | ||
107 | #include "noise_filter_table.h" | ||
108 | }; | ||
109 | |||
110 | /* | ||
111 | * Luminance Enhancement Table | ||
112 | */ | ||
113 | static u32 luma_enhance_table[] = { | ||
114 | #include "luma_enhance_table.h" | ||
115 | }; | ||
116 | |||
117 | /* | ||
118 | * preview_enable_invalaw - Enable/Disable Inverse A-Law module in Preview. | ||
119 | * @enable: 1 - Reverse the A-Law done in CCDC. | ||
120 | */ | ||
121 | static void | ||
122 | preview_enable_invalaw(struct isp_prev_device *prev, u8 enable) | ||
123 | { | ||
124 | struct isp_device *isp = to_isp_device(prev); | ||
125 | |||
126 | if (enable) | ||
127 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
128 | ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW); | ||
129 | else | ||
130 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
131 | ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW); | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * preview_enable_drkframe_capture - Enable/Disable of the darkframe capture. | ||
136 | * @prev - | ||
137 | * @enable: 1 - Enable, 0 - Disable | ||
138 | * | ||
139 | * NOTE: PRV_WSDR_ADDR and PRV_WADD_OFFSET must be set also | ||
140 | * The process is applied for each captured frame. | ||
141 | */ | ||
142 | static void | ||
143 | preview_enable_drkframe_capture(struct isp_prev_device *prev, u8 enable) | ||
144 | { | ||
145 | struct isp_device *isp = to_isp_device(prev); | ||
146 | |||
147 | if (enable) | ||
148 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
149 | ISPPRV_PCR_DRKFCAP); | ||
150 | else | ||
151 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
152 | ISPPRV_PCR_DRKFCAP); | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * preview_enable_drkframe - Enable/Disable of the darkframe subtract. | ||
157 | * @enable: 1 - Acquires memory bandwidth since the pixels in each frame is | ||
158 | * subtracted with the pixels in the current frame. | ||
159 | * | ||
160 | * The process is applied for each captured frame. | ||
161 | */ | ||
162 | static void | ||
163 | preview_enable_drkframe(struct isp_prev_device *prev, u8 enable) | ||
164 | { | ||
165 | struct isp_device *isp = to_isp_device(prev); | ||
166 | |||
167 | if (enable) | ||
168 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
169 | ISPPRV_PCR_DRKFEN); | ||
170 | else | ||
171 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
172 | ISPPRV_PCR_DRKFEN); | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * preview_config_drkf_shadcomp - Configures shift value in shading comp. | ||
177 | * @scomp_shtval: 3bit value of shift used in shading compensation. | ||
178 | */ | ||
179 | static void | ||
180 | preview_config_drkf_shadcomp(struct isp_prev_device *prev, | ||
181 | const void *scomp_shtval) | ||
182 | { | ||
183 | struct isp_device *isp = to_isp_device(prev); | ||
184 | const u32 *shtval = scomp_shtval; | ||
185 | |||
186 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
187 | ISPPRV_PCR_SCOMP_SFT_MASK, | ||
188 | *shtval << ISPPRV_PCR_SCOMP_SFT_SHIFT); | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * preview_enable_hmed - Enables/Disables of the Horizontal Median Filter. | ||
193 | * @enable: 1 - Enables Horizontal Median Filter. | ||
194 | */ | ||
195 | static void | ||
196 | preview_enable_hmed(struct isp_prev_device *prev, u8 enable) | ||
197 | { | ||
198 | struct isp_device *isp = to_isp_device(prev); | ||
199 | |||
200 | if (enable) | ||
201 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
202 | ISPPRV_PCR_HMEDEN); | ||
203 | else | ||
204 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
205 | ISPPRV_PCR_HMEDEN); | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * preview_config_hmed - Configures the Horizontal Median Filter. | ||
210 | * @prev_hmed: Structure containing the odd and even distance between the | ||
211 | * pixels in the image along with the filter threshold. | ||
212 | */ | ||
213 | static void | ||
214 | preview_config_hmed(struct isp_prev_device *prev, const void *prev_hmed) | ||
215 | { | ||
216 | struct isp_device *isp = to_isp_device(prev); | ||
217 | const struct omap3isp_prev_hmed *hmed = prev_hmed; | ||
218 | |||
219 | isp_reg_writel(isp, (hmed->odddist == 1 ? 0 : ISPPRV_HMED_ODDDIST) | | ||
220 | (hmed->evendist == 1 ? 0 : ISPPRV_HMED_EVENDIST) | | ||
221 | (hmed->thres << ISPPRV_HMED_THRESHOLD_SHIFT), | ||
222 | OMAP3_ISP_IOMEM_PREV, ISPPRV_HMED); | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | * preview_config_noisefilter - Configures the Noise Filter. | ||
227 | * @prev_nf: Structure containing the noisefilter table, strength to be used | ||
228 | * for the noise filter and the defect correction enable flag. | ||
229 | */ | ||
230 | static void | ||
231 | preview_config_noisefilter(struct isp_prev_device *prev, const void *prev_nf) | ||
232 | { | ||
233 | struct isp_device *isp = to_isp_device(prev); | ||
234 | const struct omap3isp_prev_nf *nf = prev_nf; | ||
235 | unsigned int i; | ||
236 | |||
237 | isp_reg_writel(isp, nf->spread, OMAP3_ISP_IOMEM_PREV, ISPPRV_NF); | ||
238 | isp_reg_writel(isp, ISPPRV_NF_TABLE_ADDR, | ||
239 | OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); | ||
240 | for (i = 0; i < OMAP3ISP_PREV_NF_TBL_SIZE; i++) { | ||
241 | isp_reg_writel(isp, nf->table[i], | ||
242 | OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * preview_config_dcor - Configures the defect correction | ||
248 | * @prev_dcor: Structure containing the defect correct thresholds | ||
249 | */ | ||
250 | static void | ||
251 | preview_config_dcor(struct isp_prev_device *prev, const void *prev_dcor) | ||
252 | { | ||
253 | struct isp_device *isp = to_isp_device(prev); | ||
254 | const struct omap3isp_prev_dcor *dcor = prev_dcor; | ||
255 | |||
256 | isp_reg_writel(isp, dcor->detect_correct[0], | ||
257 | OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR0); | ||
258 | isp_reg_writel(isp, dcor->detect_correct[1], | ||
259 | OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR1); | ||
260 | isp_reg_writel(isp, dcor->detect_correct[2], | ||
261 | OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR2); | ||
262 | isp_reg_writel(isp, dcor->detect_correct[3], | ||
263 | OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR3); | ||
264 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
265 | ISPPRV_PCR_DCCOUP, | ||
266 | dcor->couplet_mode_en ? ISPPRV_PCR_DCCOUP : 0); | ||
267 | } | ||
268 | |||
269 | /* | ||
270 | * preview_config_cfa - Configures the CFA Interpolation parameters. | ||
271 | * @prev_cfa: Structure containing the CFA interpolation table, CFA format | ||
272 | * in the image, vertical and horizontal gradient threshold. | ||
273 | */ | ||
274 | static void | ||
275 | preview_config_cfa(struct isp_prev_device *prev, const void *prev_cfa) | ||
276 | { | ||
277 | struct isp_device *isp = to_isp_device(prev); | ||
278 | const struct omap3isp_prev_cfa *cfa = prev_cfa; | ||
279 | unsigned int i; | ||
280 | |||
281 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
282 | ISPPRV_PCR_CFAFMT_MASK, | ||
283 | cfa->format << ISPPRV_PCR_CFAFMT_SHIFT); | ||
284 | |||
285 | isp_reg_writel(isp, | ||
286 | (cfa->gradthrs_vert << ISPPRV_CFA_GRADTH_VER_SHIFT) | | ||
287 | (cfa->gradthrs_horz << ISPPRV_CFA_GRADTH_HOR_SHIFT), | ||
288 | OMAP3_ISP_IOMEM_PREV, ISPPRV_CFA); | ||
289 | |||
290 | isp_reg_writel(isp, ISPPRV_CFA_TABLE_ADDR, | ||
291 | OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); | ||
292 | |||
293 | for (i = 0; i < OMAP3ISP_PREV_CFA_TBL_SIZE; i++) { | ||
294 | isp_reg_writel(isp, cfa->table[i], | ||
295 | OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); | ||
296 | } | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * preview_config_gammacorrn - Configures the Gamma Correction table values | ||
301 | * @gtable: Structure containing the table for red, blue, green gamma table. | ||
302 | */ | ||
303 | static void | ||
304 | preview_config_gammacorrn(struct isp_prev_device *prev, const void *gtable) | ||
305 | { | ||
306 | struct isp_device *isp = to_isp_device(prev); | ||
307 | const struct omap3isp_prev_gtables *gt = gtable; | ||
308 | unsigned int i; | ||
309 | |||
310 | isp_reg_writel(isp, ISPPRV_REDGAMMA_TABLE_ADDR, | ||
311 | OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); | ||
312 | for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++) | ||
313 | isp_reg_writel(isp, gt->red[i], OMAP3_ISP_IOMEM_PREV, | ||
314 | ISPPRV_SET_TBL_DATA); | ||
315 | |||
316 | isp_reg_writel(isp, ISPPRV_GREENGAMMA_TABLE_ADDR, | ||
317 | OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); | ||
318 | for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++) | ||
319 | isp_reg_writel(isp, gt->green[i], OMAP3_ISP_IOMEM_PREV, | ||
320 | ISPPRV_SET_TBL_DATA); | ||
321 | |||
322 | isp_reg_writel(isp, ISPPRV_BLUEGAMMA_TABLE_ADDR, | ||
323 | OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); | ||
324 | for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++) | ||
325 | isp_reg_writel(isp, gt->blue[i], OMAP3_ISP_IOMEM_PREV, | ||
326 | ISPPRV_SET_TBL_DATA); | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * preview_config_luma_enhancement - Sets the Luminance Enhancement table. | ||
331 | * @ytable: Structure containing the table for Luminance Enhancement table. | ||
332 | */ | ||
333 | static void | ||
334 | preview_config_luma_enhancement(struct isp_prev_device *prev, | ||
335 | const void *ytable) | ||
336 | { | ||
337 | struct isp_device *isp = to_isp_device(prev); | ||
338 | const struct omap3isp_prev_luma *yt = ytable; | ||
339 | unsigned int i; | ||
340 | |||
341 | isp_reg_writel(isp, ISPPRV_YENH_TABLE_ADDR, | ||
342 | OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); | ||
343 | for (i = 0; i < OMAP3ISP_PREV_YENH_TBL_SIZE; i++) { | ||
344 | isp_reg_writel(isp, yt->table[i], | ||
345 | OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | /* | ||
350 | * preview_config_chroma_suppression - Configures the Chroma Suppression. | ||
351 | * @csup: Structure containing the threshold value for suppression | ||
352 | * and the hypass filter enable flag. | ||
353 | */ | ||
354 | static void | ||
355 | preview_config_chroma_suppression(struct isp_prev_device *prev, | ||
356 | const void *csup) | ||
357 | { | ||
358 | struct isp_device *isp = to_isp_device(prev); | ||
359 | const struct omap3isp_prev_csup *cs = csup; | ||
360 | |||
361 | isp_reg_writel(isp, | ||
362 | cs->gain | (cs->thres << ISPPRV_CSUP_THRES_SHIFT) | | ||
363 | (cs->hypf_en << ISPPRV_CSUP_HPYF_SHIFT), | ||
364 | OMAP3_ISP_IOMEM_PREV, ISPPRV_CSUP); | ||
365 | } | ||
366 | |||
367 | /* | ||
368 | * preview_enable_noisefilter - Enables/Disables the Noise Filter. | ||
369 | * @enable: 1 - Enables the Noise Filter. | ||
370 | */ | ||
371 | static void | ||
372 | preview_enable_noisefilter(struct isp_prev_device *prev, u8 enable) | ||
373 | { | ||
374 | struct isp_device *isp = to_isp_device(prev); | ||
375 | |||
376 | if (enable) | ||
377 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
378 | ISPPRV_PCR_NFEN); | ||
379 | else | ||
380 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
381 | ISPPRV_PCR_NFEN); | ||
382 | } | ||
383 | |||
384 | /* | ||
385 | * preview_enable_dcor - Enables/Disables the defect correction. | ||
386 | * @enable: 1 - Enables the defect correction. | ||
387 | */ | ||
388 | static void | ||
389 | preview_enable_dcor(struct isp_prev_device *prev, u8 enable) | ||
390 | { | ||
391 | struct isp_device *isp = to_isp_device(prev); | ||
392 | |||
393 | if (enable) | ||
394 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
395 | ISPPRV_PCR_DCOREN); | ||
396 | else | ||
397 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
398 | ISPPRV_PCR_DCOREN); | ||
399 | } | ||
400 | |||
401 | /* | ||
402 | * preview_enable_cfa - Enable/Disable the CFA Interpolation. | ||
403 | * @enable: 1 - Enables the CFA. | ||
404 | */ | ||
405 | static void | ||
406 | preview_enable_cfa(struct isp_prev_device *prev, u8 enable) | ||
407 | { | ||
408 | struct isp_device *isp = to_isp_device(prev); | ||
409 | |||
410 | if (enable) | ||
411 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
412 | ISPPRV_PCR_CFAEN); | ||
413 | else | ||
414 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
415 | ISPPRV_PCR_CFAEN); | ||
416 | } | ||
417 | |||
418 | /* | ||
419 | * preview_enable_gammabypass - Enables/Disables the GammaByPass | ||
420 | * @enable: 1 - Bypasses Gamma - 10bit input is cropped to 8MSB. | ||
421 | * 0 - Goes through Gamma Correction. input and output is 10bit. | ||
422 | */ | ||
423 | static void | ||
424 | preview_enable_gammabypass(struct isp_prev_device *prev, u8 enable) | ||
425 | { | ||
426 | struct isp_device *isp = to_isp_device(prev); | ||
427 | |||
428 | if (enable) | ||
429 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
430 | ISPPRV_PCR_GAMMA_BYPASS); | ||
431 | else | ||
432 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
433 | ISPPRV_PCR_GAMMA_BYPASS); | ||
434 | } | ||
435 | |||
436 | /* | ||
437 | * preview_enable_luma_enhancement - Enables/Disables Luminance Enhancement | ||
438 | * @enable: 1 - Enable the Luminance Enhancement. | ||
439 | */ | ||
440 | static void | ||
441 | preview_enable_luma_enhancement(struct isp_prev_device *prev, u8 enable) | ||
442 | { | ||
443 | struct isp_device *isp = to_isp_device(prev); | ||
444 | |||
445 | if (enable) | ||
446 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
447 | ISPPRV_PCR_YNENHEN); | ||
448 | else | ||
449 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
450 | ISPPRV_PCR_YNENHEN); | ||
451 | } | ||
452 | |||
453 | /* | ||
454 | * preview_enable_chroma_suppression - Enables/Disables Chrominance Suppr. | ||
455 | * @enable: 1 - Enable the Chrominance Suppression. | ||
456 | */ | ||
457 | static void | ||
458 | preview_enable_chroma_suppression(struct isp_prev_device *prev, u8 enable) | ||
459 | { | ||
460 | struct isp_device *isp = to_isp_device(prev); | ||
461 | |||
462 | if (enable) | ||
463 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
464 | ISPPRV_PCR_SUPEN); | ||
465 | else | ||
466 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
467 | ISPPRV_PCR_SUPEN); | ||
468 | } | ||
469 | |||
470 | /* | ||
471 | * preview_config_whitebalance - Configures the White Balance parameters. | ||
472 | * @prev_wbal: Structure containing the digital gain and white balance | ||
473 | * coefficient. | ||
474 | * | ||
475 | * Coefficient matrix always with default values. | ||
476 | */ | ||
477 | static void | ||
478 | preview_config_whitebalance(struct isp_prev_device *prev, const void *prev_wbal) | ||
479 | { | ||
480 | struct isp_device *isp = to_isp_device(prev); | ||
481 | const struct omap3isp_prev_wbal *wbal = prev_wbal; | ||
482 | u32 val; | ||
483 | |||
484 | isp_reg_writel(isp, wbal->dgain, OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN); | ||
485 | |||
486 | val = wbal->coef0 << ISPPRV_WBGAIN_COEF0_SHIFT; | ||
487 | val |= wbal->coef1 << ISPPRV_WBGAIN_COEF1_SHIFT; | ||
488 | val |= wbal->coef2 << ISPPRV_WBGAIN_COEF2_SHIFT; | ||
489 | val |= wbal->coef3 << ISPPRV_WBGAIN_COEF3_SHIFT; | ||
490 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN); | ||
491 | |||
492 | isp_reg_writel(isp, | ||
493 | ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_0_SHIFT | | ||
494 | ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_1_SHIFT | | ||
495 | ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_2_SHIFT | | ||
496 | ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_3_SHIFT | | ||
497 | ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_0_SHIFT | | ||
498 | ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_1_SHIFT | | ||
499 | ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_2_SHIFT | | ||
500 | ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_3_SHIFT | | ||
501 | ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_0_SHIFT | | ||
502 | ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_1_SHIFT | | ||
503 | ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_2_SHIFT | | ||
504 | ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_3_SHIFT | | ||
505 | ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_0_SHIFT | | ||
506 | ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_1_SHIFT | | ||
507 | ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_2_SHIFT | | ||
508 | ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_3_SHIFT, | ||
509 | OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL); | ||
510 | } | ||
511 | |||
512 | /* | ||
513 | * preview_config_blkadj - Configures the Black Adjustment parameters. | ||
514 | * @prev_blkadj: Structure containing the black adjustment towards red, green, | ||
515 | * blue. | ||
516 | */ | ||
517 | static void | ||
518 | preview_config_blkadj(struct isp_prev_device *prev, const void *prev_blkadj) | ||
519 | { | ||
520 | struct isp_device *isp = to_isp_device(prev); | ||
521 | const struct omap3isp_prev_blkadj *blkadj = prev_blkadj; | ||
522 | |||
523 | isp_reg_writel(isp, (blkadj->blue << ISPPRV_BLKADJOFF_B_SHIFT) | | ||
524 | (blkadj->green << ISPPRV_BLKADJOFF_G_SHIFT) | | ||
525 | (blkadj->red << ISPPRV_BLKADJOFF_R_SHIFT), | ||
526 | OMAP3_ISP_IOMEM_PREV, ISPPRV_BLKADJOFF); | ||
527 | } | ||
528 | |||
529 | /* | ||
530 | * preview_config_rgb_blending - Configures the RGB-RGB Blending matrix. | ||
531 | * @rgb2rgb: Structure containing the rgb to rgb blending matrix and the rgb | ||
532 | * offset. | ||
533 | */ | ||
534 | static void | ||
535 | preview_config_rgb_blending(struct isp_prev_device *prev, const void *rgb2rgb) | ||
536 | { | ||
537 | struct isp_device *isp = to_isp_device(prev); | ||
538 | const struct omap3isp_prev_rgbtorgb *rgbrgb = rgb2rgb; | ||
539 | u32 val; | ||
540 | |||
541 | val = (rgbrgb->matrix[0][0] & 0xfff) << ISPPRV_RGB_MAT1_MTX_RR_SHIFT; | ||
542 | val |= (rgbrgb->matrix[0][1] & 0xfff) << ISPPRV_RGB_MAT1_MTX_GR_SHIFT; | ||
543 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT1); | ||
544 | |||
545 | val = (rgbrgb->matrix[0][2] & 0xfff) << ISPPRV_RGB_MAT2_MTX_BR_SHIFT; | ||
546 | val |= (rgbrgb->matrix[1][0] & 0xfff) << ISPPRV_RGB_MAT2_MTX_RG_SHIFT; | ||
547 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT2); | ||
548 | |||
549 | val = (rgbrgb->matrix[1][1] & 0xfff) << ISPPRV_RGB_MAT3_MTX_GG_SHIFT; | ||
550 | val |= (rgbrgb->matrix[1][2] & 0xfff) << ISPPRV_RGB_MAT3_MTX_BG_SHIFT; | ||
551 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT3); | ||
552 | |||
553 | val = (rgbrgb->matrix[2][0] & 0xfff) << ISPPRV_RGB_MAT4_MTX_RB_SHIFT; | ||
554 | val |= (rgbrgb->matrix[2][1] & 0xfff) << ISPPRV_RGB_MAT4_MTX_GB_SHIFT; | ||
555 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT4); | ||
556 | |||
557 | val = (rgbrgb->matrix[2][2] & 0xfff) << ISPPRV_RGB_MAT5_MTX_BB_SHIFT; | ||
558 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT5); | ||
559 | |||
560 | val = (rgbrgb->offset[0] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT; | ||
561 | val |= (rgbrgb->offset[1] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT; | ||
562 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF1); | ||
563 | |||
564 | val = (rgbrgb->offset[2] & 0x3ff) << ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT; | ||
565 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF2); | ||
566 | } | ||
567 | |||
568 | /* | ||
569 | * Configures the RGB-YCbYCr conversion matrix | ||
570 | * @prev_csc: Structure containing the RGB to YCbYCr matrix and the | ||
571 | * YCbCr offset. | ||
572 | */ | ||
573 | static void | ||
574 | preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc) | ||
575 | { | ||
576 | struct isp_device *isp = to_isp_device(prev); | ||
577 | const struct omap3isp_prev_csc *csc = prev_csc; | ||
578 | u32 val; | ||
579 | |||
580 | val = (csc->matrix[0][0] & 0x3ff) << ISPPRV_CSC0_RY_SHIFT; | ||
581 | val |= (csc->matrix[0][1] & 0x3ff) << ISPPRV_CSC0_GY_SHIFT; | ||
582 | val |= (csc->matrix[0][2] & 0x3ff) << ISPPRV_CSC0_BY_SHIFT; | ||
583 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC0); | ||
584 | |||
585 | val = (csc->matrix[1][0] & 0x3ff) << ISPPRV_CSC1_RCB_SHIFT; | ||
586 | val |= (csc->matrix[1][1] & 0x3ff) << ISPPRV_CSC1_GCB_SHIFT; | ||
587 | val |= (csc->matrix[1][2] & 0x3ff) << ISPPRV_CSC1_BCB_SHIFT; | ||
588 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC1); | ||
589 | |||
590 | val = (csc->matrix[2][0] & 0x3ff) << ISPPRV_CSC2_RCR_SHIFT; | ||
591 | val |= (csc->matrix[2][1] & 0x3ff) << ISPPRV_CSC2_GCR_SHIFT; | ||
592 | val |= (csc->matrix[2][2] & 0x3ff) << ISPPRV_CSC2_BCR_SHIFT; | ||
593 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC2); | ||
594 | |||
595 | val = (csc->offset[0] & 0xff) << ISPPRV_CSC_OFFSET_Y_SHIFT; | ||
596 | val |= (csc->offset[1] & 0xff) << ISPPRV_CSC_OFFSET_CB_SHIFT; | ||
597 | val |= (csc->offset[2] & 0xff) << ISPPRV_CSC_OFFSET_CR_SHIFT; | ||
598 | isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC_OFFSET); | ||
599 | } | ||
600 | |||
601 | /* | ||
602 | * preview_update_contrast - Updates the contrast. | ||
603 | * @contrast: Pointer to hold the current programmed contrast value. | ||
604 | * | ||
605 | * Value should be programmed before enabling the module. | ||
606 | */ | ||
607 | static void | ||
608 | preview_update_contrast(struct isp_prev_device *prev, u8 contrast) | ||
609 | { | ||
610 | struct prev_params *params = &prev->params; | ||
611 | |||
612 | if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) { | ||
613 | params->contrast = contrast * ISPPRV_CONTRAST_UNITS; | ||
614 | prev->update |= PREV_CONTRAST; | ||
615 | } | ||
616 | } | ||
617 | |||
618 | /* | ||
619 | * preview_config_contrast - Configures the Contrast. | ||
620 | * @params: Contrast value (u8 pointer, U8Q0 format). | ||
621 | * | ||
622 | * Value should be programmed before enabling the module. | ||
623 | */ | ||
624 | static void | ||
625 | preview_config_contrast(struct isp_prev_device *prev, const void *params) | ||
626 | { | ||
627 | struct isp_device *isp = to_isp_device(prev); | ||
628 | |||
629 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT, | ||
630 | 0xff << ISPPRV_CNT_BRT_CNT_SHIFT, | ||
631 | *(u8 *)params << ISPPRV_CNT_BRT_CNT_SHIFT); | ||
632 | } | ||
633 | |||
634 | /* | ||
635 | * preview_update_brightness - Updates the brightness in preview module. | ||
636 | * @brightness: Pointer to hold the current programmed brightness value. | ||
637 | * | ||
638 | */ | ||
639 | static void | ||
640 | preview_update_brightness(struct isp_prev_device *prev, u8 brightness) | ||
641 | { | ||
642 | struct prev_params *params = &prev->params; | ||
643 | |||
644 | if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) { | ||
645 | params->brightness = brightness * ISPPRV_BRIGHT_UNITS; | ||
646 | prev->update |= PREV_BRIGHTNESS; | ||
647 | } | ||
648 | } | ||
649 | |||
650 | /* | ||
651 | * preview_config_brightness - Configures the brightness. | ||
652 | * @params: Brightness value (u8 pointer, U8Q0 format). | ||
653 | */ | ||
654 | static void | ||
655 | preview_config_brightness(struct isp_prev_device *prev, const void *params) | ||
656 | { | ||
657 | struct isp_device *isp = to_isp_device(prev); | ||
658 | |||
659 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT, | ||
660 | 0xff << ISPPRV_CNT_BRT_BRT_SHIFT, | ||
661 | *(u8 *)params << ISPPRV_CNT_BRT_BRT_SHIFT); | ||
662 | } | ||
663 | |||
664 | /* | ||
665 | * preview_config_yc_range - Configures the max and min Y and C values. | ||
666 | * @yclimit: Structure containing the range of Y and C values. | ||
667 | */ | ||
668 | static void | ||
669 | preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit) | ||
670 | { | ||
671 | struct isp_device *isp = to_isp_device(prev); | ||
672 | const struct omap3isp_prev_yclimit *yc = yclimit; | ||
673 | |||
674 | isp_reg_writel(isp, | ||
675 | yc->maxC << ISPPRV_SETUP_YC_MAXC_SHIFT | | ||
676 | yc->maxY << ISPPRV_SETUP_YC_MAXY_SHIFT | | ||
677 | yc->minC << ISPPRV_SETUP_YC_MINC_SHIFT | | ||
678 | yc->minY << ISPPRV_SETUP_YC_MINY_SHIFT, | ||
679 | OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC); | ||
680 | } | ||
681 | |||
682 | /* preview parameters update structure */ | ||
683 | struct preview_update { | ||
684 | int cfg_bit; | ||
685 | int feature_bit; | ||
686 | void (*config)(struct isp_prev_device *, const void *); | ||
687 | void (*enable)(struct isp_prev_device *, u8); | ||
688 | }; | ||
689 | |||
690 | static struct preview_update update_attrs[] = { | ||
691 | {OMAP3ISP_PREV_LUMAENH, PREV_LUMA_ENHANCE, | ||
692 | preview_config_luma_enhancement, | ||
693 | preview_enable_luma_enhancement}, | ||
694 | {OMAP3ISP_PREV_INVALAW, PREV_INVERSE_ALAW, | ||
695 | NULL, | ||
696 | preview_enable_invalaw}, | ||
697 | {OMAP3ISP_PREV_HRZ_MED, PREV_HORZ_MEDIAN_FILTER, | ||
698 | preview_config_hmed, | ||
699 | preview_enable_hmed}, | ||
700 | {OMAP3ISP_PREV_CFA, PREV_CFA, | ||
701 | preview_config_cfa, | ||
702 | preview_enable_cfa}, | ||
703 | {OMAP3ISP_PREV_CHROMA_SUPP, PREV_CHROMA_SUPPRESS, | ||
704 | preview_config_chroma_suppression, | ||
705 | preview_enable_chroma_suppression}, | ||
706 | {OMAP3ISP_PREV_WB, PREV_WB, | ||
707 | preview_config_whitebalance, | ||
708 | NULL}, | ||
709 | {OMAP3ISP_PREV_BLKADJ, PREV_BLKADJ, | ||
710 | preview_config_blkadj, | ||
711 | NULL}, | ||
712 | {OMAP3ISP_PREV_RGB2RGB, PREV_RGB2RGB, | ||
713 | preview_config_rgb_blending, | ||
714 | NULL}, | ||
715 | {OMAP3ISP_PREV_COLOR_CONV, PREV_COLOR_CONV, | ||
716 | preview_config_rgb_to_ycbcr, | ||
717 | NULL}, | ||
718 | {OMAP3ISP_PREV_YC_LIMIT, PREV_YCLIMITS, | ||
719 | preview_config_yc_range, | ||
720 | NULL}, | ||
721 | {OMAP3ISP_PREV_DEFECT_COR, PREV_DEFECT_COR, | ||
722 | preview_config_dcor, | ||
723 | preview_enable_dcor}, | ||
724 | {OMAP3ISP_PREV_GAMMABYPASS, PREV_GAMMA_BYPASS, | ||
725 | NULL, | ||
726 | preview_enable_gammabypass}, | ||
727 | {OMAP3ISP_PREV_DRK_FRM_CAPTURE, PREV_DARK_FRAME_CAPTURE, | ||
728 | NULL, | ||
729 | preview_enable_drkframe_capture}, | ||
730 | {OMAP3ISP_PREV_DRK_FRM_SUBTRACT, PREV_DARK_FRAME_SUBTRACT, | ||
731 | NULL, | ||
732 | preview_enable_drkframe}, | ||
733 | {OMAP3ISP_PREV_LENS_SHADING, PREV_LENS_SHADING, | ||
734 | preview_config_drkf_shadcomp, | ||
735 | preview_enable_drkframe}, | ||
736 | {OMAP3ISP_PREV_NF, PREV_NOISE_FILTER, | ||
737 | preview_config_noisefilter, | ||
738 | preview_enable_noisefilter}, | ||
739 | {OMAP3ISP_PREV_GAMMA, PREV_GAMMA, | ||
740 | preview_config_gammacorrn, | ||
741 | NULL}, | ||
742 | {-1, PREV_CONTRAST, | ||
743 | preview_config_contrast, | ||
744 | NULL}, | ||
745 | {-1, PREV_BRIGHTNESS, | ||
746 | preview_config_brightness, | ||
747 | NULL}, | ||
748 | }; | ||
749 | |||
750 | /* | ||
751 | * __preview_get_ptrs - helper function which return pointers to members | ||
752 | * of params and config structures. | ||
753 | * @params - pointer to preview_params structure. | ||
754 | * @param - return pointer to appropriate structure field. | ||
755 | * @configs - pointer to update config structure. | ||
756 | * @config - return pointer to appropriate structure field. | ||
757 | * @bit - for which feature to return pointers. | ||
758 | * Return size of corresponding prev_params member | ||
759 | */ | ||
760 | static u32 | ||
761 | __preview_get_ptrs(struct prev_params *params, void **param, | ||
762 | struct omap3isp_prev_update_config *configs, | ||
763 | void __user **config, u32 bit) | ||
764 | { | ||
765 | #define CHKARG(cfgs, cfg, field) \ | ||
766 | if (cfgs && cfg) { \ | ||
767 | *(cfg) = (cfgs)->field; \ | ||
768 | } | ||
769 | |||
770 | switch (bit) { | ||
771 | case PREV_HORZ_MEDIAN_FILTER: | ||
772 | *param = ¶ms->hmed; | ||
773 | CHKARG(configs, config, hmed) | ||
774 | return sizeof(params->hmed); | ||
775 | case PREV_NOISE_FILTER: | ||
776 | *param = ¶ms->nf; | ||
777 | CHKARG(configs, config, nf) | ||
778 | return sizeof(params->nf); | ||
779 | break; | ||
780 | case PREV_CFA: | ||
781 | *param = ¶ms->cfa; | ||
782 | CHKARG(configs, config, cfa) | ||
783 | return sizeof(params->cfa); | ||
784 | case PREV_LUMA_ENHANCE: | ||
785 | *param = ¶ms->luma; | ||
786 | CHKARG(configs, config, luma) | ||
787 | return sizeof(params->luma); | ||
788 | case PREV_CHROMA_SUPPRESS: | ||
789 | *param = ¶ms->csup; | ||
790 | CHKARG(configs, config, csup) | ||
791 | return sizeof(params->csup); | ||
792 | case PREV_DEFECT_COR: | ||
793 | *param = ¶ms->dcor; | ||
794 | CHKARG(configs, config, dcor) | ||
795 | return sizeof(params->dcor); | ||
796 | case PREV_BLKADJ: | ||
797 | *param = ¶ms->blk_adj; | ||
798 | CHKARG(configs, config, blkadj) | ||
799 | return sizeof(params->blk_adj); | ||
800 | case PREV_YCLIMITS: | ||
801 | *param = ¶ms->yclimit; | ||
802 | CHKARG(configs, config, yclimit) | ||
803 | return sizeof(params->yclimit); | ||
804 | case PREV_RGB2RGB: | ||
805 | *param = ¶ms->rgb2rgb; | ||
806 | CHKARG(configs, config, rgb2rgb) | ||
807 | return sizeof(params->rgb2rgb); | ||
808 | case PREV_COLOR_CONV: | ||
809 | *param = ¶ms->rgb2ycbcr; | ||
810 | CHKARG(configs, config, csc) | ||
811 | return sizeof(params->rgb2ycbcr); | ||
812 | case PREV_WB: | ||
813 | *param = ¶ms->wbal; | ||
814 | CHKARG(configs, config, wbal) | ||
815 | return sizeof(params->wbal); | ||
816 | case PREV_GAMMA: | ||
817 | *param = ¶ms->gamma; | ||
818 | CHKARG(configs, config, gamma) | ||
819 | return sizeof(params->gamma); | ||
820 | case PREV_CONTRAST: | ||
821 | *param = ¶ms->contrast; | ||
822 | return 0; | ||
823 | case PREV_BRIGHTNESS: | ||
824 | *param = ¶ms->brightness; | ||
825 | return 0; | ||
826 | default: | ||
827 | *param = NULL; | ||
828 | *config = NULL; | ||
829 | break; | ||
830 | } | ||
831 | return 0; | ||
832 | } | ||
833 | |||
834 | /* | ||
835 | * preview_config - Copy and update local structure with userspace preview | ||
836 | * configuration. | ||
837 | * @prev: ISP preview engine | ||
838 | * @cfg: Configuration | ||
839 | * | ||
840 | * Return zero if success or -EFAULT if the configuration can't be copied from | ||
841 | * userspace. | ||
842 | */ | ||
843 | static int preview_config(struct isp_prev_device *prev, | ||
844 | struct omap3isp_prev_update_config *cfg) | ||
845 | { | ||
846 | struct prev_params *params; | ||
847 | struct preview_update *attr; | ||
848 | int i, bit, rval = 0; | ||
849 | |||
850 | params = &prev->params; | ||
851 | |||
852 | if (prev->state != ISP_PIPELINE_STREAM_STOPPED) { | ||
853 | unsigned long flags; | ||
854 | |||
855 | spin_lock_irqsave(&prev->lock, flags); | ||
856 | prev->shadow_update = 1; | ||
857 | spin_unlock_irqrestore(&prev->lock, flags); | ||
858 | } | ||
859 | |||
860 | for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { | ||
861 | attr = &update_attrs[i]; | ||
862 | bit = 0; | ||
863 | |||
864 | if (!(cfg->update & attr->cfg_bit)) | ||
865 | continue; | ||
866 | |||
867 | bit = cfg->flag & attr->cfg_bit; | ||
868 | if (bit) { | ||
869 | void *to = NULL, __user *from = NULL; | ||
870 | unsigned long sz = 0; | ||
871 | |||
872 | sz = __preview_get_ptrs(params, &to, cfg, &from, | ||
873 | bit); | ||
874 | if (to && from && sz) { | ||
875 | if (copy_from_user(to, from, sz)) { | ||
876 | rval = -EFAULT; | ||
877 | break; | ||
878 | } | ||
879 | } | ||
880 | params->features |= attr->feature_bit; | ||
881 | } else { | ||
882 | params->features &= ~attr->feature_bit; | ||
883 | } | ||
884 | |||
885 | prev->update |= attr->feature_bit; | ||
886 | } | ||
887 | |||
888 | prev->shadow_update = 0; | ||
889 | return rval; | ||
890 | } | ||
891 | |||
892 | /* | ||
893 | * preview_setup_hw - Setup preview registers and/or internal memory | ||
894 | * @prev: pointer to preview private structure | ||
895 | * Note: can be called from interrupt context | ||
896 | * Return none | ||
897 | */ | ||
898 | static void preview_setup_hw(struct isp_prev_device *prev) | ||
899 | { | ||
900 | struct prev_params *params = &prev->params; | ||
901 | struct preview_update *attr; | ||
902 | int i, bit; | ||
903 | void *param_ptr; | ||
904 | |||
905 | for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { | ||
906 | attr = &update_attrs[i]; | ||
907 | |||
908 | if (!(prev->update & attr->feature_bit)) | ||
909 | continue; | ||
910 | bit = params->features & attr->feature_bit; | ||
911 | if (bit) { | ||
912 | if (attr->config) { | ||
913 | __preview_get_ptrs(params, ¶m_ptr, NULL, | ||
914 | NULL, bit); | ||
915 | attr->config(prev, param_ptr); | ||
916 | } | ||
917 | if (attr->enable) | ||
918 | attr->enable(prev, 1); | ||
919 | } else | ||
920 | if (attr->enable) | ||
921 | attr->enable(prev, 0); | ||
922 | |||
923 | prev->update &= ~attr->feature_bit; | ||
924 | } | ||
925 | } | ||
926 | |||
927 | /* | ||
928 | * preview_config_ycpos - Configure byte layout of YUV image. | ||
929 | * @mode: Indicates the required byte layout. | ||
930 | */ | ||
931 | static void | ||
932 | preview_config_ycpos(struct isp_prev_device *prev, | ||
933 | enum v4l2_mbus_pixelcode pixelcode) | ||
934 | { | ||
935 | struct isp_device *isp = to_isp_device(prev); | ||
936 | enum preview_ycpos_mode mode; | ||
937 | |||
938 | switch (pixelcode) { | ||
939 | case V4L2_MBUS_FMT_YUYV8_1X16: | ||
940 | mode = YCPOS_CrYCbY; | ||
941 | break; | ||
942 | case V4L2_MBUS_FMT_UYVY8_1X16: | ||
943 | mode = YCPOS_YCrYCb; | ||
944 | break; | ||
945 | default: | ||
946 | return; | ||
947 | } | ||
948 | |||
949 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
950 | ISPPRV_PCR_YCPOS_CrYCbY, | ||
951 | mode << ISPPRV_PCR_YCPOS_SHIFT); | ||
952 | } | ||
953 | |||
954 | /* | ||
955 | * preview_config_averager - Enable / disable / configure averager | ||
956 | * @average: Average value to be configured. | ||
957 | */ | ||
958 | static void preview_config_averager(struct isp_prev_device *prev, u8 average) | ||
959 | { | ||
960 | struct isp_device *isp = to_isp_device(prev); | ||
961 | int reg = 0; | ||
962 | |||
963 | if (prev->params.cfa.format == OMAP3ISP_CFAFMT_BAYER) | ||
964 | reg = ISPPRV_AVE_EVENDIST_2 << ISPPRV_AVE_EVENDIST_SHIFT | | ||
965 | ISPPRV_AVE_ODDDIST_2 << ISPPRV_AVE_ODDDIST_SHIFT | | ||
966 | average; | ||
967 | else if (prev->params.cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON) | ||
968 | reg = ISPPRV_AVE_EVENDIST_3 << ISPPRV_AVE_EVENDIST_SHIFT | | ||
969 | ISPPRV_AVE_ODDDIST_3 << ISPPRV_AVE_ODDDIST_SHIFT | | ||
970 | average; | ||
971 | isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE); | ||
972 | } | ||
973 | |||
974 | /* | ||
975 | * preview_config_input_size - Configure the input frame size | ||
976 | * | ||
977 | * The preview engine crops several rows and columns internally depending on | ||
978 | * which processing blocks are enabled. The driver assumes all those blocks are | ||
979 | * enabled when reporting source pad formats to userspace. If this assumption is | ||
980 | * not true, rows and columns must be manually cropped at the preview engine | ||
981 | * input to avoid overflows at the end of lines and frames. | ||
982 | */ | ||
983 | static void preview_config_input_size(struct isp_prev_device *prev) | ||
984 | { | ||
985 | struct isp_device *isp = to_isp_device(prev); | ||
986 | struct prev_params *params = &prev->params; | ||
987 | struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK]; | ||
988 | unsigned int sph = 0; | ||
989 | unsigned int eph = format->width - 1; | ||
990 | unsigned int slv = 0; | ||
991 | unsigned int elv = format->height - 1; | ||
992 | |||
993 | if (prev->input == PREVIEW_INPUT_CCDC) { | ||
994 | sph += 2; | ||
995 | eph -= 2; | ||
996 | } | ||
997 | |||
998 | /* | ||
999 | * Median filter 4 pixels | ||
1000 | * Noise filter 4 pixels, 4 lines | ||
1001 | * or faulty pixels correction | ||
1002 | * CFA filter 4 pixels, 4 lines in Bayer mode | ||
1003 | * 2 lines in other modes | ||
1004 | * Color suppression 2 pixels | ||
1005 | * or luma enhancement | ||
1006 | * ------------------------------------------------------------- | ||
1007 | * Maximum total 14 pixels, 8 lines | ||
1008 | */ | ||
1009 | |||
1010 | if (!(params->features & PREV_CFA)) { | ||
1011 | sph += 2; | ||
1012 | eph -= 2; | ||
1013 | slv += 2; | ||
1014 | elv -= 2; | ||
1015 | } | ||
1016 | if (!(params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER))) { | ||
1017 | sph += 2; | ||
1018 | eph -= 2; | ||
1019 | slv += 2; | ||
1020 | elv -= 2; | ||
1021 | } | ||
1022 | if (!(params->features & PREV_HORZ_MEDIAN_FILTER)) { | ||
1023 | sph += 2; | ||
1024 | eph -= 2; | ||
1025 | } | ||
1026 | if (!(params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))) | ||
1027 | sph += 2; | ||
1028 | |||
1029 | isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph, | ||
1030 | OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO); | ||
1031 | isp_reg_writel(isp, (slv << ISPPRV_VERT_INFO_SLV_SHIFT) | elv, | ||
1032 | OMAP3_ISP_IOMEM_PREV, ISPPRV_VERT_INFO); | ||
1033 | } | ||
1034 | |||
1035 | /* | ||
1036 | * preview_config_inlineoffset - Configures the Read address line offset. | ||
1037 | * @prev: Preview module | ||
1038 | * @offset: Line offset | ||
1039 | * | ||
1040 | * According to the TRM, the line offset must be aligned on a 32 bytes boundary. | ||
1041 | * However, a hardware bug requires the memory start address to be aligned on a | ||
1042 | * 64 bytes boundary, so the offset probably should be aligned on 64 bytes as | ||
1043 | * well. | ||
1044 | */ | ||
1045 | static void | ||
1046 | preview_config_inlineoffset(struct isp_prev_device *prev, u32 offset) | ||
1047 | { | ||
1048 | struct isp_device *isp = to_isp_device(prev); | ||
1049 | |||
1050 | isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV, | ||
1051 | ISPPRV_RADR_OFFSET); | ||
1052 | } | ||
1053 | |||
1054 | /* | ||
1055 | * preview_set_inaddr - Sets memory address of input frame. | ||
1056 | * @addr: 32bit memory address aligned on 32byte boundary. | ||
1057 | * | ||
1058 | * Configures the memory address from which the input frame is to be read. | ||
1059 | */ | ||
1060 | static void preview_set_inaddr(struct isp_prev_device *prev, u32 addr) | ||
1061 | { | ||
1062 | struct isp_device *isp = to_isp_device(prev); | ||
1063 | |||
1064 | isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_RSDR_ADDR); | ||
1065 | } | ||
1066 | |||
1067 | /* | ||
1068 | * preview_config_outlineoffset - Configures the Write address line offset. | ||
1069 | * @offset: Line Offset for the preview output. | ||
1070 | * | ||
1071 | * The offset must be a multiple of 32 bytes. | ||
1072 | */ | ||
1073 | static void preview_config_outlineoffset(struct isp_prev_device *prev, | ||
1074 | u32 offset) | ||
1075 | { | ||
1076 | struct isp_device *isp = to_isp_device(prev); | ||
1077 | |||
1078 | isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV, | ||
1079 | ISPPRV_WADD_OFFSET); | ||
1080 | } | ||
1081 | |||
1082 | /* | ||
1083 | * preview_set_outaddr - Sets the memory address to store output frame | ||
1084 | * @addr: 32bit memory address aligned on 32byte boundary. | ||
1085 | * | ||
1086 | * Configures the memory address to which the output frame is written. | ||
1087 | */ | ||
1088 | static void preview_set_outaddr(struct isp_prev_device *prev, u32 addr) | ||
1089 | { | ||
1090 | struct isp_device *isp = to_isp_device(prev); | ||
1091 | |||
1092 | isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_WSDR_ADDR); | ||
1093 | } | ||
1094 | |||
1095 | static void preview_adjust_bandwidth(struct isp_prev_device *prev) | ||
1096 | { | ||
1097 | struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity); | ||
1098 | struct isp_device *isp = to_isp_device(prev); | ||
1099 | const struct v4l2_mbus_framefmt *ifmt = &prev->formats[PREV_PAD_SINK]; | ||
1100 | unsigned long l3_ick = pipe->l3_ick; | ||
1101 | struct v4l2_fract *timeperframe; | ||
1102 | unsigned int cycles_per_frame; | ||
1103 | unsigned int requests_per_frame; | ||
1104 | unsigned int cycles_per_request; | ||
1105 | unsigned int minimum; | ||
1106 | unsigned int maximum; | ||
1107 | unsigned int value; | ||
1108 | |||
1109 | if (prev->input != PREVIEW_INPUT_MEMORY) { | ||
1110 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, | ||
1111 | ISPSBL_SDR_REQ_PRV_EXP_MASK); | ||
1112 | return; | ||
1113 | } | ||
1114 | |||
1115 | /* Compute the minimum number of cycles per request, based on the | ||
1116 | * pipeline maximum data rate. This is an absolute lower bound if we | ||
1117 | * don't want SBL overflows, so round the value up. | ||
1118 | */ | ||
1119 | cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1, | ||
1120 | pipe->max_rate); | ||
1121 | minimum = DIV_ROUND_UP(cycles_per_request, 32); | ||
1122 | |||
1123 | /* Compute the maximum number of cycles per request, based on the | ||
1124 | * requested frame rate. This is a soft upper bound to achieve a frame | ||
1125 | * rate equal or higher than the requested value, so round the value | ||
1126 | * down. | ||
1127 | */ | ||
1128 | timeperframe = &pipe->max_timeperframe; | ||
1129 | |||
1130 | requests_per_frame = DIV_ROUND_UP(ifmt->width * 2, 256) * ifmt->height; | ||
1131 | cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator, | ||
1132 | timeperframe->denominator); | ||
1133 | cycles_per_request = cycles_per_frame / requests_per_frame; | ||
1134 | |||
1135 | maximum = cycles_per_request / 32; | ||
1136 | |||
1137 | value = max(minimum, maximum); | ||
1138 | |||
1139 | dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value); | ||
1140 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, | ||
1141 | ISPSBL_SDR_REQ_PRV_EXP_MASK, | ||
1142 | value << ISPSBL_SDR_REQ_PRV_EXP_SHIFT); | ||
1143 | } | ||
1144 | |||
1145 | /* | ||
1146 | * omap3isp_preview_busy - Gets busy state of preview module. | ||
1147 | */ | ||
1148 | int omap3isp_preview_busy(struct isp_prev_device *prev) | ||
1149 | { | ||
1150 | struct isp_device *isp = to_isp_device(prev); | ||
1151 | |||
1152 | return isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR) | ||
1153 | & ISPPRV_PCR_BUSY; | ||
1154 | } | ||
1155 | |||
1156 | /* | ||
1157 | * omap3isp_preview_restore_context - Restores the values of preview registers | ||
1158 | */ | ||
1159 | void omap3isp_preview_restore_context(struct isp_device *isp) | ||
1160 | { | ||
1161 | isp->isp_prev.update = PREV_FEATURES_END - 1; | ||
1162 | preview_setup_hw(&isp->isp_prev); | ||
1163 | } | ||
1164 | |||
1165 | /* | ||
1166 | * preview_print_status - Dump preview module registers to the kernel log | ||
1167 | */ | ||
1168 | #define PREV_PRINT_REGISTER(isp, name)\ | ||
1169 | dev_dbg(isp->dev, "###PRV " #name "=0x%08x\n", \ | ||
1170 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_##name)) | ||
1171 | |||
1172 | static void preview_print_status(struct isp_prev_device *prev) | ||
1173 | { | ||
1174 | struct isp_device *isp = to_isp_device(prev); | ||
1175 | |||
1176 | dev_dbg(isp->dev, "-------------Preview Register dump----------\n"); | ||
1177 | |||
1178 | PREV_PRINT_REGISTER(isp, PCR); | ||
1179 | PREV_PRINT_REGISTER(isp, HORZ_INFO); | ||
1180 | PREV_PRINT_REGISTER(isp, VERT_INFO); | ||
1181 | PREV_PRINT_REGISTER(isp, RSDR_ADDR); | ||
1182 | PREV_PRINT_REGISTER(isp, RADR_OFFSET); | ||
1183 | PREV_PRINT_REGISTER(isp, DSDR_ADDR); | ||
1184 | PREV_PRINT_REGISTER(isp, DRKF_OFFSET); | ||
1185 | PREV_PRINT_REGISTER(isp, WSDR_ADDR); | ||
1186 | PREV_PRINT_REGISTER(isp, WADD_OFFSET); | ||
1187 | PREV_PRINT_REGISTER(isp, AVE); | ||
1188 | PREV_PRINT_REGISTER(isp, HMED); | ||
1189 | PREV_PRINT_REGISTER(isp, NF); | ||
1190 | PREV_PRINT_REGISTER(isp, WB_DGAIN); | ||
1191 | PREV_PRINT_REGISTER(isp, WBGAIN); | ||
1192 | PREV_PRINT_REGISTER(isp, WBSEL); | ||
1193 | PREV_PRINT_REGISTER(isp, CFA); | ||
1194 | PREV_PRINT_REGISTER(isp, BLKADJOFF); | ||
1195 | PREV_PRINT_REGISTER(isp, RGB_MAT1); | ||
1196 | PREV_PRINT_REGISTER(isp, RGB_MAT2); | ||
1197 | PREV_PRINT_REGISTER(isp, RGB_MAT3); | ||
1198 | PREV_PRINT_REGISTER(isp, RGB_MAT4); | ||
1199 | PREV_PRINT_REGISTER(isp, RGB_MAT5); | ||
1200 | PREV_PRINT_REGISTER(isp, RGB_OFF1); | ||
1201 | PREV_PRINT_REGISTER(isp, RGB_OFF2); | ||
1202 | PREV_PRINT_REGISTER(isp, CSC0); | ||
1203 | PREV_PRINT_REGISTER(isp, CSC1); | ||
1204 | PREV_PRINT_REGISTER(isp, CSC2); | ||
1205 | PREV_PRINT_REGISTER(isp, CSC_OFFSET); | ||
1206 | PREV_PRINT_REGISTER(isp, CNT_BRT); | ||
1207 | PREV_PRINT_REGISTER(isp, CSUP); | ||
1208 | PREV_PRINT_REGISTER(isp, SETUP_YC); | ||
1209 | PREV_PRINT_REGISTER(isp, SET_TBL_ADDR); | ||
1210 | PREV_PRINT_REGISTER(isp, CDC_THR0); | ||
1211 | PREV_PRINT_REGISTER(isp, CDC_THR1); | ||
1212 | PREV_PRINT_REGISTER(isp, CDC_THR2); | ||
1213 | PREV_PRINT_REGISTER(isp, CDC_THR3); | ||
1214 | |||
1215 | dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
1216 | } | ||
1217 | |||
1218 | /* | ||
1219 | * preview_init_params - init image processing parameters. | ||
1220 | * @prev: pointer to previewer private structure | ||
1221 | * return none | ||
1222 | */ | ||
1223 | static void preview_init_params(struct isp_prev_device *prev) | ||
1224 | { | ||
1225 | struct prev_params *params = &prev->params; | ||
1226 | int i = 0; | ||
1227 | |||
1228 | /* Init values */ | ||
1229 | params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS; | ||
1230 | params->brightness = ISPPRV_BRIGHT_DEF * ISPPRV_BRIGHT_UNITS; | ||
1231 | params->average = NO_AVE; | ||
1232 | params->cfa.format = OMAP3ISP_CFAFMT_BAYER; | ||
1233 | memcpy(params->cfa.table, cfa_coef_table, | ||
1234 | sizeof(params->cfa.table)); | ||
1235 | params->cfa.gradthrs_horz = FLR_CFA_GRADTHRS_HORZ; | ||
1236 | params->cfa.gradthrs_vert = FLR_CFA_GRADTHRS_VERT; | ||
1237 | params->csup.gain = FLR_CSUP_GAIN; | ||
1238 | params->csup.thres = FLR_CSUP_THRES; | ||
1239 | params->csup.hypf_en = 0; | ||
1240 | memcpy(params->luma.table, luma_enhance_table, | ||
1241 | sizeof(params->luma.table)); | ||
1242 | params->nf.spread = FLR_NF_STRGTH; | ||
1243 | memcpy(params->nf.table, noise_filter_table, sizeof(params->nf.table)); | ||
1244 | params->dcor.couplet_mode_en = 1; | ||
1245 | for (i = 0; i < OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS; i++) | ||
1246 | params->dcor.detect_correct[i] = DEF_DETECT_CORRECT_VAL; | ||
1247 | memcpy(params->gamma.blue, gamma_table, sizeof(params->gamma.blue)); | ||
1248 | memcpy(params->gamma.green, gamma_table, sizeof(params->gamma.green)); | ||
1249 | memcpy(params->gamma.red, gamma_table, sizeof(params->gamma.red)); | ||
1250 | params->wbal.dgain = FLR_WBAL_DGAIN; | ||
1251 | params->wbal.coef0 = FLR_WBAL_COEF; | ||
1252 | params->wbal.coef1 = FLR_WBAL_COEF; | ||
1253 | params->wbal.coef2 = FLR_WBAL_COEF; | ||
1254 | params->wbal.coef3 = FLR_WBAL_COEF; | ||
1255 | params->blk_adj.red = FLR_BLKADJ_RED; | ||
1256 | params->blk_adj.green = FLR_BLKADJ_GREEN; | ||
1257 | params->blk_adj.blue = FLR_BLKADJ_BLUE; | ||
1258 | params->rgb2rgb = flr_rgb2rgb; | ||
1259 | params->rgb2ycbcr = flr_prev_csc; | ||
1260 | params->yclimit.minC = ISPPRV_YC_MIN; | ||
1261 | params->yclimit.maxC = ISPPRV_YC_MAX; | ||
1262 | params->yclimit.minY = ISPPRV_YC_MIN; | ||
1263 | params->yclimit.maxY = ISPPRV_YC_MAX; | ||
1264 | |||
1265 | params->features = PREV_CFA | PREV_DEFECT_COR | PREV_NOISE_FILTER | ||
1266 | | PREV_GAMMA | PREV_BLKADJ | PREV_YCLIMITS | ||
1267 | | PREV_RGB2RGB | PREV_COLOR_CONV | PREV_WB | ||
1268 | | PREV_BRIGHTNESS | PREV_CONTRAST; | ||
1269 | |||
1270 | prev->update = PREV_FEATURES_END - 1; | ||
1271 | } | ||
1272 | |||
1273 | /* | ||
1274 | * preview_max_out_width - Handle previewer hardware ouput limitations | ||
1275 | * @isp_revision : ISP revision | ||
1276 | * returns maximum width output for current isp revision | ||
1277 | */ | ||
1278 | static unsigned int preview_max_out_width(struct isp_prev_device *prev) | ||
1279 | { | ||
1280 | struct isp_device *isp = to_isp_device(prev); | ||
1281 | |||
1282 | switch (isp->revision) { | ||
1283 | case ISP_REVISION_1_0: | ||
1284 | return ISPPRV_MAXOUTPUT_WIDTH; | ||
1285 | |||
1286 | case ISP_REVISION_2_0: | ||
1287 | default: | ||
1288 | return ISPPRV_MAXOUTPUT_WIDTH_ES2; | ||
1289 | |||
1290 | case ISP_REVISION_15_0: | ||
1291 | return ISPPRV_MAXOUTPUT_WIDTH_3630; | ||
1292 | } | ||
1293 | } | ||
1294 | |||
1295 | static void preview_configure(struct isp_prev_device *prev) | ||
1296 | { | ||
1297 | struct isp_device *isp = to_isp_device(prev); | ||
1298 | struct v4l2_mbus_framefmt *format; | ||
1299 | unsigned int max_out_width; | ||
1300 | unsigned int format_avg; | ||
1301 | |||
1302 | preview_setup_hw(prev); | ||
1303 | |||
1304 | if (prev->output & PREVIEW_OUTPUT_MEMORY) | ||
1305 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
1306 | ISPPRV_PCR_SDRPORT); | ||
1307 | else | ||
1308 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
1309 | ISPPRV_PCR_SDRPORT); | ||
1310 | |||
1311 | if (prev->output & PREVIEW_OUTPUT_RESIZER) | ||
1312 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
1313 | ISPPRV_PCR_RSZPORT); | ||
1314 | else | ||
1315 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
1316 | ISPPRV_PCR_RSZPORT); | ||
1317 | |||
1318 | /* PREV_PAD_SINK */ | ||
1319 | format = &prev->formats[PREV_PAD_SINK]; | ||
1320 | |||
1321 | preview_adjust_bandwidth(prev); | ||
1322 | |||
1323 | preview_config_input_size(prev); | ||
1324 | |||
1325 | if (prev->input == PREVIEW_INPUT_CCDC) | ||
1326 | preview_config_inlineoffset(prev, 0); | ||
1327 | else | ||
1328 | preview_config_inlineoffset(prev, | ||
1329 | ALIGN(format->width, 0x20) * 2); | ||
1330 | |||
1331 | /* PREV_PAD_SOURCE */ | ||
1332 | format = &prev->formats[PREV_PAD_SOURCE]; | ||
1333 | |||
1334 | if (prev->output & PREVIEW_OUTPUT_MEMORY) | ||
1335 | preview_config_outlineoffset(prev, | ||
1336 | ALIGN(format->width, 0x10) * 2); | ||
1337 | |||
1338 | max_out_width = preview_max_out_width(prev); | ||
1339 | |||
1340 | format_avg = fls(DIV_ROUND_UP(format->width, max_out_width) - 1); | ||
1341 | preview_config_averager(prev, format_avg); | ||
1342 | preview_config_ycpos(prev, format->code); | ||
1343 | } | ||
1344 | |||
1345 | /* ----------------------------------------------------------------------------- | ||
1346 | * Interrupt handling | ||
1347 | */ | ||
1348 | |||
1349 | static void preview_enable_oneshot(struct isp_prev_device *prev) | ||
1350 | { | ||
1351 | struct isp_device *isp = to_isp_device(prev); | ||
1352 | |||
1353 | /* The PCR.SOURCE bit is automatically reset to 0 when the PCR.ENABLE | ||
1354 | * bit is set. As the preview engine is used in single-shot mode, we | ||
1355 | * need to set PCR.SOURCE before enabling the preview engine. | ||
1356 | */ | ||
1357 | if (prev->input == PREVIEW_INPUT_MEMORY) | ||
1358 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
1359 | ISPPRV_PCR_SOURCE); | ||
1360 | |||
1361 | isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, | ||
1362 | ISPPRV_PCR_EN | ISPPRV_PCR_ONESHOT); | ||
1363 | } | ||
1364 | |||
1365 | void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev) | ||
1366 | { | ||
1367 | /* | ||
1368 | * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun | ||
1369 | * condition, the module was paused and now we have a buffer queued | ||
1370 | * on the output again. Restart the pipeline if running in continuous | ||
1371 | * mode. | ||
1372 | */ | ||
1373 | if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS && | ||
1374 | prev->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { | ||
1375 | preview_enable_oneshot(prev); | ||
1376 | isp_video_dmaqueue_flags_clr(&prev->video_out); | ||
1377 | } | ||
1378 | } | ||
1379 | |||
1380 | static void preview_isr_buffer(struct isp_prev_device *prev) | ||
1381 | { | ||
1382 | struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity); | ||
1383 | struct isp_buffer *buffer; | ||
1384 | int restart = 0; | ||
1385 | |||
1386 | if (prev->input == PREVIEW_INPUT_MEMORY) { | ||
1387 | buffer = omap3isp_video_buffer_next(&prev->video_in, | ||
1388 | prev->error); | ||
1389 | if (buffer != NULL) | ||
1390 | preview_set_inaddr(prev, buffer->isp_addr); | ||
1391 | pipe->state |= ISP_PIPELINE_IDLE_INPUT; | ||
1392 | } | ||
1393 | |||
1394 | if (prev->output & PREVIEW_OUTPUT_MEMORY) { | ||
1395 | buffer = omap3isp_video_buffer_next(&prev->video_out, | ||
1396 | prev->error); | ||
1397 | if (buffer != NULL) { | ||
1398 | preview_set_outaddr(prev, buffer->isp_addr); | ||
1399 | restart = 1; | ||
1400 | } | ||
1401 | pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; | ||
1402 | } | ||
1403 | |||
1404 | switch (prev->state) { | ||
1405 | case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
1406 | if (isp_pipeline_ready(pipe)) | ||
1407 | omap3isp_pipeline_set_stream(pipe, | ||
1408 | ISP_PIPELINE_STREAM_SINGLESHOT); | ||
1409 | break; | ||
1410 | |||
1411 | case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
1412 | /* If an underrun occurs, the video queue operation handler will | ||
1413 | * restart the preview engine. Otherwise restart it immediately. | ||
1414 | */ | ||
1415 | if (restart) | ||
1416 | preview_enable_oneshot(prev); | ||
1417 | break; | ||
1418 | |||
1419 | case ISP_PIPELINE_STREAM_STOPPED: | ||
1420 | default: | ||
1421 | return; | ||
1422 | } | ||
1423 | |||
1424 | prev->error = 0; | ||
1425 | } | ||
1426 | |||
1427 | /* | ||
1428 | * omap3isp_preview_isr - ISP preview engine interrupt handler | ||
1429 | * | ||
1430 | * Manage the preview engine video buffers and configure shadowed registers. | ||
1431 | */ | ||
1432 | void omap3isp_preview_isr(struct isp_prev_device *prev) | ||
1433 | { | ||
1434 | unsigned long flags; | ||
1435 | |||
1436 | if (omap3isp_module_sync_is_stopping(&prev->wait, &prev->stopping)) | ||
1437 | return; | ||
1438 | |||
1439 | spin_lock_irqsave(&prev->lock, flags); | ||
1440 | if (prev->shadow_update) | ||
1441 | goto done; | ||
1442 | |||
1443 | preview_setup_hw(prev); | ||
1444 | preview_config_input_size(prev); | ||
1445 | |||
1446 | done: | ||
1447 | spin_unlock_irqrestore(&prev->lock, flags); | ||
1448 | |||
1449 | if (prev->input == PREVIEW_INPUT_MEMORY || | ||
1450 | prev->output & PREVIEW_OUTPUT_MEMORY) | ||
1451 | preview_isr_buffer(prev); | ||
1452 | else if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS) | ||
1453 | preview_enable_oneshot(prev); | ||
1454 | } | ||
1455 | |||
1456 | /* ----------------------------------------------------------------------------- | ||
1457 | * ISP video operations | ||
1458 | */ | ||
1459 | |||
1460 | static int preview_video_queue(struct isp_video *video, | ||
1461 | struct isp_buffer *buffer) | ||
1462 | { | ||
1463 | struct isp_prev_device *prev = &video->isp->isp_prev; | ||
1464 | |||
1465 | if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
1466 | preview_set_inaddr(prev, buffer->isp_addr); | ||
1467 | |||
1468 | if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1469 | preview_set_outaddr(prev, buffer->isp_addr); | ||
1470 | |||
1471 | return 0; | ||
1472 | } | ||
1473 | |||
1474 | static const struct isp_video_operations preview_video_ops = { | ||
1475 | .queue = preview_video_queue, | ||
1476 | }; | ||
1477 | |||
1478 | /* ----------------------------------------------------------------------------- | ||
1479 | * V4L2 subdev operations | ||
1480 | */ | ||
1481 | |||
1482 | /* | ||
1483 | * preview_s_ctrl - Handle set control subdev method | ||
1484 | * @ctrl: pointer to v4l2 control structure | ||
1485 | */ | ||
1486 | static int preview_s_ctrl(struct v4l2_ctrl *ctrl) | ||
1487 | { | ||
1488 | struct isp_prev_device *prev = | ||
1489 | container_of(ctrl->handler, struct isp_prev_device, ctrls); | ||
1490 | |||
1491 | switch (ctrl->id) { | ||
1492 | case V4L2_CID_BRIGHTNESS: | ||
1493 | preview_update_brightness(prev, ctrl->val); | ||
1494 | break; | ||
1495 | case V4L2_CID_CONTRAST: | ||
1496 | preview_update_contrast(prev, ctrl->val); | ||
1497 | break; | ||
1498 | } | ||
1499 | |||
1500 | return 0; | ||
1501 | } | ||
1502 | |||
1503 | static const struct v4l2_ctrl_ops preview_ctrl_ops = { | ||
1504 | .s_ctrl = preview_s_ctrl, | ||
1505 | }; | ||
1506 | |||
1507 | /* | ||
1508 | * preview_ioctl - Handle preview module private ioctl's | ||
1509 | * @prev: pointer to preview context structure | ||
1510 | * @cmd: configuration command | ||
1511 | * @arg: configuration argument | ||
1512 | * return -EINVAL or zero on success | ||
1513 | */ | ||
1514 | static long preview_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
1515 | { | ||
1516 | struct isp_prev_device *prev = v4l2_get_subdevdata(sd); | ||
1517 | |||
1518 | switch (cmd) { | ||
1519 | case VIDIOC_OMAP3ISP_PRV_CFG: | ||
1520 | return preview_config(prev, arg); | ||
1521 | |||
1522 | default: | ||
1523 | return -ENOIOCTLCMD; | ||
1524 | } | ||
1525 | } | ||
1526 | |||
1527 | /* | ||
1528 | * preview_set_stream - Enable/Disable streaming on preview subdev | ||
1529 | * @sd : pointer to v4l2 subdev structure | ||
1530 | * @enable: 1 == Enable, 0 == Disable | ||
1531 | * return -EINVAL or zero on success | ||
1532 | */ | ||
1533 | static int preview_set_stream(struct v4l2_subdev *sd, int enable) | ||
1534 | { | ||
1535 | struct isp_prev_device *prev = v4l2_get_subdevdata(sd); | ||
1536 | struct isp_video *video_out = &prev->video_out; | ||
1537 | struct isp_device *isp = to_isp_device(prev); | ||
1538 | struct device *dev = to_device(prev); | ||
1539 | unsigned long flags; | ||
1540 | |||
1541 | if (prev->state == ISP_PIPELINE_STREAM_STOPPED) { | ||
1542 | if (enable == ISP_PIPELINE_STREAM_STOPPED) | ||
1543 | return 0; | ||
1544 | |||
1545 | omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_PREVIEW); | ||
1546 | preview_configure(prev); | ||
1547 | atomic_set(&prev->stopping, 0); | ||
1548 | prev->error = 0; | ||
1549 | preview_print_status(prev); | ||
1550 | } | ||
1551 | |||
1552 | switch (enable) { | ||
1553 | case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
1554 | if (prev->output & PREVIEW_OUTPUT_MEMORY) | ||
1555 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); | ||
1556 | |||
1557 | if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED || | ||
1558 | !(prev->output & PREVIEW_OUTPUT_MEMORY)) | ||
1559 | preview_enable_oneshot(prev); | ||
1560 | |||
1561 | isp_video_dmaqueue_flags_clr(video_out); | ||
1562 | break; | ||
1563 | |||
1564 | case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
1565 | if (prev->input == PREVIEW_INPUT_MEMORY) | ||
1566 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_READ); | ||
1567 | if (prev->output & PREVIEW_OUTPUT_MEMORY) | ||
1568 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); | ||
1569 | |||
1570 | preview_enable_oneshot(prev); | ||
1571 | break; | ||
1572 | |||
1573 | case ISP_PIPELINE_STREAM_STOPPED: | ||
1574 | if (omap3isp_module_sync_idle(&sd->entity, &prev->wait, | ||
1575 | &prev->stopping)) | ||
1576 | dev_dbg(dev, "%s: stop timeout.\n", sd->name); | ||
1577 | spin_lock_irqsave(&prev->lock, flags); | ||
1578 | omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_READ); | ||
1579 | omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); | ||
1580 | omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_PREVIEW); | ||
1581 | spin_unlock_irqrestore(&prev->lock, flags); | ||
1582 | isp_video_dmaqueue_flags_clr(video_out); | ||
1583 | break; | ||
1584 | } | ||
1585 | |||
1586 | prev->state = enable; | ||
1587 | return 0; | ||
1588 | } | ||
1589 | |||
1590 | static struct v4l2_mbus_framefmt * | ||
1591 | __preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, | ||
1592 | unsigned int pad, enum v4l2_subdev_format_whence which) | ||
1593 | { | ||
1594 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
1595 | return v4l2_subdev_get_try_format(fh, pad); | ||
1596 | else | ||
1597 | return &prev->formats[pad]; | ||
1598 | } | ||
1599 | |||
1600 | /* previewer format descriptions */ | ||
1601 | static const unsigned int preview_input_fmts[] = { | ||
1602 | V4L2_MBUS_FMT_SGRBG10_1X10, | ||
1603 | V4L2_MBUS_FMT_SRGGB10_1X10, | ||
1604 | V4L2_MBUS_FMT_SBGGR10_1X10, | ||
1605 | V4L2_MBUS_FMT_SGBRG10_1X10, | ||
1606 | }; | ||
1607 | |||
1608 | static const unsigned int preview_output_fmts[] = { | ||
1609 | V4L2_MBUS_FMT_UYVY8_1X16, | ||
1610 | V4L2_MBUS_FMT_YUYV8_1X16, | ||
1611 | }; | ||
1612 | |||
1613 | /* | ||
1614 | * preview_try_format - Handle try format by pad subdev method | ||
1615 | * @prev: ISP preview device | ||
1616 | * @fh : V4L2 subdev file handle | ||
1617 | * @pad: pad num | ||
1618 | * @fmt: pointer to v4l2 format structure | ||
1619 | */ | ||
1620 | static void preview_try_format(struct isp_prev_device *prev, | ||
1621 | struct v4l2_subdev_fh *fh, unsigned int pad, | ||
1622 | struct v4l2_mbus_framefmt *fmt, | ||
1623 | enum v4l2_subdev_format_whence which) | ||
1624 | { | ||
1625 | struct v4l2_mbus_framefmt *format; | ||
1626 | unsigned int max_out_width; | ||
1627 | enum v4l2_mbus_pixelcode pixelcode; | ||
1628 | unsigned int i; | ||
1629 | |||
1630 | max_out_width = preview_max_out_width(prev); | ||
1631 | |||
1632 | switch (pad) { | ||
1633 | case PREV_PAD_SINK: | ||
1634 | /* When reading data from the CCDC, the input size has already | ||
1635 | * been mangled by the CCDC output pad so it can be accepted | ||
1636 | * as-is. | ||
1637 | * | ||
1638 | * When reading data from memory, clamp the requested width and | ||
1639 | * height. The TRM doesn't specify a minimum input height, make | ||
1640 | * sure we got enough lines to enable the noise filter and color | ||
1641 | * filter array interpolation. | ||
1642 | */ | ||
1643 | if (prev->input == PREVIEW_INPUT_MEMORY) { | ||
1644 | fmt->width = clamp_t(u32, fmt->width, PREV_MIN_WIDTH, | ||
1645 | max_out_width * 8); | ||
1646 | fmt->height = clamp_t(u32, fmt->height, PREV_MIN_HEIGHT, | ||
1647 | PREV_MAX_HEIGHT); | ||
1648 | } | ||
1649 | |||
1650 | fmt->colorspace = V4L2_COLORSPACE_SRGB; | ||
1651 | |||
1652 | for (i = 0; i < ARRAY_SIZE(preview_input_fmts); i++) { | ||
1653 | if (fmt->code == preview_input_fmts[i]) | ||
1654 | break; | ||
1655 | } | ||
1656 | |||
1657 | /* If not found, use SGRBG10 as default */ | ||
1658 | if (i >= ARRAY_SIZE(preview_input_fmts)) | ||
1659 | fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
1660 | break; | ||
1661 | |||
1662 | case PREV_PAD_SOURCE: | ||
1663 | pixelcode = fmt->code; | ||
1664 | format = __preview_get_format(prev, fh, PREV_PAD_SINK, which); | ||
1665 | memcpy(fmt, format, sizeof(*fmt)); | ||
1666 | |||
1667 | /* The preview module output size is configurable through the | ||
1668 | * input interface (horizontal and vertical cropping) and the | ||
1669 | * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). In | ||
1670 | * spite of this, hardcode the output size to the biggest | ||
1671 | * possible value for simplicity reasons. | ||
1672 | */ | ||
1673 | switch (pixelcode) { | ||
1674 | case V4L2_MBUS_FMT_YUYV8_1X16: | ||
1675 | case V4L2_MBUS_FMT_UYVY8_1X16: | ||
1676 | fmt->code = pixelcode; | ||
1677 | break; | ||
1678 | |||
1679 | default: | ||
1680 | fmt->code = V4L2_MBUS_FMT_YUYV8_1X16; | ||
1681 | break; | ||
1682 | } | ||
1683 | |||
1684 | /* The TRM states (12.1.4.7.1.2) that 2 pixels must be cropped | ||
1685 | * from the left and right sides when the input source is the | ||
1686 | * CCDC. This seems not to be needed in practice, investigation | ||
1687 | * is required. | ||
1688 | */ | ||
1689 | if (prev->input == PREVIEW_INPUT_CCDC) | ||
1690 | fmt->width -= 4; | ||
1691 | |||
1692 | /* The preview module can output a maximum of 3312 pixels | ||
1693 | * horizontally due to fixed memory-line sizes. Compute the | ||
1694 | * horizontal averaging factor accordingly. Note that the limit | ||
1695 | * applies to the noise filter and CFA interpolation blocks, so | ||
1696 | * it doesn't take cropping by further blocks into account. | ||
1697 | * | ||
1698 | * ES 1.0 hardware revision is limited to 1280 pixels | ||
1699 | * horizontally. | ||
1700 | */ | ||
1701 | fmt->width >>= fls(DIV_ROUND_UP(fmt->width, max_out_width) - 1); | ||
1702 | |||
1703 | /* Assume that all blocks are enabled and crop pixels and lines | ||
1704 | * accordingly. See preview_config_input_size() for more | ||
1705 | * information. | ||
1706 | */ | ||
1707 | fmt->width -= 14; | ||
1708 | fmt->height -= 8; | ||
1709 | |||
1710 | fmt->colorspace = V4L2_COLORSPACE_JPEG; | ||
1711 | break; | ||
1712 | } | ||
1713 | |||
1714 | fmt->field = V4L2_FIELD_NONE; | ||
1715 | } | ||
1716 | |||
1717 | /* | ||
1718 | * preview_enum_mbus_code - Handle pixel format enumeration | ||
1719 | * @sd : pointer to v4l2 subdev structure | ||
1720 | * @fh : V4L2 subdev file handle | ||
1721 | * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
1722 | * return -EINVAL or zero on success | ||
1723 | */ | ||
1724 | static int preview_enum_mbus_code(struct v4l2_subdev *sd, | ||
1725 | struct v4l2_subdev_fh *fh, | ||
1726 | struct v4l2_subdev_mbus_code_enum *code) | ||
1727 | { | ||
1728 | switch (code->pad) { | ||
1729 | case PREV_PAD_SINK: | ||
1730 | if (code->index >= ARRAY_SIZE(preview_input_fmts)) | ||
1731 | return -EINVAL; | ||
1732 | |||
1733 | code->code = preview_input_fmts[code->index]; | ||
1734 | break; | ||
1735 | case PREV_PAD_SOURCE: | ||
1736 | if (code->index >= ARRAY_SIZE(preview_output_fmts)) | ||
1737 | return -EINVAL; | ||
1738 | |||
1739 | code->code = preview_output_fmts[code->index]; | ||
1740 | break; | ||
1741 | default: | ||
1742 | return -EINVAL; | ||
1743 | } | ||
1744 | |||
1745 | return 0; | ||
1746 | } | ||
1747 | |||
1748 | static int preview_enum_frame_size(struct v4l2_subdev *sd, | ||
1749 | struct v4l2_subdev_fh *fh, | ||
1750 | struct v4l2_subdev_frame_size_enum *fse) | ||
1751 | { | ||
1752 | struct isp_prev_device *prev = v4l2_get_subdevdata(sd); | ||
1753 | struct v4l2_mbus_framefmt format; | ||
1754 | |||
1755 | if (fse->index != 0) | ||
1756 | return -EINVAL; | ||
1757 | |||
1758 | format.code = fse->code; | ||
1759 | format.width = 1; | ||
1760 | format.height = 1; | ||
1761 | preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
1762 | fse->min_width = format.width; | ||
1763 | fse->min_height = format.height; | ||
1764 | |||
1765 | if (format.code != fse->code) | ||
1766 | return -EINVAL; | ||
1767 | |||
1768 | format.code = fse->code; | ||
1769 | format.width = -1; | ||
1770 | format.height = -1; | ||
1771 | preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
1772 | fse->max_width = format.width; | ||
1773 | fse->max_height = format.height; | ||
1774 | |||
1775 | return 0; | ||
1776 | } | ||
1777 | |||
1778 | /* | ||
1779 | * preview_get_format - Handle get format by pads subdev method | ||
1780 | * @sd : pointer to v4l2 subdev structure | ||
1781 | * @fh : V4L2 subdev file handle | ||
1782 | * @fmt: pointer to v4l2 subdev format structure | ||
1783 | * return -EINVAL or zero on success | ||
1784 | */ | ||
1785 | static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1786 | struct v4l2_subdev_format *fmt) | ||
1787 | { | ||
1788 | struct isp_prev_device *prev = v4l2_get_subdevdata(sd); | ||
1789 | struct v4l2_mbus_framefmt *format; | ||
1790 | |||
1791 | format = __preview_get_format(prev, fh, fmt->pad, fmt->which); | ||
1792 | if (format == NULL) | ||
1793 | return -EINVAL; | ||
1794 | |||
1795 | fmt->format = *format; | ||
1796 | return 0; | ||
1797 | } | ||
1798 | |||
1799 | /* | ||
1800 | * preview_set_format - Handle set format by pads subdev method | ||
1801 | * @sd : pointer to v4l2 subdev structure | ||
1802 | * @fh : V4L2 subdev file handle | ||
1803 | * @fmt: pointer to v4l2 subdev format structure | ||
1804 | * return -EINVAL or zero on success | ||
1805 | */ | ||
1806 | static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1807 | struct v4l2_subdev_format *fmt) | ||
1808 | { | ||
1809 | struct isp_prev_device *prev = v4l2_get_subdevdata(sd); | ||
1810 | struct v4l2_mbus_framefmt *format; | ||
1811 | |||
1812 | format = __preview_get_format(prev, fh, fmt->pad, fmt->which); | ||
1813 | if (format == NULL) | ||
1814 | return -EINVAL; | ||
1815 | |||
1816 | preview_try_format(prev, fh, fmt->pad, &fmt->format, fmt->which); | ||
1817 | *format = fmt->format; | ||
1818 | |||
1819 | /* Propagate the format from sink to source */ | ||
1820 | if (fmt->pad == PREV_PAD_SINK) { | ||
1821 | format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, | ||
1822 | fmt->which); | ||
1823 | *format = fmt->format; | ||
1824 | preview_try_format(prev, fh, PREV_PAD_SOURCE, format, | ||
1825 | fmt->which); | ||
1826 | } | ||
1827 | |||
1828 | return 0; | ||
1829 | } | ||
1830 | |||
1831 | /* | ||
1832 | * preview_init_formats - Initialize formats on all pads | ||
1833 | * @sd: ISP preview V4L2 subdevice | ||
1834 | * @fh: V4L2 subdev file handle | ||
1835 | * | ||
1836 | * Initialize all pad formats with default values. If fh is not NULL, try | ||
1837 | * formats are initialized on the file handle. Otherwise active formats are | ||
1838 | * initialized on the device. | ||
1839 | */ | ||
1840 | static int preview_init_formats(struct v4l2_subdev *sd, | ||
1841 | struct v4l2_subdev_fh *fh) | ||
1842 | { | ||
1843 | struct v4l2_subdev_format format; | ||
1844 | |||
1845 | memset(&format, 0, sizeof(format)); | ||
1846 | format.pad = PREV_PAD_SINK; | ||
1847 | format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
1848 | format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
1849 | format.format.width = 4096; | ||
1850 | format.format.height = 4096; | ||
1851 | preview_set_format(sd, fh, &format); | ||
1852 | |||
1853 | return 0; | ||
1854 | } | ||
1855 | |||
1856 | /* subdev core operations */ | ||
1857 | static const struct v4l2_subdev_core_ops preview_v4l2_core_ops = { | ||
1858 | .ioctl = preview_ioctl, | ||
1859 | }; | ||
1860 | |||
1861 | /* subdev video operations */ | ||
1862 | static const struct v4l2_subdev_video_ops preview_v4l2_video_ops = { | ||
1863 | .s_stream = preview_set_stream, | ||
1864 | }; | ||
1865 | |||
1866 | /* subdev pad operations */ | ||
1867 | static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = { | ||
1868 | .enum_mbus_code = preview_enum_mbus_code, | ||
1869 | .enum_frame_size = preview_enum_frame_size, | ||
1870 | .get_fmt = preview_get_format, | ||
1871 | .set_fmt = preview_set_format, | ||
1872 | }; | ||
1873 | |||
1874 | /* subdev operations */ | ||
1875 | static const struct v4l2_subdev_ops preview_v4l2_ops = { | ||
1876 | .core = &preview_v4l2_core_ops, | ||
1877 | .video = &preview_v4l2_video_ops, | ||
1878 | .pad = &preview_v4l2_pad_ops, | ||
1879 | }; | ||
1880 | |||
1881 | /* subdev internal operations */ | ||
1882 | static const struct v4l2_subdev_internal_ops preview_v4l2_internal_ops = { | ||
1883 | .open = preview_init_formats, | ||
1884 | }; | ||
1885 | |||
1886 | /* ----------------------------------------------------------------------------- | ||
1887 | * Media entity operations | ||
1888 | */ | ||
1889 | |||
1890 | /* | ||
1891 | * preview_link_setup - Setup previewer connections. | ||
1892 | * @entity : Pointer to media entity structure | ||
1893 | * @local : Pointer to local pad array | ||
1894 | * @remote : Pointer to remote pad array | ||
1895 | * @flags : Link flags | ||
1896 | * return -EINVAL or zero on success | ||
1897 | */ | ||
1898 | static int preview_link_setup(struct media_entity *entity, | ||
1899 | const struct media_pad *local, | ||
1900 | const struct media_pad *remote, u32 flags) | ||
1901 | { | ||
1902 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
1903 | struct isp_prev_device *prev = v4l2_get_subdevdata(sd); | ||
1904 | |||
1905 | switch (local->index | media_entity_type(remote->entity)) { | ||
1906 | case PREV_PAD_SINK | MEDIA_ENT_T_DEVNODE: | ||
1907 | /* read from memory */ | ||
1908 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1909 | if (prev->input == PREVIEW_INPUT_CCDC) | ||
1910 | return -EBUSY; | ||
1911 | prev->input = PREVIEW_INPUT_MEMORY; | ||
1912 | } else { | ||
1913 | if (prev->input == PREVIEW_INPUT_MEMORY) | ||
1914 | prev->input = PREVIEW_INPUT_NONE; | ||
1915 | } | ||
1916 | break; | ||
1917 | |||
1918 | case PREV_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1919 | /* read from ccdc */ | ||
1920 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1921 | if (prev->input == PREVIEW_INPUT_MEMORY) | ||
1922 | return -EBUSY; | ||
1923 | prev->input = PREVIEW_INPUT_CCDC; | ||
1924 | } else { | ||
1925 | if (prev->input == PREVIEW_INPUT_CCDC) | ||
1926 | prev->input = PREVIEW_INPUT_NONE; | ||
1927 | } | ||
1928 | break; | ||
1929 | |||
1930 | /* | ||
1931 | * The ISP core doesn't support pipelines with multiple video outputs. | ||
1932 | * Revisit this when it will be implemented, and return -EBUSY for now. | ||
1933 | */ | ||
1934 | |||
1935 | case PREV_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: | ||
1936 | /* write to memory */ | ||
1937 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1938 | if (prev->output & ~PREVIEW_OUTPUT_MEMORY) | ||
1939 | return -EBUSY; | ||
1940 | prev->output |= PREVIEW_OUTPUT_MEMORY; | ||
1941 | } else { | ||
1942 | prev->output &= ~PREVIEW_OUTPUT_MEMORY; | ||
1943 | } | ||
1944 | break; | ||
1945 | |||
1946 | case PREV_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1947 | /* write to resizer */ | ||
1948 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1949 | if (prev->output & ~PREVIEW_OUTPUT_RESIZER) | ||
1950 | return -EBUSY; | ||
1951 | prev->output |= PREVIEW_OUTPUT_RESIZER; | ||
1952 | } else { | ||
1953 | prev->output &= ~PREVIEW_OUTPUT_RESIZER; | ||
1954 | } | ||
1955 | break; | ||
1956 | |||
1957 | default: | ||
1958 | return -EINVAL; | ||
1959 | } | ||
1960 | |||
1961 | return 0; | ||
1962 | } | ||
1963 | |||
1964 | /* media operations */ | ||
1965 | static const struct media_entity_operations preview_media_ops = { | ||
1966 | .link_setup = preview_link_setup, | ||
1967 | }; | ||
1968 | |||
1969 | /* | ||
1970 | * review_init_entities - Initialize subdev and media entity. | ||
1971 | * @prev : Pointer to preview structure | ||
1972 | * return -ENOMEM or zero on success | ||
1973 | */ | ||
1974 | static int preview_init_entities(struct isp_prev_device *prev) | ||
1975 | { | ||
1976 | struct v4l2_subdev *sd = &prev->subdev; | ||
1977 | struct media_pad *pads = prev->pads; | ||
1978 | struct media_entity *me = &sd->entity; | ||
1979 | int ret; | ||
1980 | |||
1981 | prev->input = PREVIEW_INPUT_NONE; | ||
1982 | |||
1983 | v4l2_subdev_init(sd, &preview_v4l2_ops); | ||
1984 | sd->internal_ops = &preview_v4l2_internal_ops; | ||
1985 | strlcpy(sd->name, "OMAP3 ISP preview", sizeof(sd->name)); | ||
1986 | sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
1987 | v4l2_set_subdevdata(sd, prev); | ||
1988 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1989 | |||
1990 | v4l2_ctrl_handler_init(&prev->ctrls, 2); | ||
1991 | v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_BRIGHTNESS, | ||
1992 | ISPPRV_BRIGHT_LOW, ISPPRV_BRIGHT_HIGH, | ||
1993 | ISPPRV_BRIGHT_STEP, ISPPRV_BRIGHT_DEF); | ||
1994 | v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_CONTRAST, | ||
1995 | ISPPRV_CONTRAST_LOW, ISPPRV_CONTRAST_HIGH, | ||
1996 | ISPPRV_CONTRAST_STEP, ISPPRV_CONTRAST_DEF); | ||
1997 | v4l2_ctrl_handler_setup(&prev->ctrls); | ||
1998 | sd->ctrl_handler = &prev->ctrls; | ||
1999 | |||
2000 | pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
2001 | pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | ||
2002 | |||
2003 | me->ops = &preview_media_ops; | ||
2004 | ret = media_entity_init(me, PREV_PADS_NUM, pads, 0); | ||
2005 | if (ret < 0) | ||
2006 | return ret; | ||
2007 | |||
2008 | preview_init_formats(sd, NULL); | ||
2009 | |||
2010 | /* According to the OMAP34xx TRM, video buffers need to be aligned on a | ||
2011 | * 32 bytes boundary. However, an undocumented hardware bug requires a | ||
2012 | * 64 bytes boundary at the preview engine input. | ||
2013 | */ | ||
2014 | prev->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
2015 | prev->video_in.ops = &preview_video_ops; | ||
2016 | prev->video_in.isp = to_isp_device(prev); | ||
2017 | prev->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; | ||
2018 | prev->video_in.bpl_alignment = 64; | ||
2019 | prev->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
2020 | prev->video_out.ops = &preview_video_ops; | ||
2021 | prev->video_out.isp = to_isp_device(prev); | ||
2022 | prev->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; | ||
2023 | prev->video_out.bpl_alignment = 32; | ||
2024 | |||
2025 | ret = omap3isp_video_init(&prev->video_in, "preview"); | ||
2026 | if (ret < 0) | ||
2027 | return ret; | ||
2028 | |||
2029 | ret = omap3isp_video_init(&prev->video_out, "preview"); | ||
2030 | if (ret < 0) | ||
2031 | return ret; | ||
2032 | |||
2033 | /* Connect the video nodes to the previewer subdev. */ | ||
2034 | ret = media_entity_create_link(&prev->video_in.video.entity, 0, | ||
2035 | &prev->subdev.entity, PREV_PAD_SINK, 0); | ||
2036 | if (ret < 0) | ||
2037 | return ret; | ||
2038 | |||
2039 | ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE, | ||
2040 | &prev->video_out.video.entity, 0, 0); | ||
2041 | if (ret < 0) | ||
2042 | return ret; | ||
2043 | |||
2044 | return 0; | ||
2045 | } | ||
2046 | |||
2047 | void omap3isp_preview_unregister_entities(struct isp_prev_device *prev) | ||
2048 | { | ||
2049 | media_entity_cleanup(&prev->subdev.entity); | ||
2050 | |||
2051 | v4l2_device_unregister_subdev(&prev->subdev); | ||
2052 | v4l2_ctrl_handler_free(&prev->ctrls); | ||
2053 | omap3isp_video_unregister(&prev->video_in); | ||
2054 | omap3isp_video_unregister(&prev->video_out); | ||
2055 | } | ||
2056 | |||
2057 | int omap3isp_preview_register_entities(struct isp_prev_device *prev, | ||
2058 | struct v4l2_device *vdev) | ||
2059 | { | ||
2060 | int ret; | ||
2061 | |||
2062 | /* Register the subdev and video nodes. */ | ||
2063 | ret = v4l2_device_register_subdev(vdev, &prev->subdev); | ||
2064 | if (ret < 0) | ||
2065 | goto error; | ||
2066 | |||
2067 | ret = omap3isp_video_register(&prev->video_in, vdev); | ||
2068 | if (ret < 0) | ||
2069 | goto error; | ||
2070 | |||
2071 | ret = omap3isp_video_register(&prev->video_out, vdev); | ||
2072 | if (ret < 0) | ||
2073 | goto error; | ||
2074 | |||
2075 | return 0; | ||
2076 | |||
2077 | error: | ||
2078 | omap3isp_preview_unregister_entities(prev); | ||
2079 | return ret; | ||
2080 | } | ||
2081 | |||
2082 | /* ----------------------------------------------------------------------------- | ||
2083 | * ISP previewer initialisation and cleanup | ||
2084 | */ | ||
2085 | |||
2086 | void omap3isp_preview_cleanup(struct isp_device *isp) | ||
2087 | { | ||
2088 | } | ||
2089 | |||
2090 | /* | ||
2091 | * isp_preview_init - Previewer initialization. | ||
2092 | * @dev : Pointer to ISP device | ||
2093 | * return -ENOMEM or zero on success | ||
2094 | */ | ||
2095 | int omap3isp_preview_init(struct isp_device *isp) | ||
2096 | { | ||
2097 | struct isp_prev_device *prev = &isp->isp_prev; | ||
2098 | int ret; | ||
2099 | |||
2100 | spin_lock_init(&prev->lock); | ||
2101 | init_waitqueue_head(&prev->wait); | ||
2102 | preview_init_params(prev); | ||
2103 | |||
2104 | ret = preview_init_entities(prev); | ||
2105 | if (ret < 0) | ||
2106 | goto out; | ||
2107 | |||
2108 | out: | ||
2109 | if (ret) | ||
2110 | omap3isp_preview_cleanup(isp); | ||
2111 | |||
2112 | return ret; | ||
2113 | } | ||
diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h new file mode 100644 index 00000000000..fa943bd05c7 --- /dev/null +++ b/drivers/media/video/omap3isp/isppreview.h | |||
@@ -0,0 +1,214 @@ | |||
1 | /* | ||
2 | * isppreview.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - Preview module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #ifndef OMAP3_ISP_PREVIEW_H | ||
28 | #define OMAP3_ISP_PREVIEW_H | ||
29 | |||
30 | #include <linux/omap3isp.h> | ||
31 | #include <linux/types.h> | ||
32 | #include <media/v4l2-ctrls.h> | ||
33 | |||
34 | #include "ispvideo.h" | ||
35 | |||
36 | #define ISPPRV_BRIGHT_STEP 0x1 | ||
37 | #define ISPPRV_BRIGHT_DEF 0x0 | ||
38 | #define ISPPRV_BRIGHT_LOW 0x0 | ||
39 | #define ISPPRV_BRIGHT_HIGH 0xFF | ||
40 | #define ISPPRV_BRIGHT_UNITS 0x1 | ||
41 | |||
42 | #define ISPPRV_CONTRAST_STEP 0x1 | ||
43 | #define ISPPRV_CONTRAST_DEF 0x10 | ||
44 | #define ISPPRV_CONTRAST_LOW 0x0 | ||
45 | #define ISPPRV_CONTRAST_HIGH 0xFF | ||
46 | #define ISPPRV_CONTRAST_UNITS 0x1 | ||
47 | |||
48 | #define NO_AVE 0x0 | ||
49 | #define AVE_2_PIX 0x1 | ||
50 | #define AVE_4_PIX 0x2 | ||
51 | #define AVE_8_PIX 0x3 | ||
52 | |||
53 | /* Features list */ | ||
54 | #define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH | ||
55 | #define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW | ||
56 | #define PREV_HORZ_MEDIAN_FILTER OMAP3ISP_PREV_HRZ_MED | ||
57 | #define PREV_CFA OMAP3ISP_PREV_CFA | ||
58 | #define PREV_CHROMA_SUPPRESS OMAP3ISP_PREV_CHROMA_SUPP | ||
59 | #define PREV_WB OMAP3ISP_PREV_WB | ||
60 | #define PREV_BLKADJ OMAP3ISP_PREV_BLKADJ | ||
61 | #define PREV_RGB2RGB OMAP3ISP_PREV_RGB2RGB | ||
62 | #define PREV_COLOR_CONV OMAP3ISP_PREV_COLOR_CONV | ||
63 | #define PREV_YCLIMITS OMAP3ISP_PREV_YC_LIMIT | ||
64 | #define PREV_DEFECT_COR OMAP3ISP_PREV_DEFECT_COR | ||
65 | #define PREV_GAMMA_BYPASS OMAP3ISP_PREV_GAMMABYPASS | ||
66 | #define PREV_DARK_FRAME_CAPTURE OMAP3ISP_PREV_DRK_FRM_CAPTURE | ||
67 | #define PREV_DARK_FRAME_SUBTRACT OMAP3ISP_PREV_DRK_FRM_SUBTRACT | ||
68 | #define PREV_LENS_SHADING OMAP3ISP_PREV_LENS_SHADING | ||
69 | #define PREV_NOISE_FILTER OMAP3ISP_PREV_NF | ||
70 | #define PREV_GAMMA OMAP3ISP_PREV_GAMMA | ||
71 | |||
72 | #define PREV_CONTRAST (1 << 17) | ||
73 | #define PREV_BRIGHTNESS (1 << 18) | ||
74 | #define PREV_AVERAGER (1 << 19) | ||
75 | #define PREV_FEATURES_END (1 << 20) | ||
76 | |||
77 | enum preview_input_entity { | ||
78 | PREVIEW_INPUT_NONE, | ||
79 | PREVIEW_INPUT_CCDC, | ||
80 | PREVIEW_INPUT_MEMORY, | ||
81 | }; | ||
82 | |||
83 | #define PREVIEW_OUTPUT_RESIZER (1 << 1) | ||
84 | #define PREVIEW_OUTPUT_MEMORY (1 << 2) | ||
85 | |||
86 | /* Configure byte layout of YUV image */ | ||
87 | enum preview_ycpos_mode { | ||
88 | YCPOS_YCrYCb = 0, | ||
89 | YCPOS_YCbYCr = 1, | ||
90 | YCPOS_CbYCrY = 2, | ||
91 | YCPOS_CrYCbY = 3 | ||
92 | }; | ||
93 | |||
94 | /* | ||
95 | * struct prev_params - Structure for all configuration | ||
96 | * @features: Set of features enabled. | ||
97 | * @cfa: CFA coefficients. | ||
98 | * @csup: Chroma suppression coefficients. | ||
99 | * @luma: Luma enhancement coefficients. | ||
100 | * @nf: Noise filter coefficients. | ||
101 | * @dcor: Noise filter coefficients. | ||
102 | * @gamma: Gamma coefficients. | ||
103 | * @wbal: White Balance parameters. | ||
104 | * @blk_adj: Black adjustment parameters. | ||
105 | * @rgb2rgb: RGB blending parameters. | ||
106 | * @rgb2ycbcr: RGB to ycbcr parameters. | ||
107 | * @hmed: Horizontal median filter. | ||
108 | * @yclimit: YC limits parameters. | ||
109 | * @average: Downsampling rate for averager. | ||
110 | * @contrast: Contrast. | ||
111 | * @brightness: Brightness. | ||
112 | */ | ||
113 | struct prev_params { | ||
114 | u32 features; | ||
115 | struct omap3isp_prev_cfa cfa; | ||
116 | struct omap3isp_prev_csup csup; | ||
117 | struct omap3isp_prev_luma luma; | ||
118 | struct omap3isp_prev_nf nf; | ||
119 | struct omap3isp_prev_dcor dcor; | ||
120 | struct omap3isp_prev_gtables gamma; | ||
121 | struct omap3isp_prev_wbal wbal; | ||
122 | struct omap3isp_prev_blkadj blk_adj; | ||
123 | struct omap3isp_prev_rgbtorgb rgb2rgb; | ||
124 | struct omap3isp_prev_csc rgb2ycbcr; | ||
125 | struct omap3isp_prev_hmed hmed; | ||
126 | struct omap3isp_prev_yclimit yclimit; | ||
127 | u8 average; | ||
128 | u8 contrast; | ||
129 | u8 brightness; | ||
130 | }; | ||
131 | |||
132 | /* | ||
133 | * struct isptables_update - Structure for Table Configuration. | ||
134 | * @update: Specifies which tables should be updated. | ||
135 | * @flag: Specifies which tables should be enabled. | ||
136 | * @nf: Pointer to structure for Noise Filter | ||
137 | * @lsc: Pointer to LSC gain table. (currently not used) | ||
138 | * @gamma: Pointer to gamma correction tables. | ||
139 | * @cfa: Pointer to color filter array configuration. | ||
140 | * @wbal: Pointer to colour and digital gain configuration. | ||
141 | */ | ||
142 | struct isptables_update { | ||
143 | u32 update; | ||
144 | u32 flag; | ||
145 | struct omap3isp_prev_nf *nf; | ||
146 | u32 *lsc; | ||
147 | struct omap3isp_prev_gtables *gamma; | ||
148 | struct omap3isp_prev_cfa *cfa; | ||
149 | struct omap3isp_prev_wbal *wbal; | ||
150 | }; | ||
151 | |||
152 | /* Sink and source previewer pads */ | ||
153 | #define PREV_PAD_SINK 0 | ||
154 | #define PREV_PAD_SOURCE 1 | ||
155 | #define PREV_PADS_NUM 2 | ||
156 | |||
157 | /* | ||
158 | * struct isp_prev_device - Structure for storing ISP Preview module information | ||
159 | * @subdev: V4L2 subdevice | ||
160 | * @pads: Media entity pads | ||
161 | * @formats: Active formats at the subdev pad | ||
162 | * @input: Module currently connected to the input pad | ||
163 | * @output: Bitmask of the active output | ||
164 | * @video_in: Input video entity | ||
165 | * @video_out: Output video entity | ||
166 | * @error: A hardware error occurred during capture | ||
167 | * @params: Module configuration data | ||
168 | * @shadow_update: If set, update the hardware configured in the next interrupt | ||
169 | * @underrun: Whether the preview entity has queued buffers on the output | ||
170 | * @state: Current preview pipeline state | ||
171 | * @lock: Shadow update lock | ||
172 | * @update: Bitmask of the parameters to be updated | ||
173 | * | ||
174 | * This structure is used to store the OMAP ISP Preview module Information. | ||
175 | */ | ||
176 | struct isp_prev_device { | ||
177 | struct v4l2_subdev subdev; | ||
178 | struct media_pad pads[PREV_PADS_NUM]; | ||
179 | struct v4l2_mbus_framefmt formats[PREV_PADS_NUM]; | ||
180 | |||
181 | struct v4l2_ctrl_handler ctrls; | ||
182 | |||
183 | enum preview_input_entity input; | ||
184 | unsigned int output; | ||
185 | struct isp_video video_in; | ||
186 | struct isp_video video_out; | ||
187 | unsigned int error; | ||
188 | |||
189 | struct prev_params params; | ||
190 | unsigned int shadow_update:1; | ||
191 | enum isp_pipeline_stream_state state; | ||
192 | wait_queue_head_t wait; | ||
193 | atomic_t stopping; | ||
194 | spinlock_t lock; | ||
195 | u32 update; | ||
196 | }; | ||
197 | |||
198 | struct isp_device; | ||
199 | |||
200 | int omap3isp_preview_init(struct isp_device *isp); | ||
201 | void omap3isp_preview_cleanup(struct isp_device *isp); | ||
202 | |||
203 | int omap3isp_preview_register_entities(struct isp_prev_device *prv, | ||
204 | struct v4l2_device *vdev); | ||
205 | void omap3isp_preview_unregister_entities(struct isp_prev_device *prv); | ||
206 | |||
207 | void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev); | ||
208 | void omap3isp_preview_isr(struct isp_prev_device *prev); | ||
209 | |||
210 | int omap3isp_preview_busy(struct isp_prev_device *isp_prev); | ||
211 | |||
212 | void omap3isp_preview_restore_context(struct isp_device *isp); | ||
213 | |||
214 | #endif /* OMAP3_ISP_PREVIEW_H */ | ||
diff --git a/drivers/media/video/omap3isp/ispqueue.c b/drivers/media/video/omap3isp/ispqueue.c new file mode 100644 index 00000000000..9c317148205 --- /dev/null +++ b/drivers/media/video/omap3isp/ispqueue.c | |||
@@ -0,0 +1,1153 @@ | |||
1 | /* | ||
2 | * ispqueue.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - Video buffers queue handling | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * | ||
8 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
9 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
23 | * 02110-1301 USA | ||
24 | */ | ||
25 | |||
26 | #include <asm/cacheflush.h> | ||
27 | #include <linux/dma-mapping.h> | ||
28 | #include <linux/mm.h> | ||
29 | #include <linux/pagemap.h> | ||
30 | #include <linux/poll.h> | ||
31 | #include <linux/scatterlist.h> | ||
32 | #include <linux/sched.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/vmalloc.h> | ||
35 | |||
36 | #include "ispqueue.h" | ||
37 | |||
38 | /* ----------------------------------------------------------------------------- | ||
39 | * Video buffers management | ||
40 | */ | ||
41 | |||
42 | /* | ||
43 | * isp_video_buffer_cache_sync - Keep the buffers coherent between CPU and ISP | ||
44 | * | ||
45 | * The typical operation required here is Cache Invalidation across | ||
46 | * the (user space) buffer address range. And this _must_ be done | ||
47 | * at QBUF stage (and *only* at QBUF). | ||
48 | * | ||
49 | * We try to use optimal cache invalidation function: | ||
50 | * - dmac_map_area: | ||
51 | * - used when the number of pages are _low_. | ||
52 | * - it becomes quite slow as the number of pages increase. | ||
53 | * - for 648x492 viewfinder (150 pages) it takes 1.3 ms. | ||
54 | * - for 5 Mpix buffer (2491 pages) it takes between 25-50 ms. | ||
55 | * | ||
56 | * - flush_cache_all: | ||
57 | * - used when the number of pages are _high_. | ||
58 | * - time taken in the range of 500-900 us. | ||
59 | * - has a higher penalty but, as whole dcache + icache is invalidated | ||
60 | */ | ||
61 | /* | ||
62 | * FIXME: dmac_inv_range crashes randomly on the user space buffer | ||
63 | * address. Fall back to flush_cache_all for now. | ||
64 | */ | ||
65 | #define ISP_CACHE_FLUSH_PAGES_MAX 0 | ||
66 | |||
67 | static void isp_video_buffer_cache_sync(struct isp_video_buffer *buf) | ||
68 | { | ||
69 | if (buf->skip_cache) | ||
70 | return; | ||
71 | |||
72 | if (buf->vbuf.m.userptr == 0 || buf->npages == 0 || | ||
73 | buf->npages > ISP_CACHE_FLUSH_PAGES_MAX) | ||
74 | flush_cache_all(); | ||
75 | else { | ||
76 | dmac_map_area((void *)buf->vbuf.m.userptr, buf->vbuf.length, | ||
77 | DMA_FROM_DEVICE); | ||
78 | outer_inv_range(buf->vbuf.m.userptr, | ||
79 | buf->vbuf.m.userptr + buf->vbuf.length); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * isp_video_buffer_lock_vma - Prevent VMAs from being unmapped | ||
85 | * | ||
86 | * Lock the VMAs underlying the given buffer into memory. This avoids the | ||
87 | * userspace buffer mapping from being swapped out, making VIPT cache handling | ||
88 | * easier. | ||
89 | * | ||
90 | * Note that the pages will not be freed as the buffers have been locked to | ||
91 | * memory using by a call to get_user_pages(), but the userspace mapping could | ||
92 | * still disappear if the VMAs are not locked. This is caused by the memory | ||
93 | * management code trying to be as lock-less as possible, which results in the | ||
94 | * userspace mapping manager not finding out that the pages are locked under | ||
95 | * some conditions. | ||
96 | */ | ||
97 | static int isp_video_buffer_lock_vma(struct isp_video_buffer *buf, int lock) | ||
98 | { | ||
99 | struct vm_area_struct *vma; | ||
100 | unsigned long start; | ||
101 | unsigned long end; | ||
102 | int ret = 0; | ||
103 | |||
104 | if (buf->vbuf.memory == V4L2_MEMORY_MMAP) | ||
105 | return 0; | ||
106 | |||
107 | /* We can be called from workqueue context if the current task dies to | ||
108 | * unlock the VMAs. In that case there's no current memory management | ||
109 | * context so unlocking can't be performed, but the VMAs have been or | ||
110 | * are getting destroyed anyway so it doesn't really matter. | ||
111 | */ | ||
112 | if (!current || !current->mm) | ||
113 | return lock ? -EINVAL : 0; | ||
114 | |||
115 | start = buf->vbuf.m.userptr; | ||
116 | end = buf->vbuf.m.userptr + buf->vbuf.length - 1; | ||
117 | |||
118 | down_write(¤t->mm->mmap_sem); | ||
119 | spin_lock(¤t->mm->page_table_lock); | ||
120 | |||
121 | do { | ||
122 | vma = find_vma(current->mm, start); | ||
123 | if (vma == NULL) { | ||
124 | ret = -EFAULT; | ||
125 | goto out; | ||
126 | } | ||
127 | |||
128 | if (lock) | ||
129 | vma->vm_flags |= VM_LOCKED; | ||
130 | else | ||
131 | vma->vm_flags &= ~VM_LOCKED; | ||
132 | |||
133 | start = vma->vm_end + 1; | ||
134 | } while (vma->vm_end < end); | ||
135 | |||
136 | if (lock) | ||
137 | buf->vm_flags |= VM_LOCKED; | ||
138 | else | ||
139 | buf->vm_flags &= ~VM_LOCKED; | ||
140 | |||
141 | out: | ||
142 | spin_unlock(¤t->mm->page_table_lock); | ||
143 | up_write(¤t->mm->mmap_sem); | ||
144 | return ret; | ||
145 | } | ||
146 | |||
147 | /* | ||
148 | * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer | ||
149 | * | ||
150 | * Iterate over the vmalloc'ed area and create a scatter list entry for every | ||
151 | * page. | ||
152 | */ | ||
153 | static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf) | ||
154 | { | ||
155 | struct scatterlist *sglist; | ||
156 | unsigned int npages; | ||
157 | unsigned int i; | ||
158 | void *addr; | ||
159 | |||
160 | addr = buf->vaddr; | ||
161 | npages = PAGE_ALIGN(buf->vbuf.length) >> PAGE_SHIFT; | ||
162 | |||
163 | sglist = vmalloc(npages * sizeof(*sglist)); | ||
164 | if (sglist == NULL) | ||
165 | return -ENOMEM; | ||
166 | |||
167 | sg_init_table(sglist, npages); | ||
168 | |||
169 | for (i = 0; i < npages; ++i, addr += PAGE_SIZE) { | ||
170 | struct page *page = vmalloc_to_page(addr); | ||
171 | |||
172 | if (page == NULL || PageHighMem(page)) { | ||
173 | vfree(sglist); | ||
174 | return -EINVAL; | ||
175 | } | ||
176 | |||
177 | sg_set_page(&sglist[i], page, PAGE_SIZE, 0); | ||
178 | } | ||
179 | |||
180 | buf->sglen = npages; | ||
181 | buf->sglist = sglist; | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer | ||
188 | * | ||
189 | * Walk the buffer pages list and create a 1:1 mapping to a scatter list. | ||
190 | */ | ||
191 | static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf) | ||
192 | { | ||
193 | struct scatterlist *sglist; | ||
194 | unsigned int offset = buf->offset; | ||
195 | unsigned int i; | ||
196 | |||
197 | sglist = vmalloc(buf->npages * sizeof(*sglist)); | ||
198 | if (sglist == NULL) | ||
199 | return -ENOMEM; | ||
200 | |||
201 | sg_init_table(sglist, buf->npages); | ||
202 | |||
203 | for (i = 0; i < buf->npages; ++i) { | ||
204 | if (PageHighMem(buf->pages[i])) { | ||
205 | vfree(sglist); | ||
206 | return -EINVAL; | ||
207 | } | ||
208 | |||
209 | sg_set_page(&sglist[i], buf->pages[i], PAGE_SIZE - offset, | ||
210 | offset); | ||
211 | offset = 0; | ||
212 | } | ||
213 | |||
214 | buf->sglen = buf->npages; | ||
215 | buf->sglist = sglist; | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer | ||
222 | * | ||
223 | * Create a scatter list of physically contiguous pages starting at the buffer | ||
224 | * memory physical address. | ||
225 | */ | ||
226 | static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf) | ||
227 | { | ||
228 | struct scatterlist *sglist; | ||
229 | unsigned int offset = buf->offset; | ||
230 | unsigned long pfn = buf->paddr >> PAGE_SHIFT; | ||
231 | unsigned int i; | ||
232 | |||
233 | sglist = vmalloc(buf->npages * sizeof(*sglist)); | ||
234 | if (sglist == NULL) | ||
235 | return -ENOMEM; | ||
236 | |||
237 | sg_init_table(sglist, buf->npages); | ||
238 | |||
239 | for (i = 0; i < buf->npages; ++i, ++pfn) { | ||
240 | sg_set_page(&sglist[i], pfn_to_page(pfn), PAGE_SIZE - offset, | ||
241 | offset); | ||
242 | /* PFNMAP buffers will not get DMA-mapped, set the DMA address | ||
243 | * manually. | ||
244 | */ | ||
245 | sg_dma_address(&sglist[i]) = (pfn << PAGE_SHIFT) + offset; | ||
246 | offset = 0; | ||
247 | } | ||
248 | |||
249 | buf->sglen = buf->npages; | ||
250 | buf->sglist = sglist; | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | /* | ||
256 | * isp_video_buffer_cleanup - Release pages for a userspace VMA. | ||
257 | * | ||
258 | * Release pages locked by a call isp_video_buffer_prepare_user and free the | ||
259 | * pages table. | ||
260 | */ | ||
261 | static void isp_video_buffer_cleanup(struct isp_video_buffer *buf) | ||
262 | { | ||
263 | enum dma_data_direction direction; | ||
264 | unsigned int i; | ||
265 | |||
266 | if (buf->queue->ops->buffer_cleanup) | ||
267 | buf->queue->ops->buffer_cleanup(buf); | ||
268 | |||
269 | if (!(buf->vm_flags & VM_PFNMAP)) { | ||
270 | direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE | ||
271 | ? DMA_FROM_DEVICE : DMA_TO_DEVICE; | ||
272 | dma_unmap_sg(buf->queue->dev, buf->sglist, buf->sglen, | ||
273 | direction); | ||
274 | } | ||
275 | |||
276 | vfree(buf->sglist); | ||
277 | buf->sglist = NULL; | ||
278 | buf->sglen = 0; | ||
279 | |||
280 | if (buf->pages != NULL) { | ||
281 | isp_video_buffer_lock_vma(buf, 0); | ||
282 | |||
283 | for (i = 0; i < buf->npages; ++i) | ||
284 | page_cache_release(buf->pages[i]); | ||
285 | |||
286 | vfree(buf->pages); | ||
287 | buf->pages = NULL; | ||
288 | } | ||
289 | |||
290 | buf->npages = 0; | ||
291 | buf->skip_cache = false; | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory. | ||
296 | * | ||
297 | * This function creates a list of pages for a userspace VMA. The number of | ||
298 | * pages is first computed based on the buffer size, and pages are then | ||
299 | * retrieved by a call to get_user_pages. | ||
300 | * | ||
301 | * Pages are pinned to memory by get_user_pages, making them available for DMA | ||
302 | * transfers. However, due to memory management optimization, it seems the | ||
303 | * get_user_pages doesn't guarantee that the pinned pages will not be written | ||
304 | * to swap and removed from the userspace mapping(s). When this happens, a page | ||
305 | * fault can be generated when accessing those unmapped pages. | ||
306 | * | ||
307 | * If the fault is triggered by a page table walk caused by VIPT cache | ||
308 | * management operations, the page fault handler might oops if the MM semaphore | ||
309 | * is held, as it can't handle kernel page faults in that case. To fix that, a | ||
310 | * fixup entry needs to be added to the cache management code, or the userspace | ||
311 | * VMA must be locked to avoid removing pages from the userspace mapping in the | ||
312 | * first place. | ||
313 | * | ||
314 | * If the number of pages retrieved is smaller than the number required by the | ||
315 | * buffer size, the function returns -EFAULT. | ||
316 | */ | ||
317 | static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf) | ||
318 | { | ||
319 | unsigned long data; | ||
320 | unsigned int first; | ||
321 | unsigned int last; | ||
322 | int ret; | ||
323 | |||
324 | data = buf->vbuf.m.userptr; | ||
325 | first = (data & PAGE_MASK) >> PAGE_SHIFT; | ||
326 | last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT; | ||
327 | |||
328 | buf->offset = data & ~PAGE_MASK; | ||
329 | buf->npages = last - first + 1; | ||
330 | buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0])); | ||
331 | if (buf->pages == NULL) | ||
332 | return -ENOMEM; | ||
333 | |||
334 | down_read(¤t->mm->mmap_sem); | ||
335 | ret = get_user_pages(current, current->mm, data & PAGE_MASK, | ||
336 | buf->npages, | ||
337 | buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, | ||
338 | buf->pages, NULL); | ||
339 | up_read(¤t->mm->mmap_sem); | ||
340 | |||
341 | if (ret != buf->npages) { | ||
342 | buf->npages = ret < 0 ? 0 : ret; | ||
343 | isp_video_buffer_cleanup(buf); | ||
344 | return -EFAULT; | ||
345 | } | ||
346 | |||
347 | ret = isp_video_buffer_lock_vma(buf, 1); | ||
348 | if (ret < 0) | ||
349 | isp_video_buffer_cleanup(buf); | ||
350 | |||
351 | return ret; | ||
352 | } | ||
353 | |||
354 | /* | ||
355 | * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer | ||
356 | * | ||
357 | * Userspace VM_PFNMAP buffers are supported only if they are contiguous in | ||
358 | * memory and if they span a single VMA. | ||
359 | * | ||
360 | * Return 0 if the buffer is valid, or -EFAULT otherwise. | ||
361 | */ | ||
362 | static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf) | ||
363 | { | ||
364 | struct vm_area_struct *vma; | ||
365 | unsigned long prev_pfn; | ||
366 | unsigned long this_pfn; | ||
367 | unsigned long start; | ||
368 | unsigned long end; | ||
369 | dma_addr_t pa; | ||
370 | int ret = -EFAULT; | ||
371 | |||
372 | start = buf->vbuf.m.userptr; | ||
373 | end = buf->vbuf.m.userptr + buf->vbuf.length - 1; | ||
374 | |||
375 | buf->offset = start & ~PAGE_MASK; | ||
376 | buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1; | ||
377 | buf->pages = NULL; | ||
378 | |||
379 | down_read(¤t->mm->mmap_sem); | ||
380 | vma = find_vma(current->mm, start); | ||
381 | if (vma == NULL || vma->vm_end < end) | ||
382 | goto done; | ||
383 | |||
384 | for (prev_pfn = 0; start <= end; start += PAGE_SIZE) { | ||
385 | ret = follow_pfn(vma, start, &this_pfn); | ||
386 | if (ret) | ||
387 | goto done; | ||
388 | |||
389 | if (prev_pfn == 0) | ||
390 | pa = this_pfn << PAGE_SHIFT; | ||
391 | else if (this_pfn != prev_pfn + 1) { | ||
392 | ret = -EFAULT; | ||
393 | goto done; | ||
394 | } | ||
395 | |||
396 | prev_pfn = this_pfn; | ||
397 | } | ||
398 | |||
399 | buf->paddr = pa + buf->offset; | ||
400 | ret = 0; | ||
401 | |||
402 | done: | ||
403 | up_read(¤t->mm->mmap_sem); | ||
404 | return ret; | ||
405 | } | ||
406 | |||
407 | /* | ||
408 | * isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address | ||
409 | * | ||
410 | * This function locates the VMAs for the buffer's userspace address and checks | ||
411 | * that their flags match. The only flag that we need to care for at the moment | ||
412 | * is VM_PFNMAP. | ||
413 | * | ||
414 | * The buffer vm_flags field is set to the first VMA flags. | ||
415 | * | ||
416 | * Return -EFAULT if no VMA can be found for part of the buffer, or if the VMAs | ||
417 | * have incompatible flags. | ||
418 | */ | ||
419 | static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf) | ||
420 | { | ||
421 | struct vm_area_struct *vma; | ||
422 | pgprot_t vm_page_prot; | ||
423 | unsigned long start; | ||
424 | unsigned long end; | ||
425 | int ret = -EFAULT; | ||
426 | |||
427 | start = buf->vbuf.m.userptr; | ||
428 | end = buf->vbuf.m.userptr + buf->vbuf.length - 1; | ||
429 | |||
430 | down_read(¤t->mm->mmap_sem); | ||
431 | |||
432 | do { | ||
433 | vma = find_vma(current->mm, start); | ||
434 | if (vma == NULL) | ||
435 | goto done; | ||
436 | |||
437 | if (start == buf->vbuf.m.userptr) { | ||
438 | buf->vm_flags = vma->vm_flags; | ||
439 | vm_page_prot = vma->vm_page_prot; | ||
440 | } | ||
441 | |||
442 | if ((buf->vm_flags ^ vma->vm_flags) & VM_PFNMAP) | ||
443 | goto done; | ||
444 | |||
445 | if (vm_page_prot != vma->vm_page_prot) | ||
446 | goto done; | ||
447 | |||
448 | start = vma->vm_end + 1; | ||
449 | } while (vma->vm_end < end); | ||
450 | |||
451 | /* Skip cache management to enhance performances for non-cached or | ||
452 | * write-combining buffers. | ||
453 | */ | ||
454 | if (vm_page_prot == pgprot_noncached(vm_page_prot) || | ||
455 | vm_page_prot == pgprot_writecombine(vm_page_prot)) | ||
456 | buf->skip_cache = true; | ||
457 | |||
458 | ret = 0; | ||
459 | |||
460 | done: | ||
461 | up_read(¤t->mm->mmap_sem); | ||
462 | return ret; | ||
463 | } | ||
464 | |||
465 | /* | ||
466 | * isp_video_buffer_prepare - Make a buffer ready for operation | ||
467 | * | ||
468 | * Preparing a buffer involves: | ||
469 | * | ||
470 | * - validating VMAs (userspace buffers only) | ||
471 | * - locking pages and VMAs into memory (userspace buffers only) | ||
472 | * - building page and scatter-gather lists | ||
473 | * - mapping buffers for DMA operation | ||
474 | * - performing driver-specific preparation | ||
475 | * | ||
476 | * The function must be called in userspace context with a valid mm context | ||
477 | * (this excludes cleanup paths such as sys_close when the userspace process | ||
478 | * segfaults). | ||
479 | */ | ||
480 | static int isp_video_buffer_prepare(struct isp_video_buffer *buf) | ||
481 | { | ||
482 | enum dma_data_direction direction; | ||
483 | int ret; | ||
484 | |||
485 | switch (buf->vbuf.memory) { | ||
486 | case V4L2_MEMORY_MMAP: | ||
487 | ret = isp_video_buffer_sglist_kernel(buf); | ||
488 | break; | ||
489 | |||
490 | case V4L2_MEMORY_USERPTR: | ||
491 | ret = isp_video_buffer_prepare_vm_flags(buf); | ||
492 | if (ret < 0) | ||
493 | return ret; | ||
494 | |||
495 | if (buf->vm_flags & VM_PFNMAP) { | ||
496 | ret = isp_video_buffer_prepare_pfnmap(buf); | ||
497 | if (ret < 0) | ||
498 | return ret; | ||
499 | |||
500 | ret = isp_video_buffer_sglist_pfnmap(buf); | ||
501 | } else { | ||
502 | ret = isp_video_buffer_prepare_user(buf); | ||
503 | if (ret < 0) | ||
504 | return ret; | ||
505 | |||
506 | ret = isp_video_buffer_sglist_user(buf); | ||
507 | } | ||
508 | break; | ||
509 | |||
510 | default: | ||
511 | return -EINVAL; | ||
512 | } | ||
513 | |||
514 | if (ret < 0) | ||
515 | goto done; | ||
516 | |||
517 | if (!(buf->vm_flags & VM_PFNMAP)) { | ||
518 | direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE | ||
519 | ? DMA_FROM_DEVICE : DMA_TO_DEVICE; | ||
520 | ret = dma_map_sg(buf->queue->dev, buf->sglist, buf->sglen, | ||
521 | direction); | ||
522 | if (ret != buf->sglen) { | ||
523 | ret = -EFAULT; | ||
524 | goto done; | ||
525 | } | ||
526 | } | ||
527 | |||
528 | if (buf->queue->ops->buffer_prepare) | ||
529 | ret = buf->queue->ops->buffer_prepare(buf); | ||
530 | |||
531 | done: | ||
532 | if (ret < 0) { | ||
533 | isp_video_buffer_cleanup(buf); | ||
534 | return ret; | ||
535 | } | ||
536 | |||
537 | return ret; | ||
538 | } | ||
539 | |||
540 | /* | ||
541 | * isp_video_queue_query - Query the status of a given buffer | ||
542 | * | ||
543 | * Locking: must be called with the queue lock held. | ||
544 | */ | ||
545 | static void isp_video_buffer_query(struct isp_video_buffer *buf, | ||
546 | struct v4l2_buffer *vbuf) | ||
547 | { | ||
548 | memcpy(vbuf, &buf->vbuf, sizeof(*vbuf)); | ||
549 | |||
550 | if (buf->vma_use_count) | ||
551 | vbuf->flags |= V4L2_BUF_FLAG_MAPPED; | ||
552 | |||
553 | switch (buf->state) { | ||
554 | case ISP_BUF_STATE_ERROR: | ||
555 | vbuf->flags |= V4L2_BUF_FLAG_ERROR; | ||
556 | case ISP_BUF_STATE_DONE: | ||
557 | vbuf->flags |= V4L2_BUF_FLAG_DONE; | ||
558 | case ISP_BUF_STATE_QUEUED: | ||
559 | case ISP_BUF_STATE_ACTIVE: | ||
560 | vbuf->flags |= V4L2_BUF_FLAG_QUEUED; | ||
561 | break; | ||
562 | case ISP_BUF_STATE_IDLE: | ||
563 | default: | ||
564 | break; | ||
565 | } | ||
566 | } | ||
567 | |||
568 | /* | ||
569 | * isp_video_buffer_wait - Wait for a buffer to be ready | ||
570 | * | ||
571 | * In non-blocking mode, return immediately with 0 if the buffer is ready or | ||
572 | * -EAGAIN if the buffer is in the QUEUED or ACTIVE state. | ||
573 | * | ||
574 | * In blocking mode, wait (interruptibly but with no timeout) on the buffer wait | ||
575 | * queue using the same condition. | ||
576 | */ | ||
577 | static int isp_video_buffer_wait(struct isp_video_buffer *buf, int nonblocking) | ||
578 | { | ||
579 | if (nonblocking) { | ||
580 | return (buf->state != ISP_BUF_STATE_QUEUED && | ||
581 | buf->state != ISP_BUF_STATE_ACTIVE) | ||
582 | ? 0 : -EAGAIN; | ||
583 | } | ||
584 | |||
585 | return wait_event_interruptible(buf->wait, | ||
586 | buf->state != ISP_BUF_STATE_QUEUED && | ||
587 | buf->state != ISP_BUF_STATE_ACTIVE); | ||
588 | } | ||
589 | |||
590 | /* ----------------------------------------------------------------------------- | ||
591 | * Queue management | ||
592 | */ | ||
593 | |||
594 | /* | ||
595 | * isp_video_queue_free - Free video buffers memory | ||
596 | * | ||
597 | * Buffers can only be freed if the queue isn't streaming and if no buffer is | ||
598 | * mapped to userspace. Return -EBUSY if those conditions aren't statisfied. | ||
599 | * | ||
600 | * This function must be called with the queue lock held. | ||
601 | */ | ||
602 | static int isp_video_queue_free(struct isp_video_queue *queue) | ||
603 | { | ||
604 | unsigned int i; | ||
605 | |||
606 | if (queue->streaming) | ||
607 | return -EBUSY; | ||
608 | |||
609 | for (i = 0; i < queue->count; ++i) { | ||
610 | if (queue->buffers[i]->vma_use_count != 0) | ||
611 | return -EBUSY; | ||
612 | } | ||
613 | |||
614 | for (i = 0; i < queue->count; ++i) { | ||
615 | struct isp_video_buffer *buf = queue->buffers[i]; | ||
616 | |||
617 | isp_video_buffer_cleanup(buf); | ||
618 | |||
619 | vfree(buf->vaddr); | ||
620 | buf->vaddr = NULL; | ||
621 | |||
622 | kfree(buf); | ||
623 | queue->buffers[i] = NULL; | ||
624 | } | ||
625 | |||
626 | INIT_LIST_HEAD(&queue->queue); | ||
627 | queue->count = 0; | ||
628 | return 0; | ||
629 | } | ||
630 | |||
631 | /* | ||
632 | * isp_video_queue_alloc - Allocate video buffers memory | ||
633 | * | ||
634 | * This function must be called with the queue lock held. | ||
635 | */ | ||
636 | static int isp_video_queue_alloc(struct isp_video_queue *queue, | ||
637 | unsigned int nbuffers, | ||
638 | unsigned int size, enum v4l2_memory memory) | ||
639 | { | ||
640 | struct isp_video_buffer *buf; | ||
641 | unsigned int i; | ||
642 | void *mem; | ||
643 | int ret; | ||
644 | |||
645 | /* Start by freeing the buffers. */ | ||
646 | ret = isp_video_queue_free(queue); | ||
647 | if (ret < 0) | ||
648 | return ret; | ||
649 | |||
650 | /* Bail out of no buffers should be allocated. */ | ||
651 | if (nbuffers == 0) | ||
652 | return 0; | ||
653 | |||
654 | /* Initialize the allocated buffers. */ | ||
655 | for (i = 0; i < nbuffers; ++i) { | ||
656 | buf = kzalloc(queue->bufsize, GFP_KERNEL); | ||
657 | if (buf == NULL) | ||
658 | break; | ||
659 | |||
660 | if (memory == V4L2_MEMORY_MMAP) { | ||
661 | /* Allocate video buffers memory for mmap mode. Align | ||
662 | * the size to the page size. | ||
663 | */ | ||
664 | mem = vmalloc_32_user(PAGE_ALIGN(size)); | ||
665 | if (mem == NULL) { | ||
666 | kfree(buf); | ||
667 | break; | ||
668 | } | ||
669 | |||
670 | buf->vbuf.m.offset = i * PAGE_ALIGN(size); | ||
671 | buf->vaddr = mem; | ||
672 | } | ||
673 | |||
674 | buf->vbuf.index = i; | ||
675 | buf->vbuf.length = size; | ||
676 | buf->vbuf.type = queue->type; | ||
677 | buf->vbuf.field = V4L2_FIELD_NONE; | ||
678 | buf->vbuf.memory = memory; | ||
679 | |||
680 | buf->queue = queue; | ||
681 | init_waitqueue_head(&buf->wait); | ||
682 | |||
683 | queue->buffers[i] = buf; | ||
684 | } | ||
685 | |||
686 | if (i == 0) | ||
687 | return -ENOMEM; | ||
688 | |||
689 | queue->count = i; | ||
690 | return nbuffers; | ||
691 | } | ||
692 | |||
693 | /** | ||
694 | * omap3isp_video_queue_cleanup - Clean up the video buffers queue | ||
695 | * @queue: Video buffers queue | ||
696 | * | ||
697 | * Free all allocated resources and clean up the video buffers queue. The queue | ||
698 | * must not be busy (no ongoing video stream) and buffers must have been | ||
699 | * unmapped. | ||
700 | * | ||
701 | * Return 0 on success or -EBUSY if the queue is busy or buffers haven't been | ||
702 | * unmapped. | ||
703 | */ | ||
704 | int omap3isp_video_queue_cleanup(struct isp_video_queue *queue) | ||
705 | { | ||
706 | return isp_video_queue_free(queue); | ||
707 | } | ||
708 | |||
709 | /** | ||
710 | * omap3isp_video_queue_init - Initialize the video buffers queue | ||
711 | * @queue: Video buffers queue | ||
712 | * @type: V4L2 buffer type (capture or output) | ||
713 | * @ops: Driver-specific queue operations | ||
714 | * @dev: Device used for DMA operations | ||
715 | * @bufsize: Size of the driver-specific buffer structure | ||
716 | * | ||
717 | * Initialize the video buffers queue with the supplied parameters. | ||
718 | * | ||
719 | * The queue type must be one of V4L2_BUF_TYPE_VIDEO_CAPTURE or | ||
720 | * V4L2_BUF_TYPE_VIDEO_OUTPUT. Other buffer types are not supported yet. | ||
721 | * | ||
722 | * Buffer objects will be allocated using the given buffer size to allow room | ||
723 | * for driver-specific fields. Driver-specific buffer structures must start | ||
724 | * with a struct isp_video_buffer field. Drivers with no driver-specific buffer | ||
725 | * structure must pass the size of the isp_video_buffer structure in the bufsize | ||
726 | * parameter. | ||
727 | * | ||
728 | * Return 0 on success. | ||
729 | */ | ||
730 | int omap3isp_video_queue_init(struct isp_video_queue *queue, | ||
731 | enum v4l2_buf_type type, | ||
732 | const struct isp_video_queue_operations *ops, | ||
733 | struct device *dev, unsigned int bufsize) | ||
734 | { | ||
735 | INIT_LIST_HEAD(&queue->queue); | ||
736 | mutex_init(&queue->lock); | ||
737 | spin_lock_init(&queue->irqlock); | ||
738 | |||
739 | queue->type = type; | ||
740 | queue->ops = ops; | ||
741 | queue->dev = dev; | ||
742 | queue->bufsize = bufsize; | ||
743 | |||
744 | return 0; | ||
745 | } | ||
746 | |||
747 | /* ----------------------------------------------------------------------------- | ||
748 | * V4L2 operations | ||
749 | */ | ||
750 | |||
751 | /** | ||
752 | * omap3isp_video_queue_reqbufs - Allocate video buffers memory | ||
753 | * | ||
754 | * This function is intended to be used as a VIDIOC_REQBUFS ioctl handler. It | ||
755 | * allocated video buffer objects and, for MMAP buffers, buffer memory. | ||
756 | * | ||
757 | * If the number of buffers is 0, all buffers are freed and the function returns | ||
758 | * without performing any allocation. | ||
759 | * | ||
760 | * If the number of buffers is not 0, currently allocated buffers (if any) are | ||
761 | * freed and the requested number of buffers are allocated. Depending on | ||
762 | * driver-specific requirements and on memory availability, a number of buffer | ||
763 | * smaller or bigger than requested can be allocated. This isn't considered as | ||
764 | * an error. | ||
765 | * | ||
766 | * Return 0 on success or one of the following error codes: | ||
767 | * | ||
768 | * -EINVAL if the buffer type or index are invalid | ||
769 | * -EBUSY if the queue is busy (streaming or buffers mapped) | ||
770 | * -ENOMEM if the buffers can't be allocated due to an out-of-memory condition | ||
771 | */ | ||
772 | int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue, | ||
773 | struct v4l2_requestbuffers *rb) | ||
774 | { | ||
775 | unsigned int nbuffers = rb->count; | ||
776 | unsigned int size; | ||
777 | int ret; | ||
778 | |||
779 | if (rb->type != queue->type) | ||
780 | return -EINVAL; | ||
781 | |||
782 | queue->ops->queue_prepare(queue, &nbuffers, &size); | ||
783 | if (size == 0) | ||
784 | return -EINVAL; | ||
785 | |||
786 | nbuffers = min_t(unsigned int, nbuffers, ISP_VIDEO_MAX_BUFFERS); | ||
787 | |||
788 | mutex_lock(&queue->lock); | ||
789 | |||
790 | ret = isp_video_queue_alloc(queue, nbuffers, size, rb->memory); | ||
791 | if (ret < 0) | ||
792 | goto done; | ||
793 | |||
794 | rb->count = ret; | ||
795 | ret = 0; | ||
796 | |||
797 | done: | ||
798 | mutex_unlock(&queue->lock); | ||
799 | return ret; | ||
800 | } | ||
801 | |||
802 | /** | ||
803 | * omap3isp_video_queue_querybuf - Query the status of a buffer in a queue | ||
804 | * | ||
805 | * This function is intended to be used as a VIDIOC_QUERYBUF ioctl handler. It | ||
806 | * returns the status of a given video buffer. | ||
807 | * | ||
808 | * Return 0 on success or -EINVAL if the buffer type or index are invalid. | ||
809 | */ | ||
810 | int omap3isp_video_queue_querybuf(struct isp_video_queue *queue, | ||
811 | struct v4l2_buffer *vbuf) | ||
812 | { | ||
813 | struct isp_video_buffer *buf; | ||
814 | int ret = 0; | ||
815 | |||
816 | if (vbuf->type != queue->type) | ||
817 | return -EINVAL; | ||
818 | |||
819 | mutex_lock(&queue->lock); | ||
820 | |||
821 | if (vbuf->index >= queue->count) { | ||
822 | ret = -EINVAL; | ||
823 | goto done; | ||
824 | } | ||
825 | |||
826 | buf = queue->buffers[vbuf->index]; | ||
827 | isp_video_buffer_query(buf, vbuf); | ||
828 | |||
829 | done: | ||
830 | mutex_unlock(&queue->lock); | ||
831 | return ret; | ||
832 | } | ||
833 | |||
834 | /** | ||
835 | * omap3isp_video_queue_qbuf - Queue a buffer | ||
836 | * | ||
837 | * This function is intended to be used as a VIDIOC_QBUF ioctl handler. | ||
838 | * | ||
839 | * The v4l2_buffer structure passed from userspace is first sanity tested. If | ||
840 | * sane, the buffer is then processed and added to the main queue and, if the | ||
841 | * queue is streaming, to the IRQ queue. | ||
842 | * | ||
843 | * Before being enqueued, USERPTR buffers are checked for address changes. If | ||
844 | * the buffer has a different userspace address, the old memory area is unlocked | ||
845 | * and the new memory area is locked. | ||
846 | */ | ||
847 | int omap3isp_video_queue_qbuf(struct isp_video_queue *queue, | ||
848 | struct v4l2_buffer *vbuf) | ||
849 | { | ||
850 | struct isp_video_buffer *buf; | ||
851 | unsigned long flags; | ||
852 | int ret = -EINVAL; | ||
853 | |||
854 | if (vbuf->type != queue->type) | ||
855 | goto done; | ||
856 | |||
857 | mutex_lock(&queue->lock); | ||
858 | |||
859 | if (vbuf->index >= queue->count) | ||
860 | goto done; | ||
861 | |||
862 | buf = queue->buffers[vbuf->index]; | ||
863 | |||
864 | if (vbuf->memory != buf->vbuf.memory) | ||
865 | goto done; | ||
866 | |||
867 | if (buf->state != ISP_BUF_STATE_IDLE) | ||
868 | goto done; | ||
869 | |||
870 | if (vbuf->memory == V4L2_MEMORY_USERPTR && | ||
871 | vbuf->m.userptr != buf->vbuf.m.userptr) { | ||
872 | isp_video_buffer_cleanup(buf); | ||
873 | buf->vbuf.m.userptr = vbuf->m.userptr; | ||
874 | buf->prepared = 0; | ||
875 | } | ||
876 | |||
877 | if (!buf->prepared) { | ||
878 | ret = isp_video_buffer_prepare(buf); | ||
879 | if (ret < 0) | ||
880 | goto done; | ||
881 | buf->prepared = 1; | ||
882 | } | ||
883 | |||
884 | isp_video_buffer_cache_sync(buf); | ||
885 | |||
886 | buf->state = ISP_BUF_STATE_QUEUED; | ||
887 | list_add_tail(&buf->stream, &queue->queue); | ||
888 | |||
889 | if (queue->streaming) { | ||
890 | spin_lock_irqsave(&queue->irqlock, flags); | ||
891 | queue->ops->buffer_queue(buf); | ||
892 | spin_unlock_irqrestore(&queue->irqlock, flags); | ||
893 | } | ||
894 | |||
895 | ret = 0; | ||
896 | |||
897 | done: | ||
898 | mutex_unlock(&queue->lock); | ||
899 | return ret; | ||
900 | } | ||
901 | |||
902 | /** | ||
903 | * omap3isp_video_queue_dqbuf - Dequeue a buffer | ||
904 | * | ||
905 | * This function is intended to be used as a VIDIOC_DQBUF ioctl handler. | ||
906 | * | ||
907 | * The v4l2_buffer structure passed from userspace is first sanity tested. If | ||
908 | * sane, the buffer is then processed and added to the main queue and, if the | ||
909 | * queue is streaming, to the IRQ queue. | ||
910 | * | ||
911 | * Before being enqueued, USERPTR buffers are checked for address changes. If | ||
912 | * the buffer has a different userspace address, the old memory area is unlocked | ||
913 | * and the new memory area is locked. | ||
914 | */ | ||
915 | int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue, | ||
916 | struct v4l2_buffer *vbuf, int nonblocking) | ||
917 | { | ||
918 | struct isp_video_buffer *buf; | ||
919 | int ret; | ||
920 | |||
921 | if (vbuf->type != queue->type) | ||
922 | return -EINVAL; | ||
923 | |||
924 | mutex_lock(&queue->lock); | ||
925 | |||
926 | if (list_empty(&queue->queue)) { | ||
927 | ret = -EINVAL; | ||
928 | goto done; | ||
929 | } | ||
930 | |||
931 | buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream); | ||
932 | ret = isp_video_buffer_wait(buf, nonblocking); | ||
933 | if (ret < 0) | ||
934 | goto done; | ||
935 | |||
936 | list_del(&buf->stream); | ||
937 | |||
938 | isp_video_buffer_query(buf, vbuf); | ||
939 | buf->state = ISP_BUF_STATE_IDLE; | ||
940 | vbuf->flags &= ~V4L2_BUF_FLAG_QUEUED; | ||
941 | |||
942 | done: | ||
943 | mutex_unlock(&queue->lock); | ||
944 | return ret; | ||
945 | } | ||
946 | |||
947 | /** | ||
948 | * omap3isp_video_queue_streamon - Start streaming | ||
949 | * | ||
950 | * This function is intended to be used as a VIDIOC_STREAMON ioctl handler. It | ||
951 | * starts streaming on the queue and calls the buffer_queue operation for all | ||
952 | * queued buffers. | ||
953 | * | ||
954 | * Return 0 on success. | ||
955 | */ | ||
956 | int omap3isp_video_queue_streamon(struct isp_video_queue *queue) | ||
957 | { | ||
958 | struct isp_video_buffer *buf; | ||
959 | unsigned long flags; | ||
960 | |||
961 | mutex_lock(&queue->lock); | ||
962 | |||
963 | if (queue->streaming) | ||
964 | goto done; | ||
965 | |||
966 | queue->streaming = 1; | ||
967 | |||
968 | spin_lock_irqsave(&queue->irqlock, flags); | ||
969 | list_for_each_entry(buf, &queue->queue, stream) | ||
970 | queue->ops->buffer_queue(buf); | ||
971 | spin_unlock_irqrestore(&queue->irqlock, flags); | ||
972 | |||
973 | done: | ||
974 | mutex_unlock(&queue->lock); | ||
975 | return 0; | ||
976 | } | ||
977 | |||
978 | /** | ||
979 | * omap3isp_video_queue_streamoff - Stop streaming | ||
980 | * | ||
981 | * This function is intended to be used as a VIDIOC_STREAMOFF ioctl handler. It | ||
982 | * stops streaming on the queue and wakes up all the buffers. | ||
983 | * | ||
984 | * Drivers must stop the hardware and synchronize with interrupt handlers and/or | ||
985 | * delayed works before calling this function to make sure no buffer will be | ||
986 | * touched by the driver and/or hardware. | ||
987 | */ | ||
988 | void omap3isp_video_queue_streamoff(struct isp_video_queue *queue) | ||
989 | { | ||
990 | struct isp_video_buffer *buf; | ||
991 | unsigned long flags; | ||
992 | unsigned int i; | ||
993 | |||
994 | mutex_lock(&queue->lock); | ||
995 | |||
996 | if (!queue->streaming) | ||
997 | goto done; | ||
998 | |||
999 | queue->streaming = 0; | ||
1000 | |||
1001 | spin_lock_irqsave(&queue->irqlock, flags); | ||
1002 | for (i = 0; i < queue->count; ++i) { | ||
1003 | buf = queue->buffers[i]; | ||
1004 | |||
1005 | if (buf->state == ISP_BUF_STATE_ACTIVE) | ||
1006 | wake_up(&buf->wait); | ||
1007 | |||
1008 | buf->state = ISP_BUF_STATE_IDLE; | ||
1009 | } | ||
1010 | spin_unlock_irqrestore(&queue->irqlock, flags); | ||
1011 | |||
1012 | INIT_LIST_HEAD(&queue->queue); | ||
1013 | |||
1014 | done: | ||
1015 | mutex_unlock(&queue->lock); | ||
1016 | } | ||
1017 | |||
1018 | /** | ||
1019 | * omap3isp_video_queue_discard_done - Discard all buffers marked as DONE | ||
1020 | * | ||
1021 | * This function is intended to be used with suspend/resume operations. It | ||
1022 | * discards all 'done' buffers as they would be too old to be requested after | ||
1023 | * resume. | ||
1024 | * | ||
1025 | * Drivers must stop the hardware and synchronize with interrupt handlers and/or | ||
1026 | * delayed works before calling this function to make sure no buffer will be | ||
1027 | * touched by the driver and/or hardware. | ||
1028 | */ | ||
1029 | void omap3isp_video_queue_discard_done(struct isp_video_queue *queue) | ||
1030 | { | ||
1031 | struct isp_video_buffer *buf; | ||
1032 | unsigned int i; | ||
1033 | |||
1034 | mutex_lock(&queue->lock); | ||
1035 | |||
1036 | if (!queue->streaming) | ||
1037 | goto done; | ||
1038 | |||
1039 | for (i = 0; i < queue->count; ++i) { | ||
1040 | buf = queue->buffers[i]; | ||
1041 | |||
1042 | if (buf->state == ISP_BUF_STATE_DONE) | ||
1043 | buf->state = ISP_BUF_STATE_ERROR; | ||
1044 | } | ||
1045 | |||
1046 | done: | ||
1047 | mutex_unlock(&queue->lock); | ||
1048 | } | ||
1049 | |||
1050 | static void isp_video_queue_vm_open(struct vm_area_struct *vma) | ||
1051 | { | ||
1052 | struct isp_video_buffer *buf = vma->vm_private_data; | ||
1053 | |||
1054 | buf->vma_use_count++; | ||
1055 | } | ||
1056 | |||
1057 | static void isp_video_queue_vm_close(struct vm_area_struct *vma) | ||
1058 | { | ||
1059 | struct isp_video_buffer *buf = vma->vm_private_data; | ||
1060 | |||
1061 | buf->vma_use_count--; | ||
1062 | } | ||
1063 | |||
1064 | static const struct vm_operations_struct isp_video_queue_vm_ops = { | ||
1065 | .open = isp_video_queue_vm_open, | ||
1066 | .close = isp_video_queue_vm_close, | ||
1067 | }; | ||
1068 | |||
1069 | /** | ||
1070 | * omap3isp_video_queue_mmap - Map buffers to userspace | ||
1071 | * | ||
1072 | * This function is intended to be used as an mmap() file operation handler. It | ||
1073 | * maps a buffer to userspace based on the VMA offset. | ||
1074 | * | ||
1075 | * Only buffers of memory type MMAP are supported. | ||
1076 | */ | ||
1077 | int omap3isp_video_queue_mmap(struct isp_video_queue *queue, | ||
1078 | struct vm_area_struct *vma) | ||
1079 | { | ||
1080 | struct isp_video_buffer *uninitialized_var(buf); | ||
1081 | unsigned long size; | ||
1082 | unsigned int i; | ||
1083 | int ret = 0; | ||
1084 | |||
1085 | mutex_lock(&queue->lock); | ||
1086 | |||
1087 | for (i = 0; i < queue->count; ++i) { | ||
1088 | buf = queue->buffers[i]; | ||
1089 | if ((buf->vbuf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) | ||
1090 | break; | ||
1091 | } | ||
1092 | |||
1093 | if (i == queue->count) { | ||
1094 | ret = -EINVAL; | ||
1095 | goto done; | ||
1096 | } | ||
1097 | |||
1098 | size = vma->vm_end - vma->vm_start; | ||
1099 | |||
1100 | if (buf->vbuf.memory != V4L2_MEMORY_MMAP || | ||
1101 | size != PAGE_ALIGN(buf->vbuf.length)) { | ||
1102 | ret = -EINVAL; | ||
1103 | goto done; | ||
1104 | } | ||
1105 | |||
1106 | ret = remap_vmalloc_range(vma, buf->vaddr, 0); | ||
1107 | if (ret < 0) | ||
1108 | goto done; | ||
1109 | |||
1110 | vma->vm_ops = &isp_video_queue_vm_ops; | ||
1111 | vma->vm_private_data = buf; | ||
1112 | isp_video_queue_vm_open(vma); | ||
1113 | |||
1114 | done: | ||
1115 | mutex_unlock(&queue->lock); | ||
1116 | return ret; | ||
1117 | } | ||
1118 | |||
1119 | /** | ||
1120 | * omap3isp_video_queue_poll - Poll video queue state | ||
1121 | * | ||
1122 | * This function is intended to be used as a poll() file operation handler. It | ||
1123 | * polls the state of the video buffer at the front of the queue and returns an | ||
1124 | * events mask. | ||
1125 | * | ||
1126 | * If no buffer is present at the front of the queue, POLLERR is returned. | ||
1127 | */ | ||
1128 | unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue, | ||
1129 | struct file *file, poll_table *wait) | ||
1130 | { | ||
1131 | struct isp_video_buffer *buf; | ||
1132 | unsigned int mask = 0; | ||
1133 | |||
1134 | mutex_lock(&queue->lock); | ||
1135 | if (list_empty(&queue->queue)) { | ||
1136 | mask |= POLLERR; | ||
1137 | goto done; | ||
1138 | } | ||
1139 | buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream); | ||
1140 | |||
1141 | poll_wait(file, &buf->wait, wait); | ||
1142 | if (buf->state == ISP_BUF_STATE_DONE || | ||
1143 | buf->state == ISP_BUF_STATE_ERROR) { | ||
1144 | if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1145 | mask |= POLLIN | POLLRDNORM; | ||
1146 | else | ||
1147 | mask |= POLLOUT | POLLWRNORM; | ||
1148 | } | ||
1149 | |||
1150 | done: | ||
1151 | mutex_unlock(&queue->lock); | ||
1152 | return mask; | ||
1153 | } | ||
diff --git a/drivers/media/video/omap3isp/ispqueue.h b/drivers/media/video/omap3isp/ispqueue.h new file mode 100644 index 00000000000..92c5a12157d --- /dev/null +++ b/drivers/media/video/omap3isp/ispqueue.h | |||
@@ -0,0 +1,187 @@ | |||
1 | /* | ||
2 | * ispqueue.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - Video buffers queue handling | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * | ||
8 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
9 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
23 | * 02110-1301 USA | ||
24 | */ | ||
25 | |||
26 | #ifndef OMAP3_ISP_QUEUE_H | ||
27 | #define OMAP3_ISP_QUEUE_H | ||
28 | |||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/list.h> | ||
31 | #include <linux/mutex.h> | ||
32 | #include <linux/videodev2.h> | ||
33 | #include <linux/wait.h> | ||
34 | |||
35 | struct isp_video_queue; | ||
36 | struct page; | ||
37 | struct scatterlist; | ||
38 | |||
39 | #define ISP_VIDEO_MAX_BUFFERS 16 | ||
40 | |||
41 | /** | ||
42 | * enum isp_video_buffer_state - ISP video buffer state | ||
43 | * @ISP_BUF_STATE_IDLE: The buffer is under userspace control (dequeued | ||
44 | * or not queued yet). | ||
45 | * @ISP_BUF_STATE_QUEUED: The buffer has been queued but isn't used by the | ||
46 | * device yet. | ||
47 | * @ISP_BUF_STATE_ACTIVE: The buffer is in use for an active video transfer. | ||
48 | * @ISP_BUF_STATE_ERROR: The device is done with the buffer and an error | ||
49 | * occurred. For capture device the buffer likely contains corrupted data or | ||
50 | * no data at all. | ||
51 | * @ISP_BUF_STATE_DONE: The device is done with the buffer and no error occurred. | ||
52 | * For capture devices the buffer contains valid data. | ||
53 | */ | ||
54 | enum isp_video_buffer_state { | ||
55 | ISP_BUF_STATE_IDLE, | ||
56 | ISP_BUF_STATE_QUEUED, | ||
57 | ISP_BUF_STATE_ACTIVE, | ||
58 | ISP_BUF_STATE_ERROR, | ||
59 | ISP_BUF_STATE_DONE, | ||
60 | }; | ||
61 | |||
62 | /** | ||
63 | * struct isp_video_buffer - ISP video buffer | ||
64 | * @vma_use_count: Number of times the buffer is mmap'ed to userspace | ||
65 | * @stream: List head for insertion into main queue | ||
66 | * @queue: ISP buffers queue this buffer belongs to | ||
67 | * @prepared: Whether the buffer has been prepared | ||
68 | * @skip_cache: Whether to skip cache management operations for this buffer | ||
69 | * @vaddr: Memory virtual address (for kernel buffers) | ||
70 | * @vm_flags: Buffer VMA flags (for userspace buffers) | ||
71 | * @offset: Offset inside the first page (for userspace buffers) | ||
72 | * @npages: Number of pages (for userspace buffers) | ||
73 | * @pages: Pages table (for userspace non-VM_PFNMAP buffers) | ||
74 | * @paddr: Memory physical address (for userspace VM_PFNMAP buffers) | ||
75 | * @sglen: Number of elements in the scatter list (for non-VM_PFNMAP buffers) | ||
76 | * @sglist: Scatter list (for non-VM_PFNMAP buffers) | ||
77 | * @vbuf: V4L2 buffer | ||
78 | * @irqlist: List head for insertion into IRQ queue | ||
79 | * @state: Current buffer state | ||
80 | * @wait: Wait queue to signal buffer completion | ||
81 | */ | ||
82 | struct isp_video_buffer { | ||
83 | unsigned long vma_use_count; | ||
84 | struct list_head stream; | ||
85 | struct isp_video_queue *queue; | ||
86 | unsigned int prepared:1; | ||
87 | bool skip_cache; | ||
88 | |||
89 | /* For kernel buffers. */ | ||
90 | void *vaddr; | ||
91 | |||
92 | /* For userspace buffers. */ | ||
93 | unsigned long vm_flags; | ||
94 | unsigned long offset; | ||
95 | unsigned int npages; | ||
96 | struct page **pages; | ||
97 | dma_addr_t paddr; | ||
98 | |||
99 | /* For all buffers except VM_PFNMAP. */ | ||
100 | unsigned int sglen; | ||
101 | struct scatterlist *sglist; | ||
102 | |||
103 | /* Touched by the interrupt handler. */ | ||
104 | struct v4l2_buffer vbuf; | ||
105 | struct list_head irqlist; | ||
106 | enum isp_video_buffer_state state; | ||
107 | wait_queue_head_t wait; | ||
108 | }; | ||
109 | |||
110 | #define to_isp_video_buffer(vb) container_of(vb, struct isp_video_buffer, vb) | ||
111 | |||
112 | /** | ||
113 | * struct isp_video_queue_operations - Driver-specific operations | ||
114 | * @queue_prepare: Called before allocating buffers. Drivers should clamp the | ||
115 | * number of buffers according to their requirements, and must return the | ||
116 | * buffer size in bytes. | ||
117 | * @buffer_prepare: Called the first time a buffer is queued, or after changing | ||
118 | * the userspace memory address for a USERPTR buffer, with the queue lock | ||
119 | * held. Drivers should perform device-specific buffer preparation (such as | ||
120 | * mapping the buffer memory in an IOMMU). This operation is optional. | ||
121 | * @buffer_queue: Called when a buffer is being added to the queue with the | ||
122 | * queue irqlock spinlock held. | ||
123 | * @buffer_cleanup: Called before freeing buffers, or before changing the | ||
124 | * userspace memory address for a USERPTR buffer, with the queue lock held. | ||
125 | * Drivers must perform cleanup operations required to undo the | ||
126 | * buffer_prepare call. This operation is optional. | ||
127 | */ | ||
128 | struct isp_video_queue_operations { | ||
129 | void (*queue_prepare)(struct isp_video_queue *queue, | ||
130 | unsigned int *nbuffers, unsigned int *size); | ||
131 | int (*buffer_prepare)(struct isp_video_buffer *buf); | ||
132 | void (*buffer_queue)(struct isp_video_buffer *buf); | ||
133 | void (*buffer_cleanup)(struct isp_video_buffer *buf); | ||
134 | }; | ||
135 | |||
136 | /** | ||
137 | * struct isp_video_queue - ISP video buffers queue | ||
138 | * @type: Type of video buffers handled by this queue | ||
139 | * @ops: Queue operations | ||
140 | * @dev: Device used for DMA operations | ||
141 | * @bufsize: Size of a driver-specific buffer object | ||
142 | * @count: Number of currently allocated buffers | ||
143 | * @buffers: ISP video buffers | ||
144 | * @lock: Mutex to protect access to the buffers, main queue and state | ||
145 | * @irqlock: Spinlock to protect access to the IRQ queue | ||
146 | * @streaming: Queue state, indicates whether the queue is streaming | ||
147 | * @queue: List of all queued buffers | ||
148 | */ | ||
149 | struct isp_video_queue { | ||
150 | enum v4l2_buf_type type; | ||
151 | const struct isp_video_queue_operations *ops; | ||
152 | struct device *dev; | ||
153 | unsigned int bufsize; | ||
154 | |||
155 | unsigned int count; | ||
156 | struct isp_video_buffer *buffers[ISP_VIDEO_MAX_BUFFERS]; | ||
157 | struct mutex lock; | ||
158 | spinlock_t irqlock; | ||
159 | |||
160 | unsigned int streaming:1; | ||
161 | |||
162 | struct list_head queue; | ||
163 | }; | ||
164 | |||
165 | int omap3isp_video_queue_cleanup(struct isp_video_queue *queue); | ||
166 | int omap3isp_video_queue_init(struct isp_video_queue *queue, | ||
167 | enum v4l2_buf_type type, | ||
168 | const struct isp_video_queue_operations *ops, | ||
169 | struct device *dev, unsigned int bufsize); | ||
170 | |||
171 | int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue, | ||
172 | struct v4l2_requestbuffers *rb); | ||
173 | int omap3isp_video_queue_querybuf(struct isp_video_queue *queue, | ||
174 | struct v4l2_buffer *vbuf); | ||
175 | int omap3isp_video_queue_qbuf(struct isp_video_queue *queue, | ||
176 | struct v4l2_buffer *vbuf); | ||
177 | int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue, | ||
178 | struct v4l2_buffer *vbuf, int nonblocking); | ||
179 | int omap3isp_video_queue_streamon(struct isp_video_queue *queue); | ||
180 | void omap3isp_video_queue_streamoff(struct isp_video_queue *queue); | ||
181 | void omap3isp_video_queue_discard_done(struct isp_video_queue *queue); | ||
182 | int omap3isp_video_queue_mmap(struct isp_video_queue *queue, | ||
183 | struct vm_area_struct *vma); | ||
184 | unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue, | ||
185 | struct file *file, poll_table *wait); | ||
186 | |||
187 | #endif /* OMAP3_ISP_QUEUE_H */ | ||
diff --git a/drivers/media/video/omap3isp/ispreg.h b/drivers/media/video/omap3isp/ispreg.h new file mode 100644 index 00000000000..69f6af6f6b9 --- /dev/null +++ b/drivers/media/video/omap3isp/ispreg.h | |||
@@ -0,0 +1,1589 @@ | |||
1 | /* | ||
2 | * ispreg.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - Registers definitions | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #ifndef OMAP3_ISP_REG_H | ||
28 | #define OMAP3_ISP_REG_H | ||
29 | |||
30 | #include <plat/omap34xx.h> | ||
31 | |||
32 | |||
33 | #define CM_CAM_MCLK_HZ 172800000 /* Hz */ | ||
34 | |||
35 | /* ISP Submodules offset */ | ||
36 | |||
37 | #define OMAP3ISP_REG_BASE OMAP3430_ISP_BASE | ||
38 | #define OMAP3ISP_REG(offset) (OMAP3ISP_REG_BASE + (offset)) | ||
39 | |||
40 | #define OMAP3ISP_CCP2_REG_OFFSET 0x0400 | ||
41 | #define OMAP3ISP_CCP2_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
42 | OMAP3ISP_CCP2_REG_OFFSET) | ||
43 | #define OMAP3ISP_CCP2_REG(offset) (OMAP3ISP_CCP2_REG_BASE + (offset)) | ||
44 | |||
45 | #define OMAP3ISP_CCDC_REG_OFFSET 0x0600 | ||
46 | #define OMAP3ISP_CCDC_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
47 | OMAP3ISP_CCDC_REG_OFFSET) | ||
48 | #define OMAP3ISP_CCDC_REG(offset) (OMAP3ISP_CCDC_REG_BASE + (offset)) | ||
49 | |||
50 | #define OMAP3ISP_HIST_REG_OFFSET 0x0A00 | ||
51 | #define OMAP3ISP_HIST_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
52 | OMAP3ISP_HIST_REG_OFFSET) | ||
53 | #define OMAP3ISP_HIST_REG(offset) (OMAP3ISP_HIST_REG_BASE + (offset)) | ||
54 | |||
55 | #define OMAP3ISP_H3A_REG_OFFSET 0x0C00 | ||
56 | #define OMAP3ISP_H3A_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
57 | OMAP3ISP_H3A_REG_OFFSET) | ||
58 | #define OMAP3ISP_H3A_REG(offset) (OMAP3ISP_H3A_REG_BASE + (offset)) | ||
59 | |||
60 | #define OMAP3ISP_PREV_REG_OFFSET 0x0E00 | ||
61 | #define OMAP3ISP_PREV_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
62 | OMAP3ISP_PREV_REG_OFFSET) | ||
63 | #define OMAP3ISP_PREV_REG(offset) (OMAP3ISP_PREV_REG_BASE + (offset)) | ||
64 | |||
65 | #define OMAP3ISP_RESZ_REG_OFFSET 0x1000 | ||
66 | #define OMAP3ISP_RESZ_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
67 | OMAP3ISP_RESZ_REG_OFFSET) | ||
68 | #define OMAP3ISP_RESZ_REG(offset) (OMAP3ISP_RESZ_REG_BASE + (offset)) | ||
69 | |||
70 | #define OMAP3ISP_SBL_REG_OFFSET 0x1200 | ||
71 | #define OMAP3ISP_SBL_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
72 | OMAP3ISP_SBL_REG_OFFSET) | ||
73 | #define OMAP3ISP_SBL_REG(offset) (OMAP3ISP_SBL_REG_BASE + (offset)) | ||
74 | |||
75 | #define OMAP3ISP_CSI2A_REGS1_REG_OFFSET 0x1800 | ||
76 | #define OMAP3ISP_CSI2A_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
77 | OMAP3ISP_CSI2A_REGS1_REG_OFFSET) | ||
78 | #define OMAP3ISP_CSI2A_REGS1_REG(offset) \ | ||
79 | (OMAP3ISP_CSI2A_REGS1_REG_BASE + (offset)) | ||
80 | |||
81 | #define OMAP3ISP_CSIPHY2_REG_OFFSET 0x1970 | ||
82 | #define OMAP3ISP_CSIPHY2_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
83 | OMAP3ISP_CSIPHY2_REG_OFFSET) | ||
84 | #define OMAP3ISP_CSIPHY2_REG(offset) (OMAP3ISP_CSIPHY2_REG_BASE + (offset)) | ||
85 | |||
86 | #define OMAP3ISP_CSI2A_REGS2_REG_OFFSET 0x19C0 | ||
87 | #define OMAP3ISP_CSI2A_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
88 | OMAP3ISP_CSI2A_REGS2_REG_OFFSET) | ||
89 | #define OMAP3ISP_CSI2A_REGS2_REG(offset) \ | ||
90 | (OMAP3ISP_CSI2A_REGS2_REG_BASE + (offset)) | ||
91 | |||
92 | #define OMAP3ISP_CSI2C_REGS1_REG_OFFSET 0x1C00 | ||
93 | #define OMAP3ISP_CSI2C_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
94 | OMAP3ISP_CSI2C_REGS1_REG_OFFSET) | ||
95 | #define OMAP3ISP_CSI2C_REGS1_REG(offset) \ | ||
96 | (OMAP3ISP_CSI2C_REGS1_REG_BASE + (offset)) | ||
97 | |||
98 | #define OMAP3ISP_CSIPHY1_REG_OFFSET 0x1D70 | ||
99 | #define OMAP3ISP_CSIPHY1_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
100 | OMAP3ISP_CSIPHY1_REG_OFFSET) | ||
101 | #define OMAP3ISP_CSIPHY1_REG(offset) (OMAP3ISP_CSIPHY1_REG_BASE + (offset)) | ||
102 | |||
103 | #define OMAP3ISP_CSI2C_REGS2_REG_OFFSET 0x1DC0 | ||
104 | #define OMAP3ISP_CSI2C_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \ | ||
105 | OMAP3ISP_CSI2C_REGS2_REG_OFFSET) | ||
106 | #define OMAP3ISP_CSI2C_REGS2_REG(offset) \ | ||
107 | (OMAP3ISP_CSI2C_REGS2_REG_BASE + (offset)) | ||
108 | |||
109 | /* ISP module register offset */ | ||
110 | |||
111 | #define ISP_REVISION (0x000) | ||
112 | #define ISP_SYSCONFIG (0x004) | ||
113 | #define ISP_SYSSTATUS (0x008) | ||
114 | #define ISP_IRQ0ENABLE (0x00C) | ||
115 | #define ISP_IRQ0STATUS (0x010) | ||
116 | #define ISP_IRQ1ENABLE (0x014) | ||
117 | #define ISP_IRQ1STATUS (0x018) | ||
118 | #define ISP_TCTRL_GRESET_LENGTH (0x030) | ||
119 | #define ISP_TCTRL_PSTRB_REPLAY (0x034) | ||
120 | #define ISP_CTRL (0x040) | ||
121 | #define ISP_SECURE (0x044) | ||
122 | #define ISP_TCTRL_CTRL (0x050) | ||
123 | #define ISP_TCTRL_FRAME (0x054) | ||
124 | #define ISP_TCTRL_PSTRB_DELAY (0x058) | ||
125 | #define ISP_TCTRL_STRB_DELAY (0x05C) | ||
126 | #define ISP_TCTRL_SHUT_DELAY (0x060) | ||
127 | #define ISP_TCTRL_PSTRB_LENGTH (0x064) | ||
128 | #define ISP_TCTRL_STRB_LENGTH (0x068) | ||
129 | #define ISP_TCTRL_SHUT_LENGTH (0x06C) | ||
130 | #define ISP_PING_PONG_ADDR (0x070) | ||
131 | #define ISP_PING_PONG_MEM_RANGE (0x074) | ||
132 | #define ISP_PING_PONG_BUF_SIZE (0x078) | ||
133 | |||
134 | /* CCP2 receiver registers */ | ||
135 | |||
136 | #define ISPCCP2_REVISION (0x000) | ||
137 | #define ISPCCP2_SYSCONFIG (0x004) | ||
138 | #define ISPCCP2_SYSCONFIG_SOFT_RESET (1 << 1) | ||
139 | #define ISPCCP2_SYSCONFIG_AUTO_IDLE 0x1 | ||
140 | #define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT 12 | ||
141 | #define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_FORCE \ | ||
142 | (0x0 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
143 | #define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_NO \ | ||
144 | (0x1 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
145 | #define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SMART \ | ||
146 | (0x2 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
147 | #define ISPCCP2_SYSSTATUS (0x008) | ||
148 | #define ISPCCP2_SYSSTATUS_RESET_DONE (1 << 0) | ||
149 | #define ISPCCP2_LC01_IRQENABLE (0x00C) | ||
150 | #define ISPCCP2_LC01_IRQSTATUS (0x010) | ||
151 | #define ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ (1 << 11) | ||
152 | #define ISPCCP2_LC01_IRQSTATUS_LC0_LE_IRQ (1 << 10) | ||
153 | #define ISPCCP2_LC01_IRQSTATUS_LC0_LS_IRQ (1 << 9) | ||
154 | #define ISPCCP2_LC01_IRQSTATUS_LC0_FE_IRQ (1 << 8) | ||
155 | #define ISPCCP2_LC01_IRQSTATUS_LC0_COUNT_IRQ (1 << 7) | ||
156 | #define ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ (1 << 5) | ||
157 | #define ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ (1 << 4) | ||
158 | #define ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ (1 << 3) | ||
159 | #define ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ (1 << 2) | ||
160 | #define ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ (1 << 1) | ||
161 | #define ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ (1 << 0) | ||
162 | |||
163 | #define ISPCCP2_LC23_IRQENABLE (0x014) | ||
164 | #define ISPCCP2_LC23_IRQSTATUS (0x018) | ||
165 | #define ISPCCP2_LCM_IRQENABLE (0x02C) | ||
166 | #define ISPCCP2_LCM_IRQSTATUS_EOF_IRQ (1 << 0) | ||
167 | #define ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ (1 << 1) | ||
168 | #define ISPCCP2_LCM_IRQSTATUS (0x030) | ||
169 | #define ISPCCP2_CTRL (0x040) | ||
170 | #define ISPCCP2_CTRL_IF_EN (1 << 0) | ||
171 | #define ISPCCP2_CTRL_PHY_SEL (1 << 1) | ||
172 | #define ISPCCP2_CTRL_PHY_SEL_CLOCK (0 << 1) | ||
173 | #define ISPCCP2_CTRL_PHY_SEL_STROBE (1 << 1) | ||
174 | #define ISPCCP2_CTRL_PHY_SEL_MASK 0x1 | ||
175 | #define ISPCCP2_CTRL_PHY_SEL_SHIFT 1 | ||
176 | #define ISPCCP2_CTRL_IO_OUT_SEL (1 << 2) | ||
177 | #define ISPCCP2_CTRL_MODE (1 << 4) | ||
178 | #define ISPCCP2_CTRL_VP_CLK_FORCE_ON (1 << 9) | ||
179 | #define ISPCCP2_CTRL_INV (1 << 10) | ||
180 | #define ISPCCP2_CTRL_INV_MASK 0x1 | ||
181 | #define ISPCCP2_CTRL_INV_SHIFT 10 | ||
182 | #define ISPCCP2_CTRL_VP_ONLY_EN (1 << 11) | ||
183 | #define ISPCCP2_CTRL_VP_CLK_POL (1 << 12) | ||
184 | #define ISPCCP2_CTRL_VPCLK_DIV_SHIFT 15 | ||
185 | #define ISPCCP2_CTRL_VPCLK_DIV_MASK 0x1ffff /* [31:15] */ | ||
186 | #define ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT 8 /* 3430 bits */ | ||
187 | #define ISPCCP2_CTRL_VP_OUT_CTRL_MASK 0x3 /* 3430 bits */ | ||
188 | #define ISPCCP2_DBG (0x044) | ||
189 | #define ISPCCP2_GNQ (0x048) | ||
190 | #define ISPCCP2_LCx_CTRL(x) ((0x050)+0x30*(x)) | ||
191 | #define ISPCCP2_LCx_CTRL_CHAN_EN (1 << 0) | ||
192 | #define ISPCCP2_LCx_CTRL_CRC_EN (1 << 19) | ||
193 | #define ISPCCP2_LCx_CTRL_CRC_MASK 0x1 | ||
194 | #define ISPCCP2_LCx_CTRL_CRC_SHIFT 2 | ||
195 | #define ISPCCP2_LCx_CTRL_CRC_SHIFT_15_0 19 | ||
196 | #define ISPCCP2_LCx_CTRL_REGION_EN (1 << 1) | ||
197 | #define ISPCCP2_LCx_CTRL_REGION_MASK 0x1 | ||
198 | #define ISPCCP2_LCx_CTRL_REGION_SHIFT 1 | ||
199 | #define ISPCCP2_LCx_CTRL_FORMAT_MASK_15_0 0x3f | ||
200 | #define ISPCCP2_LCx_CTRL_FORMAT_SHIFT_15_0 0x2 | ||
201 | #define ISPCCP2_LCx_CTRL_FORMAT_MASK 0x1f | ||
202 | #define ISPCCP2_LCx_CTRL_FORMAT_SHIFT 0x3 | ||
203 | #define ISPCCP2_LCx_CODE(x) ((0x054)+0x30*(x)) | ||
204 | #define ISPCCP2_LCx_STAT_START(x) ((0x058)+0x30*(x)) | ||
205 | #define ISPCCP2_LCx_STAT_SIZE(x) ((0x05C)+0x30*(x)) | ||
206 | #define ISPCCP2_LCx_SOF_ADDR(x) ((0x060)+0x30*(x)) | ||
207 | #define ISPCCP2_LCx_EOF_ADDR(x) ((0x064)+0x30*(x)) | ||
208 | #define ISPCCP2_LCx_DAT_START(x) ((0x068)+0x30*(x)) | ||
209 | #define ISPCCP2_LCx_DAT_SIZE(x) ((0x06C)+0x30*(x)) | ||
210 | #define ISPCCP2_LCx_DAT_MASK 0xFFF | ||
211 | #define ISPCCP2_LCx_DAT_SHIFT 16 | ||
212 | #define ISPCCP2_LCx_DAT_PING_ADDR(x) ((0x070)+0x30*(x)) | ||
213 | #define ISPCCP2_LCx_DAT_PONG_ADDR(x) ((0x074)+0x30*(x)) | ||
214 | #define ISPCCP2_LCx_DAT_OFST(x) ((0x078)+0x30*(x)) | ||
215 | #define ISPCCP2_LCM_CTRL (0x1D0) | ||
216 | #define ISPCCP2_LCM_CTRL_CHAN_EN (1 << 0) | ||
217 | #define ISPCCP2_LCM_CTRL_DST_PORT (1 << 2) | ||
218 | #define ISPCCP2_LCM_CTRL_DST_PORT_SHIFT 2 | ||
219 | #define ISPCCP2_LCM_CTRL_READ_THROTTLE_SHIFT 3 | ||
220 | #define ISPCCP2_LCM_CTRL_READ_THROTTLE_MASK 0x11 | ||
221 | #define ISPCCP2_LCM_CTRL_BURST_SIZE_SHIFT 5 | ||
222 | #define ISPCCP2_LCM_CTRL_BURST_SIZE_MASK 0x7 | ||
223 | #define ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT 16 | ||
224 | #define ISPCCP2_LCM_CTRL_SRC_FORMAT_MASK 0x7 | ||
225 | #define ISPCCP2_LCM_CTRL_SRC_DECOMPR_SHIFT 20 | ||
226 | #define ISPCCP2_LCM_CTRL_SRC_DECOMPR_MASK 0x3 | ||
227 | #define ISPCCP2_LCM_CTRL_SRC_DPCM_PRED (1 << 22) | ||
228 | #define ISPCCP2_LCM_CTRL_SRC_PACK (1 << 23) | ||
229 | #define ISPCCP2_LCM_CTRL_DST_FORMAT_SHIFT 24 | ||
230 | #define ISPCCP2_LCM_CTRL_DST_FORMAT_MASK 0x7 | ||
231 | #define ISPCCP2_LCM_VSIZE (0x1D4) | ||
232 | #define ISPCCP2_LCM_VSIZE_SHIFT 16 | ||
233 | #define ISPCCP2_LCM_HSIZE (0x1D8) | ||
234 | #define ISPCCP2_LCM_HSIZE_SHIFT 16 | ||
235 | #define ISPCCP2_LCM_PREFETCH (0x1DC) | ||
236 | #define ISPCCP2_LCM_PREFETCH_SHIFT 3 | ||
237 | #define ISPCCP2_LCM_SRC_ADDR (0x1E0) | ||
238 | #define ISPCCP2_LCM_SRC_OFST (0x1E4) | ||
239 | #define ISPCCP2_LCM_DST_ADDR (0x1E8) | ||
240 | #define ISPCCP2_LCM_DST_OFST (0x1EC) | ||
241 | |||
242 | /* CCDC module register offset */ | ||
243 | |||
244 | #define ISPCCDC_PID (0x000) | ||
245 | #define ISPCCDC_PCR (0x004) | ||
246 | #define ISPCCDC_SYN_MODE (0x008) | ||
247 | #define ISPCCDC_HD_VD_WID (0x00C) | ||
248 | #define ISPCCDC_PIX_LINES (0x010) | ||
249 | #define ISPCCDC_HORZ_INFO (0x014) | ||
250 | #define ISPCCDC_VERT_START (0x018) | ||
251 | #define ISPCCDC_VERT_LINES (0x01C) | ||
252 | #define ISPCCDC_CULLING (0x020) | ||
253 | #define ISPCCDC_HSIZE_OFF (0x024) | ||
254 | #define ISPCCDC_SDOFST (0x028) | ||
255 | #define ISPCCDC_SDR_ADDR (0x02C) | ||
256 | #define ISPCCDC_CLAMP (0x030) | ||
257 | #define ISPCCDC_DCSUB (0x034) | ||
258 | #define ISPCCDC_COLPTN (0x038) | ||
259 | #define ISPCCDC_BLKCMP (0x03C) | ||
260 | #define ISPCCDC_FPC (0x040) | ||
261 | #define ISPCCDC_FPC_ADDR (0x044) | ||
262 | #define ISPCCDC_VDINT (0x048) | ||
263 | #define ISPCCDC_ALAW (0x04C) | ||
264 | #define ISPCCDC_REC656IF (0x050) | ||
265 | #define ISPCCDC_CFG (0x054) | ||
266 | #define ISPCCDC_FMTCFG (0x058) | ||
267 | #define ISPCCDC_FMT_HORZ (0x05C) | ||
268 | #define ISPCCDC_FMT_VERT (0x060) | ||
269 | #define ISPCCDC_FMT_ADDR0 (0x064) | ||
270 | #define ISPCCDC_FMT_ADDR1 (0x068) | ||
271 | #define ISPCCDC_FMT_ADDR2 (0x06C) | ||
272 | #define ISPCCDC_FMT_ADDR3 (0x070) | ||
273 | #define ISPCCDC_FMT_ADDR4 (0x074) | ||
274 | #define ISPCCDC_FMT_ADDR5 (0x078) | ||
275 | #define ISPCCDC_FMT_ADDR6 (0x07C) | ||
276 | #define ISPCCDC_FMT_ADDR7 (0x080) | ||
277 | #define ISPCCDC_PRGEVEN0 (0x084) | ||
278 | #define ISPCCDC_PRGEVEN1 (0x088) | ||
279 | #define ISPCCDC_PRGODD0 (0x08C) | ||
280 | #define ISPCCDC_PRGODD1 (0x090) | ||
281 | #define ISPCCDC_VP_OUT (0x094) | ||
282 | |||
283 | #define ISPCCDC_LSC_CONFIG (0x098) | ||
284 | #define ISPCCDC_LSC_INITIAL (0x09C) | ||
285 | #define ISPCCDC_LSC_TABLE_BASE (0x0A0) | ||
286 | #define ISPCCDC_LSC_TABLE_OFFSET (0x0A4) | ||
287 | |||
288 | /* SBL */ | ||
289 | #define ISPSBL_PCR 0x4 | ||
290 | #define ISPSBL_PCR_H3A_AEAWB_WBL_OVF (1 << 16) | ||
291 | #define ISPSBL_PCR_H3A_AF_WBL_OVF (1 << 17) | ||
292 | #define ISPSBL_PCR_RSZ4_WBL_OVF (1 << 18) | ||
293 | #define ISPSBL_PCR_RSZ3_WBL_OVF (1 << 19) | ||
294 | #define ISPSBL_PCR_RSZ2_WBL_OVF (1 << 20) | ||
295 | #define ISPSBL_PCR_RSZ1_WBL_OVF (1 << 21) | ||
296 | #define ISPSBL_PCR_PRV_WBL_OVF (1 << 22) | ||
297 | #define ISPSBL_PCR_CCDC_WBL_OVF (1 << 23) | ||
298 | #define ISPSBL_PCR_CCDCPRV_2_RSZ_OVF (1 << 24) | ||
299 | #define ISPSBL_PCR_CSIA_WBL_OVF (1 << 25) | ||
300 | #define ISPSBL_PCR_CSIB_WBL_OVF (1 << 26) | ||
301 | #define ISPSBL_CCDC_WR_0 (0x028) | ||
302 | #define ISPSBL_CCDC_WR_0_DATA_READY (1 << 21) | ||
303 | #define ISPSBL_CCDC_WR_1 (0x02C) | ||
304 | #define ISPSBL_CCDC_WR_2 (0x030) | ||
305 | #define ISPSBL_CCDC_WR_3 (0x034) | ||
306 | |||
307 | #define ISPSBL_SDR_REQ_EXP 0xF8 | ||
308 | #define ISPSBL_SDR_REQ_HIST_EXP_SHIFT 0 | ||
309 | #define ISPSBL_SDR_REQ_HIST_EXP_MASK (0x3FF) | ||
310 | #define ISPSBL_SDR_REQ_RSZ_EXP_SHIFT 10 | ||
311 | #define ISPSBL_SDR_REQ_RSZ_EXP_MASK (0x3FF << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT) | ||
312 | #define ISPSBL_SDR_REQ_PRV_EXP_SHIFT 20 | ||
313 | #define ISPSBL_SDR_REQ_PRV_EXP_MASK (0x3FF << ISPSBL_SDR_REQ_PRV_EXP_SHIFT) | ||
314 | |||
315 | /* Histogram registers */ | ||
316 | #define ISPHIST_PID (0x000) | ||
317 | #define ISPHIST_PCR (0x004) | ||
318 | #define ISPHIST_CNT (0x008) | ||
319 | #define ISPHIST_WB_GAIN (0x00C) | ||
320 | #define ISPHIST_R0_HORZ (0x010) | ||
321 | #define ISPHIST_R0_VERT (0x014) | ||
322 | #define ISPHIST_R1_HORZ (0x018) | ||
323 | #define ISPHIST_R1_VERT (0x01C) | ||
324 | #define ISPHIST_R2_HORZ (0x020) | ||
325 | #define ISPHIST_R2_VERT (0x024) | ||
326 | #define ISPHIST_R3_HORZ (0x028) | ||
327 | #define ISPHIST_R3_VERT (0x02C) | ||
328 | #define ISPHIST_ADDR (0x030) | ||
329 | #define ISPHIST_DATA (0x034) | ||
330 | #define ISPHIST_RADD (0x038) | ||
331 | #define ISPHIST_RADD_OFF (0x03C) | ||
332 | #define ISPHIST_H_V_INFO (0x040) | ||
333 | |||
334 | /* H3A module registers */ | ||
335 | #define ISPH3A_PID (0x000) | ||
336 | #define ISPH3A_PCR (0x004) | ||
337 | #define ISPH3A_AEWWIN1 (0x04C) | ||
338 | #define ISPH3A_AEWINSTART (0x050) | ||
339 | #define ISPH3A_AEWINBLK (0x054) | ||
340 | #define ISPH3A_AEWSUBWIN (0x058) | ||
341 | #define ISPH3A_AEWBUFST (0x05C) | ||
342 | #define ISPH3A_AFPAX1 (0x008) | ||
343 | #define ISPH3A_AFPAX2 (0x00C) | ||
344 | #define ISPH3A_AFPAXSTART (0x010) | ||
345 | #define ISPH3A_AFIIRSH (0x014) | ||
346 | #define ISPH3A_AFBUFST (0x018) | ||
347 | #define ISPH3A_AFCOEF010 (0x01C) | ||
348 | #define ISPH3A_AFCOEF032 (0x020) | ||
349 | #define ISPH3A_AFCOEF054 (0x024) | ||
350 | #define ISPH3A_AFCOEF076 (0x028) | ||
351 | #define ISPH3A_AFCOEF098 (0x02C) | ||
352 | #define ISPH3A_AFCOEF0010 (0x030) | ||
353 | #define ISPH3A_AFCOEF110 (0x034) | ||
354 | #define ISPH3A_AFCOEF132 (0x038) | ||
355 | #define ISPH3A_AFCOEF154 (0x03C) | ||
356 | #define ISPH3A_AFCOEF176 (0x040) | ||
357 | #define ISPH3A_AFCOEF198 (0x044) | ||
358 | #define ISPH3A_AFCOEF1010 (0x048) | ||
359 | |||
360 | #define ISPPRV_PCR (0x004) | ||
361 | #define ISPPRV_HORZ_INFO (0x008) | ||
362 | #define ISPPRV_VERT_INFO (0x00C) | ||
363 | #define ISPPRV_RSDR_ADDR (0x010) | ||
364 | #define ISPPRV_RADR_OFFSET (0x014) | ||
365 | #define ISPPRV_DSDR_ADDR (0x018) | ||
366 | #define ISPPRV_DRKF_OFFSET (0x01C) | ||
367 | #define ISPPRV_WSDR_ADDR (0x020) | ||
368 | #define ISPPRV_WADD_OFFSET (0x024) | ||
369 | #define ISPPRV_AVE (0x028) | ||
370 | #define ISPPRV_HMED (0x02C) | ||
371 | #define ISPPRV_NF (0x030) | ||
372 | #define ISPPRV_WB_DGAIN (0x034) | ||
373 | #define ISPPRV_WBGAIN (0x038) | ||
374 | #define ISPPRV_WBSEL (0x03C) | ||
375 | #define ISPPRV_CFA (0x040) | ||
376 | #define ISPPRV_BLKADJOFF (0x044) | ||
377 | #define ISPPRV_RGB_MAT1 (0x048) | ||
378 | #define ISPPRV_RGB_MAT2 (0x04C) | ||
379 | #define ISPPRV_RGB_MAT3 (0x050) | ||
380 | #define ISPPRV_RGB_MAT4 (0x054) | ||
381 | #define ISPPRV_RGB_MAT5 (0x058) | ||
382 | #define ISPPRV_RGB_OFF1 (0x05C) | ||
383 | #define ISPPRV_RGB_OFF2 (0x060) | ||
384 | #define ISPPRV_CSC0 (0x064) | ||
385 | #define ISPPRV_CSC1 (0x068) | ||
386 | #define ISPPRV_CSC2 (0x06C) | ||
387 | #define ISPPRV_CSC_OFFSET (0x070) | ||
388 | #define ISPPRV_CNT_BRT (0x074) | ||
389 | #define ISPPRV_CSUP (0x078) | ||
390 | #define ISPPRV_SETUP_YC (0x07C) | ||
391 | #define ISPPRV_SET_TBL_ADDR (0x080) | ||
392 | #define ISPPRV_SET_TBL_DATA (0x084) | ||
393 | #define ISPPRV_CDC_THR0 (0x090) | ||
394 | #define ISPPRV_CDC_THR1 (ISPPRV_CDC_THR0 + (0x4)) | ||
395 | #define ISPPRV_CDC_THR2 (ISPPRV_CDC_THR0 + (0x4) * 2) | ||
396 | #define ISPPRV_CDC_THR3 (ISPPRV_CDC_THR0 + (0x4) * 3) | ||
397 | |||
398 | #define ISPPRV_REDGAMMA_TABLE_ADDR 0x0000 | ||
399 | #define ISPPRV_GREENGAMMA_TABLE_ADDR 0x0400 | ||
400 | #define ISPPRV_BLUEGAMMA_TABLE_ADDR 0x0800 | ||
401 | #define ISPPRV_NF_TABLE_ADDR 0x0C00 | ||
402 | #define ISPPRV_YENH_TABLE_ADDR 0x1000 | ||
403 | #define ISPPRV_CFA_TABLE_ADDR 0x1400 | ||
404 | |||
405 | #define ISPPRV_MAXOUTPUT_WIDTH 1280 | ||
406 | #define ISPPRV_MAXOUTPUT_WIDTH_ES2 3300 | ||
407 | #define ISPPRV_MAXOUTPUT_WIDTH_3630 4096 | ||
408 | #define ISPRSZ_MIN_OUTPUT 64 | ||
409 | #define ISPRSZ_MAX_OUTPUT 3312 | ||
410 | |||
411 | /* Resizer module register offset */ | ||
412 | #define ISPRSZ_PID (0x000) | ||
413 | #define ISPRSZ_PCR (0x004) | ||
414 | #define ISPRSZ_CNT (0x008) | ||
415 | #define ISPRSZ_OUT_SIZE (0x00C) | ||
416 | #define ISPRSZ_IN_START (0x010) | ||
417 | #define ISPRSZ_IN_SIZE (0x014) | ||
418 | #define ISPRSZ_SDR_INADD (0x018) | ||
419 | #define ISPRSZ_SDR_INOFF (0x01C) | ||
420 | #define ISPRSZ_SDR_OUTADD (0x020) | ||
421 | #define ISPRSZ_SDR_OUTOFF (0x024) | ||
422 | #define ISPRSZ_HFILT10 (0x028) | ||
423 | #define ISPRSZ_HFILT32 (0x02C) | ||
424 | #define ISPRSZ_HFILT54 (0x030) | ||
425 | #define ISPRSZ_HFILT76 (0x034) | ||
426 | #define ISPRSZ_HFILT98 (0x038) | ||
427 | #define ISPRSZ_HFILT1110 (0x03C) | ||
428 | #define ISPRSZ_HFILT1312 (0x040) | ||
429 | #define ISPRSZ_HFILT1514 (0x044) | ||
430 | #define ISPRSZ_HFILT1716 (0x048) | ||
431 | #define ISPRSZ_HFILT1918 (0x04C) | ||
432 | #define ISPRSZ_HFILT2120 (0x050) | ||
433 | #define ISPRSZ_HFILT2322 (0x054) | ||
434 | #define ISPRSZ_HFILT2524 (0x058) | ||
435 | #define ISPRSZ_HFILT2726 (0x05C) | ||
436 | #define ISPRSZ_HFILT2928 (0x060) | ||
437 | #define ISPRSZ_HFILT3130 (0x064) | ||
438 | #define ISPRSZ_VFILT10 (0x068) | ||
439 | #define ISPRSZ_VFILT32 (0x06C) | ||
440 | #define ISPRSZ_VFILT54 (0x070) | ||
441 | #define ISPRSZ_VFILT76 (0x074) | ||
442 | #define ISPRSZ_VFILT98 (0x078) | ||
443 | #define ISPRSZ_VFILT1110 (0x07C) | ||
444 | #define ISPRSZ_VFILT1312 (0x080) | ||
445 | #define ISPRSZ_VFILT1514 (0x084) | ||
446 | #define ISPRSZ_VFILT1716 (0x088) | ||
447 | #define ISPRSZ_VFILT1918 (0x08C) | ||
448 | #define ISPRSZ_VFILT2120 (0x090) | ||
449 | #define ISPRSZ_VFILT2322 (0x094) | ||
450 | #define ISPRSZ_VFILT2524 (0x098) | ||
451 | #define ISPRSZ_VFILT2726 (0x09C) | ||
452 | #define ISPRSZ_VFILT2928 (0x0A0) | ||
453 | #define ISPRSZ_VFILT3130 (0x0A4) | ||
454 | #define ISPRSZ_YENH (0x0A8) | ||
455 | |||
456 | #define ISP_INT_CLR 0xFF113F11 | ||
457 | #define ISPPRV_PCR_EN 1 | ||
458 | #define ISPPRV_PCR_BUSY (1 << 1) | ||
459 | #define ISPPRV_PCR_SOURCE (1 << 2) | ||
460 | #define ISPPRV_PCR_ONESHOT (1 << 3) | ||
461 | #define ISPPRV_PCR_WIDTH (1 << 4) | ||
462 | #define ISPPRV_PCR_INVALAW (1 << 5) | ||
463 | #define ISPPRV_PCR_DRKFEN (1 << 6) | ||
464 | #define ISPPRV_PCR_DRKFCAP (1 << 7) | ||
465 | #define ISPPRV_PCR_HMEDEN (1 << 8) | ||
466 | #define ISPPRV_PCR_NFEN (1 << 9) | ||
467 | #define ISPPRV_PCR_CFAEN (1 << 10) | ||
468 | #define ISPPRV_PCR_CFAFMT_SHIFT 11 | ||
469 | #define ISPPRV_PCR_CFAFMT_MASK 0x7800 | ||
470 | #define ISPPRV_PCR_CFAFMT_BAYER (0 << 11) | ||
471 | #define ISPPRV_PCR_CFAFMT_SONYVGA (1 << 11) | ||
472 | #define ISPPRV_PCR_CFAFMT_RGBFOVEON (2 << 11) | ||
473 | #define ISPPRV_PCR_CFAFMT_DNSPL (3 << 11) | ||
474 | #define ISPPRV_PCR_CFAFMT_HONEYCOMB (4 << 11) | ||
475 | #define ISPPRV_PCR_CFAFMT_RRGGBBFOVEON (5 << 11) | ||
476 | #define ISPPRV_PCR_YNENHEN (1 << 15) | ||
477 | #define ISPPRV_PCR_SUPEN (1 << 16) | ||
478 | #define ISPPRV_PCR_YCPOS_SHIFT 17 | ||
479 | #define ISPPRV_PCR_YCPOS_YCrYCb (0 << 17) | ||
480 | #define ISPPRV_PCR_YCPOS_YCbYCr (1 << 17) | ||
481 | #define ISPPRV_PCR_YCPOS_CbYCrY (2 << 17) | ||
482 | #define ISPPRV_PCR_YCPOS_CrYCbY (3 << 17) | ||
483 | #define ISPPRV_PCR_RSZPORT (1 << 19) | ||
484 | #define ISPPRV_PCR_SDRPORT (1 << 20) | ||
485 | #define ISPPRV_PCR_SCOMP_EN (1 << 21) | ||
486 | #define ISPPRV_PCR_SCOMP_SFT_SHIFT (22) | ||
487 | #define ISPPRV_PCR_SCOMP_SFT_MASK (7 << 22) | ||
488 | #define ISPPRV_PCR_GAMMA_BYPASS (1 << 26) | ||
489 | #define ISPPRV_PCR_DCOREN (1 << 27) | ||
490 | #define ISPPRV_PCR_DCCOUP (1 << 28) | ||
491 | #define ISPPRV_PCR_DRK_FAIL (1 << 31) | ||
492 | |||
493 | #define ISPPRV_HORZ_INFO_EPH_SHIFT 0 | ||
494 | #define ISPPRV_HORZ_INFO_EPH_MASK 0x3fff | ||
495 | #define ISPPRV_HORZ_INFO_SPH_SHIFT 16 | ||
496 | #define ISPPRV_HORZ_INFO_SPH_MASK 0x3fff0 | ||
497 | |||
498 | #define ISPPRV_VERT_INFO_ELV_SHIFT 0 | ||
499 | #define ISPPRV_VERT_INFO_ELV_MASK 0x3fff | ||
500 | #define ISPPRV_VERT_INFO_SLV_SHIFT 16 | ||
501 | #define ISPPRV_VERT_INFO_SLV_MASK 0x3fff0 | ||
502 | |||
503 | #define ISPPRV_AVE_EVENDIST_SHIFT 2 | ||
504 | #define ISPPRV_AVE_EVENDIST_1 0x0 | ||
505 | #define ISPPRV_AVE_EVENDIST_2 0x1 | ||
506 | #define ISPPRV_AVE_EVENDIST_3 0x2 | ||
507 | #define ISPPRV_AVE_EVENDIST_4 0x3 | ||
508 | #define ISPPRV_AVE_ODDDIST_SHIFT 4 | ||
509 | #define ISPPRV_AVE_ODDDIST_1 0x0 | ||
510 | #define ISPPRV_AVE_ODDDIST_2 0x1 | ||
511 | #define ISPPRV_AVE_ODDDIST_3 0x2 | ||
512 | #define ISPPRV_AVE_ODDDIST_4 0x3 | ||
513 | |||
514 | #define ISPPRV_HMED_THRESHOLD_SHIFT 0 | ||
515 | #define ISPPRV_HMED_EVENDIST (1 << 8) | ||
516 | #define ISPPRV_HMED_ODDDIST (1 << 9) | ||
517 | |||
518 | #define ISPPRV_WBGAIN_COEF0_SHIFT 0 | ||
519 | #define ISPPRV_WBGAIN_COEF1_SHIFT 8 | ||
520 | #define ISPPRV_WBGAIN_COEF2_SHIFT 16 | ||
521 | #define ISPPRV_WBGAIN_COEF3_SHIFT 24 | ||
522 | |||
523 | #define ISPPRV_WBSEL_COEF0 0x0 | ||
524 | #define ISPPRV_WBSEL_COEF1 0x1 | ||
525 | #define ISPPRV_WBSEL_COEF2 0x2 | ||
526 | #define ISPPRV_WBSEL_COEF3 0x3 | ||
527 | |||
528 | #define ISPPRV_WBSEL_N0_0_SHIFT 0 | ||
529 | #define ISPPRV_WBSEL_N0_1_SHIFT 2 | ||
530 | #define ISPPRV_WBSEL_N0_2_SHIFT 4 | ||
531 | #define ISPPRV_WBSEL_N0_3_SHIFT 6 | ||
532 | #define ISPPRV_WBSEL_N1_0_SHIFT 8 | ||
533 | #define ISPPRV_WBSEL_N1_1_SHIFT 10 | ||
534 | #define ISPPRV_WBSEL_N1_2_SHIFT 12 | ||
535 | #define ISPPRV_WBSEL_N1_3_SHIFT 14 | ||
536 | #define ISPPRV_WBSEL_N2_0_SHIFT 16 | ||
537 | #define ISPPRV_WBSEL_N2_1_SHIFT 18 | ||
538 | #define ISPPRV_WBSEL_N2_2_SHIFT 20 | ||
539 | #define ISPPRV_WBSEL_N2_3_SHIFT 22 | ||
540 | #define ISPPRV_WBSEL_N3_0_SHIFT 24 | ||
541 | #define ISPPRV_WBSEL_N3_1_SHIFT 26 | ||
542 | #define ISPPRV_WBSEL_N3_2_SHIFT 28 | ||
543 | #define ISPPRV_WBSEL_N3_3_SHIFT 30 | ||
544 | |||
545 | #define ISPPRV_CFA_GRADTH_HOR_SHIFT 0 | ||
546 | #define ISPPRV_CFA_GRADTH_VER_SHIFT 8 | ||
547 | |||
548 | #define ISPPRV_BLKADJOFF_B_SHIFT 0 | ||
549 | #define ISPPRV_BLKADJOFF_G_SHIFT 8 | ||
550 | #define ISPPRV_BLKADJOFF_R_SHIFT 16 | ||
551 | |||
552 | #define ISPPRV_RGB_MAT1_MTX_RR_SHIFT 0 | ||
553 | #define ISPPRV_RGB_MAT1_MTX_GR_SHIFT 16 | ||
554 | |||
555 | #define ISPPRV_RGB_MAT2_MTX_BR_SHIFT 0 | ||
556 | #define ISPPRV_RGB_MAT2_MTX_RG_SHIFT 16 | ||
557 | |||
558 | #define ISPPRV_RGB_MAT3_MTX_GG_SHIFT 0 | ||
559 | #define ISPPRV_RGB_MAT3_MTX_BG_SHIFT 16 | ||
560 | |||
561 | #define ISPPRV_RGB_MAT4_MTX_RB_SHIFT 0 | ||
562 | #define ISPPRV_RGB_MAT4_MTX_GB_SHIFT 16 | ||
563 | |||
564 | #define ISPPRV_RGB_MAT5_MTX_BB_SHIFT 0 | ||
565 | |||
566 | #define ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT 0 | ||
567 | #define ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT 16 | ||
568 | |||
569 | #define ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT 0 | ||
570 | |||
571 | #define ISPPRV_CSC0_RY_SHIFT 0 | ||
572 | #define ISPPRV_CSC0_GY_SHIFT 10 | ||
573 | #define ISPPRV_CSC0_BY_SHIFT 20 | ||
574 | |||
575 | #define ISPPRV_CSC1_RCB_SHIFT 0 | ||
576 | #define ISPPRV_CSC1_GCB_SHIFT 10 | ||
577 | #define ISPPRV_CSC1_BCB_SHIFT 20 | ||
578 | |||
579 | #define ISPPRV_CSC2_RCR_SHIFT 0 | ||
580 | #define ISPPRV_CSC2_GCR_SHIFT 10 | ||
581 | #define ISPPRV_CSC2_BCR_SHIFT 20 | ||
582 | |||
583 | #define ISPPRV_CSC_OFFSET_CR_SHIFT 0 | ||
584 | #define ISPPRV_CSC_OFFSET_CB_SHIFT 8 | ||
585 | #define ISPPRV_CSC_OFFSET_Y_SHIFT 16 | ||
586 | |||
587 | #define ISPPRV_CNT_BRT_BRT_SHIFT 0 | ||
588 | #define ISPPRV_CNT_BRT_CNT_SHIFT 8 | ||
589 | |||
590 | #define ISPPRV_CONTRAST_MAX 0x10 | ||
591 | #define ISPPRV_CONTRAST_MIN 0xFF | ||
592 | #define ISPPRV_BRIGHT_MIN 0x00 | ||
593 | #define ISPPRV_BRIGHT_MAX 0xFF | ||
594 | |||
595 | #define ISPPRV_CSUP_CSUPG_SHIFT 0 | ||
596 | #define ISPPRV_CSUP_THRES_SHIFT 8 | ||
597 | #define ISPPRV_CSUP_HPYF_SHIFT 16 | ||
598 | |||
599 | #define ISPPRV_SETUP_YC_MINC_SHIFT 0 | ||
600 | #define ISPPRV_SETUP_YC_MAXC_SHIFT 8 | ||
601 | #define ISPPRV_SETUP_YC_MINY_SHIFT 16 | ||
602 | #define ISPPRV_SETUP_YC_MAXY_SHIFT 24 | ||
603 | #define ISPPRV_YC_MAX 0xFF | ||
604 | #define ISPPRV_YC_MIN 0x0 | ||
605 | |||
606 | /* Define bit fields within selected registers */ | ||
607 | #define ISP_REVISION_SHIFT 0 | ||
608 | |||
609 | #define ISP_SYSCONFIG_AUTOIDLE (1 << 0) | ||
610 | #define ISP_SYSCONFIG_SOFTRESET (1 << 1) | ||
611 | #define ISP_SYSCONFIG_MIDLEMODE_SHIFT 12 | ||
612 | #define ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY 0x0 | ||
613 | #define ISP_SYSCONFIG_MIDLEMODE_NOSTANBY 0x1 | ||
614 | #define ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY 0x2 | ||
615 | |||
616 | #define ISP_SYSSTATUS_RESETDONE 0 | ||
617 | |||
618 | #define IRQ0ENABLE_CSIA_IRQ (1 << 0) | ||
619 | #define IRQ0ENABLE_CSIC_IRQ (1 << 1) | ||
620 | #define IRQ0ENABLE_CCP2_LCM_IRQ (1 << 3) | ||
621 | #define IRQ0ENABLE_CCP2_LC0_IRQ (1 << 4) | ||
622 | #define IRQ0ENABLE_CCP2_LC1_IRQ (1 << 5) | ||
623 | #define IRQ0ENABLE_CCP2_LC2_IRQ (1 << 6) | ||
624 | #define IRQ0ENABLE_CCP2_LC3_IRQ (1 << 7) | ||
625 | #define IRQ0ENABLE_CSIB_IRQ (IRQ0ENABLE_CCP2_LCM_IRQ | \ | ||
626 | IRQ0ENABLE_CCP2_LC0_IRQ | \ | ||
627 | IRQ0ENABLE_CCP2_LC1_IRQ | \ | ||
628 | IRQ0ENABLE_CCP2_LC2_IRQ | \ | ||
629 | IRQ0ENABLE_CCP2_LC3_IRQ) | ||
630 | |||
631 | #define IRQ0ENABLE_CCDC_VD0_IRQ (1 << 8) | ||
632 | #define IRQ0ENABLE_CCDC_VD1_IRQ (1 << 9) | ||
633 | #define IRQ0ENABLE_CCDC_VD2_IRQ (1 << 10) | ||
634 | #define IRQ0ENABLE_CCDC_ERR_IRQ (1 << 11) | ||
635 | #define IRQ0ENABLE_H3A_AF_DONE_IRQ (1 << 12) | ||
636 | #define IRQ0ENABLE_H3A_AWB_DONE_IRQ (1 << 13) | ||
637 | #define IRQ0ENABLE_HIST_DONE_IRQ (1 << 16) | ||
638 | #define IRQ0ENABLE_CCDC_LSC_DONE_IRQ (1 << 17) | ||
639 | #define IRQ0ENABLE_CCDC_LSC_PREF_COMP_IRQ (1 << 18) | ||
640 | #define IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ (1 << 19) | ||
641 | #define IRQ0ENABLE_PRV_DONE_IRQ (1 << 20) | ||
642 | #define IRQ0ENABLE_RSZ_DONE_IRQ (1 << 24) | ||
643 | #define IRQ0ENABLE_OVF_IRQ (1 << 25) | ||
644 | #define IRQ0ENABLE_PING_IRQ (1 << 26) | ||
645 | #define IRQ0ENABLE_PONG_IRQ (1 << 27) | ||
646 | #define IRQ0ENABLE_MMU_ERR_IRQ (1 << 28) | ||
647 | #define IRQ0ENABLE_OCP_ERR_IRQ (1 << 29) | ||
648 | #define IRQ0ENABLE_SEC_ERR_IRQ (1 << 30) | ||
649 | #define IRQ0ENABLE_HS_VS_IRQ (1 << 31) | ||
650 | |||
651 | #define IRQ0STATUS_CSIA_IRQ (1 << 0) | ||
652 | #define IRQ0STATUS_CSI2C_IRQ (1 << 1) | ||
653 | #define IRQ0STATUS_CCP2_LCM_IRQ (1 << 3) | ||
654 | #define IRQ0STATUS_CCP2_LC0_IRQ (1 << 4) | ||
655 | #define IRQ0STATUS_CSIB_IRQ (IRQ0STATUS_CCP2_LCM_IRQ | \ | ||
656 | IRQ0STATUS_CCP2_LC0_IRQ) | ||
657 | |||
658 | #define IRQ0STATUS_CSIB_LC1_IRQ (1 << 5) | ||
659 | #define IRQ0STATUS_CSIB_LC2_IRQ (1 << 6) | ||
660 | #define IRQ0STATUS_CSIB_LC3_IRQ (1 << 7) | ||
661 | #define IRQ0STATUS_CCDC_VD0_IRQ (1 << 8) | ||
662 | #define IRQ0STATUS_CCDC_VD1_IRQ (1 << 9) | ||
663 | #define IRQ0STATUS_CCDC_VD2_IRQ (1 << 10) | ||
664 | #define IRQ0STATUS_CCDC_ERR_IRQ (1 << 11) | ||
665 | #define IRQ0STATUS_H3A_AF_DONE_IRQ (1 << 12) | ||
666 | #define IRQ0STATUS_H3A_AWB_DONE_IRQ (1 << 13) | ||
667 | #define IRQ0STATUS_HIST_DONE_IRQ (1 << 16) | ||
668 | #define IRQ0STATUS_CCDC_LSC_DONE_IRQ (1 << 17) | ||
669 | #define IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ (1 << 18) | ||
670 | #define IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ (1 << 19) | ||
671 | #define IRQ0STATUS_PRV_DONE_IRQ (1 << 20) | ||
672 | #define IRQ0STATUS_RSZ_DONE_IRQ (1 << 24) | ||
673 | #define IRQ0STATUS_OVF_IRQ (1 << 25) | ||
674 | #define IRQ0STATUS_PING_IRQ (1 << 26) | ||
675 | #define IRQ0STATUS_PONG_IRQ (1 << 27) | ||
676 | #define IRQ0STATUS_MMU_ERR_IRQ (1 << 28) | ||
677 | #define IRQ0STATUS_OCP_ERR_IRQ (1 << 29) | ||
678 | #define IRQ0STATUS_SEC_ERR_IRQ (1 << 30) | ||
679 | #define IRQ0STATUS_HS_VS_IRQ (1 << 31) | ||
680 | |||
681 | #define TCTRL_GRESET_LEN 0 | ||
682 | |||
683 | #define TCTRL_PSTRB_REPLAY_DELAY 0 | ||
684 | #define TCTRL_PSTRB_REPLAY_COUNTER_SHIFT 25 | ||
685 | |||
686 | #define ISPCTRL_PAR_SER_CLK_SEL_PARALLEL 0x0 | ||
687 | #define ISPCTRL_PAR_SER_CLK_SEL_CSIA 0x1 | ||
688 | #define ISPCTRL_PAR_SER_CLK_SEL_CSIB 0x2 | ||
689 | #define ISPCTRL_PAR_SER_CLK_SEL_CSIC 0x3 | ||
690 | #define ISPCTRL_PAR_SER_CLK_SEL_MASK 0x3 | ||
691 | |||
692 | #define ISPCTRL_PAR_BRIDGE_SHIFT 2 | ||
693 | #define ISPCTRL_PAR_BRIDGE_DISABLE (0x0 << 2) | ||
694 | #define ISPCTRL_PAR_BRIDGE_LENDIAN (0x2 << 2) | ||
695 | #define ISPCTRL_PAR_BRIDGE_BENDIAN (0x3 << 2) | ||
696 | #define ISPCTRL_PAR_BRIDGE_MASK (0x3 << 2) | ||
697 | |||
698 | #define ISPCTRL_PAR_CLK_POL_SHIFT 4 | ||
699 | #define ISPCTRL_PAR_CLK_POL_INV (1 << 4) | ||
700 | #define ISPCTRL_PING_PONG_EN (1 << 5) | ||
701 | #define ISPCTRL_SHIFT_SHIFT 6 | ||
702 | #define ISPCTRL_SHIFT_0 (0x0 << 6) | ||
703 | #define ISPCTRL_SHIFT_2 (0x1 << 6) | ||
704 | #define ISPCTRL_SHIFT_4 (0x2 << 6) | ||
705 | #define ISPCTRL_SHIFT_MASK (0x3 << 6) | ||
706 | |||
707 | #define ISPCTRL_CCDC_CLK_EN (1 << 8) | ||
708 | #define ISPCTRL_SCMP_CLK_EN (1 << 9) | ||
709 | #define ISPCTRL_H3A_CLK_EN (1 << 10) | ||
710 | #define ISPCTRL_HIST_CLK_EN (1 << 11) | ||
711 | #define ISPCTRL_PREV_CLK_EN (1 << 12) | ||
712 | #define ISPCTRL_RSZ_CLK_EN (1 << 13) | ||
713 | #define ISPCTRL_SYNC_DETECT_SHIFT 14 | ||
714 | #define ISPCTRL_SYNC_DETECT_HSFALL (0x0 << ISPCTRL_SYNC_DETECT_SHIFT) | ||
715 | #define ISPCTRL_SYNC_DETECT_HSRISE (0x1 << ISPCTRL_SYNC_DETECT_SHIFT) | ||
716 | #define ISPCTRL_SYNC_DETECT_VSFALL (0x2 << ISPCTRL_SYNC_DETECT_SHIFT) | ||
717 | #define ISPCTRL_SYNC_DETECT_VSRISE (0x3 << ISPCTRL_SYNC_DETECT_SHIFT) | ||
718 | #define ISPCTRL_SYNC_DETECT_MASK (0x3 << ISPCTRL_SYNC_DETECT_SHIFT) | ||
719 | |||
720 | #define ISPCTRL_CCDC_RAM_EN (1 << 16) | ||
721 | #define ISPCTRL_PREV_RAM_EN (1 << 17) | ||
722 | #define ISPCTRL_SBL_RD_RAM_EN (1 << 18) | ||
723 | #define ISPCTRL_SBL_WR1_RAM_EN (1 << 19) | ||
724 | #define ISPCTRL_SBL_WR0_RAM_EN (1 << 20) | ||
725 | #define ISPCTRL_SBL_AUTOIDLE (1 << 21) | ||
726 | #define ISPCTRL_SBL_SHARED_WPORTC (1 << 26) | ||
727 | #define ISPCTRL_SBL_SHARED_RPORTA (1 << 27) | ||
728 | #define ISPCTRL_SBL_SHARED_RPORTB (1 << 28) | ||
729 | #define ISPCTRL_JPEG_FLUSH (1 << 30) | ||
730 | #define ISPCTRL_CCDC_FLUSH (1 << 31) | ||
731 | |||
732 | #define ISPSECURE_SECUREMODE 0 | ||
733 | |||
734 | #define ISPTCTRL_CTRL_DIV_LOW 0x0 | ||
735 | #define ISPTCTRL_CTRL_DIV_HIGH 0x1 | ||
736 | #define ISPTCTRL_CTRL_DIV_BYPASS 0x1F | ||
737 | |||
738 | #define ISPTCTRL_CTRL_DIVA_SHIFT 0 | ||
739 | #define ISPTCTRL_CTRL_DIVA_MASK (0x1F << ISPTCTRL_CTRL_DIVA_SHIFT) | ||
740 | |||
741 | #define ISPTCTRL_CTRL_DIVB_SHIFT 5 | ||
742 | #define ISPTCTRL_CTRL_DIVB_MASK (0x1F << ISPTCTRL_CTRL_DIVB_SHIFT) | ||
743 | |||
744 | #define ISPTCTRL_CTRL_DIVC_SHIFT 10 | ||
745 | #define ISPTCTRL_CTRL_DIVC_NOCLOCK (0x0 << 10) | ||
746 | |||
747 | #define ISPTCTRL_CTRL_SHUTEN (1 << 21) | ||
748 | #define ISPTCTRL_CTRL_PSTRBEN (1 << 22) | ||
749 | #define ISPTCTRL_CTRL_STRBEN (1 << 23) | ||
750 | #define ISPTCTRL_CTRL_SHUTPOL (1 << 24) | ||
751 | #define ISPTCTRL_CTRL_STRBPSTRBPOL (1 << 26) | ||
752 | |||
753 | #define ISPTCTRL_CTRL_INSEL_SHIFT 27 | ||
754 | #define ISPTCTRL_CTRL_INSEL_PARALLEL (0x0 << 27) | ||
755 | #define ISPTCTRL_CTRL_INSEL_CSIA (0x1 << 27) | ||
756 | #define ISPTCTRL_CTRL_INSEL_CSIB (0x2 << 27) | ||
757 | |||
758 | #define ISPTCTRL_CTRL_GRESETEn (1 << 29) | ||
759 | #define ISPTCTRL_CTRL_GRESETPOL (1 << 30) | ||
760 | #define ISPTCTRL_CTRL_GRESETDIR (1 << 31) | ||
761 | |||
762 | #define ISPTCTRL_FRAME_SHUT_SHIFT 0 | ||
763 | #define ISPTCTRL_FRAME_PSTRB_SHIFT 6 | ||
764 | #define ISPTCTRL_FRAME_STRB_SHIFT 12 | ||
765 | |||
766 | #define ISPCCDC_PID_PREV_SHIFT 0 | ||
767 | #define ISPCCDC_PID_CID_SHIFT 8 | ||
768 | #define ISPCCDC_PID_TID_SHIFT 16 | ||
769 | |||
770 | #define ISPCCDC_PCR_EN 1 | ||
771 | #define ISPCCDC_PCR_BUSY (1 << 1) | ||
772 | |||
773 | #define ISPCCDC_SYN_MODE_VDHDOUT 0x1 | ||
774 | #define ISPCCDC_SYN_MODE_FLDOUT (1 << 1) | ||
775 | #define ISPCCDC_SYN_MODE_VDPOL (1 << 2) | ||
776 | #define ISPCCDC_SYN_MODE_HDPOL (1 << 3) | ||
777 | #define ISPCCDC_SYN_MODE_FLDPOL (1 << 4) | ||
778 | #define ISPCCDC_SYN_MODE_EXWEN (1 << 5) | ||
779 | #define ISPCCDC_SYN_MODE_DATAPOL (1 << 6) | ||
780 | #define ISPCCDC_SYN_MODE_FLDMODE (1 << 7) | ||
781 | #define ISPCCDC_SYN_MODE_DATSIZ_MASK (0x7 << 8) | ||
782 | #define ISPCCDC_SYN_MODE_DATSIZ_8_16 (0x0 << 8) | ||
783 | #define ISPCCDC_SYN_MODE_DATSIZ_12 (0x4 << 8) | ||
784 | #define ISPCCDC_SYN_MODE_DATSIZ_11 (0x5 << 8) | ||
785 | #define ISPCCDC_SYN_MODE_DATSIZ_10 (0x6 << 8) | ||
786 | #define ISPCCDC_SYN_MODE_DATSIZ_8 (0x7 << 8) | ||
787 | #define ISPCCDC_SYN_MODE_PACK8 (1 << 11) | ||
788 | #define ISPCCDC_SYN_MODE_INPMOD_MASK (3 << 12) | ||
789 | #define ISPCCDC_SYN_MODE_INPMOD_RAW (0 << 12) | ||
790 | #define ISPCCDC_SYN_MODE_INPMOD_YCBCR16 (1 << 12) | ||
791 | #define ISPCCDC_SYN_MODE_INPMOD_YCBCR8 (2 << 12) | ||
792 | #define ISPCCDC_SYN_MODE_LPF (1 << 14) | ||
793 | #define ISPCCDC_SYN_MODE_FLDSTAT (1 << 15) | ||
794 | #define ISPCCDC_SYN_MODE_VDHDEN (1 << 16) | ||
795 | #define ISPCCDC_SYN_MODE_WEN (1 << 17) | ||
796 | #define ISPCCDC_SYN_MODE_VP2SDR (1 << 18) | ||
797 | #define ISPCCDC_SYN_MODE_SDR2RSZ (1 << 19) | ||
798 | |||
799 | #define ISPCCDC_HD_VD_WID_VDW_SHIFT 0 | ||
800 | #define ISPCCDC_HD_VD_WID_HDW_SHIFT 16 | ||
801 | |||
802 | #define ISPCCDC_PIX_LINES_HLPRF_SHIFT 0 | ||
803 | #define ISPCCDC_PIX_LINES_PPLN_SHIFT 16 | ||
804 | |||
805 | #define ISPCCDC_HORZ_INFO_NPH_SHIFT 0 | ||
806 | #define ISPCCDC_HORZ_INFO_NPH_MASK 0x00007fff | ||
807 | #define ISPCCDC_HORZ_INFO_SPH_SHIFT 16 | ||
808 | #define ISPCCDC_HORZ_INFO_SPH_MASK 0x7fff0000 | ||
809 | |||
810 | #define ISPCCDC_VERT_START_SLV1_SHIFT 0 | ||
811 | #define ISPCCDC_VERT_START_SLV0_SHIFT 16 | ||
812 | #define ISPCCDC_VERT_START_SLV0_MASK 0x7fff0000 | ||
813 | |||
814 | #define ISPCCDC_VERT_LINES_NLV_SHIFT 0 | ||
815 | #define ISPCCDC_VERT_LINES_NLV_MASK 0x00007fff | ||
816 | |||
817 | #define ISPCCDC_CULLING_CULV_SHIFT 0 | ||
818 | #define ISPCCDC_CULLING_CULHODD_SHIFT 16 | ||
819 | #define ISPCCDC_CULLING_CULHEVN_SHIFT 24 | ||
820 | |||
821 | #define ISPCCDC_HSIZE_OFF_SHIFT 0 | ||
822 | |||
823 | #define ISPCCDC_SDOFST_FINV (1 << 14) | ||
824 | #define ISPCCDC_SDOFST_FOFST_1L 0 | ||
825 | #define ISPCCDC_SDOFST_FOFST_4L (3 << 12) | ||
826 | #define ISPCCDC_SDOFST_LOFST3_SHIFT 0 | ||
827 | #define ISPCCDC_SDOFST_LOFST2_SHIFT 3 | ||
828 | #define ISPCCDC_SDOFST_LOFST1_SHIFT 6 | ||
829 | #define ISPCCDC_SDOFST_LOFST0_SHIFT 9 | ||
830 | #define EVENEVEN 1 | ||
831 | #define ODDEVEN 2 | ||
832 | #define EVENODD 3 | ||
833 | #define ODDODD 4 | ||
834 | |||
835 | #define ISPCCDC_CLAMP_OBGAIN_SHIFT 0 | ||
836 | #define ISPCCDC_CLAMP_OBST_SHIFT 10 | ||
837 | #define ISPCCDC_CLAMP_OBSLN_SHIFT 25 | ||
838 | #define ISPCCDC_CLAMP_OBSLEN_SHIFT 28 | ||
839 | #define ISPCCDC_CLAMP_CLAMPEN (1 << 31) | ||
840 | |||
841 | #define ISPCCDC_COLPTN_R_Ye 0x0 | ||
842 | #define ISPCCDC_COLPTN_Gr_Cy 0x1 | ||
843 | #define ISPCCDC_COLPTN_Gb_G 0x2 | ||
844 | #define ISPCCDC_COLPTN_B_Mg 0x3 | ||
845 | #define ISPCCDC_COLPTN_CP0PLC0_SHIFT 0 | ||
846 | #define ISPCCDC_COLPTN_CP0PLC1_SHIFT 2 | ||
847 | #define ISPCCDC_COLPTN_CP0PLC2_SHIFT 4 | ||
848 | #define ISPCCDC_COLPTN_CP0PLC3_SHIFT 6 | ||
849 | #define ISPCCDC_COLPTN_CP1PLC0_SHIFT 8 | ||
850 | #define ISPCCDC_COLPTN_CP1PLC1_SHIFT 10 | ||
851 | #define ISPCCDC_COLPTN_CP1PLC2_SHIFT 12 | ||
852 | #define ISPCCDC_COLPTN_CP1PLC3_SHIFT 14 | ||
853 | #define ISPCCDC_COLPTN_CP2PLC0_SHIFT 16 | ||
854 | #define ISPCCDC_COLPTN_CP2PLC1_SHIFT 18 | ||
855 | #define ISPCCDC_COLPTN_CP2PLC2_SHIFT 20 | ||
856 | #define ISPCCDC_COLPTN_CP2PLC3_SHIFT 22 | ||
857 | #define ISPCCDC_COLPTN_CP3PLC0_SHIFT 24 | ||
858 | #define ISPCCDC_COLPTN_CP3PLC1_SHIFT 26 | ||
859 | #define ISPCCDC_COLPTN_CP3PLC2_SHIFT 28 | ||
860 | #define ISPCCDC_COLPTN_CP3PLC3_SHIFT 30 | ||
861 | |||
862 | #define ISPCCDC_BLKCMP_B_MG_SHIFT 0 | ||
863 | #define ISPCCDC_BLKCMP_GB_G_SHIFT 8 | ||
864 | #define ISPCCDC_BLKCMP_GR_CY_SHIFT 16 | ||
865 | #define ISPCCDC_BLKCMP_R_YE_SHIFT 24 | ||
866 | |||
867 | #define ISPCCDC_FPC_FPNUM_SHIFT 0 | ||
868 | #define ISPCCDC_FPC_FPCEN (1 << 15) | ||
869 | #define ISPCCDC_FPC_FPERR (1 << 16) | ||
870 | |||
871 | #define ISPCCDC_VDINT_1_SHIFT 0 | ||
872 | #define ISPCCDC_VDINT_1_MASK 0x00007fff | ||
873 | #define ISPCCDC_VDINT_0_SHIFT 16 | ||
874 | #define ISPCCDC_VDINT_0_MASK 0x7fff0000 | ||
875 | |||
876 | #define ISPCCDC_ALAW_GWDI_12_3 (0x3 << 0) | ||
877 | #define ISPCCDC_ALAW_GWDI_11_2 (0x4 << 0) | ||
878 | #define ISPCCDC_ALAW_GWDI_10_1 (0x5 << 0) | ||
879 | #define ISPCCDC_ALAW_GWDI_9_0 (0x6 << 0) | ||
880 | #define ISPCCDC_ALAW_CCDTBL (1 << 3) | ||
881 | |||
882 | #define ISPCCDC_REC656IF_R656ON 1 | ||
883 | #define ISPCCDC_REC656IF_ECCFVH (1 << 1) | ||
884 | |||
885 | #define ISPCCDC_CFG_BW656 (1 << 5) | ||
886 | #define ISPCCDC_CFG_FIDMD_SHIFT 6 | ||
887 | #define ISPCCDC_CFG_WENLOG (1 << 8) | ||
888 | #define ISPCCDC_CFG_WENLOG_AND (0 << 8) | ||
889 | #define ISPCCDC_CFG_WENLOG_OR (1 << 8) | ||
890 | #define ISPCCDC_CFG_Y8POS (1 << 11) | ||
891 | #define ISPCCDC_CFG_BSWD (1 << 12) | ||
892 | #define ISPCCDC_CFG_MSBINVI (1 << 13) | ||
893 | #define ISPCCDC_CFG_VDLC (1 << 15) | ||
894 | |||
895 | #define ISPCCDC_FMTCFG_FMTEN 0x1 | ||
896 | #define ISPCCDC_FMTCFG_LNALT (1 << 1) | ||
897 | #define ISPCCDC_FMTCFG_LNUM_SHIFT 2 | ||
898 | #define ISPCCDC_FMTCFG_PLEN_ODD_SHIFT 4 | ||
899 | #define ISPCCDC_FMTCFG_PLEN_EVEN_SHIFT 8 | ||
900 | #define ISPCCDC_FMTCFG_VPIN_MASK 0x00007000 | ||
901 | #define ISPCCDC_FMTCFG_VPIN_12_3 (0x3 << 12) | ||
902 | #define ISPCCDC_FMTCFG_VPIN_11_2 (0x4 << 12) | ||
903 | #define ISPCCDC_FMTCFG_VPIN_10_1 (0x5 << 12) | ||
904 | #define ISPCCDC_FMTCFG_VPIN_9_0 (0x6 << 12) | ||
905 | #define ISPCCDC_FMTCFG_VPEN (1 << 15) | ||
906 | |||
907 | #define ISPCCDC_FMTCFG_VPIF_FRQ_MASK 0x003f0000 | ||
908 | #define ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT 16 | ||
909 | #define ISPCCDC_FMTCFG_VPIF_FRQ_BY2 (0x0 << 16) | ||
910 | #define ISPCCDC_FMTCFG_VPIF_FRQ_BY3 (0x1 << 16) | ||
911 | #define ISPCCDC_FMTCFG_VPIF_FRQ_BY4 (0x2 << 16) | ||
912 | #define ISPCCDC_FMTCFG_VPIF_FRQ_BY5 (0x3 << 16) | ||
913 | #define ISPCCDC_FMTCFG_VPIF_FRQ_BY6 (0x4 << 16) | ||
914 | |||
915 | #define ISPCCDC_FMT_HORZ_FMTLNH_SHIFT 0 | ||
916 | #define ISPCCDC_FMT_HORZ_FMTSPH_SHIFT 16 | ||
917 | |||
918 | #define ISPCCDC_FMT_VERT_FMTLNV_SHIFT 0 | ||
919 | #define ISPCCDC_FMT_VERT_FMTSLV_SHIFT 16 | ||
920 | |||
921 | #define ISPCCDC_FMT_HORZ_FMTSPH_MASK 0x1fff0000 | ||
922 | #define ISPCCDC_FMT_HORZ_FMTLNH_MASK 0x00001fff | ||
923 | |||
924 | #define ISPCCDC_FMT_VERT_FMTSLV_MASK 0x1fff0000 | ||
925 | #define ISPCCDC_FMT_VERT_FMTLNV_MASK 0x00001fff | ||
926 | |||
927 | #define ISPCCDC_VP_OUT_HORZ_ST_SHIFT 0 | ||
928 | #define ISPCCDC_VP_OUT_HORZ_NUM_SHIFT 4 | ||
929 | #define ISPCCDC_VP_OUT_VERT_NUM_SHIFT 17 | ||
930 | |||
931 | #define ISPRSZ_PID_PREV_SHIFT 0 | ||
932 | #define ISPRSZ_PID_CID_SHIFT 8 | ||
933 | #define ISPRSZ_PID_TID_SHIFT 16 | ||
934 | |||
935 | #define ISPRSZ_PCR_ENABLE (1 << 0) | ||
936 | #define ISPRSZ_PCR_BUSY (1 << 1) | ||
937 | #define ISPRSZ_PCR_ONESHOT (1 << 2) | ||
938 | |||
939 | #define ISPRSZ_CNT_HRSZ_SHIFT 0 | ||
940 | #define ISPRSZ_CNT_HRSZ_MASK \ | ||
941 | (0x3FF << ISPRSZ_CNT_HRSZ_SHIFT) | ||
942 | #define ISPRSZ_CNT_VRSZ_SHIFT 10 | ||
943 | #define ISPRSZ_CNT_VRSZ_MASK \ | ||
944 | (0x3FF << ISPRSZ_CNT_VRSZ_SHIFT) | ||
945 | #define ISPRSZ_CNT_HSTPH_SHIFT 20 | ||
946 | #define ISPRSZ_CNT_HSTPH_MASK (0x7 << ISPRSZ_CNT_HSTPH_SHIFT) | ||
947 | #define ISPRSZ_CNT_VSTPH_SHIFT 23 | ||
948 | #define ISPRSZ_CNT_VSTPH_MASK (0x7 << ISPRSZ_CNT_VSTPH_SHIFT) | ||
949 | #define ISPRSZ_CNT_YCPOS (1 << 26) | ||
950 | #define ISPRSZ_CNT_INPTYP (1 << 27) | ||
951 | #define ISPRSZ_CNT_INPSRC (1 << 28) | ||
952 | #define ISPRSZ_CNT_CBILIN (1 << 29) | ||
953 | |||
954 | #define ISPRSZ_OUT_SIZE_HORZ_SHIFT 0 | ||
955 | #define ISPRSZ_OUT_SIZE_HORZ_MASK \ | ||
956 | (0xFFF << ISPRSZ_OUT_SIZE_HORZ_SHIFT) | ||
957 | #define ISPRSZ_OUT_SIZE_VERT_SHIFT 16 | ||
958 | #define ISPRSZ_OUT_SIZE_VERT_MASK \ | ||
959 | (0xFFF << ISPRSZ_OUT_SIZE_VERT_SHIFT) | ||
960 | |||
961 | #define ISPRSZ_IN_START_HORZ_ST_SHIFT 0 | ||
962 | #define ISPRSZ_IN_START_HORZ_ST_MASK \ | ||
963 | (0x1FFF << ISPRSZ_IN_START_HORZ_ST_SHIFT) | ||
964 | #define ISPRSZ_IN_START_VERT_ST_SHIFT 16 | ||
965 | #define ISPRSZ_IN_START_VERT_ST_MASK \ | ||
966 | (0x1FFF << ISPRSZ_IN_START_VERT_ST_SHIFT) | ||
967 | |||
968 | #define ISPRSZ_IN_SIZE_HORZ_SHIFT 0 | ||
969 | #define ISPRSZ_IN_SIZE_HORZ_MASK \ | ||
970 | (0x1FFF << ISPRSZ_IN_SIZE_HORZ_SHIFT) | ||
971 | #define ISPRSZ_IN_SIZE_VERT_SHIFT 16 | ||
972 | #define ISPRSZ_IN_SIZE_VERT_MASK \ | ||
973 | (0x1FFF << ISPRSZ_IN_SIZE_VERT_SHIFT) | ||
974 | |||
975 | #define ISPRSZ_SDR_INADD_ADDR_SHIFT 0 | ||
976 | #define ISPRSZ_SDR_INADD_ADDR_MASK 0xFFFFFFFF | ||
977 | |||
978 | #define ISPRSZ_SDR_INOFF_OFFSET_SHIFT 0 | ||
979 | #define ISPRSZ_SDR_INOFF_OFFSET_MASK \ | ||
980 | (0xFFFF << ISPRSZ_SDR_INOFF_OFFSET_SHIFT) | ||
981 | |||
982 | #define ISPRSZ_SDR_OUTADD_ADDR_SHIFT 0 | ||
983 | #define ISPRSZ_SDR_OUTADD_ADDR_MASK 0xFFFFFFFF | ||
984 | |||
985 | |||
986 | #define ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT 0 | ||
987 | #define ISPRSZ_SDR_OUTOFF_OFFSET_MASK \ | ||
988 | (0xFFFF << ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT) | ||
989 | |||
990 | #define ISPRSZ_HFILT_COEF0_SHIFT 0 | ||
991 | #define ISPRSZ_HFILT_COEF0_MASK \ | ||
992 | (0x3FF << ISPRSZ_HFILT_COEF0_SHIFT) | ||
993 | #define ISPRSZ_HFILT_COEF1_SHIFT 16 | ||
994 | #define ISPRSZ_HFILT_COEF1_MASK \ | ||
995 | (0x3FF << ISPRSZ_HFILT_COEF1_SHIFT) | ||
996 | |||
997 | #define ISPRSZ_HFILT32_COEF2_SHIFT 0 | ||
998 | #define ISPRSZ_HFILT32_COEF2_MASK 0x3FF | ||
999 | #define ISPRSZ_HFILT32_COEF3_SHIFT 16 | ||
1000 | #define ISPRSZ_HFILT32_COEF3_MASK 0x3FF0000 | ||
1001 | |||
1002 | #define ISPRSZ_HFILT54_COEF4_SHIFT 0 | ||
1003 | #define ISPRSZ_HFILT54_COEF4_MASK 0x3FF | ||
1004 | #define ISPRSZ_HFILT54_COEF5_SHIFT 16 | ||
1005 | #define ISPRSZ_HFILT54_COEF5_MASK 0x3FF0000 | ||
1006 | |||
1007 | #define ISPRSZ_HFILT76_COEFF6_SHIFT 0 | ||
1008 | #define ISPRSZ_HFILT76_COEFF6_MASK 0x3FF | ||
1009 | #define ISPRSZ_HFILT76_COEFF7_SHIFT 16 | ||
1010 | #define ISPRSZ_HFILT76_COEFF7_MASK 0x3FF0000 | ||
1011 | |||
1012 | #define ISPRSZ_HFILT98_COEFF8_SHIFT 0 | ||
1013 | #define ISPRSZ_HFILT98_COEFF8_MASK 0x3FF | ||
1014 | #define ISPRSZ_HFILT98_COEFF9_SHIFT 16 | ||
1015 | #define ISPRSZ_HFILT98_COEFF9_MASK 0x3FF0000 | ||
1016 | |||
1017 | #define ISPRSZ_HFILT1110_COEF10_SHIFT 0 | ||
1018 | #define ISPRSZ_HFILT1110_COEF10_MASK 0x3FF | ||
1019 | #define ISPRSZ_HFILT1110_COEF11_SHIFT 16 | ||
1020 | #define ISPRSZ_HFILT1110_COEF11_MASK 0x3FF0000 | ||
1021 | |||
1022 | #define ISPRSZ_HFILT1312_COEFF12_SHIFT 0 | ||
1023 | #define ISPRSZ_HFILT1312_COEFF12_MASK 0x3FF | ||
1024 | #define ISPRSZ_HFILT1312_COEFF13_SHIFT 16 | ||
1025 | #define ISPRSZ_HFILT1312_COEFF13_MASK 0x3FF0000 | ||
1026 | |||
1027 | #define ISPRSZ_HFILT1514_COEFF14_SHIFT 0 | ||
1028 | #define ISPRSZ_HFILT1514_COEFF14_MASK 0x3FF | ||
1029 | #define ISPRSZ_HFILT1514_COEFF15_SHIFT 16 | ||
1030 | #define ISPRSZ_HFILT1514_COEFF15_MASK 0x3FF0000 | ||
1031 | |||
1032 | #define ISPRSZ_HFILT1716_COEF16_SHIFT 0 | ||
1033 | #define ISPRSZ_HFILT1716_COEF16_MASK 0x3FF | ||
1034 | #define ISPRSZ_HFILT1716_COEF17_SHIFT 16 | ||
1035 | #define ISPRSZ_HFILT1716_COEF17_MASK 0x3FF0000 | ||
1036 | |||
1037 | #define ISPRSZ_HFILT1918_COEF18_SHIFT 0 | ||
1038 | #define ISPRSZ_HFILT1918_COEF18_MASK 0x3FF | ||
1039 | #define ISPRSZ_HFILT1918_COEF19_SHIFT 16 | ||
1040 | #define ISPRSZ_HFILT1918_COEF19_MASK 0x3FF0000 | ||
1041 | |||
1042 | #define ISPRSZ_HFILT2120_COEF20_SHIFT 0 | ||
1043 | #define ISPRSZ_HFILT2120_COEF20_MASK 0x3FF | ||
1044 | #define ISPRSZ_HFILT2120_COEF21_SHIFT 16 | ||
1045 | #define ISPRSZ_HFILT2120_COEF21_MASK 0x3FF0000 | ||
1046 | |||
1047 | #define ISPRSZ_HFILT2322_COEF22_SHIFT 0 | ||
1048 | #define ISPRSZ_HFILT2322_COEF22_MASK 0x3FF | ||
1049 | #define ISPRSZ_HFILT2322_COEF23_SHIFT 16 | ||
1050 | #define ISPRSZ_HFILT2322_COEF23_MASK 0x3FF0000 | ||
1051 | |||
1052 | #define ISPRSZ_HFILT2524_COEF24_SHIFT 0 | ||
1053 | #define ISPRSZ_HFILT2524_COEF24_MASK 0x3FF | ||
1054 | #define ISPRSZ_HFILT2524_COEF25_SHIFT 16 | ||
1055 | #define ISPRSZ_HFILT2524_COEF25_MASK 0x3FF0000 | ||
1056 | |||
1057 | #define ISPRSZ_HFILT2726_COEF26_SHIFT 0 | ||
1058 | #define ISPRSZ_HFILT2726_COEF26_MASK 0x3FF | ||
1059 | #define ISPRSZ_HFILT2726_COEF27_SHIFT 16 | ||
1060 | #define ISPRSZ_HFILT2726_COEF27_MASK 0x3FF0000 | ||
1061 | |||
1062 | #define ISPRSZ_HFILT2928_COEF28_SHIFT 0 | ||
1063 | #define ISPRSZ_HFILT2928_COEF28_MASK 0x3FF | ||
1064 | #define ISPRSZ_HFILT2928_COEF29_SHIFT 16 | ||
1065 | #define ISPRSZ_HFILT2928_COEF29_MASK 0x3FF0000 | ||
1066 | |||
1067 | #define ISPRSZ_HFILT3130_COEF30_SHIFT 0 | ||
1068 | #define ISPRSZ_HFILT3130_COEF30_MASK 0x3FF | ||
1069 | #define ISPRSZ_HFILT3130_COEF31_SHIFT 16 | ||
1070 | #define ISPRSZ_HFILT3130_COEF31_MASK 0x3FF0000 | ||
1071 | |||
1072 | #define ISPRSZ_VFILT_COEF0_SHIFT 0 | ||
1073 | #define ISPRSZ_VFILT_COEF0_MASK \ | ||
1074 | (0x3FF << ISPRSZ_VFILT_COEF0_SHIFT) | ||
1075 | #define ISPRSZ_VFILT_COEF1_SHIFT 16 | ||
1076 | #define ISPRSZ_VFILT_COEF1_MASK \ | ||
1077 | (0x3FF << ISPRSZ_VFILT_COEF1_SHIFT) | ||
1078 | |||
1079 | #define ISPRSZ_VFILT10_COEF0_SHIFT 0 | ||
1080 | #define ISPRSZ_VFILT10_COEF0_MASK 0x3FF | ||
1081 | #define ISPRSZ_VFILT10_COEF1_SHIFT 16 | ||
1082 | #define ISPRSZ_VFILT10_COEF1_MASK 0x3FF0000 | ||
1083 | |||
1084 | #define ISPRSZ_VFILT32_COEF2_SHIFT 0 | ||
1085 | #define ISPRSZ_VFILT32_COEF2_MASK 0x3FF | ||
1086 | #define ISPRSZ_VFILT32_COEF3_SHIFT 16 | ||
1087 | #define ISPRSZ_VFILT32_COEF3_MASK 0x3FF0000 | ||
1088 | |||
1089 | #define ISPRSZ_VFILT54_COEF4_SHIFT 0 | ||
1090 | #define ISPRSZ_VFILT54_COEF4_MASK 0x3FF | ||
1091 | #define ISPRSZ_VFILT54_COEF5_SHIFT 16 | ||
1092 | #define ISPRSZ_VFILT54_COEF5_MASK 0x3FF0000 | ||
1093 | |||
1094 | #define ISPRSZ_VFILT76_COEFF6_SHIFT 0 | ||
1095 | #define ISPRSZ_VFILT76_COEFF6_MASK 0x3FF | ||
1096 | #define ISPRSZ_VFILT76_COEFF7_SHIFT 16 | ||
1097 | #define ISPRSZ_VFILT76_COEFF7_MASK 0x3FF0000 | ||
1098 | |||
1099 | #define ISPRSZ_VFILT98_COEFF8_SHIFT 0 | ||
1100 | #define ISPRSZ_VFILT98_COEFF8_MASK 0x3FF | ||
1101 | #define ISPRSZ_VFILT98_COEFF9_SHIFT 16 | ||
1102 | #define ISPRSZ_VFILT98_COEFF9_MASK 0x3FF0000 | ||
1103 | |||
1104 | #define ISPRSZ_VFILT1110_COEF10_SHIFT 0 | ||
1105 | #define ISPRSZ_VFILT1110_COEF10_MASK 0x3FF | ||
1106 | #define ISPRSZ_VFILT1110_COEF11_SHIFT 16 | ||
1107 | #define ISPRSZ_VFILT1110_COEF11_MASK 0x3FF0000 | ||
1108 | |||
1109 | #define ISPRSZ_VFILT1312_COEFF12_SHIFT 0 | ||
1110 | #define ISPRSZ_VFILT1312_COEFF12_MASK 0x3FF | ||
1111 | #define ISPRSZ_VFILT1312_COEFF13_SHIFT 16 | ||
1112 | #define ISPRSZ_VFILT1312_COEFF13_MASK 0x3FF0000 | ||
1113 | |||
1114 | #define ISPRSZ_VFILT1514_COEFF14_SHIFT 0 | ||
1115 | #define ISPRSZ_VFILT1514_COEFF14_MASK 0x3FF | ||
1116 | #define ISPRSZ_VFILT1514_COEFF15_SHIFT 16 | ||
1117 | #define ISPRSZ_VFILT1514_COEFF15_MASK 0x3FF0000 | ||
1118 | |||
1119 | #define ISPRSZ_VFILT1716_COEF16_SHIFT 0 | ||
1120 | #define ISPRSZ_VFILT1716_COEF16_MASK 0x3FF | ||
1121 | #define ISPRSZ_VFILT1716_COEF17_SHIFT 16 | ||
1122 | #define ISPRSZ_VFILT1716_COEF17_MASK 0x3FF0000 | ||
1123 | |||
1124 | #define ISPRSZ_VFILT1918_COEF18_SHIFT 0 | ||
1125 | #define ISPRSZ_VFILT1918_COEF18_MASK 0x3FF | ||
1126 | #define ISPRSZ_VFILT1918_COEF19_SHIFT 16 | ||
1127 | #define ISPRSZ_VFILT1918_COEF19_MASK 0x3FF0000 | ||
1128 | |||
1129 | #define ISPRSZ_VFILT2120_COEF20_SHIFT 0 | ||
1130 | #define ISPRSZ_VFILT2120_COEF20_MASK 0x3FF | ||
1131 | #define ISPRSZ_VFILT2120_COEF21_SHIFT 16 | ||
1132 | #define ISPRSZ_VFILT2120_COEF21_MASK 0x3FF0000 | ||
1133 | |||
1134 | #define ISPRSZ_VFILT2322_COEF22_SHIFT 0 | ||
1135 | #define ISPRSZ_VFILT2322_COEF22_MASK 0x3FF | ||
1136 | #define ISPRSZ_VFILT2322_COEF23_SHIFT 16 | ||
1137 | #define ISPRSZ_VFILT2322_COEF23_MASK 0x3FF0000 | ||
1138 | |||
1139 | #define ISPRSZ_VFILT2524_COEF24_SHIFT 0 | ||
1140 | #define ISPRSZ_VFILT2524_COEF24_MASK 0x3FF | ||
1141 | #define ISPRSZ_VFILT2524_COEF25_SHIFT 16 | ||
1142 | #define ISPRSZ_VFILT2524_COEF25_MASK 0x3FF0000 | ||
1143 | |||
1144 | #define ISPRSZ_VFILT2726_COEF26_SHIFT 0 | ||
1145 | #define ISPRSZ_VFILT2726_COEF26_MASK 0x3FF | ||
1146 | #define ISPRSZ_VFILT2726_COEF27_SHIFT 16 | ||
1147 | #define ISPRSZ_VFILT2726_COEF27_MASK 0x3FF0000 | ||
1148 | |||
1149 | #define ISPRSZ_VFILT2928_COEF28_SHIFT 0 | ||
1150 | #define ISPRSZ_VFILT2928_COEF28_MASK 0x3FF | ||
1151 | #define ISPRSZ_VFILT2928_COEF29_SHIFT 16 | ||
1152 | #define ISPRSZ_VFILT2928_COEF29_MASK 0x3FF0000 | ||
1153 | |||
1154 | #define ISPRSZ_VFILT3130_COEF30_SHIFT 0 | ||
1155 | #define ISPRSZ_VFILT3130_COEF30_MASK 0x3FF | ||
1156 | #define ISPRSZ_VFILT3130_COEF31_SHIFT 16 | ||
1157 | #define ISPRSZ_VFILT3130_COEF31_MASK 0x3FF0000 | ||
1158 | |||
1159 | #define ISPRSZ_YENH_CORE_SHIFT 0 | ||
1160 | #define ISPRSZ_YENH_CORE_MASK \ | ||
1161 | (0xFF << ISPRSZ_YENH_CORE_SHIFT) | ||
1162 | #define ISPRSZ_YENH_SLOP_SHIFT 8 | ||
1163 | #define ISPRSZ_YENH_SLOP_MASK \ | ||
1164 | (0xF << ISPRSZ_YENH_SLOP_SHIFT) | ||
1165 | #define ISPRSZ_YENH_GAIN_SHIFT 12 | ||
1166 | #define ISPRSZ_YENH_GAIN_MASK \ | ||
1167 | (0xF << ISPRSZ_YENH_GAIN_SHIFT) | ||
1168 | #define ISPRSZ_YENH_ALGO_SHIFT 16 | ||
1169 | #define ISPRSZ_YENH_ALGO_MASK \ | ||
1170 | (0x3 << ISPRSZ_YENH_ALGO_SHIFT) | ||
1171 | |||
1172 | #define ISPH3A_PCR_AEW_ALAW_EN_SHIFT 1 | ||
1173 | #define ISPH3A_PCR_AF_MED_TH_SHIFT 3 | ||
1174 | #define ISPH3A_PCR_AF_RGBPOS_SHIFT 11 | ||
1175 | #define ISPH3A_PCR_AEW_AVE2LMT_SHIFT 22 | ||
1176 | #define ISPH3A_PCR_AEW_AVE2LMT_MASK 0xFFC00000 | ||
1177 | #define ISPH3A_PCR_BUSYAF (1 << 15) | ||
1178 | #define ISPH3A_PCR_BUSYAEAWB (1 << 18) | ||
1179 | |||
1180 | #define ISPH3A_AEWWIN1_WINHC_SHIFT 0 | ||
1181 | #define ISPH3A_AEWWIN1_WINHC_MASK 0x3F | ||
1182 | #define ISPH3A_AEWWIN1_WINVC_SHIFT 6 | ||
1183 | #define ISPH3A_AEWWIN1_WINVC_MASK 0x1FC0 | ||
1184 | #define ISPH3A_AEWWIN1_WINW_SHIFT 13 | ||
1185 | #define ISPH3A_AEWWIN1_WINW_MASK 0xFE000 | ||
1186 | #define ISPH3A_AEWWIN1_WINH_SHIFT 24 | ||
1187 | #define ISPH3A_AEWWIN1_WINH_MASK 0x7F000000 | ||
1188 | |||
1189 | #define ISPH3A_AEWINSTART_WINSH_SHIFT 0 | ||
1190 | #define ISPH3A_AEWINSTART_WINSH_MASK 0x0FFF | ||
1191 | #define ISPH3A_AEWINSTART_WINSV_SHIFT 16 | ||
1192 | #define ISPH3A_AEWINSTART_WINSV_MASK 0x0FFF0000 | ||
1193 | |||
1194 | #define ISPH3A_AEWINBLK_WINH_SHIFT 0 | ||
1195 | #define ISPH3A_AEWINBLK_WINH_MASK 0x7F | ||
1196 | #define ISPH3A_AEWINBLK_WINSV_SHIFT 16 | ||
1197 | #define ISPH3A_AEWINBLK_WINSV_MASK 0x0FFF0000 | ||
1198 | |||
1199 | #define ISPH3A_AEWSUBWIN_AEWINCH_SHIFT 0 | ||
1200 | #define ISPH3A_AEWSUBWIN_AEWINCH_MASK 0x0F | ||
1201 | #define ISPH3A_AEWSUBWIN_AEWINCV_SHIFT 8 | ||
1202 | #define ISPH3A_AEWSUBWIN_AEWINCV_MASK 0x0F00 | ||
1203 | |||
1204 | #define ISPHIST_PCR_ENABLE_SHIFT 0 | ||
1205 | #define ISPHIST_PCR_ENABLE_MASK 0x01 | ||
1206 | #define ISPHIST_PCR_ENABLE (1 << ISPHIST_PCR_ENABLE_SHIFT) | ||
1207 | #define ISPHIST_PCR_BUSY 0x02 | ||
1208 | |||
1209 | #define ISPHIST_CNT_DATASIZE_SHIFT 8 | ||
1210 | #define ISPHIST_CNT_DATASIZE_MASK 0x0100 | ||
1211 | #define ISPHIST_CNT_CLEAR_SHIFT 7 | ||
1212 | #define ISPHIST_CNT_CLEAR_MASK 0x080 | ||
1213 | #define ISPHIST_CNT_CLEAR (1 << ISPHIST_CNT_CLEAR_SHIFT) | ||
1214 | #define ISPHIST_CNT_CFA_SHIFT 6 | ||
1215 | #define ISPHIST_CNT_CFA_MASK 0x040 | ||
1216 | #define ISPHIST_CNT_BINS_SHIFT 4 | ||
1217 | #define ISPHIST_CNT_BINS_MASK 0x030 | ||
1218 | #define ISPHIST_CNT_SOURCE_SHIFT 3 | ||
1219 | #define ISPHIST_CNT_SOURCE_MASK 0x08 | ||
1220 | #define ISPHIST_CNT_SHIFT_SHIFT 0 | ||
1221 | #define ISPHIST_CNT_SHIFT_MASK 0x07 | ||
1222 | |||
1223 | #define ISPHIST_WB_GAIN_WG00_SHIFT 24 | ||
1224 | #define ISPHIST_WB_GAIN_WG00_MASK 0xFF000000 | ||
1225 | #define ISPHIST_WB_GAIN_WG01_SHIFT 16 | ||
1226 | #define ISPHIST_WB_GAIN_WG01_MASK 0xFF0000 | ||
1227 | #define ISPHIST_WB_GAIN_WG02_SHIFT 8 | ||
1228 | #define ISPHIST_WB_GAIN_WG02_MASK 0xFF00 | ||
1229 | #define ISPHIST_WB_GAIN_WG03_SHIFT 0 | ||
1230 | #define ISPHIST_WB_GAIN_WG03_MASK 0xFF | ||
1231 | |||
1232 | #define ISPHIST_REG_START_END_MASK 0x3FFF | ||
1233 | #define ISPHIST_REG_START_SHIFT 16 | ||
1234 | #define ISPHIST_REG_END_SHIFT 0 | ||
1235 | #define ISPHIST_REG_START_MASK (ISPHIST_REG_START_END_MASK << \ | ||
1236 | ISPHIST_REG_START_SHIFT) | ||
1237 | #define ISPHIST_REG_END_MASK (ISPHIST_REG_START_END_MASK << \ | ||
1238 | ISPHIST_REG_END_SHIFT) | ||
1239 | |||
1240 | #define ISPHIST_REG_MASK (ISPHIST_REG_START_MASK | \ | ||
1241 | ISPHIST_REG_END_MASK) | ||
1242 | |||
1243 | #define ISPHIST_ADDR_SHIFT 0 | ||
1244 | #define ISPHIST_ADDR_MASK 0x3FF | ||
1245 | |||
1246 | #define ISPHIST_DATA_SHIFT 0 | ||
1247 | #define ISPHIST_DATA_MASK 0xFFFFF | ||
1248 | |||
1249 | #define ISPHIST_RADD_SHIFT 0 | ||
1250 | #define ISPHIST_RADD_MASK 0xFFFFFFFF | ||
1251 | |||
1252 | #define ISPHIST_RADD_OFF_SHIFT 0 | ||
1253 | #define ISPHIST_RADD_OFF_MASK 0xFFFF | ||
1254 | |||
1255 | #define ISPHIST_HV_INFO_HSIZE_SHIFT 16 | ||
1256 | #define ISPHIST_HV_INFO_HSIZE_MASK 0x3FFF0000 | ||
1257 | #define ISPHIST_HV_INFO_VSIZE_SHIFT 0 | ||
1258 | #define ISPHIST_HV_INFO_VSIZE_MASK 0x3FFF | ||
1259 | |||
1260 | #define ISPHIST_HV_INFO_MASK 0x3FFF3FFF | ||
1261 | |||
1262 | #define ISPCCDC_LSC_ENABLE 1 | ||
1263 | #define ISPCCDC_LSC_BUSY (1 << 7) | ||
1264 | #define ISPCCDC_LSC_GAIN_MODE_N_MASK 0x700 | ||
1265 | #define ISPCCDC_LSC_GAIN_MODE_N_SHIFT 8 | ||
1266 | #define ISPCCDC_LSC_GAIN_MODE_M_MASK 0x3800 | ||
1267 | #define ISPCCDC_LSC_GAIN_MODE_M_SHIFT 12 | ||
1268 | #define ISPCCDC_LSC_GAIN_FORMAT_MASK 0xE | ||
1269 | #define ISPCCDC_LSC_GAIN_FORMAT_SHIFT 1 | ||
1270 | #define ISPCCDC_LSC_AFTER_REFORMATTER_MASK (1<<6) | ||
1271 | |||
1272 | #define ISPCCDC_LSC_INITIAL_X_MASK 0x3F | ||
1273 | #define ISPCCDC_LSC_INITIAL_X_SHIFT 0 | ||
1274 | #define ISPCCDC_LSC_INITIAL_Y_MASK 0x3F0000 | ||
1275 | #define ISPCCDC_LSC_INITIAL_Y_SHIFT 16 | ||
1276 | |||
1277 | /* ----------------------------------------------------------------------------- | ||
1278 | * CSI2 receiver registers (ES2.0) | ||
1279 | */ | ||
1280 | |||
1281 | #define ISPCSI2_REVISION (0x000) | ||
1282 | #define ISPCSI2_SYSCONFIG (0x010) | ||
1283 | #define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT 12 | ||
1284 | #define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK \ | ||
1285 | (0x3 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
1286 | #define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_FORCE \ | ||
1287 | (0x0 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
1288 | #define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO \ | ||
1289 | (0x1 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
1290 | #define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART \ | ||
1291 | (0x2 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) | ||
1292 | #define ISPCSI2_SYSCONFIG_SOFT_RESET (1 << 1) | ||
1293 | #define ISPCSI2_SYSCONFIG_AUTO_IDLE (1 << 0) | ||
1294 | |||
1295 | #define ISPCSI2_SYSSTATUS (0x014) | ||
1296 | #define ISPCSI2_SYSSTATUS_RESET_DONE (1 << 0) | ||
1297 | |||
1298 | #define ISPCSI2_IRQSTATUS (0x018) | ||
1299 | #define ISPCSI2_IRQSTATUS_OCP_ERR_IRQ (1 << 14) | ||
1300 | #define ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ (1 << 13) | ||
1301 | #define ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 12) | ||
1302 | #define ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ (1 << 11) | ||
1303 | #define ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ (1 << 10) | ||
1304 | #define ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ (1 << 9) | ||
1305 | #define ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ (1 << 8) | ||
1306 | #define ISPCSI2_IRQSTATUS_CONTEXT(n) (1 << (n)) | ||
1307 | |||
1308 | #define ISPCSI2_IRQENABLE (0x01c) | ||
1309 | #define ISPCSI2_CTRL (0x040) | ||
1310 | #define ISPCSI2_CTRL_VP_CLK_EN (1 << 15) | ||
1311 | #define ISPCSI2_CTRL_VP_ONLY_EN (1 << 11) | ||
1312 | #define ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT 8 | ||
1313 | #define ISPCSI2_CTRL_VP_OUT_CTRL_MASK \ | ||
1314 | (3 << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) | ||
1315 | #define ISPCSI2_CTRL_DBG_EN (1 << 7) | ||
1316 | #define ISPCSI2_CTRL_BURST_SIZE_SHIFT 5 | ||
1317 | #define ISPCSI2_CTRL_BURST_SIZE_MASK \ | ||
1318 | (3 << ISPCSI2_CTRL_BURST_SIZE_SHIFT) | ||
1319 | #define ISPCSI2_CTRL_FRAME (1 << 3) | ||
1320 | #define ISPCSI2_CTRL_ECC_EN (1 << 2) | ||
1321 | #define ISPCSI2_CTRL_SECURE (1 << 1) | ||
1322 | #define ISPCSI2_CTRL_IF_EN (1 << 0) | ||
1323 | |||
1324 | #define ISPCSI2_DBG_H (0x044) | ||
1325 | #define ISPCSI2_GNQ (0x048) | ||
1326 | #define ISPCSI2_PHY_CFG (0x050) | ||
1327 | #define ISPCSI2_PHY_CFG_RESET_CTRL (1 << 30) | ||
1328 | #define ISPCSI2_PHY_CFG_RESET_DONE (1 << 29) | ||
1329 | #define ISPCSI2_PHY_CFG_PWR_CMD_SHIFT 27 | ||
1330 | #define ISPCSI2_PHY_CFG_PWR_CMD_MASK \ | ||
1331 | (0x3 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) | ||
1332 | #define ISPCSI2_PHY_CFG_PWR_CMD_OFF \ | ||
1333 | (0x0 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) | ||
1334 | #define ISPCSI2_PHY_CFG_PWR_CMD_ON \ | ||
1335 | (0x1 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) | ||
1336 | #define ISPCSI2_PHY_CFG_PWR_CMD_ULPW \ | ||
1337 | (0x2 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) | ||
1338 | #define ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT 25 | ||
1339 | #define ISPCSI2_PHY_CFG_PWR_STATUS_MASK \ | ||
1340 | (0x3 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) | ||
1341 | #define ISPCSI2_PHY_CFG_PWR_STATUS_OFF \ | ||
1342 | (0x0 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) | ||
1343 | #define ISPCSI2_PHY_CFG_PWR_STATUS_ON \ | ||
1344 | (0x1 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) | ||
1345 | #define ISPCSI2_PHY_CFG_PWR_STATUS_ULPW \ | ||
1346 | (0x2 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) | ||
1347 | #define ISPCSI2_PHY_CFG_PWR_AUTO (1 << 24) | ||
1348 | |||
1349 | #define ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n) (3 + ((n) * 4)) | ||
1350 | #define ISPCSI2_PHY_CFG_DATA_POL_MASK(n) \ | ||
1351 | (0x1 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n)) | ||
1352 | #define ISPCSI2_PHY_CFG_DATA_POL_PN(n) \ | ||
1353 | (0x0 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n)) | ||
1354 | #define ISPCSI2_PHY_CFG_DATA_POL_NP(n) \ | ||
1355 | (0x1 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n)) | ||
1356 | |||
1357 | #define ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n) ((n) * 4) | ||
1358 | #define ISPCSI2_PHY_CFG_DATA_POSITION_MASK(n) \ | ||
1359 | (0x7 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
1360 | #define ISPCSI2_PHY_CFG_DATA_POSITION_NC(n) \ | ||
1361 | (0x0 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
1362 | #define ISPCSI2_PHY_CFG_DATA_POSITION_1(n) \ | ||
1363 | (0x1 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
1364 | #define ISPCSI2_PHY_CFG_DATA_POSITION_2(n) \ | ||
1365 | (0x2 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
1366 | #define ISPCSI2_PHY_CFG_DATA_POSITION_3(n) \ | ||
1367 | (0x3 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
1368 | #define ISPCSI2_PHY_CFG_DATA_POSITION_4(n) \ | ||
1369 | (0x4 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
1370 | #define ISPCSI2_PHY_CFG_DATA_POSITION_5(n) \ | ||
1371 | (0x5 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) | ||
1372 | |||
1373 | #define ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT 3 | ||
1374 | #define ISPCSI2_PHY_CFG_CLOCK_POL_MASK \ | ||
1375 | (0x1 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT) | ||
1376 | #define ISPCSI2_PHY_CFG_CLOCK_POL_PN \ | ||
1377 | (0x0 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT) | ||
1378 | #define ISPCSI2_PHY_CFG_CLOCK_POL_NP \ | ||
1379 | (0x1 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT) | ||
1380 | |||
1381 | #define ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT 0 | ||
1382 | #define ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK \ | ||
1383 | (0x7 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) | ||
1384 | #define ISPCSI2_PHY_CFG_CLOCK_POSITION_1 \ | ||
1385 | (0x1 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) | ||
1386 | #define ISPCSI2_PHY_CFG_CLOCK_POSITION_2 \ | ||
1387 | (0x2 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) | ||
1388 | #define ISPCSI2_PHY_CFG_CLOCK_POSITION_3 \ | ||
1389 | (0x3 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) | ||
1390 | #define ISPCSI2_PHY_CFG_CLOCK_POSITION_4 \ | ||
1391 | (0x4 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) | ||
1392 | #define ISPCSI2_PHY_CFG_CLOCK_POSITION_5 \ | ||
1393 | (0x5 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) | ||
1394 | |||
1395 | #define ISPCSI2_PHY_IRQSTATUS (0x054) | ||
1396 | #define ISPCSI2_PHY_IRQSTATUS_STATEALLULPMEXIT (1 << 26) | ||
1397 | #define ISPCSI2_PHY_IRQSTATUS_STATEALLULPMENTER (1 << 25) | ||
1398 | #define ISPCSI2_PHY_IRQSTATUS_STATEULPM5 (1 << 24) | ||
1399 | #define ISPCSI2_PHY_IRQSTATUS_STATEULPM4 (1 << 23) | ||
1400 | #define ISPCSI2_PHY_IRQSTATUS_STATEULPM3 (1 << 22) | ||
1401 | #define ISPCSI2_PHY_IRQSTATUS_STATEULPM2 (1 << 21) | ||
1402 | #define ISPCSI2_PHY_IRQSTATUS_STATEULPM1 (1 << 20) | ||
1403 | #define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL5 (1 << 19) | ||
1404 | #define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL4 (1 << 18) | ||
1405 | #define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL3 (1 << 17) | ||
1406 | #define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL2 (1 << 16) | ||
1407 | #define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL1 (1 << 15) | ||
1408 | #define ISPCSI2_PHY_IRQSTATUS_ERRESC5 (1 << 14) | ||
1409 | #define ISPCSI2_PHY_IRQSTATUS_ERRESC4 (1 << 13) | ||
1410 | #define ISPCSI2_PHY_IRQSTATUS_ERRESC3 (1 << 12) | ||
1411 | #define ISPCSI2_PHY_IRQSTATUS_ERRESC2 (1 << 11) | ||
1412 | #define ISPCSI2_PHY_IRQSTATUS_ERRESC1 (1 << 10) | ||
1413 | #define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS5 (1 << 9) | ||
1414 | #define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS4 (1 << 8) | ||
1415 | #define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS3 (1 << 7) | ||
1416 | #define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS2 (1 << 6) | ||
1417 | #define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS1 (1 << 5) | ||
1418 | #define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS5 (1 << 4) | ||
1419 | #define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS4 (1 << 3) | ||
1420 | #define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS3 (1 << 2) | ||
1421 | #define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS2 (1 << 1) | ||
1422 | #define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS1 1 | ||
1423 | |||
1424 | #define ISPCSI2_SHORT_PACKET (0x05c) | ||
1425 | #define ISPCSI2_PHY_IRQENABLE (0x060) | ||
1426 | #define ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT (1 << 26) | ||
1427 | #define ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER (1 << 25) | ||
1428 | #define ISPCSI2_PHY_IRQENABLE_STATEULPM5 (1 << 24) | ||
1429 | #define ISPCSI2_PHY_IRQENABLE_STATEULPM4 (1 << 23) | ||
1430 | #define ISPCSI2_PHY_IRQENABLE_STATEULPM3 (1 << 22) | ||
1431 | #define ISPCSI2_PHY_IRQENABLE_STATEULPM2 (1 << 21) | ||
1432 | #define ISPCSI2_PHY_IRQENABLE_STATEULPM1 (1 << 20) | ||
1433 | #define ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 (1 << 19) | ||
1434 | #define ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 (1 << 18) | ||
1435 | #define ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 (1 << 17) | ||
1436 | #define ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 (1 << 16) | ||
1437 | #define ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 (1 << 15) | ||
1438 | #define ISPCSI2_PHY_IRQENABLE_ERRESC5 (1 << 14) | ||
1439 | #define ISPCSI2_PHY_IRQENABLE_ERRESC4 (1 << 13) | ||
1440 | #define ISPCSI2_PHY_IRQENABLE_ERRESC3 (1 << 12) | ||
1441 | #define ISPCSI2_PHY_IRQENABLE_ERRESC2 (1 << 11) | ||
1442 | #define ISPCSI2_PHY_IRQENABLE_ERRESC1 (1 << 10) | ||
1443 | #define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 (1 << 9) | ||
1444 | #define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 (1 << 8) | ||
1445 | #define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 (1 << 7) | ||
1446 | #define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 (1 << 6) | ||
1447 | #define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 (1 << 5) | ||
1448 | #define ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 (1 << 4) | ||
1449 | #define ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 (1 << 3) | ||
1450 | #define ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 (1 << 2) | ||
1451 | #define ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 (1 << 1) | ||
1452 | #define ISPCSI2_PHY_IRQENABLE_ERRSOTHS1 (1 << 0) | ||
1453 | |||
1454 | #define ISPCSI2_DBG_P (0x068) | ||
1455 | #define ISPCSI2_TIMING (0x06c) | ||
1456 | #define ISPCSI2_TIMING_FORCE_RX_MODE_IO(n) (1 << ((16 * ((n) - 1)) + 15)) | ||
1457 | #define ISPCSI2_TIMING_STOP_STATE_X16_IO(n) (1 << ((16 * ((n) - 1)) + 14)) | ||
1458 | #define ISPCSI2_TIMING_STOP_STATE_X4_IO(n) (1 << ((16 * ((n) - 1)) + 13)) | ||
1459 | #define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n) (16 * ((n) - 1)) | ||
1460 | #define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(n) \ | ||
1461 | (0x1fff << ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n)) | ||
1462 | |||
1463 | #define ISPCSI2_CTX_CTRL1(n) ((0x070) + 0x20 * (n)) | ||
1464 | #define ISPCSI2_CTX_CTRL1_COUNT_SHIFT 8 | ||
1465 | #define ISPCSI2_CTX_CTRL1_COUNT_MASK \ | ||
1466 | (0xff << ISPCSI2_CTX_CTRL1_COUNT_SHIFT) | ||
1467 | #define ISPCSI2_CTX_CTRL1_EOF_EN (1 << 7) | ||
1468 | #define ISPCSI2_CTX_CTRL1_EOL_EN (1 << 6) | ||
1469 | #define ISPCSI2_CTX_CTRL1_CS_EN (1 << 5) | ||
1470 | #define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK (1 << 4) | ||
1471 | #define ISPCSI2_CTX_CTRL1_PING_PONG (1 << 3) | ||
1472 | #define ISPCSI2_CTX_CTRL1_CTX_EN (1 << 0) | ||
1473 | |||
1474 | #define ISPCSI2_CTX_CTRL2(n) ((0x074) + 0x20 * (n)) | ||
1475 | #define ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT 13 | ||
1476 | #define ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK \ | ||
1477 | (0x3 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT) | ||
1478 | #define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT 11 | ||
1479 | #define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK \ | ||
1480 | (0x3 << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT) | ||
1481 | #define ISPCSI2_CTX_CTRL2_DPCM_PRED (1 << 10) | ||
1482 | #define ISPCSI2_CTX_CTRL2_FORMAT_SHIFT 0 | ||
1483 | #define ISPCSI2_CTX_CTRL2_FORMAT_MASK \ | ||
1484 | (0x3ff << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT) | ||
1485 | #define ISPCSI2_CTX_CTRL2_FRAME_SHIFT 16 | ||
1486 | #define ISPCSI2_CTX_CTRL2_FRAME_MASK \ | ||
1487 | (0xffff << ISPCSI2_CTX_CTRL2_FRAME_SHIFT) | ||
1488 | |||
1489 | #define ISPCSI2_CTX_DAT_OFST(n) ((0x078) + 0x20 * (n)) | ||
1490 | #define ISPCSI2_CTX_DAT_OFST_OFST_SHIFT 0 | ||
1491 | #define ISPCSI2_CTX_DAT_OFST_OFST_MASK \ | ||
1492 | (0x1ffe0 << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT) | ||
1493 | |||
1494 | #define ISPCSI2_CTX_DAT_PING_ADDR(n) ((0x07c) + 0x20 * (n)) | ||
1495 | #define ISPCSI2_CTX_DAT_PONG_ADDR(n) ((0x080) + 0x20 * (n)) | ||
1496 | #define ISPCSI2_CTX_IRQENABLE(n) ((0x084) + 0x20 * (n)) | ||
1497 | #define ISPCSI2_CTX_IRQENABLE_ECC_CORRECTION_IRQ (1 << 8) | ||
1498 | #define ISPCSI2_CTX_IRQENABLE_LINE_NUMBER_IRQ (1 << 7) | ||
1499 | #define ISPCSI2_CTX_IRQENABLE_FRAME_NUMBER_IRQ (1 << 6) | ||
1500 | #define ISPCSI2_CTX_IRQENABLE_CS_IRQ (1 << 5) | ||
1501 | #define ISPCSI2_CTX_IRQENABLE_LE_IRQ (1 << 3) | ||
1502 | #define ISPCSI2_CTX_IRQENABLE_LS_IRQ (1 << 2) | ||
1503 | #define ISPCSI2_CTX_IRQENABLE_FE_IRQ (1 << 1) | ||
1504 | #define ISPCSI2_CTX_IRQENABLE_FS_IRQ (1 << 0) | ||
1505 | |||
1506 | #define ISPCSI2_CTX_IRQSTATUS(n) ((0x088) + 0x20 * (n)) | ||
1507 | #define ISPCSI2_CTX_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 8) | ||
1508 | #define ISPCSI2_CTX_IRQSTATUS_LINE_NUMBER_IRQ (1 << 7) | ||
1509 | #define ISPCSI2_CTX_IRQSTATUS_FRAME_NUMBER_IRQ (1 << 6) | ||
1510 | #define ISPCSI2_CTX_IRQSTATUS_CS_IRQ (1 << 5) | ||
1511 | #define ISPCSI2_CTX_IRQSTATUS_LE_IRQ (1 << 3) | ||
1512 | #define ISPCSI2_CTX_IRQSTATUS_LS_IRQ (1 << 2) | ||
1513 | #define ISPCSI2_CTX_IRQSTATUS_FE_IRQ (1 << 1) | ||
1514 | #define ISPCSI2_CTX_IRQSTATUS_FS_IRQ (1 << 0) | ||
1515 | |||
1516 | #define ISPCSI2_CTX_CTRL3(n) ((0x08c) + 0x20 * (n)) | ||
1517 | #define ISPCSI2_CTX_CTRL3_ALPHA_SHIFT 5 | ||
1518 | #define ISPCSI2_CTX_CTRL3_ALPHA_MASK \ | ||
1519 | (0x3fff << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT) | ||
1520 | |||
1521 | /* This instance is for OMAP3630 only */ | ||
1522 | #define ISPCSI2_CTX_TRANSCODEH(n) (0x000 + 0x8 * (n)) | ||
1523 | #define ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT 16 | ||
1524 | #define ISPCSI2_CTX_TRANSCODEH_HCOUNT_MASK \ | ||
1525 | (0x1fff << ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT) | ||
1526 | #define ISPCSI2_CTX_TRANSCODEH_HSKIP_SHIFT 0 | ||
1527 | #define ISPCSI2_CTX_TRANSCODEH_HSKIP_MASK \ | ||
1528 | (0x1fff << ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT) | ||
1529 | #define ISPCSI2_CTX_TRANSCODEV(n) (0x004 + 0x8 * (n)) | ||
1530 | #define ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT 16 | ||
1531 | #define ISPCSI2_CTX_TRANSCODEV_VCOUNT_MASK \ | ||
1532 | (0x1fff << ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT) | ||
1533 | #define ISPCSI2_CTX_TRANSCODEV_VSKIP_SHIFT 0 | ||
1534 | #define ISPCSI2_CTX_TRANSCODEV_VSKIP_MASK \ | ||
1535 | (0x1fff << ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT) | ||
1536 | |||
1537 | /* ----------------------------------------------------------------------------- | ||
1538 | * CSI PHY registers | ||
1539 | */ | ||
1540 | |||
1541 | #define ISPCSIPHY_REG0 (0x000) | ||
1542 | #define ISPCSIPHY_REG0_THS_TERM_SHIFT 8 | ||
1543 | #define ISPCSIPHY_REG0_THS_TERM_MASK \ | ||
1544 | (0xff << ISPCSIPHY_REG0_THS_TERM_SHIFT) | ||
1545 | #define ISPCSIPHY_REG0_THS_SETTLE_SHIFT 0 | ||
1546 | #define ISPCSIPHY_REG0_THS_SETTLE_MASK \ | ||
1547 | (0xff << ISPCSIPHY_REG0_THS_SETTLE_SHIFT) | ||
1548 | |||
1549 | #define ISPCSIPHY_REG1 (0x004) | ||
1550 | #define ISPCSIPHY_REG1_RESET_DONE_CTRLCLK (1 << 29) | ||
1551 | /* This field is for OMAP3630 only */ | ||
1552 | #define ISPCSIPHY_REG1_CLOCK_MISS_DETECTOR_STATUS (1 << 25) | ||
1553 | #define ISPCSIPHY_REG1_TCLK_TERM_SHIFT 18 | ||
1554 | #define ISPCSIPHY_REG1_TCLK_TERM_MASK \ | ||
1555 | (0x7f << ISPCSIPHY_REG1_TCLK_TERM_SHIFT) | ||
1556 | #define ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN_SHIFT 10 | ||
1557 | #define ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN_MASK \ | ||
1558 | (0xff << ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN) | ||
1559 | /* This field is for OMAP3430 only */ | ||
1560 | #define ISPCSIPHY_REG1_TCLK_MISS_SHIFT 8 | ||
1561 | #define ISPCSIPHY_REG1_TCLK_MISS_MASK \ | ||
1562 | (0x3 << ISPCSIPHY_REG1_TCLK_MISS_SHIFT) | ||
1563 | /* This field is for OMAP3630 only */ | ||
1564 | #define ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT 8 | ||
1565 | #define ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_MASK \ | ||
1566 | (0x3 << ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT) | ||
1567 | #define ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT 0 | ||
1568 | #define ISPCSIPHY_REG1_TCLK_SETTLE_MASK \ | ||
1569 | (0xff << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT) | ||
1570 | |||
1571 | /* This register is for OMAP3630 only */ | ||
1572 | #define ISPCSIPHY_REG2 (0x008) | ||
1573 | #define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_SHIFT 30 | ||
1574 | #define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_MASK \ | ||
1575 | (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_SHIFT) | ||
1576 | #define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_SHIFT 28 | ||
1577 | #define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_MASK \ | ||
1578 | (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_SHIFT) | ||
1579 | #define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_SHIFT 26 | ||
1580 | #define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_MASK \ | ||
1581 | (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_SHIFT) | ||
1582 | #define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_SHIFT 24 | ||
1583 | #define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_MASK \ | ||
1584 | (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_SHIFT) | ||
1585 | #define ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_SHIFT 0 | ||
1586 | #define ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_MASK \ | ||
1587 | (0x7fffff << ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_SHIFT) | ||
1588 | |||
1589 | #endif /* OMAP3_ISP_REG_H */ | ||
diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c new file mode 100644 index 00000000000..0bb0f8cd36f --- /dev/null +++ b/drivers/media/video/omap3isp/ispresizer.c | |||
@@ -0,0 +1,1738 @@ | |||
1 | /* | ||
2 | * ispresizer.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - Resizer module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #include <linux/device.h> | ||
28 | #include <linux/mm.h> | ||
29 | #include <linux/module.h> | ||
30 | |||
31 | #include "isp.h" | ||
32 | #include "ispreg.h" | ||
33 | #include "ispresizer.h" | ||
34 | |||
35 | /* | ||
36 | * Resizer Constants | ||
37 | */ | ||
38 | #define MIN_RESIZE_VALUE 64 | ||
39 | #define MID_RESIZE_VALUE 512 | ||
40 | #define MAX_RESIZE_VALUE 1024 | ||
41 | |||
42 | #define MIN_IN_WIDTH 32 | ||
43 | #define MIN_IN_HEIGHT 32 | ||
44 | #define MAX_IN_WIDTH_MEMORY_MODE 4095 | ||
45 | #define MAX_IN_WIDTH_ONTHEFLY_MODE_ES1 1280 | ||
46 | #define MAX_IN_WIDTH_ONTHEFLY_MODE_ES2 4095 | ||
47 | #define MAX_IN_HEIGHT 4095 | ||
48 | |||
49 | #define MIN_OUT_WIDTH 16 | ||
50 | #define MIN_OUT_HEIGHT 2 | ||
51 | #define MAX_OUT_HEIGHT 4095 | ||
52 | |||
53 | /* | ||
54 | * Resizer Use Constraints | ||
55 | * "TRM ES3.1, table 12-46" | ||
56 | */ | ||
57 | #define MAX_4TAP_OUT_WIDTH_ES1 1280 | ||
58 | #define MAX_7TAP_OUT_WIDTH_ES1 640 | ||
59 | #define MAX_4TAP_OUT_WIDTH_ES2 3312 | ||
60 | #define MAX_7TAP_OUT_WIDTH_ES2 1650 | ||
61 | #define MAX_4TAP_OUT_WIDTH_3630 4096 | ||
62 | #define MAX_7TAP_OUT_WIDTH_3630 2048 | ||
63 | |||
64 | /* | ||
65 | * Constants for ratio calculation | ||
66 | */ | ||
67 | #define RESIZE_DIVISOR 256 | ||
68 | #define DEFAULT_PHASE 1 | ||
69 | |||
70 | /* | ||
71 | * Default (and only) configuration of filter coefficients. | ||
72 | * 7-tap mode is for scale factors 0.25x to 0.5x. | ||
73 | * 4-tap mode is for scale factors 0.5x to 4.0x. | ||
74 | * There shouldn't be any reason to recalculate these, EVER. | ||
75 | */ | ||
76 | static const struct isprsz_coef filter_coefs = { | ||
77 | /* For 8-phase 4-tap horizontal filter: */ | ||
78 | { | ||
79 | 0x0000, 0x0100, 0x0000, 0x0000, | ||
80 | 0x03FA, 0x00F6, 0x0010, 0x0000, | ||
81 | 0x03F9, 0x00DB, 0x002C, 0x0000, | ||
82 | 0x03FB, 0x00B3, 0x0053, 0x03FF, | ||
83 | 0x03FD, 0x0082, 0x0084, 0x03FD, | ||
84 | 0x03FF, 0x0053, 0x00B3, 0x03FB, | ||
85 | 0x0000, 0x002C, 0x00DB, 0x03F9, | ||
86 | 0x0000, 0x0010, 0x00F6, 0x03FA | ||
87 | }, | ||
88 | /* For 8-phase 4-tap vertical filter: */ | ||
89 | { | ||
90 | 0x0000, 0x0100, 0x0000, 0x0000, | ||
91 | 0x03FA, 0x00F6, 0x0010, 0x0000, | ||
92 | 0x03F9, 0x00DB, 0x002C, 0x0000, | ||
93 | 0x03FB, 0x00B3, 0x0053, 0x03FF, | ||
94 | 0x03FD, 0x0082, 0x0084, 0x03FD, | ||
95 | 0x03FF, 0x0053, 0x00B3, 0x03FB, | ||
96 | 0x0000, 0x002C, 0x00DB, 0x03F9, | ||
97 | 0x0000, 0x0010, 0x00F6, 0x03FA | ||
98 | }, | ||
99 | /* For 4-phase 7-tap horizontal filter: */ | ||
100 | #define DUMMY 0 | ||
101 | { | ||
102 | 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY, | ||
103 | 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY, | ||
104 | 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY, | ||
105 | 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY | ||
106 | }, | ||
107 | /* For 4-phase 7-tap vertical filter: */ | ||
108 | { | ||
109 | 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY, | ||
110 | 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY, | ||
111 | 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY, | ||
112 | 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY | ||
113 | } | ||
114 | /* | ||
115 | * The dummy padding is required in 7-tap mode because of how the | ||
116 | * registers are arranged physically. | ||
117 | */ | ||
118 | #undef DUMMY | ||
119 | }; | ||
120 | |||
121 | /* | ||
122 | * __resizer_get_format - helper function for getting resizer format | ||
123 | * @res : pointer to resizer private structure | ||
124 | * @pad : pad number | ||
125 | * @fh : V4L2 subdev file handle | ||
126 | * @which : wanted subdev format | ||
127 | * return zero | ||
128 | */ | ||
129 | static struct v4l2_mbus_framefmt * | ||
130 | __resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh, | ||
131 | unsigned int pad, enum v4l2_subdev_format_whence which) | ||
132 | { | ||
133 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
134 | return v4l2_subdev_get_try_format(fh, pad); | ||
135 | else | ||
136 | return &res->formats[pad]; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * __resizer_get_crop - helper function for getting resizer crop rectangle | ||
141 | * @res : pointer to resizer private structure | ||
142 | * @fh : V4L2 subdev file handle | ||
143 | * @which : wanted subdev crop rectangle | ||
144 | */ | ||
145 | static struct v4l2_rect * | ||
146 | __resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_fh *fh, | ||
147 | enum v4l2_subdev_format_whence which) | ||
148 | { | ||
149 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
150 | return v4l2_subdev_get_try_crop(fh, RESZ_PAD_SINK); | ||
151 | else | ||
152 | return &res->crop.request; | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * resizer_set_filters - Set resizer filters | ||
157 | * @res: Device context. | ||
158 | * @h_coeff: horizontal coefficient | ||
159 | * @v_coeff: vertical coefficient | ||
160 | * Return none | ||
161 | */ | ||
162 | static void resizer_set_filters(struct isp_res_device *res, const u16 *h_coeff, | ||
163 | const u16 *v_coeff) | ||
164 | { | ||
165 | struct isp_device *isp = to_isp_device(res); | ||
166 | u32 startaddr_h, startaddr_v, tmp_h, tmp_v; | ||
167 | int i; | ||
168 | |||
169 | startaddr_h = ISPRSZ_HFILT10; | ||
170 | startaddr_v = ISPRSZ_VFILT10; | ||
171 | |||
172 | for (i = 0; i < COEFF_CNT; i += 2) { | ||
173 | tmp_h = h_coeff[i] | | ||
174 | (h_coeff[i + 1] << ISPRSZ_HFILT_COEF1_SHIFT); | ||
175 | tmp_v = v_coeff[i] | | ||
176 | (v_coeff[i + 1] << ISPRSZ_VFILT_COEF1_SHIFT); | ||
177 | isp_reg_writel(isp, tmp_h, OMAP3_ISP_IOMEM_RESZ, startaddr_h); | ||
178 | isp_reg_writel(isp, tmp_v, OMAP3_ISP_IOMEM_RESZ, startaddr_v); | ||
179 | startaddr_h += 4; | ||
180 | startaddr_v += 4; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * resizer_set_bilinear - Chrominance horizontal algorithm select | ||
186 | * @res: Device context. | ||
187 | * @type: Filtering interpolation type. | ||
188 | * | ||
189 | * Filtering that is same as luminance processing is | ||
190 | * intended only for downsampling, and bilinear interpolation | ||
191 | * is intended only for upsampling. | ||
192 | */ | ||
193 | static void resizer_set_bilinear(struct isp_res_device *res, | ||
194 | enum resizer_chroma_algo type) | ||
195 | { | ||
196 | struct isp_device *isp = to_isp_device(res); | ||
197 | |||
198 | if (type == RSZ_BILINEAR) | ||
199 | isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
200 | ISPRSZ_CNT_CBILIN); | ||
201 | else | ||
202 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
203 | ISPRSZ_CNT_CBILIN); | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * resizer_set_ycpos - Luminance and chrominance order | ||
208 | * @res: Device context. | ||
209 | * @order: order type. | ||
210 | */ | ||
211 | static void resizer_set_ycpos(struct isp_res_device *res, | ||
212 | enum v4l2_mbus_pixelcode pixelcode) | ||
213 | { | ||
214 | struct isp_device *isp = to_isp_device(res); | ||
215 | |||
216 | switch (pixelcode) { | ||
217 | case V4L2_MBUS_FMT_YUYV8_1X16: | ||
218 | isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
219 | ISPRSZ_CNT_YCPOS); | ||
220 | break; | ||
221 | case V4L2_MBUS_FMT_UYVY8_1X16: | ||
222 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
223 | ISPRSZ_CNT_YCPOS); | ||
224 | break; | ||
225 | default: | ||
226 | return; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | * resizer_set_phase - Setup horizontal and vertical starting phase | ||
232 | * @res: Device context. | ||
233 | * @h_phase: horizontal phase parameters. | ||
234 | * @v_phase: vertical phase parameters. | ||
235 | * | ||
236 | * Horizontal and vertical phase range is 0 to 7 | ||
237 | */ | ||
238 | static void resizer_set_phase(struct isp_res_device *res, u32 h_phase, | ||
239 | u32 v_phase) | ||
240 | { | ||
241 | struct isp_device *isp = to_isp_device(res); | ||
242 | u32 rgval = 0; | ||
243 | |||
244 | rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & | ||
245 | ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK); | ||
246 | rgval |= (h_phase << ISPRSZ_CNT_HSTPH_SHIFT) & ISPRSZ_CNT_HSTPH_MASK; | ||
247 | rgval |= (v_phase << ISPRSZ_CNT_VSTPH_SHIFT) & ISPRSZ_CNT_VSTPH_MASK; | ||
248 | |||
249 | isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT); | ||
250 | } | ||
251 | |||
252 | /* | ||
253 | * resizer_set_luma - Setup luminance enhancer parameters | ||
254 | * @res: Device context. | ||
255 | * @luma: Structure for luminance enhancer parameters. | ||
256 | * | ||
257 | * Algorithm select: | ||
258 | * 0x0: Disable | ||
259 | * 0x1: [-1 2 -1]/2 high-pass filter | ||
260 | * 0x2: [-1 -2 6 -2 -1]/4 high-pass filter | ||
261 | * | ||
262 | * Maximum gain: | ||
263 | * The data is coded in U4Q4 representation. | ||
264 | * | ||
265 | * Slope: | ||
266 | * The data is coded in U4Q4 representation. | ||
267 | * | ||
268 | * Coring offset: | ||
269 | * The data is coded in U8Q0 representation. | ||
270 | * | ||
271 | * The new luminance value is computed as: | ||
272 | * Y += HPF(Y) x max(GAIN, (HPF(Y) - CORE) x SLOP + 8) >> 4. | ||
273 | */ | ||
274 | static void resizer_set_luma(struct isp_res_device *res, | ||
275 | struct resizer_luma_yenh *luma) | ||
276 | { | ||
277 | struct isp_device *isp = to_isp_device(res); | ||
278 | u32 rgval = 0; | ||
279 | |||
280 | rgval = (luma->algo << ISPRSZ_YENH_ALGO_SHIFT) | ||
281 | & ISPRSZ_YENH_ALGO_MASK; | ||
282 | rgval |= (luma->gain << ISPRSZ_YENH_GAIN_SHIFT) | ||
283 | & ISPRSZ_YENH_GAIN_MASK; | ||
284 | rgval |= (luma->slope << ISPRSZ_YENH_SLOP_SHIFT) | ||
285 | & ISPRSZ_YENH_SLOP_MASK; | ||
286 | rgval |= (luma->core << ISPRSZ_YENH_CORE_SHIFT) | ||
287 | & ISPRSZ_YENH_CORE_MASK; | ||
288 | |||
289 | isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH); | ||
290 | } | ||
291 | |||
292 | /* | ||
293 | * resizer_set_source - Input source select | ||
294 | * @res: Device context. | ||
295 | * @source: Input source type | ||
296 | * | ||
297 | * If this field is set to RESIZER_INPUT_VP, the resizer input is fed from | ||
298 | * Preview/CCDC engine, otherwise from memory. | ||
299 | */ | ||
300 | static void resizer_set_source(struct isp_res_device *res, | ||
301 | enum resizer_input_entity source) | ||
302 | { | ||
303 | struct isp_device *isp = to_isp_device(res); | ||
304 | |||
305 | if (source == RESIZER_INPUT_MEMORY) | ||
306 | isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
307 | ISPRSZ_CNT_INPSRC); | ||
308 | else | ||
309 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
310 | ISPRSZ_CNT_INPSRC); | ||
311 | } | ||
312 | |||
313 | /* | ||
314 | * resizer_set_ratio - Setup horizontal and vertical resizing value | ||
315 | * @res: Device context. | ||
316 | * @ratio: Structure for ratio parameters. | ||
317 | * | ||
318 | * Resizing range from 64 to 1024 | ||
319 | */ | ||
320 | static void resizer_set_ratio(struct isp_res_device *res, | ||
321 | const struct resizer_ratio *ratio) | ||
322 | { | ||
323 | struct isp_device *isp = to_isp_device(res); | ||
324 | const u16 *h_filter, *v_filter; | ||
325 | u32 rgval = 0; | ||
326 | |||
327 | rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & | ||
328 | ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK); | ||
329 | rgval |= ((ratio->horz - 1) << ISPRSZ_CNT_HRSZ_SHIFT) | ||
330 | & ISPRSZ_CNT_HRSZ_MASK; | ||
331 | rgval |= ((ratio->vert - 1) << ISPRSZ_CNT_VRSZ_SHIFT) | ||
332 | & ISPRSZ_CNT_VRSZ_MASK; | ||
333 | isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT); | ||
334 | |||
335 | /* prepare horizontal filter coefficients */ | ||
336 | if (ratio->horz > MID_RESIZE_VALUE) | ||
337 | h_filter = &filter_coefs.h_filter_coef_7tap[0]; | ||
338 | else | ||
339 | h_filter = &filter_coefs.h_filter_coef_4tap[0]; | ||
340 | |||
341 | /* prepare vertical filter coefficients */ | ||
342 | if (ratio->vert > MID_RESIZE_VALUE) | ||
343 | v_filter = &filter_coefs.v_filter_coef_7tap[0]; | ||
344 | else | ||
345 | v_filter = &filter_coefs.v_filter_coef_4tap[0]; | ||
346 | |||
347 | resizer_set_filters(res, h_filter, v_filter); | ||
348 | } | ||
349 | |||
350 | /* | ||
351 | * resizer_set_dst_size - Setup the output height and width | ||
352 | * @res: Device context. | ||
353 | * @width: Output width. | ||
354 | * @height: Output height. | ||
355 | * | ||
356 | * Width : | ||
357 | * The value must be EVEN. | ||
358 | * | ||
359 | * Height: | ||
360 | * The number of bytes written to SDRAM must be | ||
361 | * a multiple of 16-bytes if the vertical resizing factor | ||
362 | * is greater than 1x (upsizing) | ||
363 | */ | ||
364 | static void resizer_set_output_size(struct isp_res_device *res, | ||
365 | u32 width, u32 height) | ||
366 | { | ||
367 | struct isp_device *isp = to_isp_device(res); | ||
368 | u32 rgval = 0; | ||
369 | |||
370 | dev_dbg(isp->dev, "Output size[w/h]: %dx%d\n", width, height); | ||
371 | rgval = (width << ISPRSZ_OUT_SIZE_HORZ_SHIFT) | ||
372 | & ISPRSZ_OUT_SIZE_HORZ_MASK; | ||
373 | rgval |= (height << ISPRSZ_OUT_SIZE_VERT_SHIFT) | ||
374 | & ISPRSZ_OUT_SIZE_VERT_MASK; | ||
375 | isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE); | ||
376 | } | ||
377 | |||
378 | /* | ||
379 | * resizer_set_output_offset - Setup memory offset for the output lines. | ||
380 | * @res: Device context. | ||
381 | * @offset: Memory offset. | ||
382 | * | ||
383 | * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte | ||
384 | * boundary; the 5 LSBs are read-only. For optimal use of SDRAM bandwidth, | ||
385 | * the SDRAM line offset must be set on a 256-byte boundary | ||
386 | */ | ||
387 | static void resizer_set_output_offset(struct isp_res_device *res, u32 offset) | ||
388 | { | ||
389 | struct isp_device *isp = to_isp_device(res); | ||
390 | |||
391 | isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF); | ||
392 | } | ||
393 | |||
394 | /* | ||
395 | * resizer_set_start - Setup vertical and horizontal start position | ||
396 | * @res: Device context. | ||
397 | * @left: Horizontal start position. | ||
398 | * @top: Vertical start position. | ||
399 | * | ||
400 | * Vertical start line: | ||
401 | * This field makes sense only when the resizer obtains its input | ||
402 | * from the preview engine/CCDC | ||
403 | * | ||
404 | * Horizontal start pixel: | ||
405 | * Pixels are coded on 16 bits for YUV and 8 bits for color separate data. | ||
406 | * When the resizer gets its input from SDRAM, this field must be set | ||
407 | * to <= 15 for YUV 16-bit data and <= 31 for 8-bit color separate data | ||
408 | */ | ||
409 | static void resizer_set_start(struct isp_res_device *res, u32 left, u32 top) | ||
410 | { | ||
411 | struct isp_device *isp = to_isp_device(res); | ||
412 | u32 rgval = 0; | ||
413 | |||
414 | rgval = (left << ISPRSZ_IN_START_HORZ_ST_SHIFT) | ||
415 | & ISPRSZ_IN_START_HORZ_ST_MASK; | ||
416 | rgval |= (top << ISPRSZ_IN_START_VERT_ST_SHIFT) | ||
417 | & ISPRSZ_IN_START_VERT_ST_MASK; | ||
418 | |||
419 | isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START); | ||
420 | } | ||
421 | |||
422 | /* | ||
423 | * resizer_set_input_size - Setup the input size | ||
424 | * @res: Device context. | ||
425 | * @width: The range is 0 to 4095 pixels | ||
426 | * @height: The range is 0 to 4095 lines | ||
427 | */ | ||
428 | static void resizer_set_input_size(struct isp_res_device *res, | ||
429 | u32 width, u32 height) | ||
430 | { | ||
431 | struct isp_device *isp = to_isp_device(res); | ||
432 | u32 rgval = 0; | ||
433 | |||
434 | dev_dbg(isp->dev, "Input size[w/h]: %dx%d\n", width, height); | ||
435 | |||
436 | rgval = (width << ISPRSZ_IN_SIZE_HORZ_SHIFT) | ||
437 | & ISPRSZ_IN_SIZE_HORZ_MASK; | ||
438 | rgval |= (height << ISPRSZ_IN_SIZE_VERT_SHIFT) | ||
439 | & ISPRSZ_IN_SIZE_VERT_MASK; | ||
440 | |||
441 | isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE); | ||
442 | } | ||
443 | |||
444 | /* | ||
445 | * resizer_set_src_offs - Setup the memory offset for the input lines | ||
446 | * @res: Device context. | ||
447 | * @offset: Memory offset. | ||
448 | * | ||
449 | * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte | ||
450 | * boundary; the 5 LSBs are read-only. This field must be programmed to be | ||
451 | * 0x0 if the resizer input is from preview engine/CCDC. | ||
452 | */ | ||
453 | static void resizer_set_input_offset(struct isp_res_device *res, u32 offset) | ||
454 | { | ||
455 | struct isp_device *isp = to_isp_device(res); | ||
456 | |||
457 | isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF); | ||
458 | } | ||
459 | |||
460 | /* | ||
461 | * resizer_set_intype - Input type select | ||
462 | * @res: Device context. | ||
463 | * @type: Pixel format type. | ||
464 | */ | ||
465 | static void resizer_set_intype(struct isp_res_device *res, | ||
466 | enum resizer_colors_type type) | ||
467 | { | ||
468 | struct isp_device *isp = to_isp_device(res); | ||
469 | |||
470 | if (type == RSZ_COLOR8) | ||
471 | isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
472 | ISPRSZ_CNT_INPTYP); | ||
473 | else | ||
474 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, | ||
475 | ISPRSZ_CNT_INPTYP); | ||
476 | } | ||
477 | |||
478 | /* | ||
479 | * __resizer_set_inaddr - Helper function for set input address | ||
480 | * @res : pointer to resizer private data structure | ||
481 | * @addr: input address | ||
482 | * return none | ||
483 | */ | ||
484 | static void __resizer_set_inaddr(struct isp_res_device *res, u32 addr) | ||
485 | { | ||
486 | struct isp_device *isp = to_isp_device(res); | ||
487 | |||
488 | isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD); | ||
489 | } | ||
490 | |||
491 | /* | ||
492 | * The data rate at the horizontal resizer output must not exceed half the | ||
493 | * functional clock or 100 MP/s, whichever is lower. According to the TRM | ||
494 | * there's no similar requirement for the vertical resizer output. However | ||
495 | * experience showed that vertical upscaling by 4 leads to SBL overflows (with | ||
496 | * data rates at the resizer output exceeding 300 MP/s). Limiting the resizer | ||
497 | * output data rate to the functional clock or 200 MP/s, whichever is lower, | ||
498 | * seems to get rid of SBL overflows. | ||
499 | * | ||
500 | * The maximum data rate at the output of the horizontal resizer can thus be | ||
501 | * computed with | ||
502 | * | ||
503 | * max intermediate rate <= L3 clock * input height / output height | ||
504 | * max intermediate rate <= L3 clock / 2 | ||
505 | * | ||
506 | * The maximum data rate at the resizer input is then | ||
507 | * | ||
508 | * max input rate <= max intermediate rate * input width / output width | ||
509 | * | ||
510 | * where the input width and height are the resizer input crop rectangle size. | ||
511 | * The TRM doesn't clearly explain if that's a maximum instant data rate or a | ||
512 | * maximum average data rate. | ||
513 | */ | ||
514 | void omap3isp_resizer_max_rate(struct isp_res_device *res, | ||
515 | unsigned int *max_rate) | ||
516 | { | ||
517 | struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); | ||
518 | const struct v4l2_mbus_framefmt *ofmt = &res->formats[RESZ_PAD_SOURCE]; | ||
519 | unsigned long limit = min(pipe->l3_ick, 200000000UL); | ||
520 | unsigned long clock; | ||
521 | |||
522 | clock = div_u64((u64)limit * res->crop.active.height, ofmt->height); | ||
523 | clock = min(clock, limit / 2); | ||
524 | *max_rate = div_u64((u64)clock * res->crop.active.width, ofmt->width); | ||
525 | } | ||
526 | |||
527 | /* | ||
528 | * When the resizer processes images from memory, the driver must slow down read | ||
529 | * requests on the input to at least comply with the internal data rate | ||
530 | * requirements. If the application real-time requirements can cope with slower | ||
531 | * processing, the resizer can be slowed down even more to put less pressure on | ||
532 | * the overall system. | ||
533 | * | ||
534 | * When the resizer processes images on the fly (either from the CCDC or the | ||
535 | * preview module), the same data rate requirements apply but they can't be | ||
536 | * enforced at the resizer level. The image input module (sensor, CCP2 or | ||
537 | * preview module) must not provide image data faster than the resizer can | ||
538 | * process. | ||
539 | * | ||
540 | * For live image pipelines, the data rate is set by the frame format, size and | ||
541 | * rate. The sensor output frame rate must not exceed the maximum resizer data | ||
542 | * rate. | ||
543 | * | ||
544 | * The resizer slows down read requests by inserting wait cycles in the SBL | ||
545 | * requests. The maximum number of 256-byte requests per second can be computed | ||
546 | * as (the data rate is multiplied by 2 to convert from pixels per second to | ||
547 | * bytes per second) | ||
548 | * | ||
549 | * request per second = data rate * 2 / 256 | ||
550 | * cycles per request = cycles per second / requests per second | ||
551 | * | ||
552 | * The number of cycles per second is controlled by the L3 clock, leading to | ||
553 | * | ||
554 | * cycles per request = L3 frequency / 2 * 256 / data rate | ||
555 | */ | ||
556 | static void resizer_adjust_bandwidth(struct isp_res_device *res) | ||
557 | { | ||
558 | struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); | ||
559 | struct isp_device *isp = to_isp_device(res); | ||
560 | unsigned long l3_ick = pipe->l3_ick; | ||
561 | struct v4l2_fract *timeperframe; | ||
562 | unsigned int cycles_per_frame; | ||
563 | unsigned int requests_per_frame; | ||
564 | unsigned int cycles_per_request; | ||
565 | unsigned int granularity; | ||
566 | unsigned int minimum; | ||
567 | unsigned int maximum; | ||
568 | unsigned int value; | ||
569 | |||
570 | if (res->input != RESIZER_INPUT_MEMORY) { | ||
571 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, | ||
572 | ISPSBL_SDR_REQ_RSZ_EXP_MASK); | ||
573 | return; | ||
574 | } | ||
575 | |||
576 | switch (isp->revision) { | ||
577 | case ISP_REVISION_1_0: | ||
578 | case ISP_REVISION_2_0: | ||
579 | default: | ||
580 | granularity = 1024; | ||
581 | break; | ||
582 | |||
583 | case ISP_REVISION_15_0: | ||
584 | granularity = 32; | ||
585 | break; | ||
586 | } | ||
587 | |||
588 | /* Compute the minimum number of cycles per request, based on the | ||
589 | * pipeline maximum data rate. This is an absolute lower bound if we | ||
590 | * don't want SBL overflows, so round the value up. | ||
591 | */ | ||
592 | cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1, | ||
593 | pipe->max_rate); | ||
594 | minimum = DIV_ROUND_UP(cycles_per_request, granularity); | ||
595 | |||
596 | /* Compute the maximum number of cycles per request, based on the | ||
597 | * requested frame rate. This is a soft upper bound to achieve a frame | ||
598 | * rate equal or higher than the requested value, so round the value | ||
599 | * down. | ||
600 | */ | ||
601 | timeperframe = &pipe->max_timeperframe; | ||
602 | |||
603 | requests_per_frame = DIV_ROUND_UP(res->crop.active.width * 2, 256) | ||
604 | * res->crop.active.height; | ||
605 | cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator, | ||
606 | timeperframe->denominator); | ||
607 | cycles_per_request = cycles_per_frame / requests_per_frame; | ||
608 | |||
609 | maximum = cycles_per_request / granularity; | ||
610 | |||
611 | value = max(minimum, maximum); | ||
612 | |||
613 | dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value); | ||
614 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, | ||
615 | ISPSBL_SDR_REQ_RSZ_EXP_MASK, | ||
616 | value << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT); | ||
617 | } | ||
618 | |||
619 | /* | ||
620 | * omap3isp_resizer_busy - Checks if ISP resizer is busy. | ||
621 | * | ||
622 | * Returns busy field from ISPRSZ_PCR register. | ||
623 | */ | ||
624 | int omap3isp_resizer_busy(struct isp_res_device *res) | ||
625 | { | ||
626 | struct isp_device *isp = to_isp_device(res); | ||
627 | |||
628 | return isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & | ||
629 | ISPRSZ_PCR_BUSY; | ||
630 | } | ||
631 | |||
632 | /* | ||
633 | * resizer_set_inaddr - Sets the memory address of the input frame. | ||
634 | * @addr: 32bit memory address aligned on 32byte boundary. | ||
635 | */ | ||
636 | static void resizer_set_inaddr(struct isp_res_device *res, u32 addr) | ||
637 | { | ||
638 | res->addr_base = addr; | ||
639 | |||
640 | /* This will handle crop settings in stream off state */ | ||
641 | if (res->crop_offset) | ||
642 | addr += res->crop_offset & ~0x1f; | ||
643 | |||
644 | __resizer_set_inaddr(res, addr); | ||
645 | } | ||
646 | |||
647 | /* | ||
648 | * Configures the memory address to which the output frame is written. | ||
649 | * @addr: 32bit memory address aligned on 32byte boundary. | ||
650 | * Note: For SBL efficiency reasons the address should be on a 256-byte | ||
651 | * boundary. | ||
652 | */ | ||
653 | static void resizer_set_outaddr(struct isp_res_device *res, u32 addr) | ||
654 | { | ||
655 | struct isp_device *isp = to_isp_device(res); | ||
656 | |||
657 | /* | ||
658 | * Set output address. This needs to be in its own function | ||
659 | * because it changes often. | ||
660 | */ | ||
661 | isp_reg_writel(isp, addr << ISPRSZ_SDR_OUTADD_ADDR_SHIFT, | ||
662 | OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD); | ||
663 | } | ||
664 | |||
665 | /* | ||
666 | * resizer_print_status - Prints the values of the resizer module registers. | ||
667 | */ | ||
668 | #define RSZ_PRINT_REGISTER(isp, name)\ | ||
669 | dev_dbg(isp->dev, "###RSZ " #name "=0x%08x\n", \ | ||
670 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_##name)) | ||
671 | |||
672 | static void resizer_print_status(struct isp_res_device *res) | ||
673 | { | ||
674 | struct isp_device *isp = to_isp_device(res); | ||
675 | |||
676 | dev_dbg(isp->dev, "-------------Resizer Register dump----------\n"); | ||
677 | |||
678 | RSZ_PRINT_REGISTER(isp, PCR); | ||
679 | RSZ_PRINT_REGISTER(isp, CNT); | ||
680 | RSZ_PRINT_REGISTER(isp, OUT_SIZE); | ||
681 | RSZ_PRINT_REGISTER(isp, IN_START); | ||
682 | RSZ_PRINT_REGISTER(isp, IN_SIZE); | ||
683 | RSZ_PRINT_REGISTER(isp, SDR_INADD); | ||
684 | RSZ_PRINT_REGISTER(isp, SDR_INOFF); | ||
685 | RSZ_PRINT_REGISTER(isp, SDR_OUTADD); | ||
686 | RSZ_PRINT_REGISTER(isp, SDR_OUTOFF); | ||
687 | RSZ_PRINT_REGISTER(isp, YENH); | ||
688 | |||
689 | dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
690 | } | ||
691 | |||
692 | /* | ||
693 | * resizer_calc_ratios - Helper function for calculate resizer ratios | ||
694 | * @res: pointer to resizer private data structure | ||
695 | * @input: input frame size | ||
696 | * @output: output frame size | ||
697 | * @ratio : return calculated ratios | ||
698 | * return none | ||
699 | * | ||
700 | * The resizer uses a polyphase sample rate converter. The upsampling filter | ||
701 | * has a fixed number of phases that depend on the resizing ratio. As the ratio | ||
702 | * computation depends on the number of phases, we need to compute a first | ||
703 | * approximation and then refine it. | ||
704 | * | ||
705 | * The input/output/ratio relationship is given by the OMAP34xx TRM: | ||
706 | * | ||
707 | * - 8-phase, 4-tap mode (RSZ = 64 ~ 512) | ||
708 | * iw = (32 * sph + (ow - 1) * hrsz + 16) >> 8 + 7 | ||
709 | * ih = (32 * spv + (oh - 1) * vrsz + 16) >> 8 + 4 | ||
710 | * - 4-phase, 7-tap mode (RSZ = 513 ~ 1024) | ||
711 | * iw = (64 * sph + (ow - 1) * hrsz + 32) >> 8 + 7 | ||
712 | * ih = (64 * spv + (oh - 1) * vrsz + 32) >> 8 + 7 | ||
713 | * | ||
714 | * iw and ih are the input width and height after cropping. Those equations need | ||
715 | * to be satisfied exactly for the resizer to work correctly. | ||
716 | * | ||
717 | * The equations can't be easily reverted, as the >> 8 operation is not linear. | ||
718 | * In addition, not all input sizes can be achieved for a given output size. To | ||
719 | * get the highest input size lower than or equal to the requested input size, | ||
720 | * we need to compute the highest resizing ratio that satisfies the following | ||
721 | * inequality (taking the 4-tap mode width equation as an example) | ||
722 | * | ||
723 | * iw >= (32 * sph + (ow - 1) * hrsz + 16) >> 8 - 7 | ||
724 | * | ||
725 | * (where iw is the requested input width) which can be rewritten as | ||
726 | * | ||
727 | * iw - 7 >= (32 * sph + (ow - 1) * hrsz + 16) >> 8 | ||
728 | * (iw - 7) << 8 >= 32 * sph + (ow - 1) * hrsz + 16 - b | ||
729 | * ((iw - 7) << 8) + b >= 32 * sph + (ow - 1) * hrsz + 16 | ||
730 | * | ||
731 | * where b is the value of the 8 least significant bits of the right hand side | ||
732 | * expression of the last inequality. The highest resizing ratio value will be | ||
733 | * achieved when b is equal to its maximum value of 255. That resizing ratio | ||
734 | * value will still satisfy the original inequality, as b will disappear when | ||
735 | * the expression will be shifted right by 8. | ||
736 | * | ||
737 | * The reverted the equations thus become | ||
738 | * | ||
739 | * - 8-phase, 4-tap mode | ||
740 | * hrsz = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / (ow - 1) | ||
741 | * vrsz = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / (oh - 1) | ||
742 | * - 4-phase, 7-tap mode | ||
743 | * hrsz = ((iw - 7) * 256 + 255 - 32 - 64 * sph) / (ow - 1) | ||
744 | * vrsz = ((ih - 7) * 256 + 255 - 32 - 64 * spv) / (oh - 1) | ||
745 | * | ||
746 | * The ratios are integer values, and are rounded down to ensure that the | ||
747 | * cropped input size is not bigger than the uncropped input size. | ||
748 | * | ||
749 | * As the number of phases/taps, used to select the correct equations to compute | ||
750 | * the ratio, depends on the ratio, we start with the 4-tap mode equations to | ||
751 | * compute an approximation of the ratio, and switch to the 7-tap mode equations | ||
752 | * if the approximation is higher than the ratio threshold. | ||
753 | * | ||
754 | * As the 7-tap mode equations will return a ratio smaller than or equal to the | ||
755 | * 4-tap mode equations, the resulting ratio could become lower than or equal to | ||
756 | * the ratio threshold. This 'equations loop' isn't an issue as long as the | ||
757 | * correct equations are used to compute the final input size. Starting with the | ||
758 | * 4-tap mode equations ensure that, in case of values resulting in a 'ratio | ||
759 | * loop', the smallest of the ratio values will be used, never exceeding the | ||
760 | * requested input size. | ||
761 | * | ||
762 | * We first clamp the output size according to the hardware capabilitie to avoid | ||
763 | * auto-cropping the input more than required to satisfy the TRM equations. The | ||
764 | * minimum output size is achieved with a scaling factor of 1024. It is thus | ||
765 | * computed using the 7-tap equations. | ||
766 | * | ||
767 | * min ow = ((iw - 7) * 256 - 32 - 64 * sph) / 1024 + 1 | ||
768 | * min oh = ((ih - 7) * 256 - 32 - 64 * spv) / 1024 + 1 | ||
769 | * | ||
770 | * Similarly, the maximum output size is achieved with a scaling factor of 64 | ||
771 | * and computed using the 4-tap equations. | ||
772 | * | ||
773 | * max ow = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / 64 + 1 | ||
774 | * max oh = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1 | ||
775 | * | ||
776 | * The additional +255 term compensates for the round down operation performed | ||
777 | * by the TRM equations when shifting the value right by 8 bits. | ||
778 | * | ||
779 | * We then compute and clamp the ratios (x1/4 ~ x4). Clamping the output size to | ||
780 | * the maximum value guarantees that the ratio value will never be smaller than | ||
781 | * the minimum, but it could still slightly exceed the maximum. Clamping the | ||
782 | * ratio will thus result in a resizing factor slightly larger than the | ||
783 | * requested value. | ||
784 | * | ||
785 | * To accommodate that, and make sure the TRM equations are satisfied exactly, we | ||
786 | * compute the input crop rectangle as the last step. | ||
787 | * | ||
788 | * As if the situation wasn't complex enough, the maximum output width depends | ||
789 | * on the vertical resizing ratio. Fortunately, the output height doesn't | ||
790 | * depend on the horizontal resizing ratio. We can then start by computing the | ||
791 | * output height and the vertical ratio, and then move to computing the output | ||
792 | * width and the horizontal ratio. | ||
793 | */ | ||
794 | static void resizer_calc_ratios(struct isp_res_device *res, | ||
795 | struct v4l2_rect *input, | ||
796 | struct v4l2_mbus_framefmt *output, | ||
797 | struct resizer_ratio *ratio) | ||
798 | { | ||
799 | struct isp_device *isp = to_isp_device(res); | ||
800 | const unsigned int spv = DEFAULT_PHASE; | ||
801 | const unsigned int sph = DEFAULT_PHASE; | ||
802 | unsigned int upscaled_width; | ||
803 | unsigned int upscaled_height; | ||
804 | unsigned int min_width; | ||
805 | unsigned int min_height; | ||
806 | unsigned int max_width; | ||
807 | unsigned int max_height; | ||
808 | unsigned int width_alignment; | ||
809 | unsigned int width; | ||
810 | unsigned int height; | ||
811 | |||
812 | /* | ||
813 | * Clamp the output height based on the hardware capabilities and | ||
814 | * compute the vertical resizing ratio. | ||
815 | */ | ||
816 | min_height = ((input->height - 7) * 256 - 32 - 64 * spv) / 1024 + 1; | ||
817 | min_height = max_t(unsigned int, min_height, MIN_OUT_HEIGHT); | ||
818 | max_height = ((input->height - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1; | ||
819 | max_height = min_t(unsigned int, max_height, MAX_OUT_HEIGHT); | ||
820 | output->height = clamp(output->height, min_height, max_height); | ||
821 | |||
822 | ratio->vert = ((input->height - 4) * 256 + 255 - 16 - 32 * spv) | ||
823 | / (output->height - 1); | ||
824 | if (ratio->vert > MID_RESIZE_VALUE) | ||
825 | ratio->vert = ((input->height - 7) * 256 + 255 - 32 - 64 * spv) | ||
826 | / (output->height - 1); | ||
827 | ratio->vert = clamp_t(unsigned int, ratio->vert, | ||
828 | MIN_RESIZE_VALUE, MAX_RESIZE_VALUE); | ||
829 | |||
830 | if (ratio->vert <= MID_RESIZE_VALUE) { | ||
831 | upscaled_height = (output->height - 1) * ratio->vert | ||
832 | + 32 * spv + 16; | ||
833 | height = (upscaled_height >> 8) + 4; | ||
834 | } else { | ||
835 | upscaled_height = (output->height - 1) * ratio->vert | ||
836 | + 64 * spv + 32; | ||
837 | height = (upscaled_height >> 8) + 7; | ||
838 | } | ||
839 | |||
840 | /* | ||
841 | * Compute the minimum and maximum output widths based on the hardware | ||
842 | * capabilities. The maximum depends on the vertical resizing ratio. | ||
843 | */ | ||
844 | min_width = ((input->width - 7) * 256 - 32 - 64 * sph) / 1024 + 1; | ||
845 | min_width = max_t(unsigned int, min_width, MIN_OUT_WIDTH); | ||
846 | |||
847 | if (ratio->vert <= MID_RESIZE_VALUE) { | ||
848 | switch (isp->revision) { | ||
849 | case ISP_REVISION_1_0: | ||
850 | max_width = MAX_4TAP_OUT_WIDTH_ES1; | ||
851 | break; | ||
852 | |||
853 | case ISP_REVISION_2_0: | ||
854 | default: | ||
855 | max_width = MAX_4TAP_OUT_WIDTH_ES2; | ||
856 | break; | ||
857 | |||
858 | case ISP_REVISION_15_0: | ||
859 | max_width = MAX_4TAP_OUT_WIDTH_3630; | ||
860 | break; | ||
861 | } | ||
862 | } else { | ||
863 | switch (isp->revision) { | ||
864 | case ISP_REVISION_1_0: | ||
865 | max_width = MAX_7TAP_OUT_WIDTH_ES1; | ||
866 | break; | ||
867 | |||
868 | case ISP_REVISION_2_0: | ||
869 | default: | ||
870 | max_width = MAX_7TAP_OUT_WIDTH_ES2; | ||
871 | break; | ||
872 | |||
873 | case ISP_REVISION_15_0: | ||
874 | max_width = MAX_7TAP_OUT_WIDTH_3630; | ||
875 | break; | ||
876 | } | ||
877 | } | ||
878 | max_width = min(((input->width - 7) * 256 + 255 - 16 - 32 * sph) / 64 | ||
879 | + 1, max_width); | ||
880 | |||
881 | /* | ||
882 | * The output width must be even, and must be a multiple of 16 bytes | ||
883 | * when upscaling vertically. Clamp the output width to the valid range. | ||
884 | * Take the alignment into account (the maximum width in 7-tap mode on | ||
885 | * ES2 isn't a multiple of 8) and align the result up to make sure it | ||
886 | * won't be smaller than the minimum. | ||
887 | */ | ||
888 | width_alignment = ratio->vert < 256 ? 8 : 2; | ||
889 | output->width = clamp(output->width, min_width, | ||
890 | max_width & ~(width_alignment - 1)); | ||
891 | output->width = ALIGN(output->width, width_alignment); | ||
892 | |||
893 | ratio->horz = ((input->width - 7) * 256 + 255 - 16 - 32 * sph) | ||
894 | / (output->width - 1); | ||
895 | if (ratio->horz > MID_RESIZE_VALUE) | ||
896 | ratio->horz = ((input->width - 7) * 256 + 255 - 32 - 64 * sph) | ||
897 | / (output->width - 1); | ||
898 | ratio->horz = clamp_t(unsigned int, ratio->horz, | ||
899 | MIN_RESIZE_VALUE, MAX_RESIZE_VALUE); | ||
900 | |||
901 | if (ratio->horz <= MID_RESIZE_VALUE) { | ||
902 | upscaled_width = (output->width - 1) * ratio->horz | ||
903 | + 32 * sph + 16; | ||
904 | width = (upscaled_width >> 8) + 7; | ||
905 | } else { | ||
906 | upscaled_width = (output->width - 1) * ratio->horz | ||
907 | + 64 * sph + 32; | ||
908 | width = (upscaled_width >> 8) + 7; | ||
909 | } | ||
910 | |||
911 | /* Center the new crop rectangle. */ | ||
912 | input->left += (input->width - width) / 2; | ||
913 | input->top += (input->height - height) / 2; | ||
914 | input->width = width; | ||
915 | input->height = height; | ||
916 | } | ||
917 | |||
918 | /* | ||
919 | * resizer_set_crop_params - Setup hardware with cropping parameters | ||
920 | * @res : resizer private structure | ||
921 | * @crop_rect : current crop rectangle | ||
922 | * @ratio : resizer ratios | ||
923 | * return none | ||
924 | */ | ||
925 | static void resizer_set_crop_params(struct isp_res_device *res, | ||
926 | const struct v4l2_mbus_framefmt *input, | ||
927 | const struct v4l2_mbus_framefmt *output) | ||
928 | { | ||
929 | resizer_set_ratio(res, &res->ratio); | ||
930 | |||
931 | /* Set chrominance horizontal algorithm */ | ||
932 | if (res->ratio.horz >= RESIZE_DIVISOR) | ||
933 | resizer_set_bilinear(res, RSZ_THE_SAME); | ||
934 | else | ||
935 | resizer_set_bilinear(res, RSZ_BILINEAR); | ||
936 | |||
937 | resizer_adjust_bandwidth(res); | ||
938 | |||
939 | if (res->input == RESIZER_INPUT_MEMORY) { | ||
940 | /* Calculate additional offset for crop */ | ||
941 | res->crop_offset = (res->crop.active.top * input->width + | ||
942 | res->crop.active.left) * 2; | ||
943 | /* | ||
944 | * Write lowest 4 bits of horizontal pixel offset (in pixels), | ||
945 | * vertical start must be 0. | ||
946 | */ | ||
947 | resizer_set_start(res, (res->crop_offset / 2) & 0xf, 0); | ||
948 | |||
949 | /* | ||
950 | * Set start (read) address for cropping, in bytes. | ||
951 | * Lowest 5 bits must be zero. | ||
952 | */ | ||
953 | __resizer_set_inaddr(res, | ||
954 | res->addr_base + (res->crop_offset & ~0x1f)); | ||
955 | } else { | ||
956 | /* | ||
957 | * Set vertical start line and horizontal starting pixel. | ||
958 | * If the input is from CCDC/PREV, horizontal start field is | ||
959 | * in bytes (twice number of pixels). | ||
960 | */ | ||
961 | resizer_set_start(res, res->crop.active.left * 2, | ||
962 | res->crop.active.top); | ||
963 | /* Input address and offset must be 0 for preview/ccdc input */ | ||
964 | __resizer_set_inaddr(res, 0); | ||
965 | resizer_set_input_offset(res, 0); | ||
966 | } | ||
967 | |||
968 | /* Set the input size */ | ||
969 | resizer_set_input_size(res, res->crop.active.width, | ||
970 | res->crop.active.height); | ||
971 | } | ||
972 | |||
973 | static void resizer_configure(struct isp_res_device *res) | ||
974 | { | ||
975 | struct v4l2_mbus_framefmt *informat, *outformat; | ||
976 | struct resizer_luma_yenh luma = {0, 0, 0, 0}; | ||
977 | |||
978 | resizer_set_source(res, res->input); | ||
979 | |||
980 | informat = &res->formats[RESZ_PAD_SINK]; | ||
981 | outformat = &res->formats[RESZ_PAD_SOURCE]; | ||
982 | |||
983 | /* RESZ_PAD_SINK */ | ||
984 | if (res->input == RESIZER_INPUT_VP) | ||
985 | resizer_set_input_offset(res, 0); | ||
986 | else | ||
987 | resizer_set_input_offset(res, ALIGN(informat->width, 0x10) * 2); | ||
988 | |||
989 | /* YUV422 interleaved, default phase, no luma enhancement */ | ||
990 | resizer_set_intype(res, RSZ_YUV422); | ||
991 | resizer_set_ycpos(res, informat->code); | ||
992 | resizer_set_phase(res, DEFAULT_PHASE, DEFAULT_PHASE); | ||
993 | resizer_set_luma(res, &luma); | ||
994 | |||
995 | /* RESZ_PAD_SOURCE */ | ||
996 | resizer_set_output_offset(res, ALIGN(outformat->width * 2, 32)); | ||
997 | resizer_set_output_size(res, outformat->width, outformat->height); | ||
998 | |||
999 | resizer_set_crop_params(res, informat, outformat); | ||
1000 | } | ||
1001 | |||
1002 | /* ----------------------------------------------------------------------------- | ||
1003 | * Interrupt handling | ||
1004 | */ | ||
1005 | |||
1006 | static void resizer_enable_oneshot(struct isp_res_device *res) | ||
1007 | { | ||
1008 | struct isp_device *isp = to_isp_device(res); | ||
1009 | |||
1010 | isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR, | ||
1011 | ISPRSZ_PCR_ENABLE | ISPRSZ_PCR_ONESHOT); | ||
1012 | } | ||
1013 | |||
1014 | void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res) | ||
1015 | { | ||
1016 | /* | ||
1017 | * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun | ||
1018 | * condition, the module was paused and now we have a buffer queued | ||
1019 | * on the output again. Restart the pipeline if running in continuous | ||
1020 | * mode. | ||
1021 | */ | ||
1022 | if (res->state == ISP_PIPELINE_STREAM_CONTINUOUS && | ||
1023 | res->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { | ||
1024 | resizer_enable_oneshot(res); | ||
1025 | isp_video_dmaqueue_flags_clr(&res->video_out); | ||
1026 | } | ||
1027 | } | ||
1028 | |||
1029 | static void resizer_isr_buffer(struct isp_res_device *res) | ||
1030 | { | ||
1031 | struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); | ||
1032 | struct isp_buffer *buffer; | ||
1033 | int restart = 0; | ||
1034 | |||
1035 | if (res->state == ISP_PIPELINE_STREAM_STOPPED) | ||
1036 | return; | ||
1037 | |||
1038 | /* Complete the output buffer and, if reading from memory, the input | ||
1039 | * buffer. | ||
1040 | */ | ||
1041 | buffer = omap3isp_video_buffer_next(&res->video_out, res->error); | ||
1042 | if (buffer != NULL) { | ||
1043 | resizer_set_outaddr(res, buffer->isp_addr); | ||
1044 | restart = 1; | ||
1045 | } | ||
1046 | |||
1047 | pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; | ||
1048 | |||
1049 | if (res->input == RESIZER_INPUT_MEMORY) { | ||
1050 | buffer = omap3isp_video_buffer_next(&res->video_in, 0); | ||
1051 | if (buffer != NULL) | ||
1052 | resizer_set_inaddr(res, buffer->isp_addr); | ||
1053 | pipe->state |= ISP_PIPELINE_IDLE_INPUT; | ||
1054 | } | ||
1055 | |||
1056 | if (res->state == ISP_PIPELINE_STREAM_SINGLESHOT) { | ||
1057 | if (isp_pipeline_ready(pipe)) | ||
1058 | omap3isp_pipeline_set_stream(pipe, | ||
1059 | ISP_PIPELINE_STREAM_SINGLESHOT); | ||
1060 | } else { | ||
1061 | /* If an underrun occurs, the video queue operation handler will | ||
1062 | * restart the resizer. Otherwise restart it immediately. | ||
1063 | */ | ||
1064 | if (restart) | ||
1065 | resizer_enable_oneshot(res); | ||
1066 | } | ||
1067 | |||
1068 | res->error = 0; | ||
1069 | } | ||
1070 | |||
1071 | /* | ||
1072 | * omap3isp_resizer_isr - ISP resizer interrupt handler | ||
1073 | * | ||
1074 | * Manage the resizer video buffers and configure shadowed and busy-locked | ||
1075 | * registers. | ||
1076 | */ | ||
1077 | void omap3isp_resizer_isr(struct isp_res_device *res) | ||
1078 | { | ||
1079 | struct v4l2_mbus_framefmt *informat, *outformat; | ||
1080 | |||
1081 | if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping)) | ||
1082 | return; | ||
1083 | |||
1084 | if (res->applycrop) { | ||
1085 | outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE, | ||
1086 | V4L2_SUBDEV_FORMAT_ACTIVE); | ||
1087 | informat = __resizer_get_format(res, NULL, RESZ_PAD_SINK, | ||
1088 | V4L2_SUBDEV_FORMAT_ACTIVE); | ||
1089 | resizer_set_crop_params(res, informat, outformat); | ||
1090 | res->applycrop = 0; | ||
1091 | } | ||
1092 | |||
1093 | resizer_isr_buffer(res); | ||
1094 | } | ||
1095 | |||
1096 | /* ----------------------------------------------------------------------------- | ||
1097 | * ISP video operations | ||
1098 | */ | ||
1099 | |||
1100 | static int resizer_video_queue(struct isp_video *video, | ||
1101 | struct isp_buffer *buffer) | ||
1102 | { | ||
1103 | struct isp_res_device *res = &video->isp->isp_res; | ||
1104 | |||
1105 | if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
1106 | resizer_set_inaddr(res, buffer->isp_addr); | ||
1107 | |||
1108 | /* | ||
1109 | * We now have a buffer queued on the output. Despite what the | ||
1110 | * TRM says, the resizer can't be restarted immediately. | ||
1111 | * Enabling it in one shot mode in the middle of a frame (or at | ||
1112 | * least asynchronously to the frame) results in the output | ||
1113 | * being shifted randomly left/right and up/down, as if the | ||
1114 | * hardware didn't synchronize itself to the beginning of the | ||
1115 | * frame correctly. | ||
1116 | * | ||
1117 | * Restart the resizer on the next sync interrupt if running in | ||
1118 | * continuous mode or when starting the stream. | ||
1119 | */ | ||
1120 | if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1121 | resizer_set_outaddr(res, buffer->isp_addr); | ||
1122 | |||
1123 | return 0; | ||
1124 | } | ||
1125 | |||
1126 | static const struct isp_video_operations resizer_video_ops = { | ||
1127 | .queue = resizer_video_queue, | ||
1128 | }; | ||
1129 | |||
1130 | /* ----------------------------------------------------------------------------- | ||
1131 | * V4L2 subdev operations | ||
1132 | */ | ||
1133 | |||
1134 | /* | ||
1135 | * resizer_set_stream - Enable/Disable streaming on resizer subdev | ||
1136 | * @sd: ISP resizer V4L2 subdev | ||
1137 | * @enable: 1 == Enable, 0 == Disable | ||
1138 | * | ||
1139 | * The resizer hardware can't be enabled without a memory buffer to write to. | ||
1140 | * As the s_stream operation is called in response to a STREAMON call without | ||
1141 | * any buffer queued yet, just update the state field and return immediately. | ||
1142 | * The resizer will be enabled in resizer_video_queue(). | ||
1143 | */ | ||
1144 | static int resizer_set_stream(struct v4l2_subdev *sd, int enable) | ||
1145 | { | ||
1146 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1147 | struct isp_video *video_out = &res->video_out; | ||
1148 | struct isp_device *isp = to_isp_device(res); | ||
1149 | struct device *dev = to_device(res); | ||
1150 | |||
1151 | if (res->state == ISP_PIPELINE_STREAM_STOPPED) { | ||
1152 | if (enable == ISP_PIPELINE_STREAM_STOPPED) | ||
1153 | return 0; | ||
1154 | |||
1155 | omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_RESIZER); | ||
1156 | resizer_configure(res); | ||
1157 | res->error = 0; | ||
1158 | resizer_print_status(res); | ||
1159 | } | ||
1160 | |||
1161 | switch (enable) { | ||
1162 | case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
1163 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); | ||
1164 | if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { | ||
1165 | resizer_enable_oneshot(res); | ||
1166 | isp_video_dmaqueue_flags_clr(video_out); | ||
1167 | } | ||
1168 | break; | ||
1169 | |||
1170 | case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
1171 | if (res->input == RESIZER_INPUT_MEMORY) | ||
1172 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_READ); | ||
1173 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); | ||
1174 | |||
1175 | resizer_enable_oneshot(res); | ||
1176 | break; | ||
1177 | |||
1178 | case ISP_PIPELINE_STREAM_STOPPED: | ||
1179 | if (omap3isp_module_sync_idle(&sd->entity, &res->wait, | ||
1180 | &res->stopping)) | ||
1181 | dev_dbg(dev, "%s: module stop timeout.\n", sd->name); | ||
1182 | omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_RESIZER_READ | | ||
1183 | OMAP3_ISP_SBL_RESIZER_WRITE); | ||
1184 | omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_RESIZER); | ||
1185 | isp_video_dmaqueue_flags_clr(video_out); | ||
1186 | break; | ||
1187 | } | ||
1188 | |||
1189 | res->state = enable; | ||
1190 | return 0; | ||
1191 | } | ||
1192 | |||
1193 | /* | ||
1194 | * resizer_g_crop - handle get crop subdev operation | ||
1195 | * @sd : pointer to v4l2 subdev structure | ||
1196 | * @pad : subdev pad | ||
1197 | * @crop : pointer to crop structure | ||
1198 | * @which : active or try format | ||
1199 | * return zero | ||
1200 | */ | ||
1201 | static int resizer_g_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1202 | struct v4l2_subdev_crop *crop) | ||
1203 | { | ||
1204 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1205 | struct v4l2_mbus_framefmt *format; | ||
1206 | struct resizer_ratio ratio; | ||
1207 | |||
1208 | /* Only sink pad has crop capability */ | ||
1209 | if (crop->pad != RESZ_PAD_SINK) | ||
1210 | return -EINVAL; | ||
1211 | |||
1212 | format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, crop->which); | ||
1213 | crop->rect = *__resizer_get_crop(res, fh, crop->which); | ||
1214 | resizer_calc_ratios(res, &crop->rect, format, &ratio); | ||
1215 | |||
1216 | return 0; | ||
1217 | } | ||
1218 | |||
1219 | /* | ||
1220 | * resizer_try_crop - mangles crop parameters. | ||
1221 | */ | ||
1222 | static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, | ||
1223 | const struct v4l2_mbus_framefmt *source, | ||
1224 | struct v4l2_rect *crop) | ||
1225 | { | ||
1226 | const unsigned int spv = DEFAULT_PHASE; | ||
1227 | const unsigned int sph = DEFAULT_PHASE; | ||
1228 | |||
1229 | /* Crop rectangle is constrained to the output size so that zoom ratio | ||
1230 | * cannot exceed +/-4.0. | ||
1231 | */ | ||
1232 | unsigned int min_width = | ||
1233 | ((32 * sph + (source->width - 1) * 64 + 16) >> 8) + 7; | ||
1234 | unsigned int min_height = | ||
1235 | ((32 * spv + (source->height - 1) * 64 + 16) >> 8) + 4; | ||
1236 | unsigned int max_width = | ||
1237 | ((64 * sph + (source->width - 1) * 1024 + 32) >> 8) + 7; | ||
1238 | unsigned int max_height = | ||
1239 | ((64 * spv + (source->height - 1) * 1024 + 32) >> 8) + 7; | ||
1240 | |||
1241 | crop->width = clamp_t(u32, crop->width, min_width, max_width); | ||
1242 | crop->height = clamp_t(u32, crop->height, min_height, max_height); | ||
1243 | |||
1244 | /* Crop can not go beyond of the input rectangle */ | ||
1245 | crop->left = clamp_t(u32, crop->left, 0, sink->width - MIN_IN_WIDTH); | ||
1246 | crop->width = clamp_t(u32, crop->width, MIN_IN_WIDTH, | ||
1247 | sink->width - crop->left); | ||
1248 | crop->top = clamp_t(u32, crop->top, 0, sink->height - MIN_IN_HEIGHT); | ||
1249 | crop->height = clamp_t(u32, crop->height, MIN_IN_HEIGHT, | ||
1250 | sink->height - crop->top); | ||
1251 | } | ||
1252 | |||
1253 | /* | ||
1254 | * resizer_s_crop - handle set crop subdev operation | ||
1255 | * @sd : pointer to v4l2 subdev structure | ||
1256 | * @pad : subdev pad | ||
1257 | * @crop : pointer to crop structure | ||
1258 | * @which : active or try format | ||
1259 | * return -EINVAL or zero when succeed | ||
1260 | */ | ||
1261 | static int resizer_s_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1262 | struct v4l2_subdev_crop *crop) | ||
1263 | { | ||
1264 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1265 | struct isp_device *isp = to_isp_device(res); | ||
1266 | struct v4l2_mbus_framefmt *format_sink, *format_source; | ||
1267 | struct resizer_ratio ratio; | ||
1268 | |||
1269 | /* Only sink pad has crop capability */ | ||
1270 | if (crop->pad != RESZ_PAD_SINK) | ||
1271 | return -EINVAL; | ||
1272 | |||
1273 | format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, | ||
1274 | crop->which); | ||
1275 | format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, | ||
1276 | crop->which); | ||
1277 | |||
1278 | dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__, | ||
1279 | crop->rect.left, crop->rect.top, crop->rect.width, | ||
1280 | crop->rect.height, crop->which); | ||
1281 | |||
1282 | dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__, | ||
1283 | format_sink->width, format_sink->height, | ||
1284 | format_source->width, format_source->height); | ||
1285 | |||
1286 | resizer_try_crop(format_sink, format_source, &crop->rect); | ||
1287 | *__resizer_get_crop(res, fh, crop->which) = crop->rect; | ||
1288 | resizer_calc_ratios(res, &crop->rect, format_source, &ratio); | ||
1289 | |||
1290 | if (crop->which == V4L2_SUBDEV_FORMAT_TRY) | ||
1291 | return 0; | ||
1292 | |||
1293 | res->ratio = ratio; | ||
1294 | res->crop.active = crop->rect; | ||
1295 | |||
1296 | /* | ||
1297 | * s_crop can be called while streaming is on. In this case | ||
1298 | * the crop values will be set in the next IRQ. | ||
1299 | */ | ||
1300 | if (res->state != ISP_PIPELINE_STREAM_STOPPED) | ||
1301 | res->applycrop = 1; | ||
1302 | |||
1303 | return 0; | ||
1304 | } | ||
1305 | |||
1306 | /* resizer pixel formats */ | ||
1307 | static const unsigned int resizer_formats[] = { | ||
1308 | V4L2_MBUS_FMT_UYVY8_1X16, | ||
1309 | V4L2_MBUS_FMT_YUYV8_1X16, | ||
1310 | }; | ||
1311 | |||
1312 | static unsigned int resizer_max_in_width(struct isp_res_device *res) | ||
1313 | { | ||
1314 | struct isp_device *isp = to_isp_device(res); | ||
1315 | |||
1316 | if (res->input == RESIZER_INPUT_MEMORY) { | ||
1317 | return MAX_IN_WIDTH_MEMORY_MODE; | ||
1318 | } else { | ||
1319 | if (isp->revision == ISP_REVISION_1_0) | ||
1320 | return MAX_IN_WIDTH_ONTHEFLY_MODE_ES1; | ||
1321 | else | ||
1322 | return MAX_IN_WIDTH_ONTHEFLY_MODE_ES2; | ||
1323 | } | ||
1324 | } | ||
1325 | |||
1326 | /* | ||
1327 | * resizer_try_format - Handle try format by pad subdev method | ||
1328 | * @res : ISP resizer device | ||
1329 | * @fh : V4L2 subdev file handle | ||
1330 | * @pad : pad num | ||
1331 | * @fmt : pointer to v4l2 format structure | ||
1332 | * @which : wanted subdev format | ||
1333 | */ | ||
1334 | static void resizer_try_format(struct isp_res_device *res, | ||
1335 | struct v4l2_subdev_fh *fh, unsigned int pad, | ||
1336 | struct v4l2_mbus_framefmt *fmt, | ||
1337 | enum v4l2_subdev_format_whence which) | ||
1338 | { | ||
1339 | struct v4l2_mbus_framefmt *format; | ||
1340 | struct resizer_ratio ratio; | ||
1341 | struct v4l2_rect crop; | ||
1342 | |||
1343 | switch (pad) { | ||
1344 | case RESZ_PAD_SINK: | ||
1345 | if (fmt->code != V4L2_MBUS_FMT_YUYV8_1X16 && | ||
1346 | fmt->code != V4L2_MBUS_FMT_UYVY8_1X16) | ||
1347 | fmt->code = V4L2_MBUS_FMT_YUYV8_1X16; | ||
1348 | |||
1349 | fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH, | ||
1350 | resizer_max_in_width(res)); | ||
1351 | fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT, | ||
1352 | MAX_IN_HEIGHT); | ||
1353 | break; | ||
1354 | |||
1355 | case RESZ_PAD_SOURCE: | ||
1356 | format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which); | ||
1357 | fmt->code = format->code; | ||
1358 | |||
1359 | crop = *__resizer_get_crop(res, fh, which); | ||
1360 | resizer_calc_ratios(res, &crop, fmt, &ratio); | ||
1361 | break; | ||
1362 | } | ||
1363 | |||
1364 | fmt->colorspace = V4L2_COLORSPACE_JPEG; | ||
1365 | fmt->field = V4L2_FIELD_NONE; | ||
1366 | } | ||
1367 | |||
1368 | /* | ||
1369 | * resizer_enum_mbus_code - Handle pixel format enumeration | ||
1370 | * @sd : pointer to v4l2 subdev structure | ||
1371 | * @fh : V4L2 subdev file handle | ||
1372 | * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
1373 | * return -EINVAL or zero on success | ||
1374 | */ | ||
1375 | static int resizer_enum_mbus_code(struct v4l2_subdev *sd, | ||
1376 | struct v4l2_subdev_fh *fh, | ||
1377 | struct v4l2_subdev_mbus_code_enum *code) | ||
1378 | { | ||
1379 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1380 | struct v4l2_mbus_framefmt *format; | ||
1381 | |||
1382 | if (code->pad == RESZ_PAD_SINK) { | ||
1383 | if (code->index >= ARRAY_SIZE(resizer_formats)) | ||
1384 | return -EINVAL; | ||
1385 | |||
1386 | code->code = resizer_formats[code->index]; | ||
1387 | } else { | ||
1388 | if (code->index != 0) | ||
1389 | return -EINVAL; | ||
1390 | |||
1391 | format = __resizer_get_format(res, fh, RESZ_PAD_SINK, | ||
1392 | V4L2_SUBDEV_FORMAT_TRY); | ||
1393 | code->code = format->code; | ||
1394 | } | ||
1395 | |||
1396 | return 0; | ||
1397 | } | ||
1398 | |||
1399 | static int resizer_enum_frame_size(struct v4l2_subdev *sd, | ||
1400 | struct v4l2_subdev_fh *fh, | ||
1401 | struct v4l2_subdev_frame_size_enum *fse) | ||
1402 | { | ||
1403 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1404 | struct v4l2_mbus_framefmt format; | ||
1405 | |||
1406 | if (fse->index != 0) | ||
1407 | return -EINVAL; | ||
1408 | |||
1409 | format.code = fse->code; | ||
1410 | format.width = 1; | ||
1411 | format.height = 1; | ||
1412 | resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
1413 | fse->min_width = format.width; | ||
1414 | fse->min_height = format.height; | ||
1415 | |||
1416 | if (format.code != fse->code) | ||
1417 | return -EINVAL; | ||
1418 | |||
1419 | format.code = fse->code; | ||
1420 | format.width = -1; | ||
1421 | format.height = -1; | ||
1422 | resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
1423 | fse->max_width = format.width; | ||
1424 | fse->max_height = format.height; | ||
1425 | |||
1426 | return 0; | ||
1427 | } | ||
1428 | |||
1429 | /* | ||
1430 | * resizer_get_format - Handle get format by pads subdev method | ||
1431 | * @sd : pointer to v4l2 subdev structure | ||
1432 | * @fh : V4L2 subdev file handle | ||
1433 | * @fmt : pointer to v4l2 subdev format structure | ||
1434 | * return -EINVAL or zero on success | ||
1435 | */ | ||
1436 | static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1437 | struct v4l2_subdev_format *fmt) | ||
1438 | { | ||
1439 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1440 | struct v4l2_mbus_framefmt *format; | ||
1441 | |||
1442 | format = __resizer_get_format(res, fh, fmt->pad, fmt->which); | ||
1443 | if (format == NULL) | ||
1444 | return -EINVAL; | ||
1445 | |||
1446 | fmt->format = *format; | ||
1447 | return 0; | ||
1448 | } | ||
1449 | |||
1450 | /* | ||
1451 | * resizer_set_format - Handle set format by pads subdev method | ||
1452 | * @sd : pointer to v4l2 subdev structure | ||
1453 | * @fh : V4L2 subdev file handle | ||
1454 | * @fmt : pointer to v4l2 subdev format structure | ||
1455 | * return -EINVAL or zero on success | ||
1456 | */ | ||
1457 | static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1458 | struct v4l2_subdev_format *fmt) | ||
1459 | { | ||
1460 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1461 | struct v4l2_mbus_framefmt *format; | ||
1462 | struct v4l2_rect *crop; | ||
1463 | |||
1464 | format = __resizer_get_format(res, fh, fmt->pad, fmt->which); | ||
1465 | if (format == NULL) | ||
1466 | return -EINVAL; | ||
1467 | |||
1468 | resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which); | ||
1469 | *format = fmt->format; | ||
1470 | |||
1471 | if (fmt->pad == RESZ_PAD_SINK) { | ||
1472 | /* reset crop rectangle */ | ||
1473 | crop = __resizer_get_crop(res, fh, fmt->which); | ||
1474 | crop->left = 0; | ||
1475 | crop->top = 0; | ||
1476 | crop->width = fmt->format.width; | ||
1477 | crop->height = fmt->format.height; | ||
1478 | |||
1479 | /* Propagate the format from sink to source */ | ||
1480 | format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, | ||
1481 | fmt->which); | ||
1482 | *format = fmt->format; | ||
1483 | resizer_try_format(res, fh, RESZ_PAD_SOURCE, format, | ||
1484 | fmt->which); | ||
1485 | } | ||
1486 | |||
1487 | if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { | ||
1488 | /* Compute and store the active crop rectangle and resizer | ||
1489 | * ratios. format already points to the source pad active | ||
1490 | * format. | ||
1491 | */ | ||
1492 | res->crop.active = res->crop.request; | ||
1493 | resizer_calc_ratios(res, &res->crop.active, format, | ||
1494 | &res->ratio); | ||
1495 | } | ||
1496 | |||
1497 | return 0; | ||
1498 | } | ||
1499 | |||
1500 | /* | ||
1501 | * resizer_init_formats - Initialize formats on all pads | ||
1502 | * @sd: ISP resizer V4L2 subdevice | ||
1503 | * @fh: V4L2 subdev file handle | ||
1504 | * | ||
1505 | * Initialize all pad formats with default values. If fh is not NULL, try | ||
1506 | * formats are initialized on the file handle. Otherwise active formats are | ||
1507 | * initialized on the device. | ||
1508 | */ | ||
1509 | static int resizer_init_formats(struct v4l2_subdev *sd, | ||
1510 | struct v4l2_subdev_fh *fh) | ||
1511 | { | ||
1512 | struct v4l2_subdev_format format; | ||
1513 | |||
1514 | memset(&format, 0, sizeof(format)); | ||
1515 | format.pad = RESZ_PAD_SINK; | ||
1516 | format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
1517 | format.format.code = V4L2_MBUS_FMT_YUYV8_1X16; | ||
1518 | format.format.width = 4096; | ||
1519 | format.format.height = 4096; | ||
1520 | resizer_set_format(sd, fh, &format); | ||
1521 | |||
1522 | return 0; | ||
1523 | } | ||
1524 | |||
1525 | /* subdev video operations */ | ||
1526 | static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { | ||
1527 | .s_stream = resizer_set_stream, | ||
1528 | }; | ||
1529 | |||
1530 | /* subdev pad operations */ | ||
1531 | static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { | ||
1532 | .enum_mbus_code = resizer_enum_mbus_code, | ||
1533 | .enum_frame_size = resizer_enum_frame_size, | ||
1534 | .get_fmt = resizer_get_format, | ||
1535 | .set_fmt = resizer_set_format, | ||
1536 | .get_crop = resizer_g_crop, | ||
1537 | .set_crop = resizer_s_crop, | ||
1538 | }; | ||
1539 | |||
1540 | /* subdev operations */ | ||
1541 | static const struct v4l2_subdev_ops resizer_v4l2_ops = { | ||
1542 | .video = &resizer_v4l2_video_ops, | ||
1543 | .pad = &resizer_v4l2_pad_ops, | ||
1544 | }; | ||
1545 | |||
1546 | /* subdev internal operations */ | ||
1547 | static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = { | ||
1548 | .open = resizer_init_formats, | ||
1549 | }; | ||
1550 | |||
1551 | /* ----------------------------------------------------------------------------- | ||
1552 | * Media entity operations | ||
1553 | */ | ||
1554 | |||
1555 | /* | ||
1556 | * resizer_link_setup - Setup resizer connections. | ||
1557 | * @entity : Pointer to media entity structure | ||
1558 | * @local : Pointer to local pad array | ||
1559 | * @remote : Pointer to remote pad array | ||
1560 | * @flags : Link flags | ||
1561 | * return -EINVAL or zero on success | ||
1562 | */ | ||
1563 | static int resizer_link_setup(struct media_entity *entity, | ||
1564 | const struct media_pad *local, | ||
1565 | const struct media_pad *remote, u32 flags) | ||
1566 | { | ||
1567 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
1568 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1569 | |||
1570 | switch (local->index | media_entity_type(remote->entity)) { | ||
1571 | case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE: | ||
1572 | /* read from memory */ | ||
1573 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1574 | if (res->input == RESIZER_INPUT_VP) | ||
1575 | return -EBUSY; | ||
1576 | res->input = RESIZER_INPUT_MEMORY; | ||
1577 | } else { | ||
1578 | if (res->input == RESIZER_INPUT_MEMORY) | ||
1579 | res->input = RESIZER_INPUT_NONE; | ||
1580 | } | ||
1581 | break; | ||
1582 | |||
1583 | case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1584 | /* read from ccdc or previewer */ | ||
1585 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1586 | if (res->input == RESIZER_INPUT_MEMORY) | ||
1587 | return -EBUSY; | ||
1588 | res->input = RESIZER_INPUT_VP; | ||
1589 | } else { | ||
1590 | if (res->input == RESIZER_INPUT_VP) | ||
1591 | res->input = RESIZER_INPUT_NONE; | ||
1592 | } | ||
1593 | break; | ||
1594 | |||
1595 | case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: | ||
1596 | /* resizer always write to memory */ | ||
1597 | break; | ||
1598 | |||
1599 | default: | ||
1600 | return -EINVAL; | ||
1601 | } | ||
1602 | |||
1603 | return 0; | ||
1604 | } | ||
1605 | |||
1606 | /* media operations */ | ||
1607 | static const struct media_entity_operations resizer_media_ops = { | ||
1608 | .link_setup = resizer_link_setup, | ||
1609 | }; | ||
1610 | |||
1611 | /* | ||
1612 | * resizer_init_entities - Initialize resizer subdev and media entity. | ||
1613 | * @res : Pointer to resizer device structure | ||
1614 | * return -ENOMEM or zero on success | ||
1615 | */ | ||
1616 | static int resizer_init_entities(struct isp_res_device *res) | ||
1617 | { | ||
1618 | struct v4l2_subdev *sd = &res->subdev; | ||
1619 | struct media_pad *pads = res->pads; | ||
1620 | struct media_entity *me = &sd->entity; | ||
1621 | int ret; | ||
1622 | |||
1623 | res->input = RESIZER_INPUT_NONE; | ||
1624 | |||
1625 | v4l2_subdev_init(sd, &resizer_v4l2_ops); | ||
1626 | sd->internal_ops = &resizer_v4l2_internal_ops; | ||
1627 | strlcpy(sd->name, "OMAP3 ISP resizer", sizeof(sd->name)); | ||
1628 | sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
1629 | v4l2_set_subdevdata(sd, res); | ||
1630 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1631 | |||
1632 | pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
1633 | pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | ||
1634 | |||
1635 | me->ops = &resizer_media_ops; | ||
1636 | ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0); | ||
1637 | if (ret < 0) | ||
1638 | return ret; | ||
1639 | |||
1640 | resizer_init_formats(sd, NULL); | ||
1641 | |||
1642 | res->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
1643 | res->video_in.ops = &resizer_video_ops; | ||
1644 | res->video_in.isp = to_isp_device(res); | ||
1645 | res->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; | ||
1646 | res->video_in.bpl_alignment = 32; | ||
1647 | res->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1648 | res->video_out.ops = &resizer_video_ops; | ||
1649 | res->video_out.isp = to_isp_device(res); | ||
1650 | res->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; | ||
1651 | res->video_out.bpl_alignment = 32; | ||
1652 | |||
1653 | ret = omap3isp_video_init(&res->video_in, "resizer"); | ||
1654 | if (ret < 0) | ||
1655 | return ret; | ||
1656 | |||
1657 | ret = omap3isp_video_init(&res->video_out, "resizer"); | ||
1658 | if (ret < 0) | ||
1659 | return ret; | ||
1660 | |||
1661 | /* Connect the video nodes to the resizer subdev. */ | ||
1662 | ret = media_entity_create_link(&res->video_in.video.entity, 0, | ||
1663 | &res->subdev.entity, RESZ_PAD_SINK, 0); | ||
1664 | if (ret < 0) | ||
1665 | return ret; | ||
1666 | |||
1667 | ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE, | ||
1668 | &res->video_out.video.entity, 0, 0); | ||
1669 | if (ret < 0) | ||
1670 | return ret; | ||
1671 | |||
1672 | return 0; | ||
1673 | } | ||
1674 | |||
1675 | void omap3isp_resizer_unregister_entities(struct isp_res_device *res) | ||
1676 | { | ||
1677 | media_entity_cleanup(&res->subdev.entity); | ||
1678 | |||
1679 | v4l2_device_unregister_subdev(&res->subdev); | ||
1680 | omap3isp_video_unregister(&res->video_in); | ||
1681 | omap3isp_video_unregister(&res->video_out); | ||
1682 | } | ||
1683 | |||
1684 | int omap3isp_resizer_register_entities(struct isp_res_device *res, | ||
1685 | struct v4l2_device *vdev) | ||
1686 | { | ||
1687 | int ret; | ||
1688 | |||
1689 | /* Register the subdev and video nodes. */ | ||
1690 | ret = v4l2_device_register_subdev(vdev, &res->subdev); | ||
1691 | if (ret < 0) | ||
1692 | goto error; | ||
1693 | |||
1694 | ret = omap3isp_video_register(&res->video_in, vdev); | ||
1695 | if (ret < 0) | ||
1696 | goto error; | ||
1697 | |||
1698 | ret = omap3isp_video_register(&res->video_out, vdev); | ||
1699 | if (ret < 0) | ||
1700 | goto error; | ||
1701 | |||
1702 | return 0; | ||
1703 | |||
1704 | error: | ||
1705 | omap3isp_resizer_unregister_entities(res); | ||
1706 | return ret; | ||
1707 | } | ||
1708 | |||
1709 | /* ----------------------------------------------------------------------------- | ||
1710 | * ISP resizer initialization and cleanup | ||
1711 | */ | ||
1712 | |||
1713 | void omap3isp_resizer_cleanup(struct isp_device *isp) | ||
1714 | { | ||
1715 | } | ||
1716 | |||
1717 | /* | ||
1718 | * isp_resizer_init - Resizer initialization. | ||
1719 | * @isp : Pointer to ISP device | ||
1720 | * return -ENOMEM or zero on success | ||
1721 | */ | ||
1722 | int omap3isp_resizer_init(struct isp_device *isp) | ||
1723 | { | ||
1724 | struct isp_res_device *res = &isp->isp_res; | ||
1725 | int ret; | ||
1726 | |||
1727 | init_waitqueue_head(&res->wait); | ||
1728 | atomic_set(&res->stopping, 0); | ||
1729 | ret = resizer_init_entities(res); | ||
1730 | if (ret < 0) | ||
1731 | goto out; | ||
1732 | |||
1733 | out: | ||
1734 | if (ret) | ||
1735 | omap3isp_resizer_cleanup(isp); | ||
1736 | |||
1737 | return ret; | ||
1738 | } | ||
diff --git a/drivers/media/video/omap3isp/ispresizer.h b/drivers/media/video/omap3isp/ispresizer.h new file mode 100644 index 00000000000..76abc2e4212 --- /dev/null +++ b/drivers/media/video/omap3isp/ispresizer.h | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * ispresizer.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - Resizer module | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #ifndef OMAP3_ISP_RESIZER_H | ||
28 | #define OMAP3_ISP_RESIZER_H | ||
29 | |||
30 | #include <linux/types.h> | ||
31 | |||
32 | /* | ||
33 | * Constants for filter coefficents count | ||
34 | */ | ||
35 | #define COEFF_CNT 32 | ||
36 | |||
37 | /* | ||
38 | * struct isprsz_coef - Structure for resizer filter coeffcients. | ||
39 | * @h_filter_coef_4tap: Horizontal filter coefficients for 8-phase/4-tap | ||
40 | * mode (.5x-4x) | ||
41 | * @v_filter_coef_4tap: Vertical filter coefficients for 8-phase/4-tap | ||
42 | * mode (.5x-4x) | ||
43 | * @h_filter_coef_7tap: Horizontal filter coefficients for 4-phase/7-tap | ||
44 | * mode (.25x-.5x) | ||
45 | * @v_filter_coef_7tap: Vertical filter coefficients for 4-phase/7-tap | ||
46 | * mode (.25x-.5x) | ||
47 | */ | ||
48 | struct isprsz_coef { | ||
49 | u16 h_filter_coef_4tap[32]; | ||
50 | u16 v_filter_coef_4tap[32]; | ||
51 | /* Every 8th value is a dummy value in the following arrays: */ | ||
52 | u16 h_filter_coef_7tap[32]; | ||
53 | u16 v_filter_coef_7tap[32]; | ||
54 | }; | ||
55 | |||
56 | /* Chrominance horizontal algorithm */ | ||
57 | enum resizer_chroma_algo { | ||
58 | RSZ_THE_SAME = 0, /* Chrominance the same as Luminance */ | ||
59 | RSZ_BILINEAR = 1, /* Chrominance uses bilinear interpolation */ | ||
60 | }; | ||
61 | |||
62 | /* Resizer input type select */ | ||
63 | enum resizer_colors_type { | ||
64 | RSZ_YUV422 = 0, /* YUV422 color is interleaved */ | ||
65 | RSZ_COLOR8 = 1, /* Color separate data on 8 bits */ | ||
66 | }; | ||
67 | |||
68 | /* | ||
69 | * Structure for horizontal and vertical resizing value | ||
70 | */ | ||
71 | struct resizer_ratio { | ||
72 | u32 horz; | ||
73 | u32 vert; | ||
74 | }; | ||
75 | |||
76 | /* | ||
77 | * Structure for luminance enhancer parameters. | ||
78 | */ | ||
79 | struct resizer_luma_yenh { | ||
80 | u8 algo; /* algorithm select. */ | ||
81 | u8 gain; /* maximum gain. */ | ||
82 | u8 slope; /* slope. */ | ||
83 | u8 core; /* core offset. */ | ||
84 | }; | ||
85 | |||
86 | enum resizer_input_entity { | ||
87 | RESIZER_INPUT_NONE, | ||
88 | RESIZER_INPUT_VP, /* input video port - prev or ccdc */ | ||
89 | RESIZER_INPUT_MEMORY, | ||
90 | }; | ||
91 | |||
92 | /* Sink and source resizer pads */ | ||
93 | #define RESZ_PAD_SINK 0 | ||
94 | #define RESZ_PAD_SOURCE 1 | ||
95 | #define RESZ_PADS_NUM 2 | ||
96 | |||
97 | /* | ||
98 | * struct isp_res_device - OMAP3 ISP resizer module | ||
99 | * @crop.request: Crop rectangle requested by the user | ||
100 | * @crop.active: Active crop rectangle (based on hardware requirements) | ||
101 | */ | ||
102 | struct isp_res_device { | ||
103 | struct v4l2_subdev subdev; | ||
104 | struct media_pad pads[RESZ_PADS_NUM]; | ||
105 | struct v4l2_mbus_framefmt formats[RESZ_PADS_NUM]; | ||
106 | |||
107 | enum resizer_input_entity input; | ||
108 | struct isp_video video_in; | ||
109 | struct isp_video video_out; | ||
110 | unsigned int error; | ||
111 | |||
112 | u32 addr_base; /* stored source buffer address in memory mode */ | ||
113 | u32 crop_offset; /* additional offset for crop in memory mode */ | ||
114 | struct resizer_ratio ratio; | ||
115 | int pm_state; | ||
116 | unsigned int applycrop:1; | ||
117 | enum isp_pipeline_stream_state state; | ||
118 | wait_queue_head_t wait; | ||
119 | atomic_t stopping; | ||
120 | |||
121 | struct { | ||
122 | struct v4l2_rect request; | ||
123 | struct v4l2_rect active; | ||
124 | } crop; | ||
125 | }; | ||
126 | |||
127 | struct isp_device; | ||
128 | |||
129 | int omap3isp_resizer_init(struct isp_device *isp); | ||
130 | void omap3isp_resizer_cleanup(struct isp_device *isp); | ||
131 | |||
132 | int omap3isp_resizer_register_entities(struct isp_res_device *res, | ||
133 | struct v4l2_device *vdev); | ||
134 | void omap3isp_resizer_unregister_entities(struct isp_res_device *res); | ||
135 | void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res); | ||
136 | void omap3isp_resizer_isr(struct isp_res_device *isp_res); | ||
137 | |||
138 | void omap3isp_resizer_max_rate(struct isp_res_device *res, | ||
139 | unsigned int *max_rate); | ||
140 | |||
141 | void omap3isp_resizer_suspend(struct isp_res_device *isp_res); | ||
142 | |||
143 | void omap3isp_resizer_resume(struct isp_res_device *isp_res); | ||
144 | |||
145 | int omap3isp_resizer_busy(struct isp_res_device *isp_res); | ||
146 | |||
147 | #endif /* OMAP3_ISP_RESIZER_H */ | ||
diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c new file mode 100644 index 00000000000..036fc938bbb --- /dev/null +++ b/drivers/media/video/omap3isp/ispstat.c | |||
@@ -0,0 +1,1092 @@ | |||
1 | /* | ||
2 | * ispstat.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - Statistics core | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc | ||
8 | * | ||
9 | * Contacts: David Cohen <dacohen@gmail.com> | ||
10 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
11 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License version 2 as | ||
15 | * published by the Free Software Foundation. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, but | ||
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20 | * General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
25 | * 02110-1301 USA | ||
26 | */ | ||
27 | |||
28 | #include <linux/dma-mapping.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/uaccess.h> | ||
31 | |||
32 | #include "isp.h" | ||
33 | |||
34 | #define IS_COHERENT_BUF(stat) ((stat)->dma_ch >= 0) | ||
35 | |||
36 | /* | ||
37 | * MAGIC_SIZE must always be the greatest common divisor of | ||
38 | * AEWB_PACKET_SIZE and AF_PAXEL_SIZE. | ||
39 | */ | ||
40 | #define MAGIC_SIZE 16 | ||
41 | #define MAGIC_NUM 0x55 | ||
42 | |||
43 | /* HACK: AF module seems to be writing one more paxel data than it should. */ | ||
44 | #define AF_EXTRA_DATA OMAP3ISP_AF_PAXEL_SIZE | ||
45 | |||
46 | /* | ||
47 | * HACK: H3A modules go to an invalid state after have a SBL overflow. It makes | ||
48 | * the next buffer to start to be written in the same point where the overflow | ||
49 | * occurred instead of the configured address. The only known way to make it to | ||
50 | * go back to a valid state is having a valid buffer processing. Of course it | ||
51 | * requires at least a doubled buffer size to avoid an access to invalid memory | ||
52 | * region. But it does not fix everything. It may happen more than one | ||
53 | * consecutive SBL overflows. In that case, it might be unpredictable how many | ||
54 | * buffers the allocated memory should fit. For that case, a recover | ||
55 | * configuration was created. It produces the minimum buffer size for each H3A | ||
56 | * module and decrease the change for more SBL overflows. This recover state | ||
57 | * will be enabled every time a SBL overflow occur. As the output buffer size | ||
58 | * isn't big, it's possible to have an extra size able to fit many recover | ||
59 | * buffers making it extreamily unlikely to have an access to invalid memory | ||
60 | * region. | ||
61 | */ | ||
62 | #define NUM_H3A_RECOVER_BUFS 10 | ||
63 | |||
64 | /* | ||
65 | * HACK: Because of HW issues the generic layer sometimes need to have | ||
66 | * different behaviour for different statistic modules. | ||
67 | */ | ||
68 | #define IS_H3A_AF(stat) ((stat) == &(stat)->isp->isp_af) | ||
69 | #define IS_H3A_AEWB(stat) ((stat) == &(stat)->isp->isp_aewb) | ||
70 | #define IS_H3A(stat) (IS_H3A_AF(stat) || IS_H3A_AEWB(stat)) | ||
71 | |||
72 | static void __isp_stat_buf_sync_magic(struct ispstat *stat, | ||
73 | struct ispstat_buffer *buf, | ||
74 | u32 buf_size, enum dma_data_direction dir, | ||
75 | void (*dma_sync)(struct device *, | ||
76 | dma_addr_t, unsigned long, size_t, | ||
77 | enum dma_data_direction)) | ||
78 | { | ||
79 | struct device *dev = stat->isp->dev; | ||
80 | struct page *pg; | ||
81 | dma_addr_t dma_addr; | ||
82 | u32 offset; | ||
83 | |||
84 | /* Initial magic words */ | ||
85 | pg = vmalloc_to_page(buf->virt_addr); | ||
86 | dma_addr = pfn_to_dma(dev, page_to_pfn(pg)); | ||
87 | dma_sync(dev, dma_addr, 0, MAGIC_SIZE, dir); | ||
88 | |||
89 | /* Final magic words */ | ||
90 | pg = vmalloc_to_page(buf->virt_addr + buf_size); | ||
91 | dma_addr = pfn_to_dma(dev, page_to_pfn(pg)); | ||
92 | offset = ((u32)buf->virt_addr + buf_size) & ~PAGE_MASK; | ||
93 | dma_sync(dev, dma_addr, offset, MAGIC_SIZE, dir); | ||
94 | } | ||
95 | |||
96 | static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat, | ||
97 | struct ispstat_buffer *buf, | ||
98 | u32 buf_size, | ||
99 | enum dma_data_direction dir) | ||
100 | { | ||
101 | if (IS_COHERENT_BUF(stat)) | ||
102 | return; | ||
103 | |||
104 | __isp_stat_buf_sync_magic(stat, buf, buf_size, dir, | ||
105 | dma_sync_single_range_for_device); | ||
106 | } | ||
107 | |||
108 | static void isp_stat_buf_sync_magic_for_cpu(struct ispstat *stat, | ||
109 | struct ispstat_buffer *buf, | ||
110 | u32 buf_size, | ||
111 | enum dma_data_direction dir) | ||
112 | { | ||
113 | if (IS_COHERENT_BUF(stat)) | ||
114 | return; | ||
115 | |||
116 | __isp_stat_buf_sync_magic(stat, buf, buf_size, dir, | ||
117 | dma_sync_single_range_for_cpu); | ||
118 | } | ||
119 | |||
120 | static int isp_stat_buf_check_magic(struct ispstat *stat, | ||
121 | struct ispstat_buffer *buf) | ||
122 | { | ||
123 | const u32 buf_size = IS_H3A_AF(stat) ? | ||
124 | buf->buf_size + AF_EXTRA_DATA : buf->buf_size; | ||
125 | u8 *w; | ||
126 | u8 *end; | ||
127 | int ret = -EINVAL; | ||
128 | |||
129 | isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE); | ||
130 | |||
131 | /* Checking initial magic numbers. They shouldn't be here anymore. */ | ||
132 | for (w = buf->virt_addr, end = w + MAGIC_SIZE; w < end; w++) | ||
133 | if (likely(*w != MAGIC_NUM)) | ||
134 | ret = 0; | ||
135 | |||
136 | if (ret) { | ||
137 | dev_dbg(stat->isp->dev, "%s: beginning magic check does not " | ||
138 | "match.\n", stat->subdev.name); | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | /* Checking magic numbers at the end. They must be still here. */ | ||
143 | for (w = buf->virt_addr + buf_size, end = w + MAGIC_SIZE; | ||
144 | w < end; w++) { | ||
145 | if (unlikely(*w != MAGIC_NUM)) { | ||
146 | dev_dbg(stat->isp->dev, "%s: endding magic check does " | ||
147 | "not match.\n", stat->subdev.name); | ||
148 | return -EINVAL; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | isp_stat_buf_sync_magic_for_device(stat, buf, buf_size, | ||
153 | DMA_FROM_DEVICE); | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static void isp_stat_buf_insert_magic(struct ispstat *stat, | ||
159 | struct ispstat_buffer *buf) | ||
160 | { | ||
161 | const u32 buf_size = IS_H3A_AF(stat) ? | ||
162 | stat->buf_size + AF_EXTRA_DATA : stat->buf_size; | ||
163 | |||
164 | isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE); | ||
165 | |||
166 | /* | ||
167 | * Inserting MAGIC_NUM at the beginning and end of the buffer. | ||
168 | * buf->buf_size is set only after the buffer is queued. For now the | ||
169 | * right buf_size for the current configuration is pointed by | ||
170 | * stat->buf_size. | ||
171 | */ | ||
172 | memset(buf->virt_addr, MAGIC_NUM, MAGIC_SIZE); | ||
173 | memset(buf->virt_addr + buf_size, MAGIC_NUM, MAGIC_SIZE); | ||
174 | |||
175 | isp_stat_buf_sync_magic_for_device(stat, buf, buf_size, | ||
176 | DMA_BIDIRECTIONAL); | ||
177 | } | ||
178 | |||
179 | static void isp_stat_buf_sync_for_device(struct ispstat *stat, | ||
180 | struct ispstat_buffer *buf) | ||
181 | { | ||
182 | if (IS_COHERENT_BUF(stat)) | ||
183 | return; | ||
184 | |||
185 | dma_sync_sg_for_device(stat->isp->dev, buf->iovm->sgt->sgl, | ||
186 | buf->iovm->sgt->nents, DMA_FROM_DEVICE); | ||
187 | } | ||
188 | |||
189 | static void isp_stat_buf_sync_for_cpu(struct ispstat *stat, | ||
190 | struct ispstat_buffer *buf) | ||
191 | { | ||
192 | if (IS_COHERENT_BUF(stat)) | ||
193 | return; | ||
194 | |||
195 | dma_sync_sg_for_cpu(stat->isp->dev, buf->iovm->sgt->sgl, | ||
196 | buf->iovm->sgt->nents, DMA_FROM_DEVICE); | ||
197 | } | ||
198 | |||
199 | static void isp_stat_buf_clear(struct ispstat *stat) | ||
200 | { | ||
201 | int i; | ||
202 | |||
203 | for (i = 0; i < STAT_MAX_BUFS; i++) | ||
204 | stat->buf[i].empty = 1; | ||
205 | } | ||
206 | |||
207 | static struct ispstat_buffer * | ||
208 | __isp_stat_buf_find(struct ispstat *stat, int look_empty) | ||
209 | { | ||
210 | struct ispstat_buffer *found = NULL; | ||
211 | int i; | ||
212 | |||
213 | for (i = 0; i < STAT_MAX_BUFS; i++) { | ||
214 | struct ispstat_buffer *curr = &stat->buf[i]; | ||
215 | |||
216 | /* | ||
217 | * Don't select the buffer which is being copied to | ||
218 | * userspace or used by the module. | ||
219 | */ | ||
220 | if (curr == stat->locked_buf || curr == stat->active_buf) | ||
221 | continue; | ||
222 | |||
223 | /* Don't select uninitialised buffers if it's not required */ | ||
224 | if (!look_empty && curr->empty) | ||
225 | continue; | ||
226 | |||
227 | /* Pick uninitialised buffer over anything else if look_empty */ | ||
228 | if (curr->empty) { | ||
229 | found = curr; | ||
230 | break; | ||
231 | } | ||
232 | |||
233 | /* Choose the oldest buffer */ | ||
234 | if (!found || | ||
235 | (s32)curr->frame_number - (s32)found->frame_number < 0) | ||
236 | found = curr; | ||
237 | } | ||
238 | |||
239 | return found; | ||
240 | } | ||
241 | |||
242 | static inline struct ispstat_buffer * | ||
243 | isp_stat_buf_find_oldest(struct ispstat *stat) | ||
244 | { | ||
245 | return __isp_stat_buf_find(stat, 0); | ||
246 | } | ||
247 | |||
248 | static inline struct ispstat_buffer * | ||
249 | isp_stat_buf_find_oldest_or_empty(struct ispstat *stat) | ||
250 | { | ||
251 | return __isp_stat_buf_find(stat, 1); | ||
252 | } | ||
253 | |||
254 | static int isp_stat_buf_queue(struct ispstat *stat) | ||
255 | { | ||
256 | if (!stat->active_buf) | ||
257 | return STAT_NO_BUF; | ||
258 | |||
259 | do_gettimeofday(&stat->active_buf->ts); | ||
260 | |||
261 | stat->active_buf->buf_size = stat->buf_size; | ||
262 | if (isp_stat_buf_check_magic(stat, stat->active_buf)) { | ||
263 | dev_dbg(stat->isp->dev, "%s: data wasn't properly written.\n", | ||
264 | stat->subdev.name); | ||
265 | return STAT_NO_BUF; | ||
266 | } | ||
267 | stat->active_buf->config_counter = stat->config_counter; | ||
268 | stat->active_buf->frame_number = stat->frame_number; | ||
269 | stat->active_buf->empty = 0; | ||
270 | stat->active_buf = NULL; | ||
271 | |||
272 | return STAT_BUF_DONE; | ||
273 | } | ||
274 | |||
275 | /* Get next free buffer to write the statistics to and mark it active. */ | ||
276 | static void isp_stat_buf_next(struct ispstat *stat) | ||
277 | { | ||
278 | if (unlikely(stat->active_buf)) | ||
279 | /* Overwriting unused active buffer */ | ||
280 | dev_dbg(stat->isp->dev, "%s: new buffer requested without " | ||
281 | "queuing active one.\n", | ||
282 | stat->subdev.name); | ||
283 | else | ||
284 | stat->active_buf = isp_stat_buf_find_oldest_or_empty(stat); | ||
285 | } | ||
286 | |||
287 | static void isp_stat_buf_release(struct ispstat *stat) | ||
288 | { | ||
289 | unsigned long flags; | ||
290 | |||
291 | isp_stat_buf_sync_for_device(stat, stat->locked_buf); | ||
292 | spin_lock_irqsave(&stat->isp->stat_lock, flags); | ||
293 | stat->locked_buf = NULL; | ||
294 | spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
295 | } | ||
296 | |||
297 | /* Get buffer to userspace. */ | ||
298 | static struct ispstat_buffer *isp_stat_buf_get(struct ispstat *stat, | ||
299 | struct omap3isp_stat_data *data) | ||
300 | { | ||
301 | int rval = 0; | ||
302 | unsigned long flags; | ||
303 | struct ispstat_buffer *buf; | ||
304 | |||
305 | spin_lock_irqsave(&stat->isp->stat_lock, flags); | ||
306 | |||
307 | while (1) { | ||
308 | buf = isp_stat_buf_find_oldest(stat); | ||
309 | if (!buf) { | ||
310 | spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
311 | dev_dbg(stat->isp->dev, "%s: cannot find a buffer.\n", | ||
312 | stat->subdev.name); | ||
313 | return ERR_PTR(-EBUSY); | ||
314 | } | ||
315 | if (isp_stat_buf_check_magic(stat, buf)) { | ||
316 | dev_dbg(stat->isp->dev, "%s: current buffer has " | ||
317 | "corrupted data\n.", stat->subdev.name); | ||
318 | /* Mark empty because it doesn't have valid data. */ | ||
319 | buf->empty = 1; | ||
320 | } else { | ||
321 | /* Buffer isn't corrupted. */ | ||
322 | break; | ||
323 | } | ||
324 | } | ||
325 | |||
326 | stat->locked_buf = buf; | ||
327 | |||
328 | spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
329 | |||
330 | if (buf->buf_size > data->buf_size) { | ||
331 | dev_warn(stat->isp->dev, "%s: userspace's buffer size is " | ||
332 | "not enough.\n", stat->subdev.name); | ||
333 | isp_stat_buf_release(stat); | ||
334 | return ERR_PTR(-EINVAL); | ||
335 | } | ||
336 | |||
337 | isp_stat_buf_sync_for_cpu(stat, buf); | ||
338 | |||
339 | rval = copy_to_user(data->buf, | ||
340 | buf->virt_addr, | ||
341 | buf->buf_size); | ||
342 | |||
343 | if (rval) { | ||
344 | dev_info(stat->isp->dev, | ||
345 | "%s: failed copying %d bytes of stat data\n", | ||
346 | stat->subdev.name, rval); | ||
347 | buf = ERR_PTR(-EFAULT); | ||
348 | isp_stat_buf_release(stat); | ||
349 | } | ||
350 | |||
351 | return buf; | ||
352 | } | ||
353 | |||
354 | static void isp_stat_bufs_free(struct ispstat *stat) | ||
355 | { | ||
356 | struct isp_device *isp = stat->isp; | ||
357 | int i; | ||
358 | |||
359 | for (i = 0; i < STAT_MAX_BUFS; i++) { | ||
360 | struct ispstat_buffer *buf = &stat->buf[i]; | ||
361 | |||
362 | if (!IS_COHERENT_BUF(stat)) { | ||
363 | if (IS_ERR_OR_NULL((void *)buf->iommu_addr)) | ||
364 | continue; | ||
365 | if (buf->iovm) | ||
366 | dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl, | ||
367 | buf->iovm->sgt->nents, | ||
368 | DMA_FROM_DEVICE); | ||
369 | omap_iommu_vfree(isp->domain, isp->dev, | ||
370 | buf->iommu_addr); | ||
371 | } else { | ||
372 | if (!buf->virt_addr) | ||
373 | continue; | ||
374 | dma_free_coherent(stat->isp->dev, stat->buf_alloc_size, | ||
375 | buf->virt_addr, buf->dma_addr); | ||
376 | } | ||
377 | buf->iommu_addr = 0; | ||
378 | buf->iovm = NULL; | ||
379 | buf->dma_addr = 0; | ||
380 | buf->virt_addr = NULL; | ||
381 | buf->empty = 1; | ||
382 | } | ||
383 | |||
384 | dev_dbg(stat->isp->dev, "%s: all buffers were freed.\n", | ||
385 | stat->subdev.name); | ||
386 | |||
387 | stat->buf_alloc_size = 0; | ||
388 | stat->active_buf = NULL; | ||
389 | } | ||
390 | |||
391 | static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size) | ||
392 | { | ||
393 | struct isp_device *isp = stat->isp; | ||
394 | int i; | ||
395 | |||
396 | stat->buf_alloc_size = size; | ||
397 | |||
398 | for (i = 0; i < STAT_MAX_BUFS; i++) { | ||
399 | struct ispstat_buffer *buf = &stat->buf[i]; | ||
400 | struct iovm_struct *iovm; | ||
401 | |||
402 | WARN_ON(buf->dma_addr); | ||
403 | buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->dev, 0, | ||
404 | size, IOMMU_FLAG); | ||
405 | if (IS_ERR((void *)buf->iommu_addr)) { | ||
406 | dev_err(stat->isp->dev, | ||
407 | "%s: Can't acquire memory for " | ||
408 | "buffer %d\n", stat->subdev.name, i); | ||
409 | isp_stat_bufs_free(stat); | ||
410 | return -ENOMEM; | ||
411 | } | ||
412 | |||
413 | iovm = omap_find_iovm_area(isp->dev, buf->iommu_addr); | ||
414 | if (!iovm || | ||
415 | !dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents, | ||
416 | DMA_FROM_DEVICE)) { | ||
417 | isp_stat_bufs_free(stat); | ||
418 | return -ENOMEM; | ||
419 | } | ||
420 | buf->iovm = iovm; | ||
421 | |||
422 | buf->virt_addr = omap_da_to_va(stat->isp->dev, | ||
423 | (u32)buf->iommu_addr); | ||
424 | buf->empty = 1; | ||
425 | dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated." | ||
426 | "iommu_addr=0x%08lx virt_addr=0x%08lx", | ||
427 | stat->subdev.name, i, buf->iommu_addr, | ||
428 | (unsigned long)buf->virt_addr); | ||
429 | } | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | static int isp_stat_bufs_alloc_dma(struct ispstat *stat, unsigned int size) | ||
435 | { | ||
436 | int i; | ||
437 | |||
438 | stat->buf_alloc_size = size; | ||
439 | |||
440 | for (i = 0; i < STAT_MAX_BUFS; i++) { | ||
441 | struct ispstat_buffer *buf = &stat->buf[i]; | ||
442 | |||
443 | WARN_ON(buf->iommu_addr); | ||
444 | buf->virt_addr = dma_alloc_coherent(stat->isp->dev, size, | ||
445 | &buf->dma_addr, GFP_KERNEL | GFP_DMA); | ||
446 | |||
447 | if (!buf->virt_addr || !buf->dma_addr) { | ||
448 | dev_info(stat->isp->dev, | ||
449 | "%s: Can't acquire memory for " | ||
450 | "DMA buffer %d\n", stat->subdev.name, i); | ||
451 | isp_stat_bufs_free(stat); | ||
452 | return -ENOMEM; | ||
453 | } | ||
454 | buf->empty = 1; | ||
455 | |||
456 | dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated." | ||
457 | "dma_addr=0x%08lx virt_addr=0x%08lx\n", | ||
458 | stat->subdev.name, i, (unsigned long)buf->dma_addr, | ||
459 | (unsigned long)buf->virt_addr); | ||
460 | } | ||
461 | |||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size) | ||
466 | { | ||
467 | unsigned long flags; | ||
468 | |||
469 | spin_lock_irqsave(&stat->isp->stat_lock, flags); | ||
470 | |||
471 | BUG_ON(stat->locked_buf != NULL); | ||
472 | |||
473 | /* Are the old buffers big enough? */ | ||
474 | if (stat->buf_alloc_size >= size) { | ||
475 | spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | if (stat->state != ISPSTAT_DISABLED || stat->buf_processing) { | ||
480 | dev_info(stat->isp->dev, | ||
481 | "%s: trying to allocate memory when busy\n", | ||
482 | stat->subdev.name); | ||
483 | spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
484 | return -EBUSY; | ||
485 | } | ||
486 | |||
487 | spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
488 | |||
489 | isp_stat_bufs_free(stat); | ||
490 | |||
491 | if (IS_COHERENT_BUF(stat)) | ||
492 | return isp_stat_bufs_alloc_dma(stat, size); | ||
493 | else | ||
494 | return isp_stat_bufs_alloc_iommu(stat, size); | ||
495 | } | ||
496 | |||
497 | static void isp_stat_queue_event(struct ispstat *stat, int err) | ||
498 | { | ||
499 | struct video_device *vdev = &stat->subdev.devnode; | ||
500 | struct v4l2_event event; | ||
501 | struct omap3isp_stat_event_status *status = (void *)event.u.data; | ||
502 | |||
503 | memset(&event, 0, sizeof(event)); | ||
504 | if (!err) { | ||
505 | status->frame_number = stat->frame_number; | ||
506 | status->config_counter = stat->config_counter; | ||
507 | } else { | ||
508 | status->buf_err = 1; | ||
509 | } | ||
510 | event.type = stat->event_type; | ||
511 | v4l2_event_queue(vdev, &event); | ||
512 | } | ||
513 | |||
514 | |||
515 | /* | ||
516 | * omap3isp_stat_request_statistics - Request statistics. | ||
517 | * @data: Pointer to return statistics data. | ||
518 | * | ||
519 | * Returns 0 if successful. | ||
520 | */ | ||
521 | int omap3isp_stat_request_statistics(struct ispstat *stat, | ||
522 | struct omap3isp_stat_data *data) | ||
523 | { | ||
524 | struct ispstat_buffer *buf; | ||
525 | |||
526 | if (stat->state != ISPSTAT_ENABLED) { | ||
527 | dev_dbg(stat->isp->dev, "%s: engine not enabled.\n", | ||
528 | stat->subdev.name); | ||
529 | return -EINVAL; | ||
530 | } | ||
531 | |||
532 | mutex_lock(&stat->ioctl_lock); | ||
533 | buf = isp_stat_buf_get(stat, data); | ||
534 | if (IS_ERR(buf)) { | ||
535 | mutex_unlock(&stat->ioctl_lock); | ||
536 | return PTR_ERR(buf); | ||
537 | } | ||
538 | |||
539 | data->ts = buf->ts; | ||
540 | data->config_counter = buf->config_counter; | ||
541 | data->frame_number = buf->frame_number; | ||
542 | data->buf_size = buf->buf_size; | ||
543 | |||
544 | buf->empty = 1; | ||
545 | isp_stat_buf_release(stat); | ||
546 | mutex_unlock(&stat->ioctl_lock); | ||
547 | |||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | /* | ||
552 | * omap3isp_stat_config - Receives new statistic engine configuration. | ||
553 | * @new_conf: Pointer to config structure. | ||
554 | * | ||
555 | * Returns 0 if successful, -EINVAL if new_conf pointer is NULL, -ENOMEM if | ||
556 | * was unable to allocate memory for the buffer, or other errors if parameters | ||
557 | * are invalid. | ||
558 | */ | ||
559 | int omap3isp_stat_config(struct ispstat *stat, void *new_conf) | ||
560 | { | ||
561 | int ret; | ||
562 | unsigned long irqflags; | ||
563 | struct ispstat_generic_config *user_cfg = new_conf; | ||
564 | u32 buf_size = user_cfg->buf_size; | ||
565 | |||
566 | if (!new_conf) { | ||
567 | dev_dbg(stat->isp->dev, "%s: configuration is NULL\n", | ||
568 | stat->subdev.name); | ||
569 | return -EINVAL; | ||
570 | } | ||
571 | |||
572 | mutex_lock(&stat->ioctl_lock); | ||
573 | |||
574 | dev_dbg(stat->isp->dev, "%s: configuring module with buffer " | ||
575 | "size=0x%08lx\n", stat->subdev.name, (unsigned long)buf_size); | ||
576 | |||
577 | ret = stat->ops->validate_params(stat, new_conf); | ||
578 | if (ret) { | ||
579 | mutex_unlock(&stat->ioctl_lock); | ||
580 | dev_dbg(stat->isp->dev, "%s: configuration values are " | ||
581 | "invalid.\n", stat->subdev.name); | ||
582 | return ret; | ||
583 | } | ||
584 | |||
585 | if (buf_size != user_cfg->buf_size) | ||
586 | dev_dbg(stat->isp->dev, "%s: driver has corrected buffer size " | ||
587 | "request to 0x%08lx\n", stat->subdev.name, | ||
588 | (unsigned long)user_cfg->buf_size); | ||
589 | |||
590 | /* | ||
591 | * Hack: H3A modules may need a doubled buffer size to avoid access | ||
592 | * to a invalid memory address after a SBL overflow. | ||
593 | * The buffer size is always PAGE_ALIGNED. | ||
594 | * Hack 2: MAGIC_SIZE is added to buf_size so a magic word can be | ||
595 | * inserted at the end to data integrity check purpose. | ||
596 | * Hack 3: AF module writes one paxel data more than it should, so | ||
597 | * the buffer allocation must consider it to avoid invalid memory | ||
598 | * access. | ||
599 | * Hack 4: H3A need to allocate extra space for the recover state. | ||
600 | */ | ||
601 | if (IS_H3A(stat)) { | ||
602 | buf_size = user_cfg->buf_size * 2 + MAGIC_SIZE; | ||
603 | if (IS_H3A_AF(stat)) | ||
604 | /* | ||
605 | * Adding one extra paxel data size for each recover | ||
606 | * buffer + 2 regular ones. | ||
607 | */ | ||
608 | buf_size += AF_EXTRA_DATA * (NUM_H3A_RECOVER_BUFS + 2); | ||
609 | if (stat->recover_priv) { | ||
610 | struct ispstat_generic_config *recover_cfg = | ||
611 | stat->recover_priv; | ||
612 | buf_size += recover_cfg->buf_size * | ||
613 | NUM_H3A_RECOVER_BUFS; | ||
614 | } | ||
615 | buf_size = PAGE_ALIGN(buf_size); | ||
616 | } else { /* Histogram */ | ||
617 | buf_size = PAGE_ALIGN(user_cfg->buf_size + MAGIC_SIZE); | ||
618 | } | ||
619 | |||
620 | ret = isp_stat_bufs_alloc(stat, buf_size); | ||
621 | if (ret) { | ||
622 | mutex_unlock(&stat->ioctl_lock); | ||
623 | return ret; | ||
624 | } | ||
625 | |||
626 | spin_lock_irqsave(&stat->isp->stat_lock, irqflags); | ||
627 | stat->ops->set_params(stat, new_conf); | ||
628 | spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
629 | |||
630 | /* | ||
631 | * Returning the right future config_counter for this setup, so | ||
632 | * userspace can *know* when it has been applied. | ||
633 | */ | ||
634 | user_cfg->config_counter = stat->config_counter + stat->inc_config; | ||
635 | |||
636 | /* Module has a valid configuration. */ | ||
637 | stat->configured = 1; | ||
638 | dev_dbg(stat->isp->dev, "%s: module has been successfully " | ||
639 | "configured.\n", stat->subdev.name); | ||
640 | |||
641 | mutex_unlock(&stat->ioctl_lock); | ||
642 | |||
643 | return 0; | ||
644 | } | ||
645 | |||
646 | /* | ||
647 | * isp_stat_buf_process - Process statistic buffers. | ||
648 | * @buf_state: points out if buffer is ready to be processed. It's necessary | ||
649 | * because histogram needs to copy the data from internal memory | ||
650 | * before be able to process the buffer. | ||
651 | */ | ||
652 | static int isp_stat_buf_process(struct ispstat *stat, int buf_state) | ||
653 | { | ||
654 | int ret = STAT_NO_BUF; | ||
655 | |||
656 | if (!atomic_add_unless(&stat->buf_err, -1, 0) && | ||
657 | buf_state == STAT_BUF_DONE && stat->state == ISPSTAT_ENABLED) { | ||
658 | ret = isp_stat_buf_queue(stat); | ||
659 | isp_stat_buf_next(stat); | ||
660 | } | ||
661 | |||
662 | return ret; | ||
663 | } | ||
664 | |||
665 | int omap3isp_stat_pcr_busy(struct ispstat *stat) | ||
666 | { | ||
667 | return stat->ops->busy(stat); | ||
668 | } | ||
669 | |||
670 | int omap3isp_stat_busy(struct ispstat *stat) | ||
671 | { | ||
672 | return omap3isp_stat_pcr_busy(stat) | stat->buf_processing | | ||
673 | (stat->state != ISPSTAT_DISABLED); | ||
674 | } | ||
675 | |||
676 | /* | ||
677 | * isp_stat_pcr_enable - Disables/Enables statistic engines. | ||
678 | * @pcr_enable: 0/1 - Disables/Enables the engine. | ||
679 | * | ||
680 | * Must be called from ISP driver when the module is idle and synchronized | ||
681 | * with CCDC. | ||
682 | */ | ||
683 | static void isp_stat_pcr_enable(struct ispstat *stat, u8 pcr_enable) | ||
684 | { | ||
685 | if ((stat->state != ISPSTAT_ENABLING && | ||
686 | stat->state != ISPSTAT_ENABLED) && pcr_enable) | ||
687 | /* Userspace has disabled the module. Aborting. */ | ||
688 | return; | ||
689 | |||
690 | stat->ops->enable(stat, pcr_enable); | ||
691 | if (stat->state == ISPSTAT_DISABLING && !pcr_enable) | ||
692 | stat->state = ISPSTAT_DISABLED; | ||
693 | else if (stat->state == ISPSTAT_ENABLING && pcr_enable) | ||
694 | stat->state = ISPSTAT_ENABLED; | ||
695 | } | ||
696 | |||
697 | void omap3isp_stat_suspend(struct ispstat *stat) | ||
698 | { | ||
699 | unsigned long flags; | ||
700 | |||
701 | spin_lock_irqsave(&stat->isp->stat_lock, flags); | ||
702 | |||
703 | if (stat->state != ISPSTAT_DISABLED) | ||
704 | stat->ops->enable(stat, 0); | ||
705 | if (stat->state == ISPSTAT_ENABLED) | ||
706 | stat->state = ISPSTAT_SUSPENDED; | ||
707 | |||
708 | spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
709 | } | ||
710 | |||
711 | void omap3isp_stat_resume(struct ispstat *stat) | ||
712 | { | ||
713 | /* Module will be re-enabled with its pipeline */ | ||
714 | if (stat->state == ISPSTAT_SUSPENDED) | ||
715 | stat->state = ISPSTAT_ENABLING; | ||
716 | } | ||
717 | |||
718 | static void isp_stat_try_enable(struct ispstat *stat) | ||
719 | { | ||
720 | unsigned long irqflags; | ||
721 | |||
722 | if (stat->priv == NULL) | ||
723 | /* driver wasn't initialised */ | ||
724 | return; | ||
725 | |||
726 | spin_lock_irqsave(&stat->isp->stat_lock, irqflags); | ||
727 | if (stat->state == ISPSTAT_ENABLING && !stat->buf_processing && | ||
728 | stat->buf_alloc_size) { | ||
729 | /* | ||
730 | * Userspace's requested to enable the engine but it wasn't yet. | ||
731 | * Let's do that now. | ||
732 | */ | ||
733 | stat->update = 1; | ||
734 | isp_stat_buf_next(stat); | ||
735 | stat->ops->setup_regs(stat, stat->priv); | ||
736 | isp_stat_buf_insert_magic(stat, stat->active_buf); | ||
737 | |||
738 | /* | ||
739 | * H3A module has some hw issues which forces the driver to | ||
740 | * ignore next buffers even if it was disabled in the meantime. | ||
741 | * On the other hand, Histogram shouldn't ignore buffers anymore | ||
742 | * if it's being enabled. | ||
743 | */ | ||
744 | if (!IS_H3A(stat)) | ||
745 | atomic_set(&stat->buf_err, 0); | ||
746 | |||
747 | isp_stat_pcr_enable(stat, 1); | ||
748 | spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
749 | dev_dbg(stat->isp->dev, "%s: module is enabled.\n", | ||
750 | stat->subdev.name); | ||
751 | } else { | ||
752 | spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
753 | } | ||
754 | } | ||
755 | |||
756 | void omap3isp_stat_isr_frame_sync(struct ispstat *stat) | ||
757 | { | ||
758 | isp_stat_try_enable(stat); | ||
759 | } | ||
760 | |||
761 | void omap3isp_stat_sbl_overflow(struct ispstat *stat) | ||
762 | { | ||
763 | unsigned long irqflags; | ||
764 | |||
765 | spin_lock_irqsave(&stat->isp->stat_lock, irqflags); | ||
766 | /* | ||
767 | * Due to a H3A hw issue which prevents the next buffer to start from | ||
768 | * the correct memory address, 2 buffers must be ignored. | ||
769 | */ | ||
770 | atomic_set(&stat->buf_err, 2); | ||
771 | |||
772 | /* | ||
773 | * If more than one SBL overflow happen in a row, H3A module may access | ||
774 | * invalid memory region. | ||
775 | * stat->sbl_ovl_recover is set to tell to the driver to temporarily use | ||
776 | * a soft configuration which helps to avoid consecutive overflows. | ||
777 | */ | ||
778 | if (stat->recover_priv) | ||
779 | stat->sbl_ovl_recover = 1; | ||
780 | spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
781 | } | ||
782 | |||
783 | /* | ||
784 | * omap3isp_stat_enable - Disable/Enable statistic engine as soon as possible | ||
785 | * @enable: 0/1 - Disables/Enables the engine. | ||
786 | * | ||
787 | * Client should configure all the module registers before this. | ||
788 | * This function can be called from a userspace request. | ||
789 | */ | ||
790 | int omap3isp_stat_enable(struct ispstat *stat, u8 enable) | ||
791 | { | ||
792 | unsigned long irqflags; | ||
793 | |||
794 | dev_dbg(stat->isp->dev, "%s: user wants to %s module.\n", | ||
795 | stat->subdev.name, enable ? "enable" : "disable"); | ||
796 | |||
797 | /* Prevent enabling while configuring */ | ||
798 | mutex_lock(&stat->ioctl_lock); | ||
799 | |||
800 | spin_lock_irqsave(&stat->isp->stat_lock, irqflags); | ||
801 | |||
802 | if (!stat->configured && enable) { | ||
803 | spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
804 | mutex_unlock(&stat->ioctl_lock); | ||
805 | dev_dbg(stat->isp->dev, "%s: cannot enable module as it's " | ||
806 | "never been successfully configured so far.\n", | ||
807 | stat->subdev.name); | ||
808 | return -EINVAL; | ||
809 | } | ||
810 | |||
811 | if (enable) { | ||
812 | if (stat->state == ISPSTAT_DISABLING) | ||
813 | /* Previous disabling request wasn't done yet */ | ||
814 | stat->state = ISPSTAT_ENABLED; | ||
815 | else if (stat->state == ISPSTAT_DISABLED) | ||
816 | /* Module is now being enabled */ | ||
817 | stat->state = ISPSTAT_ENABLING; | ||
818 | } else { | ||
819 | if (stat->state == ISPSTAT_ENABLING) { | ||
820 | /* Previous enabling request wasn't done yet */ | ||
821 | stat->state = ISPSTAT_DISABLED; | ||
822 | } else if (stat->state == ISPSTAT_ENABLED) { | ||
823 | /* Module is now being disabled */ | ||
824 | stat->state = ISPSTAT_DISABLING; | ||
825 | isp_stat_buf_clear(stat); | ||
826 | } | ||
827 | } | ||
828 | |||
829 | spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
830 | mutex_unlock(&stat->ioctl_lock); | ||
831 | |||
832 | return 0; | ||
833 | } | ||
834 | |||
835 | int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable) | ||
836 | { | ||
837 | struct ispstat *stat = v4l2_get_subdevdata(subdev); | ||
838 | |||
839 | if (enable) { | ||
840 | /* | ||
841 | * Only set enable PCR bit if the module was previously | ||
842 | * enabled through ioct. | ||
843 | */ | ||
844 | isp_stat_try_enable(stat); | ||
845 | } else { | ||
846 | unsigned long flags; | ||
847 | /* Disable PCR bit and config enable field */ | ||
848 | omap3isp_stat_enable(stat, 0); | ||
849 | spin_lock_irqsave(&stat->isp->stat_lock, flags); | ||
850 | stat->ops->enable(stat, 0); | ||
851 | spin_unlock_irqrestore(&stat->isp->stat_lock, flags); | ||
852 | |||
853 | /* | ||
854 | * If module isn't busy, a new interrupt may come or not to | ||
855 | * set the state to DISABLED. As Histogram needs to read its | ||
856 | * internal memory to clear it, let interrupt handler | ||
857 | * responsible of changing state to DISABLED. If the last | ||
858 | * interrupt is coming, it's still safe as the handler will | ||
859 | * ignore the second time when state is already set to DISABLED. | ||
860 | * It's necessary to synchronize Histogram with streamoff, once | ||
861 | * the module may be considered idle before last SDMA transfer | ||
862 | * starts if we return here. | ||
863 | */ | ||
864 | if (!omap3isp_stat_pcr_busy(stat)) | ||
865 | omap3isp_stat_isr(stat); | ||
866 | |||
867 | dev_dbg(stat->isp->dev, "%s: module is being disabled\n", | ||
868 | stat->subdev.name); | ||
869 | } | ||
870 | |||
871 | return 0; | ||
872 | } | ||
873 | |||
874 | /* | ||
875 | * __stat_isr - Interrupt handler for statistic drivers | ||
876 | */ | ||
877 | static void __stat_isr(struct ispstat *stat, int from_dma) | ||
878 | { | ||
879 | int ret = STAT_BUF_DONE; | ||
880 | int buf_processing; | ||
881 | unsigned long irqflags; | ||
882 | struct isp_pipeline *pipe; | ||
883 | |||
884 | /* | ||
885 | * stat->buf_processing must be set before disable module. It's | ||
886 | * necessary to not inform too early the buffers aren't busy in case | ||
887 | * of SDMA is going to be used. | ||
888 | */ | ||
889 | spin_lock_irqsave(&stat->isp->stat_lock, irqflags); | ||
890 | if (stat->state == ISPSTAT_DISABLED) { | ||
891 | spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
892 | return; | ||
893 | } | ||
894 | buf_processing = stat->buf_processing; | ||
895 | stat->buf_processing = 1; | ||
896 | stat->ops->enable(stat, 0); | ||
897 | |||
898 | if (buf_processing && !from_dma) { | ||
899 | if (stat->state == ISPSTAT_ENABLED) { | ||
900 | spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
901 | dev_err(stat->isp->dev, | ||
902 | "%s: interrupt occurred when module was still " | ||
903 | "processing a buffer.\n", stat->subdev.name); | ||
904 | ret = STAT_NO_BUF; | ||
905 | goto out; | ||
906 | } else { | ||
907 | /* | ||
908 | * Interrupt handler was called from streamoff when | ||
909 | * the module wasn't busy anymore to ensure it is being | ||
910 | * disabled after process last buffer. If such buffer | ||
911 | * processing has already started, no need to do | ||
912 | * anything else. | ||
913 | */ | ||
914 | spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
915 | return; | ||
916 | } | ||
917 | } | ||
918 | spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
919 | |||
920 | /* If it's busy we can't process this buffer anymore */ | ||
921 | if (!omap3isp_stat_pcr_busy(stat)) { | ||
922 | if (!from_dma && stat->ops->buf_process) | ||
923 | /* Module still need to copy data to buffer. */ | ||
924 | ret = stat->ops->buf_process(stat); | ||
925 | if (ret == STAT_BUF_WAITING_DMA) | ||
926 | /* Buffer is not ready yet */ | ||
927 | return; | ||
928 | |||
929 | spin_lock_irqsave(&stat->isp->stat_lock, irqflags); | ||
930 | |||
931 | /* | ||
932 | * Histogram needs to read its internal memory to clear it | ||
933 | * before be disabled. For that reason, common statistic layer | ||
934 | * can return only after call stat's buf_process() operator. | ||
935 | */ | ||
936 | if (stat->state == ISPSTAT_DISABLING) { | ||
937 | stat->state = ISPSTAT_DISABLED; | ||
938 | spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
939 | stat->buf_processing = 0; | ||
940 | return; | ||
941 | } | ||
942 | pipe = to_isp_pipeline(&stat->subdev.entity); | ||
943 | stat->frame_number = atomic_read(&pipe->frame_number); | ||
944 | |||
945 | /* | ||
946 | * Before this point, 'ret' stores the buffer's status if it's | ||
947 | * ready to be processed. Afterwards, it holds the status if | ||
948 | * it was processed successfully. | ||
949 | */ | ||
950 | ret = isp_stat_buf_process(stat, ret); | ||
951 | |||
952 | if (likely(!stat->sbl_ovl_recover)) { | ||
953 | stat->ops->setup_regs(stat, stat->priv); | ||
954 | } else { | ||
955 | /* | ||
956 | * Using recover config to increase the chance to have | ||
957 | * a good buffer processing and make the H3A module to | ||
958 | * go back to a valid state. | ||
959 | */ | ||
960 | stat->update = 1; | ||
961 | stat->ops->setup_regs(stat, stat->recover_priv); | ||
962 | stat->sbl_ovl_recover = 0; | ||
963 | |||
964 | /* | ||
965 | * Set 'update' in case of the module needs to use | ||
966 | * regular configuration after next buffer. | ||
967 | */ | ||
968 | stat->update = 1; | ||
969 | } | ||
970 | |||
971 | isp_stat_buf_insert_magic(stat, stat->active_buf); | ||
972 | |||
973 | /* | ||
974 | * Hack: H3A modules may access invalid memory address or send | ||
975 | * corrupted data to userspace if more than 1 SBL overflow | ||
976 | * happens in a row without re-writing its buffer's start memory | ||
977 | * address in the meantime. Such situation is avoided if the | ||
978 | * module is not immediately re-enabled when the ISR misses the | ||
979 | * timing to process the buffer and to setup the registers. | ||
980 | * Because of that, pcr_enable(1) was moved to inside this 'if' | ||
981 | * block. But the next interruption will still happen as during | ||
982 | * pcr_enable(0) the module was busy. | ||
983 | */ | ||
984 | isp_stat_pcr_enable(stat, 1); | ||
985 | spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); | ||
986 | } else { | ||
987 | /* | ||
988 | * If a SBL overflow occurs and the H3A driver misses the timing | ||
989 | * to process the buffer, stat->buf_err is set and won't be | ||
990 | * cleared now. So the next buffer will be correctly ignored. | ||
991 | * It's necessary due to a hw issue which makes the next H3A | ||
992 | * buffer to start from the memory address where the previous | ||
993 | * one stopped, instead of start where it was configured to. | ||
994 | * Do not "stat->buf_err = 0" here. | ||
995 | */ | ||
996 | |||
997 | if (stat->ops->buf_process) | ||
998 | /* | ||
999 | * Driver may need to erase current data prior to | ||
1000 | * process a new buffer. If it misses the timing, the | ||
1001 | * next buffer might be wrong. So should be ignored. | ||
1002 | * It happens only for Histogram. | ||
1003 | */ | ||
1004 | atomic_set(&stat->buf_err, 1); | ||
1005 | |||
1006 | ret = STAT_NO_BUF; | ||
1007 | dev_dbg(stat->isp->dev, "%s: cannot process buffer, " | ||
1008 | "device is busy.\n", stat->subdev.name); | ||
1009 | } | ||
1010 | |||
1011 | out: | ||
1012 | stat->buf_processing = 0; | ||
1013 | isp_stat_queue_event(stat, ret != STAT_BUF_DONE); | ||
1014 | } | ||
1015 | |||
1016 | void omap3isp_stat_isr(struct ispstat *stat) | ||
1017 | { | ||
1018 | __stat_isr(stat, 0); | ||
1019 | } | ||
1020 | |||
1021 | void omap3isp_stat_dma_isr(struct ispstat *stat) | ||
1022 | { | ||
1023 | __stat_isr(stat, 1); | ||
1024 | } | ||
1025 | |||
1026 | static int isp_stat_init_entities(struct ispstat *stat, const char *name, | ||
1027 | const struct v4l2_subdev_ops *sd_ops) | ||
1028 | { | ||
1029 | struct v4l2_subdev *subdev = &stat->subdev; | ||
1030 | struct media_entity *me = &subdev->entity; | ||
1031 | |||
1032 | v4l2_subdev_init(subdev, sd_ops); | ||
1033 | snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name); | ||
1034 | subdev->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
1035 | subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1036 | v4l2_set_subdevdata(subdev, stat); | ||
1037 | |||
1038 | stat->pad.flags = MEDIA_PAD_FL_SINK; | ||
1039 | me->ops = NULL; | ||
1040 | |||
1041 | return media_entity_init(me, 1, &stat->pad, 0); | ||
1042 | } | ||
1043 | |||
1044 | int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, | ||
1045 | struct v4l2_fh *fh, | ||
1046 | struct v4l2_event_subscription *sub) | ||
1047 | { | ||
1048 | struct ispstat *stat = v4l2_get_subdevdata(subdev); | ||
1049 | |||
1050 | if (sub->type != stat->event_type) | ||
1051 | return -EINVAL; | ||
1052 | |||
1053 | return v4l2_event_subscribe(fh, sub, STAT_NEVENTS); | ||
1054 | } | ||
1055 | |||
1056 | int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, | ||
1057 | struct v4l2_fh *fh, | ||
1058 | struct v4l2_event_subscription *sub) | ||
1059 | { | ||
1060 | return v4l2_event_unsubscribe(fh, sub); | ||
1061 | } | ||
1062 | |||
1063 | void omap3isp_stat_unregister_entities(struct ispstat *stat) | ||
1064 | { | ||
1065 | media_entity_cleanup(&stat->subdev.entity); | ||
1066 | v4l2_device_unregister_subdev(&stat->subdev); | ||
1067 | } | ||
1068 | |||
1069 | int omap3isp_stat_register_entities(struct ispstat *stat, | ||
1070 | struct v4l2_device *vdev) | ||
1071 | { | ||
1072 | return v4l2_device_register_subdev(vdev, &stat->subdev); | ||
1073 | } | ||
1074 | |||
1075 | int omap3isp_stat_init(struct ispstat *stat, const char *name, | ||
1076 | const struct v4l2_subdev_ops *sd_ops) | ||
1077 | { | ||
1078 | stat->buf = kcalloc(STAT_MAX_BUFS, sizeof(*stat->buf), GFP_KERNEL); | ||
1079 | if (!stat->buf) | ||
1080 | return -ENOMEM; | ||
1081 | isp_stat_buf_clear(stat); | ||
1082 | mutex_init(&stat->ioctl_lock); | ||
1083 | atomic_set(&stat->buf_err, 0); | ||
1084 | |||
1085 | return isp_stat_init_entities(stat, name, sd_ops); | ||
1086 | } | ||
1087 | |||
1088 | void omap3isp_stat_free(struct ispstat *stat) | ||
1089 | { | ||
1090 | isp_stat_bufs_free(stat); | ||
1091 | kfree(stat->buf); | ||
1092 | } | ||
diff --git a/drivers/media/video/omap3isp/ispstat.h b/drivers/media/video/omap3isp/ispstat.h new file mode 100644 index 00000000000..d86da94fa50 --- /dev/null +++ b/drivers/media/video/omap3isp/ispstat.h | |||
@@ -0,0 +1,169 @@ | |||
1 | /* | ||
2 | * ispstat.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - Statistics core | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc | ||
8 | * | ||
9 | * Contacts: David Cohen <dacohen@gmail.com> | ||
10 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
11 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License version 2 as | ||
15 | * published by the Free Software Foundation. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, but | ||
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20 | * General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
25 | * 02110-1301 USA | ||
26 | */ | ||
27 | |||
28 | #ifndef OMAP3_ISP_STAT_H | ||
29 | #define OMAP3_ISP_STAT_H | ||
30 | |||
31 | #include <linux/types.h> | ||
32 | #include <linux/omap3isp.h> | ||
33 | #include <plat/dma.h> | ||
34 | #include <media/v4l2-event.h> | ||
35 | |||
36 | #include "isp.h" | ||
37 | #include "ispvideo.h" | ||
38 | |||
39 | #define STAT_MAX_BUFS 5 | ||
40 | #define STAT_NEVENTS 8 | ||
41 | |||
42 | #define STAT_BUF_DONE 0 /* Buffer is ready */ | ||
43 | #define STAT_NO_BUF 1 /* An error has occurred */ | ||
44 | #define STAT_BUF_WAITING_DMA 2 /* Histogram only: DMA is running */ | ||
45 | |||
46 | struct ispstat; | ||
47 | |||
48 | struct ispstat_buffer { | ||
49 | unsigned long iommu_addr; | ||
50 | struct iovm_struct *iovm; | ||
51 | void *virt_addr; | ||
52 | dma_addr_t dma_addr; | ||
53 | struct timeval ts; | ||
54 | u32 buf_size; | ||
55 | u32 frame_number; | ||
56 | u16 config_counter; | ||
57 | u8 empty; | ||
58 | }; | ||
59 | |||
60 | struct ispstat_ops { | ||
61 | /* | ||
62 | * Validate new params configuration. | ||
63 | * new_conf->buf_size value must be changed to the exact buffer size | ||
64 | * necessary for the new configuration if it's smaller. | ||
65 | */ | ||
66 | int (*validate_params)(struct ispstat *stat, void *new_conf); | ||
67 | |||
68 | /* | ||
69 | * Save new params configuration. | ||
70 | * stat->priv->buf_size value must be set to the exact buffer size for | ||
71 | * the new configuration. | ||
72 | * stat->update is set to 1 if new configuration is different than | ||
73 | * current one. | ||
74 | */ | ||
75 | void (*set_params)(struct ispstat *stat, void *new_conf); | ||
76 | |||
77 | /* Apply stored configuration. */ | ||
78 | void (*setup_regs)(struct ispstat *stat, void *priv); | ||
79 | |||
80 | /* Enable/Disable module. */ | ||
81 | void (*enable)(struct ispstat *stat, int enable); | ||
82 | |||
83 | /* Verify is module is busy. */ | ||
84 | int (*busy)(struct ispstat *stat); | ||
85 | |||
86 | /* Used for specific operations during generic buf process task. */ | ||
87 | int (*buf_process)(struct ispstat *stat); | ||
88 | }; | ||
89 | |||
90 | enum ispstat_state_t { | ||
91 | ISPSTAT_DISABLED = 0, | ||
92 | ISPSTAT_DISABLING, | ||
93 | ISPSTAT_ENABLED, | ||
94 | ISPSTAT_ENABLING, | ||
95 | ISPSTAT_SUSPENDED, | ||
96 | }; | ||
97 | |||
98 | struct ispstat { | ||
99 | struct v4l2_subdev subdev; | ||
100 | struct media_pad pad; /* sink pad */ | ||
101 | |||
102 | /* Control */ | ||
103 | unsigned configured:1; | ||
104 | unsigned update:1; | ||
105 | unsigned buf_processing:1; | ||
106 | unsigned sbl_ovl_recover:1; | ||
107 | u8 inc_config; | ||
108 | atomic_t buf_err; | ||
109 | enum ispstat_state_t state; /* enabling/disabling state */ | ||
110 | struct omap_dma_channel_params dma_config; | ||
111 | struct isp_device *isp; | ||
112 | void *priv; /* pointer to priv config struct */ | ||
113 | void *recover_priv; /* pointer to recover priv configuration */ | ||
114 | struct mutex ioctl_lock; /* serialize private ioctl */ | ||
115 | |||
116 | const struct ispstat_ops *ops; | ||
117 | |||
118 | /* Buffer */ | ||
119 | u8 wait_acc_frames; | ||
120 | u16 config_counter; | ||
121 | u32 frame_number; | ||
122 | u32 buf_size; | ||
123 | u32 buf_alloc_size; | ||
124 | int dma_ch; | ||
125 | unsigned long event_type; | ||
126 | struct ispstat_buffer *buf; | ||
127 | struct ispstat_buffer *active_buf; | ||
128 | struct ispstat_buffer *locked_buf; | ||
129 | }; | ||
130 | |||
131 | struct ispstat_generic_config { | ||
132 | /* | ||
133 | * Fields must be in the same order as in: | ||
134 | * - omap3isp_h3a_aewb_config | ||
135 | * - omap3isp_h3a_af_config | ||
136 | * - omap3isp_hist_config | ||
137 | */ | ||
138 | u32 buf_size; | ||
139 | u16 config_counter; | ||
140 | }; | ||
141 | |||
142 | int omap3isp_stat_config(struct ispstat *stat, void *new_conf); | ||
143 | int omap3isp_stat_request_statistics(struct ispstat *stat, | ||
144 | struct omap3isp_stat_data *data); | ||
145 | int omap3isp_stat_init(struct ispstat *stat, const char *name, | ||
146 | const struct v4l2_subdev_ops *sd_ops); | ||
147 | void omap3isp_stat_free(struct ispstat *stat); | ||
148 | int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, | ||
149 | struct v4l2_fh *fh, | ||
150 | struct v4l2_event_subscription *sub); | ||
151 | int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, | ||
152 | struct v4l2_fh *fh, | ||
153 | struct v4l2_event_subscription *sub); | ||
154 | int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable); | ||
155 | |||
156 | int omap3isp_stat_busy(struct ispstat *stat); | ||
157 | int omap3isp_stat_pcr_busy(struct ispstat *stat); | ||
158 | void omap3isp_stat_suspend(struct ispstat *stat); | ||
159 | void omap3isp_stat_resume(struct ispstat *stat); | ||
160 | int omap3isp_stat_enable(struct ispstat *stat, u8 enable); | ||
161 | void omap3isp_stat_sbl_overflow(struct ispstat *stat); | ||
162 | void omap3isp_stat_isr(struct ispstat *stat); | ||
163 | void omap3isp_stat_isr_frame_sync(struct ispstat *stat); | ||
164 | void omap3isp_stat_dma_isr(struct ispstat *stat); | ||
165 | int omap3isp_stat_register_entities(struct ispstat *stat, | ||
166 | struct v4l2_device *vdev); | ||
167 | void omap3isp_stat_unregister_entities(struct ispstat *stat); | ||
168 | |||
169 | #endif /* OMAP3_ISP_STAT_H */ | ||
diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c new file mode 100644 index 00000000000..6d1d55b7c72 --- /dev/null +++ b/drivers/media/video/omap3isp/ispvideo.c | |||
@@ -0,0 +1,1334 @@ | |||
1 | /* | ||
2 | * ispvideo.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - Generic video node | ||
5 | * | ||
6 | * Copyright (C) 2009-2010 Nokia Corporation | ||
7 | * | ||
8 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
9 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
23 | * 02110-1301 USA | ||
24 | */ | ||
25 | |||
26 | #include <asm/cacheflush.h> | ||
27 | #include <linux/clk.h> | ||
28 | #include <linux/mm.h> | ||
29 | #include <linux/pagemap.h> | ||
30 | #include <linux/scatterlist.h> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/vmalloc.h> | ||
34 | #include <media/v4l2-dev.h> | ||
35 | #include <media/v4l2-ioctl.h> | ||
36 | #include <plat/iommu.h> | ||
37 | #include <plat/iovmm.h> | ||
38 | #include <plat/omap-pm.h> | ||
39 | |||
40 | #include "ispvideo.h" | ||
41 | #include "isp.h" | ||
42 | |||
43 | |||
44 | /* ----------------------------------------------------------------------------- | ||
45 | * Helper functions | ||
46 | */ | ||
47 | |||
48 | static struct isp_format_info formats[] = { | ||
49 | { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, | ||
50 | V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, | ||
51 | V4L2_PIX_FMT_GREY, 8, }, | ||
52 | { V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10, | ||
53 | V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8, | ||
54 | V4L2_PIX_FMT_Y10, 10, }, | ||
55 | { V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10, | ||
56 | V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8, | ||
57 | V4L2_PIX_FMT_Y12, 12, }, | ||
58 | { V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8, | ||
59 | V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8, | ||
60 | V4L2_PIX_FMT_SBGGR8, 8, }, | ||
61 | { V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8, | ||
62 | V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8, | ||
63 | V4L2_PIX_FMT_SGBRG8, 8, }, | ||
64 | { V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8, | ||
65 | V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8, | ||
66 | V4L2_PIX_FMT_SGRBG8, 8, }, | ||
67 | { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, | ||
68 | V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, | ||
69 | V4L2_PIX_FMT_SRGGB8, 8, }, | ||
70 | { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, | ||
71 | V4L2_MBUS_FMT_SGRBG10_1X10, 0, | ||
72 | V4L2_PIX_FMT_SGRBG10DPCM8, 8, }, | ||
73 | { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, | ||
74 | V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8, | ||
75 | V4L2_PIX_FMT_SBGGR10, 10, }, | ||
76 | { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10, | ||
77 | V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8, | ||
78 | V4L2_PIX_FMT_SGBRG10, 10, }, | ||
79 | { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10, | ||
80 | V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8, | ||
81 | V4L2_PIX_FMT_SGRBG10, 10, }, | ||
82 | { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, | ||
83 | V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8, | ||
84 | V4L2_PIX_FMT_SRGGB10, 10, }, | ||
85 | { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10, | ||
86 | V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8, | ||
87 | V4L2_PIX_FMT_SBGGR12, 12, }, | ||
88 | { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10, | ||
89 | V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8, | ||
90 | V4L2_PIX_FMT_SGBRG12, 12, }, | ||
91 | { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10, | ||
92 | V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8, | ||
93 | V4L2_PIX_FMT_SGRBG12, 12, }, | ||
94 | { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10, | ||
95 | V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8, | ||
96 | V4L2_PIX_FMT_SRGGB12, 12, }, | ||
97 | { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16, | ||
98 | V4L2_MBUS_FMT_UYVY8_1X16, 0, | ||
99 | V4L2_PIX_FMT_UYVY, 16, }, | ||
100 | { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16, | ||
101 | V4L2_MBUS_FMT_YUYV8_1X16, 0, | ||
102 | V4L2_PIX_FMT_YUYV, 16, }, | ||
103 | }; | ||
104 | |||
105 | const struct isp_format_info * | ||
106 | omap3isp_video_format_info(enum v4l2_mbus_pixelcode code) | ||
107 | { | ||
108 | unsigned int i; | ||
109 | |||
110 | for (i = 0; i < ARRAY_SIZE(formats); ++i) { | ||
111 | if (formats[i].code == code) | ||
112 | return &formats[i]; | ||
113 | } | ||
114 | |||
115 | return NULL; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * Decide whether desired output pixel code can be obtained with | ||
120 | * the lane shifter by shifting the input pixel code. | ||
121 | * @in: input pixelcode to shifter | ||
122 | * @out: output pixelcode from shifter | ||
123 | * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0] | ||
124 | * | ||
125 | * return true if the combination is possible | ||
126 | * return false otherwise | ||
127 | */ | ||
128 | static bool isp_video_is_shiftable(enum v4l2_mbus_pixelcode in, | ||
129 | enum v4l2_mbus_pixelcode out, | ||
130 | unsigned int additional_shift) | ||
131 | { | ||
132 | const struct isp_format_info *in_info, *out_info; | ||
133 | |||
134 | if (in == out) | ||
135 | return true; | ||
136 | |||
137 | in_info = omap3isp_video_format_info(in); | ||
138 | out_info = omap3isp_video_format_info(out); | ||
139 | |||
140 | if ((in_info->flavor == 0) || (out_info->flavor == 0)) | ||
141 | return false; | ||
142 | |||
143 | if (in_info->flavor != out_info->flavor) | ||
144 | return false; | ||
145 | |||
146 | return in_info->bpp - out_info->bpp + additional_shift <= 6; | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format | ||
151 | * @video: ISP video instance | ||
152 | * @mbus: v4l2_mbus_framefmt format (input) | ||
153 | * @pix: v4l2_pix_format format (output) | ||
154 | * | ||
155 | * Fill the output pix structure with information from the input mbus format. | ||
156 | * The bytesperline and sizeimage fields are computed from the requested bytes | ||
157 | * per line value in the pix format and information from the video instance. | ||
158 | * | ||
159 | * Return the number of padding bytes at end of line. | ||
160 | */ | ||
161 | static unsigned int isp_video_mbus_to_pix(const struct isp_video *video, | ||
162 | const struct v4l2_mbus_framefmt *mbus, | ||
163 | struct v4l2_pix_format *pix) | ||
164 | { | ||
165 | unsigned int bpl = pix->bytesperline; | ||
166 | unsigned int min_bpl; | ||
167 | unsigned int i; | ||
168 | |||
169 | memset(pix, 0, sizeof(*pix)); | ||
170 | pix->width = mbus->width; | ||
171 | pix->height = mbus->height; | ||
172 | |||
173 | for (i = 0; i < ARRAY_SIZE(formats); ++i) { | ||
174 | if (formats[i].code == mbus->code) | ||
175 | break; | ||
176 | } | ||
177 | |||
178 | if (WARN_ON(i == ARRAY_SIZE(formats))) | ||
179 | return 0; | ||
180 | |||
181 | min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8; | ||
182 | |||
183 | /* Clamp the requested bytes per line value. If the maximum bytes per | ||
184 | * line value is zero, the module doesn't support user configurable line | ||
185 | * sizes. Override the requested value with the minimum in that case. | ||
186 | */ | ||
187 | if (video->bpl_max) | ||
188 | bpl = clamp(bpl, min_bpl, video->bpl_max); | ||
189 | else | ||
190 | bpl = min_bpl; | ||
191 | |||
192 | if (!video->bpl_zero_padding || bpl != min_bpl) | ||
193 | bpl = ALIGN(bpl, video->bpl_alignment); | ||
194 | |||
195 | pix->pixelformat = formats[i].pixelformat; | ||
196 | pix->bytesperline = bpl; | ||
197 | pix->sizeimage = pix->bytesperline * pix->height; | ||
198 | pix->colorspace = mbus->colorspace; | ||
199 | pix->field = mbus->field; | ||
200 | |||
201 | return bpl - min_bpl; | ||
202 | } | ||
203 | |||
204 | static void isp_video_pix_to_mbus(const struct v4l2_pix_format *pix, | ||
205 | struct v4l2_mbus_framefmt *mbus) | ||
206 | { | ||
207 | unsigned int i; | ||
208 | |||
209 | memset(mbus, 0, sizeof(*mbus)); | ||
210 | mbus->width = pix->width; | ||
211 | mbus->height = pix->height; | ||
212 | |||
213 | for (i = 0; i < ARRAY_SIZE(formats); ++i) { | ||
214 | if (formats[i].pixelformat == pix->pixelformat) | ||
215 | break; | ||
216 | } | ||
217 | |||
218 | if (WARN_ON(i == ARRAY_SIZE(formats))) | ||
219 | return; | ||
220 | |||
221 | mbus->code = formats[i].code; | ||
222 | mbus->colorspace = pix->colorspace; | ||
223 | mbus->field = pix->field; | ||
224 | } | ||
225 | |||
226 | static struct v4l2_subdev * | ||
227 | isp_video_remote_subdev(struct isp_video *video, u32 *pad) | ||
228 | { | ||
229 | struct media_pad *remote; | ||
230 | |||
231 | remote = media_entity_remote_source(&video->pad); | ||
232 | |||
233 | if (remote == NULL || | ||
234 | media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
235 | return NULL; | ||
236 | |||
237 | if (pad) | ||
238 | *pad = remote->index; | ||
239 | |||
240 | return media_entity_to_v4l2_subdev(remote->entity); | ||
241 | } | ||
242 | |||
243 | /* Return a pointer to the ISP video instance at the far end of the pipeline. */ | ||
244 | static struct isp_video * | ||
245 | isp_video_far_end(struct isp_video *video) | ||
246 | { | ||
247 | struct media_entity_graph graph; | ||
248 | struct media_entity *entity = &video->video.entity; | ||
249 | struct media_device *mdev = entity->parent; | ||
250 | struct isp_video *far_end = NULL; | ||
251 | |||
252 | mutex_lock(&mdev->graph_mutex); | ||
253 | media_entity_graph_walk_start(&graph, entity); | ||
254 | |||
255 | while ((entity = media_entity_graph_walk_next(&graph))) { | ||
256 | if (entity == &video->video.entity) | ||
257 | continue; | ||
258 | |||
259 | if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) | ||
260 | continue; | ||
261 | |||
262 | far_end = to_isp_video(media_entity_to_video_device(entity)); | ||
263 | if (far_end->type != video->type) | ||
264 | break; | ||
265 | |||
266 | far_end = NULL; | ||
267 | } | ||
268 | |||
269 | mutex_unlock(&mdev->graph_mutex); | ||
270 | return far_end; | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * Validate a pipeline by checking both ends of all links for format | ||
275 | * discrepancies. | ||
276 | * | ||
277 | * Compute the minimum time per frame value as the maximum of time per frame | ||
278 | * limits reported by every block in the pipeline. | ||
279 | * | ||
280 | * Return 0 if all formats match, or -EPIPE if at least one link is found with | ||
281 | * different formats on its two ends. | ||
282 | */ | ||
283 | static int isp_video_validate_pipeline(struct isp_pipeline *pipe) | ||
284 | { | ||
285 | struct isp_device *isp = pipe->output->isp; | ||
286 | struct v4l2_subdev_format fmt_source; | ||
287 | struct v4l2_subdev_format fmt_sink; | ||
288 | struct media_pad *pad; | ||
289 | struct v4l2_subdev *subdev; | ||
290 | int ret; | ||
291 | |||
292 | pipe->max_rate = pipe->l3_ick; | ||
293 | |||
294 | subdev = isp_video_remote_subdev(pipe->output, NULL); | ||
295 | if (subdev == NULL) | ||
296 | return -EPIPE; | ||
297 | |||
298 | while (1) { | ||
299 | unsigned int shifter_link; | ||
300 | /* Retrieve the sink format */ | ||
301 | pad = &subdev->entity.pads[0]; | ||
302 | if (!(pad->flags & MEDIA_PAD_FL_SINK)) | ||
303 | break; | ||
304 | |||
305 | fmt_sink.pad = pad->index; | ||
306 | fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
307 | ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink); | ||
308 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
309 | return -EPIPE; | ||
310 | |||
311 | /* Update the maximum frame rate */ | ||
312 | if (subdev == &isp->isp_res.subdev) | ||
313 | omap3isp_resizer_max_rate(&isp->isp_res, | ||
314 | &pipe->max_rate); | ||
315 | |||
316 | /* Check ccdc maximum data rate when data comes from sensor | ||
317 | * TODO: Include ccdc rate in pipe->max_rate and compare the | ||
318 | * total pipe rate with the input data rate from sensor. | ||
319 | */ | ||
320 | if (subdev == &isp->isp_ccdc.subdev && pipe->input == NULL) { | ||
321 | unsigned int rate = UINT_MAX; | ||
322 | |||
323 | omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); | ||
324 | if (isp->isp_ccdc.vpcfg.pixelclk > rate) | ||
325 | return -ENOSPC; | ||
326 | } | ||
327 | |||
328 | /* If sink pad is on CCDC, the link has the lane shifter | ||
329 | * in the middle of it. */ | ||
330 | shifter_link = subdev == &isp->isp_ccdc.subdev; | ||
331 | |||
332 | /* Retrieve the source format */ | ||
333 | pad = media_entity_remote_source(pad); | ||
334 | if (pad == NULL || | ||
335 | media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
336 | break; | ||
337 | |||
338 | subdev = media_entity_to_v4l2_subdev(pad->entity); | ||
339 | |||
340 | fmt_source.pad = pad->index; | ||
341 | fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
342 | ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); | ||
343 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
344 | return -EPIPE; | ||
345 | |||
346 | /* Check if the two ends match */ | ||
347 | if (fmt_source.format.width != fmt_sink.format.width || | ||
348 | fmt_source.format.height != fmt_sink.format.height) | ||
349 | return -EPIPE; | ||
350 | |||
351 | if (shifter_link) { | ||
352 | unsigned int parallel_shift = 0; | ||
353 | if (isp->isp_ccdc.input == CCDC_INPUT_PARALLEL) { | ||
354 | struct isp_parallel_platform_data *pdata = | ||
355 | &((struct isp_v4l2_subdevs_group *) | ||
356 | subdev->host_priv)->bus.parallel; | ||
357 | parallel_shift = pdata->data_lane_shift * 2; | ||
358 | } | ||
359 | if (!isp_video_is_shiftable(fmt_source.format.code, | ||
360 | fmt_sink.format.code, | ||
361 | parallel_shift)) | ||
362 | return -EPIPE; | ||
363 | } else if (fmt_source.format.code != fmt_sink.format.code) | ||
364 | return -EPIPE; | ||
365 | } | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | static int | ||
371 | __isp_video_get_format(struct isp_video *video, struct v4l2_format *format) | ||
372 | { | ||
373 | struct v4l2_subdev_format fmt; | ||
374 | struct v4l2_subdev *subdev; | ||
375 | u32 pad; | ||
376 | int ret; | ||
377 | |||
378 | subdev = isp_video_remote_subdev(video, &pad); | ||
379 | if (subdev == NULL) | ||
380 | return -EINVAL; | ||
381 | |||
382 | mutex_lock(&video->mutex); | ||
383 | |||
384 | fmt.pad = pad; | ||
385 | fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
386 | ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); | ||
387 | if (ret == -ENOIOCTLCMD) | ||
388 | ret = -EINVAL; | ||
389 | |||
390 | mutex_unlock(&video->mutex); | ||
391 | |||
392 | if (ret) | ||
393 | return ret; | ||
394 | |||
395 | format->type = video->type; | ||
396 | return isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); | ||
397 | } | ||
398 | |||
399 | static int | ||
400 | isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh) | ||
401 | { | ||
402 | struct v4l2_format format; | ||
403 | int ret; | ||
404 | |||
405 | memcpy(&format, &vfh->format, sizeof(format)); | ||
406 | ret = __isp_video_get_format(video, &format); | ||
407 | if (ret < 0) | ||
408 | return ret; | ||
409 | |||
410 | if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat || | ||
411 | vfh->format.fmt.pix.height != format.fmt.pix.height || | ||
412 | vfh->format.fmt.pix.width != format.fmt.pix.width || | ||
413 | vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline || | ||
414 | vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage) | ||
415 | return -EINVAL; | ||
416 | |||
417 | return ret; | ||
418 | } | ||
419 | |||
420 | /* ----------------------------------------------------------------------------- | ||
421 | * IOMMU management | ||
422 | */ | ||
423 | |||
424 | #define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8) | ||
425 | |||
426 | /* | ||
427 | * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list | ||
428 | * @dev: Device pointer specific to the OMAP3 ISP. | ||
429 | * @sglist: Pointer to source Scatter gather list to allocate. | ||
430 | * @sglen: Number of elements of the scatter-gatter list. | ||
431 | * | ||
432 | * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if | ||
433 | * we ran out of memory. | ||
434 | */ | ||
435 | static dma_addr_t | ||
436 | ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen) | ||
437 | { | ||
438 | struct sg_table *sgt; | ||
439 | u32 da; | ||
440 | |||
441 | sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); | ||
442 | if (sgt == NULL) | ||
443 | return -ENOMEM; | ||
444 | |||
445 | sgt->sgl = (struct scatterlist *)sglist; | ||
446 | sgt->nents = sglen; | ||
447 | sgt->orig_nents = sglen; | ||
448 | |||
449 | da = omap_iommu_vmap(isp->domain, isp->dev, 0, sgt, IOMMU_FLAG); | ||
450 | if (IS_ERR_VALUE(da)) | ||
451 | kfree(sgt); | ||
452 | |||
453 | return da; | ||
454 | } | ||
455 | |||
456 | /* | ||
457 | * ispmmu_vunmap - Unmap a device address from the ISP MMU | ||
458 | * @dev: Device pointer specific to the OMAP3 ISP. | ||
459 | * @da: Device address generated from a ispmmu_vmap call. | ||
460 | */ | ||
461 | static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da) | ||
462 | { | ||
463 | struct sg_table *sgt; | ||
464 | |||
465 | sgt = omap_iommu_vunmap(isp->domain, isp->dev, (u32)da); | ||
466 | kfree(sgt); | ||
467 | } | ||
468 | |||
469 | /* ----------------------------------------------------------------------------- | ||
470 | * Video queue operations | ||
471 | */ | ||
472 | |||
473 | static void isp_video_queue_prepare(struct isp_video_queue *queue, | ||
474 | unsigned int *nbuffers, unsigned int *size) | ||
475 | { | ||
476 | struct isp_video_fh *vfh = | ||
477 | container_of(queue, struct isp_video_fh, queue); | ||
478 | struct isp_video *video = vfh->video; | ||
479 | |||
480 | *size = vfh->format.fmt.pix.sizeimage; | ||
481 | if (*size == 0) | ||
482 | return; | ||
483 | |||
484 | *nbuffers = min(*nbuffers, video->capture_mem / PAGE_ALIGN(*size)); | ||
485 | } | ||
486 | |||
487 | static void isp_video_buffer_cleanup(struct isp_video_buffer *buf) | ||
488 | { | ||
489 | struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); | ||
490 | struct isp_buffer *buffer = to_isp_buffer(buf); | ||
491 | struct isp_video *video = vfh->video; | ||
492 | |||
493 | if (buffer->isp_addr) { | ||
494 | ispmmu_vunmap(video->isp, buffer->isp_addr); | ||
495 | buffer->isp_addr = 0; | ||
496 | } | ||
497 | } | ||
498 | |||
499 | static int isp_video_buffer_prepare(struct isp_video_buffer *buf) | ||
500 | { | ||
501 | struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); | ||
502 | struct isp_buffer *buffer = to_isp_buffer(buf); | ||
503 | struct isp_video *video = vfh->video; | ||
504 | unsigned long addr; | ||
505 | |||
506 | addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen); | ||
507 | if (IS_ERR_VALUE(addr)) | ||
508 | return -EIO; | ||
509 | |||
510 | if (!IS_ALIGNED(addr, 32)) { | ||
511 | dev_dbg(video->isp->dev, "Buffer address must be " | ||
512 | "aligned to 32 bytes boundary.\n"); | ||
513 | ispmmu_vunmap(video->isp, buffer->isp_addr); | ||
514 | return -EINVAL; | ||
515 | } | ||
516 | |||
517 | buf->vbuf.bytesused = vfh->format.fmt.pix.sizeimage; | ||
518 | buffer->isp_addr = addr; | ||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | /* | ||
523 | * isp_video_buffer_queue - Add buffer to streaming queue | ||
524 | * @buf: Video buffer | ||
525 | * | ||
526 | * In memory-to-memory mode, start streaming on the pipeline if buffers are | ||
527 | * queued on both the input and the output, if the pipeline isn't already busy. | ||
528 | * If the pipeline is busy, it will be restarted in the output module interrupt | ||
529 | * handler. | ||
530 | */ | ||
531 | static void isp_video_buffer_queue(struct isp_video_buffer *buf) | ||
532 | { | ||
533 | struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); | ||
534 | struct isp_buffer *buffer = to_isp_buffer(buf); | ||
535 | struct isp_video *video = vfh->video; | ||
536 | struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); | ||
537 | enum isp_pipeline_state state; | ||
538 | unsigned long flags; | ||
539 | unsigned int empty; | ||
540 | unsigned int start; | ||
541 | |||
542 | empty = list_empty(&video->dmaqueue); | ||
543 | list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue); | ||
544 | |||
545 | if (empty) { | ||
546 | if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
547 | state = ISP_PIPELINE_QUEUE_OUTPUT; | ||
548 | else | ||
549 | state = ISP_PIPELINE_QUEUE_INPUT; | ||
550 | |||
551 | spin_lock_irqsave(&pipe->lock, flags); | ||
552 | pipe->state |= state; | ||
553 | video->ops->queue(video, buffer); | ||
554 | video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; | ||
555 | |||
556 | start = isp_pipeline_ready(pipe); | ||
557 | if (start) | ||
558 | pipe->state |= ISP_PIPELINE_STREAM; | ||
559 | spin_unlock_irqrestore(&pipe->lock, flags); | ||
560 | |||
561 | if (start) | ||
562 | omap3isp_pipeline_set_stream(pipe, | ||
563 | ISP_PIPELINE_STREAM_SINGLESHOT); | ||
564 | } | ||
565 | } | ||
566 | |||
567 | static const struct isp_video_queue_operations isp_video_queue_ops = { | ||
568 | .queue_prepare = &isp_video_queue_prepare, | ||
569 | .buffer_prepare = &isp_video_buffer_prepare, | ||
570 | .buffer_queue = &isp_video_buffer_queue, | ||
571 | .buffer_cleanup = &isp_video_buffer_cleanup, | ||
572 | }; | ||
573 | |||
574 | /* | ||
575 | * omap3isp_video_buffer_next - Complete the current buffer and return the next | ||
576 | * @video: ISP video object | ||
577 | * @error: Whether an error occurred during capture | ||
578 | * | ||
579 | * Remove the current video buffer from the DMA queue and fill its timestamp, | ||
580 | * field count and state fields before waking up its completion handler. | ||
581 | * | ||
582 | * The buffer state is set to VIDEOBUF_DONE if no error occurred (@error is 0) | ||
583 | * or VIDEOBUF_ERROR otherwise (@error is non-zero). | ||
584 | * | ||
585 | * The DMA queue is expected to contain at least one buffer. | ||
586 | * | ||
587 | * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is | ||
588 | * empty. | ||
589 | */ | ||
590 | struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video, | ||
591 | unsigned int error) | ||
592 | { | ||
593 | struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); | ||
594 | struct isp_video_queue *queue = video->queue; | ||
595 | enum isp_pipeline_state state; | ||
596 | struct isp_video_buffer *buf; | ||
597 | unsigned long flags; | ||
598 | struct timespec ts; | ||
599 | |||
600 | spin_lock_irqsave(&queue->irqlock, flags); | ||
601 | if (WARN_ON(list_empty(&video->dmaqueue))) { | ||
602 | spin_unlock_irqrestore(&queue->irqlock, flags); | ||
603 | return NULL; | ||
604 | } | ||
605 | |||
606 | buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer, | ||
607 | irqlist); | ||
608 | list_del(&buf->irqlist); | ||
609 | spin_unlock_irqrestore(&queue->irqlock, flags); | ||
610 | |||
611 | ktime_get_ts(&ts); | ||
612 | buf->vbuf.timestamp.tv_sec = ts.tv_sec; | ||
613 | buf->vbuf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; | ||
614 | |||
615 | /* Do frame number propagation only if this is the output video node. | ||
616 | * Frame number either comes from the CSI receivers or it gets | ||
617 | * incremented here if H3A is not active. | ||
618 | * Note: There is no guarantee that the output buffer will finish | ||
619 | * first, so the input number might lag behind by 1 in some cases. | ||
620 | */ | ||
621 | if (video == pipe->output && !pipe->do_propagation) | ||
622 | buf->vbuf.sequence = atomic_inc_return(&pipe->frame_number); | ||
623 | else | ||
624 | buf->vbuf.sequence = atomic_read(&pipe->frame_number); | ||
625 | |||
626 | buf->state = error ? ISP_BUF_STATE_ERROR : ISP_BUF_STATE_DONE; | ||
627 | |||
628 | wake_up(&buf->wait); | ||
629 | |||
630 | if (list_empty(&video->dmaqueue)) { | ||
631 | if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
632 | state = ISP_PIPELINE_QUEUE_OUTPUT | ||
633 | | ISP_PIPELINE_STREAM; | ||
634 | else | ||
635 | state = ISP_PIPELINE_QUEUE_INPUT | ||
636 | | ISP_PIPELINE_STREAM; | ||
637 | |||
638 | spin_lock_irqsave(&pipe->lock, flags); | ||
639 | pipe->state &= ~state; | ||
640 | if (video->pipe.stream_state == ISP_PIPELINE_STREAM_CONTINUOUS) | ||
641 | video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; | ||
642 | spin_unlock_irqrestore(&pipe->lock, flags); | ||
643 | return NULL; | ||
644 | } | ||
645 | |||
646 | if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { | ||
647 | spin_lock_irqsave(&pipe->lock, flags); | ||
648 | pipe->state &= ~ISP_PIPELINE_STREAM; | ||
649 | spin_unlock_irqrestore(&pipe->lock, flags); | ||
650 | } | ||
651 | |||
652 | buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer, | ||
653 | irqlist); | ||
654 | buf->state = ISP_BUF_STATE_ACTIVE; | ||
655 | return to_isp_buffer(buf); | ||
656 | } | ||
657 | |||
658 | /* | ||
659 | * omap3isp_video_resume - Perform resume operation on the buffers | ||
660 | * @video: ISP video object | ||
661 | * @continuous: Pipeline is in single shot mode if 0 or continuous mode otherwise | ||
662 | * | ||
663 | * This function is intended to be used on suspend/resume scenario. It | ||
664 | * requests video queue layer to discard buffers marked as DONE if it's in | ||
665 | * continuous mode and requests ISP modules to queue again the ACTIVE buffer | ||
666 | * if there's any. | ||
667 | */ | ||
668 | void omap3isp_video_resume(struct isp_video *video, int continuous) | ||
669 | { | ||
670 | struct isp_buffer *buf = NULL; | ||
671 | |||
672 | if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
673 | omap3isp_video_queue_discard_done(video->queue); | ||
674 | |||
675 | if (!list_empty(&video->dmaqueue)) { | ||
676 | buf = list_first_entry(&video->dmaqueue, | ||
677 | struct isp_buffer, buffer.irqlist); | ||
678 | video->ops->queue(video, buf); | ||
679 | video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; | ||
680 | } else { | ||
681 | if (continuous) | ||
682 | video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; | ||
683 | } | ||
684 | } | ||
685 | |||
686 | /* ----------------------------------------------------------------------------- | ||
687 | * V4L2 ioctls | ||
688 | */ | ||
689 | |||
690 | static int | ||
691 | isp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) | ||
692 | { | ||
693 | struct isp_video *video = video_drvdata(file); | ||
694 | |||
695 | strlcpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver)); | ||
696 | strlcpy(cap->card, video->video.name, sizeof(cap->card)); | ||
697 | strlcpy(cap->bus_info, "media", sizeof(cap->bus_info)); | ||
698 | |||
699 | if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
700 | cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; | ||
701 | else | ||
702 | cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; | ||
703 | |||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | static int | ||
708 | isp_video_get_format(struct file *file, void *fh, struct v4l2_format *format) | ||
709 | { | ||
710 | struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
711 | struct isp_video *video = video_drvdata(file); | ||
712 | |||
713 | if (format->type != video->type) | ||
714 | return -EINVAL; | ||
715 | |||
716 | mutex_lock(&video->mutex); | ||
717 | *format = vfh->format; | ||
718 | mutex_unlock(&video->mutex); | ||
719 | |||
720 | return 0; | ||
721 | } | ||
722 | |||
723 | static int | ||
724 | isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) | ||
725 | { | ||
726 | struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
727 | struct isp_video *video = video_drvdata(file); | ||
728 | struct v4l2_mbus_framefmt fmt; | ||
729 | |||
730 | if (format->type != video->type) | ||
731 | return -EINVAL; | ||
732 | |||
733 | mutex_lock(&video->mutex); | ||
734 | |||
735 | /* Fill the bytesperline and sizeimage fields by converting to media bus | ||
736 | * format and back to pixel format. | ||
737 | */ | ||
738 | isp_video_pix_to_mbus(&format->fmt.pix, &fmt); | ||
739 | isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix); | ||
740 | |||
741 | vfh->format = *format; | ||
742 | |||
743 | mutex_unlock(&video->mutex); | ||
744 | return 0; | ||
745 | } | ||
746 | |||
747 | static int | ||
748 | isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) | ||
749 | { | ||
750 | struct isp_video *video = video_drvdata(file); | ||
751 | struct v4l2_subdev_format fmt; | ||
752 | struct v4l2_subdev *subdev; | ||
753 | u32 pad; | ||
754 | int ret; | ||
755 | |||
756 | if (format->type != video->type) | ||
757 | return -EINVAL; | ||
758 | |||
759 | subdev = isp_video_remote_subdev(video, &pad); | ||
760 | if (subdev == NULL) | ||
761 | return -EINVAL; | ||
762 | |||
763 | isp_video_pix_to_mbus(&format->fmt.pix, &fmt.format); | ||
764 | |||
765 | fmt.pad = pad; | ||
766 | fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
767 | ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); | ||
768 | if (ret) | ||
769 | return ret == -ENOIOCTLCMD ? -EINVAL : ret; | ||
770 | |||
771 | isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); | ||
772 | return 0; | ||
773 | } | ||
774 | |||
775 | static int | ||
776 | isp_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) | ||
777 | { | ||
778 | struct isp_video *video = video_drvdata(file); | ||
779 | struct v4l2_subdev *subdev; | ||
780 | int ret; | ||
781 | |||
782 | subdev = isp_video_remote_subdev(video, NULL); | ||
783 | if (subdev == NULL) | ||
784 | return -EINVAL; | ||
785 | |||
786 | mutex_lock(&video->mutex); | ||
787 | ret = v4l2_subdev_call(subdev, video, cropcap, cropcap); | ||
788 | mutex_unlock(&video->mutex); | ||
789 | |||
790 | return ret == -ENOIOCTLCMD ? -EINVAL : ret; | ||
791 | } | ||
792 | |||
793 | static int | ||
794 | isp_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop) | ||
795 | { | ||
796 | struct isp_video *video = video_drvdata(file); | ||
797 | struct v4l2_subdev_format format; | ||
798 | struct v4l2_subdev *subdev; | ||
799 | u32 pad; | ||
800 | int ret; | ||
801 | |||
802 | subdev = isp_video_remote_subdev(video, &pad); | ||
803 | if (subdev == NULL) | ||
804 | return -EINVAL; | ||
805 | |||
806 | /* Try the get crop operation first and fallback to get format if not | ||
807 | * implemented. | ||
808 | */ | ||
809 | ret = v4l2_subdev_call(subdev, video, g_crop, crop); | ||
810 | if (ret != -ENOIOCTLCMD) | ||
811 | return ret; | ||
812 | |||
813 | format.pad = pad; | ||
814 | format.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
815 | ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); | ||
816 | if (ret < 0) | ||
817 | return ret == -ENOIOCTLCMD ? -EINVAL : ret; | ||
818 | |||
819 | crop->c.left = 0; | ||
820 | crop->c.top = 0; | ||
821 | crop->c.width = format.format.width; | ||
822 | crop->c.height = format.format.height; | ||
823 | |||
824 | return 0; | ||
825 | } | ||
826 | |||
827 | static int | ||
828 | isp_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop) | ||
829 | { | ||
830 | struct isp_video *video = video_drvdata(file); | ||
831 | struct v4l2_subdev *subdev; | ||
832 | int ret; | ||
833 | |||
834 | subdev = isp_video_remote_subdev(video, NULL); | ||
835 | if (subdev == NULL) | ||
836 | return -EINVAL; | ||
837 | |||
838 | mutex_lock(&video->mutex); | ||
839 | ret = v4l2_subdev_call(subdev, video, s_crop, crop); | ||
840 | mutex_unlock(&video->mutex); | ||
841 | |||
842 | return ret == -ENOIOCTLCMD ? -EINVAL : ret; | ||
843 | } | ||
844 | |||
845 | static int | ||
846 | isp_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a) | ||
847 | { | ||
848 | struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
849 | struct isp_video *video = video_drvdata(file); | ||
850 | |||
851 | if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || | ||
852 | video->type != a->type) | ||
853 | return -EINVAL; | ||
854 | |||
855 | memset(a, 0, sizeof(*a)); | ||
856 | a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
857 | a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; | ||
858 | a->parm.output.timeperframe = vfh->timeperframe; | ||
859 | |||
860 | return 0; | ||
861 | } | ||
862 | |||
863 | static int | ||
864 | isp_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a) | ||
865 | { | ||
866 | struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
867 | struct isp_video *video = video_drvdata(file); | ||
868 | |||
869 | if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || | ||
870 | video->type != a->type) | ||
871 | return -EINVAL; | ||
872 | |||
873 | if (a->parm.output.timeperframe.denominator == 0) | ||
874 | a->parm.output.timeperframe.denominator = 1; | ||
875 | |||
876 | vfh->timeperframe = a->parm.output.timeperframe; | ||
877 | |||
878 | return 0; | ||
879 | } | ||
880 | |||
881 | static int | ||
882 | isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) | ||
883 | { | ||
884 | struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
885 | |||
886 | return omap3isp_video_queue_reqbufs(&vfh->queue, rb); | ||
887 | } | ||
888 | |||
889 | static int | ||
890 | isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) | ||
891 | { | ||
892 | struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
893 | |||
894 | return omap3isp_video_queue_querybuf(&vfh->queue, b); | ||
895 | } | ||
896 | |||
897 | static int | ||
898 | isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) | ||
899 | { | ||
900 | struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
901 | |||
902 | return omap3isp_video_queue_qbuf(&vfh->queue, b); | ||
903 | } | ||
904 | |||
905 | static int | ||
906 | isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) | ||
907 | { | ||
908 | struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
909 | |||
910 | return omap3isp_video_queue_dqbuf(&vfh->queue, b, | ||
911 | file->f_flags & O_NONBLOCK); | ||
912 | } | ||
913 | |||
914 | /* | ||
915 | * Stream management | ||
916 | * | ||
917 | * Every ISP pipeline has a single input and a single output. The input can be | ||
918 | * either a sensor or a video node. The output is always a video node. | ||
919 | * | ||
920 | * As every pipeline has an output video node, the ISP video objects at the | ||
921 | * pipeline output stores the pipeline state. It tracks the streaming state of | ||
922 | * both the input and output, as well as the availability of buffers. | ||
923 | * | ||
924 | * In sensor-to-memory mode, frames are always available at the pipeline input. | ||
925 | * Starting the sensor usually requires I2C transfers and must be done in | ||
926 | * interruptible context. The pipeline is started and stopped synchronously | ||
927 | * to the stream on/off commands. All modules in the pipeline will get their | ||
928 | * subdev set stream handler called. The module at the end of the pipeline must | ||
929 | * delay starting the hardware until buffers are available at its output. | ||
930 | * | ||
931 | * In memory-to-memory mode, starting/stopping the stream requires | ||
932 | * synchronization between the input and output. ISP modules can't be stopped | ||
933 | * in the middle of a frame, and at least some of the modules seem to become | ||
934 | * busy as soon as they're started, even if they don't receive a frame start | ||
935 | * event. For that reason frames need to be processed in single-shot mode. The | ||
936 | * driver needs to wait until a frame is completely processed and written to | ||
937 | * memory before restarting the pipeline for the next frame. Pipelined | ||
938 | * processing might be possible but requires more testing. | ||
939 | * | ||
940 | * Stream start must be delayed until buffers are available at both the input | ||
941 | * and output. The pipeline must be started in the videobuf queue callback with | ||
942 | * the buffers queue spinlock held. The modules subdev set stream operation must | ||
943 | * not sleep. | ||
944 | */ | ||
945 | static int | ||
946 | isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) | ||
947 | { | ||
948 | struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
949 | struct isp_video *video = video_drvdata(file); | ||
950 | enum isp_pipeline_state state; | ||
951 | struct isp_pipeline *pipe; | ||
952 | struct isp_video *far_end; | ||
953 | unsigned long flags; | ||
954 | int ret; | ||
955 | |||
956 | if (type != video->type) | ||
957 | return -EINVAL; | ||
958 | |||
959 | mutex_lock(&video->stream_lock); | ||
960 | |||
961 | if (video->streaming) { | ||
962 | mutex_unlock(&video->stream_lock); | ||
963 | return -EBUSY; | ||
964 | } | ||
965 | |||
966 | /* Start streaming on the pipeline. No link touching an entity in the | ||
967 | * pipeline can be activated or deactivated once streaming is started. | ||
968 | */ | ||
969 | pipe = video->video.entity.pipe | ||
970 | ? to_isp_pipeline(&video->video.entity) : &video->pipe; | ||
971 | media_entity_pipeline_start(&video->video.entity, &pipe->pipe); | ||
972 | |||
973 | /* Verify that the currently configured format matches the output of | ||
974 | * the connected subdev. | ||
975 | */ | ||
976 | ret = isp_video_check_format(video, vfh); | ||
977 | if (ret < 0) | ||
978 | goto error; | ||
979 | |||
980 | video->bpl_padding = ret; | ||
981 | video->bpl_value = vfh->format.fmt.pix.bytesperline; | ||
982 | |||
983 | /* Find the ISP video node connected at the far end of the pipeline and | ||
984 | * update the pipeline. | ||
985 | */ | ||
986 | far_end = isp_video_far_end(video); | ||
987 | |||
988 | if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { | ||
989 | state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT; | ||
990 | pipe->input = far_end; | ||
991 | pipe->output = video; | ||
992 | } else { | ||
993 | if (far_end == NULL) { | ||
994 | ret = -EPIPE; | ||
995 | goto error; | ||
996 | } | ||
997 | |||
998 | state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT; | ||
999 | pipe->input = video; | ||
1000 | pipe->output = far_end; | ||
1001 | } | ||
1002 | |||
1003 | if (video->isp->pdata->set_constraints) | ||
1004 | video->isp->pdata->set_constraints(video->isp, true); | ||
1005 | pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); | ||
1006 | |||
1007 | /* Validate the pipeline and update its state. */ | ||
1008 | ret = isp_video_validate_pipeline(pipe); | ||
1009 | if (ret < 0) | ||
1010 | goto error; | ||
1011 | |||
1012 | spin_lock_irqsave(&pipe->lock, flags); | ||
1013 | pipe->state &= ~ISP_PIPELINE_STREAM; | ||
1014 | pipe->state |= state; | ||
1015 | spin_unlock_irqrestore(&pipe->lock, flags); | ||
1016 | |||
1017 | /* Set the maximum time per frame as the value requested by userspace. | ||
1018 | * This is a soft limit that can be overridden if the hardware doesn't | ||
1019 | * support the request limit. | ||
1020 | */ | ||
1021 | if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
1022 | pipe->max_timeperframe = vfh->timeperframe; | ||
1023 | |||
1024 | video->queue = &vfh->queue; | ||
1025 | INIT_LIST_HEAD(&video->dmaqueue); | ||
1026 | atomic_set(&pipe->frame_number, -1); | ||
1027 | |||
1028 | ret = omap3isp_video_queue_streamon(&vfh->queue); | ||
1029 | if (ret < 0) | ||
1030 | goto error; | ||
1031 | |||
1032 | /* In sensor-to-memory mode, the stream can be started synchronously | ||
1033 | * to the stream on command. In memory-to-memory mode, it will be | ||
1034 | * started when buffers are queued on both the input and output. | ||
1035 | */ | ||
1036 | if (pipe->input == NULL) { | ||
1037 | ret = omap3isp_pipeline_set_stream(pipe, | ||
1038 | ISP_PIPELINE_STREAM_CONTINUOUS); | ||
1039 | if (ret < 0) | ||
1040 | goto error; | ||
1041 | spin_lock_irqsave(&video->queue->irqlock, flags); | ||
1042 | if (list_empty(&video->dmaqueue)) | ||
1043 | video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; | ||
1044 | spin_unlock_irqrestore(&video->queue->irqlock, flags); | ||
1045 | } | ||
1046 | |||
1047 | error: | ||
1048 | if (ret < 0) { | ||
1049 | omap3isp_video_queue_streamoff(&vfh->queue); | ||
1050 | if (video->isp->pdata->set_constraints) | ||
1051 | video->isp->pdata->set_constraints(video->isp, false); | ||
1052 | media_entity_pipeline_stop(&video->video.entity); | ||
1053 | video->queue = NULL; | ||
1054 | } | ||
1055 | |||
1056 | if (!ret) | ||
1057 | video->streaming = 1; | ||
1058 | |||
1059 | mutex_unlock(&video->stream_lock); | ||
1060 | return ret; | ||
1061 | } | ||
1062 | |||
1063 | static int | ||
1064 | isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) | ||
1065 | { | ||
1066 | struct isp_video_fh *vfh = to_isp_video_fh(fh); | ||
1067 | struct isp_video *video = video_drvdata(file); | ||
1068 | struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); | ||
1069 | enum isp_pipeline_state state; | ||
1070 | unsigned int streaming; | ||
1071 | unsigned long flags; | ||
1072 | |||
1073 | if (type != video->type) | ||
1074 | return -EINVAL; | ||
1075 | |||
1076 | mutex_lock(&video->stream_lock); | ||
1077 | |||
1078 | /* Make sure we're not streaming yet. */ | ||
1079 | mutex_lock(&vfh->queue.lock); | ||
1080 | streaming = vfh->queue.streaming; | ||
1081 | mutex_unlock(&vfh->queue.lock); | ||
1082 | |||
1083 | if (!streaming) | ||
1084 | goto done; | ||
1085 | |||
1086 | /* Update the pipeline state. */ | ||
1087 | if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1088 | state = ISP_PIPELINE_STREAM_OUTPUT | ||
1089 | | ISP_PIPELINE_QUEUE_OUTPUT; | ||
1090 | else | ||
1091 | state = ISP_PIPELINE_STREAM_INPUT | ||
1092 | | ISP_PIPELINE_QUEUE_INPUT; | ||
1093 | |||
1094 | spin_lock_irqsave(&pipe->lock, flags); | ||
1095 | pipe->state &= ~state; | ||
1096 | spin_unlock_irqrestore(&pipe->lock, flags); | ||
1097 | |||
1098 | /* Stop the stream. */ | ||
1099 | omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED); | ||
1100 | omap3isp_video_queue_streamoff(&vfh->queue); | ||
1101 | video->queue = NULL; | ||
1102 | video->streaming = 0; | ||
1103 | |||
1104 | if (video->isp->pdata->set_constraints) | ||
1105 | video->isp->pdata->set_constraints(video->isp, false); | ||
1106 | media_entity_pipeline_stop(&video->video.entity); | ||
1107 | |||
1108 | done: | ||
1109 | mutex_unlock(&video->stream_lock); | ||
1110 | return 0; | ||
1111 | } | ||
1112 | |||
1113 | static int | ||
1114 | isp_video_enum_input(struct file *file, void *fh, struct v4l2_input *input) | ||
1115 | { | ||
1116 | if (input->index > 0) | ||
1117 | return -EINVAL; | ||
1118 | |||
1119 | strlcpy(input->name, "camera", sizeof(input->name)); | ||
1120 | input->type = V4L2_INPUT_TYPE_CAMERA; | ||
1121 | |||
1122 | return 0; | ||
1123 | } | ||
1124 | |||
1125 | static int | ||
1126 | isp_video_g_input(struct file *file, void *fh, unsigned int *input) | ||
1127 | { | ||
1128 | *input = 0; | ||
1129 | |||
1130 | return 0; | ||
1131 | } | ||
1132 | |||
1133 | static int | ||
1134 | isp_video_s_input(struct file *file, void *fh, unsigned int input) | ||
1135 | { | ||
1136 | return input == 0 ? 0 : -EINVAL; | ||
1137 | } | ||
1138 | |||
1139 | static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { | ||
1140 | .vidioc_querycap = isp_video_querycap, | ||
1141 | .vidioc_g_fmt_vid_cap = isp_video_get_format, | ||
1142 | .vidioc_s_fmt_vid_cap = isp_video_set_format, | ||
1143 | .vidioc_try_fmt_vid_cap = isp_video_try_format, | ||
1144 | .vidioc_g_fmt_vid_out = isp_video_get_format, | ||
1145 | .vidioc_s_fmt_vid_out = isp_video_set_format, | ||
1146 | .vidioc_try_fmt_vid_out = isp_video_try_format, | ||
1147 | .vidioc_cropcap = isp_video_cropcap, | ||
1148 | .vidioc_g_crop = isp_video_get_crop, | ||
1149 | .vidioc_s_crop = isp_video_set_crop, | ||
1150 | .vidioc_g_parm = isp_video_get_param, | ||
1151 | .vidioc_s_parm = isp_video_set_param, | ||
1152 | .vidioc_reqbufs = isp_video_reqbufs, | ||
1153 | .vidioc_querybuf = isp_video_querybuf, | ||
1154 | .vidioc_qbuf = isp_video_qbuf, | ||
1155 | .vidioc_dqbuf = isp_video_dqbuf, | ||
1156 | .vidioc_streamon = isp_video_streamon, | ||
1157 | .vidioc_streamoff = isp_video_streamoff, | ||
1158 | .vidioc_enum_input = isp_video_enum_input, | ||
1159 | .vidioc_g_input = isp_video_g_input, | ||
1160 | .vidioc_s_input = isp_video_s_input, | ||
1161 | }; | ||
1162 | |||
1163 | /* ----------------------------------------------------------------------------- | ||
1164 | * V4L2 file operations | ||
1165 | */ | ||
1166 | |||
1167 | static int isp_video_open(struct file *file) | ||
1168 | { | ||
1169 | struct isp_video *video = video_drvdata(file); | ||
1170 | struct isp_video_fh *handle; | ||
1171 | int ret = 0; | ||
1172 | |||
1173 | handle = kzalloc(sizeof(*handle), GFP_KERNEL); | ||
1174 | if (handle == NULL) | ||
1175 | return -ENOMEM; | ||
1176 | |||
1177 | v4l2_fh_init(&handle->vfh, &video->video); | ||
1178 | v4l2_fh_add(&handle->vfh); | ||
1179 | |||
1180 | /* If this is the first user, initialise the pipeline. */ | ||
1181 | if (omap3isp_get(video->isp) == NULL) { | ||
1182 | ret = -EBUSY; | ||
1183 | goto done; | ||
1184 | } | ||
1185 | |||
1186 | ret = omap3isp_pipeline_pm_use(&video->video.entity, 1); | ||
1187 | if (ret < 0) { | ||
1188 | omap3isp_put(video->isp); | ||
1189 | goto done; | ||
1190 | } | ||
1191 | |||
1192 | omap3isp_video_queue_init(&handle->queue, video->type, | ||
1193 | &isp_video_queue_ops, video->isp->dev, | ||
1194 | sizeof(struct isp_buffer)); | ||
1195 | |||
1196 | memset(&handle->format, 0, sizeof(handle->format)); | ||
1197 | handle->format.type = video->type; | ||
1198 | handle->timeperframe.denominator = 1; | ||
1199 | |||
1200 | handle->video = video; | ||
1201 | file->private_data = &handle->vfh; | ||
1202 | |||
1203 | done: | ||
1204 | if (ret < 0) { | ||
1205 | v4l2_fh_del(&handle->vfh); | ||
1206 | kfree(handle); | ||
1207 | } | ||
1208 | |||
1209 | return ret; | ||
1210 | } | ||
1211 | |||
1212 | static int isp_video_release(struct file *file) | ||
1213 | { | ||
1214 | struct isp_video *video = video_drvdata(file); | ||
1215 | struct v4l2_fh *vfh = file->private_data; | ||
1216 | struct isp_video_fh *handle = to_isp_video_fh(vfh); | ||
1217 | |||
1218 | /* Disable streaming and free the buffers queue resources. */ | ||
1219 | isp_video_streamoff(file, vfh, video->type); | ||
1220 | |||
1221 | mutex_lock(&handle->queue.lock); | ||
1222 | omap3isp_video_queue_cleanup(&handle->queue); | ||
1223 | mutex_unlock(&handle->queue.lock); | ||
1224 | |||
1225 | omap3isp_pipeline_pm_use(&video->video.entity, 0); | ||
1226 | |||
1227 | /* Release the file handle. */ | ||
1228 | v4l2_fh_del(vfh); | ||
1229 | kfree(handle); | ||
1230 | file->private_data = NULL; | ||
1231 | |||
1232 | omap3isp_put(video->isp); | ||
1233 | |||
1234 | return 0; | ||
1235 | } | ||
1236 | |||
1237 | static unsigned int isp_video_poll(struct file *file, poll_table *wait) | ||
1238 | { | ||
1239 | struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); | ||
1240 | struct isp_video_queue *queue = &vfh->queue; | ||
1241 | |||
1242 | return omap3isp_video_queue_poll(queue, file, wait); | ||
1243 | } | ||
1244 | |||
1245 | static int isp_video_mmap(struct file *file, struct vm_area_struct *vma) | ||
1246 | { | ||
1247 | struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); | ||
1248 | |||
1249 | return omap3isp_video_queue_mmap(&vfh->queue, vma); | ||
1250 | } | ||
1251 | |||
1252 | static struct v4l2_file_operations isp_video_fops = { | ||
1253 | .owner = THIS_MODULE, | ||
1254 | .unlocked_ioctl = video_ioctl2, | ||
1255 | .open = isp_video_open, | ||
1256 | .release = isp_video_release, | ||
1257 | .poll = isp_video_poll, | ||
1258 | .mmap = isp_video_mmap, | ||
1259 | }; | ||
1260 | |||
1261 | /* ----------------------------------------------------------------------------- | ||
1262 | * ISP video core | ||
1263 | */ | ||
1264 | |||
1265 | static const struct isp_video_operations isp_video_dummy_ops = { | ||
1266 | }; | ||
1267 | |||
1268 | int omap3isp_video_init(struct isp_video *video, const char *name) | ||
1269 | { | ||
1270 | const char *direction; | ||
1271 | int ret; | ||
1272 | |||
1273 | switch (video->type) { | ||
1274 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
1275 | direction = "output"; | ||
1276 | video->pad.flags = MEDIA_PAD_FL_SINK; | ||
1277 | break; | ||
1278 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: | ||
1279 | direction = "input"; | ||
1280 | video->pad.flags = MEDIA_PAD_FL_SOURCE; | ||
1281 | break; | ||
1282 | |||
1283 | default: | ||
1284 | return -EINVAL; | ||
1285 | } | ||
1286 | |||
1287 | ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); | ||
1288 | if (ret < 0) | ||
1289 | return ret; | ||
1290 | |||
1291 | mutex_init(&video->mutex); | ||
1292 | atomic_set(&video->active, 0); | ||
1293 | |||
1294 | spin_lock_init(&video->pipe.lock); | ||
1295 | mutex_init(&video->stream_lock); | ||
1296 | |||
1297 | /* Initialize the video device. */ | ||
1298 | if (video->ops == NULL) | ||
1299 | video->ops = &isp_video_dummy_ops; | ||
1300 | |||
1301 | video->video.fops = &isp_video_fops; | ||
1302 | snprintf(video->video.name, sizeof(video->video.name), | ||
1303 | "OMAP3 ISP %s %s", name, direction); | ||
1304 | video->video.vfl_type = VFL_TYPE_GRABBER; | ||
1305 | video->video.release = video_device_release_empty; | ||
1306 | video->video.ioctl_ops = &isp_video_ioctl_ops; | ||
1307 | video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED; | ||
1308 | |||
1309 | video_set_drvdata(&video->video, video); | ||
1310 | |||
1311 | return 0; | ||
1312 | } | ||
1313 | |||
1314 | int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) | ||
1315 | { | ||
1316 | int ret; | ||
1317 | |||
1318 | video->video.v4l2_dev = vdev; | ||
1319 | |||
1320 | ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); | ||
1321 | if (ret < 0) | ||
1322 | printk(KERN_ERR "%s: could not register video device (%d)\n", | ||
1323 | __func__, ret); | ||
1324 | |||
1325 | return ret; | ||
1326 | } | ||
1327 | |||
1328 | void omap3isp_video_unregister(struct isp_video *video) | ||
1329 | { | ||
1330 | if (video_is_registered(&video->video)) { | ||
1331 | media_entity_cleanup(&video->video.entity); | ||
1332 | video_unregister_device(&video->video); | ||
1333 | } | ||
1334 | } | ||
diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h new file mode 100644 index 00000000000..53160aa24e6 --- /dev/null +++ b/drivers/media/video/omap3isp/ispvideo.h | |||
@@ -0,0 +1,204 @@ | |||
1 | /* | ||
2 | * ispvideo.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - Generic video node | ||
5 | * | ||
6 | * Copyright (C) 2009-2010 Nokia Corporation | ||
7 | * | ||
8 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
9 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
23 | * 02110-1301 USA | ||
24 | */ | ||
25 | |||
26 | #ifndef OMAP3_ISP_VIDEO_H | ||
27 | #define OMAP3_ISP_VIDEO_H | ||
28 | |||
29 | #include <linux/v4l2-mediabus.h> | ||
30 | #include <media/media-entity.h> | ||
31 | #include <media/v4l2-dev.h> | ||
32 | #include <media/v4l2-fh.h> | ||
33 | |||
34 | #include "ispqueue.h" | ||
35 | |||
36 | #define ISP_VIDEO_DRIVER_NAME "ispvideo" | ||
37 | #define ISP_VIDEO_DRIVER_VERSION "0.0.2" | ||
38 | |||
39 | struct isp_device; | ||
40 | struct isp_video; | ||
41 | struct v4l2_mbus_framefmt; | ||
42 | struct v4l2_pix_format; | ||
43 | |||
44 | /* | ||
45 | * struct isp_format_info - ISP media bus format information | ||
46 | * @code: V4L2 media bus format code | ||
47 | * @truncated: V4L2 media bus format code for the same format truncated to 10 | ||
48 | * bits. Identical to @code if the format is 10 bits wide or less. | ||
49 | * @uncompressed: V4L2 media bus format code for the corresponding uncompressed | ||
50 | * format. Identical to @code if the format is not DPCM compressed. | ||
51 | * @flavor: V4L2 media bus format code for the same pixel layout but | ||
52 | * shifted to be 8 bits per pixel. =0 if format is not shiftable. | ||
53 | * @pixelformat: V4L2 pixel format FCC identifier | ||
54 | * @bpp: Bits per pixel | ||
55 | */ | ||
56 | struct isp_format_info { | ||
57 | enum v4l2_mbus_pixelcode code; | ||
58 | enum v4l2_mbus_pixelcode truncated; | ||
59 | enum v4l2_mbus_pixelcode uncompressed; | ||
60 | enum v4l2_mbus_pixelcode flavor; | ||
61 | u32 pixelformat; | ||
62 | unsigned int bpp; | ||
63 | }; | ||
64 | |||
65 | enum isp_pipeline_stream_state { | ||
66 | ISP_PIPELINE_STREAM_STOPPED = 0, | ||
67 | ISP_PIPELINE_STREAM_CONTINUOUS = 1, | ||
68 | ISP_PIPELINE_STREAM_SINGLESHOT = 2, | ||
69 | }; | ||
70 | |||
71 | enum isp_pipeline_state { | ||
72 | /* The stream has been started on the input video node. */ | ||
73 | ISP_PIPELINE_STREAM_INPUT = 1, | ||
74 | /* The stream has been started on the output video node. */ | ||
75 | ISP_PIPELINE_STREAM_OUTPUT = 2, | ||
76 | /* At least one buffer is queued on the input video node. */ | ||
77 | ISP_PIPELINE_QUEUE_INPUT = 4, | ||
78 | /* At least one buffer is queued on the output video node. */ | ||
79 | ISP_PIPELINE_QUEUE_OUTPUT = 8, | ||
80 | /* The input entity is idle, ready to be started. */ | ||
81 | ISP_PIPELINE_IDLE_INPUT = 16, | ||
82 | /* The output entity is idle, ready to be started. */ | ||
83 | ISP_PIPELINE_IDLE_OUTPUT = 32, | ||
84 | /* The pipeline is currently streaming. */ | ||
85 | ISP_PIPELINE_STREAM = 64, | ||
86 | }; | ||
87 | |||
88 | struct isp_pipeline { | ||
89 | struct media_pipeline pipe; | ||
90 | spinlock_t lock; /* Pipeline state and queue flags */ | ||
91 | unsigned int state; | ||
92 | enum isp_pipeline_stream_state stream_state; | ||
93 | struct isp_video *input; | ||
94 | struct isp_video *output; | ||
95 | unsigned long l3_ick; | ||
96 | unsigned int max_rate; | ||
97 | atomic_t frame_number; | ||
98 | bool do_propagation; /* of frame number */ | ||
99 | struct v4l2_fract max_timeperframe; | ||
100 | }; | ||
101 | |||
102 | #define to_isp_pipeline(__e) \ | ||
103 | container_of((__e)->pipe, struct isp_pipeline, pipe) | ||
104 | |||
105 | static inline int isp_pipeline_ready(struct isp_pipeline *pipe) | ||
106 | { | ||
107 | return pipe->state == (ISP_PIPELINE_STREAM_INPUT | | ||
108 | ISP_PIPELINE_STREAM_OUTPUT | | ||
109 | ISP_PIPELINE_QUEUE_INPUT | | ||
110 | ISP_PIPELINE_QUEUE_OUTPUT | | ||
111 | ISP_PIPELINE_IDLE_INPUT | | ||
112 | ISP_PIPELINE_IDLE_OUTPUT); | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * struct isp_buffer - ISP buffer | ||
117 | * @buffer: ISP video buffer | ||
118 | * @isp_addr: MMU mapped address (a.k.a. device address) of the buffer. | ||
119 | */ | ||
120 | struct isp_buffer { | ||
121 | struct isp_video_buffer buffer; | ||
122 | dma_addr_t isp_addr; | ||
123 | }; | ||
124 | |||
125 | #define to_isp_buffer(buf) container_of(buf, struct isp_buffer, buffer) | ||
126 | |||
127 | enum isp_video_dmaqueue_flags { | ||
128 | /* Set if DMA queue becomes empty when ISP_PIPELINE_STREAM_CONTINUOUS */ | ||
129 | ISP_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0), | ||
130 | /* Set when queuing buffer to an empty DMA queue */ | ||
131 | ISP_VIDEO_DMAQUEUE_QUEUED = (1 << 1), | ||
132 | }; | ||
133 | |||
134 | #define isp_video_dmaqueue_flags_clr(video) \ | ||
135 | ({ (video)->dmaqueue_flags = 0; }) | ||
136 | |||
137 | /* | ||
138 | * struct isp_video_operations - ISP video operations | ||
139 | * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF | ||
140 | * if there was no buffer previously queued. | ||
141 | */ | ||
142 | struct isp_video_operations { | ||
143 | int(*queue)(struct isp_video *video, struct isp_buffer *buffer); | ||
144 | }; | ||
145 | |||
146 | struct isp_video { | ||
147 | struct video_device video; | ||
148 | enum v4l2_buf_type type; | ||
149 | struct media_pad pad; | ||
150 | |||
151 | struct mutex mutex; /* format and crop settings */ | ||
152 | atomic_t active; | ||
153 | |||
154 | struct isp_device *isp; | ||
155 | |||
156 | unsigned int capture_mem; | ||
157 | unsigned int bpl_alignment; /* alignment value */ | ||
158 | unsigned int bpl_zero_padding; /* whether the alignment is optional */ | ||
159 | unsigned int bpl_max; /* maximum bytes per line value */ | ||
160 | unsigned int bpl_value; /* bytes per line value */ | ||
161 | unsigned int bpl_padding; /* padding at end of line */ | ||
162 | |||
163 | /* Entity video node streaming */ | ||
164 | unsigned int streaming:1; | ||
165 | |||
166 | /* Pipeline state */ | ||
167 | struct isp_pipeline pipe; | ||
168 | struct mutex stream_lock; /* pipeline and stream states */ | ||
169 | |||
170 | /* Video buffers queue */ | ||
171 | struct isp_video_queue *queue; | ||
172 | struct list_head dmaqueue; | ||
173 | enum isp_video_dmaqueue_flags dmaqueue_flags; | ||
174 | |||
175 | const struct isp_video_operations *ops; | ||
176 | }; | ||
177 | |||
178 | #define to_isp_video(vdev) container_of(vdev, struct isp_video, video) | ||
179 | |||
180 | struct isp_video_fh { | ||
181 | struct v4l2_fh vfh; | ||
182 | struct isp_video *video; | ||
183 | struct isp_video_queue queue; | ||
184 | struct v4l2_format format; | ||
185 | struct v4l2_fract timeperframe; | ||
186 | }; | ||
187 | |||
188 | #define to_isp_video_fh(fh) container_of(fh, struct isp_video_fh, vfh) | ||
189 | #define isp_video_queue_to_isp_video_fh(q) \ | ||
190 | container_of(q, struct isp_video_fh, queue) | ||
191 | |||
192 | int omap3isp_video_init(struct isp_video *video, const char *name); | ||
193 | int omap3isp_video_register(struct isp_video *video, | ||
194 | struct v4l2_device *vdev); | ||
195 | void omap3isp_video_unregister(struct isp_video *video); | ||
196 | struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video, | ||
197 | unsigned int error); | ||
198 | void omap3isp_video_resume(struct isp_video *video, int continuous); | ||
199 | struct media_pad *omap3isp_video_remote_pad(struct isp_video *video); | ||
200 | |||
201 | const struct isp_format_info * | ||
202 | omap3isp_video_format_info(enum v4l2_mbus_pixelcode code); | ||
203 | |||
204 | #endif /* OMAP3_ISP_VIDEO_H */ | ||
diff --git a/drivers/media/video/omap3isp/luma_enhance_table.h b/drivers/media/video/omap3isp/luma_enhance_table.h new file mode 100644 index 00000000000..098b45e2280 --- /dev/null +++ b/drivers/media/video/omap3isp/luma_enhance_table.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * luma_enhance_table.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - Luminance enhancement table | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, | ||
28 | 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, | ||
29 | 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, | ||
30 | 1047552, 1047552, 1047552, 1047552, 1048575, 1047551, 1046527, 1045503, | ||
31 | 1044479, 1043455, 1042431, 1041407, 1040383, 1039359, 1038335, 1037311, | ||
32 | 1036287, 1035263, 1034239, 1033215, 1032191, 1031167, 1030143, 1028096, | ||
33 | 1028096, 1028096, 1028096, 1028096, 1028096, 1028096, 1028096, 1028096, | ||
34 | 1028096, 1028100, 1032196, 1036292, 1040388, 1044484, 0, 0, | ||
35 | 0, 5, 5125, 10245, 15365, 20485, 25605, 30720, | ||
36 | 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, | ||
37 | 30720, 30720, 31743, 30719, 29695, 28671, 27647, 26623, | ||
38 | 25599, 24575, 23551, 22527, 21503, 20479, 19455, 18431, | ||
39 | 17407, 16383, 15359, 14335, 13311, 12287, 11263, 10239, | ||
40 | 9215, 8191, 7167, 6143, 5119, 4095, 3071, 1024, | ||
41 | 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, | ||
42 | 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024 | ||
diff --git a/drivers/media/video/omap3isp/noise_filter_table.h b/drivers/media/video/omap3isp/noise_filter_table.h new file mode 100644 index 00000000000..d50451a4a24 --- /dev/null +++ b/drivers/media/video/omap3isp/noise_filter_table.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * noise_filter_table.h | ||
3 | * | ||
4 | * TI OMAP3 ISP - Noise filter table | ||
5 | * | ||
6 | * Copyright (C) 2010 Nokia Corporation | ||
7 | * Copyright (C) 2009 Texas Instruments, Inc. | ||
8 | * | ||
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
10 | * Sakari Ailus <sakari.ailus@iki.fi> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, | ||
28 | 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, | ||
29 | 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, | ||
30 | 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31 | ||