linux/drivers/media/video/indycam.c
<<
>>
Prefs
   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, &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
 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, &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
 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