linux/drivers/media/video/ovcamchip/ov7x20.c
<<
>>
Prefs
   1/* OmniVision OV7620/OV7120 Camera Chip Support Code
   2 *
   3 * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org>
   4 * http://alpha.dyndns.org/ov511/
   5 *
   6 * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms of the GNU General Public License as published by the
  10 * Free Software Foundation; either version 2 of the License, or (at your
  11 * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied.
  12 */
  13
  14#define DEBUG
  15
  16#include <linux/slab.h>
  17#include "ovcamchip_priv.h"
  18
  19/* Registers */
  20#define REG_GAIN                0x00    /* gain [5:0] */
  21#define REG_BLUE                0x01    /* blue gain */
  22#define REG_RED                 0x02    /* red gain */
  23#define REG_SAT                 0x03    /* saturation */
  24#define REG_BRT                 0x06    /* Y brightness */
  25#define REG_SHARP               0x07    /* analog sharpness */
  26#define REG_BLUE_BIAS           0x0C    /* WB blue ratio [5:0] */
  27#define REG_RED_BIAS            0x0D    /* WB red ratio [5:0] */
  28#define REG_EXP                 0x10    /* exposure */
  29
  30/* Default control settings. Values are in terms of V4L2 controls. */
  31#define OV7120_DFL_BRIGHT     0x60
  32#define OV7620_DFL_BRIGHT     0x60
  33#define OV7120_DFL_SAT        0xb0
  34#define OV7620_DFL_SAT        0xc0
  35#define DFL_AUTO_EXP             1
  36#define DFL_AUTO_GAIN            1
  37#define OV7120_DFL_GAIN       0x00
  38#define OV7620_DFL_GAIN       0x00
  39/* NOTE: Since autoexposure is the default, these aren't programmed into the
  40 * OV7x20 chip. They are just here because V4L2 expects a default */
  41#define OV7120_DFL_EXP        0x7f
  42#define OV7620_DFL_EXP        0x7f
  43
  44/* Window parameters */
  45#define HWSBASE 0x2F    /* From 7620.SET (spec is wrong) */
  46#define HWEBASE 0x2F
  47#define VWSBASE 0x05
  48#define VWEBASE 0x05
  49
  50struct ov7x20 {
  51        int auto_brt;
  52        int auto_exp;
  53        int auto_gain;
  54        int backlight;
  55        int bandfilt;
  56        int mirror;
  57};
  58
  59/* Contrast look-up table */
  60static unsigned char ctab[] = {
  61        0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
  62        0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
  63};
  64
  65/* Settings for (Black & White) OV7120 camera chip */
  66static struct ovcamchip_regvals regvals_init_7120[] = {
  67        { 0x12, 0x80 }, /* reset */
  68        { 0x13, 0x00 }, /* Autoadjust off */
  69        { 0x12, 0x20 }, /* Disable AWB */
  70        { 0x13, DFL_AUTO_GAIN?0x01:0x00 }, /* Autoadjust on (if desired) */
  71        { 0x00, OV7120_DFL_GAIN },
  72        { 0x01, 0x80 },
  73        { 0x02, 0x80 },
  74        { 0x03, OV7120_DFL_SAT },
  75        { 0x06, OV7120_DFL_BRIGHT },
  76        { 0x07, 0x00 },
  77        { 0x0c, 0x20 },
  78        { 0x0d, 0x20 },
  79        { 0x11, 0x01 },
  80        { 0x14, 0x84 },
  81        { 0x15, 0x01 },
  82        { 0x16, 0x03 },
  83        { 0x17, 0x2f },
  84        { 0x18, 0xcf },
  85        { 0x19, 0x06 },
  86        { 0x1a, 0xf5 },
  87        { 0x1b, 0x00 },
  88        { 0x20, 0x08 },
  89        { 0x21, 0x80 },
  90        { 0x22, 0x80 },
  91        { 0x23, 0x00 },
  92        { 0x26, 0xa0 },
  93        { 0x27, 0xfa },
  94        { 0x28, 0x20 }, /* DON'T set bit 6. It is for the OV7620 only */
  95        { 0x29, DFL_AUTO_EXP?0x00:0x80 },
  96        { 0x2a, 0x10 },
  97        { 0x2b, 0x00 },
  98        { 0x2c, 0x88 },
  99        { 0x2d, 0x95 },
 100        { 0x2e, 0x80 },
 101        { 0x2f, 0x44 },
 102        { 0x60, 0x20 },
 103        { 0x61, 0x02 },
 104        { 0x62, 0x5f },
 105        { 0x63, 0xd5 },
 106        { 0x64, 0x57 },
 107        { 0x65, 0x83 }, /* OV says "don't change this value" */
 108        { 0x66, 0x55 },
 109        { 0x67, 0x92 },
 110        { 0x68, 0xcf },
 111        { 0x69, 0x76 },
 112        { 0x6a, 0x22 },
 113        { 0x6b, 0xe2 },
 114        { 0x6c, 0x40 },
 115        { 0x6d, 0x48 },
 116        { 0x6e, 0x80 },
 117        { 0x6f, 0x0d },
 118        { 0x70, 0x89 },
 119        { 0x71, 0x00 },
 120        { 0x72, 0x14 },
 121        { 0x73, 0x54 },
 122        { 0x74, 0xa0 },
 123        { 0x75, 0x8e },
 124        { 0x76, 0x00 },
 125        { 0x77, 0xff },
 126        { 0x78, 0x80 },
 127        { 0x79, 0x80 },
 128        { 0x7a, 0x80 },
 129        { 0x7b, 0xe6 },
 130        { 0x7c, 0x00 },
 131        { 0x24, 0x3a },
 132        { 0x25, 0x60 },
 133        { 0xff, 0xff }, /* END MARKER */
 134};
 135
 136/* Settings for (color) OV7620 camera chip */
 137static struct ovcamchip_regvals regvals_init_7620[] = {
 138        { 0x12, 0x80 }, /* reset */
 139        { 0x00, OV7620_DFL_GAIN },
 140        { 0x01, 0x80 },
 141        { 0x02, 0x80 },
 142        { 0x03, OV7620_DFL_SAT },
 143        { 0x06, OV7620_DFL_BRIGHT },
 144        { 0x07, 0x00 },
 145        { 0x0c, 0x24 },
 146        { 0x0c, 0x24 },
 147        { 0x0d, 0x24 },
 148        { 0x11, 0x01 },
 149        { 0x12, 0x24 },
 150        { 0x13, DFL_AUTO_GAIN?0x01:0x00 },
 151        { 0x14, 0x84 },
 152        { 0x15, 0x01 },
 153        { 0x16, 0x03 },
 154        { 0x17, 0x2f },
 155        { 0x18, 0xcf },
 156        { 0x19, 0x06 },
 157        { 0x1a, 0xf5 },
 158        { 0x1b, 0x00 },
 159        { 0x20, 0x18 },
 160        { 0x21, 0x80 },
 161        { 0x22, 0x80 },
 162        { 0x23, 0x00 },
 163        { 0x26, 0xa2 },
 164        { 0x27, 0xea },
 165        { 0x28, 0x20 },
 166        { 0x29, DFL_AUTO_EXP?0x00:0x80 },
 167        { 0x2a, 0x10 },
 168        { 0x2b, 0x00 },
 169        { 0x2c, 0x88 },
 170        { 0x2d, 0x91 },
 171        { 0x2e, 0x80 },
 172        { 0x2f, 0x44 },
 173        { 0x60, 0x27 },
 174        { 0x61, 0x02 },
 175        { 0x62, 0x5f },
 176        { 0x63, 0xd5 },
 177        { 0x64, 0x57 },
 178        { 0x65, 0x83 },
 179        { 0x66, 0x55 },
 180        { 0x67, 0x92 },
 181        { 0x68, 0xcf },
 182        { 0x69, 0x76 },
 183        { 0x6a, 0x22 },
 184        { 0x6b, 0x00 },
 185        { 0x6c, 0x02 },
 186        { 0x6d, 0x44 },
 187        { 0x6e, 0x80 },
 188        { 0x6f, 0x1d },
 189        { 0x70, 0x8b },
 190        { 0x71, 0x00 },
 191        { 0x72, 0x14 },
 192        { 0x73, 0x54 },
 193        { 0x74, 0x00 },
 194        { 0x75, 0x8e },
 195        { 0x76, 0x00 },
 196        { 0x77, 0xff },
 197        { 0x78, 0x80 },
 198        { 0x79, 0x80 },
 199        { 0x7a, 0x80 },
 200        { 0x7b, 0xe2 },
 201        { 0x7c, 0x00 },
 202        { 0xff, 0xff }, /* END MARKER */
 203};
 204
 205/* Returns index into the specified look-up table, with 'n' elements, for which
 206 * the value is greater than or equal to "val". If a match isn't found, (n-1)
 207 * is returned. The entries in the table must be in ascending order. */
 208static inline int ov7x20_lut_find(unsigned char lut[], int n, unsigned char val)
 209{
 210        int i = 0;
 211
 212        while (lut[i] < val && i < n)
 213                i++;
 214
 215        return i;
 216}
 217
 218/* This initializes the OV7x20 camera chip and relevant variables. */
 219static int ov7x20_init(struct i2c_client *c)
 220{
 221        struct ovcamchip *ov = i2c_get_clientdata(c);
 222        struct ov7x20 *s;
 223        int rc;
 224
 225        DDEBUG(4, &c->dev, "entered");
 226
 227        if (ov->mono)
 228                rc = ov_write_regvals(c, regvals_init_7120);
 229        else
 230                rc = ov_write_regvals(c, regvals_init_7620);
 231
 232        if (rc < 0)
 233                return rc;
 234
 235        ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL);
 236        if (!s)
 237                return -ENOMEM;
 238
 239        s->auto_brt = 1;
 240        s->auto_exp = DFL_AUTO_EXP;
 241        s->auto_gain = DFL_AUTO_GAIN;
 242
 243        return 0;
 244}
 245
 246static int ov7x20_free(struct i2c_client *c)
 247{
 248        struct ovcamchip *ov = i2c_get_clientdata(c);
 249
 250        kfree(ov->spriv);
 251        return 0;
 252}
 253
 254static int ov7x20_set_v4l1_control(struct i2c_client *c,
 255                                   struct ovcamchip_control *ctl)
 256{
 257        struct ovcamchip *ov = i2c_get_clientdata(c);
 258        struct ov7x20 *s = ov->spriv;
 259        int rc;
 260        int v = ctl->value;
 261
 262        switch (ctl->id) {
 263        case OVCAMCHIP_CID_CONT:
 264        {
 265                /* Use Y gamma control instead. Bit 0 enables it. */
 266                rc = ov_write(c, 0x64, ctab[v >> 12]);
 267                break;
 268        }
 269        case OVCAMCHIP_CID_BRIGHT:
 270                /* 7620 doesn't like manual changes when in auto mode */
 271                if (!s->auto_brt)
 272                        rc = ov_write(c, REG_BRT, v >> 8);
 273                else
 274                        rc = 0;
 275                break;
 276        case OVCAMCHIP_CID_SAT:
 277                rc = ov_write(c, REG_SAT, v >> 8);
 278                break;
 279        case OVCAMCHIP_CID_EXP:
 280                if (!s->auto_exp)
 281                        rc = ov_write(c, REG_EXP, v);
 282                else
 283                        rc = -EBUSY;
 284                break;
 285        case OVCAMCHIP_CID_FREQ:
 286        {
 287                int sixty = (v == 60);
 288
 289                rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80);
 290                if (rc < 0)
 291                        goto out;
 292
 293                rc = ov_write(c, 0x2b, sixty?0x00:0xac);
 294                if (rc < 0)
 295                        goto out;
 296
 297                rc = ov_write_mask(c, 0x76, 0x01, 0x01);
 298                break;
 299        }
 300        case OVCAMCHIP_CID_BANDFILT:
 301                rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04);
 302                s->bandfilt = v;
 303                break;
 304        case OVCAMCHIP_CID_AUTOBRIGHT:
 305                rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10);
 306                s->auto_brt = v;
 307                break;
 308        case OVCAMCHIP_CID_AUTOEXP:
 309                rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01);
 310                s->auto_exp = v;
 311                break;
 312        case OVCAMCHIP_CID_BACKLIGHT:
 313        {
 314                rc = ov_write_mask(c, 0x68, v?0xe0:0xc0, 0xe0);
 315                if (rc < 0)
 316                        goto out;
 317
 318                rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08);
 319                if (rc < 0)
 320                        goto out;
 321
 322                rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02);
 323                s->backlight = v;
 324                break;
 325        }
 326        case OVCAMCHIP_CID_MIRROR:
 327                rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40);
 328                s->mirror = v;
 329                break;
 330        default:
 331                DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
 332                return -EPERM;
 333        }
 334
 335out:
 336        DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc);
 337        return rc;
 338}
 339
 340static int ov7x20_get_v4l1_control(struct i2c_client *c,
 341                                   struct ovcamchip_control *ctl)
 342{
 343        struct ovcamchip *ov = i2c_get_clientdata(c);
 344        struct ov7x20 *s = ov->spriv;
 345        int rc = 0;
 346        unsigned char val = 0;
 347
 348        switch (ctl->id) {
 349        case OVCAMCHIP_CID_CONT:
 350                rc = ov_read(c, 0x64, &val);
 351                ctl->value = ov7x20_lut_find(ctab, 16, val) << 12;
 352                break;
 353        case OVCAMCHIP_CID_BRIGHT:
 354                rc = ov_read(c, REG_BRT, &val);
 355                ctl->value = val << 8;
 356                break;
 357        case OVCAMCHIP_CID_SAT:
 358                rc = ov_read(c, REG_SAT, &val);
 359                ctl->value = val << 8;
 360                break;
 361        case OVCAMCHIP_CID_EXP:
 362                rc = ov_read(c, REG_EXP, &val);
 363                ctl->value = val;
 364                break;
 365        case OVCAMCHIP_CID_BANDFILT:
 366                ctl->value = s->bandfilt;
 367                break;
 368        case OVCAMCHIP_CID_AUTOBRIGHT:
 369                ctl->value = s->auto_brt;
 370                break;
 371        case OVCAMCHIP_CID_AUTOEXP:
 372                ctl->value = s->auto_exp;
 373                break;
 374        case OVCAMCHIP_CID_BACKLIGHT:
 375                ctl->value = s->backlight;
 376                break;
 377        case OVCAMCHIP_CID_MIRROR:
 378                ctl->value = s->mirror;
 379                break;
 380        default:
 381                DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
 382                return -EPERM;
 383        }
 384
 385        DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc);
 386        return rc;
 387}
 388
 389static int ov7x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win)
 390{
 391        struct ovcamchip *ov = i2c_get_clientdata(c);
 392        int qvga = win->quarter;
 393
 394        /******** QVGA-specific regs ********/
 395        ov_write_mask(c, 0x14, qvga?0x20:0x00, 0x20);
 396        ov_write_mask(c, 0x28, qvga?0x00:0x20, 0x20);
 397        ov_write(c, 0x24, qvga?0x20:0x3a);
 398        ov_write(c, 0x25, qvga?0x30:0x60);
 399        ov_write_mask(c, 0x2d, qvga?0x40:0x00, 0x40);
 400        if (!ov->mono)
 401                ov_write_mask(c, 0x67, qvga?0xf0:0x90, 0xf0);
 402        ov_write_mask(c, 0x74, qvga?0x20:0x00, 0x20);
 403
 404        /******** Clock programming ********/
 405
 406        ov_write(c, 0x11, win->clockdiv);
 407
 408        return 0;
 409}
 410
 411static int ov7x20_set_window(struct i2c_client *c, struct ovcamchip_window *win)
 412{
 413        int ret, hwscale, vwscale;
 414
 415        ret = ov7x20_mode_init(c, win);
 416        if (ret < 0)
 417                return ret;
 418
 419        if (win->quarter) {
 420                hwscale = 1;
 421                vwscale = 0;
 422        } else {
 423                hwscale = 2;
 424                vwscale = 1;
 425        }
 426
 427        ov_write(c, 0x17, HWSBASE + (win->x >> hwscale));
 428        ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale));
 429        ov_write(c, 0x19, VWSBASE + (win->y >> vwscale));
 430        ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale));
 431
 432        return 0;
 433}
 434
 435static int ov7x20_command(struct i2c_client *c, unsigned int cmd, void *arg)
 436{
 437        switch (cmd) {
 438        case OVCAMCHIP_CMD_S_CTRL:
 439                return ov7x20_set_v4l1_control(c, arg);
 440        case OVCAMCHIP_CMD_G_CTRL:
 441                return ov7x20_get_v4l1_control(c, arg);
 442        case OVCAMCHIP_CMD_S_MODE:
 443                return ov7x20_set_window(c, arg);
 444        default:
 445                DDEBUG(2, &c->dev, "command not supported: %d", cmd);
 446                return -ENOIOCTLCMD;
 447        }
 448}
 449
 450struct ovcamchip_ops ov7x20_ops = {
 451        .init    =      ov7x20_init,
 452        .free    =      ov7x20_free,
 453        .command =      ov7x20_command,
 454};
 455