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/indycam.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/media/video/indycam.c')
-rw-r--r-- | drivers/media/video/indycam.c | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/drivers/media/video/indycam.c b/drivers/media/video/indycam.c new file mode 100644 index 00000000000..e5ed4db32e7 --- /dev/null +++ b/drivers/media/video/indycam.c | |||
@@ -0,0 +1,401 @@ | |||
1 | /* | ||
2 | * indycam.c - Silicon Graphics IndyCam digital camera driver | ||
3 | * | ||
4 | * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> | ||
5 | * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/delay.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/fs.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/major.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/slab.h> | ||
21 | |||
22 | /* IndyCam decodes stream of photons into digital image representation ;-) */ | ||
23 | #include <linux/videodev2.h> | ||
24 | #include <linux/i2c.h> | ||
25 | #include <media/v4l2-device.h> | ||
26 | #include <media/v4l2-chip-ident.h> | ||
27 | |||
28 | #include "indycam.h" | ||
29 | |||
30 | #define INDYCAM_MODULE_VERSION "0.0.5" | ||
31 | |||
32 | MODULE_DESCRIPTION("SGI IndyCam driver"); | ||
33 | MODULE_VERSION(INDYCAM_MODULE_VERSION); | ||
34 | MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | |||
37 | |||
38 | // #define INDYCAM_DEBUG | ||
39 | |||
40 | #ifdef INDYCAM_DEBUG | ||
41 | #define dprintk(x...) printk("IndyCam: " x); | ||
42 | #define indycam_regdump(client) indycam_regdump_debug(client) | ||
43 | #else | ||
44 | #define dprintk(x...) | ||
45 | #define indycam_regdump(client) | ||
46 | #endif | ||
47 | |||
48 | struct indycam { | ||
49 | struct v4l2_subdev sd; | ||
50 | u8 version; | ||
51 | }; | ||
52 | |||
53 | static inline struct indycam *to_indycam(struct v4l2_subdev *sd) | ||
54 | { | ||
55 | return container_of(sd, struct indycam, sd); | ||
56 | } | ||
57 | |||
58 | static const u8 initseq[] = { | ||
59 | INDYCAM_CONTROL_AGCENA, /* INDYCAM_CONTROL */ | ||
60 | INDYCAM_SHUTTER_60, /* INDYCAM_SHUTTER */ | ||
61 | INDYCAM_GAIN_DEFAULT, /* INDYCAM_GAIN */ | ||
62 | 0x00, /* INDYCAM_BRIGHTNESS (read-only) */ | ||
63 | INDYCAM_RED_BALANCE_DEFAULT, /* INDYCAM_RED_BALANCE */ | ||
64 | INDYCAM_BLUE_BALANCE_DEFAULT, /* INDYCAM_BLUE_BALANCE */ | ||
65 | INDYCAM_RED_SATURATION_DEFAULT, /* INDYCAM_RED_SATURATION */ | ||
66 | INDYCAM_BLUE_SATURATION_DEFAULT,/* INDYCAM_BLUE_SATURATION */ | ||
67 | }; | ||
68 | |||
69 | /* IndyCam register handling */ | ||
70 | |||
71 | static int indycam_read_reg(struct v4l2_subdev *sd, u8 reg, u8 *value) | ||
72 | { | ||
73 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
74 | int ret; | ||
75 | |||
76 | if (reg == INDYCAM_REG_RESET) { | ||
77 | dprintk("indycam_read_reg(): " | ||
78 | "skipping write-only register %d\n", reg); | ||
79 | *value = 0; | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | ret = i2c_smbus_read_byte_data(client, reg); | ||
84 | |||
85 | if (ret < 0) { | ||
86 | printk(KERN_ERR "IndyCam: indycam_read_reg(): read failed, " | ||
87 | "register = 0x%02x\n", reg); | ||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | *value = (u8)ret; | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static int indycam_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) | ||
97 | { | ||
98 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
99 | int err; | ||
100 | |||
101 | if (reg == INDYCAM_REG_BRIGHTNESS || reg == INDYCAM_REG_VERSION) { | ||
102 | dprintk("indycam_write_reg(): " | ||
103 | "skipping read-only register %d\n", reg); | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | dprintk("Writing Reg %d = 0x%02x\n", reg, value); | ||
108 | err = i2c_smbus_write_byte_data(client, reg, value); | ||
109 | |||
110 | if (err) { | ||
111 | printk(KERN_ERR "IndyCam: indycam_write_reg(): write failed, " | ||
112 | "register = 0x%02x, value = 0x%02x\n", reg, value); | ||
113 | } | ||
114 | return err; | ||
115 | } | ||
116 | |||
117 | static int indycam_write_block(struct v4l2_subdev *sd, u8 reg, | ||
118 | u8 length, u8 *data) | ||
119 | { | ||
120 | int i, err; | ||
121 | |||
122 | for (i = 0; i < length; i++) { | ||
123 | err = indycam_write_reg(sd, reg + i, data[i]); | ||
124 | if (err) | ||
125 | return err; | ||
126 | } | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | /* Helper functions */ | ||
132 | |||
133 | #ifdef INDYCAM_DEBUG | ||
134 | static void indycam_regdump_debug(struct v4l2_subdev *sd) | ||
135 | { | ||
136 | int i; | ||
137 | u8 val; | ||
138 | |||
139 | for (i = 0; i < 9; i++) { | ||
140 | indycam_read_reg(sd, i, &val); | ||
141 | dprintk("Reg %d = 0x%02x\n", i, val); | ||
142 | } | ||
143 | } | ||
144 | #endif | ||
145 | |||
146 | static int indycam_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
147 | { | ||
148 | struct indycam *camera = to_indycam(sd); | ||
149 | u8 reg; | ||
150 | int ret = 0; | ||
151 | |||
152 | switch (ctrl->id) { | ||
153 | case V4L2_CID_AUTOGAIN: | ||
154 | case V4L2_CID_AUTO_WHITE_BALANCE: | ||
155 | ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®); | ||
156 | if (ret) | ||
157 | return -EIO; | ||
158 | if (ctrl->id == V4L2_CID_AUTOGAIN) | ||
159 | ctrl->value = (reg & INDYCAM_CONTROL_AGCENA) | ||
160 | ? 1 : 0; | ||
161 | else | ||
162 | ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL) | ||
163 | ? 1 : 0; | ||
164 | break; | ||
165 | case V4L2_CID_EXPOSURE: | ||
166 | ret = indycam_read_reg(sd, INDYCAM_REG_SHUTTER, ®); | ||
167 | if (ret) | ||
168 | return -EIO; | ||
169 | ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1); | ||
170 | break; | ||
171 | case V4L2_CID_GAIN: | ||
172 | ret = indycam_read_reg(sd, INDYCAM_REG_GAIN, ®); | ||
173 | if (ret) | ||
174 | return -EIO; | ||
175 | ctrl->value = (s32)reg; | ||
176 | break; | ||
177 | case V4L2_CID_RED_BALANCE: | ||
178 | ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, ®); | ||
179 | if (ret) | ||
180 | return -EIO; | ||
181 | ctrl->value = (s32)reg; | ||
182 | break; | ||
183 | case V4L2_CID_BLUE_BALANCE: | ||
184 | ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, ®); | ||
185 | if (ret) | ||
186 | return -EIO; | ||
187 | ctrl->value = (s32)reg; | ||
188 | break; | ||
189 | case INDYCAM_CONTROL_RED_SATURATION: | ||
190 | ret = indycam_read_reg(sd, | ||
191 | INDYCAM_REG_RED_SATURATION, ®); | ||
192 | if (ret) | ||
193 | return -EIO; | ||
194 | ctrl->value = (s32)reg; | ||
195 | break; | ||
196 | case INDYCAM_CONTROL_BLUE_SATURATION: | ||
197 | ret = indycam_read_reg(sd, | ||
198 | INDYCAM_REG_BLUE_SATURATION, ®); | ||
199 | if (ret) | ||
200 | return -EIO; | ||
201 | ctrl->value = (s32)reg; | ||
202 | break; | ||
203 | case V4L2_CID_GAMMA: | ||
204 | if (camera->version == CAMERA_VERSION_MOOSE) { | ||
205 | ret = indycam_read_reg(sd, | ||
206 | INDYCAM_REG_GAMMA, ®); | ||
207 | if (ret) | ||
208 | return -EIO; | ||
209 | ctrl->value = (s32)reg; | ||
210 | } else { | ||
211 | ctrl->value = INDYCAM_GAMMA_DEFAULT; | ||
212 | } | ||
213 | break; | ||
214 | default: | ||
215 | ret = -EINVAL; | ||
216 | } | ||
217 | |||
218 | return ret; | ||
219 | } | ||
220 | |||
221 | static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
222 | { | ||
223 | struct indycam *camera = to_indycam(sd); | ||
224 | u8 reg; | ||
225 | int ret = 0; | ||
226 | |||
227 | switch (ctrl->id) { | ||
228 | case V4L2_CID_AUTOGAIN: | ||
229 | case V4L2_CID_AUTO_WHITE_BALANCE: | ||
230 | ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®); | ||
231 | if (ret) | ||
232 | break; | ||
233 | |||
234 | if (ctrl->id == V4L2_CID_AUTOGAIN) { | ||
235 | if (ctrl->value) | ||
236 | reg |= INDYCAM_CONTROL_AGCENA; | ||
237 | else | ||
238 | reg &= ~INDYCAM_CONTROL_AGCENA; | ||
239 | } else { | ||
240 | if (ctrl->value) | ||
241 | reg |= INDYCAM_CONTROL_AWBCTL; | ||
242 | else | ||
243 | reg &= ~INDYCAM_CONTROL_AWBCTL; | ||
244 | } | ||
245 | |||
246 | ret = indycam_write_reg(sd, INDYCAM_REG_CONTROL, reg); | ||
247 | break; | ||
248 | case V4L2_CID_EXPOSURE: | ||
249 | reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1); | ||
250 | ret = indycam_write_reg(sd, INDYCAM_REG_SHUTTER, reg); | ||
251 | break; | ||
252 | case V4L2_CID_GAIN: | ||
253 | ret = indycam_write_reg(sd, INDYCAM_REG_GAIN, ctrl->value); | ||
254 | break; | ||
255 | case V4L2_CID_RED_BALANCE: | ||
256 | ret = indycam_write_reg(sd, INDYCAM_REG_RED_BALANCE, | ||
257 | ctrl->value); | ||
258 | break; | ||
259 | case V4L2_CID_BLUE_BALANCE: | ||
260 | ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_BALANCE, | ||
261 | ctrl->value); | ||
262 | break; | ||
263 | case INDYCAM_CONTROL_RED_SATURATION: | ||
264 | ret = indycam_write_reg(sd, INDYCAM_REG_RED_SATURATION, | ||
265 | ctrl->value); | ||
266 | break; | ||
267 | case INDYCAM_CONTROL_BLUE_SATURATION: | ||
268 | ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_SATURATION, | ||
269 | ctrl->value); | ||
270 | break; | ||
271 | case V4L2_CID_GAMMA: | ||
272 | if (camera->version == CAMERA_VERSION_MOOSE) { | ||
273 | ret = indycam_write_reg(sd, INDYCAM_REG_GAMMA, | ||
274 | ctrl->value); | ||
275 | } | ||
276 | break; | ||
277 | default: | ||
278 | ret = -EINVAL; | ||
279 | } | ||
280 | |||
281 | return ret; | ||
282 | } | ||
283 | |||
284 | /* I2C-interface */ | ||
285 | |||
286 | static int indycam_g_chip_ident(struct v4l2_subdev *sd, | ||
287 | struct v4l2_dbg_chip_ident *chip) | ||
288 | { | ||
289 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
290 | struct indycam *camera = to_indycam(sd); | ||
291 | |||
292 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_INDYCAM, | ||
293 | camera->version); | ||
294 | } | ||
295 | |||
296 | /* ----------------------------------------------------------------------- */ | ||
297 | |||
298 | static const struct v4l2_subdev_core_ops indycam_core_ops = { | ||
299 | .g_chip_ident = indycam_g_chip_ident, | ||
300 | .g_ctrl = indycam_g_ctrl, | ||
301 | .s_ctrl = indycam_s_ctrl, | ||
302 | }; | ||
303 | |||
304 | static const struct v4l2_subdev_ops indycam_ops = { | ||
305 | .core = &indycam_core_ops, | ||
306 | }; | ||
307 | |||
308 | static int indycam_probe(struct i2c_client *client, | ||
309 | const struct i2c_device_id *id) | ||
310 | { | ||
311 | int err = 0; | ||
312 | struct indycam *camera; | ||
313 | struct v4l2_subdev *sd; | ||
314 | |||
315 | v4l_info(client, "chip found @ 0x%x (%s)\n", | ||
316 | client->addr << 1, client->adapter->name); | ||
317 | |||
318 | camera = kzalloc(sizeof(struct indycam), GFP_KERNEL); | ||
319 | if (!camera) | ||
320 | return -ENOMEM; | ||
321 | |||
322 | sd = &camera->sd; | ||
323 | v4l2_i2c_subdev_init(sd, client, &indycam_ops); | ||
324 | |||
325 | camera->version = i2c_smbus_read_byte_data(client, | ||
326 | INDYCAM_REG_VERSION); | ||
327 | if (camera->version != CAMERA_VERSION_INDY && | ||
328 | camera->version != CAMERA_VERSION_MOOSE) { | ||
329 | kfree(camera); | ||
330 | return -ENODEV; | ||
331 | } | ||
332 | |||
333 | printk(KERN_INFO "IndyCam v%d.%d detected\n", | ||
334 | INDYCAM_VERSION_MAJOR(camera->version), | ||
335 | INDYCAM_VERSION_MINOR(camera->version)); | ||
336 | |||
337 | indycam_regdump(sd); | ||
338 | |||
339 | // initialize | ||
340 | err = indycam_write_block(sd, 0, sizeof(initseq), (u8 *)&initseq); | ||
341 | if (err) { | ||
342 | printk(KERN_ERR "IndyCam initialization failed\n"); | ||
343 | kfree(camera); | ||
344 | return -EIO; | ||
345 | } | ||
346 | |||
347 | indycam_regdump(sd); | ||
348 | |||
349 | // white balance | ||
350 | err = indycam_write_reg(sd, INDYCAM_REG_CONTROL, | ||
351 | INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL); | ||
352 | if (err) { | ||
353 | printk(KERN_ERR "IndyCam: White balancing camera failed\n"); | ||
354 | kfree(camera); | ||
355 | return -EIO; | ||
356 | } | ||
357 | |||
358 | indycam_regdump(sd); | ||
359 | |||
360 | printk(KERN_INFO "IndyCam initialized\n"); | ||
361 | |||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static int indycam_remove(struct i2c_client *client) | ||
366 | { | ||
367 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
368 | |||
369 | v4l2_device_unregister_subdev(sd); | ||
370 | kfree(to_indycam(sd)); | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static const struct i2c_device_id indycam_id[] = { | ||
375 | { "indycam", 0 }, | ||
376 | { } | ||
377 | }; | ||
378 | MODULE_DEVICE_TABLE(i2c, indycam_id); | ||
379 | |||
380 | static struct i2c_driver indycam_driver = { | ||
381 | .driver = { | ||
382 | .owner = THIS_MODULE, | ||
383 | .name = "indycam", | ||
384 | }, | ||
385 | .probe = indycam_probe, | ||
386 | .remove = indycam_remove, | ||
387 | .id_table = indycam_id, | ||
388 | }; | ||
389 | |||
390 | static __init int init_indycam(void) | ||
391 | { | ||
392 | return i2c_add_driver(&indycam_driver); | ||
393 | } | ||
394 | |||
395 | static __exit void exit_indycam(void) | ||
396 | { | ||
397 | i2c_del_driver(&indycam_driver); | ||
398 | } | ||
399 | |||
400 | module_init(init_indycam); | ||
401 | module_exit(exit_indycam); | ||