Logo Search packages:      
Sourcecode: qc-usb version File versions  Download package

qc-hdcs.c

/* Start of file */

/* {{{ [fold] Comments */
/*
 * qc-usb, linux V4L driver for the Logitech QuickCam USB camera family
 *
 * qc-hdcs.c - HDCS Sensor Implementation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
/* }}} */

#ifdef NOKERNEL
#include "quickcam.h"
#else
#include <linux/quickcam.h>
#endif

/* LSB bit of I2C or register address signifies write (0) or read (1) */

/* I2C Address */
#define HDCS_ADDR       (0x55<<1)
 
/* {{{ [fold] I2C registers */
/* I2C Registers common for both HDCS-1000/1100 and HDCS-1020 */
#define HDCS_IDENT      (0x00<<1)   /* Identifications Register */
#define HDCS_STATUS     (0x01<<1)   /* Status Register */
#define HDCS_IMASK      (0x02<<1)   /* Interrupt Mask Register */
#define HDCS_PCTRL      (0x03<<1)   /* Pad Control Register */
#define HDCS_PDRV (0x04<<1)   /* Pad Drive Control Register */
#define HDCS_ICTRL      (0x05<<1)   /* Interface Control Register */
#define HDCS_ITMG (0x06<<1)   /* Interface Timing Register */
#define HDCS_BFRAC      (0x07<<1)   /* Baud Fraction Register */
#define HDCS_BRATE      (0x08<<1)   /* Baud Rate Register */
#define HDCS_ADCCTRL    (0x09<<1)   /* ADC Control Register */
#define HDCS_FWROW      (0x0A<<1)   /* First Window Row Register */
#define HDCS_FWCOL      (0x0B<<1)   /* First Window Column Register */
#define HDCS_LWROW      (0x0C<<1)   /* Last Window Row Register */
#define HDCS_LWCOL      (0x0D<<1)   /* Last Window Column Register */
#define HDCS_TCTRL      (0x0E<<1)   /* Timing Control Register */
#define HDCS_ERECPGA    (0x0F<<1)   /* PGA Gain Register: Even Row, Even Column */
#define HDCS_EROCPGA    (0x10<<1)   /* PGA Gain Register: Even Row, Odd Column */
#define HDCS_ORECPGA    (0x11<<1)   /* PGA Gain Register: Odd Row, Even Column */
#define HDCS_OROCPGA    (0x12<<1)   /* PGA Gain Register: Odd Row, Odd Column */
#define HDCS_ROWEXPL    (0x13<<1)   /* Row Exposure Low Register */
#define HDCS_ROWEXPH    (0x14<<1)   /* Row Exposure High Register */

/* I2C Registers only for HDCS-1000/1100 */
#define HDCS00_SROWEXPL (0x15<<1)   /* Sub-Row Exposure Low Register */
#define HDCS00_SROWEXPH (0x16<<1)   /* Sub-Row Exposure High Register */
#define HDCS00_CONFIG   (0x17<<1)   /* Configuration Register */
#define HDCS00_CONTROL  (0x18<<1)   /* Control Register */

/* I2C Registers only for HDCS-1020 */
#define HDCS20_SROWEXP  (0x15<<1)   /* Sub-Row Exposure Register  */
#define HDCS20_ERROR    (0x16<<1)   /* Error Control Register     */
#define HDCS20_ITMG2    (0x17<<1)   /* Interface Timing 2 Register      */
#define HDCS20_ICTRL2   (0x18<<1)   /* Interface Control 2 Register     */
#define HDCS20_HBLANK   (0x19<<1)   /* Horizontal Blank Register  */
#define HDCS20_VBLANK   (0x1A<<1)   /* Vertical Blank Register    */
#define HDCS20_CONFIG   (0x1B<<1)   /* Configuration Register     */
#define HDCS20_CONTROL  (0x1C<<1)   /* Control Register           */
/* }}} */

#define IS_870(qc)      (GET_PRODUCTID(qc)==0x870)
#define IS_1020(qc)     ((qc)->sensor_data.sensor->flag != 0)
#define GET_CONTROL     (IS_1020(qc) ? HDCS20_CONTROL : HDCS00_CONTROL)
#define GET_CONFIG      (IS_1020(qc) ? HDCS20_CONFIG : HDCS00_CONFIG)

#define I2C_SET_CHECK(reg,val)      if ((r = qc_i2c_set(qc,(reg),(val)))<0) goto fail
#define STV_SET_CHECK(reg,val)      if ((r = qc_stv_set(qc,(reg),(val)))<0) goto fail
#define STV_SETW_CHECK(reg,val)     if ((r = qc_stv_setw(qc,(reg),(val)))<0) goto fail

/* Enables experimental compressed mode, works with HDCS-1000/0x840,
   mode derived from USB logs obtained from HDCS-1020/0x870
   (should give 640x480), not tested with HDCS-1020.
   On HDCS-1000, gives 30 fps but data is in unknown format,
   observed image width 163 bytes (how many pixels?).
   Frame length appears to vary, typically 3300-4550 bytes.
   (apparently quite simple, however).
    Use this with DUMPDATA mode. */
#define HDCS_COMPRESS 0

#if HDCS_COMPRESS
/* {{{ [fold] hdcs_compress_init(struct quickcam *qc, int flags) */
static int hdcs_compress_init(struct quickcam *qc, int flags)
{
      int r;

      if (flags & 1) {
      /************************************** Plugin camera **************************************/

      STV_SET_CHECK(0x1440, 0x00);                                      /* Turn on/off isochronous stream */
      // if ((r = qc_stv_getw(qc, 0xE00A)) != 0x0870)                         /* ProductId */
      //    PDEBUG("Bad value 0x%02X in reg 0xE00A, should be 0x0870", r);

      STV_SET_CHECK(0x0423, 0x05);                                      /* Unknown (sometimes 4, sometimes 5) */
      // Warning: I2C address 0xBA is invalid
      // if ((r = qc_get_i2c(qc, qc->sensor_data.sensor, HDCS_IDENT))<0)            /* Identifications Register */
      //    PDEBUG("error reading sensor reg HDCS_IDENT");
      // if ((r = qc_stv_get(qc, 0x0424)) != 0x0a)                            /* Successfully transmitted I2C commands */
      //    PDEBUG("Bad value 0x%02X in reg 0x0424, should be 0x0a", r);
      STV_SET_CHECK(0x0423, 0x04);                                      /* Unknown (sometimes 4, sometimes 5) */

      if ((r = qc_stv_get(qc, 0x1444)) != 0x10)                         /* ? */
            PDEBUG("Bad value 0x%02X in reg 0x1444, should be 0x10", r);
      if ((r = qc_stv_get(qc, 0x1444)) != 0x10)                         /* ? */
            PDEBUG("Bad value 0x%02X in reg 0x1444, should be 0x10", r);
      if ((r = qc_stv_get(qc, 0x1444)) != 0x10)                         /* ? */
            PDEBUG("Bad value 0x%02X in reg 0x1444, should be 0x10", r);

      STV_SET_CHECK(0x0423, 0x05);                                      /* Unknown (sometimes 4, sometimes 5) */
      // Warning: I2C address 0x20 is invalid
      // if ((r = qc_get_i2c(qc, qc->sensor_data.sensor, HDCS_IDENT))<0)            /* Identifications Register */
      //    PDEBUG("error reading sensor reg HDCS_IDENT");
      // if ((r = qc_get_i2c(qc, qc->sensor_data.sensor, HDCS_IDENT))<0)            /* Identifications Register */
      //    PDEBUG("error reading sensor reg HDCS_IDENT");
      // if ((r = qc_stv_get(qc, 0x0424)) != 0x0a)                            /* Successfully transmitted I2C commands */
      //    PDEBUG("Bad value 0x%02X in reg 0x0424, should be 0x0a", r);
      // Warning: I2C address 0x20 is invalid
      // if ((r = qc_get_i2c(qc, qc->sensor_data.sensor, HDCS_IDENT))<0)            /* Identifications Register */
      //    PDEBUG("error reading sensor reg HDCS_IDENT");
      // if ((r = qc_get_i2c(qc, qc->sensor_data.sensor, HDCS_IDENT))<0)            /* Identifications Register */
      //    PDEBUG("error reading sensor reg HDCS_IDENT");
      // if ((r = qc_stv_get(qc, 0x0424)) != 0x0a)                            /* Successfully transmitted I2C commands */
      //    PDEBUG("Bad value 0x%02X in reg 0x0424, should be 0x0a", r);
      // Warning: I2C address 0x20 is invalid
      // if ((r = qc_get_i2c(qc, qc->sensor_data.sensor, HDCS_IDENT))<0)            /* Identifications Register */
      //    PDEBUG("error reading sensor reg HDCS_IDENT");
      // if ((r = qc_get_i2c(qc, qc->sensor_data.sensor, HDCS_IDENT))<0)            /* Identifications Register */
      //    PDEBUG("error reading sensor reg HDCS_IDENT");
      // if ((r = qc_stv_get(qc, 0x0424)) != 0x0a)                            /* Successfully transmitted I2C commands */
      //    PDEBUG("Bad value 0x%02X in reg 0x0424, should be 0x0a", r);
      STV_SET_CHECK(0x0423, 0x04);                                      /* Unknown (sometimes 4, sometimes 5) */
      STV_SET_CHECK(0x0423, 0x04);                                      /* Unknown (sometimes 4, sometimes 5) */

      // if ((r = qc_get_i2c(qc, qc->sensor_data.sensor, HDCS_IDENT))<0)            /* Identifications Register */
      //    PDEBUG("error reading sensor reg HDCS_IDENT");
      // if ((r = qc_stv_get(qc, 0x0424)) != 0x02)                            /* Successfully transmitted I2C commands */
      //    PDEBUG("Bad value 0x%02X in reg 0x0424, should be 0x02", r);
      // if ((r = qc_stv_get(qc, 0x1410)) != 0x10)                            /* I2C area, first reg value */
      //    PDEBUG("Bad value 0x%02X in reg 0x1410, should be 0x10", r);

      // if ((r = qc_get_i2c(qc, qc->sensor_data.sensor, HDCS_IDENT))<0)            /* Identifications Register */
      //    PDEBUG("error reading sensor reg HDCS_IDENT");
      // if ((r = qc_stv_get(qc, 0x0424)) != 0x02)                            /* Successfully transmitted I2C commands */
      //    PDEBUG("Bad value 0x%02X in reg 0x0424, should be 0x02", r);
      // if ((r = qc_stv_get(qc, 0x1410)) != 0x10)                            /* I2C area, first reg value */
      //    PDEBUG("Bad value 0x%02X in reg 0x1410, should be 0x10", r);

      STV_SET_CHECK(0x0423, 0x04);                                      /* Unknown (sometimes 4, sometimes 5) */
      if ((r = qc_stv_get(qc, 0x0424)) != 0x02)                         /* Successfully transmitted I2C commands */
            PDEBUG("Bad value 0x%02X in reg 0x0424, should be 0x02", r);
      STV_SET_CHECK(0x0423, 0x04);                                      /* Unknown (sometimes 4, sometimes 5) */

      // if ((r = qc_get_i2c(qc, qc->sensor_data.sensor, HDCS_IDENT))<0)            /* Identifications Register */
      //    PDEBUG("error reading sensor reg HDCS_IDENT");
      // if ((r = qc_stv_get(qc, 0x0424)) != 0x02)                            /* Successfully transmitted I2C commands */
      //    PDEBUG("Bad value 0x%02X in reg 0x0424, should be 0x02", r);
      // if ((r = qc_stv_get(qc, 0x1410)) != 0x10)                            /* I2C area, first reg value */
      //    PDEBUG("Bad value 0x%02X in reg 0x1410, should be 0x10", r);

      // if ((r = qc_get_i2c(qc, qc->sensor_data.sensor, HDCS_IDENT))<0)            /* Identifications Register */
      //    PDEBUG("error reading sensor reg HDCS_IDENT");
      // if ((r = qc_stv_get(qc, 0x0424)) != 0x02)                            /* Successfully transmitted I2C commands */
      //    PDEBUG("Bad value 0x%02X in reg 0x0424, should be 0x02", r);
      // if ((r = qc_stv_get(qc, 0x1410)) != 0x10)                            /* I2C area, first reg value */
      //    PDEBUG("Bad value 0x%02X in reg 0x1410, should be 0x10", r);

      STV_SET_CHECK(0x1500, 0x1D);                                      /* ? */
      if ((r = qc_stv_get(qc, 0x1443)) != 0x00)                         /* Scan rate? */
            PDEBUG("Bad value 0x%02X in reg 0x1443, should be 0x00", r);
      STV_SET_CHECK(0x1443, 0x01);                                      /* Scan rate? */
      if ((r = qc_stv_get(qc, 0x1443)) != 0x01)                         /* Scan rate? */
            PDEBUG("Bad value 0x%02X in reg 0x1443, should be 0x01", r);
      STV_SET_CHECK(0x1443, 0x00);                                      /* Scan rate? */

      I2C_SET_CHECK(HDCS_PCTRL, 0x63);                                  /* Pad Control Register */
      I2C_SET_CHECK(HDCS_PDRV, 0x00);                                         /* Pad Drive Control Register */
      I2C_SET_CHECK(GET_CONFIG, 0x08);                                  /* Configuration Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      I2C_SET_CHECK(GET_CONTROL, 0x04);                                 /* Control Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      I2C_SET_CHECK(GET_CONTROL, 0x00);                                 /* Control Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1446, 0x00);                                      /* ? */
      I2C_SET_CHECK(HDCS_PCTRL, 0x63);                                  /* Pad Control Register */
      I2C_SET_CHECK(HDCS_PDRV, 0x00);                                         /* Pad Drive Control Register */
      I2C_SET_CHECK(GET_CONFIG, 0x08);                                  /* Configuration Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      if ((r = qc_stv_get(qc, 0x1445)) != 0x04)                         /* Turn LED on/off? */
            PDEBUG("Bad value 0x%02X in reg 0x1445, should be 0x04", r);
      I2C_SET_CHECK(HDCS_ERECPGA, 0x3B);                                /* PGA Gain Register: Even Row, Even Column */
      I2C_SET_CHECK(HDCS_EROCPGA, 0x3B);                                /* PGA Gain Register: Even Row, Odd Column */
      I2C_SET_CHECK(HDCS_ORECPGA, 0x3B);                                /* PGA Gain Register: Odd Row, Even Column */
      I2C_SET_CHECK(HDCS_OROCPGA, 0x3B);                                /* PGA Gain Register: Odd Row, Odd Column */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1504, 0x07);                                      /* ? */
      STV_SET_CHECK(0x1503, 0x45);                                      /* ? */
      if ((r = qc_stv_get(qc, 0x1500)) != 0x1d)                         /* ? */
            PDEBUG("Bad value 0x%02X in reg 0x1500, should be 0x1d", r);
      STV_SET_CHECK(0x1500, 0x1D);                                      /* ? */
      // if ((r = qc_stv_getw(qc, 0xE00A)) != 0x0870)                         /* ProductId */
      //    PDEBUG("Bad value 0x%02X in reg 0xE00A, should be 0x0870", r);
      }
      
      if (flags & 2) {
      /************************************** Start grabbing **************************************/

      // if ((r = qc_stv_getw(qc, 0xE00A)) != 0x0870)                         /* ProductId */
      //    PDEBUG("Bad value 0x%02X in reg 0xE00A, should be 0x0870", r);
      STV_SET_CHECK(0x0423, 0x04);                                      /* Unknown (sometimes 4, sometimes 5) */
      STV_SET_CHECK(0x1500, 0x1D);                                      /* ? */
      I2C_SET_CHECK(HDCS_PCTRL, 0x63);                                  /* Pad Control Register */
      I2C_SET_CHECK(HDCS_PDRV, 0x00);                                         /* Pad Drive Control Register */
      I2C_SET_CHECK(GET_CONFIG, 0x08);                                  /* Configuration Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x15C3, 0x02);                                      /* Y-Control, 1: 288 lines, 2: 144 lines */
      STV_SETW_CHECK(0x15C1, 0x027B);                                         /* Max. ISO packet size */
      I2C_SET_CHECK(HDCS_FWROW, 0x00);                                  /* First Window Row Register */
      I2C_SET_CHECK(HDCS_FWCOL, 0x0B);                                  /* First Window Column Register */
      I2C_SET_CHECK(HDCS_LWROW, 0x3D);                                  /* Last Window Row Register */
      I2C_SET_CHECK(HDCS_LWCOL, 0x5A);                                  /* Last Window Column Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1680, 0x00);                                      /* X-Control, 0xa: 352 columns, 6: 176 columns */
      I2C_SET_CHECK(HDCS_TCTRL, IS_1020(qc) ? 0xCB : 0x6B);                   /* Timing Control Register */
      I2C_SET_CHECK(HDCS_ICTRL, 0x00);                                  /* Interface Control Register */
      I2C_SET_CHECK(HDCS_ITMG, 0x16);                                         /* Interface Timing Register */
      if (IS_1020(qc)) I2C_SET_CHECK(HDCS20_HBLANK, 0xD6);                    /* Horizontal Blank Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1446, 0x00);                                      /* ? */
      if ((r = qc_stv_get(qc, 0x1446)) != 0x00)                         /* ? */
            PDEBUG("Bad value 0x%02X in reg 0x1446, should be 0x00", r);
      STV_SET_CHECK(0x1446, 0x00);                                      /* ? */
      I2C_SET_CHECK(HDCS_ROWEXPL, 0x7B);                                /* Row Exposure Low Register */
      I2C_SET_CHECK(HDCS_ROWEXPH, 0x00);                                /* Row Exposure High Register */
      if (IS_1020(qc)) {
            I2C_SET_CHECK(HDCS20_SROWEXP, 0x01);                              /* Sub-Row Exposure Register */
      } else {
            I2C_SET_CHECK(HDCS00_SROWEXPL, 0x01<<2);
            I2C_SET_CHECK(HDCS00_SROWEXPH, 0x00);
      }
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1501, 0xC2);                                      /* ? */
      STV_SET_CHECK(0x1502, 0xB0);                                      /* ? */
      I2C_SET_CHECK(HDCS_PCTRL, 0x63);                                  /* Pad Control Register */
      I2C_SET_CHECK(HDCS_PDRV, 0x00);                                         /* Pad Drive Control Register */
      I2C_SET_CHECK(GET_CONFIG, 0x08);                                  /* Configuration Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;

      /* Start isochronous streaming */
      I2C_SET_CHECK(GET_CONTROL, 0x04);                                 /* Control Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1440, 0x01);                                      /* Turn on/off isochronous stream */

      if ((r = qc_stv_get(qc, 0x1445)) != 0x04)                         /* Turn LED on/off? */
            PDEBUG("Bad value 0x%02X in reg 0x1445, should be 0x04", r);
      if ((r = qc_stv_get(qc, 0x1445)) != 0x04)                         /* Turn LED on/off? */
            PDEBUG("Bad value 0x%02X in reg 0x1445, should be 0x04", r);
      if ((r = qc_stv_get(qc, 0x1445)) != 0x04)                         /* Turn LED on/off? */
            PDEBUG("Bad value 0x%02X in reg 0x1445, should be 0x04", r);

      /* Stop isochronous streaming */
      STV_SET_CHECK(0x1440, 0x00);                                      /* Turn on/off isochronous stream */
      I2C_SET_CHECK(GET_CONTROL, 0x00);                                 /* Control Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;

      STV_SET_CHECK(0x0423, 0x04);                                      /* Unknown (sometimes 4, sometimes 5) */
      STV_SET_CHECK(0x1500, 0x1D);                                      /* ? */
      I2C_SET_CHECK(HDCS_PCTRL, 0x63);                                  /* Pad Control Register */
      I2C_SET_CHECK(HDCS_PDRV, 0x00);                                         /* Pad Drive Control Register */
      I2C_SET_CHECK(GET_CONFIG, 0x08);                                  /* Configuration Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x15C3, 0x02);                                      /* Y-Control, 1: 288 lines, 2: 144 lines */
      STV_SETW_CHECK(0x15C1, 0x027B);                                         /* Max. ISO packet size */
      I2C_SET_CHECK(HDCS_FWROW, 0x00);                                  /* First Window Row Register */
      I2C_SET_CHECK(HDCS_FWCOL, 0x0B);                                  /* First Window Column Register */
      I2C_SET_CHECK(HDCS_LWROW, 0x3D);                                  /* Last Window Row Register */
      I2C_SET_CHECK(HDCS_LWCOL, 0x5A);                                  /* Last Window Column Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1680, 0x00);                                      /* X-Control, 0xa: 352 columns, 6: 176 columns */
      I2C_SET_CHECK(HDCS_TCTRL, IS_1020(qc) ? 0xCB : 0x6B);                   /* Timing Control Register */
      I2C_SET_CHECK(HDCS_ICTRL, 0x00);                                  /* Interface Control Register */
      I2C_SET_CHECK(HDCS_ITMG, 0x16);                                         /* Interface Timing Register */
      if (IS_1020(qc)) I2C_SET_CHECK(HDCS20_HBLANK, 0xD6);                    /* Horizontal Blank Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1446, 0x00);                                      /* ? */
      if ((r = qc_stv_get(qc, 0x1446)) != 0x00)                         /* ? */
            PDEBUG("Bad value 0x%02X in reg 0x1446, should be 0x00", r);
      STV_SET_CHECK(0x1446, 0x00);                                      /* ? */
      I2C_SET_CHECK(HDCS_ROWEXPL, 0x7B);                                /* Row Exposure Low Register */
      I2C_SET_CHECK(HDCS_ROWEXPH, 0x00);                                /* Row Exposure High Register */
      if (IS_1020(qc)) {
            I2C_SET_CHECK(HDCS20_SROWEXP, 0x01);                              /* Sub-Row Exposure Register */
      } else {
            I2C_SET_CHECK(HDCS00_SROWEXPL, 0x01<<2);
            I2C_SET_CHECK(HDCS00_SROWEXPH, 0x00);
      }
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1501, 0xC2);                                      /* ? */
      STV_SET_CHECK(0x1502, 0xB0);                                      /* ? */
      I2C_SET_CHECK(HDCS_PCTRL, 0x63);                                  /* Pad Control Register */
      I2C_SET_CHECK(HDCS_PDRV, 0x00);                                         /* Pad Drive Control Register */
      I2C_SET_CHECK(GET_CONFIG, 0x08);                                  /* Configuration Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;

      /* Start isochronous streaming */
      I2C_SET_CHECK(GET_CONTROL, 0x04);                                 /* Control Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      if ((r = qc_stv_get(qc, 0x1445)) != 0x04)                         /* Turn LED on/off? */
            PDEBUG("Bad value 0x%02X in reg 0x1445, should be 0x04", r);
      STV_SET_CHECK(0x1440, 0x01);                                      /* Turn on/off isochronous stream */

      /* Stop isochronous streaming */
      STV_SET_CHECK(0x1440, 0x00);                                      /* Turn on/off isochronous stream */
      I2C_SET_CHECK(GET_CONTROL, 0x00);                                 /* Control Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;

      STV_SET_CHECK(0x0423, 0x04);                                      /* Unknown (sometimes 4, sometimes 5) */
      STV_SET_CHECK(0x1500, 0x1D);                                      /* ? */
      I2C_SET_CHECK(HDCS_PCTRL, 0x63);                                  /* Pad Control Register */
      I2C_SET_CHECK(HDCS_PDRV, 0x00);                                         /* Pad Drive Control Register */
      I2C_SET_CHECK(GET_CONFIG, 0x08);                                  /* Configuration Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x15C3, 0x02);                                      /* Y-Control, 1: 288 lines, 2: 144 lines */
      STV_SETW_CHECK(0x15C1, 0x0230);                                         /* Max. ISO packet size */
      I2C_SET_CHECK(HDCS_FWROW, 0x00);                                  /* First Window Row Register */
      I2C_SET_CHECK(HDCS_FWCOL, 0x07);                                  /* First Window Column Register */
      I2C_SET_CHECK(HDCS_LWROW, 0x49);                                  /* Last Window Row Register */
      I2C_SET_CHECK(HDCS_LWCOL, 0x5E);                                  /* Last Window Column Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1680, 0x00);                                      /* X-Control, 0xa: 352 columns, 6: 176 columns */
      I2C_SET_CHECK(HDCS_TCTRL, IS_1020(qc) ? 0xCE : 0x6E);                   /* Timing Control Register */
      I2C_SET_CHECK(HDCS_ICTRL, 0x00);                                  /* Interface Control Register */
      I2C_SET_CHECK(HDCS_ITMG, 0x16);                                         /* Interface Timing Register */
      if (IS_1020(qc)) I2C_SET_CHECK(HDCS20_HBLANK, 0xCF);                    /* Horizontal Blank Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1446, 0x00);                                      /* ? */
      if ((r = qc_stv_get(qc, 0x1446)) != 0x00)                         /* ? */
            PDEBUG("Bad value 0x%02X in reg 0x1446, should be 0x00", r);
      STV_SET_CHECK(0x1446, 0x00);                                      /* ? */
      I2C_SET_CHECK(HDCS_ROWEXPL, 0x62);                                /* Row Exposure Low Register */
      I2C_SET_CHECK(HDCS_ROWEXPH, 0x00);                                /* Row Exposure High Register */
      if (IS_1020(qc)) {
            I2C_SET_CHECK(HDCS20_SROWEXP, 0x1A);                              /* Sub-Row Exposure Register */
      } else {
            I2C_SET_CHECK(HDCS00_SROWEXPL, 0x1A<<2);
            I2C_SET_CHECK(HDCS00_SROWEXPH, 0x00);
      }
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1501, 0xB6);                                      /* ? */
      STV_SET_CHECK(0x1502, 0xA8);                                      /* ? */
      I2C_SET_CHECK(HDCS_PCTRL, 0x63);                                  /* Pad Control Register */
      I2C_SET_CHECK(HDCS_PDRV, 0x00);                                         /* Pad Drive Control Register */
      I2C_SET_CHECK(GET_CONFIG, 0x08);                                  /* Configuration Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;

      /* Start isochronous streaming */
      I2C_SET_CHECK(GET_CONTROL, 0x04);                                 /* Control Register */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      STV_SET_CHECK(0x1440, 0x01);                                      /* Turn on/off isochronous stream */

      if ((r = qc_stv_get(qc, 0x1445)) != 0x04)                         /* Turn LED on/off? */
            PDEBUG("Bad value 0x%02X in reg 0x1445, should be 0x04", r);
      }

      return 0;
fail: return r;
}
/* }}} */
#endif
/* {{{ [fold] hdcs_init: Initialise parameters (from Georg Acher's user module for hdcs sensor) */
static int hdcs_init(struct quickcam *qc)
{
      struct qc_sensor_data *sd = &qc->sensor_data;
      unsigned char control = GET_CONTROL;
      unsigned char config = GET_CONFIG;
      int r,tctrl,astrt,psmp;

      if (sd->compress) return -EINVAL;
      sd->maxwidth  = IS_1020(qc) ? 352 : 360;  /* CIF */
      sd->maxheight = IS_1020(qc) ? 292 : 296;
      if (sd->subsample) {
            sd->maxwidth  /= 2;           /* QCIF */
            sd->maxheight /= 2;
      }
      if ((r = qc_i2c_break(qc))<0) goto fail;  /* The following setting must go into same I2C packet */
#if HDCS_COMPRESS
r = hdcs_compress_init(qc, 3);
qc_i2c_wait(qc);
qc_i2c_break(qc);
qc_stv_set(qc, 0x1440, 0x00);       /* Turn on/off isochronous stream */
qc_i2c_set(qc, GET_CONTROL, BIT(1));      /* Stop and enter sleep mode */
qc_i2c_wait(qc);
if (r) PDEBUG("hdcs_compress_init(1) = %i", r);
return 0;
#endif
      STV_SET_CHECK(STV_REG23, 0);

      /* Set the STV0602AA in STV0600 emulation mode */
      if (IS_870(qc)) STV_SET_CHECK(0x1446, 1);

      /* Reset the image sensor (keeping it to 1 is a problem) */
      I2C_SET_CHECK(control, 1);
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      I2C_SET_CHECK(control, 0);
      if ((r = qc_i2c_wait(qc))<0) goto fail;
      
      I2C_SET_CHECK(HDCS_STATUS, BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1));  /* Clear status (writing 1 will clear the corresponding status bit) */
      
      I2C_SET_CHECK(HDCS_IMASK, 0x00);    /* Disable all interrupts */
      if ((r = qc_i2c_wait(qc))<0) goto fail;

      STV_SET_CHECK(STV_REG00, 0x1D);
      STV_SET_CHECK(STV_REG04, 0x07);
      STV_SET_CHECK(STV_REG03, 0x95);

      STV_SET_CHECK(STV_REG23, 0);

      STV_SET_CHECK(STV_SCAN_RATE, 0x20); /* Larger -> slower */

      STV_SETW_CHECK(STV_ISO_SIZE, 847);  /* ISO-Size, 0x34F = 847 .. 0x284 = 644 */

      /* Set mode */
      STV_SET_CHECK(STV_Y_CTRL, 0x01);    /* 0x02: half, 0x01: full */
      STV_SET_CHECK(STV_X_CTRL, 0x0A);    /* 0x06: half, 0x0A: full */
 
      /* These are not good final values, which will be set in set_size */
      /* However, it looks like it's best to set some values at this point nevertheless */
      I2C_SET_CHECK(HDCS_FWROW, 0);       /* Start at row 0 */
      I2C_SET_CHECK(HDCS_FWCOL, 0);       /* Start at column 0 */
      I2C_SET_CHECK(HDCS_LWROW, 0x47);    /* End at row 288 */
      I2C_SET_CHECK(HDCS_LWCOL, 0x57);    /* End at column 352 */

      /* 0x07 - 0x50 */
      astrt = 3;  /* 0..3, doesn't seem to have any effect... hmm.. smaller is slower with subsampling */
      if (!IS_1020(qc)) {
            /* HDCS-1000 (tctrl was 0x09, but caused some HDCS-1000 not to work) */
            /* Frame rate on HDCS-1000 0x46D:0x840 depending on PSMP:
             *  4 = doesn't work at all
             *  5 = 7.8 fps,
             *  6 = 6.9 fps,
             *  8 = 6.3 fps,
             * 10 = 5.5 fps,
             * 15 = 4.4 fps,
             * 31 = 2.8 fps */
            /* Frame rate on HDCS-1000 0x46D:0x870 depending on PSMP:
             * 15 = doesn't work at all
             * 18 = doesn't work at all
             * 19 = 7.3 fps
             * 20 = 7.4 fps
             * 21 = 7.4 fps
             * 22 = 7.4 fps
             * 24 = 6.3 fps
             * 30 = 5.4 fps */
            psmp = IS_870(qc) ? 20 : 5;   /* 4..31 (was 30, changed to 20) */
            tctrl = (astrt<<5) | psmp;
      } else {
            /* HDCS-1020 (tctrl was 0x7E, but causes slow frame rate on HDCS-1020) */
            /* Changed to 6 which should give 8.1 fps */
            psmp = 6;               /* 4..31 (was 9, changed to 6 to improve fps */
            tctrl = (astrt<<6) | psmp;
      }
      I2C_SET_CHECK(HDCS_TCTRL, tctrl);   /* Set PGA sample duration (was 0x7E for IS_870, but caused slow framerate with HDCS-1020) */

      I2C_SET_CHECK(control, 0);          /* FIXME:should not be anymore necessary (already done) */

      I2C_SET_CHECK(HDCS_ROWEXPL, 0);
      I2C_SET_CHECK(HDCS_ROWEXPH, 0);
      if (IS_1020(qc)) {
            I2C_SET_CHECK(HDCS20_SROWEXP, 0);
            I2C_SET_CHECK(HDCS20_ERROR, BIT(0)|BIT(2));     /* Clear error conditions by writing 1 */
      } else {
            I2C_SET_CHECK(HDCS00_SROWEXPL, 0);
            I2C_SET_CHECK(HDCS00_SROWEXPH, 0);
      }
      if ((r = qc_i2c_wait(qc))<0) goto fail;
 
      STV_SET_CHECK(STV_REG01, 0xB5);
      STV_SET_CHECK(STV_REG02, 0xA8);

      I2C_SET_CHECK(HDCS_PCTRL, BIT(6)|BIT(5)|BIT(1)|BIT(0));
      I2C_SET_CHECK(HDCS_PDRV,  0x00);
      I2C_SET_CHECK(HDCS_ICTRL, (sd->subsample ? BIT(7) : 0) | BIT(5));
      I2C_SET_CHECK(HDCS_ITMG,  BIT(4)|BIT(1));

      /* CONFIG: Bit 3: continous frame capture, bit 2: stop when frame complete */
      I2C_SET_CHECK(config, (sd->subsample ? BIT(5) : 0) | BIT(3));
      I2C_SET_CHECK(HDCS_ADCCTRL, 10);    /* ADC output resolution to 10 bits */
      if ((r = qc_i2c_wait(qc))<0) goto fail;
fail: return r;
}
/* }}} */
/* {{{ [fold] hdcs_start: Start grabbing */
static int hdcs_start(struct quickcam *qc)
{
      int r;
#if HDCS_COMPRESS
r = hdcs_compress_init(qc, 2);
qc_i2c_wait(qc);
if (r) PDEBUG("hdcs_compress_init(1) = %i", r);
return 0;
#endif
      if ((r = qc_i2c_break(qc))<0) goto fail;
      I2C_SET_CHECK(GET_CONTROL, BIT(2)); /* Run enable */
      if ((r = qc_i2c_break(qc))<0) goto fail;
fail: return r;
}
/* }}} */
/* {{{ [fold] hdcs_stop: Stop grabbing */
static int hdcs_stop(struct quickcam *qc)
{
      int r;
      if ((r = qc_i2c_break(qc))<0) goto fail;
      I2C_SET_CHECK(GET_CONTROL, BIT(1)); /* Stop and enter sleep mode */
      r = qc_i2c_wait(qc);
fail: return r;
}
/* }}} */
/* {{{ [fold] hdcs_set_exposure: Set exposure time, val=0..65535 */
static int hdcs_set_exposure(struct quickcam *qc, unsigned int val)
{
      struct qc_sensor_data *sd = &qc->sensor_data;
      unsigned char control = GET_CONTROL;
      unsigned int rowexp;          /* rowexp,srowexp = 15 bits (0..32767) */
      unsigned int srowexp;         /* sub-row exposure (smaller is brighter) */
      unsigned int max_srowexp;     /* Maximum srowexp value + 1 */
      int r;

      /* Absolute black at srowexp=2672,width=360; 2616, width=352; 1896, width=256 for hdcs1000 */

      if (val==sd->exposure) return 0;
      sd->exposure = val;
      val *= 16;        /* 16 seems to be the smallest change that actually affects brightness */
      max_srowexp = sd->width*15/2 - 104 + 1;
      srowexp = max_srowexp - (val % max_srowexp) - 1;
      rowexp  = val / max_srowexp;
      if (qcdebug&QC_DEBUGCAMERA) PDEBUG("width=%i height=%i rowexp=%i srowexp=%i",sd->width,sd->height,rowexp,srowexp);
      if ((r = qc_i2c_break(qc))<0) goto fail;        /* The following setting must go into same I2C packet */
      I2C_SET_CHECK(control, 0);                      /* Stop grabbing */
      I2C_SET_CHECK(HDCS_ROWEXPL, rowexp & 0xFF);           /* Number of rows to expose */
      I2C_SET_CHECK(HDCS_ROWEXPH, rowexp >> 8);
      if (IS_1020(qc)) {
            srowexp = 0;      //FIXME:need formula to compute srowexp for HDCS1020!
            srowexp >>= 2;                            /* Bits 0..1 are hardwired to 0 */
            I2C_SET_CHECK(HDCS20_SROWEXP, srowexp & 0xFF);  /* Number of pixels to expose */
      } else {
            I2C_SET_CHECK(HDCS00_SROWEXPL, srowexp & 0xFF); /* Number of pixels to expose */
            I2C_SET_CHECK(HDCS00_SROWEXPH, srowexp >> 8);
      }
      if (IS_1020(qc)) {
            I2C_SET_CHECK(HDCS20_ERROR, BIT(0));            /* Reset exposure error flag */
      } else {
            I2C_SET_CHECK(HDCS_STATUS, BIT(4));       /* Reset exposure error flag */
      }
      I2C_SET_CHECK(control, BIT(2));                       /* Restart grabbing */
      if ((r = qc_i2c_break(qc))<0) goto fail;
#if 0
      /* Warning: the code below will cause about 0.1 second delay and may cause lost frames */
      if (PARANOID) {
            /* Check if the new exposure setting is valid */
            if ((r = qc_i2c_wait(qc))<0) goto fail;
            if (IS_1020(qc)) {
                  if ((r = qc_get_i2c(qc,qc->sensor_data.sensor, HDCS20_ERROR))<0) goto fail;
                  if (r & BIT(0)) PDEBUG("exposure error (1020)");
            } else {
                  if ((r = qc_get_i2c(qc,qc->sensor_data.sensor, HDCS_STATUS))<0) goto fail;
                  if (r & BIT(4)) PDEBUG("exposure error (1000)");
            }
      }
      if ((r = qc_i2c_wait(qc))<0) goto fail;
#endif
      qc_frame_flush(qc);
fail: return (r<0) ? r : 0;
}
/* }}} */
/* {{{ [fold] hdcs_set_gains: Set gains */
static int hdcs_set_gains(struct quickcam *qc, unsigned int hue, unsigned int sat, unsigned int val)
{
      static const unsigned int min_gain = 8;
      struct qc_sensor_data *sd = &qc->sensor_data;
      unsigned int rgain, bgain, ggain;
      int r;
      qc_hsv2rgb(hue, sat, val, &rgain, &bgain, &ggain);
      rgain >>= 8;                              /* After this the values are 0..255 */
      ggain >>= 8;
      bgain >>= 8;
      rgain = MAX(rgain, min_gain);             /* Do not allow very small values, they cause bad (low-contrast) image */
      ggain = MAX(ggain, min_gain);
      bgain = MAX(bgain, min_gain);
      if (rgain==sd->rgain && ggain==sd->ggain && bgain==sd->bgain) return 0;
      sd->rgain = rgain;                  
      sd->ggain = ggain;
      sd->bgain = bgain;
      if (rgain > 127) rgain = rgain/2 | BIT(7);      /* Bit 7 doubles the programmed values */
      if (ggain > 127) ggain = ggain/2 | BIT(7);      /* Double programmed value if necessary */
      if (bgain > 127) bgain = bgain/2 | BIT(7);
      if ((r = qc_i2c_break(qc))<0) goto fail;
      I2C_SET_CHECK(HDCS_ERECPGA, ggain);
      I2C_SET_CHECK(HDCS_EROCPGA, rgain);
      I2C_SET_CHECK(HDCS_ORECPGA, bgain);
      I2C_SET_CHECK(HDCS_OROCPGA, ggain);
fail: return r;
}
/* }}} */
/* {{{ [fold] hdcs_set_levels() */
static int hdcs_set_levels(struct quickcam *qc, unsigned int exp, unsigned int gain, unsigned int hue, unsigned int sat)
{
      int r = 0;
//#if !HDCS_COMPRESS
      if ((r = hdcs_set_exposure(qc, gain))<0) goto fail;
//#endif
      hdcs_set_gains(qc, hue, sat, exp);
fail: return r;
}
/* }}} */
/* {{{ [fold] hdcs_set_size: Sets the size of the capture window */
/*
 *  Sets the size (scaling) of the capture window.
 *  If subsample could return the image size we use subsample.
 */
static int hdcs_set_size(struct quickcam *qc, unsigned int width, unsigned int height)
{
      /* The datasheet doesn't seem to say this, but HDCS-1000
       * has visible windows size of 360x296 pixels, the first upper-left
       * visible pixel is at 8,8.
       * From Andrey's test image: looks like HDCS-1020 upper-left
       * visible pixel is at 24,8 (y maybe even smaller?) and lower-right
       * visible pixel at 375,299 (x maybe even larger?)
       */
      unsigned int originx   = IS_1020(qc) ? 24 : 8;        /* First visible pixel */
      unsigned int maxwidth  = IS_1020(qc) ? 352 : 360;     /* Visible sensor size */
      unsigned int originy   = 8;
      unsigned int maxheight = IS_1020(qc) ? 292 : 296;

      unsigned char control = GET_CONTROL;
      struct qc_sensor_data *sd = &qc->sensor_data;
      int r;
      unsigned int x, y;

#if HDCS_COMPRESS
      return 0;
#endif
      if (sd->subsample) {
            width *= 2;
            height *= 2;
            width  = (width + 3)/4*4;           /* Width must be multiple of 4 */
            height = (height + 3)/4*4;          /* Height must be multiple of 4 */
            sd->width = width / 2;
            sd->height = height / 2;            /* The image sent will be subsampled by 2 */
      } else {
            sd->width  = width  = (width + 3)/4*4;    /* Width must be multiple of 4 */
            sd->height = height = (height + 3)/4*4;   /* Height must be multiple of 4 */
      }
      x = (maxwidth - width)/2;                 /* Center image by computing upper-left corner */
      y = (maxheight - height)/2;
      width /= 4;
      height /= 4;
      x = (x + originx)/4;                      /* Must be multiple of 4 (low bits wired to 0) */
      y = (y + originy)/4;

      if ((r = qc_i2c_break(qc))<0) goto fail;
      I2C_SET_CHECK(control, 0);                /* Stop grabbing */
      I2C_SET_CHECK(HDCS_FWROW, y);
      I2C_SET_CHECK(HDCS_FWCOL, x);
      I2C_SET_CHECK(HDCS_LWROW, y+height-1);
      I2C_SET_CHECK(HDCS_LWCOL, x+width-1);
      I2C_SET_CHECK(control, BIT(2));                 /* Restart grabbing */
      
      /* The exposure timings need to be recomputed when size is changed */
      x = sd->exposure;
      sd->exposure = -1;
      if ((r = hdcs_set_exposure(qc, x))<0) goto fail;
fail: return r;
}
/* }}} */

/* {{{ [fold] struct qc_sensor qc_sensor_hdcs1000 */
const struct qc_sensor qc_sensor_hdcs1000 = {
      name:       "HDCS-1000/1100",
      manufacturer:     "Hewlett Packard",
      init:       hdcs_init,
      start:            hdcs_start,
      stop:       hdcs_stop,
      set_size:   hdcs_set_size,
      set_levels: hdcs_set_levels,
      /* Exposure and gain control information */
      autoexposure:     FALSE,
      adapt_gainlow:    20,
      adapt_gainhigh:   20000,
      /* Information needed to access the sensor via I2C */
      reg23:            0,
      i2c_addr:   HDCS_ADDR,
      /* Identification information used for auto-detection */
      id_reg:           HDCS_IDENT | 1,
      id:         0x08,
      length_id:  1,
      flag:       0,
};
/* }}} */
/* {{{ [fold] struct qc_sensor qc_sensor_hdcs1020 */
const struct qc_sensor qc_sensor_hdcs1020 = {
      name:       "HDCS-1020",
      manufacturer:     "Agilent Technologies",
      init:       hdcs_init,
      start:            hdcs_start,
      stop:       hdcs_stop,
      set_size:   hdcs_set_size,
      set_levels: hdcs_set_levels,
      /* Exposure and gain control information */
      autoexposure:     FALSE,
      adapt_gainlow:    20,
      adapt_gainhigh:   20000,
      /* Information needed to access the sensor via I2C */
      reg23:            0,
      i2c_addr:   HDCS_ADDR,
      /* Identification information used for auto-detection */
      id_reg:           HDCS_IDENT | 1,
      id:         0x10,
      length_id:  1,
      flag:       1,
};
/* }}} */

/* End of file */

Generated by  Doxygen 1.6.0   Back to index