V4L/DVB (9493): kconfig patch
[linux-2.6] / drivers / media / video / indycam.c
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 #include <linux/videodev.h>
23 /* IndyCam decodes stream of photons into digital image representation ;-) */
24 #include <linux/video_decoder.h>
25 #include <linux/i2c.h>
26
27 #include "indycam.h"
28
29 #define INDYCAM_MODULE_VERSION "0.0.5"
30
31 MODULE_DESCRIPTION("SGI IndyCam driver");
32 MODULE_VERSION(INDYCAM_MODULE_VERSION);
33 MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
34 MODULE_LICENSE("GPL");
35
36 // #define INDYCAM_DEBUG
37
38 #ifdef INDYCAM_DEBUG
39 #define dprintk(x...) printk("IndyCam: " x);
40 #define indycam_regdump(client) indycam_regdump_debug(client)
41 #else
42 #define dprintk(x...)
43 #define indycam_regdump(client)
44 #endif
45
46 struct indycam {
47         struct i2c_client *client;
48         u8 version;
49 };
50
51 static struct i2c_driver i2c_driver_indycam;
52
53 static const u8 initseq[] = {
54         INDYCAM_CONTROL_AGCENA,         /* INDYCAM_CONTROL */
55         INDYCAM_SHUTTER_60,             /* INDYCAM_SHUTTER */
56         INDYCAM_GAIN_DEFAULT,           /* INDYCAM_GAIN */
57         0x00,                           /* INDYCAM_BRIGHTNESS (read-only) */
58         INDYCAM_RED_BALANCE_DEFAULT,    /* INDYCAM_RED_BALANCE */
59         INDYCAM_BLUE_BALANCE_DEFAULT,   /* INDYCAM_BLUE_BALANCE */
60         INDYCAM_RED_SATURATION_DEFAULT, /* INDYCAM_RED_SATURATION */
61         INDYCAM_BLUE_SATURATION_DEFAULT,/* INDYCAM_BLUE_SATURATION */
62 };
63
64 /* IndyCam register handling */
65
66 static int indycam_read_reg(struct i2c_client *client, u8 reg, u8 *value)
67 {
68         int ret;
69
70         if (reg == INDYCAM_REG_RESET) {
71                 dprintk("indycam_read_reg(): "
72                         "skipping write-only register %d\n", reg);
73                 *value = 0;
74                 return 0;
75         }
76
77         ret = i2c_smbus_read_byte_data(client, reg);
78
79         if (ret < 0) {
80                 printk(KERN_ERR "IndyCam: indycam_read_reg(): read failed, "
81                        "register = 0x%02x\n", reg);
82                 return ret;
83         }
84
85         *value = (u8)ret;
86
87         return 0;
88 }
89
90 static int indycam_write_reg(struct i2c_client *client, u8 reg, u8 value)
91 {
92         int err;
93
94         if ((reg == INDYCAM_REG_BRIGHTNESS)
95             || (reg == INDYCAM_REG_VERSION)) {
96                 dprintk("indycam_write_reg(): "
97                         "skipping read-only register %d\n", reg);
98                 return 0;
99         }
100
101         dprintk("Writing Reg %d = 0x%02x\n", reg, value);
102         err = i2c_smbus_write_byte_data(client, reg, value);
103
104         if (err) {
105                 printk(KERN_ERR "IndyCam: indycam_write_reg(): write failed, "
106                        "register = 0x%02x, value = 0x%02x\n", reg, value);
107         }
108         return err;
109 }
110
111 static int indycam_write_block(struct i2c_client *client, u8 reg,
112                                u8 length, u8 *data)
113 {
114         int i, err;
115
116         for (i = 0; i < length; i++) {
117                 err = indycam_write_reg(client, reg + i, data[i]);
118                 if (err)
119                         return err;
120         }
121
122         return 0;
123 }
124
125 /* Helper functions */
126
127 #ifdef INDYCAM_DEBUG
128 static void indycam_regdump_debug(struct i2c_client *client)
129 {
130         int i;
131         u8 val;
132
133         for (i = 0; i < 9; i++) {
134                 indycam_read_reg(client, i, &val);
135                 dprintk("Reg %d = 0x%02x\n", i, val);
136         }
137 }
138 #endif
139
140 static int indycam_get_control(struct i2c_client *client,
141                                struct indycam_control *ctrl)
142 {
143         struct indycam *camera = i2c_get_clientdata(client);
144         u8 reg;
145         int ret = 0;
146
147         switch (ctrl->type) {
148         case INDYCAM_CONTROL_AGC:
149         case INDYCAM_CONTROL_AWB:
150                 ret = indycam_read_reg(client, INDYCAM_REG_CONTROL, &reg);
151                 if (ret)
152                         return -EIO;
153                 if (ctrl->type == INDYCAM_CONTROL_AGC)
154                         ctrl->value = (reg & INDYCAM_CONTROL_AGCENA)
155                                 ? 1 : 0;
156                 else
157                         ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL)
158                                 ? 1 : 0;
159                 break;
160         case INDYCAM_CONTROL_SHUTTER:
161                 ret = indycam_read_reg(client, INDYCAM_REG_SHUTTER, &reg);
162                 if (ret)
163                         return -EIO;
164                 ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1);
165                 break;
166         case INDYCAM_CONTROL_GAIN:
167                 ret = indycam_read_reg(client, INDYCAM_REG_GAIN, &reg);
168                 if (ret)
169                         return -EIO;
170                 ctrl->value = (s32)reg;
171                 break;
172         case INDYCAM_CONTROL_RED_BALANCE:
173                 ret = indycam_read_reg(client, INDYCAM_REG_RED_BALANCE, &reg);
174                 if (ret)
175                         return -EIO;
176                 ctrl->value = (s32)reg;
177                 break;
178         case INDYCAM_CONTROL_BLUE_BALANCE:
179                 ret = indycam_read_reg(client, INDYCAM_REG_BLUE_BALANCE, &reg);
180                 if (ret)
181                         return -EIO;
182                 ctrl->value = (s32)reg;
183                 break;
184         case INDYCAM_CONTROL_RED_SATURATION:
185                 ret = indycam_read_reg(client,
186                                        INDYCAM_REG_RED_SATURATION, &reg);
187                 if (ret)
188                         return -EIO;
189                 ctrl->value = (s32)reg;
190                 break;
191         case INDYCAM_CONTROL_BLUE_SATURATION:
192                 ret = indycam_read_reg(client,
193                                        INDYCAM_REG_BLUE_SATURATION, &reg);
194                 if (ret)
195                         return -EIO;
196                 ctrl->value = (s32)reg;
197                 break;
198         case INDYCAM_CONTROL_GAMMA:
199                 if (camera->version == CAMERA_VERSION_MOOSE) {
200                         ret = indycam_read_reg(client,
201                                                INDYCAM_REG_GAMMA, &reg);
202                         if (ret)
203                                 return -EIO;
204                         ctrl->value = (s32)reg;
205                 } else {
206                         ctrl->value = INDYCAM_GAMMA_DEFAULT;
207                 }
208                 break;
209         default:
210                 ret = -EINVAL;
211         }
212
213         return ret;
214 }
215
216 static int indycam_set_control(struct i2c_client *client,
217                                struct indycam_control *ctrl)
218 {
219         struct indycam *camera = i2c_get_clientdata(client);
220         u8 reg;
221         int ret = 0;
222
223         switch (ctrl->type) {
224         case INDYCAM_CONTROL_AGC:
225         case INDYCAM_CONTROL_AWB:
226                 ret = indycam_read_reg(client, INDYCAM_REG_CONTROL, &reg);
227                 if (ret)
228                         break;
229
230                 if (ctrl->type == INDYCAM_CONTROL_AGC) {
231                         if (ctrl->value)
232                                 reg |= INDYCAM_CONTROL_AGCENA;
233                         else
234                                 reg &= ~INDYCAM_CONTROL_AGCENA;
235                 } else {
236                         if (ctrl->value)
237                                 reg |= INDYCAM_CONTROL_AWBCTL;
238                         else
239                                 reg &= ~INDYCAM_CONTROL_AWBCTL;
240                 }
241
242                 ret = indycam_write_reg(client, INDYCAM_REG_CONTROL, reg);
243                 break;
244         case INDYCAM_CONTROL_SHUTTER:
245                 reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1);
246                 ret = indycam_write_reg(client, INDYCAM_REG_SHUTTER, reg);
247                 break;
248         case INDYCAM_CONTROL_GAIN:
249                 ret = indycam_write_reg(client, INDYCAM_REG_GAIN, ctrl->value);
250                 break;
251         case INDYCAM_CONTROL_RED_BALANCE:
252                 ret = indycam_write_reg(client, INDYCAM_REG_RED_BALANCE,
253                                         ctrl->value);
254                 break;
255         case INDYCAM_CONTROL_BLUE_BALANCE:
256                 ret = indycam_write_reg(client, INDYCAM_REG_BLUE_BALANCE,
257                                         ctrl->value);
258                 break;
259         case INDYCAM_CONTROL_RED_SATURATION:
260                 ret = indycam_write_reg(client, INDYCAM_REG_RED_SATURATION,
261                                         ctrl->value);
262                 break;
263         case INDYCAM_CONTROL_BLUE_SATURATION:
264                 ret = indycam_write_reg(client, INDYCAM_REG_BLUE_SATURATION,
265                                         ctrl->value);
266                 break;
267         case INDYCAM_CONTROL_GAMMA:
268                 if (camera->version == CAMERA_VERSION_MOOSE) {
269                         ret = indycam_write_reg(client, INDYCAM_REG_GAMMA,
270                                                 ctrl->value);
271                 }
272                 break;
273         default:
274                 ret = -EINVAL;
275         }
276
277         return ret;
278 }
279
280 /* I2C-interface */
281
282 static int indycam_attach(struct i2c_adapter *adap, int addr, int kind)
283 {
284         int err = 0;
285         struct indycam *camera;
286         struct i2c_client *client;
287
288         printk(KERN_INFO "SGI IndyCam driver version %s\n",
289                INDYCAM_MODULE_VERSION);
290
291         client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
292         if (!client)
293                 return -ENOMEM;
294         camera = kzalloc(sizeof(struct indycam), GFP_KERNEL);
295         if (!camera) {
296                 err = -ENOMEM;
297                 goto out_free_client;
298         }
299
300         client->addr = addr;
301         client->adapter = adap;
302         client->driver = &i2c_driver_indycam;
303         client->flags = 0;
304         strcpy(client->name, "IndyCam client");
305         i2c_set_clientdata(client, camera);
306
307         camera->client = client;
308
309         err = i2c_attach_client(client);
310         if (err)
311                 goto out_free_camera;
312
313         camera->version = i2c_smbus_read_byte_data(client,
314                                                    INDYCAM_REG_VERSION);
315         if (camera->version != CAMERA_VERSION_INDY &&
316             camera->version != CAMERA_VERSION_MOOSE) {
317                 err = -ENODEV;
318                 goto out_detach_client;
319         }
320         printk(KERN_INFO "IndyCam v%d.%d detected\n",
321                INDYCAM_VERSION_MAJOR(camera->version),
322                INDYCAM_VERSION_MINOR(camera->version));
323
324         indycam_regdump(client);
325
326         // initialize
327         err = indycam_write_block(client, 0, sizeof(initseq), (u8 *)&initseq);
328         if (err) {
329                 printk(KERN_ERR "IndyCam initialization failed\n");
330                 err = -EIO;
331                 goto out_detach_client;
332         }
333
334         indycam_regdump(client);
335
336         // white balance
337         err = indycam_write_reg(client, INDYCAM_REG_CONTROL,
338                           INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL);
339         if (err) {
340                 printk(KERN_ERR "IndyCam: White balancing camera failed\n");
341                 err = -EIO;
342                 goto out_detach_client;
343         }
344
345         indycam_regdump(client);
346
347         printk(KERN_INFO "IndyCam initialized\n");
348
349         return 0;
350
351 out_detach_client:
352         i2c_detach_client(client);
353 out_free_camera:
354         kfree(camera);
355 out_free_client:
356         kfree(client);
357         return err;
358 }
359
360 static int indycam_probe(struct i2c_adapter *adap)
361 {
362         /* Indy specific crap */
363         if (adap->id == I2C_HW_SGI_VINO)
364                 return indycam_attach(adap, INDYCAM_ADDR, 0);
365         /* Feel free to add probe here :-) */
366         return -ENODEV;
367 }
368
369 static int indycam_detach(struct i2c_client *client)
370 {
371         struct indycam *camera = i2c_get_clientdata(client);
372
373         i2c_detach_client(client);
374         kfree(camera);
375         kfree(client);
376         return 0;
377 }
378
379 static int indycam_command(struct i2c_client *client, unsigned int cmd,
380                            void *arg)
381 {
382         // struct indycam *camera = i2c_get_clientdata(client);
383
384         /* The old video_decoder interface just isn't enough,
385          * so we'll use some custom commands. */
386         switch (cmd) {
387         case DECODER_GET_CAPABILITIES: {
388                 struct video_decoder_capability *cap = arg;
389
390                 cap->flags  = VIDEO_DECODER_NTSC;
391                 cap->inputs = 1;
392                 cap->outputs = 1;
393                 break;
394         }
395         case DECODER_GET_STATUS: {
396                 int *iarg = arg;
397
398                 *iarg = DECODER_STATUS_GOOD | DECODER_STATUS_NTSC |
399                         DECODER_STATUS_COLOR;
400                 break;
401         }
402         case DECODER_SET_NORM: {
403                 int *iarg = arg;
404
405                 switch (*iarg) {
406                 case VIDEO_MODE_NTSC:
407                         break;
408                 default:
409                         return -EINVAL;
410                 }
411                 break;
412         }
413         case DECODER_SET_INPUT: {
414                 int *iarg = arg;
415
416                 if (*iarg != 0)
417                         return -EINVAL;
418                 break;
419         }
420         case DECODER_SET_OUTPUT: {
421                 int *iarg = arg;
422
423                 if (*iarg != 0)
424                         return -EINVAL;
425                 break;
426         }
427         case DECODER_ENABLE_OUTPUT: {
428                 /* Always enabled */
429                 break;
430         }
431         case DECODER_SET_PICTURE: {
432                 // struct video_picture *pic = arg;
433                 /* TODO: convert values for indycam_set_controls() */
434                 break;
435         }
436         case DECODER_INDYCAM_GET_CONTROL: {
437                 return indycam_get_control(client, arg);
438         }
439         case DECODER_INDYCAM_SET_CONTROL: {
440                 return indycam_set_control(client, arg);
441         }
442         default:
443                 return -EINVAL;
444         }
445
446         return 0;
447 }
448
449 static struct i2c_driver i2c_driver_indycam = {
450         .driver = {
451                 .name   = "indycam",
452         },
453         .id             = I2C_DRIVERID_INDYCAM,
454         .attach_adapter = indycam_probe,
455         .detach_client  = indycam_detach,
456         .command        = indycam_command,
457 };
458
459 static int __init indycam_init(void)
460 {
461         return i2c_add_driver(&i2c_driver_indycam);
462 }
463
464 static void __exit indycam_exit(void)
465 {
466         i2c_del_driver(&i2c_driver_indycam);
467 }
468
469 module_init(indycam_init);
470 module_exit(indycam_exit);