Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
indycam.c
Go to the documentation of this file.
1 /*
2  * indycam.c - Silicon Graphics IndyCam digital camera driver
3  *
4  * Copyright (C) 2003 Ladislav Michl <[email protected]>
5  * Copyright (C) 2004,2005 Mikael Nousiainen <[email protected]>
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");
34 MODULE_AUTHOR("Mikael Nousiainen <[email protected]>");
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;
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:
155  ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, &reg);
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, &reg);
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, &reg);
173  if (ret)
174  return -EIO;
175  ctrl->value = (s32)reg;
176  break;
178  ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, &reg);
179  if (ret)
180  return -EIO;
181  ctrl->value = (s32)reg;
182  break;
184  ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, &reg);
185  if (ret)
186  return -EIO;
187  ctrl->value = (s32)reg;
188  break;
190  ret = indycam_read_reg(sd,
192  if (ret)
193  return -EIO;
194  ctrl->value = (s32)reg;
195  break;
197  ret = indycam_read_reg(sd,
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, &reg);
207  if (ret)
208  return -EIO;
209  ctrl->value = (s32)reg;
210  } else {
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:
230  ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, &reg);
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;
256  ret = indycam_write_reg(sd, INDYCAM_REG_RED_BALANCE,
257  ctrl->value);
258  break;
260  ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_BALANCE,
261  ctrl->value);
262  break;
264  ret = indycam_write_reg(sd, INDYCAM_REG_RED_SATURATION,
265  ctrl->value);
266  break;
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,
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",
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,
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 
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 module_i2c_driver(indycam_driver);