diff options
-rw-r--r-- | drivers/media/video/omap3isp/cfa_coef_table.h | 61 | ||||
-rw-r--r-- | drivers/media/video/omap3isp/gamma_table.h | 90 | ||||
-rw-r--r-- | drivers/media/video/omap3isp/ispccdc.c | 2268 | ||||
-rw-r--r-- | drivers/media/video/omap3isp/ispccdc.h | 219 | ||||
-rw-r--r-- | drivers/media/video/omap3isp/isppreview.c | 2113 | ||||
-rw-r--r-- | drivers/media/video/omap3isp/isppreview.h | 214 | ||||
-rw-r--r-- | drivers/media/video/omap3isp/ispresizer.c | 1693 | ||||
-rw-r--r-- | drivers/media/video/omap3isp/ispresizer.h | 147 | ||||
-rw-r--r-- | drivers/media/video/omap3isp/luma_enhance_table.h | 42 | ||||
-rw-r--r-- | drivers/media/video/omap3isp/noise_filter_table.h | 30 |
10 files changed, 6877 insertions, 0 deletions
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 000000000000..c60df0ed075a --- /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 000000000000..78deebf7d965 --- /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/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c new file mode 100644 index 000000000000..5ff9d14ce710 --- /dev/null +++ b/drivers/media/video/omap3isp/ispccdc.c | |||
@@ -0,0 +1,2268 @@ | |||
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 <media/v4l2-event.h> | ||
35 | |||
36 | #include "isp.h" | ||
37 | #include "ispreg.h" | ||
38 | #include "ispccdc.h" | ||
39 | |||
40 | static struct v4l2_mbus_framefmt * | ||
41 | __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | ||
42 | unsigned int pad, enum v4l2_subdev_format_whence which); | ||
43 | |||
44 | static const unsigned int ccdc_fmts[] = { | ||
45 | V4L2_MBUS_FMT_Y8_1X8, | ||
46 | V4L2_MBUS_FMT_SGRBG10_1X10, | ||
47 | V4L2_MBUS_FMT_SRGGB10_1X10, | ||
48 | V4L2_MBUS_FMT_SBGGR10_1X10, | ||
49 | V4L2_MBUS_FMT_SGBRG10_1X10, | ||
50 | V4L2_MBUS_FMT_SGRBG12_1X12, | ||
51 | V4L2_MBUS_FMT_SRGGB12_1X12, | ||
52 | V4L2_MBUS_FMT_SBGGR12_1X12, | ||
53 | V4L2_MBUS_FMT_SGBRG12_1X12, | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * ccdc_print_status - Print current CCDC Module register values. | ||
58 | * @ccdc: Pointer to ISP CCDC device. | ||
59 | * | ||
60 | * Also prints other debug information stored in the CCDC module. | ||
61 | */ | ||
62 | #define CCDC_PRINT_REGISTER(isp, name)\ | ||
63 | dev_dbg(isp->dev, "###CCDC " #name "=0x%08x\n", \ | ||
64 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_##name)) | ||
65 | |||
66 | static void ccdc_print_status(struct isp_ccdc_device *ccdc) | ||
67 | { | ||
68 | struct isp_device *isp = to_isp_device(ccdc); | ||
69 | |||
70 | dev_dbg(isp->dev, "-------------CCDC Register dump-------------\n"); | ||
71 | |||
72 | CCDC_PRINT_REGISTER(isp, PCR); | ||
73 | CCDC_PRINT_REGISTER(isp, SYN_MODE); | ||
74 | CCDC_PRINT_REGISTER(isp, HD_VD_WID); | ||
75 | CCDC_PRINT_REGISTER(isp, PIX_LINES); | ||
76 | CCDC_PRINT_REGISTER(isp, HORZ_INFO); | ||
77 | CCDC_PRINT_REGISTER(isp, VERT_START); | ||
78 | CCDC_PRINT_REGISTER(isp, VERT_LINES); | ||
79 | CCDC_PRINT_REGISTER(isp, CULLING); | ||
80 | CCDC_PRINT_REGISTER(isp, HSIZE_OFF); | ||
81 | CCDC_PRINT_REGISTER(isp, SDOFST); | ||
82 | CCDC_PRINT_REGISTER(isp, SDR_ADDR); | ||
83 | CCDC_PRINT_REGISTER(isp, CLAMP); | ||
84 | CCDC_PRINT_REGISTER(isp, DCSUB); | ||
85 | CCDC_PRINT_REGISTER(isp, COLPTN); | ||
86 | CCDC_PRINT_REGISTER(isp, BLKCMP); | ||
87 | CCDC_PRINT_REGISTER(isp, FPC); | ||
88 | CCDC_PRINT_REGISTER(isp, FPC_ADDR); | ||
89 | CCDC_PRINT_REGISTER(isp, VDINT); | ||
90 | CCDC_PRINT_REGISTER(isp, ALAW); | ||
91 | CCDC_PRINT_REGISTER(isp, REC656IF); | ||
92 | CCDC_PRINT_REGISTER(isp, CFG); | ||
93 | CCDC_PRINT_REGISTER(isp, FMTCFG); | ||
94 | CCDC_PRINT_REGISTER(isp, FMT_HORZ); | ||
95 | CCDC_PRINT_REGISTER(isp, FMT_VERT); | ||
96 | CCDC_PRINT_REGISTER(isp, PRGEVEN0); | ||
97 | CCDC_PRINT_REGISTER(isp, PRGEVEN1); | ||
98 | CCDC_PRINT_REGISTER(isp, PRGODD0); | ||
99 | CCDC_PRINT_REGISTER(isp, PRGODD1); | ||
100 | CCDC_PRINT_REGISTER(isp, VP_OUT); | ||
101 | CCDC_PRINT_REGISTER(isp, LSC_CONFIG); | ||
102 | CCDC_PRINT_REGISTER(isp, LSC_INITIAL); | ||
103 | CCDC_PRINT_REGISTER(isp, LSC_TABLE_BASE); | ||
104 | CCDC_PRINT_REGISTER(isp, LSC_TABLE_OFFSET); | ||
105 | |||
106 | dev_dbg(isp->dev, "--------------------------------------------\n"); | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * omap3isp_ccdc_busy - Get busy state of the CCDC. | ||
111 | * @ccdc: Pointer to ISP CCDC device. | ||
112 | */ | ||
113 | int omap3isp_ccdc_busy(struct isp_ccdc_device *ccdc) | ||
114 | { | ||
115 | struct isp_device *isp = to_isp_device(ccdc); | ||
116 | |||
117 | return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR) & | ||
118 | ISPCCDC_PCR_BUSY; | ||
119 | } | ||
120 | |||
121 | /* ----------------------------------------------------------------------------- | ||
122 | * Lens Shading Compensation | ||
123 | */ | ||
124 | |||
125 | /* | ||
126 | * ccdc_lsc_validate_config - Check that LSC configuration is valid. | ||
127 | * @ccdc: Pointer to ISP CCDC device. | ||
128 | * @lsc_cfg: the LSC configuration to check. | ||
129 | * | ||
130 | * Returns 0 if the LSC configuration is valid, or -EINVAL if invalid. | ||
131 | */ | ||
132 | static int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc, | ||
133 | struct omap3isp_ccdc_lsc_config *lsc_cfg) | ||
134 | { | ||
135 | struct isp_device *isp = to_isp_device(ccdc); | ||
136 | struct v4l2_mbus_framefmt *format; | ||
137 | unsigned int paxel_width, paxel_height; | ||
138 | unsigned int paxel_shift_x, paxel_shift_y; | ||
139 | unsigned int min_width, min_height, min_size; | ||
140 | unsigned int input_width, input_height; | ||
141 | |||
142 | paxel_shift_x = lsc_cfg->gain_mode_m; | ||
143 | paxel_shift_y = lsc_cfg->gain_mode_n; | ||
144 | |||
145 | if ((paxel_shift_x < 2) || (paxel_shift_x > 6) || | ||
146 | (paxel_shift_y < 2) || (paxel_shift_y > 6)) { | ||
147 | dev_dbg(isp->dev, "CCDC: LSC: Invalid paxel size\n"); | ||
148 | return -EINVAL; | ||
149 | } | ||
150 | |||
151 | if (lsc_cfg->offset & 3) { | ||
152 | dev_dbg(isp->dev, "CCDC: LSC: Offset must be a multiple of " | ||
153 | "4\n"); | ||
154 | return -EINVAL; | ||
155 | } | ||
156 | |||
157 | if ((lsc_cfg->initial_x & 1) || (lsc_cfg->initial_y & 1)) { | ||
158 | dev_dbg(isp->dev, "CCDC: LSC: initial_x and y must be even\n"); | ||
159 | return -EINVAL; | ||
160 | } | ||
161 | |||
162 | format = __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK, | ||
163 | V4L2_SUBDEV_FORMAT_ACTIVE); | ||
164 | input_width = format->width; | ||
165 | input_height = format->height; | ||
166 | |||
167 | /* Calculate minimum bytesize for validation */ | ||
168 | paxel_width = 1 << paxel_shift_x; | ||
169 | min_width = ((input_width + lsc_cfg->initial_x + paxel_width - 1) | ||
170 | >> paxel_shift_x) + 1; | ||
171 | |||
172 | paxel_height = 1 << paxel_shift_y; | ||
173 | min_height = ((input_height + lsc_cfg->initial_y + paxel_height - 1) | ||
174 | >> paxel_shift_y) + 1; | ||
175 | |||
176 | min_size = 4 * min_width * min_height; | ||
177 | if (min_size > lsc_cfg->size) { | ||
178 | dev_dbg(isp->dev, "CCDC: LSC: too small table\n"); | ||
179 | return -EINVAL; | ||
180 | } | ||
181 | if (lsc_cfg->offset < (min_width * 4)) { | ||
182 | dev_dbg(isp->dev, "CCDC: LSC: Offset is too small\n"); | ||
183 | return -EINVAL; | ||
184 | } | ||
185 | if ((lsc_cfg->size / lsc_cfg->offset) < min_height) { | ||
186 | dev_dbg(isp->dev, "CCDC: LSC: Wrong size/offset combination\n"); | ||
187 | return -EINVAL; | ||
188 | } | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | /* | ||
193 | * ccdc_lsc_program_table - Program Lens Shading Compensation table address. | ||
194 | * @ccdc: Pointer to ISP CCDC device. | ||
195 | */ | ||
196 | static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr) | ||
197 | { | ||
198 | isp_reg_writel(to_isp_device(ccdc), addr, | ||
199 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE); | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * ccdc_lsc_setup_regs - Configures the lens shading compensation module | ||
204 | * @ccdc: Pointer to ISP CCDC device. | ||
205 | */ | ||
206 | static void ccdc_lsc_setup_regs(struct isp_ccdc_device *ccdc, | ||
207 | struct omap3isp_ccdc_lsc_config *cfg) | ||
208 | { | ||
209 | struct isp_device *isp = to_isp_device(ccdc); | ||
210 | int reg; | ||
211 | |||
212 | isp_reg_writel(isp, cfg->offset, OMAP3_ISP_IOMEM_CCDC, | ||
213 | ISPCCDC_LSC_TABLE_OFFSET); | ||
214 | |||
215 | reg = 0; | ||
216 | reg |= cfg->gain_mode_n << ISPCCDC_LSC_GAIN_MODE_N_SHIFT; | ||
217 | reg |= cfg->gain_mode_m << ISPCCDC_LSC_GAIN_MODE_M_SHIFT; | ||
218 | reg |= cfg->gain_format << ISPCCDC_LSC_GAIN_FORMAT_SHIFT; | ||
219 | isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG); | ||
220 | |||
221 | reg = 0; | ||
222 | reg &= ~ISPCCDC_LSC_INITIAL_X_MASK; | ||
223 | reg |= cfg->initial_x << ISPCCDC_LSC_INITIAL_X_SHIFT; | ||
224 | reg &= ~ISPCCDC_LSC_INITIAL_Y_MASK; | ||
225 | reg |= cfg->initial_y << ISPCCDC_LSC_INITIAL_Y_SHIFT; | ||
226 | isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, | ||
227 | ISPCCDC_LSC_INITIAL); | ||
228 | } | ||
229 | |||
230 | static int ccdc_lsc_wait_prefetch(struct isp_ccdc_device *ccdc) | ||
231 | { | ||
232 | struct isp_device *isp = to_isp_device(ccdc); | ||
233 | unsigned int wait; | ||
234 | |||
235 | isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ, | ||
236 | OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); | ||
237 | |||
238 | /* timeout 1 ms */ | ||
239 | for (wait = 0; wait < 1000; wait++) { | ||
240 | if (isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS) & | ||
241 | IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ) { | ||
242 | isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ, | ||
243 | OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | rmb(); | ||
248 | udelay(1); | ||
249 | } | ||
250 | |||
251 | return -ETIMEDOUT; | ||
252 | } | ||
253 | |||
254 | /* | ||
255 | * __ccdc_lsc_enable - Enables/Disables the Lens Shading Compensation module. | ||
256 | * @ccdc: Pointer to ISP CCDC device. | ||
257 | * @enable: 0 Disables LSC, 1 Enables LSC. | ||
258 | */ | ||
259 | static int __ccdc_lsc_enable(struct isp_ccdc_device *ccdc, int enable) | ||
260 | { | ||
261 | struct isp_device *isp = to_isp_device(ccdc); | ||
262 | const struct v4l2_mbus_framefmt *format = | ||
263 | __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK, | ||
264 | V4L2_SUBDEV_FORMAT_ACTIVE); | ||
265 | |||
266 | if ((format->code != V4L2_MBUS_FMT_SGRBG10_1X10) && | ||
267 | (format->code != V4L2_MBUS_FMT_SRGGB10_1X10) && | ||
268 | (format->code != V4L2_MBUS_FMT_SBGGR10_1X10) && | ||
269 | (format->code != V4L2_MBUS_FMT_SGBRG10_1X10)) | ||
270 | return -EINVAL; | ||
271 | |||
272 | if (enable) | ||
273 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_LSC_READ); | ||
274 | |||
275 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, | ||
276 | ISPCCDC_LSC_ENABLE, enable ? ISPCCDC_LSC_ENABLE : 0); | ||
277 | |||
278 | if (enable) { | ||
279 | if (ccdc_lsc_wait_prefetch(ccdc) < 0) { | ||
280 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, | ||
281 | ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE); | ||
282 | ccdc->lsc.state = LSC_STATE_STOPPED; | ||
283 | dev_warn(to_device(ccdc), "LSC prefecth timeout\n"); | ||
284 | return -ETIMEDOUT; | ||
285 | } | ||
286 | ccdc->lsc.state = LSC_STATE_RUNNING; | ||
287 | } else { | ||
288 | ccdc->lsc.state = LSC_STATE_STOPPING; | ||
289 | } | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static int ccdc_lsc_busy(struct isp_ccdc_device *ccdc) | ||
295 | { | ||
296 | struct isp_device *isp = to_isp_device(ccdc); | ||
297 | |||
298 | return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG) & | ||
299 | ISPCCDC_LSC_BUSY; | ||
300 | } | ||
301 | |||
302 | /* __ccdc_lsc_configure - Apply a new configuration to the LSC engine | ||
303 | * @ccdc: Pointer to ISP CCDC device | ||
304 | * @req: New configuration request | ||
305 | * | ||
306 | * context: in_interrupt() | ||
307 | */ | ||
308 | static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc, | ||
309 | struct ispccdc_lsc_config_req *req) | ||
310 | { | ||
311 | if (!req->enable) | ||
312 | return -EINVAL; | ||
313 | |||
314 | if (ccdc_lsc_validate_config(ccdc, &req->config) < 0) { | ||
315 | dev_dbg(to_device(ccdc), "Discard LSC configuration\n"); | ||
316 | return -EINVAL; | ||
317 | } | ||
318 | |||
319 | if (ccdc_lsc_busy(ccdc)) | ||
320 | return -EBUSY; | ||
321 | |||
322 | ccdc_lsc_setup_regs(ccdc, &req->config); | ||
323 | ccdc_lsc_program_table(ccdc, req->table); | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | /* | ||
328 | * ccdc_lsc_error_handler - Handle LSC prefetch error scenario. | ||
329 | * @ccdc: Pointer to ISP CCDC device. | ||
330 | * | ||
331 | * Disables LSC, and defers enablement to shadow registers update time. | ||
332 | */ | ||
333 | static void ccdc_lsc_error_handler(struct isp_ccdc_device *ccdc) | ||
334 | { | ||
335 | struct isp_device *isp = to_isp_device(ccdc); | ||
336 | /* | ||
337 | * From OMAP3 TRM: When this event is pending, the module | ||
338 | * goes into transparent mode (output =input). Normal | ||
339 | * operation can be resumed at the start of the next frame | ||
340 | * after: | ||
341 | * 1) Clearing this event | ||
342 | * 2) Disabling the LSC module | ||
343 | * 3) Enabling it | ||
344 | */ | ||
345 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, | ||
346 | ISPCCDC_LSC_ENABLE); | ||
347 | ccdc->lsc.state = LSC_STATE_STOPPED; | ||
348 | } | ||
349 | |||
350 | static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc, | ||
351 | struct ispccdc_lsc_config_req *req) | ||
352 | { | ||
353 | struct isp_device *isp = to_isp_device(ccdc); | ||
354 | |||
355 | if (req == NULL) | ||
356 | return; | ||
357 | |||
358 | if (req->iovm) | ||
359 | dma_unmap_sg(isp->dev, req->iovm->sgt->sgl, | ||
360 | req->iovm->sgt->nents, DMA_TO_DEVICE); | ||
361 | if (req->table) | ||
362 | iommu_vfree(isp->iommu, req->table); | ||
363 | kfree(req); | ||
364 | } | ||
365 | |||
366 | static void ccdc_lsc_free_queue(struct isp_ccdc_device *ccdc, | ||
367 | struct list_head *queue) | ||
368 | { | ||
369 | struct ispccdc_lsc_config_req *req, *n; | ||
370 | unsigned long flags; | ||
371 | |||
372 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
373 | list_for_each_entry_safe(req, n, queue, list) { | ||
374 | list_del(&req->list); | ||
375 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
376 | ccdc_lsc_free_request(ccdc, req); | ||
377 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
378 | } | ||
379 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
380 | } | ||
381 | |||
382 | static void ccdc_lsc_free_table_work(struct work_struct *work) | ||
383 | { | ||
384 | struct isp_ccdc_device *ccdc; | ||
385 | struct ispccdc_lsc *lsc; | ||
386 | |||
387 | lsc = container_of(work, struct ispccdc_lsc, table_work); | ||
388 | ccdc = container_of(lsc, struct isp_ccdc_device, lsc); | ||
389 | |||
390 | ccdc_lsc_free_queue(ccdc, &lsc->free_queue); | ||
391 | } | ||
392 | |||
393 | /* | ||
394 | * ccdc_lsc_config - Configure the LSC module from a userspace request | ||
395 | * | ||
396 | * Store the request LSC configuration in the LSC engine request pointer. The | ||
397 | * configuration will be applied to the hardware when the CCDC will be enabled, | ||
398 | * or at the next LSC interrupt if the CCDC is already running. | ||
399 | */ | ||
400 | static int ccdc_lsc_config(struct isp_ccdc_device *ccdc, | ||
401 | struct omap3isp_ccdc_update_config *config) | ||
402 | { | ||
403 | struct isp_device *isp = to_isp_device(ccdc); | ||
404 | struct ispccdc_lsc_config_req *req; | ||
405 | unsigned long flags; | ||
406 | void *table; | ||
407 | u16 update; | ||
408 | int ret; | ||
409 | |||
410 | update = config->update & | ||
411 | (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC); | ||
412 | if (!update) | ||
413 | return 0; | ||
414 | |||
415 | if (update != (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC)) { | ||
416 | dev_dbg(to_device(ccdc), "%s: Both LSC configuration and table " | ||
417 | "need to be supplied\n", __func__); | ||
418 | return -EINVAL; | ||
419 | } | ||
420 | |||
421 | req = kzalloc(sizeof(*req), GFP_KERNEL); | ||
422 | if (req == NULL) | ||
423 | return -ENOMEM; | ||
424 | |||
425 | if (config->flag & OMAP3ISP_CCDC_CONFIG_LSC) { | ||
426 | if (copy_from_user(&req->config, config->lsc_cfg, | ||
427 | sizeof(req->config))) { | ||
428 | ret = -EFAULT; | ||
429 | goto done; | ||
430 | } | ||
431 | |||
432 | req->enable = 1; | ||
433 | |||
434 | req->table = iommu_vmalloc(isp->iommu, 0, req->config.size, | ||
435 | IOMMU_FLAG); | ||
436 | if (IS_ERR_VALUE(req->table)) { | ||
437 | req->table = 0; | ||
438 | ret = -ENOMEM; | ||
439 | goto done; | ||
440 | } | ||
441 | |||
442 | req->iovm = find_iovm_area(isp->iommu, req->table); | ||
443 | if (req->iovm == NULL) { | ||
444 | ret = -ENOMEM; | ||
445 | goto done; | ||
446 | } | ||
447 | |||
448 | if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl, | ||
449 | req->iovm->sgt->nents, DMA_TO_DEVICE)) { | ||
450 | ret = -ENOMEM; | ||
451 | req->iovm = NULL; | ||
452 | goto done; | ||
453 | } | ||
454 | |||
455 | dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl, | ||
456 | req->iovm->sgt->nents, DMA_TO_DEVICE); | ||
457 | |||
458 | table = da_to_va(isp->iommu, req->table); | ||
459 | if (copy_from_user(table, config->lsc, req->config.size)) { | ||
460 | ret = -EFAULT; | ||
461 | goto done; | ||
462 | } | ||
463 | |||
464 | dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl, | ||
465 | req->iovm->sgt->nents, DMA_TO_DEVICE); | ||
466 | } | ||
467 | |||
468 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
469 | if (ccdc->lsc.request) { | ||
470 | list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue); | ||
471 | schedule_work(&ccdc->lsc.table_work); | ||
472 | } | ||
473 | ccdc->lsc.request = req; | ||
474 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
475 | |||
476 | ret = 0; | ||
477 | |||
478 | done: | ||
479 | if (ret < 0) | ||
480 | ccdc_lsc_free_request(ccdc, req); | ||
481 | |||
482 | return ret; | ||
483 | } | ||
484 | |||
485 | static inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc) | ||
486 | { | ||
487 | unsigned long flags; | ||
488 | |||
489 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
490 | if (ccdc->lsc.active) { | ||
491 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
492 | return 1; | ||
493 | } | ||
494 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | static int ccdc_lsc_enable(struct isp_ccdc_device *ccdc) | ||
499 | { | ||
500 | struct ispccdc_lsc *lsc = &ccdc->lsc; | ||
501 | |||
502 | if (lsc->state != LSC_STATE_STOPPED) | ||
503 | return -EINVAL; | ||
504 | |||
505 | if (lsc->active) { | ||
506 | list_add_tail(&lsc->active->list, &lsc->free_queue); | ||
507 | lsc->active = NULL; | ||
508 | } | ||
509 | |||
510 | if (__ccdc_lsc_configure(ccdc, lsc->request) < 0) { | ||
511 | omap3isp_sbl_disable(to_isp_device(ccdc), | ||
512 | OMAP3_ISP_SBL_CCDC_LSC_READ); | ||
513 | list_add_tail(&lsc->request->list, &lsc->free_queue); | ||
514 | lsc->request = NULL; | ||
515 | goto done; | ||
516 | } | ||
517 | |||
518 | lsc->active = lsc->request; | ||
519 | lsc->request = NULL; | ||
520 | __ccdc_lsc_enable(ccdc, 1); | ||
521 | |||
522 | done: | ||
523 | if (!list_empty(&lsc->free_queue)) | ||
524 | schedule_work(&lsc->table_work); | ||
525 | |||
526 | return 0; | ||
527 | } | ||
528 | |||
529 | /* ----------------------------------------------------------------------------- | ||
530 | * Parameters configuration | ||
531 | */ | ||
532 | |||
533 | /* | ||
534 | * ccdc_configure_clamp - Configure optical-black or digital clamping | ||
535 | * @ccdc: Pointer to ISP CCDC device. | ||
536 | * | ||
537 | * The CCDC performs either optical-black or digital clamp. Configure and enable | ||
538 | * the selected clamp method. | ||
539 | */ | ||
540 | static void ccdc_configure_clamp(struct isp_ccdc_device *ccdc) | ||
541 | { | ||
542 | struct isp_device *isp = to_isp_device(ccdc); | ||
543 | u32 clamp; | ||
544 | |||
545 | if (ccdc->obclamp) { | ||
546 | clamp = ccdc->clamp.obgain << ISPCCDC_CLAMP_OBGAIN_SHIFT; | ||
547 | clamp |= ccdc->clamp.oblen << ISPCCDC_CLAMP_OBSLEN_SHIFT; | ||
548 | clamp |= ccdc->clamp.oblines << ISPCCDC_CLAMP_OBSLN_SHIFT; | ||
549 | clamp |= ccdc->clamp.obstpixel << ISPCCDC_CLAMP_OBST_SHIFT; | ||
550 | isp_reg_writel(isp, clamp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP); | ||
551 | } else { | ||
552 | isp_reg_writel(isp, ccdc->clamp.dcsubval, | ||
553 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB); | ||
554 | } | ||
555 | |||
556 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP, | ||
557 | ISPCCDC_CLAMP_CLAMPEN, | ||
558 | ccdc->obclamp ? ISPCCDC_CLAMP_CLAMPEN : 0); | ||
559 | } | ||
560 | |||
561 | /* | ||
562 | * ccdc_configure_fpc - Configure Faulty Pixel Correction | ||
563 | * @ccdc: Pointer to ISP CCDC device. | ||
564 | */ | ||
565 | static void ccdc_configure_fpc(struct isp_ccdc_device *ccdc) | ||
566 | { | ||
567 | struct isp_device *isp = to_isp_device(ccdc); | ||
568 | |||
569 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, ISPCCDC_FPC_FPCEN); | ||
570 | |||
571 | if (!ccdc->fpc_en) | ||
572 | return; | ||
573 | |||
574 | isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC, | ||
575 | ISPCCDC_FPC_ADDR); | ||
576 | /* The FPNUM field must be set before enabling FPC. */ | ||
577 | isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT), | ||
578 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); | ||
579 | isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT) | | ||
580 | ISPCCDC_FPC_FPCEN, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); | ||
581 | } | ||
582 | |||
583 | /* | ||
584 | * ccdc_configure_black_comp - Configure Black Level Compensation. | ||
585 | * @ccdc: Pointer to ISP CCDC device. | ||
586 | */ | ||
587 | static void ccdc_configure_black_comp(struct isp_ccdc_device *ccdc) | ||
588 | { | ||
589 | struct isp_device *isp = to_isp_device(ccdc); | ||
590 | u32 blcomp; | ||
591 | |||
592 | blcomp = ccdc->blcomp.b_mg << ISPCCDC_BLKCMP_B_MG_SHIFT; | ||
593 | blcomp |= ccdc->blcomp.gb_g << ISPCCDC_BLKCMP_GB_G_SHIFT; | ||
594 | blcomp |= ccdc->blcomp.gr_cy << ISPCCDC_BLKCMP_GR_CY_SHIFT; | ||
595 | blcomp |= ccdc->blcomp.r_ye << ISPCCDC_BLKCMP_R_YE_SHIFT; | ||
596 | |||
597 | isp_reg_writel(isp, blcomp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP); | ||
598 | } | ||
599 | |||
600 | /* | ||
601 | * ccdc_configure_lpf - Configure Low-Pass Filter (LPF). | ||
602 | * @ccdc: Pointer to ISP CCDC device. | ||
603 | */ | ||
604 | static void ccdc_configure_lpf(struct isp_ccdc_device *ccdc) | ||
605 | { | ||
606 | struct isp_device *isp = to_isp_device(ccdc); | ||
607 | |||
608 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE, | ||
609 | ISPCCDC_SYN_MODE_LPF, | ||
610 | ccdc->lpf ? ISPCCDC_SYN_MODE_LPF : 0); | ||
611 | } | ||
612 | |||
613 | /* | ||
614 | * ccdc_configure_alaw - Configure A-law compression. | ||
615 | * @ccdc: Pointer to ISP CCDC device. | ||
616 | */ | ||
617 | static void ccdc_configure_alaw(struct isp_ccdc_device *ccdc) | ||
618 | { | ||
619 | struct isp_device *isp = to_isp_device(ccdc); | ||
620 | u32 alaw = 0; | ||
621 | |||
622 | switch (ccdc->syncif.datsz) { | ||
623 | case 8: | ||
624 | return; | ||
625 | |||
626 | case 10: | ||
627 | alaw = ISPCCDC_ALAW_GWDI_9_0; | ||
628 | break; | ||
629 | case 11: | ||
630 | alaw = ISPCCDC_ALAW_GWDI_10_1; | ||
631 | break; | ||
632 | case 12: | ||
633 | alaw = ISPCCDC_ALAW_GWDI_11_2; | ||
634 | break; | ||
635 | case 13: | ||
636 | alaw = ISPCCDC_ALAW_GWDI_12_3; | ||
637 | break; | ||
638 | } | ||
639 | |||
640 | if (ccdc->alaw) | ||
641 | alaw |= ISPCCDC_ALAW_CCDTBL; | ||
642 | |||
643 | isp_reg_writel(isp, alaw, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW); | ||
644 | } | ||
645 | |||
646 | /* | ||
647 | * ccdc_config_imgattr - Configure sensor image specific attributes. | ||
648 | * @ccdc: Pointer to ISP CCDC device. | ||
649 | * @colptn: Color pattern of the sensor. | ||
650 | */ | ||
651 | static void ccdc_config_imgattr(struct isp_ccdc_device *ccdc, u32 colptn) | ||
652 | { | ||
653 | struct isp_device *isp = to_isp_device(ccdc); | ||
654 | |||
655 | isp_reg_writel(isp, colptn, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN); | ||
656 | } | ||
657 | |||
658 | /* | ||
659 | * ccdc_config - Set CCDC configuration from userspace | ||
660 | * @ccdc: Pointer to ISP CCDC device. | ||
661 | * @userspace_add: Structure containing CCDC configuration sent from userspace. | ||
662 | * | ||
663 | * Returns 0 if successful, -EINVAL if the pointer to the configuration | ||
664 | * structure is null, or the copy_from_user function fails to copy user space | ||
665 | * memory to kernel space memory. | ||
666 | */ | ||
667 | static int ccdc_config(struct isp_ccdc_device *ccdc, | ||
668 | struct omap3isp_ccdc_update_config *ccdc_struct) | ||
669 | { | ||
670 | struct isp_device *isp = to_isp_device(ccdc); | ||
671 | unsigned long flags; | ||
672 | |||
673 | spin_lock_irqsave(&ccdc->lock, flags); | ||
674 | ccdc->shadow_update = 1; | ||
675 | spin_unlock_irqrestore(&ccdc->lock, flags); | ||
676 | |||
677 | if (OMAP3ISP_CCDC_ALAW & ccdc_struct->update) { | ||
678 | ccdc->alaw = !!(OMAP3ISP_CCDC_ALAW & ccdc_struct->flag); | ||
679 | ccdc->update |= OMAP3ISP_CCDC_ALAW; | ||
680 | } | ||
681 | |||
682 | if (OMAP3ISP_CCDC_LPF & ccdc_struct->update) { | ||
683 | ccdc->lpf = !!(OMAP3ISP_CCDC_LPF & ccdc_struct->flag); | ||
684 | ccdc->update |= OMAP3ISP_CCDC_LPF; | ||
685 | } | ||
686 | |||
687 | if (OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->update) { | ||
688 | if (copy_from_user(&ccdc->clamp, ccdc_struct->bclamp, | ||
689 | sizeof(ccdc->clamp))) { | ||
690 | ccdc->shadow_update = 0; | ||
691 | return -EFAULT; | ||
692 | } | ||
693 | |||
694 | ccdc->obclamp = !!(OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->flag); | ||
695 | ccdc->update |= OMAP3ISP_CCDC_BLCLAMP; | ||
696 | } | ||
697 | |||
698 | if (OMAP3ISP_CCDC_BCOMP & ccdc_struct->update) { | ||
699 | if (copy_from_user(&ccdc->blcomp, ccdc_struct->blcomp, | ||
700 | sizeof(ccdc->blcomp))) { | ||
701 | ccdc->shadow_update = 0; | ||
702 | return -EFAULT; | ||
703 | } | ||
704 | |||
705 | ccdc->update |= OMAP3ISP_CCDC_BCOMP; | ||
706 | } | ||
707 | |||
708 | ccdc->shadow_update = 0; | ||
709 | |||
710 | if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) { | ||
711 | u32 table_old = 0; | ||
712 | u32 table_new; | ||
713 | u32 size; | ||
714 | |||
715 | if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED) | ||
716 | return -EBUSY; | ||
717 | |||
718 | ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag); | ||
719 | |||
720 | if (ccdc->fpc_en) { | ||
721 | if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc, | ||
722 | sizeof(ccdc->fpc))) | ||
723 | return -EFAULT; | ||
724 | |||
725 | /* | ||
726 | * table_new must be 64-bytes aligned, but it's | ||
727 | * already done by iommu_vmalloc(). | ||
728 | */ | ||
729 | size = ccdc->fpc.fpnum * 4; | ||
730 | table_new = iommu_vmalloc(isp->iommu, 0, size, | ||
731 | IOMMU_FLAG); | ||
732 | if (IS_ERR_VALUE(table_new)) | ||
733 | return -ENOMEM; | ||
734 | |||
735 | if (copy_from_user(da_to_va(isp->iommu, table_new), | ||
736 | (__force void __user *) | ||
737 | ccdc->fpc.fpcaddr, size)) { | ||
738 | iommu_vfree(isp->iommu, table_new); | ||
739 | return -EFAULT; | ||
740 | } | ||
741 | |||
742 | table_old = ccdc->fpc.fpcaddr; | ||
743 | ccdc->fpc.fpcaddr = table_new; | ||
744 | } | ||
745 | |||
746 | ccdc_configure_fpc(ccdc); | ||
747 | if (table_old != 0) | ||
748 | iommu_vfree(isp->iommu, table_old); | ||
749 | } | ||
750 | |||
751 | return ccdc_lsc_config(ccdc, ccdc_struct); | ||
752 | } | ||
753 | |||
754 | static void ccdc_apply_controls(struct isp_ccdc_device *ccdc) | ||
755 | { | ||
756 | if (ccdc->update & OMAP3ISP_CCDC_ALAW) { | ||
757 | ccdc_configure_alaw(ccdc); | ||
758 | ccdc->update &= ~OMAP3ISP_CCDC_ALAW; | ||
759 | } | ||
760 | |||
761 | if (ccdc->update & OMAP3ISP_CCDC_LPF) { | ||
762 | ccdc_configure_lpf(ccdc); | ||
763 | ccdc->update &= ~OMAP3ISP_CCDC_LPF; | ||
764 | } | ||
765 | |||
766 | if (ccdc->update & OMAP3ISP_CCDC_BLCLAMP) { | ||
767 | ccdc_configure_clamp(ccdc); | ||
768 | ccdc->update &= ~OMAP3ISP_CCDC_BLCLAMP; | ||
769 | } | ||
770 | |||
771 | if (ccdc->update & OMAP3ISP_CCDC_BCOMP) { | ||
772 | ccdc_configure_black_comp(ccdc); | ||
773 | ccdc->update &= ~OMAP3ISP_CCDC_BCOMP; | ||
774 | } | ||
775 | } | ||
776 | |||
777 | /* | ||
778 | * omap3isp_ccdc_restore_context - Restore values of the CCDC module registers | ||
779 | * @dev: Pointer to ISP device | ||
780 | */ | ||
781 | void omap3isp_ccdc_restore_context(struct isp_device *isp) | ||
782 | { | ||
783 | struct isp_ccdc_device *ccdc = &isp->isp_ccdc; | ||
784 | |||
785 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_VDLC); | ||
786 | |||
787 | ccdc->update = OMAP3ISP_CCDC_ALAW | OMAP3ISP_CCDC_LPF | ||
788 | | OMAP3ISP_CCDC_BLCLAMP | OMAP3ISP_CCDC_BCOMP; | ||
789 | ccdc_apply_controls(ccdc); | ||
790 | ccdc_configure_fpc(ccdc); | ||
791 | } | ||
792 | |||
793 | /* ----------------------------------------------------------------------------- | ||
794 | * Format- and pipeline-related configuration helpers | ||
795 | */ | ||
796 | |||
797 | /* | ||
798 | * ccdc_config_vp - Configure the Video Port. | ||
799 | * @ccdc: Pointer to ISP CCDC device. | ||
800 | */ | ||
801 | static void ccdc_config_vp(struct isp_ccdc_device *ccdc) | ||
802 | { | ||
803 | struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); | ||
804 | struct isp_device *isp = to_isp_device(ccdc); | ||
805 | unsigned long l3_ick = pipe->l3_ick; | ||
806 | unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8; | ||
807 | unsigned int div = 0; | ||
808 | u32 fmtcfg_vp; | ||
809 | |||
810 | fmtcfg_vp = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG) | ||
811 | & ~(ISPCCDC_FMTCFG_VPIN_MASK | ISPCCDC_FMTCFG_VPIF_FRQ_MASK); | ||
812 | |||
813 | switch (ccdc->syncif.datsz) { | ||
814 | case 8: | ||
815 | case 10: | ||
816 | fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0; | ||
817 | break; | ||
818 | case 11: | ||
819 | fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1; | ||
820 | break; | ||
821 | case 12: | ||
822 | fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2; | ||
823 | break; | ||
824 | case 13: | ||
825 | fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3; | ||
826 | break; | ||
827 | }; | ||
828 | |||
829 | if (pipe->input) | ||
830 | div = DIV_ROUND_UP(l3_ick, pipe->max_rate); | ||
831 | else if (ccdc->vpcfg.pixelclk) | ||
832 | div = l3_ick / ccdc->vpcfg.pixelclk; | ||
833 | |||
834 | div = clamp(div, 2U, max_div); | ||
835 | fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; | ||
836 | |||
837 | isp_reg_writel(isp, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); | ||
838 | } | ||
839 | |||
840 | /* | ||
841 | * ccdc_enable_vp - Enable Video Port. | ||
842 | * @ccdc: Pointer to ISP CCDC device. | ||
843 | * @enable: 0 Disables VP, 1 Enables VP | ||
844 | * | ||
845 | * This is needed for outputting image to Preview, H3A and HIST ISP submodules. | ||
846 | */ | ||
847 | static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable) | ||
848 | { | ||
849 | struct isp_device *isp = to_isp_device(ccdc); | ||
850 | |||
851 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, | ||
852 | ISPCCDC_FMTCFG_VPEN, enable ? ISPCCDC_FMTCFG_VPEN : 0); | ||
853 | } | ||
854 | |||
855 | /* | ||
856 | * ccdc_config_outlineoffset - Configure memory saving output line offset | ||
857 | * @ccdc: Pointer to ISP CCDC device. | ||
858 | * @offset: Address offset to start a new line. Must be twice the | ||
859 | * Output width and aligned on 32 byte boundary | ||
860 | * @oddeven: Specifies the odd/even line pattern to be chosen to store the | ||
861 | * output. | ||
862 | * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines. | ||
863 | * | ||
864 | * - Configures the output line offset when stored in memory | ||
865 | * - Sets the odd/even line pattern to store the output | ||
866 | * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4)) | ||
867 | * - Configures the number of even and odd line fields in case of rearranging | ||
868 | * the lines. | ||
869 | */ | ||
870 | static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc, | ||
871 | u32 offset, u8 oddeven, u8 numlines) | ||
872 | { | ||
873 | struct isp_device *isp = to_isp_device(ccdc); | ||
874 | |||
875 | isp_reg_writel(isp, offset & 0xffff, | ||
876 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF); | ||
877 | |||
878 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
879 | ISPCCDC_SDOFST_FINV); | ||
880 | |||
881 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
882 | ISPCCDC_SDOFST_FOFST_4L); | ||
883 | |||
884 | switch (oddeven) { | ||
885 | case EVENEVEN: | ||
886 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
887 | (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT); | ||
888 | break; | ||
889 | case ODDEVEN: | ||
890 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
891 | (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT); | ||
892 | break; | ||
893 | case EVENODD: | ||
894 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
895 | (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT); | ||
896 | break; | ||
897 | case ODDODD: | ||
898 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
899 | (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT); | ||
900 | break; | ||
901 | default: | ||
902 | break; | ||
903 | } | ||
904 | } | ||
905 | |||
906 | /* | ||
907 | * ccdc_set_outaddr - Set memory address to save output image | ||
908 | * @ccdc: Pointer to ISP CCDC device. | ||
909 | * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary. | ||
910 | * | ||
911 | * Sets the memory address where the output will be saved. | ||
912 | */ | ||
913 | static void ccdc_set_outaddr(struct isp_ccdc_device *ccdc, u32 addr) | ||
914 | { | ||
915 | struct isp_device *isp = to_isp_device(ccdc); | ||
916 | |||
917 | isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR); | ||
918 | } | ||
919 | |||
920 | /* | ||
921 | * omap3isp_ccdc_max_rate - Calculate maximum input data rate based on the input | ||
922 | * @ccdc: Pointer to ISP CCDC device. | ||
923 | * @max_rate: Maximum calculated data rate. | ||
924 | * | ||
925 | * Returns in *max_rate less value between calculated and passed | ||
926 | */ | ||
927 | void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc, | ||
928 | unsigned int *max_rate) | ||
929 | { | ||
930 | struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); | ||
931 | unsigned int rate; | ||
932 | |||
933 | if (pipe == NULL) | ||
934 | return; | ||
935 | |||
936 | /* | ||
937 | * TRM says that for parallel sensors the maximum data rate | ||
938 | * should be 90% form L3/2 clock, otherwise just L3/2. | ||
939 | */ | ||
940 | if (ccdc->input == CCDC_INPUT_PARALLEL) | ||
941 | rate = pipe->l3_ick / 2 * 9 / 10; | ||
942 | else | ||
943 | rate = pipe->l3_ick / 2; | ||
944 | |||
945 | *max_rate = min(*max_rate, rate); | ||
946 | } | ||
947 | |||
948 | /* | ||
949 | * ccdc_config_sync_if - Set CCDC sync interface configuration | ||
950 | * @ccdc: Pointer to ISP CCDC device. | ||
951 | * @syncif: Structure containing the sync parameters like field state, CCDC in | ||
952 | * master/slave mode, raw/yuv data, polarity of data, field, hs, vs | ||
953 | * signals. | ||
954 | */ | ||
955 | static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, | ||
956 | struct ispccdc_syncif *syncif) | ||
957 | { | ||
958 | struct isp_device *isp = to_isp_device(ccdc); | ||
959 | u32 syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, | ||
960 | ISPCCDC_SYN_MODE); | ||
961 | |||
962 | syn_mode |= ISPCCDC_SYN_MODE_VDHDEN; | ||
963 | |||
964 | if (syncif->fldstat) | ||
965 | syn_mode |= ISPCCDC_SYN_MODE_FLDSTAT; | ||
966 | else | ||
967 | syn_mode &= ~ISPCCDC_SYN_MODE_FLDSTAT; | ||
968 | |||
969 | syn_mode &= ~ISPCCDC_SYN_MODE_DATSIZ_MASK; | ||
970 | switch (syncif->datsz) { | ||
971 | case 8: | ||
972 | syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8; | ||
973 | break; | ||
974 | case 10: | ||
975 | syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10; | ||
976 | break; | ||
977 | case 11: | ||
978 | syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11; | ||
979 | break; | ||
980 | case 12: | ||
981 | syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12; | ||
982 | break; | ||
983 | }; | ||
984 | |||
985 | if (syncif->fldmode) | ||
986 | syn_mode |= ISPCCDC_SYN_MODE_FLDMODE; | ||
987 | else | ||
988 | syn_mode &= ~ISPCCDC_SYN_MODE_FLDMODE; | ||
989 | |||
990 | if (syncif->datapol) | ||
991 | syn_mode |= ISPCCDC_SYN_MODE_DATAPOL; | ||
992 | else | ||
993 | syn_mode &= ~ISPCCDC_SYN_MODE_DATAPOL; | ||
994 | |||
995 | if (syncif->fldpol) | ||
996 | syn_mode |= ISPCCDC_SYN_MODE_FLDPOL; | ||
997 | else | ||
998 | syn_mode &= ~ISPCCDC_SYN_MODE_FLDPOL; | ||
999 | |||
1000 | if (syncif->hdpol) | ||
1001 | syn_mode |= ISPCCDC_SYN_MODE_HDPOL; | ||
1002 | else | ||
1003 | syn_mode &= ~ISPCCDC_SYN_MODE_HDPOL; | ||
1004 | |||
1005 | if (syncif->vdpol) | ||
1006 | syn_mode |= ISPCCDC_SYN_MODE_VDPOL; | ||
1007 | else | ||
1008 | syn_mode &= ~ISPCCDC_SYN_MODE_VDPOL; | ||
1009 | |||
1010 | if (syncif->ccdc_mastermode) { | ||
1011 | syn_mode |= ISPCCDC_SYN_MODE_FLDOUT | ISPCCDC_SYN_MODE_VDHDOUT; | ||
1012 | isp_reg_writel(isp, | ||
1013 | syncif->hs_width << ISPCCDC_HD_VD_WID_HDW_SHIFT | ||
1014 | | syncif->vs_width << ISPCCDC_HD_VD_WID_VDW_SHIFT, | ||
1015 | OMAP3_ISP_IOMEM_CCDC, | ||
1016 | ISPCCDC_HD_VD_WID); | ||
1017 | |||
1018 | isp_reg_writel(isp, | ||
1019 | syncif->ppln << ISPCCDC_PIX_LINES_PPLN_SHIFT | ||
1020 | | syncif->hlprf << ISPCCDC_PIX_LINES_HLPRF_SHIFT, | ||
1021 | OMAP3_ISP_IOMEM_CCDC, | ||
1022 | ISPCCDC_PIX_LINES); | ||
1023 | } else | ||
1024 | syn_mode &= ~(ISPCCDC_SYN_MODE_FLDOUT | | ||
1025 | ISPCCDC_SYN_MODE_VDHDOUT); | ||
1026 | |||
1027 | isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); | ||
1028 | |||
1029 | if (!syncif->bt_r656_en) | ||
1030 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, | ||
1031 | ISPCCDC_REC656IF_R656ON); | ||
1032 | } | ||
1033 | |||
1034 | /* CCDC formats descriptions */ | ||
1035 | static const u32 ccdc_sgrbg_pattern = | ||
1036 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC0_SHIFT | | ||
1037 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC1_SHIFT | | ||
1038 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC2_SHIFT | | ||
1039 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC3_SHIFT | | ||
1040 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC0_SHIFT | | ||
1041 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC1_SHIFT | | ||
1042 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC2_SHIFT | | ||
1043 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC3_SHIFT | | ||
1044 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC0_SHIFT | | ||
1045 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC1_SHIFT | | ||
1046 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC2_SHIFT | | ||
1047 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC3_SHIFT | | ||
1048 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC0_SHIFT | | ||
1049 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC1_SHIFT | | ||
1050 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC2_SHIFT | | ||
1051 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC3_SHIFT; | ||
1052 | |||
1053 | static const u32 ccdc_srggb_pattern = | ||
1054 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC0_SHIFT | | ||
1055 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC1_SHIFT | | ||
1056 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC2_SHIFT | | ||
1057 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC3_SHIFT | | ||
1058 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC0_SHIFT | | ||
1059 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC1_SHIFT | | ||
1060 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC2_SHIFT | | ||
1061 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC3_SHIFT | | ||
1062 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC0_SHIFT | | ||
1063 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC1_SHIFT | | ||
1064 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC2_SHIFT | | ||
1065 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC3_SHIFT | | ||
1066 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC0_SHIFT | | ||
1067 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC1_SHIFT | | ||
1068 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC2_SHIFT | | ||
1069 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC3_SHIFT; | ||
1070 | |||
1071 | static const u32 ccdc_sbggr_pattern = | ||
1072 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC0_SHIFT | | ||
1073 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC1_SHIFT | | ||
1074 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC2_SHIFT | | ||
1075 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC3_SHIFT | | ||
1076 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC0_SHIFT | | ||
1077 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC1_SHIFT | | ||
1078 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC2_SHIFT | | ||
1079 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC3_SHIFT | | ||
1080 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC0_SHIFT | | ||
1081 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC1_SHIFT | | ||
1082 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC2_SHIFT | | ||
1083 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC3_SHIFT | | ||
1084 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC0_SHIFT | | ||
1085 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC1_SHIFT | | ||
1086 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC2_SHIFT | | ||
1087 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC3_SHIFT; | ||
1088 | |||
1089 | static const u32 ccdc_sgbrg_pattern = | ||
1090 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC0_SHIFT | | ||
1091 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC1_SHIFT | | ||
1092 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC2_SHIFT | | ||
1093 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC3_SHIFT | | ||
1094 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC0_SHIFT | | ||
1095 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC1_SHIFT | | ||
1096 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC2_SHIFT | | ||
1097 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC3_SHIFT | | ||
1098 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC0_SHIFT | | ||
1099 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC1_SHIFT | | ||
1100 | ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC2_SHIFT | | ||
1101 | ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC3_SHIFT | | ||
1102 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC0_SHIFT | | ||
1103 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC1_SHIFT | | ||
1104 | ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC2_SHIFT | | ||
1105 | ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC3_SHIFT; | ||
1106 | |||
1107 | static void ccdc_configure(struct isp_ccdc_device *ccdc) | ||
1108 | { | ||
1109 | struct isp_device *isp = to_isp_device(ccdc); | ||
1110 | struct isp_parallel_platform_data *pdata = NULL; | ||
1111 | struct v4l2_subdev *sensor; | ||
1112 | struct v4l2_mbus_framefmt *format; | ||
1113 | struct media_pad *pad; | ||
1114 | unsigned long flags; | ||
1115 | u32 syn_mode; | ||
1116 | u32 ccdc_pattern; | ||
1117 | |||
1118 | if (ccdc->input == CCDC_INPUT_PARALLEL) { | ||
1119 | pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]); | ||
1120 | sensor = media_entity_to_v4l2_subdev(pad->entity); | ||
1121 | pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv) | ||
1122 | ->bus.parallel; | ||
1123 | } | ||
1124 | |||
1125 | omap3isp_configure_bridge(isp, ccdc->input, pdata); | ||
1126 | |||
1127 | ccdc->syncif.datsz = pdata ? pdata->width : 10; | ||
1128 | ccdc_config_sync_if(ccdc, &ccdc->syncif); | ||
1129 | |||
1130 | /* CCDC_PAD_SINK */ | ||
1131 | format = &ccdc->formats[CCDC_PAD_SINK]; | ||
1132 | |||
1133 | syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); | ||
1134 | |||
1135 | /* Use the raw, unprocessed data when writing to memory. The H3A and | ||
1136 | * histogram modules are still fed with lens shading corrected data. | ||
1137 | */ | ||
1138 | syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; | ||
1139 | |||
1140 | if (ccdc->output & CCDC_OUTPUT_MEMORY) | ||
1141 | syn_mode |= ISPCCDC_SYN_MODE_WEN; | ||
1142 | else | ||
1143 | syn_mode &= ~ISPCCDC_SYN_MODE_WEN; | ||
1144 | |||
1145 | if (ccdc->output & CCDC_OUTPUT_RESIZER) | ||
1146 | syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ; | ||
1147 | else | ||
1148 | syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; | ||
1149 | |||
1150 | /* Use PACK8 mode for 1byte per pixel formats. */ | ||
1151 | if (omap3isp_video_format_info(format->code)->bpp <= 8) | ||
1152 | syn_mode |= ISPCCDC_SYN_MODE_PACK8; | ||
1153 | else | ||
1154 | syn_mode &= ~ISPCCDC_SYN_MODE_PACK8; | ||
1155 | |||
1156 | isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); | ||
1157 | |||
1158 | /* Mosaic filter */ | ||
1159 | switch (format->code) { | ||
1160 | case V4L2_MBUS_FMT_SRGGB10_1X10: | ||
1161 | case V4L2_MBUS_FMT_SRGGB12_1X12: | ||
1162 | ccdc_pattern = ccdc_srggb_pattern; | ||
1163 | break; | ||
1164 | case V4L2_MBUS_FMT_SBGGR10_1X10: | ||
1165 | case V4L2_MBUS_FMT_SBGGR12_1X12: | ||
1166 | ccdc_pattern = ccdc_sbggr_pattern; | ||
1167 | break; | ||
1168 | case V4L2_MBUS_FMT_SGBRG10_1X10: | ||
1169 | case V4L2_MBUS_FMT_SGBRG12_1X12: | ||
1170 | ccdc_pattern = ccdc_sgbrg_pattern; | ||
1171 | break; | ||
1172 | default: | ||
1173 | /* Use GRBG */ | ||
1174 | ccdc_pattern = ccdc_sgrbg_pattern; | ||
1175 | break; | ||
1176 | } | ||
1177 | ccdc_config_imgattr(ccdc, ccdc_pattern); | ||
1178 | |||
1179 | /* Generate VD0 on the last line of the image and VD1 on the | ||
1180 | * 2/3 height line. | ||
1181 | */ | ||
1182 | isp_reg_writel(isp, ((format->height - 2) << ISPCCDC_VDINT_0_SHIFT) | | ||
1183 | ((format->height * 2 / 3) << ISPCCDC_VDINT_1_SHIFT), | ||
1184 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT); | ||
1185 | |||
1186 | /* CCDC_PAD_SOURCE_OF */ | ||
1187 | format = &ccdc->formats[CCDC_PAD_SOURCE_OF]; | ||
1188 | |||
1189 | isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) | | ||
1190 | ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), | ||
1191 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); | ||
1192 | isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT, | ||
1193 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); | ||
1194 | isp_reg_writel(isp, (format->height - 1) | ||
1195 | << ISPCCDC_VERT_LINES_NLV_SHIFT, | ||
1196 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); | ||
1197 | |||
1198 | ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0); | ||
1199 | |||
1200 | /* CCDC_PAD_SOURCE_VP */ | ||
1201 | format = &ccdc->formats[CCDC_PAD_SOURCE_VP]; | ||
1202 | |||
1203 | isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | | ||
1204 | (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), | ||
1205 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ); | ||
1206 | isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | | ||
1207 | ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT), | ||
1208 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT); | ||
1209 | |||
1210 | isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | | ||
1211 | (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT), | ||
1212 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT); | ||
1213 | |||
1214 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
1215 | if (ccdc->lsc.request == NULL) | ||
1216 | goto unlock; | ||
1217 | |||
1218 | WARN_ON(ccdc->lsc.active); | ||
1219 | |||
1220 | /* Get last good LSC configuration. If it is not supported for | ||
1221 | * the current active resolution discard it. | ||
1222 | */ | ||
1223 | if (ccdc->lsc.active == NULL && | ||
1224 | __ccdc_lsc_configure(ccdc, ccdc->lsc.request) == 0) { | ||
1225 | ccdc->lsc.active = ccdc->lsc.request; | ||
1226 | } else { | ||
1227 | list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue); | ||
1228 | schedule_work(&ccdc->lsc.table_work); | ||
1229 | } | ||
1230 | |||
1231 | ccdc->lsc.request = NULL; | ||
1232 | |||
1233 | unlock: | ||
1234 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
1235 | |||
1236 | ccdc_apply_controls(ccdc); | ||
1237 | } | ||
1238 | |||
1239 | static void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable) | ||
1240 | { | ||
1241 | struct isp_device *isp = to_isp_device(ccdc); | ||
1242 | |||
1243 | isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR, | ||
1244 | ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0); | ||
1245 | } | ||
1246 | |||
1247 | static int ccdc_disable(struct isp_ccdc_device *ccdc) | ||
1248 | { | ||
1249 | unsigned long flags; | ||
1250 | int ret = 0; | ||
1251 | |||
1252 | spin_lock_irqsave(&ccdc->lock, flags); | ||
1253 | if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS) | ||
1254 | ccdc->stopping = CCDC_STOP_REQUEST; | ||
1255 | spin_unlock_irqrestore(&ccdc->lock, flags); | ||
1256 | |||
1257 | ret = wait_event_timeout(ccdc->wait, | ||
1258 | ccdc->stopping == CCDC_STOP_FINISHED, | ||
1259 | msecs_to_jiffies(2000)); | ||
1260 | if (ret == 0) { | ||
1261 | ret = -ETIMEDOUT; | ||
1262 | dev_warn(to_device(ccdc), "CCDC stop timeout!\n"); | ||
1263 | } | ||
1264 | |||
1265 | omap3isp_sbl_disable(to_isp_device(ccdc), OMAP3_ISP_SBL_CCDC_LSC_READ); | ||
1266 | |||
1267 | mutex_lock(&ccdc->ioctl_lock); | ||
1268 | ccdc_lsc_free_request(ccdc, ccdc->lsc.request); | ||
1269 | ccdc->lsc.request = ccdc->lsc.active; | ||
1270 | ccdc->lsc.active = NULL; | ||
1271 | cancel_work_sync(&ccdc->lsc.table_work); | ||
1272 | ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue); | ||
1273 | mutex_unlock(&ccdc->ioctl_lock); | ||
1274 | |||
1275 | ccdc->stopping = CCDC_STOP_NOT_REQUESTED; | ||
1276 | |||
1277 | return ret > 0 ? 0 : ret; | ||
1278 | } | ||
1279 | |||
1280 | static void ccdc_enable(struct isp_ccdc_device *ccdc) | ||
1281 | { | ||
1282 | if (ccdc_lsc_is_configured(ccdc)) | ||
1283 | __ccdc_lsc_enable(ccdc, 1); | ||
1284 | __ccdc_enable(ccdc, 1); | ||
1285 | } | ||
1286 | |||
1287 | /* ----------------------------------------------------------------------------- | ||
1288 | * Interrupt handling | ||
1289 | */ | ||
1290 | |||
1291 | /* | ||
1292 | * ccdc_sbl_busy - Poll idle state of CCDC and related SBL memory write bits | ||
1293 | * @ccdc: Pointer to ISP CCDC device. | ||
1294 | * | ||
1295 | * Returns zero if the CCDC is idle and the image has been written to | ||
1296 | * memory, too. | ||
1297 | */ | ||
1298 | static int ccdc_sbl_busy(struct isp_ccdc_device *ccdc) | ||
1299 | { | ||
1300 | struct isp_device *isp = to_isp_device(ccdc); | ||
1301 | |||
1302 | return omap3isp_ccdc_busy(ccdc) | ||
1303 | | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_0) & | ||
1304 | ISPSBL_CCDC_WR_0_DATA_READY) | ||
1305 | | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_1) & | ||
1306 | ISPSBL_CCDC_WR_0_DATA_READY) | ||
1307 | | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_2) & | ||
1308 | ISPSBL_CCDC_WR_0_DATA_READY) | ||
1309 | | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_3) & | ||
1310 | ISPSBL_CCDC_WR_0_DATA_READY); | ||
1311 | } | ||
1312 | |||
1313 | /* | ||
1314 | * ccdc_sbl_wait_idle - Wait until the CCDC and related SBL are idle | ||
1315 | * @ccdc: Pointer to ISP CCDC device. | ||
1316 | * @max_wait: Max retry count in us for wait for idle/busy transition. | ||
1317 | */ | ||
1318 | static int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc, | ||
1319 | unsigned int max_wait) | ||
1320 | { | ||
1321 | unsigned int wait = 0; | ||
1322 | |||
1323 | if (max_wait == 0) | ||
1324 | max_wait = 10000; /* 10 ms */ | ||
1325 | |||
1326 | for (wait = 0; wait <= max_wait; wait++) { | ||
1327 | if (!ccdc_sbl_busy(ccdc)) | ||
1328 | return 0; | ||
1329 | |||
1330 | rmb(); | ||
1331 | udelay(1); | ||
1332 | } | ||
1333 | |||
1334 | return -EBUSY; | ||
1335 | } | ||
1336 | |||
1337 | /* __ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence | ||
1338 | * @ccdc: Pointer to ISP CCDC device. | ||
1339 | * @event: Pointing which event trigger handler | ||
1340 | * | ||
1341 | * Return 1 when the event and stopping request combination is satisfyied, | ||
1342 | * zero otherwise. | ||
1343 | */ | ||
1344 | static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event) | ||
1345 | { | ||
1346 | int rval = 0; | ||
1347 | |||
1348 | switch ((ccdc->stopping & 3) | event) { | ||
1349 | case CCDC_STOP_REQUEST | CCDC_EVENT_VD1: | ||
1350 | if (ccdc->lsc.state != LSC_STATE_STOPPED) | ||
1351 | __ccdc_lsc_enable(ccdc, 0); | ||
1352 | __ccdc_enable(ccdc, 0); | ||
1353 | ccdc->stopping = CCDC_STOP_EXECUTED; | ||
1354 | return 1; | ||
1355 | |||
1356 | case CCDC_STOP_EXECUTED | CCDC_EVENT_VD0: | ||
1357 | ccdc->stopping |= CCDC_STOP_CCDC_FINISHED; | ||
1358 | if (ccdc->lsc.state == LSC_STATE_STOPPED) | ||
1359 | ccdc->stopping |= CCDC_STOP_LSC_FINISHED; | ||
1360 | rval = 1; | ||
1361 | break; | ||
1362 | |||
1363 | case CCDC_STOP_EXECUTED | CCDC_EVENT_LSC_DONE: | ||
1364 | ccdc->stopping |= CCDC_STOP_LSC_FINISHED; | ||
1365 | rval = 1; | ||
1366 | break; | ||
1367 | |||
1368 | case CCDC_STOP_EXECUTED | CCDC_EVENT_VD1: | ||
1369 | return 1; | ||
1370 | } | ||
1371 | |||
1372 | if (ccdc->stopping == CCDC_STOP_FINISHED) { | ||
1373 | wake_up(&ccdc->wait); | ||
1374 | rval = 1; | ||
1375 | } | ||
1376 | |||
1377 | return rval; | ||
1378 | } | ||
1379 | |||
1380 | static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc) | ||
1381 | { | ||
1382 | struct video_device *vdev = &ccdc->subdev.devnode; | ||
1383 | struct v4l2_event event; | ||
1384 | |||
1385 | memset(&event, 0, sizeof(event)); | ||
1386 | event.type = V4L2_EVENT_OMAP3ISP_HS_VS; | ||
1387 | |||
1388 | v4l2_event_queue(vdev, &event); | ||
1389 | } | ||
1390 | |||
1391 | /* | ||
1392 | * ccdc_lsc_isr - Handle LSC events | ||
1393 | * @ccdc: Pointer to ISP CCDC device. | ||
1394 | * @events: LSC events | ||
1395 | */ | ||
1396 | static void ccdc_lsc_isr(struct isp_ccdc_device *ccdc, u32 events) | ||
1397 | { | ||
1398 | unsigned long flags; | ||
1399 | |||
1400 | if (events & IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ) { | ||
1401 | ccdc_lsc_error_handler(ccdc); | ||
1402 | ccdc->error = 1; | ||
1403 | dev_dbg(to_device(ccdc), "lsc prefetch error\n"); | ||
1404 | } | ||
1405 | |||
1406 | if (!(events & IRQ0STATUS_CCDC_LSC_DONE_IRQ)) | ||
1407 | return; | ||
1408 | |||
1409 | /* LSC_DONE interrupt occur, there are two cases | ||
1410 | * 1. stopping for reconfiguration | ||
1411 | * 2. stopping because of STREAM OFF command | ||
1412 | */ | ||
1413 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
1414 | |||
1415 | if (ccdc->lsc.state == LSC_STATE_STOPPING) | ||
1416 | ccdc->lsc.state = LSC_STATE_STOPPED; | ||
1417 | |||
1418 | if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE)) | ||
1419 | goto done; | ||
1420 | |||
1421 | if (ccdc->lsc.state != LSC_STATE_RECONFIG) | ||
1422 | goto done; | ||
1423 | |||
1424 | /* LSC is in STOPPING state, change to the new state */ | ||
1425 | ccdc->lsc.state = LSC_STATE_STOPPED; | ||
1426 | |||
1427 | /* This is an exception. Start of frame and LSC_DONE interrupt | ||
1428 | * have been received on the same time. Skip this event and wait | ||
1429 | * for better times. | ||
1430 | */ | ||
1431 | if (events & IRQ0STATUS_HS_VS_IRQ) | ||
1432 | goto done; | ||
1433 | |||
1434 | /* The LSC engine is stopped at this point. Enable it if there's a | ||
1435 | * pending request. | ||
1436 | */ | ||
1437 | if (ccdc->lsc.request == NULL) | ||
1438 | goto done; | ||
1439 | |||
1440 | ccdc_lsc_enable(ccdc); | ||
1441 | |||
1442 | done: | ||
1443 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
1444 | } | ||
1445 | |||
1446 | static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) | ||
1447 | { | ||
1448 | struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); | ||
1449 | struct isp_device *isp = to_isp_device(ccdc); | ||
1450 | struct isp_buffer *buffer; | ||
1451 | int restart = 0; | ||
1452 | |||
1453 | /* The CCDC generates VD0 interrupts even when disabled (the datasheet | ||
1454 | * doesn't explicitly state if that's supposed to happen or not, so it | ||
1455 | * can be considered as a hardware bug or as a feature, but we have to | ||
1456 | * deal with it anyway). Disabling the CCDC when no buffer is available | ||
1457 | * would thus not be enough, we need to handle the situation explicitly. | ||
1458 | */ | ||
1459 | if (list_empty(&ccdc->video_out.dmaqueue)) | ||
1460 | goto done; | ||
1461 | |||
1462 | /* We're in continuous mode, and memory writes were disabled due to a | ||
1463 | * buffer underrun. Reenable them now that we have a buffer. The buffer | ||
1464 | * address has been set in ccdc_video_queue. | ||
1465 | */ | ||
1466 | if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) { | ||
1467 | restart = 1; | ||
1468 | ccdc->underrun = 0; | ||
1469 | goto done; | ||
1470 | } | ||
1471 | |||
1472 | if (ccdc_sbl_wait_idle(ccdc, 1000)) { | ||
1473 | dev_info(isp->dev, "CCDC won't become idle!\n"); | ||
1474 | goto done; | ||
1475 | } | ||
1476 | |||
1477 | buffer = omap3isp_video_buffer_next(&ccdc->video_out, ccdc->error); | ||
1478 | if (buffer != NULL) { | ||
1479 | ccdc_set_outaddr(ccdc, buffer->isp_addr); | ||
1480 | restart = 1; | ||
1481 | } | ||
1482 | |||
1483 | pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; | ||
1484 | |||
1485 | if (ccdc->state == ISP_PIPELINE_STREAM_SINGLESHOT && | ||
1486 | isp_pipeline_ready(pipe)) | ||
1487 | omap3isp_pipeline_set_stream(pipe, | ||
1488 | ISP_PIPELINE_STREAM_SINGLESHOT); | ||
1489 | |||
1490 | done: | ||
1491 | ccdc->error = 0; | ||
1492 | return restart; | ||
1493 | } | ||
1494 | |||
1495 | /* | ||
1496 | * ccdc_vd0_isr - Handle VD0 event | ||
1497 | * @ccdc: Pointer to ISP CCDC device. | ||
1498 | * | ||
1499 | * Executes LSC deferred enablement before next frame starts. | ||
1500 | */ | ||
1501 | static void ccdc_vd0_isr(struct isp_ccdc_device *ccdc) | ||
1502 | { | ||
1503 | unsigned long flags; | ||
1504 | int restart = 0; | ||
1505 | |||
1506 | if (ccdc->output & CCDC_OUTPUT_MEMORY) | ||
1507 | restart = ccdc_isr_buffer(ccdc); | ||
1508 | |||
1509 | spin_lock_irqsave(&ccdc->lock, flags); | ||
1510 | if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) { | ||
1511 | spin_unlock_irqrestore(&ccdc->lock, flags); | ||
1512 | return; | ||
1513 | } | ||
1514 | |||
1515 | if (!ccdc->shadow_update) | ||
1516 | ccdc_apply_controls(ccdc); | ||
1517 | spin_unlock_irqrestore(&ccdc->lock, flags); | ||
1518 | |||
1519 | if (restart) | ||
1520 | ccdc_enable(ccdc); | ||
1521 | } | ||
1522 | |||
1523 | /* | ||
1524 | * ccdc_vd1_isr - Handle VD1 event | ||
1525 | * @ccdc: Pointer to ISP CCDC device. | ||
1526 | */ | ||
1527 | static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc) | ||
1528 | { | ||
1529 | unsigned long flags; | ||
1530 | |||
1531 | spin_lock_irqsave(&ccdc->lsc.req_lock, flags); | ||
1532 | |||
1533 | /* | ||
1534 | * Depending on the CCDC pipeline state, CCDC stopping should be | ||
1535 | * handled differently. In SINGLESHOT we emulate an internal CCDC | ||
1536 | * stopping because the CCDC hw works only in continuous mode. | ||
1537 | * When CONTINUOUS pipeline state is used and the CCDC writes it's | ||
1538 | * data to memory the CCDC and LSC are stopped immediately but | ||
1539 | * without change the CCDC stopping state machine. The CCDC | ||
1540 | * stopping state machine should be used only when user request | ||
1541 | * for stopping is received (SINGLESHOT is an exeption). | ||
1542 | */ | ||
1543 | switch (ccdc->state) { | ||
1544 | case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
1545 | ccdc->stopping = CCDC_STOP_REQUEST; | ||
1546 | break; | ||
1547 | |||
1548 | case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
1549 | if (ccdc->output & CCDC_OUTPUT_MEMORY) { | ||
1550 | if (ccdc->lsc.state != LSC_STATE_STOPPED) | ||
1551 | __ccdc_lsc_enable(ccdc, 0); | ||
1552 | __ccdc_enable(ccdc, 0); | ||
1553 | } | ||
1554 | break; | ||
1555 | |||
1556 | case ISP_PIPELINE_STREAM_STOPPED: | ||
1557 | break; | ||
1558 | } | ||
1559 | |||
1560 | if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1)) | ||
1561 | goto done; | ||
1562 | |||
1563 | if (ccdc->lsc.request == NULL) | ||
1564 | goto done; | ||
1565 | |||
1566 | /* | ||
1567 | * LSC need to be reconfigured. Stop it here and on next LSC_DONE IRQ | ||
1568 | * do the appropriate changes in registers | ||
1569 | */ | ||
1570 | if (ccdc->lsc.state == LSC_STATE_RUNNING) { | ||
1571 | __ccdc_lsc_enable(ccdc, 0); | ||
1572 | ccdc->lsc.state = LSC_STATE_RECONFIG; | ||
1573 | goto done; | ||
1574 | } | ||
1575 | |||
1576 | /* LSC has been in STOPPED state, enable it */ | ||
1577 | if (ccdc->lsc.state == LSC_STATE_STOPPED) | ||
1578 | ccdc_lsc_enable(ccdc); | ||
1579 | |||
1580 | done: | ||
1581 | spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); | ||
1582 | } | ||
1583 | |||
1584 | /* | ||
1585 | * omap3isp_ccdc_isr - Configure CCDC during interframe time. | ||
1586 | * @ccdc: Pointer to ISP CCDC device. | ||
1587 | * @events: CCDC events | ||
1588 | */ | ||
1589 | int omap3isp_ccdc_isr(struct isp_ccdc_device *ccdc, u32 events) | ||
1590 | { | ||
1591 | if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) | ||
1592 | return 0; | ||
1593 | |||
1594 | if (events & IRQ0STATUS_CCDC_VD1_IRQ) | ||
1595 | ccdc_vd1_isr(ccdc); | ||
1596 | |||
1597 | ccdc_lsc_isr(ccdc, events); | ||
1598 | |||
1599 | if (events & IRQ0STATUS_CCDC_VD0_IRQ) | ||
1600 | ccdc_vd0_isr(ccdc); | ||
1601 | |||
1602 | if (events & IRQ0STATUS_HS_VS_IRQ) | ||
1603 | ccdc_hs_vs_isr(ccdc); | ||
1604 | |||
1605 | return 0; | ||
1606 | } | ||
1607 | |||
1608 | /* ----------------------------------------------------------------------------- | ||
1609 | * ISP video operations | ||
1610 | */ | ||
1611 | |||
1612 | static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer) | ||
1613 | { | ||
1614 | struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc; | ||
1615 | |||
1616 | if (!(ccdc->output & CCDC_OUTPUT_MEMORY)) | ||
1617 | return -ENODEV; | ||
1618 | |||
1619 | ccdc_set_outaddr(ccdc, buffer->isp_addr); | ||
1620 | |||
1621 | /* We now have a buffer queued on the output, restart the pipeline in | ||
1622 | * on the next CCDC interrupt if running in continuous mode (or when | ||
1623 | * starting the stream). | ||
1624 | */ | ||
1625 | ccdc->underrun = 1; | ||
1626 | |||
1627 | return 0; | ||
1628 | } | ||
1629 | |||
1630 | static const struct isp_video_operations ccdc_video_ops = { | ||
1631 | .queue = ccdc_video_queue, | ||
1632 | }; | ||
1633 | |||
1634 | /* ----------------------------------------------------------------------------- | ||
1635 | * V4L2 subdev operations | ||
1636 | */ | ||
1637 | |||
1638 | /* | ||
1639 | * ccdc_ioctl - CCDC module private ioctl's | ||
1640 | * @sd: ISP CCDC V4L2 subdevice | ||
1641 | * @cmd: ioctl command | ||
1642 | * @arg: ioctl argument | ||
1643 | * | ||
1644 | * Return 0 on success or a negative error code otherwise. | ||
1645 | */ | ||
1646 | static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
1647 | { | ||
1648 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
1649 | int ret; | ||
1650 | |||
1651 | switch (cmd) { | ||
1652 | case VIDIOC_OMAP3ISP_CCDC_CFG: | ||
1653 | mutex_lock(&ccdc->ioctl_lock); | ||
1654 | ret = ccdc_config(ccdc, arg); | ||
1655 | mutex_unlock(&ccdc->ioctl_lock); | ||
1656 | break; | ||
1657 | |||
1658 | default: | ||
1659 | return -ENOIOCTLCMD; | ||
1660 | } | ||
1661 | |||
1662 | return ret; | ||
1663 | } | ||
1664 | |||
1665 | static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, | ||
1666 | struct v4l2_event_subscription *sub) | ||
1667 | { | ||
1668 | if (sub->type != V4L2_EVENT_OMAP3ISP_HS_VS) | ||
1669 | return -EINVAL; | ||
1670 | |||
1671 | return v4l2_event_subscribe(fh, sub); | ||
1672 | } | ||
1673 | |||
1674 | static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, | ||
1675 | struct v4l2_event_subscription *sub) | ||
1676 | { | ||
1677 | return v4l2_event_unsubscribe(fh, sub); | ||
1678 | } | ||
1679 | |||
1680 | /* | ||
1681 | * ccdc_set_stream - Enable/Disable streaming on the CCDC module | ||
1682 | * @sd: ISP CCDC V4L2 subdevice | ||
1683 | * @enable: Enable/disable stream | ||
1684 | * | ||
1685 | * When writing to memory, the CCDC hardware can't be enabled without a memory | ||
1686 | * buffer to write to. As the s_stream operation is called in response to a | ||
1687 | * STREAMON call without any buffer queued yet, just update the enabled field | ||
1688 | * and return immediately. The CCDC will be enabled in ccdc_isr_buffer(). | ||
1689 | * | ||
1690 | * When not writing to memory enable the CCDC immediately. | ||
1691 | */ | ||
1692 | static int ccdc_set_stream(struct v4l2_subdev *sd, int enable) | ||
1693 | { | ||
1694 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
1695 | struct isp_device *isp = to_isp_device(ccdc); | ||
1696 | int ret = 0; | ||
1697 | |||
1698 | if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) { | ||
1699 | if (enable == ISP_PIPELINE_STREAM_STOPPED) | ||
1700 | return 0; | ||
1701 | |||
1702 | omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_CCDC); | ||
1703 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, | ||
1704 | ISPCCDC_CFG_VDLC); | ||
1705 | |||
1706 | ccdc_configure(ccdc); | ||
1707 | |||
1708 | /* TODO: Don't configure the video port if all of its output | ||
1709 | * links are inactive. | ||
1710 | */ | ||
1711 | ccdc_config_vp(ccdc); | ||
1712 | ccdc_enable_vp(ccdc, 1); | ||
1713 | ccdc->error = 0; | ||
1714 | ccdc_print_status(ccdc); | ||
1715 | } | ||
1716 | |||
1717 | switch (enable) { | ||
1718 | case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
1719 | if (ccdc->output & CCDC_OUTPUT_MEMORY) | ||
1720 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE); | ||
1721 | |||
1722 | if (ccdc->underrun || !(ccdc->output & CCDC_OUTPUT_MEMORY)) | ||
1723 | ccdc_enable(ccdc); | ||
1724 | |||
1725 | ccdc->underrun = 0; | ||
1726 | break; | ||
1727 | |||
1728 | case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
1729 | if (ccdc->output & CCDC_OUTPUT_MEMORY && | ||
1730 | ccdc->state != ISP_PIPELINE_STREAM_SINGLESHOT) | ||
1731 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE); | ||
1732 | |||
1733 | ccdc_enable(ccdc); | ||
1734 | break; | ||
1735 | |||
1736 | case ISP_PIPELINE_STREAM_STOPPED: | ||
1737 | ret = ccdc_disable(ccdc); | ||
1738 | if (ccdc->output & CCDC_OUTPUT_MEMORY) | ||
1739 | omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CCDC_WRITE); | ||
1740 | omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_CCDC); | ||
1741 | ccdc->underrun = 0; | ||
1742 | break; | ||
1743 | } | ||
1744 | |||
1745 | ccdc->state = enable; | ||
1746 | return ret; | ||
1747 | } | ||
1748 | |||
1749 | static struct v4l2_mbus_framefmt * | ||
1750 | __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | ||
1751 | unsigned int pad, enum v4l2_subdev_format_whence which) | ||
1752 | { | ||
1753 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
1754 | return v4l2_subdev_get_try_format(fh, pad); | ||
1755 | else | ||
1756 | return &ccdc->formats[pad]; | ||
1757 | } | ||
1758 | |||
1759 | /* | ||
1760 | * ccdc_try_format - Try video format on a pad | ||
1761 | * @ccdc: ISP CCDC device | ||
1762 | * @fh : V4L2 subdev file handle | ||
1763 | * @pad: Pad number | ||
1764 | * @fmt: Format | ||
1765 | */ | ||
1766 | static void | ||
1767 | ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | ||
1768 | unsigned int pad, struct v4l2_mbus_framefmt *fmt, | ||
1769 | enum v4l2_subdev_format_whence which) | ||
1770 | { | ||
1771 | struct v4l2_mbus_framefmt *format; | ||
1772 | const struct isp_format_info *info; | ||
1773 | unsigned int width = fmt->width; | ||
1774 | unsigned int height = fmt->height; | ||
1775 | unsigned int i; | ||
1776 | |||
1777 | switch (pad) { | ||
1778 | case CCDC_PAD_SINK: | ||
1779 | /* TODO: If the CCDC output formatter pad is connected directly | ||
1780 | * to the resizer, only YUV formats can be used. | ||
1781 | */ | ||
1782 | for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) { | ||
1783 | if (fmt->code == ccdc_fmts[i]) | ||
1784 | break; | ||
1785 | } | ||
1786 | |||
1787 | /* If not found, use SGRBG10 as default */ | ||
1788 | if (i >= ARRAY_SIZE(ccdc_fmts)) | ||
1789 | fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
1790 | |||
1791 | /* Clamp the input size. */ | ||
1792 | fmt->width = clamp_t(u32, width, 32, 4096); | ||
1793 | fmt->height = clamp_t(u32, height, 32, 4096); | ||
1794 | break; | ||
1795 | |||
1796 | case CCDC_PAD_SOURCE_OF: | ||
1797 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); | ||
1798 | memcpy(fmt, format, sizeof(*fmt)); | ||
1799 | |||
1800 | /* The data formatter truncates the number of horizontal output | ||
1801 | * pixels to a multiple of 16. To avoid clipping data, allow | ||
1802 | * callers to request an output size bigger than the input size | ||
1803 | * up to the nearest multiple of 16. | ||
1804 | */ | ||
1805 | fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15); | ||
1806 | fmt->width &= ~15; | ||
1807 | fmt->height = clamp_t(u32, height, 32, fmt->height); | ||
1808 | break; | ||
1809 | |||
1810 | case CCDC_PAD_SOURCE_VP: | ||
1811 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); | ||
1812 | memcpy(fmt, format, sizeof(*fmt)); | ||
1813 | |||
1814 | /* The video port interface truncates the data to 10 bits. */ | ||
1815 | info = omap3isp_video_format_info(fmt->code); | ||
1816 | fmt->code = info->truncated; | ||
1817 | |||
1818 | /* The number of lines that can be clocked out from the video | ||
1819 | * port output must be at least one line less than the number | ||
1820 | * of input lines. | ||
1821 | */ | ||
1822 | fmt->width = clamp_t(u32, width, 32, fmt->width); | ||
1823 | fmt->height = clamp_t(u32, height, 32, fmt->height - 1); | ||
1824 | break; | ||
1825 | } | ||
1826 | |||
1827 | /* Data is written to memory unpacked, each 10-bit or 12-bit pixel is | ||
1828 | * stored on 2 bytes. | ||
1829 | */ | ||
1830 | fmt->colorspace = V4L2_COLORSPACE_SRGB; | ||
1831 | fmt->field = V4L2_FIELD_NONE; | ||
1832 | } | ||
1833 | |||
1834 | /* | ||
1835 | * ccdc_enum_mbus_code - Handle pixel format enumeration | ||
1836 | * @sd : pointer to v4l2 subdev structure | ||
1837 | * @fh : V4L2 subdev file handle | ||
1838 | * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
1839 | * return -EINVAL or zero on success | ||
1840 | */ | ||
1841 | static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, | ||
1842 | struct v4l2_subdev_fh *fh, | ||
1843 | struct v4l2_subdev_mbus_code_enum *code) | ||
1844 | { | ||
1845 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
1846 | struct v4l2_mbus_framefmt *format; | ||
1847 | |||
1848 | switch (code->pad) { | ||
1849 | case CCDC_PAD_SINK: | ||
1850 | if (code->index >= ARRAY_SIZE(ccdc_fmts)) | ||
1851 | return -EINVAL; | ||
1852 | |||
1853 | code->code = ccdc_fmts[code->index]; | ||
1854 | break; | ||
1855 | |||
1856 | case CCDC_PAD_SOURCE_OF: | ||
1857 | case CCDC_PAD_SOURCE_VP: | ||
1858 | /* No format conversion inside CCDC */ | ||
1859 | if (code->index != 0) | ||
1860 | return -EINVAL; | ||
1861 | |||
1862 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, | ||
1863 | V4L2_SUBDEV_FORMAT_TRY); | ||
1864 | |||
1865 | code->code = format->code; | ||
1866 | break; | ||
1867 | |||
1868 | default: | ||
1869 | return -EINVAL; | ||
1870 | } | ||
1871 | |||
1872 | return 0; | ||
1873 | } | ||
1874 | |||
1875 | static int ccdc_enum_frame_size(struct v4l2_subdev *sd, | ||
1876 | struct v4l2_subdev_fh *fh, | ||
1877 | struct v4l2_subdev_frame_size_enum *fse) | ||
1878 | { | ||
1879 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
1880 | struct v4l2_mbus_framefmt format; | ||
1881 | |||
1882 | if (fse->index != 0) | ||
1883 | return -EINVAL; | ||
1884 | |||
1885 | format.code = fse->code; | ||
1886 | format.width = 1; | ||
1887 | format.height = 1; | ||
1888 | ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
1889 | fse->min_width = format.width; | ||
1890 | fse->min_height = format.height; | ||
1891 | |||
1892 | if (format.code != fse->code) | ||
1893 | return -EINVAL; | ||
1894 | |||
1895 | format.code = fse->code; | ||
1896 | format.width = -1; | ||
1897 | format.height = -1; | ||
1898 | ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
1899 | fse->max_width = format.width; | ||
1900 | fse->max_height = format.height; | ||
1901 | |||
1902 | return 0; | ||
1903 | } | ||
1904 | |||
1905 | /* | ||
1906 | * ccdc_get_format - Retrieve the video format on a pad | ||
1907 | * @sd : ISP CCDC V4L2 subdevice | ||
1908 | * @fh : V4L2 subdev file handle | ||
1909 | * @fmt: Format | ||
1910 | * | ||
1911 | * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond | ||
1912 | * to the format type. | ||
1913 | */ | ||
1914 | static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1915 | struct v4l2_subdev_format *fmt) | ||
1916 | { | ||
1917 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
1918 | struct v4l2_mbus_framefmt *format; | ||
1919 | |||
1920 | format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); | ||
1921 | if (format == NULL) | ||
1922 | return -EINVAL; | ||
1923 | |||
1924 | fmt->format = *format; | ||
1925 | return 0; | ||
1926 | } | ||
1927 | |||
1928 | /* | ||
1929 | * ccdc_set_format - Set the video format on a pad | ||
1930 | * @sd : ISP CCDC V4L2 subdevice | ||
1931 | * @fh : V4L2 subdev file handle | ||
1932 | * @fmt: Format | ||
1933 | * | ||
1934 | * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond | ||
1935 | * to the format type. | ||
1936 | */ | ||
1937 | static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1938 | struct v4l2_subdev_format *fmt) | ||
1939 | { | ||
1940 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
1941 | struct v4l2_mbus_framefmt *format; | ||
1942 | |||
1943 | format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); | ||
1944 | if (format == NULL) | ||
1945 | return -EINVAL; | ||
1946 | |||
1947 | ccdc_try_format(ccdc, fh, fmt->pad, &fmt->format, fmt->which); | ||
1948 | *format = fmt->format; | ||
1949 | |||
1950 | /* Propagate the format from sink to source */ | ||
1951 | if (fmt->pad == CCDC_PAD_SINK) { | ||
1952 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, | ||
1953 | fmt->which); | ||
1954 | *format = fmt->format; | ||
1955 | ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, | ||
1956 | fmt->which); | ||
1957 | |||
1958 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_VP, | ||
1959 | fmt->which); | ||
1960 | *format = fmt->format; | ||
1961 | ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_VP, format, | ||
1962 | fmt->which); | ||
1963 | } | ||
1964 | |||
1965 | return 0; | ||
1966 | } | ||
1967 | |||
1968 | /* | ||
1969 | * ccdc_init_formats - Initialize formats on all pads | ||
1970 | * @sd: ISP CCDC V4L2 subdevice | ||
1971 | * @fh: V4L2 subdev file handle | ||
1972 | * | ||
1973 | * Initialize all pad formats with default values. If fh is not NULL, try | ||
1974 | * formats are initialized on the file handle. Otherwise active formats are | ||
1975 | * initialized on the device. | ||
1976 | */ | ||
1977 | static int ccdc_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
1978 | { | ||
1979 | struct v4l2_subdev_format format; | ||
1980 | |||
1981 | memset(&format, 0, sizeof(format)); | ||
1982 | format.pad = CCDC_PAD_SINK; | ||
1983 | format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
1984 | format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
1985 | format.format.width = 4096; | ||
1986 | format.format.height = 4096; | ||
1987 | ccdc_set_format(sd, fh, &format); | ||
1988 | |||
1989 | return 0; | ||
1990 | } | ||
1991 | |||
1992 | /* V4L2 subdev core operations */ | ||
1993 | static const struct v4l2_subdev_core_ops ccdc_v4l2_core_ops = { | ||
1994 | .ioctl = ccdc_ioctl, | ||
1995 | .subscribe_event = ccdc_subscribe_event, | ||
1996 | .unsubscribe_event = ccdc_unsubscribe_event, | ||
1997 | }; | ||
1998 | |||
1999 | /* V4L2 subdev video operations */ | ||
2000 | static const struct v4l2_subdev_video_ops ccdc_v4l2_video_ops = { | ||
2001 | .s_stream = ccdc_set_stream, | ||
2002 | }; | ||
2003 | |||
2004 | /* V4L2 subdev pad operations */ | ||
2005 | static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { | ||
2006 | .enum_mbus_code = ccdc_enum_mbus_code, | ||
2007 | .enum_frame_size = ccdc_enum_frame_size, | ||
2008 | .get_fmt = ccdc_get_format, | ||
2009 | .set_fmt = ccdc_set_format, | ||
2010 | }; | ||
2011 | |||
2012 | /* V4L2 subdev operations */ | ||
2013 | static const struct v4l2_subdev_ops ccdc_v4l2_ops = { | ||
2014 | .core = &ccdc_v4l2_core_ops, | ||
2015 | .video = &ccdc_v4l2_video_ops, | ||
2016 | .pad = &ccdc_v4l2_pad_ops, | ||
2017 | }; | ||
2018 | |||
2019 | /* V4L2 subdev internal operations */ | ||
2020 | static const struct v4l2_subdev_internal_ops ccdc_v4l2_internal_ops = { | ||
2021 | .open = ccdc_init_formats, | ||
2022 | }; | ||
2023 | |||
2024 | /* ----------------------------------------------------------------------------- | ||
2025 | * Media entity operations | ||
2026 | */ | ||
2027 | |||
2028 | /* | ||
2029 | * ccdc_link_setup - Setup CCDC connections | ||
2030 | * @entity: CCDC media entity | ||
2031 | * @local: Pad at the local end of the link | ||
2032 | * @remote: Pad at the remote end of the link | ||
2033 | * @flags: Link flags | ||
2034 | * | ||
2035 | * return -EINVAL or zero on success | ||
2036 | */ | ||
2037 | static int ccdc_link_setup(struct media_entity *entity, | ||
2038 | const struct media_pad *local, | ||
2039 | const struct media_pad *remote, u32 flags) | ||
2040 | { | ||
2041 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
2042 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
2043 | struct isp_device *isp = to_isp_device(ccdc); | ||
2044 | |||
2045 | switch (local->index | media_entity_type(remote->entity)) { | ||
2046 | case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
2047 | /* Read from the sensor (parallel interface), CCP2, CSI2a or | ||
2048 | * CSI2c. | ||
2049 | */ | ||
2050 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
2051 | ccdc->input = CCDC_INPUT_NONE; | ||
2052 | break; | ||
2053 | } | ||
2054 | |||
2055 | if (ccdc->input != CCDC_INPUT_NONE) | ||
2056 | return -EBUSY; | ||
2057 | |||
2058 | if (remote->entity == &isp->isp_ccp2.subdev.entity) | ||
2059 | ccdc->input = CCDC_INPUT_CCP2B; | ||
2060 | else if (remote->entity == &isp->isp_csi2a.subdev.entity) | ||
2061 | ccdc->input = CCDC_INPUT_CSI2A; | ||
2062 | else if (remote->entity == &isp->isp_csi2c.subdev.entity) | ||
2063 | ccdc->input = CCDC_INPUT_CSI2C; | ||
2064 | else | ||
2065 | ccdc->input = CCDC_INPUT_PARALLEL; | ||
2066 | |||
2067 | break; | ||
2068 | |||
2069 | /* | ||
2070 | * The ISP core doesn't support pipelines with multiple video outputs. | ||
2071 | * Revisit this when it will be implemented, and return -EBUSY for now. | ||
2072 | */ | ||
2073 | |||
2074 | case CCDC_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: | ||
2075 | /* Write to preview engine, histogram and H3A. When none of | ||
2076 | * those links are active, the video port can be disabled. | ||
2077 | */ | ||
2078 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
2079 | if (ccdc->output & ~CCDC_OUTPUT_PREVIEW) | ||
2080 | return -EBUSY; | ||
2081 | ccdc->output |= CCDC_OUTPUT_PREVIEW; | ||
2082 | } else { | ||
2083 | ccdc->output &= ~CCDC_OUTPUT_PREVIEW; | ||
2084 | } | ||
2085 | break; | ||
2086 | |||
2087 | case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_DEVNODE: | ||
2088 | /* Write to memory */ | ||
2089 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
2090 | if (ccdc->output & ~CCDC_OUTPUT_MEMORY) | ||
2091 | return -EBUSY; | ||
2092 | ccdc->output |= CCDC_OUTPUT_MEMORY; | ||
2093 | } else { | ||
2094 | ccdc->output &= ~CCDC_OUTPUT_MEMORY; | ||
2095 | } | ||
2096 | break; | ||
2097 | |||
2098 | case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_V4L2_SUBDEV: | ||
2099 | /* Write to resizer */ | ||
2100 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
2101 | if (ccdc->output & ~CCDC_OUTPUT_RESIZER) | ||
2102 | return -EBUSY; | ||
2103 | ccdc->output |= CCDC_OUTPUT_RESIZER; | ||
2104 | } else { | ||
2105 | ccdc->output &= ~CCDC_OUTPUT_RESIZER; | ||
2106 | } | ||
2107 | break; | ||
2108 | |||
2109 | default: | ||
2110 | return -EINVAL; | ||
2111 | } | ||
2112 | |||
2113 | return 0; | ||
2114 | } | ||
2115 | |||
2116 | /* media operations */ | ||
2117 | static const struct media_entity_operations ccdc_media_ops = { | ||
2118 | .link_setup = ccdc_link_setup, | ||
2119 | }; | ||
2120 | |||
2121 | /* | ||
2122 | * ccdc_init_entities - Initialize V4L2 subdev and media entity | ||
2123 | * @ccdc: ISP CCDC module | ||
2124 | * | ||
2125 | * Return 0 on success and a negative error code on failure. | ||
2126 | */ | ||
2127 | static int ccdc_init_entities(struct isp_ccdc_device *ccdc) | ||
2128 | { | ||
2129 | struct v4l2_subdev *sd = &ccdc->subdev; | ||
2130 | struct media_pad *pads = ccdc->pads; | ||
2131 | struct media_entity *me = &sd->entity; | ||
2132 | int ret; | ||
2133 | |||
2134 | ccdc->input = CCDC_INPUT_NONE; | ||
2135 | |||
2136 | v4l2_subdev_init(sd, &ccdc_v4l2_ops); | ||
2137 | sd->internal_ops = &ccdc_v4l2_internal_ops; | ||
2138 | strlcpy(sd->name, "OMAP3 ISP CCDC", sizeof(sd->name)); | ||
2139 | sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
2140 | v4l2_set_subdevdata(sd, ccdc); | ||
2141 | sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
2142 | sd->nevents = OMAP3ISP_CCDC_NEVENTS; | ||
2143 | |||
2144 | pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
2145 | pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE; | ||
2146 | pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE; | ||
2147 | |||
2148 | me->ops = &ccdc_media_ops; | ||
2149 | ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0); | ||
2150 | if (ret < 0) | ||
2151 | return ret; | ||
2152 | |||
2153 | ccdc_init_formats(sd, NULL); | ||
2154 | |||
2155 | ccdc->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
2156 | ccdc->video_out.ops = &ccdc_video_ops; | ||
2157 | ccdc->video_out.isp = to_isp_device(ccdc); | ||
2158 | ccdc->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; | ||
2159 | ccdc->video_out.bpl_alignment = 32; | ||
2160 | |||
2161 | ret = omap3isp_video_init(&ccdc->video_out, "CCDC"); | ||
2162 | if (ret < 0) | ||
2163 | return ret; | ||
2164 | |||
2165 | /* Connect the CCDC subdev to the video node. */ | ||
2166 | ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF, | ||
2167 | &ccdc->video_out.video.entity, 0, 0); | ||
2168 | if (ret < 0) | ||
2169 | return ret; | ||
2170 | |||
2171 | return 0; | ||
2172 | } | ||
2173 | |||
2174 | void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc) | ||
2175 | { | ||
2176 | media_entity_cleanup(&ccdc->subdev.entity); | ||
2177 | |||
2178 | v4l2_device_unregister_subdev(&ccdc->subdev); | ||
2179 | omap3isp_video_unregister(&ccdc->video_out); | ||
2180 | } | ||
2181 | |||
2182 | int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc, | ||
2183 | struct v4l2_device *vdev) | ||
2184 | { | ||
2185 | int ret; | ||
2186 | |||
2187 | /* Register the subdev and video node. */ | ||
2188 | ret = v4l2_device_register_subdev(vdev, &ccdc->subdev); | ||
2189 | if (ret < 0) | ||
2190 | goto error; | ||
2191 | |||
2192 | ret = omap3isp_video_register(&ccdc->video_out, vdev); | ||
2193 | if (ret < 0) | ||
2194 | goto error; | ||
2195 | |||
2196 | return 0; | ||
2197 | |||
2198 | error: | ||
2199 | omap3isp_ccdc_unregister_entities(ccdc); | ||
2200 | return ret; | ||
2201 | } | ||
2202 | |||
2203 | /* ----------------------------------------------------------------------------- | ||
2204 | * ISP CCDC initialisation and cleanup | ||
2205 | */ | ||
2206 | |||
2207 | /* | ||
2208 | * omap3isp_ccdc_init - CCDC module initialization. | ||
2209 | * @dev: Device pointer specific to the OMAP3 ISP. | ||
2210 | * | ||
2211 | * TODO: Get the initialisation values from platform data. | ||
2212 | * | ||
2213 | * Return 0 on success or a negative error code otherwise. | ||
2214 | */ | ||
2215 | int omap3isp_ccdc_init(struct isp_device *isp) | ||
2216 | { | ||
2217 | struct isp_ccdc_device *ccdc = &isp->isp_ccdc; | ||
2218 | |||
2219 | spin_lock_init(&ccdc->lock); | ||
2220 | init_waitqueue_head(&ccdc->wait); | ||
2221 | mutex_init(&ccdc->ioctl_lock); | ||
2222 | |||
2223 | ccdc->stopping = CCDC_STOP_NOT_REQUESTED; | ||
2224 | |||
2225 | INIT_WORK(&ccdc->lsc.table_work, ccdc_lsc_free_table_work); | ||
2226 | ccdc->lsc.state = LSC_STATE_STOPPED; | ||
2227 | INIT_LIST_HEAD(&ccdc->lsc.free_queue); | ||
2228 | spin_lock_init(&ccdc->lsc.req_lock); | ||
2229 | |||
2230 | ccdc->syncif.ccdc_mastermode = 0; | ||
2231 | ccdc->syncif.datapol = 0; | ||
2232 | ccdc->syncif.datsz = 0; | ||
2233 | ccdc->syncif.fldmode = 0; | ||
2234 | ccdc->syncif.fldout = 0; | ||
2235 | ccdc->syncif.fldpol = 0; | ||
2236 | ccdc->syncif.fldstat = 0; | ||
2237 | ccdc->syncif.hdpol = 0; | ||
2238 | ccdc->syncif.vdpol = 0; | ||
2239 | |||
2240 | ccdc->clamp.oblen = 0; | ||
2241 | ccdc->clamp.dcsubval = 0; | ||
2242 | |||
2243 | ccdc->vpcfg.pixelclk = 0; | ||
2244 | |||
2245 | ccdc->update = OMAP3ISP_CCDC_BLCLAMP; | ||
2246 | ccdc_apply_controls(ccdc); | ||
2247 | |||
2248 | return ccdc_init_entities(ccdc); | ||
2249 | } | ||
2250 | |||
2251 | /* | ||
2252 | * omap3isp_ccdc_cleanup - CCDC module cleanup. | ||
2253 | * @dev: Device pointer specific to the OMAP3 ISP. | ||
2254 | */ | ||
2255 | void omap3isp_ccdc_cleanup(struct isp_device *isp) | ||
2256 | { | ||
2257 | struct isp_ccdc_device *ccdc = &isp->isp_ccdc; | ||
2258 | |||
2259 | /* Free LSC requests. As the CCDC is stopped there's no active request, | ||
2260 | * so only the pending request and the free queue need to be handled. | ||
2261 | */ | ||
2262 | ccdc_lsc_free_request(ccdc, ccdc->lsc.request); | ||
2263 | cancel_work_sync(&ccdc->lsc.table_work); | ||
2264 | ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue); | ||
2265 | |||
2266 | if (ccdc->fpc.fpcaddr != 0) | ||
2267 | iommu_vfree(isp->iommu, ccdc->fpc.fpcaddr); | ||
2268 | } | ||
diff --git a/drivers/media/video/omap3isp/ispccdc.h b/drivers/media/video/omap3isp/ispccdc.h new file mode 100644 index 000000000000..d403af5d31d2 --- /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 occured 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 occured 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/isppreview.c b/drivers/media/video/omap3isp/isppreview.c new file mode 100644 index 000000000000..baf9374201dc --- /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 Flourescent 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 Flourescent 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 Flourescent Light for CFA Gradient*/ | ||
58 | #define FLR_CFA_GRADTHRS_HORZ 0x28 | ||
59 | #define FLR_CFA_GRADTHRS_VERT 0x28 | ||
60 | |||
61 | /* Default values in Office Flourescent Light for Chroma Suppression*/ | ||
62 | #define FLR_CSUP_GAIN 0x0D | ||
63 | #define FLR_CSUP_THRES 0xEB | ||
64 | |||
65 | /* Default values in Office Flourescent 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 Flourescent 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 proccess 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 proccess 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 coresponding 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 sucess | ||
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 sucess | ||
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 000000000000..f2d63ca4bd6f --- /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 occured 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/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c new file mode 100644 index 000000000000..75d39b115d42 --- /dev/null +++ b/drivers/media/video/omap3isp/ispresizer.c | |||
@@ -0,0 +1,1693 @@ | |||
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 | * Reverting the equations, we can compute the resizing ratios with | ||
718 | * | ||
719 | * - 8-phase, 4-tap mode | ||
720 | * hrsz = ((iw - 7) * 256 - 16 - 32 * sph) / (ow - 1) | ||
721 | * vrsz = ((ih - 4) * 256 - 16 - 32 * spv) / (oh - 1) | ||
722 | * - 4-phase, 7-tap mode | ||
723 | * hrsz = ((iw - 7) * 256 - 32 - 64 * sph) / (ow - 1) | ||
724 | * vrsz = ((ih - 7) * 256 - 32 - 64 * spv) / (oh - 1) | ||
725 | * | ||
726 | * The ratios are integer values, and must be rounded down to ensure that the | ||
727 | * cropped input size is not bigger than the uncropped input size. As the ratio | ||
728 | * in 7-tap mode is always smaller than the ratio in 4-tap mode, we can use the | ||
729 | * 7-tap mode equations to compute a ratio approximation. | ||
730 | * | ||
731 | * We first clamp the output size according to the hardware capabilitie to avoid | ||
732 | * auto-cropping the input more than required to satisfy the TRM equations. The | ||
733 | * minimum output size is achieved with a scaling factor of 1024. It is thus | ||
734 | * computed using the 7-tap equations. | ||
735 | * | ||
736 | * min ow = ((iw - 7) * 256 - 32 - 64 * sph) / 1024 + 1 | ||
737 | * min oh = ((ih - 7) * 256 - 32 - 64 * spv) / 1024 + 1 | ||
738 | * | ||
739 | * Similarly, the maximum output size is achieved with a scaling factor of 64 | ||
740 | * and computed using the 4-tap equations. | ||
741 | * | ||
742 | * max ow = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / 64 + 1 | ||
743 | * max oh = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1 | ||
744 | * | ||
745 | * The additional +255 term compensates for the round down operation performed | ||
746 | * by the TRM equations when shifting the value right by 8 bits. | ||
747 | * | ||
748 | * We then compute and clamp the ratios (x1/4 ~ x4). Clamping the output size to | ||
749 | * the maximum value guarantees that the ratio value will never be smaller than | ||
750 | * the minimum, but it could still slightly exceed the maximum. Clamping the | ||
751 | * ratio will thus result in a resizing factor slightly larger than the | ||
752 | * requested value. | ||
753 | * | ||
754 | * To accomodate that, and make sure the TRM equations are satisfied exactly, we | ||
755 | * compute the input crop rectangle as the last step. | ||
756 | * | ||
757 | * As if the situation wasn't complex enough, the maximum output width depends | ||
758 | * on the vertical resizing ratio. Fortunately, the output height doesn't | ||
759 | * depend on the horizontal resizing ratio. We can then start by computing the | ||
760 | * output height and the vertical ratio, and then move to computing the output | ||
761 | * width and the horizontal ratio. | ||
762 | */ | ||
763 | static void resizer_calc_ratios(struct isp_res_device *res, | ||
764 | struct v4l2_rect *input, | ||
765 | struct v4l2_mbus_framefmt *output, | ||
766 | struct resizer_ratio *ratio) | ||
767 | { | ||
768 | struct isp_device *isp = to_isp_device(res); | ||
769 | const unsigned int spv = DEFAULT_PHASE; | ||
770 | const unsigned int sph = DEFAULT_PHASE; | ||
771 | unsigned int upscaled_width; | ||
772 | unsigned int upscaled_height; | ||
773 | unsigned int min_width; | ||
774 | unsigned int min_height; | ||
775 | unsigned int max_width; | ||
776 | unsigned int max_height; | ||
777 | unsigned int width_alignment; | ||
778 | |||
779 | /* | ||
780 | * Clamp the output height based on the hardware capabilities and | ||
781 | * compute the vertical resizing ratio. | ||
782 | */ | ||
783 | min_height = ((input->height - 7) * 256 - 32 - 64 * spv) / 1024 + 1; | ||
784 | min_height = max_t(unsigned int, min_height, MIN_OUT_HEIGHT); | ||
785 | max_height = ((input->height - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1; | ||
786 | max_height = min_t(unsigned int, max_height, MAX_OUT_HEIGHT); | ||
787 | output->height = clamp(output->height, min_height, max_height); | ||
788 | |||
789 | ratio->vert = ((input->height - 7) * 256 - 32 - 64 * spv) | ||
790 | / (output->height - 1); | ||
791 | ratio->vert = clamp_t(unsigned int, ratio->vert, | ||
792 | MIN_RESIZE_VALUE, MAX_RESIZE_VALUE); | ||
793 | |||
794 | if (ratio->vert <= MID_RESIZE_VALUE) { | ||
795 | upscaled_height = (output->height - 1) * ratio->vert | ||
796 | + 32 * spv + 16; | ||
797 | input->height = (upscaled_height >> 8) + 4; | ||
798 | } else { | ||
799 | upscaled_height = (output->height - 1) * ratio->vert | ||
800 | + 64 * spv + 32; | ||
801 | input->height = (upscaled_height >> 8) + 7; | ||
802 | } | ||
803 | |||
804 | /* | ||
805 | * Compute the minimum and maximum output widths based on the hardware | ||
806 | * capabilities. The maximum depends on the vertical resizing ratio. | ||
807 | */ | ||
808 | min_width = ((input->width - 7) * 256 - 32 - 64 * sph) / 1024 + 1; | ||
809 | min_width = max_t(unsigned int, min_width, MIN_OUT_WIDTH); | ||
810 | |||
811 | if (ratio->vert <= MID_RESIZE_VALUE) { | ||
812 | switch (isp->revision) { | ||
813 | case ISP_REVISION_1_0: | ||
814 | max_width = MAX_4TAP_OUT_WIDTH_ES1; | ||
815 | break; | ||
816 | |||
817 | case ISP_REVISION_2_0: | ||
818 | default: | ||
819 | max_width = MAX_4TAP_OUT_WIDTH_ES2; | ||
820 | break; | ||
821 | |||
822 | case ISP_REVISION_15_0: | ||
823 | max_width = MAX_4TAP_OUT_WIDTH_3630; | ||
824 | break; | ||
825 | } | ||
826 | } else { | ||
827 | switch (isp->revision) { | ||
828 | case ISP_REVISION_1_0: | ||
829 | max_width = MAX_7TAP_OUT_WIDTH_ES1; | ||
830 | break; | ||
831 | |||
832 | case ISP_REVISION_2_0: | ||
833 | default: | ||
834 | max_width = MAX_7TAP_OUT_WIDTH_ES2; | ||
835 | break; | ||
836 | |||
837 | case ISP_REVISION_15_0: | ||
838 | max_width = MAX_7TAP_OUT_WIDTH_3630; | ||
839 | break; | ||
840 | } | ||
841 | } | ||
842 | max_width = min(((input->width - 7) * 256 + 255 - 16 - 32 * sph) / 64 | ||
843 | + 1, max_width); | ||
844 | |||
845 | /* | ||
846 | * The output width must be even, and must be a multiple of 16 bytes | ||
847 | * when upscaling vertically. Clamp the output width to the valid range. | ||
848 | * Take the alignment into account (the maximum width in 7-tap mode on | ||
849 | * ES2 isn't a multiple of 8) and align the result up to make sure it | ||
850 | * won't be smaller than the minimum. | ||
851 | */ | ||
852 | width_alignment = ratio->vert < 256 ? 8 : 2; | ||
853 | output->width = clamp(output->width, min_width, | ||
854 | max_width & ~(width_alignment - 1)); | ||
855 | output->width = ALIGN(output->width, width_alignment); | ||
856 | |||
857 | ratio->horz = ((input->width - 7) * 256 - 32 - 64 * sph) | ||
858 | / (output->width - 1); | ||
859 | ratio->horz = clamp_t(unsigned int, ratio->horz, | ||
860 | MIN_RESIZE_VALUE, MAX_RESIZE_VALUE); | ||
861 | |||
862 | if (ratio->horz <= MID_RESIZE_VALUE) { | ||
863 | upscaled_width = (output->width - 1) * ratio->horz | ||
864 | + 32 * sph + 16; | ||
865 | input->width = (upscaled_width >> 8) + 7; | ||
866 | } else { | ||
867 | upscaled_width = (output->width - 1) * ratio->horz | ||
868 | + 64 * sph + 32; | ||
869 | input->width = (upscaled_width >> 8) + 7; | ||
870 | } | ||
871 | } | ||
872 | |||
873 | /* | ||
874 | * resizer_set_crop_params - Setup hardware with cropping parameters | ||
875 | * @res : resizer private structure | ||
876 | * @crop_rect : current crop rectangle | ||
877 | * @ratio : resizer ratios | ||
878 | * return none | ||
879 | */ | ||
880 | static void resizer_set_crop_params(struct isp_res_device *res, | ||
881 | const struct v4l2_mbus_framefmt *input, | ||
882 | const struct v4l2_mbus_framefmt *output) | ||
883 | { | ||
884 | resizer_set_ratio(res, &res->ratio); | ||
885 | |||
886 | /* Set chrominance horizontal algorithm */ | ||
887 | if (res->ratio.horz >= RESIZE_DIVISOR) | ||
888 | resizer_set_bilinear(res, RSZ_THE_SAME); | ||
889 | else | ||
890 | resizer_set_bilinear(res, RSZ_BILINEAR); | ||
891 | |||
892 | resizer_adjust_bandwidth(res); | ||
893 | |||
894 | if (res->input == RESIZER_INPUT_MEMORY) { | ||
895 | /* Calculate additional offset for crop */ | ||
896 | res->crop_offset = (res->crop.active.top * input->width + | ||
897 | res->crop.active.left) * 2; | ||
898 | /* | ||
899 | * Write lowest 4 bits of horizontal pixel offset (in pixels), | ||
900 | * vertical start must be 0. | ||
901 | */ | ||
902 | resizer_set_start(res, (res->crop_offset / 2) & 0xf, 0); | ||
903 | |||
904 | /* | ||
905 | * Set start (read) address for cropping, in bytes. | ||
906 | * Lowest 5 bits must be zero. | ||
907 | */ | ||
908 | __resizer_set_inaddr(res, | ||
909 | res->addr_base + (res->crop_offset & ~0x1f)); | ||
910 | } else { | ||
911 | /* | ||
912 | * Set vertical start line and horizontal starting pixel. | ||
913 | * If the input is from CCDC/PREV, horizontal start field is | ||
914 | * in bytes (twice number of pixels). | ||
915 | */ | ||
916 | resizer_set_start(res, res->crop.active.left * 2, | ||
917 | res->crop.active.top); | ||
918 | /* Input address and offset must be 0 for preview/ccdc input */ | ||
919 | __resizer_set_inaddr(res, 0); | ||
920 | resizer_set_input_offset(res, 0); | ||
921 | } | ||
922 | |||
923 | /* Set the input size */ | ||
924 | resizer_set_input_size(res, res->crop.active.width, | ||
925 | res->crop.active.height); | ||
926 | } | ||
927 | |||
928 | static void resizer_configure(struct isp_res_device *res) | ||
929 | { | ||
930 | struct v4l2_mbus_framefmt *informat, *outformat; | ||
931 | struct resizer_luma_yenh luma = {0, 0, 0, 0}; | ||
932 | |||
933 | resizer_set_source(res, res->input); | ||
934 | |||
935 | informat = &res->formats[RESZ_PAD_SINK]; | ||
936 | outformat = &res->formats[RESZ_PAD_SOURCE]; | ||
937 | |||
938 | /* RESZ_PAD_SINK */ | ||
939 | if (res->input == RESIZER_INPUT_VP) | ||
940 | resizer_set_input_offset(res, 0); | ||
941 | else | ||
942 | resizer_set_input_offset(res, ALIGN(informat->width, 0x10) * 2); | ||
943 | |||
944 | /* YUV422 interleaved, default phase, no luma enhancement */ | ||
945 | resizer_set_intype(res, RSZ_YUV422); | ||
946 | resizer_set_ycpos(res, informat->code); | ||
947 | resizer_set_phase(res, DEFAULT_PHASE, DEFAULT_PHASE); | ||
948 | resizer_set_luma(res, &luma); | ||
949 | |||
950 | /* RESZ_PAD_SOURCE */ | ||
951 | resizer_set_output_offset(res, ALIGN(outformat->width * 2, 32)); | ||
952 | resizer_set_output_size(res, outformat->width, outformat->height); | ||
953 | |||
954 | resizer_set_crop_params(res, informat, outformat); | ||
955 | } | ||
956 | |||
957 | /* ----------------------------------------------------------------------------- | ||
958 | * Interrupt handling | ||
959 | */ | ||
960 | |||
961 | static void resizer_enable_oneshot(struct isp_res_device *res) | ||
962 | { | ||
963 | struct isp_device *isp = to_isp_device(res); | ||
964 | |||
965 | isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR, | ||
966 | ISPRSZ_PCR_ENABLE | ISPRSZ_PCR_ONESHOT); | ||
967 | } | ||
968 | |||
969 | void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res) | ||
970 | { | ||
971 | /* | ||
972 | * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun | ||
973 | * condition, the module was paused and now we have a buffer queued | ||
974 | * on the output again. Restart the pipeline if running in continuous | ||
975 | * mode. | ||
976 | */ | ||
977 | if (res->state == ISP_PIPELINE_STREAM_CONTINUOUS && | ||
978 | res->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { | ||
979 | resizer_enable_oneshot(res); | ||
980 | isp_video_dmaqueue_flags_clr(&res->video_out); | ||
981 | } | ||
982 | } | ||
983 | |||
984 | static void resizer_isr_buffer(struct isp_res_device *res) | ||
985 | { | ||
986 | struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); | ||
987 | struct isp_buffer *buffer; | ||
988 | int restart = 0; | ||
989 | |||
990 | if (res->state == ISP_PIPELINE_STREAM_STOPPED) | ||
991 | return; | ||
992 | |||
993 | /* Complete the output buffer and, if reading from memory, the input | ||
994 | * buffer. | ||
995 | */ | ||
996 | buffer = omap3isp_video_buffer_next(&res->video_out, res->error); | ||
997 | if (buffer != NULL) { | ||
998 | resizer_set_outaddr(res, buffer->isp_addr); | ||
999 | restart = 1; | ||
1000 | } | ||
1001 | |||
1002 | pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; | ||
1003 | |||
1004 | if (res->input == RESIZER_INPUT_MEMORY) { | ||
1005 | buffer = omap3isp_video_buffer_next(&res->video_in, 0); | ||
1006 | if (buffer != NULL) | ||
1007 | resizer_set_inaddr(res, buffer->isp_addr); | ||
1008 | pipe->state |= ISP_PIPELINE_IDLE_INPUT; | ||
1009 | } | ||
1010 | |||
1011 | if (res->state == ISP_PIPELINE_STREAM_SINGLESHOT) { | ||
1012 | if (isp_pipeline_ready(pipe)) | ||
1013 | omap3isp_pipeline_set_stream(pipe, | ||
1014 | ISP_PIPELINE_STREAM_SINGLESHOT); | ||
1015 | } else { | ||
1016 | /* If an underrun occurs, the video queue operation handler will | ||
1017 | * restart the resizer. Otherwise restart it immediately. | ||
1018 | */ | ||
1019 | if (restart) | ||
1020 | resizer_enable_oneshot(res); | ||
1021 | } | ||
1022 | |||
1023 | res->error = 0; | ||
1024 | } | ||
1025 | |||
1026 | /* | ||
1027 | * omap3isp_resizer_isr - ISP resizer interrupt handler | ||
1028 | * | ||
1029 | * Manage the resizer video buffers and configure shadowed and busy-locked | ||
1030 | * registers. | ||
1031 | */ | ||
1032 | void omap3isp_resizer_isr(struct isp_res_device *res) | ||
1033 | { | ||
1034 | struct v4l2_mbus_framefmt *informat, *outformat; | ||
1035 | |||
1036 | if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping)) | ||
1037 | return; | ||
1038 | |||
1039 | if (res->applycrop) { | ||
1040 | outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE, | ||
1041 | V4L2_SUBDEV_FORMAT_ACTIVE); | ||
1042 | informat = __resizer_get_format(res, NULL, RESZ_PAD_SINK, | ||
1043 | V4L2_SUBDEV_FORMAT_ACTIVE); | ||
1044 | resizer_set_crop_params(res, informat, outformat); | ||
1045 | res->applycrop = 0; | ||
1046 | } | ||
1047 | |||
1048 | resizer_isr_buffer(res); | ||
1049 | } | ||
1050 | |||
1051 | /* ----------------------------------------------------------------------------- | ||
1052 | * ISP video operations | ||
1053 | */ | ||
1054 | |||
1055 | static int resizer_video_queue(struct isp_video *video, | ||
1056 | struct isp_buffer *buffer) | ||
1057 | { | ||
1058 | struct isp_res_device *res = &video->isp->isp_res; | ||
1059 | |||
1060 | if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
1061 | resizer_set_inaddr(res, buffer->isp_addr); | ||
1062 | |||
1063 | /* | ||
1064 | * We now have a buffer queued on the output. Despite what the | ||
1065 | * TRM says, the resizer can't be restarted immediately. | ||
1066 | * Enabling it in one shot mode in the middle of a frame (or at | ||
1067 | * least asynchronously to the frame) results in the output | ||
1068 | * being shifted randomly left/right and up/down, as if the | ||
1069 | * hardware didn't synchronize itself to the beginning of the | ||
1070 | * frame correctly. | ||
1071 | * | ||
1072 | * Restart the resizer on the next sync interrupt if running in | ||
1073 | * continuous mode or when starting the stream. | ||
1074 | */ | ||
1075 | if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1076 | resizer_set_outaddr(res, buffer->isp_addr); | ||
1077 | |||
1078 | return 0; | ||
1079 | } | ||
1080 | |||
1081 | static const struct isp_video_operations resizer_video_ops = { | ||
1082 | .queue = resizer_video_queue, | ||
1083 | }; | ||
1084 | |||
1085 | /* ----------------------------------------------------------------------------- | ||
1086 | * V4L2 subdev operations | ||
1087 | */ | ||
1088 | |||
1089 | /* | ||
1090 | * resizer_set_stream - Enable/Disable streaming on resizer subdev | ||
1091 | * @sd: ISP resizer V4L2 subdev | ||
1092 | * @enable: 1 == Enable, 0 == Disable | ||
1093 | * | ||
1094 | * The resizer hardware can't be enabled without a memory buffer to write to. | ||
1095 | * As the s_stream operation is called in response to a STREAMON call without | ||
1096 | * any buffer queued yet, just update the state field and return immediately. | ||
1097 | * The resizer will be enabled in resizer_video_queue(). | ||
1098 | */ | ||
1099 | static int resizer_set_stream(struct v4l2_subdev *sd, int enable) | ||
1100 | { | ||
1101 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1102 | struct isp_video *video_out = &res->video_out; | ||
1103 | struct isp_device *isp = to_isp_device(res); | ||
1104 | struct device *dev = to_device(res); | ||
1105 | |||
1106 | if (res->state == ISP_PIPELINE_STREAM_STOPPED) { | ||
1107 | if (enable == ISP_PIPELINE_STREAM_STOPPED) | ||
1108 | return 0; | ||
1109 | |||
1110 | omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_RESIZER); | ||
1111 | resizer_configure(res); | ||
1112 | res->error = 0; | ||
1113 | resizer_print_status(res); | ||
1114 | } | ||
1115 | |||
1116 | switch (enable) { | ||
1117 | case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
1118 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); | ||
1119 | if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { | ||
1120 | resizer_enable_oneshot(res); | ||
1121 | isp_video_dmaqueue_flags_clr(video_out); | ||
1122 | } | ||
1123 | break; | ||
1124 | |||
1125 | case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
1126 | if (res->input == RESIZER_INPUT_MEMORY) | ||
1127 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_READ); | ||
1128 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); | ||
1129 | |||
1130 | resizer_enable_oneshot(res); | ||
1131 | break; | ||
1132 | |||
1133 | case ISP_PIPELINE_STREAM_STOPPED: | ||
1134 | if (omap3isp_module_sync_idle(&sd->entity, &res->wait, | ||
1135 | &res->stopping)) | ||
1136 | dev_dbg(dev, "%s: module stop timeout.\n", sd->name); | ||
1137 | omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_RESIZER_READ | | ||
1138 | OMAP3_ISP_SBL_RESIZER_WRITE); | ||
1139 | omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_RESIZER); | ||
1140 | isp_video_dmaqueue_flags_clr(video_out); | ||
1141 | break; | ||
1142 | } | ||
1143 | |||
1144 | res->state = enable; | ||
1145 | return 0; | ||
1146 | } | ||
1147 | |||
1148 | /* | ||
1149 | * resizer_g_crop - handle get crop subdev operation | ||
1150 | * @sd : pointer to v4l2 subdev structure | ||
1151 | * @pad : subdev pad | ||
1152 | * @crop : pointer to crop structure | ||
1153 | * @which : active or try format | ||
1154 | * return zero | ||
1155 | */ | ||
1156 | static int resizer_g_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1157 | struct v4l2_subdev_crop *crop) | ||
1158 | { | ||
1159 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1160 | struct v4l2_mbus_framefmt *format; | ||
1161 | struct resizer_ratio ratio; | ||
1162 | |||
1163 | /* Only sink pad has crop capability */ | ||
1164 | if (crop->pad != RESZ_PAD_SINK) | ||
1165 | return -EINVAL; | ||
1166 | |||
1167 | format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, crop->which); | ||
1168 | crop->rect = *__resizer_get_crop(res, fh, crop->which); | ||
1169 | resizer_calc_ratios(res, &crop->rect, format, &ratio); | ||
1170 | |||
1171 | return 0; | ||
1172 | } | ||
1173 | |||
1174 | /* | ||
1175 | * resizer_try_crop - mangles crop parameters. | ||
1176 | */ | ||
1177 | static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, | ||
1178 | const struct v4l2_mbus_framefmt *source, | ||
1179 | struct v4l2_rect *crop) | ||
1180 | { | ||
1181 | const unsigned int spv = DEFAULT_PHASE; | ||
1182 | const unsigned int sph = DEFAULT_PHASE; | ||
1183 | |||
1184 | /* Crop rectangle is constrained to the output size so that zoom ratio | ||
1185 | * cannot exceed +/-4.0. | ||
1186 | */ | ||
1187 | unsigned int min_width = | ||
1188 | ((32 * sph + (source->width - 1) * 64 + 16) >> 8) + 7; | ||
1189 | unsigned int min_height = | ||
1190 | ((32 * spv + (source->height - 1) * 64 + 16) >> 8) + 4; | ||
1191 | unsigned int max_width = | ||
1192 | ((64 * sph + (source->width - 1) * 1024 + 32) >> 8) + 7; | ||
1193 | unsigned int max_height = | ||
1194 | ((64 * spv + (source->height - 1) * 1024 + 32) >> 8) + 7; | ||
1195 | |||
1196 | crop->width = clamp_t(u32, crop->width, min_width, max_width); | ||
1197 | crop->height = clamp_t(u32, crop->height, min_height, max_height); | ||
1198 | |||
1199 | /* Crop can not go beyond of the input rectangle */ | ||
1200 | crop->left = clamp_t(u32, crop->left, 0, sink->width - MIN_IN_WIDTH); | ||
1201 | crop->width = clamp_t(u32, crop->width, MIN_IN_WIDTH, | ||
1202 | sink->width - crop->left); | ||
1203 | crop->top = clamp_t(u32, crop->top, 0, sink->height - MIN_IN_HEIGHT); | ||
1204 | crop->height = clamp_t(u32, crop->height, MIN_IN_HEIGHT, | ||
1205 | sink->height - crop->top); | ||
1206 | } | ||
1207 | |||
1208 | /* | ||
1209 | * resizer_s_crop - handle set crop subdev operation | ||
1210 | * @sd : pointer to v4l2 subdev structure | ||
1211 | * @pad : subdev pad | ||
1212 | * @crop : pointer to crop structure | ||
1213 | * @which : active or try format | ||
1214 | * return -EINVAL or zero when succeed | ||
1215 | */ | ||
1216 | static int resizer_s_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1217 | struct v4l2_subdev_crop *crop) | ||
1218 | { | ||
1219 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1220 | struct isp_device *isp = to_isp_device(res); | ||
1221 | struct v4l2_mbus_framefmt *format_sink, *format_source; | ||
1222 | struct resizer_ratio ratio; | ||
1223 | |||
1224 | /* Only sink pad has crop capability */ | ||
1225 | if (crop->pad != RESZ_PAD_SINK) | ||
1226 | return -EINVAL; | ||
1227 | |||
1228 | format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, | ||
1229 | crop->which); | ||
1230 | format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, | ||
1231 | crop->which); | ||
1232 | |||
1233 | dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__, | ||
1234 | crop->rect.left, crop->rect.top, crop->rect.width, | ||
1235 | crop->rect.height, crop->which); | ||
1236 | |||
1237 | dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__, | ||
1238 | format_sink->width, format_sink->height, | ||
1239 | format_source->width, format_source->height); | ||
1240 | |||
1241 | resizer_try_crop(format_sink, format_source, &crop->rect); | ||
1242 | *__resizer_get_crop(res, fh, crop->which) = crop->rect; | ||
1243 | resizer_calc_ratios(res, &crop->rect, format_source, &ratio); | ||
1244 | |||
1245 | if (crop->which == V4L2_SUBDEV_FORMAT_TRY) | ||
1246 | return 0; | ||
1247 | |||
1248 | res->ratio = ratio; | ||
1249 | res->crop.active = crop->rect; | ||
1250 | |||
1251 | /* | ||
1252 | * s_crop can be called while streaming is on. In this case | ||
1253 | * the crop values will be set in the next IRQ. | ||
1254 | */ | ||
1255 | if (res->state != ISP_PIPELINE_STREAM_STOPPED) | ||
1256 | res->applycrop = 1; | ||
1257 | |||
1258 | return 0; | ||
1259 | } | ||
1260 | |||
1261 | /* resizer pixel formats */ | ||
1262 | static const unsigned int resizer_formats[] = { | ||
1263 | V4L2_MBUS_FMT_UYVY8_1X16, | ||
1264 | V4L2_MBUS_FMT_YUYV8_1X16, | ||
1265 | }; | ||
1266 | |||
1267 | static unsigned int resizer_max_in_width(struct isp_res_device *res) | ||
1268 | { | ||
1269 | struct isp_device *isp = to_isp_device(res); | ||
1270 | |||
1271 | if (res->input == RESIZER_INPUT_MEMORY) { | ||
1272 | return MAX_IN_WIDTH_MEMORY_MODE; | ||
1273 | } else { | ||
1274 | if (isp->revision == ISP_REVISION_1_0) | ||
1275 | return MAX_IN_WIDTH_ONTHEFLY_MODE_ES1; | ||
1276 | else | ||
1277 | return MAX_IN_WIDTH_ONTHEFLY_MODE_ES2; | ||
1278 | } | ||
1279 | } | ||
1280 | |||
1281 | /* | ||
1282 | * resizer_try_format - Handle try format by pad subdev method | ||
1283 | * @res : ISP resizer device | ||
1284 | * @fh : V4L2 subdev file handle | ||
1285 | * @pad : pad num | ||
1286 | * @fmt : pointer to v4l2 format structure | ||
1287 | * @which : wanted subdev format | ||
1288 | */ | ||
1289 | static void resizer_try_format(struct isp_res_device *res, | ||
1290 | struct v4l2_subdev_fh *fh, unsigned int pad, | ||
1291 | struct v4l2_mbus_framefmt *fmt, | ||
1292 | enum v4l2_subdev_format_whence which) | ||
1293 | { | ||
1294 | struct v4l2_mbus_framefmt *format; | ||
1295 | struct resizer_ratio ratio; | ||
1296 | struct v4l2_rect crop; | ||
1297 | |||
1298 | switch (pad) { | ||
1299 | case RESZ_PAD_SINK: | ||
1300 | if (fmt->code != V4L2_MBUS_FMT_YUYV8_1X16 && | ||
1301 | fmt->code != V4L2_MBUS_FMT_UYVY8_1X16) | ||
1302 | fmt->code = V4L2_MBUS_FMT_YUYV8_1X16; | ||
1303 | |||
1304 | fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH, | ||
1305 | resizer_max_in_width(res)); | ||
1306 | fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT, | ||
1307 | MAX_IN_HEIGHT); | ||
1308 | break; | ||
1309 | |||
1310 | case RESZ_PAD_SOURCE: | ||
1311 | format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which); | ||
1312 | fmt->code = format->code; | ||
1313 | |||
1314 | crop = *__resizer_get_crop(res, fh, which); | ||
1315 | resizer_calc_ratios(res, &crop, fmt, &ratio); | ||
1316 | break; | ||
1317 | } | ||
1318 | |||
1319 | fmt->colorspace = V4L2_COLORSPACE_JPEG; | ||
1320 | fmt->field = V4L2_FIELD_NONE; | ||
1321 | } | ||
1322 | |||
1323 | /* | ||
1324 | * resizer_enum_mbus_code - Handle pixel format enumeration | ||
1325 | * @sd : pointer to v4l2 subdev structure | ||
1326 | * @fh : V4L2 subdev file handle | ||
1327 | * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
1328 | * return -EINVAL or zero on success | ||
1329 | */ | ||
1330 | static int resizer_enum_mbus_code(struct v4l2_subdev *sd, | ||
1331 | struct v4l2_subdev_fh *fh, | ||
1332 | struct v4l2_subdev_mbus_code_enum *code) | ||
1333 | { | ||
1334 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1335 | struct v4l2_mbus_framefmt *format; | ||
1336 | |||
1337 | if (code->pad == RESZ_PAD_SINK) { | ||
1338 | if (code->index >= ARRAY_SIZE(resizer_formats)) | ||
1339 | return -EINVAL; | ||
1340 | |||
1341 | code->code = resizer_formats[code->index]; | ||
1342 | } else { | ||
1343 | if (code->index != 0) | ||
1344 | return -EINVAL; | ||
1345 | |||
1346 | format = __resizer_get_format(res, fh, RESZ_PAD_SINK, | ||
1347 | V4L2_SUBDEV_FORMAT_TRY); | ||
1348 | code->code = format->code; | ||
1349 | } | ||
1350 | |||
1351 | return 0; | ||
1352 | } | ||
1353 | |||
1354 | static int resizer_enum_frame_size(struct v4l2_subdev *sd, | ||
1355 | struct v4l2_subdev_fh *fh, | ||
1356 | struct v4l2_subdev_frame_size_enum *fse) | ||
1357 | { | ||
1358 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1359 | struct v4l2_mbus_framefmt format; | ||
1360 | |||
1361 | if (fse->index != 0) | ||
1362 | return -EINVAL; | ||
1363 | |||
1364 | format.code = fse->code; | ||
1365 | format.width = 1; | ||
1366 | format.height = 1; | ||
1367 | resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
1368 | fse->min_width = format.width; | ||
1369 | fse->min_height = format.height; | ||
1370 | |||
1371 | if (format.code != fse->code) | ||
1372 | return -EINVAL; | ||
1373 | |||
1374 | format.code = fse->code; | ||
1375 | format.width = -1; | ||
1376 | format.height = -1; | ||
1377 | resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
1378 | fse->max_width = format.width; | ||
1379 | fse->max_height = format.height; | ||
1380 | |||
1381 | return 0; | ||
1382 | } | ||
1383 | |||
1384 | /* | ||
1385 | * resizer_get_format - Handle get format by pads subdev method | ||
1386 | * @sd : pointer to v4l2 subdev structure | ||
1387 | * @fh : V4L2 subdev file handle | ||
1388 | * @fmt : pointer to v4l2 subdev format structure | ||
1389 | * return -EINVAL or zero on sucess | ||
1390 | */ | ||
1391 | static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1392 | struct v4l2_subdev_format *fmt) | ||
1393 | { | ||
1394 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1395 | struct v4l2_mbus_framefmt *format; | ||
1396 | |||
1397 | format = __resizer_get_format(res, fh, fmt->pad, fmt->which); | ||
1398 | if (format == NULL) | ||
1399 | return -EINVAL; | ||
1400 | |||
1401 | fmt->format = *format; | ||
1402 | return 0; | ||
1403 | } | ||
1404 | |||
1405 | /* | ||
1406 | * resizer_set_format - Handle set format by pads subdev method | ||
1407 | * @sd : pointer to v4l2 subdev structure | ||
1408 | * @fh : V4L2 subdev file handle | ||
1409 | * @fmt : pointer to v4l2 subdev format structure | ||
1410 | * return -EINVAL or zero on success | ||
1411 | */ | ||
1412 | static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1413 | struct v4l2_subdev_format *fmt) | ||
1414 | { | ||
1415 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1416 | struct v4l2_mbus_framefmt *format; | ||
1417 | struct v4l2_rect *crop; | ||
1418 | |||
1419 | format = __resizer_get_format(res, fh, fmt->pad, fmt->which); | ||
1420 | if (format == NULL) | ||
1421 | return -EINVAL; | ||
1422 | |||
1423 | resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which); | ||
1424 | *format = fmt->format; | ||
1425 | |||
1426 | if (fmt->pad == RESZ_PAD_SINK) { | ||
1427 | /* reset crop rectangle */ | ||
1428 | crop = __resizer_get_crop(res, fh, fmt->which); | ||
1429 | crop->left = 0; | ||
1430 | crop->top = 0; | ||
1431 | crop->width = fmt->format.width; | ||
1432 | crop->height = fmt->format.height; | ||
1433 | |||
1434 | /* Propagate the format from sink to source */ | ||
1435 | format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, | ||
1436 | fmt->which); | ||
1437 | *format = fmt->format; | ||
1438 | resizer_try_format(res, fh, RESZ_PAD_SOURCE, format, | ||
1439 | fmt->which); | ||
1440 | } | ||
1441 | |||
1442 | if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { | ||
1443 | /* Compute and store the active crop rectangle and resizer | ||
1444 | * ratios. format already points to the source pad active | ||
1445 | * format. | ||
1446 | */ | ||
1447 | res->crop.active = res->crop.request; | ||
1448 | resizer_calc_ratios(res, &res->crop.active, format, | ||
1449 | &res->ratio); | ||
1450 | } | ||
1451 | |||
1452 | return 0; | ||
1453 | } | ||
1454 | |||
1455 | /* | ||
1456 | * resizer_init_formats - Initialize formats on all pads | ||
1457 | * @sd: ISP resizer V4L2 subdevice | ||
1458 | * @fh: V4L2 subdev file handle | ||
1459 | * | ||
1460 | * Initialize all pad formats with default values. If fh is not NULL, try | ||
1461 | * formats are initialized on the file handle. Otherwise active formats are | ||
1462 | * initialized on the device. | ||
1463 | */ | ||
1464 | static int resizer_init_formats(struct v4l2_subdev *sd, | ||
1465 | struct v4l2_subdev_fh *fh) | ||
1466 | { | ||
1467 | struct v4l2_subdev_format format; | ||
1468 | |||
1469 | memset(&format, 0, sizeof(format)); | ||
1470 | format.pad = RESZ_PAD_SINK; | ||
1471 | format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
1472 | format.format.code = V4L2_MBUS_FMT_YUYV8_1X16; | ||
1473 | format.format.width = 4096; | ||
1474 | format.format.height = 4096; | ||
1475 | resizer_set_format(sd, fh, &format); | ||
1476 | |||
1477 | return 0; | ||
1478 | } | ||
1479 | |||
1480 | /* subdev video operations */ | ||
1481 | static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { | ||
1482 | .s_stream = resizer_set_stream, | ||
1483 | }; | ||
1484 | |||
1485 | /* subdev pad operations */ | ||
1486 | static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { | ||
1487 | .enum_mbus_code = resizer_enum_mbus_code, | ||
1488 | .enum_frame_size = resizer_enum_frame_size, | ||
1489 | .get_fmt = resizer_get_format, | ||
1490 | .set_fmt = resizer_set_format, | ||
1491 | .get_crop = resizer_g_crop, | ||
1492 | .set_crop = resizer_s_crop, | ||
1493 | }; | ||
1494 | |||
1495 | /* subdev operations */ | ||
1496 | static const struct v4l2_subdev_ops resizer_v4l2_ops = { | ||
1497 | .video = &resizer_v4l2_video_ops, | ||
1498 | .pad = &resizer_v4l2_pad_ops, | ||
1499 | }; | ||
1500 | |||
1501 | /* subdev internal operations */ | ||
1502 | static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = { | ||
1503 | .open = resizer_init_formats, | ||
1504 | }; | ||
1505 | |||
1506 | /* ----------------------------------------------------------------------------- | ||
1507 | * Media entity operations | ||
1508 | */ | ||
1509 | |||
1510 | /* | ||
1511 | * resizer_link_setup - Setup resizer connections. | ||
1512 | * @entity : Pointer to media entity structure | ||
1513 | * @local : Pointer to local pad array | ||
1514 | * @remote : Pointer to remote pad array | ||
1515 | * @flags : Link flags | ||
1516 | * return -EINVAL or zero on success | ||
1517 | */ | ||
1518 | static int resizer_link_setup(struct media_entity *entity, | ||
1519 | const struct media_pad *local, | ||
1520 | const struct media_pad *remote, u32 flags) | ||
1521 | { | ||
1522 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
1523 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1524 | |||
1525 | switch (local->index | media_entity_type(remote->entity)) { | ||
1526 | case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE: | ||
1527 | /* read from memory */ | ||
1528 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1529 | if (res->input == RESIZER_INPUT_VP) | ||
1530 | return -EBUSY; | ||
1531 | res->input = RESIZER_INPUT_MEMORY; | ||
1532 | } else { | ||
1533 | if (res->input == RESIZER_INPUT_MEMORY) | ||
1534 | res->input = RESIZER_INPUT_NONE; | ||
1535 | } | ||
1536 | break; | ||
1537 | |||
1538 | case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1539 | /* read from ccdc or previewer */ | ||
1540 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1541 | if (res->input == RESIZER_INPUT_MEMORY) | ||
1542 | return -EBUSY; | ||
1543 | res->input = RESIZER_INPUT_VP; | ||
1544 | } else { | ||
1545 | if (res->input == RESIZER_INPUT_VP) | ||
1546 | res->input = RESIZER_INPUT_NONE; | ||
1547 | } | ||
1548 | break; | ||
1549 | |||
1550 | case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: | ||
1551 | /* resizer always write to memory */ | ||
1552 | break; | ||
1553 | |||
1554 | default: | ||
1555 | return -EINVAL; | ||
1556 | } | ||
1557 | |||
1558 | return 0; | ||
1559 | } | ||
1560 | |||
1561 | /* media operations */ | ||
1562 | static const struct media_entity_operations resizer_media_ops = { | ||
1563 | .link_setup = resizer_link_setup, | ||
1564 | }; | ||
1565 | |||
1566 | /* | ||
1567 | * resizer_init_entities - Initialize resizer subdev and media entity. | ||
1568 | * @res : Pointer to resizer device structure | ||
1569 | * return -ENOMEM or zero on success | ||
1570 | */ | ||
1571 | static int resizer_init_entities(struct isp_res_device *res) | ||
1572 | { | ||
1573 | struct v4l2_subdev *sd = &res->subdev; | ||
1574 | struct media_pad *pads = res->pads; | ||
1575 | struct media_entity *me = &sd->entity; | ||
1576 | int ret; | ||
1577 | |||
1578 | res->input = RESIZER_INPUT_NONE; | ||
1579 | |||
1580 | v4l2_subdev_init(sd, &resizer_v4l2_ops); | ||
1581 | sd->internal_ops = &resizer_v4l2_internal_ops; | ||
1582 | strlcpy(sd->name, "OMAP3 ISP resizer", sizeof(sd->name)); | ||
1583 | sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
1584 | v4l2_set_subdevdata(sd, res); | ||
1585 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1586 | |||
1587 | pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
1588 | pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | ||
1589 | |||
1590 | me->ops = &resizer_media_ops; | ||
1591 | ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0); | ||
1592 | if (ret < 0) | ||
1593 | return ret; | ||
1594 | |||
1595 | resizer_init_formats(sd, NULL); | ||
1596 | |||
1597 | res->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
1598 | res->video_in.ops = &resizer_video_ops; | ||
1599 | res->video_in.isp = to_isp_device(res); | ||
1600 | res->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; | ||
1601 | res->video_in.bpl_alignment = 32; | ||
1602 | res->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1603 | res->video_out.ops = &resizer_video_ops; | ||
1604 | res->video_out.isp = to_isp_device(res); | ||
1605 | res->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; | ||
1606 | res->video_out.bpl_alignment = 32; | ||
1607 | |||
1608 | ret = omap3isp_video_init(&res->video_in, "resizer"); | ||
1609 | if (ret < 0) | ||
1610 | return ret; | ||
1611 | |||
1612 | ret = omap3isp_video_init(&res->video_out, "resizer"); | ||
1613 | if (ret < 0) | ||
1614 | return ret; | ||
1615 | |||
1616 | /* Connect the video nodes to the resizer subdev. */ | ||
1617 | ret = media_entity_create_link(&res->video_in.video.entity, 0, | ||
1618 | &res->subdev.entity, RESZ_PAD_SINK, 0); | ||
1619 | if (ret < 0) | ||
1620 | return ret; | ||
1621 | |||
1622 | ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE, | ||
1623 | &res->video_out.video.entity, 0, 0); | ||
1624 | if (ret < 0) | ||
1625 | return ret; | ||
1626 | |||
1627 | return 0; | ||
1628 | } | ||
1629 | |||
1630 | void omap3isp_resizer_unregister_entities(struct isp_res_device *res) | ||
1631 | { | ||
1632 | media_entity_cleanup(&res->subdev.entity); | ||
1633 | |||
1634 | v4l2_device_unregister_subdev(&res->subdev); | ||
1635 | omap3isp_video_unregister(&res->video_in); | ||
1636 | omap3isp_video_unregister(&res->video_out); | ||
1637 | } | ||
1638 | |||
1639 | int omap3isp_resizer_register_entities(struct isp_res_device *res, | ||
1640 | struct v4l2_device *vdev) | ||
1641 | { | ||
1642 | int ret; | ||
1643 | |||
1644 | /* Register the subdev and video nodes. */ | ||
1645 | ret = v4l2_device_register_subdev(vdev, &res->subdev); | ||
1646 | if (ret < 0) | ||
1647 | goto error; | ||
1648 | |||
1649 | ret = omap3isp_video_register(&res->video_in, vdev); | ||
1650 | if (ret < 0) | ||
1651 | goto error; | ||
1652 | |||
1653 | ret = omap3isp_video_register(&res->video_out, vdev); | ||
1654 | if (ret < 0) | ||
1655 | goto error; | ||
1656 | |||
1657 | return 0; | ||
1658 | |||
1659 | error: | ||
1660 | omap3isp_resizer_unregister_entities(res); | ||
1661 | return ret; | ||
1662 | } | ||
1663 | |||
1664 | /* ----------------------------------------------------------------------------- | ||
1665 | * ISP resizer initialization and cleanup | ||
1666 | */ | ||
1667 | |||
1668 | void omap3isp_resizer_cleanup(struct isp_device *isp) | ||
1669 | { | ||
1670 | } | ||
1671 | |||
1672 | /* | ||
1673 | * isp_resizer_init - Resizer initialization. | ||
1674 | * @isp : Pointer to ISP device | ||
1675 | * return -ENOMEM or zero on success | ||
1676 | */ | ||
1677 | int omap3isp_resizer_init(struct isp_device *isp) | ||
1678 | { | ||
1679 | struct isp_res_device *res = &isp->isp_res; | ||
1680 | int ret; | ||
1681 | |||
1682 | init_waitqueue_head(&res->wait); | ||
1683 | atomic_set(&res->stopping, 0); | ||
1684 | ret = resizer_init_entities(res); | ||
1685 | if (ret < 0) | ||
1686 | goto out; | ||
1687 | |||
1688 | out: | ||
1689 | if (ret) | ||
1690 | omap3isp_resizer_cleanup(isp); | ||
1691 | |||
1692 | return ret; | ||
1693 | } | ||
diff --git a/drivers/media/video/omap3isp/ispresizer.h b/drivers/media/video/omap3isp/ispresizer.h new file mode 100644 index 000000000000..76abc2e42126 --- /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/luma_enhance_table.h b/drivers/media/video/omap3isp/luma_enhance_table.h new file mode 100644 index 000000000000..098b45e2280f --- /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 000000000000..d50451a4a242 --- /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 | ||