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 31MODULE_DESCRIPTION("SGI IndyCam driver"); 32MODULE_VERSION(INDYCAM_MODULE_VERSION); 33MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); 34MODULE_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 46struct indycam { 47 struct i2c_client *client; 48 u8 version; 49}; 50 51static struct i2c_driver i2c_driver_indycam; 52 53static 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 66static 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 90static 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 111static 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 128static 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 140static 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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 216static 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, ®); 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 282static 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 initalization 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 351out_detach_client: 352 i2c_detach_client(client); 353out_free_camera: 354 kfree(camera); 355out_free_client: 356 kfree(client); 357 return err; 358} 359 360static 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 369static 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 379static 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 449static 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 459static int __init indycam_init(void) 460{ 461 return i2c_add_driver(&i2c_driver_indycam); 462} 463 464static void __exit indycam_exit(void) 465{ 466 i2c_del_driver(&i2c_driver_indycam); 467} 468 469module_init(indycam_init); 470module_exit(indycam_exit); 471