diff options
Diffstat (limited to 'drivers/media/video/tegra/ov2710.c')
-rw-r--r-- | drivers/media/video/tegra/ov2710.c | 690 |
1 files changed, 690 insertions, 0 deletions
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 | |||