1*0c0d06caSMauro Carvalho Chehab /* 2*0c0d06caSMauro Carvalho Chehab * Sunplus spca561 subdriver 3*0c0d06caSMauro Carvalho Chehab * 4*0c0d06caSMauro Carvalho Chehab * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr 5*0c0d06caSMauro Carvalho Chehab * 6*0c0d06caSMauro Carvalho Chehab * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> 7*0c0d06caSMauro Carvalho Chehab * 8*0c0d06caSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 9*0c0d06caSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 10*0c0d06caSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 11*0c0d06caSMauro Carvalho Chehab * any later version. 12*0c0d06caSMauro Carvalho Chehab * 13*0c0d06caSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 14*0c0d06caSMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 15*0c0d06caSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16*0c0d06caSMauro Carvalho Chehab * GNU General Public License for more details. 17*0c0d06caSMauro Carvalho Chehab * 18*0c0d06caSMauro Carvalho Chehab * You should have received a copy of the GNU General Public License 19*0c0d06caSMauro Carvalho Chehab * along with this program; if not, write to the Free Software 20*0c0d06caSMauro Carvalho Chehab * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21*0c0d06caSMauro Carvalho Chehab */ 22*0c0d06caSMauro Carvalho Chehab 23*0c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 24*0c0d06caSMauro Carvalho Chehab 25*0c0d06caSMauro Carvalho Chehab #define MODULE_NAME "spca561" 26*0c0d06caSMauro Carvalho Chehab 27*0c0d06caSMauro Carvalho Chehab #include <linux/input.h> 28*0c0d06caSMauro Carvalho Chehab #include "gspca.h" 29*0c0d06caSMauro Carvalho Chehab 30*0c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); 31*0c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver"); 32*0c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 33*0c0d06caSMauro Carvalho Chehab 34*0c0d06caSMauro Carvalho Chehab #define EXPOSURE_MAX (2047 + 325) 35*0c0d06caSMauro Carvalho Chehab 36*0c0d06caSMauro Carvalho Chehab /* specific webcam descriptor */ 37*0c0d06caSMauro Carvalho Chehab struct sd { 38*0c0d06caSMauro Carvalho Chehab struct gspca_dev gspca_dev; /* !! must be the first item */ 39*0c0d06caSMauro Carvalho Chehab 40*0c0d06caSMauro Carvalho Chehab struct { /* hue/contrast control cluster */ 41*0c0d06caSMauro Carvalho Chehab struct v4l2_ctrl *contrast; 42*0c0d06caSMauro Carvalho Chehab struct v4l2_ctrl *hue; 43*0c0d06caSMauro Carvalho Chehab }; 44*0c0d06caSMauro Carvalho Chehab struct v4l2_ctrl *autogain; 45*0c0d06caSMauro Carvalho Chehab 46*0c0d06caSMauro Carvalho Chehab #define EXPO12A_DEF 3 47*0c0d06caSMauro Carvalho Chehab __u8 expo12a; /* expo/gain? for rev 12a */ 48*0c0d06caSMauro Carvalho Chehab 49*0c0d06caSMauro Carvalho Chehab __u8 chip_revision; 50*0c0d06caSMauro Carvalho Chehab #define Rev012A 0 51*0c0d06caSMauro Carvalho Chehab #define Rev072A 1 52*0c0d06caSMauro Carvalho Chehab 53*0c0d06caSMauro Carvalho Chehab signed char ag_cnt; 54*0c0d06caSMauro Carvalho Chehab #define AG_CNT_START 13 55*0c0d06caSMauro Carvalho Chehab }; 56*0c0d06caSMauro Carvalho Chehab 57*0c0d06caSMauro Carvalho Chehab static const struct v4l2_pix_format sif_012a_mode[] = { 58*0c0d06caSMauro Carvalho Chehab {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 59*0c0d06caSMauro Carvalho Chehab .bytesperline = 160, 60*0c0d06caSMauro Carvalho Chehab .sizeimage = 160 * 120, 61*0c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 62*0c0d06caSMauro Carvalho Chehab .priv = 3}, 63*0c0d06caSMauro Carvalho Chehab {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 64*0c0d06caSMauro Carvalho Chehab .bytesperline = 176, 65*0c0d06caSMauro Carvalho Chehab .sizeimage = 176 * 144, 66*0c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 67*0c0d06caSMauro Carvalho Chehab .priv = 2}, 68*0c0d06caSMauro Carvalho Chehab {320, 240, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE, 69*0c0d06caSMauro Carvalho Chehab .bytesperline = 320, 70*0c0d06caSMauro Carvalho Chehab .sizeimage = 320 * 240 * 4 / 8, 71*0c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 72*0c0d06caSMauro Carvalho Chehab .priv = 1}, 73*0c0d06caSMauro Carvalho Chehab {352, 288, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE, 74*0c0d06caSMauro Carvalho Chehab .bytesperline = 352, 75*0c0d06caSMauro Carvalho Chehab .sizeimage = 352 * 288 * 4 / 8, 76*0c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 77*0c0d06caSMauro Carvalho Chehab .priv = 0}, 78*0c0d06caSMauro Carvalho Chehab }; 79*0c0d06caSMauro Carvalho Chehab 80*0c0d06caSMauro Carvalho Chehab static const struct v4l2_pix_format sif_072a_mode[] = { 81*0c0d06caSMauro Carvalho Chehab {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 82*0c0d06caSMauro Carvalho Chehab .bytesperline = 160, 83*0c0d06caSMauro Carvalho Chehab .sizeimage = 160 * 120, 84*0c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 85*0c0d06caSMauro Carvalho Chehab .priv = 3}, 86*0c0d06caSMauro Carvalho Chehab {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 87*0c0d06caSMauro Carvalho Chehab .bytesperline = 176, 88*0c0d06caSMauro Carvalho Chehab .sizeimage = 176 * 144, 89*0c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 90*0c0d06caSMauro Carvalho Chehab .priv = 2}, 91*0c0d06caSMauro Carvalho Chehab {320, 240, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 92*0c0d06caSMauro Carvalho Chehab .bytesperline = 320, 93*0c0d06caSMauro Carvalho Chehab .sizeimage = 320 * 240, 94*0c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 95*0c0d06caSMauro Carvalho Chehab .priv = 1}, 96*0c0d06caSMauro Carvalho Chehab {352, 288, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 97*0c0d06caSMauro Carvalho Chehab .bytesperline = 352, 98*0c0d06caSMauro Carvalho Chehab .sizeimage = 352 * 288, 99*0c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 100*0c0d06caSMauro Carvalho Chehab .priv = 0}, 101*0c0d06caSMauro Carvalho Chehab }; 102*0c0d06caSMauro Carvalho Chehab 103*0c0d06caSMauro Carvalho Chehab /* 104*0c0d06caSMauro Carvalho Chehab * Initialization data 105*0c0d06caSMauro Carvalho Chehab * I'm not very sure how to split initialization from open data 106*0c0d06caSMauro Carvalho Chehab * chunks. For now, we'll consider everything as initialization 107*0c0d06caSMauro Carvalho Chehab */ 108*0c0d06caSMauro Carvalho Chehab /* Frame packet header offsets for the spca561 */ 109*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_SNAP 1 110*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_TYPE 2 111*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_COMPRESS 3 112*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_FRAMSEQ 4 113*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_GPIO 5 114*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_USBBUFF 6 115*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_WIN2GRAVE 7 116*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_WIN2RAVE 8 117*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_WIN2BAVE 9 118*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_WIN2GBAVE 10 119*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_WIN1GRAVE 11 120*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_WIN1RAVE 12 121*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_WIN1BAVE 13 122*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_WIN1GBAVE 14 123*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_FREQ 15 124*0c0d06caSMauro Carvalho Chehab #define SPCA561_OFFSET_VSYNC 16 125*0c0d06caSMauro Carvalho Chehab #define SPCA561_INDEX_I2C_BASE 0x8800 126*0c0d06caSMauro Carvalho Chehab #define SPCA561_SNAPBIT 0x20 127*0c0d06caSMauro Carvalho Chehab #define SPCA561_SNAPCTRL 0x40 128*0c0d06caSMauro Carvalho Chehab 129*0c0d06caSMauro Carvalho Chehab static const u16 rev72a_reset[][2] = { 130*0c0d06caSMauro Carvalho Chehab {0x0000, 0x8114}, /* Software GPIO output data */ 131*0c0d06caSMauro Carvalho Chehab {0x0001, 0x8114}, /* Software GPIO output data */ 132*0c0d06caSMauro Carvalho Chehab {0x0000, 0x8112}, /* Some kind of reset */ 133*0c0d06caSMauro Carvalho Chehab {} 134*0c0d06caSMauro Carvalho Chehab }; 135*0c0d06caSMauro Carvalho Chehab static const __u16 rev72a_init_data1[][2] = { 136*0c0d06caSMauro Carvalho Chehab {0x0003, 0x8701}, /* PCLK clock delay adjustment */ 137*0c0d06caSMauro Carvalho Chehab {0x0001, 0x8703}, /* HSYNC from cmos inverted */ 138*0c0d06caSMauro Carvalho Chehab {0x0011, 0x8118}, /* Enable and conf sensor */ 139*0c0d06caSMauro Carvalho Chehab {0x0001, 0x8118}, /* Conf sensor */ 140*0c0d06caSMauro Carvalho Chehab {0x0092, 0x8804}, /* I know nothing about these */ 141*0c0d06caSMauro Carvalho Chehab {0x0010, 0x8802}, /* 0x88xx registers, so I won't */ 142*0c0d06caSMauro Carvalho Chehab {} 143*0c0d06caSMauro Carvalho Chehab }; 144*0c0d06caSMauro Carvalho Chehab static const u16 rev72a_init_sensor1[][2] = { 145*0c0d06caSMauro Carvalho Chehab {0x0001, 0x000d}, 146*0c0d06caSMauro Carvalho Chehab {0x0002, 0x0018}, 147*0c0d06caSMauro Carvalho Chehab {0x0004, 0x0165}, 148*0c0d06caSMauro Carvalho Chehab {0x0005, 0x0021}, 149*0c0d06caSMauro Carvalho Chehab {0x0007, 0x00aa}, 150*0c0d06caSMauro Carvalho Chehab {0x0020, 0x1504}, 151*0c0d06caSMauro Carvalho Chehab {0x0039, 0x0002}, 152*0c0d06caSMauro Carvalho Chehab {0x0035, 0x0010}, 153*0c0d06caSMauro Carvalho Chehab {0x0009, 0x1049}, 154*0c0d06caSMauro Carvalho Chehab {0x0028, 0x000b}, 155*0c0d06caSMauro Carvalho Chehab {0x003b, 0x000f}, 156*0c0d06caSMauro Carvalho Chehab {0x003c, 0x0000}, 157*0c0d06caSMauro Carvalho Chehab {} 158*0c0d06caSMauro Carvalho Chehab }; 159*0c0d06caSMauro Carvalho Chehab static const __u16 rev72a_init_data2[][2] = { 160*0c0d06caSMauro Carvalho Chehab {0x0018, 0x8601}, /* Pixel/line selection for color separation */ 161*0c0d06caSMauro Carvalho Chehab {0x0000, 0x8602}, /* Optical black level for user setting */ 162*0c0d06caSMauro Carvalho Chehab {0x0060, 0x8604}, /* Optical black horizontal offset */ 163*0c0d06caSMauro Carvalho Chehab {0x0002, 0x8605}, /* Optical black vertical offset */ 164*0c0d06caSMauro Carvalho Chehab {0x0000, 0x8603}, /* Non-automatic optical black level */ 165*0c0d06caSMauro Carvalho Chehab {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ 166*0c0d06caSMauro Carvalho Chehab {0x0000, 0x865f}, /* Vertical valid pixels window (x2) */ 167*0c0d06caSMauro Carvalho Chehab {0x00b0, 0x865d}, /* Horizontal valid pixels window (x2) */ 168*0c0d06caSMauro Carvalho Chehab {0x0090, 0x865e}, /* Vertical valid lines window (x2) */ 169*0c0d06caSMauro Carvalho Chehab {0x00e0, 0x8406}, /* Memory buffer threshold */ 170*0c0d06caSMauro Carvalho Chehab {0x0000, 0x8660}, /* Compensation memory stuff */ 171*0c0d06caSMauro Carvalho Chehab {0x0002, 0x8201}, /* Output address for r/w serial EEPROM */ 172*0c0d06caSMauro Carvalho Chehab {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */ 173*0c0d06caSMauro Carvalho Chehab {0x0001, 0x8200}, /* OprMode to be executed by hardware */ 174*0c0d06caSMauro Carvalho Chehab /* from ms-win */ 175*0c0d06caSMauro Carvalho Chehab {0x0000, 0x8611}, /* R offset for white balance */ 176*0c0d06caSMauro Carvalho Chehab {0x00fd, 0x8612}, /* Gr offset for white balance */ 177*0c0d06caSMauro Carvalho Chehab {0x0003, 0x8613}, /* B offset for white balance */ 178*0c0d06caSMauro Carvalho Chehab {0x0000, 0x8614}, /* Gb offset for white balance */ 179*0c0d06caSMauro Carvalho Chehab /* from ms-win */ 180*0c0d06caSMauro Carvalho Chehab {0x0035, 0x8651}, /* R gain for white balance */ 181*0c0d06caSMauro Carvalho Chehab {0x0040, 0x8652}, /* Gr gain for white balance */ 182*0c0d06caSMauro Carvalho Chehab {0x005f, 0x8653}, /* B gain for white balance */ 183*0c0d06caSMauro Carvalho Chehab {0x0040, 0x8654}, /* Gb gain for white balance */ 184*0c0d06caSMauro Carvalho Chehab {0x0002, 0x8502}, /* Maximum average bit rate stuff */ 185*0c0d06caSMauro Carvalho Chehab {0x0011, 0x8802}, 186*0c0d06caSMauro Carvalho Chehab 187*0c0d06caSMauro Carvalho Chehab {0x0087, 0x8700}, /* Set master clock (96Mhz????) */ 188*0c0d06caSMauro Carvalho Chehab {0x0081, 0x8702}, /* Master clock output enable */ 189*0c0d06caSMauro Carvalho Chehab 190*0c0d06caSMauro Carvalho Chehab {0x0000, 0x8500}, /* Set image type (352x288 no compression) */ 191*0c0d06caSMauro Carvalho Chehab /* Originally was 0x0010 (352x288 compression) */ 192*0c0d06caSMauro Carvalho Chehab 193*0c0d06caSMauro Carvalho Chehab {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ 194*0c0d06caSMauro Carvalho Chehab {0x0003, 0x865c}, /* Vertical offset for valid lines */ 195*0c0d06caSMauro Carvalho Chehab {} 196*0c0d06caSMauro Carvalho Chehab }; 197*0c0d06caSMauro Carvalho Chehab static const u16 rev72a_init_sensor2[][2] = { 198*0c0d06caSMauro Carvalho Chehab {0x0003, 0x0121}, 199*0c0d06caSMauro Carvalho Chehab {0x0004, 0x0165}, 200*0c0d06caSMauro Carvalho Chehab {0x0005, 0x002f}, /* blanking control column */ 201*0c0d06caSMauro Carvalho Chehab {0x0006, 0x0000}, /* blanking mode row*/ 202*0c0d06caSMauro Carvalho Chehab {0x000a, 0x0002}, 203*0c0d06caSMauro Carvalho Chehab {0x0009, 0x1061}, /* setexposure times && pixel clock 204*0c0d06caSMauro Carvalho Chehab * 0001 0 | 000 0110 0001 */ 205*0c0d06caSMauro Carvalho Chehab {0x0035, 0x0014}, 206*0c0d06caSMauro Carvalho Chehab {} 207*0c0d06caSMauro Carvalho Chehab }; 208*0c0d06caSMauro Carvalho Chehab 209*0c0d06caSMauro Carvalho Chehab /******************** QC Express etch2 stuff ********************/ 210*0c0d06caSMauro Carvalho Chehab static const __u16 Pb100_1map8300[][2] = { 211*0c0d06caSMauro Carvalho Chehab /* reg, value */ 212*0c0d06caSMauro Carvalho Chehab {0x8320, 0x3304}, 213*0c0d06caSMauro Carvalho Chehab 214*0c0d06caSMauro Carvalho Chehab {0x8303, 0x0125}, /* image area */ 215*0c0d06caSMauro Carvalho Chehab {0x8304, 0x0169}, 216*0c0d06caSMauro Carvalho Chehab {0x8328, 0x000b}, 217*0c0d06caSMauro Carvalho Chehab {0x833c, 0x0001}, /*fixme: win:07*/ 218*0c0d06caSMauro Carvalho Chehab 219*0c0d06caSMauro Carvalho Chehab {0x832f, 0x1904}, /*fixme: was 0419*/ 220*0c0d06caSMauro Carvalho Chehab {0x8307, 0x00aa}, 221*0c0d06caSMauro Carvalho Chehab {0x8301, 0x0003}, 222*0c0d06caSMauro Carvalho Chehab {0x8302, 0x000e}, 223*0c0d06caSMauro Carvalho Chehab {} 224*0c0d06caSMauro Carvalho Chehab }; 225*0c0d06caSMauro Carvalho Chehab static const __u16 Pb100_2map8300[][2] = { 226*0c0d06caSMauro Carvalho Chehab /* reg, value */ 227*0c0d06caSMauro Carvalho Chehab {0x8339, 0x0000}, 228*0c0d06caSMauro Carvalho Chehab {0x8307, 0x00aa}, 229*0c0d06caSMauro Carvalho Chehab {} 230*0c0d06caSMauro Carvalho Chehab }; 231*0c0d06caSMauro Carvalho Chehab 232*0c0d06caSMauro Carvalho Chehab static const __u16 spca561_161rev12A_data1[][2] = { 233*0c0d06caSMauro Carvalho Chehab {0x29, 0x8118}, /* Control register (various enable bits) */ 234*0c0d06caSMauro Carvalho Chehab {0x08, 0x8114}, /* GPIO: Led off */ 235*0c0d06caSMauro Carvalho Chehab {0x0e, 0x8112}, /* 0x0e stream off 0x3e stream on */ 236*0c0d06caSMauro Carvalho Chehab {0x00, 0x8102}, /* white balance - new */ 237*0c0d06caSMauro Carvalho Chehab {0x92, 0x8804}, 238*0c0d06caSMauro Carvalho Chehab {0x04, 0x8802}, /* windows uses 08 */ 239*0c0d06caSMauro Carvalho Chehab {} 240*0c0d06caSMauro Carvalho Chehab }; 241*0c0d06caSMauro Carvalho Chehab static const __u16 spca561_161rev12A_data2[][2] = { 242*0c0d06caSMauro Carvalho Chehab {0x21, 0x8118}, 243*0c0d06caSMauro Carvalho Chehab {0x10, 0x8500}, 244*0c0d06caSMauro Carvalho Chehab {0x07, 0x8601}, 245*0c0d06caSMauro Carvalho Chehab {0x07, 0x8602}, 246*0c0d06caSMauro Carvalho Chehab {0x04, 0x8501}, 247*0c0d06caSMauro Carvalho Chehab 248*0c0d06caSMauro Carvalho Chehab {0x07, 0x8201}, /* windows uses 02 */ 249*0c0d06caSMauro Carvalho Chehab {0x08, 0x8200}, 250*0c0d06caSMauro Carvalho Chehab {0x01, 0x8200}, 251*0c0d06caSMauro Carvalho Chehab 252*0c0d06caSMauro Carvalho Chehab {0x90, 0x8604}, 253*0c0d06caSMauro Carvalho Chehab {0x00, 0x8605}, 254*0c0d06caSMauro Carvalho Chehab {0xb0, 0x8603}, 255*0c0d06caSMauro Carvalho Chehab 256*0c0d06caSMauro Carvalho Chehab /* sensor gains */ 257*0c0d06caSMauro Carvalho Chehab {0x07, 0x8601}, /* white balance - new */ 258*0c0d06caSMauro Carvalho Chehab {0x07, 0x8602}, /* white balance - new */ 259*0c0d06caSMauro Carvalho Chehab {0x00, 0x8610}, /* *red */ 260*0c0d06caSMauro Carvalho Chehab {0x00, 0x8611}, /* 3f *green */ 261*0c0d06caSMauro Carvalho Chehab {0x00, 0x8612}, /* green *blue */ 262*0c0d06caSMauro Carvalho Chehab {0x00, 0x8613}, /* blue *green */ 263*0c0d06caSMauro Carvalho Chehab {0x43, 0x8614}, /* green *red - white balance - was 0x35 */ 264*0c0d06caSMauro Carvalho Chehab {0x40, 0x8615}, /* 40 *green - white balance - was 0x35 */ 265*0c0d06caSMauro Carvalho Chehab {0x71, 0x8616}, /* 7a *blue - white balance - was 0x35 */ 266*0c0d06caSMauro Carvalho Chehab {0x40, 0x8617}, /* 40 *green - white balance - was 0x35 */ 267*0c0d06caSMauro Carvalho Chehab 268*0c0d06caSMauro Carvalho Chehab {0x0c, 0x8620}, /* 0c */ 269*0c0d06caSMauro Carvalho Chehab {0xc8, 0x8631}, /* c8 */ 270*0c0d06caSMauro Carvalho Chehab {0xc8, 0x8634}, /* c8 */ 271*0c0d06caSMauro Carvalho Chehab {0x23, 0x8635}, /* 23 */ 272*0c0d06caSMauro Carvalho Chehab {0x1f, 0x8636}, /* 1f */ 273*0c0d06caSMauro Carvalho Chehab {0xdd, 0x8637}, /* dd */ 274*0c0d06caSMauro Carvalho Chehab {0xe1, 0x8638}, /* e1 */ 275*0c0d06caSMauro Carvalho Chehab {0x1d, 0x8639}, /* 1d */ 276*0c0d06caSMauro Carvalho Chehab {0x21, 0x863a}, /* 21 */ 277*0c0d06caSMauro Carvalho Chehab {0xe3, 0x863b}, /* e3 */ 278*0c0d06caSMauro Carvalho Chehab {0xdf, 0x863c}, /* df */ 279*0c0d06caSMauro Carvalho Chehab {0xf0, 0x8505}, 280*0c0d06caSMauro Carvalho Chehab {0x32, 0x850a}, 281*0c0d06caSMauro Carvalho Chehab /* {0x99, 0x8700}, * - white balance - new (removed) */ 282*0c0d06caSMauro Carvalho Chehab /* HDG we used to do this in stop0, making the init state and the state 283*0c0d06caSMauro Carvalho Chehab after a start / stop different, so do this here instead. */ 284*0c0d06caSMauro Carvalho Chehab {0x29, 0x8118}, 285*0c0d06caSMauro Carvalho Chehab {} 286*0c0d06caSMauro Carvalho Chehab }; 287*0c0d06caSMauro Carvalho Chehab 288*0c0d06caSMauro Carvalho Chehab static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) 289*0c0d06caSMauro Carvalho Chehab { 290*0c0d06caSMauro Carvalho Chehab int ret; 291*0c0d06caSMauro Carvalho Chehab 292*0c0d06caSMauro Carvalho Chehab ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 293*0c0d06caSMauro Carvalho Chehab 0, /* request */ 294*0c0d06caSMauro Carvalho Chehab USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 295*0c0d06caSMauro Carvalho Chehab value, index, NULL, 0, 500); 296*0c0d06caSMauro Carvalho Chehab PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value); 297*0c0d06caSMauro Carvalho Chehab if (ret < 0) 298*0c0d06caSMauro Carvalho Chehab pr_err("reg write: error %d\n", ret); 299*0c0d06caSMauro Carvalho Chehab } 300*0c0d06caSMauro Carvalho Chehab 301*0c0d06caSMauro Carvalho Chehab static void write_vector(struct gspca_dev *gspca_dev, 302*0c0d06caSMauro Carvalho Chehab const __u16 data[][2]) 303*0c0d06caSMauro Carvalho Chehab { 304*0c0d06caSMauro Carvalho Chehab struct usb_device *dev = gspca_dev->dev; 305*0c0d06caSMauro Carvalho Chehab int i; 306*0c0d06caSMauro Carvalho Chehab 307*0c0d06caSMauro Carvalho Chehab i = 0; 308*0c0d06caSMauro Carvalho Chehab while (data[i][1] != 0) { 309*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, data[i][1], data[i][0]); 310*0c0d06caSMauro Carvalho Chehab i++; 311*0c0d06caSMauro Carvalho Chehab } 312*0c0d06caSMauro Carvalho Chehab } 313*0c0d06caSMauro Carvalho Chehab 314*0c0d06caSMauro Carvalho Chehab /* read 'len' bytes to gspca_dev->usb_buf */ 315*0c0d06caSMauro Carvalho Chehab static void reg_r(struct gspca_dev *gspca_dev, 316*0c0d06caSMauro Carvalho Chehab __u16 index, __u16 length) 317*0c0d06caSMauro Carvalho Chehab { 318*0c0d06caSMauro Carvalho Chehab usb_control_msg(gspca_dev->dev, 319*0c0d06caSMauro Carvalho Chehab usb_rcvctrlpipe(gspca_dev->dev, 0), 320*0c0d06caSMauro Carvalho Chehab 0, /* request */ 321*0c0d06caSMauro Carvalho Chehab USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 322*0c0d06caSMauro Carvalho Chehab 0, /* value */ 323*0c0d06caSMauro Carvalho Chehab index, gspca_dev->usb_buf, length, 500); 324*0c0d06caSMauro Carvalho Chehab } 325*0c0d06caSMauro Carvalho Chehab 326*0c0d06caSMauro Carvalho Chehab /* write 'len' bytes from gspca_dev->usb_buf */ 327*0c0d06caSMauro Carvalho Chehab static void reg_w_buf(struct gspca_dev *gspca_dev, 328*0c0d06caSMauro Carvalho Chehab __u16 index, __u16 len) 329*0c0d06caSMauro Carvalho Chehab { 330*0c0d06caSMauro Carvalho Chehab usb_control_msg(gspca_dev->dev, 331*0c0d06caSMauro Carvalho Chehab usb_sndctrlpipe(gspca_dev->dev, 0), 332*0c0d06caSMauro Carvalho Chehab 0, /* request */ 333*0c0d06caSMauro Carvalho Chehab USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 334*0c0d06caSMauro Carvalho Chehab 0, /* value */ 335*0c0d06caSMauro Carvalho Chehab index, gspca_dev->usb_buf, len, 500); 336*0c0d06caSMauro Carvalho Chehab } 337*0c0d06caSMauro Carvalho Chehab 338*0c0d06caSMauro Carvalho Chehab static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg) 339*0c0d06caSMauro Carvalho Chehab { 340*0c0d06caSMauro Carvalho Chehab int retry = 60; 341*0c0d06caSMauro Carvalho Chehab 342*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8801, reg); 343*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8805, value); 344*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8800, value >> 8); 345*0c0d06caSMauro Carvalho Chehab do { 346*0c0d06caSMauro Carvalho Chehab reg_r(gspca_dev, 0x8803, 1); 347*0c0d06caSMauro Carvalho Chehab if (!gspca_dev->usb_buf[0]) 348*0c0d06caSMauro Carvalho Chehab return; 349*0c0d06caSMauro Carvalho Chehab msleep(10); 350*0c0d06caSMauro Carvalho Chehab } while (--retry); 351*0c0d06caSMauro Carvalho Chehab } 352*0c0d06caSMauro Carvalho Chehab 353*0c0d06caSMauro Carvalho Chehab static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) 354*0c0d06caSMauro Carvalho Chehab { 355*0c0d06caSMauro Carvalho Chehab int retry = 60; 356*0c0d06caSMauro Carvalho Chehab __u8 value; 357*0c0d06caSMauro Carvalho Chehab 358*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8804, 0x92); 359*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8801, reg); 360*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8802, mode | 0x01); 361*0c0d06caSMauro Carvalho Chehab do { 362*0c0d06caSMauro Carvalho Chehab reg_r(gspca_dev, 0x8803, 1); 363*0c0d06caSMauro Carvalho Chehab if (!gspca_dev->usb_buf[0]) { 364*0c0d06caSMauro Carvalho Chehab reg_r(gspca_dev, 0x8800, 1); 365*0c0d06caSMauro Carvalho Chehab value = gspca_dev->usb_buf[0]; 366*0c0d06caSMauro Carvalho Chehab reg_r(gspca_dev, 0x8805, 1); 367*0c0d06caSMauro Carvalho Chehab return ((int) value << 8) | gspca_dev->usb_buf[0]; 368*0c0d06caSMauro Carvalho Chehab } 369*0c0d06caSMauro Carvalho Chehab msleep(10); 370*0c0d06caSMauro Carvalho Chehab } while (--retry); 371*0c0d06caSMauro Carvalho Chehab return -1; 372*0c0d06caSMauro Carvalho Chehab } 373*0c0d06caSMauro Carvalho Chehab 374*0c0d06caSMauro Carvalho Chehab static void sensor_mapwrite(struct gspca_dev *gspca_dev, 375*0c0d06caSMauro Carvalho Chehab const __u16 (*sensormap)[2]) 376*0c0d06caSMauro Carvalho Chehab { 377*0c0d06caSMauro Carvalho Chehab while ((*sensormap)[0]) { 378*0c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[0] = (*sensormap)[1]; 379*0c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8; 380*0c0d06caSMauro Carvalho Chehab reg_w_buf(gspca_dev, (*sensormap)[0], 2); 381*0c0d06caSMauro Carvalho Chehab sensormap++; 382*0c0d06caSMauro Carvalho Chehab } 383*0c0d06caSMauro Carvalho Chehab } 384*0c0d06caSMauro Carvalho Chehab 385*0c0d06caSMauro Carvalho Chehab static void write_sensor_72a(struct gspca_dev *gspca_dev, 386*0c0d06caSMauro Carvalho Chehab const __u16 (*sensor)[2]) 387*0c0d06caSMauro Carvalho Chehab { 388*0c0d06caSMauro Carvalho Chehab while ((*sensor)[0]) { 389*0c0d06caSMauro Carvalho Chehab i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]); 390*0c0d06caSMauro Carvalho Chehab sensor++; 391*0c0d06caSMauro Carvalho Chehab } 392*0c0d06caSMauro Carvalho Chehab } 393*0c0d06caSMauro Carvalho Chehab 394*0c0d06caSMauro Carvalho Chehab static void init_161rev12A(struct gspca_dev *gspca_dev) 395*0c0d06caSMauro Carvalho Chehab { 396*0c0d06caSMauro Carvalho Chehab write_vector(gspca_dev, spca561_161rev12A_data1); 397*0c0d06caSMauro Carvalho Chehab sensor_mapwrite(gspca_dev, Pb100_1map8300); 398*0c0d06caSMauro Carvalho Chehab /*fixme: should be in sd_start*/ 399*0c0d06caSMauro Carvalho Chehab write_vector(gspca_dev, spca561_161rev12A_data2); 400*0c0d06caSMauro Carvalho Chehab sensor_mapwrite(gspca_dev, Pb100_2map8300); 401*0c0d06caSMauro Carvalho Chehab } 402*0c0d06caSMauro Carvalho Chehab 403*0c0d06caSMauro Carvalho Chehab /* this function is called at probe time */ 404*0c0d06caSMauro Carvalho Chehab static int sd_config(struct gspca_dev *gspca_dev, 405*0c0d06caSMauro Carvalho Chehab const struct usb_device_id *id) 406*0c0d06caSMauro Carvalho Chehab { 407*0c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 408*0c0d06caSMauro Carvalho Chehab struct cam *cam; 409*0c0d06caSMauro Carvalho Chehab __u16 vendor, product; 410*0c0d06caSMauro Carvalho Chehab __u8 data1, data2; 411*0c0d06caSMauro Carvalho Chehab 412*0c0d06caSMauro Carvalho Chehab /* Read frm global register the USB product and vendor IDs, just to 413*0c0d06caSMauro Carvalho Chehab * prove that we can communicate with the device. This works, which 414*0c0d06caSMauro Carvalho Chehab * confirms at we are communicating properly and that the device 415*0c0d06caSMauro Carvalho Chehab * is a 561. */ 416*0c0d06caSMauro Carvalho Chehab reg_r(gspca_dev, 0x8104, 1); 417*0c0d06caSMauro Carvalho Chehab data1 = gspca_dev->usb_buf[0]; 418*0c0d06caSMauro Carvalho Chehab reg_r(gspca_dev, 0x8105, 1); 419*0c0d06caSMauro Carvalho Chehab data2 = gspca_dev->usb_buf[0]; 420*0c0d06caSMauro Carvalho Chehab vendor = (data2 << 8) | data1; 421*0c0d06caSMauro Carvalho Chehab reg_r(gspca_dev, 0x8106, 1); 422*0c0d06caSMauro Carvalho Chehab data1 = gspca_dev->usb_buf[0]; 423*0c0d06caSMauro Carvalho Chehab reg_r(gspca_dev, 0x8107, 1); 424*0c0d06caSMauro Carvalho Chehab data2 = gspca_dev->usb_buf[0]; 425*0c0d06caSMauro Carvalho Chehab product = (data2 << 8) | data1; 426*0c0d06caSMauro Carvalho Chehab if (vendor != id->idVendor || product != id->idProduct) { 427*0c0d06caSMauro Carvalho Chehab PDEBUG(D_PROBE, "Bad vendor / product from device"); 428*0c0d06caSMauro Carvalho Chehab return -EINVAL; 429*0c0d06caSMauro Carvalho Chehab } 430*0c0d06caSMauro Carvalho Chehab 431*0c0d06caSMauro Carvalho Chehab cam = &gspca_dev->cam; 432*0c0d06caSMauro Carvalho Chehab cam->needs_full_bandwidth = 1; 433*0c0d06caSMauro Carvalho Chehab 434*0c0d06caSMauro Carvalho Chehab sd->chip_revision = id->driver_info; 435*0c0d06caSMauro Carvalho Chehab if (sd->chip_revision == Rev012A) { 436*0c0d06caSMauro Carvalho Chehab cam->cam_mode = sif_012a_mode; 437*0c0d06caSMauro Carvalho Chehab cam->nmodes = ARRAY_SIZE(sif_012a_mode); 438*0c0d06caSMauro Carvalho Chehab } else { 439*0c0d06caSMauro Carvalho Chehab cam->cam_mode = sif_072a_mode; 440*0c0d06caSMauro Carvalho Chehab cam->nmodes = ARRAY_SIZE(sif_072a_mode); 441*0c0d06caSMauro Carvalho Chehab } 442*0c0d06caSMauro Carvalho Chehab sd->expo12a = EXPO12A_DEF; 443*0c0d06caSMauro Carvalho Chehab return 0; 444*0c0d06caSMauro Carvalho Chehab } 445*0c0d06caSMauro Carvalho Chehab 446*0c0d06caSMauro Carvalho Chehab /* this function is called at probe and resume time */ 447*0c0d06caSMauro Carvalho Chehab static int sd_init_12a(struct gspca_dev *gspca_dev) 448*0c0d06caSMauro Carvalho Chehab { 449*0c0d06caSMauro Carvalho Chehab PDEBUG(D_STREAM, "Chip revision: 012a"); 450*0c0d06caSMauro Carvalho Chehab init_161rev12A(gspca_dev); 451*0c0d06caSMauro Carvalho Chehab return 0; 452*0c0d06caSMauro Carvalho Chehab } 453*0c0d06caSMauro Carvalho Chehab static int sd_init_72a(struct gspca_dev *gspca_dev) 454*0c0d06caSMauro Carvalho Chehab { 455*0c0d06caSMauro Carvalho Chehab PDEBUG(D_STREAM, "Chip revision: 072a"); 456*0c0d06caSMauro Carvalho Chehab write_vector(gspca_dev, rev72a_reset); 457*0c0d06caSMauro Carvalho Chehab msleep(200); 458*0c0d06caSMauro Carvalho Chehab write_vector(gspca_dev, rev72a_init_data1); 459*0c0d06caSMauro Carvalho Chehab write_sensor_72a(gspca_dev, rev72a_init_sensor1); 460*0c0d06caSMauro Carvalho Chehab write_vector(gspca_dev, rev72a_init_data2); 461*0c0d06caSMauro Carvalho Chehab write_sensor_72a(gspca_dev, rev72a_init_sensor2); 462*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8112, 0x30); 463*0c0d06caSMauro Carvalho Chehab return 0; 464*0c0d06caSMauro Carvalho Chehab } 465*0c0d06caSMauro Carvalho Chehab 466*0c0d06caSMauro Carvalho Chehab static void setbrightness(struct gspca_dev *gspca_dev, s32 val) 467*0c0d06caSMauro Carvalho Chehab { 468*0c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 469*0c0d06caSMauro Carvalho Chehab struct usb_device *dev = gspca_dev->dev; 470*0c0d06caSMauro Carvalho Chehab __u16 reg; 471*0c0d06caSMauro Carvalho Chehab 472*0c0d06caSMauro Carvalho Chehab if (sd->chip_revision == Rev012A) 473*0c0d06caSMauro Carvalho Chehab reg = 0x8610; 474*0c0d06caSMauro Carvalho Chehab else 475*0c0d06caSMauro Carvalho Chehab reg = 0x8611; 476*0c0d06caSMauro Carvalho Chehab 477*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, reg + 0, val); /* R */ 478*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, reg + 1, val); /* Gr */ 479*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, reg + 2, val); /* B */ 480*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, reg + 3, val); /* Gb */ 481*0c0d06caSMauro Carvalho Chehab } 482*0c0d06caSMauro Carvalho Chehab 483*0c0d06caSMauro Carvalho Chehab static void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast) 484*0c0d06caSMauro Carvalho Chehab { 485*0c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 486*0c0d06caSMauro Carvalho Chehab struct usb_device *dev = gspca_dev->dev; 487*0c0d06caSMauro Carvalho Chehab __u8 blue, red; 488*0c0d06caSMauro Carvalho Chehab __u16 reg; 489*0c0d06caSMauro Carvalho Chehab 490*0c0d06caSMauro Carvalho Chehab /* try to emulate MS-win as possible */ 491*0c0d06caSMauro Carvalho Chehab red = 0x20 + white * 3 / 8; 492*0c0d06caSMauro Carvalho Chehab blue = 0x90 - white * 5 / 8; 493*0c0d06caSMauro Carvalho Chehab if (sd->chip_revision == Rev012A) { 494*0c0d06caSMauro Carvalho Chehab reg = 0x8614; 495*0c0d06caSMauro Carvalho Chehab } else { 496*0c0d06caSMauro Carvalho Chehab reg = 0x8651; 497*0c0d06caSMauro Carvalho Chehab red += contrast - 0x20; 498*0c0d06caSMauro Carvalho Chehab blue += contrast - 0x20; 499*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, 0x8652, contrast + 0x20); /* Gr */ 500*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, 0x8654, contrast + 0x20); /* Gb */ 501*0c0d06caSMauro Carvalho Chehab } 502*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, reg, red); 503*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, reg + 2, blue); 504*0c0d06caSMauro Carvalho Chehab } 505*0c0d06caSMauro Carvalho Chehab 506*0c0d06caSMauro Carvalho Chehab /* rev 12a only */ 507*0c0d06caSMauro Carvalho Chehab static void setexposure(struct gspca_dev *gspca_dev, s32 val) 508*0c0d06caSMauro Carvalho Chehab { 509*0c0d06caSMauro Carvalho Chehab int i, expo = 0; 510*0c0d06caSMauro Carvalho Chehab 511*0c0d06caSMauro Carvalho Chehab /* Register 0x8309 controls exposure for the spca561, 512*0c0d06caSMauro Carvalho Chehab the basic exposure setting goes from 1-2047, where 1 is completely 513*0c0d06caSMauro Carvalho Chehab dark and 2047 is very bright. It not only influences exposure but 514*0c0d06caSMauro Carvalho Chehab also the framerate (to allow for longer exposure) from 1 - 300 it 515*0c0d06caSMauro Carvalho Chehab only raises the exposure time then from 300 - 600 it halves the 516*0c0d06caSMauro Carvalho Chehab framerate to be able to further raise the exposure time and for every 517*0c0d06caSMauro Carvalho Chehab 300 more it halves the framerate again. This allows for a maximum 518*0c0d06caSMauro Carvalho Chehab exposure time of circa 0.2 - 0.25 seconds (30 / (2000/3000) fps). 519*0c0d06caSMauro Carvalho Chehab Sometimes this is not enough, the 1-2047 uses bits 0-10, bits 11-12 520*0c0d06caSMauro Carvalho Chehab configure a divider for the base framerate which us used at the 521*0c0d06caSMauro Carvalho Chehab exposure setting of 1-300. These bits configure the base framerate 522*0c0d06caSMauro Carvalho Chehab according to the following formula: fps = 60 / (value + 2) */ 523*0c0d06caSMauro Carvalho Chehab 524*0c0d06caSMauro Carvalho Chehab /* We choose to use the high bits setting the fixed framerate divisor 525*0c0d06caSMauro Carvalho Chehab asap, as setting high basic exposure setting without the fixed 526*0c0d06caSMauro Carvalho Chehab divider in combination with high gains makes the cam stop */ 527*0c0d06caSMauro Carvalho Chehab int table[] = { 0, 450, 550, 625, EXPOSURE_MAX }; 528*0c0d06caSMauro Carvalho Chehab 529*0c0d06caSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(table) - 1; i++) { 530*0c0d06caSMauro Carvalho Chehab if (val <= table[i + 1]) { 531*0c0d06caSMauro Carvalho Chehab expo = val - table[i]; 532*0c0d06caSMauro Carvalho Chehab if (i) 533*0c0d06caSMauro Carvalho Chehab expo += 300; 534*0c0d06caSMauro Carvalho Chehab expo |= i << 11; 535*0c0d06caSMauro Carvalho Chehab break; 536*0c0d06caSMauro Carvalho Chehab } 537*0c0d06caSMauro Carvalho Chehab } 538*0c0d06caSMauro Carvalho Chehab 539*0c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[0] = expo; 540*0c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[1] = expo >> 8; 541*0c0d06caSMauro Carvalho Chehab reg_w_buf(gspca_dev, 0x8309, 2); 542*0c0d06caSMauro Carvalho Chehab } 543*0c0d06caSMauro Carvalho Chehab 544*0c0d06caSMauro Carvalho Chehab /* rev 12a only */ 545*0c0d06caSMauro Carvalho Chehab static void setgain(struct gspca_dev *gspca_dev, s32 val) 546*0c0d06caSMauro Carvalho Chehab { 547*0c0d06caSMauro Carvalho Chehab /* gain reg low 6 bits 0-63 gain, bit 6 and 7, both double the 548*0c0d06caSMauro Carvalho Chehab sensitivity when set, so 31 + one of them set == 63, and 15 549*0c0d06caSMauro Carvalho Chehab with both of them set == 63 */ 550*0c0d06caSMauro Carvalho Chehab if (val < 64) 551*0c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[0] = val; 552*0c0d06caSMauro Carvalho Chehab else if (val < 128) 553*0c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[0] = (val / 2) | 0x40; 554*0c0d06caSMauro Carvalho Chehab else 555*0c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[0] = (val / 4) | 0xc0; 556*0c0d06caSMauro Carvalho Chehab 557*0c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[1] = 0; 558*0c0d06caSMauro Carvalho Chehab reg_w_buf(gspca_dev, 0x8335, 2); 559*0c0d06caSMauro Carvalho Chehab } 560*0c0d06caSMauro Carvalho Chehab 561*0c0d06caSMauro Carvalho Chehab static void setautogain(struct gspca_dev *gspca_dev, s32 val) 562*0c0d06caSMauro Carvalho Chehab { 563*0c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 564*0c0d06caSMauro Carvalho Chehab 565*0c0d06caSMauro Carvalho Chehab if (val) 566*0c0d06caSMauro Carvalho Chehab sd->ag_cnt = AG_CNT_START; 567*0c0d06caSMauro Carvalho Chehab else 568*0c0d06caSMauro Carvalho Chehab sd->ag_cnt = -1; 569*0c0d06caSMauro Carvalho Chehab } 570*0c0d06caSMauro Carvalho Chehab 571*0c0d06caSMauro Carvalho Chehab static int sd_start_12a(struct gspca_dev *gspca_dev) 572*0c0d06caSMauro Carvalho Chehab { 573*0c0d06caSMauro Carvalho Chehab struct usb_device *dev = gspca_dev->dev; 574*0c0d06caSMauro Carvalho Chehab int mode; 575*0c0d06caSMauro Carvalho Chehab static const __u8 Reg8391[8] = 576*0c0d06caSMauro Carvalho Chehab {0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00}; 577*0c0d06caSMauro Carvalho Chehab 578*0c0d06caSMauro Carvalho Chehab mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; 579*0c0d06caSMauro Carvalho Chehab if (mode <= 1) { 580*0c0d06caSMauro Carvalho Chehab /* Use compression on 320x240 and above */ 581*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, 0x8500, 0x10 | mode); 582*0c0d06caSMauro Carvalho Chehab } else { 583*0c0d06caSMauro Carvalho Chehab /* I couldn't get the compression to work below 320x240 584*0c0d06caSMauro Carvalho Chehab * Fortunately at these resolutions the bandwidth 585*0c0d06caSMauro Carvalho Chehab * is sufficient to push raw frames at ~20fps */ 586*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, 0x8500, mode); 587*0c0d06caSMauro Carvalho Chehab } /* -- qq@kuku.eu.org */ 588*0c0d06caSMauro Carvalho Chehab 589*0c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[0] = 0xaa; 590*0c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[1] = 0x00; 591*0c0d06caSMauro Carvalho Chehab reg_w_buf(gspca_dev, 0x8307, 2); 592*0c0d06caSMauro Carvalho Chehab /* clock - lower 0x8X values lead to fps > 30 */ 593*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8700, 0x8a); 594*0c0d06caSMauro Carvalho Chehab /* 0x8f 0x85 0x27 clock */ 595*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20); 596*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x850b, 0x03); 597*0c0d06caSMauro Carvalho Chehab memcpy(gspca_dev->usb_buf, Reg8391, 8); 598*0c0d06caSMauro Carvalho Chehab reg_w_buf(gspca_dev, 0x8391, 8); 599*0c0d06caSMauro Carvalho Chehab reg_w_buf(gspca_dev, 0x8390, 8); 600*0c0d06caSMauro Carvalho Chehab 601*0c0d06caSMauro Carvalho Chehab /* Led ON (bit 3 -> 0 */ 602*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8114, 0x00); 603*0c0d06caSMauro Carvalho Chehab return 0; 604*0c0d06caSMauro Carvalho Chehab } 605*0c0d06caSMauro Carvalho Chehab static int sd_start_72a(struct gspca_dev *gspca_dev) 606*0c0d06caSMauro Carvalho Chehab { 607*0c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 608*0c0d06caSMauro Carvalho Chehab struct usb_device *dev = gspca_dev->dev; 609*0c0d06caSMauro Carvalho Chehab int Clck; 610*0c0d06caSMauro Carvalho Chehab int mode; 611*0c0d06caSMauro Carvalho Chehab 612*0c0d06caSMauro Carvalho Chehab write_vector(gspca_dev, rev72a_reset); 613*0c0d06caSMauro Carvalho Chehab msleep(200); 614*0c0d06caSMauro Carvalho Chehab write_vector(gspca_dev, rev72a_init_data1); 615*0c0d06caSMauro Carvalho Chehab write_sensor_72a(gspca_dev, rev72a_init_sensor1); 616*0c0d06caSMauro Carvalho Chehab 617*0c0d06caSMauro Carvalho Chehab mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; 618*0c0d06caSMauro Carvalho Chehab switch (mode) { 619*0c0d06caSMauro Carvalho Chehab default: 620*0c0d06caSMauro Carvalho Chehab case 0: 621*0c0d06caSMauro Carvalho Chehab Clck = 0x27; /* ms-win 0x87 */ 622*0c0d06caSMauro Carvalho Chehab break; 623*0c0d06caSMauro Carvalho Chehab case 1: 624*0c0d06caSMauro Carvalho Chehab Clck = 0x25; 625*0c0d06caSMauro Carvalho Chehab break; 626*0c0d06caSMauro Carvalho Chehab case 2: 627*0c0d06caSMauro Carvalho Chehab Clck = 0x22; 628*0c0d06caSMauro Carvalho Chehab break; 629*0c0d06caSMauro Carvalho Chehab case 3: 630*0c0d06caSMauro Carvalho Chehab Clck = 0x21; 631*0c0d06caSMauro Carvalho Chehab break; 632*0c0d06caSMauro Carvalho Chehab } 633*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ 634*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, 0x8702, 0x81); 635*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, 0x8500, mode); /* mode */ 636*0c0d06caSMauro Carvalho Chehab write_sensor_72a(gspca_dev, rev72a_init_sensor2); 637*0c0d06caSMauro Carvalho Chehab setwhite(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue), 638*0c0d06caSMauro Carvalho Chehab v4l2_ctrl_g_ctrl(sd->contrast)); 639*0c0d06caSMauro Carvalho Chehab /* setbrightness(gspca_dev); * fixme: bad values */ 640*0c0d06caSMauro Carvalho Chehab setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); 641*0c0d06caSMauro Carvalho Chehab reg_w_val(dev, 0x8112, 0x10 | 0x20); 642*0c0d06caSMauro Carvalho Chehab return 0; 643*0c0d06caSMauro Carvalho Chehab } 644*0c0d06caSMauro Carvalho Chehab 645*0c0d06caSMauro Carvalho Chehab static void sd_stopN(struct gspca_dev *gspca_dev) 646*0c0d06caSMauro Carvalho Chehab { 647*0c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 648*0c0d06caSMauro Carvalho Chehab 649*0c0d06caSMauro Carvalho Chehab if (sd->chip_revision == Rev012A) { 650*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8112, 0x0e); 651*0c0d06caSMauro Carvalho Chehab /* Led Off (bit 3 -> 1 */ 652*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8114, 0x08); 653*0c0d06caSMauro Carvalho Chehab } else { 654*0c0d06caSMauro Carvalho Chehab reg_w_val(gspca_dev->dev, 0x8112, 0x20); 655*0c0d06caSMauro Carvalho Chehab /* reg_w_val(gspca_dev->dev, 0x8102, 0x00); ?? */ 656*0c0d06caSMauro Carvalho Chehab } 657*0c0d06caSMauro Carvalho Chehab } 658*0c0d06caSMauro Carvalho Chehab 659*0c0d06caSMauro Carvalho Chehab static void do_autogain(struct gspca_dev *gspca_dev) 660*0c0d06caSMauro Carvalho Chehab { 661*0c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 662*0c0d06caSMauro Carvalho Chehab int expotimes; 663*0c0d06caSMauro Carvalho Chehab int pixelclk; 664*0c0d06caSMauro Carvalho Chehab int gainG; 665*0c0d06caSMauro Carvalho Chehab __u8 R, Gr, Gb, B; 666*0c0d06caSMauro Carvalho Chehab int y; 667*0c0d06caSMauro Carvalho Chehab __u8 luma_mean = 110; 668*0c0d06caSMauro Carvalho Chehab __u8 luma_delta = 20; 669*0c0d06caSMauro Carvalho Chehab __u8 spring = 4; 670*0c0d06caSMauro Carvalho Chehab 671*0c0d06caSMauro Carvalho Chehab if (sd->ag_cnt < 0) 672*0c0d06caSMauro Carvalho Chehab return; 673*0c0d06caSMauro Carvalho Chehab if (--sd->ag_cnt >= 0) 674*0c0d06caSMauro Carvalho Chehab return; 675*0c0d06caSMauro Carvalho Chehab sd->ag_cnt = AG_CNT_START; 676*0c0d06caSMauro Carvalho Chehab 677*0c0d06caSMauro Carvalho Chehab switch (sd->chip_revision) { 678*0c0d06caSMauro Carvalho Chehab case Rev072A: 679*0c0d06caSMauro Carvalho Chehab reg_r(gspca_dev, 0x8621, 1); 680*0c0d06caSMauro Carvalho Chehab Gr = gspca_dev->usb_buf[0]; 681*0c0d06caSMauro Carvalho Chehab reg_r(gspca_dev, 0x8622, 1); 682*0c0d06caSMauro Carvalho Chehab R = gspca_dev->usb_buf[0]; 683*0c0d06caSMauro Carvalho Chehab reg_r(gspca_dev, 0x8623, 1); 684*0c0d06caSMauro Carvalho Chehab B = gspca_dev->usb_buf[0]; 685*0c0d06caSMauro Carvalho Chehab reg_r(gspca_dev, 0x8624, 1); 686*0c0d06caSMauro Carvalho Chehab Gb = gspca_dev->usb_buf[0]; 687*0c0d06caSMauro Carvalho Chehab y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8; 688*0c0d06caSMauro Carvalho Chehab /* u= (128*B-(43*(Gr+Gb+R))) >> 8; */ 689*0c0d06caSMauro Carvalho Chehab /* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */ 690*0c0d06caSMauro Carvalho Chehab /* PDEBUG(D_CONF,"reading Y %d U %d V %d ",y,u,v); */ 691*0c0d06caSMauro Carvalho Chehab 692*0c0d06caSMauro Carvalho Chehab if (y < luma_mean - luma_delta || 693*0c0d06caSMauro Carvalho Chehab y > luma_mean + luma_delta) { 694*0c0d06caSMauro Carvalho Chehab expotimes = i2c_read(gspca_dev, 0x09, 0x10); 695*0c0d06caSMauro Carvalho Chehab pixelclk = 0x0800; 696*0c0d06caSMauro Carvalho Chehab expotimes = expotimes & 0x07ff; 697*0c0d06caSMauro Carvalho Chehab /* PDEBUG(D_PACK, 698*0c0d06caSMauro Carvalho Chehab "Exposition Times 0x%03X Clock 0x%04X ", 699*0c0d06caSMauro Carvalho Chehab expotimes,pixelclk); */ 700*0c0d06caSMauro Carvalho Chehab gainG = i2c_read(gspca_dev, 0x35, 0x10); 701*0c0d06caSMauro Carvalho Chehab /* PDEBUG(D_PACK, 702*0c0d06caSMauro Carvalho Chehab "reading Gain register %d", gainG); */ 703*0c0d06caSMauro Carvalho Chehab 704*0c0d06caSMauro Carvalho Chehab expotimes += (luma_mean - y) >> spring; 705*0c0d06caSMauro Carvalho Chehab gainG += (luma_mean - y) / 50; 706*0c0d06caSMauro Carvalho Chehab /* PDEBUG(D_PACK, 707*0c0d06caSMauro Carvalho Chehab "compute expotimes %d gain %d", 708*0c0d06caSMauro Carvalho Chehab expotimes,gainG); */ 709*0c0d06caSMauro Carvalho Chehab 710*0c0d06caSMauro Carvalho Chehab if (gainG > 0x3f) 711*0c0d06caSMauro Carvalho Chehab gainG = 0x3f; 712*0c0d06caSMauro Carvalho Chehab else if (gainG < 3) 713*0c0d06caSMauro Carvalho Chehab gainG = 3; 714*0c0d06caSMauro Carvalho Chehab i2c_write(gspca_dev, gainG, 0x35); 715*0c0d06caSMauro Carvalho Chehab 716*0c0d06caSMauro Carvalho Chehab if (expotimes > 0x0256) 717*0c0d06caSMauro Carvalho Chehab expotimes = 0x0256; 718*0c0d06caSMauro Carvalho Chehab else if (expotimes < 3) 719*0c0d06caSMauro Carvalho Chehab expotimes = 3; 720*0c0d06caSMauro Carvalho Chehab i2c_write(gspca_dev, expotimes | pixelclk, 0x09); 721*0c0d06caSMauro Carvalho Chehab } 722*0c0d06caSMauro Carvalho Chehab break; 723*0c0d06caSMauro Carvalho Chehab } 724*0c0d06caSMauro Carvalho Chehab } 725*0c0d06caSMauro Carvalho Chehab 726*0c0d06caSMauro Carvalho Chehab static void sd_pkt_scan(struct gspca_dev *gspca_dev, 727*0c0d06caSMauro Carvalho Chehab u8 *data, /* isoc packet */ 728*0c0d06caSMauro Carvalho Chehab int len) /* iso packet length */ 729*0c0d06caSMauro Carvalho Chehab { 730*0c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 731*0c0d06caSMauro Carvalho Chehab 732*0c0d06caSMauro Carvalho Chehab len--; 733*0c0d06caSMauro Carvalho Chehab switch (*data++) { /* sequence number */ 734*0c0d06caSMauro Carvalho Chehab case 0: /* start of frame */ 735*0c0d06caSMauro Carvalho Chehab gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); 736*0c0d06caSMauro Carvalho Chehab 737*0c0d06caSMauro Carvalho Chehab /* This should never happen */ 738*0c0d06caSMauro Carvalho Chehab if (len < 2) { 739*0c0d06caSMauro Carvalho Chehab PDEBUG(D_ERR, "Short SOF packet, ignoring"); 740*0c0d06caSMauro Carvalho Chehab gspca_dev->last_packet_type = DISCARD_PACKET; 741*0c0d06caSMauro Carvalho Chehab return; 742*0c0d06caSMauro Carvalho Chehab } 743*0c0d06caSMauro Carvalho Chehab 744*0c0d06caSMauro Carvalho Chehab #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) 745*0c0d06caSMauro Carvalho Chehab if (data[0] & 0x20) { 746*0c0d06caSMauro Carvalho Chehab input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); 747*0c0d06caSMauro Carvalho Chehab input_sync(gspca_dev->input_dev); 748*0c0d06caSMauro Carvalho Chehab input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); 749*0c0d06caSMauro Carvalho Chehab input_sync(gspca_dev->input_dev); 750*0c0d06caSMauro Carvalho Chehab } 751*0c0d06caSMauro Carvalho Chehab #endif 752*0c0d06caSMauro Carvalho Chehab 753*0c0d06caSMauro Carvalho Chehab if (data[1] & 0x10) { 754*0c0d06caSMauro Carvalho Chehab /* compressed bayer */ 755*0c0d06caSMauro Carvalho Chehab gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); 756*0c0d06caSMauro Carvalho Chehab } else { 757*0c0d06caSMauro Carvalho Chehab /* raw bayer (with a header, which we skip) */ 758*0c0d06caSMauro Carvalho Chehab if (sd->chip_revision == Rev012A) { 759*0c0d06caSMauro Carvalho Chehab data += 20; 760*0c0d06caSMauro Carvalho Chehab len -= 20; 761*0c0d06caSMauro Carvalho Chehab } else { 762*0c0d06caSMauro Carvalho Chehab data += 16; 763*0c0d06caSMauro Carvalho Chehab len -= 16; 764*0c0d06caSMauro Carvalho Chehab } 765*0c0d06caSMauro Carvalho Chehab gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); 766*0c0d06caSMauro Carvalho Chehab } 767*0c0d06caSMauro Carvalho Chehab return; 768*0c0d06caSMauro Carvalho Chehab case 0xff: /* drop (empty mpackets) */ 769*0c0d06caSMauro Carvalho Chehab return; 770*0c0d06caSMauro Carvalho Chehab } 771*0c0d06caSMauro Carvalho Chehab gspca_frame_add(gspca_dev, INTER_PACKET, data, len); 772*0c0d06caSMauro Carvalho Chehab } 773*0c0d06caSMauro Carvalho Chehab 774*0c0d06caSMauro Carvalho Chehab static int sd_s_ctrl(struct v4l2_ctrl *ctrl) 775*0c0d06caSMauro Carvalho Chehab { 776*0c0d06caSMauro Carvalho Chehab struct gspca_dev *gspca_dev = 777*0c0d06caSMauro Carvalho Chehab container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 778*0c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *)gspca_dev; 779*0c0d06caSMauro Carvalho Chehab 780*0c0d06caSMauro Carvalho Chehab gspca_dev->usb_err = 0; 781*0c0d06caSMauro Carvalho Chehab 782*0c0d06caSMauro Carvalho Chehab if (!gspca_dev->streaming) 783*0c0d06caSMauro Carvalho Chehab return 0; 784*0c0d06caSMauro Carvalho Chehab 785*0c0d06caSMauro Carvalho Chehab switch (ctrl->id) { 786*0c0d06caSMauro Carvalho Chehab case V4L2_CID_BRIGHTNESS: 787*0c0d06caSMauro Carvalho Chehab setbrightness(gspca_dev, ctrl->val); 788*0c0d06caSMauro Carvalho Chehab break; 789*0c0d06caSMauro Carvalho Chehab case V4L2_CID_CONTRAST: 790*0c0d06caSMauro Carvalho Chehab /* hue/contrast control cluster for 72a */ 791*0c0d06caSMauro Carvalho Chehab setwhite(gspca_dev, sd->hue->val, ctrl->val); 792*0c0d06caSMauro Carvalho Chehab break; 793*0c0d06caSMauro Carvalho Chehab case V4L2_CID_HUE: 794*0c0d06caSMauro Carvalho Chehab /* just plain hue control for 12a */ 795*0c0d06caSMauro Carvalho Chehab setwhite(gspca_dev, ctrl->val, 0); 796*0c0d06caSMauro Carvalho Chehab break; 797*0c0d06caSMauro Carvalho Chehab case V4L2_CID_EXPOSURE: 798*0c0d06caSMauro Carvalho Chehab setexposure(gspca_dev, ctrl->val); 799*0c0d06caSMauro Carvalho Chehab break; 800*0c0d06caSMauro Carvalho Chehab case V4L2_CID_GAIN: 801*0c0d06caSMauro Carvalho Chehab setgain(gspca_dev, ctrl->val); 802*0c0d06caSMauro Carvalho Chehab break; 803*0c0d06caSMauro Carvalho Chehab case V4L2_CID_AUTOGAIN: 804*0c0d06caSMauro Carvalho Chehab setautogain(gspca_dev, ctrl->val); 805*0c0d06caSMauro Carvalho Chehab break; 806*0c0d06caSMauro Carvalho Chehab } 807*0c0d06caSMauro Carvalho Chehab return gspca_dev->usb_err; 808*0c0d06caSMauro Carvalho Chehab } 809*0c0d06caSMauro Carvalho Chehab 810*0c0d06caSMauro Carvalho Chehab static const struct v4l2_ctrl_ops sd_ctrl_ops = { 811*0c0d06caSMauro Carvalho Chehab .s_ctrl = sd_s_ctrl, 812*0c0d06caSMauro Carvalho Chehab }; 813*0c0d06caSMauro Carvalho Chehab 814*0c0d06caSMauro Carvalho Chehab static int sd_init_controls_12a(struct gspca_dev *gspca_dev) 815*0c0d06caSMauro Carvalho Chehab { 816*0c0d06caSMauro Carvalho Chehab struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 817*0c0d06caSMauro Carvalho Chehab 818*0c0d06caSMauro Carvalho Chehab gspca_dev->vdev.ctrl_handler = hdl; 819*0c0d06caSMauro Carvalho Chehab v4l2_ctrl_handler_init(hdl, 3); 820*0c0d06caSMauro Carvalho Chehab v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 821*0c0d06caSMauro Carvalho Chehab V4L2_CID_HUE, 1, 0x7f, 1, 0x40); 822*0c0d06caSMauro Carvalho Chehab v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 823*0c0d06caSMauro Carvalho Chehab V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); 824*0c0d06caSMauro Carvalho Chehab v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 825*0c0d06caSMauro Carvalho Chehab V4L2_CID_EXPOSURE, 1, EXPOSURE_MAX, 1, 700); 826*0c0d06caSMauro Carvalho Chehab v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 827*0c0d06caSMauro Carvalho Chehab V4L2_CID_GAIN, 0, 255, 1, 63); 828*0c0d06caSMauro Carvalho Chehab 829*0c0d06caSMauro Carvalho Chehab if (hdl->error) { 830*0c0d06caSMauro Carvalho Chehab pr_err("Could not initialize controls\n"); 831*0c0d06caSMauro Carvalho Chehab return hdl->error; 832*0c0d06caSMauro Carvalho Chehab } 833*0c0d06caSMauro Carvalho Chehab return 0; 834*0c0d06caSMauro Carvalho Chehab } 835*0c0d06caSMauro Carvalho Chehab 836*0c0d06caSMauro Carvalho Chehab static int sd_init_controls_72a(struct gspca_dev *gspca_dev) 837*0c0d06caSMauro Carvalho Chehab { 838*0c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *)gspca_dev; 839*0c0d06caSMauro Carvalho Chehab struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 840*0c0d06caSMauro Carvalho Chehab 841*0c0d06caSMauro Carvalho Chehab gspca_dev->vdev.ctrl_handler = hdl; 842*0c0d06caSMauro Carvalho Chehab v4l2_ctrl_handler_init(hdl, 4); 843*0c0d06caSMauro Carvalho Chehab sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 844*0c0d06caSMauro Carvalho Chehab V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x20); 845*0c0d06caSMauro Carvalho Chehab sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 846*0c0d06caSMauro Carvalho Chehab V4L2_CID_HUE, 1, 0x7f, 1, 0x40); 847*0c0d06caSMauro Carvalho Chehab v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 848*0c0d06caSMauro Carvalho Chehab V4L2_CID_BRIGHTNESS, 0, 0x3f, 1, 0x20); 849*0c0d06caSMauro Carvalho Chehab sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 850*0c0d06caSMauro Carvalho Chehab V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 851*0c0d06caSMauro Carvalho Chehab 852*0c0d06caSMauro Carvalho Chehab if (hdl->error) { 853*0c0d06caSMauro Carvalho Chehab pr_err("Could not initialize controls\n"); 854*0c0d06caSMauro Carvalho Chehab return hdl->error; 855*0c0d06caSMauro Carvalho Chehab } 856*0c0d06caSMauro Carvalho Chehab v4l2_ctrl_cluster(2, &sd->contrast); 857*0c0d06caSMauro Carvalho Chehab return 0; 858*0c0d06caSMauro Carvalho Chehab } 859*0c0d06caSMauro Carvalho Chehab 860*0c0d06caSMauro Carvalho Chehab /* sub-driver description */ 861*0c0d06caSMauro Carvalho Chehab static const struct sd_desc sd_desc_12a = { 862*0c0d06caSMauro Carvalho Chehab .name = MODULE_NAME, 863*0c0d06caSMauro Carvalho Chehab .init_controls = sd_init_controls_12a, 864*0c0d06caSMauro Carvalho Chehab .config = sd_config, 865*0c0d06caSMauro Carvalho Chehab .init = sd_init_12a, 866*0c0d06caSMauro Carvalho Chehab .start = sd_start_12a, 867*0c0d06caSMauro Carvalho Chehab .stopN = sd_stopN, 868*0c0d06caSMauro Carvalho Chehab .pkt_scan = sd_pkt_scan, 869*0c0d06caSMauro Carvalho Chehab #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) 870*0c0d06caSMauro Carvalho Chehab .other_input = 1, 871*0c0d06caSMauro Carvalho Chehab #endif 872*0c0d06caSMauro Carvalho Chehab }; 873*0c0d06caSMauro Carvalho Chehab static const struct sd_desc sd_desc_72a = { 874*0c0d06caSMauro Carvalho Chehab .name = MODULE_NAME, 875*0c0d06caSMauro Carvalho Chehab .init_controls = sd_init_controls_72a, 876*0c0d06caSMauro Carvalho Chehab .config = sd_config, 877*0c0d06caSMauro Carvalho Chehab .init = sd_init_72a, 878*0c0d06caSMauro Carvalho Chehab .start = sd_start_72a, 879*0c0d06caSMauro Carvalho Chehab .stopN = sd_stopN, 880*0c0d06caSMauro Carvalho Chehab .pkt_scan = sd_pkt_scan, 881*0c0d06caSMauro Carvalho Chehab .dq_callback = do_autogain, 882*0c0d06caSMauro Carvalho Chehab #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) 883*0c0d06caSMauro Carvalho Chehab .other_input = 1, 884*0c0d06caSMauro Carvalho Chehab #endif 885*0c0d06caSMauro Carvalho Chehab }; 886*0c0d06caSMauro Carvalho Chehab static const struct sd_desc *sd_desc[2] = { 887*0c0d06caSMauro Carvalho Chehab &sd_desc_12a, 888*0c0d06caSMauro Carvalho Chehab &sd_desc_72a 889*0c0d06caSMauro Carvalho Chehab }; 890*0c0d06caSMauro Carvalho Chehab 891*0c0d06caSMauro Carvalho Chehab /* -- module initialisation -- */ 892*0c0d06caSMauro Carvalho Chehab static const struct usb_device_id device_table[] = { 893*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x041e, 0x401a), .driver_info = Rev072A}, 894*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x041e, 0x403b), .driver_info = Rev012A}, 895*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x0458, 0x7004), .driver_info = Rev072A}, 896*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x0461, 0x0815), .driver_info = Rev072A}, 897*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x046d, 0x0928), .driver_info = Rev012A}, 898*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x046d, 0x0929), .driver_info = Rev012A}, 899*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x046d, 0x092a), .driver_info = Rev012A}, 900*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x046d, 0x092b), .driver_info = Rev012A}, 901*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x046d, 0x092c), .driver_info = Rev012A}, 902*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x046d, 0x092d), .driver_info = Rev012A}, 903*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x046d, 0x092e), .driver_info = Rev012A}, 904*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x046d, 0x092f), .driver_info = Rev012A}, 905*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x04fc, 0x0561), .driver_info = Rev072A}, 906*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x060b, 0xa001), .driver_info = Rev072A}, 907*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x10fd, 0x7e50), .driver_info = Rev072A}, 908*0c0d06caSMauro Carvalho Chehab {USB_DEVICE(0xabcd, 0xcdee), .driver_info = Rev072A}, 909*0c0d06caSMauro Carvalho Chehab {} 910*0c0d06caSMauro Carvalho Chehab }; 911*0c0d06caSMauro Carvalho Chehab 912*0c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, device_table); 913*0c0d06caSMauro Carvalho Chehab 914*0c0d06caSMauro Carvalho Chehab /* -- device connect -- */ 915*0c0d06caSMauro Carvalho Chehab static int sd_probe(struct usb_interface *intf, 916*0c0d06caSMauro Carvalho Chehab const struct usb_device_id *id) 917*0c0d06caSMauro Carvalho Chehab { 918*0c0d06caSMauro Carvalho Chehab return gspca_dev_probe(intf, id, 919*0c0d06caSMauro Carvalho Chehab sd_desc[id->driver_info], 920*0c0d06caSMauro Carvalho Chehab sizeof(struct sd), 921*0c0d06caSMauro Carvalho Chehab THIS_MODULE); 922*0c0d06caSMauro Carvalho Chehab } 923*0c0d06caSMauro Carvalho Chehab 924*0c0d06caSMauro Carvalho Chehab static struct usb_driver sd_driver = { 925*0c0d06caSMauro Carvalho Chehab .name = MODULE_NAME, 926*0c0d06caSMauro Carvalho Chehab .id_table = device_table, 927*0c0d06caSMauro Carvalho Chehab .probe = sd_probe, 928*0c0d06caSMauro Carvalho Chehab .disconnect = gspca_disconnect, 929*0c0d06caSMauro Carvalho Chehab #ifdef CONFIG_PM 930*0c0d06caSMauro Carvalho Chehab .suspend = gspca_suspend, 931*0c0d06caSMauro Carvalho Chehab .resume = gspca_resume, 932*0c0d06caSMauro Carvalho Chehab .reset_resume = gspca_resume, 933*0c0d06caSMauro Carvalho Chehab #endif 934*0c0d06caSMauro Carvalho Chehab }; 935*0c0d06caSMauro Carvalho Chehab 936*0c0d06caSMauro Carvalho Chehab module_usb_driver(sd_driver); 937