diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/media/video/tegra | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/media/video/tegra')
35 files changed, 20292 insertions, 0 deletions
diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig new file mode 100644 index 00000000000..404d771a717 --- /dev/null +++ b/drivers/media/video/tegra/Kconfig | |||
@@ -0,0 +1,90 @@ | |||
1 | source "drivers/media/video/tegra/avp/Kconfig" | ||
2 | source "drivers/media/video/tegra/mediaserver/Kconfig" | ||
3 | source "drivers/media/video/tegra/nvavp/Kconfig" | ||
4 | |||
5 | config TEGRA_CAMERA | ||
6 | bool "Enable support for tegra camera/isp hardware" | ||
7 | depends on ARCH_TEGRA | ||
8 | default y | ||
9 | help | ||
10 | Enables support for the Tegra camera interface | ||
11 | |||
12 | If unsure, say Y | ||
13 | |||
14 | config TEGRA_DTV | ||
15 | bool "Enable support for tegra dtv interface" | ||
16 | depends on ARCH_TEGRA | ||
17 | default y | ||
18 | help | ||
19 | Enables support for the Tegra dtv interface | ||
20 | |||
21 | If unsure, say Y | ||
22 | |||
23 | config VIDEO_OV5650 | ||
24 | tristate "OV5650 camera sensor support" | ||
25 | depends on I2C && ARCH_TEGRA | ||
26 | ---help--- | ||
27 | This is a driver for the Omnivision OV5650 5MP camera sensor | ||
28 | for use with the tegra isp. | ||
29 | |||
30 | config VIDEO_OV14810 | ||
31 | tristate "OV14810 camera sensor support" | ||
32 | depends on I2C && ARCH_TEGRA | ||
33 | ---help--- | ||
34 | This is a driver for the Omnivision OV14810 14MP camera sensor | ||
35 | for use with the tegra isp. | ||
36 | |||
37 | |||
38 | config VIDEO_OV9726 | ||
39 | tristate "OV9726 camera sensor support" | ||
40 | depends on I2C && ARCH_TEGRA | ||
41 | ---help--- | ||
42 | This is a driver for the Omnivision OV9726 camera sensor | ||
43 | for use with the tegra isp. | ||
44 | |||
45 | config VIDEO_OV2710 | ||
46 | tristate "OV2710 camera sensor support" | ||
47 | depends on I2C && ARCH_TEGRA | ||
48 | ---help--- | ||
49 | This is a driver for the Omnivision OV2710 camera sensor | ||
50 | for use with the tegra isp. | ||
51 | |||
52 | config VIDEO_AR0832 | ||
53 | tristate "AR0832 camera sensor support" | ||
54 | depends on I2C && ARCH_TEGRA | ||
55 | ---help--- | ||
56 | This is a driver for the AR0832 camera sensor | ||
57 | for use with the tegra isp. | ||
58 | |||
59 | config VIDEO_SOC380 | ||
60 | tristate "SOC380 camera sensor support" | ||
61 | depends on I2C && ARCH_TEGRA | ||
62 | ---help--- | ||
63 | This is a driver for the Semco soc380 camera sensor | ||
64 | for use with the tegra isp. | ||
65 | |||
66 | config TORCH_SSL3250A | ||
67 | tristate "SSL3250A flash/torch support" | ||
68 | depends on I2C && ARCH_TEGRA | ||
69 | ---help--- | ||
70 | This is a driver for the SSL3250A flash/torch camera device | ||
71 | |||
72 | config TORCH_TPS61050 | ||
73 | tristate "TPS61050 flash/torch support" | ||
74 | depends on I2C && ARCH_TEGRA | ||
75 | ---help--- | ||
76 | This is a driver for the TPS61050 flash/torch camera device | ||
77 | |||
78 | config VIDEO_SH532U | ||
79 | tristate "SH532U focuser support" | ||
80 | depends on I2C && ARCH_TEGRA | ||
81 | ---help--- | ||
82 | This is a driver for the SEMCO SH532U focuser | ||
83 | for use with the tegra isp. | ||
84 | |||
85 | config VIDEO_AD5820 | ||
86 | tristate "AD5820 focuser support" | ||
87 | depends on I2C && ARCH_TEGRA | ||
88 | ---help--- | ||
89 | This is a driver for the AD5820 focuser | ||
90 | for use with the tegra isp. | ||
diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile new file mode 100644 index 00000000000..5d404e44fd2 --- /dev/null +++ b/drivers/media/video/tegra/Makefile | |||
@@ -0,0 +1,20 @@ | |||
1 | GCOV_PROFILE := y | ||
2 | # | ||
3 | # Makefile for the video capture/playback device drivers. | ||
4 | # | ||
5 | obj-y += avp/ | ||
6 | obj-$(CONFIG_TEGRA_MEDIASERVER) += mediaserver/ | ||
7 | obj-$(CONFIG_TEGRA_NVAVP) += nvavp/ | ||
8 | obj-$(CONFIG_TEGRA_DTV) += tegra_dtv.o | ||
9 | obj-$(CONFIG_TEGRA_CAMERA) += tegra_camera.o | ||
10 | obj-$(CONFIG_VIDEO_AR0832) += ar0832_main.o | ||
11 | obj-$(CONFIG_VIDEO_OV5650) += ov5650.o | ||
12 | obj-$(CONFIG_VIDEO_OV14810) += ov14810.o | ||
13 | obj-$(CONFIG_VIDEO_OV9726) += ov9726.o | ||
14 | obj-$(CONFIG_VIDEO_OV2710) += ov2710.o | ||
15 | obj-$(CONFIG_VIDEO_SOC380) += soc380.o | ||
16 | obj-$(CONFIG_TORCH_SSL3250A) += ssl3250a.o | ||
17 | obj-$(CONFIG_TORCH_TPS61050) += tps61050.o | ||
18 | obj-$(CONFIG_VIDEO_SH532U) += sh532u.o | ||
19 | obj-$(CONFIG_VIDEO_AD5820) += ad5820.o | ||
20 | |||
diff --git a/drivers/media/video/tegra/ad5820.c b/drivers/media/video/tegra/ad5820.c new file mode 100644 index 00000000000..19d35bca5b0 --- /dev/null +++ b/drivers/media/video/tegra/ad5820.c | |||
@@ -0,0 +1,231 @@ | |||
1 | /* | ||
2 | * AD5820 focuser driver. | ||
3 | * | ||
4 | * Copyright (C) 2010-2011 NVIDIA Corporation. | ||
5 | * | ||
6 | * Contributors: | ||
7 | * Sachin Nikam <snikam@nvidia.com> | ||
8 | * | ||
9 | * Based on ov5650.c. | ||
10 | * | ||
11 | * This file is licensed under the terms of the GNU General Public License | ||
12 | * version 2. This program is licensed "as is" without any warranty of any | ||
13 | * kind, whether express or implied. | ||
14 | */ | ||
15 | |||
16 | #include <linux/delay.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/miscdevice.h> | ||
20 | #include <linux/regulator/consumer.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/uaccess.h> | ||
23 | #include <media/ad5820.h> | ||
24 | |||
25 | /* Focuser single step & full scale transition time truth table | ||
26 | * in the format of: | ||
27 | * index mode single step transition full scale transition | ||
28 | * 0 0 0 0 | ||
29 | * 1 1 50uS 51.2mS | ||
30 | * 2 1 100uS 102.3mS | ||
31 | * 3 1 200uS 204.6mS | ||
32 | * 4 1 400uS 409.2mS | ||
33 | * 5 1 800uS 818.4mS | ||
34 | * 6 1 1600uS 1636.8mS | ||
35 | * 7 1 3200uS 3273.6mS | ||
36 | * 8 0 0 0 | ||
37 | * 9 2 50uS 1.1mS | ||
38 | * A 2 100uS 2.2mS | ||
39 | * B 2 200uS 4.4mS | ||
40 | * C 2 400uS 8.8mS | ||
41 | * D 2 800uS 17.6mS | ||
42 | * E 2 1600uS 35.2mS | ||
43 | * F 2 3200uS 70.4mS | ||
44 | */ | ||
45 | |||
46 | /* pick up the mode index setting and its settle time from the above table */ | ||
47 | #define AD5820_TRANSITION_MODE 0x0B | ||
48 | #define SETTLETIME_MS 5 | ||
49 | |||
50 | #define POS_LOW (0) | ||
51 | #define POS_HIGH (1023) | ||
52 | #define FOCAL_LENGTH (4.507f) | ||
53 | #define FNUMBER (2.8f) | ||
54 | #define FPOS_COUNT 1024 | ||
55 | |||
56 | struct ad5820_info { | ||
57 | struct i2c_client *i2c_client; | ||
58 | struct regulator *regulator; | ||
59 | struct ad5820_config config; | ||
60 | }; | ||
61 | |||
62 | static int ad5820_write(struct i2c_client *client, u32 value) | ||
63 | { | ||
64 | int count; | ||
65 | struct i2c_msg msg[1]; | ||
66 | unsigned char data[2]; | ||
67 | |||
68 | if (!client->adapter) | ||
69 | return -ENODEV; | ||
70 | |||
71 | data[0] = (u8) ((value >> 4) & 0x3F); | ||
72 | data[1] = (u8) ((value & 0xF) << 4) | AD5820_TRANSITION_MODE; | ||
73 | |||
74 | msg[0].addr = client->addr; | ||
75 | msg[0].flags = 0; | ||
76 | msg[0].len = ARRAY_SIZE(data); | ||
77 | msg[0].buf = data; | ||
78 | |||
79 | count = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); | ||
80 | if (count == ARRAY_SIZE(msg)) | ||
81 | return 0; | ||
82 | |||
83 | return -EIO; | ||
84 | } | ||
85 | |||
86 | static int ad5820_set_position(struct ad5820_info *info, u32 position) | ||
87 | { | ||
88 | if (position < info->config.pos_low || | ||
89 | position > info->config.pos_high) | ||
90 | return -EINVAL; | ||
91 | |||
92 | return ad5820_write(info->i2c_client, position); | ||
93 | } | ||
94 | |||
95 | static long ad5820_ioctl(struct file *file, | ||
96 | unsigned int cmd, unsigned long arg) | ||
97 | { | ||
98 | struct ad5820_info *info = file->private_data; | ||
99 | |||
100 | switch (cmd) { | ||
101 | case AD5820_IOCTL_GET_CONFIG: | ||
102 | { | ||
103 | if (copy_to_user((void __user *) arg, | ||
104 | &info->config, | ||
105 | sizeof(info->config))) { | ||
106 | pr_err("%s: 0x%x\n", __func__, __LINE__); | ||
107 | return -EFAULT; | ||
108 | } | ||
109 | |||
110 | break; | ||
111 | } | ||
112 | case AD5820_IOCTL_SET_POSITION: | ||
113 | return ad5820_set_position(info, (u32) arg); | ||
114 | default: | ||
115 | return -EINVAL; | ||
116 | } | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | struct ad5820_info *info; | ||
122 | |||
123 | static int ad5820_open(struct inode *inode, struct file *file) | ||
124 | { | ||
125 | file->private_data = info; | ||
126 | if (info->regulator) | ||
127 | regulator_enable(info->regulator); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | int ad5820_release(struct inode *inode, struct file *file) | ||
132 | { | ||
133 | if (info->regulator) | ||
134 | regulator_disable(info->regulator); | ||
135 | file->private_data = NULL; | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | |||
140 | static const struct file_operations ad5820_fileops = { | ||
141 | .owner = THIS_MODULE, | ||
142 | .open = ad5820_open, | ||
143 | .unlocked_ioctl = ad5820_ioctl, | ||
144 | .release = ad5820_release, | ||
145 | }; | ||
146 | |||
147 | static struct miscdevice ad5820_device = { | ||
148 | .minor = MISC_DYNAMIC_MINOR, | ||
149 | .name = "ad5820", | ||
150 | .fops = &ad5820_fileops, | ||
151 | }; | ||
152 | |||
153 | static int ad5820_probe(struct i2c_client *client, | ||
154 | const struct i2c_device_id *id) | ||
155 | { | ||
156 | int err; | ||
157 | |||
158 | pr_info("ad5820: probing sensor.\n"); | ||
159 | |||
160 | info = kzalloc(sizeof(struct ad5820_info), GFP_KERNEL); | ||
161 | if (!info) { | ||
162 | pr_err("ad5820: Unable to allocate memory!\n"); | ||
163 | return -ENOMEM; | ||
164 | } | ||
165 | |||
166 | err = misc_register(&ad5820_device); | ||
167 | if (err) { | ||
168 | pr_err("ad5820: Unable to register misc device!\n"); | ||
169 | kfree(info); | ||
170 | return err; | ||
171 | } | ||
172 | |||
173 | info->regulator = regulator_get(&client->dev, "vdd_vcore_af"); | ||
174 | if (IS_ERR_OR_NULL(info->regulator)) { | ||
175 | dev_err(&client->dev, "unable to get regulator %s\n", | ||
176 | dev_name(&client->dev)); | ||
177 | info->regulator = NULL; | ||
178 | } else { | ||
179 | regulator_enable(info->regulator); | ||
180 | } | ||
181 | |||
182 | info->i2c_client = client; | ||
183 | info->config.settle_time = SETTLETIME_MS; | ||
184 | info->config.focal_length = FOCAL_LENGTH; | ||
185 | info->config.fnumber = FNUMBER; | ||
186 | info->config.pos_low = POS_LOW; | ||
187 | info->config.pos_high = POS_HIGH; | ||
188 | i2c_set_clientdata(client, info); | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static int ad5820_remove(struct i2c_client *client) | ||
193 | { | ||
194 | struct ad5820_info *info; | ||
195 | info = i2c_get_clientdata(client); | ||
196 | misc_deregister(&ad5820_device); | ||
197 | kfree(info); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static const struct i2c_device_id ad5820_id[] = { | ||
202 | { "ad5820", 0 }, | ||
203 | { }, | ||
204 | }; | ||
205 | |||
206 | MODULE_DEVICE_TABLE(i2c, ad5820_id); | ||
207 | |||
208 | static struct i2c_driver ad5820_i2c_driver = { | ||
209 | .driver = { | ||
210 | .name = "ad5820", | ||
211 | .owner = THIS_MODULE, | ||
212 | }, | ||
213 | .probe = ad5820_probe, | ||
214 | .remove = ad5820_remove, | ||
215 | .id_table = ad5820_id, | ||
216 | }; | ||
217 | |||
218 | static int __init ad5820_init(void) | ||
219 | { | ||
220 | pr_info("ad5820 sensor driver loading\n"); | ||
221 | return i2c_add_driver(&ad5820_i2c_driver); | ||
222 | } | ||
223 | |||
224 | static void __exit ad5820_exit(void) | ||
225 | { | ||
226 | i2c_del_driver(&ad5820_i2c_driver); | ||
227 | } | ||
228 | |||
229 | module_init(ad5820_init); | ||
230 | module_exit(ad5820_exit); | ||
231 | |||
diff --git a/drivers/media/video/tegra/ar0832_main.c b/drivers/media/video/tegra/ar0832_main.c new file mode 100644 index 00000000000..129825cd5f8 --- /dev/null +++ b/drivers/media/video/tegra/ar0832_main.c | |||
@@ -0,0 +1,2549 @@ | |||
1 | /* | ||
2 | * ar0832_main.c - Aptina AR0832 8M Bayer type sensor driver | ||
3 | * | ||
4 | * Copyright (c) 2011, NVIDIA, All Rights Reserved. | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public License | ||
7 | * version 2. This program is licensed "as is" without any warranty of any | ||
8 | * kind, whether express or implied. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/fs.h> | ||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/miscdevice.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/uaccess.h> | ||
18 | #include <mach/hardware.h> | ||
19 | #include <linux/gpio.h> | ||
20 | #include <linux/debugfs.h> | ||
21 | #include <linux/seq_file.h> | ||
22 | #include <asm/atomic.h> | ||
23 | #include <linux/regulator/consumer.h> | ||
24 | #include <media/ar0832_main.h> | ||
25 | |||
26 | #define POS_LOW 0 | ||
27 | #define POS_HIGH 1000 | ||
28 | #define SETTLETIME_MS 100 | ||
29 | |||
30 | struct ar0832_sensor_info { | ||
31 | int mode; | ||
32 | struct ar0832_stereo_region region; | ||
33 | }; | ||
34 | |||
35 | struct ar0832_focuser_info { | ||
36 | struct ar0832_focuser_config config; | ||
37 | int focuser_init_flag; | ||
38 | u16 last_position; | ||
39 | }; | ||
40 | |||
41 | struct ar0832_power_rail { | ||
42 | struct regulator *sen_1v8_reg; | ||
43 | struct regulator *sen_2v8_reg; | ||
44 | }; | ||
45 | |||
46 | struct ar0832_dev { | ||
47 | struct ar0832_sensor_info *sensor_info; | ||
48 | struct ar0832_focuser_info *focuser_info; | ||
49 | struct ar0832_platform_data *pdata; | ||
50 | struct i2c_client *i2c_client; | ||
51 | struct mutex ar0832_camera_lock; | ||
52 | struct miscdevice misc_dev; | ||
53 | struct ar0832_power_rail power_rail; | ||
54 | int brd_power_cnt; | ||
55 | atomic_t in_use; | ||
56 | char dname[20]; | ||
57 | int is_stereo; | ||
58 | u16 sensor_id_data; | ||
59 | struct dentry *debugdir; | ||
60 | }; | ||
61 | |||
62 | #define UpperByte16to8(x) ((u8)((x & 0xFF00) >> 8)) | ||
63 | #define LowerByte16to8(x) ((u8)(x & 0x00FF)) | ||
64 | |||
65 | #define ar0832_TABLE_WAIT_MS 0 | ||
66 | #define ar0832_TABLE_END 1 | ||
67 | #define ar0832_MAX_RETRIES 3 | ||
68 | |||
69 | /* AR0832 Register */ | ||
70 | #define AR0832_SENSORID_REG 0x0002 | ||
71 | #define AR0832_RESET_REG 0x301A | ||
72 | #define AR0832_ID_REG 0x31FC | ||
73 | #define AR0832_GLOBAL_GAIN_REG 0x305E | ||
74 | #define AR0832_TEST_PATTERN_REG 0x0600 | ||
75 | #define AR0832_GROUP_HOLD_REG 0x0104 | ||
76 | #define AR0832_TEST_RED_REG 0x0602 | ||
77 | #define AR0832_TEST_GREENR_REG 0x0604 | ||
78 | #define AR0832_TEST_BLUE_REG 0x0606 | ||
79 | #define AR0832_TEST_GREENB_REG 0x0608 | ||
80 | |||
81 | |||
82 | |||
83 | /* AR0832_RESET_REG */ | ||
84 | #define AR0832_RESET_REG_GROUPED_PARAMETER_HOLD (1 << 15) | ||
85 | #define AR0832_RESET_REG_GAIN_INSERT (1 << 14) | ||
86 | #define AR0832_RESET_REG_SMIA_SERIALIZER_DIS (1 << 12) | ||
87 | #define AR0832_RESET_REG_RESTART_BAD (1 << 10) | ||
88 | #define AR0832_RESET_REG_MASK_BAD (1 << 9) | ||
89 | #define AR0832_RESET_REG_GPI_EN (1 << 8) | ||
90 | #define AR0832_RESET_REG_PARALLEL_EN (1 << 7) | ||
91 | #define AR0832_RESET_REG_DRIVE_PINS (1 << 6) | ||
92 | #define AR0832_RESET_REG_STDBY_EOF (1 << 4) | ||
93 | #define AR0832_RESET_REG_LOCK_REG (1 << 3) | ||
94 | #define AR0832_RESET_REG_STREAM (1 << 2) | ||
95 | #define AR0832_RESET_REG_RESTART (1 << 1) | ||
96 | #define AR0832_RESET_REG_RESET (1 << 0) | ||
97 | |||
98 | static struct ar0832_reg mode_start[] = { | ||
99 | {ar0832_TABLE_END, 0x0000} | ||
100 | }; | ||
101 | |||
102 | static struct ar0832_reg mode_3264X2448_8140[] = { | ||
103 | /* mode start */ | ||
104 | {0x301A, 0x0058}, /* RESET_REGISTER */ | ||
105 | {0x301A, 0x0050}, /* RESET_REGISTER */ | ||
106 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
107 | {0x3064, 0x7800}, /* RESERVED_MFR_3064 */ | ||
108 | {0x31AE, 0x0202}, /* SERIAL_FORMAT */ | ||
109 | /* MT9E013 Recommended Settings */ | ||
110 | {0x31B0, 0x0083}, /* FRAME_PREAMBLE */ | ||
111 | {0x31B2, 0x004D}, /* LINE_PREAMBLE */ | ||
112 | {0x31B4, 0x0E77}, /* MIPI_TIMING_0 */ | ||
113 | {0x31B6, 0x0D20}, /* MIPI_TIMING_1 */ | ||
114 | {0x31B8, 0x020E}, /* MIPI_TIMING_2 */ | ||
115 | {0x31BA, 0x0710}, /* MIPI_TIMING_3 */ | ||
116 | {0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */ | ||
117 | {ar0832_TABLE_WAIT_MS, 0x0005}, | ||
118 | {0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */ | ||
119 | {0x3044, 0x0590}, | ||
120 | {0x306E, 0xFC80}, | ||
121 | {0x30B2, 0xC000}, | ||
122 | {0x30D6, 0x0800}, | ||
123 | {0x316C, 0xB42F}, | ||
124 | {0x316E, 0x869A}, | ||
125 | {0x3170, 0x210E}, | ||
126 | {0x317A, 0x010E}, | ||
127 | {0x31E0, 0x1FB9}, | ||
128 | {0x31E6, 0x07FC}, | ||
129 | {0x37C0, 0x0000}, | ||
130 | {0x37C2, 0x0000}, | ||
131 | {0x37C4, 0x0000}, | ||
132 | {0x37C6, 0x0000}, | ||
133 | {0x3E00, 0x0011}, | ||
134 | {0x3E02, 0x8801}, | ||
135 | {0x3E04, 0x2801}, | ||
136 | {0x3E06, 0x8449}, | ||
137 | {0x3E08, 0x6841}, | ||
138 | {0x3E0A, 0x400C}, | ||
139 | {0x3E0C, 0x1001}, | ||
140 | {0x3E0E, 0x2603}, | ||
141 | {0x3E10, 0x4B41}, | ||
142 | {0x3E12, 0x4B24}, | ||
143 | {0x3E14, 0xA3CF}, | ||
144 | {0x3E16, 0x8802}, | ||
145 | {0x3E18, 0x8401}, | ||
146 | {0x3E1A, 0x8601}, | ||
147 | {0x3E1C, 0x8401}, | ||
148 | {0x3E1E, 0x840A}, | ||
149 | {0x3E20, 0xFF00}, | ||
150 | {0x3E22, 0x8401}, | ||
151 | {0x3E24, 0x00FF}, | ||
152 | {0x3E26, 0x0088}, | ||
153 | {0x3E28, 0x2E8A}, | ||
154 | {0x3E30, 0x0000}, | ||
155 | {0x3E32, 0x8801}, | ||
156 | {0x3E34, 0x4029}, | ||
157 | {0x3E36, 0x00FF}, | ||
158 | {0x3E38, 0x8469}, | ||
159 | {0x3E3A, 0x00FF}, | ||
160 | {0x3E3C, 0x2801}, | ||
161 | {0x3E3E, 0x3E2A}, | ||
162 | {0x3E40, 0x1C01}, | ||
163 | {0x3E42, 0xFF84}, | ||
164 | {0x3E44, 0x8401}, | ||
165 | {0x3E46, 0x0C01}, | ||
166 | {0x3E48, 0x8401}, | ||
167 | {0x3E4A, 0x00FF}, | ||
168 | {0x3E4C, 0x8402}, | ||
169 | {0x3E4E, 0x8984}, | ||
170 | {0x3E50, 0x6628}, | ||
171 | {0x3E52, 0x8340}, | ||
172 | {0x3E54, 0x00FF}, | ||
173 | {0x3E56, 0x4A42}, | ||
174 | {0x3E58, 0x2703}, | ||
175 | {0x3E5A, 0x6752}, | ||
176 | {0x3E5C, 0x3F2A}, | ||
177 | {0x3E5E, 0x846A}, | ||
178 | {0x3E60, 0x4C01}, | ||
179 | {0x3E62, 0x8401}, | ||
180 | {0x3E66, 0x3901}, | ||
181 | {0x3E90, 0x2C01}, | ||
182 | {0x3E98, 0x2B02}, | ||
183 | {0x3E92, 0x2A04}, | ||
184 | {0x3E94, 0x2509}, | ||
185 | {0x3E96, 0x0000}, | ||
186 | {0x3E9A, 0x2905}, | ||
187 | {0x3E9C, 0x00FF}, | ||
188 | {0x3ECC, 0x00EB}, | ||
189 | {0x3ED0, 0x1E24}, | ||
190 | {0x3ED4, 0xAFC4}, | ||
191 | {0x3ED6, 0x909B}, | ||
192 | {0x3EE0, 0x2424}, | ||
193 | {0x3EE2, 0x9797}, | ||
194 | {0x3EE4, 0xC100}, | ||
195 | {0x3EE6, 0x0540}, | ||
196 | {0x3174, 0x8000}, | ||
197 | |||
198 | /* mode end */ | ||
199 | {0x0300, 0x0004}, /* VT_PIX_CLK_DIV */ | ||
200 | {0x0302, 0x0001}, /* VT_SYS_CLK_DIV */ | ||
201 | {0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */ | ||
202 | {0x0306, 0x0040}, /* PLL_MULTIPLIER */ | ||
203 | {0x0308, 0x000A}, /* OP_PIX_CLK_DIV */ | ||
204 | {0x030A, 0x0001}, /* OP_SYS_CLK_DIV */ | ||
205 | {ar0832_TABLE_WAIT_MS, 0x0001}, | ||
206 | {0x3064, 0x7400}, /* RESERVED_MFR_3064 */ | ||
207 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
208 | {0x0344, 0x0004}, /* X_ADDR_START */ | ||
209 | {0x0348, 0x0CCB}, /* X_ADDR_END */ | ||
210 | {0x0346, 0x0004}, /* Y_ADDR_START */ | ||
211 | {0x034A, 0x099B}, /* Y_ADDR_END */ | ||
212 | {0x034C, 0x0CC8}, /* X_OUTPUT_SIZE */ | ||
213 | {0x034E, 0x0998}, /* Y_OUTPUT_SIZE */ | ||
214 | {0x3040, 0xC041}, /* READ_MODE */ | ||
215 | {0x306E, 0xFC80}, /* DATAPATH_SELECT */ | ||
216 | {0x0400, 0x0000}, /* SCALING_MODE */ | ||
217 | {0x0404, 0x0010}, /* SCALE_M */ | ||
218 | {0x3178, 0x0000}, /* RESERVED_MFR_3178 */ | ||
219 | {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ | ||
220 | {0x0400, 0x0000}, /* SCALING_MODE */ | ||
221 | {0x0404, 0x0010}, /* SCALE_M */ | ||
222 | {0x0342, 0x133C}, /* LINE_LENGTH_PCK */ | ||
223 | {0x0340, 0x0A27}, /* FRAME_LENGTH_LINES */ | ||
224 | {0x0202, 0x0A27}, /* COARSE_INTEGRATION_TIME */ | ||
225 | {0x3014, 0x09DC}, /* FINE_INTEGRATION_TIME */ | ||
226 | {0x3010, 0x0078}, /* FINE_CORRECTION */ | ||
227 | {0x301A, 0x8250}, /* RESET_REGISTER */ | ||
228 | {0x301A, 0x8650}, /* RESET_REGISTER */ | ||
229 | {0x301A, 0x8658}, /* RESET_REGISTER */ | ||
230 | /* gain */ | ||
231 | {0x305e, 0x10AA}, /* gain */ | ||
232 | {0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */ | ||
233 | {0x301A, 0x065C}, /* RESET_REGISTER */ | ||
234 | {ar0832_TABLE_END, 0x0000} | ||
235 | }; | ||
236 | |||
237 | static struct ar0832_reg mode_3264X2448_8141[] = { | ||
238 | /* mode start */ | ||
239 | {0x301A, 0x0058}, /* RESET_REGISTER */ | ||
240 | {0x301A, 0x0050}, /* RESET_REGISTER */ | ||
241 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
242 | {0x3064, 0x7800}, /* RESERVED_MFR_3064 */ | ||
243 | {0x31AE, 0x0202}, /* SERIAL_FORMAT */ | ||
244 | /* AR0832 Recommended Settings */ | ||
245 | {0x3044, 0x0590}, | ||
246 | {0x306E, 0xFC80}, | ||
247 | {0x30B2, 0xC000}, | ||
248 | {0x30D6, 0x0800}, | ||
249 | {0x316C, 0xB42F}, | ||
250 | {0x316E, 0x869A}, | ||
251 | {0x3170, 0x210E}, | ||
252 | {0x317A, 0x010E}, | ||
253 | {0x31E0, 0x1FB9}, | ||
254 | {0x31E6, 0x07FC}, | ||
255 | {0x37C0, 0x0000}, | ||
256 | {0x37C2, 0x0000}, | ||
257 | {0x37C4, 0x0000}, | ||
258 | {0x37C6, 0x0000}, | ||
259 | {0x3E00, 0x0011}, | ||
260 | {0x3E02, 0x8801}, | ||
261 | {0x3E04, 0x2801}, | ||
262 | {0x3E06, 0x8449}, | ||
263 | {0x3E08, 0x6841}, | ||
264 | {0x3E0A, 0x400C}, | ||
265 | {0x3E0C, 0x1001}, | ||
266 | {0x3E0E, 0x2603}, | ||
267 | {0x3E10, 0x4B41}, | ||
268 | {0x3E12, 0x4B24}, | ||
269 | {0x3E14, 0xA3CF}, | ||
270 | {0x3E16, 0x8802}, | ||
271 | {0x3E18, 0x8401}, | ||
272 | {0x3E1A, 0x8601}, | ||
273 | {0x3E1C, 0x8401}, | ||
274 | {0x3E1E, 0x840A}, | ||
275 | {0x3E20, 0xFF00}, | ||
276 | {0x3E22, 0x8401}, | ||
277 | {0x3E24, 0x00FF}, | ||
278 | {0x3E26, 0x0088}, | ||
279 | {0x3E28, 0x2E8A}, | ||
280 | {0x3E30, 0x0000}, | ||
281 | {0x3E32, 0x8801}, | ||
282 | {0x3E34, 0x4029}, | ||
283 | {0x3E36, 0x00FF}, | ||
284 | {0x3E38, 0x8469}, | ||
285 | {0x3E3A, 0x00FF}, | ||
286 | {0x3E3C, 0x2801}, | ||
287 | {0x3E3E, 0x3E2A}, | ||
288 | {0x3E40, 0x1C01}, | ||
289 | {0x3E42, 0xFF84}, | ||
290 | {0x3E44, 0x8401}, | ||
291 | {0x3E46, 0x0C01}, | ||
292 | {0x3E48, 0x8401}, | ||
293 | {0x3E4A, 0x00FF}, | ||
294 | {0x3E4C, 0x8402}, | ||
295 | {0x3E4E, 0x8984}, | ||
296 | {0x3E50, 0x6628}, | ||
297 | {0x3E52, 0x8340}, | ||
298 | {0x3E54, 0x00FF}, | ||
299 | {0x3E56, 0x4A42}, | ||
300 | {0x3E58, 0x2703}, | ||
301 | {0x3E5A, 0x6752}, | ||
302 | {0x3E5C, 0x3F2A}, | ||
303 | {0x3E5E, 0x846A}, | ||
304 | {0x3E60, 0x4C01}, | ||
305 | {0x3E62, 0x8401}, | ||
306 | {0x3E66, 0x3901}, | ||
307 | {0x3E90, 0x2C01}, | ||
308 | {0x3E98, 0x2B02}, | ||
309 | {0x3E92, 0x2A04}, | ||
310 | {0x3E94, 0x2509}, | ||
311 | {0x3E96, 0x0000}, | ||
312 | {0x3E9A, 0x2905}, | ||
313 | {0x3E9C, 0x00FF}, | ||
314 | {0x3ECC, 0x00EB}, | ||
315 | {0x3ED0, 0x1E24}, | ||
316 | {0x3ED4, 0xAFC4}, | ||
317 | {0x3ED6, 0x909B}, | ||
318 | {0x3EE0, 0x2424}, | ||
319 | {0x3EE2, 0x9797}, | ||
320 | {0x3EE4, 0xC100}, | ||
321 | {0x3EE6, 0x0540}, | ||
322 | {0x3174, 0x8000}, | ||
323 | |||
324 | /* mode end */ | ||
325 | {0x0300, 0x0004}, /* VT_PIX_CLK_DIV */ | ||
326 | {0x0302, 0x0001}, /* VT_SYS_CLK_DIV */ | ||
327 | {0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */ | ||
328 | {0x0306, 0x0040}, /* PLL_MULTIPLIER */ | ||
329 | {0x0308, 0x000A}, /* OP_PIX_CLK_DIV */ | ||
330 | {0x030A, 0x0001}, /* OP_SYS_CLK_DIV */ | ||
331 | {ar0832_TABLE_WAIT_MS, 0x0001}, | ||
332 | {0x3064, 0x7400}, /* RESERVED_MFR_3064 */ | ||
333 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
334 | {0x0344, 0x0004}, /* X_ADDR_START */ | ||
335 | {0x0348, 0x0CCB}, /* X_ADDR_END */ | ||
336 | {0x0346, 0x0004}, /* Y_ADDR_START */ | ||
337 | {0x034A, 0x099B}, /* Y_ADDR_END */ | ||
338 | {0x034C, 0x0CC8}, /* X_OUTPUT_SIZE */ | ||
339 | {0x034E, 0x0998}, /* Y_OUTPUT_SIZE */ | ||
340 | {0x3040, 0xC041}, /* READ_MODE */ | ||
341 | {0x306E, 0xFC80}, /* DATAPATH_SELECT */ | ||
342 | {0x0400, 0x0000}, /* SCALING_MODE */ | ||
343 | {0x0404, 0x0010}, /* SCALE_M */ | ||
344 | {0x3178, 0x0000}, /* RESERVED_MFR_3178 */ | ||
345 | {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ | ||
346 | {0x0400, 0x0000}, /* SCALING_MODE */ | ||
347 | {0x0404, 0x0010}, /* SCALE_M */ | ||
348 | {0x0342, 0x133C}, /* LINE_LENGTH_PCK */ | ||
349 | {0x0340, 0x0A27}, /* FRAME_LENGTH_LINES */ | ||
350 | {0x0202, 0x0A27}, /* COARSE_INTEGRATION_TIME */ | ||
351 | {0x3014, 0x09DC}, /* FINE_INTEGRATION_TIME */ | ||
352 | {0x3010, 0x0078}, /* FINE_CORRECTION */ | ||
353 | {0x301A, 0x8250}, /* RESET_REGISTER */ | ||
354 | {0x301A, 0x8650}, /* RESET_REGISTER */ | ||
355 | {0x301A, 0x8658}, /* RESET_REGISTER */ | ||
356 | /* gain */ | ||
357 | {0x305e, 0x10AA}, /* gain */ | ||
358 | {0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */ | ||
359 | {0x301A, 0x065C}, /* RESET_REGISTER */ | ||
360 | {ar0832_TABLE_END, 0x0000} | ||
361 | }; | ||
362 | |||
363 | static struct ar0832_reg mode_2880X1620_8140[] = { | ||
364 | /* mode start */ | ||
365 | {0x301A, 0x0058}, /* RESET_REGISTER */ | ||
366 | {0x301A, 0x0050}, /* RESET_REGISTER */ | ||
367 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
368 | {0x3064, 0x7800}, /* RESERVED_MFR_3064 */ | ||
369 | {0x31AE, 0x0202}, /* SERIAL_FORMAT */ | ||
370 | /* MT9E013 Recommended Settings */ | ||
371 | {0x31B0, 0x0083}, /* FRAME_PREAMBLE */ | ||
372 | {0x31B2, 0x004D}, /* LINE_PREAMBLE */ | ||
373 | {0x31B4, 0x0E77}, /* MIPI_TIMING_0 */ | ||
374 | {0x31B6, 0x0D20}, /* MIPI_TIMING_1 */ | ||
375 | {0x31B8, 0x020E}, /* MIPI_TIMING_2 */ | ||
376 | {0x31BA, 0x0710}, /* MIPI_TIMING_3 */ | ||
377 | {0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */ | ||
378 | {ar0832_TABLE_WAIT_MS, 0x0005}, | ||
379 | {0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */ | ||
380 | {0x3044, 0x0590}, | ||
381 | {0x306E, 0xFC80}, | ||
382 | {0x30B2, 0xC000}, | ||
383 | {0x30D6, 0x0800}, | ||
384 | {0x316C, 0xB42F}, | ||
385 | {0x316E, 0x869A}, | ||
386 | {0x3170, 0x210E}, | ||
387 | {0x317A, 0x010E}, | ||
388 | {0x31E0, 0x1FB9}, | ||
389 | {0x31E6, 0x07FC}, | ||
390 | {0x37C0, 0x0000}, | ||
391 | {0x37C2, 0x0000}, | ||
392 | {0x37C4, 0x0000}, | ||
393 | {0x37C6, 0x0000}, | ||
394 | {0x3E00, 0x0011}, | ||
395 | {0x3E02, 0x8801}, | ||
396 | {0x3E04, 0x2801}, | ||
397 | {0x3E06, 0x8449}, | ||
398 | {0x3E08, 0x6841}, | ||
399 | {0x3E0A, 0x400C}, | ||
400 | {0x3E0C, 0x1001}, | ||
401 | {0x3E0E, 0x2603}, | ||
402 | {0x3E10, 0x4B41}, | ||
403 | {0x3E12, 0x4B24}, | ||
404 | {0x3E14, 0xA3CF}, | ||
405 | {0x3E16, 0x8802}, | ||
406 | {0x3E18, 0x8401}, | ||
407 | {0x3E1A, 0x8601}, | ||
408 | {0x3E1C, 0x8401}, | ||
409 | {0x3E1E, 0x840A}, | ||
410 | {0x3E20, 0xFF00}, | ||
411 | {0x3E22, 0x8401}, | ||
412 | {0x3E24, 0x00FF}, | ||
413 | {0x3E26, 0x0088}, | ||
414 | {0x3E28, 0x2E8A}, | ||
415 | {0x3E30, 0x0000}, | ||
416 | {0x3E32, 0x8801}, | ||
417 | {0x3E34, 0x4029}, | ||
418 | {0x3E36, 0x00FF}, | ||
419 | {0x3E38, 0x8469}, | ||
420 | {0x3E3A, 0x00FF}, | ||
421 | {0x3E3C, 0x2801}, | ||
422 | {0x3E3E, 0x3E2A}, | ||
423 | {0x3E40, 0x1C01}, | ||
424 | {0x3E42, 0xFF84}, | ||
425 | {0x3E44, 0x8401}, | ||
426 | {0x3E46, 0x0C01}, | ||
427 | {0x3E48, 0x8401}, | ||
428 | {0x3E4A, 0x00FF}, | ||
429 | {0x3E4C, 0x8402}, | ||
430 | {0x3E4E, 0x8984}, | ||
431 | {0x3E50, 0x6628}, | ||
432 | {0x3E52, 0x8340}, | ||
433 | {0x3E54, 0x00FF}, | ||
434 | {0x3E56, 0x4A42}, | ||
435 | {0x3E58, 0x2703}, | ||
436 | {0x3E5A, 0x6752}, | ||
437 | {0x3E5C, 0x3F2A}, | ||
438 | {0x3E5E, 0x846A}, | ||
439 | {0x3E60, 0x4C01}, | ||
440 | {0x3E62, 0x8401}, | ||
441 | {0x3E66, 0x3901}, | ||
442 | {0x3E90, 0x2C01}, | ||
443 | {0x3E98, 0x2B02}, | ||
444 | {0x3E92, 0x2A04}, | ||
445 | {0x3E94, 0x2509}, | ||
446 | {0x3E96, 0x0000}, | ||
447 | {0x3E9A, 0x2905}, | ||
448 | {0x3E9C, 0x00FF}, | ||
449 | {0x3ECC, 0x00EB}, | ||
450 | {0x3ED0, 0x1E24}, | ||
451 | {0x3ED4, 0xAFC4}, | ||
452 | {0x3ED6, 0x909B}, | ||
453 | {0x3EE0, 0x2424}, | ||
454 | {0x3EE2, 0x9797}, | ||
455 | {0x3EE4, 0xC100}, | ||
456 | {0x3EE6, 0x0540}, | ||
457 | {0x3174, 0x8000}, | ||
458 | |||
459 | /* mode end */ | ||
460 | {0x0300, 0x0004}, /* VT_PIX_CLK_DIV */ | ||
461 | {0x0302, 0x0001}, /* VT_SYS_CLK_DIV */ | ||
462 | {0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */ | ||
463 | {0x0306, 0x0040}, /* PLL_MULTIPLIER */ | ||
464 | {0x0308, 0x000A}, /* OP_PIX_CLK_DIV */ | ||
465 | {0x030A, 0x0001}, /* OP_SYS_CLK_DIV */ | ||
466 | {ar0832_TABLE_WAIT_MS, 0x0001}, | ||
467 | {0x3064, 0x7400}, /* RESERVED_MFR_3064 */ | ||
468 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
469 | {0x0344, 0x00C8}, /* X_ADDR_START */ | ||
470 | {0x0348, 0x0C07}, /* X_ADDR_END */ | ||
471 | {0x0346, 0x01A6}, /* Y_ADDR_START */ | ||
472 | {0x034A, 0x07F9}, /* Y_ADDR_END */ | ||
473 | {0x034C, 0x0B40}, /* X_OUTPUT_SIZE */ | ||
474 | {0x034E, 0x0654}, /* Y_OUTPUT_SIZE */ | ||
475 | {0x3040, 0xC041}, /* READ_MODE */ | ||
476 | {0x306E, 0xFC80}, /* DATAPATH_SELECT */ | ||
477 | {0x0400, 0x0000}, /* SCALING_MODE */ | ||
478 | {0x0404, 0x0010}, /* SCALE_M */ | ||
479 | {0x3178, 0x0000}, /* RESERVED_MFR_3178 */ | ||
480 | {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ | ||
481 | |||
482 | {0x0342, 0x11B8}, /* LINE_LENGTH_PCK */ | ||
483 | {0x0340, 0x06E3}, /* FRAME_LENGTH_LINES */ | ||
484 | {0x0202, 0x06E3}, /* COARSE_INTEGRATION_TIME */ | ||
485 | {0x3014, 0x0BD8}, /* FINE_INTEGRATION_TIME */ | ||
486 | {0x3010, 0x0078}, /* FINE_CORRECTION */ | ||
487 | {0x301A, 0x8250}, /* RESET_REGISTER */ | ||
488 | {0x301A, 0x8650}, /* RESET_REGISTER */ | ||
489 | {0x301A, 0x8658}, /* RESET_REGISTER */ | ||
490 | /* gain */ | ||
491 | {0x305e, 0x10AA}, /* gain */ | ||
492 | {0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */ | ||
493 | {0x301A, 0x065C}, /* RESET_REGISTER */ | ||
494 | {ar0832_TABLE_END, 0x0000} | ||
495 | }; | ||
496 | |||
497 | static struct ar0832_reg mode_2880X1620_8141[] = { | ||
498 | /* mode start */ | ||
499 | {0x301A, 0x0058}, /* RESET_REGISTER */ | ||
500 | {0x301A, 0x0050}, /* RESET_REGISTER */ | ||
501 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
502 | {0x3064, 0x7800}, /* RESERVED_MFR_3064 */ | ||
503 | {0x31AE, 0x0202}, /* SERIAL_FORMAT */ | ||
504 | /* AR0832 Recommended Settings */ | ||
505 | {0x3044, 0x0590}, | ||
506 | {0x306E, 0xFC80}, | ||
507 | {0x30B2, 0xC000}, | ||
508 | {0x30D6, 0x0800}, | ||
509 | {0x316C, 0xB42F}, | ||
510 | {0x316E, 0x869A}, | ||
511 | {0x3170, 0x210E}, | ||
512 | {0x317A, 0x010E}, | ||
513 | {0x31E0, 0x1FB9}, | ||
514 | {0x31E6, 0x07FC}, | ||
515 | {0x37C0, 0x0000}, | ||
516 | {0x37C2, 0x0000}, | ||
517 | {0x37C4, 0x0000}, | ||
518 | {0x37C6, 0x0000}, | ||
519 | {0x3E00, 0x0011}, | ||
520 | {0x3E02, 0x8801}, | ||
521 | {0x3E04, 0x2801}, | ||
522 | {0x3E06, 0x8449}, | ||
523 | {0x3E08, 0x6841}, | ||
524 | {0x3E0A, 0x400C}, | ||
525 | {0x3E0C, 0x1001}, | ||
526 | {0x3E0E, 0x2603}, | ||
527 | {0x3E10, 0x4B41}, | ||
528 | {0x3E12, 0x4B24}, | ||
529 | {0x3E14, 0xA3CF}, | ||
530 | {0x3E16, 0x8802}, | ||
531 | {0x3E18, 0x8401}, | ||
532 | {0x3E1A, 0x8601}, | ||
533 | {0x3E1C, 0x8401}, | ||
534 | {0x3E1E, 0x840A}, | ||
535 | {0x3E20, 0xFF00}, | ||
536 | {0x3E22, 0x8401}, | ||
537 | {0x3E24, 0x00FF}, | ||
538 | {0x3E26, 0x0088}, | ||
539 | {0x3E28, 0x2E8A}, | ||
540 | {0x3E30, 0x0000}, | ||
541 | {0x3E32, 0x8801}, | ||
542 | {0x3E34, 0x4029}, | ||
543 | {0x3E36, 0x00FF}, | ||
544 | {0x3E38, 0x8469}, | ||
545 | {0x3E3A, 0x00FF}, | ||
546 | {0x3E3C, 0x2801}, | ||
547 | {0x3E3E, 0x3E2A}, | ||
548 | {0x3E40, 0x1C01}, | ||
549 | {0x3E42, 0xFF84}, | ||
550 | {0x3E44, 0x8401}, | ||
551 | {0x3E46, 0x0C01}, | ||
552 | {0x3E48, 0x8401}, | ||
553 | {0x3E4A, 0x00FF}, | ||
554 | {0x3E4C, 0x8402}, | ||
555 | {0x3E4E, 0x8984}, | ||
556 | {0x3E50, 0x6628}, | ||
557 | {0x3E52, 0x8340}, | ||
558 | {0x3E54, 0x00FF}, | ||
559 | {0x3E56, 0x4A42}, | ||
560 | {0x3E58, 0x2703}, | ||
561 | {0x3E5A, 0x6752}, | ||
562 | {0x3E5C, 0x3F2A}, | ||
563 | {0x3E5E, 0x846A}, | ||
564 | {0x3E60, 0x4C01}, | ||
565 | {0x3E62, 0x8401}, | ||
566 | {0x3E66, 0x3901}, | ||
567 | {0x3E90, 0x2C01}, | ||
568 | {0x3E98, 0x2B02}, | ||
569 | {0x3E92, 0x2A04}, | ||
570 | {0x3E94, 0x2509}, | ||
571 | {0x3E96, 0x0000}, | ||
572 | {0x3E9A, 0x2905}, | ||
573 | {0x3E9C, 0x00FF}, | ||
574 | {0x3ECC, 0x00EB}, | ||
575 | {0x3ED0, 0x1E24}, | ||
576 | {0x3ED4, 0xAFC4}, | ||
577 | {0x3ED6, 0x909B}, | ||
578 | {0x3EE0, 0x2424}, | ||
579 | {0x3EE2, 0x9797}, | ||
580 | {0x3EE4, 0xC100}, | ||
581 | {0x3EE6, 0x0540}, | ||
582 | {0x3174, 0x8000}, | ||
583 | |||
584 | /* mode end */ | ||
585 | {0x0300, 0x0004}, /* VT_PIX_CLK_DIV */ | ||
586 | {0x0302, 0x0001}, /* VT_SYS_CLK_DIV */ | ||
587 | {0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */ | ||
588 | {0x0306, 0x0040}, /* PLL_MULTIPLIER */ | ||
589 | {0x0308, 0x000A}, /* OP_PIX_CLK_DIV */ | ||
590 | {0x030A, 0x0001}, /* OP_SYS_CLK_DIV */ | ||
591 | {ar0832_TABLE_WAIT_MS, 0x0001}, | ||
592 | {0x3064, 0x7400}, /* RESERVED_MFR_3064 */ | ||
593 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
594 | {0x0344, 0x00C8}, /* X_ADDR_START */ | ||
595 | {0x0348, 0x0C07}, /* X_ADDR_END */ | ||
596 | {0x0346, 0x01A6}, /* Y_ADDR_START */ | ||
597 | {0x034A, 0x07F9}, /* Y_ADDR_END */ | ||
598 | {0x034C, 0x0B40}, /* X_OUTPUT_SIZE */ | ||
599 | {0x034E, 0x0654}, /* Y_OUTPUT_SIZE */ | ||
600 | {0x3040, 0xC041}, /* READ_MODE */ | ||
601 | {0x306E, 0xFC80}, /* DATAPATH_SELECT */ | ||
602 | {0x0400, 0x0000}, /* SCALING_MODE */ | ||
603 | {0x0404, 0x0010}, /* SCALE_M */ | ||
604 | {0x3178, 0x0000}, /* RESERVED_MFR_3178 */ | ||
605 | {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ | ||
606 | |||
607 | {0x0342, 0x11B8}, /* LINE_LENGTH_PCK */ | ||
608 | {0x0340, 0x06E3}, /* FRAME_LENGTH_LINES */ | ||
609 | {0x0202, 0x06E3}, /* COARSE_INTEGRATION_TIME */ | ||
610 | {0x3014, 0x0BD8}, /* FINE_INTEGRATION_TIME */ | ||
611 | {0x3010, 0x0078}, /* FINE_CORRECTION */ | ||
612 | {0x301A, 0x8250}, /* RESET_REGISTER */ | ||
613 | {0x301A, 0x8650}, /* RESET_REGISTER */ | ||
614 | {0x301A, 0x8658}, /* RESET_REGISTER */ | ||
615 | /* gain */ | ||
616 | {0x305e, 0x10AA}, /* gain */ | ||
617 | {0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */ | ||
618 | {0x301A, 0x065C}, /* RESET_REGISTER */ | ||
619 | {ar0832_TABLE_END, 0x0000} | ||
620 | }; | ||
621 | |||
622 | static struct ar0832_reg mode_1920X1080_8140[] = { | ||
623 | /* mode start */ | ||
624 | {0x301A, 0x0058}, /* RESET_REGISTER */ | ||
625 | {0x301A, 0x0050}, /* RESET_REGISTER */ | ||
626 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
627 | {0x3064, 0x7800}, /* RESERVED_MFR_3064 */ | ||
628 | {0x31AE, 0x0202}, /* SERIAL_FORMAT */ | ||
629 | /* MT9E013 Recommended Settings */ | ||
630 | {0x31B0, 0x0083}, /* FRAME_PREAMBLE */ | ||
631 | {0x31B2, 0x004D}, /* LINE_PREAMBLE */ | ||
632 | {0x31B4, 0x0E77}, /* MIPI_TIMING_0 */ | ||
633 | {0x31B6, 0x0D20}, /* MIPI_TIMING_1 */ | ||
634 | {0x31B8, 0x020E}, /* MIPI_TIMING_2 */ | ||
635 | {0x31BA, 0x0710}, /* MIPI_TIMING_3 */ | ||
636 | {0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */ | ||
637 | {ar0832_TABLE_WAIT_MS, 0x0005}, | ||
638 | {0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */ | ||
639 | {0x3044, 0x0590}, | ||
640 | {0x306E, 0xFC80}, | ||
641 | {0x30B2, 0xC000}, | ||
642 | {0x30D6, 0x0800}, | ||
643 | {0x316C, 0xB42F}, | ||
644 | {0x316E, 0x869A}, | ||
645 | {0x3170, 0x210E}, | ||
646 | {0x317A, 0x010E}, | ||
647 | {0x31E0, 0x1FB9}, | ||
648 | {0x31E6, 0x07FC}, | ||
649 | {0x37C0, 0x0000}, | ||
650 | {0x37C2, 0x0000}, | ||
651 | {0x37C4, 0x0000}, | ||
652 | {0x37C6, 0x0000}, | ||
653 | {0x3E00, 0x0011}, | ||
654 | {0x3E02, 0x8801}, | ||
655 | {0x3E04, 0x2801}, | ||
656 | {0x3E06, 0x8449}, | ||
657 | {0x3E08, 0x6841}, | ||
658 | {0x3E0A, 0x400C}, | ||
659 | {0x3E0C, 0x1001}, | ||
660 | {0x3E0E, 0x2603}, | ||
661 | {0x3E10, 0x4B41}, | ||
662 | {0x3E12, 0x4B24}, | ||
663 | {0x3E14, 0xA3CF}, | ||
664 | {0x3E16, 0x8802}, | ||
665 | {0x3E18, 0x8401}, | ||
666 | {0x3E1A, 0x8601}, | ||
667 | {0x3E1C, 0x8401}, | ||
668 | {0x3E1E, 0x840A}, | ||
669 | {0x3E20, 0xFF00}, | ||
670 | {0x3E22, 0x8401}, | ||
671 | {0x3E24, 0x00FF}, | ||
672 | {0x3E26, 0x0088}, | ||
673 | {0x3E28, 0x2E8A}, | ||
674 | {0x3E30, 0x0000}, | ||
675 | {0x3E32, 0x8801}, | ||
676 | {0x3E34, 0x4029}, | ||
677 | {0x3E36, 0x00FF}, | ||
678 | {0x3E38, 0x8469}, | ||
679 | {0x3E3A, 0x00FF}, | ||
680 | {0x3E3C, 0x2801}, | ||
681 | {0x3E3E, 0x3E2A}, | ||
682 | {0x3E40, 0x1C01}, | ||
683 | {0x3E42, 0xFF84}, | ||
684 | {0x3E44, 0x8401}, | ||
685 | {0x3E46, 0x0C01}, | ||
686 | {0x3E48, 0x8401}, | ||
687 | {0x3E4A, 0x00FF}, | ||
688 | {0x3E4C, 0x8402}, | ||
689 | {0x3E4E, 0x8984}, | ||
690 | {0x3E50, 0x6628}, | ||
691 | {0x3E52, 0x8340}, | ||
692 | {0x3E54, 0x00FF}, | ||
693 | {0x3E56, 0x4A42}, | ||
694 | {0x3E58, 0x2703}, | ||
695 | {0x3E5A, 0x6752}, | ||
696 | {0x3E5C, 0x3F2A}, | ||
697 | {0x3E5E, 0x846A}, | ||
698 | {0x3E60, 0x4C01}, | ||
699 | {0x3E62, 0x8401}, | ||
700 | {0x3E66, 0x3901}, | ||
701 | {0x3E90, 0x2C01}, | ||
702 | {0x3E98, 0x2B02}, | ||
703 | {0x3E92, 0x2A04}, | ||
704 | {0x3E94, 0x2509}, | ||
705 | {0x3E96, 0x0000}, | ||
706 | {0x3E9A, 0x2905}, | ||
707 | {0x3E9C, 0x00FF}, | ||
708 | {0x3ECC, 0x00EB}, | ||
709 | {0x3ED0, 0x1E24}, | ||
710 | {0x3ED4, 0xAFC4}, | ||
711 | {0x3ED6, 0x909B}, | ||
712 | {0x3EE0, 0x2424}, | ||
713 | {0x3EE2, 0x9797}, | ||
714 | {0x3EE4, 0xC100}, | ||
715 | {0x3EE6, 0x0540}, | ||
716 | {0x3174, 0x8000}, | ||
717 | |||
718 | /* mode end */ | ||
719 | {0x0300, 0x0004}, /* VT_PIX_CLK_DIV */ | ||
720 | {0x0302, 0x0001}, /* VT_SYS_CLK_DIV */ | ||
721 | {0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */ | ||
722 | {0x0306, 0x0040}, /* PLL_MULTIPLIER */ | ||
723 | {0x0308, 0x000A}, /* OP_PIX_CLK_DIV */ | ||
724 | {0x030A, 0x0001}, /* OP_SYS_CLK_DIV */ | ||
725 | {ar0832_TABLE_WAIT_MS, 0x0001}, | ||
726 | {0x3064, 0x7400}, /* RESERVED_MFR_3064 */ | ||
727 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
728 | {0x0344, 0x028C}, /* X_ADDR_START */ | ||
729 | {0x0348, 0x0A0B}, /* X_ADDR_END */ | ||
730 | {0x0346, 0x006E}, /* Y_ADDR_START */ | ||
731 | {0x034A, 0x04A5}, /* Y_ADDR_END */ | ||
732 | {0x034C, 0x0780}, /* X_OUTPUT_SIZE */ | ||
733 | {0x034E, 0x0438}, /* Y_OUTPUT_SIZE */ | ||
734 | {0x3040, 0xC041}, /* READ_MODE */ | ||
735 | {0x306E, 0xFC80}, /* DATAPATH_SELECT */ | ||
736 | {0x0400, 0x0000}, /* SCALING_MODE */ | ||
737 | {0x0404, 0x0010}, /* SCALE_M */ | ||
738 | {0x3178, 0x0000}, /* RESERVED_MFR_3178 */ | ||
739 | {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ | ||
740 | |||
741 | {0x0342, 0x103B}, /* LINE_LENGTH_PCK */ | ||
742 | {0x0340, 0x05C4}, /* FRAME_LENGTH_LINES */ | ||
743 | {0x0202, 0x05C4}, /* COARSE_INTEGRATION_TIME */ | ||
744 | {0x3014, 0x0702}, /* FINE_INTEGRATION_TIME */ | ||
745 | {0x3010, 0x0078}, /* FINE_CORRECTION */ | ||
746 | {0x301A, 0x8250}, /* RESET_REGISTER */ | ||
747 | {0x301A, 0x8650}, /* RESET_REGISTER */ | ||
748 | {0x301A, 0x8658}, /* RESET_REGISTER */ | ||
749 | /* gain */ | ||
750 | {0x305e, 0x10AA}, /* gain */ | ||
751 | {0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */ | ||
752 | {0x301A, 0x065C}, /* RESET_REGISTER */ | ||
753 | {ar0832_TABLE_END, 0x0000} | ||
754 | }; | ||
755 | |||
756 | static struct ar0832_reg mode_1920X1080_8141[] = { | ||
757 | /* mode start */ | ||
758 | {0x301A, 0x0058}, /* RESET_REGISTER */ | ||
759 | {0x301A, 0x0050}, /* RESET_REGISTER */ | ||
760 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
761 | {0x3064, 0x7800}, /* RESERVED_MFR_3064 */ | ||
762 | {0x31AE, 0x0202}, /* SERIAL_FORMAT */ | ||
763 | /* AR0832 Recommended Settings */ | ||
764 | {0x3044, 0x0590}, | ||
765 | {0x306E, 0xFC80}, | ||
766 | {0x30B2, 0xC000}, | ||
767 | {0x30D6, 0x0800}, | ||
768 | {0x316C, 0xB42F}, | ||
769 | {0x316E, 0x869A}, | ||
770 | {0x3170, 0x210E}, | ||
771 | {0x317A, 0x010E}, | ||
772 | {0x31E0, 0x1FB9}, | ||
773 | {0x31E6, 0x07FC}, | ||
774 | {0x37C0, 0x0000}, | ||
775 | {0x37C2, 0x0000}, | ||
776 | {0x37C4, 0x0000}, | ||
777 | {0x37C6, 0x0000}, | ||
778 | {0x3E00, 0x0011}, | ||
779 | {0x3E02, 0x8801}, | ||
780 | {0x3E04, 0x2801}, | ||
781 | {0x3E06, 0x8449}, | ||
782 | {0x3E08, 0x6841}, | ||
783 | {0x3E0A, 0x400C}, | ||
784 | {0x3E0C, 0x1001}, | ||
785 | {0x3E0E, 0x2603}, | ||
786 | {0x3E10, 0x4B41}, | ||
787 | {0x3E12, 0x4B24}, | ||
788 | {0x3E14, 0xA3CF}, | ||
789 | {0x3E16, 0x8802}, | ||
790 | {0x3E18, 0x8401}, | ||
791 | {0x3E1A, 0x8601}, | ||
792 | {0x3E1C, 0x8401}, | ||
793 | {0x3E1E, 0x840A}, | ||
794 | {0x3E20, 0xFF00}, | ||
795 | {0x3E22, 0x8401}, | ||
796 | {0x3E24, 0x00FF}, | ||
797 | {0x3E26, 0x0088}, | ||
798 | {0x3E28, 0x2E8A}, | ||
799 | {0x3E30, 0x0000}, | ||
800 | {0x3E32, 0x8801}, | ||
801 | {0x3E34, 0x4029}, | ||
802 | {0x3E36, 0x00FF}, | ||
803 | {0x3E38, 0x8469}, | ||
804 | {0x3E3A, 0x00FF}, | ||
805 | {0x3E3C, 0x2801}, | ||
806 | {0x3E3E, 0x3E2A}, | ||
807 | {0x3E40, 0x1C01}, | ||
808 | {0x3E42, 0xFF84}, | ||
809 | {0x3E44, 0x8401}, | ||
810 | {0x3E46, 0x0C01}, | ||
811 | {0x3E48, 0x8401}, | ||
812 | {0x3E4A, 0x00FF}, | ||
813 | {0x3E4C, 0x8402}, | ||
814 | {0x3E4E, 0x8984}, | ||
815 | {0x3E50, 0x6628}, | ||
816 | {0x3E52, 0x8340}, | ||
817 | {0x3E54, 0x00FF}, | ||
818 | {0x3E56, 0x4A42}, | ||
819 | {0x3E58, 0x2703}, | ||
820 | {0x3E5A, 0x6752}, | ||
821 | {0x3E5C, 0x3F2A}, | ||
822 | {0x3E5E, 0x846A}, | ||
823 | {0x3E60, 0x4C01}, | ||
824 | {0x3E62, 0x8401}, | ||
825 | {0x3E66, 0x3901}, | ||
826 | {0x3E90, 0x2C01}, | ||
827 | {0x3E98, 0x2B02}, | ||
828 | {0x3E92, 0x2A04}, | ||
829 | {0x3E94, 0x2509}, | ||
830 | {0x3E96, 0x0000}, | ||
831 | {0x3E9A, 0x2905}, | ||
832 | {0x3E9C, 0x00FF}, | ||
833 | {0x3ECC, 0x00EB}, | ||
834 | {0x3ED0, 0x1E24}, | ||
835 | {0x3ED4, 0xAFC4}, | ||
836 | {0x3ED6, 0x909B}, | ||
837 | {0x3EE0, 0x2424}, | ||
838 | {0x3EE2, 0x9797}, | ||
839 | {0x3EE4, 0xC100}, | ||
840 | {0x3EE6, 0x0540}, | ||
841 | {0x3174, 0x8000}, | ||
842 | |||
843 | /* mode end */ | ||
844 | {0x0300, 0x0004}, /* VT_PIX_CLK_DIV */ | ||
845 | {0x0302, 0x0001}, /* VT_SYS_CLK_DIV */ | ||
846 | {0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */ | ||
847 | {0x0306, 0x0040}, /* PLL_MULTIPLIER */ | ||
848 | {0x0308, 0x000A}, /* OP_PIX_CLK_DIV */ | ||
849 | {0x030A, 0x0001}, /* OP_SYS_CLK_DIV */ | ||
850 | {ar0832_TABLE_WAIT_MS, 0x0001}, | ||
851 | {0x3064, 0x7400}, /* RESERVED_MFR_3064 */ | ||
852 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
853 | {0x0344, 0x028C}, /* X_ADDR_START */ | ||
854 | {0x0348, 0x0A0B}, /* X_ADDR_END */ | ||
855 | {0x0346, 0x006E}, /* Y_ADDR_START */ | ||
856 | {0x034A, 0x04A5}, /* Y_ADDR_END */ | ||
857 | {0x034C, 0x0780}, /* X_OUTPUT_SIZE */ | ||
858 | {0x034E, 0x0438}, /* Y_OUTPUT_SIZE */ | ||
859 | {0x3040, 0xC041}, /* READ_MODE */ | ||
860 | {0x306E, 0xFC80}, /* DATAPATH_SELECT */ | ||
861 | {0x0400, 0x0000}, /* SCALING_MODE */ | ||
862 | {0x0404, 0x0010}, /* SCALE_M */ | ||
863 | {0x3178, 0x0000}, /* RESERVED_MFR_3178 */ | ||
864 | {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ | ||
865 | |||
866 | {0x0342, 0x103B}, /* LINE_LENGTH_PCK */ | ||
867 | {0x0340, 0x05C4}, /* FRAME_LENGTH_LINES */ | ||
868 | {0x0202, 0x05C4}, /* COARSE_INTEGRATION_TIME */ | ||
869 | {0x3014, 0x0702}, /* FINE_INTEGRATION_TIME */ | ||
870 | {0x3010, 0x0078}, /* FINE_CORRECTION */ | ||
871 | {0x301A, 0x8250}, /* RESET_REGISTER */ | ||
872 | {0x301A, 0x8650}, /* RESET_REGISTER */ | ||
873 | {0x301A, 0x8658}, /* RESET_REGISTER */ | ||
874 | /* gain */ | ||
875 | {0x305e, 0x10AA}, /* gain */ | ||
876 | {0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */ | ||
877 | {0x301A, 0x065C}, /* RESET_REGISTER */ | ||
878 | {ar0832_TABLE_END, 0x0000} | ||
879 | }; | ||
880 | |||
881 | static struct ar0832_reg mode_1632X1224_8140[] = { | ||
882 | /* mode start */ | ||
883 | {0x301A, 0x0058}, /* RESET_REGISTER */ | ||
884 | {0x301A, 0x0050}, /* RESET_REGISTER */ | ||
885 | |||
886 | /* SC-CHANGE: to-do 8 bit write */ | ||
887 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
888 | |||
889 | {0x3064, 0x7800}, /* RESERVED_MFR_3064 */ | ||
890 | {0x31AE, 0x0202}, /* SERIAL_FORMAT */ | ||
891 | /* MT9E013 Recommended Settings */ | ||
892 | {0x31B0, 0x0083}, /* FRAME_PREAMBLE */ | ||
893 | {0x31B2, 0x004D}, /* LINE_PREAMBLE */ | ||
894 | {0x31B4, 0x0E77}, /* MIPI_TIMING_0 */ | ||
895 | {0x31B6, 0x0D20}, /* MIPI_TIMING_1 */ | ||
896 | {0x31B8, 0x020E}, /* MIPI_TIMING_2 */ | ||
897 | {0x31BA, 0x0710}, /* MIPI_TIMING_3 */ | ||
898 | {0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */ | ||
899 | {ar0832_TABLE_WAIT_MS, 0x0005}, | ||
900 | {0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */ | ||
901 | {0x3044, 0x0590}, | ||
902 | {0x306E, 0xFC80}, | ||
903 | {0x30B2, 0xC000}, | ||
904 | {0x30D6, 0x0800}, | ||
905 | {0x316C, 0xB42F}, | ||
906 | {0x316E, 0x869A}, | ||
907 | {0x3170, 0x210E}, | ||
908 | {0x317A, 0x010E}, | ||
909 | {0x31E0, 0x1FB9}, | ||
910 | {0x31E6, 0x07FC}, | ||
911 | {0x37C0, 0x0000}, | ||
912 | {0x37C2, 0x0000}, | ||
913 | {0x37C4, 0x0000}, | ||
914 | {0x37C6, 0x0000}, | ||
915 | {0x3E00, 0x0011}, | ||
916 | {0x3E02, 0x8801}, | ||
917 | {0x3E04, 0x2801}, | ||
918 | {0x3E06, 0x8449}, | ||
919 | {0x3E08, 0x6841}, | ||
920 | {0x3E0A, 0x400C}, | ||
921 | {0x3E0C, 0x1001}, | ||
922 | {0x3E0E, 0x2603}, | ||
923 | {0x3E10, 0x4B41}, | ||
924 | {0x3E12, 0x4B24}, | ||
925 | {0x3E14, 0xA3CF}, | ||
926 | {0x3E16, 0x8802}, | ||
927 | {0x3E18, 0x8401}, | ||
928 | {0x3E1A, 0x8601}, | ||
929 | {0x3E1C, 0x8401}, | ||
930 | {0x3E1E, 0x840A}, | ||
931 | {0x3E20, 0xFF00}, | ||
932 | {0x3E22, 0x8401}, | ||
933 | {0x3E24, 0x00FF}, | ||
934 | {0x3E26, 0x0088}, | ||
935 | {0x3E28, 0x2E8A}, | ||
936 | {0x3E30, 0x0000}, | ||
937 | {0x3E32, 0x8801}, | ||
938 | {0x3E34, 0x4029}, | ||
939 | {0x3E36, 0x00FF}, | ||
940 | {0x3E38, 0x8469}, | ||
941 | {0x3E3A, 0x00FF}, | ||
942 | {0x3E3C, 0x2801}, | ||
943 | {0x3E3E, 0x3E2A}, | ||
944 | {0x3E40, 0x1C01}, | ||
945 | {0x3E42, 0xFF84}, | ||
946 | {0x3E44, 0x8401}, | ||
947 | {0x3E46, 0x0C01}, | ||
948 | {0x3E48, 0x8401}, | ||
949 | {0x3E4A, 0x00FF}, | ||
950 | {0x3E4C, 0x8402}, | ||
951 | {0x3E4E, 0x8984}, | ||
952 | {0x3E50, 0x6628}, | ||
953 | {0x3E52, 0x8340}, | ||
954 | {0x3E54, 0x00FF}, | ||
955 | {0x3E56, 0x4A42}, | ||
956 | {0x3E58, 0x2703}, | ||
957 | {0x3E5A, 0x6752}, | ||
958 | {0x3E5C, 0x3F2A}, | ||
959 | {0x3E5E, 0x846A}, | ||
960 | {0x3E60, 0x4C01}, | ||
961 | {0x3E62, 0x8401}, | ||
962 | {0x3E66, 0x3901}, | ||
963 | {0x3E90, 0x2C01}, | ||
964 | {0x3E98, 0x2B02}, | ||
965 | {0x3E92, 0x2A04}, | ||
966 | {0x3E94, 0x2509}, | ||
967 | {0x3E96, 0x0000}, | ||
968 | {0x3E9A, 0x2905}, | ||
969 | {0x3E9C, 0x00FF}, | ||
970 | {0x3ECC, 0x00EB}, | ||
971 | {0x3ED0, 0x1E24}, | ||
972 | {0x3ED4, 0xAFC4}, | ||
973 | {0x3ED6, 0x909B}, | ||
974 | {0x3EE0, 0x2424}, | ||
975 | {0x3EE2, 0x9797}, | ||
976 | {0x3EE4, 0xC100}, | ||
977 | {0x3EE6, 0x0540}, | ||
978 | {0x3174, 0x8000}, | ||
979 | |||
980 | /* mode end */ | ||
981 | {0x0300, 0x0004}, /* VT_PIX_CLK_DIV */ | ||
982 | {0x0302, 0x0001}, /* VT_SYS_CLK_DIV */ | ||
983 | {0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */ | ||
984 | |||
985 | {0x0306, 0x0040}, /* PLL_MULTIPLIER */ | ||
986 | |||
987 | {0x0308, 0x000A}, /* OP_PIX_CLK_DIV */ | ||
988 | {0x030A, 0x0001}, /* OP_SYS_CLK_DIV */ | ||
989 | {ar0832_TABLE_WAIT_MS, 0x0001}, /* waitmsec 1 */ | ||
990 | |||
991 | {0x3064, 0x7400}, /* RESERVED_MFR_3064 */ | ||
992 | |||
993 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
994 | |||
995 | {0x0344, 0x0008}, /* X_ADDR_START */ | ||
996 | {0x0348, 0x0CC9}, /* X_ADDR_END */ | ||
997 | {0x0346, 0x0008}, /* Y_ADDR_START */ | ||
998 | {0x034A, 0x0999}, /* Y_ADDR_END */ | ||
999 | {0x034C, 0x0660}, /* X_OUTPUT_SIZE */ | ||
1000 | {0x034E, 0x04C8}, /* Y_OUTPUT_SIZE */ | ||
1001 | {0x3040, 0xC4C3}, /* READ_MODE */ | ||
1002 | {0x306E, 0xFC80}, /* DATAPATH_SELECT */ | ||
1003 | {0x3178, 0x0000}, /* RESERVED_MFR_3178 */ | ||
1004 | {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ | ||
1005 | {0x0400, 0x0002}, /* SCALING_MODE */ | ||
1006 | {0x0404, 0x0010}, /* SCALE_M */ | ||
1007 | {0x0342, 0x101A}, /* LINE_LENGTH_PCK */ | ||
1008 | {0x0340, 0x0610}, /* FRAME_LENGTH_LINES */ | ||
1009 | {0x0202, 0x0557}, /* COARSE_INTEGRATION_TIME */ | ||
1010 | {0x3014, 0x0988}, /* FINE_INTEGRATION_TIME */ | ||
1011 | {0x3010, 0x0130}, /* FINE_CORRECTION */ | ||
1012 | {0x301A, 0x8250}, /* RESET_REGISTER */ | ||
1013 | {0x301A, 0x8650}, /* RESET_REGISTER */ | ||
1014 | {0x301A, 0x8658}, /* RESET_REGISTER */ | ||
1015 | |||
1016 | /* gain */ | ||
1017 | {0x305e, 0x10AA}, /* gain */ | ||
1018 | |||
1019 | /* todo 8-bit write */ | ||
1020 | {0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */ | ||
1021 | {0x301A, 0x065C}, /* RESET_REGISTER */ | ||
1022 | {ar0832_TABLE_END, 0x0000} | ||
1023 | }; | ||
1024 | |||
1025 | static struct ar0832_reg mode_1632X1224_8141[] = { | ||
1026 | /* mode start */ | ||
1027 | {0x301A, 0x0058}, /* RESET_REGISTER */ | ||
1028 | {0x301A, 0x0050}, /* RESET_REGISTER */ | ||
1029 | |||
1030 | /* SC-CHANGE: to-do 8 bit write */ | ||
1031 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
1032 | |||
1033 | {0x3064, 0x7800}, /* RESERVED_MFR_3064 */ | ||
1034 | {0x31AE, 0x0202}, /* SERIAL_FORMAT */ | ||
1035 | /* AR0832 Recommended Settings */ | ||
1036 | {0x31B0, 0x0083}, /* FRAME_PREAMBLE */ | ||
1037 | {0x31B2, 0x004D}, /* LINE_PREAMBLE */ | ||
1038 | {0x31B4, 0x0E77}, /* MIPI_TIMING_0 */ | ||
1039 | {0x31B6, 0x0D20}, /* MIPI_TIMING_1 */ | ||
1040 | {0x31B8, 0x020E}, /* MIPI_TIMING_2 */ | ||
1041 | {0x31BA, 0x0710}, /* MIPI_TIMING_3 */ | ||
1042 | {0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */ | ||
1043 | {ar0832_TABLE_WAIT_MS, 0x0005}, | ||
1044 | {0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */ | ||
1045 | {0x3044, 0x0590}, | ||
1046 | {0x306E, 0xFC80}, | ||
1047 | {0x30B2, 0xC000}, | ||
1048 | {0x30D6, 0x0800}, | ||
1049 | {0x316C, 0xB42F}, | ||
1050 | {0x316E, 0x869A}, | ||
1051 | {0x3170, 0x210E}, | ||
1052 | {0x317A, 0x010E}, | ||
1053 | {0x31E0, 0x1FB9}, | ||
1054 | {0x31E6, 0x07FC}, | ||
1055 | {0x37C0, 0x0000}, | ||
1056 | {0x37C2, 0x0000}, | ||
1057 | {0x37C4, 0x0000}, | ||
1058 | {0x37C6, 0x0000}, | ||
1059 | {0x3E00, 0x0011}, | ||
1060 | {0x3E02, 0x8801}, | ||
1061 | {0x3E04, 0x2801}, | ||
1062 | {0x3E06, 0x8449}, | ||
1063 | {0x3E08, 0x6841}, | ||
1064 | {0x3E0A, 0x400C}, | ||
1065 | {0x3E0C, 0x1001}, | ||
1066 | {0x3E0E, 0x2603}, | ||
1067 | {0x3E10, 0x4B41}, | ||
1068 | {0x3E12, 0x4B24}, | ||
1069 | {0x3E14, 0xA3CF}, | ||
1070 | {0x3E16, 0x8802}, | ||
1071 | {0x3E18, 0x8401}, | ||
1072 | {0x3E1A, 0x8601}, | ||
1073 | {0x3E1C, 0x8401}, | ||
1074 | {0x3E1E, 0x840A}, | ||
1075 | {0x3E20, 0xFF00}, | ||
1076 | {0x3E22, 0x8401}, | ||
1077 | {0x3E24, 0x00FF}, | ||
1078 | {0x3E26, 0x0088}, | ||
1079 | {0x3E28, 0x2E8A}, | ||
1080 | {0x3E30, 0x0000}, | ||
1081 | {0x3E32, 0x8801}, | ||
1082 | {0x3E34, 0x4029}, | ||
1083 | {0x3E36, 0x00FF}, | ||
1084 | {0x3E38, 0x8469}, | ||
1085 | {0x3E3A, 0x00FF}, | ||
1086 | {0x3E3C, 0x2801}, | ||
1087 | {0x3E3E, 0x3E2A}, | ||
1088 | {0x3E40, 0x1C01}, | ||
1089 | {0x3E42, 0xFF84}, | ||
1090 | {0x3E44, 0x8401}, | ||
1091 | {0x3E46, 0x0C01}, | ||
1092 | {0x3E48, 0x8401}, | ||
1093 | {0x3E4A, 0x00FF}, | ||
1094 | {0x3E4C, 0x8402}, | ||
1095 | {0x3E4E, 0x8984}, | ||
1096 | {0x3E50, 0x6628}, | ||
1097 | {0x3E52, 0x8340}, | ||
1098 | {0x3E54, 0x00FF}, | ||
1099 | {0x3E56, 0x4A42}, | ||
1100 | {0x3E58, 0x2703}, | ||
1101 | {0x3E5A, 0x6752}, | ||
1102 | {0x3E5C, 0x3F2A}, | ||
1103 | {0x3E5E, 0x846A}, | ||
1104 | {0x3E60, 0x4C01}, | ||
1105 | {0x3E62, 0x8401}, | ||
1106 | {0x3E66, 0x3901}, | ||
1107 | {0x3E90, 0x2C01}, | ||
1108 | {0x3E98, 0x2B02}, | ||
1109 | {0x3E92, 0x2A04}, | ||
1110 | {0x3E94, 0x2509}, | ||
1111 | {0x3E96, 0x0000}, | ||
1112 | {0x3E9A, 0x2905}, | ||
1113 | {0x3E9C, 0x00FF}, | ||
1114 | {0x3ECC, 0x00EB}, | ||
1115 | {0x3ED0, 0x1E24}, | ||
1116 | {0x3ED4, 0xAFC4}, | ||
1117 | {0x3ED6, 0x909B}, | ||
1118 | {0x3EE0, 0x2424}, | ||
1119 | {0x3EE2, 0x9797}, | ||
1120 | {0x3EE4, 0xC100}, | ||
1121 | {0x3EE6, 0x0540}, | ||
1122 | {0x3174, 0x8000}, | ||
1123 | |||
1124 | /* mode end */ | ||
1125 | {0x0300, 0x0004}, /* VT_PIX_CLK_DIV */ | ||
1126 | {0x0302, 0x0001}, /* VT_SYS_CLK_DIV */ | ||
1127 | {0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */ | ||
1128 | |||
1129 | {0x0306, 0x0040}, /* PLL_MULTIPLIER */ | ||
1130 | |||
1131 | {0x0308, 0x000A}, /* OP_PIX_CLK_DIV */ | ||
1132 | {0x030A, 0x0001}, /* OP_SYS_CLK_DIV */ | ||
1133 | {ar0832_TABLE_WAIT_MS, 0x0001}, /* waitmsec 1 */ | ||
1134 | |||
1135 | {0x3064, 0x7400}, /* RESERVED_MFR_3064 */ | ||
1136 | |||
1137 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
1138 | |||
1139 | {0x0344, 0x0008}, /* X_ADDR_START */ | ||
1140 | {0x0348, 0x0CC9}, /* X_ADDR_END */ | ||
1141 | {0x0346, 0x0008}, /* Y_ADDR_START */ | ||
1142 | {0x034A, 0x0999}, /* Y_ADDR_END */ | ||
1143 | {0x034C, 0x0660}, /* X_OUTPUT_SIZE */ | ||
1144 | {0x034E, 0x04C8}, /* Y_OUTPUT_SIZE */ | ||
1145 | {0x3040, 0xC4C3}, /* READ_MODE */ | ||
1146 | {0x306E, 0xFC80}, /* DATAPATH_SELECT */ | ||
1147 | {0x3178, 0x0000}, /* RESERVED_MFR_3178 */ | ||
1148 | {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ | ||
1149 | {0x0400, 0x0002}, /* SCALING_MODE */ | ||
1150 | {0x0404, 0x0010}, /* SCALE_M */ | ||
1151 | {0x0342, 0x101A}, /* LINE_LENGTH_PCK */ | ||
1152 | {0x0340, 0x0610}, /* FRAME_LENGTH_LINES */ | ||
1153 | {0x0202, 0x0557}, /* COARSE_INTEGRATION_TIME */ | ||
1154 | {0x3014, 0x0988}, /* FINE_INTEGRATION_TIME */ | ||
1155 | {0x3010, 0x0130}, /* FINE_CORRECTION */ | ||
1156 | {0x301A, 0x8250}, /* RESET_REGISTER */ | ||
1157 | {0x301A, 0x8650}, /* RESET_REGISTER */ | ||
1158 | {0x301A, 0x8658}, /* RESET_REGISTER */ | ||
1159 | |||
1160 | /* gain */ | ||
1161 | {0x305e, 0x10AA}, /* gain */ | ||
1162 | |||
1163 | /* todo 8-bit write */ | ||
1164 | {0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */ | ||
1165 | {0x301A, 0x065C}, /* RESET_REGISTER */ | ||
1166 | {ar0832_TABLE_END, 0x0000} | ||
1167 | }; | ||
1168 | |||
1169 | static struct ar0832_reg mode_800X600_8140[] = { | ||
1170 | /* mode start */ | ||
1171 | {0x301A, 0x0058}, | ||
1172 | {0x301A, 0x0050}, | ||
1173 | {0x0104, 0x0100}, | ||
1174 | {0x3064, 0x7800}, | ||
1175 | {0x31AE, 0x0202}, | ||
1176 | {0x31B8, 0x0E3F}, | ||
1177 | {0x31BE, 0xC003}, | ||
1178 | {0x3070, 0x0000}, | ||
1179 | {ar0832_TABLE_WAIT_MS, 0x0005}, | ||
1180 | |||
1181 | /* MT9E013 Recommended Settings */ | ||
1182 | {0x3044, 0x0590}, | ||
1183 | {0x306E, 0xFC80}, | ||
1184 | {0x30B2, 0xC000}, | ||
1185 | {0x30D6, 0x0800}, | ||
1186 | {0x316C, 0xB42F}, | ||
1187 | {0x316E, 0x869A}, | ||
1188 | {0x3170, 0x210E}, | ||
1189 | {0x317A, 0x010E}, | ||
1190 | {0x31E0, 0x1FB9}, | ||
1191 | {0x31E6, 0x07FC}, | ||
1192 | {0x37C0, 0x0000}, | ||
1193 | {0x37C2, 0x0000}, | ||
1194 | {0x37C4, 0x0000}, | ||
1195 | {0x37C6, 0x0000}, | ||
1196 | {0x3E00, 0x0011}, | ||
1197 | {0x3E02, 0x8801}, | ||
1198 | {0x3E04, 0x2801}, | ||
1199 | {0x3E06, 0x8449}, | ||
1200 | {0x3E08, 0x6841}, | ||
1201 | {0x3E0A, 0x400C}, | ||
1202 | {0x3E0C, 0x1001}, | ||
1203 | {0x3E0E, 0x2603}, | ||
1204 | {0x3E10, 0x4B41}, | ||
1205 | {0x3E12, 0x4B24}, | ||
1206 | {0x3E14, 0xA3CF}, | ||
1207 | {0x3E16, 0x8802}, | ||
1208 | {0x3E18, 0x8401}, | ||
1209 | {0x3E1A, 0x8601}, | ||
1210 | {0x3E1C, 0x8401}, | ||
1211 | {0x3E1E, 0x840A}, | ||
1212 | {0x3E20, 0xFF00}, | ||
1213 | {0x3E22, 0x8401}, | ||
1214 | {0x3E24, 0x00FF}, | ||
1215 | {0x3E26, 0x0088}, | ||
1216 | {0x3E28, 0x2E8A}, | ||
1217 | {0x3E30, 0x0000}, | ||
1218 | {0x3E32, 0x8801}, | ||
1219 | {0x3E34, 0x4029}, | ||
1220 | {0x3E36, 0x00FF}, | ||
1221 | {0x3E38, 0x8469}, | ||
1222 | {0x3E3A, 0x00FF}, | ||
1223 | {0x3E3C, 0x2801}, | ||
1224 | {0x3E3E, 0x3E2A}, | ||
1225 | {0x3E40, 0x1C01}, | ||
1226 | {0x3E42, 0xFF84}, | ||
1227 | {0x3E44, 0x8401}, | ||
1228 | {0x3E46, 0x0C01}, | ||
1229 | {0x3E48, 0x8401}, | ||
1230 | {0x3E4A, 0x00FF}, | ||
1231 | {0x3E4C, 0x8402}, | ||
1232 | {0x3E4E, 0x8984}, | ||
1233 | {0x3E50, 0x6628}, | ||
1234 | {0x3E52, 0x8340}, | ||
1235 | {0x3E54, 0x00FF}, | ||
1236 | {0x3E56, 0x4A42}, | ||
1237 | {0x3E58, 0x2703}, | ||
1238 | {0x3E5A, 0x6752}, | ||
1239 | {0x3E5C, 0x3F2A}, | ||
1240 | {0x3E5E, 0x846A}, | ||
1241 | {0x3E60, 0x4C01}, | ||
1242 | {0x3E62, 0x8401}, | ||
1243 | {0x3E66, 0x3901}, | ||
1244 | {0x3E90, 0x2C01}, | ||
1245 | {0x3E98, 0x2B02}, | ||
1246 | {0x3E92, 0x2A04}, | ||
1247 | {0x3E94, 0x2509}, | ||
1248 | {0x3E96, 0x0000}, | ||
1249 | {0x3E9A, 0x2905}, | ||
1250 | {0x3E9C, 0x00FF}, | ||
1251 | {0x3ECC, 0x00EB}, | ||
1252 | {0x3ED0, 0x1E24}, | ||
1253 | {0x3ED4, 0xAFC4}, | ||
1254 | {0x3ED6, 0x909B}, | ||
1255 | {0x3EE0, 0x2424}, | ||
1256 | {0x3EE2, 0x9797}, | ||
1257 | {0x3EE4, 0xC100}, | ||
1258 | {0x3EE6, 0x0540}, | ||
1259 | |||
1260 | /* mode end */ | ||
1261 | {0x3174, 0x8000}, | ||
1262 | |||
1263 | /* [RAW10] */ | ||
1264 | {0x0112, 0x0A0A}, | ||
1265 | |||
1266 | /* PLL Configuration Ext=24MHz */ | ||
1267 | {0x0300, 0x0004}, | ||
1268 | {0x0302, 0x0001}, | ||
1269 | {0x0304, 0x0002}, | ||
1270 | {0x0306, 0x0042}, | ||
1271 | {0x0308, 0x000A}, | ||
1272 | {0x030A, 0x0001}, | ||
1273 | {ar0832_TABLE_WAIT_MS, 0x0001}, | ||
1274 | |||
1275 | /* Output size */ | ||
1276 | {0x0344, 0x04D8}, | ||
1277 | {0x0348, 0x07F7}, | ||
1278 | {0x0346, 0x03A4}, | ||
1279 | {0x034A, 0x05FB}, | ||
1280 | {0x034C, 0x0320}, | ||
1281 | {0x034E, 0x0258}, | ||
1282 | {0x3040, 0xC041}, | ||
1283 | |||
1284 | {0x306E, 0xFC80}, | ||
1285 | {0x3178, 0x0000}, | ||
1286 | {0x3ED0, 0x1E24}, | ||
1287 | |||
1288 | /* Scale Configuration */ | ||
1289 | {0x0400, 0x0000}, | ||
1290 | {0x0404, 0x0010}, | ||
1291 | |||
1292 | /* Timing Configuration */ | ||
1293 | {0x0342, 0x08A8}, | ||
1294 | {0x0340, 0x02E7}, | ||
1295 | {0x0202, 0x02E7}, | ||
1296 | {0x3014, 0x03F6}, | ||
1297 | {0x3010, 0x0078}, | ||
1298 | |||
1299 | {0x301A, 0x8250}, | ||
1300 | {0x301A, 0x8650}, | ||
1301 | {0x301A, 0x8658}, | ||
1302 | /* STATE= Minimum Gain, 1500 */ | ||
1303 | {0x305E, 0x13AF}, | ||
1304 | |||
1305 | {0x0104, 0x0000}, | ||
1306 | {0x301A, 0x065C}, | ||
1307 | {ar0832_TABLE_END, 0x0000} | ||
1308 | }; | ||
1309 | |||
1310 | static struct ar0832_reg mode_800X600_8141[] = { | ||
1311 | /* mode start */ | ||
1312 | {0x301A, 0x0058}, /* RESET_REGISTER */ | ||
1313 | {0x301A, 0x0050}, /* RESET_REGISTER */ | ||
1314 | |||
1315 | /* SC-CHANGE: to-do 8 bit write */ | ||
1316 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
1317 | |||
1318 | {0x3064, 0x7800}, /* RESERVED_MFR_3064 */ | ||
1319 | {0x31AE, 0x0202}, /* SERIAL_FORMAT */ | ||
1320 | /* AR0832 Recommended Settings */ | ||
1321 | {0x31B0, 0x0083}, /* FRAME_PREAMBLE */ | ||
1322 | {0x31B2, 0x004D}, /* LINE_PREAMBLE */ | ||
1323 | {0x31B4, 0x0E88}, /* MIPI_TIMING_0 */ | ||
1324 | {0x31B6, 0x0D24}, /* MIPI_TIMING_1 */ | ||
1325 | {0x31B8, 0x020E}, /* MIPI_TIMING_2 */ | ||
1326 | {0x31BA, 0x0710}, /* MIPI_TIMING_3 */ | ||
1327 | {0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */ | ||
1328 | {ar0832_TABLE_WAIT_MS, 0x0005}, | ||
1329 | {0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */ | ||
1330 | {0x3044, 0x0590}, /* RESERVED_MFR_3044 */ | ||
1331 | {0x306E, 0xFC80}, /* DATAPATH_SELECT */ | ||
1332 | {0x30B2, 0xC000}, /* RESERVED_MFR_30B2 */ | ||
1333 | {0x30D6, 0x0800}, /* RESERVED_MFR_30D6 */ | ||
1334 | {0x316C, 0xB42F}, /* RESERVED_MFR_316C */ | ||
1335 | {0x316E, 0x869A}, /* RESERVED_MFR_316E */ | ||
1336 | {0x3170, 0x210E}, /* RESERVED_MFR_3170 */ | ||
1337 | {0x317A, 0x010E}, /* RESERVED_MFR_317A */ | ||
1338 | {0x31E0, 0x1FB9}, /* RESERVED_MFR_31E0 */ | ||
1339 | {0x31E6, 0x07FC}, /* RESERVED_MFR_31E6 */ | ||
1340 | {0x37C0, 0x0000}, /* P_GR_Q5 */ | ||
1341 | {0x37C2, 0x0000}, /* P_RD_Q5 */ | ||
1342 | {0x37C4, 0x0000}, /* P_BL_Q5 */ | ||
1343 | {0x37C6, 0x0000}, /* P_GB_Q5 */ | ||
1344 | {0x3E00, 0x0011}, /* RESERVED_MFR_3E00 */ | ||
1345 | {0x3E02, 0x8801}, /* RESERVED_MFR_3E02 */ | ||
1346 | {0x3E04, 0x2801}, /* RESERVED_MFR_3E04 */ | ||
1347 | {0x3E06, 0x8449}, /* RESERVED_MFR_3E06 */ | ||
1348 | {0x3E08, 0x6841}, /* RESERVED_MFR_3E08 */ | ||
1349 | {0x3E0A, 0x400C}, /* RESERVED_MFR_3E0A */ | ||
1350 | {0x3E0C, 0x1001}, /* RESERVED_MFR_3E0C */ | ||
1351 | {0x3E0E, 0x2603}, /* RESERVED_MFR_3E0E */ | ||
1352 | {0x3E10, 0x4B41}, /* RESERVED_MFR_3E10 */ | ||
1353 | {0x3E12, 0x4B24}, /* RESERVED_MFR_3E12 */ | ||
1354 | {0x3E14, 0xA3CF}, /* RESERVED_MFR_3E14 */ | ||
1355 | {0x3E16, 0x8802}, /* RESERVED_MFR_3E16 */ | ||
1356 | {0x3E18, 0x84FF}, /* RESERVED_MFR_3E18 */ | ||
1357 | {0x3E1A, 0x8601}, /* RESERVED_MFR_3E1A */ | ||
1358 | {0x3E1C, 0x8401}, /* RESERVED_MFR_3E1C */ | ||
1359 | {0x3E1E, 0x840A}, /* RESERVED_MFR_3E1E */ | ||
1360 | {0x3E20, 0xFF00}, /* RESERVED_MFR_3E20 */ | ||
1361 | {0x3E22, 0x8401}, /* RESERVED_MFR_3E22 */ | ||
1362 | {0x3E24, 0x00FF}, /* RESERVED_MFR_3E24 */ | ||
1363 | {0x3E26, 0x0088}, /* RESERVED_MFR_3E26 */ | ||
1364 | {0x3E28, 0x2E8A}, /* RESERVED_MFR_3E28 */ | ||
1365 | {0x3E30, 0x0000}, /* RESERVED_MFR_3E30 */ | ||
1366 | {0x3E32, 0x8801}, /* RESERVED_MFR_3E32 */ | ||
1367 | {0x3E34, 0x4029}, /* RESERVED_MFR_3E34 */ | ||
1368 | {0x3E36, 0x00FF}, /* RESERVED_MFR_3E36 */ | ||
1369 | {0x3E38, 0x8469}, /* RESERVED_MFR_3E38 */ | ||
1370 | {0x3E3A, 0x00FF}, /* RESERVED_MFR_3E3A */ | ||
1371 | {0x3E3C, 0x2801}, /* RESERVED_MFR_3E3C */ | ||
1372 | {0x3E3E, 0x3E2A}, /* RESERVED_MFR_3E3E */ | ||
1373 | {0x3E40, 0x1C01}, /* RESERVED_MFR_3E40 */ | ||
1374 | {0x3E42, 0xFF84}, /* RESERVED_MFR_3E42 */ | ||
1375 | {0x3E44, 0x8401}, /* RESERVED_MFR_3E44 */ | ||
1376 | {0x3E46, 0x0C01}, /* RESERVED_MFR_3E46 */ | ||
1377 | {0x3E48, 0x8401}, /* RESERVED_MFR_3E48 */ | ||
1378 | {0x3E4A, 0x00FF}, /* RESERVED_MFR_3E4A */ | ||
1379 | {0x3E4C, 0x8402}, /* RESERVED_MFR_3E4C */ | ||
1380 | {0x3E4E, 0x8984}, /* RESERVED_MFR_3E4E */ | ||
1381 | {0x3E50, 0x6628}, /* RESERVED_MFR_3E50 */ | ||
1382 | {0x3E52, 0x8340}, /* RESERVED_MFR_3E52 */ | ||
1383 | {0x3E54, 0x00FF}, /* RESERVED_MFR_3E54 */ | ||
1384 | {0x3E56, 0x4A42}, /* RESERVED_MFR_3E56 */ | ||
1385 | {0x3E58, 0x2703}, /* RESERVED_MFR_3E58 */ | ||
1386 | {0x3E5A, 0x6752}, /* RESERVED_MFR_3E5A */ | ||
1387 | {0x3E5C, 0x3F2A}, /* RESERVED_MFR_3E5C */ | ||
1388 | {0x3E5E, 0x846A}, /* RESERVED_MFR_3E5E */ | ||
1389 | {0x3E60, 0x4C01}, /* RESERVED_MFR_3E60 */ | ||
1390 | {0x3E62, 0x8401}, /* RESERVED_MFR_3E62 */ | ||
1391 | {0x3E66, 0x3901}, /* RESERVED_MFR_3E66 */ | ||
1392 | {0x3E90, 0x2C01}, /* RESERVED_MFR_3E90 */ | ||
1393 | {0x3E98, 0x2B02}, /* RESERVED_MFR_3E98 */ | ||
1394 | {0x3E92, 0x2A04}, /* RESERVED_MFR_3E92 */ | ||
1395 | {0x3E94, 0x2509}, /* RESERVED_MFR_3E94 */ | ||
1396 | {0x3E96, 0x0000}, /* RESERVED_MFR_3E96 */ | ||
1397 | {0x3E9A, 0x2905}, /* RESERVED_MFR_3E9A */ | ||
1398 | {0x3E9C, 0x00FF}, /* RESERVED_MFR_3E9C */ | ||
1399 | {0x3ECC, 0x00EB}, /* RESERVED_MFR_3ECC */ | ||
1400 | {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ | ||
1401 | {0x3ED4, 0xAFC4}, /* RESERVED_MFR_3ED4 */ | ||
1402 | {0x3ED6, 0x909B}, /* RESERVED_MFR_3ED6 */ | ||
1403 | {0x3EE0, 0x2424}, /* RESERVED_MFR_3EE0 */ | ||
1404 | {0x3EE2, 0x9797}, /* RESERVED_MFR_3EE2 */ | ||
1405 | {0x3EE4, 0xC100}, /* RESERVED_MFR_3EE4 */ | ||
1406 | {0x3EE6, 0x0540}, /* RESERVED_MFR_3EE6 */ | ||
1407 | |||
1408 | /* mode end */ | ||
1409 | {0x3174, 0x8000}, /* RESERVED_MFR_3174 */ | ||
1410 | {0x0300, 0x0004}, /* VT_PIX_CLK_DIV */ | ||
1411 | {0x0302, 0x0001}, /* VT_SYS_CLK_DIV */ | ||
1412 | {0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */ | ||
1413 | |||
1414 | {0x0306, 0x0042}, /* PLL_MULTIPLIER */ | ||
1415 | |||
1416 | {0x0308, 0x000A}, /* OP_PIX_CLK_DIV */ | ||
1417 | {0x030A, 0x0001}, /* OP_SYS_CLK_DIV */ | ||
1418 | {ar0832_TABLE_WAIT_MS, 0x0001}, /* waitmsec 1 */ | ||
1419 | |||
1420 | {0x3064, 0x7400}, /* RESERVED_MFR_3064 */ | ||
1421 | |||
1422 | {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ | ||
1423 | |||
1424 | {0x0344, 0x04D8}, /* X_ADDR_START */ | ||
1425 | {0x0348, 0x07F7}, /* X_ADDR_END */ | ||
1426 | {0x0346, 0x03A4}, /* Y_ADDR_START */ | ||
1427 | {0x034A, 0x05FB}, /* Y_ADDR_END */ | ||
1428 | {0x034C, 0x0320}, /* X_OUTPUT_SIZE */ | ||
1429 | {0x034E, 0x0260}, /* Y_OUTPUT_SIZE */ | ||
1430 | {0x3040, 0xC041}, /* READ_MODE */ | ||
1431 | {0x306E, 0xFC80}, /* DATAPATH_SELECT */ | ||
1432 | {0x3178, 0x0000}, /* RESERVED_MFR_3178 */ | ||
1433 | {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ | ||
1434 | {0x0400, 0x0000}, /* SCALING_MODE */ | ||
1435 | {0x0404, 0x0010}, /* SCALE_M */ | ||
1436 | {0x0342, 0x08A8}, /* LINE_LENGTH_PCK */ | ||
1437 | {0x0340, 0x02E7}, /* FRAME_LENGTH_LINES */ | ||
1438 | {0x0202, 0x02E7}, /* COARSE_INTEGRATION_TIME */ | ||
1439 | {0x3014, 0x03F6}, /* FINE_INTEGRATION_TIME */ | ||
1440 | {0x3010, 0x0078}, /* FINE_CORRECTION */ | ||
1441 | {0x301A, 0x8250}, /* RESET_REGISTER */ | ||
1442 | {0x301A, 0x8650}, /* RESET_REGISTER */ | ||
1443 | {0x301A, 0x8658}, /* RESET_REGISTER */ | ||
1444 | |||
1445 | /* gain */ | ||
1446 | {0x305e, 0x10AA}, /* gain */ | ||
1447 | |||
1448 | /* todo 8-bit write */ | ||
1449 | {0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */ | ||
1450 | {0x301A, 0x065C}, /* RESET_REGISTER */ | ||
1451 | {ar0832_TABLE_END, 0x0000} | ||
1452 | }; | ||
1453 | |||
1454 | static struct ar0832_reg mode_end[] = { | ||
1455 | {ar0832_TABLE_END, 0x0000} | ||
1456 | }; | ||
1457 | |||
1458 | enum { | ||
1459 | ar0832_MODE_3264X2448, | ||
1460 | ar0832_MODE_2880X1620, | ||
1461 | ar0832_MODE_1920X1080, | ||
1462 | ar0832_MODE_1632X1224, | ||
1463 | ar0832_MODE_800X600 | ||
1464 | }; | ||
1465 | |||
1466 | static struct ar0832_reg *mode_table_8140[] = { | ||
1467 | [ar0832_MODE_3264X2448] = mode_3264X2448_8140, | ||
1468 | [ar0832_MODE_2880X1620] = mode_2880X1620_8140, | ||
1469 | [ar0832_MODE_1920X1080] = mode_1920X1080_8140, | ||
1470 | [ar0832_MODE_1632X1224] = mode_1632X1224_8140, | ||
1471 | [ar0832_MODE_800X600] = mode_800X600_8140, | ||
1472 | }; | ||
1473 | |||
1474 | static struct ar0832_reg *mode_table_8141[] = { | ||
1475 | [ar0832_MODE_3264X2448] = mode_3264X2448_8141, | ||
1476 | [ar0832_MODE_2880X1620] = mode_2880X1620_8141, | ||
1477 | [ar0832_MODE_1920X1080] = mode_1920X1080_8141, | ||
1478 | [ar0832_MODE_1632X1224] = mode_1632X1224_8141, | ||
1479 | [ar0832_MODE_800X600] = mode_800X600_8141, | ||
1480 | }; | ||
1481 | |||
1482 | static inline void ar0832_msleep(u32 t) | ||
1483 | { | ||
1484 | /* | ||
1485 | why usleep_range() instead of msleep() ? | ||
1486 | Read Documentation/timers/timers-howto.txt | ||
1487 | */ | ||
1488 | usleep_range(t*1000, t*1000 + 500); | ||
1489 | } | ||
1490 | |||
1491 | /* 16 bit reg to program frame length */ | ||
1492 | static inline void ar0832_get_frame_length_regs(struct ar0832_reg *regs, | ||
1493 | u32 frame_length) | ||
1494 | { | ||
1495 | regs->addr = 0x0340; | ||
1496 | regs->val = (frame_length) & 0xFFFF; | ||
1497 | } | ||
1498 | |||
1499 | static inline void ar0832_get_coarse_time_regs(struct ar0832_reg *regs, | ||
1500 | u32 coarse_time) | ||
1501 | { | ||
1502 | regs->addr = 0x0202; | ||
1503 | regs->val = (coarse_time) & 0xFFFF; | ||
1504 | } | ||
1505 | |||
1506 | static inline void ar0832_get_focuser_vcm_control_regs(struct ar0832_reg *regs, | ||
1507 | u16 value) | ||
1508 | { | ||
1509 | regs->addr = 0x30F0; | ||
1510 | regs->val = (value) & 0xFFFF; | ||
1511 | } | ||
1512 | |||
1513 | static inline void ar0832_get_focuser_vcm_step_time_regs | ||
1514 | (struct ar0832_reg *regs, u16 value) | ||
1515 | { | ||
1516 | regs->addr = 0x30F4; | ||
1517 | regs->val = (value) & 0xFFFF; | ||
1518 | } | ||
1519 | |||
1520 | static inline void ar0832_get_focuser_data_regs(struct ar0832_reg *regs, | ||
1521 | u16 value) | ||
1522 | { | ||
1523 | regs->addr = 0x30F2; | ||
1524 | regs->val = (value) & 0xFFFF; | ||
1525 | } | ||
1526 | |||
1527 | static inline void ar0832_get_gain_regs(struct ar0832_reg *regs, u16 gain) | ||
1528 | { | ||
1529 | /* global_gain register*/ | ||
1530 | regs->addr = AR0832_GLOBAL_GAIN_REG; | ||
1531 | regs->val = gain; | ||
1532 | } | ||
1533 | |||
1534 | static int ar0832_write_reg8(struct i2c_client *client, u16 addr, u8 val) | ||
1535 | { | ||
1536 | int err; | ||
1537 | struct i2c_msg msg; | ||
1538 | unsigned char data[3]; | ||
1539 | int retry = 0; | ||
1540 | |||
1541 | if (!client->adapter) | ||
1542 | return -ENODEV; | ||
1543 | |||
1544 | data[0] = (u8) (addr >> 8);; | ||
1545 | data[1] = (u8) (addr & 0xff); | ||
1546 | data[2] = (u8) (val & 0xff); | ||
1547 | |||
1548 | msg.addr = client->addr; | ||
1549 | msg.flags = 0; | ||
1550 | msg.len = 3; | ||
1551 | msg.buf = data; | ||
1552 | |||
1553 | dev_dbg(&client->dev, "0x%x = 0x%x\n", addr, val); | ||
1554 | |||
1555 | do { | ||
1556 | err = i2c_transfer(client->adapter, &msg, 1); | ||
1557 | if (err > 0) | ||
1558 | return 0; | ||
1559 | retry++; | ||
1560 | dev_err(&client->dev, | ||
1561 | "%s: i2c transfer failed, retrying %x %x\n", | ||
1562 | __func__, addr, val); | ||
1563 | ar0832_msleep(3); | ||
1564 | } while (retry < ar0832_MAX_RETRIES); | ||
1565 | |||
1566 | return err; | ||
1567 | } | ||
1568 | |||
1569 | static int ar0832_write_reg16(struct i2c_client *client, u16 addr, u16 val) | ||
1570 | { | ||
1571 | int count; | ||
1572 | struct i2c_msg msg; | ||
1573 | unsigned char data[4]; | ||
1574 | int retry = 0; | ||
1575 | |||
1576 | if (!client->adapter) | ||
1577 | return -ENODEV; | ||
1578 | |||
1579 | data[0] = (u8) (addr >> 8); | ||
1580 | data[1] = (u8) (addr & 0xff); | ||
1581 | data[2] = (u8) (val >> 8); | ||
1582 | data[3] = (u8) (val & 0xff); | ||
1583 | |||
1584 | msg.addr = client->addr; | ||
1585 | msg.flags = 0; | ||
1586 | msg.len = 4; | ||
1587 | msg.buf = data; | ||
1588 | |||
1589 | dev_dbg(&client->dev, "0x%x = 0x%x\n", addr, val); | ||
1590 | |||
1591 | do { | ||
1592 | count = i2c_transfer(client->adapter, &msg, 1); | ||
1593 | if (count == 1) | ||
1594 | return 0; | ||
1595 | retry++; | ||
1596 | dev_err(&client->dev, | ||
1597 | "%s: i2c transfer failed, retrying %x %x\n", | ||
1598 | __func__, addr, val); | ||
1599 | ar0832_msleep(3); | ||
1600 | } while (retry <= ar0832_MAX_RETRIES); | ||
1601 | |||
1602 | return -EIO; | ||
1603 | } | ||
1604 | |||
1605 | static int ar0832_read_reg16(struct i2c_client *client, u16 addr, u16 *val) | ||
1606 | { | ||
1607 | struct i2c_msg msg[2]; | ||
1608 | u8 data[4]; | ||
1609 | |||
1610 | msg[0].addr = client->addr; | ||
1611 | msg[0].flags = 0; | ||
1612 | msg[0].len = 2; | ||
1613 | msg[0].buf = data; | ||
1614 | data[0] = (addr >> 8); | ||
1615 | data[1] = (addr & 0xff); | ||
1616 | msg[1].addr = client->addr; | ||
1617 | msg[1].flags = I2C_M_RD; | ||
1618 | msg[1].len = 2; | ||
1619 | msg[1].buf = data + 2; | ||
1620 | |||
1621 | if (i2c_transfer(client->adapter, msg, 2) == 2) { | ||
1622 | *val = ((data[2] << 8) | data[3]); | ||
1623 | dev_dbg(&client->dev, "0x%x = 0x%x\n", addr, *val); | ||
1624 | return 0; | ||
1625 | } else { | ||
1626 | *val = 0; | ||
1627 | dev_err(&client->dev, | ||
1628 | "%s: i2c read failed.\n", __func__); | ||
1629 | return -1; | ||
1630 | } | ||
1631 | } | ||
1632 | |||
1633 | static int ar0832_write_reg_helper(struct ar0832_dev *dev, | ||
1634 | u16 addr, | ||
1635 | u16 val) | ||
1636 | { | ||
1637 | int ret; | ||
1638 | |||
1639 | if (addr == 0x104) | ||
1640 | ret = ar0832_write_reg8(dev->i2c_client, addr, | ||
1641 | (val >> 8 & 0xff)); | ||
1642 | else | ||
1643 | ret = ar0832_write_reg16(dev->i2c_client, addr, val); | ||
1644 | |||
1645 | return ret; | ||
1646 | } | ||
1647 | |||
1648 | static int ar0832_write_table(struct ar0832_dev *dev, | ||
1649 | const struct ar0832_reg table[], | ||
1650 | const struct ar0832_reg override_list[], | ||
1651 | int num_override_regs) | ||
1652 | { | ||
1653 | int err; | ||
1654 | const struct ar0832_reg *next; | ||
1655 | u16 val; | ||
1656 | int i; | ||
1657 | |||
1658 | for (next = table; next->addr != ar0832_TABLE_END; next++) { | ||
1659 | if (next->addr == ar0832_TABLE_WAIT_MS) { | ||
1660 | ar0832_msleep(next->val); | ||
1661 | continue; | ||
1662 | } | ||
1663 | |||
1664 | val = next->val; | ||
1665 | /* When an override list is passed in, replace the reg */ | ||
1666 | /* value to write if the reg is in the list */ | ||
1667 | if (override_list) { | ||
1668 | for (i = 0; i < num_override_regs; i++) { | ||
1669 | if (next->addr == override_list[i].addr) { | ||
1670 | val = override_list[i].val; | ||
1671 | break; | ||
1672 | } | ||
1673 | } | ||
1674 | } | ||
1675 | |||
1676 | err = ar0832_write_reg_helper(dev, next->addr, val); | ||
1677 | if (err) | ||
1678 | return err; | ||
1679 | } | ||
1680 | return 0; | ||
1681 | } | ||
1682 | |||
1683 | static int ar0832_set_frame_length(struct ar0832_dev *dev, | ||
1684 | u32 frame_length) | ||
1685 | { | ||
1686 | struct ar0832_reg reg_list; | ||
1687 | struct i2c_client *i2c_client = dev->i2c_client; | ||
1688 | int ret; | ||
1689 | |||
1690 | dev_dbg(&i2c_client->dev, "[%s] (0x%08x)\n", __func__, frame_length); | ||
1691 | |||
1692 | ar0832_get_frame_length_regs(®_list, frame_length); | ||
1693 | ret = ar0832_write_reg8(i2c_client, AR0832_GROUP_HOLD_REG, 0x1); | ||
1694 | if (ret) | ||
1695 | return ret; | ||
1696 | |||
1697 | ret = ar0832_write_reg16(i2c_client, reg_list.addr, | ||
1698 | reg_list.val); | ||
1699 | if (ret) | ||
1700 | return ret; | ||
1701 | |||
1702 | ret = ar0832_write_reg8(i2c_client, AR0832_GROUP_HOLD_REG, 0x0); | ||
1703 | if (ret) | ||
1704 | return ret; | ||
1705 | |||
1706 | return 0; | ||
1707 | } | ||
1708 | |||
1709 | static int ar0832_set_coarse_time(struct ar0832_dev *dev, | ||
1710 | u32 coarse_time) | ||
1711 | { | ||
1712 | int ret; | ||
1713 | struct ar0832_reg reg_list; | ||
1714 | struct i2c_client *i2c_client = dev->i2c_client; | ||
1715 | |||
1716 | dev_dbg(&i2c_client->dev, "[%s] (0x%08x)\n", __func__, coarse_time); | ||
1717 | ar0832_get_coarse_time_regs(®_list, coarse_time); | ||
1718 | |||
1719 | ret = ar0832_write_reg8(i2c_client, AR0832_GROUP_HOLD_REG, 0x1); | ||
1720 | if (ret) | ||
1721 | return ret; | ||
1722 | |||
1723 | ret = ar0832_write_reg16(i2c_client, reg_list.addr, | ||
1724 | reg_list.val); | ||
1725 | if (ret) | ||
1726 | return ret; | ||
1727 | |||
1728 | ret = ar0832_write_reg8(i2c_client, AR0832_GROUP_HOLD_REG, 0x0); | ||
1729 | if (ret) | ||
1730 | return ret; | ||
1731 | |||
1732 | return 0; | ||
1733 | } | ||
1734 | |||
1735 | static int ar0832_set_gain(struct ar0832_dev *dev, u16 gain) | ||
1736 | { | ||
1737 | int ret = 0; | ||
1738 | struct ar0832_reg reg_list_gain; | ||
1739 | |||
1740 | ret = ar0832_write_reg8(dev->i2c_client, AR0832_GROUP_HOLD_REG, 0x1); | ||
1741 | /* Gain Registers Start */ | ||
1742 | ar0832_get_gain_regs(®_list_gain, gain); | ||
1743 | ret |= ar0832_write_reg16(dev->i2c_client, | ||
1744 | reg_list_gain.addr, | ||
1745 | reg_list_gain.val); | ||
1746 | if (ret) | ||
1747 | return ret; | ||
1748 | |||
1749 | /* Gain register End */ | ||
1750 | ret |= ar0832_write_reg8(dev->i2c_client, AR0832_GROUP_HOLD_REG, 0x0); | ||
1751 | |||
1752 | return ret; | ||
1753 | } | ||
1754 | |||
1755 | static int ar0832_set_mode(struct ar0832_dev *dev, | ||
1756 | struct ar0832_mode *mode) | ||
1757 | { | ||
1758 | int sensor_mode; | ||
1759 | int err; | ||
1760 | struct i2c_client *i2c_client = dev->i2c_client; | ||
1761 | struct ar0832_reg reg_ovr[3]; | ||
1762 | struct ar0832_reg *mode_seq; | ||
1763 | |||
1764 | dev_dbg(&i2c_client->dev, "%s: ++\n", __func__); | ||
1765 | |||
1766 | if (mode->xres == 3264 && mode->yres == 2448) | ||
1767 | sensor_mode = ar0832_MODE_3264X2448; | ||
1768 | else if (mode->xres == 2880 && mode->yres == 1620) | ||
1769 | sensor_mode = ar0832_MODE_2880X1620; | ||
1770 | else if (mode->xres == 1920 && mode->yres == 1080) | ||
1771 | sensor_mode = ar0832_MODE_1920X1080; | ||
1772 | else if (mode->xres == 1632 && mode->yres == 1224) | ||
1773 | sensor_mode = ar0832_MODE_1632X1224; | ||
1774 | else if (mode->xres == 800 && mode->yres == 600) | ||
1775 | sensor_mode = ar0832_MODE_800X600; | ||
1776 | else { | ||
1777 | dev_err(&i2c_client->dev, | ||
1778 | "%s: invalid resolution supplied to set mode %d %d\n", | ||
1779 | __func__ , mode->xres, mode->yres); | ||
1780 | return -EINVAL; | ||
1781 | } | ||
1782 | |||
1783 | if (dev->sensor_id_data == AR0832_SENSOR_ID_8141) | ||
1784 | mode_seq = mode_table_8141[sensor_mode]; | ||
1785 | else | ||
1786 | mode_seq = mode_table_8140[sensor_mode]; | ||
1787 | /* get a list of override regs for the asking frame length, */ | ||
1788 | /* coarse integration time, and gain.*/ | ||
1789 | err = ar0832_write_table(dev, mode_start, NULL, 0); | ||
1790 | if (err) | ||
1791 | return err; | ||
1792 | |||
1793 | /* When we change the resolution */ | ||
1794 | ar0832_get_frame_length_regs(®_ovr[0], mode->frame_length); | ||
1795 | ar0832_get_coarse_time_regs(®_ovr[1], mode->coarse_time); | ||
1796 | ar0832_get_gain_regs(®_ovr[2], mode->gain); | ||
1797 | err = ar0832_write_table(dev, mode_seq, reg_ovr, ARRAY_SIZE(reg_ovr)); | ||
1798 | if (err) | ||
1799 | return err; | ||
1800 | |||
1801 | err = ar0832_write_table(dev, mode_end, NULL, 0); | ||
1802 | if (err) | ||
1803 | return err; | ||
1804 | |||
1805 | dev->sensor_info->mode = sensor_mode; | ||
1806 | dev_dbg(&i2c_client->dev, "%s: --\n", __func__); | ||
1807 | |||
1808 | return 0; | ||
1809 | } | ||
1810 | |||
1811 | static int ar0832_get_status(struct ar0832_dev *dev, u8 *status) | ||
1812 | { | ||
1813 | int err = 0; | ||
1814 | struct i2c_client *i2c_client = dev->i2c_client; | ||
1815 | |||
1816 | *status = 0; | ||
1817 | /* FixMe */ | ||
1818 | /* | ||
1819 | err = ar0832_read_reg(dev->i2c_client, 0x001, status); | ||
1820 | */ | ||
1821 | dev_dbg(&i2c_client->dev, "%s: %u %d\n", __func__, *status, err); | ||
1822 | return err; | ||
1823 | } | ||
1824 | |||
1825 | static int ar0832_set_alternate_addr(struct i2c_client *client) | ||
1826 | { | ||
1827 | int ret = 0; | ||
1828 | u8 new_addr = client->addr; | ||
1829 | u16 val; | ||
1830 | |||
1831 | /* Default slave address of ar0832 is 0x36 */ | ||
1832 | client->addr = 0x36; | ||
1833 | ret = ar0832_read_reg16(client, AR0832_RESET_REG, &val); | ||
1834 | val &= ~AR0832_RESET_REG_LOCK_REG; | ||
1835 | ret |= ar0832_write_reg16(client, AR0832_RESET_REG, val); | ||
1836 | ret |= ar0832_write_reg16(client, AR0832_ID_REG, new_addr << 1); | ||
1837 | |||
1838 | if (!ret) { | ||
1839 | client->addr = new_addr; | ||
1840 | dev_dbg(&client->dev, | ||
1841 | "new slave address is set to 0x%x\n", new_addr); | ||
1842 | } | ||
1843 | |||
1844 | ret |= ar0832_read_reg16(client, AR0832_RESET_REG, &val); | ||
1845 | val |= AR0832_RESET_REG_LOCK_REG; | ||
1846 | ret |= ar0832_write_reg16(client, AR0832_RESET_REG, val); | ||
1847 | |||
1848 | return ret; | ||
1849 | } | ||
1850 | |||
1851 | static int ar0832_power_on(struct ar0832_dev *dev) | ||
1852 | { | ||
1853 | struct i2c_client *i2c_client = dev->i2c_client; | ||
1854 | int ret = 0; | ||
1855 | |||
1856 | dev_dbg(&i2c_client->dev, "%s: ++ %d %d\n", | ||
1857 | __func__, dev->is_stereo, | ||
1858 | dev->brd_power_cnt); | ||
1859 | |||
1860 | /* Board specific power-on sequence */ | ||
1861 | mutex_lock(&dev->ar0832_camera_lock); | ||
1862 | if (dev->brd_power_cnt == 0) { | ||
1863 | /* Plug 1.8V and 2.8V power to sensor */ | ||
1864 | if (dev->power_rail.sen_1v8_reg) { | ||
1865 | ret = regulator_enable(dev->power_rail.sen_1v8_reg); | ||
1866 | if (ret) { | ||
1867 | dev_err(&i2c_client->dev, | ||
1868 | "%s: failed to enable vdd\n", | ||
1869 | __func__); | ||
1870 | goto fail_regulator_1v8_reg; | ||
1871 | } | ||
1872 | } | ||
1873 | |||
1874 | if (dev->power_rail.sen_2v8_reg) { | ||
1875 | ret = regulator_enable(dev->power_rail.sen_2v8_reg); | ||
1876 | if (ret) { | ||
1877 | dev_err(&i2c_client->dev, | ||
1878 | "%s: failed to enable vaa\n", | ||
1879 | __func__); | ||
1880 | goto fail_regulator_2v8_reg; | ||
1881 | } | ||
1882 | } | ||
1883 | dev->pdata->power_on(dev->is_stereo); | ||
1884 | } | ||
1885 | dev->brd_power_cnt++; | ||
1886 | mutex_unlock(&dev->ar0832_camera_lock); | ||
1887 | |||
1888 | /* Change slave address */ | ||
1889 | if (i2c_client->addr) | ||
1890 | ret = ar0832_set_alternate_addr(i2c_client); | ||
1891 | |||
1892 | return 0; | ||
1893 | |||
1894 | fail_regulator_2v8_reg: | ||
1895 | regulator_put(dev->power_rail.sen_2v8_reg); | ||
1896 | dev->power_rail.sen_2v8_reg = NULL; | ||
1897 | regulator_disable(dev->power_rail.sen_1v8_reg); | ||
1898 | fail_regulator_1v8_reg: | ||
1899 | regulator_put(dev->power_rail.sen_1v8_reg); | ||
1900 | dev->power_rail.sen_1v8_reg = NULL; | ||
1901 | return ret; | ||
1902 | } | ||
1903 | |||
1904 | static void ar0832_power_off(struct ar0832_dev *dev) | ||
1905 | { | ||
1906 | struct i2c_client *i2c_client = dev->i2c_client; | ||
1907 | dev_dbg(&i2c_client->dev, "%s: ++ %d\n", __func__, dev->brd_power_cnt); | ||
1908 | |||
1909 | /* Board specific power-down sequence */ | ||
1910 | mutex_lock(&dev->ar0832_camera_lock); | ||
1911 | |||
1912 | if (dev->brd_power_cnt <= 0) | ||
1913 | goto ar0832_pwdn_exit; | ||
1914 | |||
1915 | if (dev->brd_power_cnt-- == 1) { | ||
1916 | /* Unplug 1.8V and 2.8V power from sensor */ | ||
1917 | if (dev->power_rail.sen_2v8_reg) | ||
1918 | regulator_disable(dev->power_rail.sen_2v8_reg); | ||
1919 | if (dev->power_rail.sen_1v8_reg) | ||
1920 | regulator_disable(dev->power_rail.sen_1v8_reg); | ||
1921 | dev->pdata->power_off(dev->is_stereo); | ||
1922 | } | ||
1923 | |||
1924 | ar0832_pwdn_exit: | ||
1925 | mutex_unlock(&dev->ar0832_camera_lock); | ||
1926 | } | ||
1927 | |||
1928 | static int ar0832_focuser_set_config(struct ar0832_dev *dev) | ||
1929 | { | ||
1930 | struct i2c_client *i2c_client = dev->i2c_client; | ||
1931 | struct ar0832_reg reg_vcm_ctrl, reg_vcm_step_time; | ||
1932 | int ret = 0; | ||
1933 | u8 vcm_slew = 1; | ||
1934 | |||
1935 | /* bit15(0x80) means that VCM driver enable bit. */ | ||
1936 | /* bit3(0x08) means that keep VCM(AF position) */ | ||
1937 | /* while sensor is in soft standby mode during mode transitions. */ | ||
1938 | u16 vcm_control_data = (0x80 << 8 | (0x08 | (vcm_slew & 0x07))); | ||
1939 | u16 vcm_step_time = 1024; | ||
1940 | |||
1941 | ar0832_get_focuser_vcm_control_regs(®_vcm_ctrl, vcm_control_data); | ||
1942 | ret = ar0832_write_reg16(dev->i2c_client, reg_vcm_ctrl.addr, | ||
1943 | reg_vcm_ctrl.val); | ||
1944 | |||
1945 | dev_dbg(&i2c_client->dev, "%s Reg 0x%X Value 0x%X\n", __func__, | ||
1946 | reg_vcm_ctrl.addr, reg_vcm_ctrl.val); | ||
1947 | |||
1948 | if (ret) { | ||
1949 | dev_dbg(&i2c_client->dev, "%s Error writing to register 0x%X\n", | ||
1950 | __func__, reg_vcm_ctrl.addr); | ||
1951 | return ret; | ||
1952 | } | ||
1953 | |||
1954 | ar0832_get_focuser_vcm_step_time_regs(®_vcm_step_time, | ||
1955 | vcm_step_time); | ||
1956 | ret = ar0832_write_reg16(dev->i2c_client, reg_vcm_step_time.addr, | ||
1957 | reg_vcm_step_time.val); | ||
1958 | |||
1959 | dev_dbg(&i2c_client->dev, "%s Reg step_time 0x%X Value 0x%X\n", | ||
1960 | __func__, reg_vcm_step_time.addr, | ||
1961 | reg_vcm_step_time.val); | ||
1962 | |||
1963 | return ret; | ||
1964 | } | ||
1965 | |||
1966 | static int ar0832_focuser_set_position(struct ar0832_dev *dev, | ||
1967 | u32 position) | ||
1968 | { | ||
1969 | int ret = 0; | ||
1970 | struct ar0832_reg reg_data; | ||
1971 | |||
1972 | ar0832_get_focuser_data_regs(®_data, position); | ||
1973 | ret = ar0832_write_reg16(dev->i2c_client, reg_data.addr, | ||
1974 | reg_data.val); | ||
1975 | dev->focuser_info->last_position = position; | ||
1976 | |||
1977 | return ret; | ||
1978 | } | ||
1979 | |||
1980 | |||
1981 | /* | ||
1982 | * This function is not currently called as we have the hardcoded | ||
1983 | * step time in ar0832_focuser_set_config function. If we need to | ||
1984 | * compute the actual step time based on a number of clocks, we need | ||
1985 | * to use this function. The formula for computing the clock-based | ||
1986 | * step time is obtained from Aptina and is not part of external | ||
1987 | * documentation and hence this code needs to be saved. | ||
1988 | */ | ||
1989 | static u16 ar0832_get_focuser_vcm_step_time(struct ar0832_dev *dev) | ||
1990 | { | ||
1991 | struct i2c_client *i2c_client = dev->i2c_client; | ||
1992 | int ret; | ||
1993 | u16 pll_multiplier = 0; | ||
1994 | u16 pre_pll_clk_div = 0; | ||
1995 | u16 vt_sys_clk_div = 0; | ||
1996 | u16 vt_pix_clk_div = 0; | ||
1997 | u16 vt_pix_clk_freq_mhz = 0; | ||
1998 | |||
1999 | ret = ar0832_read_reg16(dev->i2c_client, 0x306, &pll_multiplier); | ||
2000 | if (ret) { | ||
2001 | dev_err(&i2c_client->dev, "%s pll_multiplier read failed\n", | ||
2002 | __func__); | ||
2003 | } | ||
2004 | |||
2005 | ret = ar0832_read_reg16(dev->i2c_client, 0x304, &pre_pll_clk_div); | ||
2006 | if (ret) { | ||
2007 | dev_err(&i2c_client->dev, "%s pre_pll_clk_div read failed\n", | ||
2008 | __func__); | ||
2009 | } | ||
2010 | |||
2011 | ret = ar0832_read_reg16(dev->i2c_client, 0x302, &vt_sys_clk_div); | ||
2012 | if (ret) { | ||
2013 | dev_err(&i2c_client->dev, "%s vt_sys_clk_div read failed\n", | ||
2014 | __func__); | ||
2015 | } | ||
2016 | |||
2017 | ret = ar0832_read_reg16(dev->i2c_client, 0x300, &vt_pix_clk_div); | ||
2018 | if (ret) { | ||
2019 | dev_err(&i2c_client->dev, "%s vt_pix_clk_div read failed\n", | ||
2020 | __func__); | ||
2021 | } | ||
2022 | |||
2023 | vt_pix_clk_freq_mhz = | ||
2024 | (24 * pll_multiplier) / (pre_pll_clk_div * vt_sys_clk_div * | ||
2025 | vt_pix_clk_div); | ||
2026 | |||
2027 | dev_dbg(&i2c_client->dev, "%s pll_multiplier 0x%X pre_pll_clk_div 0x%X " | ||
2028 | "vt_sys_clk_div 0x%X vt_pix_clk_div 0x%X vt_pix_clk_freq_mhz 0x%X\n", | ||
2029 | __func__, pll_multiplier, | ||
2030 | pre_pll_clk_div, vt_sys_clk_div, | ||
2031 | vt_pix_clk_div, vt_pix_clk_freq_mhz); | ||
2032 | |||
2033 | return vt_pix_clk_freq_mhz; | ||
2034 | |||
2035 | } | ||
2036 | |||
2037 | static inline | ||
2038 | int ar0832_get_sensorid(struct ar0832_dev *dev, u16 *sensor_id) | ||
2039 | { | ||
2040 | int ret; | ||
2041 | struct i2c_client *i2c_client = dev->i2c_client; | ||
2042 | |||
2043 | ret = ar0832_power_on(dev); | ||
2044 | if (ret) | ||
2045 | return ret; | ||
2046 | |||
2047 | ret = ar0832_read_reg16(i2c_client, AR0832_SENSORID_REG, sensor_id); | ||
2048 | dev_dbg(&i2c_client->dev, | ||
2049 | "%s: sensor_id - %04x\n", __func__, *sensor_id); | ||
2050 | |||
2051 | ar0832_power_off(dev); | ||
2052 | |||
2053 | return ret; | ||
2054 | } | ||
2055 | |||
2056 | static long ar0832_ioctl(struct file *file, | ||
2057 | unsigned int cmd, unsigned long arg) | ||
2058 | { | ||
2059 | int err; | ||
2060 | struct ar0832_dev *dev = file->private_data; | ||
2061 | struct i2c_client *i2c_client = dev->i2c_client; | ||
2062 | struct ar0832_mode mode; | ||
2063 | u16 pos; | ||
2064 | |||
2065 | switch (cmd) { | ||
2066 | case AR0832_IOCTL_SET_POWER_ON: | ||
2067 | dev_dbg(&i2c_client->dev, "AR0832_IOCTL_SET_POWER_ON\n"); | ||
2068 | if (copy_from_user(&mode, | ||
2069 | (const void __user *)arg, | ||
2070 | sizeof(struct ar0832_mode))) { | ||
2071 | dev_err(&i2c_client->dev, | ||
2072 | "%s: AR0832_IOCTL_SET_POWER_ON failed\n", | ||
2073 | __func__); | ||
2074 | return -EFAULT; | ||
2075 | } | ||
2076 | dev->is_stereo = mode.stereo; | ||
2077 | return ar0832_power_on(dev); | ||
2078 | case AR0832_IOCTL_SET_MODE: | ||
2079 | { | ||
2080 | dev_dbg(&i2c_client->dev, "AR0832_IOCTL_SET_MODE\n"); | ||
2081 | if (copy_from_user(&mode, | ||
2082 | (const void __user *)arg, | ||
2083 | sizeof(struct ar0832_mode))) { | ||
2084 | dev_err(&i2c_client->dev, | ||
2085 | "%s: AR0832_IOCTL_SET_MODE failed\n", | ||
2086 | __func__); | ||
2087 | return -EFAULT; | ||
2088 | } | ||
2089 | mutex_lock(&dev->ar0832_camera_lock); | ||
2090 | err = ar0832_set_mode(dev, &mode); | ||
2091 | |||
2092 | /* | ||
2093 | * We need to re-initialize the Focuser registers during mode | ||
2094 | * switch due to the known issue of focuser retracting | ||
2095 | */ | ||
2096 | ar0832_focuser_set_config(dev); | ||
2097 | dev->focuser_info->focuser_init_flag = true; | ||
2098 | |||
2099 | /* | ||
2100 | * If the last focuser position is not at infinity when we | ||
2101 | * did the mode switch, we need to go there. Before that, | ||
2102 | * we need to come back to 0. | ||
2103 | */ | ||
2104 | if (dev->focuser_info->last_position > 0) { | ||
2105 | pos = dev->focuser_info->last_position; | ||
2106 | dev_dbg(&i2c_client->dev, "%s: AR0832_IOCTL_SET_MODE: " | ||
2107 | " Move to 0, restore the backedup focuser position of %d\n", | ||
2108 | __func__, pos); | ||
2109 | ar0832_focuser_set_position(dev, 0); | ||
2110 | ar0832_msleep(10); | ||
2111 | |||
2112 | ar0832_focuser_set_position(dev, pos); | ||
2113 | ar0832_msleep(10); | ||
2114 | } | ||
2115 | mutex_unlock(&dev->ar0832_camera_lock); | ||
2116 | return err; | ||
2117 | } | ||
2118 | case AR0832_IOCTL_SET_FRAME_LENGTH: | ||
2119 | mutex_lock(&dev->ar0832_camera_lock); | ||
2120 | err = ar0832_set_frame_length(dev, (u32)arg); | ||
2121 | mutex_unlock(&dev->ar0832_camera_lock); | ||
2122 | return err; | ||
2123 | case AR0832_IOCTL_SET_COARSE_TIME: | ||
2124 | mutex_lock(&dev->ar0832_camera_lock); | ||
2125 | err = ar0832_set_coarse_time(dev, (u32)arg); | ||
2126 | mutex_unlock(&dev->ar0832_camera_lock); | ||
2127 | return err; | ||
2128 | case AR0832_IOCTL_SET_GAIN: | ||
2129 | mutex_lock(&dev->ar0832_camera_lock); | ||
2130 | err = ar0832_set_gain(dev, (u16)arg); | ||
2131 | mutex_unlock(&dev->ar0832_camera_lock); | ||
2132 | return err; | ||
2133 | case AR0832_IOCTL_GET_STATUS: | ||
2134 | { | ||
2135 | u8 status; | ||
2136 | dev_dbg(&i2c_client->dev, "AR0832_IOCTL_GET_STATUS\n"); | ||
2137 | err = ar0832_get_status(dev, &status); | ||
2138 | if (err) | ||
2139 | return err; | ||
2140 | if (copy_to_user((void __user *)arg, &status, | ||
2141 | 2)) { | ||
2142 | dev_err(&i2c_client->dev, | ||
2143 | "%s: AR0832_IOCTL_GET_STATUS failed\n", | ||
2144 | __func__); | ||
2145 | return -EFAULT; | ||
2146 | } | ||
2147 | return 0; | ||
2148 | } | ||
2149 | case AR0832_IOCTL_SET_SENSOR_REGION: | ||
2150 | { | ||
2151 | struct ar0832_stereo_region region; | ||
2152 | dev_dbg(&i2c_client->dev, "AR0832_IOCTL_SET_SENSOR_REGION\n"); | ||
2153 | /* Right now, it doesn't do anything */ | ||
2154 | |||
2155 | return 0; | ||
2156 | } | ||
2157 | |||
2158 | case AR0832_FOCUSER_IOCTL_GET_CONFIG: | ||
2159 | dev_dbg(&i2c_client->dev, | ||
2160 | "%s AR0832_FOCUSER_IOCTL_GET_CONFIG\n", __func__); | ||
2161 | if (copy_to_user((void __user *) arg, | ||
2162 | &dev->focuser_info->config, | ||
2163 | sizeof(dev->focuser_info->config))) { | ||
2164 | dev_err(&i2c_client->dev, | ||
2165 | "%s: AR0832_FOCUSER_IOCTL_GET_CONFIG failed\n", | ||
2166 | __func__); | ||
2167 | return -EFAULT; | ||
2168 | } | ||
2169 | return 0; | ||
2170 | |||
2171 | case AR0832_FOCUSER_IOCTL_SET_POSITION: | ||
2172 | dev_dbg(&i2c_client->dev, | ||
2173 | "%s AR0832_FOCUSER_IOCTL_SET_POSITION\n", __func__); | ||
2174 | mutex_lock(&dev->ar0832_camera_lock); | ||
2175 | if (dev->focuser_info->focuser_init_flag == false) { | ||
2176 | ar0832_focuser_set_config(dev); | ||
2177 | dev->focuser_info->focuser_init_flag = true; | ||
2178 | } | ||
2179 | err = ar0832_focuser_set_position(dev, (u32)arg); | ||
2180 | mutex_unlock(&dev->ar0832_camera_lock); | ||
2181 | return err; | ||
2182 | |||
2183 | case AR0832_IOCTL_GET_SENSOR_ID: | ||
2184 | dev_dbg(&i2c_client->dev, | ||
2185 | "%s AR0832_IOCTL_GET_SENSOR_ID\n", __func__); | ||
2186 | |||
2187 | if (!dev->sensor_id_data) { | ||
2188 | err = ar0832_get_sensorid(dev, &dev->sensor_id_data); | ||
2189 | if (err) { | ||
2190 | dev_err(&i2c_client->dev, | ||
2191 | "%s: failed to get sensor id\n", | ||
2192 | __func__); | ||
2193 | return -EFAULT; | ||
2194 | } | ||
2195 | } | ||
2196 | |||
2197 | if (copy_to_user((void __user *) arg, | ||
2198 | &dev->sensor_id_data, | ||
2199 | sizeof(dev->sensor_id_data))) { | ||
2200 | dev_err(&i2c_client->dev, | ||
2201 | "%s: AR0832_IOCTL_GET_SENSOR_ID failed\n", | ||
2202 | __func__); | ||
2203 | return -EFAULT; | ||
2204 | } | ||
2205 | return 0; | ||
2206 | |||
2207 | default: | ||
2208 | dev_err(&i2c_client->dev, "(error) %s NONE IOCTL\n", | ||
2209 | __func__); | ||
2210 | return -EINVAL; | ||
2211 | } | ||
2212 | return 0; | ||
2213 | } | ||
2214 | |||
2215 | static int ar0832_open(struct inode *inode, struct file *file) | ||
2216 | { | ||
2217 | struct miscdevice *miscdev = file->private_data; | ||
2218 | struct ar0832_dev *dev = dev_get_drvdata(miscdev->parent); | ||
2219 | struct i2c_client *i2c_client = dev->i2c_client; | ||
2220 | |||
2221 | dev_dbg(&i2c_client->dev, "%s: ++\n", __func__); | ||
2222 | if (atomic_xchg(&dev->in_use, 1)) | ||
2223 | return -EBUSY; | ||
2224 | |||
2225 | dev->focuser_info->focuser_init_flag = false; | ||
2226 | file->private_data = dev; | ||
2227 | |||
2228 | return 0; | ||
2229 | } | ||
2230 | |||
2231 | static int ar0832_release(struct inode *inode, struct file *file) | ||
2232 | { | ||
2233 | struct ar0832_dev *dev = file->private_data; | ||
2234 | struct i2c_client *i2c_client = dev->i2c_client; | ||
2235 | |||
2236 | dev_dbg(&i2c_client->dev, "%s: ++\n", __func__); | ||
2237 | |||
2238 | ar0832_power_off(dev); | ||
2239 | |||
2240 | file->private_data = NULL; | ||
2241 | |||
2242 | dev->focuser_info->focuser_init_flag = false; | ||
2243 | |||
2244 | WARN_ON(!atomic_xchg(&dev->in_use, 0)); | ||
2245 | return 0; | ||
2246 | } | ||
2247 | |||
2248 | static const struct file_operations ar0832_fileops = { | ||
2249 | .owner = THIS_MODULE, | ||
2250 | .open = ar0832_open, | ||
2251 | .unlocked_ioctl = ar0832_ioctl, | ||
2252 | .release = ar0832_release, | ||
2253 | }; | ||
2254 | |||
2255 | static int ar0832_debugfs_show(struct seq_file *s, void *unused) | ||
2256 | { | ||
2257 | struct ar0832_dev *dev = s->private; | ||
2258 | struct i2c_client *i2c_client = dev->i2c_client; | ||
2259 | int ret; | ||
2260 | u16 test_pattern_reg; | ||
2261 | |||
2262 | dev_dbg(&dev->i2c_client->dev, "%s: ++\n", __func__); | ||
2263 | if (!dev->brd_power_cnt) { | ||
2264 | dev_info(&i2c_client->dev, | ||
2265 | "%s: camera is off\n", __func__); | ||
2266 | return 0; | ||
2267 | } | ||
2268 | |||
2269 | mutex_lock(&dev->ar0832_camera_lock); | ||
2270 | ret = ar0832_read_reg16(i2c_client, | ||
2271 | AR0832_TEST_PATTERN_REG, &test_pattern_reg); | ||
2272 | mutex_unlock(&dev->ar0832_camera_lock); | ||
2273 | |||
2274 | if (ret) { | ||
2275 | dev_err(&i2c_client->dev, | ||
2276 | "%s: test pattern write failed\n", __func__); | ||
2277 | return -EFAULT; | ||
2278 | } | ||
2279 | |||
2280 | seq_printf(s, "%d\n", test_pattern_reg); | ||
2281 | return 0; | ||
2282 | } | ||
2283 | |||
2284 | static ssize_t ar0832_debugfs_write( | ||
2285 | struct file *file, | ||
2286 | char const __user *buf, | ||
2287 | size_t count, | ||
2288 | loff_t *offset) | ||
2289 | { | ||
2290 | struct ar0832_dev *dev = ((struct seq_file *)file->private_data)->private; | ||
2291 | struct i2c_client *i2c_client = dev->i2c_client; | ||
2292 | int ret = 0; | ||
2293 | char buffer[10]; | ||
2294 | u16 input, val, red = 0, green = 0, blue = 0; | ||
2295 | |||
2296 | dev_dbg(&i2c_client->dev, "%s: ++\n", __func__); | ||
2297 | if (!dev->brd_power_cnt) { | ||
2298 | dev_info(&i2c_client->dev, | ||
2299 | "%s: camera is off\n", __func__); | ||
2300 | return count; | ||
2301 | } | ||
2302 | |||
2303 | if (copy_from_user(&buffer, buf, sizeof(buffer))) | ||
2304 | goto debugfs_write_fail; | ||
2305 | |||
2306 | input = (u16)simple_strtoul(buffer, NULL, 10); | ||
2307 | |||
2308 | mutex_lock(&dev->ar0832_camera_lock); | ||
2309 | ret = ar0832_write_reg8(i2c_client, AR0832_GROUP_HOLD_REG, 0x1); | ||
2310 | |||
2311 | switch (input) { | ||
2312 | case 1: /* color bar */ | ||
2313 | val = 2; | ||
2314 | break; | ||
2315 | case 2: /* Red */ | ||
2316 | val = 1; | ||
2317 | red = 0x300; /* 10 bit value */ | ||
2318 | green = 0; | ||
2319 | blue = 0; | ||
2320 | break; | ||
2321 | case 3: /* Green */ | ||
2322 | val = 1; | ||
2323 | red = 0; | ||
2324 | green = 0x300; /* 10 bit value */ | ||
2325 | blue = 0; | ||
2326 | break; | ||
2327 | case 4: /* Blue */ | ||
2328 | val = 1; | ||
2329 | red = 0; | ||
2330 | green = 0; | ||
2331 | blue = 0x300; /* 10 bit value */ | ||
2332 | break; | ||
2333 | default: | ||
2334 | val = 0; | ||
2335 | break; | ||
2336 | } | ||
2337 | |||
2338 | if (input == 2 || input == 3 || input == 4) { | ||
2339 | ret |= ar0832_write_reg_helper(dev, | ||
2340 | AR0832_TEST_RED_REG, red); | ||
2341 | ret |= ar0832_write_reg_helper(dev, | ||
2342 | AR0832_TEST_GREENR_REG, green); | ||
2343 | ret |= ar0832_write_reg_helper(dev, | ||
2344 | AR0832_TEST_GREENR_REG, green); | ||
2345 | ret |= ar0832_write_reg_helper(dev, | ||
2346 | AR0832_TEST_BLUE_REG, blue); | ||
2347 | } | ||
2348 | |||
2349 | ret |= ar0832_write_reg_helper(dev, AR0832_TEST_PATTERN_REG, val); | ||
2350 | ret |= ar0832_write_reg8(i2c_client, AR0832_GROUP_HOLD_REG, 0x0); | ||
2351 | mutex_unlock(&dev->ar0832_camera_lock); | ||
2352 | |||
2353 | if (ret) | ||
2354 | goto debugfs_write_fail; | ||
2355 | |||
2356 | return count; | ||
2357 | |||
2358 | debugfs_write_fail: | ||
2359 | dev_err(&i2c_client->dev, | ||
2360 | "%s: test pattern write failed\n", __func__); | ||
2361 | return -EFAULT; | ||
2362 | } | ||
2363 | |||
2364 | static int ar0832_debugfs_open(struct inode *inode, struct file *file) | ||
2365 | { | ||
2366 | struct ar0832_dev *dev = inode->i_private; | ||
2367 | struct i2c_client *i2c_client = dev->i2c_client; | ||
2368 | |||
2369 | dev_dbg(&i2c_client->dev, "%s: ++\n", __func__); | ||
2370 | |||
2371 | return single_open(file, ar0832_debugfs_show, inode->i_private); | ||
2372 | } | ||
2373 | |||
2374 | static const struct file_operations ar0832_debugfs_fops = { | ||
2375 | .open = ar0832_debugfs_open, | ||
2376 | .read = seq_read, | ||
2377 | .write = ar0832_debugfs_write, | ||
2378 | .llseek = seq_lseek, | ||
2379 | .release = single_release, | ||
2380 | }; | ||
2381 | |||
2382 | static void __devexit ar0832_remove_debugfs(struct ar0832_dev *dev) | ||
2383 | { | ||
2384 | struct i2c_client *i2c_client = dev->i2c_client; | ||
2385 | |||
2386 | dev_dbg(&i2c_client->dev, "%s: ++\n", __func__); | ||
2387 | |||
2388 | if (dev->debugdir) | ||
2389 | debugfs_remove_recursive(dev->debugdir); | ||
2390 | dev->debugdir = NULL; | ||
2391 | } | ||
2392 | |||
2393 | static void ar0832_create_debugfs(struct ar0832_dev *dev) | ||
2394 | { | ||
2395 | struct dentry *ret; | ||
2396 | struct i2c_client *i2c_client = dev->i2c_client; | ||
2397 | |||
2398 | dev_dbg(&i2c_client->dev, "%s\n", __func__); | ||
2399 | |||
2400 | dev->debugdir = debugfs_create_dir(dev->dname, NULL); | ||
2401 | if (!dev->debugdir) | ||
2402 | goto remove_debugfs; | ||
2403 | |||
2404 | ret = debugfs_create_file("test_pattern", | ||
2405 | S_IWUGO | S_IRUGO, | ||
2406 | dev->debugdir, dev, | ||
2407 | &ar0832_debugfs_fops); | ||
2408 | if (!ret) | ||
2409 | goto remove_debugfs; | ||
2410 | |||
2411 | return; | ||
2412 | remove_debugfs: | ||
2413 | dev_err(&i2c_client->dev, "couldn't create debugfs\n"); | ||
2414 | ar0832_remove_debugfs(dev); | ||
2415 | } | ||
2416 | |||
2417 | static int ar0832_probe(struct i2c_client *client, | ||
2418 | const struct i2c_device_id *id) | ||
2419 | { | ||
2420 | int err; | ||
2421 | struct ar0832_dev *dev = NULL; | ||
2422 | int ret; | ||
2423 | |||
2424 | dev_info(&client->dev, "ar0832: probing sensor.(id:%s)\n", | ||
2425 | id->name); | ||
2426 | |||
2427 | dev = kzalloc(sizeof(struct ar0832_dev), GFP_KERNEL); | ||
2428 | if (!dev) | ||
2429 | goto probe_fail_release; | ||
2430 | |||
2431 | dev->sensor_info = kzalloc(sizeof(struct ar0832_sensor_info), | ||
2432 | GFP_KERNEL); | ||
2433 | if (!dev->sensor_info) | ||
2434 | goto probe_fail_release; | ||
2435 | |||
2436 | dev->focuser_info = kzalloc(sizeof(struct ar0832_focuser_info), | ||
2437 | GFP_KERNEL); | ||
2438 | if (!dev->focuser_info) | ||
2439 | goto probe_fail_release; | ||
2440 | |||
2441 | /* sensor */ | ||
2442 | dev->pdata = client->dev.platform_data; | ||
2443 | dev->i2c_client = client; | ||
2444 | |||
2445 | /* focuser */ | ||
2446 | dev->focuser_info->config.settle_time = SETTLETIME_MS; | ||
2447 | dev->focuser_info->config.pos_low = POS_LOW; | ||
2448 | dev->focuser_info->config.pos_high = POS_HIGH; | ||
2449 | |||
2450 | snprintf(dev->dname, sizeof(dev->dname), "%s-%s", | ||
2451 | id->name, dev->pdata->id); | ||
2452 | dev->misc_dev.minor = MISC_DYNAMIC_MINOR; | ||
2453 | dev->misc_dev.name = dev->dname; | ||
2454 | dev->misc_dev.fops = &ar0832_fileops; | ||
2455 | dev->misc_dev.mode = S_IRWXUGO; | ||
2456 | dev->misc_dev.parent = &client->dev; | ||
2457 | err = misc_register(&dev->misc_dev); | ||
2458 | if (err) { | ||
2459 | dev_err(&client->dev, "Unable to register misc device!\n"); | ||
2460 | ret = -ENOMEM; | ||
2461 | goto probe_fail_free; | ||
2462 | } | ||
2463 | |||
2464 | i2c_set_clientdata(client, dev); | ||
2465 | mutex_init(&dev->ar0832_camera_lock); | ||
2466 | |||
2467 | dev->power_rail.sen_1v8_reg = regulator_get(&client->dev, "vdd"); | ||
2468 | if (IS_ERR_OR_NULL(dev->power_rail.sen_1v8_reg)) { | ||
2469 | dev_err(&client->dev, "%s: failed to get vdd\n", | ||
2470 | __func__); | ||
2471 | ret = PTR_ERR(dev->power_rail.sen_1v8_reg); | ||
2472 | goto probe_fail_free; | ||
2473 | } | ||
2474 | |||
2475 | dev->power_rail.sen_2v8_reg = regulator_get(&client->dev, "vaa"); | ||
2476 | if (IS_ERR_OR_NULL(dev->power_rail.sen_2v8_reg)) { | ||
2477 | dev_err(&client->dev, "%s: failed to get vaa\n", | ||
2478 | __func__); | ||
2479 | ret = PTR_ERR(dev->power_rail.sen_2v8_reg); | ||
2480 | regulator_put(dev->power_rail.sen_1v8_reg); | ||
2481 | dev->power_rail.sen_1v8_reg = NULL; | ||
2482 | goto probe_fail_free; | ||
2483 | } | ||
2484 | /* create debugfs interface */ | ||
2485 | ar0832_create_debugfs(dev); | ||
2486 | |||
2487 | return 0; | ||
2488 | |||
2489 | probe_fail_release: | ||
2490 | dev_err(&client->dev, "%s: unable to allocate memory!\n", __func__); | ||
2491 | ret = -ENOMEM; | ||
2492 | probe_fail_free: | ||
2493 | if (dev) { | ||
2494 | kfree(dev->focuser_info); | ||
2495 | kfree(dev->sensor_info); | ||
2496 | } | ||
2497 | kfree(dev); | ||
2498 | return ret; | ||
2499 | } | ||
2500 | |||
2501 | static int ar0832_remove(struct i2c_client *client) | ||
2502 | { | ||
2503 | struct ar0832_dev *dev = i2c_get_clientdata(client); | ||
2504 | |||
2505 | if (dev->power_rail.sen_1v8_reg) | ||
2506 | regulator_put(dev->power_rail.sen_1v8_reg); | ||
2507 | if (dev->power_rail.sen_2v8_reg) | ||
2508 | regulator_put(dev->power_rail.sen_2v8_reg); | ||
2509 | |||
2510 | misc_deregister(&dev->misc_dev); | ||
2511 | if (dev) { | ||
2512 | kfree(dev->sensor_info); | ||
2513 | kfree(dev->focuser_info); | ||
2514 | } | ||
2515 | |||
2516 | ar0832_remove_debugfs(dev); | ||
2517 | |||
2518 | kfree(dev); | ||
2519 | return 0; | ||
2520 | } | ||
2521 | |||
2522 | static const struct i2c_device_id ar0832_id[] = { | ||
2523 | { "ar0832", 0 }, | ||
2524 | { } | ||
2525 | }; | ||
2526 | |||
2527 | static struct i2c_driver ar0832_i2c_driver = { | ||
2528 | .probe = ar0832_probe, | ||
2529 | .remove = ar0832_remove, | ||
2530 | .id_table = ar0832_id, | ||
2531 | .driver = { | ||
2532 | .name = "ar0832", | ||
2533 | .owner = THIS_MODULE, | ||
2534 | }, | ||
2535 | }; | ||
2536 | |||
2537 | static int __init ar0832_init(void) | ||
2538 | { | ||
2539 | pr_info("%s: ++\n", __func__); | ||
2540 | return i2c_add_driver(&ar0832_i2c_driver); | ||
2541 | } | ||
2542 | |||
2543 | static void __exit ar0832_exit(void) | ||
2544 | { | ||
2545 | i2c_del_driver(&ar0832_i2c_driver); | ||
2546 | } | ||
2547 | |||
2548 | module_init(ar0832_init); | ||
2549 | module_exit(ar0832_exit); | ||
diff --git a/drivers/media/video/tegra/avp/Kconfig b/drivers/media/video/tegra/avp/Kconfig new file mode 100644 index 00000000000..fdd208510fc --- /dev/null +++ b/drivers/media/video/tegra/avp/Kconfig | |||
@@ -0,0 +1,25 @@ | |||
1 | config TEGRA_RPC | ||
2 | bool "Enable support for Tegra RPC" | ||
3 | depends on ARCH_TEGRA | ||
4 | default y | ||
5 | help | ||
6 | Enables support for the RPC mechanism necessary for the Tegra | ||
7 | multimedia framework. It is both used to communicate locally on the | ||
8 | CPU between multiple multimedia components as well as to communicate | ||
9 | with the AVP for offloading media decode. | ||
10 | |||
11 | Exports the local tegra RPC interface on device node | ||
12 | /dev/tegra_rpc. Also provides tegra fd based semaphores needed by | ||
13 | the tegra multimedia framework. | ||
14 | |||
15 | If unsure, say Y | ||
16 | |||
17 | config TEGRA_AVP | ||
18 | bool "Enable support for the AVP multimedia offload engine" | ||
19 | depends on ARCH_TEGRA && TEGRA_RPC | ||
20 | default y | ||
21 | help | ||
22 | Enables support for the multimedia offload engine used by Tegra | ||
23 | multimedia framework. | ||
24 | |||
25 | If unsure, say Y | ||
diff --git a/drivers/media/video/tegra/avp/Makefile b/drivers/media/video/tegra/avp/Makefile new file mode 100644 index 00000000000..148265648a4 --- /dev/null +++ b/drivers/media/video/tegra/avp/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | GCOV_PROFILE := y | ||
2 | obj-$(CONFIG_TEGRA_RPC) += tegra_rpc.o | ||
3 | obj-$(CONFIG_TEGRA_RPC) += trpc_local.o | ||
4 | obj-$(CONFIG_TEGRA_RPC) += trpc_sema.o | ||
5 | obj-$(CONFIG_TEGRA_AVP) += avp.o | ||
6 | obj-$(CONFIG_TEGRA_AVP) += avp_svc.o | ||
7 | obj-$(CONFIG_TEGRA_AVP) += headavp.o | ||
diff --git a/drivers/media/video/tegra/avp/avp.c b/drivers/media/video/tegra/avp/avp.c new file mode 100644 index 00000000000..074a42f125b --- /dev/null +++ b/drivers/media/video/tegra/avp/avp.c | |||
@@ -0,0 +1,1949 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Google, Inc. | ||
3 | * Author: Dima Zavin <dima@android.com> | ||
4 | * | ||
5 | * Copyright (C) 2010-2012 NVIDIA Corporation | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/clk.h> | ||
19 | #include <linux/completion.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/dma-mapping.h> | ||
22 | #include <linux/err.h> | ||
23 | #include <linux/firmware.h> | ||
24 | #include <linux/fs.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/io.h> | ||
27 | #include <linux/ioctl.h> | ||
28 | #include <linux/irq.h> | ||
29 | #include <linux/kref.h> | ||
30 | #include <linux/list.h> | ||
31 | #include <linux/miscdevice.h> | ||
32 | #include <linux/mutex.h> | ||
33 | #include <linux/platform_device.h> | ||
34 | #include <linux/rbtree.h> | ||
35 | #include <linux/seq_file.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/tegra_rpc.h> | ||
38 | #include <linux/types.h> | ||
39 | #include <linux/uaccess.h> | ||
40 | #include <linux/workqueue.h> | ||
41 | |||
42 | #include <mach/clk.h> | ||
43 | #include <mach/io.h> | ||
44 | #include <mach/iomap.h> | ||
45 | #include <mach/nvmap.h> | ||
46 | #include <mach/legacy_irq.h> | ||
47 | #include <mach/hardware.h> | ||
48 | |||
49 | #include "../../../../video/tegra/nvmap/nvmap.h" | ||
50 | |||
51 | #include "headavp.h" | ||
52 | #include "avp_msg.h" | ||
53 | #include "trpc.h" | ||
54 | #include "avp.h" | ||
55 | #include "nvavp.h" | ||
56 | |||
57 | enum { | ||
58 | AVP_DBG_TRACE_XPC = 1U << 0, | ||
59 | AVP_DBG_TRACE_XPC_IRQ = 1U << 1, | ||
60 | AVP_DBG_TRACE_XPC_MSG = 1U << 2, | ||
61 | AVP_DBG_TRACE_XPC_CONN = 1U << 3, | ||
62 | AVP_DBG_TRACE_TRPC_MSG = 1U << 4, | ||
63 | AVP_DBG_TRACE_TRPC_CONN = 1U << 5, | ||
64 | AVP_DBG_TRACE_LIB = 1U << 6, | ||
65 | }; | ||
66 | |||
67 | static u32 avp_debug_mask = | ||
68 | AVP_DBG_TRACE_XPC | | ||
69 | /* AVP_DBG_TRACE_XPC_IRQ | */ | ||
70 | /* AVP_DBG_TRACE_XPC_MSG | */ | ||
71 | /* AVP_DBG_TRACE_TRPC_MSG | */ | ||
72 | AVP_DBG_TRACE_XPC_CONN | | ||
73 | AVP_DBG_TRACE_TRPC_CONN | | ||
74 | AVP_DBG_TRACE_LIB; | ||
75 | |||
76 | module_param_named(debug_mask, avp_debug_mask, uint, S_IWUSR | S_IRUGO); | ||
77 | |||
78 | #define DBG(flag, args...) \ | ||
79 | do { if (unlikely(avp_debug_mask & (flag))) pr_info(args); } while (0) | ||
80 | |||
81 | #define TEGRA_AVP_NAME "tegra-avp" | ||
82 | |||
83 | #define TEGRA_AVP_RESET_VECTOR_ADDR \ | ||
84 | (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x200) | ||
85 | |||
86 | #define TEGRA_AVP_RESUME_ADDR IO_ADDRESS(TEGRA_IRAM_BASE + \ | ||
87 | TEGRA_RESET_HANDLER_SIZE) | ||
88 | |||
89 | #define FLOW_CTRL_HALT_COP_EVENTS IO_ADDRESS(TEGRA_FLOW_CTRL_BASE + 0x4) | ||
90 | #define FLOW_MODE_STOP (0x2 << 29) | ||
91 | #define FLOW_MODE_NONE 0x0 | ||
92 | |||
93 | #define MBOX_FROM_AVP IO_ADDRESS(TEGRA_RES_SEMA_BASE + 0x10) | ||
94 | #define MBOX_TO_AVP IO_ADDRESS(TEGRA_RES_SEMA_BASE + 0x20) | ||
95 | |||
96 | /* Layout of the mailbox registers: | ||
97 | * bit 31 - pending message interrupt enable (mailbox full, i.e. valid=1) | ||
98 | * bit 30 - message cleared interrupt enable (mailbox empty, i.e. valid=0) | ||
99 | * bit 29 - message valid. peer clears this bit after reading msg | ||
100 | * bits 27:0 - message data | ||
101 | */ | ||
102 | #define MBOX_MSG_PENDING_INT_EN (1 << 31) | ||
103 | #define MBOX_MSG_READ_INT_EN (1 << 30) | ||
104 | #define MBOX_MSG_VALID (1 << 29) | ||
105 | |||
106 | #define AVP_MSG_MAX_CMD_LEN 16 | ||
107 | #define AVP_MSG_AREA_SIZE (AVP_MSG_MAX_CMD_LEN + TEGRA_RPC_MAX_MSG_LEN) | ||
108 | |||
109 | struct tegra_avp_info { | ||
110 | struct clk *cop_clk; | ||
111 | |||
112 | int mbox_from_avp_pend_irq; | ||
113 | |||
114 | dma_addr_t msg_area_addr; | ||
115 | u32 msg; | ||
116 | void *msg_to_avp; | ||
117 | void *msg_from_avp; | ||
118 | struct mutex to_avp_lock; | ||
119 | struct mutex from_avp_lock; | ||
120 | |||
121 | struct work_struct recv_work; | ||
122 | struct workqueue_struct *recv_wq; | ||
123 | |||
124 | struct trpc_node *rpc_node; | ||
125 | struct miscdevice misc_dev; | ||
126 | int refcount; | ||
127 | struct mutex open_lock; | ||
128 | |||
129 | spinlock_t state_lock; | ||
130 | bool initialized; | ||
131 | bool shutdown; | ||
132 | bool suspending; | ||
133 | bool defer_remote; | ||
134 | |||
135 | struct mutex libs_lock; | ||
136 | struct list_head libs; | ||
137 | struct nvmap_client *nvmap_libs; | ||
138 | |||
139 | /* client for driver allocations, persistent */ | ||
140 | struct nvmap_client *nvmap_drv; | ||
141 | struct nvmap_handle_ref *kernel_handle; | ||
142 | void *kernel_data; | ||
143 | phys_addr_t kernel_phys; | ||
144 | |||
145 | struct nvmap_handle_ref *iram_backup_handle; | ||
146 | void *iram_backup_data; | ||
147 | phys_addr_t iram_backup_phys; | ||
148 | unsigned long resume_addr; | ||
149 | unsigned long reset_addr; | ||
150 | |||
151 | struct trpc_endpoint *avp_ep; | ||
152 | struct rb_root endpoints; | ||
153 | |||
154 | struct avp_svc_info *avp_svc; | ||
155 | }; | ||
156 | |||
157 | struct remote_info { | ||
158 | u32 loc_id; | ||
159 | u32 rem_id; | ||
160 | struct kref ref; | ||
161 | |||
162 | struct trpc_endpoint *trpc_ep; | ||
163 | struct rb_node rb_node; | ||
164 | }; | ||
165 | |||
166 | struct lib_item { | ||
167 | struct list_head list; | ||
168 | u32 handle; | ||
169 | char name[TEGRA_AVP_LIB_MAX_NAME]; | ||
170 | }; | ||
171 | |||
172 | static struct tegra_avp_info *tegra_avp; | ||
173 | |||
174 | static int avp_trpc_send(struct trpc_endpoint *ep, void *buf, size_t len); | ||
175 | static void avp_trpc_close(struct trpc_endpoint *ep); | ||
176 | static void avp_trpc_show(struct seq_file *s, struct trpc_endpoint *ep); | ||
177 | static void libs_cleanup(struct tegra_avp_info *avp); | ||
178 | |||
179 | static struct trpc_ep_ops remote_ep_ops = { | ||
180 | .send = avp_trpc_send, | ||
181 | .close = avp_trpc_close, | ||
182 | .show = avp_trpc_show, | ||
183 | }; | ||
184 | |||
185 | static struct remote_info *rinfo_alloc(struct tegra_avp_info *avp) | ||
186 | { | ||
187 | struct remote_info *rinfo; | ||
188 | |||
189 | rinfo = kzalloc(sizeof(struct remote_info), GFP_KERNEL); | ||
190 | if (!rinfo) | ||
191 | return NULL; | ||
192 | kref_init(&rinfo->ref); | ||
193 | return rinfo; | ||
194 | } | ||
195 | |||
196 | static void _rinfo_release(struct kref *ref) | ||
197 | { | ||
198 | struct remote_info *rinfo = container_of(ref, struct remote_info, ref); | ||
199 | kfree(rinfo); | ||
200 | } | ||
201 | |||
202 | static inline void rinfo_get(struct remote_info *rinfo) | ||
203 | { | ||
204 | kref_get(&rinfo->ref); | ||
205 | } | ||
206 | |||
207 | static inline void rinfo_put(struct remote_info *rinfo) | ||
208 | { | ||
209 | kref_put(&rinfo->ref, _rinfo_release); | ||
210 | } | ||
211 | |||
212 | static int remote_insert(struct tegra_avp_info *avp, struct remote_info *rinfo) | ||
213 | { | ||
214 | struct rb_node **p; | ||
215 | struct rb_node *parent; | ||
216 | struct remote_info *tmp; | ||
217 | |||
218 | p = &avp->endpoints.rb_node; | ||
219 | parent = NULL; | ||
220 | while (*p) { | ||
221 | parent = *p; | ||
222 | tmp = rb_entry(parent, struct remote_info, rb_node); | ||
223 | |||
224 | if (rinfo->loc_id < tmp->loc_id) | ||
225 | p = &(*p)->rb_left; | ||
226 | else if (rinfo->loc_id > tmp->loc_id) | ||
227 | p = &(*p)->rb_right; | ||
228 | else { | ||
229 | pr_info("%s: avp endpoint id=%x (%s) already exists\n", | ||
230 | __func__, rinfo->loc_id, | ||
231 | trpc_name(rinfo->trpc_ep)); | ||
232 | return -EEXIST; | ||
233 | } | ||
234 | } | ||
235 | rb_link_node(&rinfo->rb_node, parent, p); | ||
236 | rb_insert_color(&rinfo->rb_node, &avp->endpoints); | ||
237 | rinfo_get(rinfo); | ||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | static struct remote_info *remote_find(struct tegra_avp_info *avp, u32 local_id) | ||
242 | { | ||
243 | struct rb_node *n = avp->endpoints.rb_node; | ||
244 | struct remote_info *rinfo; | ||
245 | |||
246 | while (n) { | ||
247 | rinfo = rb_entry(n, struct remote_info, rb_node); | ||
248 | |||
249 | if (local_id < rinfo->loc_id) | ||
250 | n = n->rb_left; | ||
251 | else if (local_id > rinfo->loc_id) | ||
252 | n = n->rb_right; | ||
253 | else | ||
254 | return rinfo; | ||
255 | } | ||
256 | return NULL; | ||
257 | } | ||
258 | |||
259 | static void remote_remove(struct tegra_avp_info *avp, struct remote_info *rinfo) | ||
260 | { | ||
261 | rb_erase(&rinfo->rb_node, &avp->endpoints); | ||
262 | rinfo_put(rinfo); | ||
263 | } | ||
264 | |||
265 | /* test whether or not the trpc endpoint provided is a valid AVP node | ||
266 | * endpoint */ | ||
267 | static struct remote_info *validate_trpc_ep(struct tegra_avp_info *avp, | ||
268 | struct trpc_endpoint *ep) | ||
269 | { | ||
270 | struct remote_info *tmp = trpc_priv(ep); | ||
271 | struct remote_info *rinfo; | ||
272 | |||
273 | if (!tmp) | ||
274 | return NULL; | ||
275 | rinfo = remote_find(avp, tmp->loc_id); | ||
276 | if (rinfo && rinfo == tmp && rinfo->trpc_ep == ep) | ||
277 | return rinfo; | ||
278 | return NULL; | ||
279 | } | ||
280 | |||
281 | static void avp_trpc_show(struct seq_file *s, struct trpc_endpoint *ep) | ||
282 | { | ||
283 | struct tegra_avp_info *avp = tegra_avp; | ||
284 | struct remote_info *rinfo; | ||
285 | unsigned long flags; | ||
286 | |||
287 | spin_lock_irqsave(&avp->state_lock, flags); | ||
288 | rinfo = validate_trpc_ep(avp, ep); | ||
289 | if (!rinfo) { | ||
290 | seq_printf(s, " <unknown>\n"); | ||
291 | goto out; | ||
292 | } | ||
293 | seq_printf(s, " loc_id:0x%x\n rem_id:0x%x\n", | ||
294 | rinfo->loc_id, rinfo->rem_id); | ||
295 | out: | ||
296 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
297 | } | ||
298 | |||
299 | static inline void mbox_writel(u32 val, void __iomem *mbox) | ||
300 | { | ||
301 | writel(val, mbox); | ||
302 | } | ||
303 | |||
304 | static inline u32 mbox_readl(void __iomem *mbox) | ||
305 | { | ||
306 | return readl(mbox); | ||
307 | } | ||
308 | |||
309 | static inline void msg_ack_remote(struct tegra_avp_info *avp, u32 cmd, u32 arg) | ||
310 | { | ||
311 | struct msg_ack *ack = avp->msg_from_avp; | ||
312 | |||
313 | /* must make sure the arg is there first */ | ||
314 | ack->arg = arg; | ||
315 | wmb(); | ||
316 | ack->cmd = cmd; | ||
317 | wmb(); | ||
318 | } | ||
319 | |||
320 | static inline u32 msg_recv_get_cmd(struct tegra_avp_info *avp) | ||
321 | { | ||
322 | volatile u32 *cmd = avp->msg_from_avp; | ||
323 | rmb(); | ||
324 | return *cmd; | ||
325 | } | ||
326 | |||
327 | static inline int __msg_write(struct tegra_avp_info *avp, void *hdr, | ||
328 | size_t hdr_len, void *buf, size_t len) | ||
329 | { | ||
330 | memcpy(avp->msg_to_avp, hdr, hdr_len); | ||
331 | if (buf && len) | ||
332 | memcpy(avp->msg_to_avp + hdr_len, buf, len); | ||
333 | mbox_writel(avp->msg, MBOX_TO_AVP); | ||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | static inline int msg_write(struct tegra_avp_info *avp, void *hdr, | ||
338 | size_t hdr_len, void *buf, size_t len) | ||
339 | { | ||
340 | /* rem_ack is a pointer into shared memory that the AVP modifies */ | ||
341 | volatile u32 *rem_ack = avp->msg_to_avp; | ||
342 | unsigned long endtime = jiffies + HZ; | ||
343 | |||
344 | /* the other side ack's the message by clearing the first word, | ||
345 | * wait for it to do so */ | ||
346 | rmb(); | ||
347 | while (*rem_ack != 0 && time_before(jiffies, endtime)) { | ||
348 | usleep_range(100, 2000); | ||
349 | rmb(); | ||
350 | } | ||
351 | if (*rem_ack != 0) | ||
352 | return -ETIMEDOUT; | ||
353 | __msg_write(avp, hdr, hdr_len, buf, len); | ||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | static inline int msg_check_ack(struct tegra_avp_info *avp, u32 cmd, u32 *arg) | ||
358 | { | ||
359 | struct msg_ack ack; | ||
360 | |||
361 | rmb(); | ||
362 | memcpy(&ack, avp->msg_to_avp, sizeof(ack)); | ||
363 | if (ack.cmd != cmd) | ||
364 | return -ENOENT; | ||
365 | if (arg) | ||
366 | *arg = ack.arg; | ||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | /* XXX: add timeout */ | ||
371 | static int msg_wait_ack_locked(struct tegra_avp_info *avp, u32 cmd, u32 *arg) | ||
372 | { | ||
373 | /* rem_ack is a pointer into shared memory that the AVP modifies */ | ||
374 | volatile u32 *rem_ack = avp->msg_to_avp; | ||
375 | unsigned long endtime = jiffies + msecs_to_jiffies(400); | ||
376 | int ret; | ||
377 | |||
378 | do { | ||
379 | ret = msg_check_ack(avp, cmd, arg); | ||
380 | usleep_range(1000, 5000); | ||
381 | } while (ret && time_before(jiffies, endtime)); | ||
382 | |||
383 | /* if we timed out, try one more time */ | ||
384 | if (ret) | ||
385 | ret = msg_check_ack(avp, cmd, arg); | ||
386 | |||
387 | /* clear out the ack */ | ||
388 | *rem_ack = 0; | ||
389 | wmb(); | ||
390 | return ret; | ||
391 | } | ||
392 | |||
393 | static int avp_trpc_send(struct trpc_endpoint *ep, void *buf, size_t len) | ||
394 | { | ||
395 | struct tegra_avp_info *avp = tegra_avp; | ||
396 | struct remote_info *rinfo; | ||
397 | struct msg_port_data msg; | ||
398 | int ret; | ||
399 | unsigned long flags; | ||
400 | |||
401 | DBG(AVP_DBG_TRACE_TRPC_MSG, "%s: ep=%p priv=%p buf=%p len=%d\n", | ||
402 | __func__, ep, trpc_priv(ep), buf, len); | ||
403 | |||
404 | spin_lock_irqsave(&avp->state_lock, flags); | ||
405 | if (unlikely(avp->suspending && trpc_peer(ep) != avp->avp_ep)) { | ||
406 | ret = -EBUSY; | ||
407 | goto err_state_locked; | ||
408 | } else if (avp->shutdown) { | ||
409 | ret = -ENODEV; | ||
410 | goto err_state_locked; | ||
411 | } | ||
412 | rinfo = validate_trpc_ep(avp, ep); | ||
413 | if (!rinfo) { | ||
414 | ret = -ENOTTY; | ||
415 | goto err_state_locked; | ||
416 | } | ||
417 | rinfo_get(rinfo); | ||
418 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
419 | |||
420 | msg.cmd = CMD_MESSAGE; | ||
421 | msg.port_id = rinfo->rem_id; | ||
422 | msg.msg_len = len; | ||
423 | |||
424 | mutex_lock(&avp->to_avp_lock); | ||
425 | ret = msg_write(avp, &msg, sizeof(msg), buf, len); | ||
426 | mutex_unlock(&avp->to_avp_lock); | ||
427 | |||
428 | DBG(AVP_DBG_TRACE_TRPC_MSG, "%s: msg sent for %s (%x->%x) (%d)\n", | ||
429 | __func__, trpc_name(ep), rinfo->loc_id, rinfo->rem_id, ret); | ||
430 | rinfo_put(rinfo); | ||
431 | return ret; | ||
432 | |||
433 | err_state_locked: | ||
434 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
435 | return ret; | ||
436 | } | ||
437 | |||
438 | static int _send_disconnect(struct tegra_avp_info *avp, u32 port_id) | ||
439 | { | ||
440 | struct msg_disconnect msg; | ||
441 | int ret; | ||
442 | |||
443 | msg.cmd = CMD_DISCONNECT; | ||
444 | msg.port_id = port_id; | ||
445 | |||
446 | mutex_lock(&avp->to_avp_lock); | ||
447 | ret = msg_write(avp, &msg, sizeof(msg), NULL, 0); | ||
448 | if (ret) { | ||
449 | pr_err("%s: remote has not acked last message (%x)\n", __func__, | ||
450 | port_id); | ||
451 | goto err_msg_write; | ||
452 | } | ||
453 | |||
454 | ret = msg_wait_ack_locked(avp, CMD_ACK, NULL); | ||
455 | if (ret) { | ||
456 | pr_err("%s: remote end won't respond for %x\n", __func__, | ||
457 | port_id); | ||
458 | goto err_wait_ack; | ||
459 | } | ||
460 | |||
461 | DBG(AVP_DBG_TRACE_XPC_CONN, "%s: sent disconnect msg for %x\n", | ||
462 | __func__, port_id); | ||
463 | |||
464 | err_wait_ack: | ||
465 | err_msg_write: | ||
466 | mutex_unlock(&avp->to_avp_lock); | ||
467 | return ret; | ||
468 | } | ||
469 | |||
470 | /* Note: Assumes that the rinfo was previously successfully added to the | ||
471 | * endpoints rb_tree. The initial refcnt of 1 is inherited by the port when the | ||
472 | * trpc endpoint is created with thi trpc_xxx functions. Thus, on close, | ||
473 | * we must drop that reference here. | ||
474 | * The avp->endpoints rb_tree keeps its own reference on rinfo objects. | ||
475 | * | ||
476 | * The try_connect function does not use this on error because it needs to | ||
477 | * split the close of trpc_ep port and the put. | ||
478 | */ | ||
479 | static inline void remote_close(struct remote_info *rinfo) | ||
480 | { | ||
481 | trpc_close(rinfo->trpc_ep); | ||
482 | rinfo_put(rinfo); | ||
483 | } | ||
484 | |||
485 | static void avp_trpc_close(struct trpc_endpoint *ep) | ||
486 | { | ||
487 | struct tegra_avp_info *avp = tegra_avp; | ||
488 | struct remote_info *rinfo; | ||
489 | unsigned long flags; | ||
490 | int ret; | ||
491 | |||
492 | spin_lock_irqsave(&avp->state_lock, flags); | ||
493 | if (avp->shutdown) { | ||
494 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
495 | return; | ||
496 | } | ||
497 | |||
498 | rinfo = validate_trpc_ep(avp, ep); | ||
499 | if (!rinfo) { | ||
500 | pr_err("%s: tried to close invalid port '%s' endpoint (%p)\n", | ||
501 | __func__, trpc_name(ep), ep); | ||
502 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
503 | return; | ||
504 | } | ||
505 | rinfo_get(rinfo); | ||
506 | remote_remove(avp, rinfo); | ||
507 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
508 | |||
509 | DBG(AVP_DBG_TRACE_TRPC_CONN, "%s: closing '%s' (%x)\n", __func__, | ||
510 | trpc_name(ep), rinfo->rem_id); | ||
511 | |||
512 | ret = _send_disconnect(avp, rinfo->rem_id); | ||
513 | if (ret) | ||
514 | pr_err("%s: error while closing remote port '%s' (%x)\n", | ||
515 | __func__, trpc_name(ep), rinfo->rem_id); | ||
516 | remote_close(rinfo); | ||
517 | rinfo_put(rinfo); | ||
518 | } | ||
519 | |||
520 | /* takes and holds avp->from_avp_lock */ | ||
521 | static void recv_msg_lock(struct tegra_avp_info *avp) | ||
522 | { | ||
523 | unsigned long flags; | ||
524 | |||
525 | mutex_lock(&avp->from_avp_lock); | ||
526 | spin_lock_irqsave(&avp->state_lock, flags); | ||
527 | avp->defer_remote = true; | ||
528 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
529 | } | ||
530 | |||
531 | /* MUST be called with avp->from_avp_lock held */ | ||
532 | static void recv_msg_unlock(struct tegra_avp_info *avp) | ||
533 | { | ||
534 | unsigned long flags; | ||
535 | |||
536 | spin_lock_irqsave(&avp->state_lock, flags); | ||
537 | avp->defer_remote = false; | ||
538 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
539 | mutex_unlock(&avp->from_avp_lock); | ||
540 | } | ||
541 | |||
542 | static int avp_node_try_connect(struct trpc_node *node, | ||
543 | struct trpc_node *src_node, | ||
544 | struct trpc_endpoint *from) | ||
545 | { | ||
546 | struct tegra_avp_info *avp = tegra_avp; | ||
547 | const char *port_name = trpc_name(from); | ||
548 | struct remote_info *rinfo; | ||
549 | struct msg_connect msg; | ||
550 | int ret; | ||
551 | unsigned long flags; | ||
552 | int len; | ||
553 | const int max_retry_cnt = 6; | ||
554 | int cnt = 0; | ||
555 | |||
556 | DBG(AVP_DBG_TRACE_TRPC_CONN, "%s: trying connect from %s\n", __func__, | ||
557 | port_name); | ||
558 | |||
559 | if (node != avp->rpc_node || node->priv != avp) | ||
560 | return -ENODEV; | ||
561 | |||
562 | len = strlen(port_name); | ||
563 | if (len > XPC_PORT_NAME_LEN) { | ||
564 | pr_err("%s: port name (%s) too long\n", __func__, port_name); | ||
565 | return -EINVAL; | ||
566 | } | ||
567 | |||
568 | ret = 0; | ||
569 | spin_lock_irqsave(&avp->state_lock, flags); | ||
570 | if (avp->suspending) { | ||
571 | ret = -EBUSY; | ||
572 | } else if (likely(src_node != avp->rpc_node)) { | ||
573 | /* only check for initialized when the source is not ourselves | ||
574 | * since we'll end up calling into here during initialization */ | ||
575 | if (!avp->initialized) | ||
576 | ret = -ENODEV; | ||
577 | } else if (strncmp(port_name, "RPC_AVP_PORT", XPC_PORT_NAME_LEN)) { | ||
578 | /* we only allow connections to ourselves for the cpu-to-avp | ||
579 | port */ | ||
580 | ret = -EINVAL; | ||
581 | } | ||
582 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
583 | if (ret) | ||
584 | return ret; | ||
585 | |||
586 | rinfo = rinfo_alloc(avp); | ||
587 | if (!rinfo) { | ||
588 | pr_err("%s: cannot alloc mem for rinfo\n", __func__); | ||
589 | ret = -ENOMEM; | ||
590 | goto err_alloc_rinfo; | ||
591 | } | ||
592 | rinfo->loc_id = (u32)rinfo; | ||
593 | |||
594 | msg.cmd = CMD_CONNECT; | ||
595 | msg.port_id = rinfo->loc_id; | ||
596 | memcpy(msg.name, port_name, len); | ||
597 | memset(msg.name + len, 0, XPC_PORT_NAME_LEN - len); | ||
598 | |||
599 | /* when trying to connect to remote, we need to block remote | ||
600 | * messages until we get our ack and can insert it into our lists. | ||
601 | * Otherwise, we can get a message from the other side for a port | ||
602 | * that we haven't finished setting up. | ||
603 | * | ||
604 | * 'defer_remote' will force the irq handler to not process messages | ||
605 | * at irq context but to schedule work to do so. The work function will | ||
606 | * take the from_avp_lock and everything should stay consistent. | ||
607 | */ | ||
608 | recv_msg_lock(avp); | ||
609 | for (cnt = 0; cnt < max_retry_cnt; cnt++) { | ||
610 | /* Retry to connect to AVP at this function maximum 6 times. | ||
611 | * Because this section is protected by mutex and | ||
612 | * needed to re-send the CMD_CONNECT command by CPU | ||
613 | * if AVP didn't receive the command. | ||
614 | */ | ||
615 | mutex_lock(&avp->to_avp_lock); | ||
616 | ret = msg_write(avp, &msg, sizeof(msg), NULL, 0); | ||
617 | if (ret) { | ||
618 | pr_err("%s: remote has not acked last message (%s)\n", | ||
619 | __func__, port_name); | ||
620 | mutex_unlock(&avp->to_avp_lock); | ||
621 | goto err_msg_write; | ||
622 | } | ||
623 | ret = msg_wait_ack_locked(avp, CMD_RESPONSE, &rinfo->rem_id); | ||
624 | mutex_unlock(&avp->to_avp_lock); | ||
625 | if (!ret && rinfo->rem_id) | ||
626 | break; | ||
627 | |||
628 | /* Skip the sleep function at last retry count */ | ||
629 | if ((cnt + 1) < max_retry_cnt) | ||
630 | usleep_range(100, 2000); | ||
631 | } | ||
632 | |||
633 | if (ret) { | ||
634 | pr_err("%s: remote end won't respond for '%s'\n", __func__, | ||
635 | port_name); | ||
636 | goto err_wait_ack; | ||
637 | } | ||
638 | if (!rinfo->rem_id) { | ||
639 | pr_err("%s: can't connect to '%s'\n", __func__, port_name); | ||
640 | ret = -ECONNREFUSED; | ||
641 | goto err_nack; | ||
642 | } | ||
643 | |||
644 | DBG(AVP_DBG_TRACE_TRPC_CONN, "%s: got conn ack '%s' (%x <-> %x)\n", | ||
645 | __func__, port_name, rinfo->loc_id, rinfo->rem_id); | ||
646 | |||
647 | rinfo->trpc_ep = trpc_create_peer(node, from, &remote_ep_ops, | ||
648 | rinfo); | ||
649 | if (!rinfo->trpc_ep) { | ||
650 | pr_err("%s: cannot create peer for %s\n", __func__, port_name); | ||
651 | ret = -EINVAL; | ||
652 | goto err_create_peer; | ||
653 | } | ||
654 | |||
655 | spin_lock_irqsave(&avp->state_lock, flags); | ||
656 | ret = remote_insert(avp, rinfo); | ||
657 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
658 | if (ret) | ||
659 | goto err_ep_insert; | ||
660 | |||
661 | recv_msg_unlock(avp); | ||
662 | return 0; | ||
663 | |||
664 | err_ep_insert: | ||
665 | trpc_close(rinfo->trpc_ep); | ||
666 | err_create_peer: | ||
667 | _send_disconnect(avp, rinfo->rem_id); | ||
668 | err_nack: | ||
669 | err_wait_ack: | ||
670 | err_msg_write: | ||
671 | recv_msg_unlock(avp); | ||
672 | rinfo_put(rinfo); | ||
673 | err_alloc_rinfo: | ||
674 | return ret; | ||
675 | } | ||
676 | |||
677 | static void process_disconnect_locked(struct tegra_avp_info *avp, | ||
678 | struct msg_data *raw_msg) | ||
679 | { | ||
680 | struct msg_disconnect *disconn_msg = (struct msg_disconnect *)raw_msg; | ||
681 | unsigned long flags; | ||
682 | struct remote_info *rinfo; | ||
683 | |||
684 | DBG(AVP_DBG_TRACE_XPC_CONN, "%s: got disconnect (%x)\n", __func__, | ||
685 | disconn_msg->port_id); | ||
686 | |||
687 | if (avp_debug_mask & AVP_DBG_TRACE_XPC_MSG) | ||
688 | print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, disconn_msg, | ||
689 | sizeof(struct msg_disconnect)); | ||
690 | |||
691 | spin_lock_irqsave(&avp->state_lock, flags); | ||
692 | rinfo = remote_find(avp, disconn_msg->port_id); | ||
693 | if (!rinfo) { | ||
694 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
695 | pr_warning("%s: got disconnect for unknown port %x\n", | ||
696 | __func__, disconn_msg->port_id); | ||
697 | goto ack; | ||
698 | } | ||
699 | rinfo_get(rinfo); | ||
700 | remote_remove(avp, rinfo); | ||
701 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
702 | |||
703 | remote_close(rinfo); | ||
704 | rinfo_put(rinfo); | ||
705 | ack: | ||
706 | msg_ack_remote(avp, CMD_ACK, 0); | ||
707 | } | ||
708 | |||
709 | static void process_connect_locked(struct tegra_avp_info *avp, | ||
710 | struct msg_data *raw_msg) | ||
711 | { | ||
712 | struct msg_connect *conn_msg = (struct msg_connect *)raw_msg; | ||
713 | struct trpc_endpoint *trpc_ep; | ||
714 | struct remote_info *rinfo; | ||
715 | char name[XPC_PORT_NAME_LEN + 1]; | ||
716 | int ret; | ||
717 | u32 local_port_id = 0; | ||
718 | unsigned long flags; | ||
719 | |||
720 | DBG(AVP_DBG_TRACE_XPC_CONN, "%s: got connect (%x)\n", __func__, | ||
721 | conn_msg->port_id); | ||
722 | if (avp_debug_mask & AVP_DBG_TRACE_XPC_MSG) | ||
723 | print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, | ||
724 | conn_msg, sizeof(struct msg_connect)); | ||
725 | |||
726 | rinfo = rinfo_alloc(avp); | ||
727 | if (!rinfo) { | ||
728 | pr_err("%s: cannot alloc mem for rinfo\n", __func__); | ||
729 | ret = -ENOMEM; | ||
730 | goto ack; | ||
731 | } | ||
732 | rinfo->loc_id = (u32)rinfo; | ||
733 | rinfo->rem_id = conn_msg->port_id; | ||
734 | |||
735 | memcpy(name, conn_msg->name, XPC_PORT_NAME_LEN); | ||
736 | name[XPC_PORT_NAME_LEN] = '\0'; | ||
737 | trpc_ep = trpc_create_connect(avp->rpc_node, name, &remote_ep_ops, | ||
738 | rinfo, 0); | ||
739 | if (IS_ERR(trpc_ep)) { | ||
740 | pr_err("%s: remote requested unknown port '%s' (%d)\n", | ||
741 | __func__, name, (int)PTR_ERR(trpc_ep)); | ||
742 | goto nack; | ||
743 | } | ||
744 | rinfo->trpc_ep = trpc_ep; | ||
745 | |||
746 | spin_lock_irqsave(&avp->state_lock, flags); | ||
747 | ret = remote_insert(avp, rinfo); | ||
748 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
749 | if (ret) | ||
750 | goto err_ep_insert; | ||
751 | |||
752 | local_port_id = rinfo->loc_id; | ||
753 | goto ack; | ||
754 | |||
755 | err_ep_insert: | ||
756 | trpc_close(trpc_ep); | ||
757 | nack: | ||
758 | rinfo_put(rinfo); | ||
759 | local_port_id = 0; | ||
760 | ack: | ||
761 | msg_ack_remote(avp, CMD_RESPONSE, local_port_id); | ||
762 | } | ||
763 | |||
764 | static int process_message(struct tegra_avp_info *avp, struct msg_data *raw_msg, | ||
765 | gfp_t gfp_flags) | ||
766 | { | ||
767 | struct msg_port_data *port_msg = (struct msg_port_data *)raw_msg; | ||
768 | struct remote_info *rinfo; | ||
769 | unsigned long flags; | ||
770 | int len; | ||
771 | int ret; | ||
772 | |||
773 | len = min(port_msg->msg_len, (u32)TEGRA_RPC_MAX_MSG_LEN); | ||
774 | |||
775 | if (avp_debug_mask & AVP_DBG_TRACE_XPC_MSG) { | ||
776 | pr_info("%s: got message cmd=%x port=%x len=%d\n", __func__, | ||
777 | port_msg->cmd, port_msg->port_id, port_msg->msg_len); | ||
778 | print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, port_msg, | ||
779 | sizeof(struct msg_port_data) + len); | ||
780 | } | ||
781 | |||
782 | if (len != port_msg->msg_len) | ||
783 | pr_err("%s: message sent is too long (%d bytes)\n", __func__, | ||
784 | port_msg->msg_len); | ||
785 | |||
786 | spin_lock_irqsave(&avp->state_lock, flags); | ||
787 | rinfo = remote_find(avp, port_msg->port_id); | ||
788 | if (rinfo) { | ||
789 | rinfo_get(rinfo); | ||
790 | trpc_get(rinfo->trpc_ep); | ||
791 | } else { | ||
792 | pr_err("%s: port %x not found\n", __func__, port_msg->port_id); | ||
793 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
794 | ret = -ENOENT; | ||
795 | goto ack; | ||
796 | } | ||
797 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
798 | |||
799 | ret = trpc_send_msg(avp->rpc_node, rinfo->trpc_ep, port_msg->data, | ||
800 | len, gfp_flags); | ||
801 | if (ret == -ENOMEM) { | ||
802 | trpc_put(rinfo->trpc_ep); | ||
803 | rinfo_put(rinfo); | ||
804 | goto no_ack; | ||
805 | } else if (ret) { | ||
806 | pr_err("%s: cannot queue message for port %s/%x (%d)\n", | ||
807 | __func__, trpc_name(rinfo->trpc_ep), rinfo->loc_id, | ||
808 | ret); | ||
809 | } else { | ||
810 | DBG(AVP_DBG_TRACE_XPC_MSG, "%s: msg queued\n", __func__); | ||
811 | } | ||
812 | |||
813 | trpc_put(rinfo->trpc_ep); | ||
814 | rinfo_put(rinfo); | ||
815 | ack: | ||
816 | msg_ack_remote(avp, CMD_ACK, 0); | ||
817 | no_ack: | ||
818 | return ret; | ||
819 | } | ||
820 | |||
821 | static void process_avp_message(struct work_struct *work) | ||
822 | { | ||
823 | struct tegra_avp_info *avp = container_of(work, struct tegra_avp_info, | ||
824 | recv_work); | ||
825 | struct msg_data *msg = avp->msg_from_avp; | ||
826 | |||
827 | mutex_lock(&avp->from_avp_lock); | ||
828 | rmb(); | ||
829 | switch (msg->cmd) { | ||
830 | case CMD_CONNECT: | ||
831 | process_connect_locked(avp, msg); | ||
832 | break; | ||
833 | case CMD_DISCONNECT: | ||
834 | process_disconnect_locked(avp, msg); | ||
835 | break; | ||
836 | case CMD_MESSAGE: | ||
837 | process_message(avp, msg, GFP_KERNEL); | ||
838 | break; | ||
839 | default: | ||
840 | pr_err("%s: unknown cmd (%x) received\n", __func__, msg->cmd); | ||
841 | break; | ||
842 | } | ||
843 | mutex_unlock(&avp->from_avp_lock); | ||
844 | } | ||
845 | |||
846 | static irqreturn_t avp_mbox_pending_isr(int irq, void *data) | ||
847 | { | ||
848 | struct tegra_avp_info *avp = data; | ||
849 | struct msg_data *msg = avp->msg_from_avp; | ||
850 | u32 mbox_msg; | ||
851 | unsigned long flags; | ||
852 | int ret; | ||
853 | |||
854 | mbox_msg = mbox_readl(MBOX_FROM_AVP); | ||
855 | mbox_writel(0, MBOX_FROM_AVP); | ||
856 | |||
857 | DBG(AVP_DBG_TRACE_XPC_IRQ, "%s: got msg %x\n", __func__, mbox_msg); | ||
858 | |||
859 | /* XXX: re-use previous message? */ | ||
860 | if (!(mbox_msg & MBOX_MSG_VALID)) { | ||
861 | WARN_ON(1); | ||
862 | goto done; | ||
863 | } | ||
864 | |||
865 | mbox_msg <<= 4; | ||
866 | if (mbox_msg == 0x2f00bad0UL) { | ||
867 | pr_info("%s: petting watchdog\n", __func__); | ||
868 | goto done; | ||
869 | } | ||
870 | |||
871 | spin_lock_irqsave(&avp->state_lock, flags); | ||
872 | if (avp->shutdown) { | ||
873 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
874 | goto done; | ||
875 | } else if (avp->defer_remote) { | ||
876 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
877 | goto defer; | ||
878 | } | ||
879 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
880 | |||
881 | rmb(); | ||
882 | if (msg->cmd == CMD_MESSAGE) { | ||
883 | ret = process_message(avp, msg, GFP_ATOMIC); | ||
884 | if (ret != -ENOMEM) | ||
885 | goto done; | ||
886 | pr_info("%s: deferring message (%d)\n", __func__, ret); | ||
887 | } | ||
888 | defer: | ||
889 | queue_work(avp->recv_wq, &avp->recv_work); | ||
890 | done: | ||
891 | return IRQ_HANDLED; | ||
892 | } | ||
893 | |||
894 | static int avp_reset(struct tegra_avp_info *avp, unsigned long reset_addr) | ||
895 | { | ||
896 | unsigned long stub_code_phys = virt_to_phys(_tegra_avp_boot_stub); | ||
897 | dma_addr_t stub_data_phys; | ||
898 | unsigned long timeout; | ||
899 | int ret = 0; | ||
900 | |||
901 | writel(FLOW_MODE_STOP, FLOW_CTRL_HALT_COP_EVENTS); | ||
902 | |||
903 | _tegra_avp_boot_stub_data.map_phys_addr = avp->kernel_phys; | ||
904 | _tegra_avp_boot_stub_data.jump_addr = reset_addr; | ||
905 | wmb(); | ||
906 | stub_data_phys = dma_map_single(NULL, &_tegra_avp_boot_stub_data, | ||
907 | sizeof(_tegra_avp_boot_stub_data), | ||
908 | DMA_TO_DEVICE); | ||
909 | |||
910 | writel(stub_code_phys, TEGRA_AVP_RESET_VECTOR_ADDR); | ||
911 | |||
912 | pr_debug("%s: TEGRA_AVP_RESET_VECTOR=%x\n", __func__, readl(TEGRA_AVP_RESET_VECTOR_ADDR)); | ||
913 | pr_info("%s: Resetting AVP: reset_addr=%lx\n", __func__, reset_addr); | ||
914 | |||
915 | tegra_periph_reset_assert(avp->cop_clk); | ||
916 | udelay(10); | ||
917 | tegra_periph_reset_deassert(avp->cop_clk); | ||
918 | |||
919 | writel(FLOW_MODE_NONE, FLOW_CTRL_HALT_COP_EVENTS); | ||
920 | |||
921 | /* the AVP firmware will reprogram its reset vector as the kernel | ||
922 | * starts, so a dead kernel can be detected by polling this value */ | ||
923 | timeout = jiffies + msecs_to_jiffies(2000); | ||
924 | while (time_before(jiffies, timeout)) { | ||
925 | pr_debug("%s: TEGRA_AVP_RESET_VECTOR=%x\n", __func__, readl(TEGRA_AVP_RESET_VECTOR_ADDR)); | ||
926 | if (readl(TEGRA_AVP_RESET_VECTOR_ADDR) != stub_code_phys) | ||
927 | break; | ||
928 | cpu_relax(); | ||
929 | } | ||
930 | if (readl(TEGRA_AVP_RESET_VECTOR_ADDR) == stub_code_phys) { | ||
931 | pr_err("%s: Timed out waiting for AVP kernel to start\n", __func__); | ||
932 | ret = -EINVAL; | ||
933 | } | ||
934 | pr_debug("%s: TEGRA_AVP_RESET_VECTOR=%x\n", __func__, readl(TEGRA_AVP_RESET_VECTOR_ADDR)); | ||
935 | WARN_ON(ret); | ||
936 | dma_unmap_single(NULL, stub_data_phys, | ||
937 | sizeof(_tegra_avp_boot_stub_data), | ||
938 | DMA_TO_DEVICE); | ||
939 | return ret; | ||
940 | } | ||
941 | |||
942 | static void avp_halt(struct tegra_avp_info *avp) | ||
943 | { | ||
944 | /* ensure the AVP is halted */ | ||
945 | writel(FLOW_MODE_STOP, FLOW_CTRL_HALT_COP_EVENTS); | ||
946 | tegra_periph_reset_assert(avp->cop_clk); | ||
947 | |||
948 | /* set up the initial memory areas and mailbox contents */ | ||
949 | *((u32 *)avp->msg_from_avp) = 0; | ||
950 | *((u32 *)avp->msg_to_avp) = 0xfeedf00d; | ||
951 | mbox_writel(0, MBOX_FROM_AVP); | ||
952 | mbox_writel(0, MBOX_TO_AVP); | ||
953 | } | ||
954 | |||
955 | /* Note: CPU_PORT server and AVP_PORT client are registered with the avp | ||
956 | * node, but are actually meant to be processed on our side (either | ||
957 | * by the svc thread for processing remote calls or by the client | ||
958 | * of the char dev for receiving replies for managing remote | ||
959 | * libraries/modules. */ | ||
960 | |||
961 | static int avp_init(struct tegra_avp_info *avp) | ||
962 | { | ||
963 | const struct firmware *avp_fw; | ||
964 | int ret; | ||
965 | struct trpc_endpoint *ep; | ||
966 | char fw_file[30]; | ||
967 | |||
968 | avp->nvmap_libs = nvmap_create_client(nvmap_dev, "avp_libs"); | ||
969 | if (IS_ERR_OR_NULL(avp->nvmap_libs)) { | ||
970 | pr_err("%s: cannot create libs nvmap client\n", __func__); | ||
971 | ret = PTR_ERR(avp->nvmap_libs); | ||
972 | goto err_nvmap_create_libs_client; | ||
973 | } | ||
974 | |||
975 | /* put the address of the shared mem area into the mailbox for AVP | ||
976 | * to read out when its kernel boots. */ | ||
977 | mbox_writel(avp->msg, MBOX_TO_AVP); | ||
978 | |||
979 | #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) /* Tegra2 with AVP MMU */ | ||
980 | /* paddr is any address returned from nvmap_pin */ | ||
981 | /* vaddr is AVP_KERNEL_VIRT_BASE */ | ||
982 | pr_info("%s: Using AVP MMU to relocate AVP kernel\n", __func__); | ||
983 | sprintf(fw_file, "nvrm_avp.bin"); | ||
984 | avp->reset_addr = AVP_KERNEL_VIRT_BASE; | ||
985 | #elif defined(CONFIG_TEGRA_AVP_KERNEL_ON_SMMU) /* Tegra3 with SMMU */ | ||
986 | /* paddr is any address behind SMMU */ | ||
987 | /* vaddr is TEGRA_SMMU_BASE */ | ||
988 | pr_info("%s: Using SMMU at %lx to load AVP kernel\n", | ||
989 | __func__, (unsigned long)avp->kernel_phys); | ||
990 | BUG_ON(avp->kernel_phys != 0xeff00000 | ||
991 | && avp->kernel_phys != 0x0ff00000); | ||
992 | sprintf(fw_file, "nvrm_avp_%08lx.bin", (unsigned long)avp->kernel_phys); | ||
993 | avp->reset_addr = avp->kernel_phys; | ||
994 | #else /* nvmem= carveout */ | ||
995 | /* paddr is found in nvmem= carveout */ | ||
996 | /* vaddr is same as paddr */ | ||
997 | /* Find nvmem carveout */ | ||
998 | if (!pfn_valid(__phys_to_pfn(0x8e000000))) { | ||
999 | avp->kernel_phys = 0x8e000000; | ||
1000 | } | ||
1001 | else if (!pfn_valid(__phys_to_pfn(0x9e000000))) { | ||
1002 | avp->kernel_phys = 0x9e000000; | ||
1003 | } | ||
1004 | else if (!pfn_valid(__phys_to_pfn(0xbe000000))) { | ||
1005 | avp->kernel_phys = 0xbe000000; | ||
1006 | } | ||
1007 | else { | ||
1008 | pr_err("Cannot find nvmem= carveout to load AVP kernel\n"); | ||
1009 | pr_err("Check kernel command line " | ||
1010 | "to see if nvmem= is defined\n"); | ||
1011 | BUG(); | ||
1012 | } | ||
1013 | pr_info("%s: Using nvmem= carveout at %lx to load AVP kernel\n", | ||
1014 | __func__, (unsigned long)avp->kernel_phys); | ||
1015 | sprintf(fw_file, "nvrm_avp_%08lx.bin", (unsigned long)avp->kernel_phys); | ||
1016 | avp->reset_addr = avp->kernel_phys; | ||
1017 | avp->kernel_data = ioremap(avp->kernel_phys, SZ_1M); | ||
1018 | #endif | ||
1019 | |||
1020 | ret = request_firmware(&avp_fw, fw_file, avp->misc_dev.this_device); | ||
1021 | if (ret) { | ||
1022 | pr_err("%s: Cannot read firmware '%s'\n", __func__, fw_file); | ||
1023 | goto err_req_fw; | ||
1024 | } | ||
1025 | pr_info("%s: Reading firmware from '%s' (%d bytes)\n", __func__, | ||
1026 | fw_file, avp_fw->size); | ||
1027 | |||
1028 | pr_info("%s: Loading AVP kernel at vaddr=%p paddr=%lx\n", | ||
1029 | __func__, avp->kernel_data, (unsigned long)avp->kernel_phys); | ||
1030 | memcpy(avp->kernel_data, avp_fw->data, avp_fw->size); | ||
1031 | memset(avp->kernel_data + avp_fw->size, 0, SZ_1M - avp_fw->size); | ||
1032 | |||
1033 | wmb(); | ||
1034 | release_firmware(avp_fw); | ||
1035 | |||
1036 | tegra_init_legacy_irq_cop(); | ||
1037 | |||
1038 | ret = avp_reset(avp, avp->reset_addr); | ||
1039 | if (ret) { | ||
1040 | pr_err("%s: cannot reset the AVP.. aborting..\n", __func__); | ||
1041 | goto err_reset; | ||
1042 | } | ||
1043 | |||
1044 | enable_irq(avp->mbox_from_avp_pend_irq); | ||
1045 | /* Initialize the avp_svc *first*. This creates RPC_CPU_PORT to be | ||
1046 | * ready for remote commands. Then, connect to the | ||
1047 | * remote RPC_AVP_PORT to be able to send library load/unload and | ||
1048 | * suspend commands to it */ | ||
1049 | ret = avp_svc_start(avp->avp_svc); | ||
1050 | if (ret) | ||
1051 | goto err_avp_svc_start; | ||
1052 | |||
1053 | ep = trpc_create_connect(avp->rpc_node, "RPC_AVP_PORT", NULL, | ||
1054 | NULL, -1); | ||
1055 | if (IS_ERR(ep)) { | ||
1056 | pr_err("%s: can't connect to RPC_AVP_PORT server\n", __func__); | ||
1057 | ret = PTR_ERR(ep); | ||
1058 | goto err_rpc_avp_port; | ||
1059 | } | ||
1060 | avp->avp_ep = ep; | ||
1061 | |||
1062 | avp->initialized = true; | ||
1063 | smp_wmb(); | ||
1064 | pr_info("%s: avp init done\n", __func__); | ||
1065 | return 0; | ||
1066 | |||
1067 | err_rpc_avp_port: | ||
1068 | avp_svc_stop(avp->avp_svc); | ||
1069 | err_avp_svc_start: | ||
1070 | disable_irq(avp->mbox_from_avp_pend_irq); | ||
1071 | err_reset: | ||
1072 | avp_halt(avp); | ||
1073 | err_req_fw: | ||
1074 | nvmap_client_put(avp->nvmap_libs); | ||
1075 | err_nvmap_create_libs_client: | ||
1076 | avp->nvmap_libs = NULL; | ||
1077 | return ret; | ||
1078 | } | ||
1079 | |||
1080 | static void avp_uninit(struct tegra_avp_info *avp) | ||
1081 | { | ||
1082 | unsigned long flags; | ||
1083 | struct rb_node *n; | ||
1084 | struct remote_info *rinfo; | ||
1085 | |||
1086 | spin_lock_irqsave(&avp->state_lock, flags); | ||
1087 | avp->initialized = false; | ||
1088 | avp->shutdown = true; | ||
1089 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
1090 | |||
1091 | disable_irq(avp->mbox_from_avp_pend_irq); | ||
1092 | cancel_work_sync(&avp->recv_work); | ||
1093 | |||
1094 | avp_halt(avp); | ||
1095 | |||
1096 | spin_lock_irqsave(&avp->state_lock, flags); | ||
1097 | while ((n = rb_first(&avp->endpoints)) != NULL) { | ||
1098 | rinfo = rb_entry(n, struct remote_info, rb_node); | ||
1099 | rinfo_get(rinfo); | ||
1100 | remote_remove(avp, rinfo); | ||
1101 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
1102 | |||
1103 | remote_close(rinfo); | ||
1104 | rinfo_put(rinfo); | ||
1105 | |||
1106 | spin_lock_irqsave(&avp->state_lock, flags); | ||
1107 | } | ||
1108 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
1109 | |||
1110 | avp_svc_stop(avp->avp_svc); | ||
1111 | |||
1112 | if (avp->avp_ep) { | ||
1113 | trpc_close(avp->avp_ep); | ||
1114 | avp->avp_ep = NULL; | ||
1115 | } | ||
1116 | |||
1117 | libs_cleanup(avp); | ||
1118 | |||
1119 | avp->shutdown = false; | ||
1120 | smp_wmb(); | ||
1121 | pr_info("%s: avp teardown done\n", __func__); | ||
1122 | } | ||
1123 | |||
1124 | /* returns the remote lib handle in lib->handle */ | ||
1125 | static int _load_lib(struct tegra_avp_info *avp, struct tegra_avp_lib *lib, | ||
1126 | bool from_user) | ||
1127 | { | ||
1128 | struct svc_lib_attach svc; | ||
1129 | struct svc_lib_attach_resp resp; | ||
1130 | const struct firmware *fw; | ||
1131 | void *args; | ||
1132 | struct nvmap_handle_ref *lib_handle; | ||
1133 | void *lib_data; | ||
1134 | phys_addr_t lib_phys; | ||
1135 | int ret; | ||
1136 | |||
1137 | DBG(AVP_DBG_TRACE_LIB, "avp_lib: loading library '%s'\n", lib->name); | ||
1138 | |||
1139 | args = kmalloc(lib->args_len, GFP_KERNEL); | ||
1140 | if (!args) { | ||
1141 | pr_err("avp_lib: can't alloc mem for args (%d)\n", | ||
1142 | lib->args_len); | ||
1143 | return -ENOMEM; | ||
1144 | } | ||
1145 | |||
1146 | if (!from_user) | ||
1147 | memcpy(args, lib->args, lib->args_len); | ||
1148 | else if (copy_from_user(args, lib->args, lib->args_len)) { | ||
1149 | pr_err("avp_lib: can't copy lib args\n"); | ||
1150 | ret = -EFAULT; | ||
1151 | goto err_cp_args; | ||
1152 | } | ||
1153 | |||
1154 | ret = request_firmware(&fw, lib->name, avp->misc_dev.this_device); | ||
1155 | if (ret) { | ||
1156 | pr_err("avp_lib: Cannot read firmware '%s'\n", lib->name); | ||
1157 | goto err_req_fw; | ||
1158 | } | ||
1159 | |||
1160 | lib_handle = nvmap_alloc(avp->nvmap_libs, fw->size, L1_CACHE_BYTES, | ||
1161 | NVMAP_HANDLE_UNCACHEABLE, 0); | ||
1162 | if (IS_ERR_OR_NULL(lib_handle)) { | ||
1163 | pr_err("avp_lib: can't nvmap alloc for lib '%s'\n", lib->name); | ||
1164 | ret = PTR_ERR(lib_handle); | ||
1165 | goto err_nvmap_alloc; | ||
1166 | } | ||
1167 | |||
1168 | lib_data = nvmap_mmap(lib_handle); | ||
1169 | if (!lib_data) { | ||
1170 | pr_err("avp_lib: can't nvmap map for lib '%s'\n", lib->name); | ||
1171 | ret = -ENOMEM; | ||
1172 | goto err_nvmap_mmap; | ||
1173 | } | ||
1174 | |||
1175 | lib_phys = nvmap_pin(avp->nvmap_libs, lib_handle); | ||
1176 | if (IS_ERR_VALUE(lib_phys)) { | ||
1177 | pr_err("avp_lib: can't nvmap pin for lib '%s'\n", lib->name); | ||
1178 | ret = lib_phys; | ||
1179 | goto err_nvmap_pin; | ||
1180 | } | ||
1181 | |||
1182 | memcpy(lib_data, fw->data, fw->size); | ||
1183 | |||
1184 | svc.svc_id = SVC_LIBRARY_ATTACH; | ||
1185 | svc.address = lib_phys; | ||
1186 | svc.args_len = lib->args_len; | ||
1187 | svc.lib_size = fw->size; | ||
1188 | svc.reason = lib->greedy ? AVP_LIB_REASON_ATTACH_GREEDY : | ||
1189 | AVP_LIB_REASON_ATTACH; | ||
1190 | memcpy(svc.args, args, lib->args_len); | ||
1191 | wmb(); | ||
1192 | |||
1193 | /* send message, wait for reply */ | ||
1194 | ret = trpc_send_msg(avp->rpc_node, avp->avp_ep, &svc, sizeof(svc), | ||
1195 | GFP_KERNEL); | ||
1196 | if (ret) | ||
1197 | goto err_send_msg; | ||
1198 | |||
1199 | ret = trpc_recv_msg(avp->rpc_node, avp->avp_ep, &resp, | ||
1200 | sizeof(resp), -1); | ||
1201 | if (ret != sizeof(resp)) { | ||
1202 | pr_err("avp_lib: Couldn't get lib load reply (%d)\n", ret); | ||
1203 | goto err_recv_msg; | ||
1204 | } else if (resp.err) { | ||
1205 | pr_err("avp_lib: got remote error (%d) while loading lib %s\n", | ||
1206 | resp.err, lib->name); | ||
1207 | ret = -EPROTO; | ||
1208 | goto err_recv_msg; | ||
1209 | } | ||
1210 | lib->handle = resp.lib_id; | ||
1211 | ret = 0; | ||
1212 | DBG(AVP_DBG_TRACE_LIB, | ||
1213 | "avp_lib: Successfully loaded library %s (lib_id=%x)\n", | ||
1214 | lib->name, resp.lib_id); | ||
1215 | |||
1216 | /* We free the memory here because by this point the AVP has already | ||
1217 | * requested memory for the library for all the sections since it does | ||
1218 | * it's own relocation and memory management. So, our allocations were | ||
1219 | * temporary to hand the library code over to the AVP. | ||
1220 | */ | ||
1221 | |||
1222 | err_recv_msg: | ||
1223 | err_send_msg: | ||
1224 | nvmap_unpin(avp->nvmap_libs, lib_handle); | ||
1225 | err_nvmap_pin: | ||
1226 | nvmap_munmap(lib_handle, lib_data); | ||
1227 | err_nvmap_mmap: | ||
1228 | nvmap_free(avp->nvmap_libs, lib_handle); | ||
1229 | err_nvmap_alloc: | ||
1230 | release_firmware(fw); | ||
1231 | err_req_fw: | ||
1232 | err_cp_args: | ||
1233 | kfree(args); | ||
1234 | return ret; | ||
1235 | } | ||
1236 | |||
1237 | static int send_unload_lib_msg(struct tegra_avp_info *avp, u32 handle, | ||
1238 | const char *name) | ||
1239 | { | ||
1240 | struct svc_lib_detach svc; | ||
1241 | struct svc_lib_detach_resp resp; | ||
1242 | int ret; | ||
1243 | |||
1244 | svc.svc_id = SVC_LIBRARY_DETACH; | ||
1245 | svc.reason = AVP_LIB_REASON_DETACH; | ||
1246 | svc.lib_id = handle; | ||
1247 | |||
1248 | ret = trpc_send_msg(avp->rpc_node, avp->avp_ep, &svc, sizeof(svc), | ||
1249 | GFP_KERNEL); | ||
1250 | if (ret) { | ||
1251 | pr_err("avp_lib: can't send unload message to avp for '%s'\n", | ||
1252 | name); | ||
1253 | goto err; | ||
1254 | } | ||
1255 | |||
1256 | /* Give it a few extra moments to unload. */ | ||
1257 | msleep(20); | ||
1258 | |||
1259 | ret = trpc_recv_msg(avp->rpc_node, avp->avp_ep, &resp, | ||
1260 | sizeof(resp), -1); | ||
1261 | if (ret != sizeof(resp)) { | ||
1262 | pr_err("avp_lib: Couldn't get unload reply for '%s' (%d)\n", | ||
1263 | name, ret); | ||
1264 | } else if (resp.err) { | ||
1265 | pr_err("avp_lib: remote error (%d) while unloading lib %s\n", | ||
1266 | resp.err, name); | ||
1267 | ret = -EPROTO; | ||
1268 | } else { | ||
1269 | pr_info("avp_lib: Successfully unloaded '%s'\n", | ||
1270 | name); | ||
1271 | ret = 0; | ||
1272 | } | ||
1273 | |||
1274 | err: | ||
1275 | return ret; | ||
1276 | } | ||
1277 | |||
1278 | static struct lib_item *_find_lib_locked(struct tegra_avp_info *avp, u32 handle) | ||
1279 | { | ||
1280 | struct lib_item *item; | ||
1281 | |||
1282 | list_for_each_entry(item, &avp->libs, list) { | ||
1283 | if (item->handle == handle) | ||
1284 | return item; | ||
1285 | } | ||
1286 | return NULL; | ||
1287 | } | ||
1288 | |||
1289 | static int _insert_lib_locked(struct tegra_avp_info *avp, u32 handle, | ||
1290 | char *name) | ||
1291 | { | ||
1292 | struct lib_item *item; | ||
1293 | |||
1294 | item = kzalloc(sizeof(struct lib_item), GFP_KERNEL); | ||
1295 | if (!item) | ||
1296 | return -ENOMEM; | ||
1297 | item->handle = handle; | ||
1298 | strlcpy(item->name, name, TEGRA_AVP_LIB_MAX_NAME); | ||
1299 | list_add_tail(&item->list, &avp->libs); | ||
1300 | return 0; | ||
1301 | } | ||
1302 | |||
1303 | static void _delete_lib_locked(struct tegra_avp_info *avp, | ||
1304 | struct lib_item *item) | ||
1305 | { | ||
1306 | list_del(&item->list); | ||
1307 | kfree(item); | ||
1308 | } | ||
1309 | |||
1310 | static int handle_load_lib_ioctl(struct tegra_avp_info *avp, unsigned long arg) | ||
1311 | { | ||
1312 | struct tegra_avp_lib lib; | ||
1313 | int ret; | ||
1314 | |||
1315 | pr_debug("%s: ioctl\n", __func__); | ||
1316 | if (copy_from_user(&lib, (void __user *)arg, sizeof(lib))) | ||
1317 | return -EFAULT; | ||
1318 | lib.name[TEGRA_AVP_LIB_MAX_NAME - 1] = '\0'; | ||
1319 | |||
1320 | if (lib.args_len > TEGRA_AVP_LIB_MAX_ARGS) { | ||
1321 | pr_err("%s: library args too long (%d)\n", __func__, | ||
1322 | lib.args_len); | ||
1323 | return -E2BIG; | ||
1324 | } | ||
1325 | |||
1326 | mutex_lock(&avp->libs_lock); | ||
1327 | ret = _load_lib(avp, &lib, true); | ||
1328 | if (ret) | ||
1329 | goto err_load_lib; | ||
1330 | |||
1331 | if (copy_to_user((void __user *)arg, &lib, sizeof(lib))) { | ||
1332 | /* TODO: probably need to free the library from remote | ||
1333 | * we just loaded */ | ||
1334 | ret = -EFAULT; | ||
1335 | goto err_copy_to_user; | ||
1336 | } | ||
1337 | ret = _insert_lib_locked(avp, lib.handle, lib.name); | ||
1338 | if (ret) { | ||
1339 | pr_err("%s: can't insert lib (%d)\n", __func__, ret); | ||
1340 | goto err_insert_lib; | ||
1341 | } | ||
1342 | |||
1343 | mutex_unlock(&avp->libs_lock); | ||
1344 | return 0; | ||
1345 | |||
1346 | err_insert_lib: | ||
1347 | err_copy_to_user: | ||
1348 | send_unload_lib_msg(avp, lib.handle, lib.name); | ||
1349 | err_load_lib: | ||
1350 | mutex_unlock(&avp->libs_lock); | ||
1351 | return ret; | ||
1352 | } | ||
1353 | |||
1354 | static void libs_cleanup(struct tegra_avp_info *avp) | ||
1355 | { | ||
1356 | struct lib_item *lib; | ||
1357 | struct lib_item *lib_tmp; | ||
1358 | |||
1359 | mutex_lock(&avp->libs_lock); | ||
1360 | list_for_each_entry_safe(lib, lib_tmp, &avp->libs, list) { | ||
1361 | _delete_lib_locked(avp, lib); | ||
1362 | } | ||
1363 | |||
1364 | nvmap_client_put(avp->nvmap_libs); | ||
1365 | avp->nvmap_libs = NULL; | ||
1366 | mutex_unlock(&avp->libs_lock); | ||
1367 | } | ||
1368 | |||
1369 | static long tegra_avp_ioctl(struct file *file, unsigned int cmd, | ||
1370 | unsigned long arg) | ||
1371 | { | ||
1372 | struct tegra_avp_info *avp = tegra_avp; | ||
1373 | int ret; | ||
1374 | |||
1375 | if (_IOC_TYPE(cmd) != TEGRA_AVP_IOCTL_MAGIC || | ||
1376 | _IOC_NR(cmd) < TEGRA_AVP_IOCTL_MIN_NR || | ||
1377 | _IOC_NR(cmd) > TEGRA_AVP_IOCTL_MAX_NR) | ||
1378 | return -ENOTTY; | ||
1379 | |||
1380 | switch (cmd) { | ||
1381 | case TEGRA_AVP_IOCTL_LOAD_LIB: | ||
1382 | ret = handle_load_lib_ioctl(avp, arg); | ||
1383 | break; | ||
1384 | case TEGRA_AVP_IOCTL_UNLOAD_LIB: | ||
1385 | ret = tegra_avp_unload_lib(avp, arg); | ||
1386 | break; | ||
1387 | default: | ||
1388 | pr_err("avp_lib: Unknown tegra_avp ioctl 0x%x\n", _IOC_NR(cmd)); | ||
1389 | ret = -ENOTTY; | ||
1390 | break; | ||
1391 | } | ||
1392 | return ret; | ||
1393 | } | ||
1394 | |||
1395 | int tegra_avp_open(struct tegra_avp_info **avp) | ||
1396 | { | ||
1397 | struct tegra_avp_info *new_avp = tegra_avp; | ||
1398 | int ret = 0; | ||
1399 | |||
1400 | pr_debug("%s: open\n", __func__); | ||
1401 | mutex_lock(&new_avp->open_lock); | ||
1402 | |||
1403 | if (!new_avp->refcount) | ||
1404 | ret = avp_init(new_avp); | ||
1405 | |||
1406 | if (ret < 0) { | ||
1407 | mutex_unlock(&new_avp->open_lock); | ||
1408 | new_avp = 0; | ||
1409 | goto out; | ||
1410 | } | ||
1411 | |||
1412 | new_avp->refcount++; | ||
1413 | |||
1414 | mutex_unlock(&new_avp->open_lock); | ||
1415 | out: | ||
1416 | *avp = new_avp; | ||
1417 | return ret; | ||
1418 | } | ||
1419 | |||
1420 | static int tegra_avp_open_fops(struct inode *inode, struct file *file) | ||
1421 | { | ||
1422 | struct tegra_avp_info *avp; | ||
1423 | |||
1424 | nonseekable_open(inode, file); | ||
1425 | return tegra_avp_open(&avp); | ||
1426 | } | ||
1427 | |||
1428 | int tegra_avp_release(struct tegra_avp_info *avp) | ||
1429 | { | ||
1430 | int ret = 0; | ||
1431 | |||
1432 | pr_debug("%s: close\n", __func__); | ||
1433 | mutex_lock(&avp->open_lock); | ||
1434 | if (!avp->refcount) { | ||
1435 | pr_err("%s: releasing while in invalid state\n", __func__); | ||
1436 | ret = -EINVAL; | ||
1437 | goto out; | ||
1438 | } | ||
1439 | if (avp->refcount > 0) | ||
1440 | avp->refcount--; | ||
1441 | if (!avp->refcount) | ||
1442 | avp_uninit(avp); | ||
1443 | |||
1444 | out: | ||
1445 | mutex_unlock(&avp->open_lock); | ||
1446 | return ret; | ||
1447 | } | ||
1448 | |||
1449 | static int tegra_avp_release_fops(struct inode *inode, struct file *file) | ||
1450 | { | ||
1451 | struct tegra_avp_info *avp = tegra_avp; | ||
1452 | return tegra_avp_release(avp); | ||
1453 | } | ||
1454 | |||
1455 | static int avp_enter_lp0(struct tegra_avp_info *avp) | ||
1456 | { | ||
1457 | volatile u32 *avp_suspend_done = avp->iram_backup_data | ||
1458 | + TEGRA_IRAM_SIZE - TEGRA_RESET_HANDLER_SIZE; | ||
1459 | struct svc_enter_lp0 svc; | ||
1460 | unsigned long endtime; | ||
1461 | int ret; | ||
1462 | |||
1463 | svc.svc_id = SVC_ENTER_LP0; | ||
1464 | svc.src_addr = (u32)TEGRA_IRAM_BASE + TEGRA_RESET_HANDLER_SIZE; | ||
1465 | svc.buf_addr = (u32)avp->iram_backup_phys; | ||
1466 | svc.buf_size = TEGRA_IRAM_SIZE - TEGRA_RESET_HANDLER_SIZE; | ||
1467 | |||
1468 | *avp_suspend_done = 0; | ||
1469 | wmb(); | ||
1470 | |||
1471 | ret = trpc_send_msg(avp->rpc_node, avp->avp_ep, &svc, sizeof(svc), | ||
1472 | GFP_KERNEL); | ||
1473 | if (ret) { | ||
1474 | pr_err("%s: cannot send AVP suspend message\n", __func__); | ||
1475 | return ret; | ||
1476 | } | ||
1477 | |||
1478 | endtime = jiffies + msecs_to_jiffies(1000); | ||
1479 | rmb(); | ||
1480 | while ((*avp_suspend_done == 0) && time_before(jiffies, endtime)) { | ||
1481 | udelay(10); | ||
1482 | rmb(); | ||
1483 | } | ||
1484 | |||
1485 | rmb(); | ||
1486 | if (*avp_suspend_done == 0) { | ||
1487 | pr_err("%s: AVP failed to suspend\n", __func__); | ||
1488 | ret = -ETIMEDOUT; | ||
1489 | goto err; | ||
1490 | } | ||
1491 | |||
1492 | return 0; | ||
1493 | |||
1494 | err: | ||
1495 | return ret; | ||
1496 | } | ||
1497 | |||
1498 | static int tegra_avp_suspend(struct platform_device *pdev, pm_message_t state) | ||
1499 | { | ||
1500 | struct tegra_avp_info *avp = tegra_avp; | ||
1501 | unsigned long flags; | ||
1502 | int ret; | ||
1503 | |||
1504 | pr_info("%s()+\n", __func__); | ||
1505 | spin_lock_irqsave(&avp->state_lock, flags); | ||
1506 | if (!avp->initialized) { | ||
1507 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
1508 | return 0; | ||
1509 | } | ||
1510 | avp->suspending = true; | ||
1511 | spin_unlock_irqrestore(&avp->state_lock, flags); | ||
1512 | |||
1513 | ret = avp_enter_lp0(avp); | ||
1514 | if (ret) | ||
1515 | goto err; | ||
1516 | |||
1517 | avp->resume_addr = readl(TEGRA_AVP_RESUME_ADDR); | ||
1518 | if (!avp->resume_addr) { | ||
1519 | pr_err("%s: AVP failed to set it's resume address\n", __func__); | ||
1520 | ret = -EINVAL; | ||
1521 | goto err; | ||
1522 | } | ||
1523 | |||
1524 | disable_irq(avp->mbox_from_avp_pend_irq); | ||
1525 | |||
1526 | pr_info("avp_suspend: resume_addr=%lx\n", avp->resume_addr); | ||
1527 | avp->resume_addr &= 0xfffffffeUL; | ||
1528 | pr_info("%s()-\n", __func__); | ||
1529 | |||
1530 | return 0; | ||
1531 | |||
1532 | err: | ||
1533 | /* TODO: we need to kill the AVP so that when we come back | ||
1534 | * it could be reinitialized.. We'd probably need to kill | ||
1535 | * the users of it so they don't have the wrong state. | ||
1536 | */ | ||
1537 | return ret; | ||
1538 | } | ||
1539 | |||
1540 | static int tegra_avp_resume(struct platform_device *pdev) | ||
1541 | { | ||
1542 | struct tegra_avp_info *avp = tegra_avp; | ||
1543 | int ret = 0; | ||
1544 | |||
1545 | pr_info("%s()+\n", __func__); | ||
1546 | smp_rmb(); | ||
1547 | if (!avp->initialized) | ||
1548 | goto out; | ||
1549 | |||
1550 | BUG_ON(!avp->resume_addr); | ||
1551 | |||
1552 | avp_reset(avp, avp->resume_addr); | ||
1553 | avp->resume_addr = 0; | ||
1554 | avp->suspending = false; | ||
1555 | smp_wmb(); | ||
1556 | enable_irq(avp->mbox_from_avp_pend_irq); | ||
1557 | |||
1558 | pr_info("%s()-\n", __func__); | ||
1559 | |||
1560 | out: | ||
1561 | return ret; | ||
1562 | } | ||
1563 | |||
1564 | static const struct file_operations tegra_avp_fops = { | ||
1565 | .owner = THIS_MODULE, | ||
1566 | .open = tegra_avp_open_fops, | ||
1567 | .release = tegra_avp_release_fops, | ||
1568 | .unlocked_ioctl = tegra_avp_ioctl, | ||
1569 | }; | ||
1570 | |||
1571 | static struct trpc_node avp_trpc_node = { | ||
1572 | .name = "avp-remote", | ||
1573 | .type = TRPC_NODE_REMOTE, | ||
1574 | .try_connect = avp_node_try_connect, | ||
1575 | }; | ||
1576 | |||
1577 | static int tegra_avp_probe(struct platform_device *pdev) | ||
1578 | { | ||
1579 | void *msg_area; | ||
1580 | struct tegra_avp_info *avp; | ||
1581 | int ret = 0; | ||
1582 | int irq; | ||
1583 | unsigned int heap_mask; | ||
1584 | |||
1585 | irq = platform_get_irq_byname(pdev, "mbox_from_avp_pending"); | ||
1586 | if (irq < 0) { | ||
1587 | pr_err("%s: invalid platform data\n", __func__); | ||
1588 | return -EINVAL; | ||
1589 | } | ||
1590 | |||
1591 | avp = kzalloc(sizeof(struct tegra_avp_info), GFP_KERNEL); | ||
1592 | if (!avp) { | ||
1593 | pr_err("%s: cannot allocate tegra_avp_info\n", __func__); | ||
1594 | return -ENOMEM; | ||
1595 | } | ||
1596 | |||
1597 | avp->nvmap_drv = nvmap_create_client(nvmap_dev, "avp_core"); | ||
1598 | if (IS_ERR_OR_NULL(avp->nvmap_drv)) { | ||
1599 | pr_err("%s: cannot create drv nvmap client\n", __func__); | ||
1600 | ret = PTR_ERR(avp->nvmap_drv); | ||
1601 | goto err_nvmap_create_drv_client; | ||
1602 | } | ||
1603 | |||
1604 | #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) /* Tegra2 with AVP MMU */ | ||
1605 | heap_mask = NVMAP_HEAP_CARVEOUT_GENERIC; | ||
1606 | #elif defined(CONFIG_TEGRA_AVP_KERNEL_ON_SMMU) /* Tegra3 with SMMU */ | ||
1607 | heap_mask = NVMAP_HEAP_IOVMM; | ||
1608 | #else /* nvmem= carveout */ | ||
1609 | heap_mask = 0; | ||
1610 | #endif | ||
1611 | |||
1612 | if (heap_mask == NVMAP_HEAP_IOVMM) { | ||
1613 | int i; | ||
1614 | /* Tegra3 A01 has different SMMU address in 0xe00000000- */ | ||
1615 | u32 iovmm_addr[] = {0x0ff00000, 0xeff00000}; | ||
1616 | |||
1617 | for (i = 0; i < ARRAY_SIZE(iovmm_addr); i++) { | ||
1618 | avp->kernel_handle = nvmap_alloc_iovm(avp->nvmap_drv, | ||
1619 | SZ_1M, L1_CACHE_BYTES, | ||
1620 | NVMAP_HANDLE_WRITE_COMBINE, | ||
1621 | iovmm_addr[i]); | ||
1622 | if (!IS_ERR_OR_NULL(avp->kernel_handle)) | ||
1623 | break; | ||
1624 | } | ||
1625 | if (IS_ERR_OR_NULL(avp->kernel_handle)) { | ||
1626 | pr_err("%s: cannot create handle\n", __func__); | ||
1627 | ret = PTR_ERR(avp->kernel_handle); | ||
1628 | goto err_nvmap_alloc; | ||
1629 | } | ||
1630 | |||
1631 | avp->kernel_data = nvmap_mmap(avp->kernel_handle); | ||
1632 | if (!avp->kernel_data) { | ||
1633 | pr_err("%s: cannot map kernel handle\n", __func__); | ||
1634 | ret = -ENOMEM; | ||
1635 | goto err_nvmap_mmap; | ||
1636 | } | ||
1637 | |||
1638 | avp->kernel_phys = | ||
1639 | nvmap_pin(avp->nvmap_drv, avp->kernel_handle); | ||
1640 | if (IS_ERR_VALUE(avp->kernel_phys)) { | ||
1641 | pr_err("%s: cannot pin kernel handle\n", __func__); | ||
1642 | ret = avp->kernel_phys; | ||
1643 | goto err_nvmap_pin; | ||
1644 | } | ||
1645 | |||
1646 | pr_info("%s: allocated IOVM at %lx for AVP kernel\n", | ||
1647 | __func__, (unsigned long)avp->kernel_phys); | ||
1648 | } | ||
1649 | |||
1650 | if (heap_mask == NVMAP_HEAP_CARVEOUT_GENERIC) { | ||
1651 | avp->kernel_handle = nvmap_alloc(avp->nvmap_drv, SZ_1M, SZ_1M, | ||
1652 | NVMAP_HANDLE_UNCACHEABLE, 0); | ||
1653 | if (IS_ERR_OR_NULL(avp->kernel_handle)) { | ||
1654 | pr_err("%s: cannot create handle\n", __func__); | ||
1655 | ret = PTR_ERR(avp->kernel_handle); | ||
1656 | goto err_nvmap_alloc; | ||
1657 | } | ||
1658 | |||
1659 | avp->kernel_data = nvmap_mmap(avp->kernel_handle); | ||
1660 | if (!avp->kernel_data) { | ||
1661 | pr_err("%s: cannot map kernel handle\n", __func__); | ||
1662 | ret = -ENOMEM; | ||
1663 | goto err_nvmap_mmap; | ||
1664 | } | ||
1665 | |||
1666 | avp->kernel_phys = nvmap_pin(avp->nvmap_drv, | ||
1667 | avp->kernel_handle); | ||
1668 | if (IS_ERR_VALUE(avp->kernel_phys)) { | ||
1669 | pr_err("%s: cannot pin kernel handle\n", __func__); | ||
1670 | ret = avp->kernel_phys; | ||
1671 | goto err_nvmap_pin; | ||
1672 | } | ||
1673 | |||
1674 | pr_info("%s: allocated carveout memory at %lx for AVP kernel\n", | ||
1675 | __func__, (unsigned long)avp->kernel_phys); | ||
1676 | } | ||
1677 | |||
1678 | /* allocate an extra 4 bytes at the end which AVP uses to signal to | ||
1679 | * us that it is done suspending. | ||
1680 | */ | ||
1681 | avp->iram_backup_handle = | ||
1682 | nvmap_alloc(avp->nvmap_drv, TEGRA_IRAM_SIZE + 4, | ||
1683 | L1_CACHE_BYTES, NVMAP_HANDLE_UNCACHEABLE, 0); | ||
1684 | if (IS_ERR_OR_NULL(avp->iram_backup_handle)) { | ||
1685 | pr_err("%s: cannot create handle for iram backup\n", __func__); | ||
1686 | ret = PTR_ERR(avp->iram_backup_handle); | ||
1687 | goto err_iram_nvmap_alloc; | ||
1688 | } | ||
1689 | avp->iram_backup_data = nvmap_mmap(avp->iram_backup_handle); | ||
1690 | if (!avp->iram_backup_data) { | ||
1691 | pr_err("%s: cannot map iram backup handle\n", __func__); | ||
1692 | ret = -ENOMEM; | ||
1693 | goto err_iram_nvmap_mmap; | ||
1694 | } | ||
1695 | avp->iram_backup_phys = nvmap_pin(avp->nvmap_drv, | ||
1696 | avp->iram_backup_handle); | ||
1697 | if (IS_ERR_VALUE(avp->iram_backup_phys)) { | ||
1698 | pr_err("%s: cannot pin iram backup handle\n", __func__); | ||
1699 | ret = avp->iram_backup_phys; | ||
1700 | goto err_iram_nvmap_pin; | ||
1701 | } | ||
1702 | |||
1703 | avp->mbox_from_avp_pend_irq = irq; | ||
1704 | avp->endpoints = RB_ROOT; | ||
1705 | spin_lock_init(&avp->state_lock); | ||
1706 | mutex_init(&avp->open_lock); | ||
1707 | mutex_init(&avp->to_avp_lock); | ||
1708 | mutex_init(&avp->from_avp_lock); | ||
1709 | INIT_WORK(&avp->recv_work, process_avp_message); | ||
1710 | |||
1711 | mutex_init(&avp->libs_lock); | ||
1712 | INIT_LIST_HEAD(&avp->libs); | ||
1713 | |||
1714 | avp->recv_wq = alloc_workqueue("avp-msg-recv", | ||
1715 | WQ_NON_REENTRANT | WQ_HIGHPRI, 1); | ||
1716 | if (!avp->recv_wq) { | ||
1717 | pr_err("%s: can't create recve workqueue\n", __func__); | ||
1718 | ret = -ENOMEM; | ||
1719 | goto err_create_wq; | ||
1720 | } | ||
1721 | |||
1722 | avp->cop_clk = clk_get(&pdev->dev, "cop"); | ||
1723 | if (IS_ERR_OR_NULL(avp->cop_clk)) { | ||
1724 | pr_err("%s: Couldn't get cop clock\n", TEGRA_AVP_NAME); | ||
1725 | ret = -ENOENT; | ||
1726 | goto err_get_cop_clk; | ||
1727 | } | ||
1728 | |||
1729 | msg_area = dma_alloc_coherent(&pdev->dev, AVP_MSG_AREA_SIZE * 2, | ||
1730 | &avp->msg_area_addr, GFP_KERNEL); | ||
1731 | if (!msg_area) { | ||
1732 | pr_err("%s: cannot allocate msg_area\n", __func__); | ||
1733 | ret = -ENOMEM; | ||
1734 | goto err_alloc_msg_area; | ||
1735 | } | ||
1736 | memset(msg_area, 0, AVP_MSG_AREA_SIZE * 2); | ||
1737 | avp->msg = ((avp->msg_area_addr >> 4) | | ||
1738 | MBOX_MSG_VALID | MBOX_MSG_PENDING_INT_EN); | ||
1739 | avp->msg_to_avp = msg_area; | ||
1740 | avp->msg_from_avp = msg_area + AVP_MSG_AREA_SIZE; | ||
1741 | |||
1742 | avp_halt(avp); | ||
1743 | |||
1744 | avp_trpc_node.priv = avp; | ||
1745 | ret = trpc_node_register(&avp_trpc_node); | ||
1746 | if (ret) { | ||
1747 | pr_err("%s: Can't register avp rpc node\n", __func__); | ||
1748 | goto err_node_reg; | ||
1749 | } | ||
1750 | avp->rpc_node = &avp_trpc_node; | ||
1751 | |||
1752 | avp->avp_svc = avp_svc_init(pdev, avp->rpc_node); | ||
1753 | if (IS_ERR_OR_NULL(avp->avp_svc)) { | ||
1754 | pr_err("%s: Cannot initialize avp_svc\n", __func__); | ||
1755 | ret = PTR_ERR(avp->avp_svc); | ||
1756 | goto err_avp_svc_init; | ||
1757 | } | ||
1758 | |||
1759 | avp->misc_dev.minor = MISC_DYNAMIC_MINOR; | ||
1760 | avp->misc_dev.name = "tegra_avp"; | ||
1761 | avp->misc_dev.fops = &tegra_avp_fops; | ||
1762 | |||
1763 | ret = misc_register(&avp->misc_dev); | ||
1764 | if (ret) { | ||
1765 | pr_err("%s: Unable to register misc device!\n", TEGRA_AVP_NAME); | ||
1766 | goto err_misc_reg; | ||
1767 | } | ||
1768 | |||
1769 | ret = request_irq(irq, avp_mbox_pending_isr, 0, TEGRA_AVP_NAME, avp); | ||
1770 | if (ret) { | ||
1771 | pr_err("%s: cannot register irq handler\n", __func__); | ||
1772 | goto err_req_irq_pend; | ||
1773 | } | ||
1774 | disable_irq(avp->mbox_from_avp_pend_irq); | ||
1775 | |||
1776 | tegra_avp = avp; | ||
1777 | |||
1778 | pr_info("%s: message area %lx/%lx\n", __func__, | ||
1779 | (unsigned long)avp->msg_area_addr, | ||
1780 | (unsigned long)avp->msg_area_addr + AVP_MSG_AREA_SIZE); | ||
1781 | |||
1782 | return 0; | ||
1783 | |||
1784 | err_req_irq_pend: | ||
1785 | misc_deregister(&avp->misc_dev); | ||
1786 | err_misc_reg: | ||
1787 | avp_svc_destroy(avp->avp_svc); | ||
1788 | err_avp_svc_init: | ||
1789 | trpc_node_unregister(avp->rpc_node); | ||
1790 | err_node_reg: | ||
1791 | dma_free_coherent(&pdev->dev, AVP_MSG_AREA_SIZE * 2, msg_area, | ||
1792 | avp->msg_area_addr); | ||
1793 | err_alloc_msg_area: | ||
1794 | clk_put(avp->cop_clk); | ||
1795 | err_get_cop_clk: | ||
1796 | destroy_workqueue(avp->recv_wq); | ||
1797 | err_create_wq: | ||
1798 | nvmap_unpin(avp->nvmap_drv, avp->iram_backup_handle); | ||
1799 | err_iram_nvmap_pin: | ||
1800 | nvmap_munmap(avp->iram_backup_handle, avp->iram_backup_data); | ||
1801 | err_iram_nvmap_mmap: | ||
1802 | nvmap_free(avp->nvmap_drv, avp->iram_backup_handle); | ||
1803 | err_iram_nvmap_alloc: | ||
1804 | nvmap_unpin(avp->nvmap_drv, avp->kernel_handle); | ||
1805 | err_nvmap_pin: | ||
1806 | nvmap_munmap(avp->kernel_handle, avp->kernel_data); | ||
1807 | err_nvmap_mmap: | ||
1808 | nvmap_free(avp->nvmap_drv, avp->kernel_handle); | ||
1809 | err_nvmap_alloc: | ||
1810 | nvmap_client_put(avp->nvmap_drv); | ||
1811 | err_nvmap_create_drv_client: | ||
1812 | kfree(avp); | ||
1813 | tegra_avp = NULL; | ||
1814 | return ret; | ||
1815 | } | ||
1816 | |||
1817 | static int tegra_avp_remove(struct platform_device *pdev) | ||
1818 | { | ||
1819 | struct tegra_avp_info *avp = tegra_avp; | ||
1820 | |||
1821 | if (!avp) | ||
1822 | return 0; | ||
1823 | |||
1824 | mutex_lock(&avp->open_lock); | ||
1825 | /* ensure that noone can open while we tear down */ | ||
1826 | if (avp->refcount) { | ||
1827 | mutex_unlock(&avp->open_lock); | ||
1828 | return -EBUSY; | ||
1829 | } | ||
1830 | mutex_unlock(&avp->open_lock); | ||
1831 | |||
1832 | misc_deregister(&avp->misc_dev); | ||
1833 | |||
1834 | avp_halt(avp); | ||
1835 | |||
1836 | avp_svc_destroy(avp->avp_svc); | ||
1837 | trpc_node_unregister(avp->rpc_node); | ||
1838 | dma_free_coherent(&pdev->dev, AVP_MSG_AREA_SIZE * 2, avp->msg_to_avp, | ||
1839 | avp->msg_area_addr); | ||
1840 | clk_put(avp->cop_clk); | ||
1841 | destroy_workqueue(avp->recv_wq); | ||
1842 | nvmap_unpin(avp->nvmap_drv, avp->iram_backup_handle); | ||
1843 | nvmap_munmap(avp->iram_backup_handle, avp->iram_backup_data); | ||
1844 | nvmap_free(avp->nvmap_drv, avp->iram_backup_handle); | ||
1845 | nvmap_unpin(avp->nvmap_drv, avp->kernel_handle); | ||
1846 | nvmap_munmap(avp->kernel_handle, avp->kernel_data); | ||
1847 | nvmap_free(avp->nvmap_drv, avp->kernel_handle); | ||
1848 | nvmap_client_put(avp->nvmap_drv); | ||
1849 | kfree(avp); | ||
1850 | tegra_avp = NULL; | ||
1851 | return 0; | ||
1852 | } | ||
1853 | |||
1854 | int tegra_avp_load_lib(struct tegra_avp_info *avp, struct tegra_avp_lib *lib) | ||
1855 | { | ||
1856 | int ret; | ||
1857 | |||
1858 | if (!avp) | ||
1859 | return -ENODEV; | ||
1860 | |||
1861 | if (!lib) | ||
1862 | return -EFAULT; | ||
1863 | |||
1864 | lib->name[TEGRA_AVP_LIB_MAX_NAME - 1] = '\0'; | ||
1865 | |||
1866 | if (lib->args_len > TEGRA_AVP_LIB_MAX_ARGS) { | ||
1867 | pr_err("%s: library args too long (%d)\n", __func__, | ||
1868 | lib->args_len); | ||
1869 | return -E2BIG; | ||
1870 | } | ||
1871 | |||
1872 | mutex_lock(&avp->libs_lock); | ||
1873 | ret = _load_lib(avp, lib, false); | ||
1874 | if (ret) | ||
1875 | goto err_load_lib; | ||
1876 | |||
1877 | ret = _insert_lib_locked(avp, lib->handle, lib->name); | ||
1878 | if (ret) { | ||
1879 | pr_err("%s: can't insert lib (%d)\n", __func__, ret); | ||
1880 | goto err_insert_lib; | ||
1881 | } | ||
1882 | |||
1883 | mutex_unlock(&avp->libs_lock); | ||
1884 | return 0; | ||
1885 | |||
1886 | err_insert_lib: | ||
1887 | ret = send_unload_lib_msg(avp, lib->handle, lib->name); | ||
1888 | if (!ret) | ||
1889 | DBG(AVP_DBG_TRACE_LIB, "avp_lib: unloaded '%s'\n", lib->name); | ||
1890 | else | ||
1891 | pr_err("avp_lib: can't unload lib '%s' (%d)\n", lib->name, ret); | ||
1892 | lib->handle = 0; | ||
1893 | err_load_lib: | ||
1894 | mutex_unlock(&avp->libs_lock); | ||
1895 | return ret; | ||
1896 | } | ||
1897 | |||
1898 | int tegra_avp_unload_lib(struct tegra_avp_info *avp, unsigned long handle) | ||
1899 | { | ||
1900 | struct lib_item *item; | ||
1901 | int ret; | ||
1902 | |||
1903 | if (!avp) | ||
1904 | return -ENODEV; | ||
1905 | |||
1906 | mutex_lock(&avp->libs_lock); | ||
1907 | item = _find_lib_locked(avp, handle); | ||
1908 | if (!item) { | ||
1909 | pr_err("avp_lib: avp lib with handle 0x%x not found\n", | ||
1910 | (u32)handle); | ||
1911 | ret = -ENOENT; | ||
1912 | goto err_find; | ||
1913 | } | ||
1914 | ret = send_unload_lib_msg(avp, item->handle, item->name); | ||
1915 | if (!ret) | ||
1916 | DBG(AVP_DBG_TRACE_LIB, "avp_lib: unloaded '%s'\n", item->name); | ||
1917 | else | ||
1918 | pr_err("avp_lib: can't unload lib '%s'/0x%x (%d)\n", item->name, | ||
1919 | item->handle, ret); | ||
1920 | _delete_lib_locked(avp, item); | ||
1921 | |||
1922 | err_find: | ||
1923 | mutex_unlock(&avp->libs_lock); | ||
1924 | return ret; | ||
1925 | } | ||
1926 | |||
1927 | static struct platform_driver tegra_avp_driver = { | ||
1928 | .probe = tegra_avp_probe, | ||
1929 | .remove = tegra_avp_remove, | ||
1930 | .suspend = tegra_avp_suspend, | ||
1931 | .resume = tegra_avp_resume, | ||
1932 | .driver = { | ||
1933 | .name = TEGRA_AVP_NAME, | ||
1934 | .owner = THIS_MODULE, | ||
1935 | }, | ||
1936 | }; | ||
1937 | |||
1938 | static int __init tegra_avp_init(void) | ||
1939 | { | ||
1940 | return platform_driver_register(&tegra_avp_driver); | ||
1941 | } | ||
1942 | |||
1943 | static void __exit tegra_avp_exit(void) | ||
1944 | { | ||
1945 | platform_driver_unregister(&tegra_avp_driver); | ||
1946 | } | ||
1947 | |||
1948 | module_init(tegra_avp_init); | ||
1949 | module_exit(tegra_avp_exit); | ||
diff --git a/drivers/media/video/tegra/avp/avp.h b/drivers/media/video/tegra/avp/avp.h new file mode 100644 index 00000000000..4f2287743a0 --- /dev/null +++ b/drivers/media/video/tegra/avp/avp.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Google, Inc. | ||
3 | * Author: Dima Zavin <dima@android.com> | ||
4 | * | ||
5 | * This software is licensed under the terms of the GNU General Public | ||
6 | * License version 2, as published by the Free Software Foundation, and | ||
7 | * may be copied, distributed, and modified under those terms. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #ifndef __MEDIA_VIDEO_TEGRA_AVP_H | ||
17 | #define __MEDIA_VIDEO_TEGRA_AVP_H | ||
18 | |||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/types.h> | ||
21 | |||
22 | #include "trpc.h" | ||
23 | |||
24 | struct avp_svc_info; | ||
25 | |||
26 | struct avp_svc_info *avp_svc_init(struct platform_device *pdev, | ||
27 | struct trpc_node *rpc_node); | ||
28 | void avp_svc_destroy(struct avp_svc_info *avp_svc); | ||
29 | int avp_svc_start(struct avp_svc_info *svc); | ||
30 | void avp_svc_stop(struct avp_svc_info *svc); | ||
31 | |||
32 | #endif | ||
diff --git a/drivers/media/video/tegra/avp/avp_msg.h b/drivers/media/video/tegra/avp/avp_msg.h new file mode 100644 index 00000000000..615d890d544 --- /dev/null +++ b/drivers/media/video/tegra/avp/avp_msg.h | |||
@@ -0,0 +1,358 @@ | |||
1 | /* drivers/media/video/tegra/avp/avp_msg.h | ||
2 | * | ||
3 | * Copyright (C) 2010 Google, Inc. | ||
4 | * Author: Dima Zavin <dima@android.com> | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #ifndef __MEDIA_VIDEO_TEGRA_AVP_MSG_H | ||
18 | #define __MEDIA_VIDEO_TEGRA_AVP_MSG_H | ||
19 | |||
20 | #include <linux/tegra_avp.h> | ||
21 | #include <linux/types.h> | ||
22 | |||
23 | /* Note: the port name string is not NUL terminated, so make sure to | ||
24 | * allocate appropriate space locally when operating on the string */ | ||
25 | #define XPC_PORT_NAME_LEN 16 | ||
26 | |||
27 | #define SVC_ARGS_MAX_LEN 220 | ||
28 | #define SVC_MAX_STRING_LEN 200 | ||
29 | |||
30 | #define AVP_ERR_ENOTSUP 0x2 | ||
31 | #define AVP_ERR_EINVAL 0x4 | ||
32 | #define AVP_ERR_ENOMEM 0x6 | ||
33 | #define AVP_ERR_EACCES 0x00030010 | ||
34 | |||
35 | enum { | ||
36 | SVC_NVMAP_CREATE = 0, | ||
37 | SVC_NVMAP_CREATE_RESPONSE = 1, | ||
38 | SVC_NVMAP_FREE = 3, | ||
39 | SVC_NVMAP_ALLOC = 4, | ||
40 | SVC_NVMAP_ALLOC_RESPONSE = 5, | ||
41 | SVC_NVMAP_PIN = 6, | ||
42 | SVC_NVMAP_PIN_RESPONSE = 7, | ||
43 | SVC_NVMAP_UNPIN = 8, | ||
44 | SVC_NVMAP_UNPIN_RESPONSE = 9, | ||
45 | SVC_NVMAP_GET_ADDRESS = 10, | ||
46 | SVC_NVMAP_GET_ADDRESS_RESPONSE = 11, | ||
47 | SVC_NVMAP_FROM_ID = 12, | ||
48 | SVC_NVMAP_FROM_ID_RESPONSE = 13, | ||
49 | SVC_MODULE_CLOCK = 14, | ||
50 | SVC_MODULE_CLOCK_RESPONSE = 15, | ||
51 | SVC_MODULE_RESET = 16, | ||
52 | SVC_MODULE_RESET_RESPONSE = 17, | ||
53 | SVC_POWER_REGISTER = 18, | ||
54 | SVC_POWER_UNREGISTER = 19, | ||
55 | SVC_POWER_STARVATION = 20, | ||
56 | SVC_POWER_BUSY_HINT = 21, | ||
57 | SVC_POWER_BUSY_HINT_MULTI = 22, | ||
58 | SVC_DFS_GETSTATE = 23, | ||
59 | SVC_DFS_GETSTATE_RESPONSE = 24, | ||
60 | SVC_POWER_RESPONSE = 25, | ||
61 | SVC_POWER_MAXFREQ = 26, | ||
62 | SVC_ENTER_LP0 = 27, | ||
63 | SVC_ENTER_LP0_RESPONSE = 28, | ||
64 | SVC_PRINTF = 29, | ||
65 | SVC_LIBRARY_ATTACH = 30, | ||
66 | SVC_LIBRARY_ATTACH_RESPONSE = 31, | ||
67 | SVC_LIBRARY_DETACH = 32, | ||
68 | SVC_LIBRARY_DETACH_RESPONSE = 33, | ||
69 | SVC_AVP_WDT_RESET = 34, | ||
70 | SVC_DFS_GET_CLK_UTIL = 35, | ||
71 | SVC_DFS_GET_CLK_UTIL_RESPONSE = 36, | ||
72 | SVC_MODULE_CLOCK_SET = 37, | ||
73 | SVC_MODULE_CLOCK_SET_RESPONSE = 38, | ||
74 | SVC_MODULE_CLOCK_GET = 39, | ||
75 | SVC_MODULE_CLOCK_GET_RESPONSE = 40, | ||
76 | }; | ||
77 | |||
78 | struct svc_msg { | ||
79 | u32 svc_id; | ||
80 | u8 data[0]; | ||
81 | }; | ||
82 | |||
83 | struct svc_common_resp { | ||
84 | u32 svc_id; | ||
85 | u32 err; | ||
86 | }; | ||
87 | |||
88 | struct svc_printf { | ||
89 | u32 svc_id; | ||
90 | const char str[SVC_MAX_STRING_LEN]; | ||
91 | }; | ||
92 | |||
93 | struct svc_enter_lp0 { | ||
94 | u32 svc_id; | ||
95 | u32 src_addr; | ||
96 | u32 buf_addr; | ||
97 | u32 buf_size; | ||
98 | }; | ||
99 | |||
100 | /* nvmap messages */ | ||
101 | struct svc_nvmap_create { | ||
102 | u32 svc_id; | ||
103 | u32 size; | ||
104 | }; | ||
105 | |||
106 | struct svc_nvmap_create_resp { | ||
107 | u32 svc_id; | ||
108 | u32 handle_id; | ||
109 | u32 err; | ||
110 | }; | ||
111 | |||
112 | enum { | ||
113 | AVP_NVMAP_HEAP_EXTERNAL = 1, | ||
114 | AVP_NVMAP_HEAP_GART = 2, | ||
115 | AVP_NVMAP_HEAP_EXTERNAL_CARVEOUT = 3, | ||
116 | AVP_NVMAP_HEAP_IRAM = 4, | ||
117 | }; | ||
118 | |||
119 | struct svc_nvmap_alloc { | ||
120 | u32 svc_id; | ||
121 | u32 handle_id; | ||
122 | u32 heaps[4]; | ||
123 | u32 num_heaps; | ||
124 | u32 align; | ||
125 | u32 mapping_type; | ||
126 | }; | ||
127 | |||
128 | struct svc_nvmap_free { | ||
129 | u32 svc_id; | ||
130 | u32 handle_id; | ||
131 | }; | ||
132 | |||
133 | struct svc_nvmap_pin { | ||
134 | u32 svc_id; | ||
135 | u32 handle_id; | ||
136 | }; | ||
137 | |||
138 | struct svc_nvmap_pin_resp { | ||
139 | u32 svc_id; | ||
140 | u32 addr; | ||
141 | }; | ||
142 | |||
143 | struct svc_nvmap_unpin { | ||
144 | u32 svc_id; | ||
145 | u32 handle_id; | ||
146 | }; | ||
147 | |||
148 | struct svc_nvmap_from_id { | ||
149 | u32 svc_id; | ||
150 | u32 handle_id; | ||
151 | }; | ||
152 | |||
153 | struct svc_nvmap_get_addr { | ||
154 | u32 svc_id; | ||
155 | u32 handle_id; | ||
156 | u32 offs; | ||
157 | }; | ||
158 | |||
159 | struct svc_nvmap_get_addr_resp { | ||
160 | u32 svc_id; | ||
161 | u32 addr; | ||
162 | }; | ||
163 | |||
164 | /* library management messages */ | ||
165 | enum { | ||
166 | AVP_LIB_REASON_ATTACH = 0, | ||
167 | AVP_LIB_REASON_DETACH = 1, | ||
168 | AVP_LIB_REASON_ATTACH_GREEDY = 2, | ||
169 | }; | ||
170 | |||
171 | struct svc_lib_attach { | ||
172 | u32 svc_id; | ||
173 | u32 address; | ||
174 | u32 args_len; | ||
175 | u32 lib_size; | ||
176 | u8 args[SVC_ARGS_MAX_LEN]; | ||
177 | u32 reason; | ||
178 | }; | ||
179 | |||
180 | struct svc_lib_attach_resp { | ||
181 | u32 svc_id; | ||
182 | u32 err; | ||
183 | u32 lib_id; | ||
184 | }; | ||
185 | |||
186 | struct svc_lib_detach { | ||
187 | u32 svc_id; | ||
188 | u32 reason; | ||
189 | u32 lib_id; | ||
190 | }; | ||
191 | |||
192 | struct svc_lib_detach_resp { | ||
193 | u32 svc_id; | ||
194 | u32 err; | ||
195 | }; | ||
196 | |||
197 | /* hw module management from the AVP side */ | ||
198 | enum { | ||
199 | AVP_MODULE_ID_AVP = 2, | ||
200 | AVP_MODULE_ID_VCP = 3, | ||
201 | AVP_MODULE_ID_BSEA = 27, | ||
202 | AVP_MODULE_ID_VDE = 28, | ||
203 | AVP_MODULE_ID_MPE = 29, | ||
204 | }; | ||
205 | |||
206 | struct svc_module_ctrl { | ||
207 | u32 svc_id; | ||
208 | u32 module_id; | ||
209 | u32 client_id; | ||
210 | u8 enable; | ||
211 | }; | ||
212 | |||
213 | struct svc_clock_ctrl { | ||
214 | u32 svc_id; | ||
215 | u32 module_id; | ||
216 | u32 clk_freq; | ||
217 | }; | ||
218 | |||
219 | struct svc_clock_ctrl_response { | ||
220 | u32 svc_id; | ||
221 | u32 err; | ||
222 | u32 act_freq; | ||
223 | }; | ||
224 | |||
225 | /* power messages */ | ||
226 | struct svc_pwr_register { | ||
227 | u32 svc_id; | ||
228 | u32 client_id; | ||
229 | u32 unused; | ||
230 | }; | ||
231 | |||
232 | struct svc_pwr_register_resp { | ||
233 | u32 svc_id; | ||
234 | u32 err; | ||
235 | u32 client_id; | ||
236 | }; | ||
237 | |||
238 | struct svc_pwr_starve_hint { | ||
239 | u32 svc_id; | ||
240 | u32 dfs_clk_id; | ||
241 | u32 client_id; | ||
242 | u8 starving; | ||
243 | }; | ||
244 | |||
245 | struct svc_pwr_busy_hint { | ||
246 | u32 svc_id; | ||
247 | u32 dfs_clk_id; | ||
248 | u32 client_id; | ||
249 | u32 boost_ms; /* duration */ | ||
250 | u32 boost_freq; /* in khz */ | ||
251 | }; | ||
252 | |||
253 | struct svc_pwr_max_freq { | ||
254 | u32 svc_id; | ||
255 | u32 module_id; | ||
256 | }; | ||
257 | |||
258 | struct svc_pwr_max_freq_resp { | ||
259 | u32 svc_id; | ||
260 | u32 freq; | ||
261 | }; | ||
262 | |||
263 | /* dfs related messages */ | ||
264 | enum { | ||
265 | AVP_DFS_STATE_INVALID = 0, | ||
266 | AVP_DFS_STATE_DISABLED = 1, | ||
267 | AVP_DFS_STATE_STOPPED = 2, | ||
268 | AVP_DFS_STATE_CLOSED_LOOP = 3, | ||
269 | AVP_DFS_STATE_PROFILED_LOOP = 4, | ||
270 | }; | ||
271 | |||
272 | struct svc_dfs_get_state_resp { | ||
273 | u32 svc_id; | ||
274 | u32 state; | ||
275 | }; | ||
276 | |||
277 | enum { | ||
278 | AVP_DFS_CLK_CPU = 1, | ||
279 | AVP_DFS_CLK_AVP = 2, | ||
280 | AVP_DFS_CLK_SYSTEM = 3, | ||
281 | AVP_DFS_CLK_AHB = 4, | ||
282 | AVP_DFS_CLK_APB = 5, | ||
283 | AVP_DFS_CLK_VDE = 6, | ||
284 | /* external memory controller */ | ||
285 | AVP_DFS_CLK_EMC = 7, | ||
286 | }; | ||
287 | |||
288 | struct avp_clk_usage { | ||
289 | u32 min; | ||
290 | u32 max; | ||
291 | u32 curr_min; | ||
292 | u32 curr_max; | ||
293 | u32 curr; | ||
294 | u32 avg; /* average activity.. whatever that means */ | ||
295 | }; | ||
296 | |||
297 | struct svc_dfs_get_clk_util { | ||
298 | u32 svc_id; | ||
299 | u32 dfs_clk_id; | ||
300 | }; | ||
301 | |||
302 | /* all units are in kHz */ | ||
303 | struct svc_dfs_get_clk_util_resp { | ||
304 | u32 svc_id; | ||
305 | u32 err; | ||
306 | struct avp_clk_usage usage; | ||
307 | }; | ||
308 | |||
309 | /************************/ | ||
310 | |||
311 | enum { | ||
312 | CMD_ACK = 0, | ||
313 | CMD_CONNECT = 2, | ||
314 | CMD_DISCONNECT = 3, | ||
315 | CMD_MESSAGE = 4, | ||
316 | CMD_RESPONSE = 5, | ||
317 | }; | ||
318 | |||
319 | struct msg_data { | ||
320 | u32 cmd; | ||
321 | u8 data[0]; | ||
322 | }; | ||
323 | |||
324 | struct msg_ack { | ||
325 | u32 cmd; | ||
326 | u32 arg; | ||
327 | }; | ||
328 | |||
329 | struct msg_connect { | ||
330 | u32 cmd; | ||
331 | u32 port_id; | ||
332 | /* not NUL terminated, just 0 padded */ | ||
333 | char name[XPC_PORT_NAME_LEN]; | ||
334 | }; | ||
335 | |||
336 | struct msg_connect_reply { | ||
337 | u32 cmd; | ||
338 | u32 port_id; | ||
339 | }; | ||
340 | |||
341 | struct msg_disconnect { | ||
342 | u32 cmd; | ||
343 | u32 port_id; | ||
344 | }; | ||
345 | |||
346 | struct msg_disconnect_reply { | ||
347 | u32 cmd; | ||
348 | u32 ack; | ||
349 | }; | ||
350 | |||
351 | struct msg_port_data { | ||
352 | u32 cmd; | ||
353 | u32 port_id; | ||
354 | u32 msg_len; | ||
355 | u8 data[0]; | ||
356 | }; | ||
357 | |||
358 | #endif | ||
diff --git a/drivers/media/video/tegra/avp/avp_svc.c b/drivers/media/video/tegra/avp/avp_svc.c new file mode 100644 index 00000000000..17c8b8535a6 --- /dev/null +++ b/drivers/media/video/tegra/avp/avp_svc.c | |||
@@ -0,0 +1,890 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Google, Inc. | ||
3 | * Author: Dima Zavin <dima@android.com> | ||
4 | * | ||
5 | * Copyright (C) 2010-2011 NVIDIA Corporation | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/clk.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/dma-mapping.h> | ||
21 | #include <linux/err.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <linux/kthread.h> | ||
24 | #include <linux/list.h> | ||
25 | #include <linux/mutex.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/tegra_rpc.h> | ||
28 | #include <linux/tegra_avp.h> | ||
29 | #include <linux/types.h> | ||
30 | |||
31 | #include <mach/clk.h> | ||
32 | #include <mach/nvmap.h> | ||
33 | |||
34 | #include "../../../../video/tegra/nvmap/nvmap.h" | ||
35 | |||
36 | #include "avp_msg.h" | ||
37 | #include "trpc.h" | ||
38 | #include "avp.h" | ||
39 | |||
40 | enum { | ||
41 | AVP_DBG_TRACE_SVC = 1U << 0, | ||
42 | }; | ||
43 | |||
44 | static u32 debug_mask; | ||
45 | module_param_named(debug_mask, debug_mask, uint, S_IWUSR | S_IRUGO); | ||
46 | |||
47 | #define DBG(flag, args...) \ | ||
48 | do { if (unlikely(debug_mask & (flag))) pr_info(args); } while (0) | ||
49 | |||
50 | enum { | ||
51 | CLK_REQUEST_VCP = 0, | ||
52 | CLK_REQUEST_BSEA = 1, | ||
53 | CLK_REQUEST_VDE = 2, | ||
54 | CLK_REQUEST_AVP = 3, | ||
55 | NUM_CLK_REQUESTS, | ||
56 | }; | ||
57 | |||
58 | struct avp_module { | ||
59 | const char *name; | ||
60 | u32 clk_req; | ||
61 | }; | ||
62 | |||
63 | static struct avp_module avp_modules[] = { | ||
64 | [AVP_MODULE_ID_AVP] = { | ||
65 | .name = "cop", | ||
66 | .clk_req = CLK_REQUEST_AVP, | ||
67 | }, | ||
68 | [AVP_MODULE_ID_VCP] = { | ||
69 | .name = "vcp", | ||
70 | .clk_req = CLK_REQUEST_VCP, | ||
71 | }, | ||
72 | [AVP_MODULE_ID_BSEA] = { | ||
73 | .name = "bsea", | ||
74 | .clk_req = CLK_REQUEST_BSEA, | ||
75 | }, | ||
76 | [AVP_MODULE_ID_VDE] = { | ||
77 | .name = "vde", | ||
78 | .clk_req = CLK_REQUEST_VDE, | ||
79 | }, | ||
80 | }; | ||
81 | #define NUM_AVP_MODULES ARRAY_SIZE(avp_modules) | ||
82 | |||
83 | struct avp_clk { | ||
84 | struct clk *clk; | ||
85 | int refcnt; | ||
86 | struct avp_module *mod; | ||
87 | }; | ||
88 | |||
89 | struct avp_svc_info { | ||
90 | struct avp_clk clks[NUM_CLK_REQUESTS]; | ||
91 | /* used for dvfs */ | ||
92 | struct clk *sclk; | ||
93 | struct clk *emcclk; | ||
94 | |||
95 | struct mutex clk_lock; | ||
96 | |||
97 | struct trpc_endpoint *cpu_ep; | ||
98 | struct task_struct *svc_thread; | ||
99 | |||
100 | /* client for remote allocations, for easy tear down */ | ||
101 | struct nvmap_client *nvmap_remote; | ||
102 | struct trpc_node *rpc_node; | ||
103 | unsigned long max_avp_rate; | ||
104 | unsigned long emc_rate; | ||
105 | |||
106 | /* variable to check if video is present */ | ||
107 | bool is_vde_on; | ||
108 | }; | ||
109 | |||
110 | static void do_svc_nvmap_create(struct avp_svc_info *avp_svc, | ||
111 | struct svc_msg *_msg, | ||
112 | size_t len) | ||
113 | { | ||
114 | struct svc_nvmap_create *msg = (struct svc_nvmap_create *)_msg; | ||
115 | struct svc_nvmap_create_resp resp; | ||
116 | struct nvmap_handle_ref *handle; | ||
117 | u32 handle_id = 0; | ||
118 | u32 err = 0; | ||
119 | |||
120 | handle = nvmap_create_handle(avp_svc->nvmap_remote, msg->size); | ||
121 | if (unlikely(IS_ERR(handle))) { | ||
122 | pr_err("avp_svc: error creating handle (%d bytes) for remote\n", | ||
123 | msg->size); | ||
124 | err = AVP_ERR_ENOMEM; | ||
125 | } else | ||
126 | handle_id = (u32)nvmap_ref_to_id(handle); | ||
127 | |||
128 | resp.svc_id = SVC_NVMAP_CREATE_RESPONSE; | ||
129 | resp.err = err; | ||
130 | resp.handle_id = handle_id; | ||
131 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
132 | sizeof(resp), GFP_KERNEL); | ||
133 | /* TODO: do we need to put the handle if send_msg failed? */ | ||
134 | } | ||
135 | |||
136 | static void do_svc_nvmap_alloc(struct avp_svc_info *avp_svc, | ||
137 | struct svc_msg *_msg, | ||
138 | size_t len) | ||
139 | { | ||
140 | struct svc_nvmap_alloc *msg = (struct svc_nvmap_alloc *)_msg; | ||
141 | struct svc_common_resp resp; | ||
142 | struct nvmap_handle *handle; | ||
143 | u32 err = 0; | ||
144 | u32 heap_mask = 0; | ||
145 | int i; | ||
146 | size_t align; | ||
147 | |||
148 | handle = nvmap_get_handle_id(avp_svc->nvmap_remote, msg->handle_id); | ||
149 | if (IS_ERR(handle)) { | ||
150 | pr_err("avp_svc: unknown remote handle 0x%x\n", msg->handle_id); | ||
151 | err = AVP_ERR_EACCES; | ||
152 | goto out; | ||
153 | } | ||
154 | |||
155 | if (msg->num_heaps > 4) { | ||
156 | pr_err("avp_svc: invalid remote alloc request (%d heaps?!)\n", | ||
157 | msg->num_heaps); | ||
158 | /* TODO: should we error out instead ? */ | ||
159 | msg->num_heaps = 0; | ||
160 | } | ||
161 | if (msg->num_heaps == 0) | ||
162 | heap_mask = NVMAP_HEAP_CARVEOUT_GENERIC | NVMAP_HEAP_SYSMEM; | ||
163 | |||
164 | for (i = 0; i < msg->num_heaps; i++) { | ||
165 | switch (msg->heaps[i]) { | ||
166 | case AVP_NVMAP_HEAP_EXTERNAL: | ||
167 | heap_mask |= NVMAP_HEAP_SYSMEM; | ||
168 | break; | ||
169 | case AVP_NVMAP_HEAP_GART: | ||
170 | heap_mask |= NVMAP_HEAP_IOVMM; | ||
171 | break; | ||
172 | case AVP_NVMAP_HEAP_EXTERNAL_CARVEOUT: | ||
173 | heap_mask |= NVMAP_HEAP_CARVEOUT_GENERIC; | ||
174 | break; | ||
175 | case AVP_NVMAP_HEAP_IRAM: | ||
176 | heap_mask |= NVMAP_HEAP_CARVEOUT_IRAM; | ||
177 | break; | ||
178 | default: | ||
179 | break; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | align = max_t(size_t, L1_CACHE_BYTES, msg->align); | ||
184 | err = nvmap_alloc_handle_id(avp_svc->nvmap_remote, msg->handle_id, | ||
185 | heap_mask, align, 0); | ||
186 | nvmap_handle_put(handle); | ||
187 | if (err) { | ||
188 | pr_err("avp_svc: can't allocate for handle 0x%x (%d)\n", | ||
189 | msg->handle_id, err); | ||
190 | err = AVP_ERR_ENOMEM; | ||
191 | } | ||
192 | |||
193 | out: | ||
194 | resp.svc_id = SVC_NVMAP_ALLOC_RESPONSE; | ||
195 | resp.err = err; | ||
196 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
197 | sizeof(resp), GFP_KERNEL); | ||
198 | } | ||
199 | |||
200 | static void do_svc_nvmap_free(struct avp_svc_info *avp_svc, | ||
201 | struct svc_msg *_msg, | ||
202 | size_t len) | ||
203 | { | ||
204 | struct svc_nvmap_free *msg = (struct svc_nvmap_free *)_msg; | ||
205 | |||
206 | nvmap_free_handle_id(avp_svc->nvmap_remote, msg->handle_id); | ||
207 | } | ||
208 | |||
209 | static void do_svc_nvmap_pin(struct avp_svc_info *avp_svc, | ||
210 | struct svc_msg *_msg, | ||
211 | size_t len) | ||
212 | { | ||
213 | struct svc_nvmap_pin *msg = (struct svc_nvmap_pin *)_msg; | ||
214 | struct svc_nvmap_pin_resp resp; | ||
215 | struct nvmap_handle_ref *handle; | ||
216 | phys_addr_t addr = ~0UL; | ||
217 | unsigned long id = msg->handle_id; | ||
218 | int err; | ||
219 | |||
220 | handle = nvmap_duplicate_handle_id(avp_svc->nvmap_remote, id); | ||
221 | if (IS_ERR(handle)) { | ||
222 | pr_err("avp_svc: can't dup handle %lx\n", id); | ||
223 | goto out; | ||
224 | } | ||
225 | err = nvmap_pin_ids(avp_svc->nvmap_remote, 1, &id); | ||
226 | if (err) { | ||
227 | pr_err("avp_svc: can't pin for handle %lx (%d)\n", id, err); | ||
228 | goto out; | ||
229 | } | ||
230 | addr = nvmap_handle_address(avp_svc->nvmap_remote, id); | ||
231 | |||
232 | out: | ||
233 | resp.svc_id = SVC_NVMAP_PIN_RESPONSE; | ||
234 | resp.addr = addr; | ||
235 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
236 | sizeof(resp), GFP_KERNEL); | ||
237 | } | ||
238 | |||
239 | static void do_svc_nvmap_unpin(struct avp_svc_info *avp_svc, | ||
240 | struct svc_msg *_msg, | ||
241 | size_t len) | ||
242 | { | ||
243 | struct svc_nvmap_unpin *msg = (struct svc_nvmap_unpin *)_msg; | ||
244 | struct svc_common_resp resp; | ||
245 | unsigned long id = msg->handle_id; | ||
246 | |||
247 | nvmap_unpin_ids(avp_svc->nvmap_remote, 1, &id); | ||
248 | nvmap_free_handle_id(avp_svc->nvmap_remote, id); | ||
249 | |||
250 | resp.svc_id = SVC_NVMAP_UNPIN_RESPONSE; | ||
251 | resp.err = 0; | ||
252 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
253 | sizeof(resp), GFP_KERNEL); | ||
254 | } | ||
255 | |||
256 | static void do_svc_nvmap_from_id(struct avp_svc_info *avp_svc, | ||
257 | struct svc_msg *_msg, | ||
258 | size_t len) | ||
259 | { | ||
260 | struct svc_nvmap_from_id *msg = (struct svc_nvmap_from_id *)_msg; | ||
261 | struct svc_common_resp resp; | ||
262 | struct nvmap_handle_ref *handle; | ||
263 | int err = 0; | ||
264 | |||
265 | handle = nvmap_duplicate_handle_id(avp_svc->nvmap_remote, | ||
266 | msg->handle_id); | ||
267 | if (IS_ERR(handle)) { | ||
268 | pr_err("avp_svc: can't duplicate handle for id 0x%x (%d)\n", | ||
269 | msg->handle_id, (int)PTR_ERR(handle)); | ||
270 | err = AVP_ERR_ENOMEM; | ||
271 | } | ||
272 | |||
273 | resp.svc_id = SVC_NVMAP_FROM_ID_RESPONSE; | ||
274 | resp.err = err; | ||
275 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
276 | sizeof(resp), GFP_KERNEL); | ||
277 | } | ||
278 | |||
279 | static void do_svc_nvmap_get_addr(struct avp_svc_info *avp_svc, | ||
280 | struct svc_msg *_msg, | ||
281 | size_t len) | ||
282 | { | ||
283 | struct svc_nvmap_get_addr *msg = (struct svc_nvmap_get_addr *)_msg; | ||
284 | struct svc_nvmap_get_addr_resp resp; | ||
285 | |||
286 | resp.svc_id = SVC_NVMAP_GET_ADDRESS_RESPONSE; | ||
287 | resp.addr = nvmap_handle_address(avp_svc->nvmap_remote, msg->handle_id); | ||
288 | resp.addr += msg->offs; | ||
289 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
290 | sizeof(resp), GFP_KERNEL); | ||
291 | } | ||
292 | |||
293 | static void do_svc_pwr_register(struct avp_svc_info *avp_svc, | ||
294 | struct svc_msg *_msg, | ||
295 | size_t len) | ||
296 | { | ||
297 | struct svc_pwr_register *msg = (struct svc_pwr_register *)_msg; | ||
298 | struct svc_pwr_register_resp resp; | ||
299 | |||
300 | resp.svc_id = SVC_POWER_RESPONSE; | ||
301 | resp.err = 0; | ||
302 | resp.client_id = msg->client_id; | ||
303 | |||
304 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
305 | sizeof(resp), GFP_KERNEL); | ||
306 | } | ||
307 | |||
308 | static struct avp_module *find_avp_module(struct avp_svc_info *avp_svc, u32 id) | ||
309 | { | ||
310 | if (id < NUM_AVP_MODULES && avp_modules[id].name) | ||
311 | return &avp_modules[id]; | ||
312 | return NULL; | ||
313 | } | ||
314 | |||
315 | static void do_svc_module_reset(struct avp_svc_info *avp_svc, | ||
316 | struct svc_msg *_msg, | ||
317 | size_t len) | ||
318 | { | ||
319 | struct svc_module_ctrl *msg = (struct svc_module_ctrl *)_msg; | ||
320 | struct svc_common_resp resp; | ||
321 | struct avp_module *mod; | ||
322 | struct avp_clk *aclk; | ||
323 | |||
324 | mod = find_avp_module(avp_svc, msg->module_id); | ||
325 | if (!mod) { | ||
326 | if (msg->module_id == AVP_MODULE_ID_AVP) | ||
327 | pr_err("avp_svc: AVP suicidal?!?!\n"); | ||
328 | else | ||
329 | pr_err("avp_svc: Unknown module reset requested: %d\n", | ||
330 | msg->module_id); | ||
331 | /* other side doesn't handle errors for reset */ | ||
332 | resp.err = 0; | ||
333 | goto send_response; | ||
334 | } | ||
335 | |||
336 | aclk = &avp_svc->clks[mod->clk_req]; | ||
337 | tegra_periph_reset_assert(aclk->clk); | ||
338 | udelay(10); | ||
339 | tegra_periph_reset_deassert(aclk->clk); | ||
340 | resp.err = 0; | ||
341 | |||
342 | send_response: | ||
343 | resp.svc_id = SVC_MODULE_RESET_RESPONSE; | ||
344 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
345 | sizeof(resp), GFP_KERNEL); | ||
346 | } | ||
347 | |||
348 | static void do_svc_module_clock(struct avp_svc_info *avp_svc, | ||
349 | struct svc_msg *_msg, | ||
350 | size_t len) | ||
351 | { | ||
352 | struct svc_module_ctrl *msg = (struct svc_module_ctrl *)_msg; | ||
353 | struct svc_common_resp resp; | ||
354 | struct avp_module *mod; | ||
355 | struct avp_clk *aclk; | ||
356 | unsigned long emc_rate = 0; | ||
357 | |||
358 | mod = find_avp_module(avp_svc, msg->module_id); | ||
359 | if (!mod) { | ||
360 | pr_err("avp_svc: unknown module clock requested: %d\n", | ||
361 | msg->module_id); | ||
362 | resp.err = AVP_ERR_EINVAL; | ||
363 | goto send_response; | ||
364 | } | ||
365 | |||
366 | if (msg->module_id == AVP_MODULE_ID_VDE) | ||
367 | avp_svc->is_vde_on = msg->enable; | ||
368 | |||
369 | if (avp_svc->is_vde_on == true) | ||
370 | emc_rate = ULONG_MAX; | ||
371 | |||
372 | mutex_lock(&avp_svc->clk_lock); | ||
373 | aclk = &avp_svc->clks[mod->clk_req]; | ||
374 | if (msg->enable) { | ||
375 | if (aclk->refcnt++ == 0) { | ||
376 | clk_set_rate(avp_svc->emcclk, emc_rate); | ||
377 | clk_enable(avp_svc->emcclk); | ||
378 | clk_enable(avp_svc->sclk); | ||
379 | clk_enable(aclk->clk); | ||
380 | } | ||
381 | } else { | ||
382 | if (unlikely(aclk->refcnt == 0)) { | ||
383 | pr_err("avp_svc: unbalanced clock disable for '%s'\n", | ||
384 | aclk->mod->name); | ||
385 | } else if (--aclk->refcnt == 0) { | ||
386 | clk_disable(aclk->clk); | ||
387 | clk_set_rate(avp_svc->sclk, 0); | ||
388 | clk_disable(avp_svc->sclk); | ||
389 | clk_set_rate(avp_svc->emcclk, 0); | ||
390 | clk_disable(avp_svc->emcclk); | ||
391 | } | ||
392 | } | ||
393 | mutex_unlock(&avp_svc->clk_lock); | ||
394 | resp.err = 0; | ||
395 | |||
396 | send_response: | ||
397 | resp.svc_id = SVC_MODULE_CLOCK_RESPONSE; | ||
398 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
399 | sizeof(resp), GFP_KERNEL); | ||
400 | } | ||
401 | |||
402 | static void do_svc_null_response(struct avp_svc_info *avp_svc, | ||
403 | struct svc_msg *_msg, | ||
404 | size_t len, u32 resp_svc_id) | ||
405 | { | ||
406 | struct svc_common_resp resp; | ||
407 | resp.svc_id = resp_svc_id; | ||
408 | resp.err = 0; | ||
409 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
410 | sizeof(resp), GFP_KERNEL); | ||
411 | } | ||
412 | |||
413 | static void do_svc_dfs_get_state(struct avp_svc_info *avp_svc, | ||
414 | struct svc_msg *_msg, | ||
415 | size_t len) | ||
416 | { | ||
417 | struct svc_dfs_get_state_resp resp; | ||
418 | resp.svc_id = SVC_DFS_GETSTATE_RESPONSE; | ||
419 | resp.state = AVP_DFS_STATE_STOPPED; | ||
420 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
421 | sizeof(resp), GFP_KERNEL); | ||
422 | } | ||
423 | |||
424 | static void do_svc_dfs_get_clk_util(struct avp_svc_info *avp_svc, | ||
425 | struct svc_msg *_msg, | ||
426 | size_t len) | ||
427 | { | ||
428 | struct svc_dfs_get_clk_util_resp resp; | ||
429 | |||
430 | resp.svc_id = SVC_DFS_GET_CLK_UTIL_RESPONSE; | ||
431 | resp.err = 0; | ||
432 | memset(&resp.usage, 0, sizeof(struct avp_clk_usage)); | ||
433 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
434 | sizeof(resp), GFP_KERNEL); | ||
435 | } | ||
436 | |||
437 | static void do_svc_pwr_max_freq(struct avp_svc_info *avp_svc, | ||
438 | struct svc_msg *_msg, | ||
439 | size_t len) | ||
440 | { | ||
441 | struct svc_pwr_max_freq_resp resp; | ||
442 | |||
443 | resp.svc_id = SVC_POWER_MAXFREQ; | ||
444 | resp.freq = 0; | ||
445 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
446 | sizeof(resp), GFP_KERNEL); | ||
447 | } | ||
448 | |||
449 | static void do_svc_printf(struct avp_svc_info *avp_svc, struct svc_msg *_msg, | ||
450 | size_t len) | ||
451 | { | ||
452 | struct svc_printf *msg = (struct svc_printf *)_msg; | ||
453 | char tmp_str[SVC_MAX_STRING_LEN]; | ||
454 | |||
455 | /* ensure we null terminate the source */ | ||
456 | strlcpy(tmp_str, msg->str, SVC_MAX_STRING_LEN); | ||
457 | pr_info("[AVP]: %s", tmp_str); | ||
458 | } | ||
459 | |||
460 | static void do_svc_module_clock_set(struct avp_svc_info *avp_svc, | ||
461 | struct svc_msg *_msg, | ||
462 | size_t len) | ||
463 | { | ||
464 | struct svc_clock_ctrl *msg = (struct svc_clock_ctrl *)_msg; | ||
465 | struct svc_clock_ctrl_response resp; | ||
466 | struct avp_module *mod; | ||
467 | struct avp_clk *aclk; | ||
468 | int ret = 0; | ||
469 | |||
470 | mod = find_avp_module(avp_svc, msg->module_id); | ||
471 | if (!mod) { | ||
472 | pr_err("avp_svc: unknown module clock requested: %d\n", | ||
473 | msg->module_id); | ||
474 | resp.err = AVP_ERR_EINVAL; | ||
475 | goto send_response; | ||
476 | } | ||
477 | |||
478 | mutex_lock(&avp_svc->clk_lock); | ||
479 | if (msg->module_id == AVP_MODULE_ID_AVP) { | ||
480 | /* check if max avp clock is asked and set max emc frequency */ | ||
481 | if (msg->clk_freq >= avp_svc->max_avp_rate) { | ||
482 | clk_set_rate(avp_svc->emcclk, ULONG_MAX); | ||
483 | } | ||
484 | else { | ||
485 | /* if no, set emc frequency as per platform data. | ||
486 | * if no platform data is send, set it to maximum */ | ||
487 | if (avp_svc->emc_rate) | ||
488 | clk_set_rate(avp_svc->emcclk, avp_svc->emc_rate); | ||
489 | else | ||
490 | clk_set_rate(avp_svc->emcclk, ULONG_MAX); | ||
491 | } | ||
492 | ret = clk_set_rate(avp_svc->sclk, msg->clk_freq); | ||
493 | } else { | ||
494 | aclk = &avp_svc->clks[mod->clk_req]; | ||
495 | ret = clk_set_rate(aclk->clk, msg->clk_freq); | ||
496 | } | ||
497 | if (ret) { | ||
498 | pr_err("avp_svc: Failed to set module (id = %d) frequency to %d Hz\n", | ||
499 | msg->module_id, msg->clk_freq); | ||
500 | resp.err = AVP_ERR_EINVAL; | ||
501 | resp.act_freq = 0; | ||
502 | mutex_unlock(&avp_svc->clk_lock); | ||
503 | goto send_response; | ||
504 | } | ||
505 | |||
506 | if (msg->module_id == AVP_MODULE_ID_AVP) | ||
507 | resp.act_freq = clk_get_rate(avp_svc->sclk); | ||
508 | else | ||
509 | resp.act_freq = clk_get_rate(aclk->clk); | ||
510 | |||
511 | mutex_unlock(&avp_svc->clk_lock); | ||
512 | resp.err = 0; | ||
513 | |||
514 | send_response: | ||
515 | resp.svc_id = SVC_MODULE_CLOCK_SET_RESPONSE; | ||
516 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
517 | sizeof(resp), GFP_KERNEL); | ||
518 | } | ||
519 | |||
520 | static void do_svc_unsupported_msg(struct avp_svc_info *avp_svc, | ||
521 | u32 resp_svc_id) | ||
522 | { | ||
523 | struct svc_common_resp resp; | ||
524 | |||
525 | resp.err = AVP_ERR_ENOTSUP; | ||
526 | resp.svc_id = resp_svc_id; | ||
527 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
528 | sizeof(resp), GFP_KERNEL); | ||
529 | } | ||
530 | |||
531 | static void do_svc_module_clock_get(struct avp_svc_info *avp_svc, | ||
532 | struct svc_msg *_msg, | ||
533 | size_t len) | ||
534 | { | ||
535 | struct svc_clock_ctrl *msg = (struct svc_clock_ctrl *)_msg; | ||
536 | struct svc_clock_ctrl_response resp; | ||
537 | struct avp_module *mod; | ||
538 | struct avp_clk *aclk; | ||
539 | |||
540 | mod = find_avp_module(avp_svc, msg->module_id); | ||
541 | if (!mod) { | ||
542 | pr_err("avp_svc: unknown module get clock requested: %d\n", | ||
543 | msg->module_id); | ||
544 | resp.err = AVP_ERR_EINVAL; | ||
545 | goto send_response; | ||
546 | } | ||
547 | |||
548 | mutex_lock(&avp_svc->clk_lock); | ||
549 | aclk = &avp_svc->clks[mod->clk_req]; | ||
550 | resp.act_freq = clk_get_rate(aclk->clk); | ||
551 | mutex_unlock(&avp_svc->clk_lock); | ||
552 | resp.err = 0; | ||
553 | |||
554 | send_response: | ||
555 | resp.svc_id = SVC_MODULE_CLOCK_GET_RESPONSE; | ||
556 | trpc_send_msg(avp_svc->rpc_node, avp_svc->cpu_ep, &resp, | ||
557 | sizeof(resp), GFP_KERNEL); | ||
558 | } | ||
559 | |||
560 | static int dispatch_svc_message(struct avp_svc_info *avp_svc, | ||
561 | struct svc_msg *msg, | ||
562 | size_t len) | ||
563 | { | ||
564 | int ret = 0; | ||
565 | |||
566 | switch (msg->svc_id) { | ||
567 | case SVC_NVMAP_CREATE: | ||
568 | DBG(AVP_DBG_TRACE_SVC, "%s: got nvmap_create\n", __func__); | ||
569 | do_svc_nvmap_create(avp_svc, msg, len); | ||
570 | break; | ||
571 | case SVC_NVMAP_ALLOC: | ||
572 | DBG(AVP_DBG_TRACE_SVC, "%s: got nvmap_alloc\n", __func__); | ||
573 | do_svc_nvmap_alloc(avp_svc, msg, len); | ||
574 | break; | ||
575 | case SVC_NVMAP_FREE: | ||
576 | DBG(AVP_DBG_TRACE_SVC, "%s: got nvmap_free\n", __func__); | ||
577 | do_svc_nvmap_free(avp_svc, msg, len); | ||
578 | break; | ||
579 | case SVC_NVMAP_PIN: | ||
580 | DBG(AVP_DBG_TRACE_SVC, "%s: got nvmap_pin\n", __func__); | ||
581 | do_svc_nvmap_pin(avp_svc, msg, len); | ||
582 | break; | ||
583 | case SVC_NVMAP_UNPIN: | ||
584 | DBG(AVP_DBG_TRACE_SVC, "%s: got nvmap_unpin\n", __func__); | ||
585 | do_svc_nvmap_unpin(avp_svc, msg, len); | ||
586 | break; | ||
587 | case SVC_NVMAP_FROM_ID: | ||
588 | DBG(AVP_DBG_TRACE_SVC, "%s: got nvmap_from_id\n", __func__); | ||
589 | do_svc_nvmap_from_id(avp_svc, msg, len); | ||
590 | break; | ||
591 | case SVC_NVMAP_GET_ADDRESS: | ||
592 | DBG(AVP_DBG_TRACE_SVC, "%s: got nvmap_get_addr\n", __func__); | ||
593 | do_svc_nvmap_get_addr(avp_svc, msg, len); | ||
594 | break; | ||
595 | case SVC_POWER_REGISTER: | ||
596 | DBG(AVP_DBG_TRACE_SVC, "%s: got power_register\n", __func__); | ||
597 | do_svc_pwr_register(avp_svc, msg, len); | ||
598 | break; | ||
599 | case SVC_POWER_UNREGISTER: | ||
600 | DBG(AVP_DBG_TRACE_SVC, "%s: got power_unregister\n", __func__); | ||
601 | /* nothing to do */ | ||
602 | break; | ||
603 | case SVC_POWER_BUSY_HINT_MULTI: | ||
604 | DBG(AVP_DBG_TRACE_SVC, "%s: got power_busy_hint_multi\n", | ||
605 | __func__); | ||
606 | /* nothing to do */ | ||
607 | break; | ||
608 | case SVC_POWER_BUSY_HINT: | ||
609 | case SVC_POWER_STARVATION: | ||
610 | DBG(AVP_DBG_TRACE_SVC, "%s: got power busy/starve hint\n", | ||
611 | __func__); | ||
612 | do_svc_null_response(avp_svc, msg, len, SVC_POWER_RESPONSE); | ||
613 | break; | ||
614 | case SVC_POWER_MAXFREQ: | ||
615 | DBG(AVP_DBG_TRACE_SVC, "%s: got power get_max_freq\n", | ||
616 | __func__); | ||
617 | do_svc_pwr_max_freq(avp_svc, msg, len); | ||
618 | break; | ||
619 | case SVC_DFS_GETSTATE: | ||
620 | DBG(AVP_DBG_TRACE_SVC, "%s: got dfs_get_state\n", __func__); | ||
621 | do_svc_dfs_get_state(avp_svc, msg, len); | ||
622 | break; | ||
623 | case SVC_MODULE_RESET: | ||
624 | DBG(AVP_DBG_TRACE_SVC, "%s: got module_reset\n", __func__); | ||
625 | do_svc_module_reset(avp_svc, msg, len); | ||
626 | break; | ||
627 | case SVC_MODULE_CLOCK: | ||
628 | DBG(AVP_DBG_TRACE_SVC, "%s: got module_clock\n", __func__); | ||
629 | do_svc_module_clock(avp_svc, msg, len); | ||
630 | break; | ||
631 | case SVC_DFS_GET_CLK_UTIL: | ||
632 | DBG(AVP_DBG_TRACE_SVC, "%s: got get_clk_util\n", __func__); | ||
633 | do_svc_dfs_get_clk_util(avp_svc, msg, len); | ||
634 | break; | ||
635 | case SVC_PRINTF: | ||
636 | DBG(AVP_DBG_TRACE_SVC, "%s: got remote printf\n", __func__); | ||
637 | do_svc_printf(avp_svc, msg, len); | ||
638 | break; | ||
639 | case SVC_AVP_WDT_RESET: | ||
640 | pr_err("avp_svc: AVP has been reset by watchdog\n"); | ||
641 | break; | ||
642 | case SVC_MODULE_CLOCK_SET: | ||
643 | DBG(AVP_DBG_TRACE_SVC, "%s: got module_clock_set\n", __func__); | ||
644 | do_svc_module_clock_set(avp_svc, msg, len); | ||
645 | break; | ||
646 | case SVC_MODULE_CLOCK_GET: | ||
647 | DBG(AVP_DBG_TRACE_SVC, "%s: got module_clock_get\n", __func__); | ||
648 | do_svc_module_clock_get(avp_svc, msg, len); | ||
649 | break; | ||
650 | default: | ||
651 | pr_warning("avp_svc: Unsupported SVC call 0x%x\n", msg->svc_id); | ||
652 | do_svc_unsupported_msg(avp_svc, msg->svc_id); | ||
653 | ret = -ENOMSG; | ||
654 | break; | ||
655 | } | ||
656 | |||
657 | return ret; | ||
658 | } | ||
659 | |||
660 | static int avp_svc_thread(void *data) | ||
661 | { | ||
662 | struct avp_svc_info *avp_svc = data; | ||
663 | u8 buf[TEGRA_RPC_MAX_MSG_LEN]; | ||
664 | struct svc_msg *msg = (struct svc_msg *)buf; | ||
665 | int ret; | ||
666 | long timeout; | ||
667 | |||
668 | BUG_ON(!avp_svc->cpu_ep); | ||
669 | |||
670 | ret = trpc_wait_peer(avp_svc->cpu_ep, -1); | ||
671 | if (ret) { | ||
672 | pr_err("%s: no connection from AVP (%d)\n", __func__, ret); | ||
673 | goto err; | ||
674 | } | ||
675 | |||
676 | pr_info("%s: got remote peer\n", __func__); | ||
677 | |||
678 | while (!kthread_should_stop()) { | ||
679 | DBG(AVP_DBG_TRACE_SVC, "%s: waiting for message\n", __func__); | ||
680 | ret = trpc_recv_msg(avp_svc->rpc_node, avp_svc->cpu_ep, buf, | ||
681 | TEGRA_RPC_MAX_MSG_LEN, -1); | ||
682 | DBG(AVP_DBG_TRACE_SVC, "%s: got message\n", __func__); | ||
683 | |||
684 | if (ret == -ECONNRESET || ret == -ENOTCONN) { | ||
685 | wait_queue_head_t wq; | ||
686 | init_waitqueue_head(&wq); | ||
687 | |||
688 | pr_info("%s: AVP seems to be down; " | ||
689 | "wait for kthread_stop\n", __func__); | ||
690 | timeout = msecs_to_jiffies(100); | ||
691 | timeout = wait_event_interruptible_timeout(wq, | ||
692 | kthread_should_stop(), timeout); | ||
693 | if (timeout == 0) | ||
694 | pr_err("%s: timed out while waiting for " | ||
695 | "kthread_stop\n", __func__); | ||
696 | continue; | ||
697 | } else if (ret <= 0) { | ||
698 | pr_err("%s: couldn't receive msg (ret=%d)\n", | ||
699 | __func__, ret); | ||
700 | continue; | ||
701 | } | ||
702 | dispatch_svc_message(avp_svc, msg, ret); | ||
703 | } | ||
704 | |||
705 | err: | ||
706 | trpc_put(avp_svc->cpu_ep); | ||
707 | pr_info("%s: exiting\n", __func__); | ||
708 | return ret; | ||
709 | } | ||
710 | |||
711 | int avp_svc_start(struct avp_svc_info *avp_svc) | ||
712 | { | ||
713 | struct trpc_endpoint *ep; | ||
714 | int ret; | ||
715 | |||
716 | avp_svc->nvmap_remote = nvmap_create_client(nvmap_dev, "avp_remote"); | ||
717 | if (IS_ERR(avp_svc->nvmap_remote)) { | ||
718 | pr_err("%s: cannot create remote nvmap client\n", __func__); | ||
719 | ret = PTR_ERR(avp_svc->nvmap_remote); | ||
720 | goto err_nvmap_create_remote_client; | ||
721 | } | ||
722 | |||
723 | ep = trpc_create(avp_svc->rpc_node, "RPC_CPU_PORT", NULL, NULL); | ||
724 | if (IS_ERR(ep)) { | ||
725 | pr_err("%s: can't create RPC_CPU_PORT\n", __func__); | ||
726 | ret = PTR_ERR(ep); | ||
727 | goto err_cpu_port_create; | ||
728 | } | ||
729 | |||
730 | /* TODO: protect this */ | ||
731 | avp_svc->cpu_ep = ep; | ||
732 | |||
733 | /* the service thread should get an extra reference for the port */ | ||
734 | trpc_get(avp_svc->cpu_ep); | ||
735 | avp_svc->svc_thread = kthread_run(avp_svc_thread, avp_svc, | ||
736 | "avp_svc_thread"); | ||
737 | if (IS_ERR_OR_NULL(avp_svc->svc_thread)) { | ||
738 | avp_svc->svc_thread = NULL; | ||
739 | pr_err("%s: can't create svc thread\n", __func__); | ||
740 | ret = -ENOMEM; | ||
741 | goto err_kthread; | ||
742 | } | ||
743 | return 0; | ||
744 | |||
745 | err_kthread: | ||
746 | trpc_close(avp_svc->cpu_ep); | ||
747 | trpc_put(avp_svc->cpu_ep); | ||
748 | avp_svc->cpu_ep = NULL; | ||
749 | err_cpu_port_create: | ||
750 | nvmap_client_put(avp_svc->nvmap_remote); | ||
751 | err_nvmap_create_remote_client: | ||
752 | avp_svc->nvmap_remote = NULL; | ||
753 | return ret; | ||
754 | } | ||
755 | |||
756 | void avp_svc_stop(struct avp_svc_info *avp_svc) | ||
757 | { | ||
758 | int ret; | ||
759 | int i; | ||
760 | |||
761 | trpc_close(avp_svc->cpu_ep); | ||
762 | ret = kthread_stop(avp_svc->svc_thread); | ||
763 | if (ret == -EINTR) { | ||
764 | /* the thread never started, drop it's extra reference */ | ||
765 | trpc_put(avp_svc->cpu_ep); | ||
766 | } | ||
767 | avp_svc->cpu_ep = NULL; | ||
768 | |||
769 | nvmap_client_put(avp_svc->nvmap_remote); | ||
770 | avp_svc->nvmap_remote = NULL; | ||
771 | |||
772 | mutex_lock(&avp_svc->clk_lock); | ||
773 | for (i = 0; i < NUM_CLK_REQUESTS; i++) { | ||
774 | struct avp_clk *aclk = &avp_svc->clks[i]; | ||
775 | BUG_ON(aclk->refcnt < 0); | ||
776 | if (aclk->refcnt > 0) { | ||
777 | pr_info("%s: remote left clock '%s' on\n", __func__, | ||
778 | aclk->mod->name); | ||
779 | clk_disable(aclk->clk); | ||
780 | /* sclk/emcclk was enabled once for every clock */ | ||
781 | clk_set_rate(avp_svc->sclk, 0); | ||
782 | clk_disable(avp_svc->sclk); | ||
783 | clk_set_rate(avp_svc->emcclk, 0); | ||
784 | clk_disable(avp_svc->emcclk); | ||
785 | } | ||
786 | aclk->refcnt = 0; | ||
787 | } | ||
788 | mutex_unlock(&avp_svc->clk_lock); | ||
789 | } | ||
790 | |||
791 | struct avp_svc_info *avp_svc_init(struct platform_device *pdev, | ||
792 | struct trpc_node *rpc_node) | ||
793 | { | ||
794 | struct tegra_avp_platform_data *pdata; | ||
795 | struct avp_svc_info *avp_svc; | ||
796 | int ret; | ||
797 | int i; | ||
798 | int cnt = 0; | ||
799 | |||
800 | BUG_ON(!rpc_node); | ||
801 | |||
802 | avp_svc = kzalloc(sizeof(struct avp_svc_info), GFP_KERNEL); | ||
803 | if (!avp_svc) { | ||
804 | ret = -ENOMEM; | ||
805 | goto err_alloc; | ||
806 | } | ||
807 | |||
808 | BUILD_BUG_ON(NUM_CLK_REQUESTS > BITS_PER_LONG); | ||
809 | |||
810 | pdata = pdev->dev.platform_data; | ||
811 | |||
812 | for (i = 0; i < NUM_AVP_MODULES; i++) { | ||
813 | struct avp_module *mod = &avp_modules[i]; | ||
814 | struct clk *clk; | ||
815 | if (!mod->name) | ||
816 | continue; | ||
817 | BUG_ON(mod->clk_req >= NUM_CLK_REQUESTS || | ||
818 | cnt++ >= NUM_CLK_REQUESTS); | ||
819 | |||
820 | clk = clk_get(&pdev->dev, mod->name); | ||
821 | if (IS_ERR(clk)) { | ||
822 | ret = PTR_ERR(clk); | ||
823 | pr_err("avp_svc: Couldn't get required clocks\n"); | ||
824 | goto err_get_clks; | ||
825 | } | ||
826 | avp_svc->clks[mod->clk_req].clk = clk; | ||
827 | avp_svc->clks[mod->clk_req].mod = mod; | ||
828 | avp_svc->clks[mod->clk_req].refcnt = 0; | ||
829 | } | ||
830 | |||
831 | avp_svc->sclk = clk_get(&pdev->dev, "sclk"); | ||
832 | if (IS_ERR(avp_svc->sclk)) { | ||
833 | pr_err("avp_svc: Couldn't get sclk for dvfs\n"); | ||
834 | ret = -ENOENT; | ||
835 | goto err_get_clks; | ||
836 | } | ||
837 | avp_svc->max_avp_rate = clk_round_rate(avp_svc->sclk, ULONG_MAX); | ||
838 | clk_set_rate(avp_svc->sclk, 0); | ||
839 | |||
840 | avp_svc->emcclk = clk_get(&pdev->dev, "emc"); | ||
841 | if (IS_ERR(avp_svc->emcclk)) { | ||
842 | pr_err("avp_svc: Couldn't get emcclk for dvfs\n"); | ||
843 | ret = -ENOENT; | ||
844 | goto err_get_clks; | ||
845 | } | ||
846 | |||
847 | /* | ||
848 | * The emc is a shared clock, it will be set to the rate | ||
849 | * requested in platform data. Set the rate to ULONG_MAX | ||
850 | * if platform data is NULL. | ||
851 | */ | ||
852 | avp_svc->emc_rate = 0; | ||
853 | if (pdata) { | ||
854 | clk_set_rate(avp_svc->emcclk, pdata->emc_clk_rate); | ||
855 | avp_svc->emc_rate = pdata->emc_clk_rate; | ||
856 | } | ||
857 | else { | ||
858 | clk_set_rate(avp_svc->emcclk, ULONG_MAX); | ||
859 | } | ||
860 | |||
861 | avp_svc->rpc_node = rpc_node; | ||
862 | |||
863 | mutex_init(&avp_svc->clk_lock); | ||
864 | |||
865 | return avp_svc; | ||
866 | |||
867 | err_get_clks: | ||
868 | for (i = 0; i < NUM_CLK_REQUESTS; i++) | ||
869 | if (avp_svc->clks[i].clk) | ||
870 | clk_put(avp_svc->clks[i].clk); | ||
871 | if (!IS_ERR_OR_NULL(avp_svc->sclk)) | ||
872 | clk_put(avp_svc->sclk); | ||
873 | if (!IS_ERR_OR_NULL(avp_svc->emcclk)) | ||
874 | clk_put(avp_svc->emcclk); | ||
875 | kfree(avp_svc); | ||
876 | err_alloc: | ||
877 | return ERR_PTR(ret); | ||
878 | } | ||
879 | |||
880 | void avp_svc_destroy(struct avp_svc_info *avp_svc) | ||
881 | { | ||
882 | int i; | ||
883 | |||
884 | for (i = 0; i < NUM_CLK_REQUESTS; i++) | ||
885 | clk_put(avp_svc->clks[i].clk); | ||
886 | clk_put(avp_svc->sclk); | ||
887 | clk_put(avp_svc->emcclk); | ||
888 | |||
889 | kfree(avp_svc); | ||
890 | } | ||
diff --git a/drivers/media/video/tegra/avp/headavp.S b/drivers/media/video/tegra/avp/headavp.S new file mode 100644 index 00000000000..c1f8e9fea1c --- /dev/null +++ b/drivers/media/video/tegra/avp/headavp.S | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-tegra/headavp.S | ||
3 | * | ||
4 | * AVP kernel launcher stub; programs the AVP MMU and jumps to the | ||
5 | * kernel code. Must use ONLY ARMv4 instructions, and must be compiled | ||
6 | * in ARM mode. | ||
7 | * | ||
8 | * Copyright (c) 2010, NVIDIA Corporation. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
18 | * more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License along | ||
21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
22 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
23 | */ | ||
24 | |||
25 | #include <linux/linkage.h> | ||
26 | #include <asm/assembler.h> | ||
27 | #include "headavp.h" | ||
28 | |||
29 | #define PTE0_COMPARE 0 | ||
30 | /* the default translation will translate any VA within | ||
31 | * 0x0010:0000..0x001f:ffff to the (megabyte-aligned) value written to | ||
32 | * _tegra_avp_boot_stub_data.map_phys_addr | ||
33 | */ | ||
34 | #define PTE0_DEFAULT (AVP_KERNEL_VIRT_BASE | 0x3ff0) | ||
35 | |||
36 | #define PTE0_TRANSLATE 4 | ||
37 | |||
38 | #define TRANSLATE_DATA (1 << 11) | ||
39 | #define TRANSLATE_CODE (1 << 10) | ||
40 | #define TRANSLATE_WR (1 << 9) | ||
41 | #define TRANSLATE_RD (1 << 8) | ||
42 | #define TRANSLATE_HIT (1 << 7) | ||
43 | #define TRANSLATE_EN (1 << 2) | ||
44 | |||
45 | #define TRANSLATE_OPT (TRANSLATE_DATA | TRANSLATE_CODE | TRANSLATE_WR | \ | ||
46 | TRANSLATE_RD | TRANSLATE_HIT) | ||
47 | |||
48 | ENTRY(_tegra_avp_boot_stub) | ||
49 | adr r4, _tegra_avp_boot_stub_data | ||
50 | ldmia r4, {r0-r3} | ||
51 | #ifdef CONFIG_TEGRA_AVP_KERNEL_ON_MMU | ||
52 | str r2, [r0, #PTE0_COMPARE] | ||
53 | bic r3, r3, #0xff0 | ||
54 | bic r3, r3, #0x00f | ||
55 | orr r3, r3, #TRANSLATE_OPT | ||
56 | orr r3, r3, #TRANSLATE_EN | ||
57 | str r3, [r0, #PTE0_TRANSLATE] | ||
58 | #endif | ||
59 | bx r1 | ||
60 | b . | ||
61 | ENDPROC(_tegra_avp_boot_stub) | ||
62 | .type _tegra_avp_boot_stub_data, %object | ||
63 | ENTRY(_tegra_avp_boot_stub_data) | ||
64 | .long AVP_MMU_TLB_BASE | ||
65 | .long 0xdeadbeef | ||
66 | .long PTE0_DEFAULT | ||
67 | .long 0xdeadd00d | ||
68 | .size _tegra_avp_boot_stub_data, . - _tegra_avp_boot_stub_data | ||
diff --git a/drivers/media/video/tegra/avp/headavp.h b/drivers/media/video/tegra/avp/headavp.h new file mode 100644 index 00000000000..2bcc3297bfa --- /dev/null +++ b/drivers/media/video/tegra/avp/headavp.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-tegra/headavp.h | ||
3 | * | ||
4 | * Copyright (c) 2010, NVIDIA Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
14 | * more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef _MACH_TEGRA_HEADAVP_H | ||
22 | #define _MACH_TEGRA_HEADAVP_H | ||
23 | |||
24 | #define AVP_MMU_TLB_BASE 0xF000F000 | ||
25 | #define AVP_KERNEL_VIRT_BASE 0x00100000 | ||
26 | |||
27 | #ifndef __ASSEMBLY__ | ||
28 | |||
29 | struct tegra_avp_boot_stub_data { | ||
30 | unsigned long mmu_tlb_base; | ||
31 | unsigned long jump_addr; | ||
32 | unsigned long map_virt_addr; | ||
33 | unsigned long map_phys_addr; | ||
34 | }; | ||
35 | |||
36 | extern void _tegra_avp_boot_stub(void); | ||
37 | extern struct tegra_avp_boot_stub_data _tegra_avp_boot_stub_data; | ||
38 | |||
39 | #endif | ||
40 | |||
41 | #endif | ||
diff --git a/drivers/media/video/tegra/avp/nvavp.h b/drivers/media/video/tegra/avp/nvavp.h new file mode 100644 index 00000000000..dbc62b48588 --- /dev/null +++ b/drivers/media/video/tegra/avp/nvavp.h | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Nvidia Corp | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #ifndef __MEDIA_VIDEO_TEGRA_NVAVP_H | ||
16 | #define __MEDIA_VIDEO_TEGRA_NVAVP_H | ||
17 | |||
18 | #include <linux/tegra_avp.h> | ||
19 | |||
20 | struct tegra_avp_info; | ||
21 | |||
22 | int tegra_avp_open(struct tegra_avp_info **avp); | ||
23 | int tegra_avp_release(struct tegra_avp_info *avp); | ||
24 | int tegra_avp_load_lib(struct tegra_avp_info *avp, struct tegra_avp_lib *lib); | ||
25 | int tegra_avp_unload_lib(struct tegra_avp_info *avp, unsigned long handle); | ||
26 | |||
27 | |||
28 | #include <linux/tegra_sema.h> | ||
29 | |||
30 | struct tegra_sema_info; | ||
31 | |||
32 | int tegra_sema_open(struct tegra_sema_info **sema); | ||
33 | int tegra_sema_release(struct tegra_sema_info *sema); | ||
34 | int tegra_sema_wait(struct tegra_sema_info *sema, long* timeout); | ||
35 | int tegra_sema_signal(struct tegra_sema_info *sema); | ||
36 | |||
37 | |||
38 | #include <linux/tegra_rpc.h> | ||
39 | |||
40 | struct tegra_rpc_info; | ||
41 | |||
42 | int tegra_rpc_open(struct tegra_rpc_info **rpc); | ||
43 | int tegra_rpc_release(struct tegra_rpc_info *rpc); | ||
44 | int tegra_rpc_port_create(struct tegra_rpc_info *rpc, char *name, | ||
45 | struct tegra_sema_info *sema); | ||
46 | int tegra_rpc_get_name(struct tegra_rpc_info *rpc, char* name); | ||
47 | int tegra_rpc_port_connect(struct tegra_rpc_info *rpc, long timeout); | ||
48 | int tegra_rpc_port_listen(struct tegra_rpc_info *rpc, long timeout); | ||
49 | int tegra_rpc_write(struct tegra_rpc_info *rpc, u8* buf, size_t size); | ||
50 | int tegra_rpc_read(struct tegra_rpc_info *rpc, u8 *buf, size_t max); | ||
51 | |||
52 | |||
53 | #endif | ||
diff --git a/drivers/media/video/tegra/avp/tegra_rpc.c b/drivers/media/video/tegra/avp/tegra_rpc.c new file mode 100644 index 00000000000..a0fd1dc999f --- /dev/null +++ b/drivers/media/video/tegra/avp/tegra_rpc.c | |||
@@ -0,0 +1,796 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Google, Inc. | ||
3 | * | ||
4 | * Author: | ||
5 | * Dima Zavin <dima@android.com> | ||
6 | * | ||
7 | * Based on original NVRM code from NVIDIA, and a partial rewrite by: | ||
8 | * Gary King <gking@nvidia.com> | ||
9 | * | ||
10 | * This software is licensed under the terms of the GNU General Public | ||
11 | * License version 2, as published by the Free Software Foundation, and | ||
12 | * may be copied, distributed, and modified under those terms. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <linux/debugfs.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/err.h> | ||
24 | #include <linux/kref.h> | ||
25 | #include <linux/list.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/mutex.h> | ||
28 | #include <linux/rbtree.h> | ||
29 | #include <linux/sched.h> | ||
30 | #include <linux/seq_file.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/tegra_rpc.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/wait.h> | ||
35 | |||
36 | #include "trpc.h" | ||
37 | |||
38 | struct trpc_port; | ||
39 | struct trpc_endpoint { | ||
40 | struct list_head msg_list; | ||
41 | wait_queue_head_t msg_waitq; | ||
42 | |||
43 | struct trpc_endpoint *out; | ||
44 | struct trpc_port *port; | ||
45 | |||
46 | struct trpc_node *owner; | ||
47 | |||
48 | struct completion *connect_done; | ||
49 | bool ready; | ||
50 | struct trpc_ep_ops *ops; | ||
51 | void *priv; | ||
52 | }; | ||
53 | |||
54 | struct trpc_port { | ||
55 | char name[TEGRA_RPC_MAX_NAME_LEN]; | ||
56 | |||
57 | /* protects peer and closed state */ | ||
58 | spinlock_t lock; | ||
59 | struct trpc_endpoint peers[2]; | ||
60 | bool closed; | ||
61 | |||
62 | /* private */ | ||
63 | struct kref ref; | ||
64 | struct rb_node rb_node; | ||
65 | }; | ||
66 | |||
67 | enum { | ||
68 | TRPC_TRACE_MSG = 1U << 0, | ||
69 | TRPC_TRACE_CONN = 1U << 1, | ||
70 | TRPC_TRACE_PORT = 1U << 2, | ||
71 | }; | ||
72 | |||
73 | static u32 trpc_debug_mask; | ||
74 | module_param_named(debug_mask, trpc_debug_mask, uint, S_IWUSR | S_IRUGO); | ||
75 | |||
76 | #define DBG(flag, args...) \ | ||
77 | do { if (trpc_debug_mask & (flag)) pr_info(args); } while (0) | ||
78 | |||
79 | struct tegra_rpc_info { | ||
80 | struct kmem_cache *msg_cache; | ||
81 | |||
82 | spinlock_t ports_lock; | ||
83 | struct rb_root ports; | ||
84 | |||
85 | struct list_head node_list; | ||
86 | struct mutex node_lock; | ||
87 | }; | ||
88 | |||
89 | struct trpc_msg { | ||
90 | struct list_head list; | ||
91 | |||
92 | size_t len; | ||
93 | u8 payload[TEGRA_RPC_MAX_MSG_LEN]; | ||
94 | }; | ||
95 | |||
96 | static struct tegra_rpc_info *tegra_rpc; | ||
97 | static struct dentry *trpc_debug_root; | ||
98 | |||
99 | static struct trpc_msg *dequeue_msg_locked(struct trpc_endpoint *ep); | ||
100 | |||
101 | /* a few accessors for the outside world to keep the trpc_endpoint struct | ||
102 | * definition private to this module */ | ||
103 | void *trpc_priv(struct trpc_endpoint *ep) | ||
104 | { | ||
105 | return ep->priv; | ||
106 | } | ||
107 | |||
108 | struct trpc_endpoint *trpc_peer(struct trpc_endpoint *ep) | ||
109 | { | ||
110 | return ep->out; | ||
111 | } | ||
112 | |||
113 | const char *trpc_name(struct trpc_endpoint *ep) | ||
114 | { | ||
115 | return ep->port->name; | ||
116 | } | ||
117 | |||
118 | static inline bool is_connected(struct trpc_port *port) | ||
119 | { | ||
120 | return port->peers[0].ready && port->peers[1].ready; | ||
121 | } | ||
122 | |||
123 | static inline bool is_closed(struct trpc_port *port) | ||
124 | { | ||
125 | return port->closed; | ||
126 | } | ||
127 | |||
128 | static void rpc_port_free(struct tegra_rpc_info *info, struct trpc_port *port) | ||
129 | { | ||
130 | struct trpc_msg *msg; | ||
131 | int i; | ||
132 | |||
133 | for (i = 0; i < 2; ++i) { | ||
134 | struct list_head *list = &port->peers[i].msg_list; | ||
135 | while (!list_empty(list)) { | ||
136 | msg = list_first_entry(list, struct trpc_msg, list); | ||
137 | list_del(&msg->list); | ||
138 | kmem_cache_free(info->msg_cache, msg); | ||
139 | } | ||
140 | } | ||
141 | kfree(port); | ||
142 | } | ||
143 | |||
144 | static void _rpc_port_release(struct kref *kref) | ||
145 | { | ||
146 | struct tegra_rpc_info *info = tegra_rpc; | ||
147 | struct trpc_port *port = container_of(kref, struct trpc_port, ref); | ||
148 | unsigned long flags; | ||
149 | |||
150 | DBG(TRPC_TRACE_PORT, "%s: releasing port '%s' (%p)\n", __func__, | ||
151 | port->name, port); | ||
152 | spin_lock_irqsave(&info->ports_lock, flags); | ||
153 | rb_erase(&port->rb_node, &info->ports); | ||
154 | spin_unlock_irqrestore(&info->ports_lock, flags); | ||
155 | rpc_port_free(info, port); | ||
156 | } | ||
157 | |||
158 | /* note that the refcount is actually on the port and not on the endpoint */ | ||
159 | void trpc_put(struct trpc_endpoint *ep) | ||
160 | { | ||
161 | kref_put(&ep->port->ref, _rpc_port_release); | ||
162 | } | ||
163 | |||
164 | void trpc_get(struct trpc_endpoint *ep) | ||
165 | { | ||
166 | kref_get(&ep->port->ref); | ||
167 | } | ||
168 | |||
169 | /* Searches the rb_tree for a port with the provided name. If one is not found, | ||
170 | * the new port in inserted. Otherwise, the existing port is returned. | ||
171 | * Must be called with the ports_lock held */ | ||
172 | static struct trpc_port *rpc_port_find_insert(struct tegra_rpc_info *info, | ||
173 | struct trpc_port *port) | ||
174 | { | ||
175 | struct rb_node **p; | ||
176 | struct rb_node *parent; | ||
177 | struct trpc_port *tmp; | ||
178 | int ret = 0; | ||
179 | |||
180 | p = &info->ports.rb_node; | ||
181 | parent = NULL; | ||
182 | while (*p) { | ||
183 | parent = *p; | ||
184 | tmp = rb_entry(parent, struct trpc_port, rb_node); | ||
185 | |||
186 | ret = strncmp(port->name, tmp->name, TEGRA_RPC_MAX_NAME_LEN); | ||
187 | if (ret < 0) | ||
188 | p = &(*p)->rb_left; | ||
189 | else if (ret > 0) | ||
190 | p = &(*p)->rb_right; | ||
191 | else | ||
192 | return tmp; | ||
193 | } | ||
194 | rb_link_node(&port->rb_node, parent, p); | ||
195 | rb_insert_color(&port->rb_node, &info->ports); | ||
196 | DBG(TRPC_TRACE_PORT, "%s: inserted port '%s' (%p)\n", __func__, | ||
197 | port->name, port); | ||
198 | return port; | ||
199 | } | ||
200 | |||
201 | static int nodes_try_connect(struct tegra_rpc_info *info, | ||
202 | struct trpc_node *src, | ||
203 | struct trpc_endpoint *from) | ||
204 | { | ||
205 | struct trpc_node *node; | ||
206 | int ret; | ||
207 | |||
208 | mutex_lock(&info->node_lock); | ||
209 | list_for_each_entry(node, &info->node_list, list) { | ||
210 | if (!node->try_connect) | ||
211 | continue; | ||
212 | ret = node->try_connect(node, src, from); | ||
213 | if (!ret) { | ||
214 | mutex_unlock(&info->node_lock); | ||
215 | return 0; | ||
216 | } | ||
217 | } | ||
218 | mutex_unlock(&info->node_lock); | ||
219 | return -ECONNREFUSED; | ||
220 | } | ||
221 | |||
222 | static struct trpc_port *rpc_port_alloc(const char *name) | ||
223 | { | ||
224 | struct trpc_port *port; | ||
225 | int i; | ||
226 | |||
227 | port = kzalloc(sizeof(struct trpc_port), GFP_KERNEL); | ||
228 | if (!port) { | ||
229 | pr_err("%s: can't alloc rpc_port\n", __func__); | ||
230 | return NULL; | ||
231 | } | ||
232 | BUILD_BUG_ON(2 != ARRAY_SIZE(port->peers)); | ||
233 | |||
234 | spin_lock_init(&port->lock); | ||
235 | kref_init(&port->ref); | ||
236 | strlcpy(port->name, name, TEGRA_RPC_MAX_NAME_LEN); | ||
237 | for (i = 0; i < 2; i++) { | ||
238 | struct trpc_endpoint *ep = port->peers + i; | ||
239 | INIT_LIST_HEAD(&ep->msg_list); | ||
240 | init_waitqueue_head(&ep->msg_waitq); | ||
241 | ep->port = port; | ||
242 | } | ||
243 | port->peers[0].out = &port->peers[1]; | ||
244 | port->peers[1].out = &port->peers[0]; | ||
245 | |||
246 | return port; | ||
247 | } | ||
248 | |||
249 | /* must be holding the ports lock */ | ||
250 | static inline void handle_port_connected(struct trpc_port *port) | ||
251 | { | ||
252 | int i; | ||
253 | |||
254 | DBG(TRPC_TRACE_CONN, "tegra_rpc: port '%s' connected\n", port->name); | ||
255 | |||
256 | for (i = 0; i < 2; i++) | ||
257 | if (port->peers[i].connect_done) | ||
258 | complete(port->peers[i].connect_done); | ||
259 | } | ||
260 | |||
261 | static inline void _ready_ep(struct trpc_endpoint *ep, | ||
262 | struct trpc_node *owner, | ||
263 | struct trpc_ep_ops *ops, | ||
264 | void *priv) | ||
265 | { | ||
266 | ep->ready = true; | ||
267 | ep->owner = owner; | ||
268 | ep->ops = ops; | ||
269 | ep->priv = priv; | ||
270 | } | ||
271 | |||
272 | /* this keeps a reference on the port */ | ||
273 | static struct trpc_endpoint *_create_peer(struct tegra_rpc_info *info, | ||
274 | struct trpc_node *owner, | ||
275 | struct trpc_endpoint *ep, | ||
276 | struct trpc_ep_ops *ops, | ||
277 | void *priv) | ||
278 | { | ||
279 | struct trpc_port *port = ep->port; | ||
280 | struct trpc_endpoint *peer = ep->out; | ||
281 | unsigned long flags; | ||
282 | |||
283 | spin_lock_irqsave(&port->lock, flags); | ||
284 | BUG_ON(port->closed); | ||
285 | if (peer->ready || !ep->ready) { | ||
286 | peer = NULL; | ||
287 | goto out; | ||
288 | } | ||
289 | _ready_ep(peer, owner, ops, priv); | ||
290 | if (WARN_ON(!is_connected(port))) | ||
291 | pr_warning("%s: created peer but no connection established?!\n", | ||
292 | __func__); | ||
293 | else | ||
294 | handle_port_connected(port); | ||
295 | trpc_get(peer); | ||
296 | out: | ||
297 | spin_unlock_irqrestore(&port->lock, flags); | ||
298 | return peer; | ||
299 | } | ||
300 | |||
301 | /* Exported code. This is out interface to the outside world */ | ||
302 | struct trpc_endpoint *trpc_create(struct trpc_node *owner, const char *name, | ||
303 | struct trpc_ep_ops *ops, void *priv) | ||
304 | { | ||
305 | struct tegra_rpc_info *info = tegra_rpc; | ||
306 | struct trpc_endpoint *ep; | ||
307 | struct trpc_port *new_port; | ||
308 | struct trpc_port *port; | ||
309 | unsigned long flags; | ||
310 | |||
311 | BUG_ON(!owner); | ||
312 | |||
313 | /* we always allocate a new port even if one already might exist. This | ||
314 | * is slightly inefficient, but it allows us to do the allocation | ||
315 | * without holding our ports_lock spinlock. */ | ||
316 | new_port = rpc_port_alloc(name); | ||
317 | if (!new_port) { | ||
318 | pr_err("%s: can't allocate memory for '%s'\n", __func__, name); | ||
319 | return ERR_PTR(-ENOMEM); | ||
320 | } | ||
321 | |||
322 | spin_lock_irqsave(&info->ports_lock, flags); | ||
323 | port = rpc_port_find_insert(info, new_port); | ||
324 | if (port != new_port) { | ||
325 | rpc_port_free(info, new_port); | ||
326 | /* There was already a port by that name in the rb_tree, | ||
327 | * so just try to create its peer[1], i.e. peer for peer[0] | ||
328 | */ | ||
329 | ep = _create_peer(info, owner, &port->peers[0], ops, priv); | ||
330 | if (!ep) { | ||
331 | pr_err("%s: port '%s' is not in a connectable state\n", | ||
332 | __func__, port->name); | ||
333 | ep = ERR_PTR(-EINVAL); | ||
334 | } | ||
335 | goto out; | ||
336 | } | ||
337 | /* don't need to grab the individual port lock here since we must be | ||
338 | * holding the ports_lock to add the new element, and never dropped | ||
339 | * it, and thus noone could have gotten a reference to this port | ||
340 | * and thus the state couldn't have been touched */ | ||
341 | ep = &port->peers[0]; | ||
342 | _ready_ep(ep, owner, ops, priv); | ||
343 | out: | ||
344 | spin_unlock_irqrestore(&info->ports_lock, flags); | ||
345 | return ep; | ||
346 | } | ||
347 | |||
348 | struct trpc_endpoint *trpc_create_peer(struct trpc_node *owner, | ||
349 | struct trpc_endpoint *ep, | ||
350 | struct trpc_ep_ops *ops, | ||
351 | void *priv) | ||
352 | { | ||
353 | struct tegra_rpc_info *info = tegra_rpc; | ||
354 | struct trpc_endpoint *peer; | ||
355 | unsigned long flags; | ||
356 | |||
357 | BUG_ON(!owner); | ||
358 | |||
359 | spin_lock_irqsave(&info->ports_lock, flags); | ||
360 | peer = _create_peer(info, owner, ep, ops, priv); | ||
361 | spin_unlock_irqrestore(&info->ports_lock, flags); | ||
362 | return peer; | ||
363 | } | ||
364 | |||
365 | /* timeout == -1, waits forever | ||
366 | * timeout == 0, return immediately | ||
367 | */ | ||
368 | int trpc_connect(struct trpc_endpoint *from, long timeout) | ||
369 | { | ||
370 | struct tegra_rpc_info *info = tegra_rpc; | ||
371 | struct trpc_port *port = from->port; | ||
372 | struct trpc_node *src = from->owner; | ||
373 | int ret; | ||
374 | bool no_retry = !timeout; | ||
375 | unsigned long endtime = jiffies + msecs_to_jiffies(timeout); | ||
376 | unsigned long flags; | ||
377 | |||
378 | spin_lock_irqsave(&port->lock, flags); | ||
379 | /* XXX: add state for connections and ports to prevent invalid | ||
380 | * states like multiple connections, etc. ? */ | ||
381 | if (unlikely(is_closed(port))) { | ||
382 | ret = -ECONNRESET; | ||
383 | pr_err("%s: can't connect to %s, closed\n", __func__, | ||
384 | port->name); | ||
385 | goto out; | ||
386 | } else if (is_connected(port)) { | ||
387 | ret = 0; | ||
388 | goto out; | ||
389 | } | ||
390 | spin_unlock_irqrestore(&port->lock, flags); | ||
391 | |||
392 | do { | ||
393 | ret = nodes_try_connect(info, src, from); | ||
394 | |||
395 | spin_lock_irqsave(&port->lock, flags); | ||
396 | if (is_connected(port)) { | ||
397 | ret = 0; | ||
398 | goto out; | ||
399 | } else if (no_retry) { | ||
400 | goto out; | ||
401 | } else if (signal_pending(current)) { | ||
402 | ret = -EINTR; | ||
403 | goto out; | ||
404 | } | ||
405 | spin_unlock_irqrestore(&port->lock, flags); | ||
406 | usleep_range(5000, 20000); | ||
407 | } while (timeout < 0 || time_before(jiffies, endtime)); | ||
408 | |||
409 | return -ETIMEDOUT; | ||
410 | |||
411 | out: | ||
412 | spin_unlock_irqrestore(&port->lock, flags); | ||
413 | return ret; | ||
414 | } | ||
415 | |||
416 | /* convenience function for doing this common pattern in a single call */ | ||
417 | struct trpc_endpoint *trpc_create_connect(struct trpc_node *src, | ||
418 | char *name, | ||
419 | struct trpc_ep_ops *ops, | ||
420 | void *priv, | ||
421 | long timeout) | ||
422 | { | ||
423 | struct trpc_endpoint *ep; | ||
424 | int ret; | ||
425 | |||
426 | ep = trpc_create(src, name, ops, priv); | ||
427 | if (IS_ERR(ep)) | ||
428 | return ep; | ||
429 | |||
430 | ret = trpc_connect(ep, timeout); | ||
431 | if (ret) { | ||
432 | trpc_close(ep); | ||
433 | return ERR_PTR(ret); | ||
434 | } | ||
435 | |||
436 | return ep; | ||
437 | } | ||
438 | |||
439 | void trpc_close(struct trpc_endpoint *ep) | ||
440 | { | ||
441 | struct trpc_port *port = ep->port; | ||
442 | struct trpc_endpoint *peer = ep->out; | ||
443 | bool need_close_op = false; | ||
444 | unsigned long flags; | ||
445 | |||
446 | spin_lock_irqsave(&port->lock, flags); | ||
447 | BUG_ON(!ep->ready); | ||
448 | ep->ready = false; | ||
449 | port->closed = true; | ||
450 | if (peer->ready) { | ||
451 | need_close_op = true; | ||
452 | /* the peer may be waiting for a message */ | ||
453 | wake_up_all(&peer->msg_waitq); | ||
454 | if (peer->connect_done) | ||
455 | complete(peer->connect_done); | ||
456 | } | ||
457 | spin_unlock_irqrestore(&port->lock, flags); | ||
458 | if (need_close_op && peer->ops && peer->ops->close) | ||
459 | peer->ops->close(peer); | ||
460 | trpc_put(ep); | ||
461 | } | ||
462 | |||
463 | int trpc_wait_peer(struct trpc_endpoint *ep, long timeout) | ||
464 | { | ||
465 | struct trpc_port *port = ep->port; | ||
466 | DECLARE_COMPLETION_ONSTACK(event); | ||
467 | int ret; | ||
468 | unsigned long flags; | ||
469 | |||
470 | if (timeout < 0) | ||
471 | timeout = MAX_SCHEDULE_TIMEOUT; | ||
472 | else if (timeout > 0) | ||
473 | timeout = msecs_to_jiffies(timeout); | ||
474 | |||
475 | spin_lock_irqsave(&port->lock, flags); | ||
476 | if (ep->connect_done) { | ||
477 | ret = -EBUSY; | ||
478 | goto done; | ||
479 | } else if (is_connected(port)) { | ||
480 | ret = 0; | ||
481 | goto done; | ||
482 | } else if (is_closed(port)) { | ||
483 | ret = -ECONNRESET; | ||
484 | goto done; | ||
485 | } else if (!timeout) { | ||
486 | ret = -EAGAIN; | ||
487 | goto done; | ||
488 | } | ||
489 | ep->connect_done = &event; | ||
490 | spin_unlock_irqrestore(&port->lock, flags); | ||
491 | |||
492 | ret = wait_for_completion_interruptible_timeout(&event, timeout); | ||
493 | |||
494 | spin_lock_irqsave(&port->lock, flags); | ||
495 | ep->connect_done = NULL; | ||
496 | |||
497 | if (is_connected(port)) { | ||
498 | ret = 0; | ||
499 | } else { | ||
500 | if (is_closed(port)) | ||
501 | ret = -ECONNRESET; | ||
502 | else if (ret == -ERESTARTSYS) | ||
503 | ret = -EINTR; | ||
504 | else if (!ret) | ||
505 | ret = -ETIMEDOUT; | ||
506 | } | ||
507 | |||
508 | done: | ||
509 | spin_unlock_irqrestore(&port->lock, flags); | ||
510 | return ret; | ||
511 | } | ||
512 | |||
513 | static inline int _ep_id(struct trpc_endpoint *ep) | ||
514 | { | ||
515 | return ep - ep->port->peers; | ||
516 | } | ||
517 | |||
518 | static int queue_msg(struct trpc_node *src, struct trpc_endpoint *from, | ||
519 | void *buf, size_t len, gfp_t gfp_flags) | ||
520 | { | ||
521 | struct tegra_rpc_info *info = tegra_rpc; | ||
522 | struct trpc_endpoint *peer = from->out; | ||
523 | struct trpc_port *port = from->port; | ||
524 | struct trpc_msg *msg; | ||
525 | unsigned long flags; | ||
526 | int ret; | ||
527 | |||
528 | BUG_ON(len > TEGRA_RPC_MAX_MSG_LEN); | ||
529 | /* shouldn't be enqueueing to the endpoint */ | ||
530 | BUG_ON(peer->ops && peer->ops->send); | ||
531 | |||
532 | DBG(TRPC_TRACE_MSG, "%s: queueing message for %s.%d\n", __func__, | ||
533 | port->name, _ep_id(peer)); | ||
534 | |||
535 | msg = kmem_cache_alloc(info->msg_cache, gfp_flags); | ||
536 | if (!msg) { | ||
537 | pr_err("%s: can't alloc memory for msg\n", __func__); | ||
538 | return -ENOMEM; | ||
539 | } | ||
540 | |||
541 | memcpy(msg->payload, buf, len); | ||
542 | msg->len = len; | ||
543 | |||
544 | spin_lock_irqsave(&port->lock, flags); | ||
545 | if (is_closed(port)) { | ||
546 | pr_err("%s: cannot send message for closed port %s.%d\n", | ||
547 | __func__, port->name, _ep_id(peer)); | ||
548 | ret = -ECONNRESET; | ||
549 | goto err; | ||
550 | } else if (!is_connected(port)) { | ||
551 | pr_err("%s: cannot send message for unconnected port %s.%d\n", | ||
552 | __func__, port->name, _ep_id(peer)); | ||
553 | ret = -ENOTCONN; | ||
554 | goto err; | ||
555 | } | ||
556 | |||
557 | list_add_tail(&msg->list, &peer->msg_list); | ||
558 | if (peer->ops && peer->ops->notify_recv) | ||
559 | peer->ops->notify_recv(peer); | ||
560 | wake_up_all(&peer->msg_waitq); | ||
561 | spin_unlock_irqrestore(&port->lock, flags); | ||
562 | return 0; | ||
563 | |||
564 | err: | ||
565 | spin_unlock_irqrestore(&port->lock, flags); | ||
566 | kmem_cache_free(info->msg_cache, msg); | ||
567 | return ret; | ||
568 | } | ||
569 | |||
570 | /* Returns -ENOMEM if failed to allocate memory for the message. */ | ||
571 | int trpc_send_msg(struct trpc_node *src, struct trpc_endpoint *from, | ||
572 | void *buf, size_t len, gfp_t gfp_flags) | ||
573 | { | ||
574 | struct trpc_endpoint *peer = from->out; | ||
575 | struct trpc_port *port = from->port; | ||
576 | |||
577 | BUG_ON(len > TEGRA_RPC_MAX_MSG_LEN); | ||
578 | |||
579 | DBG(TRPC_TRACE_MSG, "%s: sending message from %s.%d to %s.%d\n", | ||
580 | __func__, port->name, _ep_id(from), port->name, _ep_id(peer)); | ||
581 | |||
582 | if (peer->ops && peer->ops->send) { | ||
583 | might_sleep(); | ||
584 | return peer->ops->send(peer, buf, len); | ||
585 | } else { | ||
586 | might_sleep_if(gfp_flags & __GFP_WAIT); | ||
587 | return queue_msg(src, from, buf, len, gfp_flags); | ||
588 | } | ||
589 | } | ||
590 | |||
591 | static inline struct trpc_msg *dequeue_msg_locked(struct trpc_endpoint *ep) | ||
592 | { | ||
593 | struct trpc_msg *msg = NULL; | ||
594 | |||
595 | if (!list_empty(&ep->msg_list)) { | ||
596 | msg = list_first_entry(&ep->msg_list, struct trpc_msg, list); | ||
597 | list_del_init(&msg->list); | ||
598 | } | ||
599 | |||
600 | return msg; | ||
601 | } | ||
602 | |||
603 | static bool __should_wake(struct trpc_endpoint *ep) | ||
604 | { | ||
605 | struct trpc_port *port = ep->port; | ||
606 | unsigned long flags; | ||
607 | bool ret; | ||
608 | |||
609 | spin_lock_irqsave(&port->lock, flags); | ||
610 | ret = !list_empty(&ep->msg_list) || is_closed(port); | ||
611 | spin_unlock_irqrestore(&port->lock, flags); | ||
612 | return ret; | ||
613 | } | ||
614 | |||
615 | int trpc_recv_msg(struct trpc_node *src, struct trpc_endpoint *ep, | ||
616 | void *buf, size_t buf_len, long timeout) | ||
617 | { | ||
618 | struct tegra_rpc_info *info = tegra_rpc; | ||
619 | struct trpc_port *port = ep->port; | ||
620 | struct trpc_msg *msg; | ||
621 | size_t len; | ||
622 | long ret; | ||
623 | unsigned long flags; | ||
624 | |||
625 | BUG_ON(buf_len > TEGRA_RPC_MAX_MSG_LEN); | ||
626 | |||
627 | spin_lock_irqsave(&port->lock, flags); | ||
628 | /* we allow closed ports to finish receiving already-queued messages */ | ||
629 | msg = dequeue_msg_locked(ep); | ||
630 | if (msg) { | ||
631 | goto got_msg; | ||
632 | } else if (is_closed(port)) { | ||
633 | ret = -ECONNRESET; | ||
634 | goto out; | ||
635 | } else if (!is_connected(port)) { | ||
636 | ret = -ENOTCONN; | ||
637 | goto out; | ||
638 | } | ||
639 | |||
640 | if (timeout == 0) { | ||
641 | ret = 0; | ||
642 | goto out; | ||
643 | } else if (timeout < 0) { | ||
644 | timeout = MAX_SCHEDULE_TIMEOUT; | ||
645 | } else { | ||
646 | timeout = msecs_to_jiffies(timeout); | ||
647 | } | ||
648 | spin_unlock_irqrestore(&port->lock, flags); | ||
649 | DBG(TRPC_TRACE_MSG, "%s: waiting for message for %s.%d\n", __func__, | ||
650 | port->name, _ep_id(ep)); | ||
651 | |||
652 | ret = wait_event_interruptible_timeout(ep->msg_waitq, __should_wake(ep), | ||
653 | timeout); | ||
654 | |||
655 | DBG(TRPC_TRACE_MSG, "%s: woke up for %s\n", __func__, port->name); | ||
656 | spin_lock_irqsave(&port->lock, flags); | ||
657 | msg = dequeue_msg_locked(ep); | ||
658 | if (!msg) { | ||
659 | if (is_closed(port)) | ||
660 | ret = -ECONNRESET; | ||
661 | else if (!ret) | ||
662 | ret = -ETIMEDOUT; | ||
663 | else if (ret == -ERESTARTSYS) | ||
664 | ret = -EINTR; | ||
665 | else | ||
666 | pr_err("%s: error (%d) while receiving msg for '%s'\n", | ||
667 | __func__, (int)ret, port->name); | ||
668 | goto out; | ||
669 | } | ||
670 | |||
671 | got_msg: | ||
672 | spin_unlock_irqrestore(&port->lock, flags); | ||
673 | len = min(buf_len, msg->len); | ||
674 | memcpy(buf, msg->payload, len); | ||
675 | kmem_cache_free(info->msg_cache, msg); | ||
676 | return len; | ||
677 | |||
678 | out: | ||
679 | spin_unlock_irqrestore(&port->lock, flags); | ||
680 | return ret; | ||
681 | } | ||
682 | |||
683 | int trpc_node_register(struct trpc_node *node) | ||
684 | { | ||
685 | struct tegra_rpc_info *info = tegra_rpc; | ||
686 | |||
687 | if (!info) | ||
688 | return -ENOMEM; | ||
689 | |||
690 | pr_info("%s: Adding '%s' to node list\n", __func__, node->name); | ||
691 | |||
692 | mutex_lock(&info->node_lock); | ||
693 | if (node->type == TRPC_NODE_LOCAL) | ||
694 | list_add(&node->list, &info->node_list); | ||
695 | else | ||
696 | list_add_tail(&node->list, &info->node_list); | ||
697 | mutex_unlock(&info->node_lock); | ||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | void trpc_node_unregister(struct trpc_node *node) | ||
702 | { | ||
703 | struct tegra_rpc_info *info = tegra_rpc; | ||
704 | |||
705 | mutex_lock(&info->node_lock); | ||
706 | list_del(&node->list); | ||
707 | mutex_unlock(&info->node_lock); | ||
708 | } | ||
709 | |||
710 | static int trpc_debug_ports_show(struct seq_file *s, void *data) | ||
711 | { | ||
712 | struct tegra_rpc_info *info = s->private; | ||
713 | struct rb_node *n; | ||
714 | unsigned long flags; | ||
715 | int i; | ||
716 | |||
717 | spin_lock_irqsave(&info->ports_lock, flags); | ||
718 | for (n = rb_first(&info->ports); n; n = rb_next(n)) { | ||
719 | struct trpc_port *port = rb_entry(n, struct trpc_port, rb_node); | ||
720 | seq_printf(s, "port: %s\n closed:%s\n", port->name, | ||
721 | port->closed ? "yes" : "no"); | ||
722 | |||
723 | spin_lock(&port->lock); | ||
724 | for (i = 0; i < ARRAY_SIZE(port->peers); i++) { | ||
725 | struct trpc_endpoint *ep = &port->peers[i]; | ||
726 | seq_printf(s, " peer%d: %s\n ready:%s\n", i, | ||
727 | ep->owner ? ep->owner->name : "<none>", | ||
728 | ep->ready ? "yes" : "no"); | ||
729 | if (ep->ops && ep->ops->show) | ||
730 | ep->ops->show(s, ep); | ||
731 | } | ||
732 | spin_unlock(&port->lock); | ||
733 | } | ||
734 | spin_unlock_irqrestore(&info->ports_lock, flags); | ||
735 | |||
736 | return 0; | ||
737 | } | ||
738 | |||
739 | static int trpc_debug_ports_open(struct inode *inode, struct file *file) | ||
740 | { | ||
741 | return single_open(file, trpc_debug_ports_show, inode->i_private); | ||
742 | } | ||
743 | |||
744 | static const struct file_operations trpc_debug_ports_fops = { | ||
745 | .open = trpc_debug_ports_open, | ||
746 | .read = seq_read, | ||
747 | .llseek = seq_lseek, | ||
748 | .release = single_release, | ||
749 | }; | ||
750 | |||
751 | static void trpc_debug_init(struct tegra_rpc_info *info) | ||
752 | { | ||
753 | trpc_debug_root = debugfs_create_dir("tegra_rpc", NULL); | ||
754 | if (IS_ERR_OR_NULL(trpc_debug_root)) { | ||
755 | pr_err("%s: couldn't create debug files\n", __func__); | ||
756 | return; | ||
757 | } | ||
758 | |||
759 | debugfs_create_file("ports", 0664, trpc_debug_root, info, | ||
760 | &trpc_debug_ports_fops); | ||
761 | } | ||
762 | |||
763 | static int __init tegra_rpc_init(void) | ||
764 | { | ||
765 | struct tegra_rpc_info *rpc_info; | ||
766 | int ret; | ||
767 | |||
768 | rpc_info = kzalloc(sizeof(struct tegra_rpc_info), GFP_KERNEL); | ||
769 | if (!rpc_info) { | ||
770 | pr_err("%s: error allocating rpc_info\n", __func__); | ||
771 | return -ENOMEM; | ||
772 | } | ||
773 | |||
774 | rpc_info->ports = RB_ROOT; | ||
775 | spin_lock_init(&rpc_info->ports_lock); | ||
776 | INIT_LIST_HEAD(&rpc_info->node_list); | ||
777 | mutex_init(&rpc_info->node_lock); | ||
778 | |||
779 | rpc_info->msg_cache = KMEM_CACHE(trpc_msg, 0); | ||
780 | if (!rpc_info->msg_cache) { | ||
781 | pr_err("%s: unable to create message cache\n", __func__); | ||
782 | ret = -ENOMEM; | ||
783 | goto err_kmem_cache; | ||
784 | } | ||
785 | |||
786 | trpc_debug_init(rpc_info); | ||
787 | tegra_rpc = rpc_info; | ||
788 | |||
789 | return 0; | ||
790 | |||
791 | err_kmem_cache: | ||
792 | kfree(rpc_info); | ||
793 | return ret; | ||
794 | } | ||
795 | |||
796 | subsys_initcall(tegra_rpc_init); | ||
diff --git a/drivers/media/video/tegra/avp/trpc.h b/drivers/media/video/tegra/avp/trpc.h new file mode 100644 index 00000000000..e7b0d2d5578 --- /dev/null +++ b/drivers/media/video/tegra/avp/trpc.h | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Google, Inc. | ||
3 | * | ||
4 | * Author: | ||
5 | * Dima Zavin <dima@android.com> | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #ifndef __ARM_MACH_TEGRA_RPC_H | ||
19 | #define __ARM_MACH_TEGRA_RPC_H | ||
20 | |||
21 | #include <linux/list.h> | ||
22 | #include <linux/seq_file.h> | ||
23 | #include <linux/tegra_rpc.h> | ||
24 | |||
25 | struct trpc_endpoint; | ||
26 | struct trpc_ep_ops { | ||
27 | /* send is allowed to sleep */ | ||
28 | int (*send)(struct trpc_endpoint *ep, void *buf, size_t len); | ||
29 | /* notify_recv is NOT allowed to sleep */ | ||
30 | void (*notify_recv)(struct trpc_endpoint *ep); | ||
31 | /* close is allowed to sleep */ | ||
32 | void (*close)(struct trpc_endpoint *ep); | ||
33 | /* not allowed to sleep, not allowed to call back into trpc */ | ||
34 | void (*show)(struct seq_file *s, struct trpc_endpoint *ep); | ||
35 | }; | ||
36 | |||
37 | enum { | ||
38 | TRPC_NODE_LOCAL, | ||
39 | TRPC_NODE_REMOTE, | ||
40 | }; | ||
41 | |||
42 | struct trpc_node { | ||
43 | struct list_head list; | ||
44 | const char *name; | ||
45 | int type; | ||
46 | void *priv; | ||
47 | |||
48 | int (*try_connect)(struct trpc_node *node, | ||
49 | struct trpc_node *src, | ||
50 | struct trpc_endpoint *from); | ||
51 | }; | ||
52 | |||
53 | struct trpc_endpoint *trpc_peer(struct trpc_endpoint *ep); | ||
54 | void *trpc_priv(struct trpc_endpoint *ep); | ||
55 | const char *trpc_name(struct trpc_endpoint *ep); | ||
56 | |||
57 | void trpc_put(struct trpc_endpoint *ep); | ||
58 | void trpc_get(struct trpc_endpoint *ep); | ||
59 | |||
60 | int trpc_send_msg(struct trpc_node *src, struct trpc_endpoint *ep, void *buf, | ||
61 | size_t len, gfp_t gfp_flags); | ||
62 | int trpc_recv_msg(struct trpc_node *src, struct trpc_endpoint *ep, | ||
63 | void *buf, size_t len, long timeout); | ||
64 | struct trpc_endpoint *trpc_create(struct trpc_node *owner, const char *name, | ||
65 | struct trpc_ep_ops *ops, void *priv); | ||
66 | struct trpc_endpoint *trpc_create_connect(struct trpc_node *src, char *name, | ||
67 | struct trpc_ep_ops *ops, void *priv, | ||
68 | long timeout); | ||
69 | int trpc_connect(struct trpc_endpoint *from, long timeout); | ||
70 | struct trpc_endpoint *trpc_create_peer(struct trpc_node *owner, | ||
71 | struct trpc_endpoint *ep, | ||
72 | struct trpc_ep_ops *ops, | ||
73 | void *priv); | ||
74 | void trpc_close(struct trpc_endpoint *ep); | ||
75 | int trpc_wait_peer(struct trpc_endpoint *ep, long timeout); | ||
76 | |||
77 | int trpc_node_register(struct trpc_node *node); | ||
78 | void trpc_node_unregister(struct trpc_node *node); | ||
79 | |||
80 | #endif | ||
diff --git a/drivers/media/video/tegra/avp/trpc_local.c b/drivers/media/video/tegra/avp/trpc_local.c new file mode 100644 index 00000000000..77692e09438 --- /dev/null +++ b/drivers/media/video/tegra/avp/trpc_local.c | |||
@@ -0,0 +1,419 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Google, Inc. | ||
3 | * | ||
4 | * Author: | ||
5 | * Dima Zavin <dima@android.com> | ||
6 | * | ||
7 | * Based on original NVRM code from NVIDIA, and a partial rewrite by | ||
8 | * Gary King <gking@nvidia.com> | ||
9 | * | ||
10 | * This software is licensed under the terms of the GNU General Public | ||
11 | * License version 2, as published by the Free Software Foundation, and | ||
12 | * may be copied, distributed, and modified under those terms. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <linux/err.h> | ||
22 | #include <linux/file.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/list.h> | ||
25 | #include <linux/miscdevice.h> | ||
26 | #include <linux/sched.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/spinlock.h> | ||
29 | #include <linux/tegra_rpc.h> | ||
30 | #include <linux/types.h> | ||
31 | #include <linux/uaccess.h> | ||
32 | #include <linux/wait.h> | ||
33 | |||
34 | #include "trpc.h" | ||
35 | #include "trpc_sema.h" | ||
36 | #include "nvavp.h" | ||
37 | |||
38 | struct tegra_rpc_info { | ||
39 | struct trpc_endpoint *rpc_ep; | ||
40 | struct tegra_sema_info *sema; | ||
41 | }; | ||
42 | |||
43 | /* ports names reserved for system functions, i.e. communicating with the | ||
44 | * AVP */ | ||
45 | static const char reserved_ports[][TEGRA_RPC_MAX_NAME_LEN] = { | ||
46 | "RPC_AVP_PORT", | ||
47 | "RPC_CPU_PORT", | ||
48 | }; | ||
49 | static int num_reserved_ports = ARRAY_SIZE(reserved_ports); | ||
50 | |||
51 | static void rpc_notify_recv(struct trpc_endpoint *ep); | ||
52 | |||
53 | /* TODO: do we need to do anything when port is closed from the other side? */ | ||
54 | static struct trpc_ep_ops ep_ops = { | ||
55 | .notify_recv = rpc_notify_recv, | ||
56 | }; | ||
57 | |||
58 | static struct trpc_node rpc_node = { | ||
59 | .name = "local", | ||
60 | .type = TRPC_NODE_LOCAL, | ||
61 | }; | ||
62 | |||
63 | static void rpc_notify_recv(struct trpc_endpoint *ep) | ||
64 | { | ||
65 | struct tegra_rpc_info *info = trpc_priv(ep); | ||
66 | |||
67 | if (WARN_ON(!info)) | ||
68 | return; | ||
69 | if (info->sema) | ||
70 | tegra_sema_signal(info->sema); | ||
71 | } | ||
72 | |||
73 | int tegra_rpc_open(struct tegra_rpc_info **info) | ||
74 | { | ||
75 | struct tegra_rpc_info *new_info; | ||
76 | |||
77 | new_info = kzalloc(sizeof(struct tegra_rpc_info), GFP_KERNEL); | ||
78 | if (!new_info) | ||
79 | return -ENOMEM; | ||
80 | |||
81 | *info = new_info; | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | static int local_rpc_open(struct inode *inode, struct file *file) | ||
86 | { | ||
87 | struct tegra_rpc_info *info; | ||
88 | int ret = 0; | ||
89 | |||
90 | ret = tegra_rpc_open(&info); | ||
91 | if (ret < 0) | ||
92 | return -ENOMEM; | ||
93 | |||
94 | nonseekable_open(inode, file); | ||
95 | file->private_data = info; | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | int tegra_rpc_release(struct tegra_rpc_info *info) | ||
100 | { | ||
101 | if (info->rpc_ep) | ||
102 | trpc_close(info->rpc_ep); | ||
103 | if (info->sema) | ||
104 | trpc_sema_put(info->sema); | ||
105 | kfree(info); | ||
106 | return 0; | ||
107 | } | ||
108 | EXPORT_SYMBOL(tegra_rpc_release); | ||
109 | |||
110 | static int local_rpc_release(struct inode *inode, struct file *file) | ||
111 | { | ||
112 | struct tegra_rpc_info *info = file->private_data; | ||
113 | |||
114 | tegra_rpc_release(info); | ||
115 | file->private_data = NULL; | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static char uniq_name[] = "aaaaaaaa+"; | ||
120 | static const int uniq_len = sizeof(uniq_name) - 1; | ||
121 | static DEFINE_MUTEX(uniq_lock); | ||
122 | |||
123 | static void _gen_port_name(char *new_name) | ||
124 | { | ||
125 | int i; | ||
126 | |||
127 | mutex_lock(&uniq_lock); | ||
128 | for (i = 0; i < uniq_len - 1; i++) { | ||
129 | ++uniq_name[i]; | ||
130 | if (uniq_name[i] != 'z') | ||
131 | break; | ||
132 | uniq_name[i] = 'a'; | ||
133 | } | ||
134 | strlcpy(new_name, uniq_name, TEGRA_RPC_MAX_NAME_LEN); | ||
135 | mutex_unlock(&uniq_lock); | ||
136 | } | ||
137 | |||
138 | static int _validate_port_name(const char *name) | ||
139 | { | ||
140 | int i; | ||
141 | |||
142 | for (i = 0; i < num_reserved_ports; i++) | ||
143 | if (!strncmp(name, reserved_ports[i], TEGRA_RPC_MAX_NAME_LEN)) | ||
144 | return -EINVAL; | ||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | int tegra_rpc_port_create(struct tegra_rpc_info *info, char *name, | ||
149 | struct tegra_sema_info *sema) | ||
150 | { | ||
151 | struct trpc_endpoint *ep; | ||
152 | int ret = 0; | ||
153 | |||
154 | if (info->rpc_ep) { | ||
155 | ret = -EINVAL; | ||
156 | goto err; | ||
157 | } | ||
158 | |||
159 | name[TEGRA_RPC_MAX_NAME_LEN - 1] = '\0'; | ||
160 | if (name[0]) { | ||
161 | ret = _validate_port_name(name); | ||
162 | if (ret) | ||
163 | goto err; | ||
164 | } else { | ||
165 | _gen_port_name(name); | ||
166 | } | ||
167 | ep = trpc_create(&rpc_node, name, &ep_ops, info); | ||
168 | if (IS_ERR(ep)) { | ||
169 | ret = PTR_ERR(ep); | ||
170 | goto err; | ||
171 | } | ||
172 | info->rpc_ep = ep; | ||
173 | info->sema = sema; | ||
174 | return 0; | ||
175 | |||
176 | err: | ||
177 | return ret; | ||
178 | } | ||
179 | |||
180 | int tegra_rpc_get_name(struct tegra_rpc_info *info, char* name) | ||
181 | { | ||
182 | if (!info->rpc_ep) | ||
183 | return -EINVAL; | ||
184 | |||
185 | strcpy(name, trpc_name(info->rpc_ep)); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | int tegra_rpc_port_connect(struct tegra_rpc_info *info, long timeout) | ||
190 | { | ||
191 | if (!info->rpc_ep) | ||
192 | return -EINVAL; | ||
193 | |||
194 | return trpc_connect(info->rpc_ep, timeout); | ||
195 | |||
196 | } | ||
197 | |||
198 | int tegra_rpc_port_listen(struct tegra_rpc_info *info, long timeout) | ||
199 | { | ||
200 | if (!info->rpc_ep) | ||
201 | return -EINVAL; | ||
202 | |||
203 | return trpc_wait_peer(info->rpc_ep, timeout); | ||
204 | } | ||
205 | |||
206 | static long local_rpc_ioctl(struct file *file, unsigned int cmd, | ||
207 | unsigned long arg) | ||
208 | { | ||
209 | struct tegra_rpc_info *info = file->private_data; | ||
210 | struct tegra_rpc_port_desc desc; | ||
211 | struct tegra_sema_info *sema = NULL; | ||
212 | int ret = 0; | ||
213 | |||
214 | if (_IOC_TYPE(cmd) != TEGRA_RPC_IOCTL_MAGIC || | ||
215 | _IOC_NR(cmd) < TEGRA_RPC_IOCTL_MIN_NR || | ||
216 | _IOC_NR(cmd) > TEGRA_RPC_IOCTL_MAX_NR) { | ||
217 | ret = -ENOTTY; | ||
218 | goto err; | ||
219 | } | ||
220 | |||
221 | switch (cmd) { | ||
222 | case TEGRA_RPC_IOCTL_PORT_CREATE: | ||
223 | |||
224 | if (_IOC_SIZE(cmd) != sizeof(struct tegra_rpc_port_desc)) | ||
225 | return -EINVAL; | ||
226 | if (copy_from_user(&desc, (void __user *)arg, sizeof(desc))) | ||
227 | return -EFAULT; | ||
228 | if (desc.notify_fd != -1) { | ||
229 | sema = trpc_sema_get_from_fd(desc.notify_fd); | ||
230 | if (IS_ERR(sema)) { | ||
231 | ret = PTR_ERR(sema); | ||
232 | goto err; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | ret = tegra_rpc_port_create(info, desc.name, sema); | ||
237 | if (ret < 0) | ||
238 | goto err; | ||
239 | |||
240 | break; | ||
241 | case TEGRA_RPC_IOCTL_PORT_GET_NAME: | ||
242 | if (!info->rpc_ep) { | ||
243 | ret = -EINVAL; | ||
244 | goto err; | ||
245 | } | ||
246 | if (copy_to_user((void __user *)arg, | ||
247 | trpc_name(info->rpc_ep), | ||
248 | TEGRA_RPC_MAX_NAME_LEN)) { | ||
249 | ret = -EFAULT; | ||
250 | goto err; | ||
251 | } | ||
252 | break; | ||
253 | case TEGRA_RPC_IOCTL_PORT_CONNECT: | ||
254 | if (!info->rpc_ep) { | ||
255 | ret = -EINVAL; | ||
256 | goto err; | ||
257 | } | ||
258 | ret = trpc_connect(info->rpc_ep, (long)arg); | ||
259 | if (ret) { | ||
260 | pr_err("%s: can't connect to '%s' (%d)\n", __func__, | ||
261 | trpc_name(info->rpc_ep), ret); | ||
262 | goto err; | ||
263 | } | ||
264 | break; | ||
265 | case TEGRA_RPC_IOCTL_PORT_LISTEN: | ||
266 | if (!info->rpc_ep) { | ||
267 | ret = -EINVAL; | ||
268 | goto err; | ||
269 | } | ||
270 | ret = trpc_wait_peer(info->rpc_ep, (long)arg); | ||
271 | if (ret) { | ||
272 | pr_err("%s: error waiting for peer for '%s' (%d)\n", | ||
273 | __func__, trpc_name(info->rpc_ep), ret); | ||
274 | goto err; | ||
275 | } | ||
276 | break; | ||
277 | default: | ||
278 | pr_err("%s: unknown cmd %d\n", __func__, _IOC_NR(cmd)); | ||
279 | ret = -EINVAL; | ||
280 | goto err; | ||
281 | } | ||
282 | |||
283 | return 0; | ||
284 | |||
285 | err: | ||
286 | if (ret && ret != -ERESTARTSYS) | ||
287 | pr_err("tegra_rpc: pid=%d ioctl=%x/%lx (%x) ret=%d\n", | ||
288 | current->pid, cmd, arg, _IOC_NR(cmd), ret); | ||
289 | return (long)ret; | ||
290 | } | ||
291 | |||
292 | int tegra_rpc_write(struct tegra_rpc_info *info, u8* buf, size_t size) | ||
293 | { | ||
294 | int ret; | ||
295 | |||
296 | if (!info->rpc_ep) | ||
297 | return -EINVAL; | ||
298 | |||
299 | if (TEGRA_RPC_MAX_MSG_LEN < size) | ||
300 | return -EINVAL; | ||
301 | |||
302 | ret = trpc_send_msg(&rpc_node, info->rpc_ep, buf, size, | ||
303 | GFP_KERNEL); | ||
304 | if (ret) | ||
305 | return ret; | ||
306 | return size; | ||
307 | } | ||
308 | |||
309 | static ssize_t local_rpc_write(struct file *file, const char __user *buf, | ||
310 | size_t count, loff_t *ppos) | ||
311 | { | ||
312 | struct tegra_rpc_info *info = file->private_data; | ||
313 | u8 data[TEGRA_RPC_MAX_MSG_LEN]; | ||
314 | int ret; | ||
315 | |||
316 | if (!info) | ||
317 | return -EINVAL; | ||
318 | else if (count > TEGRA_RPC_MAX_MSG_LEN) | ||
319 | return -EINVAL; | ||
320 | |||
321 | if (copy_from_user(data, buf, count)) | ||
322 | return -EFAULT; | ||
323 | |||
324 | ret = trpc_send_msg(&rpc_node, info->rpc_ep, data, count, | ||
325 | GFP_KERNEL); | ||
326 | if (ret) | ||
327 | return ret; | ||
328 | return count; | ||
329 | } | ||
330 | |||
331 | int tegra_rpc_read(struct tegra_rpc_info *info, u8 *buf, size_t max) | ||
332 | { | ||
333 | int ret; | ||
334 | |||
335 | if (max > TEGRA_RPC_MAX_MSG_LEN) | ||
336 | return -EINVAL; | ||
337 | |||
338 | ret = trpc_recv_msg(&rpc_node, info->rpc_ep, buf, | ||
339 | TEGRA_RPC_MAX_MSG_LEN, 0); | ||
340 | if (ret == 0) | ||
341 | return 0; | ||
342 | else if (ret < 0) | ||
343 | return ret; | ||
344 | else if (ret > max) | ||
345 | return -ENOSPC; | ||
346 | |||
347 | return ret; | ||
348 | } | ||
349 | |||
350 | static ssize_t local_rpc_read(struct file *file, char __user *buf, size_t max, | ||
351 | loff_t *ppos) | ||
352 | { | ||
353 | struct tegra_rpc_info *info = file->private_data; | ||
354 | int ret; | ||
355 | u8 data[TEGRA_RPC_MAX_MSG_LEN]; | ||
356 | |||
357 | if (max > TEGRA_RPC_MAX_MSG_LEN) | ||
358 | return -EINVAL; | ||
359 | |||
360 | ret = trpc_recv_msg(&rpc_node, info->rpc_ep, data, | ||
361 | TEGRA_RPC_MAX_MSG_LEN, 0); | ||
362 | if (ret == 0) | ||
363 | return 0; | ||
364 | else if (ret < 0) | ||
365 | return ret; | ||
366 | else if (ret > max) | ||
367 | return -ENOSPC; | ||
368 | else if (copy_to_user(buf, data, ret)) | ||
369 | return -EFAULT; | ||
370 | |||
371 | return ret; | ||
372 | } | ||
373 | |||
374 | static const struct file_operations local_rpc_misc_fops = { | ||
375 | .owner = THIS_MODULE, | ||
376 | .open = local_rpc_open, | ||
377 | .release = local_rpc_release, | ||
378 | .unlocked_ioctl = local_rpc_ioctl, | ||
379 | .write = local_rpc_write, | ||
380 | .read = local_rpc_read, | ||
381 | }; | ||
382 | |||
383 | static struct miscdevice local_rpc_misc_device = { | ||
384 | .minor = MISC_DYNAMIC_MINOR, | ||
385 | .name = "tegra_rpc", | ||
386 | .fops = &local_rpc_misc_fops, | ||
387 | }; | ||
388 | |||
389 | int __init rpc_local_init(void) | ||
390 | { | ||
391 | int ret; | ||
392 | |||
393 | ret = trpc_sema_init(); | ||
394 | if (ret) { | ||
395 | pr_err("%s: error in trpc_sema_init\n", __func__); | ||
396 | goto err_sema_init; | ||
397 | } | ||
398 | |||
399 | ret = misc_register(&local_rpc_misc_device); | ||
400 | if (ret) { | ||
401 | pr_err("%s: can't register misc device\n", __func__); | ||
402 | goto err_misc; | ||
403 | } | ||
404 | |||
405 | ret = trpc_node_register(&rpc_node); | ||
406 | if (ret) { | ||
407 | pr_err("%s: can't register rpc node\n", __func__); | ||
408 | goto err_node_reg; | ||
409 | } | ||
410 | return 0; | ||
411 | |||
412 | err_node_reg: | ||
413 | misc_deregister(&local_rpc_misc_device); | ||
414 | err_misc: | ||
415 | err_sema_init: | ||
416 | return ret; | ||
417 | } | ||
418 | |||
419 | module_init(rpc_local_init); | ||
diff --git a/drivers/media/video/tegra/avp/trpc_sema.c b/drivers/media/video/tegra/avp/trpc_sema.c new file mode 100644 index 00000000000..cd717a1a0ca --- /dev/null +++ b/drivers/media/video/tegra/avp/trpc_sema.c | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Google, Inc. | ||
3 | * | ||
4 | * Author: | ||
5 | * Dima Zavin <dima@android.com> | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/err.h> | ||
19 | #include <linux/file.h> | ||
20 | #include <linux/fs.h> | ||
21 | #include <linux/miscdevice.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/spinlock.h> | ||
25 | #include <linux/tegra_sema.h> | ||
26 | #include <linux/types.h> | ||
27 | #include <linux/uaccess.h> | ||
28 | #include <linux/wait.h> | ||
29 | |||
30 | #include "trpc_sema.h" | ||
31 | |||
32 | struct tegra_sema_info { | ||
33 | struct file *file; | ||
34 | wait_queue_head_t wq; | ||
35 | spinlock_t lock; | ||
36 | int count; | ||
37 | }; | ||
38 | |||
39 | static int rpc_sema_minor = -1; | ||
40 | |||
41 | static inline bool is_trpc_sema_file(struct file *file) | ||
42 | { | ||
43 | dev_t rdev = file->f_dentry->d_inode->i_rdev; | ||
44 | |||
45 | if (MAJOR(rdev) == MISC_MAJOR && MINOR(rdev) == rpc_sema_minor) | ||
46 | return true; | ||
47 | return false; | ||
48 | } | ||
49 | |||
50 | struct tegra_sema_info *trpc_sema_get_from_fd(int fd) | ||
51 | { | ||
52 | struct file *file; | ||
53 | |||
54 | file = fget(fd); | ||
55 | if (unlikely(file == NULL)) { | ||
56 | pr_err("%s: fd %d is invalid\n", __func__, fd); | ||
57 | return ERR_PTR(-EINVAL); | ||
58 | } | ||
59 | |||
60 | if (!is_trpc_sema_file(file)) { | ||
61 | pr_err("%s: fd (%d) is not a trpc_sema file\n", __func__, fd); | ||
62 | fput(file); | ||
63 | return ERR_PTR(-EINVAL); | ||
64 | } | ||
65 | |||
66 | return file->private_data; | ||
67 | } | ||
68 | |||
69 | void trpc_sema_put(struct tegra_sema_info *info) | ||
70 | { | ||
71 | if (info->file) | ||
72 | fput(info->file); | ||
73 | } | ||
74 | |||
75 | int tegra_sema_signal(struct tegra_sema_info *info) | ||
76 | { | ||
77 | unsigned long flags; | ||
78 | |||
79 | if (!info) | ||
80 | return -EINVAL; | ||
81 | |||
82 | spin_lock_irqsave(&info->lock, flags); | ||
83 | info->count++; | ||
84 | wake_up_interruptible_all(&info->wq); | ||
85 | spin_unlock_irqrestore(&info->lock, flags); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | int tegra_sema_wait(struct tegra_sema_info *info, long *timeout) | ||
90 | { | ||
91 | unsigned long flags; | ||
92 | int ret = 0; | ||
93 | unsigned long endtime; | ||
94 | long timeleft = *timeout; | ||
95 | |||
96 | *timeout = 0; | ||
97 | if (timeleft < 0) | ||
98 | timeleft = MAX_SCHEDULE_TIMEOUT; | ||
99 | |||
100 | timeleft = msecs_to_jiffies(timeleft); | ||
101 | endtime = jiffies + timeleft; | ||
102 | |||
103 | again: | ||
104 | if (timeleft) | ||
105 | ret = wait_event_interruptible_timeout(info->wq, | ||
106 | info->count > 0, | ||
107 | timeleft); | ||
108 | spin_lock_irqsave(&info->lock, flags); | ||
109 | if (info->count > 0) { | ||
110 | info->count--; | ||
111 | ret = 0; | ||
112 | } else if (ret == 0 || timeout == 0) { | ||
113 | ret = -ETIMEDOUT; | ||
114 | } else if (ret < 0) { | ||
115 | ret = -EINTR; | ||
116 | if (timeleft != MAX_SCHEDULE_TIMEOUT && | ||
117 | time_before(jiffies, endtime)) | ||
118 | *timeout = jiffies_to_msecs(endtime - jiffies); | ||
119 | else | ||
120 | *timeout = 0; | ||
121 | } else { | ||
122 | /* we woke up but someone else got the semaphore and we have | ||
123 | * time left, try again */ | ||
124 | timeleft = ret; | ||
125 | spin_unlock_irqrestore(&info->lock, flags); | ||
126 | goto again; | ||
127 | } | ||
128 | spin_unlock_irqrestore(&info->lock, flags); | ||
129 | return ret; | ||
130 | } | ||
131 | |||
132 | int tegra_sema_open(struct tegra_sema_info **sema) | ||
133 | { | ||
134 | struct tegra_sema_info *info; | ||
135 | info = kzalloc(sizeof(struct tegra_sema_info), GFP_KERNEL); | ||
136 | if (!info) | ||
137 | return -ENOMEM; | ||
138 | |||
139 | init_waitqueue_head(&info->wq); | ||
140 | spin_lock_init(&info->lock); | ||
141 | *sema = info; | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | static int trpc_sema_open(struct inode *inode, struct file *file) | ||
146 | { | ||
147 | struct tegra_sema_info *info; | ||
148 | int ret; | ||
149 | |||
150 | ret = tegra_sema_open(&info); | ||
151 | if (ret < 0) | ||
152 | return ret; | ||
153 | |||
154 | info->file = file; | ||
155 | nonseekable_open(inode, file); | ||
156 | file->private_data = info; | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | int tegra_sema_release(struct tegra_sema_info *sema) | ||
161 | { | ||
162 | kfree(sema); | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static int trpc_sema_release(struct inode *inode, struct file *file) | ||
167 | { | ||
168 | struct tegra_sema_info *info = file->private_data; | ||
169 | |||
170 | file->private_data = NULL; | ||
171 | tegra_sema_release(info); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | static long trpc_sema_ioctl(struct file *file, unsigned int cmd, | ||
176 | unsigned long arg) | ||
177 | { | ||
178 | struct tegra_sema_info *info = file->private_data; | ||
179 | int ret; | ||
180 | long timeout; | ||
181 | |||
182 | if (_IOC_TYPE(cmd) != TEGRA_SEMA_IOCTL_MAGIC || | ||
183 | _IOC_NR(cmd) < TEGRA_SEMA_IOCTL_MIN_NR || | ||
184 | _IOC_NR(cmd) > TEGRA_SEMA_IOCTL_MAX_NR) | ||
185 | return -ENOTTY; | ||
186 | else if (!info) | ||
187 | return -EINVAL; | ||
188 | |||
189 | switch (cmd) { | ||
190 | case TEGRA_SEMA_IOCTL_WAIT: | ||
191 | if (copy_from_user(&timeout, (void __user *)arg, sizeof(long))) | ||
192 | return -EFAULT; | ||
193 | ret = tegra_sema_wait(info, &timeout); | ||
194 | if (ret != -EINTR) | ||
195 | break; | ||
196 | if (copy_to_user((void __user *)arg, &timeout, sizeof(long))) | ||
197 | ret = -EFAULT; | ||
198 | break; | ||
199 | case TEGRA_SEMA_IOCTL_SIGNAL: | ||
200 | ret = tegra_sema_signal(info); | ||
201 | break; | ||
202 | default: | ||
203 | pr_err("%s: Unknown tegra_sema ioctl 0x%x\n", __func__, | ||
204 | _IOC_NR(cmd)); | ||
205 | ret = -ENOTTY; | ||
206 | break; | ||
207 | } | ||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | static const struct file_operations trpc_sema_misc_fops = { | ||
212 | .owner = THIS_MODULE, | ||
213 | .open = trpc_sema_open, | ||
214 | .release = trpc_sema_release, | ||
215 | .unlocked_ioctl = trpc_sema_ioctl, | ||
216 | }; | ||
217 | |||
218 | static struct miscdevice trpc_sema_misc_device = { | ||
219 | .minor = MISC_DYNAMIC_MINOR, | ||
220 | .name = "tegra_sema", | ||
221 | .fops = &trpc_sema_misc_fops, | ||
222 | }; | ||
223 | |||
224 | int __init trpc_sema_init(void) | ||
225 | { | ||
226 | int ret; | ||
227 | |||
228 | if (rpc_sema_minor >= 0) { | ||
229 | pr_err("%s: trpc_sema already registered\n", __func__); | ||
230 | return -EBUSY; | ||
231 | } | ||
232 | |||
233 | ret = misc_register(&trpc_sema_misc_device); | ||
234 | if (ret) { | ||
235 | pr_err("%s: can't register misc device\n", __func__); | ||
236 | return ret; | ||
237 | } | ||
238 | |||
239 | rpc_sema_minor = trpc_sema_misc_device.minor; | ||
240 | pr_info("%s: registered misc dev %d:%d\n", __func__, MISC_MAJOR, | ||
241 | rpc_sema_minor); | ||
242 | |||
243 | return 0; | ||
244 | } | ||
diff --git a/drivers/media/video/tegra/avp/trpc_sema.h b/drivers/media/video/tegra/avp/trpc_sema.h new file mode 100644 index 00000000000..2a7c42245b7 --- /dev/null +++ b/drivers/media/video/tegra/avp/trpc_sema.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Google, Inc. | ||
3 | * | ||
4 | * Author: | ||
5 | * Dima Zavin <dima@android.com> | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #ifndef __ARM_MACH_TEGRA_RPC_SEMA_H | ||
19 | #define __ARM_MACH_TEGRA_RPC_SEMA_H | ||
20 | |||
21 | #include <linux/types.h> | ||
22 | #include <linux/fs.h> | ||
23 | |||
24 | struct tegra_sema_info; | ||
25 | |||
26 | struct tegra_sema_info *trpc_sema_get_from_fd(int fd); | ||
27 | void trpc_sema_put(struct tegra_sema_info *sema); | ||
28 | int __init trpc_sema_init(void); | ||
29 | |||
30 | #endif | ||
diff --git a/drivers/media/video/tegra/mediaserver/Kconfig b/drivers/media/video/tegra/mediaserver/Kconfig new file mode 100644 index 00000000000..9e60a5b49cd --- /dev/null +++ b/drivers/media/video/tegra/mediaserver/Kconfig | |||
@@ -0,0 +1,10 @@ | |||
1 | config TEGRA_MEDIASERVER | ||
2 | bool "Tegra Media Server support" | ||
3 | depends on ARCH_TEGRA && TEGRA_RPC | ||
4 | default y | ||
5 | help | ||
6 | Enables support for the multiple OpenMAX clients. Exports the | ||
7 | interface on the device node /dev/tegra_mediaserver. | ||
8 | |||
9 | If unsure, say Y | ||
10 | |||
diff --git a/drivers/media/video/tegra/mediaserver/Makefile b/drivers/media/video/tegra/mediaserver/Makefile new file mode 100644 index 00000000000..ed24e91932b --- /dev/null +++ b/drivers/media/video/tegra/mediaserver/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | GCOV_PROFILE := y | ||
2 | obj-$(CONFIG_TEGRA_MEDIASERVER) += tegra_mediaserver.o | ||
3 | |||
diff --git a/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c b/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c new file mode 100644 index 00000000000..f6f5b1ec8b7 --- /dev/null +++ b/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c | |||
@@ -0,0 +1,556 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 NVIDIA Corp. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/miscdevice.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/uaccess.h> | ||
21 | #include <linux/mm.h> | ||
22 | |||
23 | #include <linux/tegra_mediaserver.h> | ||
24 | #include "../avp/nvavp.h" | ||
25 | #include "../../../../video/tegra/nvmap/nvmap.h" | ||
26 | |||
27 | #define CHECK_STATUS(e, tag) \ | ||
28 | do { if (e < 0) goto tag; } while (0) | ||
29 | |||
30 | #define CHECK_NULL(ptr, tag) \ | ||
31 | do { if (!ptr) goto tag; } while (0) | ||
32 | |||
33 | #define CHECK_CONDITION(c, tag) \ | ||
34 | do { if (c) goto tag; } while (0) | ||
35 | |||
36 | struct tegra_mediasrv_block { | ||
37 | struct list_head entry; | ||
38 | struct tegra_mediaserver_block_info block; | ||
39 | }; | ||
40 | |||
41 | struct tegra_mediasrv_iram { | ||
42 | struct list_head entry; | ||
43 | struct tegra_mediaserver_iram_info iram; | ||
44 | }; | ||
45 | |||
46 | struct tegra_mediasrv_node { | ||
47 | struct tegra_mediasrv_info *mediasrv; | ||
48 | struct list_head blocks; | ||
49 | int nr_iram_shared; | ||
50 | }; | ||
51 | |||
52 | struct tegra_mediasrv_manager { | ||
53 | struct tegra_avp_lib lib; | ||
54 | struct tegra_rpc_info *rpc; | ||
55 | struct tegra_sema_info *sema; | ||
56 | }; | ||
57 | |||
58 | struct tegra_mediasrv_info { | ||
59 | int minor; | ||
60 | struct mutex lock; | ||
61 | struct nvmap_client *nvmap; | ||
62 | struct tegra_avp_info *avp; | ||
63 | struct tegra_mediasrv_manager manager; | ||
64 | int nr_nodes; | ||
65 | int nr_blocks; | ||
66 | struct tegra_mediaserver_iram_info iram; /* only one supported */ | ||
67 | int nr_iram_shared; | ||
68 | }; | ||
69 | |||
70 | static struct tegra_mediasrv_info *mediasrv_info; | ||
71 | |||
72 | |||
73 | /* | ||
74 | * File entry points | ||
75 | */ | ||
76 | static int mediasrv_open(struct inode *inode, struct file *file) | ||
77 | { | ||
78 | struct tegra_mediasrv_info *mediasrv = mediasrv_info; | ||
79 | struct tegra_mediasrv_node *node = NULL; | ||
80 | struct tegra_mediasrv_manager *manager = &mediasrv->manager; | ||
81 | struct tegra_avp_lib *lib = &manager->lib; | ||
82 | int e; | ||
83 | |||
84 | node = kzalloc(sizeof(struct tegra_mediasrv_node), GFP_KERNEL); | ||
85 | CHECK_NULL(node, node_alloc_fail); | ||
86 | INIT_LIST_HEAD(&node->blocks); | ||
87 | node->mediasrv = mediasrv; | ||
88 | |||
89 | mutex_lock(&mediasrv->lock); | ||
90 | nonseekable_open(inode, file); | ||
91 | |||
92 | if (!mediasrv->nr_nodes) { | ||
93 | e = tegra_sema_open(&manager->sema); | ||
94 | CHECK_STATUS(e, fail); | ||
95 | |||
96 | e = tegra_rpc_open(&manager->rpc); | ||
97 | CHECK_STATUS(e, fail); | ||
98 | |||
99 | e = tegra_rpc_port_create(manager->rpc, "NVMM_MANAGER_SRV", | ||
100 | manager->sema); | ||
101 | CHECK_STATUS(e, fail); | ||
102 | |||
103 | e = tegra_avp_open(&mediasrv->avp); | ||
104 | CHECK_STATUS(e, fail); | ||
105 | |||
106 | memcpy(lib->name, "nvmm_manager.axf\0", | ||
107 | strlen("nvmm_manager.axf") + 1); | ||
108 | lib->args = &mediasrv; | ||
109 | lib->args_len = sizeof(unsigned long); | ||
110 | e = tegra_avp_load_lib(mediasrv->avp, lib); | ||
111 | CHECK_STATUS(e, fail); | ||
112 | |||
113 | e = tegra_rpc_port_connect(manager->rpc, 50000); | ||
114 | CHECK_STATUS(e, fail); | ||
115 | } | ||
116 | |||
117 | mediasrv->nr_nodes++; | ||
118 | try_module_get(THIS_MODULE); | ||
119 | |||
120 | mutex_unlock(&mediasrv->lock); | ||
121 | |||
122 | file->private_data = node; | ||
123 | |||
124 | return 0; | ||
125 | |||
126 | fail: | ||
127 | if (lib->handle) { | ||
128 | tegra_avp_unload_lib(mediasrv->avp, lib->handle); | ||
129 | lib->handle = 0; | ||
130 | } | ||
131 | |||
132 | if (mediasrv->avp) { | ||
133 | tegra_avp_release(mediasrv->avp); | ||
134 | mediasrv->avp = NULL; | ||
135 | } | ||
136 | |||
137 | if (manager->rpc) { | ||
138 | tegra_rpc_release(manager->rpc); | ||
139 | manager->rpc = NULL; | ||
140 | } | ||
141 | if (manager->sema) { | ||
142 | tegra_sema_release(manager->sema); | ||
143 | manager->sema = NULL; | ||
144 | } | ||
145 | |||
146 | kfree(node); | ||
147 | |||
148 | mutex_unlock(&mediasrv->lock); | ||
149 | return e; | ||
150 | |||
151 | node_alloc_fail: | ||
152 | e = -ENOMEM; | ||
153 | return e; | ||
154 | } | ||
155 | |||
156 | static int mediasrv_release(struct inode *inode, struct file *file) | ||
157 | { | ||
158 | struct tegra_mediasrv_info *mediasrv = mediasrv_info; | ||
159 | struct tegra_mediasrv_node *node = file->private_data; | ||
160 | struct tegra_mediasrv_block *block; | ||
161 | struct list_head *entry; | ||
162 | struct list_head *temp; | ||
163 | u32 message[2]; | ||
164 | int e; | ||
165 | |||
166 | mutex_lock(&mediasrv->lock); | ||
167 | |||
168 | list_for_each_safe(entry, temp, &node->blocks) { | ||
169 | block = list_entry(entry, struct tegra_mediasrv_block, entry); | ||
170 | |||
171 | pr_info("Improperly closed block found!"); | ||
172 | pr_info(" NVMM Block Handle: 0x%08x\n", | ||
173 | block->block.nvmm_block_handle); | ||
174 | pr_info(" AVP Block Handle: 0x%08x\n", | ||
175 | block->block.avp_block_handle); | ||
176 | |||
177 | message[0] = 1; /* NvmmManagerMsgType_AbnormalTerm */ | ||
178 | message[1] = block->block.avp_block_handle; | ||
179 | |||
180 | e = tegra_rpc_write(mediasrv->manager.rpc, (u8 *)message, | ||
181 | sizeof(u32) * 2); | ||
182 | pr_info("Abnormal termination message result: %d\n", e); | ||
183 | |||
184 | if (block->block.avp_block_library_handle) { | ||
185 | e = tegra_avp_unload_lib(mediasrv->avp, | ||
186 | block->block.avp_block_library_handle); | ||
187 | pr_info("Unload block (0x%08x) result: %d\n", | ||
188 | block->block.avp_block_library_handle, e); | ||
189 | } | ||
190 | |||
191 | if (block->block.service_library_handle) { | ||
192 | e = tegra_avp_unload_lib(mediasrv->avp, | ||
193 | block->block.service_library_handle); | ||
194 | pr_info("Unload service (0x%08x) result: %d\n", | ||
195 | block->block.service_library_handle, e); | ||
196 | } | ||
197 | |||
198 | mediasrv->nr_blocks--; | ||
199 | list_del(entry); | ||
200 | kfree(block); | ||
201 | } | ||
202 | |||
203 | mediasrv->nr_iram_shared -= node->nr_iram_shared; | ||
204 | if (mediasrv->iram.rm_handle && !mediasrv->nr_iram_shared) { | ||
205 | pr_info("Improperly freed shared iram found!"); | ||
206 | nvmap_unpin_ids(mediasrv->nvmap, 1, &mediasrv->iram.rm_handle); | ||
207 | nvmap_free_handle_id(mediasrv->nvmap, mediasrv->iram.rm_handle); | ||
208 | mediasrv->iram.rm_handle = 0; | ||
209 | mediasrv->iram.physical_address = 0; | ||
210 | } | ||
211 | |||
212 | kfree(node); | ||
213 | mediasrv->nr_nodes--; | ||
214 | if (!mediasrv->nr_nodes) { | ||
215 | struct tegra_mediasrv_manager *manager = &mediasrv->manager; | ||
216 | |||
217 | tegra_avp_unload_lib(mediasrv->avp, manager->lib.handle); | ||
218 | manager->lib.handle = 0; | ||
219 | |||
220 | tegra_avp_release(mediasrv->avp); | ||
221 | mediasrv->avp = NULL; | ||
222 | |||
223 | tegra_rpc_release(manager->rpc); | ||
224 | manager->rpc = NULL; | ||
225 | |||
226 | tegra_sema_release(manager->sema); | ||
227 | manager->sema = NULL; | ||
228 | } | ||
229 | |||
230 | mutex_unlock(&mediasrv->lock); | ||
231 | module_put(THIS_MODULE); | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static int mediasrv_alloc(struct tegra_mediasrv_node *node, | ||
236 | union tegra_mediaserver_alloc_info *in, | ||
237 | union tegra_mediaserver_alloc_info *out) | ||
238 | { | ||
239 | struct tegra_mediasrv_info *mediasrv = node->mediasrv; | ||
240 | int e; | ||
241 | |||
242 | switch (in->in.tegra_mediaserver_resource_type) { | ||
243 | case TEGRA_MEDIASERVER_RESOURCE_BLOCK: | ||
244 | { | ||
245 | struct tegra_mediasrv_block *block; | ||
246 | |||
247 | block = kzalloc(sizeof(struct tegra_mediasrv_block), | ||
248 | GFP_KERNEL); | ||
249 | CHECK_NULL(block, block_alloc_fail); | ||
250 | |||
251 | block->block = in->in.u.block; | ||
252 | list_add(&block->entry, &node->blocks); | ||
253 | goto block_done; | ||
254 | |||
255 | block_alloc_fail: | ||
256 | e = -ENOMEM; | ||
257 | goto fail; | ||
258 | |||
259 | block_done: | ||
260 | mediasrv->nr_blocks++; | ||
261 | out->out.u.block.count = mediasrv->nr_blocks; | ||
262 | } | ||
263 | break; | ||
264 | |||
265 | case TEGRA_MEDIASERVER_RESOURCE_IRAM: | ||
266 | { | ||
267 | if (in->in.u.iram.tegra_mediaserver_iram_type == | ||
268 | TEGRA_MEDIASERVER_IRAM_SHARED) { | ||
269 | if (!mediasrv->nr_iram_shared) { | ||
270 | size_t align, size; | ||
271 | struct nvmap_handle_ref *r = NULL; | ||
272 | unsigned long id; | ||
273 | int physical_address; | ||
274 | |||
275 | size = PAGE_ALIGN(in->in.u.iram.size); | ||
276 | r = nvmap_create_handle(mediasrv->nvmap, size); | ||
277 | CHECK_CONDITION((r < 0), | ||
278 | iram_shared_handle_fail); | ||
279 | |||
280 | id = nvmap_ref_to_id(r); | ||
281 | |||
282 | align = max_t(size_t, in->in.u.iram.alignment, | ||
283 | PAGE_SIZE); | ||
284 | e = nvmap_alloc_handle_id(mediasrv->nvmap, id, | ||
285 | NVMAP_HEAP_CARVEOUT_IRAM, align, | ||
286 | NVMAP_HANDLE_WRITE_COMBINE); | ||
287 | CHECK_STATUS(e, iram_shared_alloc_fail); | ||
288 | |||
289 | physical_address = | ||
290 | nvmap_pin_ids(mediasrv->nvmap, 1, &id); | ||
291 | CHECK_CONDITION((physical_address < 0), | ||
292 | iram_shared_pin_fail); | ||
293 | |||
294 | mediasrv->iram.rm_handle = id; | ||
295 | mediasrv->iram.physical_address = | ||
296 | physical_address; | ||
297 | goto iram_shared_done; | ||
298 | |||
299 | iram_shared_pin_fail: | ||
300 | e = physical_address; | ||
301 | iram_shared_alloc_fail: | ||
302 | nvmap_free_handle_id(mediasrv->nvmap, id); | ||
303 | iram_shared_handle_fail: | ||
304 | goto fail; | ||
305 | } | ||
306 | |||
307 | iram_shared_done: | ||
308 | out->out.u.iram.rm_handle = mediasrv->iram.rm_handle; | ||
309 | out->out.u.iram.physical_address = | ||
310 | mediasrv->iram.physical_address; | ||
311 | mediasrv->nr_iram_shared++; | ||
312 | node->nr_iram_shared++; | ||
313 | } else if (in->in.u.iram.tegra_mediaserver_iram_type == | ||
314 | TEGRA_MEDIASERVER_IRAM_SCRATCH) { | ||
315 | e = -EINVAL; | ||
316 | goto fail; | ||
317 | } | ||
318 | } | ||
319 | break; | ||
320 | |||
321 | default: | ||
322 | { | ||
323 | e = -EINVAL; | ||
324 | goto fail; | ||
325 | } | ||
326 | break; | ||
327 | } | ||
328 | |||
329 | return 0; | ||
330 | |||
331 | fail: | ||
332 | return e; | ||
333 | } | ||
334 | |||
335 | static void mediasrv_free(struct tegra_mediasrv_node *node, | ||
336 | union tegra_mediaserver_free_info *in) | ||
337 | { | ||
338 | struct tegra_mediasrv_info *mediasrv = node->mediasrv; | ||
339 | |||
340 | switch (in->in.tegra_mediaserver_resource_type) { | ||
341 | case TEGRA_MEDIASERVER_RESOURCE_BLOCK: | ||
342 | { | ||
343 | struct tegra_mediasrv_block *block = NULL; | ||
344 | struct tegra_mediasrv_block *temp; | ||
345 | struct list_head *entry; | ||
346 | |||
347 | list_for_each(entry, &node->blocks) { | ||
348 | temp = list_entry(entry, struct tegra_mediasrv_block, | ||
349 | entry); | ||
350 | if (temp->block.nvmm_block_handle != | ||
351 | in->in.u.nvmm_block_handle) | ||
352 | continue; | ||
353 | |||
354 | block = temp; | ||
355 | break; | ||
356 | } | ||
357 | |||
358 | CHECK_NULL(block, done); | ||
359 | list_del(&block->entry); | ||
360 | kfree(block); | ||
361 | } | ||
362 | break; | ||
363 | |||
364 | case TEGRA_MEDIASERVER_RESOURCE_IRAM: | ||
365 | { | ||
366 | if (in->in.u.iram_rm_handle == mediasrv->iram.rm_handle && | ||
367 | node->nr_iram_shared) { | ||
368 | node->nr_iram_shared--; | ||
369 | mediasrv->nr_iram_shared--; | ||
370 | |||
371 | if (!mediasrv->nr_iram_shared) { | ||
372 | nvmap_unpin_ids(mediasrv->nvmap, 1, | ||
373 | &mediasrv->iram.rm_handle); | ||
374 | nvmap_free_handle_id(mediasrv->nvmap, | ||
375 | mediasrv->iram.rm_handle); | ||
376 | mediasrv->iram.rm_handle = 0; | ||
377 | mediasrv->iram.physical_address = 0; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | else | ||
382 | goto done; | ||
383 | } | ||
384 | break; | ||
385 | } | ||
386 | |||
387 | done: | ||
388 | return; | ||
389 | } | ||
390 | |||
391 | static int mediasrv_update_block_info( | ||
392 | struct tegra_mediasrv_node *node, | ||
393 | union tegra_mediaserver_update_block_info *in | ||
394 | ) | ||
395 | { | ||
396 | struct tegra_mediasrv_block *entry = NULL; | ||
397 | struct tegra_mediasrv_block *block = NULL; | ||
398 | int e; | ||
399 | |||
400 | list_for_each_entry(entry, &node->blocks, entry) { | ||
401 | if (entry->block.nvmm_block_handle != in->in.nvmm_block_handle) | ||
402 | continue; | ||
403 | |||
404 | block = entry; | ||
405 | break; | ||
406 | } | ||
407 | |||
408 | CHECK_NULL(block, fail); | ||
409 | |||
410 | block->block = in->in; | ||
411 | return 0; | ||
412 | |||
413 | fail: | ||
414 | e = -EINVAL; | ||
415 | return e; | ||
416 | } | ||
417 | |||
418 | static long mediasrv_unlocked_ioctl(struct file *file, unsigned int cmd, | ||
419 | unsigned long arg) | ||
420 | { | ||
421 | struct tegra_mediasrv_info *mediasrv = mediasrv_info; | ||
422 | struct tegra_mediasrv_node *node = file->private_data; | ||
423 | int e = -ENODEV; | ||
424 | |||
425 | mutex_lock(&mediasrv->lock); | ||
426 | |||
427 | switch (cmd) { | ||
428 | case TEGRA_MEDIASERVER_IOCTL_ALLOC: | ||
429 | { | ||
430 | union tegra_mediaserver_alloc_info in, out; | ||
431 | e = copy_from_user(&in, (void __user *)arg, sizeof(in)); | ||
432 | CHECK_CONDITION(e, copy_fail); | ||
433 | e = mediasrv_alloc(node, &in, &out); | ||
434 | CHECK_STATUS(e, fail); | ||
435 | e = copy_to_user((void __user *)arg, &out, sizeof(out)); | ||
436 | CHECK_CONDITION(e, copy_fail); | ||
437 | } | ||
438 | break; | ||
439 | |||
440 | case TEGRA_MEDIASERVER_IOCTL_FREE: | ||
441 | { | ||
442 | union tegra_mediaserver_free_info in; | ||
443 | e = copy_from_user(&in, (void __user *)arg, sizeof(in)); | ||
444 | CHECK_CONDITION(e, copy_fail); | ||
445 | mediasrv_free(node, &in); | ||
446 | } | ||
447 | break; | ||
448 | |||
449 | case TEGRA_MEDIASERVER_IOCTL_UPDATE_BLOCK_INFO: | ||
450 | { | ||
451 | union tegra_mediaserver_update_block_info in; | ||
452 | e = copy_from_user(&in, (void __user *)arg, sizeof(in)); | ||
453 | CHECK_CONDITION(e, copy_fail); | ||
454 | e = mediasrv_update_block_info(node, &in); | ||
455 | CHECK_CONDITION(e, fail); | ||
456 | } | ||
457 | break; | ||
458 | |||
459 | default: | ||
460 | { | ||
461 | e = -ENODEV; | ||
462 | goto fail; | ||
463 | } | ||
464 | break; | ||
465 | } | ||
466 | |||
467 | mutex_unlock(&mediasrv->lock); | ||
468 | return 0; | ||
469 | |||
470 | copy_fail: | ||
471 | e = -EFAULT; | ||
472 | fail: | ||
473 | mutex_unlock(&mediasrv->lock); | ||
474 | return e; | ||
475 | } | ||
476 | |||
477 | /* | ||
478 | * Kernel structures and entry points | ||
479 | */ | ||
480 | static const struct file_operations mediaserver_fops = { | ||
481 | .owner = THIS_MODULE, | ||
482 | .open = mediasrv_open, | ||
483 | .release = mediasrv_release, | ||
484 | .unlocked_ioctl = mediasrv_unlocked_ioctl, | ||
485 | }; | ||
486 | |||
487 | static struct miscdevice mediaserver_misc_device = { | ||
488 | .minor = MISC_DYNAMIC_MINOR, | ||
489 | .name = "tegra_mediaserver", | ||
490 | .fops = &mediaserver_fops, | ||
491 | }; | ||
492 | |||
493 | static int __init tegra_mediaserver_init(void) | ||
494 | { | ||
495 | struct tegra_mediasrv_info *mediasrv; | ||
496 | int e = 0; | ||
497 | |||
498 | CHECK_NULL(!mediasrv_info, busy); | ||
499 | |||
500 | mediasrv = kzalloc(sizeof(struct tegra_mediasrv_info), GFP_KERNEL); | ||
501 | CHECK_NULL(mediasrv, alloc_fail); | ||
502 | |||
503 | mediasrv->nvmap = nvmap_create_client(nvmap_dev, "tegra_mediaserver"); | ||
504 | CHECK_NULL(mediasrv, nvmap_create_fail); | ||
505 | |||
506 | e = misc_register(&mediaserver_misc_device); | ||
507 | CHECK_STATUS(e, register_fail); | ||
508 | |||
509 | mediasrv->nr_nodes = 0; | ||
510 | mutex_init(&mediasrv->lock); | ||
511 | |||
512 | mediasrv_info = mediasrv; | ||
513 | goto done; | ||
514 | |||
515 | nvmap_create_fail: | ||
516 | e = -ENOMEM; | ||
517 | kfree(mediasrv); | ||
518 | goto done; | ||
519 | |||
520 | register_fail: | ||
521 | nvmap_client_put(mediasrv->nvmap); | ||
522 | kfree(mediasrv); | ||
523 | goto done; | ||
524 | |||
525 | alloc_fail: | ||
526 | e = -ENOMEM; | ||
527 | goto done; | ||
528 | |||
529 | busy: | ||
530 | e = -EBUSY; | ||
531 | goto done; | ||
532 | |||
533 | done: | ||
534 | return e; | ||
535 | } | ||
536 | |||
537 | void __exit tegra_mediaserver_cleanup(void) | ||
538 | { | ||
539 | struct tegra_mediasrv_info *mediasrv = mediasrv_info; | ||
540 | int e; | ||
541 | |||
542 | e = misc_deregister(&mediaserver_misc_device); | ||
543 | CHECK_STATUS(e, fail); | ||
544 | |||
545 | nvmap_client_put(mediasrv->nvmap); | ||
546 | kfree(mediasrv); | ||
547 | mediasrv_info = NULL; | ||
548 | |||
549 | fail: | ||
550 | return; | ||
551 | } | ||
552 | |||
553 | module_init(tegra_mediaserver_init); | ||
554 | module_exit(tegra_mediaserver_cleanup); | ||
555 | MODULE_LICENSE("GPL"); | ||
556 | |||
diff --git a/drivers/media/video/tegra/nvavp/Kconfig b/drivers/media/video/tegra/nvavp/Kconfig new file mode 100644 index 00000000000..2d3af3f79fb --- /dev/null +++ b/drivers/media/video/tegra/nvavp/Kconfig | |||
@@ -0,0 +1,10 @@ | |||
1 | config TEGRA_NVAVP | ||
2 | bool "Enable support for Tegra NVAVP driver" | ||
3 | depends on ARCH_TEGRA && TEGRA_GRHOST | ||
4 | default n | ||
5 | help | ||
6 | Enables support for the push-buffer mechanism based driver for the Tegra | ||
7 | multimedia framework. Exports the Tegra nvavp interface on device node | ||
8 | /dev/tegra_avpchannel. | ||
9 | |||
10 | If unsure, say N | ||
diff --git a/drivers/media/video/tegra/nvavp/Makefile b/drivers/media/video/tegra/nvavp/Makefile new file mode 100644 index 00000000000..82b4238fd08 --- /dev/null +++ b/drivers/media/video/tegra/nvavp/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | GCOV_PROFILE := y | ||
2 | obj-$(CONFIG_TEGRA_NVAVP) += nvavp_dev.o | ||
3 | obj-$(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) += ../avp/headavp.o | ||
diff --git a/drivers/media/video/tegra/nvavp/nvavp_dev.c b/drivers/media/video/tegra/nvavp/nvavp_dev.c new file mode 100644 index 00000000000..407e35b40c4 --- /dev/null +++ b/drivers/media/video/tegra/nvavp/nvavp_dev.c | |||
@@ -0,0 +1,1441 @@ | |||
1 | /* | ||
2 | * drivers/media/video/tegra/nvavp/nvavp_dev.c | ||
3 | * | ||
4 | * Copyright (C) 2011-2012 NVIDIA Corp. | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public License | ||
7 | * version 2. This program is licensed "as is" without any warranty of any | ||
8 | * kind, whether express or implied. | ||
9 | */ | ||
10 | |||
11 | #include <linux/uaccess.h> | ||
12 | #include <linux/clk.h> | ||
13 | #include <linux/completion.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/dma-mapping.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/firmware.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/ioctl.h> | ||
22 | #include <linux/irq.h> | ||
23 | #include <linux/kref.h> | ||
24 | #include <linux/list.h> | ||
25 | #include <linux/miscdevice.h> | ||
26 | #include <linux/mutex.h> | ||
27 | #include <linux/nvhost.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/rbtree.h> | ||
30 | #include <linux/seq_file.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/string.h> | ||
33 | #include <linux/tegra_nvavp.h> | ||
34 | #include <linux/types.h> | ||
35 | #include <linux/vmalloc.h> | ||
36 | #include <linux/workqueue.h> | ||
37 | |||
38 | #include <mach/clk.h> | ||
39 | #include <mach/hardware.h> | ||
40 | #include <mach/io.h> | ||
41 | #include <mach/iomap.h> | ||
42 | #include <mach/legacy_irq.h> | ||
43 | #include <mach/nvmap.h> | ||
44 | |||
45 | #include "../../../../video/tegra/nvmap/nvmap.h" | ||
46 | #include "../../../../video/tegra/host/host1x/host1x_syncpt.h" | ||
47 | #include "../../../../video/tegra/host/dev.h" | ||
48 | #include "../../../../video/tegra/host/nvhost_acm.h" | ||
49 | |||
50 | #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) | ||
51 | #include "../avp/headavp.h" | ||
52 | #endif | ||
53 | #include "nvavp_os.h" | ||
54 | |||
55 | #define TEGRA_NVAVP_NAME "nvavp" | ||
56 | |||
57 | #define NVAVP_PUSHBUFFER_SIZE 4096 | ||
58 | |||
59 | #define NVAVP_PUSHBUFFER_MIN_UPDATE_SPACE (sizeof(u32) * 3) | ||
60 | |||
61 | #define TEGRA_NVAVP_RESET_VECTOR_ADDR \ | ||
62 | (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x200) | ||
63 | |||
64 | #define FLOW_CTRL_HALT_COP_EVENTS IO_ADDRESS(TEGRA_FLOW_CTRL_BASE + 0x4) | ||
65 | #define FLOW_MODE_STOP (0x2 << 29) | ||
66 | #define FLOW_MODE_NONE 0x0 | ||
67 | |||
68 | #define NVAVP_OS_INBOX IO_ADDRESS(TEGRA_RES_SEMA_BASE + 0x10) | ||
69 | #define NVAVP_OS_OUTBOX IO_ADDRESS(TEGRA_RES_SEMA_BASE + 0x20) | ||
70 | |||
71 | #define NVAVP_INBOX_VALID (1 << 29) | ||
72 | |||
73 | /* AVP behavior params */ | ||
74 | #define NVAVP_OS_IDLE_TIMEOUT 100 /* milli-seconds */ | ||
75 | |||
76 | struct nvavp_info { | ||
77 | u32 clk_enabled; | ||
78 | struct clk *bsev_clk; | ||
79 | struct clk *vde_clk; | ||
80 | struct clk *cop_clk; | ||
81 | |||
82 | /* used for dvfs */ | ||
83 | struct clk *sclk; | ||
84 | struct clk *emc_clk; | ||
85 | unsigned long sclk_rate; | ||
86 | unsigned long emc_clk_rate; | ||
87 | |||
88 | int mbox_from_avp_pend_irq; | ||
89 | |||
90 | struct mutex open_lock; | ||
91 | int refcount; | ||
92 | int initialized; | ||
93 | |||
94 | struct work_struct clock_disable_work; | ||
95 | |||
96 | /* os information */ | ||
97 | struct nvavp_os_info os_info; | ||
98 | |||
99 | /* ucode information */ | ||
100 | struct nvavp_ucode_info ucode_info; | ||
101 | |||
102 | /* client for driver allocations, persistent */ | ||
103 | struct nvmap_client *nvmap; | ||
104 | |||
105 | struct mutex pushbuffer_lock; | ||
106 | struct nvmap_handle_ref *pushbuf_handle; | ||
107 | unsigned long pushbuf_phys; | ||
108 | u8 *pushbuf_data; | ||
109 | u32 pushbuf_index; | ||
110 | u32 pushbuf_fence; | ||
111 | |||
112 | struct nv_e276_control *os_control; | ||
113 | |||
114 | struct nvhost_syncpt *nvhost_syncpt; | ||
115 | u32 syncpt_id; | ||
116 | u32 syncpt_value; | ||
117 | |||
118 | struct nvhost_device *nvhost_dev; | ||
119 | struct miscdevice misc_dev; | ||
120 | }; | ||
121 | |||
122 | struct nvavp_clientctx { | ||
123 | struct nvmap_client *nvmap; | ||
124 | struct nvavp_pushbuffer_submit_hdr submit_hdr; | ||
125 | struct nvavp_reloc relocs[NVAVP_MAX_RELOCATION_COUNT]; | ||
126 | struct nvmap_handle_ref *gather_mem; | ||
127 | int num_relocs; | ||
128 | struct nvavp_info *nvavp; | ||
129 | }; | ||
130 | |||
131 | static struct clk *nvavp_clk_get(struct nvavp_info *nvavp, int id) | ||
132 | { | ||
133 | if (!nvavp) | ||
134 | return NULL; | ||
135 | |||
136 | if (id == NVAVP_MODULE_ID_AVP) | ||
137 | return nvavp->sclk; | ||
138 | if (id == NVAVP_MODULE_ID_VDE) | ||
139 | return nvavp->vde_clk; | ||
140 | if (id == NVAVP_MODULE_ID_EMC) | ||
141 | return nvavp->emc_clk; | ||
142 | |||
143 | return NULL; | ||
144 | } | ||
145 | |||
146 | static void nvavp_clk_ctrl(struct nvavp_info *nvavp, u32 clk_en) | ||
147 | { | ||
148 | if (clk_en && !nvavp->clk_enabled) { | ||
149 | nvhost_module_busy(nvhost_get_host(nvavp->nvhost_dev)->dev); | ||
150 | clk_enable(nvavp->bsev_clk); | ||
151 | clk_enable(nvavp->vde_clk); | ||
152 | clk_set_rate(nvavp->emc_clk, nvavp->emc_clk_rate); | ||
153 | clk_set_rate(nvavp->sclk, nvavp->sclk_rate); | ||
154 | nvavp->clk_enabled = 1; | ||
155 | dev_dbg(&nvavp->nvhost_dev->dev, "%s: setting sclk to %lu\n", | ||
156 | __func__, nvavp->sclk_rate); | ||
157 | dev_dbg(&nvavp->nvhost_dev->dev, "%s: setting emc_clk to %lu\n", | ||
158 | __func__, nvavp->emc_clk_rate); | ||
159 | } else if (!clk_en && nvavp->clk_enabled) { | ||
160 | clk_disable(nvavp->bsev_clk); | ||
161 | clk_disable(nvavp->vde_clk); | ||
162 | clk_set_rate(nvavp->emc_clk, 0); | ||
163 | clk_set_rate(nvavp->sclk, 0); | ||
164 | nvhost_module_idle(nvhost_get_host(nvavp->nvhost_dev)->dev); | ||
165 | nvavp->clk_enabled = 0; | ||
166 | dev_dbg(&nvavp->nvhost_dev->dev, "%s: resetting emc_clk " | ||
167 | "and sclk\n", __func__); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | static u32 nvavp_check_idle(struct nvavp_info *nvavp) | ||
172 | { | ||
173 | struct nv_e276_control *control = nvavp->os_control; | ||
174 | return (control->put == control->get) ? 1 : 0; | ||
175 | } | ||
176 | |||
177 | static void clock_disable_handler(struct work_struct *work) | ||
178 | { | ||
179 | struct nvavp_info *nvavp; | ||
180 | |||
181 | nvavp = container_of(work, struct nvavp_info, | ||
182 | clock_disable_work); | ||
183 | |||
184 | mutex_lock(&nvavp->pushbuffer_lock); | ||
185 | nvavp_clk_ctrl(nvavp, !nvavp_check_idle(nvavp)); | ||
186 | mutex_unlock(&nvavp->pushbuffer_lock); | ||
187 | } | ||
188 | |||
189 | static int nvavp_service(struct nvavp_info *nvavp) | ||
190 | { | ||
191 | struct nvavp_os_info *os = &nvavp->os_info; | ||
192 | u8 *debug_print; | ||
193 | u32 inbox; | ||
194 | |||
195 | inbox = readl(NVAVP_OS_INBOX); | ||
196 | if (!(inbox & NVAVP_INBOX_VALID)) | ||
197 | inbox = 0x00000000; | ||
198 | |||
199 | writel(0x00000000, NVAVP_OS_INBOX); | ||
200 | |||
201 | if (inbox & NVE276_OS_INTERRUPT_VIDEO_IDLE) | ||
202 | schedule_work(&nvavp->clock_disable_work); | ||
203 | |||
204 | if (inbox & NVE276_OS_INTERRUPT_DEBUG_STRING) { | ||
205 | /* Should only occur with debug AVP OS builds */ | ||
206 | debug_print = os->data; | ||
207 | debug_print += os->debug_offset; | ||
208 | dev_info(&nvavp->nvhost_dev->dev, "%s\n", debug_print); | ||
209 | } | ||
210 | if (inbox & (NVE276_OS_INTERRUPT_SEMAPHORE_AWAKEN | | ||
211 | NVE276_OS_INTERRUPT_EXECUTE_AWAKEN)) { | ||
212 | dev_info(&nvavp->nvhost_dev->dev, | ||
213 | "AVP awaken event (0x%x)\n", inbox); | ||
214 | } | ||
215 | if (inbox & NVE276_OS_INTERRUPT_AVP_FATAL_ERROR) { | ||
216 | dev_err(&nvavp->nvhost_dev->dev, | ||
217 | "fatal AVP error (0x%08X)\n", inbox); | ||
218 | } | ||
219 | if (inbox & NVE276_OS_INTERRUPT_AVP_BREAKPOINT) | ||
220 | dev_err(&nvavp->nvhost_dev->dev, "AVP breakpoint hit\n"); | ||
221 | if (inbox & NVE276_OS_INTERRUPT_TIMEOUT) | ||
222 | dev_err(&nvavp->nvhost_dev->dev, "AVP timeout\n"); | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static irqreturn_t nvavp_mbox_pending_isr(int irq, void *data) | ||
228 | { | ||
229 | struct nvavp_info *nvavp = data; | ||
230 | |||
231 | nvavp_service(nvavp); | ||
232 | |||
233 | return IRQ_HANDLED; | ||
234 | } | ||
235 | |||
236 | static void nvavp_halt_avp(struct nvavp_info *nvavp) | ||
237 | { | ||
238 | /* ensure the AVP is halted */ | ||
239 | writel(FLOW_MODE_STOP, FLOW_CTRL_HALT_COP_EVENTS); | ||
240 | tegra_periph_reset_assert(nvavp->cop_clk); | ||
241 | |||
242 | writel(0, NVAVP_OS_OUTBOX); | ||
243 | writel(0, NVAVP_OS_INBOX); | ||
244 | } | ||
245 | |||
246 | static int nvavp_reset_avp(struct nvavp_info *nvavp, unsigned long reset_addr) | ||
247 | { | ||
248 | #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) | ||
249 | unsigned long stub_code_phys = virt_to_phys(_tegra_avp_boot_stub); | ||
250 | dma_addr_t stub_data_phys; | ||
251 | |||
252 | _tegra_avp_boot_stub_data.map_phys_addr = avp->kernel_phys; | ||
253 | _tegra_avp_boot_stub_data.jump_addr = reset_addr; | ||
254 | wmb(); | ||
255 | stub_data_phys = dma_map_single(NULL, &_tegra_avp_boot_stub_data, | ||
256 | sizeof(_tegra_avp_boot_stub_data), | ||
257 | DMA_TO_DEVICE); | ||
258 | rmb(); | ||
259 | reset_addr = (unsigned long)stub_data_phys; | ||
260 | #endif | ||
261 | writel(FLOW_MODE_STOP, FLOW_CTRL_HALT_COP_EVENTS); | ||
262 | |||
263 | writel(reset_addr, TEGRA_NVAVP_RESET_VECTOR_ADDR); | ||
264 | |||
265 | clk_enable(nvavp->sclk); | ||
266 | clk_enable(nvavp->emc_clk); | ||
267 | |||
268 | /* If sclk_rate and emc_clk is not set by user space, | ||
269 | * max clock in dvfs table will be used to get best performance. | ||
270 | */ | ||
271 | nvavp->sclk_rate = ULONG_MAX; | ||
272 | nvavp->emc_clk_rate = ULONG_MAX; | ||
273 | |||
274 | tegra_periph_reset_assert(nvavp->cop_clk); | ||
275 | udelay(2); | ||
276 | tegra_periph_reset_deassert(nvavp->cop_clk); | ||
277 | |||
278 | writel(FLOW_MODE_NONE, FLOW_CTRL_HALT_COP_EVENTS); | ||
279 | |||
280 | #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) | ||
281 | dma_unmap_single(NULL, stub_data_phys, | ||
282 | sizeof(_tegra_avp_boot_stub_data), | ||
283 | DMA_TO_DEVICE); | ||
284 | #endif | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static void nvavp_halt_vde(struct nvavp_info *nvavp) | ||
289 | { | ||
290 | if (nvavp->clk_enabled) { | ||
291 | tegra_periph_reset_assert(nvavp->bsev_clk); | ||
292 | clk_disable(nvavp->bsev_clk); | ||
293 | tegra_periph_reset_assert(nvavp->vde_clk); | ||
294 | clk_disable(nvavp->vde_clk); | ||
295 | nvhost_module_idle(nvhost_get_host(nvavp->nvhost_dev)->dev); | ||
296 | nvavp->clk_enabled = 0; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | static int nvavp_reset_vde(struct nvavp_info *nvavp) | ||
301 | { | ||
302 | if (!nvavp->clk_enabled) | ||
303 | nvhost_module_busy(nvhost_get_host(nvavp->nvhost_dev)->dev); | ||
304 | |||
305 | clk_enable(nvavp->bsev_clk); | ||
306 | tegra_periph_reset_assert(nvavp->bsev_clk); | ||
307 | udelay(2); | ||
308 | tegra_periph_reset_deassert(nvavp->bsev_clk); | ||
309 | |||
310 | clk_enable(nvavp->vde_clk); | ||
311 | tegra_periph_reset_assert(nvavp->vde_clk); | ||
312 | udelay(2); | ||
313 | tegra_periph_reset_deassert(nvavp->vde_clk); | ||
314 | |||
315 | /* | ||
316 | * VDE clock is set to max freq by default. | ||
317 | * VDE clock can be set to different freq if needed | ||
318 | * through ioctl. | ||
319 | */ | ||
320 | clk_set_rate(nvavp->vde_clk, ULONG_MAX); | ||
321 | |||
322 | nvavp->clk_enabled = 1; | ||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | static int nvavp_pushbuffer_alloc(struct nvavp_info *nvavp) | ||
327 | { | ||
328 | int ret = 0; | ||
329 | |||
330 | nvavp->pushbuf_handle = nvmap_alloc(nvavp->nvmap, NVAVP_PUSHBUFFER_SIZE, | ||
331 | SZ_1M, NVMAP_HANDLE_UNCACHEABLE, 0); | ||
332 | if (IS_ERR(nvavp->pushbuf_handle)) { | ||
333 | dev_err(&nvavp->nvhost_dev->dev, | ||
334 | "cannot create pushbuffer handle\n"); | ||
335 | ret = PTR_ERR(nvavp->pushbuf_handle); | ||
336 | goto err_pushbuf_alloc; | ||
337 | } | ||
338 | nvavp->pushbuf_data = (u8 *)nvmap_mmap(nvavp->pushbuf_handle); | ||
339 | if (!nvavp->pushbuf_data) { | ||
340 | dev_err(&nvavp->nvhost_dev->dev, | ||
341 | "cannot map pushbuffer handle\n"); | ||
342 | ret = -ENOMEM; | ||
343 | goto err_pushbuf_mmap; | ||
344 | } | ||
345 | nvavp->pushbuf_phys = nvmap_pin(nvavp->nvmap, nvavp->pushbuf_handle); | ||
346 | if (IS_ERR((void *)nvavp->pushbuf_phys)) { | ||
347 | dev_err(&nvavp->nvhost_dev->dev, | ||
348 | "cannot pin pushbuffer handle\n"); | ||
349 | ret = PTR_ERR((void *)nvavp->pushbuf_phys); | ||
350 | goto err_pushbuf_pin; | ||
351 | } | ||
352 | |||
353 | memset(nvavp->pushbuf_data, 0, NVAVP_PUSHBUFFER_SIZE); | ||
354 | |||
355 | return 0; | ||
356 | |||
357 | err_pushbuf_pin: | ||
358 | nvmap_munmap(nvavp->pushbuf_handle, nvavp->pushbuf_data); | ||
359 | err_pushbuf_mmap: | ||
360 | nvmap_free(nvavp->nvmap, nvavp->pushbuf_handle); | ||
361 | err_pushbuf_alloc: | ||
362 | return ret; | ||
363 | } | ||
364 | |||
365 | static void nvavp_pushbuffer_free(struct nvavp_info *nvavp) | ||
366 | { | ||
367 | nvmap_unpin(nvavp->nvmap, nvavp->pushbuf_handle); | ||
368 | nvmap_munmap(nvavp->pushbuf_handle, nvavp->pushbuf_data); | ||
369 | nvmap_free(nvavp->nvmap, nvavp->pushbuf_handle); | ||
370 | } | ||
371 | |||
372 | static int nvavp_pushbuffer_init(struct nvavp_info *nvavp) | ||
373 | { | ||
374 | void *ptr; | ||
375 | struct nvavp_os_info *os = &nvavp->os_info; | ||
376 | struct nv_e276_control *control; | ||
377 | u32 temp; | ||
378 | int ret; | ||
379 | |||
380 | ret = nvavp_pushbuffer_alloc(nvavp); | ||
381 | if (ret) { | ||
382 | dev_err(&nvavp->nvhost_dev->dev, | ||
383 | "unable to alloc pushbuffer\n"); | ||
384 | return ret; | ||
385 | } | ||
386 | |||
387 | ptr = os->data; | ||
388 | ptr += os->control_offset; | ||
389 | nvavp->os_control = (struct nv_e276_control *)ptr; | ||
390 | |||
391 | control = nvavp->os_control; | ||
392 | memset(control, 0, sizeof(struct nvavp_os_info)); | ||
393 | |||
394 | /* init get and put pointers */ | ||
395 | writel(0x0, &control->put); | ||
396 | writel(0x0, &control->get); | ||
397 | |||
398 | /* enable avp VDE clock control and disable iram clock gating */ | ||
399 | writel(0x0, &control->idle_clk_enable); | ||
400 | writel(0x0, &control->iram_clk_gating); | ||
401 | |||
402 | /* enable avp idle timeout interrupt */ | ||
403 | writel(0x1, &control->idle_notify_enable); | ||
404 | writel(NVAVP_OS_IDLE_TIMEOUT, &control->idle_notify_delay); | ||
405 | |||
406 | /* init dma start and end pointers */ | ||
407 | writel(nvavp->pushbuf_phys, &control->dma_start); | ||
408 | writel((nvavp->pushbuf_phys + NVAVP_PUSHBUFFER_SIZE), | ||
409 | &control->dma_end); | ||
410 | |||
411 | writel(0x00, &nvavp->pushbuf_index); | ||
412 | temp = NVAVP_PUSHBUFFER_SIZE - NVAVP_PUSHBUFFER_MIN_UPDATE_SPACE; | ||
413 | writel(temp, &nvavp->pushbuf_fence); | ||
414 | |||
415 | nvavp->syncpt_id = NVSYNCPT_AVP_0; | ||
416 | nvavp->syncpt_value = nvhost_syncpt_read(nvavp->nvhost_syncpt, | ||
417 | nvavp->syncpt_id); | ||
418 | |||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | static void nvavp_pushbuffer_deinit(struct nvavp_info *nvavp) | ||
423 | { | ||
424 | nvavp_pushbuffer_free(nvavp); | ||
425 | } | ||
426 | |||
427 | static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr, | ||
428 | u32 gather_count, struct nvavp_syncpt *syncpt, | ||
429 | u32 ext_ucode_flag) | ||
430 | { | ||
431 | struct nv_e276_control *control = nvavp->os_control; | ||
432 | u32 gather_cmd, setucode_cmd, sync = 0; | ||
433 | u32 wordcount = 0; | ||
434 | u32 index, value = -1; | ||
435 | |||
436 | mutex_lock(&nvavp->pushbuffer_lock); | ||
437 | |||
438 | /* check for pushbuffer wrapping */ | ||
439 | if (nvavp->pushbuf_index >= nvavp->pushbuf_fence) | ||
440 | nvavp->pushbuf_index = 0; | ||
441 | |||
442 | if (!ext_ucode_flag) { | ||
443 | setucode_cmd = | ||
444 | NVE26E_CH_OPCODE_INCR(NVE276_SET_MICROCODE_A, 3); | ||
445 | |||
446 | index = wordcount + nvavp->pushbuf_index; | ||
447 | writel(setucode_cmd, (nvavp->pushbuf_data + index)); | ||
448 | wordcount += sizeof(u32); | ||
449 | |||
450 | index = wordcount + nvavp->pushbuf_index; | ||
451 | writel(0, (nvavp->pushbuf_data + index)); | ||
452 | wordcount += sizeof(u32); | ||
453 | |||
454 | index = wordcount + nvavp->pushbuf_index; | ||
455 | writel(nvavp->ucode_info.phys, (nvavp->pushbuf_data + index)); | ||
456 | wordcount += sizeof(u32); | ||
457 | |||
458 | index = wordcount + nvavp->pushbuf_index; | ||
459 | writel(nvavp->ucode_info.size, (nvavp->pushbuf_data + index)); | ||
460 | wordcount += sizeof(u32); | ||
461 | } | ||
462 | |||
463 | gather_cmd = NVE26E_CH_OPCODE_GATHER(0, 0, 0, gather_count); | ||
464 | |||
465 | if (syncpt) { | ||
466 | value = ++nvavp->syncpt_value; | ||
467 | /* XXX: NvSchedValueWrappingComparison */ | ||
468 | sync = NVE26E_CH_OPCODE_IMM(NVE26E_HOST1X_INCR_SYNCPT, | ||
469 | (NVE26E_HOST1X_INCR_SYNCPT_COND_OP_DONE << 8) | | ||
470 | (nvavp->syncpt_id & 0xFF)); | ||
471 | } | ||
472 | |||
473 | /* write commands out */ | ||
474 | index = wordcount + nvavp->pushbuf_index; | ||
475 | writel(gather_cmd, (nvavp->pushbuf_data + index)); | ||
476 | wordcount += sizeof(u32); | ||
477 | |||
478 | index = wordcount + nvavp->pushbuf_index; | ||
479 | writel(phys_addr, (nvavp->pushbuf_data + index)); | ||
480 | wordcount += sizeof(u32); | ||
481 | |||
482 | if (syncpt) { | ||
483 | index = wordcount + nvavp->pushbuf_index; | ||
484 | writel(sync, (nvavp->pushbuf_data + index)); | ||
485 | wordcount += sizeof(u32); | ||
486 | } | ||
487 | |||
488 | /* enable clocks to VDE/BSEV */ | ||
489 | nvavp_clk_ctrl(nvavp, 1); | ||
490 | |||
491 | /* update put pointer */ | ||
492 | nvavp->pushbuf_index = (nvavp->pushbuf_index + wordcount) & | ||
493 | (NVAVP_PUSHBUFFER_SIZE - 1); | ||
494 | writel(nvavp->pushbuf_index, &control->put); | ||
495 | wmb(); | ||
496 | |||
497 | /* wake up avp */ | ||
498 | writel(0xA0000001, NVAVP_OS_OUTBOX); | ||
499 | |||
500 | /* Fill out fence struct */ | ||
501 | if (syncpt) { | ||
502 | syncpt->id = nvavp->syncpt_id; | ||
503 | syncpt->value = value; | ||
504 | } | ||
505 | |||
506 | mutex_unlock(&nvavp->pushbuffer_lock); | ||
507 | |||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | static void nvavp_unload_ucode(struct nvavp_info *nvavp) | ||
512 | { | ||
513 | nvmap_unpin(nvavp->nvmap, nvavp->ucode_info.handle); | ||
514 | nvmap_munmap(nvavp->ucode_info.handle, nvavp->ucode_info.data); | ||
515 | nvmap_free(nvavp->nvmap, nvavp->ucode_info.handle); | ||
516 | kfree(nvavp->ucode_info.ucode_bin); | ||
517 | } | ||
518 | |||
519 | static int nvavp_load_ucode(struct nvavp_info *nvavp) | ||
520 | { | ||
521 | struct nvavp_ucode_info *ucode_info = &nvavp->ucode_info; | ||
522 | const struct firmware *nvavp_ucode_fw; | ||
523 | char fw_ucode_file[32]; | ||
524 | void *ptr; | ||
525 | int ret = 0; | ||
526 | |||
527 | if (!ucode_info->ucode_bin) { | ||
528 | sprintf(fw_ucode_file, "nvavp_vid_ucode.bin"); | ||
529 | |||
530 | ret = request_firmware(&nvavp_ucode_fw, fw_ucode_file, | ||
531 | nvavp->misc_dev.this_device); | ||
532 | if (ret) { | ||
533 | /* Try alternative version */ | ||
534 | sprintf(fw_ucode_file, "nvavp_vid_ucode_alt.bin"); | ||
535 | |||
536 | ret = request_firmware(&nvavp_ucode_fw, | ||
537 | fw_ucode_file, | ||
538 | nvavp->misc_dev.this_device); | ||
539 | |||
540 | if (ret) { | ||
541 | dev_err(&nvavp->nvhost_dev->dev, | ||
542 | "cannot read ucode firmware '%s'\n", | ||
543 | fw_ucode_file); | ||
544 | goto err_req_ucode; | ||
545 | } | ||
546 | } | ||
547 | |||
548 | dev_info(&nvavp->nvhost_dev->dev, | ||
549 | "read ucode firmware from '%s' (%d bytes)\n", | ||
550 | fw_ucode_file, nvavp_ucode_fw->size); | ||
551 | |||
552 | ptr = (void *)nvavp_ucode_fw->data; | ||
553 | |||
554 | if (strncmp((const char *)ptr, "NVAVPAPP", 8)) { | ||
555 | dev_info(&nvavp->nvhost_dev->dev, | ||
556 | "ucode hdr string mismatch\n"); | ||
557 | ret = -EINVAL; | ||
558 | goto err_req_ucode; | ||
559 | } | ||
560 | ptr += 8; | ||
561 | ucode_info->size = nvavp_ucode_fw->size - 8; | ||
562 | |||
563 | ucode_info->ucode_bin = kzalloc(ucode_info->size, | ||
564 | GFP_KERNEL); | ||
565 | if (!ucode_info->ucode_bin) { | ||
566 | dev_err(&nvavp->nvhost_dev->dev, | ||
567 | "cannot allocate ucode bin\n"); | ||
568 | ret = -ENOMEM; | ||
569 | goto err_ubin_alloc; | ||
570 | } | ||
571 | |||
572 | ucode_info->handle = nvmap_alloc(nvavp->nvmap, | ||
573 | nvavp->ucode_info.size, | ||
574 | SZ_1M, NVMAP_HANDLE_UNCACHEABLE, 0); | ||
575 | if (IS_ERR(ucode_info->handle)) { | ||
576 | dev_err(&nvavp->nvhost_dev->dev, | ||
577 | "cannot create ucode handle\n"); | ||
578 | ret = PTR_ERR(ucode_info->handle); | ||
579 | goto err_ucode_alloc; | ||
580 | } | ||
581 | ucode_info->data = (u8 *)nvmap_mmap(ucode_info->handle); | ||
582 | if (!ucode_info->data) { | ||
583 | dev_err(&nvavp->nvhost_dev->dev, | ||
584 | "cannot map ucode handle\n"); | ||
585 | ret = -ENOMEM; | ||
586 | goto err_ucode_mmap; | ||
587 | } | ||
588 | ucode_info->phys = nvmap_pin(nvavp->nvmap, ucode_info->handle); | ||
589 | if (IS_ERR((void *)ucode_info->phys)) { | ||
590 | dev_err(&nvavp->nvhost_dev->dev, | ||
591 | "cannot pin ucode handle\n"); | ||
592 | ret = PTR_ERR((void *)ucode_info->phys); | ||
593 | goto err_ucode_pin; | ||
594 | } | ||
595 | memcpy(ucode_info->ucode_bin, ptr, ucode_info->size); | ||
596 | release_firmware(nvavp_ucode_fw); | ||
597 | } | ||
598 | |||
599 | memcpy(ucode_info->data, ucode_info->ucode_bin, ucode_info->size); | ||
600 | return 0; | ||
601 | |||
602 | err_ucode_pin: | ||
603 | nvmap_munmap(ucode_info->handle, ucode_info->data); | ||
604 | err_ucode_mmap: | ||
605 | nvmap_free(nvavp->nvmap, ucode_info->handle); | ||
606 | err_ucode_alloc: | ||
607 | kfree(nvavp->ucode_info.ucode_bin); | ||
608 | err_ubin_alloc: | ||
609 | release_firmware(nvavp_ucode_fw); | ||
610 | err_req_ucode: | ||
611 | return ret; | ||
612 | } | ||
613 | |||
614 | static void nvavp_unload_os(struct nvavp_info *nvavp) | ||
615 | { | ||
616 | nvmap_unpin(nvavp->nvmap, nvavp->os_info.handle); | ||
617 | nvmap_munmap(nvavp->os_info.handle, nvavp->os_info.data); | ||
618 | #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) | ||
619 | nvmap_free(nvavp->nvmap, nvavp->os_info.handle); | ||
620 | #elif defined(CONFIG_TEGRA_AVP_KERNEL_ON_SMMU) | ||
621 | nvmap_free_iovm(nvavp->nvmap, nvavp->os_info.handle); | ||
622 | #endif | ||
623 | kfree(nvavp->os_info.os_bin); | ||
624 | } | ||
625 | |||
626 | static int nvavp_load_os(struct nvavp_info *nvavp, char *fw_os_file) | ||
627 | { | ||
628 | struct nvavp_os_info *os_info = &nvavp->os_info; | ||
629 | const struct firmware *nvavp_os_fw; | ||
630 | void *ptr; | ||
631 | u32 size; | ||
632 | int ret = 0; | ||
633 | |||
634 | if (!os_info->os_bin) { | ||
635 | ret = request_firmware(&nvavp_os_fw, fw_os_file, | ||
636 | nvavp->misc_dev.this_device); | ||
637 | if (ret) { | ||
638 | dev_err(&nvavp->nvhost_dev->dev, | ||
639 | "cannot read os firmware '%s'\n", fw_os_file); | ||
640 | goto err_req_fw; | ||
641 | } | ||
642 | |||
643 | dev_info(&nvavp->nvhost_dev->dev, | ||
644 | "read firmware from '%s' (%d bytes)\n", | ||
645 | fw_os_file, nvavp_os_fw->size); | ||
646 | |||
647 | ptr = (void *)nvavp_os_fw->data; | ||
648 | |||
649 | if (strncmp((const char *)ptr, "NVAVP-OS", 8)) { | ||
650 | dev_info(&nvavp->nvhost_dev->dev, | ||
651 | "os hdr string mismatch\n"); | ||
652 | ret = -EINVAL; | ||
653 | goto err_os_bin; | ||
654 | } | ||
655 | |||
656 | ptr += 8; | ||
657 | os_info->entry_offset = *((u32 *)ptr); | ||
658 | ptr += sizeof(u32); | ||
659 | os_info->control_offset = *((u32 *)ptr); | ||
660 | ptr += sizeof(u32); | ||
661 | os_info->debug_offset = *((u32 *)ptr); | ||
662 | ptr += sizeof(u32); | ||
663 | |||
664 | size = *((u32 *)ptr); ptr += sizeof(u32); | ||
665 | |||
666 | os_info->size = size; | ||
667 | os_info->os_bin = kzalloc(os_info->size, | ||
668 | GFP_KERNEL); | ||
669 | if (!os_info->os_bin) { | ||
670 | dev_err(&nvavp->nvhost_dev->dev, | ||
671 | "cannot allocate os bin\n"); | ||
672 | ret = -ENOMEM; | ||
673 | goto err_os_bin; | ||
674 | } | ||
675 | |||
676 | memcpy(os_info->os_bin, ptr, os_info->size); | ||
677 | memset(os_info->data + os_info->size, 0, SZ_1M - os_info->size); | ||
678 | |||
679 | dev_info(&nvavp->nvhost_dev->dev, | ||
680 | "entry=%08x control=%08x debug=%08x size=%d\n", | ||
681 | os_info->entry_offset, os_info->control_offset, | ||
682 | os_info->debug_offset, os_info->size); | ||
683 | release_firmware(nvavp_os_fw); | ||
684 | } | ||
685 | |||
686 | memcpy(os_info->data, os_info->os_bin, os_info->size); | ||
687 | os_info->reset_addr = os_info->phys + os_info->entry_offset; | ||
688 | |||
689 | dev_info(&nvavp->nvhost_dev->dev, | ||
690 | "AVP os at vaddr=%p paddr=%lx reset_addr=%p\n", | ||
691 | os_info->data, (unsigned long)(os_info->phys), | ||
692 | (void *)os_info->reset_addr); | ||
693 | return 0; | ||
694 | |||
695 | err_os_bin: | ||
696 | release_firmware(nvavp_os_fw); | ||
697 | err_req_fw: | ||
698 | return ret; | ||
699 | } | ||
700 | |||
701 | static int nvavp_init(struct nvavp_info *nvavp) | ||
702 | { | ||
703 | char fw_os_file[32]; | ||
704 | int ret = 0; | ||
705 | |||
706 | if (nvavp->initialized) | ||
707 | return ret; | ||
708 | |||
709 | #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) /* Tegra2 with AVP MMU */ | ||
710 | /* paddr is any address returned from nvmap_pin */ | ||
711 | /* vaddr is AVP_KERNEL_VIRT_BASE */ | ||
712 | dev_info(&nvavp->nvhost_dev->dev, | ||
713 | "using AVP MMU to relocate AVP os\n"); | ||
714 | sprintf(fw_os_file, "nvavp_os.bin"); | ||
715 | nvavp->os_info.reset_addr = AVP_KERNEL_VIRT_BASE; | ||
716 | #elif defined(CONFIG_TEGRA_AVP_KERNEL_ON_SMMU) /* Tegra3 with SMMU */ | ||
717 | /* paddr is any address behind SMMU */ | ||
718 | /* vaddr is TEGRA_SMMU_BASE */ | ||
719 | dev_info(&nvavp->nvhost_dev->dev, | ||
720 | "using SMMU at %lx to load AVP kernel\n", | ||
721 | (unsigned long)nvavp->os_info.phys); | ||
722 | BUG_ON(nvavp->os_info.phys != 0xeff00000 | ||
723 | && nvavp->os_info.phys != 0x0ff00000); | ||
724 | sprintf(fw_os_file, "nvavp_os_%08lx.bin", | ||
725 | (unsigned long)nvavp->os_info.phys); | ||
726 | nvavp->os_info.reset_addr = nvavp->os_info.phys; | ||
727 | #else /* nvmem= carveout */ | ||
728 | /* paddr is found in nvmem= carveout */ | ||
729 | /* vaddr is same as paddr */ | ||
730 | /* Find nvmem carveout */ | ||
731 | if (!pfn_valid(__phys_to_pfn(0x8e000000))) { | ||
732 | nvavp->os_info.phys = 0x8e000000; | ||
733 | } else if (!pfn_valid(__phys_to_pfn(0x9e000000))) { | ||
734 | nvavp->os_info.phys = 0x9e000000; | ||
735 | } else if (!pfn_valid(__phys_to_pfn(0xbe000000))) { | ||
736 | nvavp->os_info.phys = 0xbe000000; | ||
737 | } else { | ||
738 | dev_err(&nvavp->nvhost_dev->dev, | ||
739 | "cannot find nvmem= carveout to load AVP os\n"); | ||
740 | dev_err(&nvavp->nvhost_dev->dev, | ||
741 | "check kernel command line " | ||
742 | "to see if nvmem= is defined\n"); | ||
743 | BUG(); | ||
744 | } | ||
745 | dev_info(&nvavp->nvhost_dev->dev, | ||
746 | "using nvmem= carveout at %lx to load AVP os\n", | ||
747 | nvavp->os_info.phys); | ||
748 | sprintf(fw_os_file, "nvavp_os_%08lx.bin", nvavp->os_info.phys); | ||
749 | nvavp->os_info.reset_addr = nvavp->os_info.phys; | ||
750 | nvavp->os_info.data = ioremap(nvavp->os_info.phys, SZ_1M); | ||
751 | #endif | ||
752 | |||
753 | ret = nvavp_load_os(nvavp, fw_os_file); | ||
754 | if (ret) { | ||
755 | dev_err(&nvavp->nvhost_dev->dev, | ||
756 | "unable to load os firmware '%s'\n", fw_os_file); | ||
757 | goto err_exit; | ||
758 | } | ||
759 | |||
760 | ret = nvavp_pushbuffer_init(nvavp); | ||
761 | if (ret) { | ||
762 | dev_err(&nvavp->nvhost_dev->dev, | ||
763 | "unable to init pushbuffer\n"); | ||
764 | goto err_exit; | ||
765 | } | ||
766 | |||
767 | ret = nvavp_load_ucode(nvavp); | ||
768 | if (ret) { | ||
769 | dev_err(&nvavp->nvhost_dev->dev, | ||
770 | "unable to load ucode\n"); | ||
771 | goto err_exit; | ||
772 | } | ||
773 | |||
774 | tegra_init_legacy_irq_cop(); | ||
775 | |||
776 | nvavp_reset_vde(nvavp); | ||
777 | nvavp_reset_avp(nvavp, nvavp->os_info.reset_addr); | ||
778 | enable_irq(nvavp->mbox_from_avp_pend_irq); | ||
779 | |||
780 | nvavp->initialized = 1; | ||
781 | |||
782 | err_exit: | ||
783 | return ret; | ||
784 | } | ||
785 | |||
786 | static void nvavp_uninit(struct nvavp_info *nvavp) | ||
787 | { | ||
788 | if (!nvavp->initialized) | ||
789 | return; | ||
790 | |||
791 | disable_irq(nvavp->mbox_from_avp_pend_irq); | ||
792 | |||
793 | cancel_work_sync(&nvavp->clock_disable_work); | ||
794 | |||
795 | nvavp_pushbuffer_deinit(nvavp); | ||
796 | |||
797 | nvavp_halt_vde(nvavp); | ||
798 | nvavp_halt_avp(nvavp); | ||
799 | |||
800 | clk_disable(nvavp->sclk); | ||
801 | clk_disable(nvavp->emc_clk); | ||
802 | |||
803 | nvavp->initialized = 0; | ||
804 | } | ||
805 | |||
806 | static int nvavp_set_clock_ioctl(struct file *filp, unsigned int cmd, | ||
807 | unsigned long arg) | ||
808 | { | ||
809 | struct nvavp_clientctx *clientctx = filp->private_data; | ||
810 | struct nvavp_info *nvavp = clientctx->nvavp; | ||
811 | struct clk *c; | ||
812 | struct nvavp_clock_args config; | ||
813 | |||
814 | if (copy_from_user(&config, (void __user *)arg, sizeof(struct nvavp_clock_args))) | ||
815 | return -EFAULT; | ||
816 | |||
817 | dev_dbg(&nvavp->nvhost_dev->dev, "%s: clk_id=%d, clk_rate=%u\n", | ||
818 | __func__, config.id, config.rate); | ||
819 | |||
820 | if (config.id == NVAVP_MODULE_ID_AVP) | ||
821 | nvavp->sclk_rate = config.rate; | ||
822 | else if (config.id == NVAVP_MODULE_ID_EMC) | ||
823 | nvavp->emc_clk_rate = config.rate; | ||
824 | |||
825 | c = nvavp_clk_get(nvavp, config.id); | ||
826 | if (IS_ERR_OR_NULL(c)) | ||
827 | return -EINVAL; | ||
828 | |||
829 | clk_enable(c); | ||
830 | clk_set_rate(c, config.rate); | ||
831 | |||
832 | config.rate = clk_get_rate(c); | ||
833 | clk_disable(c); | ||
834 | if (copy_to_user((void __user *)arg, &config, sizeof(struct nvavp_clock_args))) | ||
835 | return -EFAULT; | ||
836 | |||
837 | return 0; | ||
838 | } | ||
839 | |||
840 | static int nvavp_get_clock_ioctl(struct file *filp, unsigned int cmd, | ||
841 | unsigned long arg) | ||
842 | { | ||
843 | struct nvavp_clientctx *clientctx = filp->private_data; | ||
844 | struct nvavp_info *nvavp = clientctx->nvavp; | ||
845 | struct clk *c; | ||
846 | struct nvavp_clock_args config; | ||
847 | |||
848 | if (copy_from_user(&config, (void __user *)arg, sizeof(struct nvavp_clock_args))) | ||
849 | return -EFAULT; | ||
850 | |||
851 | c = nvavp_clk_get(nvavp, config.id); | ||
852 | if (IS_ERR_OR_NULL(c)) | ||
853 | return -EINVAL; | ||
854 | |||
855 | clk_enable(c); | ||
856 | config.rate = clk_get_rate(c); | ||
857 | clk_disable(c); | ||
858 | |||
859 | if (copy_to_user((void __user *)arg, &config, sizeof(struct nvavp_clock_args))) | ||
860 | return -EFAULT; | ||
861 | |||
862 | return 0; | ||
863 | } | ||
864 | |||
865 | static int nvavp_get_syncpointid_ioctl(struct file *filp, unsigned int cmd, | ||
866 | unsigned long arg) | ||
867 | { | ||
868 | struct nvavp_clientctx *clientctx = filp->private_data; | ||
869 | struct nvavp_info *nvavp = clientctx->nvavp; | ||
870 | u32 id = nvavp->syncpt_id; | ||
871 | |||
872 | if (_IOC_DIR(cmd) & _IOC_READ) { | ||
873 | if (copy_to_user((void __user *)arg, &id, sizeof(u32))) | ||
874 | return -EFAULT; | ||
875 | else | ||
876 | return 0; | ||
877 | } | ||
878 | return -EFAULT; | ||
879 | } | ||
880 | |||
881 | static int nvavp_set_nvmapfd_ioctl(struct file *filp, unsigned int cmd, | ||
882 | unsigned long arg) | ||
883 | { | ||
884 | struct nvavp_clientctx *clientctx = filp->private_data; | ||
885 | struct nvavp_set_nvmap_fd_args buf; | ||
886 | struct nvmap_client *new_client; | ||
887 | int fd; | ||
888 | |||
889 | if (_IOC_DIR(cmd) & _IOC_WRITE) { | ||
890 | if (copy_from_user(&buf, (void __user *)arg, _IOC_SIZE(cmd))) | ||
891 | return -EFAULT; | ||
892 | } | ||
893 | |||
894 | fd = buf.fd; | ||
895 | new_client = nvmap_client_get_file(fd); | ||
896 | if (IS_ERR(new_client)) | ||
897 | return PTR_ERR(new_client); | ||
898 | |||
899 | clientctx->nvmap = new_client; | ||
900 | return 0; | ||
901 | } | ||
902 | |||
903 | static int nvavp_pushbuffer_submit_ioctl(struct file *filp, unsigned int cmd, | ||
904 | unsigned long arg) | ||
905 | { | ||
906 | struct nvavp_clientctx *clientctx = filp->private_data; | ||
907 | struct nvavp_info *nvavp = clientctx->nvavp; | ||
908 | struct nvavp_pushbuffer_submit_hdr hdr; | ||
909 | u32 *cmdbuf_data; | ||
910 | struct nvmap_handle *cmdbuf_handle = NULL; | ||
911 | struct nvmap_handle_ref *cmdbuf_dupe; | ||
912 | int ret = 0, i; | ||
913 | unsigned long phys_addr; | ||
914 | unsigned long virt_addr; | ||
915 | struct nvavp_pushbuffer_submit_hdr *user_hdr = | ||
916 | (struct nvavp_pushbuffer_submit_hdr *) arg; | ||
917 | struct nvavp_syncpt syncpt; | ||
918 | |||
919 | syncpt.id = NVSYNCPT_INVALID; | ||
920 | syncpt.value = 0; | ||
921 | |||
922 | if (_IOC_DIR(cmd) & _IOC_WRITE) { | ||
923 | if (copy_from_user(&hdr, (void __user *)arg, | ||
924 | sizeof(struct nvavp_pushbuffer_submit_hdr))) | ||
925 | return -EFAULT; | ||
926 | } | ||
927 | |||
928 | if (!hdr.cmdbuf.mem) | ||
929 | return 0; | ||
930 | |||
931 | if (copy_from_user(clientctx->relocs, (void __user *)hdr.relocs, | ||
932 | sizeof(struct nvavp_reloc) * hdr.num_relocs)) { | ||
933 | return -EFAULT; | ||
934 | } | ||
935 | |||
936 | cmdbuf_handle = nvmap_get_handle_id(clientctx->nvmap, hdr.cmdbuf.mem); | ||
937 | if (cmdbuf_handle == NULL) { | ||
938 | dev_err(&nvavp->nvhost_dev->dev, | ||
939 | "invalid cmd buffer handle %08x\n", hdr.cmdbuf.mem); | ||
940 | return -EPERM; | ||
941 | } | ||
942 | |||
943 | /* duplicate the new pushbuffer's handle into the nvavp driver's | ||
944 | * nvmap context, to ensure that the handle won't be freed as | ||
945 | * long as it is in-use by the fb driver */ | ||
946 | cmdbuf_dupe = nvmap_duplicate_handle_id(nvavp->nvmap, hdr.cmdbuf.mem); | ||
947 | nvmap_handle_put(cmdbuf_handle); | ||
948 | |||
949 | if (IS_ERR(cmdbuf_dupe)) { | ||
950 | dev_err(&nvavp->nvhost_dev->dev, | ||
951 | "could not duplicate handle\n"); | ||
952 | return PTR_ERR(cmdbuf_dupe); | ||
953 | } | ||
954 | |||
955 | phys_addr = nvmap_pin(nvavp->nvmap, cmdbuf_dupe); | ||
956 | if (IS_ERR((void *)phys_addr)) { | ||
957 | dev_err(&nvavp->nvhost_dev->dev, "could not pin handle\n"); | ||
958 | nvmap_free(nvavp->nvmap, cmdbuf_dupe); | ||
959 | return PTR_ERR((void *)phys_addr); | ||
960 | } | ||
961 | |||
962 | virt_addr = (unsigned long)nvmap_mmap(cmdbuf_dupe); | ||
963 | if (!virt_addr) { | ||
964 | dev_err(&nvavp->nvhost_dev->dev, "cannot map cmdbuf handle\n"); | ||
965 | ret = -ENOMEM; | ||
966 | goto err_cmdbuf_mmap; | ||
967 | } | ||
968 | |||
969 | cmdbuf_data = (u32 *)(virt_addr + hdr.cmdbuf.offset); | ||
970 | |||
971 | for (i = 0; i < hdr.num_relocs; i++) { | ||
972 | u32 *reloc_addr, target_phys_addr; | ||
973 | |||
974 | if (clientctx->relocs[i].cmdbuf_mem != hdr.cmdbuf.mem) { | ||
975 | dev_err(&nvavp->nvhost_dev->dev, | ||
976 | "reloc info does not match target bufferID\n"); | ||
977 | ret = -EPERM; | ||
978 | goto err_reloc_info; | ||
979 | } | ||
980 | |||
981 | reloc_addr = cmdbuf_data + | ||
982 | (clientctx->relocs[i].cmdbuf_offset >> 2); | ||
983 | |||
984 | target_phys_addr = nvmap_handle_address(clientctx->nvmap, | ||
985 | clientctx->relocs[i].target); | ||
986 | target_phys_addr += clientctx->relocs[i].target_offset; | ||
987 | writel(target_phys_addr, reloc_addr); | ||
988 | } | ||
989 | |||
990 | if (hdr.syncpt) { | ||
991 | ret = nvavp_pushbuffer_update(nvavp, | ||
992 | (phys_addr + hdr.cmdbuf.offset), | ||
993 | hdr.cmdbuf.words, &syncpt, | ||
994 | (hdr.flags & NVAVP_UCODE_EXT)); | ||
995 | |||
996 | if (copy_to_user((void __user *)user_hdr->syncpt, &syncpt, | ||
997 | sizeof(struct nvavp_syncpt))) { | ||
998 | ret = -EFAULT; | ||
999 | goto err_reloc_info; | ||
1000 | } | ||
1001 | } else { | ||
1002 | ret = nvavp_pushbuffer_update(nvavp, | ||
1003 | (phys_addr + hdr.cmdbuf.offset), | ||
1004 | hdr.cmdbuf.words, NULL, | ||
1005 | (hdr.flags & NVAVP_UCODE_EXT)); | ||
1006 | } | ||
1007 | |||
1008 | err_reloc_info: | ||
1009 | nvmap_munmap(cmdbuf_dupe, (void *)virt_addr); | ||
1010 | err_cmdbuf_mmap: | ||
1011 | nvmap_unpin(nvavp->nvmap, cmdbuf_dupe); | ||
1012 | nvmap_free(nvavp->nvmap, cmdbuf_dupe); | ||
1013 | return ret; | ||
1014 | } | ||
1015 | |||
1016 | static int tegra_nvavp_open(struct inode *inode, struct file *filp) | ||
1017 | { | ||
1018 | struct miscdevice *miscdev = filp->private_data; | ||
1019 | struct nvavp_info *nvavp = dev_get_drvdata(miscdev->parent); | ||
1020 | int ret = 0; | ||
1021 | struct nvavp_clientctx *clientctx; | ||
1022 | |||
1023 | dev_dbg(&nvavp->nvhost_dev->dev, "%s: ++\n", __func__); | ||
1024 | |||
1025 | nonseekable_open(inode, filp); | ||
1026 | |||
1027 | clientctx = kzalloc(sizeof(*clientctx), GFP_KERNEL); | ||
1028 | if (!clientctx) | ||
1029 | return -ENOMEM; | ||
1030 | |||
1031 | mutex_lock(&nvavp->open_lock); | ||
1032 | |||
1033 | if (!nvavp->refcount) | ||
1034 | ret = nvavp_init(nvavp); | ||
1035 | |||
1036 | if (!ret) | ||
1037 | nvavp->refcount++; | ||
1038 | |||
1039 | clientctx->nvmap = nvavp->nvmap; | ||
1040 | clientctx->nvavp = nvavp; | ||
1041 | |||
1042 | filp->private_data = clientctx; | ||
1043 | |||
1044 | mutex_unlock(&nvavp->open_lock); | ||
1045 | |||
1046 | return ret; | ||
1047 | } | ||
1048 | |||
1049 | static int tegra_nvavp_release(struct inode *inode, struct file *filp) | ||
1050 | { | ||
1051 | struct nvavp_clientctx *clientctx = filp->private_data; | ||
1052 | struct nvavp_info *nvavp = clientctx->nvavp; | ||
1053 | int ret = 0; | ||
1054 | |||
1055 | dev_dbg(&nvavp->nvhost_dev->dev, "%s: ++\n", __func__); | ||
1056 | |||
1057 | filp->private_data = NULL; | ||
1058 | |||
1059 | mutex_lock(&nvavp->open_lock); | ||
1060 | |||
1061 | if (!nvavp->refcount) { | ||
1062 | dev_err(&nvavp->nvhost_dev->dev, | ||
1063 | "releasing while in invalid state\n"); | ||
1064 | ret = -EINVAL; | ||
1065 | goto out; | ||
1066 | } | ||
1067 | |||
1068 | if (nvavp->refcount > 0) | ||
1069 | nvavp->refcount--; | ||
1070 | if (!nvavp->refcount) | ||
1071 | nvavp_uninit(nvavp); | ||
1072 | |||
1073 | out: | ||
1074 | nvmap_client_put(clientctx->nvmap); | ||
1075 | mutex_unlock(&nvavp->open_lock); | ||
1076 | kfree(clientctx); | ||
1077 | return ret; | ||
1078 | } | ||
1079 | |||
1080 | static long tegra_nvavp_ioctl(struct file *filp, unsigned int cmd, | ||
1081 | unsigned long arg) | ||
1082 | { | ||
1083 | int ret = 0; | ||
1084 | |||
1085 | if (_IOC_TYPE(cmd) != NVAVP_IOCTL_MAGIC || | ||
1086 | _IOC_NR(cmd) < NVAVP_IOCTL_MIN_NR || | ||
1087 | _IOC_NR(cmd) > NVAVP_IOCTL_MAX_NR) | ||
1088 | return -EFAULT; | ||
1089 | |||
1090 | switch (cmd) { | ||
1091 | case NVAVP_IOCTL_SET_NVMAP_FD: | ||
1092 | ret = nvavp_set_nvmapfd_ioctl(filp, cmd, arg); | ||
1093 | break; | ||
1094 | case NVAVP_IOCTL_GET_SYNCPOINT_ID: | ||
1095 | ret = nvavp_get_syncpointid_ioctl(filp, cmd, arg); | ||
1096 | break; | ||
1097 | case NVAVP_IOCTL_PUSH_BUFFER_SUBMIT: | ||
1098 | ret = nvavp_pushbuffer_submit_ioctl(filp, cmd, arg); | ||
1099 | break; | ||
1100 | case NVAVP_IOCTL_SET_CLOCK: | ||
1101 | ret = nvavp_set_clock_ioctl(filp, cmd, arg); | ||
1102 | break; | ||
1103 | case NVAVP_IOCTL_GET_CLOCK: | ||
1104 | ret = nvavp_get_clock_ioctl(filp, cmd, arg); | ||
1105 | break; | ||
1106 | default: | ||
1107 | ret = -EINVAL; | ||
1108 | break; | ||
1109 | } | ||
1110 | return ret; | ||
1111 | } | ||
1112 | |||
1113 | static const struct file_operations tegra_nvavp_fops = { | ||
1114 | .owner = THIS_MODULE, | ||
1115 | .open = tegra_nvavp_open, | ||
1116 | .release = tegra_nvavp_release, | ||
1117 | .unlocked_ioctl = tegra_nvavp_ioctl, | ||
1118 | }; | ||
1119 | |||
1120 | static int tegra_nvavp_probe(struct nvhost_device *ndev) | ||
1121 | { | ||
1122 | struct nvavp_info *nvavp; | ||
1123 | int irq; | ||
1124 | unsigned int heap_mask; | ||
1125 | u32 iovmm_addr; | ||
1126 | int ret = 0; | ||
1127 | |||
1128 | irq = nvhost_get_irq_byname(ndev, "mbox_from_nvavp_pending"); | ||
1129 | if (irq < 0) { | ||
1130 | dev_err(&ndev->dev, "invalid nvhost data\n"); | ||
1131 | return -EINVAL; | ||
1132 | } | ||
1133 | |||
1134 | |||
1135 | nvavp = kzalloc(sizeof(struct nvavp_info), GFP_KERNEL); | ||
1136 | if (!nvavp) { | ||
1137 | dev_err(&ndev->dev, "cannot allocate avp_info\n"); | ||
1138 | return -ENOMEM; | ||
1139 | } | ||
1140 | |||
1141 | memset(nvavp, 0, sizeof(*nvavp)); | ||
1142 | |||
1143 | nvavp->nvhost_syncpt = &nvhost_get_host(ndev)->syncpt; | ||
1144 | if (!nvavp->nvhost_syncpt) { | ||
1145 | dev_err(&ndev->dev, "cannot get syncpt handle\n"); | ||
1146 | ret = -ENOENT; | ||
1147 | goto err_get_syncpt; | ||
1148 | } | ||
1149 | |||
1150 | nvavp->nvmap = nvmap_create_client(nvmap_dev, "nvavp_drv"); | ||
1151 | if (IS_ERR_OR_NULL(nvavp->nvmap)) { | ||
1152 | dev_err(&ndev->dev, "cannot create nvmap client\n"); | ||
1153 | ret = PTR_ERR(nvavp->nvmap); | ||
1154 | goto err_nvmap_create_drv_client; | ||
1155 | } | ||
1156 | |||
1157 | #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) /* Tegra2 with AVP MMU */ | ||
1158 | heap_mask = NVMAP_HEAP_CARVEOUT_GENERIC; | ||
1159 | #elif defined(CONFIG_TEGRA_AVP_KERNEL_ON_SMMU) /* Tegra3 with SMMU */ | ||
1160 | heap_mask = NVMAP_HEAP_IOVMM; | ||
1161 | #else /* nvmem= carveout */ | ||
1162 | heap_mask = 0; | ||
1163 | #endif | ||
1164 | switch (heap_mask) { | ||
1165 | case NVMAP_HEAP_IOVMM: | ||
1166 | |||
1167 | #ifdef CONFIG_TEGRA_SMMU_BASE_AT_E0000000 | ||
1168 | iovmm_addr = 0xeff00000; | ||
1169 | #else | ||
1170 | iovmm_addr = 0x0ff00000; | ||
1171 | #endif | ||
1172 | |||
1173 | /* Tegra3 A01 has different SMMU address */ | ||
1174 | if (tegra_get_chipid() == TEGRA_CHIPID_TEGRA3 | ||
1175 | && tegra_get_revision() == TEGRA_REVISION_A01) { | ||
1176 | iovmm_addr = 0xeff00000; | ||
1177 | } | ||
1178 | |||
1179 | nvavp->os_info.handle = nvmap_alloc_iovm(nvavp->nvmap, SZ_1M, | ||
1180 | L1_CACHE_BYTES, | ||
1181 | NVMAP_HANDLE_UNCACHEABLE, | ||
1182 | iovmm_addr); | ||
1183 | if (IS_ERR_OR_NULL(nvavp->os_info.handle)) { | ||
1184 | dev_err(&ndev->dev, | ||
1185 | "cannot create os handle\n"); | ||
1186 | ret = PTR_ERR(nvavp->os_info.handle); | ||
1187 | goto err_nvmap_alloc; | ||
1188 | } | ||
1189 | |||
1190 | nvavp->os_info.data = nvmap_mmap(nvavp->os_info.handle); | ||
1191 | if (!nvavp->os_info.data) { | ||
1192 | dev_err(&ndev->dev, | ||
1193 | "cannot map os handle\n"); | ||
1194 | ret = -ENOMEM; | ||
1195 | goto err_nvmap_mmap; | ||
1196 | } | ||
1197 | |||
1198 | nvavp->os_info.phys = | ||
1199 | nvmap_pin(nvavp->nvmap, nvavp->os_info.handle); | ||
1200 | if (IS_ERR_OR_NULL((void *)nvavp->os_info.phys)) { | ||
1201 | dev_err(&ndev->dev, | ||
1202 | "cannot pin os handle\n"); | ||
1203 | ret = PTR_ERR((void *)nvavp->os_info.phys); | ||
1204 | goto err_nvmap_pin; | ||
1205 | } | ||
1206 | |||
1207 | dev_info(&ndev->dev, | ||
1208 | "allocated IOVM at %lx for AVP os\n", | ||
1209 | (unsigned long)nvavp->os_info.phys); | ||
1210 | break; | ||
1211 | case NVMAP_HEAP_CARVEOUT_GENERIC: | ||
1212 | nvavp->os_info.handle = nvmap_alloc(nvavp->nvmap, SZ_1M, SZ_1M, | ||
1213 | NVMAP_HANDLE_UNCACHEABLE, 0); | ||
1214 | if (IS_ERR_OR_NULL(nvavp->os_info.handle)) { | ||
1215 | dev_err(&ndev->dev, "cannot create AVP os handle\n"); | ||
1216 | ret = PTR_ERR(nvavp->os_info.handle); | ||
1217 | goto err_nvmap_alloc; | ||
1218 | } | ||
1219 | |||
1220 | nvavp->os_info.data = nvmap_mmap(nvavp->os_info.handle); | ||
1221 | if (!nvavp->os_info.data) { | ||
1222 | dev_err(&ndev->dev, "cannot map AVP os handle\n"); | ||
1223 | ret = -ENOMEM; | ||
1224 | goto err_nvmap_mmap; | ||
1225 | } | ||
1226 | |||
1227 | nvavp->os_info.phys = nvmap_pin(nvavp->nvmap, | ||
1228 | nvavp->os_info.handle); | ||
1229 | if (IS_ERR_OR_NULL((void *)nvavp->os_info.phys)) { | ||
1230 | dev_err(&ndev->dev, "cannot pin AVP os handle\n"); | ||
1231 | ret = PTR_ERR((void *)nvavp->os_info.phys); | ||
1232 | goto err_nvmap_pin; | ||
1233 | } | ||
1234 | |||
1235 | dev_info(&ndev->dev, | ||
1236 | "allocated carveout memory at %lx for AVP os\n", | ||
1237 | (unsigned long)nvavp->os_info.phys); | ||
1238 | break; | ||
1239 | default: | ||
1240 | dev_err(&ndev->dev, "invalid/non-supported heap for AVP os\n"); | ||
1241 | ret = -EINVAL; | ||
1242 | goto err_get_syncpt; | ||
1243 | } | ||
1244 | |||
1245 | nvavp->mbox_from_avp_pend_irq = irq; | ||
1246 | mutex_init(&nvavp->open_lock); | ||
1247 | mutex_init(&nvavp->pushbuffer_lock); | ||
1248 | |||
1249 | /* TODO DO NOT USE NVAVP DEVICE */ | ||
1250 | nvavp->cop_clk = clk_get(&ndev->dev, "cop"); | ||
1251 | if (IS_ERR(nvavp->cop_clk)) { | ||
1252 | dev_err(&ndev->dev, "cannot get cop clock\n"); | ||
1253 | ret = -ENOENT; | ||
1254 | goto err_get_cop_clk; | ||
1255 | } | ||
1256 | |||
1257 | nvavp->vde_clk = clk_get(&ndev->dev, "vde"); | ||
1258 | if (IS_ERR(nvavp->vde_clk)) { | ||
1259 | dev_err(&ndev->dev, "cannot get vde clock\n"); | ||
1260 | ret = -ENOENT; | ||
1261 | goto err_get_vde_clk; | ||
1262 | } | ||
1263 | |||
1264 | nvavp->bsev_clk = clk_get(&ndev->dev, "bsev"); | ||
1265 | if (IS_ERR(nvavp->bsev_clk)) { | ||
1266 | dev_err(&ndev->dev, "cannot get bsev clock\n"); | ||
1267 | ret = -ENOENT; | ||
1268 | goto err_get_bsev_clk; | ||
1269 | } | ||
1270 | |||
1271 | nvavp->sclk = clk_get(&ndev->dev, "sclk"); | ||
1272 | if (IS_ERR(nvavp->sclk)) { | ||
1273 | dev_err(&ndev->dev, "cannot get avp.sclk clock\n"); | ||
1274 | ret = -ENOENT; | ||
1275 | goto err_get_sclk; | ||
1276 | } | ||
1277 | |||
1278 | nvavp->emc_clk = clk_get(&ndev->dev, "emc"); | ||
1279 | if (IS_ERR(nvavp->emc_clk)) { | ||
1280 | dev_err(&ndev->dev, "cannot get emc clock\n"); | ||
1281 | ret = -ENOENT; | ||
1282 | goto err_get_emc_clk; | ||
1283 | } | ||
1284 | |||
1285 | nvavp->clk_enabled = 0; | ||
1286 | nvavp_halt_avp(nvavp); | ||
1287 | |||
1288 | INIT_WORK(&nvavp->clock_disable_work, clock_disable_handler); | ||
1289 | |||
1290 | nvavp->misc_dev.minor = MISC_DYNAMIC_MINOR; | ||
1291 | nvavp->misc_dev.name = "tegra_avpchannel"; | ||
1292 | nvavp->misc_dev.fops = &tegra_nvavp_fops; | ||
1293 | nvavp->misc_dev.mode = S_IRWXUGO; | ||
1294 | nvavp->misc_dev.parent = &ndev->dev; | ||
1295 | |||
1296 | ret = misc_register(&nvavp->misc_dev); | ||
1297 | if (ret) { | ||
1298 | dev_err(&ndev->dev, "unable to register misc device!\n"); | ||
1299 | goto err_misc_reg; | ||
1300 | } | ||
1301 | |||
1302 | ret = request_irq(irq, nvavp_mbox_pending_isr, 0, | ||
1303 | TEGRA_NVAVP_NAME, nvavp); | ||
1304 | if (ret) { | ||
1305 | dev_err(&ndev->dev, "cannot register irq handler\n"); | ||
1306 | goto err_req_irq_pend; | ||
1307 | } | ||
1308 | disable_irq(nvavp->mbox_from_avp_pend_irq); | ||
1309 | |||
1310 | nvhost_set_drvdata(ndev, nvavp); | ||
1311 | nvavp->nvhost_dev = ndev; | ||
1312 | |||
1313 | return 0; | ||
1314 | |||
1315 | err_req_irq_pend: | ||
1316 | misc_deregister(&nvavp->misc_dev); | ||
1317 | err_misc_reg: | ||
1318 | clk_put(nvavp->emc_clk); | ||
1319 | err_get_emc_clk: | ||
1320 | clk_put(nvavp->sclk); | ||
1321 | err_get_sclk: | ||
1322 | clk_put(nvavp->bsev_clk); | ||
1323 | err_get_bsev_clk: | ||
1324 | clk_put(nvavp->vde_clk); | ||
1325 | err_get_vde_clk: | ||
1326 | clk_put(nvavp->cop_clk); | ||
1327 | err_get_cop_clk: | ||
1328 | nvmap_unpin(nvavp->nvmap, nvavp->os_info.handle); | ||
1329 | err_nvmap_pin: | ||
1330 | nvmap_munmap(nvavp->os_info.handle, nvavp->os_info.data); | ||
1331 | err_nvmap_mmap: | ||
1332 | #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) | ||
1333 | nvmap_free(nvavp->nvmap, nvavp->os_info.handle); | ||
1334 | #elif defined(CONFIG_TEGRA_AVP_KERNEL_ON_SMMU) | ||
1335 | nvmap_free_iovm(nvavp->nvmap, nvavp->os_info.handle); | ||
1336 | #endif | ||
1337 | err_nvmap_alloc: | ||
1338 | nvmap_client_put(nvavp->nvmap); | ||
1339 | err_nvmap_create_drv_client: | ||
1340 | err_get_syncpt: | ||
1341 | kfree(nvavp); | ||
1342 | return ret; | ||
1343 | } | ||
1344 | |||
1345 | static int tegra_nvavp_remove(struct nvhost_device *ndev) | ||
1346 | { | ||
1347 | struct nvavp_info *nvavp = nvhost_get_drvdata(ndev); | ||
1348 | |||
1349 | if (!nvavp) | ||
1350 | return 0; | ||
1351 | |||
1352 | mutex_lock(&nvavp->open_lock); | ||
1353 | if (nvavp->refcount) { | ||
1354 | mutex_unlock(&nvavp->open_lock); | ||
1355 | return -EBUSY; | ||
1356 | } | ||
1357 | mutex_unlock(&nvavp->open_lock); | ||
1358 | |||
1359 | nvavp_unload_ucode(nvavp); | ||
1360 | nvavp_unload_os(nvavp); | ||
1361 | |||
1362 | misc_deregister(&nvavp->misc_dev); | ||
1363 | |||
1364 | clk_put(nvavp->bsev_clk); | ||
1365 | clk_put(nvavp->vde_clk); | ||
1366 | clk_put(nvavp->cop_clk); | ||
1367 | |||
1368 | clk_put(nvavp->emc_clk); | ||
1369 | clk_put(nvavp->sclk); | ||
1370 | |||
1371 | nvmap_client_put(nvavp->nvmap); | ||
1372 | |||
1373 | kfree(nvavp); | ||
1374 | return 0; | ||
1375 | } | ||
1376 | |||
1377 | #ifdef CONFIG_PM | ||
1378 | static int tegra_nvavp_suspend(struct nvhost_device *ndev, pm_message_t state) | ||
1379 | { | ||
1380 | struct nvavp_info *nvavp = nvhost_get_drvdata(ndev); | ||
1381 | int ret = 0; | ||
1382 | |||
1383 | mutex_lock(&nvavp->open_lock); | ||
1384 | |||
1385 | if (nvavp->refcount) { | ||
1386 | if (nvavp_check_idle(nvavp)) | ||
1387 | nvavp_uninit(nvavp); | ||
1388 | else | ||
1389 | ret = -EBUSY; | ||
1390 | } | ||
1391 | |||
1392 | mutex_unlock(&nvavp->open_lock); | ||
1393 | |||
1394 | return ret; | ||
1395 | } | ||
1396 | |||
1397 | static int tegra_nvavp_resume(struct nvhost_device *ndev) | ||
1398 | { | ||
1399 | struct nvavp_info *nvavp = nvhost_get_drvdata(ndev); | ||
1400 | |||
1401 | mutex_lock(&nvavp->open_lock); | ||
1402 | |||
1403 | if (nvavp->refcount) | ||
1404 | nvavp_init(nvavp); | ||
1405 | |||
1406 | mutex_unlock(&nvavp->open_lock); | ||
1407 | |||
1408 | return 0; | ||
1409 | } | ||
1410 | #endif | ||
1411 | |||
1412 | static struct nvhost_driver tegra_nvavp_driver = { | ||
1413 | .driver = { | ||
1414 | .name = TEGRA_NVAVP_NAME, | ||
1415 | .owner = THIS_MODULE, | ||
1416 | }, | ||
1417 | .probe = tegra_nvavp_probe, | ||
1418 | .remove = tegra_nvavp_remove, | ||
1419 | #ifdef CONFIG_PM | ||
1420 | .suspend = tegra_nvavp_suspend, | ||
1421 | .resume = tegra_nvavp_resume, | ||
1422 | #endif | ||
1423 | }; | ||
1424 | |||
1425 | static int __init tegra_nvavp_init(void) | ||
1426 | { | ||
1427 | return nvhost_driver_register(&tegra_nvavp_driver); | ||
1428 | } | ||
1429 | |||
1430 | static void __exit tegra_nvavp_exit(void) | ||
1431 | { | ||
1432 | nvhost_driver_unregister(&tegra_nvavp_driver); | ||
1433 | } | ||
1434 | |||
1435 | module_init(tegra_nvavp_init); | ||
1436 | module_exit(tegra_nvavp_exit); | ||
1437 | |||
1438 | MODULE_AUTHOR("NVIDIA"); | ||
1439 | MODULE_DESCRIPTION("Channel based AVP driver for Tegra"); | ||
1440 | MODULE_VERSION("1.0"); | ||
1441 | MODULE_LICENSE("Dual BSD/GPL"); | ||
diff --git a/drivers/media/video/tegra/nvavp/nvavp_os.h b/drivers/media/video/tegra/nvavp/nvavp_os.h new file mode 100644 index 00000000000..4d7f6776f11 --- /dev/null +++ b/drivers/media/video/tegra/nvavp/nvavp_os.h | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * drivers/media/video/tegra/nvavp/nvavp_os.h | ||
3 | * | ||
4 | * Copyright (C) 2011 NVIDIA Corp. | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public License | ||
7 | * version 2. This program is licensed "as is" without any warranty of any | ||
8 | * kind, whether express or implied. | ||
9 | */ | ||
10 | |||
11 | #ifndef __MEDIA_VIDEO_TEGRA_NVAVP_OS_H | ||
12 | #define __MEDIA_VIDEO_TEGRA_NVAVP_OS_H | ||
13 | |||
14 | #include <linux/types.h> | ||
15 | |||
16 | #include "../../../../video/tegra/nvmap/nvmap.h" | ||
17 | |||
18 | #define NVE2_AVP (0x0000E276) | ||
19 | |||
20 | struct nv_e276_control { | ||
21 | u32 reserved00[5]; | ||
22 | u32 dma_start; | ||
23 | u32 reserved01[2]; | ||
24 | u32 dma_end; | ||
25 | u32 reserved02[7]; | ||
26 | u32 put; | ||
27 | u32 reserved03[15]; | ||
28 | u32 get; | ||
29 | u32 reserved04[10]; | ||
30 | u32 watchdog_timeout; | ||
31 | u32 idle_notify_enable; | ||
32 | u32 idle_notify_delay; | ||
33 | u32 idle_clk_enable; | ||
34 | u32 iram_clk_gating; | ||
35 | u32 idle; | ||
36 | u32 outbox_data; | ||
37 | u32 app_intr_enable; | ||
38 | u32 app_start_time; | ||
39 | u32 app_in_iram; | ||
40 | u32 iram_ucode_addr; | ||
41 | u32 iram_ucode_size; | ||
42 | u32 dbg_state[57]; | ||
43 | u32 os_method_data[16]; | ||
44 | u32 app_method_data[128]; | ||
45 | }; | ||
46 | |||
47 | #define NVE26E_HOST1X_INCR_SYNCPT (0x00000000) | ||
48 | #define NVE26E_HOST1X_INCR_SYNCPT_COND_OP_DONE (0x00000001) | ||
49 | |||
50 | #define NVE26E_CH_OPCODE_INCR(Addr, Count) \ | ||
51 | /* op, addr, count */ \ | ||
52 | ((1UL << 28) | ((Addr) << 16) | (Count)) | ||
53 | |||
54 | #define NVE26E_CH_OPCODE_IMM(addr, value) \ | ||
55 | /* op, addr, count */ \ | ||
56 | ((4UL << 28) | ((addr) << 16) | (value)) | ||
57 | |||
58 | #define NVE26E_CH_OPCODE_GATHER(off, ins, type, cnt) \ | ||
59 | /* op, offset, insert, type, count */ \ | ||
60 | ((6UL << 28) | ((off) << 16) | ((ins) << 15) | ((type) << 14) | cnt) | ||
61 | |||
62 | /* AVP OS methods */ | ||
63 | #define NVE276_NOP (0x00000080) | ||
64 | #define NVE276_SET_APP_TIMEOUT (0x00000084) | ||
65 | #define NVE276_SET_MICROCODE_A (0x00000085) | ||
66 | #define NVE276_SET_MICROCODE_B (0x00000086) | ||
67 | #define NVE276_SET_MICROCODE_C (0x00000087) | ||
68 | |||
69 | /* Interrupt codes through inbox/outbox data codes (cpu->avp or avp->cpu) */ | ||
70 | #define NVE276_OS_INTERRUPT_NOP (0x00000000) /* wake up avp */ | ||
71 | #define NVE276_OS_INTERRUPT_TIMEOUT (0x00000001) | ||
72 | #define NVE276_OS_INTERRUPT_SEMAPHORE_AWAKEN (0x00000002) | ||
73 | #define NVE276_OS_INTERRUPT_EXECUTE_AWAKEN (0x00000004) | ||
74 | #define NVE276_OS_INTERRUPT_DEBUG_STRING (0x00000008) | ||
75 | #define NVE276_OS_INTERRUPT_DH_KEYEXCHANGE (0x00000010) | ||
76 | #define NVE276_OS_INTERRUPT_APP_NOTIFY (0x00000020) | ||
77 | #define NVE276_OS_INTERRUPT_VIDEO_IDLE (0x00000040) | ||
78 | #define NVE276_OS_INTERRUPT_AUDIO_IDLE (0x00000080) | ||
79 | #define NVE276_OS_INTERRUPT_AVP_BREAKPOINT (0x00800000) | ||
80 | #define NVE276_OS_INTERRUPT_AVP_FATAL_ERROR (0x01000000) | ||
81 | |||
82 | struct nvavp_os_info { | ||
83 | u32 entry_offset; | ||
84 | u32 control_offset; | ||
85 | u32 debug_offset; | ||
86 | |||
87 | struct nvmap_handle_ref *handle; | ||
88 | void *data; | ||
89 | u32 size; | ||
90 | phys_addr_t phys; | ||
91 | void *os_bin; | ||
92 | phys_addr_t reset_addr; | ||
93 | }; | ||
94 | |||
95 | struct nvavp_ucode_info { | ||
96 | struct nvmap_handle_ref *handle; | ||
97 | void *data; | ||
98 | u32 size; | ||
99 | phys_addr_t phys; | ||
100 | void *ucode_bin; | ||
101 | }; | ||
102 | |||
103 | #endif /* __MEDIA_VIDEO_TEGRA_NVAVP_OS_H */ | ||
diff --git a/drivers/media/video/tegra/ov14810.c b/drivers/media/video/tegra/ov14810.c new file mode 100644 index 00000000000..2efd283b32b --- /dev/null +++ b/drivers/media/video/tegra/ov14810.c | |||
@@ -0,0 +1,1390 @@ | |||
1 | /* | ||
2 | * ov14810.c - ov14810 sensor driver | ||
3 | * | ||
4 | * Copyright (c) 2011-2012, NVIDIA, All Rights Reserved. | ||
5 | * | ||
6 | * Contributors: | ||
7 | * Krupal Divvela <kdivvela@nvidia.com> | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public License | ||
10 | * version 2. This program is licensed "as is" without any warranty of any | ||
11 | * kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #include <linux/delay.h> | ||
15 | #include <linux/fs.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <linux/miscdevice.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/uaccess.h> | ||
20 | #include <media/ov14810.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/moduleparam.h> | ||
23 | |||
24 | |||
25 | #define OV14810_I2C_WRITE8(ADDR, OFFSET, VAL) do { \ | ||
26 | if (0 != ov14810_write8(ADDR, OFFSET, VAL)) \ | ||
27 | return 1; \ | ||
28 | } while(0) | ||
29 | |||
30 | #define OV14810_FRAME_LENGTH_REG_ADDR0 0x380e | ||
31 | #define OV14810_FRAME_LENGTH_REG_ADDR1 0x380f | ||
32 | |||
33 | #define OV14810_COARSE_TIME_REG_ADDR0 0x3500 | ||
34 | #define OV14810_COARSE_TIME_REG_ADDR1 0x3501 | ||
35 | #define OV14810_COARSE_TIME_REG_ADDR2 0x3502 | ||
36 | |||
37 | #define OV14810_GAIN_REG_ADDR0 0x350b | ||
38 | |||
39 | #define OV14810_GROUP_ACCESS_REG_ADDR 0x3212 | ||
40 | |||
41 | static u8 uCProgram[] = { | ||
42 | 0x02,0x03,0x6E,0x02,0x19,0x74,0xBB,0x01,0x06,0x89,0x82,0x8A,0x83,0xE0,0x22,0x50 | ||
43 | ,0x02,0xE7,0x22,0xBB,0xFE,0x02,0xE3,0x22,0x89,0x82,0x8A,0x83,0xE4,0x93,0x22,0xBB | ||
44 | ,0x01,0x0C,0xE5,0x82,0x29,0xF5,0x82,0xE5,0x83,0x3A,0xF5,0x83,0xE0,0x22,0x50,0x06 | ||
45 | ,0xE9,0x25,0x82,0xF8,0xE6,0x22,0xBB,0xFE,0x06,0xE9,0x25,0x82,0xF8,0xE2,0x22,0xE5 | ||
46 | ,0x82,0x29,0xF5,0x82,0xE5,0x83,0x3A,0xF5,0x83,0xE4,0x93,0x22,0xBB,0x01,0x06,0x89 | ||
47 | ,0x82,0x8A,0x83,0xF0,0x22,0x50,0x02,0xF7,0x22,0xBB,0xFE,0x01,0xF3,0x22,0xEF,0x8D | ||
48 | ,0xF0,0xA4,0xA8,0xF0,0xCF,0x8C,0xF0,0xA4,0x28,0xCE,0x8D,0xF0,0xA4,0x2E,0xFE,0x22 | ||
49 | ,0xBC,0x00,0x0B,0xBE,0x00,0x29,0xEF,0x8D,0xF0,0x84,0xFF,0xAD,0xF0,0x22,0xE4,0xCC | ||
50 | ,0xF8,0x75,0xF0,0x08,0xEF,0x2F,0xFF,0xEE,0x33,0xFE,0xEC,0x33,0xFC,0xEE,0x9D,0xEC | ||
51 | ,0x98,0x40,0x05,0xFC,0xEE,0x9D,0xFE,0x0F,0xD5,0xF0,0xE9,0xE4,0xCE,0xFD,0x22,0xED | ||
52 | ,0xF8,0xF5,0xF0,0xEE,0x84,0x20,0xD2,0x1C,0xFE,0xAD,0xF0,0x75,0xF0,0x08,0xEF,0x2F | ||
53 | ,0xFF,0xED,0x33,0xFD,0x40,0x07,0x98,0x50,0x06,0xD5,0xF0,0xF2,0x22,0xC3,0x98,0xFD | ||
54 | ,0x0F,0xD5,0xF0,0xEA,0x22,0xC2,0xD5,0xEC,0x30,0xE7,0x09,0xB2,0xD5,0xE4,0xC3,0x9D | ||
55 | ,0xFD,0xE4,0x9C,0xFC,0xEE,0x30,0xE7,0x15,0xB2,0xD5,0xE4,0xC3,0x9F,0xFF,0xE4,0x9E | ||
56 | ,0xFE,0x12,0x00,0x70,0xC3,0xE4,0x9D,0xFD,0xE4,0x9C,0xFC,0x80,0x03,0x12,0x00,0x70 | ||
57 | ,0x30,0xD5,0x07,0xC3,0xE4,0x9F,0xFF,0xE4,0x9E,0xFE,0x22,0xC5,0xF0,0xF8,0xA3,0xE0 | ||
58 | ,0x28,0xF0,0xC5,0xF0,0xF8,0xE5,0x82,0x15,0x82,0x70,0x02,0x15,0x83,0xE0,0x38,0xF0 | ||
59 | ,0x22,0xBB,0x01,0x0A,0x89,0x82,0x8A,0x83,0xE0,0xF5,0xF0,0xA3,0xE0,0x22,0x50,0x06 | ||
60 | ,0x87,0xF0,0x09,0xE7,0x19,0x22,0xBB,0xFE,0x07,0xE3,0xF5,0xF0,0x09,0xE3,0x19,0x22 | ||
61 | ,0x89,0x82,0x8A,0x83,0xE4,0x93,0xF5,0xF0,0x74,0x01,0x93,0x22,0xBB,0x01,0x10,0xE5 | ||
62 | ,0x82,0x29,0xF5,0x82,0xE5,0x83,0x3A,0xF5,0x83,0xE0,0xF5,0xF0,0xA3,0xE0,0x22,0x50 | ||
63 | ,0x09,0xE9,0x25,0x82,0xF8,0x86,0xF0,0x08,0xE6,0x22,0xBB,0xFE,0x0A,0xE9,0x25,0x82 | ||
64 | ,0xF8,0xE2,0xF5,0xF0,0x08,0xE2,0x22,0xE5,0x83,0x2A,0xF5,0x83,0xE9,0x93,0xF5,0xF0 | ||
65 | ,0xA3,0xE9,0x93,0x22,0xE8,0x8F,0xF0,0xA4,0xCC,0x8B,0xF0,0xA4,0x2C,0xFC,0xE9,0x8E | ||
66 | ,0xF0,0xA4,0x2C,0xFC,0x8A,0xF0,0xED,0xA4,0x2C,0xFC,0xEA,0x8E,0xF0,0xA4,0xCD,0xA8 | ||
67 | ,0xF0,0x8B,0xF0,0xA4,0x2D,0xCC,0x38,0x25,0xF0,0xFD,0xE9,0x8F,0xF0,0xA4,0x2C,0xCD | ||
68 | ,0x35,0xF0,0xFC,0xEB,0x8E,0xF0,0xA4,0xFE,0xA9,0xF0,0xEB,0x8F,0xF0,0xA4,0xCF,0xC5 | ||
69 | ,0xF0,0x2E,0xCD,0x39,0xFE,0xE4,0x3C,0xFC,0xEA,0xA4,0x2D,0xCE,0x35,0xF0,0xFD,0xE4 | ||
70 | ,0x3C,0xFC,0x22,0x75,0xF0,0x08,0x75,0x82,0x00,0xEF,0x2F,0xFF,0xEE,0x33,0xFE,0xCD | ||
71 | ,0x33,0xCD,0xCC,0x33,0xCC,0xC5,0x82,0x33,0xC5,0x82,0x9B,0xED,0x9A,0xEC,0x99,0xE5 | ||
72 | ,0x82,0x98,0x40,0x0C,0xF5,0x82,0xEE,0x9B,0xFE,0xED,0x9A,0xFD,0xEC,0x99,0xFC,0x0F | ||
73 | ,0xD5,0xF0,0xD6,0xE4,0xCE,0xFB,0xE4,0xCD,0xFA,0xE4,0xCC,0xF9,0xA8,0x82,0x22,0xB8 | ||
74 | ,0x00,0xC1,0xB9,0x00,0x59,0xBA,0x00,0x2D,0xEC,0x8B,0xF0,0x84,0xCF,0xCE,0xCD,0xFC | ||
75 | ,0xE5,0xF0,0xCB,0xF9,0x78,0x18,0xEF,0x2F,0xFF,0xEE,0x33,0xFE,0xED,0x33,0xFD,0xEC | ||
76 | ,0x33,0xFC,0xEB,0x33,0xFB,0x10,0xD7,0x03,0x99,0x40,0x04,0xEB,0x99,0xFB,0x0F,0xD8 | ||
77 | ,0xE5,0xE4,0xF9,0xFA,0x22,0x78,0x18,0xEF,0x2F,0xFF,0xEE,0x33,0xFE,0xED,0x33,0xFD | ||
78 | ,0xEC,0x33,0xFC,0xC9,0x33,0xC9,0x10,0xD7,0x05,0x9B,0xE9,0x9A,0x40,0x07,0xEC,0x9B | ||
79 | ,0xFC,0xE9,0x9A,0xF9,0x0F,0xD8,0xE0,0xE4,0xC9,0xFA,0xE4,0xCC,0xFB,0x22,0x75,0xF0 | ||
80 | ,0x10,0xEF,0x2F,0xFF,0xEE,0x33,0xFE,0xED,0x33,0xFD,0xCC,0x33,0xCC,0xC8,0x33,0xC8 | ||
81 | ,0x10,0xD7,0x07,0x9B,0xEC,0x9A,0xE8,0x99,0x40,0x0A,0xED,0x9B,0xFD,0xEC,0x9A,0xFC | ||
82 | ,0xE8,0x99,0xF8,0x0F,0xD5,0xF0,0xDA,0xE4,0xCD,0xFB,0xE4,0xCC,0xFA,0xE4,0xC8,0xF9 | ||
83 | ,0x22,0xEB,0x9F,0xF5,0xF0,0xEA,0x9E,0x42,0xF0,0xE9,0x9D,0x42,0xF0,0xE8,0x9C,0x45 | ||
84 | ,0xF0,0x22,0xE8,0x60,0x0F,0xEC,0xC3,0x13,0xFC,0xED,0x13,0xFD,0xEE,0x13,0xFE,0xEF | ||
85 | ,0x13,0xFF,0xD8,0xF1,0x22,0xE8,0x60,0x0F,0xEF,0xC3,0x33,0xFF,0xEE,0x33,0xFE,0xED | ||
86 | ,0x33,0xFD,0xEC,0x33,0xFC,0xD8,0xF1,0x22,0xE0,0xFC,0xA3,0xE0,0xFD,0xA3,0xE0,0xFE | ||
87 | ,0xA3,0xE0,0xFF,0x22,0xE0,0xF8,0xA3,0xE0,0xF9,0xA3,0xE0,0xFA,0xA3,0xE0,0xFB,0x22 | ||
88 | ,0xEC,0xF0,0xA3,0xED,0xF0,0xA3,0xEE,0xF0,0xA3,0xEF,0xF0,0x22,0xE0,0xFB,0xA3,0xE0 | ||
89 | ,0xFA,0xA3,0xE0,0xF9,0x22,0xF8,0xE0,0xFB,0xA3,0xA3,0xE0,0xF9,0x25,0xF0,0xF0,0xE5 | ||
90 | ,0x82,0x15,0x82,0x70,0x02,0x15,0x83,0xE0,0xFA,0x38,0xF0,0x22,0xEB,0xF0,0xA3,0xEA | ||
91 | ,0xF0,0xA3,0xE9,0xF0,0x22,0xD0,0x83,0xD0,0x82,0xF8,0xE4,0x93,0x70,0x12,0x74,0x01 | ||
92 | ,0x93,0x70,0x0D,0xA3,0xA3,0x93,0xF8,0x74,0x01,0x93,0xF5,0x82,0x88,0x83,0xE4,0x73 | ||
93 | ,0x74,0x02,0x93,0x68,0x60,0xEF,0xA3,0xA3,0xA3,0x80,0xDF,0x8A,0x83,0x89,0x82,0xE4 | ||
94 | ,0x73,0xEC,0x8E,0xF0,0xA4,0xCC,0xC5,0xF0,0xCC,0xCD,0xF8,0xEF,0xA4,0xCE,0xC5,0xF0 | ||
95 | ,0x2D,0xFD,0xE4,0x3C,0xFC,0xE8,0xA4,0x2E,0xC8,0xC5,0xF0,0x3D,0xFD,0xE4,0x3C,0xFC | ||
96 | ,0xEF,0xA4,0xFF,0xE5,0xF0,0x28,0xFE,0xE4,0x3D,0xFD,0xE4,0x3C,0xFC,0x22,0x78,0x7F | ||
97 | ,0xE4,0xF6,0xD8,0xFD,0x75,0x81,0x7F,0x02,0x03,0xB5,0x02,0x17,0x7F,0xE4,0x93,0xA3 | ||
98 | ,0xF8,0xE4,0x93,0xA3,0x40,0x03,0xF6,0x80,0x01,0xF2,0x08,0xDF,0xF4,0x80,0x29,0xE4 | ||
99 | ,0x93,0xA3,0xF8,0x54,0x07,0x24,0x0C,0xC8,0xC3,0x33,0xC4,0x54,0x0F,0x44,0x20,0xC8 | ||
100 | ,0x83,0x40,0x04,0xF4,0x56,0x80,0x01,0x46,0xF6,0xDF,0xE4,0x80,0x0B,0x01,0x02,0x04 | ||
101 | ,0x08,0x10,0x20,0x40,0x80,0x90,0x03,0xFA,0xE4,0x7E,0x01,0x93,0x60,0xBC,0xA3,0xFF | ||
102 | ,0x54,0x3F,0x30,0xE5,0x09,0x54,0x1F,0xFE,0xE4,0x93,0xA3,0x60,0x01,0x0E,0xCF,0x54 | ||
103 | ,0xC0,0x25,0xE0,0x60,0xA8,0x40,0xB8,0xE4,0x93,0xA3,0xFA,0xE4,0x93,0xA3,0xF8,0xE4 | ||
104 | ,0x93,0xA3,0xC8,0xC5,0x82,0xC8,0xCA,0xC5,0x83,0xCA,0xF0,0xA3,0xC8,0xC5,0x82,0xC8 | ||
105 | ,0xCA,0xC5,0x83,0xCA,0xDF,0xE9,0xDE,0xE7,0x80,0xBE,0x41,0x1B,0x5F,0x00,0x60,0x26 | ||
106 | ,0x1B,0x23,0x07,0x83,0x07,0xE6,0x08,0x0D,0x1F,0x5D,0x1F,0x98,0x07,0x5F,0x04,0x73 | ||
107 | ,0xFC,0x18,0x03,0xE8,0xFF,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,0x00,0x0B | ||
108 | ,0x00,0x01,0x00,0x04,0x00,0xA0,0x00,0x64,0xC1,0x45,0xC1,0x46,0xC1,0x48,0xC1,0x47 | ||
109 | ,0x00,0xE4,0xFD,0xFC,0xC2,0x8D,0xC2,0xA9,0xD2,0x8C,0xED,0x6F,0x70,0x02,0xEC,0x6E | ||
110 | ,0x60,0x0C,0x30,0x8D,0xFD,0x0D,0xBD,0x00,0x01,0x0C,0xC2,0x8D,0x80,0xEC,0xC2,0x8C | ||
111 | ,0x22,0xC2,0x8C,0xC2,0xA9,0x53,0x89,0xF0,0x43,0x89,0x02,0x75,0x8C,0x9C,0x75,0x8A | ||
112 | ,0x9C,0xC2,0x8D,0x22,0xD0,0xE0,0xD0,0xE0,0xE4,0xC0,0xE0,0xC0,0xE0,0x32,0x22,0x02 | ||
113 | ,0xFF,0xFC,0x22,0x42,0x30,0x00,0x47,0x30,0x03,0x47,0x30,0x0C,0x41,0x30,0x18,0x42 | ||
114 | ,0x30,0x1B,0x41,0x30,0x1E,0x41,0x30,0x20,0x41,0x31,0x06,0x41,0x35,0x03,0x42,0x36 | ||
115 | ,0x00,0x42,0x36,0x03,0x42,0x36,0x0A,0x41,0x36,0x0F,0x41,0x36,0x11,0x41,0x36,0x13 | ||
116 | ,0x41,0x36,0x15,0x44,0x37,0x02,0x43,0x37,0x07,0x41,0x37,0x0E,0x42,0x37,0x10,0x42 | ||
117 | ,0x37,0x14,0x43,0x37,0x17,0x44,0x37,0x1B,0x45,0x37,0x22,0x44,0x38,0x08,0x41,0x38 | ||
118 | ,0x19,0x41,0x3B,0x09,0x41,0x3C,0x01,0x41,0x40,0x00,0x44,0x40,0x02,0x41,0x40,0x09 | ||
119 | ,0x41,0x40,0x0C,0x41,0x40,0x4F,0x43,0x43,0x00,0x48,0x47,0x00,0x41,0x47,0x09,0x42 | ||
120 | ,0x47,0x0B,0x42,0x48,0x00,0x41,0x48,0x03,0x41,0x48,0x06,0x41,0x48,0x37,0x41,0x48 | ||
121 | ,0x42,0x41,0x48,0x4A,0x42,0x50,0x00,0x41,0x50,0x1F,0x41,0x50,0x25,0x42,0x50,0x3B | ||
122 | ,0x41,0x50,0x41,0x41,0x50,0x43,0x41,0x5B,0x01,0x41,0x5B,0x03,0x42,0x38,0x2C,0x41 | ||
123 | ,0x01,0x00,0x41,0x32,0x12,0x41,0x30,0x13,0x41,0x36,0x02,0x41,0x36,0x05,0x42,0x36 | ||
124 | ,0x0C,0x41,0x36,0x14,0x44,0x37,0x0A,0x41,0x37,0x0F,0x41,0x37,0x13,0x41,0x37,0x16 | ||
125 | ,0x41,0x37,0x21,0x42,0x37,0x27,0x45,0x38,0x03,0x46,0x38,0x0C,0x42,0x38,0x17,0x46 | ||
126 | ,0x38,0x1C,0x42,0x38,0x2C,0x41,0x40,0x01,0x42,0x40,0x50,0x41,0x40,0x53,0x41,0x50 | ||
127 | ,0x02,0x41,0x50,0x3D,0x41,0x50,0x42,0x41,0x50,0x47,0x41,0x59,0x01,0xEF,0xFF,0xFF | ||
128 | ,0x41,0x32,0x12,0x41,0x32,0x12,0x84,0xEE,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x09 | ||
129 | ,0x00,0xA7,0xA0,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x04,0xE0,0xF8 | ||
130 | ,0xF0,0x01,0x05,0x03,0x2D,0x1F,0x20,0x80,0x2E,0x00,0x24,0x6C,0x84,0x13,0x20,0x3D | ||
131 | ,0x28,0xD1,0x73,0x01,0x00,0x04,0x40,0x16,0x5F,0x58,0x80,0x11,0x11,0xA0,0x46,0x40 | ||
132 | ,0x2C,0x1A,0x30,0x2E,0x2E,0x70,0x00,0x40,0x00,0xF0,0x80,0x0A,0x80,0x29,0xC5,0x08 | ||
133 | ,0x02,0x10,0x40,0x00,0xFF,0xFF,0x00,0xF0,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00 | ||
134 | ,0x00,0x00,0x00,0x04,0x0F,0x50,0x80,0x1B,0x01,0x00,0x5F,0x4E,0x00,0x10,0x01,0x10 | ||
135 | ,0x0E,0x08,0x03,0x00,0x02,0x02,0x00,0x00,0x1F,0x53,0x11,0x42,0x13,0x0F,0x83,0x00 | ||
136 | ,0x04,0x81,0x00,0x57,0xB6,0x08,0x66,0x02,0x07,0x02,0x88,0x01,0xE6,0x0F,0xA0,0x01 | ||
137 | ,0xF4,0x22,0x02,0x24,0x4A,0x32,0xAC,0x07,0xA4,0x03,0xA0,0x10,0x10,0x00,0xC0,0x00 | ||
138 | ,0xA1,0x00,0x00,0x21,0x00,0x00,0xFF,0xFF,0x10,0xA0,0x02,0x88,0x01,0xE6,0xFF,0xFF | ||
139 | ,0xFF,0xFF,0xFF,0x00,0x00,0x09,0x00,0xA7,0xA0,0x08,0x00,0x00,0x00,0x00,0x00,0x00 | ||
140 | ,0x00,0x00,0x40,0x04,0xE0,0xF8,0xF0,0x01,0x05,0x03,0x2D,0x1F,0x20,0x80,0x2E,0x00 | ||
141 | ,0x24,0x6C,0x84,0x13,0x20,0x3D,0x28,0xD1,0x73,0x01,0x00,0x04,0x40,0x16,0x5F,0x58 | ||
142 | ,0x80,0x11,0x11,0xA0,0x46,0x40,0x2C,0x1A,0x30,0x2E,0x2E,0x70,0x00,0x40,0x00,0xF0 | ||
143 | ,0x80,0x0A,0x80,0x29,0xC5,0x08,0x02,0x10,0x40,0x00,0xFF,0xFF,0x00,0xF0,0x04,0x01 | ||
144 | ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0F,0x50,0x80,0x1B,0x01,0x00 | ||
145 | ,0x5F,0x4E,0x00,0x10,0x01,0x10,0x0E,0x08,0x03,0x00,0x02,0x02,0x00,0x00,0x1F,0x42 | ||
146 | ,0x11,0x42,0x13,0x30,0x80,0x00,0x84,0x01,0x61,0xF6,0x17,0x08,0x66,0x0C,0x0B,0x11 | ||
147 | ,0x40,0x0C,0xF0,0x1F,0x00,0x0D,0x08,0x44,0x96,0x24,0x40,0x30,0x0C,0x0C,0xFC,0x00 | ||
148 | ,0x08,0x04,0x04,0x02,0xC0,0x00,0xA1,0x00,0x00,0x21,0x00,0x00,0xFF,0xFF,0x10,0xA0 | ||
149 | ,0x11,0x40,0x0C,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x09,0x00,0xA7,0xA0,0x08 | ||
150 | ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x04,0xE0,0xF8,0xF0,0x01,0x05,0x03 | ||
151 | ,0x2D,0x1F,0x20,0x80,0x2E,0x00,0x24,0x6C,0x84,0x13,0x20,0x3D,0x28,0xD1,0x73,0x01 | ||
152 | ,0x00,0x04,0x40,0x16,0x5F,0x58,0x80,0x11,0x11,0xA0,0x46,0x40,0x2C,0x1A,0x30,0x2E | ||
153 | ,0x2E,0x70,0x00,0x40,0x00,0xF0,0x80,0x0A,0x80,0x29,0xC5,0x08,0x02,0x10,0x40,0x00 | ||
154 | ,0xFF,0xFF,0x00,0xF0,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04 | ||
155 | ,0x0F,0x50,0x80,0x1B,0x01,0x00,0x5F,0x4E,0x00,0x10,0x01,0x10,0x0E,0x08,0x03,0x00 | ||
156 | ,0x02,0x02,0x00,0x00,0x1F,0x42,0x11,0x42,0x13,0x30,0x80,0x00,0x84,0x01,0x61,0xF6 | ||
157 | ,0x17,0x08,0x66,0x0C,0x0B,0x02,0x88,0x01,0xE6,0x0A,0x40,0x01,0xFC,0x44,0x96,0x24 | ||
158 | ,0x40,0x35,0x85,0x01,0xF2,0x07,0x6C,0x04,0x04,0x02,0xC0,0x00,0xA1,0x00,0x00,0x31 | ||
159 | ,0x00,0x00,0xFF,0xFF,0x10,0xA0,0x02,0x88,0x01,0xE6,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
160 | ,0x00,0x05,0x6D,0x07,0x18,0x06,0x13,0xFF,0x05,0x06,0xB9,0xFF,0xFF,0xFF,0xFF,0xFF | ||
161 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
162 | ,0xFF,0xFF,0xFF,0x01,0x0D,0x4C,0x02,0x08,0xB9,0x03,0x0F,0xF5,0x05,0x0D,0xC7,0x0A | ||
163 | ,0x14,0x6D,0x0B,0x14,0xB6,0x0C,0x0F,0xD2,0x0D,0x14,0xED,0x0F,0x15,0x59,0x10,0x15 | ||
164 | ,0x5D,0x14,0x15,0x70,0x18,0x11,0xAF,0x19,0x12,0x79,0x1F,0x15,0x61,0x20,0x17,0x29 | ||
165 | ,0x22,0x14,0x06,0x23,0x14,0x2D,0x29,0x18,0xAF,0x2A,0x10,0x9F,0x39,0x17,0x9F,0x3B | ||
166 | ,0x0E,0xA6,0x3C,0x17,0xEB,0x4A,0x18,0x92,0x4B,0x14,0x88,0x50,0x17,0x42,0x57,0x15 | ||
167 | ,0x61,0x59,0x11,0x3D,0x60,0x17,0x65,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
168 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x22,0xA2,0xC1,0xCC,0xC1,0xCB,0x25,0x11,0x26,0x88 | ||
169 | ,0x60,0x00,0x64,0x45,0x30,0x03,0x41,0x01,0x00,0x60,0x00,0x64,0x41,0x01,0x00,0xEF | ||
170 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0B,0x02,0xF7 | ||
171 | ,0x00,0x00,0x00,0x00,0x00,0x04,0x14,0x28,0x36,0x64,0x04,0x00,0x09,0x00,0x9D,0xC3 | ||
172 | ,0x0D,0x01,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
173 | ,0x90,0x1B,0x23,0x12,0x18,0x70,0x90,0x1B,0x68,0x12,0x03,0x0C,0x90,0x1B,0x68,0xF1 | ||
174 | ,0xE3,0xFF,0xF4,0x60,0x34,0xEF,0x14,0xB5,0xC5,0x23,0x75,0xC5,0xFE,0xE4,0xFD,0x7F | ||
175 | ,0x8E,0x11,0x89,0x90,0x1B,0x68,0x12,0x02,0xEC,0x90,0x00,0x01,0x12,0x01,0x3C,0xAA | ||
176 | ,0xF0,0xF9,0x12,0x03,0x3B,0x7D,0x04,0x7F,0x8E,0x11,0x89,0x80,0x0F,0x90,0x1B,0x69 | ||
177 | ,0xE4,0x75,0xF0,0x03,0x12,0x00,0xFB,0x80,0xC3,0x75,0xC1,0x06,0x75,0xC5,0xFF,0x22 | ||
178 | ,0xFD,0x7F,0x20,0x11,0x89,0xE4,0xFD,0x7F,0x12,0x8F,0xFA,0x8D,0xFB,0xE4,0xF5,0xF8 | ||
179 | ,0x90,0x1B,0x35,0xE0,0xFE,0xA3,0xE0,0xFF,0x43,0xF8,0x01,0xEF,0x4E,0x70,0x08,0xE5 | ||
180 | ,0xF9,0x64,0x0F,0x60,0x13,0x80,0xF8,0xE5,0xF9,0x64,0x0F,0x60,0x0B,0xEF,0x1F,0xAC | ||
181 | ,0x06,0x70,0x01,0x1E,0x4C,0x70,0xF0,0x22,0x22,0xAF,0xC1,0xEF,0x14,0x60,0x15,0x24 | ||
182 | ,0xFE,0x60,0x3E,0x24,0x03,0x70,0x63,0xE5,0xC2,0xB4,0x01,0x06,0x12,0x17,0x92,0x02 | ||
183 | ,0x04,0x64,0x80,0x56,0xE5,0xC2,0x64,0x01,0x70,0x25,0x31,0x35,0xE4,0xFD,0x7F,0x20 | ||
184 | ,0x11,0x89,0x7D,0x03,0x7F,0x12,0x11,0x89,0x7D,0x07,0x7F,0x14,0x11,0x89,0x7D,0x03 | ||
185 | ,0x7F,0x13,0x11,0x89,0x75,0xC5,0xFF,0xE4,0xF5,0xC1,0xFD,0x7F,0x18,0x80,0x8A,0x80 | ||
186 | ,0x29,0xE5,0xC2,0x70,0x12,0x31,0x35,0x7F,0x20,0xB1,0x22,0x90,0x1B,0x5F,0xEF,0xF0 | ||
187 | ,0xE4,0x11,0x80,0x7D,0x03,0x80,0x0D,0xE5,0xC2,0xB4,0x01,0x0C,0x90,0x1B,0x5F,0xE0 | ||
188 | ,0x11,0x80,0xE4,0xFD,0x7F,0x13,0xA1,0xAD,0x80,0x00,0x75,0xC1,0x01,0x22,0xE4,0xFD | ||
189 | ,0xFF,0x7E,0x01,0x80,0x0E,0x31,0x2E,0x7F,0x0A,0x7E,0x00,0x02,0x04,0x31,0xFD,0x7F | ||
190 | ,0x02,0x7E,0x35,0xAB,0x07,0xAA,0x06,0xEA,0xF5,0xFA,0xEB,0xF5,0xFB,0x8D,0xFC,0x75 | ||
191 | ,0xF8,0x10,0x01,0x90,0xE4,0xFD,0x7F,0x12,0x7E,0x32,0x31,0x43,0xAD,0x43,0xAC,0x42 | ||
192 | ,0x7F,0x00,0x7E,0x35,0x31,0x84,0xE5,0x45,0x31,0x3E,0xAD,0x49,0xAC,0x48,0x7F,0x0A | ||
193 | ,0x7E,0x35,0x31,0x84,0x7D,0x10,0x7F,0x12,0x7E,0x32,0x31,0x43,0x7D,0xA0,0x7F,0x12 | ||
194 | ,0x7E,0x32,0x80,0xBF,0x8E,0x31,0x8F,0x32,0x8C,0x33,0x8D,0x34,0xE5,0x33,0xFD,0x31 | ||
195 | ,0x43,0xE5,0x32,0x24,0x01,0xFF,0xE4,0x35,0x31,0xFE,0xAD,0x34,0x80,0xA5,0x8E,0x2F | ||
196 | ,0x8F,0x30,0x90,0x1B,0x10,0xE0,0xFF,0x90,0x1B,0x0F,0xE0,0xFD,0x12,0x18,0x35,0xAA | ||
197 | ,0x06,0xA9,0x07,0x7B,0xFF,0x90,0x1B,0x4E,0x12,0x03,0x0C,0x90,0x00,0x80,0x12,0x00 | ||
198 | ,0x1F,0xFE,0x90,0x00,0x81,0x12,0x00,0x1F,0x7C,0x00,0x90,0x1B,0x5B,0xB1,0x18,0x90 | ||
199 | ,0x00,0x82,0x12,0x00,0x1F,0xFE,0x90,0x00,0x83,0x12,0x00,0x1F,0x90,0x1B,0x5D,0xB1 | ||
200 | ,0x18,0xB1,0x05,0x7C,0x41,0x7D,0x1A,0x12,0x03,0x41,0x7B,0x00,0x7A,0x71,0x79,0x02 | ||
201 | ,0x78,0x00,0x12,0x01,0xFF,0x90,0x1B,0x51,0xEE,0xF0,0xA3,0xEF,0xF0,0xAE,0x2F,0xAF | ||
202 | ,0x30,0x7C,0x27,0x7D,0x10,0x12,0x03,0x41,0xC0,0x06,0xC0,0x07,0x90,0x1B,0x51,0xE0 | ||
203 | ,0xFE,0xA3,0xE0,0xFB,0xAA,0x06,0xE4,0xF9,0xF8,0xD0,0x07,0xD0,0x06,0x12,0x01,0xFF | ||
204 | ,0x90,0x1B,0x53,0x12,0x02,0xE0,0xB1,0x05,0xE4,0xFC,0xFD,0x90,0x1B,0x53,0x12,0x02 | ||
205 | ,0xD4,0x12,0x01,0x74,0xE4,0x7B,0x20,0xFA,0xF9,0xF8,0x12,0x01,0x74,0xE4,0x7B,0xE0 | ||
206 | ,0x7A,0x2E,0xF9,0xF8,0x12,0x01,0xFF,0x90,0x1B,0x57,0x12,0x02,0xE0,0x90,0x1B,0x57 | ||
207 | ,0x12,0x02,0xC8,0x78,0x08,0x12,0x02,0xA2,0x90,0x1B,0x11,0xEE,0xF0,0xA3,0xEF,0xF0 | ||
208 | ,0x90,0x1B,0x57,0x12,0x02,0xC8,0xE4,0x90,0x1B,0x13,0xEF,0xF0,0x7F,0x18,0x7E,0x38 | ||
209 | ,0xB1,0x3A,0xEF,0x30,0xE0,0x0D,0x51,0xD8,0x78,0x01,0x12,0x02,0xA2,0x90,0x1B,0x53 | ||
210 | ,0x12,0x02,0xE0,0x51,0xD8,0xEF,0x54,0xF0,0xFF,0xE4,0xF5,0x44,0x8F,0x45,0x51,0xD8 | ||
211 | ,0x78,0x08,0x12,0x02,0xA2,0xE4,0x8E,0x42,0x8F,0x43,0x51,0xD8,0x78,0x04,0x12,0x02 | ||
212 | ,0xA2,0xF1,0x35,0x90,0x1B,0x5E,0xE0,0x24,0xF8,0xFF,0x90,0x1B,0x5D,0xE0,0x34,0xFF | ||
213 | ,0xFE,0xE4,0xFC,0xFD,0xD3,0x12,0x02,0x91,0x40,0x10,0x51,0xD8,0x78,0x04,0x12,0x02 | ||
214 | ,0xA2,0xEF,0x24,0x08,0xFD,0xE4,0x3E,0xFC,0x80,0x08,0x90,0x1B,0x5D,0xE0,0xFC,0xA3 | ||
215 | ,0xE0,0xFD,0x7F,0x0E,0x7E,0x38,0x21,0x84,0x90,0x1B,0x53,0x02,0x02,0xC8,0x90,0x1B | ||
216 | ,0x2F,0xE0,0xFE,0xA3,0xE0,0xFF,0x90,0x1B,0x49,0x12,0x02,0xEC,0xAC,0x02,0xAD,0x01 | ||
217 | ,0x8E,0x33,0x8F,0x34,0x8C,0x35,0x8D,0x36,0x8F,0x82,0x8E,0x83,0xE4,0x93,0x91,0xFC | ||
218 | ,0x70,0x02,0x05,0x33,0x90,0x1B,0x4C,0xE0,0xFF,0xF4,0x70,0x02,0x81,0x19,0xEF,0x54 | ||
219 | ,0xE0,0xFB,0x70,0x24,0xE0,0x54,0x1F,0xB1,0x0E,0x05,0x34,0xE5,0x34,0x70,0x02,0x05 | ||
220 | ,0x33,0xF5,0x82,0x85,0x33,0x83,0xE4,0x93,0x90,0x1B,0x4C,0xF0,0x60,0xEB,0x05,0x34 | ||
221 | ,0xE5,0x34,0x70,0x02,0x05,0x33,0x80,0xCC,0x90,0x1B,0x4C,0xE0,0x54,0x1F,0xA3,0xF0 | ||
222 | ,0x90,0x1B,0x4C,0xEB,0xF0,0x64,0x20,0x60,0x0A,0xE0,0xFF,0x64,0x80,0x60,0x04,0xEF | ||
223 | ,0xB4,0xC0,0x15,0x91,0x2A,0xFF,0x90,0x1B,0x4E,0xE4,0xF0,0xA3,0xEF,0xF0,0x05,0x34 | ||
224 | ,0xE5,0x34,0x70,0x02,0x05,0x33,0x80,0x19,0x91,0x2A,0xFF,0x74,0x01,0x93,0x90,0x1B | ||
225 | ,0x4E,0xCF,0xF0,0xA3,0xEF,0xF0,0x74,0x02,0x25,0x34,0xF5,0x34,0xE4,0x35,0x33,0xF5 | ||
226 | ,0x33,0x90,0x1B,0x4C,0xE0,0xB4,0x60,0x08,0x91,0x33,0xFF,0x12,0x04,0x31,0x80,0x7F | ||
227 | ,0x90,0x1B,0x4C,0xE0,0xB4,0xE0,0x21,0xA3,0xE0,0xB4,0x02,0x16,0xAA,0x35,0xA9,0x36 | ||
228 | ,0x7B,0xFF,0x12,0x01,0x11,0x85,0xF0,0x35,0xF5,0x36,0x91,0x33,0x8E,0x33,0xF5,0x34 | ||
229 | ,0x80,0x5D,0x74,0x02,0xB1,0x0E,0x80,0x57,0x90,0x1B,0x4D,0xE0,0xD3,0x94,0x00,0x40 | ||
230 | ,0x4E,0x90,0x1B,0x4C,0xE0,0xB4,0xC0,0x07,0x91,0x1A,0x12,0x18,0x7A,0x80,0x26,0x90 | ||
231 | ,0x1B,0x4C,0xE0,0xB4,0x80,0x09,0x91,0x1A,0x7B,0x01,0x12,0x15,0x27,0x80,0x16,0x90 | ||
232 | ,0x1B,0x4C,0xE0,0x90,0x1B,0x4E,0xB4,0x40,0x08,0xE0,0xFE,0x91,0x1D,0x31,0x43,0x80 | ||
233 | ,0x04,0x91,0x1D,0x11,0x89,0x05,0x36,0xE5,0x36,0x70,0x02,0x05,0x35,0x90,0x1B,0x4E | ||
234 | ,0xE4,0x75,0xF0,0x01,0x12,0x00,0xFB,0x90,0x1B,0x4D,0xE0,0x14,0xF0,0x80,0xA9,0x91 | ||
235 | ,0x2A,0x91,0xFC,0x70,0x02,0x05,0x33,0x61,0x04,0x22,0x90,0x1B,0x4E,0xA3,0xE0,0xFF | ||
236 | ,0x85,0x36,0x82,0x85,0x35,0x83,0xE4,0x93,0xFD,0x22,0x85,0x34,0x82,0x85,0x33,0x83 | ||
237 | ,0xE4,0x93,0x22,0x90,0x1B,0x4E,0xE0,0xFE,0xA3,0xE0,0x22,0xE4,0x90,0x1B,0x4E,0xF0 | ||
238 | ,0xA3,0xF0,0x30,0x47,0x05,0x90,0x1B,0x01,0x80,0x03,0x90,0x1B,0x45,0xE0,0xFA,0xA3 | ||
239 | ,0xE0,0xFB,0x90,0x1B,0x44,0xE0,0x2B,0xFE,0x90,0x1B,0x43,0xE0,0x3A,0x90,0x1B,0x50 | ||
240 | ,0xF0,0xA3,0xCE,0xF0,0xA3,0xEA,0xF0,0xA3,0xEB,0xF0,0x30,0x48,0x6B,0xD2,0x45,0x30 | ||
241 | ,0x47,0x53,0x91,0xF3,0x90,0x1B,0x43,0xE0,0xFE,0xA3,0xE0,0xFF,0xD3,0x94,0x00,0xEE | ||
242 | ,0x94,0x00,0x40,0x0A,0xEF,0x24,0x06,0xFF,0xE4,0x3E,0xFE,0x12,0x04,0x31,0x91,0x33 | ||
243 | ,0xFF,0x90,0x1B,0x52,0xD1,0x25,0x40,0x54,0xB1,0x2C,0xC3,0x90,0x1B,0x38,0xE0,0x9F | ||
244 | ,0x90,0x1B,0x37,0xE0,0x9E,0x50,0x45,0x90,0x1B,0x39,0xA3,0xE0,0xFF,0xBF,0xFF,0x02 | ||
245 | ,0x80,0x22,0x90,0x1B,0x37,0xF1,0xBE,0x90,0x1B,0x4D,0xE0,0x9F,0xFF,0x90,0x1B,0x4C | ||
246 | ,0xE0,0x9E,0xFE,0x80,0x24,0x91,0xF3,0x91,0x33,0xFF,0xA3,0xD1,0x25,0x40,0x1D,0xB1 | ||
247 | ,0x2C,0x4E,0x60,0x18,0xF1,0xB2,0x80,0x11,0x91,0xF3,0x90,0x1B,0x50,0xE0,0xFE,0xA3 | ||
248 | ,0xE0,0xFF,0x7C,0x00,0x7D,0x05,0x12,0x00,0x5E,0x12,0x04,0x31,0x31,0x37,0x53,0xCB | ||
249 | ,0xFB,0x21,0x2E,0xF1,0xAA,0x30,0x48,0x03,0x43,0xCB,0x04,0x22,0x90,0x1B,0x4C,0xF0 | ||
250 | ,0x05,0x34,0xE5,0x34,0x22,0x90,0x1B,0x5B,0xE0,0xFE,0xA3,0xE0,0xFF,0x22,0x25,0x36 | ||
251 | ,0xF5,0x36,0xE4,0x35,0x35,0xF5,0x35,0x22,0x24,0x00,0xFF,0xEC,0x3E,0xF0,0xA3,0xEF | ||
252 | ,0xF0,0x22,0x8F,0xFA,0x75,0xF8,0x02,0x11,0x90,0xAF,0xFB,0x22,0xED,0x9F,0xFF,0xEC | ||
253 | ,0x9E,0xFE,0x90,0x1B,0x4C,0xF0,0xA3,0xEF,0xF0,0x22,0xAD,0x07,0xAC,0x06,0xEC,0xF5 | ||
254 | ,0xFA,0xED,0xF5,0xFB,0x75,0xF8,0x12,0x11,0x90,0xAF,0xFB,0x22,0x12,0x04,0x51,0x90 | ||
255 | ,0x1B,0x25,0xF1,0xC5,0x90,0x1F,0xF8,0xE4,0x93,0xF4,0x70,0x4D,0x7F,0x05,0x7E,0x3D | ||
256 | ,0xB1,0x3A,0xEF,0x60,0x44,0x75,0x2F,0x3D,0x75,0x30,0x0E,0x75,0x31,0x1F,0x75,0x32 | ||
257 | ,0x83,0xE4,0x90,0x1B,0x49,0xF0,0xB1,0xB3,0x70,0x02,0x05,0x31,0x05,0x30,0xE5,0x30 | ||
258 | ,0x70,0x02,0x05,0x2F,0xD1,0x11,0x94,0x04,0x40,0xEC,0x75,0x31,0x1F,0x75,0x32,0xB8 | ||
259 | ,0xE4,0x90,0x1B,0x49,0xF0,0xB1,0xB3,0x70,0x02,0x05,0x31,0x05,0x30,0xE5,0x30,0x70 | ||
260 | ,0x02,0x05,0x2F,0xD1,0x11,0x94,0x1E,0x40,0xEC,0x7D,0x04,0x7F,0x8E,0x11,0x89,0xE4 | ||
261 | ,0xF5,0xC1,0x22,0xAF,0x30,0xAE,0x2F,0xB1,0x3A,0xEF,0xF4,0x85,0x32,0x82,0x85,0x31 | ||
262 | ,0x83,0xF0,0x05,0x32,0xE5,0x32,0x22,0x7F,0x03,0x7E,0x30,0xB1,0x3A,0x8F,0x2F,0x7F | ||
263 | ,0x06,0x7E,0x30,0xB1,0x3A,0x8F,0x30,0xAF,0xC1,0xEF,0x24,0xFC,0x60,0x12,0x24,0x02 | ||
264 | ,0x70,0x1C,0x53,0x2F,0xFC,0x43,0x2F,0x01,0x53,0x30,0xDF,0x43,0x30,0x20,0x80,0x09 | ||
265 | ,0x53,0x2F,0xFC,0x43,0x2F,0x02,0x53,0x30,0xDF,0xE4,0xF5,0xC1,0x80,0x03,0x75,0xC1 | ||
266 | ,0x01,0xAD,0x2F,0x7F,0x03,0x7E,0x30,0x31,0x43,0xAD,0x30,0x7F,0x06,0x7E,0x30,0x21 | ||
267 | ,0x43,0x90,0x1B,0x49,0xE0,0x04,0xF0,0xE0,0xC3,0x22,0x90,0x1B,0x41,0xE0,0xFE,0xA3 | ||
268 | ,0xE0,0xFF,0x90,0x1B,0x3F,0xE0,0xFC,0xA3,0xE0,0xFD,0xD3,0x9F,0xEC,0x9E,0x22,0x30 | ||
269 | ,0x47,0x22,0xD1,0x1A,0x40,0x11,0xED,0x9F,0xFF,0xEC,0x9E,0xFE,0x90,0x1B,0x02,0xE0 | ||
270 | ,0x90,0x1B,0x01,0xF1,0x15,0x80,0x30,0xF1,0x0C,0x90,0x1B,0x02,0xE0,0x2F,0xFF,0x90 | ||
271 | ,0x1B,0x01,0x80,0x23,0x30,0x48,0x27,0xD1,0x1A,0x40,0x11,0xED,0x9F,0xFF,0xEC,0x9E | ||
272 | ,0xFE,0x90,0x1B,0x46,0xE0,0x90,0x1B,0x45,0xF1,0x15,0x80,0x0B,0xF1,0x0C,0x90,0x1B | ||
273 | ,0x46,0xE0,0x2F,0xFF,0x90,0x1B,0x45,0xE0,0x3E,0x90,0x1B,0x4C,0x80,0x0B,0x90,0x1B | ||
274 | ,0x45,0xE0,0xFF,0xA3,0xE0,0x90,0x1B,0x4C,0xCF,0xF0,0xA3,0xEF,0xF0,0xF1,0xB2,0x31 | ||
275 | ,0x9E,0x30,0x47,0x05,0x90,0x1B,0x03,0x80,0x03,0x90,0x1B,0x47,0xE0,0xFE,0xA3,0xE0 | ||
276 | ,0xFF,0x12,0x15,0xB2,0x21,0x54,0x7D,0x01,0xAF,0xC1,0x12,0x18,0x35,0x12,0x19,0xB7 | ||
277 | ,0x70,0x06,0xE9,0xF4,0x70,0x02,0xEA,0xF4,0x70,0x04,0x75,0xC1,0x01,0x22,0xD2,0x46 | ||
278 | ,0x12,0x19,0x3B,0x90,0x1B,0x0F,0xE0,0x70,0x06,0x31,0x2E,0xF1,0x5C,0x51,0xDE,0x90 | ||
279 | ,0x1B,0x0F,0x74,0x01,0xF0,0xA3,0xE5,0xC1,0xF0,0x30,0x47,0x05,0x90,0x1B,0x01,0x80 | ||
280 | ,0x03,0x90,0x1B,0x45,0xE0,0xFF,0xA3,0xE0,0x90,0x1B,0x17,0xCF,0xF0,0xA3,0xEF,0xF0 | ||
281 | ,0xD1,0x2F,0xE4,0x90,0x1B,0x19,0xF0,0x90,0x1B,0x14,0xE0,0xFF,0xA3,0xE0,0x90,0x1B | ||
282 | ,0x1A,0xCF,0xF0,0xA3,0xEF,0xF0,0x91,0x3B,0xE4,0xF5,0xC1,0x22,0x90,0x1B,0x3D,0xE0 | ||
283 | ,0xFE,0xA3,0xE0,0xFF,0x22,0x2F,0xFF,0xE0,0x3E,0xFE,0x90,0x1B,0x3E,0xE0,0x2F,0xFF | ||
284 | ,0x90,0x1B,0x3D,0x22,0xE4,0x33,0x24,0x01,0xFF,0xE4,0x33,0xFE,0xE4,0x33,0xFD,0xE4 | ||
285 | ,0x33,0xFC,0x12,0x01,0x74,0xA8,0x04,0xA9,0x05,0xAA,0x06,0xAB,0x07,0x22,0xAB,0x07 | ||
286 | ,0xAA,0x06,0xB1,0x3A,0x90,0x1B,0x4C,0xEF,0xF0,0xEB,0x24,0x01,0xFF,0xE4,0x3A,0xFE | ||
287 | ,0xB1,0x3A,0xEF,0xFD,0x90,0x1B,0x4C,0xE0,0xFE,0xED,0xFF,0x22,0x7F,0x00,0x7E,0x35 | ||
288 | ,0xF1,0x3E,0x90,0x1B,0x1D,0xEE,0xF0,0xA3,0xEF,0xF0,0x7F,0x02,0x7E,0x35,0xB1,0x3A | ||
289 | ,0x90,0x1B,0x1F,0xE4,0xF0,0xA3,0xEF,0xF0,0x7F,0x0A,0x7E,0x35,0xF1,0x3E,0x90,0x1B | ||
290 | ,0x21,0xEE,0xF0,0xA3,0xEF,0xF0,0x22,0x90,0x1B,0x1D,0xE0,0xFC,0xA3,0xE0,0xFD,0x7F | ||
291 | ,0x00,0x7E,0x35,0x31,0x84,0x90,0x1B,0x1F,0xA3,0xE0,0x31,0x3E,0x90,0x1B,0x21,0xE0 | ||
292 | ,0xFC,0xA3,0xE0,0xFD,0x7F,0x0A,0x7E,0x35,0x21,0x84,0x7D,0x01,0x7F,0x00,0x7E,0x01 | ||
293 | ,0x21,0x43,0x90,0x1B,0x4C,0xE0,0xFE,0xA3,0xE0,0xFF,0x22,0x90,0x1B,0x3B,0xE0,0xFE | ||
294 | ,0xA3,0xE0,0xFF,0xC3,0x22,0xE0,0xFE,0xA3,0xE0,0xFF,0xA3,0xE0,0xFC,0xA3,0xE0,0xFD | ||
295 | ,0x41,0xF0,0xE5,0xC1,0xC3,0x94,0xC1,0x50,0x06,0xAD,0xC4,0xAF,0xC1,0xA1,0xAD,0x75 | ||
296 | ,0xC1,0x01,0x22,0x12,0x02,0xEC,0x02,0x00,0x06,0x7F,0x0A,0x7E,0x30,0xE1,0x3E,0x7F | ||
297 | ,0x2A,0x7E,0x30,0xA1,0x3A,0x90,0x1B,0x4B,0xE5,0xC1,0xF0,0xE4,0xF5,0xC1,0xE0,0x12 | ||
298 | ,0x03,0x15,0x10,0x21,0x00,0x10,0x2B,0x01,0x10,0x40,0x02,0x10,0x52,0x03,0x10,0x5C | ||
299 | ,0x04,0x10,0x63,0x05,0x10,0x6A,0x07,0x10,0x7B,0x08,0x10,0x7F,0x09,0x00,0x00,0x10 | ||
300 | ,0x88,0x75,0xC2,0x4B,0xE4,0xF5,0xC3,0x75,0xC4,0x01,0x22,0x12,0x0F,0xE9,0x11,0x8C | ||
301 | ,0x12,0x0F,0xEF,0x8F,0xC2,0x90,0x1B,0x49,0x11,0x98,0xF5,0xC3,0xED,0xF5,0xC4,0x22 | ||
302 | ,0x75,0xC2,0x08,0xE4,0xFF,0x12,0x0D,0x22,0x8F,0xC3,0x7F,0x01,0x12,0x0D,0x22,0x8F | ||
303 | ,0xC4,0x22,0xE4,0xF5,0xC2,0x75,0xC3,0x2C,0x75,0xC4,0x38,0x22,0x75,0xC2,0x07,0xE4 | ||
304 | ,0xF5,0xC3,0x22,0x75,0xC2,0x5D,0x75,0xC3,0xC0,0x22,0x7F,0x02,0x71,0x44,0x11,0x8C | ||
305 | ,0x90,0x1B,0x49,0x11,0x98,0xF5,0xC2,0xED,0xF5,0xC3,0x22,0x75,0xC2,0x0A,0x22,0x31 | ||
306 | ,0x36,0x8F,0xC2,0x31,0x29,0x8F,0xC3,0x22,0x75,0xC1,0x01,0x22,0x90,0x1B,0x49,0xEE | ||
307 | ,0xF0,0xA3,0xEF,0xF0,0x22,0x90,0x1B,0x11,0xE0,0xFC,0xA3,0xE0,0xFD,0xEC,0x22,0xE4 | ||
308 | ,0x90,0x1B,0x49,0xF0,0xA3,0xF0,0xA3,0xF0,0xA3,0xF0,0xAF,0xC1,0xEF,0x12,0x03,0x15 | ||
309 | ,0x10,0xC9,0x00,0x10,0xD2,0x01,0x10,0xD7,0x05,0x10,0xDC,0x10,0x10,0xED,0x11,0x10 | ||
310 | ,0xF6,0x15,0x11,0x00,0x20,0x00,0x00,0x11,0x08,0x11,0x95,0x31,0x21,0x90,0x1B,0x13 | ||
311 | ,0x80,0x14,0x90,0x1B,0x14,0x80,0x19,0x90,0x1B,0x16,0x80,0x1D,0x90,0x1B,0x17,0x11 | ||
312 | ,0x98,0x31,0x21,0x90,0x1B,0x19,0xE0,0x90,0x1B,0x4C,0xF0,0x80,0x21,0x90,0x1B,0x1A | ||
313 | ,0x11,0x98,0x31,0x21,0x80,0x18,0x90,0x1B,0x1C,0xE0,0x90,0x1B,0x4A,0xF0,0x80,0x0E | ||
314 | ,0x90,0x1B,0x49,0x74,0x04,0xF0,0x80,0x06,0x90,0x1B,0x49,0x74,0x01,0xF0,0x90,0x1B | ||
315 | ,0x49,0xE0,0xF5,0xC1,0xA3,0xE0,0xF5,0xC2,0xA3,0xE0,0xF5,0xC3,0xA3,0xE0,0xF5,0xC4 | ||
316 | ,0x22,0x90,0x1B,0x4A,0xF0,0xED,0xA3,0xF0,0x22,0x7F,0x01,0x8F,0xFA,0x75,0xF8,0x22 | ||
317 | ,0x12,0x08,0x90,0xAF,0xFB,0x22,0xE4,0xFF,0x31,0x2B,0x7E,0x00,0x22,0xE4,0xF5,0x2F | ||
318 | ,0xAF,0xC1,0xEF,0x14,0x60,0x11,0x14,0x60,0x18,0x14,0x60,0x15,0x14,0x60,0x17,0x24 | ||
319 | ,0x04,0x70,0x1F,0x31,0x8B,0x80,0x1E,0x90,0x1B,0x64,0x11,0x98,0xFF,0x8F,0xC3,0x80 | ||
320 | ,0x0C,0x75,0x2F,0x04,0x80,0x0F,0x90,0x1B,0x66,0x11,0x98,0xF5,0xC3,0xED,0xF5,0xC4 | ||
321 | ,0x80,0x03,0x75,0x2F,0x01,0x85,0x2F,0xC1,0x22,0xE0,0x54,0x1F,0xFC,0xA3,0xE0,0xFD | ||
322 | ,0x7F,0x50,0x51,0x62,0x7F,0xE8,0x7E,0x03,0x12,0x04,0x31,0x71,0x3C,0xC3,0x33,0xCE | ||
323 | ,0x33,0xCE,0xD8,0xF9,0xFF,0x7C,0x00,0x7D,0x08,0x12,0x00,0xC5,0x90,0x1B,0x64,0xEE | ||
324 | ,0xF0,0xA3,0xEF,0xF0,0x71,0xAA,0x90,0x1B,0x66,0xEE,0xF0,0xA3,0xEF,0xF0,0x22,0x90 | ||
325 | ,0x1B,0x2B,0xE0,0xFE,0xA3,0xE0,0x8E,0x2F,0xF5,0x30,0x8E,0x31,0xF5,0x32,0x90,0x1B | ||
326 | ,0x29,0x12,0x0F,0xC5,0xAF,0xC1,0xEF,0x24,0xFD,0x60,0x0B,0x04,0x70,0x10,0x74,0x36 | ||
327 | ,0x51,0x58,0x74,0x38,0x80,0x0E,0x74,0x3A,0x51,0x58,0x74,0x3C,0x80,0x06,0x74,0x32 | ||
328 | ,0x51,0x58,0x74,0x34,0x25,0x32,0xF5,0x32,0xE4,0x35,0x31,0xF5,0x31,0xE5,0xC1,0x60 | ||
329 | ,0x3A,0x85,0x30,0x82,0x85,0x2F,0x83,0xE4,0x93,0xFE,0x05,0x30,0xE5,0x30,0x70,0x02 | ||
330 | ,0x05,0x2F,0xF5,0x82,0x85,0x2F,0x83,0xE4,0x93,0x90,0x1B,0x31,0x51,0x70,0x85,0x32 | ||
331 | ,0x82,0x85,0x31,0x83,0xE4,0x93,0xFE,0x05,0x32,0xE5,0x32,0x70,0x02,0x05,0x31,0xF5 | ||
332 | ,0x82,0x85,0x31,0x83,0xE4,0x93,0x90,0x1B,0x33,0x51,0x70,0x90,0x1B,0x31,0x31,0x79 | ||
333 | ,0x90,0x1B,0x33,0x31,0x79,0x90,0x1B,0x31,0xE0,0xF5,0x3E,0xA3,0xE0,0xF5,0x3F,0xA3 | ||
334 | ,0xE0,0xF5,0x40,0xA3,0xE0,0xF5,0x41,0xE4,0xF5,0x3A,0xF5,0x3B,0x75,0xC1,0x01,0x75 | ||
335 | ,0xC2,0x44,0x51,0x79,0xE4,0xF5,0xC1,0x22,0x25,0x30,0xF5,0x30,0xE4,0x35,0x2F,0xF5 | ||
336 | ,0x2F,0x22,0x8F,0xFA,0xED,0xF5,0xFB,0xEC,0xF5,0xFC,0x75,0xF8,0x04,0x02,0x08,0x90 | ||
337 | ,0xFD,0xED,0xFF,0xEE,0xF0,0xA3,0xEF,0xF0,0x22,0xE4,0xF5,0x33,0xAF,0xC1,0xEF,0xAD | ||
338 | ,0xC2,0xF5,0x3A,0xED,0xF5,0x3B,0xD3,0x94,0x00,0xE5,0x3A,0x94,0x02,0x40,0x09,0x75 | ||
339 | ,0x33,0x01,0x75,0x3A,0x02,0x75,0x3B,0x00,0xC3,0xE5,0x3A,0x94,0x00,0x50,0x08,0x75 | ||
340 | ,0x33,0x01,0xE4,0xF5,0x3A,0xF5,0x3B,0xAF,0x3B,0xAE,0x3A,0x71,0x6A,0x8E,0x3C,0x8F | ||
341 | ,0x3D,0x53,0xCB,0xF7,0xE5,0x3C,0x54,0x1F,0xFC,0xAD,0x3D,0x7F,0x50,0x51,0x62,0x75 | ||
342 | ,0x34,0x00,0x75,0x35,0xFA,0x7F,0x05,0x7E,0x00,0x12,0x04,0x31,0x85,0x3C,0x36,0x85 | ||
343 | ,0x3D,0x37,0x71,0x3C,0xC3,0x33,0xCE,0x33,0xCE,0xD8,0xF9,0x78,0x03,0xCE,0xA2,0xE7 | ||
344 | ,0x13,0xCE,0x13,0xD8,0xF8,0xFF,0xC3,0xE5,0x37,0x9F,0xF5,0x37,0xE5,0x36,0x9E,0xF5 | ||
345 | ,0x36,0xC3,0x64,0x80,0x94,0x80,0x50,0x0F,0xAE,0x36,0xAF,0x37,0x7C,0xFF,0x7D,0xFF | ||
346 | ,0x12,0x00,0x5E,0x8E,0x36,0x8F,0x37,0xE5,0x35,0x15,0x35,0x70,0x02,0x15,0x34,0xC3 | ||
347 | ,0xE5,0x37,0x94,0x14,0xE5,0x36,0x64,0x80,0x94,0x80,0x40,0x06,0xE5,0x35,0x45,0x34 | ||
348 | ,0x70,0xA3,0xE5,0x35,0x45,0x34,0x70,0x03,0x75,0x33,0x04,0x71,0x55,0xE4,0xF5,0xC1 | ||
349 | ,0x85,0x33,0xC2,0xE5,0x34,0xF5,0xC3,0xE5,0x35,0xF5,0xC4,0x22,0x7F,0x5A,0x71,0x44 | ||
350 | ,0xEF,0x78,0x03,0x22,0x8F,0xFA,0x75,0xF8,0x06,0x12,0x08,0x90,0xAF,0xFC,0xEF,0xFE | ||
351 | ,0xAD,0xFB,0xED,0xFF,0x22,0xE5,0x3C,0x54,0x1F,0xFE,0xE4,0x25,0x3D,0xFD,0xEE,0x34 | ||
352 | ,0x20,0xFC,0x7F,0x50,0x51,0x62,0x43,0xCB,0x08,0x22,0x8E,0x38,0x8F,0x39,0x71,0xFB | ||
353 | ,0x78,0x02,0x71,0xF2,0x12,0x0F,0x35,0xAE,0x38,0xAF,0x39,0xE4,0xFC,0xFD,0x12,0x01 | ||
354 | ,0x74,0x78,0x09,0x12,0x02,0xA2,0xAD,0x07,0xAC,0x06,0xE5,0x41,0xAE,0x40,0x78,0x02 | ||
355 | ,0xC3,0x33,0xCE,0x33,0xCE,0xD8,0xF9,0xC3,0x9D,0xFF,0xEE,0x9C,0xFE,0xEF,0x78,0x02 | ||
356 | ,0xCE,0xA2,0xE7,0x13,0xCE,0x13,0xD8,0xF8,0xFF,0x22,0xD3,0xEF,0x95,0x41,0xE5,0x40 | ||
357 | ,0x64,0x80,0xF8,0xEE,0x64,0x80,0x98,0x40,0x04,0xE4,0xFE,0xFF,0x22,0xC3,0xEF,0x95 | ||
358 | ,0x3F,0xE5,0x3E,0x64,0x80,0xF8,0xEE,0x64,0x80,0x98,0x50,0x05,0x7E,0x02,0x7F,0x00 | ||
359 | ,0x22,0xC3,0xE5,0x41,0x9F,0xFF,0xE5,0x40,0x9E,0x78,0x09,0x71,0xF2,0xC0,0x06,0xC0 | ||
360 | ,0x07,0x71,0xFB,0xAB,0x07,0xFA,0x33,0x95,0xE0,0xF9,0xF8,0xD0,0x07,0xD0,0x06,0x02 | ||
361 | ,0x01,0xFF,0xFE,0x33,0x95,0xE0,0xFD,0xFC,0x02,0x02,0xB5,0xC3,0xE5,0x41,0x95,0x3F | ||
362 | ,0xFF,0xE5,0x40,0x95,0x3E,0x22,0x91,0x55,0x4E,0x70,0x05,0x75,0xC1,0x01,0x80,0x05 | ||
363 | ,0x90,0x1B,0x45,0x91,0x21,0x11,0x95,0xF5,0xC2,0xED,0xF5,0xC3,0xA3,0xE0,0xF5,0xC4 | ||
364 | ,0x22,0xEE,0xF0,0xA3,0xEF,0xF0,0x12,0x0E,0x2F,0xE4,0xF5,0xC1,0x22,0x91,0x55,0xD3 | ||
365 | ,0x94,0x80,0xEE,0x94,0x0C,0x50,0x09,0xC3,0xEF,0x94,0x32,0xEE,0x94,0x00,0x50,0x05 | ||
366 | ,0x75,0xC1,0x01,0x80,0x05,0x90,0x1B,0x47,0x91,0x21,0x90,0x1B,0x14,0x11,0x98,0xF5 | ||
367 | ,0xC2,0xED,0xF5,0xC3,0x22,0xAF,0xC1,0xEF,0xFF,0xAD,0xC2,0xED,0x90,0x1B,0x49,0xCF | ||
368 | ,0xF0,0xA3,0xEF,0xF0,0x90,0x1B,0x49,0xE0,0xFE,0xA3,0xE0,0xFF,0x22,0x91,0x55,0xC3 | ||
369 | ,0x94,0xFF,0xEE,0x94,0x5F,0x50,0x0A,0xAD,0xC4,0x12,0x09,0x43,0xE4,0xF5,0xC1,0x80 | ||
370 | ,0x03,0x75,0xC1,0x01,0xE4,0xF5,0xC2,0x22,0x90,0x1B,0x49,0xE5,0xC2,0xF0,0xE4,0xA3 | ||
371 | ,0xF0,0x90,0x1B,0x49,0xE0,0xFF,0xC3,0x94,0xFF,0x50,0x0C,0x31,0x2B,0x90,0x1B,0x4A | ||
372 | ,0xEF,0xF0,0xE4,0xF5,0xC1,0x80,0x03,0x75,0xC1,0x01,0xE4,0xF5,0xC2,0xF5,0xC3,0x90 | ||
373 | ,0x1B,0x4A,0xE0,0xF5,0xC4,0x22,0xAF,0xC1,0xEF,0xFF,0xAD,0xC2,0xED,0x90,0x1B,0x49 | ||
374 | ,0xCF,0xF0,0xA3,0xEF,0xF0,0xE4,0xA3,0x91,0x63,0xC3,0x94,0xFF,0xEE,0x94,0x5F,0x50 | ||
375 | ,0x0D,0x12,0x0D,0x3A,0x90,0x1B,0x4B,0xEF,0xF0,0xE4,0xF5,0xC1,0x80,0x03,0x75,0xC1 | ||
376 | ,0x01,0xE4,0xF5,0xC2,0xF5,0xC3,0x90,0x1B,0x4B,0xE0,0xF5,0xC4,0x22,0xE4,0xFF,0xE5 | ||
377 | ,0xC1,0xC3,0x94,0xC1,0x50,0x0A,0xAF,0xC1,0x12,0x0D,0x22,0xE4,0xF5,0xC1,0x80,0x03 | ||
378 | ,0x75,0xC1,0x01,0x8F,0xC4,0xE4,0xF5,0xC3,0xF5,0xC2,0x22,0x8F,0x2F,0xAB,0x2F,0xAD | ||
379 | ,0xC3,0xAF,0xC2,0xB1,0x27,0x8F,0xC3,0x05,0xC2,0xAB,0x2F,0xAD,0xC4,0xAF,0xC2,0xB1 | ||
380 | ,0x27,0x8F,0xC4,0xE4,0xF5,0xC1,0x22,0x8D,0x37,0xAE,0x03,0xEF,0x7C,0x00,0x7B,0x01 | ||
381 | ,0x24,0x23,0xF9,0xEC,0x34,0x1B,0xFA,0x90,0x1B,0x50,0x12,0x03,0x0C,0xEE,0x60,0x11 | ||
382 | ,0xEF,0xC3,0x94,0x26,0x50,0x0B,0x90,0x1B,0x50,0x12,0x02,0xEC,0xE5,0x37,0x12,0x00 | ||
383 | ,0x4C,0x90,0x1B,0x50,0x12,0x0F,0xE3,0xFF,0x22,0xE4,0xFF,0x80,0xAE,0x7F,0x01,0x80 | ||
384 | ,0xAA,0xAD,0x3B,0xAC,0x3A,0xE4,0xF5,0xC1,0x8C,0xC3,0xAF,0x05,0xEF,0xF5,0xC4,0x22 | ||
385 | ,0xAF,0xC2,0x7E,0x00,0xEF,0x78,0x10,0xC3,0x33,0xCE,0x33,0xCE,0xD8,0xF9,0xFD,0xAC | ||
386 | ,0x06,0xAF,0xC1,0x7E,0x00,0xEF,0x78,0x18,0xC3,0x33,0xCE,0x33,0xCE,0xD8,0xF9,0xB1 | ||
387 | ,0xAA,0xAF,0xC3,0xEF,0x4C,0xFE,0xED,0xFF,0xAD,0xC4,0xEF,0x4D,0xFF,0xE4,0xFC,0xFD | ||
388 | ,0x90,0x1B,0x60,0x12,0x02,0xE0,0xE4,0xF5,0xC1,0x22,0xFF,0xEE,0x4C,0xFC,0xEF,0x4D | ||
389 | ,0xFD,0x22,0x8E,0x2F,0x8F,0x30,0xC3,0xE5,0x30,0x94,0x64,0xE5,0x2F,0x94,0x00,0x50 | ||
390 | ,0x0C,0xF1,0x20,0xC2,0x44,0xF1,0x17,0x7E,0x40,0x7D,0x06,0x80,0x6D,0xC3,0xE5,0x30 | ||
391 | ,0x94,0xC8,0xE5,0x2F,0x94,0x00,0x50,0x0A,0xF1,0x20,0xF1,0x11,0x7E,0x80,0x7D,0x0C | ||
392 | ,0x80,0x58,0xC3,0xE5,0x30,0x94,0x90,0xE5,0x2F,0x94,0x01,0x50,0x0F,0xC2,0x40,0xD2 | ||
393 | ,0x41,0xC2,0x42,0xC2,0x43,0xF1,0x11,0xFE,0x7D,0x19,0x80,0x3E,0xC3,0xE5,0x30,0x94 | ||
394 | ,0x20,0xE5,0x2F,0x94,0x03,0x50,0x0D,0xD2,0x40,0xC2,0x41,0xC2,0x42,0xF1,0x0F,0xFE | ||
395 | ,0x7D,0x32,0x80,0x26,0xC3,0xE5,0x30,0x94,0x40,0xE5,0x2F,0x94,0x06,0x50,0x09,0xC2 | ||
396 | ,0x40,0xF1,0x0B,0xFE,0x7D,0x64,0x80,0x12,0xC3,0xE5,0x30,0x94,0x80,0xE5,0x2F,0x94 | ||
397 | ,0x0C,0x50,0x0F,0xD2,0x40,0xF1,0x0B,0xFE,0x7D,0xC8,0xFC,0x12,0x01,0xFF,0x8E,0x31 | ||
398 | ,0x8F,0x32,0xC3,0xE4,0x95,0x32,0xF5,0x34,0x74,0x20,0x95,0x31,0xF5,0x33,0xA2,0x41 | ||
399 | ,0xE4,0x33,0x24,0x01,0xFB,0xE4,0x33,0xFA,0xE4,0x33,0xF9,0xE4,0x33,0xF8,0xA2,0x40 | ||
400 | ,0x12,0x0F,0x24,0xA2,0x42,0x12,0x0F,0x24,0xA2,0x43,0x12,0x0F,0x24,0xA2,0x44,0xE4 | ||
401 | ,0x33,0x24,0x01,0xFF,0xE4,0x33,0xFE,0xE4,0x33,0xFD,0xE4,0x33,0xFC,0x12,0x01,0x74 | ||
402 | ,0xC0,0x04,0x12,0x0F,0x37,0xE5,0x33,0xFF,0xC3,0x74,0x20,0x9F,0xFD,0xE4,0x94,0x00 | ||
403 | ,0xFC,0x7E,0x06,0x7F,0x40,0x12,0x00,0x70,0xE4,0xFC,0xFD,0xD0,0x00,0x12,0x01,0x74 | ||
404 | ,0x90,0x1B,0x14,0xEE,0xF0,0xA3,0xEF,0xF0,0xA2,0x41,0xE4,0xFE,0x33,0x54,0x01,0x78 | ||
405 | ,0x07,0xC3,0x33,0xCE,0x33,0xCE,0xD8,0xF9,0xFD,0xAC,0x06,0xA2,0x40,0xE4,0x33,0x54 | ||
406 | ,0x01,0x4C,0xFC,0xA2,0x42,0xE4,0xFE,0x33,0x54,0x01,0x78,0x06,0xC3,0x33,0xCE,0x33 | ||
407 | ,0xCE,0xD8,0xF9,0xB1,0xAA,0xA2,0x43,0xE4,0xFE,0x33,0x54,0x01,0x78,0x05,0xC3,0x33 | ||
408 | ,0xCE,0x33,0xCE,0xD8,0xF9,0xB1,0xAA,0xA2,0x44,0xE4,0x33,0x54,0x01,0xC4,0xF8,0x54 | ||
409 | ,0x0F,0xC8,0x68,0xFF,0xE4,0xC4,0x54,0xF0,0x48,0x4C,0xFC,0xEF,0x4D,0xFD,0xE5,0x33 | ||
410 | ,0x54,0x0F,0xFF,0xEC,0xF5,0x48,0xEF,0x4D,0xF5,0x49,0x22,0xD2,0x41,0xD2,0x42,0xD2 | ||
411 | ,0x43,0xD2,0x44,0xAE,0x2F,0xAF,0x30,0xAB,0x07,0xAA,0x06,0xE4,0xF9,0xF8,0xFF,0x22 | ||
412 | ,0xC2,0x40,0xC2,0x41,0xC2,0x42,0xC2,0x43,0x22,0xE4,0xF5,0xC1,0xF1,0x31,0x8F,0xC2 | ||
413 | ,0x22,0x7F,0x20,0x12,0x0D,0x22,0xEF,0x54,0x03,0x64,0x03,0x7F,0x00,0x60,0x02,0x7F | ||
414 | ,0x01,0x22,0xAF,0xC1,0xEF,0x14,0x60,0x0C,0x04,0x70,0x16,0x74,0xFF,0x90,0x1B,0x3B | ||
415 | ,0xF0,0xA3,0x80,0x08,0x90,0x1B,0x3B,0xE4,0xF0,0xA3,0x74,0x0A,0xF0,0xE4,0xF5,0xC1 | ||
416 | ,0x22,0x75,0xC1,0x01,0x22,0xAF,0xC1,0xEF,0xFE,0xAD,0xC2,0xED,0xFF,0xE4,0xF5,0xC1 | ||
417 | ,0x8F,0x82,0x8E,0x83,0x93,0xFC,0x74,0x01,0x93,0xF5,0xC3,0xEC,0xF5,0xC4,0x22,0xE4 | ||
418 | ,0xF5,0x8F,0xF1,0x92,0x75,0xA8,0x81,0x53,0xC9,0xFE,0x75,0xC5,0xFF,0x12,0x04,0x6F | ||
419 | ,0x80,0xFB,0x75,0xC9,0xFF,0x75,0xCA,0xFF,0x75,0xC7,0xFF,0x75,0xC8,0xFF,0x22,0xE5 | ||
420 | ,0xC1,0xB4,0x80,0x05,0x12,0x09,0x2E,0x80,0x3E,0xE4,0xFD,0xAF,0xC1,0x12,0x18,0x35 | ||
421 | ,0x12,0x19,0xB7,0x70,0x06,0xE9,0xF4,0x70,0x02,0xEA,0xF4,0x70,0x04,0x75,0xC1,0x01 | ||
422 | ,0x22,0x90,0x1B,0x0F,0xE0,0x60,0x03,0x12,0x0F,0x87,0xE4,0x90,0x1B,0x0F,0xF0,0xA3 | ||
423 | ,0xE5,0xC1,0xF0,0xC2,0x48,0xC2,0x47,0x12,0x0A,0xDE,0x12,0x0F,0xAA,0x7F,0x03,0x7E | ||
424 | ,0x00,0x12,0x04,0x31,0x12,0x0E,0x2F,0xE4,0xF5,0xC1,0x22,0xAD,0xC2,0xAF,0xC3,0x12 | ||
425 | ,0x18,0x35,0x12,0x19,0xB7,0x70,0x06,0xE9,0xF4,0x70,0x02,0xEA,0xF4,0x70,0x08,0x75 | ||
426 | ,0xC1,0x01,0xF5,0xC2,0xF5,0xC3,0x22,0xE5,0xC1,0x90,0x1B,0x4A,0x70,0x05,0x75,0xF0 | ||
427 | ,0x9D,0x80,0x04,0xE4,0x75,0xF0,0x9F,0x12,0x00,0xFB,0x90,0x1B,0x49,0xE4,0x75,0xF0 | ||
428 | ,0x01,0x12,0x02,0xF5,0x12,0x00,0x06,0xF5,0xC2,0x90,0x1B,0x49,0x12,0x0F,0xE3,0xF5 | ||
429 | ,0xC3,0xE4,0xF5,0xC1,0x22,0xAC,0x07,0x90,0x1B,0x2D,0x11,0x70,0x12,0x01,0x11,0xFF | ||
430 | ,0xAE,0xF0,0xF4,0x70,0x02,0xEE,0xF4,0x60,0x1D,0xED,0x70,0x04,0xEF,0x6C,0x60,0x16 | ||
431 | ,0xBD,0x01,0x0A,0x12,0x01,0x11,0xE5,0xF0,0xB5,0x04,0x02,0x80,0x09,0x74,0x04,0x29 | ||
432 | ,0xF9,0xE4,0x3A,0xFA,0x80,0xD6,0x90,0x00,0x02,0x12,0x01,0x3C,0xFF,0xAE,0xF0,0x22 | ||
433 | ,0xE0,0xFE,0xA3,0xE0,0xAA,0x06,0xF9,0x7B,0xFF,0x22,0xEF,0x24,0x34,0x60,0x06,0x04 | ||
434 | ,0x70,0x05,0x8D,0xCB,0x22,0x8D,0xCC,0x22,0x8F,0xFA,0x8D,0xFB,0x75,0xF8,0x20,0x02 | ||
435 | ,0x08,0x90,0x90,0x1B,0x49,0xE5,0xC2,0xF0,0xE0,0xFF,0xC3,0x94,0xFF,0x50,0x09,0xAD | ||
436 | ,0xC4,0x11,0x88,0xE4,0xF5,0xC1,0x80,0x03,0x75,0xC1,0x01,0xE4,0xF5,0xC2,0x22,0xE4 | ||
437 | ,0xFD,0xAF,0xC1,0xEF,0x14,0x60,0x25,0x14,0x60,0x39,0x24,0xFC,0x60,0x51,0x14,0x60 | ||
438 | ,0x56,0x14,0x60,0x5B,0x24,0x08,0x70,0x63,0xE5,0xC2,0xD3,0x94,0x01,0x40,0x04,0x7D | ||
439 | ,0x01,0x80,0x5A,0xAF,0xC2,0x90,0x1B,0x00,0xEF,0xF0,0x80,0x51,0xAF,0xC3,0xAC,0xC2 | ||
440 | ,0xEC,0x2F,0xFF,0xE4,0x33,0x4F,0x70,0x04,0x7D,0x01,0x80,0x41,0x31,0x30,0x90,0x1B | ||
441 | ,0x01,0x80,0x29,0x31,0x30,0xD3,0x94,0x80,0xEE,0x94,0x0C,0x50,0x09,0xC3,0xEF,0x94 | ||
442 | ,0x32,0xEE,0x94,0x00,0x50,0x04,0x7D,0x01,0x80,0x23,0x90,0x1B,0x03,0x80,0x0D,0x90 | ||
443 | ,0x1B,0x09,0xE5,0xC2,0xF0,0x80,0x16,0x31,0x30,0x90,0x1B,0x37,0xEE,0x80,0x06,0xAF | ||
444 | ,0xC2,0x90,0x1B,0x39,0xE4,0xF0,0xA3,0xEF,0xF0,0x80,0x02,0x7D,0x01,0x8D,0xC1,0x22 | ||
445 | ,0xAF,0xC2,0xEF,0xFE,0xAC,0xC3,0xEC,0xFB,0xEB,0xFF,0x22,0x90,0x1B,0x00,0xE0,0x60 | ||
446 | ,0x04,0xD2,0x47,0x80,0x02,0xC2,0x47,0x30,0x47,0x15,0x12,0x0F,0xBB,0x90,0x1B,0x02 | ||
447 | ,0xE0,0x9F,0x90,0x1B,0x01,0xE0,0x9E,0x40,0x03,0xD2,0x48,0x22,0xC2,0x48,0x22,0x12 | ||
448 | ,0x0F,0xBB,0x90,0x1B,0x46,0xE0,0x9F,0x90,0x1B,0x45,0xE0,0x9E,0x40,0x03,0xD2,0x48 | ||
449 | ,0x22,0xC2,0x48,0x22,0xC0,0xE0,0xC0,0xF0,0xC0,0x83,0xC0,0x82,0xC0,0xD0,0x75,0xD0 | ||
450 | ,0x00,0xC0,0x00,0xC0,0x01,0xC0,0x02,0xC0,0x03,0xC0,0x04,0xC0,0x05,0xC0,0x06,0xC0 | ||
451 | ,0x07,0xE5,0xC7,0x30,0xE0,0x06,0x12,0x08,0x30,0x53,0xC7,0x01,0xD0,0x07,0xD0,0x06 | ||
452 | ,0xD0,0x05,0xD0,0x04,0xD0,0x03,0xD0,0x02,0xD0,0x01,0xD0,0x00,0xD0,0xD0,0xD0,0x82 | ||
453 | ,0xD0,0x83,0xD0,0xF0,0xD0,0xE0,0x32,0xAA,0x06,0xA9,0x07,0x7B,0xFF,0x90,0x1B,0x49 | ||
454 | ,0x12,0x03,0x0C,0x90,0x1B,0x49,0x12,0x02,0xEC,0x74,0xFF,0xF5,0x83,0xF5,0x82,0x6B | ||
455 | ,0x22,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
456 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
457 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
458 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
459 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
460 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
461 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
462 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
463 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
464 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
465 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
466 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
467 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
468 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
469 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
470 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
471 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
472 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
473 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
474 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
475 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
476 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
477 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
478 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
479 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
480 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
481 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
482 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
483 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
484 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
485 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
486 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
487 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
488 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
489 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
490 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
491 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
492 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
493 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
494 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
495 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
496 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
497 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
498 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
499 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
500 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
501 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
502 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
503 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
504 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
505 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
506 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
507 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
508 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
509 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
510 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
511 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
512 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
513 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
514 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
515 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
516 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
517 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
518 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
519 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
520 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
521 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
522 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
523 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
524 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
525 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
526 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
527 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
528 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
529 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
530 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
531 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
532 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
533 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
534 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
535 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
536 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
537 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
538 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
539 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
540 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
541 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
542 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
543 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF,0xFF | ||
544 | ,0x21,0x20,0x24,0x90,0x21,0xB2,0x21,0x95,0x21,0x94,0x60,0x00,0x64,0x21,0x95,0x26 | ||
545 | ,0x5E,0x25,0x66,0x2A,0x22,0x21,0x2E,0x26,0x30,0x22,0x38,0x24,0x40,0x23,0x9E,0x22 | ||
546 | ,0x9A,0x84,0x0E,0x84,0xEE,0x84,0xEE,0x22,0x52,0x21,0x20,0x21,0x58,0xFF,0xFF,0xFF | ||
547 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x40,0x03,0x00,0x01 | ||
548 | ,0x03,0x02,0x01,0x00,0xFF,0x60,0x03,0x18,0x00,0xE1,0x0F,0xF8,0xF4,0xF8,0x28,0x24 | ||
549 | ,0x0C,0x26,0x00,0x27,0x0F,0x00,0x0E,0x02,0x01,0xD0,0x07,0x64,0x00,0x94,0x11,0xE8 | ||
550 | ,0x03,0x64,0x00,0xF4,0x01,0x02,0x11,0x00,0xE8,0x03,0xFC,0x18,0x03,0xE9,0xFF,0xFF | ||
551 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x60,0x03,0x07,0x06,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
552 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
553 | ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
554 | }; | ||
555 | |||
556 | struct ov14810_reg { | ||
557 | u16 addr; | ||
558 | u16 val; | ||
559 | }; | ||
560 | |||
561 | struct ov14810_sensor { | ||
562 | struct i2c_client *i2c_client; | ||
563 | struct ov14810_platform_data *pdata; | ||
564 | }; | ||
565 | |||
566 | struct ov14810_info { | ||
567 | int mode; | ||
568 | int uC_programmed; | ||
569 | struct ov14810_sensor sensor; | ||
570 | struct ov14810_sensor uC; | ||
571 | struct ov14810_sensor slaveDev; | ||
572 | }; | ||
573 | |||
574 | static struct ov14810_info *info; | ||
575 | |||
576 | #define OV14810_TABLE_WAIT_MS 0 | ||
577 | #define OV14810_TABLE_END 1 | ||
578 | |||
579 | static struct ov14810_reg mode_4416x3312[] = { | ||
580 | {0x0103, 0x01}, | ||
581 | |||
582 | {0x3003, 0x09}, | ||
583 | {0x3004, 0x00}, | ||
584 | {0x3005, 0xa7}, | ||
585 | {0x3006, 0x80}, | ||
586 | {0x3007, 0x08}, | ||
587 | {0x3013, 0x1f}, | ||
588 | |||
589 | {0x3018, 0x04}, | ||
590 | {0x301b, 0xe0}, | ||
591 | {0x301c, 0xf8}, | ||
592 | {0x3020, 0x01}, | ||
593 | {0x3106, 0x05}, | ||
594 | {0x3600, 0x2d}, | ||
595 | {0x3601, 0x1f}, | ||
596 | {0x360a, 0x2e}, | ||
597 | {0x360f, 0x24}, | ||
598 | {0x3611, 0x6c}, | ||
599 | {0x3613, 0x84}, | ||
600 | {0x3705, 0xd1}, | ||
601 | {0x3707, 0x73}, | ||
602 | {0x3708, 0x01}, | ||
603 | {0x370e, 0x04}, | ||
604 | {0x3710, 0x40}, | ||
605 | {0x3711, 0x1c}, | ||
606 | {0x3717, 0x80}, | ||
607 | {0x3718, 0x11}, | ||
608 | {0x3719, 0x11}, | ||
609 | {0x371b, 0xa0}, | ||
610 | {0x371e, 0x2c}, | ||
611 | {0x3723, 0x30}, | ||
612 | {0x3726, 0x70}, | ||
613 | {0x3808, 0x00}, | ||
614 | {0x380a, 0x00}, | ||
615 | {0x3817, 0x24}, | ||
616 | {0x3819, 0x80}, | ||
617 | {0x3a00, 0x78}, | ||
618 | {0x3a13, 0x46}, | ||
619 | {0x3a18, 0x00}, | ||
620 | {0x3a19, 0x7f}, | ||
621 | {0x3a1a, 0x06}, | ||
622 | {0x3a25, 0x83}, | ||
623 | {0x3b09, 0x0a}, | ||
624 | {0x4002, 0xc5}, | ||
625 | {0x4004, 0x02}, | ||
626 | {0x4005, 0x10}, | ||
627 | {0x4009, 0x40}, | ||
628 | {0x404f, 0xff}, | ||
629 | {0x4709, 0x00}, | ||
630 | {0x4801, 0x0f}, | ||
631 | {0x4806, 0x80}, | ||
632 | {0x4842, 0x01}, | ||
633 | {0x5000, 0x00}, | ||
634 | {0x5001, 0x00}, | ||
635 | {0x5002, 0x00}, | ||
636 | {0x503b, 0x01}, | ||
637 | {0x503c, 0x10}, | ||
638 | {0x5041, 0x0e}, | ||
639 | {0x5780, 0xfc}, | ||
640 | {0x5b01, 0x03}, | ||
641 | {0x5b03, 0x00}, | ||
642 | |||
643 | {0x3003, 0x0a}, | ||
644 | {0x3005, 0xa7}, | ||
645 | |||
646 | {0x3006, 0x80}, | ||
647 | {0x3007, 0x08}, | ||
648 | {0x3013, 0x1f}, | ||
649 | |||
650 | {0x3602, 0x42}, | ||
651 | {0x3604, 0x80}, | ||
652 | {0x3605, 0x11}, | ||
653 | {0x360c, 0x42}, | ||
654 | {0x360d, 0x13}, | ||
655 | {0x3614, 0x05}, | ||
656 | |||
657 | {0x3702, 0x10}, | ||
658 | {0x3704, 0x14}, | ||
659 | {0x3707, 0x73}, | ||
660 | {0x370a, 0x80}, | ||
661 | {0x370b, 0x00}, | ||
662 | {0x370c, 0x04}, | ||
663 | {0x370d, 0x0d}, | ||
664 | {0x370f, 0x61}, | ||
665 | {0x3713, 0xfa}, | ||
666 | {0x3714, 0x2f}, | ||
667 | {0x3715, 0x2c}, | ||
668 | {0x3716, 0x0b}, | ||
669 | {0x371c, 0x28}, | ||
670 | {0x371d, 0x20}, | ||
671 | {0x3721, 0x08}, | ||
672 | {0x3724, 0x18}, | ||
673 | {0x3725, 0x17}, | ||
674 | {0x3727, 0x65}, | ||
675 | {0x3728, 0x0c}, | ||
676 | |||
677 | {0x3803, 0x0b}, | ||
678 | {0x3804, 0x11}, | ||
679 | {0x3805, 0x40}, | ||
680 | {0x3806, 0x0c}, | ||
681 | {0x3807, 0xf9}, | ||
682 | {0x380c, 0x09}, | ||
683 | {0x380d, 0x5c}, | ||
684 | {0x380e, 0x0d}, | ||
685 | {0x380f, 0x08}, | ||
686 | {0x3810, 0x44}, | ||
687 | {0x3811, 0x96}, | ||
688 | {0x3818, 0x40}, | ||
689 | {0x381c, 0x30}, | ||
690 | {0x381d, 0x10}, | ||
691 | {0x381e, 0x0c}, | ||
692 | {0x381f, 0xf8}, | ||
693 | {0x3820, 0x00}, | ||
694 | {0x3821, 0x0c}, | ||
695 | {0x3503, 0x13}, | ||
696 | |||
697 | {0x4050, 0xc0}, | ||
698 | {0x4051, 0x00}, | ||
699 | {0x4053, 0xa1}, | ||
700 | {0x4837, 0x1b}, | ||
701 | {0x503d, 0x00}, | ||
702 | {0x5042, 0x21}, | ||
703 | {0x5047, 0x00}, | ||
704 | |||
705 | {0x3a08, 0x1f}, | ||
706 | {0x3a09, 0x40}, | ||
707 | {0x3a0a, 0x1a}, | ||
708 | {0x3a0b, 0x00}, | ||
709 | {0x3a0d, 0x08}, | ||
710 | {0x3a0e, 0x06}, | ||
711 | |||
712 | {0x503d, 0x00}, | ||
713 | |||
714 | {0x0100, 0x01}, | ||
715 | {OV14810_TABLE_END, 0x0000} | ||
716 | }; | ||
717 | |||
718 | static struct ov14810_reg mode_1280x720[] = { | ||
719 | {0x0103, 0x01}, | ||
720 | {OV14810_TABLE_WAIT_MS, 20}, | ||
721 | |||
722 | {0x3003, 0x0a}, | ||
723 | {0x3004, 0x00}, | ||
724 | {0x3005, 0xa7}, | ||
725 | {0x3006, 0x80}, | ||
726 | {0x3007, 0x08}, | ||
727 | |||
728 | {0x3018, 0x04}, | ||
729 | {0x301b, 0xe0}, | ||
730 | {0x301c, 0xf8}, | ||
731 | {0x3020, 0x01}, | ||
732 | {0x3106, 0x05}, | ||
733 | {0x3600, 0x2d}, | ||
734 | {0x3601, 0x1f}, | ||
735 | {0x3609, 0x00}, | ||
736 | {0x360a, 0x2e}, | ||
737 | {0x360f, 0x24}, | ||
738 | {0x3611, 0x6c}, | ||
739 | {0x3613, 0x84}, | ||
740 | {0x3702, 0x20}, | ||
741 | {0x3704, 0x28}, | ||
742 | {0x3705, 0xd1}, | ||
743 | {0x3708, 0x01}, | ||
744 | {0x370e, 0x04}, | ||
745 | {0x3710, 0x40}, | ||
746 | {0x3711, 0x1c}, | ||
747 | {0x3714, 0x5f}, | ||
748 | {0x3715, 0x58}, | ||
749 | {0x3717, 0x80}, | ||
750 | {0x3718, 0x11}, | ||
751 | {0x3719, 0x11}, | ||
752 | {0x371b, 0xa0}, | ||
753 | {0x371c, 0x46}, | ||
754 | {0x371d, 0x40}, | ||
755 | {0x371e, 0x2c}, | ||
756 | {0x3723, 0x30}, | ||
757 | {0x3725, 0x2e}, | ||
758 | {0x3726, 0x70}, | ||
759 | {0x3808, 0x00}, | ||
760 | {0x380a, 0x00}, | ||
761 | {0x3817, 0x24}, | ||
762 | {0x3819, 0x80}, | ||
763 | {0x382c, 0x02}, | ||
764 | {0x382d, 0x01}, | ||
765 | {0x3a00, 0x78}, | ||
766 | {0x3a13, 0x46}, | ||
767 | {0x3a18, 0x00}, | ||
768 | {0x3a19, 0x7f}, | ||
769 | {0x3a1a, 0x06}, | ||
770 | {0x3a25, 0x83}, | ||
771 | {0x3b09, 0x0a}, | ||
772 | {0x4002, 0xc5}, | ||
773 | {0x4004, 0x02}, | ||
774 | {0x4005, 0x10}, | ||
775 | {0x4009, 0x40}, | ||
776 | {0x404f, 0xff}, | ||
777 | {0x4709, 0x00}, | ||
778 | {0x4801, 0x0f}, | ||
779 | {0x4806, 0x80}, | ||
780 | {0x4842, 0x01}, | ||
781 | {0x5000, 0x00}, | ||
782 | {0x5001, 0x00}, | ||
783 | {0x5002, 0x00}, | ||
784 | {0x503b, 0x01}, | ||
785 | {0x503c, 0x10}, | ||
786 | {0x5041, 0x0e}, | ||
787 | {0x5780, 0xfc}, | ||
788 | {0x5b00, 0x10}, | ||
789 | {0x5b01, 0x5b}, | ||
790 | {0x5b03, 0x00}, | ||
791 | |||
792 | |||
793 | {0x3005, 0xa7}, | ||
794 | {0x3006, 0x80}, | ||
795 | {0x3007, 0x08}, | ||
796 | {0x3013, 0x1f}, | ||
797 | |||
798 | {0x3602, 0x53}, | ||
799 | {0x3604, 0x80}, | ||
800 | {0x3605, 0x01}, | ||
801 | {0x360b, 0x0c}, | ||
802 | {0x360c, 0x45}, | ||
803 | {0x360d, 0x03}, | ||
804 | {0x3614, 0x05}, | ||
805 | |||
806 | {0x3707, 0x73}, | ||
807 | {0x370a, 0x81}, | ||
808 | {0x370b, 0x20}, | ||
809 | {0x370c, 0x04}, | ||
810 | {0x370d, 0x01}, | ||
811 | {0x370f, 0x00}, | ||
812 | {0x3713, 0xe6}, | ||
813 | {0x3716, 0xf0}, | ||
814 | {0x3721, 0x08}, | ||
815 | {0x3724, 0x2e}, | ||
816 | {0x3727, 0x60}, | ||
817 | {0x3728, 0x02}, | ||
818 | |||
819 | {0x3803, 0x07}, | ||
820 | {0x3804, 0x05}, /* width */ | ||
821 | {0x3805, 0x09}, | ||
822 | {0x3806, 0x02}, /* height */ | ||
823 | {0x3807, 0xd8}, | ||
824 | {0x380c, 0x05}, | ||
825 | {0x380d, 0x66}, | ||
826 | {0x380e, 0x02}, | ||
827 | {0x380f, 0xe4}, | ||
828 | {0x3810, 0x22}, | ||
829 | {0x3811, 0x02}, | ||
830 | {0x3818, 0x45}, | ||
831 | {0x381c, 0x13}, | ||
832 | {0x381d, 0xb8}, | ||
833 | {0x381e, 0x05}, /* height w/o skipping */ | ||
834 | {0x381f, 0xc0}, | ||
835 | {0x3820, 0x03}, | ||
836 | {0x3821, 0xa8}, | ||
837 | {0x3503, 0x13}, /* Manual exposure, gain control */ | ||
838 | |||
839 | {0x4050, 0xc0}, | ||
840 | {0x4051, 0x00}, | ||
841 | {0x4053, 0xa1}, | ||
842 | {0x4837, 0x1b}, | ||
843 | {0x503d, 0x00}, | ||
844 | {0x5042, 0x31}, | ||
845 | {0x5047, 0x00}, | ||
846 | |||
847 | {0x100, 0x01}, | ||
848 | {OV14810_TABLE_END, 0x0000} | ||
849 | }; | ||
850 | |||
851 | enum { | ||
852 | OV14810_MODE_4416x3312, | ||
853 | OV14810_MODE_1280x720 | ||
854 | }; | ||
855 | |||
856 | static struct ov14810_reg *mode_table[] = { | ||
857 | [OV14810_MODE_4416x3312] = mode_4416x3312, | ||
858 | [OV14810_MODE_1280x720] = mode_1280x720 | ||
859 | }; | ||
860 | |||
861 | static inline void ov14810_get_frame_length_regs(struct ov14810_reg *regs, | ||
862 | u32 frame_length) | ||
863 | { | ||
864 | regs->addr = OV14810_FRAME_LENGTH_REG_ADDR0; | ||
865 | regs->val = (frame_length >> 8) & 0xff; | ||
866 | (regs + 1)->addr = OV14810_FRAME_LENGTH_REG_ADDR1; | ||
867 | (regs + 1)->val = (frame_length) & 0xff; | ||
868 | } | ||
869 | |||
870 | static inline void ov14810_get_coarse_time_regs(struct ov14810_reg *regs, | ||
871 | u32 coarse_time) | ||
872 | { | ||
873 | regs->addr = OV14810_COARSE_TIME_REG_ADDR0; | ||
874 | regs->val = (coarse_time >> 12) & 0xff; | ||
875 | (regs + 1)->addr = OV14810_COARSE_TIME_REG_ADDR1; | ||
876 | (regs + 1)->val = (coarse_time >> 4) & 0xff; | ||
877 | (regs + 2)->addr = OV14810_COARSE_TIME_REG_ADDR2; | ||
878 | (regs + 2)->val = (coarse_time & 0xf) << 4; | ||
879 | } | ||
880 | |||
881 | static inline void ov14810_get_gain_reg(struct ov14810_reg *regs, u16 gain) | ||
882 | { | ||
883 | regs->addr = OV14810_GAIN_REG_ADDR0; | ||
884 | regs->val = gain; | ||
885 | } | ||
886 | |||
887 | static int ov14810_write16(struct i2c_client *client, u16 addr, u8 val) | ||
888 | { | ||
889 | int err; | ||
890 | struct i2c_msg msg; | ||
891 | unsigned char data[3]; | ||
892 | |||
893 | if (!client->adapter) | ||
894 | return -ENODEV; | ||
895 | |||
896 | data[0] = (u8) (addr >> 8); | ||
897 | data[1] = (u8) (addr & 0xff); | ||
898 | data[2] = (u8) (val & 0xff); | ||
899 | |||
900 | msg.addr = client->addr; | ||
901 | msg.flags = 0; | ||
902 | msg.len = 3; | ||
903 | msg.buf = data; | ||
904 | |||
905 | err = i2c_transfer(client->adapter, &msg, 1); | ||
906 | if (err != 1) { | ||
907 | pr_err("ov14810: i2c transfer failed %x %x\n", addr, val); | ||
908 | return -EIO; | ||
909 | } | ||
910 | |||
911 | return 0; | ||
912 | } | ||
913 | |||
914 | static int ov14810_write8(struct i2c_client *client, u8 addr, u8 val) | ||
915 | { | ||
916 | int err; | ||
917 | struct i2c_msg msg; | ||
918 | unsigned char data[2]; | ||
919 | |||
920 | if (!client->adapter) | ||
921 | return -ENODEV; | ||
922 | |||
923 | data[0] = (u8) (addr); | ||
924 | data[1] = (u8) (val & 0xff); | ||
925 | |||
926 | msg.addr = client->addr; | ||
927 | msg.flags = 0; | ||
928 | msg.len = 2; | ||
929 | msg.buf = data; | ||
930 | |||
931 | err = i2c_transfer(client->adapter, &msg, 1); | ||
932 | if (err != 1) { | ||
933 | pr_err("ov14810: i2c transfer failed %x %x\n",addr, val); | ||
934 | return -EIO; | ||
935 | } | ||
936 | |||
937 | return 0; | ||
938 | } | ||
939 | |||
940 | static int ov14810_write_reg_helper(struct ov14810_info *info, | ||
941 | u16 addr, u8 val) | ||
942 | { | ||
943 | return ov14810_write16(info->sensor.i2c_client, addr, val); | ||
944 | } | ||
945 | |||
946 | static int ov14810_write_table(struct ov14810_info *info, | ||
947 | const struct ov14810_reg table[], | ||
948 | const struct ov14810_reg override_list[], | ||
949 | int num_override_regs) | ||
950 | { | ||
951 | int err; | ||
952 | const struct ov14810_reg *next; | ||
953 | int i; | ||
954 | u16 val; | ||
955 | |||
956 | for (next = table; next->addr != OV14810_TABLE_END; next++) { | ||
957 | val = next->val; | ||
958 | |||
959 | if (next->addr == OV14810_TABLE_WAIT_MS) { | ||
960 | msleep(val); | ||
961 | continue; | ||
962 | } | ||
963 | |||
964 | /* When an override list is passed in, replace the reg */ | ||
965 | /* value to write if the reg is in the list */ | ||
966 | if (override_list) { | ||
967 | for (i = 0; i < num_override_regs; i++) { | ||
968 | if (next->addr == override_list[i].addr) { | ||
969 | val = override_list[i].val; | ||
970 | break; | ||
971 | } | ||
972 | } | ||
973 | } | ||
974 | err = ov14810_write_reg_helper(info, next->addr, val); | ||
975 | } | ||
976 | return err; | ||
977 | } | ||
978 | |||
979 | static int ov14810_set_mode(struct ov14810_info *info, struct ov14810_mode *mode) | ||
980 | { | ||
981 | int sensor_mode; | ||
982 | int err; | ||
983 | struct ov14810_reg reg_list[6]; | ||
984 | |||
985 | pr_info("%s: xres %u yres %u framelength %u coarsetime %u gain %u\n", | ||
986 | __func__, mode->xres, mode->yres, mode->frame_length, | ||
987 | mode->coarse_time, mode->gain); | ||
988 | if (mode->xres == 1280 && mode->yres == 720) | ||
989 | sensor_mode = OV14810_MODE_1280x720; | ||
990 | else if (mode->xres == 4416 && mode->yres == 3312) | ||
991 | sensor_mode = OV14810_MODE_4416x3312; | ||
992 | else { | ||
993 | pr_err("%s: invalid resolution supplied to set mode %d %d\n", | ||
994 | __func__, mode->xres, mode->yres); | ||
995 | return -EINVAL; | ||
996 | } | ||
997 | |||
998 | /* get a list of override regs for the asking frame length, */ | ||
999 | /* coarse integration time, and gain. */ | ||
1000 | ov14810_get_frame_length_regs(reg_list, mode->frame_length); | ||
1001 | ov14810_get_coarse_time_regs(reg_list + 2, mode->coarse_time); | ||
1002 | ov14810_get_gain_reg(reg_list + 5, mode->gain); | ||
1003 | |||
1004 | err = ov14810_write_table(info, mode_table[sensor_mode], | ||
1005 | reg_list, 6); | ||
1006 | |||
1007 | if (err) | ||
1008 | return err; | ||
1009 | |||
1010 | info->mode = sensor_mode; | ||
1011 | return 0; | ||
1012 | } | ||
1013 | |||
1014 | static int ov14810_set_frame_length(struct ov14810_info *info, u32 frame_length) | ||
1015 | { | ||
1016 | struct ov14810_reg reg_list[2]; | ||
1017 | int i; | ||
1018 | int ret; | ||
1019 | |||
1020 | ov14810_get_frame_length_regs(reg_list, frame_length); | ||
1021 | |||
1022 | for (i = 0; i < 2; i++) { | ||
1023 | ret = ov14810_write_reg_helper(info, reg_list[i].addr, | ||
1024 | reg_list[i].val); | ||
1025 | if (ret) | ||
1026 | return ret; | ||
1027 | } | ||
1028 | |||
1029 | return 0; | ||
1030 | } | ||
1031 | |||
1032 | static int ov14810_set_coarse_time(struct ov14810_info *info, u32 coarse_time) | ||
1033 | { | ||
1034 | int ret; | ||
1035 | |||
1036 | struct ov14810_reg reg_list[3]; | ||
1037 | int i; | ||
1038 | |||
1039 | ov14810_get_coarse_time_regs(reg_list, coarse_time); | ||
1040 | |||
1041 | ret = ov14810_write_reg_helper(info, OV14810_GROUP_ACCESS_REG_ADDR, 0x01); | ||
1042 | if (ret) | ||
1043 | return ret; | ||
1044 | |||
1045 | for (i = 0; i < 3; i++) { | ||
1046 | ret = ov14810_write_reg_helper(info, reg_list[i].addr, | ||
1047 | reg_list[i].val); | ||
1048 | if (ret) | ||
1049 | return ret; | ||
1050 | } | ||
1051 | |||
1052 | ret = ov14810_write_reg_helper(info, OV14810_GROUP_ACCESS_REG_ADDR, 0x11); | ||
1053 | if (ret) | ||
1054 | return ret; | ||
1055 | |||
1056 | ret = ov14810_write_reg_helper(info, OV14810_GROUP_ACCESS_REG_ADDR, 0xa1); | ||
1057 | if (ret) | ||
1058 | return ret; | ||
1059 | |||
1060 | return 0; | ||
1061 | } | ||
1062 | |||
1063 | static int ov14810_set_gain(struct ov14810_info *info, u16 gain) | ||
1064 | { | ||
1065 | int ret; | ||
1066 | struct ov14810_reg reg_list; | ||
1067 | |||
1068 | ov14810_get_gain_reg(®_list, gain); | ||
1069 | |||
1070 | ret = ov14810_write_reg_helper(info, reg_list.addr, reg_list.val); | ||
1071 | |||
1072 | return ret; | ||
1073 | } | ||
1074 | |||
1075 | static int ov14810_set_power(int powerLevel) | ||
1076 | { | ||
1077 | pr_info("%s: powerLevel=%d \n", __func__, powerLevel); | ||
1078 | |||
1079 | if (info->sensor.pdata) { | ||
1080 | if (powerLevel && info->sensor.pdata->power_on) { | ||
1081 | info->sensor.pdata->power_on(); | ||
1082 | msleep(1000); | ||
1083 | } | ||
1084 | else if (info->sensor.pdata->power_off) { | ||
1085 | info->sensor.pdata->power_off(); | ||
1086 | } | ||
1087 | } | ||
1088 | |||
1089 | return 0; | ||
1090 | } | ||
1091 | |||
1092 | static long ov14810_ioctl(struct file *file, | ||
1093 | unsigned int cmd, unsigned long arg) | ||
1094 | { | ||
1095 | struct ov14810_info *info = file->private_data; | ||
1096 | int err; | ||
1097 | |||
1098 | switch (cmd) { | ||
1099 | case OV14810_IOCTL_SET_MODE: | ||
1100 | { | ||
1101 | struct ov14810_mode mode; | ||
1102 | |||
1103 | err = copy_from_user(&mode,(const void __user *)arg, | ||
1104 | sizeof(struct ov14810_mode)); | ||
1105 | if (err) { | ||
1106 | pr_err("%s %d\n", __func__, __LINE__); | ||
1107 | return err; | ||
1108 | } | ||
1109 | |||
1110 | return ov14810_set_mode(info, &mode); | ||
1111 | } | ||
1112 | case OV14810_IOCTL_SET_FRAME_LENGTH: | ||
1113 | return ov14810_set_frame_length(info, (u32)arg); | ||
1114 | case OV14810_IOCTL_SET_COARSE_TIME: | ||
1115 | return ov14810_set_coarse_time(info, (u32)arg); | ||
1116 | case OV14810_IOCTL_SET_GAIN: | ||
1117 | return ov14810_set_gain(info, (u16)arg); | ||
1118 | case OV14810_IOCTL_GET_STATUS: | ||
1119 | { | ||
1120 | u16 status = 0; | ||
1121 | err = copy_to_user((void __user *)arg, &status,2); | ||
1122 | if (err) { | ||
1123 | pr_err("%s %d\n", __func__, __LINE__); | ||
1124 | return err; | ||
1125 | } | ||
1126 | return 0; | ||
1127 | } | ||
1128 | default: | ||
1129 | return -EINVAL; | ||
1130 | } | ||
1131 | return 0; | ||
1132 | } | ||
1133 | |||
1134 | static int ov14810_slavedev_open(void) | ||
1135 | { | ||
1136 | pr_info("%s\n", __func__); | ||
1137 | |||
1138 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0x19, 0x67); | ||
1139 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0x18, 0x02); | ||
1140 | |||
1141 | return 0; | ||
1142 | } | ||
1143 | |||
1144 | static int ov14810_slavedev_reset(void) | ||
1145 | { | ||
1146 | pr_info("%s\n", __func__); | ||
1147 | |||
1148 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0x18, 0x03); | ||
1149 | msleep(1000); | ||
1150 | |||
1151 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0xc1, 0x0); | ||
1152 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0xc2, 0x0); | ||
1153 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0xc3, 0x0); | ||
1154 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0xc4, 0x0); | ||
1155 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0xc5, 0x0); | ||
1156 | |||
1157 | msleep(1000); | ||
1158 | |||
1159 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0xc1, 0x0); | ||
1160 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0xc2, 0x0); | ||
1161 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0xc3, 0x0); | ||
1162 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0xc4, 0x0); | ||
1163 | OV14810_I2C_WRITE8(info->slaveDev.i2c_client, 0xc5, 0x17); | ||
1164 | |||
1165 | msleep(1000); | ||
1166 | |||
1167 | return 0; | ||
1168 | } | ||
1169 | |||
1170 | static int ov14810uC_open(void) | ||
1171 | { | ||
1172 | int i; | ||
1173 | int err; | ||
1174 | |||
1175 | pr_info("ov14810uC programmming started \n"); | ||
1176 | |||
1177 | for (i = 0; i < sizeof(uCProgram); i++) { | ||
1178 | ov14810_write16(info->uC.i2c_client, | ||
1179 | ( ( (i & 0xff) << 8) | ( (i & 0xff00) >> 8) ), uCProgram[i]); | ||
1180 | } | ||
1181 | pr_info("ov14810uC programmming finished \n"); | ||
1182 | |||
1183 | err = ov14810_slavedev_reset(); | ||
1184 | |||
1185 | return err; | ||
1186 | } | ||
1187 | |||
1188 | static int ov14810_open(struct inode *inode, struct file *file) | ||
1189 | { | ||
1190 | int err; | ||
1191 | pr_info("%s\n", __func__); | ||
1192 | file->private_data = info; | ||
1193 | |||
1194 | err = ov14810_set_power(1); | ||
1195 | |||
1196 | if (err) | ||
1197 | return err; | ||
1198 | |||
1199 | if (info->uC_programmed == 0) { | ||
1200 | err = ov14810_slavedev_open(); | ||
1201 | |||
1202 | if (err) | ||
1203 | return err; | ||
1204 | |||
1205 | err = ov14810uC_open(); | ||
1206 | if (!err) | ||
1207 | info->uC_programmed = 1; | ||
1208 | } | ||
1209 | |||
1210 | return err; | ||
1211 | } | ||
1212 | |||
1213 | int ov14810_release(struct inode *inode, struct file *file) | ||
1214 | { | ||
1215 | pr_info("%s\n", __func__); | ||
1216 | ov14810_set_power(0); | ||
1217 | file->private_data = NULL; | ||
1218 | return 0; | ||
1219 | } | ||
1220 | |||
1221 | static const struct file_operations ov14810_fileops = { | ||
1222 | .owner = THIS_MODULE, | ||
1223 | .open = ov14810_open, | ||
1224 | .unlocked_ioctl = ov14810_ioctl, | ||
1225 | .release = ov14810_release, | ||
1226 | }; | ||
1227 | |||
1228 | static struct miscdevice ov14810_device = { | ||
1229 | .minor = MISC_DYNAMIC_MINOR, | ||
1230 | .name = "ov14810", | ||
1231 | .fops = &ov14810_fileops, | ||
1232 | }; | ||
1233 | |||
1234 | static int ov14810_probe(struct i2c_client *client, | ||
1235 | const struct i2c_device_id *id) | ||
1236 | { | ||
1237 | int err; | ||
1238 | pr_info("%s: probing sensor.\n", __func__); | ||
1239 | |||
1240 | if (!info) { | ||
1241 | info = kzalloc(sizeof(struct ov14810_info), GFP_KERNEL); | ||
1242 | if (!info) { | ||
1243 | pr_err("ov14810: Unable to allocate memory!\n"); | ||
1244 | return -ENOMEM; | ||
1245 | } | ||
1246 | } | ||
1247 | |||
1248 | err = misc_register(&ov14810_device); | ||
1249 | if (err) { | ||
1250 | pr_err("ov14810: Unable to register misc device!\n"); | ||
1251 | kfree(info); | ||
1252 | return err; | ||
1253 | } | ||
1254 | |||
1255 | info->sensor.pdata = client->dev.platform_data; | ||
1256 | info->sensor.i2c_client = client; | ||
1257 | |||
1258 | return 0; | ||
1259 | } | ||
1260 | |||
1261 | static int ov14810_remove(struct i2c_client *client) | ||
1262 | { | ||
1263 | misc_deregister(&ov14810_device); | ||
1264 | kfree(info); | ||
1265 | return 0; | ||
1266 | } | ||
1267 | |||
1268 | static int ov14810_uC_probe(struct i2c_client *client, | ||
1269 | const struct i2c_device_id *id) | ||
1270 | { | ||
1271 | if (!info) { | ||
1272 | info = kzalloc(sizeof(struct ov14810_sensor), GFP_KERNEL); | ||
1273 | if (!info) { | ||
1274 | pr_err("ov14810uC: Unable to allocate memory!\n"); | ||
1275 | return -ENOMEM; | ||
1276 | } | ||
1277 | } | ||
1278 | info->uC.pdata = client->dev.platform_data; | ||
1279 | info->uC.i2c_client = client; | ||
1280 | |||
1281 | return 0; | ||
1282 | } | ||
1283 | |||
1284 | static int ov14810_uC_remove(struct i2c_client *client) | ||
1285 | { | ||
1286 | return 0; | ||
1287 | } | ||
1288 | |||
1289 | static int ov14810_slavedev_probe(struct i2c_client *client, | ||
1290 | const struct i2c_device_id *id) | ||
1291 | { | ||
1292 | pr_info("%s: probing slave Dev of sensor.\n", __func__); | ||
1293 | |||
1294 | if (!info) { | ||
1295 | info = kzalloc(sizeof(struct ov14810_sensor), GFP_KERNEL); | ||
1296 | if (!info) { | ||
1297 | pr_err("ov14810uC: Unable to allocate memory!\n"); | ||
1298 | return -ENOMEM; | ||
1299 | } | ||
1300 | } | ||
1301 | |||
1302 | info->slaveDev.pdata = client->dev.platform_data; | ||
1303 | info->slaveDev.i2c_client = client; | ||
1304 | info->uC_programmed = 0; | ||
1305 | |||
1306 | return 0; | ||
1307 | } | ||
1308 | |||
1309 | static int ov14810_slavedev_remove(struct i2c_client *client) | ||
1310 | { | ||
1311 | return 0; | ||
1312 | } | ||
1313 | |||
1314 | static const struct i2c_device_id ov14810_id[] = { | ||
1315 | { "ov14810", 0 }, | ||
1316 | { }, | ||
1317 | }; | ||
1318 | |||
1319 | MODULE_DEVICE_TABLE(i2c, ov14810_id); | ||
1320 | |||
1321 | static struct i2c_driver ov14810_i2c_driver = { | ||
1322 | .driver = { | ||
1323 | .name = "ov14810", | ||
1324 | .owner = THIS_MODULE, | ||
1325 | }, | ||
1326 | .probe = ov14810_probe, | ||
1327 | .remove = ov14810_remove, | ||
1328 | .id_table = ov14810_id, | ||
1329 | }; | ||
1330 | |||
1331 | |||
1332 | static const struct i2c_device_id ov14810_uC_id[] = { | ||
1333 | { "ov14810uC", 0 }, | ||
1334 | { }, | ||
1335 | }; | ||
1336 | |||
1337 | MODULE_DEVICE_TABLE(i2c, ov14810_uC_id); | ||
1338 | |||
1339 | static struct i2c_driver ov14810_uC_i2c_driver = { | ||
1340 | .driver = { | ||
1341 | .name = "ov14810uC", | ||
1342 | .owner = THIS_MODULE, | ||
1343 | }, | ||
1344 | .probe = ov14810_uC_probe, | ||
1345 | .remove = ov14810_uC_remove, | ||
1346 | .id_table = ov14810_uC_id, | ||
1347 | }; | ||
1348 | |||
1349 | static const struct i2c_device_id ov14810_slavedev_id[] = { | ||
1350 | { "ov14810SlaveDev", 0 }, | ||
1351 | { }, | ||
1352 | }; | ||
1353 | |||
1354 | MODULE_DEVICE_TABLE(i2c, ov14810_slavedev_id); | ||
1355 | |||
1356 | static struct i2c_driver ov14810_slavedev_i2c_driver = { | ||
1357 | .driver = { | ||
1358 | .name = "ov14810SlaveDev", | ||
1359 | .owner = THIS_MODULE, | ||
1360 | }, | ||
1361 | .probe = ov14810_slavedev_probe, | ||
1362 | .remove = ov14810_slavedev_remove, | ||
1363 | .id_table = ov14810_slavedev_id, | ||
1364 | }; | ||
1365 | |||
1366 | static int __init ov14810_init(void) | ||
1367 | { | ||
1368 | int ret; | ||
1369 | pr_info("ov14810 sensor driver loading\n"); | ||
1370 | ret = i2c_add_driver(&ov14810_i2c_driver); | ||
1371 | if (ret) | ||
1372 | return ret; | ||
1373 | |||
1374 | ret = i2c_add_driver(&ov14810_uC_i2c_driver); | ||
1375 | if (ret) | ||
1376 | return ret; | ||
1377 | |||
1378 | return i2c_add_driver(&ov14810_slavedev_i2c_driver); | ||
1379 | } | ||
1380 | |||
1381 | static void __exit ov14810_exit(void) | ||
1382 | { | ||
1383 | i2c_del_driver(&ov14810_slavedev_i2c_driver); | ||
1384 | i2c_del_driver(&ov14810_uC_i2c_driver); | ||
1385 | i2c_del_driver(&ov14810_i2c_driver); | ||
1386 | } | ||
1387 | |||
1388 | module_init(ov14810_init); | ||
1389 | module_exit(ov14810_exit); | ||
1390 | |||
diff --git a/drivers/media/video/tegra/ov2710.c b/drivers/media/video/tegra/ov2710.c new file mode 100644 index 00000000000..a2e02369d8e --- /dev/null +++ b/drivers/media/video/tegra/ov2710.c | |||
@@ -0,0 +1,690 @@ | |||
1 | /* | ||
2 | * ov2710.c - ov2710 sensor driver | ||
3 | * | ||
4 | * Copyright (c) 2011, NVIDIA, All Rights Reserved. | ||
5 | * | ||
6 | * Contributors: | ||
7 | * erik lilliebjerg <elilliebjerg@nvidia.com> | ||
8 | * | ||
9 | * Leverage OV5650.c | ||
10 | * | ||
11 | * This file is licensed under the terms of the GNU General Public License | ||
12 | * version 2. This program is licensed "as is" without any warranty of any | ||
13 | * kind, whether express or implied. | ||
14 | */ | ||
15 | |||
16 | #include <linux/delay.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/miscdevice.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/uaccess.h> | ||
22 | #include <media/ov2710.h> | ||
23 | |||
24 | struct ov2710_reg { | ||
25 | u16 addr; | ||
26 | u16 val; | ||
27 | }; | ||
28 | |||
29 | struct ov2710_info { | ||
30 | int mode; | ||
31 | struct i2c_client *i2c_client; | ||
32 | struct ov2710_platform_data *pdata; | ||
33 | }; | ||
34 | |||
35 | #define OV2710_TABLE_WAIT_MS 0 | ||
36 | #define OV2710_TABLE_END 1 | ||
37 | #define OV2710_MAX_RETRIES 3 | ||
38 | |||
39 | static struct ov2710_reg mode_1920x1080[] = { | ||
40 | {0x3103, 0x93}, | ||
41 | {0x3008, 0x82}, | ||
42 | {OV2710_TABLE_WAIT_MS, 5}, | ||
43 | {0x3008, 0x42}, | ||
44 | {OV2710_TABLE_WAIT_MS, 5}, | ||
45 | {0x3017, 0x7f}, | ||
46 | {0x3018, 0xfc}, | ||
47 | {0x3706, 0x61}, | ||
48 | {0x3712, 0x0c}, | ||
49 | {0x3630, 0x6d}, | ||
50 | {0x3801, 0xb4}, | ||
51 | |||
52 | {0x3621, 0x04}, | ||
53 | {0x3604, 0x60}, | ||
54 | {0x3603, 0xa7}, | ||
55 | {0x3631, 0x26}, | ||
56 | {0x3600, 0x04}, | ||
57 | {0x3620, 0x37}, | ||
58 | {0x3623, 0x00}, | ||
59 | {0x3702, 0x9e}, | ||
60 | {0x3703, 0x5c}, | ||
61 | {0x3704, 0x40}, | ||
62 | {0x370d, 0x0f}, | ||
63 | {0x3713, 0x9f}, | ||
64 | {0x3714, 0x4c}, | ||
65 | {0x3710, 0x9e}, | ||
66 | {0x3801, 0xc4}, | ||
67 | {0x3605, 0x05}, | ||
68 | {0x3606, 0x3f}, | ||
69 | {0x302d, 0x90}, | ||
70 | {0x370b, 0x40}, | ||
71 | {0x3716, 0x31}, | ||
72 | {0x3707, 0x52}, | ||
73 | {0x380d, 0x74}, | ||
74 | {0x5181, 0x20}, | ||
75 | {0x518f, 0x00}, | ||
76 | {0x4301, 0xff}, | ||
77 | {0x4303, 0x00}, | ||
78 | {0x3a00, 0x78}, | ||
79 | {0x300f, 0x88}, | ||
80 | {0x3011, 0x28}, | ||
81 | {0x3a1a, 0x06}, | ||
82 | {0x3a18, 0x00}, | ||
83 | {0x3a19, 0x7a}, | ||
84 | {0x3a13, 0x54}, | ||
85 | {0x382e, 0x0f}, | ||
86 | {0x381a, 0x1a}, | ||
87 | {0x401d, 0x02}, | ||
88 | |||
89 | {0x381c, 0x00}, | ||
90 | {0x381d, 0x02}, | ||
91 | {0x381e, 0x04}, | ||
92 | {0x381f, 0x38}, | ||
93 | {0x3820, 0x00}, | ||
94 | {0x3821, 0x98}, | ||
95 | {0x3800, 0x01}, | ||
96 | {0x3802, 0x00}, | ||
97 | {0x3803, 0x0a}, | ||
98 | {0x3804, 0x07}, | ||
99 | {0x3805, 0x90}, | ||
100 | {0x3806, 0x04}, | ||
101 | {0x3807, 0x40}, | ||
102 | {0x3808, 0x07}, | ||
103 | {0x3809, 0x90}, | ||
104 | {0x380a, 0x04}, | ||
105 | {0x380b, 0x40}, | ||
106 | {0x380e, 0x04}, | ||
107 | {0x380f, 0x50}, | ||
108 | {0x380c, 0x09}, | ||
109 | {0x380d, 0x74}, | ||
110 | {0x3810, 0x08}, | ||
111 | {0x3811, 0x02}, | ||
112 | |||
113 | {0x5688, 0x03}, | ||
114 | {0x5684, 0x07}, | ||
115 | {0x5685, 0xa0}, | ||
116 | {0x5686, 0x04}, | ||
117 | {0x5687, 0x43}, | ||
118 | {0x3011, 0x0a}, | ||
119 | {0x300f, 0x8a}, | ||
120 | {0x3017, 0x00}, | ||
121 | {0x3018, 0x00}, | ||
122 | {0x4800, 0x24}, | ||
123 | {0x300e, 0x04}, | ||
124 | {0x4801, 0x0f}, | ||
125 | |||
126 | {0x300f, 0xc3}, | ||
127 | {0x3010, 0x00}, | ||
128 | {0x3011, 0x0a}, | ||
129 | {0x3012, 0x01}, | ||
130 | |||
131 | {0x3a0f, 0x40}, | ||
132 | {0x3a10, 0x38}, | ||
133 | {0x3a1b, 0x48}, | ||
134 | {0x3a1e, 0x30}, | ||
135 | {0x3a11, 0x90}, | ||
136 | {0x3a1f, 0x10}, | ||
137 | |||
138 | {0x3a0e, 0x03}, | ||
139 | {0x3a0d, 0x04}, | ||
140 | {0x3a08, 0x14}, | ||
141 | {0x3a09, 0xc0}, | ||
142 | {0x3a0a, 0x11}, | ||
143 | {0x3a0b, 0x40}, | ||
144 | |||
145 | {0x300f, 0xc3}, | ||
146 | {0x3010, 0x00}, | ||
147 | {0x3011, 0x0e}, | ||
148 | {0x3012, 0x02}, | ||
149 | {0x380c, 0x09}, | ||
150 | {0x380d, 0xec}, | ||
151 | {0x3703, 0x61}, | ||
152 | {0x3704, 0x44}, | ||
153 | {0x3801, 0xd2}, | ||
154 | |||
155 | {0x3503, 0x17}, | ||
156 | {0x3500, 0x00}, | ||
157 | {0x3501, 0x00}, | ||
158 | {0x3502, 0x00}, | ||
159 | {0x350a, 0x00}, | ||
160 | {0x350b, 0x00}, | ||
161 | {0x5001, 0x4e}, | ||
162 | {0x5000, 0x5f}, | ||
163 | {0x3008, 0x02}, | ||
164 | |||
165 | {OV2710_TABLE_END, 0x0000} | ||
166 | }; | ||
167 | |||
168 | static struct ov2710_reg mode_1280x720[] = { | ||
169 | {0x3103, 0x93}, | ||
170 | {0x3008, 0x82}, | ||
171 | {OV2710_TABLE_WAIT_MS, 5}, | ||
172 | {0x3008, 0x42}, | ||
173 | {OV2710_TABLE_WAIT_MS, 5}, | ||
174 | {0x3017, 0x7f}, | ||
175 | {0x3018, 0xfc}, | ||
176 | |||
177 | {0x3706, 0x61}, | ||
178 | {0x3712, 0x0c}, | ||
179 | {0x3630, 0x6d}, | ||
180 | {0x3801, 0xb4}, | ||
181 | {0x3621, 0x04}, | ||
182 | {0x3604, 0x60}, | ||
183 | {0x3603, 0xa7}, | ||
184 | {0x3631, 0x26}, | ||
185 | {0x3600, 0x04}, | ||
186 | {0x3620, 0x37}, | ||
187 | {0x3623, 0x00}, | ||
188 | {0x3702, 0x9e}, | ||
189 | {0x3703, 0x5c}, | ||
190 | {0x3704, 0x40}, | ||
191 | {0x370d, 0x0f}, | ||
192 | {0x3713, 0x9f}, | ||
193 | {0x3714, 0x4c}, | ||
194 | {0x3710, 0x9e}, | ||
195 | {0x3801, 0xc4}, | ||
196 | {0x3605, 0x05}, | ||
197 | {0x3606, 0x3f}, | ||
198 | {0x302d, 0x90}, | ||
199 | {0x370b, 0x40}, | ||
200 | {0x3716, 0x31}, | ||
201 | {0x3707, 0x52}, | ||
202 | {0x380d, 0x74}, | ||
203 | {0x5181, 0x20}, | ||
204 | {0x518f, 0x00}, | ||
205 | {0x4301, 0xff}, | ||
206 | {0x4303, 0x00}, | ||
207 | {0x3a00, 0x78}, | ||
208 | {0x300f, 0x88}, | ||
209 | {0x3011, 0x28}, | ||
210 | {0x3a1a, 0x06}, | ||
211 | {0x3a18, 0x00}, | ||
212 | {0x3a19, 0x7a}, | ||
213 | {0x3a13, 0x54}, | ||
214 | {0x382e, 0x0f}, | ||
215 | {0x381a, 0x1a}, | ||
216 | {0x401d, 0x02}, | ||
217 | |||
218 | {0x381c, 0x10}, | ||
219 | {0x381d, 0xb0}, | ||
220 | {0x381e, 0x02}, | ||
221 | {0x381f, 0xec}, | ||
222 | {0x3800, 0x01}, | ||
223 | {0x3820, 0x0a}, | ||
224 | {0x3821, 0x2a}, | ||
225 | {0x3804, 0x05}, | ||
226 | {0x3805, 0x10}, | ||
227 | {0x3802, 0x00}, | ||
228 | {0x3803, 0x04}, | ||
229 | {0x3806, 0x02}, | ||
230 | {0x3807, 0xe0}, | ||
231 | {0x3808, 0x05}, | ||
232 | {0x3809, 0x10}, | ||
233 | {0x380a, 0x02}, | ||
234 | {0x380b, 0xe0}, | ||
235 | {0x380e, 0x02}, | ||
236 | {0x380f, 0xf0}, | ||
237 | {0x380c, 0x07}, | ||
238 | {0x380d, 0x00}, | ||
239 | {0x3810, 0x10}, | ||
240 | {0x3811, 0x06}, | ||
241 | |||
242 | {0x5688, 0x03}, | ||
243 | {0x5684, 0x05}, | ||
244 | {0x5685, 0x00}, | ||
245 | {0x5686, 0x02}, | ||
246 | {0x5687, 0xd0}, | ||
247 | |||
248 | {0x3a08, 0x1b}, | ||
249 | {0x3a09, 0xe6}, | ||
250 | {0x3a0a, 0x17}, | ||
251 | {0x3a0b, 0x40}, | ||
252 | {0x3a0e, 0x01}, | ||
253 | {0x3a0d, 0x02}, | ||
254 | {0x3011, 0x0a}, | ||
255 | {0x300f, 0x8a}, | ||
256 | {0x3017, 0x00}, | ||
257 | {0x3018, 0x00}, | ||
258 | {0x4800, 0x24}, | ||
259 | {0x300e, 0x04}, | ||
260 | {0x4801, 0x0f}, | ||
261 | {0x300f, 0xc3}, | ||
262 | {0x3a0f, 0x40}, | ||
263 | {0x3a10, 0x38}, | ||
264 | {0x3a1b, 0x48}, | ||
265 | {0x3a1e, 0x30}, | ||
266 | {0x3a11, 0x90}, | ||
267 | {0x3a1f, 0x10}, | ||
268 | |||
269 | {0x3010, 0x10}, | ||
270 | {0x3a0e, 0x02}, | ||
271 | {0x3a0d, 0x03}, | ||
272 | {0x3a08, 0x0d}, | ||
273 | {0x3a09, 0xf3}, | ||
274 | {0x3a0a, 0x0b}, | ||
275 | {0x3a0b, 0xa0}, | ||
276 | |||
277 | {0x300f, 0xc3}, | ||
278 | {0x3011, 0x0e}, | ||
279 | {0x3012, 0x02}, | ||
280 | {0x380c, 0x07}, | ||
281 | {0x380d, 0x6a}, | ||
282 | {0x3703, 0x5c}, | ||
283 | {0x3704, 0x40}, | ||
284 | {0x3801, 0xbc}, | ||
285 | |||
286 | {0x3503, 0x17}, | ||
287 | {0x3500, 0x00}, | ||
288 | {0x3501, 0x00}, | ||
289 | {0x3502, 0x00}, | ||
290 | {0x350a, 0x00}, | ||
291 | {0x350b, 0x00}, | ||
292 | {0x5001, 0x4e}, | ||
293 | {0x5000, 0x5f}, | ||
294 | {0x3008, 0x02}, | ||
295 | |||
296 | {OV2710_TABLE_END, 0x0000} | ||
297 | }; | ||
298 | |||
299 | enum { | ||
300 | OV2710_MODE_1920x1080, | ||
301 | OV2710_MODE_1280x720, | ||
302 | }; | ||
303 | |||
304 | |||
305 | static struct ov2710_reg *mode_table[] = { | ||
306 | [OV2710_MODE_1920x1080] = mode_1920x1080, | ||
307 | [OV2710_MODE_1280x720] = mode_1280x720, | ||
308 | }; | ||
309 | |||
310 | static inline void ov2710_get_frame_length_regs(struct ov2710_reg *regs, | ||
311 | u32 frame_length) | ||
312 | { | ||
313 | regs->addr = 0x380e; | ||
314 | regs->val = (frame_length >> 8) & 0xff; | ||
315 | (regs + 1)->addr = 0x380f; | ||
316 | (regs + 1)->val = (frame_length) & 0xff; | ||
317 | } | ||
318 | |||
319 | static inline void ov2710_get_coarse_time_regs(struct ov2710_reg *regs, | ||
320 | u32 coarse_time) | ||
321 | { | ||
322 | regs->addr = 0x3500; | ||
323 | regs->val = (coarse_time >> 12) & 0xff; | ||
324 | (regs + 1)->addr = 0x3501; | ||
325 | (regs + 1)->val = (coarse_time >> 4) & 0xff; | ||
326 | (regs + 2)->addr = 0x3502; | ||
327 | (regs + 2)->val = (coarse_time & 0xf) << 4; | ||
328 | } | ||
329 | |||
330 | static inline void ov2710_get_gain_reg(struct ov2710_reg *regs, u16 gain) | ||
331 | { | ||
332 | regs->addr = 0x350b; | ||
333 | regs->val = gain; | ||
334 | } | ||
335 | |||
336 | static int ov2710_read_reg(struct i2c_client *client, u16 addr, u8 *val) | ||
337 | { | ||
338 | int err; | ||
339 | struct i2c_msg msg[2]; | ||
340 | unsigned char data[3]; | ||
341 | |||
342 | if (!client->adapter) | ||
343 | return -ENODEV; | ||
344 | |||
345 | msg[0].addr = client->addr; | ||
346 | msg[0].flags = 0; | ||
347 | msg[0].len = 2; | ||
348 | msg[0].buf = data; | ||
349 | |||
350 | /* high byte goes out first */ | ||
351 | data[0] = (u8) (addr >> 8);; | ||
352 | data[1] = (u8) (addr & 0xff); | ||
353 | |||
354 | msg[1].addr = client->addr; | ||
355 | msg[1].flags = I2C_M_RD; | ||
356 | msg[1].len = 1; | ||
357 | msg[1].buf = data + 2; | ||
358 | |||
359 | err = i2c_transfer(client->adapter, msg, 2); | ||
360 | |||
361 | if (err != 2) | ||
362 | |||
363 | return -EINVAL; | ||
364 | |||
365 | *val = data[2]; | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | static int ov2710_write_reg(struct i2c_client *client, u16 addr, u8 val) | ||
371 | { | ||
372 | int err; | ||
373 | struct i2c_msg msg; | ||
374 | unsigned char data[3]; | ||
375 | int retry = 0; | ||
376 | |||
377 | if (!client->adapter) | ||
378 | return -ENODEV; | ||
379 | |||
380 | data[0] = (u8) (addr >> 8);; | ||
381 | data[1] = (u8) (addr & 0xff); | ||
382 | data[2] = (u8) (val & 0xff); | ||
383 | |||
384 | msg.addr = client->addr; | ||
385 | msg.flags = 0; | ||
386 | msg.len = 3; | ||
387 | msg.buf = data; | ||
388 | |||
389 | do { | ||
390 | err = i2c_transfer(client->adapter, &msg, 1); | ||
391 | if (err == 1) | ||
392 | return 0; | ||
393 | retry++; | ||
394 | pr_err("ov2710: i2c transfer failed, retrying %x %x\n", | ||
395 | addr, val); | ||
396 | |||
397 | msleep(3); | ||
398 | } while (retry <= OV2710_MAX_RETRIES); | ||
399 | |||
400 | return err; | ||
401 | } | ||
402 | |||
403 | static int ov2710_write_table(struct i2c_client *client, | ||
404 | const struct ov2710_reg table[], | ||
405 | const struct ov2710_reg override_list[], | ||
406 | int num_override_regs) | ||
407 | { | ||
408 | int err; | ||
409 | const struct ov2710_reg *next; | ||
410 | int i; | ||
411 | u16 val; | ||
412 | |||
413 | for (next = table; next->addr != OV2710_TABLE_END; next++) { | ||
414 | if (next->addr == OV2710_TABLE_WAIT_MS) { | ||
415 | msleep(next->val); | ||
416 | continue; | ||
417 | } | ||
418 | |||
419 | |||
420 | val = next->val; | ||
421 | |||
422 | /* When an override list is passed in, replace the reg */ | ||
423 | /* value to write if the reg is in the list */ | ||
424 | if (override_list) { | ||
425 | for (i = 0; i < num_override_regs; i++) { | ||
426 | if (next->addr == override_list[i].addr) { | ||
427 | val = override_list[i].val; | ||
428 | break; | ||
429 | } | ||
430 | } | ||
431 | } | ||
432 | |||
433 | err = ov2710_write_reg(client, next->addr, val); | ||
434 | if (err) | ||
435 | return err; | ||
436 | } | ||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | static int ov2710_set_mode(struct ov2710_info *info, struct ov2710_mode *mode) | ||
441 | { | ||
442 | int sensor_mode; | ||
443 | int err; | ||
444 | struct ov2710_reg reg_list[6]; | ||
445 | |||
446 | pr_info("%s: xres %u yres %u framelength %u coarsetime %u gain %u\n", | ||
447 | __func__, mode->xres, mode->yres, mode->frame_length, | ||
448 | mode->coarse_time, mode->gain); | ||
449 | |||
450 | if (mode->xres == 1920 && mode->yres == 1080) | ||
451 | sensor_mode = OV2710_MODE_1920x1080; | ||
452 | else if (mode->xres == 1280 && mode->yres == 720) | ||
453 | sensor_mode = OV2710_MODE_1280x720; | ||
454 | else { | ||
455 | pr_err("%s: invalid resolution supplied to set mode %d %d\n", | ||
456 | __func__, mode->xres, mode->yres); | ||
457 | return -EINVAL; | ||
458 | } | ||
459 | |||
460 | /* get a list of override regs for the asking frame length, */ | ||
461 | /* coarse integration time, and gain. */ | ||
462 | ov2710_get_frame_length_regs(reg_list, mode->frame_length); | ||
463 | ov2710_get_coarse_time_regs(reg_list + 2, mode->coarse_time); | ||
464 | ov2710_get_gain_reg(reg_list + 5, mode->gain); | ||
465 | |||
466 | err = ov2710_write_table(info->i2c_client, mode_table[sensor_mode], | ||
467 | reg_list, 6); | ||
468 | if (err) | ||
469 | return err; | ||
470 | |||
471 | info->mode = sensor_mode; | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | static int ov2710_set_frame_length(struct ov2710_info *info, u32 frame_length) | ||
476 | { | ||
477 | struct ov2710_reg reg_list[2]; | ||
478 | int i = 0; | ||
479 | int ret; | ||
480 | |||
481 | ov2710_get_frame_length_regs(reg_list, frame_length); | ||
482 | |||
483 | for (i = 0; i < 2; i++) { | ||
484 | ret = ov2710_write_reg(info->i2c_client, reg_list[i].addr, | ||
485 | reg_list[i].val); | ||
486 | if (ret) | ||
487 | return ret; | ||
488 | } | ||
489 | |||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | static int ov2710_set_coarse_time(struct ov2710_info *info, u32 coarse_time) | ||
494 | { | ||
495 | int ret; | ||
496 | |||
497 | struct ov2710_reg reg_list[3]; | ||
498 | int i = 0; | ||
499 | |||
500 | ov2710_get_coarse_time_regs(reg_list, coarse_time); | ||
501 | |||
502 | ret = ov2710_write_reg(info->i2c_client, 0x3212, 0x01); | ||
503 | if (ret) | ||
504 | return ret; | ||
505 | |||
506 | for (i = 0; i < 3; i++) { | ||
507 | ret = ov2710_write_reg(info->i2c_client, reg_list[i].addr, | ||
508 | reg_list[i].val); | ||
509 | if (ret) | ||
510 | return ret; | ||
511 | } | ||
512 | |||
513 | ret = ov2710_write_reg(info->i2c_client, 0x3212, 0x11); | ||
514 | if (ret) | ||
515 | return ret; | ||
516 | |||
517 | ret = ov2710_write_reg(info->i2c_client, 0x3212, 0xa1); | ||
518 | if (ret) | ||
519 | return ret; | ||
520 | |||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | static int ov2710_set_gain(struct ov2710_info *info, u16 gain) | ||
525 | { | ||
526 | int ret; | ||
527 | struct ov2710_reg reg_list; | ||
528 | |||
529 | ov2710_get_gain_reg(®_list, gain); | ||
530 | |||
531 | ret = ov2710_write_reg(info->i2c_client, reg_list.addr, reg_list.val); | ||
532 | |||
533 | return ret; | ||
534 | } | ||
535 | |||
536 | static int ov2710_get_status(struct ov2710_info *info, u8 *status) | ||
537 | { | ||
538 | int err; | ||
539 | |||
540 | *status = 0; | ||
541 | err = ov2710_read_reg(info->i2c_client, 0x002, status); | ||
542 | return err; | ||
543 | } | ||
544 | |||
545 | |||
546 | static long ov2710_ioctl(struct file *file, | ||
547 | unsigned int cmd, unsigned long arg) | ||
548 | { | ||
549 | int err; | ||
550 | struct ov2710_info *info = file->private_data; | ||
551 | |||
552 | switch (cmd) { | ||
553 | case OV2710_IOCTL_SET_MODE: | ||
554 | { | ||
555 | struct ov2710_mode mode; | ||
556 | if (copy_from_user(&mode, | ||
557 | (const void __user *)arg, | ||
558 | sizeof(struct ov2710_mode))) { | ||
559 | return -EFAULT; | ||
560 | } | ||
561 | |||
562 | return ov2710_set_mode(info, &mode); | ||
563 | } | ||
564 | case OV2710_IOCTL_SET_FRAME_LENGTH: | ||
565 | return ov2710_set_frame_length(info, (u32)arg); | ||
566 | case OV2710_IOCTL_SET_COARSE_TIME: | ||
567 | return ov2710_set_coarse_time(info, (u32)arg); | ||
568 | case OV2710_IOCTL_SET_GAIN: | ||
569 | return ov2710_set_gain(info, (u16)arg); | ||
570 | case OV2710_IOCTL_GET_STATUS: | ||
571 | { | ||
572 | u8 status; | ||
573 | |||
574 | err = ov2710_get_status(info, &status); | ||
575 | if (err) | ||
576 | return err; | ||
577 | if (copy_to_user((void __user *)arg, &status, | ||
578 | 2)) { | ||
579 | return -EFAULT; | ||
580 | } | ||
581 | return 0; | ||
582 | } | ||
583 | default: | ||
584 | return -EINVAL; | ||
585 | } | ||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | static struct ov2710_info *info; | ||
590 | |||
591 | static int ov2710_open(struct inode *inode, struct file *file) | ||
592 | { | ||
593 | u8 status; | ||
594 | |||
595 | file->private_data = info; | ||
596 | if (info->pdata && info->pdata->power_on) | ||
597 | info->pdata->power_on(); | ||
598 | ov2710_get_status(info, &status); | ||
599 | return 0; | ||
600 | } | ||
601 | |||
602 | int ov2710_release(struct inode *inode, struct file *file) | ||
603 | { | ||
604 | if (info->pdata && info->pdata->power_off) | ||
605 | info->pdata->power_off(); | ||
606 | file->private_data = NULL; | ||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | |||
611 | static const struct file_operations ov2710_fileops = { | ||
612 | .owner = THIS_MODULE, | ||
613 | .open = ov2710_open, | ||
614 | .unlocked_ioctl = ov2710_ioctl, | ||
615 | .release = ov2710_release, | ||
616 | }; | ||
617 | |||
618 | static struct miscdevice ov2710_device = { | ||
619 | .minor = MISC_DYNAMIC_MINOR, | ||
620 | .name = "ov2710", | ||
621 | .fops = &ov2710_fileops, | ||
622 | }; | ||
623 | |||
624 | static int ov2710_probe(struct i2c_client *client, | ||
625 | const struct i2c_device_id *id) | ||
626 | { | ||
627 | int err; | ||
628 | |||
629 | pr_info("ov2710: probing sensor.\n"); | ||
630 | |||
631 | info = kzalloc(sizeof(struct ov2710_info), GFP_KERNEL); | ||
632 | if (!info) { | ||
633 | pr_err("ov2710: Unable to allocate memory!\n"); | ||
634 | return -ENOMEM; | ||
635 | } | ||
636 | |||
637 | err = misc_register(&ov2710_device); | ||
638 | if (err) { | ||
639 | pr_err("ov2710: Unable to register misc device!\n"); | ||
640 | kfree(info); | ||
641 | return err; | ||
642 | } | ||
643 | |||
644 | info->pdata = client->dev.platform_data; | ||
645 | info->i2c_client = client; | ||
646 | |||
647 | i2c_set_clientdata(client, info); | ||
648 | return 0; | ||
649 | } | ||
650 | |||
651 | static int ov2710_remove(struct i2c_client *client) | ||
652 | { | ||
653 | struct ov2710_info *info; | ||
654 | info = i2c_get_clientdata(client); | ||
655 | misc_deregister(&ov2710_device); | ||
656 | kfree(info); | ||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | static const struct i2c_device_id ov2710_id[] = { | ||
661 | { "ov2710", 0 }, | ||
662 | { }, | ||
663 | }; | ||
664 | |||
665 | MODULE_DEVICE_TABLE(i2c, ov2710_id); | ||
666 | |||
667 | static struct i2c_driver ov2710_i2c_driver = { | ||
668 | .driver = { | ||
669 | .name = "ov2710", | ||
670 | .owner = THIS_MODULE, | ||
671 | }, | ||
672 | .probe = ov2710_probe, | ||
673 | .remove = ov2710_remove, | ||
674 | .id_table = ov2710_id, | ||
675 | }; | ||
676 | |||
677 | static int __init ov2710_init(void) | ||
678 | { | ||
679 | pr_info("ov2710 sensor driver loading\n"); | ||
680 | return i2c_add_driver(&ov2710_i2c_driver); | ||
681 | } | ||
682 | |||
683 | static void __exit ov2710_exit(void) | ||
684 | { | ||
685 | i2c_del_driver(&ov2710_i2c_driver); | ||
686 | } | ||
687 | |||
688 | module_init(ov2710_init); | ||
689 | module_exit(ov2710_exit); | ||
690 | |||
diff --git a/drivers/media/video/tegra/ov5650.c b/drivers/media/video/tegra/ov5650.c new file mode 100644 index 00000000000..3adb46a3bb8 --- /dev/null +++ b/drivers/media/video/tegra/ov5650.c | |||
@@ -0,0 +1,1520 @@ | |||
1 | /* | ||
2 | * ov5650.c - ov5650 sensor driver | ||
3 | * | ||
4 | * Copyright (C) 2011 Google Inc. | ||
5 | * | ||
6 | * Contributors: | ||
7 | * Rebecca Schultz Zavin <rebecca@android.com> | ||
8 | * | ||
9 | * Leverage OV9640.c | ||
10 | * | ||
11 | * This file is licensed under the terms of the GNU General Public License | ||
12 | * version 2. This program is licensed "as is" without any warranty of any | ||
13 | * kind, whether express or implied. | ||
14 | */ | ||
15 | |||
16 | #include <linux/delay.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/miscdevice.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/uaccess.h> | ||
22 | #include <media/ov5650.h> | ||
23 | #include <media/tegra_camera.h> | ||
24 | |||
25 | #define SIZEOF_I2C_TRANSBUF 32 | ||
26 | |||
27 | struct ov5650_reg { | ||
28 | u16 addr; | ||
29 | u16 val; | ||
30 | }; | ||
31 | |||
32 | struct ov5650_sensor { | ||
33 | struct i2c_client *i2c_client; | ||
34 | struct ov5650_platform_data *pdata; | ||
35 | }; | ||
36 | |||
37 | struct ov5650_info { | ||
38 | int mode; | ||
39 | enum StereoCameraMode camera_mode; | ||
40 | struct ov5650_sensor left; | ||
41 | struct ov5650_sensor right; | ||
42 | u8 i2c_trans_buf[SIZEOF_I2C_TRANSBUF]; | ||
43 | }; | ||
44 | |||
45 | static struct ov5650_info *stereo_ov5650_info; | ||
46 | |||
47 | #define OV5650_TABLE_WAIT_MS 0 | ||
48 | #define OV5650_TABLE_END 1 | ||
49 | #define OV5650_MAX_RETRIES 3 | ||
50 | |||
51 | static struct ov5650_reg tp_none_seq[] = { | ||
52 | {0x5046, 0x00}, | ||
53 | {OV5650_TABLE_END, 0x0000} | ||
54 | }; | ||
55 | |||
56 | static struct ov5650_reg tp_cbars_seq[] = { | ||
57 | {0x503D, 0xC0}, | ||
58 | {0x503E, 0x00}, | ||
59 | {0x5046, 0x01}, | ||
60 | {OV5650_TABLE_END, 0x0000} | ||
61 | }; | ||
62 | |||
63 | static struct ov5650_reg tp_checker_seq[] = { | ||
64 | {0x503D, 0xC0}, | ||
65 | {0x503E, 0x0A}, | ||
66 | {0x5046, 0x01}, | ||
67 | {OV5650_TABLE_END, 0x0000} | ||
68 | }; | ||
69 | |||
70 | static struct ov5650_reg *test_pattern_modes[] = { | ||
71 | tp_none_seq, | ||
72 | tp_cbars_seq, | ||
73 | tp_checker_seq, | ||
74 | }; | ||
75 | |||
76 | static struct ov5650_reg reset_seq[] = { | ||
77 | {0x3008, 0x82}, | ||
78 | {OV5650_TABLE_WAIT_MS, 5}, | ||
79 | {0x3008, 0x42}, | ||
80 | {OV5650_TABLE_WAIT_MS, 5}, | ||
81 | {OV5650_TABLE_END, 0x0000}, | ||
82 | }; | ||
83 | |||
84 | static struct ov5650_reg mode_start[] = { | ||
85 | {0x3103, 0x93}, | ||
86 | {0x3017, 0xff}, | ||
87 | {0x3018, 0xfc}, | ||
88 | |||
89 | {0x3600, 0x50}, | ||
90 | {0x3601, 0x0d}, | ||
91 | {0x3604, 0x50}, | ||
92 | {0x3605, 0x04}, | ||
93 | {0x3606, 0x3f}, | ||
94 | {0x3612, 0x1a}, | ||
95 | {0x3630, 0x22}, | ||
96 | {0x3631, 0x22}, | ||
97 | {0x3702, 0x3a}, | ||
98 | {0x3704, 0x18}, | ||
99 | {0x3705, 0xda}, | ||
100 | {0x3706, 0x41}, | ||
101 | {0x370a, 0x80}, | ||
102 | {0x370b, 0x40}, | ||
103 | {0x370e, 0x00}, | ||
104 | {0x3710, 0x28}, | ||
105 | {0x3712, 0x13}, | ||
106 | {0x3830, 0x50}, | ||
107 | {0x3a18, 0x00}, | ||
108 | {0x3a19, 0xf8}, | ||
109 | {0x3a00, 0x38}, | ||
110 | |||
111 | |||
112 | {0x3603, 0xa7}, | ||
113 | {0x3615, 0x50}, | ||
114 | {0x3620, 0x56}, | ||
115 | {0x3810, 0x00}, | ||
116 | {0x3836, 0x00}, | ||
117 | {0x3a1a, 0x06}, | ||
118 | {0x4000, 0x01}, | ||
119 | {0x401c, 0x48}, | ||
120 | {0x401d, 0x08}, | ||
121 | {0x5000, 0x00}, | ||
122 | {0x5001, 0x00}, | ||
123 | {0x5002, 0x00}, | ||
124 | {0x503d, 0x00}, | ||
125 | {0x5046, 0x00}, | ||
126 | |||
127 | {0x300f, 0x8f}, | ||
128 | |||
129 | {0x3010, 0x10}, | ||
130 | {0x3011, 0x14}, | ||
131 | {0x3012, 0x02}, | ||
132 | {0x3815, 0x82}, | ||
133 | {0x3503, 0x33}, | ||
134 | {0x3613, 0x44}, | ||
135 | {OV5650_TABLE_END, 0x0}, | ||
136 | }; | ||
137 | |||
138 | static struct ov5650_reg mode_2592x1944[] = { | ||
139 | {0x3621, 0x2f}, | ||
140 | |||
141 | {0x3632, 0x55}, | ||
142 | {0x3703, 0xe6}, | ||
143 | {0x370c, 0xa0}, | ||
144 | {0x370d, 0x04}, | ||
145 | {0x3713, 0x2f}, | ||
146 | {0x3800, 0x02}, | ||
147 | {0x3801, 0x58}, | ||
148 | {0x3802, 0x00}, | ||
149 | {0x3803, 0x0c}, | ||
150 | {0x3804, 0x0a}, | ||
151 | {0x3805, 0x20}, | ||
152 | {0x3806, 0x07}, | ||
153 | {0x3807, 0xa0}, | ||
154 | {0x3808, 0x0a}, | ||
155 | |||
156 | {0x3809, 0x20}, | ||
157 | |||
158 | {0x380a, 0x07}, | ||
159 | |||
160 | {0x380b, 0xa0}, | ||
161 | |||
162 | {0x380c, 0x0c}, | ||
163 | |||
164 | {0x380d, 0xb4}, | ||
165 | |||
166 | {0x380e, 0x07}, | ||
167 | |||
168 | {0x380f, 0xb0}, | ||
169 | |||
170 | {0x3818, 0xc0}, | ||
171 | {0x381a, 0x3c}, | ||
172 | {0x3a0d, 0x06}, | ||
173 | {0x3c01, 0x00}, | ||
174 | {0x3007, 0x3f}, | ||
175 | {0x5059, 0x80}, | ||
176 | {0x3003, 0x03}, | ||
177 | {0x3500, 0x00}, | ||
178 | {0x3501, 0x7a}, | ||
179 | |||
180 | {0x3502, 0xd0}, | ||
181 | |||
182 | {0x350a, 0x00}, | ||
183 | {0x350b, 0x00}, | ||
184 | {0x401d, 0x08}, | ||
185 | {0x4801, 0x0f}, | ||
186 | {0x300e, 0x0c}, | ||
187 | {0x4803, 0x50}, | ||
188 | {0x4800, 0x34}, | ||
189 | {OV5650_TABLE_END, 0x0000} | ||
190 | }; | ||
191 | |||
192 | static struct ov5650_reg mode_1296x972[] = { | ||
193 | {0x3621, 0xaf}, | ||
194 | |||
195 | {0x3632, 0x5a}, | ||
196 | {0x3703, 0xb0}, | ||
197 | {0x370c, 0xc5}, | ||
198 | {0x370d, 0x42}, | ||
199 | {0x3713, 0x2f}, | ||
200 | {0x3800, 0x03}, | ||
201 | {0x3801, 0x3c}, | ||
202 | {0x3802, 0x00}, | ||
203 | {0x3803, 0x06}, | ||
204 | {0x3804, 0x05}, | ||
205 | {0x3805, 0x10}, | ||
206 | {0x3806, 0x03}, | ||
207 | {0x3807, 0xd0}, | ||
208 | {0x3808, 0x05}, | ||
209 | |||
210 | {0x3809, 0x10}, | ||
211 | |||
212 | {0x380a, 0x03}, | ||
213 | |||
214 | {0x380b, 0xd0}, | ||
215 | |||
216 | {0x380c, 0x08}, | ||
217 | |||
218 | {0x380d, 0xa8}, | ||
219 | |||
220 | {0x380e, 0x05}, | ||
221 | |||
222 | {0x380f, 0xa4}, | ||
223 | |||
224 | {0x3818, 0xc1}, | ||
225 | {0x381a, 0x00}, | ||
226 | {0x3a0d, 0x08}, | ||
227 | {0x3c01, 0x00}, | ||
228 | {0x3007, 0x3b}, | ||
229 | {0x5059, 0x80}, | ||
230 | {0x3003, 0x03}, | ||
231 | {0x3500, 0x00}, | ||
232 | |||
233 | {0x3501, 0x5a}, | ||
234 | {0x3502, 0x10}, | ||
235 | {0x350a, 0x00}, | ||
236 | {0x350b, 0x10}, | ||
237 | {0x401d, 0x08}, | ||
238 | {0x4801, 0x0f}, | ||
239 | {0x300e, 0x0c}, | ||
240 | {0x4803, 0x50}, | ||
241 | {0x4800, 0x34}, | ||
242 | {OV5650_TABLE_END, 0x0000} | ||
243 | }; | ||
244 | |||
245 | static struct ov5650_reg mode_2080x1164[] = { | ||
246 | {0x3103, 0x93}, | ||
247 | {0x3007, 0x3b}, | ||
248 | {0x3017, 0xff}, | ||
249 | {0x3018, 0xfc}, | ||
250 | |||
251 | {0x3600, 0x54}, | ||
252 | {0x3601, 0x05}, | ||
253 | {0x3603, 0xa7}, | ||
254 | {0x3604, 0x40}, | ||
255 | {0x3605, 0x04}, | ||
256 | {0x3606, 0x3f}, | ||
257 | {0x3612, 0x1a}, | ||
258 | {0x3613, 0x44}, | ||
259 | {0x3615, 0x52}, | ||
260 | {0x3620, 0x56}, | ||
261 | {0x3623, 0x01}, | ||
262 | {0x3630, 0x22}, | ||
263 | {0x3631, 0x36}, | ||
264 | {0x3632, 0x5f}, | ||
265 | {0x3633, 0x24}, | ||
266 | |||
267 | {0x3702, 0x3a}, | ||
268 | {0x3704, 0x18}, | ||
269 | {0x3706, 0x41}, | ||
270 | {0x370b, 0x40}, | ||
271 | {0x370e, 0x00}, | ||
272 | {0x3710, 0x28}, | ||
273 | {0x3711, 0x24}, | ||
274 | {0x3712, 0x13}, | ||
275 | |||
276 | {0x3810, 0x00}, | ||
277 | {0x3815, 0x82}, | ||
278 | {0x3830, 0x50}, | ||
279 | {0x3836, 0x00}, | ||
280 | |||
281 | {0x3a1a, 0x06}, | ||
282 | {0x3a18, 0x00}, | ||
283 | {0x3a19, 0xf8}, | ||
284 | {0x3a00, 0x38}, | ||
285 | |||
286 | {0x3a0d, 0x06}, | ||
287 | {0x3c01, 0x34}, | ||
288 | |||
289 | {0x401f, 0x03}, | ||
290 | {0x4000, 0x05}, | ||
291 | {0x401d, 0x08}, | ||
292 | {0x4001, 0x02}, | ||
293 | |||
294 | {0x5000, 0x00}, | ||
295 | {0x5001, 0x00}, | ||
296 | {0x5002, 0x00}, | ||
297 | {0x503d, 0x00}, | ||
298 | {0x5046, 0x00}, | ||
299 | |||
300 | {0x300f, 0x8f}, | ||
301 | |||
302 | {0x3010, 0x10}, | ||
303 | {0x3011, 0x14}, | ||
304 | {0x3012, 0x02}, | ||
305 | {0x3503, 0x33}, | ||
306 | |||
307 | |||
308 | {0x3621, 0x2f}, | ||
309 | |||
310 | {0x3703, 0xe6}, | ||
311 | {0x370c, 0x00}, | ||
312 | {0x370d, 0x04}, | ||
313 | {0x3713, 0x22}, | ||
314 | {0x3714, 0x27}, | ||
315 | {0x3705, 0xda}, | ||
316 | {0x370a, 0x80}, | ||
317 | |||
318 | {0x3800, 0x02}, | ||
319 | {0x3801, 0x12}, | ||
320 | {0x3802, 0x00}, | ||
321 | {0x3803, 0x0a}, | ||
322 | {0x3804, 0x08}, | ||
323 | {0x3805, 0x20}, | ||
324 | {0x3806, 0x04}, | ||
325 | {0x3807, 0x92}, | ||
326 | {0x3808, 0x08}, | ||
327 | |||
328 | {0x3809, 0x20}, | ||
329 | |||
330 | {0x380a, 0x04}, | ||
331 | |||
332 | {0x380b, 0x92}, | ||
333 | |||
334 | {0x380c, 0x0a}, | ||
335 | |||
336 | {0x380d, 0x96}, | ||
337 | |||
338 | {0x380e, 0x04}, | ||
339 | |||
340 | {0x380f, 0x9e}, | ||
341 | |||
342 | {0x3818, 0xc0}, | ||
343 | {0x381a, 0x3c}, | ||
344 | {0x381c, 0x31}, | ||
345 | {0x381d, 0x8e}, | ||
346 | {0x381e, 0x04}, | ||
347 | {0x381f, 0x92}, | ||
348 | {0x3820, 0x04}, | ||
349 | {0x3821, 0x19}, | ||
350 | {0x3824, 0x01}, | ||
351 | {0x3827, 0x0a}, | ||
352 | {0x401c, 0x46}, | ||
353 | |||
354 | {0x3003, 0x03}, | ||
355 | {0x3500, 0x00}, | ||
356 | {0x3501, 0x49}, | ||
357 | {0x3502, 0xa0}, | ||
358 | {0x350a, 0x00}, | ||
359 | {0x350b, 0x00}, | ||
360 | {0x4801, 0x0f}, | ||
361 | {0x300e, 0x0c}, | ||
362 | {0x4803, 0x50}, | ||
363 | {0x4800, 0x34}, | ||
364 | |||
365 | {OV5650_TABLE_END, 0x0000} | ||
366 | }; | ||
367 | |||
368 | static struct ov5650_reg mode_1920x1080[] = { | ||
369 | {0x3103, 0x93}, | ||
370 | {0x3007, 0x3b}, | ||
371 | {0x3017, 0xff}, | ||
372 | {0x3018, 0xfc}, | ||
373 | |||
374 | {0x3600, 0x54}, | ||
375 | {0x3601, 0x05}, | ||
376 | {0x3603, 0xa7}, | ||
377 | {0x3604, 0x40}, | ||
378 | {0x3605, 0x04}, | ||
379 | {0x3606, 0x3f}, | ||
380 | {0x3612, 0x1a}, | ||
381 | {0x3613, 0x44}, | ||
382 | {0x3615, 0x52}, | ||
383 | {0x3620, 0x56}, | ||
384 | {0x3623, 0x01}, | ||
385 | {0x3630, 0x22}, | ||
386 | {0x3631, 0x36}, | ||
387 | {0x3632, 0x5f}, | ||
388 | {0x3633, 0x24}, | ||
389 | |||
390 | {0x3702, 0x3a}, | ||
391 | {0x3704, 0x18}, | ||
392 | {0x3706, 0x41}, | ||
393 | {0x370b, 0x40}, | ||
394 | {0x370e, 0x00}, | ||
395 | {0x3710, 0x28}, | ||
396 | {0x3711, 0x24}, | ||
397 | {0x3712, 0x13}, | ||
398 | |||
399 | {0x3810, 0x00}, | ||
400 | {0x3815, 0x82}, | ||
401 | |||
402 | {0x3830, 0x50}, | ||
403 | {0x3836, 0x00}, | ||
404 | |||
405 | {0x3a1a, 0x06}, | ||
406 | {0x3a18, 0x00}, | ||
407 | {0x3a19, 0xf8}, | ||
408 | {0x3a00, 0x38}, | ||
409 | {0x3a0d, 0x06}, | ||
410 | {0x3c01, 0x34}, | ||
411 | |||
412 | {0x401f, 0x03}, | ||
413 | {0x4000, 0x05}, | ||
414 | {0x401d, 0x08}, | ||
415 | {0x4001, 0x02}, | ||
416 | |||
417 | {0x5000, 0x00}, | ||
418 | {0x5001, 0x00}, | ||
419 | {0x5002, 0x00}, | ||
420 | {0x503d, 0x00}, | ||
421 | {0x5046, 0x00}, | ||
422 | |||
423 | {0x300f, 0x8f}, | ||
424 | {0x3010, 0x10}, | ||
425 | {0x3011, 0x14}, | ||
426 | {0x3012, 0x02}, | ||
427 | {0x3503, 0x33}, | ||
428 | |||
429 | {0x3621, 0x2f}, | ||
430 | {0x3703, 0xe6}, | ||
431 | {0x370c, 0x00}, | ||
432 | {0x370d, 0x04}, | ||
433 | {0x3713, 0x22}, | ||
434 | {0x3714, 0x27}, | ||
435 | {0x3705, 0xda}, | ||
436 | {0x370a, 0x80}, | ||
437 | |||
438 | {0x3800, 0x02}, | ||
439 | {0x3801, 0x94}, | ||
440 | {0x3802, 0x00}, | ||
441 | {0x3803, 0x0c}, | ||
442 | {0x3804, 0x07}, | ||
443 | {0x3805, 0x80}, | ||
444 | {0x3806, 0x04}, | ||
445 | {0x3807, 0x40}, | ||
446 | {0x3808, 0x07}, | ||
447 | {0x3809, 0x80}, | ||
448 | {0x380a, 0x04}, | ||
449 | {0x380b, 0x40}, | ||
450 | {0x380c, 0x0a}, | ||
451 | {0x380d, 0x84}, | ||
452 | {0x380e, 0x04}, | ||
453 | {0x380f, 0xa4}, | ||
454 | {0x3818, 0xc0}, | ||
455 | {0x381a, 0x3c}, | ||
456 | {0x381c, 0x31}, | ||
457 | {0x381d, 0xa4}, | ||
458 | {0x381e, 0x04}, | ||
459 | {0x381f, 0x60}, | ||
460 | {0x3820, 0x03}, | ||
461 | {0x3821, 0x1a}, | ||
462 | {0x3824, 0x01}, | ||
463 | {0x3827, 0x0a}, | ||
464 | {0x401c, 0x46}, | ||
465 | |||
466 | {0x3003, 0x03}, | ||
467 | {0x3500, 0x00}, | ||
468 | {0x3501, 0x49}, | ||
469 | {0x3502, 0xa0}, | ||
470 | {0x350a, 0x00}, | ||
471 | {0x350b, 0x00}, | ||
472 | {0x4801, 0x0f}, | ||
473 | {0x300e, 0x0c}, | ||
474 | {0x4803, 0x50}, | ||
475 | {0x4800, 0x34}, | ||
476 | |||
477 | {OV5650_TABLE_END, 0x0000} | ||
478 | }; | ||
479 | |||
480 | |||
481 | static struct ov5650_reg mode_1264x704[] = { | ||
482 | {0x3600, 0x54}, | ||
483 | {0x3601, 0x05}, | ||
484 | {0x3604, 0x40}, | ||
485 | {0x3705, 0xdb}, | ||
486 | {0x370a, 0x81}, | ||
487 | {0x3615, 0x52}, | ||
488 | {0x3810, 0x40}, | ||
489 | {0x3836, 0x41}, | ||
490 | {0x4000, 0x05}, | ||
491 | {0x401c, 0x42}, | ||
492 | {0x401d, 0x08}, | ||
493 | {0x5046, 0x09}, | ||
494 | {0x3010, 0x00}, | ||
495 | {0x3503, 0x00}, | ||
496 | {0x3613, 0xc4}, | ||
497 | |||
498 | {0x3621, 0xaf}, | ||
499 | |||
500 | {0x3632, 0x55}, | ||
501 | {0x3703, 0x9a}, | ||
502 | {0x370c, 0x00}, | ||
503 | {0x370d, 0x42}, | ||
504 | {0x3713, 0x22}, | ||
505 | {0x3800, 0x02}, | ||
506 | {0x3801, 0x54}, | ||
507 | {0x3802, 0x00}, | ||
508 | {0x3803, 0x0c}, | ||
509 | {0x3804, 0x05}, | ||
510 | {0x3805, 0x00}, | ||
511 | {0x3806, 0x02}, | ||
512 | {0x3807, 0xd0}, | ||
513 | {0x3808, 0x05}, | ||
514 | |||
515 | {0x3809, 0x00}, | ||
516 | |||
517 | {0x380a, 0x02}, | ||
518 | |||
519 | {0x380b, 0xd0}, | ||
520 | |||
521 | {0x380c, 0x08}, | ||
522 | |||
523 | {0x380d, 0x72}, | ||
524 | |||
525 | {0x380e, 0x02}, | ||
526 | |||
527 | {0x380f, 0xe4}, | ||
528 | |||
529 | {0x3818, 0xc1}, | ||
530 | {0x381a, 0x3c}, | ||
531 | {0x3a0d, 0x06}, | ||
532 | {0x3c01, 0x34}, | ||
533 | {0x3007, 0x3b}, | ||
534 | {0x5059, 0x80}, | ||
535 | {0x3003, 0x03}, | ||
536 | {0x3500, 0x04}, | ||
537 | {0x3501, 0xa5}, | ||
538 | |||
539 | {0x3502, 0x10}, | ||
540 | |||
541 | {0x350a, 0x00}, | ||
542 | {0x350b, 0x00}, | ||
543 | {0x4801, 0x0f}, | ||
544 | {0x300e, 0x0c}, | ||
545 | {0x4803, 0x50}, | ||
546 | {0x4800, 0x24}, | ||
547 | {0x300f, 0x8b}, | ||
548 | |||
549 | {0x3711, 0x24}, | ||
550 | {0x3713, 0x92}, | ||
551 | {0x3714, 0x17}, | ||
552 | {0x381c, 0x10}, | ||
553 | {0x381d, 0x82}, | ||
554 | {0x381e, 0x05}, | ||
555 | {0x381f, 0xc0}, | ||
556 | {0x3821, 0x20}, | ||
557 | {0x3824, 0x23}, | ||
558 | {0x3825, 0x2c}, | ||
559 | {0x3826, 0x00}, | ||
560 | {0x3827, 0x0c}, | ||
561 | {0x3623, 0x01}, | ||
562 | {0x3633, 0x24}, | ||
563 | {0x3632, 0x5f}, | ||
564 | {0x401f, 0x03}, | ||
565 | |||
566 | {OV5650_TABLE_END, 0x0000} | ||
567 | }; | ||
568 | |||
569 | static struct ov5650_reg mode_320x240[] = { | ||
570 | {0x3103, 0x93}, | ||
571 | {0x3b07, 0x0c}, | ||
572 | {0x3017, 0xff}, | ||
573 | {0x3018, 0xfc}, | ||
574 | {0x3706, 0x41}, | ||
575 | {0x3613, 0xc4}, | ||
576 | {0x370d, 0x42}, | ||
577 | {0x3703, 0x9a}, | ||
578 | {0x3630, 0x22}, | ||
579 | {0x3605, 0x04}, | ||
580 | {0x3606, 0x3f}, | ||
581 | {0x3712, 0x13}, | ||
582 | {0x370e, 0x00}, | ||
583 | {0x370b, 0x40}, | ||
584 | {0x3600, 0x54}, | ||
585 | {0x3601, 0x05}, | ||
586 | {0x3713, 0x22}, | ||
587 | {0x3714, 0x27}, | ||
588 | {0x3631, 0x22}, | ||
589 | {0x3612, 0x1a}, | ||
590 | {0x3604, 0x40}, | ||
591 | {0x3705, 0xdc}, | ||
592 | {0x370a, 0x83}, | ||
593 | {0x370c, 0xc8}, | ||
594 | {0x3710, 0x28}, | ||
595 | {0x3702, 0x3a}, | ||
596 | {0x3704, 0x18}, | ||
597 | {0x3a18, 0x00}, | ||
598 | {0x3a19, 0xf8}, | ||
599 | {0x3a00, 0x38}, | ||
600 | {0x3800, 0x02}, | ||
601 | {0x3801, 0x54}, | ||
602 | {0x3803, 0x0c}, | ||
603 | {0x380c, 0x0c}, | ||
604 | {0x380d, 0xb4}, | ||
605 | {0x380e, 0x07}, | ||
606 | {0x380f, 0xb0}, | ||
607 | {0x3830, 0x50}, | ||
608 | {0x3a08, 0x12}, | ||
609 | {0x3a09, 0x70}, | ||
610 | {0x3a0a, 0x0f}, | ||
611 | {0x3a0b, 0x60}, | ||
612 | {0x3a0d, 0x06}, | ||
613 | {0x3a0e, 0x06}, | ||
614 | {0x3a13, 0x54}, | ||
615 | {0x3815, 0x82}, | ||
616 | {0x5059, 0x80}, | ||
617 | {0x3615, 0x52}, | ||
618 | {0x505a, 0x0a}, | ||
619 | {0x505b, 0x2e}, | ||
620 | {0x3713, 0x92}, | ||
621 | {0x3714, 0x17}, | ||
622 | {0x3803, 0x0a}, | ||
623 | {0x3804, 0x05}, | ||
624 | {0x3805, 0x00}, | ||
625 | {0x3806, 0x01}, | ||
626 | {0x3807, 0x00}, | ||
627 | {0x3808, 0x01}, | ||
628 | {0x3809, 0x40}, | ||
629 | {0x380a, 0x01}, | ||
630 | {0x380b, 0x00}, | ||
631 | {0x380c, 0x0a}, | ||
632 | |||
633 | {0x380d, 0x04}, | ||
634 | |||
635 | {0x380e, 0x01}, | ||
636 | |||
637 | {0x380f, 0x38}, | ||
638 | |||
639 | {0x3815, 0x81}, | ||
640 | {0x3824, 0x23}, | ||
641 | {0x3825, 0x20}, | ||
642 | {0x3826, 0x00}, | ||
643 | {0x3827, 0x08}, | ||
644 | {0x370d, 0xc2}, | ||
645 | {0x3a08, 0x17}, | ||
646 | {0x3a09, 0x64}, | ||
647 | {0x3a0a, 0x13}, | ||
648 | {0x3a0b, 0x80}, | ||
649 | {0x3a00, 0x58}, | ||
650 | {0x3a1a, 0x06}, | ||
651 | {0x3503, 0x33}, | ||
652 | {0x3623, 0x01}, | ||
653 | {0x3633, 0x24}, | ||
654 | {0x3c01, 0x34}, | ||
655 | {0x3c04, 0x28}, | ||
656 | {0x3c05, 0x98}, | ||
657 | {0x3c07, 0x07}, | ||
658 | {0x3c09, 0xc2}, | ||
659 | {0x4000, 0x05}, | ||
660 | {0x401d, 0x08}, | ||
661 | {0x4001, 0x02}, | ||
662 | {0x401c, 0x42}, | ||
663 | {0x5046, 0x09}, | ||
664 | {0x3810, 0x40}, | ||
665 | {0x3836, 0x41}, | ||
666 | {0x505f, 0x04}, | ||
667 | {0x5000, 0x06}, | ||
668 | {0x5001, 0x00}, | ||
669 | {0x5002, 0x02}, | ||
670 | {0x503d, 0x00}, | ||
671 | {0x5901, 0x08}, | ||
672 | {0x585a, 0x01}, | ||
673 | {0x585b, 0x2c}, | ||
674 | {0x585c, 0x01}, | ||
675 | {0x585d, 0x93}, | ||
676 | {0x585e, 0x01}, | ||
677 | {0x585f, 0x90}, | ||
678 | {0x5860, 0x01}, | ||
679 | {0x5861, 0x0d}, | ||
680 | {0x5180, 0xc0}, | ||
681 | {0x5184, 0x00}, | ||
682 | {0x470a, 0x00}, | ||
683 | {0x470b, 0x00}, | ||
684 | {0x470c, 0x00}, | ||
685 | {0x300f, 0x8e}, | ||
686 | {0x3603, 0xa7}, | ||
687 | {0x3632, 0x55}, | ||
688 | {0x3620, 0x56}, | ||
689 | {0x3621, 0xaf}, | ||
690 | {0x3818, 0xc3}, | ||
691 | {0x3631, 0x36}, | ||
692 | {0x3632, 0x5f}, | ||
693 | {0x3711, 0x24}, | ||
694 | {0x401f, 0x03}, | ||
695 | |||
696 | {0x3011, 0x14}, | ||
697 | {0x3007, 0x3B}, | ||
698 | {0x300f, 0x8f}, | ||
699 | {0x4801, 0x0f}, | ||
700 | {0x3003, 0x03}, | ||
701 | {0x300e, 0x0c}, | ||
702 | {0x3010, 0x15}, | ||
703 | {0x4803, 0x50}, | ||
704 | {0x4800, 0x24}, | ||
705 | {0x4837, 0x40}, | ||
706 | {0x3815, 0x82}, | ||
707 | |||
708 | {OV5650_TABLE_END, 0x0000} | ||
709 | }; | ||
710 | |||
711 | static struct ov5650_reg mode_end[] = { | ||
712 | {0x3212, 0x00}, | ||
713 | {0x3003, 0x01}, | ||
714 | {0x3212, 0x10}, | ||
715 | {0x3212, 0xa0}, | ||
716 | {0x3008, 0x02}, | ||
717 | |||
718 | {OV5650_TABLE_END, 0x0000} | ||
719 | }; | ||
720 | |||
721 | enum { | ||
722 | OV5650_MODE_2592x1944, | ||
723 | OV5650_MODE_1296x972, | ||
724 | OV5650_MODE_2080x1164, | ||
725 | OV5650_MODE_1920x1080, | ||
726 | OV5650_MODE_1264x704, | ||
727 | OV5650_MODE_320x240, | ||
728 | OV5650_MODE_INVALID | ||
729 | }; | ||
730 | |||
731 | static struct ov5650_reg *mode_table[] = { | ||
732 | [OV5650_MODE_2592x1944] = mode_2592x1944, | ||
733 | [OV5650_MODE_1296x972] = mode_1296x972, | ||
734 | [OV5650_MODE_2080x1164] = mode_2080x1164, | ||
735 | [OV5650_MODE_1920x1080] = mode_1920x1080, | ||
736 | [OV5650_MODE_1264x704] = mode_1264x704, | ||
737 | [OV5650_MODE_320x240] = mode_320x240 | ||
738 | }; | ||
739 | |||
740 | static inline void ov5650_get_frame_length_regs(struct ov5650_reg *regs, | ||
741 | u32 frame_length) | ||
742 | { | ||
743 | regs->addr = 0x380e; | ||
744 | regs->val = (frame_length >> 8) & 0xff; | ||
745 | (regs + 1)->addr = 0x380f; | ||
746 | (regs + 1)->val = (frame_length) & 0xff; | ||
747 | } | ||
748 | |||
749 | static inline void ov5650_get_coarse_time_regs(struct ov5650_reg *regs, | ||
750 | u32 coarse_time) | ||
751 | { | ||
752 | regs->addr = 0x3500; | ||
753 | regs->val = (coarse_time >> 12) & 0xff; | ||
754 | (regs + 1)->addr = 0x3501; | ||
755 | (regs + 1)->val = (coarse_time >> 4) & 0xff; | ||
756 | (regs + 2)->addr = 0x3502; | ||
757 | (regs + 2)->val = (coarse_time & 0xf) << 4; | ||
758 | } | ||
759 | |||
760 | static inline void ov5650_get_gain_reg(struct ov5650_reg *regs, u16 gain) | ||
761 | { | ||
762 | regs->addr = 0x350b; | ||
763 | regs->val = gain; | ||
764 | } | ||
765 | |||
766 | static int ov5650_read_reg(struct i2c_client *client, u16 addr, u8 *val) | ||
767 | { | ||
768 | int err; | ||
769 | struct i2c_msg msg[2]; | ||
770 | unsigned char data[3]; | ||
771 | |||
772 | if (!client->adapter) | ||
773 | return -ENODEV; | ||
774 | |||
775 | msg[0].addr = client->addr; | ||
776 | msg[0].flags = 0; | ||
777 | msg[0].len = 2; | ||
778 | msg[0].buf = data; | ||
779 | |||
780 | /* high byte goes out first */ | ||
781 | data[0] = (u8) (addr >> 8); | ||
782 | data[1] = (u8) (addr & 0xff); | ||
783 | |||
784 | msg[1].addr = client->addr; | ||
785 | msg[1].flags = I2C_M_RD; | ||
786 | msg[1].len = 1; | ||
787 | msg[1].buf = data + 2; | ||
788 | |||
789 | err = i2c_transfer(client->adapter, msg, 2); | ||
790 | |||
791 | if (err != 1) | ||
792 | return -EINVAL; | ||
793 | |||
794 | *val = data[2]; | ||
795 | |||
796 | return 0; | ||
797 | } | ||
798 | |||
799 | static int ov5650_read_reg_helper(struct ov5650_info *info, | ||
800 | u16 addr, u8 *val) | ||
801 | { | ||
802 | int ret; | ||
803 | switch (info->camera_mode) { | ||
804 | case Main: | ||
805 | case StereoCameraMode_Left: | ||
806 | ret = ov5650_read_reg(info->left.i2c_client, addr, val); | ||
807 | break; | ||
808 | case StereoCameraMode_Stereo: | ||
809 | ret = ov5650_read_reg(info->left.i2c_client, addr, val); | ||
810 | if (ret) | ||
811 | break; | ||
812 | ret = ov5650_read_reg(info->right.i2c_client, addr, val); | ||
813 | break; | ||
814 | case StereoCameraMode_Right: | ||
815 | ret = ov5650_read_reg(info->right.i2c_client, addr, val); | ||
816 | break; | ||
817 | default: | ||
818 | return -1; | ||
819 | } | ||
820 | return ret; | ||
821 | } | ||
822 | |||
823 | static int ov5650_write_reg(struct i2c_client *client, u16 addr, u8 val) | ||
824 | { | ||
825 | int err; | ||
826 | struct i2c_msg msg; | ||
827 | unsigned char data[3]; | ||
828 | |||
829 | if (!client->adapter) | ||
830 | return -ENODEV; | ||
831 | |||
832 | data[0] = (u8) (addr >> 8); | ||
833 | data[1] = (u8) (addr & 0xff); | ||
834 | data[2] = (u8) (val & 0xff); | ||
835 | |||
836 | msg.addr = client->addr; | ||
837 | msg.flags = 0; | ||
838 | msg.len = 3; | ||
839 | msg.buf = data; | ||
840 | |||
841 | err = i2c_transfer(client->adapter, &msg, 1); | ||
842 | if (err == 1) | ||
843 | return 0; | ||
844 | |||
845 | pr_err("ov5650: i2c transfer failed, retrying %x %x\n", addr, val); | ||
846 | |||
847 | return err; | ||
848 | } | ||
849 | |||
850 | static int ov5650_write_reg_helper(struct ov5650_info *info, | ||
851 | u16 addr, u8 val) | ||
852 | { | ||
853 | int ret; | ||
854 | switch (info->camera_mode) { | ||
855 | case Main: | ||
856 | case StereoCameraMode_Left: | ||
857 | ret = ov5650_write_reg(info->left.i2c_client, addr, val); | ||
858 | break; | ||
859 | case StereoCameraMode_Stereo: | ||
860 | ret = ov5650_write_reg(info->left.i2c_client, addr, val); | ||
861 | if (ret) | ||
862 | break; | ||
863 | ret = ov5650_write_reg(info->right.i2c_client, addr, val); | ||
864 | break; | ||
865 | case StereoCameraMode_Right: | ||
866 | ret = ov5650_write_reg(info->right.i2c_client, addr, val); | ||
867 | break; | ||
868 | default: | ||
869 | return -1; | ||
870 | } | ||
871 | return ret; | ||
872 | } | ||
873 | |||
874 | static int ov5650_write_bulk_reg(struct i2c_client *client, u8 *data, int len) | ||
875 | { | ||
876 | int err; | ||
877 | struct i2c_msg msg; | ||
878 | |||
879 | if (!client->adapter) | ||
880 | return -ENODEV; | ||
881 | |||
882 | msg.addr = client->addr; | ||
883 | msg.flags = 0; | ||
884 | msg.len = len; | ||
885 | msg.buf = data; | ||
886 | |||
887 | err = i2c_transfer(client->adapter, &msg, 1); | ||
888 | if (err == 1) | ||
889 | return 0; | ||
890 | |||
891 | pr_err("ov5650: i2c bulk transfer failed at %x\n", | ||
892 | (int)data[0] << 8 | data[1]); | ||
893 | |||
894 | return err; | ||
895 | } | ||
896 | |||
897 | static int ov5650_write_bulk_reg_helper(struct ov5650_info *info, int len) | ||
898 | { | ||
899 | int ret; | ||
900 | switch (info->camera_mode) { | ||
901 | case Main: | ||
902 | case StereoCameraMode_Left: | ||
903 | ret = ov5650_write_bulk_reg(info->left.i2c_client, | ||
904 | info->i2c_trans_buf, len); | ||
905 | break; | ||
906 | case StereoCameraMode_Stereo: | ||
907 | ret = ov5650_write_bulk_reg(info->left.i2c_client, | ||
908 | info->i2c_trans_buf, len); | ||
909 | if (ret) | ||
910 | break; | ||
911 | ret = ov5650_write_bulk_reg(info->right.i2c_client, | ||
912 | info->i2c_trans_buf, len); | ||
913 | break; | ||
914 | case StereoCameraMode_Right: | ||
915 | ret = ov5650_write_bulk_reg(info->right.i2c_client, | ||
916 | info->i2c_trans_buf, len); | ||
917 | break; | ||
918 | default: | ||
919 | return -1; | ||
920 | } | ||
921 | return ret; | ||
922 | } | ||
923 | |||
924 | static int ov5650_write_table(struct ov5650_info *info, | ||
925 | const struct ov5650_reg table[], | ||
926 | const struct ov5650_reg override_list[], | ||
927 | int num_override_regs) | ||
928 | { | ||
929 | int err; | ||
930 | const struct ov5650_reg *next, *n_next; | ||
931 | u8 *b_ptr = info->i2c_trans_buf; | ||
932 | unsigned int buf_filled = 0; | ||
933 | unsigned int i; | ||
934 | u16 val; | ||
935 | |||
936 | for (next = table; next->addr != OV5650_TABLE_END; next++) { | ||
937 | if (next->addr == OV5650_TABLE_WAIT_MS) { | ||
938 | msleep(next->val); | ||
939 | continue; | ||
940 | } | ||
941 | |||
942 | val = next->val; | ||
943 | /* When an override list is passed in, replace the reg */ | ||
944 | /* value to write if the reg is in the list */ | ||
945 | if (override_list) { | ||
946 | for (i = 0; i < num_override_regs; i++) { | ||
947 | if (next->addr == override_list[i].addr) { | ||
948 | val = override_list[i].val; | ||
949 | break; | ||
950 | } | ||
951 | } | ||
952 | } | ||
953 | |||
954 | if (!buf_filled) { | ||
955 | b_ptr = info->i2c_trans_buf; | ||
956 | *b_ptr++ = next->addr >> 8; | ||
957 | *b_ptr++ = next->addr & 0xff; | ||
958 | buf_filled = 2; | ||
959 | } | ||
960 | *b_ptr++ = val; | ||
961 | buf_filled++; | ||
962 | |||
963 | n_next = next + 1; | ||
964 | if (n_next->addr != OV5650_TABLE_END && | ||
965 | n_next->addr != OV5650_TABLE_WAIT_MS && | ||
966 | buf_filled < SIZEOF_I2C_TRANSBUF && | ||
967 | n_next->addr == next->addr + 1) { | ||
968 | continue; | ||
969 | } | ||
970 | |||
971 | err = ov5650_write_bulk_reg_helper(info, buf_filled); | ||
972 | if (err) | ||
973 | return err; | ||
974 | |||
975 | buf_filled = 0; | ||
976 | } | ||
977 | return 0; | ||
978 | } | ||
979 | |||
980 | static int ov5650_set_mode(struct ov5650_info *info, struct ov5650_mode *mode) | ||
981 | { | ||
982 | int sensor_mode; | ||
983 | int err; | ||
984 | struct ov5650_reg reg_list[6]; | ||
985 | |||
986 | pr_info("%s: xres %u yres %u framelength %u coarsetime %u gain %u\n", | ||
987 | __func__, mode->xres, mode->yres, mode->frame_length, | ||
988 | mode->coarse_time, mode->gain); | ||
989 | if (mode->xres == 2592 && mode->yres == 1944) | ||
990 | sensor_mode = OV5650_MODE_2592x1944; | ||
991 | else if (mode->xres == 1296 && mode->yres == 972) | ||
992 | sensor_mode = OV5650_MODE_1296x972; | ||
993 | else if (mode->xres == 2080 && mode->yres == 1164) | ||
994 | sensor_mode = OV5650_MODE_2080x1164; | ||
995 | else if (mode->xres == 1920 && mode->yres == 1080) | ||
996 | sensor_mode = OV5650_MODE_1920x1080; | ||
997 | else if (mode->xres == 1264 && mode->yres == 704) | ||
998 | sensor_mode = OV5650_MODE_1264x704; | ||
999 | else if (mode->xres == 320 && mode->yres == 240) | ||
1000 | sensor_mode = OV5650_MODE_320x240; | ||
1001 | else { | ||
1002 | pr_err("%s: invalid resolution supplied to set mode %d %d\n", | ||
1003 | __func__, mode->xres, mode->yres); | ||
1004 | return -EINVAL; | ||
1005 | } | ||
1006 | |||
1007 | /* get a list of override regs for the asking frame length, */ | ||
1008 | /* coarse integration time, and gain. */ | ||
1009 | ov5650_get_frame_length_regs(reg_list, mode->frame_length); | ||
1010 | ov5650_get_coarse_time_regs(reg_list + 2, mode->coarse_time); | ||
1011 | ov5650_get_gain_reg(reg_list + 5, mode->gain); | ||
1012 | |||
1013 | err = ov5650_write_table(info, reset_seq, NULL, 0); | ||
1014 | if (err) | ||
1015 | return err; | ||
1016 | |||
1017 | err = ov5650_write_table(info, mode_start, NULL, 0); | ||
1018 | if (err) | ||
1019 | return err; | ||
1020 | |||
1021 | err = ov5650_write_table(info, mode_table[sensor_mode], | ||
1022 | reg_list, 6); | ||
1023 | if (err) | ||
1024 | return err; | ||
1025 | |||
1026 | err = ov5650_write_table(info, mode_end, NULL, 0); | ||
1027 | if (err) | ||
1028 | return err; | ||
1029 | |||
1030 | info->mode = sensor_mode; | ||
1031 | return 0; | ||
1032 | } | ||
1033 | |||
1034 | static int ov5650_set_frame_length(struct ov5650_info *info, u32 frame_length) | ||
1035 | { | ||
1036 | int ret; | ||
1037 | struct ov5650_reg reg_list[2]; | ||
1038 | u8 *b_ptr = info->i2c_trans_buf; | ||
1039 | |||
1040 | ov5650_get_frame_length_regs(reg_list, frame_length); | ||
1041 | |||
1042 | *b_ptr++ = reg_list[0].addr >> 8; | ||
1043 | *b_ptr++ = reg_list[0].addr & 0xff; | ||
1044 | *b_ptr++ = reg_list[0].val & 0xff; | ||
1045 | *b_ptr++ = reg_list[1].val & 0xff; | ||
1046 | ret = ov5650_write_bulk_reg_helper(info, 4); | ||
1047 | |||
1048 | return ret; | ||
1049 | } | ||
1050 | |||
1051 | static int ov5650_set_coarse_time(struct ov5650_info *info, u32 coarse_time) | ||
1052 | { | ||
1053 | int ret; | ||
1054 | struct ov5650_reg reg_list[3]; | ||
1055 | u8 *b_ptr = info->i2c_trans_buf; | ||
1056 | |||
1057 | ov5650_get_coarse_time_regs(reg_list, coarse_time); | ||
1058 | |||
1059 | *b_ptr++ = reg_list[0].addr >> 8; | ||
1060 | *b_ptr++ = reg_list[0].addr & 0xff; | ||
1061 | *b_ptr++ = reg_list[0].val & 0xff; | ||
1062 | *b_ptr++ = reg_list[1].val & 0xff; | ||
1063 | *b_ptr++ = reg_list[2].val & 0xff; | ||
1064 | ret = ov5650_write_bulk_reg_helper(info, 5); | ||
1065 | |||
1066 | return ret; | ||
1067 | } | ||
1068 | |||
1069 | static int ov5650_set_gain(struct ov5650_info *info, u16 gain) | ||
1070 | { | ||
1071 | int ret; | ||
1072 | struct ov5650_reg reg_list; | ||
1073 | |||
1074 | ov5650_get_gain_reg(®_list, gain); | ||
1075 | ret = ov5650_write_reg_helper(info, reg_list.addr, reg_list.val); | ||
1076 | |||
1077 | return ret; | ||
1078 | } | ||
1079 | |||
1080 | static int ov5650_set_group_hold(struct ov5650_info *info, struct ov5650_ae *ae) | ||
1081 | { | ||
1082 | int ret; | ||
1083 | int count = 0; | ||
1084 | bool groupHoldEnabled = false; | ||
1085 | |||
1086 | if (ae->gain_enable) | ||
1087 | count++; | ||
1088 | if (ae->coarse_time_enable) | ||
1089 | count++; | ||
1090 | if (ae->frame_length_enable) | ||
1091 | count++; | ||
1092 | if (count >= 2) | ||
1093 | groupHoldEnabled = true; | ||
1094 | |||
1095 | if (groupHoldEnabled) { | ||
1096 | ret = ov5650_write_reg_helper(info, 0x3212, 0x01); | ||
1097 | if (ret) | ||
1098 | return ret; | ||
1099 | } | ||
1100 | |||
1101 | if (ae->gain_enable) | ||
1102 | ov5650_set_gain(info, ae->gain); | ||
1103 | if (ae->coarse_time_enable) | ||
1104 | ov5650_set_coarse_time(info, ae->coarse_time); | ||
1105 | if (ae->frame_length_enable) | ||
1106 | ov5650_set_frame_length(info, ae->frame_length); | ||
1107 | |||
1108 | if (groupHoldEnabled) { | ||
1109 | ret = ov5650_write_reg_helper(info, 0x3212, 0x11); | ||
1110 | if (ret) | ||
1111 | return ret; | ||
1112 | |||
1113 | ret = ov5650_write_reg_helper(info, 0x3212, 0xa1); | ||
1114 | if (ret) | ||
1115 | return ret; | ||
1116 | } | ||
1117 | |||
1118 | return 0; | ||
1119 | } | ||
1120 | |||
1121 | |||
1122 | static int ov5650_set_binning(struct ov5650_info *info, u8 enable) | ||
1123 | { | ||
1124 | s32 ret; | ||
1125 | u8 array_ctrl_reg, analog_ctrl_reg, timing_reg; | ||
1126 | u32 val; | ||
1127 | |||
1128 | if (info->mode == OV5650_MODE_2592x1944 | ||
1129 | || info->mode == OV5650_MODE_2080x1164 | ||
1130 | || info->mode >= OV5650_MODE_INVALID) { | ||
1131 | return -EINVAL; | ||
1132 | } | ||
1133 | |||
1134 | ov5650_read_reg_helper(info, OV5650_ARRAY_CONTROL_01, &array_ctrl_reg); | ||
1135 | ov5650_read_reg_helper(info, OV5650_ANALOG_CONTROL_D, &analog_ctrl_reg); | ||
1136 | ov5650_read_reg_helper(info, OV5650_TIMING_TC_REG_18, &timing_reg); | ||
1137 | |||
1138 | ret = ov5650_write_reg_helper(info, | ||
1139 | OV5650_SRM_GRUP_ACCESS, | ||
1140 | OV5650_GROUP_ID(3)); | ||
1141 | if (ret < 0) | ||
1142 | return -EIO; | ||
1143 | |||
1144 | if (!enable) { | ||
1145 | ret = ov5650_write_reg_helper(info, | ||
1146 | OV5650_ARRAY_CONTROL_01, | ||
1147 | array_ctrl_reg | | ||
1148 | (OV5650_H_BINNING_BIT | OV5650_H_SUBSAMPLING_BIT)); | ||
1149 | |||
1150 | if (ret < 0) | ||
1151 | goto exit; | ||
1152 | |||
1153 | ret = ov5650_write_reg_helper(info, | ||
1154 | OV5650_ANALOG_CONTROL_D, | ||
1155 | analog_ctrl_reg & ~OV5650_V_BINNING_BIT); | ||
1156 | |||
1157 | if (ret < 0) | ||
1158 | goto exit; | ||
1159 | |||
1160 | ret = ov5650_write_reg_helper(info, | ||
1161 | OV5650_TIMING_TC_REG_18, | ||
1162 | timing_reg | OV5650_V_SUBSAMPLING_BIT); | ||
1163 | |||
1164 | if (ret < 0) | ||
1165 | goto exit; | ||
1166 | |||
1167 | if (info->mode == OV5650_MODE_1296x972) | ||
1168 | val = 0x1A2; | ||
1169 | else | ||
1170 | /* FIXME: this value is not verified yet. */ | ||
1171 | val = 0x1A8; | ||
1172 | |||
1173 | ret = ov5650_write_reg_helper(info, | ||
1174 | OV5650_TIMING_CONTROL_HS_HIGH, | ||
1175 | (val >> 8)); | ||
1176 | |||
1177 | if (ret < 0) | ||
1178 | goto exit; | ||
1179 | |||
1180 | ret = ov5650_write_reg_helper(info, | ||
1181 | OV5650_TIMING_CONTROL_HS_LOW, | ||
1182 | (val & 0xFF)); | ||
1183 | } else { | ||
1184 | ret = ov5650_write_reg_helper(info, | ||
1185 | OV5650_ARRAY_CONTROL_01, | ||
1186 | (array_ctrl_reg | OV5650_H_BINNING_BIT) | ||
1187 | & ~OV5650_H_SUBSAMPLING_BIT); | ||
1188 | |||
1189 | if (ret < 0) | ||
1190 | goto exit; | ||
1191 | |||
1192 | ret = ov5650_write_reg_helper(info, | ||
1193 | OV5650_ANALOG_CONTROL_D, | ||
1194 | analog_ctrl_reg | OV5650_V_BINNING_BIT); | ||
1195 | |||
1196 | if (ret < 0) | ||
1197 | goto exit; | ||
1198 | |||
1199 | ret = ov5650_write_reg_helper(info, | ||
1200 | OV5650_TIMING_TC_REG_18, | ||
1201 | timing_reg | OV5650_V_SUBSAMPLING_BIT); | ||
1202 | |||
1203 | if (ret < 0) | ||
1204 | goto exit; | ||
1205 | |||
1206 | if (info->mode == OV5650_MODE_1296x972) | ||
1207 | val = 0x33C; | ||
1208 | else | ||
1209 | val = 0x254; | ||
1210 | |||
1211 | ret = ov5650_write_reg_helper(info, | ||
1212 | OV5650_TIMING_CONTROL_HS_HIGH, | ||
1213 | (val >> 8)); | ||
1214 | |||
1215 | if (ret < 0) | ||
1216 | goto exit; | ||
1217 | |||
1218 | ret = ov5650_write_reg_helper(info, | ||
1219 | OV5650_TIMING_CONTROL_HS_LOW, | ||
1220 | (val & 0xFF)); | ||
1221 | } | ||
1222 | |||
1223 | exit: | ||
1224 | ret = ov5650_write_reg_helper(info, | ||
1225 | OV5650_SRM_GRUP_ACCESS, | ||
1226 | (OV5650_GROUP_HOLD_END_BIT | OV5650_GROUP_ID(3))); | ||
1227 | |||
1228 | ret |= ov5650_write_reg_helper(info, | ||
1229 | OV5650_SRM_GRUP_ACCESS, | ||
1230 | (OV5650_GROUP_HOLD_BIT | OV5650_GROUP_LAUNCH_BIT | | ||
1231 | OV5650_GROUP_ID(3))); | ||
1232 | |||
1233 | return ret; | ||
1234 | } | ||
1235 | |||
1236 | static int ov5650_test_pattern(struct ov5650_info *info, | ||
1237 | enum ov5650_test_pattern pattern) | ||
1238 | { | ||
1239 | if (pattern >= ARRAY_SIZE(test_pattern_modes)) | ||
1240 | return -EINVAL; | ||
1241 | |||
1242 | return ov5650_write_table(info, | ||
1243 | test_pattern_modes[pattern], | ||
1244 | NULL, 0); | ||
1245 | } | ||
1246 | |||
1247 | static int set_power_helper(struct ov5650_platform_data *pdata, | ||
1248 | int powerLevel) | ||
1249 | { | ||
1250 | if (pdata) { | ||
1251 | if (powerLevel && pdata->power_on) | ||
1252 | pdata->power_on(); | ||
1253 | else if (pdata->power_off) | ||
1254 | pdata->power_off(); | ||
1255 | } | ||
1256 | return 0; | ||
1257 | } | ||
1258 | |||
1259 | static int ov5650_set_power(int powerLevel) | ||
1260 | { | ||
1261 | pr_info("%s: powerLevel=%d camera mode=%d\n", __func__, powerLevel, | ||
1262 | stereo_ov5650_info->camera_mode); | ||
1263 | |||
1264 | if (StereoCameraMode_Left & stereo_ov5650_info->camera_mode) | ||
1265 | set_power_helper(stereo_ov5650_info->left.pdata, powerLevel); | ||
1266 | |||
1267 | if (StereoCameraMode_Right & stereo_ov5650_info->camera_mode) | ||
1268 | set_power_helper(stereo_ov5650_info->right.pdata, powerLevel); | ||
1269 | |||
1270 | return 0; | ||
1271 | } | ||
1272 | |||
1273 | static long ov5650_ioctl(struct file *file, | ||
1274 | unsigned int cmd, unsigned long arg) | ||
1275 | { | ||
1276 | int err; | ||
1277 | struct ov5650_info *info = file->private_data; | ||
1278 | |||
1279 | switch (cmd) { | ||
1280 | case OV5650_IOCTL_SET_CAMERA_MODE: | ||
1281 | { | ||
1282 | if (info->camera_mode != arg) { | ||
1283 | err = ov5650_set_power(0); | ||
1284 | if (err) { | ||
1285 | pr_info("%s %d\n", __func__, __LINE__); | ||
1286 | return err; | ||
1287 | } | ||
1288 | info->camera_mode = arg; | ||
1289 | err = ov5650_set_power(1); | ||
1290 | if (err) | ||
1291 | return err; | ||
1292 | } | ||
1293 | return 0; | ||
1294 | } | ||
1295 | case OV5650_IOCTL_SYNC_SENSORS: | ||
1296 | if (info->right.pdata->synchronize_sensors) | ||
1297 | info->right.pdata->synchronize_sensors(); | ||
1298 | return 0; | ||
1299 | case OV5650_IOCTL_SET_MODE: | ||
1300 | { | ||
1301 | struct ov5650_mode mode; | ||
1302 | if (copy_from_user(&mode, | ||
1303 | (const void __user *)arg, | ||
1304 | sizeof(struct ov5650_mode))) { | ||
1305 | pr_info("%s %d\n", __func__, __LINE__); | ||
1306 | return -EFAULT; | ||
1307 | } | ||
1308 | |||
1309 | return ov5650_set_mode(info, &mode); | ||
1310 | } | ||
1311 | case OV5650_IOCTL_SET_FRAME_LENGTH: | ||
1312 | return ov5650_set_frame_length(info, (u32)arg); | ||
1313 | case OV5650_IOCTL_SET_COARSE_TIME: | ||
1314 | return ov5650_set_coarse_time(info, (u32)arg); | ||
1315 | case OV5650_IOCTL_SET_GAIN: | ||
1316 | return ov5650_set_gain(info, (u16)arg); | ||
1317 | case OV5650_IOCTL_SET_BINNING: | ||
1318 | return ov5650_set_binning(info, (u8)arg); | ||
1319 | case OV5650_IOCTL_GET_STATUS: | ||
1320 | { | ||
1321 | u16 status = 0; | ||
1322 | if (copy_to_user((void __user *)arg, &status, | ||
1323 | 2)) { | ||
1324 | pr_info("%s %d\n", __func__, __LINE__); | ||
1325 | return -EFAULT; | ||
1326 | } | ||
1327 | return 0; | ||
1328 | } | ||
1329 | case OV5650_IOCTL_TEST_PATTERN: | ||
1330 | { | ||
1331 | err = ov5650_test_pattern(info, (enum ov5650_test_pattern) arg); | ||
1332 | if (err) | ||
1333 | pr_err("%s %d %d\n", __func__, __LINE__, err); | ||
1334 | return err; | ||
1335 | } | ||
1336 | case OV5650_IOCTL_SET_GROUP_HOLD: | ||
1337 | { | ||
1338 | struct ov5650_ae ae; | ||
1339 | if (copy_from_user(&ae, | ||
1340 | (const void __user *)arg, | ||
1341 | sizeof(struct ov5650_ae))) { | ||
1342 | pr_info("%s %d\n", __func__, __LINE__); | ||
1343 | return -EFAULT; | ||
1344 | } | ||
1345 | return ov5650_set_group_hold(info, &ae); | ||
1346 | } | ||
1347 | default: | ||
1348 | return -EINVAL; | ||
1349 | } | ||
1350 | return 0; | ||
1351 | } | ||
1352 | |||
1353 | static int ov5650_open(struct inode *inode, struct file *file) | ||
1354 | { | ||
1355 | pr_info("%s\n", __func__); | ||
1356 | file->private_data = stereo_ov5650_info; | ||
1357 | ov5650_set_power(1); | ||
1358 | return 0; | ||
1359 | } | ||
1360 | |||
1361 | int ov5650_release(struct inode *inode, struct file *file) | ||
1362 | { | ||
1363 | ov5650_set_power(0); | ||
1364 | file->private_data = NULL; | ||
1365 | return 0; | ||
1366 | } | ||
1367 | |||
1368 | |||
1369 | static const struct file_operations ov5650_fileops = { | ||
1370 | .owner = THIS_MODULE, | ||
1371 | .open = ov5650_open, | ||
1372 | .unlocked_ioctl = ov5650_ioctl, | ||
1373 | .release = ov5650_release, | ||
1374 | }; | ||
1375 | |||
1376 | static struct miscdevice ov5650_device = { | ||
1377 | .minor = MISC_DYNAMIC_MINOR, | ||
1378 | .name = "ov5650", | ||
1379 | .fops = &ov5650_fileops, | ||
1380 | }; | ||
1381 | |||
1382 | static int ov5650_probe_common(void) | ||
1383 | { | ||
1384 | int err; | ||
1385 | |||
1386 | if (!stereo_ov5650_info) { | ||
1387 | stereo_ov5650_info = kzalloc(sizeof(struct ov5650_info), | ||
1388 | GFP_KERNEL); | ||
1389 | if (!stereo_ov5650_info) { | ||
1390 | pr_err("ov5650: Unable to allocate memory!\n"); | ||
1391 | return -ENOMEM; | ||
1392 | } | ||
1393 | |||
1394 | err = misc_register(&ov5650_device); | ||
1395 | if (err) { | ||
1396 | pr_err("ov5650: Unable to register misc device!\n"); | ||
1397 | kfree(stereo_ov5650_info); | ||
1398 | return err; | ||
1399 | } | ||
1400 | } | ||
1401 | return 0; | ||
1402 | } | ||
1403 | |||
1404 | static int ov5650_remove_common(struct i2c_client *client) | ||
1405 | { | ||
1406 | if (stereo_ov5650_info->left.i2c_client || | ||
1407 | stereo_ov5650_info->right.i2c_client) | ||
1408 | return 0; | ||
1409 | |||
1410 | misc_deregister(&ov5650_device); | ||
1411 | kfree(stereo_ov5650_info); | ||
1412 | stereo_ov5650_info = NULL; | ||
1413 | |||
1414 | return 0; | ||
1415 | } | ||
1416 | |||
1417 | static int left_ov5650_probe(struct i2c_client *client, | ||
1418 | const struct i2c_device_id *id) | ||
1419 | { | ||
1420 | int err; | ||
1421 | pr_info("%s: probing sensor.\n", __func__); | ||
1422 | |||
1423 | err = ov5650_probe_common(); | ||
1424 | if (err) | ||
1425 | return err; | ||
1426 | |||
1427 | stereo_ov5650_info->left.pdata = client->dev.platform_data; | ||
1428 | stereo_ov5650_info->left.i2c_client = client; | ||
1429 | |||
1430 | return 0; | ||
1431 | } | ||
1432 | |||
1433 | static int left_ov5650_remove(struct i2c_client *client) | ||
1434 | { | ||
1435 | if (stereo_ov5650_info) { | ||
1436 | stereo_ov5650_info->left.i2c_client = NULL; | ||
1437 | ov5650_remove_common(client); | ||
1438 | } | ||
1439 | return 0; | ||
1440 | } | ||
1441 | |||
1442 | static const struct i2c_device_id left_ov5650_id[] = { | ||
1443 | { "ov5650", 0 }, | ||
1444 | { "ov5650L", 0 }, | ||
1445 | { }, | ||
1446 | }; | ||
1447 | |||
1448 | MODULE_DEVICE_TABLE(i2c, left_ov5650_id); | ||
1449 | |||
1450 | static struct i2c_driver left_ov5650_i2c_driver = { | ||
1451 | .driver = { | ||
1452 | .name = "ov5650", | ||
1453 | .owner = THIS_MODULE, | ||
1454 | }, | ||
1455 | .probe = left_ov5650_probe, | ||
1456 | .remove = left_ov5650_remove, | ||
1457 | .id_table = left_ov5650_id, | ||
1458 | }; | ||
1459 | |||
1460 | static int right_ov5650_probe(struct i2c_client *client, | ||
1461 | const struct i2c_device_id *id) | ||
1462 | { | ||
1463 | int err; | ||
1464 | pr_info("%s: probing sensor.\n", __func__); | ||
1465 | |||
1466 | err = ov5650_probe_common(); | ||
1467 | if (err) | ||
1468 | return err; | ||
1469 | |||
1470 | stereo_ov5650_info->right.pdata = client->dev.platform_data; | ||
1471 | stereo_ov5650_info->right.i2c_client = client; | ||
1472 | |||
1473 | return 0; | ||
1474 | } | ||
1475 | |||
1476 | static int right_ov5650_remove(struct i2c_client *client) | ||
1477 | { | ||
1478 | if (stereo_ov5650_info) { | ||
1479 | stereo_ov5650_info->right.i2c_client = NULL; | ||
1480 | ov5650_remove_common(client); | ||
1481 | } | ||
1482 | return 0; | ||
1483 | } | ||
1484 | |||
1485 | static const struct i2c_device_id right_ov5650_id[] = { | ||
1486 | { "ov5650R", 0 }, | ||
1487 | { }, | ||
1488 | }; | ||
1489 | |||
1490 | MODULE_DEVICE_TABLE(i2c, right_ov5650_id); | ||
1491 | |||
1492 | static struct i2c_driver right_ov5650_i2c_driver = { | ||
1493 | .driver = { | ||
1494 | .name = "ov5650R", | ||
1495 | .owner = THIS_MODULE, | ||
1496 | }, | ||
1497 | .probe = right_ov5650_probe, | ||
1498 | .remove = right_ov5650_remove, | ||
1499 | .id_table = right_ov5650_id, | ||
1500 | }; | ||
1501 | |||
1502 | static int __init ov5650_init(void) | ||
1503 | { | ||
1504 | int ret; | ||
1505 | pr_info("ov5650 sensor driver loading\n"); | ||
1506 | ret = i2c_add_driver(&left_ov5650_i2c_driver); | ||
1507 | if (ret) | ||
1508 | return ret; | ||
1509 | return i2c_add_driver(&right_ov5650_i2c_driver); | ||
1510 | } | ||
1511 | |||
1512 | static void __exit ov5650_exit(void) | ||
1513 | { | ||
1514 | i2c_del_driver(&right_ov5650_i2c_driver); | ||
1515 | i2c_del_driver(&left_ov5650_i2c_driver); | ||
1516 | } | ||
1517 | |||
1518 | module_init(ov5650_init); | ||
1519 | module_exit(ov5650_exit); | ||
1520 | |||
diff --git a/drivers/media/video/tegra/ov9726.c b/drivers/media/video/tegra/ov9726.c new file mode 100644 index 00000000000..655d07c736a --- /dev/null +++ b/drivers/media/video/tegra/ov9726.c | |||
@@ -0,0 +1,845 @@ | |||
1 | /* | ||
2 | * ov9726.c - ov9726 sensor driver | ||
3 | * | ||
4 | * Copyright (c) 2011, NVIDIA, All Rights Reserved. | ||
5 | * | ||
6 | * Contributors: | ||
7 | * Charlie Huang <chahuang@nvidia.com> | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public License | ||
10 | * version 2. This program is licensed "as is" without any warranty of any | ||
11 | * kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/fs.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <linux/miscdevice.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/uaccess.h> | ||
22 | #include <mach/iomap.h> | ||
23 | #include <linux/atomic.h> | ||
24 | #include <mach/gpio.h> | ||
25 | #include <linux/regulator/consumer.h> | ||
26 | |||
27 | #include <media/ov9726.h> | ||
28 | |||
29 | struct ov9726_power_rail { | ||
30 | struct regulator *sen_1v8_reg; | ||
31 | struct regulator *sen_2v8_reg; | ||
32 | }; | ||
33 | |||
34 | struct ov9726_devinfo { | ||
35 | struct miscdevice miscdev_info; | ||
36 | struct i2c_client *i2c_client; | ||
37 | struct ov9726_platform_data *pdata; | ||
38 | struct ov9726_power_rail power_rail; | ||
39 | atomic_t in_use; | ||
40 | __u32 mode; | ||
41 | }; | ||
42 | |||
43 | static struct ov9726_reg mode_1280x720[] = { | ||
44 | /* | ||
45 | (1) clock setting | ||
46 | clock formula: (Ref_clk / pre_pll_clk_div) * pll_multiplier / | ||
47 | vt_sys_clk_div / vt_pix_clk_div / divmip | ||
48 | input clk at 24Mhz | ||
49 | pre_pll_clk_div 0305[3:0] => 4 | ||
50 | pll_multiplier 0307[6:0] => 100 | ||
51 | vt_sys_clk_div 0303[3:0] => 1 | ||
52 | vt_pix_clk_div 0301[3:0] => 10 | ||
53 | divmip 3010[2:0] => 1 | ||
54 | |||
55 | Overall timing: | ||
56 | line length: 1664 (reg 0x342/0x343) | ||
57 | frame length: 840 (reg 0x340/0x341) | ||
58 | coarse integration time: 835 lines (reg 0x202/0x203) => change to 836 | ||
59 | |||
60 | visible pixels: (0,40) - (1280, 720+40) with size 1280x720 | ||
61 | Output pixels (1280x720) | ||
62 | |||
63 | Frame rate if MCLK=24MHz: | ||
64 | 24Mhz/4 *100/1/10/1 = 60 Mhz | ||
65 | 60Mhz/1664/840 = 42.9 fps | ||
66 | */ | ||
67 | |||
68 | {0x0103, 0x01}, | ||
69 | |||
70 | {OV9726_TABLE_WAIT_MS, 10}, | ||
71 | |||
72 | {0x3026, 0x00}, | ||
73 | {0x3027, 0x00}, | ||
74 | {0x3705, 0x45}, | ||
75 | {0x3603, 0xaa}, | ||
76 | {0x3632, 0x2f}, | ||
77 | {0x3620, 0x66}, | ||
78 | {0x3621, 0xc0}, | ||
79 | {0x0202, 0x03}, | ||
80 | {0x0203, 0x13}, | ||
81 | {0x3833, 0x04}, | ||
82 | {0x3835, 0x02}, | ||
83 | {0x4702, 0x04}, | ||
84 | {0x4704, 0x00}, | ||
85 | {0x4706, 0x08}, | ||
86 | {0x5052, 0x01}, | ||
87 | {0x3819, 0x6c}, | ||
88 | {0x3817, 0x94}, | ||
89 | {0x404e, 0x7e}, | ||
90 | {0x3601, 0x40}, | ||
91 | {0x3610, 0xa0}, | ||
92 | {0x0344, 0x00}, | ||
93 | {0x0345, 0x00}, | ||
94 | {0x0346, 0x00}, | ||
95 | {0x0347, 0x28}, | ||
96 | |||
97 | {0x034c, 0x05}, | ||
98 | {0x034d, 0x00}, | ||
99 | {0x034e, 0x02}, | ||
100 | {0x034f, 0xd8}, | ||
101 | {0x3002, 0x00}, | ||
102 | {0x3004, 0x00}, | ||
103 | {0x3005, 0x00}, | ||
104 | {0x4800, 0x44}, | ||
105 | {0x4801, 0x0f}, | ||
106 | {0x4803, 0x05}, | ||
107 | {0x4601, 0x16}, | ||
108 | {0x3014, 0x05}, | ||
109 | {0x0101, 0x01}, | ||
110 | {0x3707, 0x14}, | ||
111 | {0x3622, 0x9f}, | ||
112 | {0x4002, 0x45}, | ||
113 | {0x5001, 0x00}, | ||
114 | {0x3406, 0x01}, | ||
115 | {0x3503, 0x17}, | ||
116 | {0x0205, 0x3f}, | ||
117 | {0x0100, 0x01}, | ||
118 | {0x0112, 0x0a}, | ||
119 | {0x0113, 0x0a}, | ||
120 | {0x3013, 0x20}, | ||
121 | {0x4837, 0x2f}, | ||
122 | {0x3615, 0xf0}, | ||
123 | {0x0340, 0x03}, | ||
124 | {0x0341, 0x48}, | ||
125 | {0x0342, 0x06}, | ||
126 | {0x0343, 0x80}, | ||
127 | {0x3702, 0x1e}, | ||
128 | {0x3703, 0x3c}, | ||
129 | {0x3704, 0x0e}, | ||
130 | |||
131 | {0x3104, 0x20}, | ||
132 | {0x0305, 0x04}, | ||
133 | {0x0307, 0x46}, | ||
134 | {0x0303, 0x01}, | ||
135 | {0x0301, 0x0a}, | ||
136 | {0x3010, 0x01}, | ||
137 | {0x460e, 0x00}, | ||
138 | |||
139 | {0x5000, 0x00}, | ||
140 | {0x5002, 0x00}, | ||
141 | {0x3017, 0xd2}, | ||
142 | {0x3018, 0x69}, | ||
143 | {0x3019, 0x96}, | ||
144 | {0x5047, 0x61}, | ||
145 | {0x3604, 0x1c}, | ||
146 | {0x3602, 0x10}, | ||
147 | {0x3612, 0x21}, | ||
148 | {0x3630, 0x0a}, | ||
149 | {0x3631, 0x53}, | ||
150 | {0x3633, 0x70}, | ||
151 | {0x4005, 0x1a}, | ||
152 | {0x4009, 0x10}, | ||
153 | |||
154 | {OV9726_TABLE_END, 0x0000} | ||
155 | }; | ||
156 | |||
157 | static struct ov9726_reg mode_1280x800[] = { | ||
158 | {0x0103, 0x01}, | ||
159 | |||
160 | {OV9726_TABLE_WAIT_MS, 10}, | ||
161 | |||
162 | {0x3026, 0x00}, | ||
163 | {0x3027, 0x00}, | ||
164 | {0x3705, 0x45}, | ||
165 | {0x3603, 0xaa}, | ||
166 | {0x3632, 0x2f}, | ||
167 | {0x3620, 0x66}, | ||
168 | {0x3621, 0xc0}, | ||
169 | {0x0202, 0x03}, | ||
170 | {0x0203, 0x13}, | ||
171 | {0x3833, 0x04}, | ||
172 | {0x3835, 0x02}, | ||
173 | {0x4702, 0x04}, | ||
174 | {0x4704, 0x00}, | ||
175 | {0x4706, 0x08}, | ||
176 | {0x5052, 0x01}, | ||
177 | {0x3819, 0x6c}, | ||
178 | {0x3817, 0x94}, | ||
179 | {0x404e, 0x7e}, | ||
180 | {0x3601, 0x40}, | ||
181 | {0x3610, 0xa0}, | ||
182 | |||
183 | {0x0344, 0x00}, | ||
184 | {0x0345, 0x00}, | ||
185 | {0x0346, 0x00}, | ||
186 | {0x0347, 0x00}, | ||
187 | {0x034c, 0x05}, | ||
188 | {0x034d, 0x10}, | ||
189 | {0x034e, 0x03}, | ||
190 | {0x034f, 0x28}, | ||
191 | |||
192 | {0x3002, 0x00}, | ||
193 | {0x3004, 0x00}, | ||
194 | {0x3005, 0x00}, | ||
195 | {0x4800, 0x44}, | ||
196 | {0x4801, 0x0f}, | ||
197 | {0x4803, 0x05}, | ||
198 | {0x4601, 0x16}, | ||
199 | {0x3014, 0x05}, | ||
200 | {0x0101, 0x01}, | ||
201 | {0x3707, 0x14}, | ||
202 | {0x3622, 0x9f}, | ||
203 | {0x4002, 0x45}, | ||
204 | {0x5001, 0x00}, | ||
205 | {0x3406, 0x01}, | ||
206 | {0x3503, 0x17}, | ||
207 | {0x0205, 0x3f}, | ||
208 | {0x0100, 0x01}, | ||
209 | {0x0112, 0x0a}, | ||
210 | {0x0113, 0x0a}, | ||
211 | {0x3013, 0x20}, | ||
212 | {0x4837, 0x2f}, | ||
213 | {0x3615, 0xf0}, | ||
214 | {0x0340, 0x03}, | ||
215 | {0x0341, 0x48}, | ||
216 | {0x0342, 0x06}, | ||
217 | {0x0343, 0x80}, | ||
218 | {0x3702, 0x1e}, | ||
219 | {0x3703, 0x3c}, | ||
220 | {0x3704, 0x0e}, | ||
221 | |||
222 | {0x3104, 0x20}, | ||
223 | {0x0305, 0x04}, | ||
224 | {0x0307, 0x46}, | ||
225 | {0x0303, 0x01}, | ||
226 | {0x0301, 0x0a}, | ||
227 | {0x3010, 0x01}, | ||
228 | {0x460e, 0x00}, | ||
229 | |||
230 | {0x5000, 0x00}, | ||
231 | {0x5002, 0x00}, | ||
232 | {0x3017, 0xd2}, | ||
233 | {0x3018, 0x69}, | ||
234 | {0x3019, 0x96}, | ||
235 | {0x5047, 0x61}, | ||
236 | {0x3604, 0x1c}, | ||
237 | {0x3602, 0x10}, | ||
238 | {0x3612, 0x21}, | ||
239 | {0x3630, 0x0a}, | ||
240 | {0x3631, 0x53}, | ||
241 | {0x3633, 0x70}, | ||
242 | {0x4005, 0x1a}, | ||
243 | {0x4009, 0x10}, | ||
244 | |||
245 | {OV9726_TABLE_END, 0x0000} | ||
246 | }; | ||
247 | |||
248 | enum { | ||
249 | OV9726_MODE_1280x720, | ||
250 | OV9726_MODE_1280x800, | ||
251 | }; | ||
252 | |||
253 | static struct ov9726_reg *mode_table[] = { | ||
254 | [OV9726_MODE_1280x720] = mode_1280x720, | ||
255 | [OV9726_MODE_1280x800] = mode_1280x800, | ||
256 | }; | ||
257 | |||
258 | static inline void | ||
259 | msleep_range(unsigned int delay_base) | ||
260 | { | ||
261 | usleep_range(delay_base*1000, delay_base*1000 + 500); | ||
262 | } | ||
263 | |||
264 | static inline int | ||
265 | ov9726_power_init(struct ov9726_devinfo *dev) | ||
266 | { | ||
267 | struct i2c_client *i2c_client = dev->i2c_client; | ||
268 | int err = 0; | ||
269 | |||
270 | dev->power_rail.sen_1v8_reg = regulator_get(&i2c_client->dev, "dovdd"); | ||
271 | if (IS_ERR_OR_NULL(dev->power_rail.sen_1v8_reg)) { | ||
272 | dev_err(&i2c_client->dev, "%s: failed to get vdd\n", | ||
273 | __func__); | ||
274 | err = PTR_ERR(dev->power_rail.sen_1v8_reg); | ||
275 | goto ov9726_power_init_end; | ||
276 | } | ||
277 | |||
278 | dev->power_rail.sen_2v8_reg = regulator_get(&i2c_client->dev, "avdd"); | ||
279 | if (IS_ERR_OR_NULL(dev->power_rail.sen_2v8_reg)) { | ||
280 | dev_err(&i2c_client->dev, "%s: failed to get vaa\n", | ||
281 | __func__); | ||
282 | err = PTR_ERR(dev->power_rail.sen_2v8_reg); | ||
283 | |||
284 | regulator_put(dev->power_rail.sen_1v8_reg); | ||
285 | dev->power_rail.sen_1v8_reg = NULL; | ||
286 | } | ||
287 | |||
288 | ov9726_power_init_end: | ||
289 | return err; | ||
290 | } | ||
291 | |||
292 | static inline void | ||
293 | ov9726_power_release(struct ov9726_devinfo *dev) | ||
294 | { | ||
295 | regulator_put(dev->power_rail.sen_1v8_reg); | ||
296 | regulator_put(dev->power_rail.sen_2v8_reg); | ||
297 | } | ||
298 | |||
299 | static int | ||
300 | ov9726_power(struct ov9726_devinfo *dev, bool pwr_on) | ||
301 | { | ||
302 | struct i2c_client *i2c_client = dev->i2c_client; | ||
303 | int rst_active_state = dev->pdata->rst_low_active ? 0 : 1; | ||
304 | int pwdn_active_state = dev->pdata->pwdn_low_active ? 0 : 1; | ||
305 | int ret = 0; | ||
306 | |||
307 | dev_info(&i2c_client->dev, "%s %s\n", __func__, pwr_on ? "on" : "off"); | ||
308 | |||
309 | if (pwr_on) { | ||
310 | /* pull low the RST pin of ov9726 first */ | ||
311 | gpio_set_value(dev->pdata->gpio_rst, rst_active_state); | ||
312 | msleep_range(1); | ||
313 | /* Plug 1.8V and 2.8V power to sensor */ | ||
314 | ret = regulator_enable(dev->power_rail.sen_1v8_reg); | ||
315 | if (ret) { | ||
316 | dev_err(&i2c_client->dev, "%s: failed to enable vdd\n", | ||
317 | __func__); | ||
318 | goto fail_regulator_1v8_reg; | ||
319 | } | ||
320 | |||
321 | msleep_range(20); | ||
322 | |||
323 | ret = regulator_enable(dev->power_rail.sen_2v8_reg); | ||
324 | if (ret) { | ||
325 | dev_err(&i2c_client->dev, "%s: failed to enable vaa\n", | ||
326 | __func__); | ||
327 | goto fail_regulator_2v8_reg; | ||
328 | } | ||
329 | msleep_range(1); | ||
330 | /* turn on ov9726 */ | ||
331 | gpio_set_value(dev->pdata->gpio_pwdn, !pwdn_active_state); | ||
332 | |||
333 | msleep_range(5); | ||
334 | /* release RST pin */ | ||
335 | gpio_set_value(dev->pdata->gpio_rst, !rst_active_state); | ||
336 | msleep_range(20); | ||
337 | |||
338 | /* Board specific power-on sequence */ | ||
339 | dev->pdata->power_on(); | ||
340 | } else { | ||
341 | /* pull low the RST pin of ov9726 */ | ||
342 | gpio_set_value(dev->pdata->gpio_rst, rst_active_state); | ||
343 | msleep_range(1); | ||
344 | /* turn off ov9726 */ | ||
345 | gpio_set_value(dev->pdata->gpio_pwdn, pwdn_active_state); | ||
346 | msleep_range(1); | ||
347 | |||
348 | /* Unplug 1.8V and 2.8V power from sensor */ | ||
349 | regulator_disable(dev->power_rail.sen_2v8_reg); | ||
350 | regulator_disable(dev->power_rail.sen_1v8_reg); | ||
351 | |||
352 | /* Board specific power-down sequence */ | ||
353 | dev->pdata->power_off(); | ||
354 | } | ||
355 | |||
356 | return 0; | ||
357 | |||
358 | fail_regulator_2v8_reg: | ||
359 | regulator_put(dev->power_rail.sen_2v8_reg); | ||
360 | dev->power_rail.sen_2v8_reg = NULL; | ||
361 | regulator_disable(dev->power_rail.sen_1v8_reg); | ||
362 | fail_regulator_1v8_reg: | ||
363 | regulator_put(dev->power_rail.sen_1v8_reg); | ||
364 | dev->power_rail.sen_1v8_reg = NULL; | ||
365 | return ret; | ||
366 | } | ||
367 | |||
368 | static inline void | ||
369 | ov9726_get_frame_length_regs(struct ov9726_reg *regs, u32 frame_length) | ||
370 | { | ||
371 | regs->addr = OV9726_REG_FRAME_LENGTH_HI; | ||
372 | regs->val = (frame_length >> 8) & 0xff; | ||
373 | regs++; | ||
374 | regs->addr = OV9726_REG_FRAME_LENGTH_LO; | ||
375 | regs->val = frame_length & 0xff; | ||
376 | } | ||
377 | |||
378 | static inline void | ||
379 | ov9726_get_coarse_time_regs(struct ov9726_reg *regs, u32 coarse_time) | ||
380 | { | ||
381 | regs->addr = OV9726_REG_COARSE_TIME_HI; | ||
382 | regs->val = (coarse_time >> 8) & 0xff; | ||
383 | regs++; | ||
384 | regs->addr = OV9726_REG_COARSE_TIME_LO; | ||
385 | regs->val = coarse_time & 0xff; | ||
386 | } | ||
387 | |||
388 | static inline void | ||
389 | ov9726_get_gain_reg(struct ov9726_reg *regs, u16 gain) | ||
390 | { | ||
391 | regs->addr = OV9726_REG_GAIN_HI; | ||
392 | regs->val = (gain >> 8) & 0xff; | ||
393 | regs++; | ||
394 | regs->addr = OV9726_REG_GAIN_LO; | ||
395 | regs->val = gain & 0xff; | ||
396 | } | ||
397 | |||
398 | static int | ||
399 | ov9726_read_reg8(struct i2c_client *client, u16 addr, u8 *val) | ||
400 | { | ||
401 | int err; | ||
402 | struct i2c_msg msg[2]; | ||
403 | unsigned char data[3]; | ||
404 | |||
405 | if (!client->adapter) | ||
406 | return -ENODEV; | ||
407 | |||
408 | msg[0].addr = client->addr; | ||
409 | msg[0].flags = 0; | ||
410 | msg[0].len = 2; | ||
411 | msg[0].buf = data; | ||
412 | |||
413 | /* high byte goes out first */ | ||
414 | data[0] = (u8)(addr >> 8); | ||
415 | data[1] = (u8)(addr & 0xff); | ||
416 | |||
417 | msg[1].addr = client->addr; | ||
418 | msg[1].flags = I2C_M_RD; | ||
419 | msg[1].len = 1; | ||
420 | msg[1].buf = data + 2; | ||
421 | |||
422 | err = i2c_transfer(client->adapter, msg, 2); | ||
423 | |||
424 | if (err != 2) | ||
425 | err = -EINVAL; | ||
426 | else { | ||
427 | *val = data[2]; | ||
428 | err = 0; | ||
429 | } | ||
430 | |||
431 | return err; | ||
432 | } | ||
433 | |||
434 | static int | ||
435 | ov9726_write_reg8(struct i2c_client *client, u16 addr, u8 val) | ||
436 | { | ||
437 | int err; | ||
438 | struct i2c_msg msg; | ||
439 | unsigned char data[3]; | ||
440 | int retry = 0; | ||
441 | |||
442 | if (!client->adapter) | ||
443 | return -ENODEV; | ||
444 | |||
445 | data[0] = (u8)(addr >> 8); | ||
446 | data[1] = (u8)(addr & 0xff); | ||
447 | data[2] = (u8)(val & 0xff); | ||
448 | |||
449 | msg.addr = client->addr; | ||
450 | msg.flags = 0; | ||
451 | msg.len = 3; | ||
452 | msg.buf = data; | ||
453 | |||
454 | do { | ||
455 | err = i2c_transfer(client->adapter, &msg, 1); | ||
456 | if (err == 1) | ||
457 | break; | ||
458 | |||
459 | retry++; | ||
460 | dev_err(&client->dev, | ||
461 | "ov9726: i2c transfer failed, retrying %x %x\n", | ||
462 | addr, val); | ||
463 | msleep_range(3); | ||
464 | } while (retry <= OV9726_MAX_RETRIES); | ||
465 | |||
466 | return (err != 1); | ||
467 | } | ||
468 | |||
469 | static int | ||
470 | ov9726_write_reg16(struct i2c_client *client, u16 addr, u16 val) | ||
471 | { | ||
472 | int count; | ||
473 | struct i2c_msg msg; | ||
474 | unsigned char data[4]; | ||
475 | int retry = 0; | ||
476 | |||
477 | if (!client->adapter) | ||
478 | return -ENODEV; | ||
479 | |||
480 | data[0] = (u8)(addr >> 8); | ||
481 | data[1] = (u8)(addr & 0xff); | ||
482 | data[2] = (u8)(val >> 8); | ||
483 | data[3] = (u8)(val & 0xff); | ||
484 | |||
485 | msg.addr = client->addr; | ||
486 | msg.flags = 0; | ||
487 | msg.len = 4; | ||
488 | msg.buf = data; | ||
489 | |||
490 | do { | ||
491 | count = i2c_transfer(client->adapter, &msg, 1); | ||
492 | if (count == 1) | ||
493 | return 0; | ||
494 | |||
495 | retry++; | ||
496 | dev_err(&client->dev, | ||
497 | "ov9726: i2c transfer failed, retrying %x %x %x\n", | ||
498 | addr, val, count); | ||
499 | msleep_range(3); | ||
500 | } while (retry <= OV9726_MAX_RETRIES); | ||
501 | |||
502 | return -EIO; | ||
503 | } | ||
504 | |||
505 | static int | ||
506 | ov9726_write_table( | ||
507 | struct i2c_client *client, | ||
508 | struct ov9726_reg table[], | ||
509 | struct ov9726_reg override_list[], | ||
510 | int num_override_regs) | ||
511 | { | ||
512 | const struct ov9726_reg *next; | ||
513 | int err = 0; | ||
514 | int i; | ||
515 | u16 val; | ||
516 | |||
517 | dev_info(&client->dev, "ov9726_write_table\n"); | ||
518 | |||
519 | for (next = table; next->addr != OV9726_TABLE_END; next++) { | ||
520 | |||
521 | if (next->addr == OV9726_TABLE_WAIT_MS) { | ||
522 | msleep_range(next->val); | ||
523 | continue; | ||
524 | } | ||
525 | |||
526 | val = next->val; | ||
527 | |||
528 | /* When an override list is passed in, replace the reg */ | ||
529 | /* value to write if the reg is in the list */ | ||
530 | if (override_list) { | ||
531 | for (i = 0; i < num_override_regs; i++) { | ||
532 | if (next->addr == override_list[i].addr) { | ||
533 | val = override_list[i].val; | ||
534 | break; | ||
535 | } | ||
536 | } | ||
537 | } | ||
538 | |||
539 | err = ov9726_write_reg8(client, next->addr, val); | ||
540 | if (err) | ||
541 | break; | ||
542 | } | ||
543 | |||
544 | return err; | ||
545 | } | ||
546 | |||
547 | static int | ||
548 | ov9726_set_frame_length(struct i2c_client *i2c_client, u32 frame_length) | ||
549 | { | ||
550 | int ret; | ||
551 | |||
552 | dev_info(&i2c_client->dev, "[%s] (0x%08x)\n", __func__, frame_length); | ||
553 | /* hold register value */ | ||
554 | ret = ov9726_write_reg8(i2c_client, 0x104, 0x01); | ||
555 | if (ret) | ||
556 | return ret; | ||
557 | |||
558 | ret = ov9726_write_reg16(i2c_client, | ||
559 | OV9726_REG_FRAME_LENGTH_HI, | ||
560 | frame_length); | ||
561 | |||
562 | /* release hold, update register value */ | ||
563 | ret |= ov9726_write_reg8(i2c_client, 0x104, 0x00); | ||
564 | |||
565 | return ret; | ||
566 | } | ||
567 | |||
568 | static int | ||
569 | ov9726_set_coarse_time(struct i2c_client *i2c_client, u32 coarse_time) | ||
570 | { | ||
571 | int ret; | ||
572 | |||
573 | dev_info(&i2c_client->dev, "[%s] (0x%08x)\n", __func__, coarse_time); | ||
574 | /* hold register value */ | ||
575 | ret = ov9726_write_reg8(i2c_client, 0x104, 0x01); | ||
576 | if (ret) | ||
577 | return ret; | ||
578 | |||
579 | ret = ov9726_write_reg16(i2c_client, | ||
580 | OV9726_REG_COARSE_TIME_HI, | ||
581 | coarse_time); | ||
582 | |||
583 | /* release hold, update register value */ | ||
584 | ret |= ov9726_write_reg8(i2c_client, 0x104, 0x00); | ||
585 | |||
586 | return ret; | ||
587 | } | ||
588 | |||
589 | static int ov9726_set_gain(struct i2c_client *i2c_client, u16 gain) | ||
590 | { | ||
591 | int ret; | ||
592 | |||
593 | /* hold register value */ | ||
594 | ret = ov9726_write_reg8(i2c_client, 0x104, 0x01); | ||
595 | if (ret) | ||
596 | return ret; | ||
597 | |||
598 | ret = ov9726_write_reg16(i2c_client, OV9726_REG_GAIN_HI, gain); | ||
599 | |||
600 | /* release hold, update register value */ | ||
601 | ret |= ov9726_write_reg8(i2c_client, 0x104, 0x00); | ||
602 | |||
603 | return ret; | ||
604 | } | ||
605 | |||
606 | static int ov9726_get_status(struct i2c_client *i2c_client, u8 *status) | ||
607 | { | ||
608 | int err; | ||
609 | |||
610 | err = ov9726_read_reg8(i2c_client, 0x003, status); | ||
611 | *status = 0; | ||
612 | return err; | ||
613 | } | ||
614 | |||
615 | static int | ||
616 | ov9726_set_mode( | ||
617 | struct ov9726_devinfo *dev, | ||
618 | struct ov9726_mode *mode) | ||
619 | { | ||
620 | struct i2c_client *i2c_client = dev->i2c_client; | ||
621 | struct ov9726_reg reg_override[6]; | ||
622 | int err = 0; | ||
623 | int sensor_mode; | ||
624 | |||
625 | dev_info(&i2c_client->dev, "%s.\n", __func__); | ||
626 | |||
627 | if (mode->xres == 1280 && mode->yres == 800) | ||
628 | sensor_mode = OV9726_MODE_1280x800; | ||
629 | else if (mode->xres == 1280 && mode->yres == 720) | ||
630 | sensor_mode = OV9726_MODE_1280x720; | ||
631 | else { | ||
632 | dev_err(&i2c_client->dev, | ||
633 | "%s: invalid resolution supplied to set mode %d %d\n", | ||
634 | __func__, mode->xres, mode->yres); | ||
635 | return -EINVAL; | ||
636 | } | ||
637 | |||
638 | ov9726_get_frame_length_regs(reg_override, mode->frame_length); | ||
639 | ov9726_get_coarse_time_regs(reg_override + 2, mode->coarse_time); | ||
640 | ov9726_get_gain_reg(reg_override + 4, mode->gain); | ||
641 | |||
642 | if (dev->mode != mode->mode_id) { | ||
643 | dev_info(&i2c_client->dev, | ||
644 | "%s: xres %u yres %u framelen %u coarse %u gain %u\n", | ||
645 | __func__, mode->xres, mode->yres, mode->frame_length, | ||
646 | mode->coarse_time, mode->gain); | ||
647 | |||
648 | err = ov9726_write_table(i2c_client, | ||
649 | mode_table[sensor_mode], reg_override, | ||
650 | sizeof(reg_override) / sizeof(reg_override[0])); | ||
651 | if (err) | ||
652 | goto ov9726_set_mode_exit; | ||
653 | |||
654 | dev->mode = mode->mode_id; | ||
655 | } | ||
656 | |||
657 | ov9726_set_mode_exit: | ||
658 | return err; | ||
659 | } | ||
660 | |||
661 | static long | ||
662 | ov9726_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
663 | { | ||
664 | struct ov9726_devinfo *dev = file->private_data; | ||
665 | struct i2c_client *i2c_client = dev->i2c_client; | ||
666 | int err = 0; | ||
667 | |||
668 | switch (cmd) { | ||
669 | case OV9726_IOCTL_SET_MODE: | ||
670 | { | ||
671 | struct ov9726_mode mode; | ||
672 | |||
673 | if (copy_from_user(&mode, | ||
674 | (const void __user *)arg, | ||
675 | sizeof(struct ov9726_mode))) { | ||
676 | err = -EFAULT; | ||
677 | break; | ||
678 | } | ||
679 | |||
680 | err = ov9726_set_mode(dev, &mode); | ||
681 | |||
682 | break; | ||
683 | } | ||
684 | |||
685 | case OV9726_IOCTL_SET_FRAME_LENGTH: | ||
686 | err = ov9726_set_frame_length(i2c_client, (u32)arg); | ||
687 | break; | ||
688 | |||
689 | case OV9726_IOCTL_SET_COARSE_TIME: | ||
690 | err = ov9726_set_coarse_time(i2c_client, (u32)arg); | ||
691 | break; | ||
692 | |||
693 | case OV9726_IOCTL_SET_GAIN: | ||
694 | err = ov9726_set_gain(i2c_client, (u16)arg); | ||
695 | break; | ||
696 | |||
697 | case OV9726_IOCTL_GET_STATUS: | ||
698 | { | ||
699 | u8 status; | ||
700 | |||
701 | err = ov9726_get_status(i2c_client, &status); | ||
702 | if (!err) { | ||
703 | if (copy_to_user((void __user *)arg, | ||
704 | &status, sizeof(status))) | ||
705 | err = -EFAULT; | ||
706 | } | ||
707 | break; | ||
708 | } | ||
709 | |||
710 | default: | ||
711 | err = -EINVAL; | ||
712 | break; | ||
713 | } | ||
714 | |||
715 | return err; | ||
716 | } | ||
717 | |||
718 | static int ov9726_open(struct inode *inode, struct file *file) | ||
719 | { | ||
720 | struct miscdevice *miscdev = file->private_data; | ||
721 | struct ov9726_devinfo *dev; | ||
722 | |||
723 | dev = container_of(miscdev, struct ov9726_devinfo, miscdev_info); | ||
724 | /* check if device is in use */ | ||
725 | if (atomic_xchg(&dev->in_use, 1)) | ||
726 | return -EBUSY; | ||
727 | dev->mode = (__u32)-1; | ||
728 | file->private_data = dev; | ||
729 | |||
730 | ov9726_power(dev, true); | ||
731 | |||
732 | return 0; | ||
733 | } | ||
734 | |||
735 | int ov9726_release(struct inode *inode, struct file *file) | ||
736 | { | ||
737 | struct ov9726_devinfo *dev; | ||
738 | |||
739 | dev = file->private_data; | ||
740 | file->private_data = NULL; | ||
741 | |||
742 | ov9726_power(dev, false); | ||
743 | |||
744 | /* warn if device already released */ | ||
745 | WARN_ON(!atomic_xchg(&dev->in_use, 0)); | ||
746 | return 0; | ||
747 | } | ||
748 | |||
749 | static const struct file_operations ov9726_fileops = { | ||
750 | .owner = THIS_MODULE, | ||
751 | .open = ov9726_open, | ||
752 | .unlocked_ioctl = ov9726_ioctl, | ||
753 | .release = ov9726_release, | ||
754 | }; | ||
755 | |||
756 | static struct miscdevice ov9726_device = { | ||
757 | .name = "ov9726", | ||
758 | .minor = MISC_DYNAMIC_MINOR, | ||
759 | .fops = &ov9726_fileops, | ||
760 | }; | ||
761 | |||
762 | static int | ||
763 | ov9726_probe(struct i2c_client *client, const struct i2c_device_id *id) | ||
764 | { | ||
765 | struct ov9726_devinfo *dev; | ||
766 | int err = 0; | ||
767 | |||
768 | dev_info(&client->dev, "ov9726: probing sensor.\n"); | ||
769 | |||
770 | dev = kzalloc(sizeof(struct ov9726_devinfo), GFP_KERNEL); | ||
771 | if (!dev) { | ||
772 | dev_err(&client->dev, "ov9726: Unable to allocate memory!\n"); | ||
773 | err = -ENOMEM; | ||
774 | goto probe_end; | ||
775 | } | ||
776 | |||
777 | memcpy(&(dev->miscdev_info), | ||
778 | &ov9726_device, | ||
779 | sizeof(struct miscdevice)); | ||
780 | |||
781 | err = misc_register(&(dev->miscdev_info)); | ||
782 | if (err) { | ||
783 | dev_err(&client->dev, "ov9726: Unable to register misc device!\n"); | ||
784 | goto probe_end; | ||
785 | } | ||
786 | |||
787 | dev->pdata = client->dev.platform_data; | ||
788 | dev->i2c_client = client; | ||
789 | atomic_set(&dev->in_use, 0); | ||
790 | i2c_set_clientdata(client, dev); | ||
791 | |||
792 | err = ov9726_power_init(dev); | ||
793 | |||
794 | probe_end: | ||
795 | if (err) { | ||
796 | kfree(dev); | ||
797 | dev_err(&client->dev, "failed.\n"); | ||
798 | } | ||
799 | |||
800 | return err; | ||
801 | } | ||
802 | |||
803 | static int ov9726_remove(struct i2c_client *client) | ||
804 | { | ||
805 | struct ov9726_devinfo *dev; | ||
806 | |||
807 | dev = i2c_get_clientdata(client); | ||
808 | i2c_set_clientdata(client, NULL); | ||
809 | misc_deregister(&ov9726_device); | ||
810 | ov9726_power_release(dev); | ||
811 | kfree(dev); | ||
812 | |||
813 | return 0; | ||
814 | } | ||
815 | |||
816 | static const struct i2c_device_id ov9726_id[] = { | ||
817 | {"ov9726", 0}, | ||
818 | {}, | ||
819 | }; | ||
820 | |||
821 | MODULE_DEVICE_TABLE(i2c, ov9726_id); | ||
822 | |||
823 | static struct i2c_driver ov9726_i2c_driver = { | ||
824 | .driver = { | ||
825 | .name = "ov9726", | ||
826 | .owner = THIS_MODULE, | ||
827 | }, | ||
828 | .probe = ov9726_probe, | ||
829 | .remove = ov9726_remove, | ||
830 | .id_table = ov9726_id, | ||
831 | }; | ||
832 | |||
833 | static int __init ov9726_init(void) | ||
834 | { | ||
835 | pr_info("ov9726 sensor driver loading\n"); | ||
836 | return i2c_add_driver(&ov9726_i2c_driver); | ||
837 | } | ||
838 | |||
839 | static void __exit ov9726_exit(void) | ||
840 | { | ||
841 | i2c_del_driver(&ov9726_i2c_driver); | ||
842 | } | ||
843 | |||
844 | module_init(ov9726_init); | ||
845 | module_exit(ov9726_exit); | ||
diff --git a/drivers/media/video/tegra/sh532u.c b/drivers/media/video/tegra/sh532u.c new file mode 100644 index 00000000000..7db14fcf257 --- /dev/null +++ b/drivers/media/video/tegra/sh532u.c | |||
@@ -0,0 +1,1688 @@ | |||
1 | /* | ||
2 | * SH532U focuser driver. | ||
3 | * | ||
4 | * Copyright (C) 2011 NVIDIA Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
18 | * 02111-1307, USA | ||
19 | */ | ||
20 | |||
21 | /* Implementation | ||
22 | * -------------- | ||
23 | * The board level details about the device need to be provided in the board | ||
24 | * file with the sh532u_platform_data structure. | ||
25 | * Standard among NVC kernel drivers in this structure is: | ||
26 | * .cfg = Use the NVC_CFG_ defines that are in nvc.h. | ||
27 | * Descriptions of the configuration options are with the defines. | ||
28 | * This value is typically 0. | ||
29 | * .num = The number of the instance of the device. This should start at 1 and | ||
30 | * and increment for each device on the board. This number will be | ||
31 | * appended to the MISC driver name, Example: /dev/focuser.1 | ||
32 | * If not used or 0, then nothing is appended to the name. | ||
33 | * .sync = If there is a need to synchronize two devices, then this value is | ||
34 | * the number of the device instance (.num above) this device is to | ||
35 | * sync to. For example: | ||
36 | * Device 1 platform entries = | ||
37 | * .num = 1, | ||
38 | * .sync = 2, | ||
39 | * Device 2 platfrom entries = | ||
40 | * .num = 2, | ||
41 | * .sync = 1, | ||
42 | * The above example sync's device 1 and 2. | ||
43 | * This is typically used for stereo applications. | ||
44 | * .dev_name = The MISC driver name the device registers as. If not used, | ||
45 | * then the part number of the device is used for the driver name. | ||
46 | * If using the NVC user driver then use the name found in this | ||
47 | * driver under _default_pdata. | ||
48 | * | ||
49 | * The following is specific to NVC kernel focus drivers: | ||
50 | * .nvc = Pointer to the nvc_focus_nvc structure. This structure needs to | ||
51 | * be defined and populated if overriding the driver defaults. | ||
52 | * .cap = Pointer to the nvc_focus_cap structure. This structure needs to | ||
53 | * be defined and populated if overriding the driver defaults. | ||
54 | * | ||
55 | * The following is specific to only this NVC kernel focus driver: | ||
56 | * .info = Pointer to the sh532u_pdata_info structure. This structure does | ||
57 | * not need to be defined and populated unless overriding ROM data. | ||
58 | .* .i2c_addr_rom = The I2C address of the onboard ROM. | ||
59 | * .gpio_reset = The GPIO connected to the devices reset. If not used then | ||
60 | * leave blank. | ||
61 | * .gpio_en = Due to a Linux limitation, a GPIO is defined to "enable" the | ||
62 | * device. This workaround is for when the device's power GPIO's | ||
63 | * are behind an I2C expander. The Linux limitation doesn't allow | ||
64 | * the I2C GPIO expander to be ready for use when this device is | ||
65 | * probed. When this problem is solved, this driver needs to | ||
66 | * remove the gpio_en WAR. | ||
67 | * | ||
68 | * Power Requirements | ||
69 | * The board power file must contain the following labels for the power | ||
70 | * regulator(s) of this device: | ||
71 | * "vdd" = the power regulator for the device's power. | ||
72 | * "vdd_i2c" = the power regulator for the I2C power. | ||
73 | * | ||
74 | * The above values should be all that is needed to use the device with this | ||
75 | * driver. Modifications of this driver should not be needed. | ||
76 | */ | ||
77 | |||
78 | |||
79 | #include <linux/fs.h> | ||
80 | #include <linux/i2c.h> | ||
81 | #include <linux/miscdevice.h> | ||
82 | #include <linux/slab.h> | ||
83 | #include <linux/delay.h> | ||
84 | #include <linux/uaccess.h> | ||
85 | #include <linux/list.h> | ||
86 | #include <linux/jiffies.h> | ||
87 | #include <linux/gpio.h> | ||
88 | #include <media/nvc.h> | ||
89 | #include <media/sh532u.h> | ||
90 | |||
91 | #define SH532U_ID 0xF0 | ||
92 | /* defaults if no ROM data */ | ||
93 | #define SH532U_HYPERFOCAL_RATIO 1836 /* 41.2f/224.4f Ratio source: SEMCO */ | ||
94 | /* _HYPERFOCAL_RATIO is multiplied and _HYPERFOCAL_DIV divides for float */ | ||
95 | #define SH532U_HYPERFOCAL_DIV 10000 | ||
96 | #define SH532U_FOCAL_LENGTH 0x408D70A4 | ||
97 | #define SH532U_FNUMBER 0x40333333 | ||
98 | #define SH532U_MAX_APERATURE 0x3FCA0EA1 | ||
99 | /* SH532U_CAPS_VER = 0: invalid value */ | ||
100 | /* SH532U_CAPS_VER = 1: added NVC_PARAM_STS */ | ||
101 | /* SH532U_CAPS_VER = 2: expanded nvc_focus_cap */ | ||
102 | #define SH532U_CAPS_VER 2 | ||
103 | #define SH532U_ACTUATOR_RANGE 1000 | ||
104 | #define SH532U_SETTLETIME 30 | ||
105 | #define SH532U_FOCUS_MACRO 950 | ||
106 | #define SH532U_FOCUS_HYPER 250 | ||
107 | #define SH532U_FOCUS_INFINITY 50 | ||
108 | #define SH532U_TIMEOUT_MS 200 | ||
109 | #define SH532U_POS_LOW_DEFAULT 0xA000 | ||
110 | #define SH532U_POS_HIGH_DEFAULT 0x6000 | ||
111 | |||
112 | |||
113 | struct sh532u_info { | ||
114 | atomic_t in_use; | ||
115 | struct i2c_client *i2c_client; | ||
116 | struct sh532u_platform_data *pdata; | ||
117 | struct miscdevice miscdev; | ||
118 | struct list_head list; | ||
119 | int pwr_api; | ||
120 | int pwr_dev; | ||
121 | struct nvc_regulator vreg_vdd; | ||
122 | struct nvc_regulator vreg_i2c; | ||
123 | u8 s_mode; | ||
124 | struct sh532u_info *s_info; | ||
125 | unsigned i2c_addr_rom; | ||
126 | struct nvc_focus_nvc nvc; | ||
127 | struct nvc_focus_cap cap; | ||
128 | enum nvc_focus_sts sts; | ||
129 | struct sh532u_pdata_info cfg; | ||
130 | bool gpio_flag_reset; | ||
131 | bool init_cal_flag; | ||
132 | s16 abs_base; | ||
133 | u32 abs_range; | ||
134 | u32 pos_rel; | ||
135 | s16 pos_abs; | ||
136 | long pos_time_wr; | ||
137 | }; | ||
138 | |||
139 | static struct sh532u_pdata_info sh532u_default_info = { | ||
140 | .move_timeoutms = SH532U_TIMEOUT_MS, | ||
141 | .focus_hyper_ratio = SH532U_HYPERFOCAL_RATIO, | ||
142 | .focus_hyper_div = SH532U_HYPERFOCAL_DIV, | ||
143 | }; | ||
144 | |||
145 | static struct nvc_focus_cap sh532u_default_cap = { | ||
146 | .version = SH532U_CAPS_VER, | ||
147 | .actuator_range = SH532U_ACTUATOR_RANGE, | ||
148 | .settle_time = SH532U_SETTLETIME, | ||
149 | .focus_macro = SH532U_FOCUS_MACRO, | ||
150 | .focus_hyper = SH532U_FOCUS_HYPER, | ||
151 | .focus_infinity = SH532U_FOCUS_INFINITY, | ||
152 | }; | ||
153 | |||
154 | static struct nvc_focus_nvc sh532u_default_nvc = { | ||
155 | .focal_length = SH532U_FOCAL_LENGTH, | ||
156 | .fnumber = SH532U_FNUMBER, | ||
157 | .max_aperature = SH532U_MAX_APERATURE, | ||
158 | }; | ||
159 | |||
160 | static struct sh532u_platform_data sh532u_default_pdata = { | ||
161 | .cfg = 0, | ||
162 | .num = 0, | ||
163 | .sync = 0, | ||
164 | .dev_name = "focuser", | ||
165 | .i2c_addr_rom = 0x50, | ||
166 | }; | ||
167 | |||
168 | static u32 sh532u_a2buf[] = { | ||
169 | 0x0018019c, | ||
170 | 0x0018019d, | ||
171 | 0x0000019e, | ||
172 | 0x007f0192, | ||
173 | 0x00000194, | ||
174 | 0x00f00184, | ||
175 | 0x00850187, | ||
176 | 0x0000018a, | ||
177 | 0x00fd7187, | ||
178 | 0x007f7183, | ||
179 | 0x0008025a, | ||
180 | 0x05042218, | ||
181 | 0x80010216, | ||
182 | 0x000601a0, | ||
183 | 0x00808183, | ||
184 | 0xffffffff | ||
185 | }; | ||
186 | |||
187 | static LIST_HEAD(sh532u_info_list); | ||
188 | static DEFINE_SPINLOCK(sh532u_spinlock); | ||
189 | |||
190 | |||
191 | static int sh532u_i2c_rd8(struct sh532u_info *info, u8 addr, u8 reg, u8 *val) | ||
192 | { | ||
193 | struct i2c_msg msg[2]; | ||
194 | u8 buf[2]; | ||
195 | |||
196 | buf[0] = reg; | ||
197 | if (addr) { | ||
198 | msg[0].addr = addr; | ||
199 | msg[1].addr = addr; | ||
200 | } else { | ||
201 | msg[0].addr = info->i2c_client->addr; | ||
202 | msg[1].addr = info->i2c_client->addr; | ||
203 | } | ||
204 | msg[0].flags = 0; | ||
205 | msg[0].len = 1; | ||
206 | msg[0].buf = &buf[0]; | ||
207 | msg[1].flags = I2C_M_RD; | ||
208 | msg[1].len = 1; | ||
209 | msg[1].buf = &buf[1]; | ||
210 | *val = 0; | ||
211 | if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2) | ||
212 | return -EIO; | ||
213 | |||
214 | *val = buf[1]; | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static int sh532u_i2c_wr8(struct sh532u_info *info, u8 reg, u8 val) | ||
219 | { | ||
220 | struct i2c_msg msg; | ||
221 | u8 buf[2]; | ||
222 | |||
223 | buf[0] = reg; | ||
224 | buf[1] = val; | ||
225 | msg.addr = info->i2c_client->addr; | ||
226 | msg.flags = 0; | ||
227 | msg.len = 2; | ||
228 | msg.buf = &buf[0]; | ||
229 | if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) | ||
230 | return -EIO; | ||
231 | |||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static int sh532u_i2c_rd16(struct sh532u_info *info, u8 reg, u16 *val) | ||
236 | { | ||
237 | struct i2c_msg msg[2]; | ||
238 | u8 buf[3]; | ||
239 | |||
240 | buf[0] = reg; | ||
241 | msg[0].addr = info->i2c_client->addr; | ||
242 | msg[0].flags = 0; | ||
243 | msg[0].len = 1; | ||
244 | msg[0].buf = &buf[0]; | ||
245 | msg[1].addr = info->i2c_client->addr; | ||
246 | msg[1].flags = I2C_M_RD; | ||
247 | msg[1].len = 2; | ||
248 | msg[1].buf = &buf[1]; | ||
249 | if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2) | ||
250 | return -EIO; | ||
251 | |||
252 | *val = (((u16)buf[1] << 8) | (u16)buf[2]); | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | |||
257 | static int sh532u_i2c_wr16(struct sh532u_info *info, u8 reg, u16 val) | ||
258 | { | ||
259 | struct i2c_msg msg; | ||
260 | u8 buf[3]; | ||
261 | |||
262 | buf[0] = reg; | ||
263 | buf[1] = (u8)(val >> 8); | ||
264 | buf[2] = (u8)(val & 0xff); | ||
265 | msg.addr = info->i2c_client->addr; | ||
266 | msg.flags = 0; | ||
267 | msg.len = 3; | ||
268 | msg.buf = &buf[0]; | ||
269 | if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) | ||
270 | return -EIO; | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int sh532u_i2c_rd32(struct sh532u_info *info, u8 addr, u8 reg, u32 *val) | ||
276 | { | ||
277 | struct i2c_msg msg[2]; | ||
278 | u8 buf[5]; | ||
279 | |||
280 | buf[0] = reg; | ||
281 | if (addr) { | ||
282 | msg[0].addr = addr; | ||
283 | msg[1].addr = addr; | ||
284 | } else { | ||
285 | msg[0].addr = info->i2c_client->addr; | ||
286 | msg[1].addr = info->i2c_client->addr; | ||
287 | } | ||
288 | msg[0].flags = 0; | ||
289 | msg[0].len = 1; | ||
290 | msg[0].buf = &buf[0]; | ||
291 | msg[1].flags = I2C_M_RD; | ||
292 | msg[1].len = 4; | ||
293 | msg[1].buf = &buf[1]; | ||
294 | if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2) | ||
295 | return -EIO; | ||
296 | |||
297 | *val = (((u32)buf[4] << 24) | ((u32)buf[3] << 16) | | ||
298 | ((u32)buf[2] << 8) | ((u32)buf[1])); | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | static void sh532u_gpio_en(struct sh532u_info *info, int val) | ||
303 | { | ||
304 | if (info->pdata->gpio_en) | ||
305 | gpio_set_value_cansleep(info->pdata->gpio_en, val); | ||
306 | } | ||
307 | |||
308 | static void sh532u_gpio_reset(struct sh532u_info *info, int val) | ||
309 | { | ||
310 | if (val) { | ||
311 | if (!info->gpio_flag_reset && info->pdata->gpio_reset) { | ||
312 | gpio_set_value_cansleep(info->pdata->gpio_reset, 0); | ||
313 | mdelay(1); | ||
314 | gpio_set_value_cansleep(info->pdata->gpio_reset, 1); | ||
315 | mdelay(10); /* delay for device startup */ | ||
316 | info->gpio_flag_reset = 1; | ||
317 | } | ||
318 | } else { | ||
319 | info->gpio_flag_reset = 0; | ||
320 | } | ||
321 | } | ||
322 | |||
323 | static void sh532u_pm_regulator_put(struct nvc_regulator *sreg) | ||
324 | { | ||
325 | regulator_put(sreg->vreg); | ||
326 | sreg->vreg = NULL; | ||
327 | } | ||
328 | |||
329 | static int sh532u_pm_regulator_get(struct sh532u_info *info, | ||
330 | struct nvc_regulator *sreg, | ||
331 | char vreg_name[]) | ||
332 | { | ||
333 | int err = 0; | ||
334 | |||
335 | sreg->vreg_flag = 0; | ||
336 | sreg->vreg = regulator_get(&info->i2c_client->dev, vreg_name); | ||
337 | if (IS_ERR_OR_NULL(sreg->vreg)) { | ||
338 | dev_err(&info->i2c_client->dev, | ||
339 | "%s err for regulator: %s err: %d\n", | ||
340 | __func__, vreg_name, (int)sreg->vreg); | ||
341 | err = PTR_ERR(sreg->vreg); | ||
342 | sreg->vreg = NULL; | ||
343 | } else { | ||
344 | sreg->vreg_name = vreg_name; | ||
345 | dev_dbg(&info->i2c_client->dev, | ||
346 | "%s vreg_name: %s\n", | ||
347 | __func__, sreg->vreg_name); | ||
348 | } | ||
349 | return err; | ||
350 | } | ||
351 | |||
352 | static int sh532u_pm_regulator_en(struct sh532u_info *info, | ||
353 | struct nvc_regulator *sreg) | ||
354 | { | ||
355 | int err = 0; | ||
356 | |||
357 | if (!sreg->vreg_flag && (sreg->vreg != NULL)) { | ||
358 | err = regulator_enable(sreg->vreg); | ||
359 | if (!err) { | ||
360 | dev_dbg(&info->i2c_client->dev, | ||
361 | "%s vreg_name: %s\n", | ||
362 | __func__, sreg->vreg_name); | ||
363 | sreg->vreg_flag = 1; | ||
364 | err = 1; /* flag regulator state change */ | ||
365 | } else { | ||
366 | dev_err(&info->i2c_client->dev, | ||
367 | "%s err, regulator: %s\n", | ||
368 | __func__, sreg->vreg_name); | ||
369 | } | ||
370 | } | ||
371 | return err; | ||
372 | } | ||
373 | |||
374 | static int sh532u_pm_regulator_dis(struct sh532u_info *info, | ||
375 | struct nvc_regulator *sreg) | ||
376 | { | ||
377 | int err = 0; | ||
378 | |||
379 | if (sreg->vreg_flag && (sreg->vreg != NULL)) { | ||
380 | err = regulator_disable(sreg->vreg); | ||
381 | if (err) | ||
382 | dev_err(&info->i2c_client->dev, | ||
383 | "%s err, regulator: %s\n", | ||
384 | __func__, sreg->vreg_name); | ||
385 | } | ||
386 | sreg->vreg_flag = 0; | ||
387 | return err; | ||
388 | } | ||
389 | |||
390 | static int sh532u_pm_wr(struct sh532u_info *info, int pwr) | ||
391 | { | ||
392 | int err = 0; | ||
393 | |||
394 | if ((info->pdata->cfg & (NVC_CFG_OFF2STDBY | NVC_CFG_BOOT_INIT)) && | ||
395 | (pwr == NVC_PWR_OFF || | ||
396 | pwr == NVC_PWR_STDBY_OFF)) | ||
397 | pwr = NVC_PWR_STDBY; | ||
398 | |||
399 | if (pwr == info->pwr_dev) | ||
400 | return 0; | ||
401 | |||
402 | switch (pwr) { | ||
403 | case NVC_PWR_OFF_FORCE: | ||
404 | case NVC_PWR_OFF: | ||
405 | sh532u_gpio_en(info, 0); | ||
406 | err = sh532u_pm_regulator_dis(info, &info->vreg_vdd); | ||
407 | err |= sh532u_pm_regulator_dis(info, &info->vreg_i2c); | ||
408 | sh532u_gpio_reset(info, 0); | ||
409 | break; | ||
410 | |||
411 | case NVC_PWR_STDBY_OFF: | ||
412 | case NVC_PWR_STDBY: | ||
413 | err = sh532u_pm_regulator_en(info, &info->vreg_vdd); | ||
414 | err |= sh532u_pm_regulator_en(info, &info->vreg_i2c); | ||
415 | sh532u_gpio_en(info, 1); | ||
416 | sh532u_gpio_reset(info, 1); | ||
417 | err |= sh532u_i2c_wr8(info, STBY_211, 0x80); | ||
418 | err |= sh532u_i2c_wr8(info, CLKSEL_211, 0x38); | ||
419 | err |= sh532u_i2c_wr8(info, CLKSEL_211, 0x39); | ||
420 | break; | ||
421 | |||
422 | case NVC_PWR_COMM: | ||
423 | case NVC_PWR_ON: | ||
424 | err = sh532u_pm_regulator_en(info, &info->vreg_vdd); | ||
425 | err |= sh532u_pm_regulator_en(info, &info->vreg_i2c); | ||
426 | sh532u_gpio_en(info, 1); | ||
427 | sh532u_gpio_reset(info, 1); | ||
428 | err |= sh532u_i2c_wr8(info, CLKSEL_211, 0x38); | ||
429 | err |= sh532u_i2c_wr8(info, CLKSEL_211, 0x34); | ||
430 | err |= sh532u_i2c_wr8(info, STBY_211, 0xF0); | ||
431 | break; | ||
432 | |||
433 | default: | ||
434 | err = -EINVAL; | ||
435 | break; | ||
436 | } | ||
437 | |||
438 | if (err < 0) { | ||
439 | dev_err(&info->i2c_client->dev, "%s pwr err: %d\n", | ||
440 | __func__, pwr); | ||
441 | pwr = NVC_PWR_ERR; | ||
442 | } | ||
443 | info->pwr_dev = pwr; | ||
444 | if (err > 0) | ||
445 | return 0; | ||
446 | |||
447 | return err; | ||
448 | } | ||
449 | |||
450 | static int sh532u_pm_wr_s(struct sh532u_info *info, int pwr) | ||
451 | { | ||
452 | int err1 = 0; | ||
453 | int err2 = 0; | ||
454 | |||
455 | if ((info->s_mode == NVC_SYNC_OFF) || | ||
456 | (info->s_mode == NVC_SYNC_MASTER) || | ||
457 | (info->s_mode == NVC_SYNC_STEREO)) | ||
458 | err1 = sh532u_pm_wr(info, pwr); | ||
459 | if ((info->s_mode == NVC_SYNC_SLAVE) || | ||
460 | (info->s_mode == NVC_SYNC_STEREO)) | ||
461 | err2 = sh532u_pm_wr(info->s_info, pwr); | ||
462 | return err1 | err2; | ||
463 | } | ||
464 | |||
465 | static int sh532u_pm_api_wr(struct sh532u_info *info, int pwr) | ||
466 | { | ||
467 | int err = 0; | ||
468 | |||
469 | if (!pwr || (pwr > NVC_PWR_ON)) | ||
470 | return 0; | ||
471 | |||
472 | if (pwr > info->pwr_dev) | ||
473 | err = sh532u_pm_wr_s(info, pwr); | ||
474 | if (!err) | ||
475 | info->pwr_api = pwr; | ||
476 | else | ||
477 | info->pwr_api = NVC_PWR_ERR; | ||
478 | if (info->pdata->cfg & NVC_CFG_NOERR) | ||
479 | return 0; | ||
480 | |||
481 | return err; | ||
482 | } | ||
483 | |||
484 | static int sh532u_pm_dev_wr(struct sh532u_info *info, int pwr) | ||
485 | { | ||
486 | if (pwr < info->pwr_api) | ||
487 | pwr = info->pwr_api; | ||
488 | if (info->sts == NVC_FOCUS_STS_WAIT_FOR_MOVE_END) | ||
489 | pwr = NVC_PWR_ON; | ||
490 | return sh532u_pm_wr(info, pwr); | ||
491 | } | ||
492 | |||
493 | static void sh532u_pm_exit(struct sh532u_info *info) | ||
494 | { | ||
495 | sh532u_pm_wr(info, NVC_PWR_OFF_FORCE); | ||
496 | sh532u_pm_regulator_put(&info->vreg_vdd); | ||
497 | sh532u_pm_regulator_put(&info->vreg_i2c); | ||
498 | if (info->s_info != NULL) { | ||
499 | sh532u_pm_wr(info->s_info, NVC_PWR_OFF_FORCE); | ||
500 | sh532u_pm_regulator_put(&info->s_info->vreg_vdd); | ||
501 | sh532u_pm_regulator_put(&info->s_info->vreg_i2c); | ||
502 | } | ||
503 | } | ||
504 | |||
505 | static void sh532u_pm_init(struct sh532u_info *info) | ||
506 | { | ||
507 | sh532u_pm_regulator_get(info, &info->vreg_vdd, "vdd"); | ||
508 | sh532u_pm_regulator_get(info, &info->vreg_i2c, "vdd_i2c"); | ||
509 | } | ||
510 | |||
511 | static int sh532u_dev_id(struct sh532u_info *info) | ||
512 | { | ||
513 | u8 val; | ||
514 | int err; | ||
515 | |||
516 | err = sh532u_i2c_rd8(info, 0, HVCA_DEVICE_ID, &val); | ||
517 | if (!err && (val == SH532U_ID)) | ||
518 | return 0; | ||
519 | |||
520 | return -ENODEV; | ||
521 | } | ||
522 | |||
523 | static void sh532u_sts_rd(struct sh532u_info *info) | ||
524 | { | ||
525 | u8 us_tmp; | ||
526 | u16 us_smv_fin; | ||
527 | int err; | ||
528 | |||
529 | if (info->sts == NVC_FOCUS_STS_INITIALIZING) | ||
530 | return; | ||
531 | |||
532 | info->sts = NVC_FOCUS_STS_NO_DEVICE; /* assume I2C err */ | ||
533 | err = sh532u_i2c_rd8(info, 0, STMVEN_211, &us_tmp); | ||
534 | err |= sh532u_i2c_rd16(info, RZ_211H, &us_smv_fin); | ||
535 | if (err) | ||
536 | return; | ||
537 | |||
538 | /* StepMove Error Handling, Unexpected Position */ | ||
539 | if ((us_smv_fin == 0x7FFF) || (us_smv_fin == 0x8001)) | ||
540 | /* Stop StepMove Operation */ | ||
541 | sh532u_i2c_wr8(info, STMVEN_211, us_tmp & 0xFE); | ||
542 | if (us_tmp & STMVEN_ON) { | ||
543 | err = sh532u_i2c_rd8(info, 0, MSSET_211, &us_tmp); | ||
544 | if (!err) { | ||
545 | if (us_tmp & CHTGST_ON) | ||
546 | info->sts = NVC_FOCUS_STS_WAIT_FOR_SETTLE; | ||
547 | else | ||
548 | info->sts = NVC_FOCUS_STS_LENS_SETTLED; | ||
549 | } | ||
550 | } else { | ||
551 | info->sts = NVC_FOCUS_STS_WAIT_FOR_MOVE_END; | ||
552 | } | ||
553 | } | ||
554 | |||
555 | static s16 sh532u_rel2abs(struct sh532u_info *info, u32 rel_position) | ||
556 | { | ||
557 | s16 abs_pos; | ||
558 | |||
559 | if (rel_position > info->cap.actuator_range) | ||
560 | rel_position = info->cap.actuator_range; | ||
561 | rel_position = info->cap.actuator_range - rel_position; | ||
562 | if (rel_position) { | ||
563 | rel_position *= info->abs_range; | ||
564 | rel_position /= info->cap.actuator_range; | ||
565 | } | ||
566 | abs_pos = (s16)(info->abs_base + rel_position); | ||
567 | if (abs_pos < info->cfg.limit_low) | ||
568 | abs_pos = info->cfg.limit_low; | ||
569 | if (abs_pos > info->cfg.limit_high) | ||
570 | abs_pos = info->cfg.limit_high; | ||
571 | return abs_pos; | ||
572 | } | ||
573 | |||
574 | static u32 sh532u_abs2rel(struct sh532u_info *info, s16 abs_position) | ||
575 | { | ||
576 | u32 rel_pos; | ||
577 | |||
578 | if (abs_position > info->cfg.limit_high) | ||
579 | abs_position = info->cfg.limit_high; | ||
580 | if (abs_position < info->abs_base) | ||
581 | abs_position = info->abs_base; | ||
582 | rel_pos = (u32)(abs_position - info->abs_base); | ||
583 | rel_pos *= info->cap.actuator_range; | ||
584 | rel_pos /= info->abs_range; | ||
585 | if (rel_pos > info->cap.actuator_range) | ||
586 | rel_pos = info->cap.actuator_range; | ||
587 | rel_pos = info->cap.actuator_range - rel_pos; | ||
588 | return rel_pos; | ||
589 | } | ||
590 | |||
591 | static int sh532u_abs_pos_rd(struct sh532u_info *info, s16 *position) | ||
592 | { | ||
593 | int err; | ||
594 | u16 abs_pos = 0; | ||
595 | |||
596 | err = sh532u_i2c_rd16(info, RZ_211H, &abs_pos); | ||
597 | *position = (s16)abs_pos; | ||
598 | return err; | ||
599 | } | ||
600 | |||
601 | static int sh532u_rel_pos_rd(struct sh532u_info *info, u32 *position) | ||
602 | { | ||
603 | s16 abs_pos; | ||
604 | long msec; | ||
605 | int pos; | ||
606 | int err; | ||
607 | |||
608 | err = sh532u_abs_pos_rd(info, &abs_pos); | ||
609 | if (err) | ||
610 | return -EINVAL; | ||
611 | |||
612 | if ((abs_pos >= (info->pos_abs - STMV_SIZE)) && | ||
613 | (abs_pos <= (info->pos_abs + STMV_SIZE))) { | ||
614 | pos = (int)info->pos_rel; | ||
615 | } else { | ||
616 | msec = jiffies; | ||
617 | msec -= info->pos_time_wr; | ||
618 | msec = msec * 1000 / HZ; | ||
619 | sh532u_sts_rd(info); | ||
620 | if ((info->sts == NVC_FOCUS_STS_LENS_SETTLED) || | ||
621 | (msec > info->cfg.move_timeoutms)) { | ||
622 | pos = (int)info->pos_rel; | ||
623 | } else { | ||
624 | pos = (int)sh532u_abs2rel(info, abs_pos); | ||
625 | if ((pos == (info->pos_rel - 1)) || | ||
626 | (pos == (info->pos_rel + 1))) | ||
627 | pos = (int)info->pos_rel; | ||
628 | } | ||
629 | } | ||
630 | if (pos < 0) | ||
631 | pos = 0; | ||
632 | *position = (u32)pos; | ||
633 | return 0; | ||
634 | } | ||
635 | |||
636 | static int sh532u_calibration(struct sh532u_info *info, bool use_defaults) | ||
637 | { | ||
638 | u8 reg; | ||
639 | s16 abs_top; | ||
640 | u32 rel_range; | ||
641 | u32 rel_lo; | ||
642 | u32 rel_hi; | ||
643 | u32 step; | ||
644 | u32 loop_limit; | ||
645 | u32 i; | ||
646 | int err; | ||
647 | int ret = 0; | ||
648 | |||
649 | if (info->init_cal_flag) | ||
650 | return 0; | ||
651 | |||
652 | /* set defaults */ | ||
653 | memcpy(&info->cfg, &sh532u_default_info, sizeof(info->cfg)); | ||
654 | memcpy(&info->nvc, &sh532u_default_nvc, sizeof(info->nvc)); | ||
655 | memcpy(&info->cap, &sh532u_default_cap, sizeof(info->cap)); | ||
656 | if (info->pdata->i2c_addr_rom) | ||
657 | info->i2c_addr_rom = info->pdata->i2c_addr_rom; | ||
658 | else | ||
659 | info->i2c_addr_rom = sh532u_default_pdata.i2c_addr_rom; | ||
660 | /* set overrides if any */ | ||
661 | if (info->pdata->nvc) { | ||
662 | if (info->pdata->nvc->fnumber) | ||
663 | info->nvc.fnumber = info->pdata->nvc->fnumber; | ||
664 | if (info->pdata->nvc->focal_length) | ||
665 | info->nvc.focal_length = | ||
666 | info->pdata->nvc->focal_length; | ||
667 | if (info->pdata->nvc->max_aperature) | ||
668 | info->nvc.max_aperature = | ||
669 | info->pdata->nvc->max_aperature; | ||
670 | } | ||
671 | if (info->pdata->cap) { | ||
672 | if (info->pdata->cap->actuator_range) | ||
673 | info->cap.actuator_range = | ||
674 | info->pdata->cap->actuator_range; | ||
675 | if (info->pdata->cap->settle_time) | ||
676 | info->cap.settle_time = info->pdata->cap->settle_time; | ||
677 | if (info->pdata->cap->focus_macro) | ||
678 | info->cap.focus_macro = info->pdata->cap->focus_macro; | ||
679 | if (info->pdata->cap->focus_hyper) | ||
680 | info->cap.focus_hyper = info->pdata->cap->focus_hyper; | ||
681 | if (info->pdata->cap->focus_infinity) | ||
682 | info->cap.focus_infinity = | ||
683 | info->pdata->cap->focus_infinity; | ||
684 | } | ||
685 | /* | ||
686 | * Get Inf1, Mac1 | ||
687 | * Inf1 and Mac1 are the mechanical limit position. | ||
688 | * Inf1: top limit. | ||
689 | * Mac1: bottom limit. | ||
690 | */ | ||
691 | err = sh532u_i2c_rd8(info, info->i2c_addr_rom, addrMac1, ®); | ||
692 | if (!err && (reg != 0) && (reg != 0xFF)) | ||
693 | info->cfg.limit_low = (reg<<8) & 0xff00; | ||
694 | ret = err; | ||
695 | err = sh532u_i2c_rd8(info, info->i2c_addr_rom, addrInf1, ®); | ||
696 | if (!err && (reg != 0) && (reg != 0xFF)) | ||
697 | info->cfg.limit_high = (reg<<8) & 0xff00; | ||
698 | ret |= err; | ||
699 | /* | ||
700 | * Get Inf2, Mac2 | ||
701 | * Inf2 and Mac2 are the calibration data for SEMCO AF lens. | ||
702 | * Inf2: Best focus (lens position) when object distance is 1.2M. | ||
703 | * Mac2: Best focus (lens position) when object distance is 10cm. | ||
704 | */ | ||
705 | err = sh532u_i2c_rd8(info, info->i2c_addr_rom, addrMac2, ®); | ||
706 | if (!err && (reg != 0) && (reg != 0xFF)) | ||
707 | info->cfg.pos_low = (reg << 8) & 0xff00; | ||
708 | ret |= err; | ||
709 | err = sh532u_i2c_rd8(info, info->i2c_addr_rom, addrInf2, ®); | ||
710 | if (!err && (reg != 0) && (reg != 0xFF)) | ||
711 | info->cfg.pos_high = (reg << 8) & 0xff00; | ||
712 | ret |= err; | ||
713 | /* set overrides */ | ||
714 | if (info->pdata->info) { | ||
715 | if (info->pdata->info->pos_low) | ||
716 | info->cfg.pos_low = info->pdata->info->pos_low; | ||
717 | if (info->pdata->info->pos_high) | ||
718 | info->cfg.pos_high = info->pdata->info->pos_high; | ||
719 | if (info->pdata->info->limit_low) | ||
720 | info->cfg.limit_low = info->pdata->info->limit_low; | ||
721 | if (info->pdata->info->limit_high) | ||
722 | info->cfg.limit_high = info->pdata->info->limit_high; | ||
723 | if (info->pdata->info->move_timeoutms) | ||
724 | info->cfg.move_timeoutms = | ||
725 | info->pdata->info->move_timeoutms; | ||
726 | if (info->pdata->info->focus_hyper_ratio) | ||
727 | info->cfg.focus_hyper_ratio = | ||
728 | info->pdata->info->focus_hyper_ratio; | ||
729 | if (info->pdata->info->focus_hyper_div) | ||
730 | info->cfg.focus_hyper_div = | ||
731 | info->pdata->info->focus_hyper_div; | ||
732 | } | ||
733 | /* | ||
734 | * There is known to be many sh532u devices with no EPROM data. | ||
735 | * Using default data is known to reduce the sh532u performance since | ||
736 | * the defaults may no where be close to the correct values that | ||
737 | * should be used. However, we don't want to prevent the camera from | ||
738 | * starting due to the lack of the EPROM data. | ||
739 | * The following truth table shows the action to take at this point: | ||
740 | * DFLT = the use_defaults flag (used after multiple attempts) | ||
741 | * I2C = the I2C transactions to get the data. | ||
742 | * DATA = the needed data either from the EPROM or board file. | ||
743 | * DFLT I2C DATA Action | ||
744 | * -------------------------- | ||
745 | * 0 FAIL FAIL Exit with -EIO | ||
746 | * 0 FAIL PASS Continue to calculations | ||
747 | * 0 PASS FAIL Use defaults | ||
748 | * 0 PASS PASS Continue to calculations | ||
749 | * 1 FAIL FAIL Use defaults | ||
750 | * 1 FAIL PASS Continue to calculations | ||
751 | * 1 PASS FAIL Use defaults | ||
752 | * 1 PASS PASS Continue to calculations | ||
753 | */ | ||
754 | /* err = DATA where FAIL = 1 */ | ||
755 | if (!info->cfg.pos_low || !info->cfg.pos_high || | ||
756 | !info->cfg.limit_low || !info->cfg.limit_high) | ||
757 | err = 1; | ||
758 | else | ||
759 | err = 0; | ||
760 | /* Exit with -EIO */ | ||
761 | if (!use_defaults && ret && err) { | ||
762 | dev_err(&info->i2c_client->dev, "%s ERR\n", __func__); | ||
763 | return -EIO; | ||
764 | } | ||
765 | |||
766 | /* Use defaults */ | ||
767 | if (err) { | ||
768 | info->cfg.pos_low = SH532U_POS_LOW_DEFAULT; | ||
769 | info->cfg.pos_high = SH532U_POS_HIGH_DEFAULT; | ||
770 | info->cfg.limit_low = SH532U_POS_LOW_DEFAULT; | ||
771 | info->cfg.limit_high = SH532U_POS_HIGH_DEFAULT; | ||
772 | dev_err(&info->i2c_client->dev, "%s ERR: ERPOM data is void! " | ||
773 | "Focuser will use defaults that will cause " | ||
774 | "reduced functionality!\n", __func__); | ||
775 | } | ||
776 | if (info->cfg.pos_low < info->cfg.limit_low) | ||
777 | info->cfg.pos_low = info->cfg.limit_low; | ||
778 | if (info->cfg.pos_high > info->cfg.limit_high) | ||
779 | info->cfg.pos_high = info->cfg.limit_high; | ||
780 | dev_dbg(&info->i2c_client->dev, "%s pos_low=%d\n", __func__, | ||
781 | (int)info->cfg.pos_low); | ||
782 | dev_dbg(&info->i2c_client->dev, "%s pos_high=%d\n", __func__, | ||
783 | (int)info->cfg.pos_high); | ||
784 | dev_dbg(&info->i2c_client->dev, "%s limit_low=%d\n", __func__, | ||
785 | (int)info->cfg.limit_low); | ||
786 | dev_dbg(&info->i2c_client->dev, "%s limit_high=%d\n", __func__, | ||
787 | (int)info->cfg.limit_high); | ||
788 | /* | ||
789 | * calculate relative and absolute positions | ||
790 | * Note that relative values, what upper SW uses, are the | ||
791 | * abstraction of HW (absolute) values. | ||
792 | * |<--limit_low limit_high-->| | ||
793 | * | |<-------------------_ACTUATOR_RANGE------------------->| | | ||
794 | * -focus_inf -focus_mac | ||
795 | * |<---RI--->| |<---RM--->| | ||
796 | * -abs_base -pos_low -pos_high -abs_top | ||
797 | * | ||
798 | * The pos_low and pos_high are fixed absolute positions and correspond | ||
799 | * to the relative focus_infinity and focus_macro, respectively. We'd | ||
800 | * like to have "wiggle" room (RI and RM) around these relative | ||
801 | * positions so the loop below finds the best fit for RI and RM without | ||
802 | * passing the absolute limits. | ||
803 | * We want our _ACTUATOR_RANGE to be infinity on the 0 end and macro | ||
804 | * on the max end. However, the focuser HW is opposite this. | ||
805 | * Therefore we use the rel(ative)_lo/hi variables in the calculation | ||
806 | * loop and assign them the focus_infinity and focus_macro values. | ||
807 | */ | ||
808 | rel_lo = (info->cap.actuator_range - info->cap.focus_macro); | ||
809 | rel_hi = info->cap.focus_infinity; | ||
810 | info->abs_range = (u32)(info->cfg.pos_high - info->cfg.pos_low); | ||
811 | loop_limit = (rel_lo > rel_hi) ? rel_lo : rel_hi; | ||
812 | for (i = 0; i <= loop_limit; i++) { | ||
813 | rel_range = info->cap.actuator_range - (rel_lo + rel_hi); | ||
814 | step = info->abs_range / rel_range; | ||
815 | info->abs_base = info->cfg.pos_low - (step * rel_lo); | ||
816 | abs_top = info->cfg.pos_high + (step * rel_hi); | ||
817 | if (info->abs_base < info->cfg.limit_low) { | ||
818 | if (rel_lo > 0) | ||
819 | rel_lo--; | ||
820 | } | ||
821 | if (abs_top > info->cfg.limit_high) { | ||
822 | if (rel_hi > 0) | ||
823 | rel_hi--; | ||
824 | } | ||
825 | if (info->abs_base >= info->cfg.limit_low && | ||
826 | abs_top <= info->cfg.limit_high) | ||
827 | break; | ||
828 | } | ||
829 | info->cap.focus_hyper = info->abs_range; | ||
830 | info->abs_range = (u32)(abs_top - info->abs_base); | ||
831 | /* calculate absolute hyperfocus position */ | ||
832 | info->cap.focus_hyper *= info->cfg.focus_hyper_ratio; | ||
833 | info->cap.focus_hyper /= info->cfg.focus_hyper_div; | ||
834 | abs_top = (s16)(info->cfg.pos_high - info->cap.focus_hyper); | ||
835 | /* update actual relative positions */ | ||
836 | info->cap.focus_hyper = sh532u_abs2rel(info, abs_top); | ||
837 | info->cap.focus_infinity = sh532u_abs2rel(info, info->cfg.pos_high); | ||
838 | info->cap.focus_macro = sh532u_abs2rel(info, info->cfg.pos_low); | ||
839 | dev_dbg(&info->i2c_client->dev, "%s focus_macro=%u\n", __func__, | ||
840 | info->cap.focus_macro); | ||
841 | dev_dbg(&info->i2c_client->dev, "%s focus_infinity=%u\n", __func__, | ||
842 | info->cap.focus_infinity); | ||
843 | dev_dbg(&info->i2c_client->dev, "%s focus_hyper=%u\n", __func__, | ||
844 | info->cap.focus_hyper); | ||
845 | info->init_cal_flag = 1; | ||
846 | dev_dbg(&info->i2c_client->dev, "%s complete\n", __func__); | ||
847 | return 0; | ||
848 | } | ||
849 | |||
850 | /* Write 1 byte data to the HVCA Drive IC by data type */ | ||
851 | static int sh532u_hvca_wr1(struct sh532u_info *info, | ||
852 | u8 ep_type, u8 ep_data1, u8 ep_addr) | ||
853 | { | ||
854 | u8 us_data; | ||
855 | int err = 0; | ||
856 | |||
857 | switch (ep_type & 0xF0) { | ||
858 | case DIRECT_MODE: | ||
859 | us_data = ep_data1; | ||
860 | break; | ||
861 | |||
862 | case INDIRECT_EEPROM: | ||
863 | err = sh532u_i2c_rd8(info, | ||
864 | info->i2c_addr_rom, | ||
865 | ep_data1, | ||
866 | &us_data); | ||
867 | break; | ||
868 | |||
869 | case INDIRECT_HVCA: | ||
870 | err = sh532u_i2c_rd8(info, 0, ep_data1, &us_data); | ||
871 | break; | ||
872 | |||
873 | case MASK_AND: | ||
874 | err = sh532u_i2c_rd8(info, 0, ep_addr, &us_data); | ||
875 | us_data &= ep_data1; | ||
876 | break; | ||
877 | |||
878 | case MASK_OR: | ||
879 | err = sh532u_i2c_rd8(info, 0, ep_addr, &us_data); | ||
880 | us_data |= ep_data1; | ||
881 | break; | ||
882 | |||
883 | default: | ||
884 | err = -EINVAL; | ||
885 | } | ||
886 | if (!err) | ||
887 | err = sh532u_i2c_wr8(info, ep_addr, us_data); | ||
888 | return err; | ||
889 | } | ||
890 | |||
891 | /* Write 2 byte data to the HVCA Drive IC by data type */ | ||
892 | static int sh532u_hvca_wr2(struct sh532u_info *info, u8 ep_type, | ||
893 | u8 ep_data1, u8 ep_data2, u8 ep_addr) | ||
894 | { | ||
895 | u8 uc_data1; | ||
896 | u8 uc_data2; | ||
897 | u16 us_data; | ||
898 | int err = 0; | ||
899 | |||
900 | switch (ep_type & 0xF0) { | ||
901 | case DIRECT_MODE: | ||
902 | us_data = (((u16)ep_data1 << 8) & 0xFF00) | | ||
903 | ((u16)ep_data2 & 0x00FF); | ||
904 | break; | ||
905 | |||
906 | case INDIRECT_EEPROM: | ||
907 | err = sh532u_i2c_rd8(info, | ||
908 | info->i2c_addr_rom, | ||
909 | ep_data1, | ||
910 | &uc_data1); | ||
911 | err |= sh532u_i2c_rd8(info, | ||
912 | info->i2c_addr_rom, | ||
913 | ep_data2, | ||
914 | &uc_data2); | ||
915 | us_data = (((u16)uc_data1 << 8) & 0xFF00) | | ||
916 | ((u16)uc_data2 & 0x00FF); | ||
917 | break; | ||
918 | |||
919 | case INDIRECT_HVCA: | ||
920 | err = sh532u_i2c_rd8(info, 0, ep_data1, &uc_data1); | ||
921 | err |= sh532u_i2c_rd8(info, 0, ep_data2, &uc_data2); | ||
922 | us_data = (((u16)uc_data1 << 8) & 0xFF00) | | ||
923 | ((u16)uc_data2 & 0x00FF); | ||
924 | break; | ||
925 | |||
926 | case MASK_AND: | ||
927 | err = sh532u_i2c_rd16(info, ep_addr, &us_data); | ||
928 | us_data &= ((((u16)ep_data1 << 8) & 0xFF00) | | ||
929 | ((u16)ep_data2 & 0x00FF)); | ||
930 | break; | ||
931 | |||
932 | case MASK_OR: | ||
933 | err = sh532u_i2c_rd16(info, ep_addr, &us_data); | ||
934 | us_data |= ((((u16)ep_data1 << 8) & 0xFF00) | | ||
935 | ((u16)ep_data2 & 0x00FF)); | ||
936 | break; | ||
937 | |||
938 | default: | ||
939 | err = -EINVAL; | ||
940 | } | ||
941 | if (!err) | ||
942 | err = sh532u_i2c_wr16(info, ep_addr, us_data); | ||
943 | return err; | ||
944 | } | ||
945 | |||
946 | static int sh532u_dev_init(struct sh532u_info *info) | ||
947 | { | ||
948 | int eeprom_reg; | ||
949 | unsigned eeprom_data = 0; | ||
950 | u8 ep_addr; | ||
951 | u8 ep_type; | ||
952 | u8 ep_data1; | ||
953 | u8 ep_data2; | ||
954 | int err; | ||
955 | int ret = 0; | ||
956 | |||
957 | err = sh532u_i2c_rd8(info, 0, SWTCH_211, &ep_data1); | ||
958 | ep_data2 = ep_data1; | ||
959 | err |= sh532u_i2c_rd8(info, 0, ANA1_211, &ep_data1); | ||
960 | ep_data2 |= ep_data1; | ||
961 | if (!err && ep_data2) | ||
962 | return 0; /* Already initialized */ | ||
963 | |||
964 | info->sts = NVC_FOCUS_STS_INITIALIZING; | ||
965 | for (eeprom_reg = 0x30; eeprom_reg <= 0x013C; eeprom_reg += 4) { | ||
966 | if (eeprom_reg > 0xFF) { | ||
967 | /* use hardcoded data instead */ | ||
968 | eeprom_data = sh532u_a2buf[(eeprom_reg & 0xFF) / 4]; | ||
969 | } else { | ||
970 | err = (sh532u_i2c_rd32(info, | ||
971 | info->i2c_addr_rom, | ||
972 | eeprom_reg & 0xFF, | ||
973 | &eeprom_data)); | ||
974 | if (err) { | ||
975 | ret |= err; | ||
976 | continue; | ||
977 | } | ||
978 | } | ||
979 | |||
980 | /* HVCA Address to write eeprom Data1,Data2 by the Data type */ | ||
981 | ep_addr = (u8)(eeprom_data & 0x000000ff); | ||
982 | ep_type = (u8)((eeprom_data & 0x0000ff00) >> 8); | ||
983 | ep_data1 = (u8)((eeprom_data & 0x00ff0000) >> 16); | ||
984 | ep_data2 = (u8)((eeprom_data & 0xff000000) >> 24); | ||
985 | if (ep_addr == 0xFF) | ||
986 | break; | ||
987 | |||
988 | if (ep_addr == 0xDD) { | ||
989 | mdelay((unsigned int)((ep_data1 << 8) | ep_data2)); | ||
990 | } else { | ||
991 | if ((ep_type & 0x0F) == DATA_1BYTE) { | ||
992 | err = sh532u_hvca_wr1(info, | ||
993 | ep_type, | ||
994 | ep_data1, | ||
995 | ep_addr); | ||
996 | } else { | ||
997 | err = sh532u_hvca_wr2(info, | ||
998 | ep_type, | ||
999 | ep_data1, | ||
1000 | ep_data2, | ||
1001 | ep_addr); | ||
1002 | } | ||
1003 | } | ||
1004 | ret |= err; | ||
1005 | } | ||
1006 | |||
1007 | err = ret; | ||
1008 | if (err) | ||
1009 | dev_err(&info->i2c_client->dev, "%s programming err=%d\n", | ||
1010 | __func__, err); | ||
1011 | err |= sh532u_calibration(info, false); | ||
1012 | info->sts = NVC_FOCUS_STS_LENS_SETTLED; | ||
1013 | return err; | ||
1014 | } | ||
1015 | |||
1016 | static int sh532u_pos_abs_wr(struct sh532u_info *info, s16 tar_pos) | ||
1017 | { | ||
1018 | s16 cur_pos; | ||
1019 | s16 move_step; | ||
1020 | u16 move_distance; | ||
1021 | int err; | ||
1022 | |||
1023 | sh532u_pm_dev_wr(info, NVC_PWR_ON); | ||
1024 | err = sh532u_dev_init(info); | ||
1025 | if (err) | ||
1026 | return err; | ||
1027 | |||
1028 | /* Read Current Position */ | ||
1029 | err = sh532u_abs_pos_rd(info, &cur_pos); | ||
1030 | if (err) | ||
1031 | return err; | ||
1032 | |||
1033 | dev_dbg(&info->i2c_client->dev, "%s cur_pos=%d tar_pos=%d\n", | ||
1034 | __func__, (int)cur_pos, (int)tar_pos); | ||
1035 | info->sts = NVC_FOCUS_STS_WAIT_FOR_MOVE_END; | ||
1036 | /* Check move distance to Target Position */ | ||
1037 | move_distance = abs((int)cur_pos - (int)tar_pos); | ||
1038 | /* if move distance is shorter than MS1Z12(=Step width) */ | ||
1039 | if (move_distance <= STMV_SIZE) { | ||
1040 | err = sh532u_i2c_wr8(info, MSSET_211, | ||
1041 | (INI_MSSET_211 | 0x01)); | ||
1042 | err |= sh532u_i2c_wr16(info, MS1Z22_211H, tar_pos); | ||
1043 | } else { | ||
1044 | if (cur_pos < tar_pos) | ||
1045 | move_step = STMV_SIZE; | ||
1046 | else | ||
1047 | move_step = -STMV_SIZE; | ||
1048 | /* Set StepMove Target Positon */ | ||
1049 | err = sh532u_i2c_wr16(info, MS1Z12_211H, move_step); | ||
1050 | err |= sh532u_i2c_wr16(info, STMVENDH_211, tar_pos); | ||
1051 | /* Start StepMove */ | ||
1052 | err |= sh532u_i2c_wr8(info, STMVEN_211, | ||
1053 | (STMCHTG_ON | | ||
1054 | STMSV_ON | | ||
1055 | STMLFF_OFF | | ||
1056 | STMVEN_ON)); | ||
1057 | } | ||
1058 | return err; | ||
1059 | } | ||
1060 | |||
1061 | static int sh532u_move_wait(struct sh532u_info *info) | ||
1062 | { | ||
1063 | u16 us_smv_fin; | ||
1064 | u8 moveTime; | ||
1065 | u8 ucParMod; | ||
1066 | u8 tmp; | ||
1067 | int err; | ||
1068 | |||
1069 | moveTime = 0; | ||
1070 | do { | ||
1071 | mdelay(1); | ||
1072 | err = sh532u_i2c_rd8(info, 0, STMVEN_211, &ucParMod); | ||
1073 | err |= sh532u_i2c_rd16(info, RZ_211H, &us_smv_fin); | ||
1074 | if (err) | ||
1075 | return err; | ||
1076 | |||
1077 | /* StepMove Error Handling, Unexpected Position */ | ||
1078 | if ((us_smv_fin == 0x7FFF) || (us_smv_fin == 0x8001)) { | ||
1079 | /* Stop StepMove Operation */ | ||
1080 | err = sh532u_i2c_wr8(info, STMVEN_211, | ||
1081 | ucParMod & 0xFE); | ||
1082 | if (err) | ||
1083 | return err; | ||
1084 | } | ||
1085 | |||
1086 | moveTime++; | ||
1087 | /* Wait StepMove operation end */ | ||
1088 | } while ((ucParMod & STMVEN_ON) && (moveTime < 50)); | ||
1089 | |||
1090 | moveTime = 0; | ||
1091 | if ((ucParMod & 0x08) == STMCHTG_ON) { | ||
1092 | mdelay(5); | ||
1093 | do { | ||
1094 | mdelay(1); | ||
1095 | moveTime++; | ||
1096 | err = sh532u_i2c_rd8(info, 0, MSSET_211, &tmp); | ||
1097 | if (err) | ||
1098 | return err; | ||
1099 | |||
1100 | } while ((tmp & CHTGST_ON) && (moveTime < 15)); | ||
1101 | } | ||
1102 | return err; | ||
1103 | } | ||
1104 | |||
1105 | static int sh532u_move_pulse(struct sh532u_info *info, s16 position) | ||
1106 | { | ||
1107 | int err; | ||
1108 | |||
1109 | err = sh532u_pos_abs_wr(info, position); | ||
1110 | err |= sh532u_move_wait(info); | ||
1111 | return err; | ||
1112 | } | ||
1113 | |||
1114 | static int sh532u_hvca_pos_init(struct sh532u_info *info) | ||
1115 | { | ||
1116 | s16 limit_bottom; | ||
1117 | s16 limit_top; | ||
1118 | int err; | ||
1119 | |||
1120 | limit_bottom = (((int)info->cfg.limit_low * 5) >> 3) & 0xFFC0; | ||
1121 | if (limit_bottom < info->cfg.limit_low) | ||
1122 | limit_bottom = info->cfg.limit_low; | ||
1123 | limit_top = (((int)info->cfg.limit_high * 5) >> 3) & 0xFFC0; | ||
1124 | if (limit_top > info->cfg.limit_high) | ||
1125 | limit_top = info->cfg.limit_high; | ||
1126 | err = sh532u_move_pulse(info, limit_bottom); | ||
1127 | err |= sh532u_move_pulse(info, limit_top); | ||
1128 | err |= sh532u_move_pulse(info, info->cfg.pos_high); | ||
1129 | return err; | ||
1130 | } | ||
1131 | |||
1132 | static int sh532u_pos_rel_wr(struct sh532u_info *info, u32 position) | ||
1133 | { | ||
1134 | s16 abs_pos; | ||
1135 | |||
1136 | if (position > info->cap.actuator_range) { | ||
1137 | dev_err(&info->i2c_client->dev, "%s invalid position %u\n", | ||
1138 | __func__, position); | ||
1139 | return -EINVAL; | ||
1140 | } | ||
1141 | |||
1142 | abs_pos = sh532u_rel2abs(info, position); | ||
1143 | info->pos_rel = position; | ||
1144 | info->pos_abs = abs_pos; | ||
1145 | info->pos_time_wr = jiffies; | ||
1146 | return sh532u_pos_abs_wr(info, abs_pos); | ||
1147 | } | ||
1148 | |||
1149 | |||
1150 | static int sh532u_param_rd(struct sh532u_info *info, unsigned long arg) | ||
1151 | { | ||
1152 | struct nvc_param params; | ||
1153 | const void *data_ptr; | ||
1154 | u32 data_size = 0; | ||
1155 | u32 position; | ||
1156 | int err; | ||
1157 | |||
1158 | if (copy_from_user(¶ms, | ||
1159 | (const void __user *)arg, | ||
1160 | sizeof(struct nvc_param))) { | ||
1161 | dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", | ||
1162 | __func__, __LINE__); | ||
1163 | return -EFAULT; | ||
1164 | } | ||
1165 | |||
1166 | if (info->s_mode == NVC_SYNC_SLAVE) | ||
1167 | info = info->s_info; | ||
1168 | switch (params.param) { | ||
1169 | case NVC_PARAM_LOCUS: | ||
1170 | sh532u_pm_dev_wr(info, NVC_PWR_COMM); | ||
1171 | err = sh532u_rel_pos_rd(info, &position); | ||
1172 | if (err && !(info->pdata->cfg & NVC_CFG_NOERR)) | ||
1173 | return -EINVAL; | ||
1174 | |||
1175 | data_ptr = &position; | ||
1176 | data_size = sizeof(position); | ||
1177 | sh532u_pm_dev_wr(info, NVC_PWR_STDBY); | ||
1178 | dev_dbg(&info->i2c_client->dev, "%s LOCUS: %d\n", | ||
1179 | __func__, position); | ||
1180 | break; | ||
1181 | |||
1182 | case NVC_PARAM_FOCAL_LEN: | ||
1183 | data_ptr = &info->nvc.focal_length; | ||
1184 | data_size = sizeof(info->nvc.focal_length); | ||
1185 | dev_dbg(&info->i2c_client->dev, "%s FOCAL_LEN: %x\n", | ||
1186 | __func__, info->nvc.focal_length); | ||
1187 | break; | ||
1188 | |||
1189 | case NVC_PARAM_MAX_APERTURE: | ||
1190 | data_ptr = &info->nvc.max_aperature; | ||
1191 | data_size = sizeof(info->nvc.max_aperature); | ||
1192 | dev_dbg(&info->i2c_client->dev, "%s MAX_APERTURE: %x\n", | ||
1193 | __func__, info->nvc.max_aperature); | ||
1194 | break; | ||
1195 | |||
1196 | case NVC_PARAM_FNUMBER: | ||
1197 | data_ptr = &info->nvc.fnumber; | ||
1198 | data_size = sizeof(info->nvc.fnumber); | ||
1199 | dev_dbg(&info->i2c_client->dev, "%s FNUMBER: %x\n", | ||
1200 | __func__, info->nvc.fnumber); | ||
1201 | break; | ||
1202 | |||
1203 | case NVC_PARAM_CAPS: | ||
1204 | sh532u_pm_dev_wr(info, NVC_PWR_COMM); | ||
1205 | err = sh532u_calibration(info, true); | ||
1206 | sh532u_pm_dev_wr(info, NVC_PWR_STDBY); | ||
1207 | if (err) | ||
1208 | return -EIO; | ||
1209 | |||
1210 | data_ptr = &info->cap; | ||
1211 | /* there are different sizes depending on the version */ | ||
1212 | /* send back just what's requested or our max size */ | ||
1213 | if (params.sizeofvalue < sizeof(info->cap)) | ||
1214 | data_size = params.sizeofvalue; | ||
1215 | else | ||
1216 | data_size = sizeof(info->cap); | ||
1217 | dev_dbg(&info->i2c_client->dev, "%s CAPS\n", | ||
1218 | __func__); | ||
1219 | break; | ||
1220 | |||
1221 | case NVC_PARAM_STS: | ||
1222 | data_ptr = &info->sts; | ||
1223 | data_size = sizeof(info->sts); | ||
1224 | dev_dbg(&info->i2c_client->dev, "%s STS: %d\n", | ||
1225 | __func__, info->sts); | ||
1226 | break; | ||
1227 | |||
1228 | case NVC_PARAM_STEREO: | ||
1229 | data_ptr = &info->s_mode; | ||
1230 | data_size = sizeof(info->s_mode); | ||
1231 | dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n", | ||
1232 | __func__, info->s_mode); | ||
1233 | break; | ||
1234 | |||
1235 | default: | ||
1236 | dev_err(&info->i2c_client->dev, | ||
1237 | "%s unsupported parameter: %d\n", | ||
1238 | __func__, params.param); | ||
1239 | return -EINVAL; | ||
1240 | } | ||
1241 | |||
1242 | if (params.sizeofvalue < data_size) { | ||
1243 | dev_err(&info->i2c_client->dev, "%s %d data size err\n", | ||
1244 | __func__, __LINE__); | ||
1245 | return -EINVAL; | ||
1246 | } | ||
1247 | |||
1248 | if (copy_to_user((void __user *)params.p_value, | ||
1249 | data_ptr, | ||
1250 | data_size)) { | ||
1251 | dev_err(&info->i2c_client->dev, "%s %d copy_to_user err\n", | ||
1252 | __func__, __LINE__); | ||
1253 | return -EFAULT; | ||
1254 | } | ||
1255 | |||
1256 | return 0; | ||
1257 | } | ||
1258 | |||
1259 | static int sh532u_param_wr_s(struct sh532u_info *info, | ||
1260 | struct nvc_param *params, | ||
1261 | u32 u32_val) | ||
1262 | { | ||
1263 | int err; | ||
1264 | |||
1265 | switch (params->param) { | ||
1266 | case NVC_PARAM_LOCUS: | ||
1267 | dev_dbg(&info->i2c_client->dev, "%s LOCUS: %u\n", | ||
1268 | __func__, u32_val); | ||
1269 | err = sh532u_pos_rel_wr(info, u32_val); | ||
1270 | return err; | ||
1271 | |||
1272 | case NVC_PARAM_RESET: | ||
1273 | err = sh532u_pm_wr(info, NVC_PWR_OFF); | ||
1274 | err |= sh532u_pm_wr(info, NVC_PWR_ON); | ||
1275 | err |= sh532u_pm_wr(info, info->pwr_api); | ||
1276 | dev_dbg(&info->i2c_client->dev, "%s RESET: %d\n", | ||
1277 | __func__, err); | ||
1278 | return err; | ||
1279 | |||
1280 | case NVC_PARAM_SELF_TEST: | ||
1281 | err = sh532u_hvca_pos_init(info); | ||
1282 | dev_dbg(&info->i2c_client->dev, "%s SELF_TEST: %d\n", | ||
1283 | __func__, err); | ||
1284 | return err; | ||
1285 | |||
1286 | default: | ||
1287 | dev_err(&info->i2c_client->dev, | ||
1288 | "%s unsupported parameter: %d\n", | ||
1289 | __func__, params->param); | ||
1290 | return -EINVAL; | ||
1291 | } | ||
1292 | } | ||
1293 | |||
1294 | static int sh532u_param_wr(struct sh532u_info *info, unsigned long arg) | ||
1295 | { | ||
1296 | struct nvc_param params; | ||
1297 | u8 val; | ||
1298 | u32 u32_val; | ||
1299 | int err = 0; | ||
1300 | |||
1301 | if (copy_from_user(¶ms, | ||
1302 | (const void __user *)arg, | ||
1303 | sizeof(struct nvc_param))) { | ||
1304 | dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", | ||
1305 | __func__, __LINE__); | ||
1306 | return -EFAULT; | ||
1307 | } | ||
1308 | |||
1309 | if (copy_from_user(&u32_val, (const void __user *)params.p_value, | ||
1310 | sizeof(u32_val))) { | ||
1311 | dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", | ||
1312 | __func__, __LINE__); | ||
1313 | return -EFAULT; | ||
1314 | } | ||
1315 | |||
1316 | /* parameters independent of sync mode */ | ||
1317 | switch (params.param) { | ||
1318 | case NVC_PARAM_STEREO: | ||
1319 | dev_dbg(&info->i2c_client->dev, "%s STEREO: %u\n", | ||
1320 | __func__, u32_val); | ||
1321 | val = (u8)u32_val; | ||
1322 | if (val == info->s_mode) | ||
1323 | return 0; | ||
1324 | |||
1325 | switch (val) { | ||
1326 | case NVC_SYNC_OFF: | ||
1327 | info->s_mode = val; | ||
1328 | if (info->s_info != NULL) { | ||
1329 | info->s_info->s_mode = val; | ||
1330 | sh532u_pm_wr(info->s_info, NVC_PWR_OFF); | ||
1331 | } | ||
1332 | break; | ||
1333 | |||
1334 | case NVC_SYNC_MASTER: | ||
1335 | info->s_mode = val; | ||
1336 | if (info->s_info != NULL) | ||
1337 | info->s_info->s_mode = val; | ||
1338 | break; | ||
1339 | |||
1340 | case NVC_SYNC_SLAVE: | ||
1341 | if (info->s_info != NULL) { | ||
1342 | /* default slave lens position */ | ||
1343 | err = sh532u_pos_rel_wr(info->s_info, | ||
1344 | info->s_info->cap.focus_infinity); | ||
1345 | if (!err) { | ||
1346 | info->s_mode = val; | ||
1347 | info->s_info->s_mode = val; | ||
1348 | } else { | ||
1349 | if (info->s_mode != NVC_SYNC_STEREO) | ||
1350 | sh532u_pm_wr(info->s_info, | ||
1351 | NVC_PWR_OFF); | ||
1352 | err = -EIO; | ||
1353 | } | ||
1354 | } else { | ||
1355 | err = -EINVAL; | ||
1356 | } | ||
1357 | break; | ||
1358 | |||
1359 | case NVC_SYNC_STEREO: | ||
1360 | if (info->s_info != NULL) { | ||
1361 | /* sync power */ | ||
1362 | info->s_info->pwr_api = info->pwr_api; | ||
1363 | /* move slave lens to master position */ | ||
1364 | err = sh532u_pos_rel_wr(info->s_info, | ||
1365 | info->pos_rel); | ||
1366 | if (!err) { | ||
1367 | info->s_mode = val; | ||
1368 | info->s_info->s_mode = val; | ||
1369 | } else { | ||
1370 | if (info->s_mode != NVC_SYNC_SLAVE) | ||
1371 | sh532u_pm_wr(info->s_info, | ||
1372 | NVC_PWR_OFF); | ||
1373 | err = -EIO; | ||
1374 | } | ||
1375 | } else { | ||
1376 | err = -EINVAL; | ||
1377 | } | ||
1378 | break; | ||
1379 | |||
1380 | default: | ||
1381 | err = -EINVAL; | ||
1382 | } | ||
1383 | if (info->pdata->cfg & NVC_CFG_NOERR) | ||
1384 | return 0; | ||
1385 | |||
1386 | return err; | ||
1387 | |||
1388 | default: | ||
1389 | /* parameters dependent on sync mode */ | ||
1390 | switch (info->s_mode) { | ||
1391 | case NVC_SYNC_OFF: | ||
1392 | case NVC_SYNC_MASTER: | ||
1393 | return sh532u_param_wr_s(info, ¶ms, u32_val); | ||
1394 | |||
1395 | case NVC_SYNC_SLAVE: | ||
1396 | return sh532u_param_wr_s(info->s_info, | ||
1397 | ¶ms, | ||
1398 | u32_val); | ||
1399 | |||
1400 | case NVC_SYNC_STEREO: | ||
1401 | err = sh532u_param_wr_s(info, ¶ms, u32_val); | ||
1402 | if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX)) | ||
1403 | err |= sh532u_param_wr_s(info->s_info, | ||
1404 | ¶ms, | ||
1405 | u32_val); | ||
1406 | return err; | ||
1407 | |||
1408 | default: | ||
1409 | dev_err(&info->i2c_client->dev, "%s %d internal err\n", | ||
1410 | __func__, __LINE__); | ||
1411 | return -EINVAL; | ||
1412 | } | ||
1413 | } | ||
1414 | } | ||
1415 | |||
1416 | static long sh532u_ioctl(struct file *file, | ||
1417 | unsigned int cmd, | ||
1418 | unsigned long arg) | ||
1419 | { | ||
1420 | struct sh532u_info *info = file->private_data; | ||
1421 | int pwr; | ||
1422 | |||
1423 | switch (cmd) { | ||
1424 | case NVC_IOCTL_PARAM_WR: | ||
1425 | return sh532u_param_wr(info, arg); | ||
1426 | |||
1427 | case NVC_IOCTL_PARAM_RD: | ||
1428 | return sh532u_param_rd(info, arg); | ||
1429 | |||
1430 | case NVC_IOCTL_PWR_WR: | ||
1431 | /* This is a Guaranteed Level of Service (GLOS) call */ | ||
1432 | pwr = (int)arg * 2; | ||
1433 | dev_dbg(&info->i2c_client->dev, "%s PWR: %d\n", | ||
1434 | __func__, pwr); | ||
1435 | return sh532u_pm_api_wr(info, pwr); | ||
1436 | |||
1437 | case NVC_IOCTL_PWR_RD: | ||
1438 | if (info->s_mode == NVC_SYNC_SLAVE) | ||
1439 | pwr = info->s_info->pwr_api / 2; | ||
1440 | else | ||
1441 | pwr = info->pwr_api / 2; | ||
1442 | dev_dbg(&info->i2c_client->dev, "%s PWR_RD: %d\n", | ||
1443 | __func__, pwr); | ||
1444 | if (copy_to_user((void __user *)arg, (const void *)&pwr, | ||
1445 | sizeof(pwr))) { | ||
1446 | dev_err(&info->i2c_client->dev, | ||
1447 | "%s copy_to_user err line %d\n", | ||
1448 | __func__, __LINE__); | ||
1449 | return -EFAULT; | ||
1450 | } | ||
1451 | |||
1452 | return 0; | ||
1453 | |||
1454 | default: | ||
1455 | dev_err(&info->i2c_client->dev, "%s unsupported ioctl: %x\n", | ||
1456 | __func__, cmd); | ||
1457 | return -EINVAL; | ||
1458 | } | ||
1459 | } | ||
1460 | |||
1461 | static int sh532u_sync_en(int dev1, int dev2) | ||
1462 | { | ||
1463 | struct sh532u_info *sync1 = NULL; | ||
1464 | struct sh532u_info *sync2 = NULL; | ||
1465 | struct sh532u_info *pos = NULL; | ||
1466 | |||
1467 | rcu_read_lock(); | ||
1468 | list_for_each_entry_rcu(pos, &sh532u_info_list, list) { | ||
1469 | if (pos->pdata->num == dev1) { | ||
1470 | sync1 = pos; | ||
1471 | break; | ||
1472 | } | ||
1473 | } | ||
1474 | pos = NULL; | ||
1475 | list_for_each_entry_rcu(pos, &sh532u_info_list, list) { | ||
1476 | if (pos->pdata->num == dev2) { | ||
1477 | sync2 = pos; | ||
1478 | break; | ||
1479 | } | ||
1480 | } | ||
1481 | rcu_read_unlock(); | ||
1482 | if (sync1 != NULL) | ||
1483 | sync1->s_info = NULL; | ||
1484 | if (sync2 != NULL) | ||
1485 | sync2->s_info = NULL; | ||
1486 | if (!dev1 && !dev2) | ||
1487 | return 0; /* no err if default instance 0's used */ | ||
1488 | |||
1489 | if (dev1 == dev2) | ||
1490 | return -EINVAL; /* err if sync instance is itself */ | ||
1491 | |||
1492 | if ((sync1 != NULL) && (sync2 != NULL)) { | ||
1493 | sync1->s_info = sync2; | ||
1494 | sync2->s_info = sync1; | ||
1495 | } | ||
1496 | return 0; | ||
1497 | } | ||
1498 | |||
1499 | static int sh532u_sync_dis(struct sh532u_info *info) | ||
1500 | { | ||
1501 | if (info->s_info != NULL) { | ||
1502 | info->s_info->s_mode = 0; | ||
1503 | info->s_info->s_info = NULL; | ||
1504 | info->s_mode = 0; | ||
1505 | info->s_info = NULL; | ||
1506 | return 0; | ||
1507 | } | ||
1508 | |||
1509 | return -EINVAL; | ||
1510 | } | ||
1511 | |||
1512 | static int sh532u_open(struct inode *inode, struct file *file) | ||
1513 | { | ||
1514 | struct sh532u_info *info = NULL; | ||
1515 | struct sh532u_info *pos = NULL; | ||
1516 | int err; | ||
1517 | |||
1518 | rcu_read_lock(); | ||
1519 | list_for_each_entry_rcu(pos, &sh532u_info_list, list) { | ||
1520 | if (pos->miscdev.minor == iminor(inode)) { | ||
1521 | info = pos; | ||
1522 | break; | ||
1523 | } | ||
1524 | } | ||
1525 | rcu_read_unlock(); | ||
1526 | if (!info) | ||
1527 | return -ENODEV; | ||
1528 | |||
1529 | err = sh532u_sync_en(info->pdata->num, info->pdata->sync); | ||
1530 | if (err == -EINVAL) | ||
1531 | dev_err(&info->i2c_client->dev, | ||
1532 | "%s err: invalid num (%u) and sync (%u) instance\n", | ||
1533 | __func__, info->pdata->num, info->pdata->sync); | ||
1534 | if (atomic_xchg(&info->in_use, 1)) | ||
1535 | return -EBUSY; | ||
1536 | |||
1537 | if (info->s_info != NULL) { | ||
1538 | if (atomic_xchg(&info->s_info->in_use, 1)) | ||
1539 | return -EBUSY; | ||
1540 | } | ||
1541 | |||
1542 | file->private_data = info; | ||
1543 | dev_dbg(&info->i2c_client->dev, "%s\n", __func__); | ||
1544 | sh532u_pos_rel_wr(info, info->cap.focus_infinity); | ||
1545 | return 0; | ||
1546 | } | ||
1547 | |||
1548 | int sh532u_release(struct inode *inode, struct file *file) | ||
1549 | { | ||
1550 | struct sh532u_info *info = file->private_data; | ||
1551 | |||
1552 | dev_dbg(&info->i2c_client->dev, "%s\n", __func__); | ||
1553 | sh532u_pm_wr_s(info, NVC_PWR_OFF); | ||
1554 | file->private_data = NULL; | ||
1555 | WARN_ON(!atomic_xchg(&info->in_use, 0)); | ||
1556 | if (info->s_info != NULL) | ||
1557 | WARN_ON(!atomic_xchg(&info->s_info->in_use, 0)); | ||
1558 | sh532u_sync_dis(info); | ||
1559 | return 0; | ||
1560 | } | ||
1561 | |||
1562 | static const struct file_operations sh532u_fileops = { | ||
1563 | .owner = THIS_MODULE, | ||
1564 | .open = sh532u_open, | ||
1565 | .unlocked_ioctl = sh532u_ioctl, | ||
1566 | .release = sh532u_release, | ||
1567 | }; | ||
1568 | |||
1569 | static void sh532u_del(struct sh532u_info *info) | ||
1570 | { | ||
1571 | sh532u_pm_exit(info); | ||
1572 | sh532u_sync_dis(info); | ||
1573 | spin_lock(&sh532u_spinlock); | ||
1574 | list_del_rcu(&info->list); | ||
1575 | spin_unlock(&sh532u_spinlock); | ||
1576 | synchronize_rcu(); | ||
1577 | } | ||
1578 | |||
1579 | static int sh532u_remove(struct i2c_client *client) | ||
1580 | { | ||
1581 | struct sh532u_info *info = i2c_get_clientdata(client); | ||
1582 | |||
1583 | dev_dbg(&info->i2c_client->dev, "%s\n", __func__); | ||
1584 | misc_deregister(&info->miscdev); | ||
1585 | sh532u_del(info); | ||
1586 | return 0; | ||
1587 | } | ||
1588 | |||
1589 | static int sh532u_probe( | ||
1590 | struct i2c_client *client, | ||
1591 | const struct i2c_device_id *id) | ||
1592 | { | ||
1593 | struct sh532u_info *info = NULL; | ||
1594 | char dname[16]; | ||
1595 | int err; | ||
1596 | |||
1597 | dev_dbg(&client->dev, "%s\n", __func__); | ||
1598 | info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); | ||
1599 | if (info == NULL) { | ||
1600 | dev_err(&client->dev, "%s: kzalloc error\n", __func__); | ||
1601 | return -ENOMEM; | ||
1602 | } | ||
1603 | |||
1604 | info->i2c_client = client; | ||
1605 | if (client->dev.platform_data) { | ||
1606 | info->pdata = client->dev.platform_data; | ||
1607 | } else { | ||
1608 | info->pdata = &sh532u_default_pdata; | ||
1609 | dev_dbg(&client->dev, | ||
1610 | "%s No platform data. Using defaults.\n", | ||
1611 | __func__); | ||
1612 | } | ||
1613 | i2c_set_clientdata(client, info); | ||
1614 | INIT_LIST_HEAD(&info->list); | ||
1615 | spin_lock(&sh532u_spinlock); | ||
1616 | list_add_rcu(&info->list, &sh532u_info_list); | ||
1617 | spin_unlock(&sh532u_spinlock); | ||
1618 | sh532u_pm_init(info); | ||
1619 | sh532u_pm_dev_wr(info, NVC_PWR_COMM); | ||
1620 | err = sh532u_dev_id(info); | ||
1621 | if (err < 0) { | ||
1622 | dev_err(&client->dev, "%s device not found\n", __func__); | ||
1623 | sh532u_pm_wr(info, NVC_PWR_OFF); | ||
1624 | if (info->pdata->cfg & NVC_CFG_NODEV) { | ||
1625 | sh532u_del(info); | ||
1626 | return -ENODEV; | ||
1627 | } | ||
1628 | } else { | ||
1629 | dev_dbg(&client->dev, "%s device found\n", __func__); | ||
1630 | sh532u_calibration(info, false); | ||
1631 | if (info->pdata->cfg & NVC_CFG_BOOT_INIT) { | ||
1632 | /* initial move causes full initialization */ | ||
1633 | sh532u_pos_rel_wr(info, info->cap.focus_infinity); | ||
1634 | } else { | ||
1635 | sh532u_pm_wr(info, NVC_PWR_OFF); | ||
1636 | } | ||
1637 | } | ||
1638 | |||
1639 | if (info->pdata->dev_name != 0) | ||
1640 | strcpy(dname, info->pdata->dev_name); | ||
1641 | else | ||
1642 | strcpy(dname, "sh532u"); | ||
1643 | if (info->pdata->num) | ||
1644 | snprintf(dname, sizeof(dname), "%s.%u", | ||
1645 | dname, info->pdata->num); | ||
1646 | info->miscdev.name = dname; | ||
1647 | info->miscdev.fops = &sh532u_fileops; | ||
1648 | info->miscdev.minor = MISC_DYNAMIC_MINOR; | ||
1649 | if (misc_register(&info->miscdev)) { | ||
1650 | dev_err(&client->dev, "%s unable to register misc device %s\n", | ||
1651 | __func__, dname); | ||
1652 | sh532u_del(info); | ||
1653 | return -ENODEV; | ||
1654 | } | ||
1655 | |||
1656 | return 0; | ||
1657 | } | ||
1658 | |||
1659 | static const struct i2c_device_id sh532u_id[] = { | ||
1660 | { "sh532u", 0 }, | ||
1661 | { }, | ||
1662 | }; | ||
1663 | |||
1664 | MODULE_DEVICE_TABLE(i2c, sh532u_id); | ||
1665 | |||
1666 | static struct i2c_driver sh532u_i2c_driver = { | ||
1667 | .driver = { | ||
1668 | .name = "sh532u", | ||
1669 | .owner = THIS_MODULE, | ||
1670 | }, | ||
1671 | .id_table = sh532u_id, | ||
1672 | .probe = sh532u_probe, | ||
1673 | .remove = sh532u_remove, | ||
1674 | }; | ||
1675 | |||
1676 | static int __init sh532u_init(void) | ||
1677 | { | ||
1678 | return i2c_add_driver(&sh532u_i2c_driver); | ||
1679 | } | ||
1680 | |||
1681 | static void __exit sh532u_exit(void) | ||
1682 | { | ||
1683 | i2c_del_driver(&sh532u_i2c_driver); | ||
1684 | } | ||
1685 | |||
1686 | module_init(sh532u_init); | ||
1687 | module_exit(sh532u_exit); | ||
1688 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/video/tegra/soc380.c b/drivers/media/video/tegra/soc380.c new file mode 100644 index 00000000000..7f2c1361466 --- /dev/null +++ b/drivers/media/video/tegra/soc380.c | |||
@@ -0,0 +1,473 @@ | |||
1 | /* | ||
2 | * soc380.c - soc380 sensor driver | ||
3 | * | ||
4 | * Copyright (c) 2011, NVIDIA, All Rights Reserved. | ||
5 | * | ||
6 | * Contributors: | ||
7 | * Abhinav Sinha <absinha@nvidia.com> | ||
8 | * | ||
9 | * Leverage OV2710.c | ||
10 | * | ||
11 | * This file is licensed under the terms of the GNU General Public License | ||
12 | * version 2. This program is licensed "as is" without any warranty of any | ||
13 | * kind, whether express or implied. | ||
14 | */ | ||
15 | |||
16 | /** | ||
17 | * SetMode Sequence for 640x480. Phase 0. Sensor Dependent. | ||
18 | * This sequence should put sensor in streaming mode for 640x480 | ||
19 | * This is usually given by the FAE or the sensor vendor. | ||
20 | */ | ||
21 | |||
22 | #include <linux/delay.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/i2c.h> | ||
25 | #include <linux/miscdevice.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/uaccess.h> | ||
28 | #include <media/soc380.h> | ||
29 | |||
30 | struct soc380_reg { | ||
31 | u16 addr; | ||
32 | u16 val; | ||
33 | }; | ||
34 | |||
35 | struct soc380_info { | ||
36 | int mode; | ||
37 | struct i2c_client *i2c_client; | ||
38 | struct soc380_platform_data *pdata; | ||
39 | }; | ||
40 | |||
41 | #define SOC380_TABLE_WAIT_MS 0 | ||
42 | #define SOC380_TABLE_END 1 | ||
43 | #define SOC380_MAX_RETRIES 3 | ||
44 | |||
45 | static struct soc380_reg mode_640x480[] = { | ||
46 | {0x001A, 0x0011}, | ||
47 | |||
48 | {SOC380_TABLE_WAIT_MS, 1}, | ||
49 | |||
50 | {0x001A, 0x0010}, | ||
51 | |||
52 | {SOC380_TABLE_WAIT_MS, 1}, | ||
53 | |||
54 | {0x0018, 0x4028}, | ||
55 | {0x001A, 0x0210}, | ||
56 | {0x0010, 0x021c}, | ||
57 | {0x0012, 0x0000}, | ||
58 | {0x0014, 0x244B}, | ||
59 | |||
60 | {SOC380_TABLE_WAIT_MS, 10}, | ||
61 | |||
62 | {0x0014, 0x304B}, | ||
63 | |||
64 | {SOC380_TABLE_WAIT_MS, 50}, | ||
65 | |||
66 | {0x0014, 0xB04A}, | ||
67 | |||
68 | {0x098C, 0x2703}, | ||
69 | {0x0990, 0x0280}, | ||
70 | {0x098C, 0x2705}, | ||
71 | {0x0990, 0x01E0}, | ||
72 | {0x098C, 0x2707}, | ||
73 | {0x0990, 0x0280}, | ||
74 | {0x098C, 0x2709}, | ||
75 | {0x0990, 0x01E0}, | ||
76 | {0x098C, 0x270D}, | ||
77 | {0x0990, 0x0000}, | ||
78 | {0x098C, 0x270F}, | ||
79 | {0x0990, 0x0000}, | ||
80 | {0x098C, 0x2711}, | ||
81 | {0x0990, 0x01E7}, | ||
82 | {0x098C, 0x2713}, | ||
83 | {0x0990, 0x0287}, | ||
84 | {0x098C, 0x2715}, | ||
85 | {0x0990, 0x0001}, | ||
86 | {0x098C, 0x2717}, | ||
87 | {0x0990, 0x0026}, | ||
88 | {0x098C, 0x2719}, | ||
89 | {0x0990, 0x001A}, | ||
90 | {0x098C, 0x271B}, | ||
91 | {0x0990, 0x006B}, | ||
92 | {0x098C, 0x271D}, | ||
93 | {0x0990, 0x006B}, | ||
94 | {0x098C, 0x271F}, | ||
95 | {0x0990, 0x022A}, | ||
96 | {0x098C, 0x2721}, | ||
97 | {0x0990, 0x034A}, | ||
98 | {0x098C, 0x2723}, | ||
99 | {0x0990, 0x0000}, | ||
100 | {0x098C, 0x2725}, | ||
101 | {0x0990, 0x0000}, | ||
102 | {0x098C, 0x2727}, | ||
103 | {0x0990, 0x01E7}, | ||
104 | {0x098C, 0x2729}, | ||
105 | {0x0990, 0x0287}, | ||
106 | {0x098C, 0x272B}, | ||
107 | {0x0990, 0x0001}, | ||
108 | {0x098C, 0x272D}, | ||
109 | {0x0990, 0x0026}, | ||
110 | {0x098C, 0x272F}, | ||
111 | {0x0990, 0x001A}, | ||
112 | {0x098C, 0x2731}, | ||
113 | {0x0990, 0x006B}, | ||
114 | {0x098C, 0x2733}, | ||
115 | {0x0990, 0x006B}, | ||
116 | {0x098C, 0x2735}, | ||
117 | {0x0990, 0x022A}, | ||
118 | {0x098C, 0x2737}, | ||
119 | {0x0990, 0x034A}, | ||
120 | {0x098C, 0x2739}, | ||
121 | {0x0990, 0x0000}, | ||
122 | {0x098C, 0x273B}, | ||
123 | {0x0990, 0x027F}, | ||
124 | {0x098C, 0x273D}, | ||
125 | {0x0990, 0x0000}, | ||
126 | {0x098C, 0x273F}, | ||
127 | {0x0990, 0x01DF}, | ||
128 | {0x098C, 0x2747}, | ||
129 | {0x0990, 0x0000}, | ||
130 | {0x098C, 0x2749}, | ||
131 | {0x0990, 0x027F}, | ||
132 | {0x098C, 0x274B}, | ||
133 | {0x0990, 0x0000}, | ||
134 | {0x098C, 0x274D}, | ||
135 | {0x0990, 0x01DF}, | ||
136 | {0x098C, 0x222D}, | ||
137 | {0x0990, 0x008B}, | ||
138 | {0x098C, 0xA408}, | ||
139 | {0x0990, 0x0021}, | ||
140 | {0x098C, 0xA409}, | ||
141 | {0x0990, 0x0023}, | ||
142 | {0x098C, 0xA40A}, | ||
143 | {0x0990, 0x0028}, | ||
144 | {0x098C, 0xA40B}, | ||
145 | {0x0990, 0x002A}, | ||
146 | {0x098C, 0x2411}, | ||
147 | {0x0990, 0x008B}, | ||
148 | {0x098C, 0x2413}, | ||
149 | {0x0990, 0x00A6}, | ||
150 | {0x098C, 0x2415}, | ||
151 | {0x0990, 0x008B}, | ||
152 | {0x098C, 0x2417}, | ||
153 | {0x0990, 0x00A6}, | ||
154 | {0x098C, 0xA404}, | ||
155 | {0x0990, 0x0010}, | ||
156 | {0x098C, 0xA40D}, | ||
157 | {0x0990, 0x0002}, | ||
158 | {0x098C, 0xA40E}, | ||
159 | {0x0990, 0x0003}, | ||
160 | {0x098C, 0xA410}, | ||
161 | {0x0990, 0x000A}, | ||
162 | {0x098C, 0xA215}, | ||
163 | {0x0990, 0x0003}, | ||
164 | {0x098C, 0xA20C}, | ||
165 | {0x0990, 0x0003}, | ||
166 | |||
167 | {0x098C, 0xA103}, | ||
168 | {0x0990, 0x0006}, | ||
169 | {SOC380_TABLE_WAIT_MS, 100}, | ||
170 | |||
171 | {0x098C, 0xA103}, | ||
172 | {0x0990, 0x0005}, | ||
173 | {SOC380_TABLE_WAIT_MS, 50}, | ||
174 | |||
175 | {SOC380_TABLE_END, 0x0000} | ||
176 | }; | ||
177 | |||
178 | enum { | ||
179 | SOC380_MODE_680x480, | ||
180 | }; | ||
181 | |||
182 | static struct soc380_reg *mode_table[] = { | ||
183 | [SOC380_MODE_680x480] = mode_640x480, | ||
184 | }; | ||
185 | |||
186 | static int soc380_read_reg(struct i2c_client *client, u16 addr, u16 *val) | ||
187 | { | ||
188 | int err; | ||
189 | struct i2c_msg msg[2]; | ||
190 | unsigned char data[4]; | ||
191 | |||
192 | if (!client->adapter) | ||
193 | return -ENODEV; | ||
194 | |||
195 | msg[0].addr = client->addr; | ||
196 | msg[0].flags = 0; | ||
197 | msg[0].len = 2; | ||
198 | msg[0].buf = data; | ||
199 | |||
200 | /* high byte goes out first */ | ||
201 | data[0] = (u8) (addr >> 8); | ||
202 | data[1] = (u8) (addr & 0xff); | ||
203 | |||
204 | msg[1].addr = client->addr; | ||
205 | msg[1].flags = I2C_M_RD; | ||
206 | msg[1].len = 2; | ||
207 | msg[1].buf = data + 2; | ||
208 | |||
209 | err = i2c_transfer(client->adapter, msg, 2); | ||
210 | |||
211 | if (err != 2) | ||
212 | return -EINVAL; | ||
213 | |||
214 | *val = data[2] << 8 | data[3]; | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static int soc380_write_reg(struct i2c_client *client, u16 addr, u16 val) | ||
220 | { | ||
221 | int err; | ||
222 | struct i2c_msg msg; | ||
223 | unsigned char data[4]; | ||
224 | int retry = 0; | ||
225 | |||
226 | if (!client->adapter) | ||
227 | return -ENODEV; | ||
228 | |||
229 | data[0] = (u8) (addr >> 8); | ||
230 | data[1] = (u8) (addr & 0xff); | ||
231 | data[2] = (u8) (val >> 8); | ||
232 | data[3] = (u8) (val & 0xff); | ||
233 | |||
234 | msg.addr = client->addr; | ||
235 | msg.flags = 0; | ||
236 | msg.len = 4; | ||
237 | msg.buf = data; | ||
238 | |||
239 | do { | ||
240 | err = i2c_transfer(client->adapter, &msg, 1); | ||
241 | if (err == 1) | ||
242 | return 0; | ||
243 | retry++; | ||
244 | pr_err("soc380: i2c transfer failed, retrying %x %x\n", | ||
245 | addr, val); | ||
246 | msleep(3); | ||
247 | } while (retry <= SOC380_MAX_RETRIES); | ||
248 | |||
249 | return err; | ||
250 | } | ||
251 | |||
252 | static int soc380_write_table(struct i2c_client *client, | ||
253 | const struct soc380_reg table[], | ||
254 | const struct soc380_reg override_list[], | ||
255 | int num_override_regs) | ||
256 | { | ||
257 | int err; | ||
258 | const struct soc380_reg *next; | ||
259 | int i; | ||
260 | u16 val; | ||
261 | |||
262 | for (next = table; next->addr != SOC380_TABLE_END; next++) { | ||
263 | if (next->addr == SOC380_TABLE_WAIT_MS) { | ||
264 | msleep(next->val); | ||
265 | continue; | ||
266 | } | ||
267 | |||
268 | val = next->val; | ||
269 | |||
270 | /* When an override list is passed in, replace the reg */ | ||
271 | /* value to write if the reg is in the list */ | ||
272 | if (override_list) { | ||
273 | for (i = 0; i < num_override_regs; i++) { | ||
274 | if (next->addr == override_list[i].addr) { | ||
275 | val = override_list[i].val; | ||
276 | break; | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | |||
281 | err = soc380_write_reg(client, next->addr, val); | ||
282 | if (err) | ||
283 | return err; | ||
284 | } | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static int soc380_set_mode(struct soc380_info *info, struct soc380_mode *mode) | ||
289 | { | ||
290 | int sensor_mode; | ||
291 | int err; | ||
292 | |||
293 | pr_info("%s: xres %u yres %u\n", __func__, mode->xres, mode->yres); | ||
294 | if (mode->xres == 640 && mode->yres == 480) | ||
295 | sensor_mode = SOC380_MODE_680x480; | ||
296 | else { | ||
297 | pr_err("%s: invalid resolution supplied to set mode %d %d\n", | ||
298 | __func__, mode->xres, mode->yres); | ||
299 | return -EINVAL; | ||
300 | } | ||
301 | |||
302 | err = soc380_write_table(info->i2c_client, mode_table[sensor_mode], | ||
303 | NULL, 0); | ||
304 | if (err) | ||
305 | return err; | ||
306 | |||
307 | info->mode = sensor_mode; | ||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static int soc380_get_status(struct soc380_info *info, | ||
312 | struct soc380_status *dev_status) | ||
313 | { | ||
314 | int err; | ||
315 | |||
316 | err = soc380_write_reg(info->i2c_client, 0x98C, dev_status->data); | ||
317 | if (err) | ||
318 | return err; | ||
319 | |||
320 | err = soc380_read_reg(info->i2c_client, 0x0990, | ||
321 | (u16 *) &dev_status->status); | ||
322 | if (err) | ||
323 | return err; | ||
324 | |||
325 | return err; | ||
326 | } | ||
327 | |||
328 | static long soc380_ioctl(struct file *file, | ||
329 | unsigned int cmd, unsigned long arg) | ||
330 | { | ||
331 | int err; | ||
332 | struct soc380_info *info = file->private_data; | ||
333 | |||
334 | switch (cmd) { | ||
335 | case SOC380_IOCTL_SET_MODE: | ||
336 | { | ||
337 | struct soc380_mode mode; | ||
338 | if (copy_from_user(&mode, | ||
339 | (const void __user *)arg, | ||
340 | sizeof(struct soc380_mode))) { | ||
341 | return -EFAULT; | ||
342 | } | ||
343 | |||
344 | return soc380_set_mode(info, &mode); | ||
345 | } | ||
346 | case SOC380_IOCTL_GET_STATUS: | ||
347 | { | ||
348 | struct soc380_status dev_status; | ||
349 | if (copy_from_user(&dev_status, | ||
350 | (const void __user *)arg, | ||
351 | sizeof(struct soc380_status))) { | ||
352 | return -EFAULT; | ||
353 | } | ||
354 | |||
355 | err = soc380_get_status(info, &dev_status); | ||
356 | if (err) | ||
357 | return err; | ||
358 | if (copy_to_user((void __user *)arg, &dev_status, | ||
359 | sizeof(struct soc380_status))) { | ||
360 | return -EFAULT; | ||
361 | } | ||
362 | return 0; | ||
363 | } | ||
364 | default: | ||
365 | return -EINVAL; | ||
366 | } | ||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | static struct soc380_info *info; | ||
371 | |||
372 | static int soc380_open(struct inode *inode, struct file *file) | ||
373 | { | ||
374 | struct soc380_status dev_status; | ||
375 | int err; | ||
376 | |||
377 | file->private_data = info; | ||
378 | if (info->pdata && info->pdata->power_on) | ||
379 | info->pdata->power_on(); | ||
380 | |||
381 | dev_status.data = 0; | ||
382 | dev_status.status = 0; | ||
383 | err = soc380_get_status(info, &dev_status); | ||
384 | return err; | ||
385 | } | ||
386 | |||
387 | int soc380_release(struct inode *inode, struct file *file) | ||
388 | { | ||
389 | if (info->pdata && info->pdata->power_off) | ||
390 | info->pdata->power_off(); | ||
391 | file->private_data = NULL; | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static const struct file_operations soc380_fileops = { | ||
396 | .owner = THIS_MODULE, | ||
397 | .open = soc380_open, | ||
398 | .unlocked_ioctl = soc380_ioctl, | ||
399 | .release = soc380_release, | ||
400 | }; | ||
401 | |||
402 | static struct miscdevice soc380_device = { | ||
403 | .minor = MISC_DYNAMIC_MINOR, | ||
404 | .name = "soc380", | ||
405 | .fops = &soc380_fileops, | ||
406 | }; | ||
407 | |||
408 | static int soc380_probe(struct i2c_client *client, | ||
409 | const struct i2c_device_id *id) | ||
410 | { | ||
411 | int err; | ||
412 | |||
413 | pr_info("soc380: probing sensor.\n"); | ||
414 | |||
415 | info = kzalloc(sizeof(struct soc380_info), GFP_KERNEL); | ||
416 | if (!info) { | ||
417 | pr_err("soc380: Unable to allocate memory!\n"); | ||
418 | return -ENOMEM; | ||
419 | } | ||
420 | |||
421 | err = misc_register(&soc380_device); | ||
422 | if (err) { | ||
423 | pr_err("soc380: Unable to register misc device!\n"); | ||
424 | kfree(info); | ||
425 | return err; | ||
426 | } | ||
427 | |||
428 | info->pdata = client->dev.platform_data; | ||
429 | info->i2c_client = client; | ||
430 | |||
431 | i2c_set_clientdata(client, info); | ||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | static int soc380_remove(struct i2c_client *client) | ||
436 | { | ||
437 | struct soc380_info *info; | ||
438 | info = i2c_get_clientdata(client); | ||
439 | misc_deregister(&soc380_device); | ||
440 | kfree(info); | ||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | static const struct i2c_device_id soc380_id[] = { | ||
445 | { "soc380", 0 }, | ||
446 | { }, | ||
447 | }; | ||
448 | |||
449 | MODULE_DEVICE_TABLE(i2c, soc380_id); | ||
450 | |||
451 | static struct i2c_driver soc380_i2c_driver = { | ||
452 | .driver = { | ||
453 | .name = "soc380", | ||
454 | .owner = THIS_MODULE, | ||
455 | }, | ||
456 | .probe = soc380_probe, | ||
457 | .remove = soc380_remove, | ||
458 | .id_table = soc380_id, | ||
459 | }; | ||
460 | |||
461 | static int __init soc380_init(void) | ||
462 | { | ||
463 | pr_info("soc380 sensor driver loading\n"); | ||
464 | return i2c_add_driver(&soc380_i2c_driver); | ||
465 | } | ||
466 | |||
467 | static void __exit soc380_exit(void) | ||
468 | { | ||
469 | i2c_del_driver(&soc380_i2c_driver); | ||
470 | } | ||
471 | |||
472 | module_init(soc380_init); | ||
473 | module_exit(soc380_exit); | ||
diff --git a/drivers/media/video/tegra/ssl3250a.c b/drivers/media/video/tegra/ssl3250a.c new file mode 100644 index 00000000000..ef2c80a883c --- /dev/null +++ b/drivers/media/video/tegra/ssl3250a.c | |||
@@ -0,0 +1,986 @@ | |||
1 | /* | ||
2 | * ssl3250a.c - ssl3250a flash/torch kernel driver | ||
3 | * | ||
4 | * Copyright (C) 2011 NVIDIA Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
18 | * 02111-1307, USA | ||
19 | */ | ||
20 | |||
21 | /* Implementation | ||
22 | * -------------- | ||
23 | * The board level details about the device need to be provided in the board | ||
24 | * file with the ssl3250a_platform_data structure. | ||
25 | * Standard among NVC kernel drivers in this structure is: | ||
26 | * .cfg = Use the NVC_CFG_ defines that are in nvc_torch.h. | ||
27 | * Descriptions of the configuration options are with the defines. | ||
28 | * This value is typically 0. | ||
29 | * .num = The number of the instance of the device. This should start at 1 and | ||
30 | * and increment for each device on the board. This number will be | ||
31 | * appended to the MISC driver name, Example: /dev/ssl3250a.1 | ||
32 | * .sync = If there is a need to synchronize two devices, then this value is | ||
33 | * the number of the device instance this device is allowed to sync to. | ||
34 | * This is typically used for stereo applications. | ||
35 | * .dev_name = The MISC driver name the device registers as. If not used, | ||
36 | * then the part number of the device is used for the driver name. | ||
37 | * If using the NVC user driver then use the name found in this | ||
38 | * driver under _default_pdata. | ||
39 | * | ||
40 | * The following is specific to NVC kernel flash/torch drivers: | ||
41 | * .pinstate = a pointer to the nvc_torch_pin_state structure. This | ||
42 | * structure gives the details of which VI GPIO to use to trigger | ||
43 | * the flash. The mask tells which pin and the values is the | ||
44 | * level. For example, if VI GPIO pin 6 is used, then | ||
45 | * .mask = 0x0040 | ||
46 | * .values = 0x0040 | ||
47 | * If VI GPIO pin 0 is used, then | ||
48 | * .mask = 0x0001 | ||
49 | * .values = 0x0001 | ||
50 | * This is typically just one pin but there is some legacy | ||
51 | * here that insinuates more than one pin can be used. | ||
52 | * When the flash level is set, then the driver will return the | ||
53 | * value in values. When the flash level is off, the driver will | ||
54 | * return 0 for the values to deassert the signal. | ||
55 | * If a VI GPIO is not used, then the mask and values must be set | ||
56 | * to 0. The flash may then be triggered via I2C instead. | ||
57 | * However, a VI GPIO is strongly encouraged since it allows | ||
58 | * tighter timing with the picture taken as well as reduced power | ||
59 | * by asserting the trigger signal for only when needed. | ||
60 | * .max_amp_torch = Is the maximum torch value allowed. The value is 0 to | ||
61 | * _MAX_TORCH_LEVEL. This is to allow a limit to the amount | ||
62 | * of amps used. If left blank then _MAX_TORCH_LEVEL will be | ||
63 | * used. | ||
64 | * .max_amp_flash = Is the maximum flash value allowed. The value is 0 to | ||
65 | * _MAX_FLASH_LEVEL. This is to allow a limit to the amount | ||
66 | * of amps used. If left blank then _MAX_FLASH_LEVEL will be | ||
67 | * used. | ||
68 | * | ||
69 | * The following is specific to only this NVC kernel flash/torch driver: | ||
70 | * .gpio_act = Is the GPIO needed to control the ACT signal. If tied high, | ||
71 | * then this can be left blank. | ||
72 | * | ||
73 | * Power Requirements | ||
74 | * The board power file must contain the following labels for the power | ||
75 | * regulator(s) of this device: | ||
76 | * "vdd_i2c" = the power regulator for the I2C power. | ||
77 | * Note that this device is typically connected directly to the battery rail | ||
78 | * and does not need a source power regulator (vdd). | ||
79 | * | ||
80 | * The above values should be all that is needed to use the device with this | ||
81 | * driver. Modifications of this driver should not be needed. | ||
82 | */ | ||
83 | |||
84 | |||
85 | #include <linux/fs.h> | ||
86 | #include <linux/i2c.h> | ||
87 | #include <linux/miscdevice.h> | ||
88 | #include <linux/slab.h> | ||
89 | #include <linux/delay.h> | ||
90 | #include <linux/uaccess.h> | ||
91 | #include <linux/list.h> | ||
92 | #include <linux/regulator/consumer.h> | ||
93 | #include <linux/gpio.h> | ||
94 | #include <media/nvc.h> | ||
95 | #include <media/ssl3250a.h> | ||
96 | |||
97 | #define SSL3250A_REG_AMP 0x00 | ||
98 | #define SSL3250A_REG_TMR 0x01 | ||
99 | #define SSL3250A_REG_STRB 0x02 | ||
100 | #define SSL3250A_REG_STS 0x03 | ||
101 | #define ssl3250a_flash_cap_size (sizeof(ssl3250a_flash_cap.numberoflevels) \ | ||
102 | + (sizeof(ssl3250a_flash_cap.levels[0]) \ | ||
103 | * (SSL3250A_MAX_FLASH_LEVEL + 1))) | ||
104 | #define ssl3250a_torch_cap_size (sizeof(ssl3250a_torch_cap.numberoflevels) \ | ||
105 | + (sizeof(ssl3250a_torch_cap.guidenum[0]) \ | ||
106 | * (SSL3250A_MAX_TORCH_LEVEL + 1))) | ||
107 | |||
108 | |||
109 | static struct nvc_torch_flash_capabilities ssl3250a_flash_cap = { | ||
110 | SSL3250A_MAX_FLASH_LEVEL + 1, | ||
111 | { | ||
112 | { 0, 0xFFFFFFFF, 0 }, | ||
113 | { 215, 820, 20 }, | ||
114 | { 230, 820, 20 }, | ||
115 | { 245, 820, 20 }, | ||
116 | { 260, 820, 20 }, | ||
117 | { 275, 820, 20 }, | ||
118 | { 290, 820, 20 }, | ||
119 | { 305, 820, 20 }, | ||
120 | { 320, 820, 20 }, | ||
121 | { 335, 820, 20 }, | ||
122 | { 350, 820, 20 }, | ||
123 | { 365, 820, 20 }, | ||
124 | { 380, 820, 20 }, | ||
125 | { 395, 820, 20 }, | ||
126 | { 410, 820, 20 }, | ||
127 | { 425, 820, 20 }, | ||
128 | { 440, 820, 20 }, | ||
129 | { 455, 820, 20 }, | ||
130 | { 470, 820, 20 }, | ||
131 | { 485, 820, 20 }, | ||
132 | { 500, 820, 20 } | ||
133 | } | ||
134 | }; | ||
135 | |||
136 | static struct nvc_torch_torch_capabilities ssl3250a_torch_cap = { | ||
137 | SSL3250A_MAX_TORCH_LEVEL + 1, | ||
138 | { | ||
139 | 0, | ||
140 | 50, | ||
141 | 65, | ||
142 | 80, | ||
143 | 95, | ||
144 | 110, | ||
145 | 125, | ||
146 | 140, | ||
147 | 155, | ||
148 | 170, | ||
149 | 185, | ||
150 | 200 | ||
151 | } | ||
152 | }; | ||
153 | |||
154 | struct ssl3250a_info { | ||
155 | atomic_t in_use; | ||
156 | struct i2c_client *i2c_client; | ||
157 | struct ssl3250a_platform_data *pdata; | ||
158 | struct miscdevice miscdev; | ||
159 | struct list_head list; | ||
160 | int pwr_api; | ||
161 | int pwr_dev; | ||
162 | struct nvc_regulator vreg_i2c; | ||
163 | u8 s_mode; | ||
164 | struct ssl3250a_info *s_info; | ||
165 | }; | ||
166 | |||
167 | static struct nvc_torch_pin_state ssl3250a_default_pinstate = { | ||
168 | .mask = 0x0000, | ||
169 | .values = 0x0000, | ||
170 | }; | ||
171 | |||
172 | static struct ssl3250a_platform_data ssl3250a_default_pdata = { | ||
173 | .cfg = 0, | ||
174 | .num = 0, | ||
175 | .sync = 0, | ||
176 | .dev_name = "torch", | ||
177 | .pinstate = &ssl3250a_default_pinstate, | ||
178 | .max_amp_torch = SSL3250A_MAX_TORCH_LEVEL, | ||
179 | .max_amp_flash = SSL3250A_MAX_FLASH_LEVEL, | ||
180 | }; | ||
181 | |||
182 | static LIST_HEAD(ssl3250a_info_list); | ||
183 | static DEFINE_SPINLOCK(ssl3250a_spinlock); | ||
184 | |||
185 | |||
186 | static int ssl3250a_i2c_rd(struct ssl3250a_info *info, u8 reg, u8 *val) | ||
187 | { | ||
188 | struct i2c_msg msg[2]; | ||
189 | u8 buf[2]; | ||
190 | |||
191 | buf[0] = reg; | ||
192 | msg[0].addr = info->i2c_client->addr; | ||
193 | msg[0].flags = 0; | ||
194 | msg[0].len = 1; | ||
195 | msg[0].buf = &buf[0]; | ||
196 | msg[1].addr = info->i2c_client->addr; | ||
197 | msg[1].flags = I2C_M_RD; | ||
198 | msg[1].len = 1; | ||
199 | msg[1].buf = &buf[1]; | ||
200 | *val = 0; | ||
201 | if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2) | ||
202 | return -EIO; | ||
203 | |||
204 | *val = buf[1]; | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int ssl3250a_i2c_wr(struct ssl3250a_info *info, u8 reg, u8 val) | ||
209 | { | ||
210 | struct i2c_msg msg; | ||
211 | u8 buf[2]; | ||
212 | |||
213 | buf[0] = reg; | ||
214 | buf[1] = val; | ||
215 | msg.addr = info->i2c_client->addr; | ||
216 | msg.flags = 0; | ||
217 | msg.len = 2; | ||
218 | msg.buf = &buf[0]; | ||
219 | if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) | ||
220 | return -EIO; | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static void ssl3250a_gpio_act(struct ssl3250a_info *info, int val) | ||
226 | { | ||
227 | int prev_val; | ||
228 | |||
229 | if (info->pdata->gpio_act) { | ||
230 | prev_val = gpio_get_value(info->pdata->gpio_act); | ||
231 | if (val != prev_val) { | ||
232 | gpio_set_value(info->pdata->gpio_act, val); | ||
233 | if (val) | ||
234 | mdelay(1); /* delay for device startup */ | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | |||
239 | static void ssl3250a_pm_regulator_put(struct nvc_regulator *sreg) | ||
240 | { | ||
241 | regulator_put(sreg->vreg); | ||
242 | sreg->vreg = NULL; | ||
243 | } | ||
244 | |||
245 | static int ssl3250a_pm_regulator_get(struct ssl3250a_info *info, | ||
246 | struct nvc_regulator *sreg, | ||
247 | char vreg_name[]) | ||
248 | { | ||
249 | int err = 0; | ||
250 | |||
251 | sreg->vreg_flag = 0; | ||
252 | sreg->vreg = regulator_get(&info->i2c_client->dev, vreg_name); | ||
253 | if (WARN_ON(IS_ERR(sreg->vreg))) { | ||
254 | dev_err(&info->i2c_client->dev, | ||
255 | "%s err for regulator: %s err: %d\n", | ||
256 | __func__, vreg_name, (int)sreg->vreg); | ||
257 | err = PTR_ERR(sreg->vreg); | ||
258 | sreg->vreg = NULL; | ||
259 | } else { | ||
260 | sreg->vreg_name = vreg_name; | ||
261 | dev_dbg(&info->i2c_client->dev, | ||
262 | "%s vreg_name: %s\n", | ||
263 | __func__, sreg->vreg_name); | ||
264 | } | ||
265 | return err; | ||
266 | } | ||
267 | |||
268 | static int ssl3250a_pm_regulator_en(struct ssl3250a_info *info, | ||
269 | struct nvc_regulator *sreg) | ||
270 | { | ||
271 | int err = 0; | ||
272 | |||
273 | if (!sreg->vreg_flag && (sreg->vreg != NULL)) { | ||
274 | err = regulator_enable(sreg->vreg); | ||
275 | if (!err) { | ||
276 | dev_dbg(&info->i2c_client->dev, | ||
277 | "%s vreg_name: %s\n", | ||
278 | __func__, sreg->vreg_name); | ||
279 | sreg->vreg_flag = 1; | ||
280 | err = 1; /* flag regulator state change */ | ||
281 | } else { | ||
282 | dev_err(&info->i2c_client->dev, | ||
283 | "%s err, regulator: %s\n", | ||
284 | __func__, sreg->vreg_name); | ||
285 | } | ||
286 | } | ||
287 | return err; | ||
288 | } | ||
289 | |||
290 | static int ssl3250a_pm_regulator_dis(struct ssl3250a_info *info, | ||
291 | struct nvc_regulator *sreg) | ||
292 | { | ||
293 | int err = 0; | ||
294 | |||
295 | if (sreg->vreg_flag && (sreg->vreg != NULL)) { | ||
296 | err = regulator_disable(sreg->vreg); | ||
297 | if (!err) | ||
298 | dev_dbg(&info->i2c_client->dev, | ||
299 | "%s vreg_name: %s\n", | ||
300 | __func__, sreg->vreg_name); | ||
301 | else | ||
302 | dev_err(&info->i2c_client->dev, | ||
303 | "%s err, regulator: %s\n", | ||
304 | __func__, sreg->vreg_name); | ||
305 | } | ||
306 | sreg->vreg_flag = 0; | ||
307 | return err; | ||
308 | } | ||
309 | |||
310 | static int ssl3250a_pm_wr(struct ssl3250a_info *info, int pwr) | ||
311 | { | ||
312 | int err = 0; | ||
313 | |||
314 | if (pwr == info->pwr_dev) | ||
315 | return 0; | ||
316 | |||
317 | switch (pwr) { | ||
318 | case NVC_PWR_OFF: | ||
319 | if ((info->pdata->cfg & NVC_CFG_OFF2STDBY) || | ||
320 | (info->pdata->cfg & NVC_CFG_BOOT_INIT)) { | ||
321 | pwr = NVC_PWR_STDBY; | ||
322 | } else { | ||
323 | ssl3250a_gpio_act(info, 0); | ||
324 | err = ssl3250a_pm_regulator_dis(info, &info->vreg_i2c); | ||
325 | break; | ||
326 | } | ||
327 | case NVC_PWR_STDBY_OFF: | ||
328 | if ((info->pdata->cfg & NVC_CFG_OFF2STDBY) || | ||
329 | (info->pdata->cfg & NVC_CFG_BOOT_INIT)) { | ||
330 | pwr = NVC_PWR_STDBY; | ||
331 | } else { | ||
332 | ssl3250a_gpio_act(info, 0); | ||
333 | err = ssl3250a_pm_regulator_en(info, &info->vreg_i2c); | ||
334 | break; | ||
335 | } | ||
336 | case NVC_PWR_STDBY: | ||
337 | err = ssl3250a_pm_regulator_en(info, &info->vreg_i2c); | ||
338 | ssl3250a_gpio_act(info, 1); | ||
339 | err |= ssl3250a_i2c_wr(info, SSL3250A_REG_AMP, 0x00); | ||
340 | break; | ||
341 | |||
342 | case NVC_PWR_COMM: | ||
343 | case NVC_PWR_ON: | ||
344 | err = ssl3250a_pm_regulator_en(info, &info->vreg_i2c); | ||
345 | ssl3250a_gpio_act(info, 1); | ||
346 | break; | ||
347 | |||
348 | default: | ||
349 | err = -EINVAL; | ||
350 | break; | ||
351 | } | ||
352 | |||
353 | if (err < 0) { | ||
354 | dev_err(&info->i2c_client->dev, "%s error\n", __func__); | ||
355 | pwr = NVC_PWR_ERR; | ||
356 | } | ||
357 | info->pwr_dev = pwr; | ||
358 | if (err > 0) | ||
359 | return 0; | ||
360 | |||
361 | return err; | ||
362 | } | ||
363 | |||
364 | static int ssl3250a_pm_wr_s(struct ssl3250a_info *info, int pwr) | ||
365 | { | ||
366 | int err1 = 0; | ||
367 | int err2 = 0; | ||
368 | |||
369 | if ((info->s_mode == NVC_SYNC_OFF) || | ||
370 | (info->s_mode == NVC_SYNC_MASTER) || | ||
371 | (info->s_mode == NVC_SYNC_STEREO)) | ||
372 | err1 = ssl3250a_pm_wr(info, pwr); | ||
373 | if ((info->s_mode == NVC_SYNC_SLAVE) || | ||
374 | (info->s_mode == NVC_SYNC_STEREO)) | ||
375 | err2 = ssl3250a_pm_wr(info->s_info, pwr); | ||
376 | return err1 | err2; | ||
377 | } | ||
378 | |||
379 | static int ssl3250a_pm_api_wr(struct ssl3250a_info *info, int pwr) | ||
380 | { | ||
381 | int err = 0; | ||
382 | |||
383 | if (!pwr || (pwr > NVC_PWR_ON)) | ||
384 | return 0; | ||
385 | |||
386 | if (pwr > info->pwr_dev) | ||
387 | err = ssl3250a_pm_wr_s(info, pwr); | ||
388 | if (!err) | ||
389 | info->pwr_api = pwr; | ||
390 | else | ||
391 | info->pwr_api = NVC_PWR_ERR; | ||
392 | if (info->pdata->cfg & NVC_CFG_NOERR) | ||
393 | return 0; | ||
394 | |||
395 | return err; | ||
396 | } | ||
397 | |||
398 | static int ssl3250a_pm_dev_wr(struct ssl3250a_info *info, int pwr) | ||
399 | { | ||
400 | if (pwr < info->pwr_api) | ||
401 | pwr = info->pwr_api; | ||
402 | return ssl3250a_pm_wr(info, pwr); | ||
403 | } | ||
404 | |||
405 | static void ssl3250a_pm_exit(struct ssl3250a_info *info) | ||
406 | { | ||
407 | ssl3250a_pm_wr_s(info, NVC_PWR_OFF); | ||
408 | ssl3250a_pm_regulator_put(&info->vreg_i2c); | ||
409 | } | ||
410 | |||
411 | static void ssl3250a_pm_init(struct ssl3250a_info *info) | ||
412 | { | ||
413 | ssl3250a_pm_regulator_get(info, &info->vreg_i2c, "vdd_i2c"); | ||
414 | } | ||
415 | |||
416 | static int ssl3250a_dev_id(struct ssl3250a_info *info) | ||
417 | { | ||
418 | u8 addr; | ||
419 | u8 reg; | ||
420 | int err; | ||
421 | |||
422 | ssl3250a_pm_dev_wr(info, NVC_PWR_COMM); | ||
423 | /* There isn't a device ID so we just check that all the registers | ||
424 | * equal their startup defaults, which in this case, is 0. | ||
425 | */ | ||
426 | for (addr = 0; addr < SSL3250A_REG_STS; addr++) { | ||
427 | err = ssl3250a_i2c_rd(info, addr, ®); | ||
428 | if (err) { | ||
429 | break; | ||
430 | } else { | ||
431 | if (reg) { | ||
432 | err = -ENODEV; | ||
433 | break; | ||
434 | } | ||
435 | } | ||
436 | } | ||
437 | ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); | ||
438 | return err; | ||
439 | } | ||
440 | |||
441 | static int ssl3250a_param_rd(struct ssl3250a_info *info, long arg) | ||
442 | { | ||
443 | struct nvc_param params; | ||
444 | struct nvc_torch_pin_state pinstate; | ||
445 | const void *data_ptr; | ||
446 | u32 data_size = 0; | ||
447 | int err; | ||
448 | u8 reg; | ||
449 | |||
450 | if (copy_from_user(¶ms, | ||
451 | (const void __user *)arg, | ||
452 | sizeof(struct nvc_param))) { | ||
453 | dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", | ||
454 | __func__, __LINE__); | ||
455 | return -EINVAL; | ||
456 | } | ||
457 | |||
458 | if (info->s_mode == NVC_SYNC_SLAVE) | ||
459 | info = info->s_info; | ||
460 | switch (params.param) { | ||
461 | case NVC_PARAM_FLASH_CAPS: | ||
462 | dev_dbg(&info->i2c_client->dev, "%s FLASH_CAPS\n", __func__); | ||
463 | data_ptr = &ssl3250a_flash_cap; | ||
464 | data_size = ssl3250a_flash_cap_size; | ||
465 | break; | ||
466 | |||
467 | case NVC_PARAM_FLASH_LEVEL: | ||
468 | ssl3250a_pm_dev_wr(info, NVC_PWR_COMM); | ||
469 | err = ssl3250a_i2c_rd(info, SSL3250A_REG_AMP, ®); | ||
470 | ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); | ||
471 | if (err < 0) | ||
472 | return err; | ||
473 | |||
474 | reg >>= 3; /*7:3=flash amps*/ | ||
475 | reg &= 0x1F; /*4:0=flash amps*/ | ||
476 | if (reg < 12) /*flash starts at 12*/ | ||
477 | reg = 0; /*<12=torch or off*/ | ||
478 | else | ||
479 | reg -= 11; /*create flash index*/ | ||
480 | dev_dbg(&info->i2c_client->dev, "%s FLASH_LEVEL: %u\n", | ||
481 | __func__, | ||
482 | (unsigned)ssl3250a_flash_cap.levels[reg].guidenum); | ||
483 | data_ptr = &ssl3250a_flash_cap.levels[reg].guidenum; | ||
484 | data_size = sizeof(ssl3250a_flash_cap.levels[reg].guidenum); | ||
485 | break; | ||
486 | |||
487 | case NVC_PARAM_TORCH_CAPS: | ||
488 | dev_dbg(&info->i2c_client->dev, "%s TORCH_CAPS\n", __func__); | ||
489 | data_ptr = &ssl3250a_torch_cap; | ||
490 | data_size = ssl3250a_torch_cap_size; | ||
491 | break; | ||
492 | |||
493 | case NVC_PARAM_TORCH_LEVEL: | ||
494 | ssl3250a_pm_dev_wr(info, NVC_PWR_COMM); | ||
495 | err = ssl3250a_i2c_rd(info, SSL3250A_REG_AMP, ®); | ||
496 | ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); | ||
497 | if (err < 0) | ||
498 | return err; | ||
499 | |||
500 | reg >>= 3; /*7:3=torch amps*/ | ||
501 | reg &= 0x1F; /*4:0=torch amps*/ | ||
502 | if (reg > 11) /*flash starts at 12*/ | ||
503 | reg = 0; /*>11=flash mode (torch off)*/ | ||
504 | dev_dbg(&info->i2c_client->dev, "%s TORCH_LEVEL: %u\n", | ||
505 | __func__, | ||
506 | (unsigned)ssl3250a_torch_cap.guidenum[reg]); | ||
507 | data_ptr = &ssl3250a_torch_cap.guidenum[reg]; | ||
508 | data_size = sizeof(ssl3250a_torch_cap.guidenum[reg]); | ||
509 | break; | ||
510 | |||
511 | case NVC_PARAM_FLASH_PIN_STATE: | ||
512 | pinstate.mask = info->pdata->pinstate->mask; | ||
513 | ssl3250a_pm_dev_wr(info, NVC_PWR_COMM); | ||
514 | err = ssl3250a_i2c_rd(info, SSL3250A_REG_AMP, ®); | ||
515 | ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); | ||
516 | if (err < 0) | ||
517 | return err; | ||
518 | |||
519 | reg >>= 3; /*7:3=flash amps*/ | ||
520 | reg &= 0x1F; /*4:0=flash amps*/ | ||
521 | if (reg < 12) /*flash starts at 12*/ | ||
522 | pinstate.values = 0; /*deassert strobe*/ | ||
523 | else | ||
524 | /*assert strobe*/ | ||
525 | pinstate.values = info->pdata->pinstate->values; | ||
526 | dev_dbg(&info->i2c_client->dev, "%s FLASH_PIN_STATE: %x&%x\n", | ||
527 | __func__, pinstate.mask, pinstate.values); | ||
528 | data_ptr = &pinstate; | ||
529 | data_size = sizeof(struct nvc_torch_pin_state); | ||
530 | break; | ||
531 | |||
532 | case NVC_PARAM_STEREO: | ||
533 | dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n", | ||
534 | __func__, info->s_mode); | ||
535 | data_ptr = &info->s_mode; | ||
536 | data_size = sizeof(info->s_mode); | ||
537 | break; | ||
538 | |||
539 | default: | ||
540 | dev_err(&info->i2c_client->dev, | ||
541 | "%s unsupported parameter: %d\n", | ||
542 | __func__, params.param); | ||
543 | return -EINVAL; | ||
544 | } | ||
545 | |||
546 | if (params.sizeofvalue < data_size) { | ||
547 | dev_err(&info->i2c_client->dev, | ||
548 | "%s data size mismatch %d != %d\n", | ||
549 | __func__, params.sizeofvalue, data_size); | ||
550 | return -EINVAL; | ||
551 | } | ||
552 | |||
553 | if (copy_to_user((void __user *)params.p_value, | ||
554 | data_ptr, | ||
555 | data_size)) { | ||
556 | dev_err(&info->i2c_client->dev, | ||
557 | "%s copy_to_user err line %d\n", | ||
558 | __func__, __LINE__); | ||
559 | return -EFAULT; | ||
560 | } | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | static int ssl3250a_param_wr_s(struct ssl3250a_info *info, | ||
566 | struct nvc_param *params, | ||
567 | u8 val) | ||
568 | { | ||
569 | int err; | ||
570 | |||
571 | switch (params->param) { | ||
572 | case NVC_PARAM_FLASH_LEVEL: | ||
573 | dev_dbg(&info->i2c_client->dev, "%s FLASH_LEVEL: %d\n", | ||
574 | __func__, val); | ||
575 | ssl3250a_pm_dev_wr(info, NVC_PWR_ON); | ||
576 | if (val > ssl3250a_default_pdata.max_amp_flash) | ||
577 | val = ssl3250a_default_pdata.max_amp_flash; | ||
578 | /*Amp limit values are in the board-sensors file.*/ | ||
579 | if (info->pdata->max_amp_flash && | ||
580 | (val > info->pdata->max_amp_flash)) | ||
581 | val = info->pdata->max_amp_flash; | ||
582 | if (val) { | ||
583 | val += 11; /*flash starts at 12*/ | ||
584 | val <<= 3; /*7:3=flash/torch amps*/ | ||
585 | } | ||
586 | err = ssl3250a_i2c_wr(info, SSL3250A_REG_AMP, val); | ||
587 | if (!val) /*turn pwr off if no flash && no pwr_api*/ | ||
588 | ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); | ||
589 | return err; | ||
590 | |||
591 | case NVC_PARAM_TORCH_LEVEL: | ||
592 | dev_dbg(&info->i2c_client->dev, "%s TORCH_LEVEL: %d\n", | ||
593 | __func__, val); | ||
594 | ssl3250a_pm_dev_wr(info, NVC_PWR_ON); | ||
595 | if (val > ssl3250a_default_pdata.max_amp_torch) | ||
596 | val = ssl3250a_default_pdata.max_amp_torch; | ||
597 | /*Amp limit values are in the board-sensors file.*/ | ||
598 | if (info->pdata->max_amp_torch && | ||
599 | (val > info->pdata->max_amp_torch)) | ||
600 | val = info->pdata->max_amp_torch; | ||
601 | if (val) | ||
602 | val <<= 3; /*7:3=flash/torch amps*/ | ||
603 | err = ssl3250a_i2c_wr(info, SSL3250A_REG_AMP, val); | ||
604 | if (!val) /*turn pwr off if no torch && no pwr_api*/ | ||
605 | ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); | ||
606 | return err; | ||
607 | |||
608 | case NVC_PARAM_FLASH_PIN_STATE: | ||
609 | dev_dbg(&info->i2c_client->dev, "%s FLASH_PIN_STATE: %d\n", | ||
610 | __func__, val); | ||
611 | if (val) | ||
612 | val = 0x01; /*0:0=soft trigger*/ | ||
613 | return ssl3250a_i2c_wr(info, SSL3250A_REG_STRB, val); | ||
614 | |||
615 | default: | ||
616 | dev_err(&info->i2c_client->dev, | ||
617 | "%s unsupported parameter: %d\n", | ||
618 | __func__, params->param); | ||
619 | return -EINVAL; | ||
620 | } | ||
621 | } | ||
622 | |||
623 | static int ssl3250a_param_wr(struct ssl3250a_info *info, long arg) | ||
624 | { | ||
625 | struct nvc_param params; | ||
626 | u8 val; | ||
627 | int err = 0; | ||
628 | |||
629 | if (copy_from_user(¶ms, | ||
630 | (const void __user *)arg, | ||
631 | sizeof(struct nvc_param))) { | ||
632 | dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", | ||
633 | __func__, __LINE__); | ||
634 | return -EINVAL; | ||
635 | } | ||
636 | |||
637 | if (copy_from_user(&val, (const void __user *)params.p_value, | ||
638 | sizeof(val))) { | ||
639 | dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", | ||
640 | __func__, __LINE__); | ||
641 | return -EINVAL; | ||
642 | } | ||
643 | |||
644 | /* parameters independent of sync mode */ | ||
645 | switch (params.param) { | ||
646 | case NVC_PARAM_STEREO: | ||
647 | dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n", | ||
648 | __func__, (int)val); | ||
649 | if (val == info->s_mode) | ||
650 | return 0; | ||
651 | |||
652 | switch (val) { | ||
653 | case NVC_SYNC_OFF: | ||
654 | info->s_mode = val; | ||
655 | if (info->s_info != NULL) { | ||
656 | info->s_info->s_mode = val; | ||
657 | ssl3250a_pm_wr(info->s_info, NVC_PWR_OFF); | ||
658 | } | ||
659 | break; | ||
660 | |||
661 | case NVC_SYNC_MASTER: | ||
662 | info->s_mode = val; | ||
663 | if (info->s_info != NULL) | ||
664 | info->s_info->s_mode = val; | ||
665 | break; | ||
666 | |||
667 | case NVC_SYNC_SLAVE: | ||
668 | case NVC_SYNC_STEREO: | ||
669 | if (info->s_info != NULL) { | ||
670 | /* sync power */ | ||
671 | info->s_info->pwr_api = info->pwr_api; | ||
672 | err = ssl3250a_pm_wr(info->s_info, | ||
673 | info->pwr_dev); | ||
674 | if (!err) { | ||
675 | info->s_mode = val; | ||
676 | info->s_info->s_mode = val; | ||
677 | } else { | ||
678 | ssl3250a_pm_wr(info->s_info, | ||
679 | NVC_PWR_OFF); | ||
680 | err = -EIO; | ||
681 | } | ||
682 | } else { | ||
683 | err = -EINVAL; | ||
684 | } | ||
685 | break; | ||
686 | |||
687 | default: | ||
688 | err = -EINVAL; | ||
689 | } | ||
690 | if (info->pdata->cfg & NVC_CFG_NOERR) | ||
691 | return 0; | ||
692 | |||
693 | return err; | ||
694 | |||
695 | default: | ||
696 | /* parameters dependent on sync mode */ | ||
697 | switch (info->s_mode) { | ||
698 | case NVC_SYNC_OFF: | ||
699 | case NVC_SYNC_MASTER: | ||
700 | return ssl3250a_param_wr_s(info, ¶ms, val); | ||
701 | |||
702 | case NVC_SYNC_SLAVE: | ||
703 | return ssl3250a_param_wr_s(info->s_info, | ||
704 | ¶ms, | ||
705 | val); | ||
706 | |||
707 | case NVC_SYNC_STEREO: | ||
708 | err = ssl3250a_param_wr_s(info, ¶ms, val); | ||
709 | if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX)) | ||
710 | err |= ssl3250a_param_wr_s(info->s_info, | ||
711 | ¶ms, | ||
712 | val); | ||
713 | return err; | ||
714 | |||
715 | default: | ||
716 | dev_err(&info->i2c_client->dev, "%s %d internal err\n", | ||
717 | __func__, __LINE__); | ||
718 | return -EINVAL; | ||
719 | } | ||
720 | } | ||
721 | } | ||
722 | |||
723 | static long ssl3250a_ioctl(struct file *file, | ||
724 | unsigned int cmd, | ||
725 | unsigned long arg) | ||
726 | { | ||
727 | struct ssl3250a_info *info = file->private_data; | ||
728 | int pwr; | ||
729 | |||
730 | switch (cmd) { | ||
731 | case NVC_IOCTL_PARAM_WR: | ||
732 | return ssl3250a_param_wr(info, arg); | ||
733 | |||
734 | case NVC_IOCTL_PARAM_RD: | ||
735 | return ssl3250a_param_rd(info, arg); | ||
736 | |||
737 | case NVC_IOCTL_PWR_WR: | ||
738 | /* This is a Guaranteed Level of Service (GLOS) call */ | ||
739 | pwr = (int)arg * 2; | ||
740 | dev_dbg(&info->i2c_client->dev, "%s PWR_WR: %d\n", | ||
741 | __func__, pwr); | ||
742 | return ssl3250a_pm_api_wr(info, pwr); | ||
743 | |||
744 | case NVC_IOCTL_PWR_RD: | ||
745 | if (info->s_mode == NVC_SYNC_SLAVE) | ||
746 | pwr = info->s_info->pwr_api / 2; | ||
747 | else | ||
748 | pwr = info->pwr_api / 2; | ||
749 | dev_dbg(&info->i2c_client->dev, "%s PWR_RD: %d\n", | ||
750 | __func__, pwr); | ||
751 | if (copy_to_user((void __user *)arg, (const void *)&pwr, | ||
752 | sizeof(pwr))) { | ||
753 | dev_err(&info->i2c_client->dev, | ||
754 | "%s copy_to_user err line %d\n", | ||
755 | __func__, __LINE__); | ||
756 | return -EFAULT; | ||
757 | } | ||
758 | |||
759 | return 0; | ||
760 | |||
761 | default: | ||
762 | dev_err(&info->i2c_client->dev, "%s unsupported ioctl: %x\n", | ||
763 | __func__, cmd); | ||
764 | return -EINVAL; | ||
765 | } | ||
766 | } | ||
767 | |||
768 | static int ssl3250a_sync_en(int dev1, int dev2) | ||
769 | { | ||
770 | struct ssl3250a_info *sync1 = NULL; | ||
771 | struct ssl3250a_info *sync2 = NULL; | ||
772 | struct ssl3250a_info *pos = NULL; | ||
773 | |||
774 | rcu_read_lock(); | ||
775 | list_for_each_entry_rcu(pos, &ssl3250a_info_list, list) { | ||
776 | if (pos->pdata->num == dev1) { | ||
777 | sync1 = pos; | ||
778 | break; | ||
779 | } | ||
780 | } | ||
781 | pos = NULL; | ||
782 | list_for_each_entry_rcu(pos, &ssl3250a_info_list, list) { | ||
783 | if (pos->pdata->num == dev2) { | ||
784 | sync2 = pos; | ||
785 | break; | ||
786 | } | ||
787 | } | ||
788 | rcu_read_unlock(); | ||
789 | if (sync1 != NULL) | ||
790 | sync1->s_info = NULL; | ||
791 | if (sync2 != NULL) | ||
792 | sync2->s_info = NULL; | ||
793 | if (!dev1 && !dev2) | ||
794 | return 0; /* no err if default instance 0's used */ | ||
795 | |||
796 | if (dev1 == dev2) | ||
797 | return -EINVAL; /* err if sync instance is itself */ | ||
798 | |||
799 | if ((sync1 != NULL) && (sync2 != NULL)) { | ||
800 | sync1->s_info = sync2; | ||
801 | sync2->s_info = sync1; | ||
802 | } | ||
803 | |||
804 | return 0; | ||
805 | } | ||
806 | |||
807 | static int ssl3250a_sync_dis(struct ssl3250a_info *info) | ||
808 | { | ||
809 | if (info->s_info != NULL) { | ||
810 | info->s_info->s_mode = 0; | ||
811 | info->s_info->s_info = NULL; | ||
812 | info->s_mode = 0; | ||
813 | info->s_info = NULL; | ||
814 | return 0; | ||
815 | } | ||
816 | |||
817 | return -EINVAL; | ||
818 | } | ||
819 | |||
820 | static int ssl3250a_open(struct inode *inode, struct file *file) | ||
821 | { | ||
822 | struct ssl3250a_info *info = NULL; | ||
823 | struct ssl3250a_info *pos = NULL; | ||
824 | int err; | ||
825 | |||
826 | rcu_read_lock(); | ||
827 | list_for_each_entry_rcu(pos, &ssl3250a_info_list, list) { | ||
828 | if (pos->miscdev.minor == iminor(inode)) { | ||
829 | info = pos; | ||
830 | break; | ||
831 | } | ||
832 | } | ||
833 | rcu_read_unlock(); | ||
834 | if (!info) | ||
835 | return -ENODEV; | ||
836 | |||
837 | err = ssl3250a_sync_en(info->pdata->num, info->pdata->sync); | ||
838 | if (err == -EINVAL) | ||
839 | dev_err(&info->i2c_client->dev, | ||
840 | "%s err: invalid num (%u) and sync (%u) instance\n", | ||
841 | __func__, info->pdata->num, info->pdata->sync); | ||
842 | if (atomic_xchg(&info->in_use, 1)) | ||
843 | return -EBUSY; | ||
844 | |||
845 | if (info->s_info != NULL) { | ||
846 | if (atomic_xchg(&info->s_info->in_use, 1)) | ||
847 | return -EBUSY; | ||
848 | } | ||
849 | |||
850 | file->private_data = info; | ||
851 | dev_dbg(&info->i2c_client->dev, "%s\n", __func__); | ||
852 | return 0; | ||
853 | } | ||
854 | |||
855 | static int ssl3250a_release(struct inode *inode, struct file *file) | ||
856 | { | ||
857 | struct ssl3250a_info *info = file->private_data; | ||
858 | |||
859 | dev_dbg(&info->i2c_client->dev, "%s\n", __func__); | ||
860 | ssl3250a_pm_wr_s(info, NVC_PWR_OFF); | ||
861 | file->private_data = NULL; | ||
862 | WARN_ON(!atomic_xchg(&info->in_use, 0)); | ||
863 | if (info->s_info != NULL) | ||
864 | WARN_ON(!atomic_xchg(&info->s_info->in_use, 0)); | ||
865 | ssl3250a_sync_dis(info); | ||
866 | return 0; | ||
867 | } | ||
868 | |||
869 | static const struct file_operations ssl3250a_fileops = { | ||
870 | .owner = THIS_MODULE, | ||
871 | .open = ssl3250a_open, | ||
872 | .unlocked_ioctl = ssl3250a_ioctl, | ||
873 | .release = ssl3250a_release, | ||
874 | }; | ||
875 | |||
876 | static void ssl3250a_del(struct ssl3250a_info *info) | ||
877 | { | ||
878 | ssl3250a_pm_exit(info); | ||
879 | ssl3250a_sync_dis(info); | ||
880 | spin_lock(&ssl3250a_spinlock); | ||
881 | list_del_rcu(&info->list); | ||
882 | spin_unlock(&ssl3250a_spinlock); | ||
883 | synchronize_rcu(); | ||
884 | } | ||
885 | |||
886 | static int ssl3250a_remove(struct i2c_client *client) | ||
887 | { | ||
888 | struct ssl3250a_info *info = i2c_get_clientdata(client); | ||
889 | |||
890 | dev_dbg(&info->i2c_client->dev, "%s\n", __func__); | ||
891 | misc_deregister(&info->miscdev); | ||
892 | ssl3250a_del(info); | ||
893 | return 0; | ||
894 | } | ||
895 | |||
896 | static int ssl3250a_probe( | ||
897 | struct i2c_client *client, | ||
898 | const struct i2c_device_id *id) | ||
899 | { | ||
900 | struct ssl3250a_info *info; | ||
901 | char dname[16]; | ||
902 | int err; | ||
903 | |||
904 | dev_dbg(&client->dev, "%s\n", __func__); | ||
905 | info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); | ||
906 | if (info == NULL) { | ||
907 | dev_err(&client->dev, "%s: kzalloc error\n", __func__); | ||
908 | return -ENOMEM; | ||
909 | } | ||
910 | |||
911 | info->i2c_client = client; | ||
912 | if (client->dev.platform_data) { | ||
913 | info->pdata = client->dev.platform_data; | ||
914 | } else { | ||
915 | info->pdata = &ssl3250a_default_pdata; | ||
916 | dev_dbg(&client->dev, | ||
917 | "%s No platform data. Using defaults.\n", | ||
918 | __func__); | ||
919 | } | ||
920 | i2c_set_clientdata(client, info); | ||
921 | INIT_LIST_HEAD(&info->list); | ||
922 | spin_lock(&ssl3250a_spinlock); | ||
923 | list_add_rcu(&info->list, &ssl3250a_info_list); | ||
924 | spin_unlock(&ssl3250a_spinlock); | ||
925 | ssl3250a_pm_init(info); | ||
926 | err = ssl3250a_dev_id(info); | ||
927 | if (err < 0) { | ||
928 | dev_err(&client->dev, "%s device not found\n", __func__); | ||
929 | if (info->pdata->cfg & NVC_CFG_NODEV) { | ||
930 | ssl3250a_del(info); | ||
931 | return -ENODEV; | ||
932 | } | ||
933 | } else { | ||
934 | dev_dbg(&client->dev, "%s device found\n", __func__); | ||
935 | } | ||
936 | |||
937 | if (info->pdata->dev_name != 0) | ||
938 | strcpy(dname, info->pdata->dev_name); | ||
939 | else | ||
940 | strcpy(dname, "ssl3250a"); | ||
941 | if (info->pdata->num) | ||
942 | snprintf(dname, sizeof(dname), "%s.%u", | ||
943 | dname, info->pdata->num); | ||
944 | info->miscdev.name = dname; | ||
945 | info->miscdev.fops = &ssl3250a_fileops; | ||
946 | info->miscdev.minor = MISC_DYNAMIC_MINOR; | ||
947 | if (misc_register(&info->miscdev)) { | ||
948 | dev_err(&client->dev, "%s unable to register misc device %s\n", | ||
949 | __func__, dname); | ||
950 | ssl3250a_del(info); | ||
951 | return -ENODEV; | ||
952 | } | ||
953 | |||
954 | return 0; | ||
955 | } | ||
956 | |||
957 | static const struct i2c_device_id ssl3250a_id[] = { | ||
958 | { "ssl3250a", 0 }, | ||
959 | { }, | ||
960 | }; | ||
961 | |||
962 | MODULE_DEVICE_TABLE(i2c, ssl3250a_id); | ||
963 | |||
964 | static struct i2c_driver ssl3250a_i2c_driver = { | ||
965 | .driver = { | ||
966 | .name = "ssl3250a", | ||
967 | .owner = THIS_MODULE, | ||
968 | }, | ||
969 | .id_table = ssl3250a_id, | ||
970 | .probe = ssl3250a_probe, | ||
971 | .remove = ssl3250a_remove, | ||
972 | }; | ||
973 | |||
974 | static int __init ssl3250a_init(void) | ||
975 | { | ||
976 | return i2c_add_driver(&ssl3250a_i2c_driver); | ||
977 | } | ||
978 | |||
979 | static void __exit ssl3250a_exit(void) | ||
980 | { | ||
981 | i2c_del_driver(&ssl3250a_i2c_driver); | ||
982 | } | ||
983 | |||
984 | module_init(ssl3250a_init); | ||
985 | module_exit(ssl3250a_exit); | ||
986 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/video/tegra/tegra_camera.c b/drivers/media/video/tegra/tegra_camera.c new file mode 100644 index 00000000000..e90b130bcd6 --- /dev/null +++ b/drivers/media/video/tegra/tegra_camera.c | |||
@@ -0,0 +1,612 @@ | |||
1 | /* | ||
2 | * drivers/media/video/tegra/tegra_camera.c | ||
3 | * | ||
4 | * Copyright (C) 2010 Google, Inc. | ||
5 | * Copyright (C) 2012 Nvidia Corp | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/miscdevice.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/ioctl.h> | ||
21 | #include <linux/fs.h> | ||
22 | #include <linux/device.h> | ||
23 | #include <linux/regulator/consumer.h> | ||
24 | #include <linux/clk.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/uaccess.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <mach/iomap.h> | ||
29 | #include <mach/clk.h> | ||
30 | #include <mach/powergate.h> | ||
31 | |||
32 | #include <media/tegra_camera.h> | ||
33 | |||
34 | /* Eventually this should handle all clock and reset calls for the isp, vi, | ||
35 | * vi_sensor, and csi modules, replacing nvrm and nvos completely for camera | ||
36 | */ | ||
37 | #define TEGRA_CAMERA_NAME "tegra_camera" | ||
38 | |||
39 | struct tegra_camera_dev { | ||
40 | struct device *dev; | ||
41 | struct miscdevice misc_dev; | ||
42 | struct clk *isp_clk; | ||
43 | struct clk *vi_clk; | ||
44 | struct clk *vi_sensor_clk; | ||
45 | struct clk *csus_clk; | ||
46 | struct clk *csi_clk; | ||
47 | struct clk *emc_clk; | ||
48 | struct regulator *reg; | ||
49 | struct tegra_camera_clk_info info; | ||
50 | struct mutex tegra_camera_lock; | ||
51 | int power_refcnt; | ||
52 | }; | ||
53 | |||
54 | struct tegra_camera_block { | ||
55 | int (*enable) (struct tegra_camera_dev *dev); | ||
56 | int (*disable) (struct tegra_camera_dev *dev); | ||
57 | bool is_enabled; | ||
58 | }; | ||
59 | |||
60 | static int tegra_camera_enable_isp(struct tegra_camera_dev *dev) | ||
61 | { | ||
62 | return clk_enable(dev->isp_clk); | ||
63 | } | ||
64 | |||
65 | static int tegra_camera_disable_isp(struct tegra_camera_dev *dev) | ||
66 | { | ||
67 | clk_disable(dev->isp_clk); | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | static int tegra_camera_enable_vi(struct tegra_camera_dev *dev) | ||
72 | { | ||
73 | int ret = 0; | ||
74 | |||
75 | ret |= clk_enable(dev->vi_clk); | ||
76 | ret |= clk_enable(dev->vi_sensor_clk); | ||
77 | ret |= clk_enable(dev->csus_clk); | ||
78 | return ret; | ||
79 | } | ||
80 | |||
81 | static int tegra_camera_disable_vi(struct tegra_camera_dev *dev) | ||
82 | { | ||
83 | clk_disable(dev->vi_clk); | ||
84 | clk_disable(dev->vi_sensor_clk); | ||
85 | clk_disable(dev->csus_clk); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static int tegra_camera_enable_csi(struct tegra_camera_dev *dev) | ||
90 | { | ||
91 | return clk_enable(dev->csi_clk); | ||
92 | } | ||
93 | |||
94 | static int tegra_camera_disable_csi(struct tegra_camera_dev *dev) | ||
95 | { | ||
96 | clk_disable(dev->csi_clk); | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static int tegra_camera_enable_emc(struct tegra_camera_dev *dev) | ||
101 | { | ||
102 | /* tegra_camera wasn't added as a user of emc_clk until 3x. | ||
103 | set to 150 MHz, will likely need to be increased as we support | ||
104 | sensors with higher framerates and resolutions. */ | ||
105 | clk_enable(dev->emc_clk); | ||
106 | #ifdef CONFIG_ARCH_TEGRA_2x_SOC | ||
107 | clk_set_rate(dev->emc_clk, 300000000); | ||
108 | #else | ||
109 | clk_set_rate(dev->emc_clk, 150000000); | ||
110 | #endif | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int tegra_camera_disable_emc(struct tegra_camera_dev *dev) | ||
115 | { | ||
116 | clk_disable(dev->emc_clk); | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | struct tegra_camera_block tegra_camera_block[] = { | ||
121 | [TEGRA_CAMERA_MODULE_ISP] = {tegra_camera_enable_isp, | ||
122 | tegra_camera_disable_isp, false}, | ||
123 | [TEGRA_CAMERA_MODULE_VI] = {tegra_camera_enable_vi, | ||
124 | tegra_camera_disable_vi, false}, | ||
125 | [TEGRA_CAMERA_MODULE_CSI] = {tegra_camera_enable_csi, | ||
126 | tegra_camera_disable_csi, false}, | ||
127 | }; | ||
128 | |||
129 | #define TEGRA_CAMERA_VI_CLK_SEL_INTERNAL 0 | ||
130 | #define TEGRA_CAMERA_VI_CLK_SEL_EXTERNAL (1<<24) | ||
131 | #define TEGRA_CAMERA_PD2VI_CLK_SEL_VI_SENSOR_CLK (1<<25) | ||
132 | #define TEGRA_CAMERA_PD2VI_CLK_SEL_PD2VI_CLK 0 | ||
133 | |||
134 | static bool tegra_camera_enabled(struct tegra_camera_dev *dev) | ||
135 | { | ||
136 | bool ret = false; | ||
137 | |||
138 | mutex_lock(&dev->tegra_camera_lock); | ||
139 | ret = tegra_camera_block[TEGRA_CAMERA_MODULE_ISP].is_enabled == true || | ||
140 | tegra_camera_block[TEGRA_CAMERA_MODULE_VI].is_enabled == true || | ||
141 | tegra_camera_block[TEGRA_CAMERA_MODULE_CSI].is_enabled == true; | ||
142 | mutex_unlock(&dev->tegra_camera_lock); | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev) | ||
147 | { | ||
148 | struct clk *clk, *clk_parent; | ||
149 | struct tegra_camera_clk_info *info = &dev->info; | ||
150 | unsigned long parent_rate, parent_div_rate, parent_div_rate_pre; | ||
151 | |||
152 | if (!info) { | ||
153 | dev_err(dev->dev, | ||
154 | "%s: no clock info %d\n", | ||
155 | __func__, info->id); | ||
156 | return -EINVAL; | ||
157 | } | ||
158 | |||
159 | if (info->id != TEGRA_CAMERA_MODULE_VI) { | ||
160 | dev_err(dev->dev, | ||
161 | "%s: set rate only aplies to vi module %d\n", | ||
162 | __func__, info->id); | ||
163 | return -EINVAL; | ||
164 | } | ||
165 | |||
166 | switch (info->clk_id) { | ||
167 | case TEGRA_CAMERA_VI_CLK: | ||
168 | clk = dev->vi_clk; | ||
169 | break; | ||
170 | case TEGRA_CAMERA_VI_SENSOR_CLK: | ||
171 | clk = dev->vi_sensor_clk; | ||
172 | break; | ||
173 | default: | ||
174 | dev_err(dev->dev, | ||
175 | "%s: invalid clk id for set rate %d\n", | ||
176 | __func__, info->clk_id); | ||
177 | return -EINVAL; | ||
178 | } | ||
179 | |||
180 | clk_parent = clk_get_parent(clk); | ||
181 | parent_rate = clk_get_rate(clk_parent); | ||
182 | dev_dbg(dev->dev, "%s: clk_id=%d, parent_rate=%lu, clk_rate=%lu\n", | ||
183 | __func__, info->clk_id, parent_rate, info->rate); | ||
184 | parent_div_rate = parent_rate; | ||
185 | parent_div_rate_pre = parent_rate; | ||
186 | |||
187 | /* | ||
188 | * The requested clock rate from user space should be respected. | ||
189 | * This loop is to search the clock rate that is higher than requested | ||
190 | * clock. | ||
191 | */ | ||
192 | while (parent_div_rate >= info->rate) { | ||
193 | parent_div_rate_pre = parent_div_rate; | ||
194 | parent_div_rate = clk_round_rate(clk, parent_div_rate-1); | ||
195 | } | ||
196 | |||
197 | dev_dbg(dev->dev, "%s: set_rate=%lu", | ||
198 | __func__, parent_div_rate_pre); | ||
199 | |||
200 | clk_set_rate(clk, parent_div_rate_pre); | ||
201 | |||
202 | if (info->clk_id == TEGRA_CAMERA_VI_CLK) { | ||
203 | /* | ||
204 | * bit 25: 0 = pd2vi_Clk, 1 = vi_sensor_clk | ||
205 | * bit 24: 0 = internal clock, 1 = external clock(pd2vi_clk) | ||
206 | */ | ||
207 | if (info->flag == TEGRA_CAMERA_ENABLE_PD2VI_CLK) | ||
208 | tegra_clk_cfg_ex(clk, TEGRA_CLK_VI_INP_SEL, 2); | ||
209 | |||
210 | #ifdef CONFIG_ARCH_TEGRA_2x_SOC | ||
211 | u32 val; | ||
212 | void __iomem *apb_misc = IO_ADDRESS(TEGRA_APB_MISC_BASE); | ||
213 | val = readl(apb_misc + 0x42c); | ||
214 | writel(val | 0x1, apb_misc + 0x42c); | ||
215 | #endif | ||
216 | } | ||
217 | |||
218 | info->rate = clk_get_rate(clk); | ||
219 | dev_dbg(dev->dev, "%s: get_rate=%lu", | ||
220 | __func__, info->rate); | ||
221 | return 0; | ||
222 | |||
223 | } | ||
224 | static int tegra_camera_reset(struct tegra_camera_dev *dev, uint id) | ||
225 | { | ||
226 | struct clk *clk; | ||
227 | |||
228 | switch (id) { | ||
229 | case TEGRA_CAMERA_MODULE_VI: | ||
230 | clk = dev->vi_clk; | ||
231 | break; | ||
232 | case TEGRA_CAMERA_MODULE_ISP: | ||
233 | clk = dev->isp_clk; | ||
234 | break; | ||
235 | case TEGRA_CAMERA_MODULE_CSI: | ||
236 | clk = dev->csi_clk; | ||
237 | break; | ||
238 | default: | ||
239 | return -EINVAL; | ||
240 | } | ||
241 | tegra_periph_reset_assert(clk); | ||
242 | udelay(10); | ||
243 | tegra_periph_reset_deassert(clk); | ||
244 | |||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | static int tegra_camera_power_on(struct tegra_camera_dev *dev) | ||
249 | { | ||
250 | int ret = 0; | ||
251 | |||
252 | if (dev->power_refcnt++ == 0) { | ||
253 | /* Enable external power */ | ||
254 | if (dev->reg) { | ||
255 | ret = regulator_enable(dev->reg); | ||
256 | if (ret) { | ||
257 | dev_err(dev->dev, | ||
258 | "%s: enable csi regulator failed.\n", | ||
259 | __func__); | ||
260 | return ret; | ||
261 | } | ||
262 | } | ||
263 | #ifndef CONFIG_ARCH_TEGRA_2x_SOC | ||
264 | /* Unpowergate VE */ | ||
265 | ret = tegra_unpowergate_partition(TEGRA_POWERGATE_VENC); | ||
266 | if (ret) | ||
267 | dev_err(dev->dev, | ||
268 | "%s: unpowergate failed.\n", | ||
269 | __func__); | ||
270 | #endif | ||
271 | } | ||
272 | |||
273 | return ret; | ||
274 | } | ||
275 | |||
276 | static int tegra_camera_power_off(struct tegra_camera_dev *dev) | ||
277 | { | ||
278 | int ret = 0; | ||
279 | |||
280 | if (--dev->power_refcnt == 0) { | ||
281 | #ifndef CONFIG_ARCH_TEGRA_2x_SOC | ||
282 | /* Powergate VE */ | ||
283 | ret = tegra_powergate_partition(TEGRA_POWERGATE_VENC); | ||
284 | if (ret) | ||
285 | dev_err(dev->dev, | ||
286 | "%s: powergate failed.\n", | ||
287 | __func__); | ||
288 | #endif | ||
289 | /* Disable external power */ | ||
290 | if (dev->reg) { | ||
291 | ret = regulator_disable(dev->reg); | ||
292 | if (ret) { | ||
293 | dev_err(dev->dev, | ||
294 | "%s: disable csi regulator failed.\n", | ||
295 | __func__); | ||
296 | return ret; | ||
297 | } | ||
298 | } | ||
299 | } | ||
300 | return ret; | ||
301 | } | ||
302 | |||
303 | static long tegra_camera_ioctl(struct file *file, | ||
304 | unsigned int cmd, unsigned long arg) | ||
305 | { | ||
306 | uint id; | ||
307 | struct tegra_camera_dev *dev = file->private_data; | ||
308 | |||
309 | /* first element of arg must be u32 with id of module to talk to */ | ||
310 | if (copy_from_user(&id, (const void __user *)arg, sizeof(uint))) { | ||
311 | dev_err(dev->dev, | ||
312 | "%s: Failed to copy arg from user", __func__); | ||
313 | return -EFAULT; | ||
314 | } | ||
315 | |||
316 | if (id >= ARRAY_SIZE(tegra_camera_block)) { | ||
317 | dev_err(dev->dev, | ||
318 | "%s: Invalid id to tegra isp ioctl%d\n", | ||
319 | __func__, id); | ||
320 | return -EINVAL; | ||
321 | } | ||
322 | |||
323 | switch (cmd) { | ||
324 | case TEGRA_CAMERA_IOCTL_ENABLE: | ||
325 | { | ||
326 | int ret = 0; | ||
327 | |||
328 | mutex_lock(&dev->tegra_camera_lock); | ||
329 | /* Unpowergate camera blocks (vi, csi and isp) | ||
330 | before enabling clocks */ | ||
331 | ret = tegra_camera_power_on(dev); | ||
332 | if (ret) { | ||
333 | dev->power_refcnt = 0; | ||
334 | mutex_unlock(&dev->tegra_camera_lock); | ||
335 | return ret; | ||
336 | } | ||
337 | |||
338 | if (!tegra_camera_block[id].is_enabled) { | ||
339 | ret = tegra_camera_block[id].enable(dev); | ||
340 | tegra_camera_block[id].is_enabled = true; | ||
341 | } | ||
342 | mutex_unlock(&dev->tegra_camera_lock); | ||
343 | return ret; | ||
344 | } | ||
345 | case TEGRA_CAMERA_IOCTL_DISABLE: | ||
346 | { | ||
347 | int ret = 0; | ||
348 | |||
349 | mutex_lock(&dev->tegra_camera_lock); | ||
350 | if (tegra_camera_block[id].is_enabled) { | ||
351 | ret = tegra_camera_block[id].disable(dev); | ||
352 | tegra_camera_block[id].is_enabled = false; | ||
353 | } | ||
354 | /* Powergate camera blocks (vi, csi and isp) | ||
355 | after disabling all the clocks */ | ||
356 | if (!ret) { | ||
357 | ret = tegra_camera_power_off(dev); | ||
358 | } | ||
359 | mutex_unlock(&dev->tegra_camera_lock); | ||
360 | return ret; | ||
361 | } | ||
362 | case TEGRA_CAMERA_IOCTL_CLK_SET_RATE: | ||
363 | { | ||
364 | int ret; | ||
365 | |||
366 | if (copy_from_user(&dev->info, (const void __user *)arg, | ||
367 | sizeof(struct tegra_camera_clk_info))) { | ||
368 | dev_err(dev->dev, | ||
369 | "%s: Failed to copy arg from user\n", __func__); | ||
370 | return -EFAULT; | ||
371 | } | ||
372 | ret = tegra_camera_clk_set_rate(dev); | ||
373 | if (ret) | ||
374 | return ret; | ||
375 | if (copy_to_user((void __user *)arg, &dev->info, | ||
376 | sizeof(struct tegra_camera_clk_info))) { | ||
377 | dev_err(dev->dev, | ||
378 | "%s: Failed to copy arg to user\n", __func__); | ||
379 | return -EFAULT; | ||
380 | } | ||
381 | return 0; | ||
382 | } | ||
383 | case TEGRA_CAMERA_IOCTL_RESET: | ||
384 | return tegra_camera_reset(dev, id); | ||
385 | default: | ||
386 | dev_err(dev->dev, | ||
387 | "%s: Unknown tegra_camera ioctl.\n", __func__); | ||
388 | return -EINVAL; | ||
389 | } | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | static int tegra_camera_open(struct inode *inode, struct file *file) | ||
394 | { | ||
395 | struct miscdevice *miscdev = file->private_data; | ||
396 | struct tegra_camera_dev *dev = container_of(miscdev, | ||
397 | struct tegra_camera_dev, | ||
398 | misc_dev); | ||
399 | dev_info(dev->dev, "%s\n", __func__); | ||
400 | file->private_data = dev; | ||
401 | |||
402 | tegra_camera_enable_emc(dev); | ||
403 | |||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | static int tegra_camera_release(struct inode *inode, struct file *file) | ||
408 | { | ||
409 | int i, err; | ||
410 | struct tegra_camera_dev *dev = file->private_data; | ||
411 | |||
412 | dev_info(dev->dev, "%s\n", __func__); | ||
413 | for (i = 0; i < ARRAY_SIZE(tegra_camera_block); i++) | ||
414 | if (tegra_camera_block[i].is_enabled) { | ||
415 | tegra_camera_block[i].disable(dev); | ||
416 | tegra_camera_block[i].is_enabled = false; | ||
417 | } | ||
418 | |||
419 | /* If camera blocks are not powergated yet, do it now */ | ||
420 | if (dev->power_refcnt > 0) { | ||
421 | mutex_lock(&dev->tegra_camera_lock); | ||
422 | #ifndef CONFIG_ARCH_TEGRA_2x_SOC | ||
423 | err = tegra_powergate_partition(TEGRA_POWERGATE_VENC); | ||
424 | if (err) | ||
425 | dev_err(dev->dev, "%s: powergate failed.\n", __func__); | ||
426 | #endif | ||
427 | dev->power_refcnt = 0; | ||
428 | mutex_unlock(&dev->tegra_camera_lock); | ||
429 | } | ||
430 | |||
431 | tegra_camera_disable_emc(dev); | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static const struct file_operations tegra_camera_fops = { | ||
437 | .owner = THIS_MODULE, | ||
438 | .open = tegra_camera_open, | ||
439 | .unlocked_ioctl = tegra_camera_ioctl, | ||
440 | .release = tegra_camera_release, | ||
441 | }; | ||
442 | |||
443 | static int tegra_camera_clk_get(struct platform_device *pdev, const char *name, | ||
444 | struct clk **clk) | ||
445 | { | ||
446 | *clk = clk_get(&pdev->dev, name); | ||
447 | if (IS_ERR_OR_NULL(*clk)) { | ||
448 | dev_err(&pdev->dev, "%s: unable to get clock for %s\n", | ||
449 | __func__, name); | ||
450 | *clk = NULL; | ||
451 | return PTR_ERR(*clk); | ||
452 | } | ||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | static int tegra_camera_probe(struct platform_device *pdev) | ||
457 | { | ||
458 | int err; | ||
459 | struct tegra_camera_dev *dev; | ||
460 | |||
461 | dev_info(&pdev->dev, "%s\n", __func__); | ||
462 | dev = devm_kzalloc(&pdev->dev, sizeof(struct tegra_camera_dev), | ||
463 | GFP_KERNEL); | ||
464 | if (!dev) { | ||
465 | err = -ENOMEM; | ||
466 | dev_err(&pdev->dev, "%s: unable to allocate memory\n", | ||
467 | __func__); | ||
468 | goto alloc_err; | ||
469 | } | ||
470 | |||
471 | mutex_init(&dev->tegra_camera_lock); | ||
472 | |||
473 | /* Powergate VE when boot */ | ||
474 | mutex_lock(&dev->tegra_camera_lock); | ||
475 | dev->power_refcnt = 0; | ||
476 | #ifndef CONFIG_ARCH_TEGRA_2x_SOC | ||
477 | err = tegra_powergate_partition(TEGRA_POWERGATE_VENC); | ||
478 | if (err) | ||
479 | dev_err(&pdev->dev, "%s: powergate failed.\n", __func__); | ||
480 | #endif | ||
481 | mutex_unlock(&dev->tegra_camera_lock); | ||
482 | |||
483 | dev->dev = &pdev->dev; | ||
484 | |||
485 | /* Get regulator pointer */ | ||
486 | #ifdef CONFIG_ARCH_TEGRA_2x_SOC | ||
487 | dev->reg = regulator_get(&pdev->dev, "vcsi"); | ||
488 | #else | ||
489 | dev->reg = regulator_get(&pdev->dev, "avdd_dsi_csi"); | ||
490 | #endif | ||
491 | if (IS_ERR_OR_NULL(dev->reg)) { | ||
492 | if (dev->reg == ERR_PTR(-ENODEV)) { | ||
493 | dev->reg = NULL; | ||
494 | dev_info(&pdev->dev, "%s: no regulator device, overriding\n", | ||
495 | __func__); | ||
496 | } else { | ||
497 | dev_err(&pdev->dev, "%s: couldn't get regulator\n", | ||
498 | __func__); | ||
499 | return PTR_ERR(dev->reg); | ||
500 | } | ||
501 | } | ||
502 | |||
503 | dev->misc_dev.minor = MISC_DYNAMIC_MINOR; | ||
504 | dev->misc_dev.name = TEGRA_CAMERA_NAME; | ||
505 | dev->misc_dev.fops = &tegra_camera_fops; | ||
506 | dev->misc_dev.parent = &pdev->dev; | ||
507 | |||
508 | err = misc_register(&dev->misc_dev); | ||
509 | if (err) { | ||
510 | dev_err(&pdev->dev, "%s: Unable to register misc device!\n", | ||
511 | TEGRA_CAMERA_NAME); | ||
512 | goto misc_register_err; | ||
513 | } | ||
514 | |||
515 | err = tegra_camera_clk_get(pdev, "isp", &dev->isp_clk); | ||
516 | if (err) | ||
517 | goto misc_register_err; | ||
518 | err = tegra_camera_clk_get(pdev, "vi", &dev->vi_clk); | ||
519 | if (err) | ||
520 | goto vi_clk_get_err; | ||
521 | err = tegra_camera_clk_get(pdev, "vi_sensor", &dev->vi_sensor_clk); | ||
522 | if (err) | ||
523 | goto vi_sensor_clk_get_err; | ||
524 | err = tegra_camera_clk_get(pdev, "csus", &dev->csus_clk); | ||
525 | if (err) | ||
526 | goto csus_clk_get_err; | ||
527 | err = tegra_camera_clk_get(pdev, "csi", &dev->csi_clk); | ||
528 | if (err) | ||
529 | goto csi_clk_get_err; | ||
530 | err = tegra_camera_clk_get(pdev, "emc", &dev->emc_clk); | ||
531 | if (err) | ||
532 | goto emc_clk_get_err; | ||
533 | |||
534 | /* dev is set in order to restore in _remove */ | ||
535 | platform_set_drvdata(pdev, dev); | ||
536 | |||
537 | return 0; | ||
538 | |||
539 | emc_clk_get_err: | ||
540 | clk_put(dev->emc_clk); | ||
541 | csi_clk_get_err: | ||
542 | clk_put(dev->csus_clk); | ||
543 | csus_clk_get_err: | ||
544 | clk_put(dev->vi_sensor_clk); | ||
545 | vi_sensor_clk_get_err: | ||
546 | clk_put(dev->vi_clk); | ||
547 | vi_clk_get_err: | ||
548 | clk_put(dev->isp_clk); | ||
549 | misc_register_err: | ||
550 | regulator_put(dev->reg); | ||
551 | alloc_err: | ||
552 | return err; | ||
553 | } | ||
554 | |||
555 | static int tegra_camera_remove(struct platform_device *pdev) | ||
556 | { | ||
557 | struct tegra_camera_dev *dev = platform_get_drvdata(pdev); | ||
558 | |||
559 | clk_put(dev->isp_clk); | ||
560 | clk_put(dev->vi_clk); | ||
561 | clk_put(dev->vi_sensor_clk); | ||
562 | clk_put(dev->csus_clk); | ||
563 | clk_put(dev->csi_clk); | ||
564 | |||
565 | misc_deregister(&dev->misc_dev); | ||
566 | regulator_put(dev->reg); | ||
567 | mutex_destroy(&dev->tegra_camera_lock); | ||
568 | |||
569 | return 0; | ||
570 | } | ||
571 | |||
572 | static int tegra_camera_suspend(struct platform_device *pdev, pm_message_t state) | ||
573 | { | ||
574 | struct tegra_camera_dev *dev = platform_get_drvdata(pdev); | ||
575 | int ret = 0; | ||
576 | |||
577 | if (tegra_camera_enabled(dev)) { | ||
578 | ret = -EBUSY; | ||
579 | dev_err(&pdev->dev, | ||
580 | "tegra_camera cannot suspend, " | ||
581 | "application is holding on to camera. \n"); | ||
582 | } | ||
583 | |||
584 | return ret; | ||
585 | } | ||
586 | |||
587 | static int tegra_camera_resume(struct platform_device *pdev) | ||
588 | { | ||
589 | return 0; | ||
590 | } | ||
591 | |||
592 | static struct platform_driver tegra_camera_driver = { | ||
593 | .probe = tegra_camera_probe, | ||
594 | .remove = tegra_camera_remove, | ||
595 | .suspend = tegra_camera_suspend, | ||
596 | .resume = tegra_camera_resume, | ||
597 | .driver = { .name = TEGRA_CAMERA_NAME } | ||
598 | }; | ||
599 | |||
600 | static int __init tegra_camera_init(void) | ||
601 | { | ||
602 | return platform_driver_register(&tegra_camera_driver); | ||
603 | } | ||
604 | |||
605 | static void __exit tegra_camera_exit(void) | ||
606 | { | ||
607 | platform_driver_unregister(&tegra_camera_driver); | ||
608 | } | ||
609 | |||
610 | module_init(tegra_camera_init); | ||
611 | module_exit(tegra_camera_exit); | ||
612 | |||
diff --git a/drivers/media/video/tegra/tegra_dtv.c b/drivers/media/video/tegra/tegra_dtv.c new file mode 100644 index 00000000000..95270c4c3be --- /dev/null +++ b/drivers/media/video/tegra/tegra_dtv.c | |||
@@ -0,0 +1,1089 @@ | |||
1 | /* | ||
2 | * tegra_dtv.c - Tegra DTV interface driver | ||
3 | * | ||
4 | * Author: Adam Jiang <chaoj@nvidia.com> | ||
5 | * Copyright (c) 2011, NVIDIA Corporation. | ||
6 | * Copyright (c) 2012, NVIDIA Corporation. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20 | * 02110-1301 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/clk.h> | ||
27 | #include <linux/fs.h> | ||
28 | #include <linux/completion.h> | ||
29 | #include <linux/dma-mapping.h> | ||
30 | #include <linux/vmalloc.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/spinlock.h> | ||
33 | #include <linux/mutex.h> | ||
34 | #include <linux/workqueue.h> | ||
35 | #include <linux/wakelock.h> | ||
36 | #include <linux/platform_device.h> | ||
37 | #include <linux/miscdevice.h> | ||
38 | #include <linux/debugfs.h> | ||
39 | #include <linux/seq_file.h> | ||
40 | #include <linux/delay.h> | ||
41 | |||
42 | #include <media/tegra_dtv.h> | ||
43 | |||
44 | #include <linux/uaccess.h> | ||
45 | #include <mach/iomap.h> | ||
46 | #include <mach/dma.h> | ||
47 | |||
48 | /* offsets from TEGRA_DTV_BASE */ | ||
49 | #define DTV_SPI_CONTROL 0x40 | ||
50 | #define DTV_MODE 0x44 | ||
51 | #define DTV_CTRL 0x48 | ||
52 | #define DTV_PACKET_COUNT 0x4c | ||
53 | #define DTV_ERROR_COUNT 0x50 | ||
54 | #define DTV_INTERRUPT_STATUS 0x54 | ||
55 | #define DTV_STATUS 0x58 | ||
56 | #define DTV_RX_FIFO 0x5c | ||
57 | |||
58 | /* DTV_SPI_CONTROL */ | ||
59 | #define DTV_SPI_CONTROL_ENABLE_DTV 1 | ||
60 | |||
61 | /* DTV_MODE_0 */ | ||
62 | #define DTV_MODE_BYTE_SWIZZLE_SHIFT 6 | ||
63 | #define DTV_MODE_BYTE_SWIZZLE (1 << DTV_MODE_BYTE_SWIZZLE_SHIFT) | ||
64 | #define DTV_MODE_BIT_SWIZZLE_SHIFT 5 | ||
65 | #define DTV_MODE_BIT_SWIZZLE (1 << DTV_MODE_BIT_SWIZZLE_SHIFT) | ||
66 | #define DTV_MODE_CLK_EDGE_SHIFT 4 | ||
67 | #define DTV_MODE_CLK_EDGE_MASK 1 | ||
68 | #define DTV_MODE_CLK_EDGE_NEG (1 << DTV_MODE_CLK_EDGE_SHIFT) | ||
69 | #define DTV_MODE_PRTL_SEL_SHIFT 2 | ||
70 | #define DTV_MODE_PRTL_SEL_MASK (0x3 << DTV_MODE_PRTL_SEL_SHIFT) | ||
71 | #define DTV_MODE_CLK_MODE_SHIFT 1 | ||
72 | #define DTV_MODE_CLK_MODE_MASK (0x1 << DTV_MODE_CLK_MODE_SHIFT) | ||
73 | #define DTV_MODE_PRTL_ENABLE 1 | ||
74 | |||
75 | /* DTV_CONTROL_0 */ | ||
76 | #define DTV_CTRL_FEC_SIZE_SHIFT 24 | ||
77 | #define DTV_CTRL_FEC_SIZE_MASK (0x7F << DTV_CTRL_FEC_SIZE_SHIFT) | ||
78 | #define DTV_CTRL_BODY_SIZE_SHIFT 16 | ||
79 | #define DTV_CTRL_BODY_SIZE_MASK (0xFF << DTV_CTRL_BODY_SIZE_SHIFT) | ||
80 | #define DTV_CTRL_FIFO_ATTN_LEVEL_SHIFT 8 | ||
81 | #define DTV_CTRL_FIFO_ATTN_LEVEL_MASK (0x1F << DTV_CTRL_FIFO_ATTN_LEVEL_SHIFT) | ||
82 | #define DTV_CTRL_FIFO_ATTN_ONE_WORD (0 << DTV_CTRL_FIFO_ATTN_LEVEL_SHIFT) | ||
83 | #define DTV_CTRL_FIFO_ATTN_TWO_WORD (1 << DTV_CTRL_FIFO_ATTN_LEVEL_SHIFT) | ||
84 | #define DTV_CTRL_FIFO_ATTN_THREE_WORD (2 << DTV_CTRL_FIFO_ATTN_LEVEL_SHIFT) | ||
85 | #define DTV_CTRL_FIFO_ATTN_FOUR_WORD (3 << DTV_CTRL_FIFO_ATTN_LEVEL_SHIFT) | ||
86 | #define DTV_CTRL_BODY_VALID_SEL_SHIFT 6 | ||
87 | #define DTV_CTRL_BODY_VALID_SEL_MASK (1 << DTV_CTRL_BODY_VALID_SEL_SHIFT) | ||
88 | #define DTV_CTRL_START_SEL_SHIFT 4 | ||
89 | #define DTV_CTRL_START_SEL_MASK (1 << DTV_CTRL_START_SEL_SHIFT) | ||
90 | #define DTV_CTRL_ERROR_POLARITY_SHIFT 2 | ||
91 | #define DTV_CTRL_ERROR_POLARITY_MASK (1 << DTV_CTRL_ERROR_POLARITY_SHIFT) | ||
92 | #define DTV_CTRL_PSYNC_POLARITY_SHIFT 1 | ||
93 | #define DTV_CTRL_PSYNC_POLARITY_MASK (1 << DTV_CTRL_PSYNC_POLARITY_SHIFT) | ||
94 | #define DTV_CTRL_VALID_POLARITY_SHIFT 0 | ||
95 | #define DTV_CTRL_VALID_POLARITY_MASK (1 << DTV_CTRL_VALID_POLARITY_SHIFT) | ||
96 | |||
97 | /* DTV_INTERRUPT_STATUS_0 */ | ||
98 | #define DTV_INTERRUPT_PACKET_UNDERRUN_ERR 8 | ||
99 | #define DTV_INTERRUPT_BODY_OVERRUN_ERR 4 | ||
100 | #define DTV_INTERRUPT_BODY_UNDERRUN_ERR 2 | ||
101 | #define DTV_INTERRUPT_UPSTREAM_ERR 1 | ||
102 | |||
103 | /* DTV_STATUS_0 */ | ||
104 | #define DTV_STATUS_RXF_UNDERRUN 4 | ||
105 | #define DTV_STATUS_RXF_EMPTY 2 | ||
106 | #define DTV_STATUS_RXF_FULL 1 | ||
107 | |||
108 | #define TEGRA_DTV_NAME "tegra_dtv" | ||
109 | |||
110 | /* default sw config */ | ||
111 | #define DTV_BUF_SIZE_ORDER PAGE_SHIFT | ||
112 | #define DTV_MAX_NUM_BUFS 4 | ||
113 | |||
114 | #define DTV_FIFO_ATN_LVL_LOW_GEAR 0 | ||
115 | #define DTV_FIFO_ATN_LVL_SECOND_GEAR 1 | ||
116 | #define DTV_FIFO_ATN_LVL_THIRD_GEAR 2 | ||
117 | #define DTV_FIFO_ATN_LVL_TOP_GEAR 3 | ||
118 | |||
119 | struct dtv_stream { | ||
120 | struct mutex mtx; | ||
121 | |||
122 | bool xferring; /* is DMA in progress */ | ||
123 | unsigned num_bufs; | ||
124 | void *buffer[DTV_MAX_NUM_BUFS]; | ||
125 | dma_addr_t buf_phy[DTV_MAX_NUM_BUFS]; | ||
126 | struct completion comp[DTV_MAX_NUM_BUFS]; | ||
127 | struct tegra_dma_req dma_req[DTV_MAX_NUM_BUFS]; | ||
128 | int last_queued; | ||
129 | |||
130 | int fifo_atn_level; | ||
131 | |||
132 | struct tegra_dma_channel *dma_chan; | ||
133 | bool stopped; | ||
134 | struct completion stop_completion; | ||
135 | spinlock_t dma_req_lock; | ||
136 | size_t buf_size; | ||
137 | |||
138 | struct work_struct work; | ||
139 | struct wake_lock wake_lock; | ||
140 | char wake_lock_name[16]; | ||
141 | }; | ||
142 | |||
143 | struct tegra_dtv_context { | ||
144 | struct tegra_dtv_hw_config config; | ||
145 | struct clk *clk; | ||
146 | int clk_enabled; | ||
147 | |||
148 | phys_addr_t phys; | ||
149 | void * __iomem base; | ||
150 | unsigned long dma_req_sel; | ||
151 | |||
152 | struct dtv_stream stream; | ||
153 | /* debugfs */ | ||
154 | struct dentry *d; | ||
155 | /* for refer back */ | ||
156 | struct platform_device *pdev; | ||
157 | struct miscdevice miscdev; | ||
158 | }; | ||
159 | |||
160 | static inline struct tegra_dtv_context *to_ctx(struct dtv_stream *s) | ||
161 | { | ||
162 | return container_of(s, struct tegra_dtv_context, stream); | ||
163 | } | ||
164 | |||
165 | /* access control */ | ||
166 | static atomic_t tegra_dtv_instance_nr = ATOMIC_INIT(1); | ||
167 | |||
168 | static inline u32 tegra_dtv_readl(struct tegra_dtv_context *dtv, | ||
169 | unsigned long reg) | ||
170 | { | ||
171 | BUG_ON(!dtv->clk_enabled); | ||
172 | return readl(dtv->base + reg); | ||
173 | } | ||
174 | |||
175 | static inline void tegra_dtv_writel(struct tegra_dtv_context *dtv, | ||
176 | u32 val, unsigned long reg) | ||
177 | { | ||
178 | BUG_ON(!dtv->clk_enabled); | ||
179 | writel(val, dtv->base + reg); | ||
180 | } | ||
181 | |||
182 | /* process */ | ||
183 | static inline void prevent_suspend(struct dtv_stream *s) | ||
184 | { | ||
185 | pr_debug("%s called.\n", __func__); | ||
186 | cancel_work_sync(&s->work); | ||
187 | wake_lock(&s->wake_lock); | ||
188 | } | ||
189 | |||
190 | static void tegra_dtv_worker(struct work_struct *w) | ||
191 | { | ||
192 | struct dtv_stream *s = container_of(w, struct dtv_stream, work); | ||
193 | pr_debug("%s called.\n", __func__); | ||
194 | wake_unlock(&s->wake_lock); | ||
195 | } | ||
196 | |||
197 | static inline void wakeup_suspend(struct dtv_stream *s) | ||
198 | { | ||
199 | schedule_work(&s->work); | ||
200 | } | ||
201 | |||
202 | static inline bool wait_till_stopped(struct dtv_stream *s) | ||
203 | { | ||
204 | int ret; | ||
205 | |||
206 | pr_debug("%s: wait for completion\n", __func__); | ||
207 | |||
208 | ret = wait_for_completion_timeout( | ||
209 | &s->stop_completion, HZ); | ||
210 | if (!ret) | ||
211 | pr_err("%s: wait timed out", __func__); | ||
212 | if (ret < 0) | ||
213 | pr_err("%s: wait error %d\n", __func__, ret); | ||
214 | |||
215 | wakeup_suspend(s); | ||
216 | |||
217 | pr_debug("%s: done: %d\n", __func__, ret); | ||
218 | |||
219 | return true; | ||
220 | } | ||
221 | |||
222 | /* dma transfer */ | ||
223 | static inline bool are_xfers_pending(struct dtv_stream *s) | ||
224 | { | ||
225 | int i; | ||
226 | |||
227 | pr_debug("%s called\n", __func__); | ||
228 | |||
229 | for (i = 0; i < s->num_bufs; i++) | ||
230 | if (!completion_done(&s->comp[i])) | ||
231 | return true; | ||
232 | return false; | ||
233 | } | ||
234 | |||
235 | static void tegra_dtv_rx_dma_complete(struct tegra_dma_req *req) | ||
236 | { | ||
237 | unsigned long flags; | ||
238 | unsigned req_num; | ||
239 | struct dtv_stream *s = req->dev; | ||
240 | |||
241 | spin_lock_irqsave(&s->dma_req_lock, flags); | ||
242 | |||
243 | pr_debug("%s called.\n", __func__); | ||
244 | |||
245 | req_num = req - s->dma_req; | ||
246 | pr_debug("%s: complete buffer %d size %d bytes\n", | ||
247 | __func__, req_num, req->bytes_transferred); | ||
248 | BUG_ON(req_num >= s->num_bufs); | ||
249 | |||
250 | complete(&s->comp[req_num]); | ||
251 | |||
252 | if (!are_xfers_pending(s)) | ||
253 | pr_debug("%s: overflow.\n", __func__); | ||
254 | |||
255 | spin_unlock_irqrestore(&s->dma_req_lock, flags); | ||
256 | } | ||
257 | |||
258 | /* hw */ | ||
259 | static inline void _dtv_enable_protocol(struct tegra_dtv_context *dtv_ctx) | ||
260 | { | ||
261 | u32 val; | ||
262 | |||
263 | val = tegra_dtv_readl(dtv_ctx, DTV_MODE); | ||
264 | val &= ~0x01; | ||
265 | val |= DTV_MODE_PRTL_ENABLE; | ||
266 | tegra_dtv_writel(dtv_ctx, val, DTV_MODE); | ||
267 | } | ||
268 | |||
269 | static inline void _dtv_disable_protocol(struct tegra_dtv_context *dtv_ctx) | ||
270 | { | ||
271 | u32 val; | ||
272 | |||
273 | val = tegra_dtv_readl(dtv_ctx, DTV_MODE); | ||
274 | val &= ~DTV_MODE_PRTL_ENABLE; | ||
275 | tegra_dtv_writel(dtv_ctx, val, DTV_MODE); | ||
276 | } | ||
277 | |||
278 | static inline u32 _dtv_get_status(struct tegra_dtv_context *dtv_ctx) | ||
279 | { | ||
280 | return tegra_dtv_readl(dtv_ctx, DTV_STATUS); | ||
281 | } | ||
282 | |||
283 | static inline void _dtv_set_attn_level(struct tegra_dtv_context *dtv_ctx) | ||
284 | { | ||
285 | /* TODO: consider have this set to corresponding transfer request */ | ||
286 | u32 val; | ||
287 | |||
288 | val = tegra_dtv_readl(dtv_ctx, DTV_CTRL); | ||
289 | val &= ~DTV_CTRL_FIFO_ATTN_LEVEL_MASK; | ||
290 | val |= DTV_CTRL_FIFO_ATTN_FOUR_WORD; | ||
291 | tegra_dtv_writel(dtv_ctx, val, DTV_CTRL); | ||
292 | } | ||
293 | |||
294 | /* ioctl */ | ||
295 | static inline void _dtv_set_hw_params(struct tegra_dtv_context *dtv_ctx) | ||
296 | { | ||
297 | u32 val = 0; | ||
298 | u32 reg; | ||
299 | struct tegra_dtv_hw_config *cfg = &dtv_ctx->config; | ||
300 | |||
301 | val = (cfg->clk_edge << DTV_MODE_CLK_EDGE_SHIFT) | | ||
302 | (cfg->protocol_sel << DTV_MODE_PRTL_SEL_SHIFT) | | ||
303 | (cfg->clk_mode << DTV_MODE_CLK_MODE_SHIFT); | ||
304 | reg = tegra_dtv_readl(dtv_ctx, DTV_MODE); | ||
305 | reg &= ~(DTV_MODE_CLK_EDGE_MASK | | ||
306 | DTV_MODE_PRTL_SEL_MASK | | ||
307 | DTV_MODE_CLK_MODE_MASK); | ||
308 | reg |= val; | ||
309 | tegra_dtv_writel(dtv_ctx, reg, DTV_MODE); | ||
310 | |||
311 | val = 0; | ||
312 | reg = 0; | ||
313 | val = (cfg->fec_size << DTV_CTRL_FEC_SIZE_SHIFT) | | ||
314 | (cfg->body_size << DTV_CTRL_BODY_SIZE_SHIFT) | | ||
315 | (cfg->body_valid_sel << DTV_CTRL_BODY_VALID_SEL_SHIFT) | | ||
316 | (cfg->start_sel << DTV_CTRL_START_SEL_SHIFT) | | ||
317 | (cfg->err_pol << DTV_CTRL_ERROR_POLARITY_SHIFT) | | ||
318 | (cfg->psync_pol << DTV_CTRL_PSYNC_POLARITY_SHIFT) | | ||
319 | (cfg->valid_pol << DTV_CTRL_VALID_POLARITY_SHIFT); | ||
320 | reg = tegra_dtv_readl(dtv_ctx, DTV_CTRL); | ||
321 | reg &= ~(DTV_CTRL_FEC_SIZE_MASK | | ||
322 | DTV_CTRL_BODY_SIZE_MASK | | ||
323 | DTV_CTRL_BODY_VALID_SEL_MASK | | ||
324 | DTV_CTRL_START_SEL_MASK | | ||
325 | DTV_CTRL_ERROR_POLARITY_MASK | | ||
326 | DTV_CTRL_PSYNC_POLARITY_MASK | | ||
327 | DTV_CTRL_VALID_POLARITY_MASK); | ||
328 | reg |= val; | ||
329 | tegra_dtv_writel(dtv_ctx, reg, DTV_CTRL); | ||
330 | } | ||
331 | |||
332 | #define DTV_GET_REG_VAL(x, reg, seg) \ | ||
333 | ((x & reg##_##seg##_MASK) >> reg##_##seg##_SHIFT) | ||
334 | |||
335 | static inline void _dtv_get_hw_params(struct tegra_dtv_context *dtv_ctx, | ||
336 | struct tegra_dtv_hw_config *cfg) | ||
337 | { | ||
338 | u32 reg; | ||
339 | |||
340 | reg = tegra_dtv_readl(dtv_ctx, DTV_MODE); | ||
341 | cfg->clk_edge = DTV_GET_REG_VAL(reg, DTV_MODE, CLK_EDGE); | ||
342 | cfg->protocol_sel = DTV_GET_REG_VAL(reg, DTV_MODE, PRTL_SEL); | ||
343 | cfg->clk_mode = DTV_GET_REG_VAL(reg, DTV_MODE, CLK_MODE); | ||
344 | |||
345 | reg = tegra_dtv_readl(dtv_ctx, DTV_CTRL); | ||
346 | cfg->fec_size = DTV_GET_REG_VAL(reg, DTV_CTRL, FEC_SIZE); | ||
347 | cfg->body_size = DTV_GET_REG_VAL(reg, DTV_CTRL, BODY_SIZE); | ||
348 | cfg->body_valid_sel = DTV_GET_REG_VAL(reg, DTV_CTRL, BODY_VALID_SEL); | ||
349 | cfg->start_sel = DTV_GET_REG_VAL(reg, DTV_CTRL, START_SEL); | ||
350 | cfg->err_pol = DTV_GET_REG_VAL(reg, DTV_CTRL, ERROR_POLARITY); | ||
351 | cfg->psync_pol = DTV_GET_REG_VAL(reg, DTV_CTRL, PSYNC_POLARITY); | ||
352 | cfg->valid_pol = DTV_GET_REG_VAL(reg, DTV_CTRL, VALID_POLARITY); | ||
353 | } | ||
354 | |||
355 | /* must call with stream->dma_req_lock held. */ | ||
356 | static int stop_xfer_unsafe(struct dtv_stream *s) | ||
357 | { | ||
358 | int spin = 0; | ||
359 | struct tegra_dtv_context *dtv_ctx = to_ctx(s); | ||
360 | |||
361 | pr_debug("%s called\n", __func__); | ||
362 | tegra_dma_cancel(s->dma_chan); | ||
363 | _dtv_disable_protocol(dtv_ctx); | ||
364 | while ((_dtv_get_status(dtv_ctx) & DTV_STATUS_RXF_FULL) && | ||
365 | spin < 100) { | ||
366 | udelay(10); | ||
367 | if (spin++ > 50) | ||
368 | pr_info("%s : spin %d\n", __func__, spin); | ||
369 | } | ||
370 | if (spin == 100) | ||
371 | pr_warn("%s : spinny\n", __func__); | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | /* must call with stream->mtx held */ | ||
377 | static void __force_xfer_stop(struct dtv_stream *s) | ||
378 | { | ||
379 | int i; | ||
380 | |||
381 | pr_debug("%s called.\n", __func__); | ||
382 | |||
383 | if (!s->stopped) { | ||
384 | s->stopped = true; | ||
385 | if (are_xfers_pending(s)) | ||
386 | wait_till_stopped(s); | ||
387 | for (i = 0; i < s->num_bufs; i++) { | ||
388 | init_completion(&s->comp[i]); | ||
389 | complete(&s->comp[i]); | ||
390 | } | ||
391 | } | ||
392 | |||
393 | /* just in case. dma should be cancelled before this */ | ||
394 | if (!tegra_dma_is_empty(s->dma_chan)) | ||
395 | pr_err("%s: DMA channel is not empty!\n", __func__); | ||
396 | tegra_dma_cancel(s->dma_chan); | ||
397 | s->xferring = false; | ||
398 | |||
399 | pr_debug("%s: done\n", __func__); | ||
400 | } | ||
401 | |||
402 | static long tegra_dtv_ioctl(struct file *file, unsigned int cmd, | ||
403 | unsigned long arg) | ||
404 | { | ||
405 | int ret = 0; | ||
406 | struct tegra_dtv_context *dtv_ctx; | ||
407 | struct dtv_stream *s; | ||
408 | |||
409 | dtv_ctx = (struct tegra_dtv_context *) file->private_data; | ||
410 | s = &dtv_ctx->stream; | ||
411 | |||
412 | /* process may sleep on this */ | ||
413 | mutex_lock(&s->mtx); | ||
414 | |||
415 | switch (cmd) { | ||
416 | case TEGRA_DTV_IOCTL_START: | ||
417 | pr_debug("%s: run serial ts handling.\n", __func__); | ||
418 | s->stopped = false; | ||
419 | break; | ||
420 | case TEGRA_DTV_IOCTL_STOP: | ||
421 | pr_debug("%s: stop serial ts handling.\n", __func__); | ||
422 | if (s->xferring) { | ||
423 | stop_xfer_unsafe(s); | ||
424 | complete(&s->stop_completion); | ||
425 | __force_xfer_stop(s); | ||
426 | s->stopped = true; | ||
427 | } | ||
428 | break; | ||
429 | case TEGRA_DTV_IOCTL_SET_HW_CONFIG: | ||
430 | { | ||
431 | struct tegra_dtv_hw_config cfg; | ||
432 | |||
433 | if (s->xferring) { | ||
434 | pr_err("%s: tranfering is in progress.\n", __func__); | ||
435 | ret = -EBUSY; | ||
436 | break; | ||
437 | } | ||
438 | |||
439 | if (copy_from_user(&cfg, (const void __user *) arg, | ||
440 | sizeof(cfg))) { | ||
441 | ret = -EFAULT; | ||
442 | break; | ||
443 | } | ||
444 | |||
445 | dtv_ctx->config = cfg; | ||
446 | _dtv_set_hw_params(dtv_ctx); | ||
447 | break; | ||
448 | } | ||
449 | case TEGRA_DTV_IOCTL_GET_HW_CONFIG: | ||
450 | { | ||
451 | struct tegra_dtv_hw_config cfg; | ||
452 | |||
453 | _dtv_get_hw_params(dtv_ctx, &cfg); | ||
454 | |||
455 | if (copy_to_user((void __user *)arg, &cfg, | ||
456 | sizeof(cfg))) | ||
457 | ret = -EFAULT; | ||
458 | break; | ||
459 | } | ||
460 | default: | ||
461 | ret = -EINVAL; | ||
462 | } | ||
463 | |||
464 | mutex_unlock(&s->mtx); | ||
465 | |||
466 | return ret; | ||
467 | } | ||
468 | |||
469 | /* must call with stream->dma_req_lock held. */ | ||
470 | static int start_xfer_unsafe(struct dtv_stream *s, size_t size) | ||
471 | { | ||
472 | int i; | ||
473 | u32 reg; | ||
474 | struct tegra_dtv_context *dtv_ctx = to_ctx(s); | ||
475 | |||
476 | BUG_ON(are_xfers_pending(s)); | ||
477 | |||
478 | pr_debug("%s called.\n", __func__); | ||
479 | |||
480 | for (i = 0; i < s->num_bufs; i++) { | ||
481 | init_completion(&s->comp[i]); | ||
482 | s->dma_req[i].dest_addr = s->buf_phy[i]; | ||
483 | s->dma_req[i].size = size; | ||
484 | tegra_dma_enqueue_req(s->dma_chan, &s->dma_req[i]); | ||
485 | } | ||
486 | |||
487 | s->last_queued = s->num_bufs - 1; | ||
488 | |||
489 | /* too late ? */ | ||
490 | _dtv_set_attn_level(dtv_ctx); | ||
491 | _dtv_enable_protocol(dtv_ctx); | ||
492 | |||
493 | reg = tegra_dtv_readl(dtv_ctx, DTV_MODE); | ||
494 | pr_debug("DTV_MODE = 0x%08x\n", reg); | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | static int try_start_fill_buf(struct dtv_stream *s, size_t size) | ||
500 | { | ||
501 | int ret = 0; | ||
502 | unsigned long flags; | ||
503 | |||
504 | pr_debug("%s called\n", __func__); | ||
505 | |||
506 | prevent_suspend(s); | ||
507 | |||
508 | spin_lock_irqsave(&s->dma_req_lock, flags); | ||
509 | if (!s->stopped && !are_xfers_pending(s)) { | ||
510 | ret = start_xfer_unsafe(s, size); | ||
511 | if (ret) { | ||
512 | pr_err("%s: start tranfer failed.\n", __func__); | ||
513 | /* let process not wait stupid */ | ||
514 | wakeup_suspend(s); | ||
515 | } | ||
516 | } | ||
517 | spin_unlock_irqrestore(&s->dma_req_lock, flags); | ||
518 | |||
519 | return ret; | ||
520 | } | ||
521 | |||
522 | static ssize_t tegra_dtv_read(struct file *file, char __user *buf, | ||
523 | size_t size, loff_t *off) | ||
524 | { | ||
525 | ssize_t ret; | ||
526 | ssize_t xfer_size = 0; | ||
527 | int buf_no; | ||
528 | struct tegra_dma_req *req; | ||
529 | struct tegra_dtv_context *dtv_ctx; | ||
530 | |||
531 | dtv_ctx = (struct tegra_dtv_context *) file->private_data; | ||
532 | |||
533 | mutex_lock(&dtv_ctx->stream.mtx); | ||
534 | |||
535 | if (!IS_ALIGNED(size, 4) || size < 4 || | ||
536 | size > dtv_ctx->stream.buf_size) { | ||
537 | pr_err("%s: invalid user size %d\n", __func__, size); | ||
538 | ret = -EINVAL; | ||
539 | mutex_unlock(&dtv_ctx->stream.mtx); | ||
540 | return ret; | ||
541 | } | ||
542 | |||
543 | pr_debug("%s: read %d bytes.\n", __func__, size); | ||
544 | |||
545 | if (dtv_ctx->stream.stopped) { | ||
546 | pr_debug("%s: tegra dtv transferring is stopped.\n", | ||
547 | __func__); | ||
548 | ret = 0; | ||
549 | mutex_unlock(&dtv_ctx->stream.mtx); | ||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | /* start dma transfer */ | ||
554 | ret = try_start_fill_buf(&dtv_ctx->stream, size); | ||
555 | if (ret < 0 && ret != -EALREADY) { | ||
556 | pr_err("%s: could not start recording.\n", __func__); | ||
557 | mutex_unlock(&dtv_ctx->stream.mtx); | ||
558 | return ret; | ||
559 | } | ||
560 | dtv_ctx->stream.xferring = true; | ||
561 | |||
562 | buf_no = (dtv_ctx->stream.last_queued + 1) % dtv_ctx->stream.num_bufs; | ||
563 | pr_debug("%s: buf_no = %d\n", __func__, buf_no); | ||
564 | |||
565 | /* Wait for the buffers to be filled up. The maximum timeout | ||
566 | *value should be caculated dynamically based on | ||
567 | * buf_size(dtv_ctx->stream).buf_size. For isdb-t 1seg signal, | ||
568 | *it bit rate is 300 - 456 kpbs, if buf_size = 4096 bytes, then | ||
569 | * to fill up one buffer takes ~77ms. | ||
570 | */ | ||
571 | ret = wait_for_completion_interruptible_timeout( | ||
572 | &dtv_ctx->stream.comp[buf_no], HZ); | ||
573 | if (!ret) { | ||
574 | pr_err("%s: timeout", __func__); | ||
575 | ret = -ETIMEDOUT; | ||
576 | mutex_unlock(&dtv_ctx->stream.mtx); | ||
577 | return ret; | ||
578 | } else if (ret < 0) { | ||
579 | pr_err("%s: wait error %d", __func__, ret); | ||
580 | mutex_unlock(&dtv_ctx->stream.mtx); | ||
581 | return ret; | ||
582 | } | ||
583 | |||
584 | req = &dtv_ctx->stream.dma_req[buf_no]; | ||
585 | |||
586 | /* xfer cannot exceed buffer size */ | ||
587 | xfer_size = size > req->size ? req->size : size; | ||
588 | req->size = size; | ||
589 | dma_sync_single_for_cpu(NULL, | ||
590 | dtv_ctx->stream.dma_req[buf_no].dest_addr, | ||
591 | dtv_ctx->stream.dma_req[buf_no].size, | ||
592 | DMA_FROM_DEVICE); | ||
593 | ret = copy_to_user(buf, dtv_ctx->stream.buffer[buf_no], xfer_size); | ||
594 | if (ret) { | ||
595 | ret = -EFAULT; | ||
596 | mutex_unlock(&dtv_ctx->stream.mtx); | ||
597 | return ret; | ||
598 | } | ||
599 | |||
600 | /* not stopped, reinitial stop */ | ||
601 | init_completion(&dtv_ctx->stream.stop_completion); | ||
602 | |||
603 | dtv_ctx->stream.last_queued = buf_no; | ||
604 | |||
605 | /* refill copied buffer */ | ||
606 | ret = tegra_dma_enqueue_req(dtv_ctx->stream.dma_chan, req); | ||
607 | BUG_ON(ret); | ||
608 | |||
609 | ret = xfer_size; | ||
610 | *off += xfer_size; | ||
611 | |||
612 | mutex_unlock(&dtv_ctx->stream.mtx); | ||
613 | |||
614 | pr_debug("%s : done with ret = %d\n", __func__, ret); | ||
615 | |||
616 | return ret; | ||
617 | } | ||
618 | |||
619 | static int tegra_dtv_open(struct inode *inode, struct file *file) | ||
620 | { | ||
621 | int i; | ||
622 | struct miscdevice *miscdev = file->private_data; | ||
623 | struct tegra_dtv_context *dtv_ctx = | ||
624 | container_of(miscdev, struct tegra_dtv_context, miscdev); | ||
625 | file->private_data = dtv_ctx; | ||
626 | |||
627 | dtv_ctx = (struct tegra_dtv_context *) file->private_data; | ||
628 | |||
629 | pr_debug("%s called\n", __func__); | ||
630 | |||
631 | /* can be opened once */ | ||
632 | if (!atomic_dec_and_test(&tegra_dtv_instance_nr)) { | ||
633 | atomic_inc(&tegra_dtv_instance_nr); | ||
634 | pr_err("tegra_dtv device can only be opened once.\n"); | ||
635 | return -EBUSY; | ||
636 | } | ||
637 | |||
638 | mutex_lock(&dtv_ctx->stream.mtx); | ||
639 | |||
640 | dtv_ctx->stream.stopped = false; | ||
641 | |||
642 | /* cleanup completion */ | ||
643 | for (i = 0; i < dtv_ctx->stream.num_bufs; i++) { | ||
644 | init_completion(&dtv_ctx->stream.comp[i]); | ||
645 | /* complete all */ | ||
646 | complete(&dtv_ctx->stream.comp[i]); | ||
647 | } | ||
648 | |||
649 | mutex_unlock(&dtv_ctx->stream.mtx); | ||
650 | |||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | static int tegra_dtv_release(struct inode *inode, struct file *file) | ||
655 | { | ||
656 | struct tegra_dtv_context *dtv_ctx = | ||
657 | (struct tegra_dtv_context *) file->private_data; | ||
658 | |||
659 | pr_debug("%s called\n", __func__); | ||
660 | |||
661 | atomic_inc(&tegra_dtv_instance_nr); | ||
662 | |||
663 | mutex_lock(&dtv_ctx->stream.mtx); | ||
664 | if (dtv_ctx->stream.xferring) { | ||
665 | stop_xfer_unsafe(&dtv_ctx->stream); | ||
666 | /* clean up stop condition */ | ||
667 | complete(&dtv_ctx->stream.stop_completion); | ||
668 | __force_xfer_stop(&dtv_ctx->stream); | ||
669 | } | ||
670 | /* wakeup any pending process */ | ||
671 | wakeup_suspend(&dtv_ctx->stream); | ||
672 | mutex_unlock(&dtv_ctx->stream.mtx); | ||
673 | |||
674 | pr_debug("%s : done\n", __func__); | ||
675 | |||
676 | return 0; | ||
677 | } | ||
678 | |||
679 | static const struct file_operations tegra_dtv_fops = { | ||
680 | .owner = THIS_MODULE, | ||
681 | .open = tegra_dtv_open, | ||
682 | .read = tegra_dtv_read, | ||
683 | .unlocked_ioctl = tegra_dtv_ioctl, | ||
684 | .release = tegra_dtv_release, | ||
685 | }; | ||
686 | |||
687 | #ifdef CONFIG_DEBUG_FS | ||
688 | static int dtv_reg_show(struct seq_file *s, void *unused) | ||
689 | { | ||
690 | struct tegra_dtv_context *dtv_ctx = s->private; | ||
691 | |||
692 | seq_printf(s, "tegra_dtv register list\n"); | ||
693 | seq_printf(s, "-------------------------------\n"); | ||
694 | seq_printf(s, "DTV_SPI_CONTROL_0: 0x%08x\n", | ||
695 | tegra_dtv_readl(dtv_ctx, DTV_SPI_CONTROL)); | ||
696 | seq_printf(s, "DTV_MODE_0: 0x%08x\n", | ||
697 | tegra_dtv_readl(dtv_ctx, DTV_MODE)); | ||
698 | seq_printf(s, "DTV_CONTROL: 0x%08x\n", | ||
699 | tegra_dtv_readl(dtv_ctx, DTV_CTRL)); | ||
700 | seq_printf(s, "DTV_FIFO: 0x%08x\n", | ||
701 | tegra_dtv_readl(dtv_ctx, DTV_RX_FIFO)); | ||
702 | |||
703 | return 0; | ||
704 | |||
705 | } | ||
706 | |||
707 | static int dtv_debugfs_open(struct inode *inode, struct file *file) | ||
708 | { | ||
709 | return single_open(file, dtv_reg_show, inode->i_private); | ||
710 | } | ||
711 | |||
712 | static const struct file_operations dtv_debugfs_fops = { | ||
713 | .open = dtv_debugfs_open, | ||
714 | .read = seq_read, | ||
715 | .llseek = seq_lseek, | ||
716 | .release = single_release, | ||
717 | }; | ||
718 | |||
719 | static int dtv_debugfs_init(struct tegra_dtv_context *dtv_ctx) | ||
720 | { | ||
721 | struct dentry *d; | ||
722 | |||
723 | d = debugfs_create_file("tegra_dtv", S_IRUGO, NULL, dtv_ctx, | ||
724 | &dtv_debugfs_fops); | ||
725 | if (!d) | ||
726 | return -ENOMEM; | ||
727 | |||
728 | dtv_ctx->d = d; | ||
729 | |||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | static void dtv_debugfs_exit(struct tegra_dtv_context *dtv_ctx) | ||
734 | { | ||
735 | debugfs_remove(dtv_ctx->d); | ||
736 | } | ||
737 | #else | ||
738 | static int dtv_debugfs_init(struct tegra_dtv_context *dtv_ctx) { return 0; } | ||
739 | static void dtv_debugfs_exit(struct tegra_dtv_context *dtv_ctx) {}; | ||
740 | #endif | ||
741 | |||
742 | static void setup_dma_rx_request(struct tegra_dma_req *req, | ||
743 | struct dtv_stream *s) | ||
744 | { | ||
745 | struct tegra_dtv_context *dtv_ctx; | ||
746 | |||
747 | pr_debug("%s before to_ctx\n", __func__); | ||
748 | dtv_ctx = to_ctx(s); | ||
749 | |||
750 | pr_debug("%s called\n", __func__); | ||
751 | |||
752 | memset(req, 0, sizeof(*req)); | ||
753 | |||
754 | req->complete = tegra_dtv_rx_dma_complete; | ||
755 | req->dev = s; | ||
756 | req->to_memory = true; | ||
757 | req->req_sel = TEGRA_DMA_REQ_SEL_DTV; | ||
758 | |||
759 | req->source_addr = dtv_ctx->phys + DTV_RX_FIFO; | ||
760 | req->source_wrap = 4; | ||
761 | req->source_bus_width = 32; | ||
762 | req->fixed_burst_size = 1; | ||
763 | |||
764 | req->dest_wrap = 0; | ||
765 | req->dest_bus_width = 32; | ||
766 | } | ||
767 | |||
768 | static int setup_dma(struct tegra_dtv_context *dtv_ctx) | ||
769 | { | ||
770 | int ret = 0; | ||
771 | int i; | ||
772 | |||
773 | pr_debug("%s called\n", __func__); | ||
774 | |||
775 | for (i = 0; i < dtv_ctx->stream.num_bufs; i++) { | ||
776 | dtv_ctx->stream.buf_phy[i] = dma_map_single( | ||
777 | &dtv_ctx->pdev->dev, | ||
778 | dtv_ctx->stream.buffer[i], | ||
779 | dtv_ctx->stream.buf_size, | ||
780 | DMA_FROM_DEVICE); | ||
781 | BUG_ON(!dtv_ctx->stream.buf_phy[i]); | ||
782 | setup_dma_rx_request(&dtv_ctx->stream.dma_req[i], | ||
783 | &dtv_ctx->stream); | ||
784 | dtv_ctx->stream.dma_req[i].dest_addr = | ||
785 | dtv_ctx->stream.buf_phy[i]; | ||
786 | } | ||
787 | dtv_ctx->stream.dma_chan = tegra_dma_allocate_channel( | ||
788 | TEGRA_DMA_MODE_CONTINUOUS_SINGLE, | ||
789 | "tegra_dtv_rx", dtv_ctx->dma_req_sel); | ||
790 | if (!dtv_ctx->stream.dma_chan) { | ||
791 | pr_err("%s : cannot allocate input DMA channel: %ld\n", | ||
792 | __func__, PTR_ERR(dtv_ctx->stream.dma_chan)); | ||
793 | ret = -ENODEV; | ||
794 | /* release */ | ||
795 | for (i = 0; i < dtv_ctx->stream.num_bufs; i++) { | ||
796 | dma_unmap_single(&dtv_ctx->pdev->dev, | ||
797 | dtv_ctx->stream.buf_phy[i], | ||
798 | 1 << DTV_BUF_SIZE_ORDER, | ||
799 | DMA_FROM_DEVICE); | ||
800 | dtv_ctx->stream.buf_phy[i] = 0; | ||
801 | } | ||
802 | tegra_dma_free_channel(dtv_ctx->stream.dma_chan); | ||
803 | dtv_ctx->stream.dma_chan = 0; | ||
804 | |||
805 | return ret; | ||
806 | } | ||
807 | |||
808 | return ret; | ||
809 | } | ||
810 | |||
811 | static void tear_down_dma(struct tegra_dtv_context *dtv_ctx) | ||
812 | { | ||
813 | int i; | ||
814 | |||
815 | pr_debug("%s called\n", __func__); | ||
816 | |||
817 | for (i = 0; i < dtv_ctx->stream.num_bufs; i++) { | ||
818 | dma_unmap_single(&dtv_ctx->pdev->dev, | ||
819 | dtv_ctx->stream.buf_phy[i], | ||
820 | 1 << DTV_BUF_SIZE_ORDER, | ||
821 | DMA_FROM_DEVICE); | ||
822 | dtv_ctx->stream.buf_phy[i] = 0; | ||
823 | } | ||
824 | tegra_dma_free_channel(dtv_ctx->stream.dma_chan); | ||
825 | dtv_ctx->stream.dma_chan = 0; | ||
826 | } | ||
827 | |||
828 | static int init_stream_buffer(struct dtv_stream *s, unsigned num) | ||
829 | { | ||
830 | int ret; | ||
831 | int i, j; | ||
832 | |||
833 | pr_debug("%s (num %d)\n", __func__, num); | ||
834 | |||
835 | for (i = 0; i < num; i++) { | ||
836 | kfree(s->buffer[i]); | ||
837 | s->buffer[i] = kmalloc((1 << DTV_BUF_SIZE_ORDER), | ||
838 | GFP_KERNEL | GFP_DMA); | ||
839 | if (!s->buffer[i]) { | ||
840 | pr_err("%s : cannot allocate buffer.\n", __func__); | ||
841 | for (j = i - 1; j >= 0; j--) { | ||
842 | kfree(s->buffer[j]); | ||
843 | s->buffer[j] = 0; | ||
844 | } | ||
845 | ret = -ENOMEM; | ||
846 | return ret; | ||
847 | } | ||
848 | } | ||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | static void release_stream_buffer(struct dtv_stream *s, unsigned num) | ||
853 | { | ||
854 | int i; | ||
855 | |||
856 | pr_debug("%s (num %d)\n", __func__, num); | ||
857 | |||
858 | for (i = 0; i < num; i++) { | ||
859 | kfree(s->buffer[i]); | ||
860 | s->buffer[i] = 0; | ||
861 | } | ||
862 | } | ||
863 | |||
864 | static int setup_stream(struct dtv_stream *stream) | ||
865 | { | ||
866 | int ret = 0; | ||
867 | int i; | ||
868 | |||
869 | pr_debug("%s called\n", __func__); | ||
870 | |||
871 | stream->xferring = false; | ||
872 | mutex_init(&stream->mtx); | ||
873 | init_completion(&stream->stop_completion); | ||
874 | spin_lock_init(&stream->dma_req_lock); | ||
875 | stream->dma_chan = NULL; | ||
876 | stream->fifo_atn_level = DTV_FIFO_ATN_LVL_TOP_GEAR; | ||
877 | stream->buf_size = 1 << DTV_BUF_SIZE_ORDER; | ||
878 | stream->num_bufs = DTV_MAX_NUM_BUFS; | ||
879 | /* init each buffer */ | ||
880 | for (i = 0; i < stream->num_bufs; i++) { | ||
881 | init_completion(&stream->comp[i]); | ||
882 | /* complete all at this moment */ | ||
883 | complete(&stream->comp[i]); | ||
884 | stream->buffer[i] = 0; | ||
885 | stream->buf_phy[i] = 0; | ||
886 | } | ||
887 | stream->last_queued = 0; | ||
888 | ret = init_stream_buffer(stream, stream->num_bufs); | ||
889 | if (ret < 0) | ||
890 | return ret; | ||
891 | |||
892 | INIT_WORK(&stream->work, tegra_dtv_worker); | ||
893 | wake_lock_init(&stream->wake_lock, WAKE_LOCK_SUSPEND, "tegra_dtv"); | ||
894 | |||
895 | return ret; | ||
896 | } | ||
897 | |||
898 | static int tegra_dtv_probe(struct platform_device *pdev) | ||
899 | { | ||
900 | int ret; | ||
901 | struct tegra_dtv_context *dtv_ctx; | ||
902 | struct clk *clk; | ||
903 | struct resource *res; | ||
904 | |||
905 | pr_info("%s: probing dtv.\n", __func__); | ||
906 | |||
907 | dtv_ctx = devm_kzalloc(&pdev->dev, sizeof(struct tegra_dtv_context), | ||
908 | GFP_KERNEL); | ||
909 | if (!dtv_ctx) { | ||
910 | pr_err("%s: Failed to allocate memory for dtv context.\n", | ||
911 | __func__); | ||
912 | ret = -ENOMEM; | ||
913 | return ret; | ||
914 | } | ||
915 | platform_set_drvdata(pdev, dtv_ctx); | ||
916 | |||
917 | /* for refer back */ | ||
918 | dtv_ctx->pdev = pdev; | ||
919 | |||
920 | /* enable clk for dtv */ | ||
921 | clk = clk_get(&pdev->dev, NULL); | ||
922 | if (!clk) { | ||
923 | dev_err(&pdev->dev, "cannot get clock for tegra_dtv.\n"); | ||
924 | ret = -EIO; | ||
925 | goto fail_no_clk; | ||
926 | } | ||
927 | ret = clk_enable(clk); | ||
928 | if (ret < 0) { | ||
929 | dev_err(&pdev->dev, "cannot enable clk for tegra_dtv.\n"); | ||
930 | goto fail_clk_enable; | ||
931 | } | ||
932 | dtv_ctx->clk_enabled = 1; | ||
933 | |||
934 | /* get resource */ | ||
935 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
936 | if (unlikely(!res)) { | ||
937 | pr_err("%s: Failed to get resource for dtv.\n", | ||
938 | __func__); | ||
939 | ret = -ENODEV; | ||
940 | goto fail_no_res; | ||
941 | } | ||
942 | |||
943 | if (!devm_request_mem_region(&pdev->dev, res->start, | ||
944 | resource_size(res), dev_name(&pdev->dev))) { | ||
945 | ret = -EBUSY; | ||
946 | return ret; | ||
947 | } | ||
948 | dtv_ctx->phys = res->start; | ||
949 | dtv_ctx->base = devm_ioremap(&pdev->dev, res->start, | ||
950 | resource_size(res)); | ||
951 | if (!dtv_ctx->base) { | ||
952 | dev_err(&pdev->dev, "cannot ioremap iomem.\n"); | ||
953 | ret = -ENOMEM; | ||
954 | return ret; | ||
955 | } | ||
956 | |||
957 | ret = setup_stream(&dtv_ctx->stream); | ||
958 | if (ret < 0) | ||
959 | goto fail_setup_stream; | ||
960 | |||
961 | ret = setup_dma(dtv_ctx); | ||
962 | if (ret < 0) | ||
963 | goto fail_setup_dma; | ||
964 | |||
965 | /* register as a misc device */ | ||
966 | dtv_ctx->miscdev.minor = MISC_DYNAMIC_MINOR; | ||
967 | dtv_ctx->miscdev.name = TEGRA_DTV_NAME; | ||
968 | dtv_ctx->miscdev.fops = &tegra_dtv_fops; | ||
969 | ret = misc_register(&dtv_ctx->miscdev); | ||
970 | if (ret) { | ||
971 | pr_err("%s: Unable to register misc device.\n", | ||
972 | __func__); | ||
973 | ret = -ENODEV; | ||
974 | goto fail_misc_reg; | ||
975 | } | ||
976 | |||
977 | ret = dtv_debugfs_init(dtv_ctx); | ||
978 | if (ret) { | ||
979 | pr_err("%s: Unable to register debugfs entry.\n", | ||
980 | __func__); | ||
981 | goto fail_debugfs_reg; | ||
982 | } | ||
983 | |||
984 | return 0; | ||
985 | |||
986 | fail_debugfs_reg: | ||
987 | dtv_debugfs_exit(dtv_ctx); | ||
988 | fail_misc_reg: | ||
989 | misc_deregister(&dtv_ctx->miscdev); | ||
990 | fail_setup_stream: | ||
991 | fail_setup_dma: | ||
992 | tear_down_dma(dtv_ctx); | ||
993 | fail_no_res: | ||
994 | fail_clk_enable: | ||
995 | fail_no_clk: | ||
996 | if (clk) | ||
997 | clk_put(clk); | ||
998 | |||
999 | return ret; | ||
1000 | } | ||
1001 | |||
1002 | static int __devexit tegra_dtv_remove(struct platform_device *pdev) | ||
1003 | { | ||
1004 | struct tegra_dtv_context *dtv_ctx; | ||
1005 | |||
1006 | pr_info("%s: remove dtv.\n", __func__); | ||
1007 | |||
1008 | dtv_ctx = platform_get_drvdata(pdev); | ||
1009 | |||
1010 | dtv_debugfs_exit(dtv_ctx); | ||
1011 | tear_down_dma(dtv_ctx); | ||
1012 | release_stream_buffer(&dtv_ctx->stream, dtv_ctx->stream.num_bufs); | ||
1013 | |||
1014 | clk_put(dtv_ctx->clk); | ||
1015 | |||
1016 | misc_deregister(&dtv_ctx->miscdev); | ||
1017 | |||
1018 | return 0; | ||
1019 | } | ||
1020 | |||
1021 | #ifdef CONFIG_PM | ||
1022 | static int tegra_dtv_suspend(struct platform_device *pdev, pm_message_t state) | ||
1023 | { | ||
1024 | struct tegra_dtv_context *dtv_ctx; | ||
1025 | |||
1026 | pr_info("%s: suspend dtv.\n", __func__); | ||
1027 | |||
1028 | dtv_ctx = platform_get_drvdata(pdev); | ||
1029 | |||
1030 | /* stop xferring */ | ||
1031 | mutex_lock(&dtv_ctx->stream.mtx); | ||
1032 | if (dtv_ctx->stream.xferring) { | ||
1033 | stop_xfer_unsafe(&dtv_ctx->stream); | ||
1034 | /* clean up stop condition */ | ||
1035 | complete(&dtv_ctx->stream.stop_completion); | ||
1036 | __force_xfer_stop(&dtv_ctx->stream); | ||
1037 | } | ||
1038 | /* wakeup any pending process */ | ||
1039 | wakeup_suspend(&dtv_ctx->stream); | ||
1040 | mutex_unlock(&dtv_ctx->stream.mtx); | ||
1041 | |||
1042 | clk_disable(dtv_ctx->clk); | ||
1043 | |||
1044 | return 0; | ||
1045 | } | ||
1046 | |||
1047 | static int tegra_dtv_resume(struct platform_device *pdev) | ||
1048 | { | ||
1049 | struct tegra_dtv_context *dtv_ctx; | ||
1050 | |||
1051 | pr_info("%s: resume dtv.\n", __func__); | ||
1052 | |||
1053 | dtv_ctx = platform_get_drvdata(pdev); | ||
1054 | clk_enable(dtv_ctx->clk); | ||
1055 | |||
1056 | return 0; | ||
1057 | } | ||
1058 | #endif /* CONFIG_PM */ | ||
1059 | |||
1060 | static struct platform_driver tegra_dtv_driver = { | ||
1061 | .driver = { | ||
1062 | .name = TEGRA_DTV_NAME, | ||
1063 | .owner = THIS_MODULE, | ||
1064 | }, | ||
1065 | .probe = tegra_dtv_probe, | ||
1066 | .remove = __devexit_p(tegra_dtv_remove), | ||
1067 | #ifdef CONFIG_PM | ||
1068 | .suspend = tegra_dtv_suspend, | ||
1069 | .resume = tegra_dtv_resume, | ||
1070 | #endif | ||
1071 | }; | ||
1072 | |||
1073 | static int __init tegra_dtv_init(void) | ||
1074 | { | ||
1075 | return platform_driver_register(&tegra_dtv_driver); | ||
1076 | } | ||
1077 | |||
1078 | static void __exit tegra_dtv_exit(void) | ||
1079 | { | ||
1080 | platform_driver_unregister(&tegra_dtv_driver); | ||
1081 | } | ||
1082 | |||
1083 | module_init(tegra_dtv_init); | ||
1084 | module_exit(tegra_dtv_exit); | ||
1085 | |||
1086 | MODULE_AUTHOR("Adam Jiang <chaoj@nvidia.com>"); | ||
1087 | MODULE_DESCRIPTION("Tegra DTV interface driver"); | ||
1088 | MODULE_LICENSE("GPL"); | ||
1089 | MODULE_ALIAS("platform:" TEGRA_DTV_NAME); | ||
diff --git a/drivers/media/video/tegra/tps61050.c b/drivers/media/video/tegra/tps61050.c new file mode 100644 index 00000000000..def353ca9c1 --- /dev/null +++ b/drivers/media/video/tegra/tps61050.c | |||
@@ -0,0 +1,991 @@ | |||
1 | /* | ||
2 | * tps61050.c - tps61050 flash/torch kernel driver | ||
3 | * | ||
4 | * Copyright (C) 2011 NVIDIA Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
18 | * 02111-1307, USA | ||
19 | */ | ||
20 | |||
21 | /* Implementation | ||
22 | * -------------- | ||
23 | * The board level details about the device need to be provided in the board | ||
24 | * file with the tps61050_platform_data structure. | ||
25 | * Standard among NVC kernel drivers in this structure is: | ||
26 | * .cfg = Use the NVC_CFG_ defines that are in nvc_torch.h. | ||
27 | * Descriptions of the configuration options are with the defines. | ||
28 | * This value is typically 0. | ||
29 | * .num = The number of the instance of the device. This should start at 1 and | ||
30 | * and increment for each device on the board. This number will be | ||
31 | * appended to the MISC driver name, Example: /dev/tps61050.1 | ||
32 | * .sync = If there is a need to synchronize two devices, then this value is | ||
33 | * the number of the device instance this device is allowed to sync to. | ||
34 | * This is typically used for stereo applications. | ||
35 | * .dev_name = The MISC driver name the device registers as. If not used, | ||
36 | * then the part number of the device is used for the driver name. | ||
37 | * If using the NVC user driver then use the name found in this | ||
38 | * driver under _default_pdata. | ||
39 | * | ||
40 | * The following is specific to NVC kernel flash/torch drivers: | ||
41 | * .pinstate = a pointer to the nvc_torch_pin_state structure. This | ||
42 | * structure gives the details of which VI GPIO to use to trigger | ||
43 | * the flash. The mask tells which pin and the values is the | ||
44 | * level. For example, if VI GPIO pin 6 is used, then | ||
45 | * .mask = 0x0040 | ||
46 | * .values = 0x0040 | ||
47 | * If VI GPIO pin 0 is used, then | ||
48 | * .mask = 0x0001 | ||
49 | * .values = 0x0001 | ||
50 | * This is typically just one pin but there is some legacy | ||
51 | * here that insinuates more than one pin can be used. | ||
52 | * When the flash level is set, then the driver will return the | ||
53 | * value in values. When the flash level is off, the driver will | ||
54 | * return 0 for the values to deassert the signal. | ||
55 | * If a VI GPIO is not used, then the mask and values must be set | ||
56 | * to 0. The flash may then be triggered via I2C instead. | ||
57 | * However, a VI GPIO is strongly encouraged since it allows | ||
58 | * tighter timing with the picture taken as well as reduced power | ||
59 | * by asserting the trigger signal for only when needed. | ||
60 | * .max_amp_torch = Is the maximum torch value allowed. The value is 0 to | ||
61 | * _MAX_TORCH_LEVEL. This is to allow a limit to the amount | ||
62 | * of amps used. If left blank then _MAX_TORCH_LEVEL will be | ||
63 | * used. | ||
64 | * .max_amp_flash = Is the maximum flash value allowed. The value is 0 to | ||
65 | * _MAX_FLASH_LEVEL. This is to allow a limit to the amount | ||
66 | * of amps used. If left blank then _MAX_FLASH_LEVEL will be | ||
67 | * used. | ||
68 | * | ||
69 | * The following is specific to only this NVC kernel flash/torch driver: | ||
70 | * N/A | ||
71 | * | ||
72 | * Power Requirements | ||
73 | * The board power file must contain the following labels for the power | ||
74 | * regulator(s) of this device: | ||
75 | * "vdd_i2c" = the power regulator for the I2C power. | ||
76 | * Note that this device is typically connected directly to the battery rail | ||
77 | * and does not need a source power regulator (vdd). | ||
78 | * | ||
79 | * The above values should be all that is needed to use the device with this | ||
80 | * driver. Modifications of this driver should not be needed. | ||
81 | */ | ||
82 | |||
83 | |||
84 | #include <linux/fs.h> | ||
85 | #include <linux/i2c.h> | ||
86 | #include <linux/miscdevice.h> | ||
87 | #include <linux/slab.h> | ||
88 | #include <linux/delay.h> | ||
89 | #include <linux/uaccess.h> | ||
90 | #include <linux/list.h> | ||
91 | #include <linux/regulator/consumer.h> | ||
92 | #include <media/nvc.h> | ||
93 | #include <media/tps61050.h> | ||
94 | |||
95 | #define TPS61050_REG0 0x00 | ||
96 | #define TPS61050_REG1 0x01 | ||
97 | #define TPS61050_REG2 0x02 | ||
98 | #define TPS61050_REG3 0x03 | ||
99 | #define tps61050_flash_cap_size (sizeof(tps61050_flash_cap.numberoflevels) \ | ||
100 | + (sizeof(tps61050_flash_cap.levels[0]) \ | ||
101 | * (TPS61050_MAX_FLASH_LEVEL + 1))) | ||
102 | #define tps61050_torch_cap_size (sizeof(tps61050_torch_cap.numberoflevels) \ | ||
103 | + (sizeof(tps61050_torch_cap.guidenum[0]) \ | ||
104 | * (TPS61050_MAX_TORCH_LEVEL + 1))) | ||
105 | |||
106 | |||
107 | static struct nvc_torch_flash_capabilities tps61050_flash_cap = { | ||
108 | TPS61050_MAX_FLASH_LEVEL + 1, | ||
109 | { | ||
110 | { 0, 0xFFFFFFFF, 0 }, | ||
111 | { 150, 558, 2 }, | ||
112 | { 200, 558, 2 }, | ||
113 | { 300, 558, 2 }, | ||
114 | { 400, 558, 2 }, | ||
115 | { 500, 558, 2 }, | ||
116 | { 700, 558, 2 }, | ||
117 | { 900, 558, 2 }, | ||
118 | { 900, 558, 2 } | ||
119 | } | ||
120 | }; | ||
121 | |||
122 | static struct nvc_torch_torch_capabilities tps61050_torch_cap = { | ||
123 | TPS61050_MAX_TORCH_LEVEL + 1, | ||
124 | { | ||
125 | 0, | ||
126 | 50, | ||
127 | 75, | ||
128 | 100, | ||
129 | 150, | ||
130 | 200, | ||
131 | 491, | ||
132 | 491 | ||
133 | } | ||
134 | }; | ||
135 | |||
136 | struct tps61050_info { | ||
137 | atomic_t in_use; | ||
138 | struct i2c_client *i2c_client; | ||
139 | struct tps61050_platform_data *pdata; | ||
140 | struct miscdevice miscdev; | ||
141 | struct list_head list; | ||
142 | int pwr_api; | ||
143 | int pwr_dev; | ||
144 | struct nvc_regulator vreg_i2c; | ||
145 | u8 s_mode; | ||
146 | struct tps61050_info *s_info; | ||
147 | }; | ||
148 | |||
149 | static struct nvc_torch_pin_state tps61050_default_pinstate = { | ||
150 | .mask = 0x0000, | ||
151 | .values = 0x0000, | ||
152 | }; | ||
153 | |||
154 | static struct tps61050_platform_data tps61050_default_pdata = { | ||
155 | .cfg = 0, | ||
156 | .num = 0, | ||
157 | .sync = 0, | ||
158 | .dev_name = "torch", | ||
159 | .pinstate = &tps61050_default_pinstate, | ||
160 | .max_amp_torch = TPS61050_MAX_TORCH_LEVEL, | ||
161 | .max_amp_flash = TPS61050_MAX_FLASH_LEVEL, | ||
162 | }; | ||
163 | |||
164 | static LIST_HEAD(tps61050_info_list); | ||
165 | static DEFINE_SPINLOCK(tps61050_spinlock); | ||
166 | |||
167 | |||
168 | static int tps61050_i2c_rd(struct tps61050_info *info, u8 reg, u8 *val) | ||
169 | { | ||
170 | struct i2c_msg msg[2]; | ||
171 | u8 buf[2]; | ||
172 | |||
173 | buf[0] = reg; | ||
174 | msg[0].addr = info->i2c_client->addr; | ||
175 | msg[0].flags = 0; | ||
176 | msg[0].len = 1; | ||
177 | msg[0].buf = &buf[0]; | ||
178 | msg[1].addr = info->i2c_client->addr; | ||
179 | msg[1].flags = I2C_M_RD; | ||
180 | msg[1].len = 1; | ||
181 | msg[1].buf = &buf[1]; | ||
182 | *val = 0; | ||
183 | if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2) | ||
184 | return -EIO; | ||
185 | |||
186 | *val = buf[1]; | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static int tps61050_i2c_wr(struct tps61050_info *info, u8 reg, u8 val) | ||
191 | { | ||
192 | struct i2c_msg msg; | ||
193 | u8 buf[2]; | ||
194 | |||
195 | buf[0] = reg; | ||
196 | buf[1] = val; | ||
197 | msg.addr = info->i2c_client->addr; | ||
198 | msg.flags = 0; | ||
199 | msg.len = 2; | ||
200 | msg.buf = &buf[0]; | ||
201 | if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) | ||
202 | return -EIO; | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static void tps61050_pm_regulator_put(struct nvc_regulator *sreg) | ||
208 | { | ||
209 | regulator_put(sreg->vreg); | ||
210 | sreg->vreg = NULL; | ||
211 | } | ||
212 | |||
213 | static int tps61050_pm_regulator_get(struct tps61050_info *info, | ||
214 | struct nvc_regulator *sreg, | ||
215 | char vreg_name[]) | ||
216 | { | ||
217 | int err = 0; | ||
218 | |||
219 | sreg->vreg_flag = 0; | ||
220 | sreg->vreg = regulator_get(&info->i2c_client->dev, vreg_name); | ||
221 | if (IS_ERR_OR_NULL(sreg->vreg)) { | ||
222 | dev_err(&info->i2c_client->dev, | ||
223 | "%s err for regulator: %s err: %d\n", | ||
224 | __func__, vreg_name, (int)sreg->vreg); | ||
225 | err = PTR_ERR(sreg->vreg); | ||
226 | sreg->vreg = NULL; | ||
227 | } else { | ||
228 | sreg->vreg_name = vreg_name; | ||
229 | dev_dbg(&info->i2c_client->dev, | ||
230 | "%s vreg_name: %s\n", | ||
231 | __func__, sreg->vreg_name); | ||
232 | } | ||
233 | return err; | ||
234 | } | ||
235 | |||
236 | static int tps61050_pm_regulator_en(struct tps61050_info *info, | ||
237 | struct nvc_regulator *sreg) | ||
238 | { | ||
239 | int err = 0; | ||
240 | |||
241 | if (!sreg->vreg_flag && (sreg->vreg != NULL)) { | ||
242 | err = regulator_enable(sreg->vreg); | ||
243 | if (!err) { | ||
244 | dev_dbg(&info->i2c_client->dev, | ||
245 | "%s vreg_name: %s\n", | ||
246 | __func__, sreg->vreg_name); | ||
247 | sreg->vreg_flag = 1; | ||
248 | err = 1; /* flag regulator state change */ | ||
249 | mdelay(5); /* device powerup delay */ | ||
250 | } else { | ||
251 | dev_err(&info->i2c_client->dev, | ||
252 | "%s err, regulator: %s\n", | ||
253 | __func__, sreg->vreg_name); | ||
254 | } | ||
255 | } | ||
256 | return err; | ||
257 | } | ||
258 | |||
259 | static int tps61050_pm_regulator_dis(struct tps61050_info *info, | ||
260 | struct nvc_regulator *sreg) | ||
261 | { | ||
262 | int err = 0; | ||
263 | |||
264 | if (sreg->vreg_flag && (sreg->vreg != NULL)) { | ||
265 | err = regulator_disable(sreg->vreg); | ||
266 | if (!err) | ||
267 | dev_dbg(&info->i2c_client->dev, | ||
268 | "%s vreg_name: %s\n", | ||
269 | __func__, sreg->vreg_name); | ||
270 | else | ||
271 | dev_err(&info->i2c_client->dev, | ||
272 | "%s err, regulator: %s\n", | ||
273 | __func__, sreg->vreg_name); | ||
274 | } | ||
275 | sreg->vreg_flag = 0; | ||
276 | return err; | ||
277 | } | ||
278 | |||
279 | static int tps61050_pm_wr(struct tps61050_info *info, int pwr) | ||
280 | { | ||
281 | int err = 0; | ||
282 | u8 reg; | ||
283 | |||
284 | if (pwr == info->pwr_dev) | ||
285 | return 0; | ||
286 | |||
287 | switch (pwr) { | ||
288 | case NVC_PWR_OFF: | ||
289 | if ((info->pdata->cfg & NVC_CFG_OFF2STDBY) || | ||
290 | (info->pdata->cfg & NVC_CFG_BOOT_INIT)) { | ||
291 | pwr = NVC_PWR_STDBY; | ||
292 | } else { | ||
293 | err = tps61050_pm_regulator_en(info, &info->vreg_i2c); | ||
294 | err |= tps61050_i2c_wr(info, TPS61050_REG0, 0x00); | ||
295 | err |= tps61050_i2c_wr(info, TPS61050_REG1, 0x00); | ||
296 | err |= tps61050_pm_regulator_dis(info, &info->vreg_i2c); | ||
297 | break; | ||
298 | } | ||
299 | case NVC_PWR_STDBY_OFF: | ||
300 | if ((info->pdata->cfg & NVC_CFG_OFF2STDBY) || | ||
301 | (info->pdata->cfg & NVC_CFG_BOOT_INIT)) { | ||
302 | pwr = NVC_PWR_STDBY; | ||
303 | } else { | ||
304 | err = tps61050_pm_regulator_en(info, &info->vreg_i2c); | ||
305 | err |= tps61050_i2c_wr(info, TPS61050_REG0, 0x00); | ||
306 | err |= tps61050_i2c_wr(info, TPS61050_REG1, 0x00); | ||
307 | break; | ||
308 | } | ||
309 | case NVC_PWR_STDBY: | ||
310 | err = tps61050_pm_regulator_en(info, &info->vreg_i2c); | ||
311 | err |= tps61050_i2c_rd(info, TPS61050_REG0, ®); | ||
312 | reg &= 0x3F; /* 7:6 = mode */ | ||
313 | err |= tps61050_i2c_wr(info, TPS61050_REG0, reg); | ||
314 | break; | ||
315 | |||
316 | case NVC_PWR_COMM: | ||
317 | case NVC_PWR_ON: | ||
318 | err = tps61050_pm_regulator_en(info, &info->vreg_i2c); | ||
319 | break; | ||
320 | |||
321 | default: | ||
322 | err = -EINVAL; | ||
323 | break; | ||
324 | } | ||
325 | |||
326 | if (err < 0) { | ||
327 | dev_err(&info->i2c_client->dev, "%s error\n", __func__); | ||
328 | pwr = NVC_PWR_ERR; | ||
329 | } | ||
330 | info->pwr_dev = pwr; | ||
331 | if (err > 0) | ||
332 | return 0; | ||
333 | |||
334 | return err; | ||
335 | } | ||
336 | |||
337 | static int tps61050_pm_wr_s(struct tps61050_info *info, int pwr) | ||
338 | { | ||
339 | int err1 = 0; | ||
340 | int err2 = 0; | ||
341 | |||
342 | if ((info->s_mode == NVC_SYNC_OFF) || | ||
343 | (info->s_mode == NVC_SYNC_MASTER) || | ||
344 | (info->s_mode == NVC_SYNC_STEREO)) | ||
345 | err1 = tps61050_pm_wr(info, pwr); | ||
346 | if ((info->s_mode == NVC_SYNC_SLAVE) || | ||
347 | (info->s_mode == NVC_SYNC_STEREO)) | ||
348 | err2 = tps61050_pm_wr(info->s_info, pwr); | ||
349 | return err1 | err2; | ||
350 | } | ||
351 | |||
352 | static int tps61050_pm_api_wr(struct tps61050_info *info, int pwr) | ||
353 | { | ||
354 | int err = 0; | ||
355 | |||
356 | if (!pwr || (pwr > NVC_PWR_ON)) | ||
357 | return 0; | ||
358 | |||
359 | if (pwr > info->pwr_dev) | ||
360 | err = tps61050_pm_wr_s(info, pwr); | ||
361 | if (!err) | ||
362 | info->pwr_api = pwr; | ||
363 | else | ||
364 | info->pwr_api = NVC_PWR_ERR; | ||
365 | if (info->pdata->cfg & NVC_CFG_NOERR) | ||
366 | return 0; | ||
367 | |||
368 | return err; | ||
369 | } | ||
370 | |||
371 | static int tps61050_pm_dev_wr(struct tps61050_info *info, int pwr) | ||
372 | { | ||
373 | if (pwr < info->pwr_api) | ||
374 | pwr = info->pwr_api; | ||
375 | return tps61050_pm_wr(info, pwr); | ||
376 | } | ||
377 | |||
378 | static void tps61050_pm_exit(struct tps61050_info *info) | ||
379 | { | ||
380 | tps61050_pm_wr_s(info, NVC_PWR_OFF); | ||
381 | tps61050_pm_regulator_put(&info->vreg_i2c); | ||
382 | } | ||
383 | |||
384 | static void tps61050_pm_init(struct tps61050_info *info) | ||
385 | { | ||
386 | tps61050_pm_regulator_get(info, &info->vreg_i2c, "vdd_i2c"); | ||
387 | } | ||
388 | |||
389 | struct tps61050_reg_init { | ||
390 | u8 mask; | ||
391 | u8 val; | ||
392 | }; | ||
393 | |||
394 | static struct tps61050_reg_init tps61050_reg_init_id[] = { | ||
395 | {0xC0, 0x00}, | ||
396 | {0xC0, 0x00}, | ||
397 | {0x87, 0x00}, | ||
398 | {0xFF, 0xD1}, | ||
399 | }; | ||
400 | |||
401 | static int tps61050_dev_id(struct tps61050_info *info) | ||
402 | { | ||
403 | u8 reg; | ||
404 | u8 i; | ||
405 | int err; | ||
406 | |||
407 | tps61050_pm_dev_wr(info, NVC_PWR_COMM); | ||
408 | /* There isn't a device ID so we just check that all the registers | ||
409 | * equal their startup defaults. | ||
410 | */ | ||
411 | for (i = TPS61050_REG0; i <= TPS61050_REG3; i++) { | ||
412 | err = tps61050_i2c_rd(info, i, ®); | ||
413 | if (err) { | ||
414 | break; | ||
415 | } else { | ||
416 | reg &= tps61050_reg_init_id[i].mask; | ||
417 | if (reg != tps61050_reg_init_id[i].val) { | ||
418 | err = -ENODEV; | ||
419 | break; | ||
420 | } | ||
421 | } | ||
422 | } | ||
423 | tps61050_pm_dev_wr(info, NVC_PWR_OFF); | ||
424 | return err; | ||
425 | } | ||
426 | |||
427 | static int tps61050_param_rd(struct tps61050_info *info, long arg) | ||
428 | { | ||
429 | struct nvc_param params; | ||
430 | struct nvc_torch_pin_state pinstate; | ||
431 | const void *data_ptr; | ||
432 | u8 reg; | ||
433 | u32 data_size = 0; | ||
434 | int err; | ||
435 | |||
436 | if (copy_from_user(¶ms, | ||
437 | (const void __user *)arg, | ||
438 | sizeof(struct nvc_param))) { | ||
439 | dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", | ||
440 | __func__, __LINE__); | ||
441 | return -EINVAL; | ||
442 | } | ||
443 | |||
444 | if (info->s_mode == NVC_SYNC_SLAVE) | ||
445 | info = info->s_info; | ||
446 | switch (params.param) { | ||
447 | case NVC_PARAM_FLASH_CAPS: | ||
448 | dev_dbg(&info->i2c_client->dev, "%s FLASH_CAPS\n", __func__); | ||
449 | data_ptr = &tps61050_flash_cap; | ||
450 | data_size = tps61050_flash_cap_size; | ||
451 | break; | ||
452 | |||
453 | case NVC_PARAM_FLASH_LEVEL: | ||
454 | tps61050_pm_dev_wr(info, NVC_PWR_COMM); | ||
455 | err = tps61050_i2c_rd(info, TPS61050_REG1, ®); | ||
456 | tps61050_pm_dev_wr(info, NVC_PWR_OFF); | ||
457 | if (err < 0) | ||
458 | return err; | ||
459 | |||
460 | if (reg & 0x80) { /* 7:7 flash on/off */ | ||
461 | reg &= 0x07; /* 2:0 flash setting */ | ||
462 | reg++; /* flash setting +1 if flash on */ | ||
463 | } else { | ||
464 | reg = 0; /* flash is off */ | ||
465 | } | ||
466 | dev_dbg(&info->i2c_client->dev, "%s FLASH_LEVEL: %u\n", | ||
467 | __func__, | ||
468 | (unsigned)tps61050_flash_cap.levels[reg].guidenum); | ||
469 | data_ptr = &tps61050_flash_cap.levels[reg].guidenum; | ||
470 | data_size = sizeof(tps61050_flash_cap.levels[reg].guidenum); | ||
471 | break; | ||
472 | |||
473 | case NVC_PARAM_TORCH_CAPS: | ||
474 | dev_dbg(&info->i2c_client->dev, "%s TORCH_CAPS\n", __func__); | ||
475 | data_ptr = &tps61050_torch_cap; | ||
476 | data_size = tps61050_torch_cap_size; | ||
477 | break; | ||
478 | |||
479 | case NVC_PARAM_TORCH_LEVEL: | ||
480 | tps61050_pm_dev_wr(info, NVC_PWR_COMM); | ||
481 | err = tps61050_i2c_rd(info, TPS61050_REG0, ®); | ||
482 | tps61050_pm_dev_wr(info, NVC_PWR_OFF); | ||
483 | if (err < 0) | ||
484 | return err; | ||
485 | |||
486 | reg &= 0x07; | ||
487 | dev_dbg(&info->i2c_client->dev, "%s TORCH_LEVEL: %u\n", | ||
488 | __func__, | ||
489 | (unsigned)tps61050_torch_cap.guidenum[reg]); | ||
490 | data_ptr = &tps61050_torch_cap.guidenum[reg]; | ||
491 | data_size = sizeof(tps61050_torch_cap.guidenum[reg]); | ||
492 | break; | ||
493 | |||
494 | case NVC_PARAM_FLASH_PIN_STATE: | ||
495 | pinstate.mask = info->pdata->pinstate->mask; | ||
496 | tps61050_pm_dev_wr(info, NVC_PWR_COMM); | ||
497 | err = tps61050_i2c_rd(info, TPS61050_REG1, ®); | ||
498 | tps61050_pm_dev_wr(info, NVC_PWR_OFF); | ||
499 | if (err < 0) | ||
500 | return err; | ||
501 | |||
502 | reg &= 0x80; /* 7:7=flash enable */ | ||
503 | if (reg) | ||
504 | /* assert strobe */ | ||
505 | pinstate.values = info->pdata->pinstate->values; | ||
506 | else | ||
507 | pinstate.values = 0; /* deassert strobe */ | ||
508 | dev_dbg(&info->i2c_client->dev, "%s FLASH_PIN_STATE: %x&%x\n", | ||
509 | __func__, pinstate.mask, pinstate.values); | ||
510 | data_ptr = &pinstate; | ||
511 | data_size = sizeof(struct nvc_torch_pin_state); | ||
512 | break; | ||
513 | |||
514 | case NVC_PARAM_STEREO: | ||
515 | dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n", | ||
516 | __func__, (int)info->s_mode); | ||
517 | data_ptr = &info->s_mode; | ||
518 | data_size = sizeof(info->s_mode); | ||
519 | break; | ||
520 | |||
521 | default: | ||
522 | dev_err(&info->i2c_client->dev, | ||
523 | "%s unsupported parameter: %d\n", | ||
524 | __func__, params.param); | ||
525 | return -EINVAL; | ||
526 | } | ||
527 | |||
528 | if (params.sizeofvalue < data_size) { | ||
529 | dev_err(&info->i2c_client->dev, | ||
530 | "%s data size mismatch %d != %d\n", | ||
531 | __func__, params.sizeofvalue, data_size); | ||
532 | return -EINVAL; | ||
533 | } | ||
534 | |||
535 | if (copy_to_user((void __user *)params.p_value, | ||
536 | data_ptr, | ||
537 | data_size)) { | ||
538 | dev_err(&info->i2c_client->dev, | ||
539 | "%s copy_to_user err line %d\n", | ||
540 | __func__, __LINE__); | ||
541 | return -EFAULT; | ||
542 | } | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | static int tps61050_param_wr_s(struct tps61050_info *info, | ||
548 | struct nvc_param *params, | ||
549 | u8 val) | ||
550 | { | ||
551 | u8 reg; | ||
552 | int err = 0; | ||
553 | |||
554 | /* | ||
555 | * 7:6 flash/torch mode | ||
556 | * 0 0 = off (power save) | ||
557 | * 0 1 = torch only (torch power is 2:0 REG0 where 0 = off) | ||
558 | * 1 0 = flash and torch (flash power is 2:0 REG1 (0 is a power level)) | ||
559 | * 1 1 = N/A | ||
560 | * Note that 7:6 of REG0 and REG1 are shadowed with each other. | ||
561 | * In the code below we want to turn on/off one | ||
562 | * without affecting the other. | ||
563 | */ | ||
564 | switch (params->param) { | ||
565 | case NVC_PARAM_FLASH_LEVEL: | ||
566 | dev_dbg(&info->i2c_client->dev, "%s FLASH_LEVEL: %d\n", | ||
567 | __func__, val); | ||
568 | tps61050_pm_dev_wr(info, NVC_PWR_ON); | ||
569 | if (val) { | ||
570 | val--; | ||
571 | if (val > tps61050_default_pdata.max_amp_flash) | ||
572 | val = tps61050_default_pdata.max_amp_flash; | ||
573 | /* Amp limit values are in the board-sensors file. */ | ||
574 | if (info->pdata->max_amp_flash && | ||
575 | (val > info->pdata->max_amp_flash)) | ||
576 | val = info->pdata->max_amp_flash; | ||
577 | val |= 0x80; /* 7:7=flash mode */ | ||
578 | } else { | ||
579 | err = tps61050_i2c_rd(info, TPS61050_REG0, ®); | ||
580 | if (reg & 0x07) /* 2:0=torch setting */ | ||
581 | val = 0x40; /* 6:6 enable just torch */ | ||
582 | } | ||
583 | err |= tps61050_i2c_wr(info, TPS61050_REG1, val); | ||
584 | val &= 0xC0; /* 7:6=flash/torch mode */ | ||
585 | if (!val) /* turn pwr off if no flash && no pwr_api */ | ||
586 | tps61050_pm_dev_wr(info, NVC_PWR_OFF); | ||
587 | return err; | ||
588 | |||
589 | case NVC_PARAM_TORCH_LEVEL: | ||
590 | dev_dbg(&info->i2c_client->dev, "%s TORCH_LEVEL: %d\n", | ||
591 | __func__, val); | ||
592 | tps61050_pm_dev_wr(info, NVC_PWR_ON); | ||
593 | err = tps61050_i2c_rd(info, TPS61050_REG1, ®); | ||
594 | reg &= 0x80; /* 7:7=flash */ | ||
595 | if (val) { | ||
596 | if (val > tps61050_default_pdata.max_amp_torch) | ||
597 | val = tps61050_default_pdata.max_amp_torch; | ||
598 | /* Amp limit values are in the board-sensors file. */ | ||
599 | if (info->pdata->max_amp_torch && | ||
600 | (val > info->pdata->max_amp_torch)) | ||
601 | val = info->pdata->max_amp_torch; | ||
602 | if (!reg) /* test if flash/torch off */ | ||
603 | val |= (0x40); /* 6:6=torch only mode */ | ||
604 | } else { | ||
605 | val |= reg; | ||
606 | } | ||
607 | err |= tps61050_i2c_wr(info, TPS61050_REG0, val); | ||
608 | val &= 0xC0; /* 7:6=mode */ | ||
609 | if (!val) /* turn pwr off if no torch && no pwr_api */ | ||
610 | tps61050_pm_dev_wr(info, NVC_PWR_OFF); | ||
611 | return err; | ||
612 | |||
613 | case NVC_PARAM_FLASH_PIN_STATE: | ||
614 | dev_dbg(&info->i2c_client->dev, "%s FLASH_PIN_STATE: %d\n", | ||
615 | __func__, val); | ||
616 | if (val) | ||
617 | val = 0x08; /* 3:3=soft trigger */ | ||
618 | err = tps61050_i2c_rd(info, TPS61050_REG1, ®); | ||
619 | val |= reg; | ||
620 | err |= tps61050_i2c_wr(info, TPS61050_REG1, val); | ||
621 | return err; | ||
622 | |||
623 | default: | ||
624 | dev_err(&info->i2c_client->dev, | ||
625 | "%s unsupported parameter: %d\n", | ||
626 | __func__, params->param); | ||
627 | return -EINVAL; | ||
628 | } | ||
629 | } | ||
630 | |||
631 | static int tps61050_param_wr(struct tps61050_info *info, long arg) | ||
632 | { | ||
633 | struct nvc_param params; | ||
634 | u8 val; | ||
635 | int err = 0; | ||
636 | |||
637 | if (copy_from_user(¶ms, (const void __user *)arg, | ||
638 | sizeof(struct nvc_param))) { | ||
639 | dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", | ||
640 | __func__, __LINE__); | ||
641 | return -EINVAL; | ||
642 | } | ||
643 | |||
644 | if (copy_from_user(&val, (const void __user *)params.p_value, | ||
645 | sizeof(val))) { | ||
646 | dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", | ||
647 | __func__, __LINE__); | ||
648 | return -EINVAL; | ||
649 | } | ||
650 | |||
651 | /* parameters independent of sync mode */ | ||
652 | switch (params.param) { | ||
653 | case NVC_PARAM_STEREO: | ||
654 | dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n", | ||
655 | __func__, (int)val); | ||
656 | if (val == info->s_mode) | ||
657 | return 0; | ||
658 | |||
659 | switch (val) { | ||
660 | case NVC_SYNC_OFF: | ||
661 | info->s_mode = val; | ||
662 | if (info->s_info != NULL) { | ||
663 | info->s_info->s_mode = val; | ||
664 | tps61050_pm_wr(info->s_info, NVC_PWR_OFF); | ||
665 | } | ||
666 | break; | ||
667 | |||
668 | case NVC_SYNC_MASTER: | ||
669 | info->s_mode = val; | ||
670 | if (info->s_info != NULL) | ||
671 | info->s_info->s_mode = val; | ||
672 | break; | ||
673 | |||
674 | case NVC_SYNC_SLAVE: | ||
675 | case NVC_SYNC_STEREO: | ||
676 | if (info->s_info != NULL) { | ||
677 | /* sync power */ | ||
678 | info->s_info->pwr_api = info->pwr_api; | ||
679 | err = tps61050_pm_wr(info->s_info, | ||
680 | info->pwr_dev); | ||
681 | if (!err) { | ||
682 | info->s_mode = val; | ||
683 | info->s_info->s_mode = val; | ||
684 | } else { | ||
685 | tps61050_pm_wr(info->s_info, | ||
686 | NVC_PWR_OFF); | ||
687 | err = -EIO; | ||
688 | } | ||
689 | } else { | ||
690 | err = -EINVAL; | ||
691 | } | ||
692 | break; | ||
693 | |||
694 | default: | ||
695 | err = -EINVAL; | ||
696 | } | ||
697 | if (info->pdata->cfg & NVC_CFG_NOERR) | ||
698 | return 0; | ||
699 | |||
700 | return err; | ||
701 | |||
702 | default: | ||
703 | /* parameters dependent on sync mode */ | ||
704 | switch (info->s_mode) { | ||
705 | case NVC_SYNC_OFF: | ||
706 | case NVC_SYNC_MASTER: | ||
707 | return tps61050_param_wr_s(info, ¶ms, val); | ||
708 | |||
709 | case NVC_SYNC_SLAVE: | ||
710 | return tps61050_param_wr_s(info->s_info, | ||
711 | ¶ms, | ||
712 | val); | ||
713 | |||
714 | case NVC_SYNC_STEREO: | ||
715 | err = tps61050_param_wr_s(info, ¶ms, val); | ||
716 | if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX)) | ||
717 | err |= tps61050_param_wr_s(info->s_info, | ||
718 | ¶ms, | ||
719 | val); | ||
720 | return err; | ||
721 | |||
722 | default: | ||
723 | dev_err(&info->i2c_client->dev, "%s %d internal err\n", | ||
724 | __func__, __LINE__); | ||
725 | return -EINVAL; | ||
726 | } | ||
727 | } | ||
728 | } | ||
729 | |||
730 | static long tps61050_ioctl(struct file *file, | ||
731 | unsigned int cmd, | ||
732 | unsigned long arg) | ||
733 | { | ||
734 | struct tps61050_info *info = file->private_data; | ||
735 | int pwr; | ||
736 | |||
737 | switch (cmd) { | ||
738 | case NVC_IOCTL_PARAM_WR: | ||
739 | return tps61050_param_wr(info, arg); | ||
740 | |||
741 | case NVC_IOCTL_PARAM_RD: | ||
742 | return tps61050_param_rd(info, arg); | ||
743 | |||
744 | case NVC_IOCTL_PWR_WR: | ||
745 | /* This is a Guaranteed Level of Service (GLOS) call */ | ||
746 | pwr = (int)arg * 2; | ||
747 | dev_dbg(&info->i2c_client->dev, "%s PWR_WR: %d\n", | ||
748 | __func__, pwr); | ||
749 | return tps61050_pm_api_wr(info, pwr); | ||
750 | |||
751 | case NVC_IOCTL_PWR_RD: | ||
752 | if (info->s_mode == NVC_SYNC_SLAVE) | ||
753 | pwr = info->s_info->pwr_api / 2; | ||
754 | else | ||
755 | pwr = info->pwr_api / 2; | ||
756 | dev_dbg(&info->i2c_client->dev, "%s PWR_RD: %d\n", | ||
757 | __func__, pwr); | ||
758 | if (copy_to_user((void __user *)arg, (const void *)&pwr, | ||
759 | sizeof(pwr))) { | ||
760 | dev_err(&info->i2c_client->dev, | ||
761 | "%s copy_to_user err line %d\n", | ||
762 | __func__, __LINE__); | ||
763 | return -EFAULT; | ||
764 | } | ||
765 | return 0; | ||
766 | |||
767 | default: | ||
768 | dev_err(&info->i2c_client->dev, "%s unsupported ioctl: %x\n", | ||
769 | __func__, cmd); | ||
770 | return -EINVAL; | ||
771 | } | ||
772 | } | ||
773 | |||
774 | static int tps61050_sync_en(int dev1, int dev2) | ||
775 | { | ||
776 | struct tps61050_info *sync1 = NULL; | ||
777 | struct tps61050_info *sync2 = NULL; | ||
778 | struct tps61050_info *pos = NULL; | ||
779 | |||
780 | rcu_read_lock(); | ||
781 | list_for_each_entry_rcu(pos, &tps61050_info_list, list) { | ||
782 | if (pos->pdata->num == dev1) { | ||
783 | sync1 = pos; | ||
784 | break; | ||
785 | } | ||
786 | } | ||
787 | pos = NULL; | ||
788 | list_for_each_entry_rcu(pos, &tps61050_info_list, list) { | ||
789 | if (pos->pdata->num == dev2) { | ||
790 | sync2 = pos; | ||
791 | break; | ||
792 | } | ||
793 | } | ||
794 | rcu_read_unlock(); | ||
795 | if (sync1 != NULL) | ||
796 | sync1->s_info = NULL; | ||
797 | if (sync2 != NULL) | ||
798 | sync2->s_info = NULL; | ||
799 | if (!dev1 && !dev2) | ||
800 | return 0; /* no err if default instance 0's used */ | ||
801 | |||
802 | if (dev1 == dev2) | ||
803 | return -EINVAL; /* err if sync instance is itself */ | ||
804 | |||
805 | if ((sync1 != NULL) && (sync2 != NULL)) { | ||
806 | sync1->s_info = sync2; | ||
807 | sync2->s_info = sync1; | ||
808 | } | ||
809 | return 0; | ||
810 | } | ||
811 | |||
812 | static int tps61050_sync_dis(struct tps61050_info *info) | ||
813 | { | ||
814 | if (info->s_info != NULL) { | ||
815 | info->s_info->s_mode = 0; | ||
816 | info->s_info->s_info = NULL; | ||
817 | info->s_mode = 0; | ||
818 | info->s_info = NULL; | ||
819 | return 0; | ||
820 | } | ||
821 | |||
822 | return -EINVAL; | ||
823 | } | ||
824 | |||
825 | static int tps61050_open(struct inode *inode, struct file *file) | ||
826 | { | ||
827 | struct tps61050_info *info = NULL; | ||
828 | struct tps61050_info *pos = NULL; | ||
829 | int err; | ||
830 | |||
831 | rcu_read_lock(); | ||
832 | list_for_each_entry_rcu(pos, &tps61050_info_list, list) { | ||
833 | if (pos->miscdev.minor == iminor(inode)) { | ||
834 | info = pos; | ||
835 | break; | ||
836 | } | ||
837 | } | ||
838 | rcu_read_unlock(); | ||
839 | if (!info) | ||
840 | return -ENODEV; | ||
841 | |||
842 | err = tps61050_sync_en(info->pdata->num, info->pdata->sync); | ||
843 | if (err == -EINVAL) | ||
844 | dev_err(&info->i2c_client->dev, | ||
845 | "%s err: invalid num (%u) and sync (%u) instance\n", | ||
846 | __func__, info->pdata->num, info->pdata->sync); | ||
847 | if (atomic_xchg(&info->in_use, 1)) | ||
848 | return -EBUSY; | ||
849 | |||
850 | if (info->s_info != NULL) { | ||
851 | if (atomic_xchg(&info->s_info->in_use, 1)) | ||
852 | return -EBUSY; | ||
853 | } | ||
854 | |||
855 | file->private_data = info; | ||
856 | dev_dbg(&info->i2c_client->dev, "%s\n", __func__); | ||
857 | return 0; | ||
858 | } | ||
859 | |||
860 | static int tps61050_release(struct inode *inode, struct file *file) | ||
861 | { | ||
862 | struct tps61050_info *info = file->private_data; | ||
863 | |||
864 | dev_dbg(&info->i2c_client->dev, "%s\n", __func__); | ||
865 | tps61050_pm_wr_s(info, NVC_PWR_OFF); | ||
866 | file->private_data = NULL; | ||
867 | WARN_ON(!atomic_xchg(&info->in_use, 0)); | ||
868 | if (info->s_info != NULL) | ||
869 | WARN_ON(!atomic_xchg(&info->s_info->in_use, 0)); | ||
870 | tps61050_sync_dis(info); | ||
871 | return 0; | ||
872 | } | ||
873 | |||
874 | static const struct file_operations tps61050_fileops = { | ||
875 | .owner = THIS_MODULE, | ||
876 | .open = tps61050_open, | ||
877 | .unlocked_ioctl = tps61050_ioctl, | ||
878 | .release = tps61050_release, | ||
879 | }; | ||
880 | |||
881 | static void tps61050_del(struct tps61050_info *info) | ||
882 | { | ||
883 | tps61050_pm_exit(info); | ||
884 | tps61050_sync_dis(info); | ||
885 | spin_lock(&tps61050_spinlock); | ||
886 | list_del_rcu(&info->list); | ||
887 | spin_unlock(&tps61050_spinlock); | ||
888 | synchronize_rcu(); | ||
889 | } | ||
890 | |||
891 | static int tps61050_remove(struct i2c_client *client) | ||
892 | { | ||
893 | struct tps61050_info *info = i2c_get_clientdata(client); | ||
894 | |||
895 | dev_dbg(&info->i2c_client->dev, "%s\n", __func__); | ||
896 | misc_deregister(&info->miscdev); | ||
897 | tps61050_del(info); | ||
898 | return 0; | ||
899 | } | ||
900 | |||
901 | static int tps61050_probe( | ||
902 | struct i2c_client *client, | ||
903 | const struct i2c_device_id *id) | ||
904 | { | ||
905 | struct tps61050_info *info; | ||
906 | char dname[16]; | ||
907 | int err; | ||
908 | |||
909 | dev_dbg(&client->dev, "%s\n", __func__); | ||
910 | info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); | ||
911 | if (info == NULL) { | ||
912 | dev_err(&client->dev, "%s: kzalloc error\n", __func__); | ||
913 | return -ENOMEM; | ||
914 | } | ||
915 | |||
916 | info->i2c_client = client; | ||
917 | if (client->dev.platform_data) { | ||
918 | info->pdata = client->dev.platform_data; | ||
919 | } else { | ||
920 | info->pdata = &tps61050_default_pdata; | ||
921 | dev_dbg(&client->dev, | ||
922 | "%s No platform data. Using defaults.\n", | ||
923 | __func__); | ||
924 | } | ||
925 | i2c_set_clientdata(client, info); | ||
926 | INIT_LIST_HEAD(&info->list); | ||
927 | spin_lock(&tps61050_spinlock); | ||
928 | list_add_rcu(&info->list, &tps61050_info_list); | ||
929 | spin_unlock(&tps61050_spinlock); | ||
930 | tps61050_pm_init(info); | ||
931 | err = tps61050_dev_id(info); | ||
932 | if (err < 0) { | ||
933 | dev_err(&client->dev, "%s device not found\n", __func__); | ||
934 | if (info->pdata->cfg & NVC_CFG_NODEV) { | ||
935 | tps61050_del(info); | ||
936 | return -ENODEV; | ||
937 | } | ||
938 | } else { | ||
939 | dev_dbg(&client->dev, "%s device found\n", __func__); | ||
940 | } | ||
941 | |||
942 | if (info->pdata->dev_name != 0) | ||
943 | strcpy(dname, info->pdata->dev_name); | ||
944 | else | ||
945 | strcpy(dname, "tps61050"); | ||
946 | if (info->pdata->num) | ||
947 | snprintf(dname, sizeof(dname), "%s.%u", | ||
948 | dname, info->pdata->num); | ||
949 | info->miscdev.name = dname; | ||
950 | info->miscdev.fops = &tps61050_fileops; | ||
951 | info->miscdev.minor = MISC_DYNAMIC_MINOR; | ||
952 | if (misc_register(&info->miscdev)) { | ||
953 | dev_err(&client->dev, "%s unable to register misc device %s\n", | ||
954 | __func__, dname); | ||
955 | tps61050_del(info); | ||
956 | return -ENODEV; | ||
957 | } | ||
958 | |||
959 | return 0; | ||
960 | } | ||
961 | |||
962 | static const struct i2c_device_id tps61050_id[] = { | ||
963 | { "tps61050", 0 }, | ||
964 | { }, | ||
965 | }; | ||
966 | |||
967 | MODULE_DEVICE_TABLE(i2c, tps61050_id); | ||
968 | |||
969 | static struct i2c_driver tps61050_i2c_driver = { | ||
970 | .driver = { | ||
971 | .name = "tps61050", | ||
972 | .owner = THIS_MODULE, | ||
973 | }, | ||
974 | .id_table = tps61050_id, | ||
975 | .probe = tps61050_probe, | ||
976 | .remove = tps61050_remove, | ||
977 | }; | ||
978 | |||
979 | static int __init tps61050_init(void) | ||
980 | { | ||
981 | return i2c_add_driver(&tps61050_i2c_driver); | ||
982 | } | ||
983 | |||
984 | static void __exit tps61050_exit(void) | ||
985 | { | ||
986 | i2c_del_driver(&tps61050_i2c_driver); | ||
987 | } | ||
988 | |||
989 | module_init(tps61050_init); | ||
990 | module_exit(tps61050_exit); | ||
991 | MODULE_LICENSE("GPL"); | ||