1*fd9871f7SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 26a7eba24SJean-Francois Moine /* 36a7eba24SJean-Francois Moine * Sunplus spca561 subdriver 46a7eba24SJean-Francois Moine * 56a7eba24SJean-Francois Moine * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr 66a7eba24SJean-Francois Moine * 76a7eba24SJean-Francois Moine * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> 86a7eba24SJean-Francois Moine */ 96a7eba24SJean-Francois Moine 10133a9fe9SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11133a9fe9SJoe Perches 126a7eba24SJean-Francois Moine #define MODULE_NAME "spca561" 136a7eba24SJean-Francois Moine 14436c2c53SHans de Goede #include <linux/input.h> 156a7eba24SJean-Francois Moine #include "gspca.h" 166a7eba24SJean-Francois Moine 176a7eba24SJean-Francois Moine MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); 186a7eba24SJean-Francois Moine MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver"); 196a7eba24SJean-Francois Moine MODULE_LICENSE("GPL"); 206a7eba24SJean-Francois Moine 213fa24bf5SHans Verkuil #define EXPOSURE_MAX (2047 + 325) 223fa24bf5SHans Verkuil 236a7eba24SJean-Francois Moine /* specific webcam descriptor */ 246a7eba24SJean-Francois Moine struct sd { 256a7eba24SJean-Francois Moine struct gspca_dev gspca_dev; /* !! must be the first item */ 266a7eba24SJean-Francois Moine 273fa24bf5SHans Verkuil struct { /* hue/contrast control cluster */ 283fa24bf5SHans Verkuil struct v4l2_ctrl *contrast; 293fa24bf5SHans Verkuil struct v4l2_ctrl *hue; 303fa24bf5SHans Verkuil }; 313fa24bf5SHans Verkuil struct v4l2_ctrl *autogain; 326a7eba24SJean-Francois Moine 33d698dc6bSJean-Francois Moine #define EXPO12A_DEF 3 34d698dc6bSJean-Francois Moine __u8 expo12a; /* expo/gain? for rev 12a */ 35d698dc6bSJean-Francois Moine 366a7eba24SJean-Francois Moine __u8 chip_revision; 377879d459SJean-Francois Moine #define Rev012A 0 387879d459SJean-Francois Moine #define Rev072A 1 397879d459SJean-Francois Moine 406a7eba24SJean-Francois Moine signed char ag_cnt; 416a7eba24SJean-Francois Moine #define AG_CNT_START 13 426a7eba24SJean-Francois Moine }; 436a7eba24SJean-Francois Moine 44cc611b8aSJean-Francois Moine static const struct v4l2_pix_format sif_012a_mode[] = { 45c2446b3eSJean-Francois Moine {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 46c2446b3eSJean-Francois Moine .bytesperline = 160, 47c2446b3eSJean-Francois Moine .sizeimage = 160 * 120, 48c2446b3eSJean-Francois Moine .colorspace = V4L2_COLORSPACE_SRGB, 49c2446b3eSJean-Francois Moine .priv = 3}, 50c2446b3eSJean-Francois Moine {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 51c2446b3eSJean-Francois Moine .bytesperline = 176, 52c2446b3eSJean-Francois Moine .sizeimage = 176 * 144, 53c2446b3eSJean-Francois Moine .colorspace = V4L2_COLORSPACE_SRGB, 54c2446b3eSJean-Francois Moine .priv = 2}, 55c2446b3eSJean-Francois Moine {320, 240, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE, 56c2446b3eSJean-Francois Moine .bytesperline = 320, 57c2446b3eSJean-Francois Moine .sizeimage = 320 * 240 * 4 / 8, 58c2446b3eSJean-Francois Moine .colorspace = V4L2_COLORSPACE_SRGB, 59c2446b3eSJean-Francois Moine .priv = 1}, 60c2446b3eSJean-Francois Moine {352, 288, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE, 61c2446b3eSJean-Francois Moine .bytesperline = 352, 62c2446b3eSJean-Francois Moine .sizeimage = 352 * 288 * 4 / 8, 63c2446b3eSJean-Francois Moine .colorspace = V4L2_COLORSPACE_SRGB, 64c2446b3eSJean-Francois Moine .priv = 0}, 656a7eba24SJean-Francois Moine }; 666a7eba24SJean-Francois Moine 67cc611b8aSJean-Francois Moine static const struct v4l2_pix_format sif_072a_mode[] = { 68b77c0046SJean-Francois Moine {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 69b77c0046SJean-Francois Moine .bytesperline = 160, 70b77c0046SJean-Francois Moine .sizeimage = 160 * 120, 71b77c0046SJean-Francois Moine .colorspace = V4L2_COLORSPACE_SRGB, 72b77c0046SJean-Francois Moine .priv = 3}, 73b77c0046SJean-Francois Moine {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 74b77c0046SJean-Francois Moine .bytesperline = 176, 75b77c0046SJean-Francois Moine .sizeimage = 176 * 144, 76b77c0046SJean-Francois Moine .colorspace = V4L2_COLORSPACE_SRGB, 77b77c0046SJean-Francois Moine .priv = 2}, 78b77c0046SJean-Francois Moine {320, 240, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 79b77c0046SJean-Francois Moine .bytesperline = 320, 80b77c0046SJean-Francois Moine .sizeimage = 320 * 240, 81b77c0046SJean-Francois Moine .colorspace = V4L2_COLORSPACE_SRGB, 82b77c0046SJean-Francois Moine .priv = 1}, 83b77c0046SJean-Francois Moine {352, 288, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 84b77c0046SJean-Francois Moine .bytesperline = 352, 85b77c0046SJean-Francois Moine .sizeimage = 352 * 288, 86b77c0046SJean-Francois Moine .colorspace = V4L2_COLORSPACE_SRGB, 87b77c0046SJean-Francois Moine .priv = 0}, 88b77c0046SJean-Francois Moine }; 89b77c0046SJean-Francois Moine 906a7eba24SJean-Francois Moine /* 916a7eba24SJean-Francois Moine * Initialization data 926a7eba24SJean-Francois Moine * I'm not very sure how to split initialization from open data 936a7eba24SJean-Francois Moine * chunks. For now, we'll consider everything as initialization 946a7eba24SJean-Francois Moine */ 956a7eba24SJean-Francois Moine /* Frame packet header offsets for the spca561 */ 966a7eba24SJean-Francois Moine #define SPCA561_OFFSET_SNAP 1 976a7eba24SJean-Francois Moine #define SPCA561_OFFSET_TYPE 2 986a7eba24SJean-Francois Moine #define SPCA561_OFFSET_COMPRESS 3 996a7eba24SJean-Francois Moine #define SPCA561_OFFSET_FRAMSEQ 4 1006a7eba24SJean-Francois Moine #define SPCA561_OFFSET_GPIO 5 1016a7eba24SJean-Francois Moine #define SPCA561_OFFSET_USBBUFF 6 1026a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN2GRAVE 7 1036a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN2RAVE 8 1046a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN2BAVE 9 1056a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN2GBAVE 10 1066a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN1GRAVE 11 1076a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN1RAVE 12 1086a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN1BAVE 13 1096a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN1GBAVE 14 1106a7eba24SJean-Francois Moine #define SPCA561_OFFSET_FREQ 15 1116a7eba24SJean-Francois Moine #define SPCA561_OFFSET_VSYNC 16 1126a7eba24SJean-Francois Moine #define SPCA561_INDEX_I2C_BASE 0x8800 1136a7eba24SJean-Francois Moine #define SPCA561_SNAPBIT 0x20 1146a7eba24SJean-Francois Moine #define SPCA561_SNAPCTRL 0x40 1156a7eba24SJean-Francois Moine 1160dbc2c16SJean-Francois Moine static const u16 rev72a_reset[][2] = { 1176a7eba24SJean-Francois Moine {0x0000, 0x8114}, /* Software GPIO output data */ 1186a7eba24SJean-Francois Moine {0x0001, 0x8114}, /* Software GPIO output data */ 1196a7eba24SJean-Francois Moine {0x0000, 0x8112}, /* Some kind of reset */ 1200dbc2c16SJean-Francois Moine {} 1210dbc2c16SJean-Francois Moine }; 1220dbc2c16SJean-Francois Moine static const __u16 rev72a_init_data1[][2] = { 1236a7eba24SJean-Francois Moine {0x0003, 0x8701}, /* PCLK clock delay adjustment */ 1246a7eba24SJean-Francois Moine {0x0001, 0x8703}, /* HSYNC from cmos inverted */ 1256a7eba24SJean-Francois Moine {0x0011, 0x8118}, /* Enable and conf sensor */ 1266a7eba24SJean-Francois Moine {0x0001, 0x8118}, /* Conf sensor */ 1276a7eba24SJean-Francois Moine {0x0092, 0x8804}, /* I know nothing about these */ 1286a7eba24SJean-Francois Moine {0x0010, 0x8802}, /* 0x88xx registers, so I won't */ 129f8a04a6fSJean-Francois Moine {} 130f8a04a6fSJean-Francois Moine }; 1310dbc2c16SJean-Francois Moine static const u16 rev72a_init_sensor1[][2] = { 1320dbc2c16SJean-Francois Moine {0x0001, 0x000d}, 1330dbc2c16SJean-Francois Moine {0x0002, 0x0018}, 1340dbc2c16SJean-Francois Moine {0x0004, 0x0165}, 1350dbc2c16SJean-Francois Moine {0x0005, 0x0021}, 1360dbc2c16SJean-Francois Moine {0x0007, 0x00aa}, 1370dbc2c16SJean-Francois Moine {0x0020, 0x1504}, 1380dbc2c16SJean-Francois Moine {0x0039, 0x0002}, 1390dbc2c16SJean-Francois Moine {0x0035, 0x0010}, 1400dbc2c16SJean-Francois Moine {0x0009, 0x1049}, 1410dbc2c16SJean-Francois Moine {0x0028, 0x000b}, 1420dbc2c16SJean-Francois Moine {0x003b, 0x000f}, 1430dbc2c16SJean-Francois Moine {0x003c, 0x0000}, 144f8a04a6fSJean-Francois Moine {} 145f8a04a6fSJean-Francois Moine }; 146f8a04a6fSJean-Francois Moine static const __u16 rev72a_init_data2[][2] = { 1476a7eba24SJean-Francois Moine {0x0018, 0x8601}, /* Pixel/line selection for color separation */ 1486a7eba24SJean-Francois Moine {0x0000, 0x8602}, /* Optical black level for user setting */ 1496a7eba24SJean-Francois Moine {0x0060, 0x8604}, /* Optical black horizontal offset */ 1506a7eba24SJean-Francois Moine {0x0002, 0x8605}, /* Optical black vertical offset */ 1516a7eba24SJean-Francois Moine {0x0000, 0x8603}, /* Non-automatic optical black level */ 1526a7eba24SJean-Francois Moine {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ 1536a7eba24SJean-Francois Moine {0x0000, 0x865f}, /* Vertical valid pixels window (x2) */ 1546a7eba24SJean-Francois Moine {0x00b0, 0x865d}, /* Horizontal valid pixels window (x2) */ 1556a7eba24SJean-Francois Moine {0x0090, 0x865e}, /* Vertical valid lines window (x2) */ 1566a7eba24SJean-Francois Moine {0x00e0, 0x8406}, /* Memory buffer threshold */ 1576a7eba24SJean-Francois Moine {0x0000, 0x8660}, /* Compensation memory stuff */ 1586a7eba24SJean-Francois Moine {0x0002, 0x8201}, /* Output address for r/w serial EEPROM */ 1596a7eba24SJean-Francois Moine {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */ 1606a7eba24SJean-Francois Moine {0x0001, 0x8200}, /* OprMode to be executed by hardware */ 1610dbc2c16SJean-Francois Moine /* from ms-win */ 1620dbc2c16SJean-Francois Moine {0x0000, 0x8611}, /* R offset for white balance */ 1630dbc2c16SJean-Francois Moine {0x00fd, 0x8612}, /* Gr offset for white balance */ 1640dbc2c16SJean-Francois Moine {0x0003, 0x8613}, /* B offset for white balance */ 1656a7eba24SJean-Francois Moine {0x0000, 0x8614}, /* Gb offset for white balance */ 1665b7ed28eSJean-Francois Moine /* from ms-win */ 1675b7ed28eSJean-Francois Moine {0x0035, 0x8651}, /* R gain for white balance */ 1685b7ed28eSJean-Francois Moine {0x0040, 0x8652}, /* Gr gain for white balance */ 1695b7ed28eSJean-Francois Moine {0x005f, 0x8653}, /* B gain for white balance */ 1705b7ed28eSJean-Francois Moine {0x0040, 0x8654}, /* Gb gain for white balance */ 1716a7eba24SJean-Francois Moine {0x0002, 0x8502}, /* Maximum average bit rate stuff */ 1726a7eba24SJean-Francois Moine {0x0011, 0x8802}, 1730dbc2c16SJean-Francois Moine 1746a7eba24SJean-Francois Moine {0x0087, 0x8700}, /* Set master clock (96Mhz????) */ 1756a7eba24SJean-Francois Moine {0x0081, 0x8702}, /* Master clock output enable */ 1766a7eba24SJean-Francois Moine 1776a7eba24SJean-Francois Moine {0x0000, 0x8500}, /* Set image type (352x288 no compression) */ 1786a7eba24SJean-Francois Moine /* Originally was 0x0010 (352x288 compression) */ 1796a7eba24SJean-Francois Moine 1806a7eba24SJean-Francois Moine {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ 1816a7eba24SJean-Francois Moine {0x0003, 0x865c}, /* Vertical offset for valid lines */ 182f8a04a6fSJean-Francois Moine {} 183f8a04a6fSJean-Francois Moine }; 1840dbc2c16SJean-Francois Moine static const u16 rev72a_init_sensor2[][2] = { 1850dbc2c16SJean-Francois Moine {0x0003, 0x0121}, 1860dbc2c16SJean-Francois Moine {0x0004, 0x0165}, 1870dbc2c16SJean-Francois Moine {0x0005, 0x002f}, /* blanking control column */ 1880dbc2c16SJean-Francois Moine {0x0006, 0x0000}, /* blanking mode row*/ 1890dbc2c16SJean-Francois Moine {0x000a, 0x0002}, 1900dbc2c16SJean-Francois Moine {0x0009, 0x1061}, /* setexposure times && pixel clock 1916a7eba24SJean-Francois Moine * 0001 0 | 000 0110 0001 */ 1920dbc2c16SJean-Francois Moine {0x0035, 0x0014}, 1936a7eba24SJean-Francois Moine {} 1946a7eba24SJean-Francois Moine }; 1956a7eba24SJean-Francois Moine 1966a7eba24SJean-Francois Moine /******************** QC Express etch2 stuff ********************/ 197a5ae2062SJean-Francois Moine static const __u16 Pb100_1map8300[][2] = { 1986a7eba24SJean-Francois Moine /* reg, value */ 1996a7eba24SJean-Francois Moine {0x8320, 0x3304}, 2006a7eba24SJean-Francois Moine 2016a7eba24SJean-Francois Moine {0x8303, 0x0125}, /* image area */ 2026a7eba24SJean-Francois Moine {0x8304, 0x0169}, 2036a7eba24SJean-Francois Moine {0x8328, 0x000b}, 2047879d459SJean-Francois Moine {0x833c, 0x0001}, /*fixme: win:07*/ 2056a7eba24SJean-Francois Moine 2067879d459SJean-Francois Moine {0x832f, 0x1904}, /*fixme: was 0419*/ 2076a7eba24SJean-Francois Moine {0x8307, 0x00aa}, 2086a7eba24SJean-Francois Moine {0x8301, 0x0003}, 2096a7eba24SJean-Francois Moine {0x8302, 0x000e}, 2106a7eba24SJean-Francois Moine {} 2116a7eba24SJean-Francois Moine }; 212a5ae2062SJean-Francois Moine static const __u16 Pb100_2map8300[][2] = { 2136a7eba24SJean-Francois Moine /* reg, value */ 2146a7eba24SJean-Francois Moine {0x8339, 0x0000}, 2156a7eba24SJean-Francois Moine {0x8307, 0x00aa}, 2166a7eba24SJean-Francois Moine {} 2176a7eba24SJean-Francois Moine }; 2186a7eba24SJean-Francois Moine 219a5ae2062SJean-Francois Moine static const __u16 spca561_161rev12A_data1[][2] = { 2206b33e5e7SHans de Goede {0x29, 0x8118}, /* Control register (various enable bits) */ 2216b33e5e7SHans de Goede {0x08, 0x8114}, /* GPIO: Led off */ 2226b33e5e7SHans de Goede {0x0e, 0x8112}, /* 0x0e stream off 0x3e stream on */ 2236c9d3c59SJean-Francois Moine {0x00, 0x8102}, /* white balance - new */ 2246a7eba24SJean-Francois Moine {0x92, 0x8804}, 2256a7eba24SJean-Francois Moine {0x04, 0x8802}, /* windows uses 08 */ 2266a7eba24SJean-Francois Moine {} 2276a7eba24SJean-Francois Moine }; 228a5ae2062SJean-Francois Moine static const __u16 spca561_161rev12A_data2[][2] = { 2296a7eba24SJean-Francois Moine {0x21, 0x8118}, 2306a7eba24SJean-Francois Moine {0x10, 0x8500}, 2316a7eba24SJean-Francois Moine {0x07, 0x8601}, 2326a7eba24SJean-Francois Moine {0x07, 0x8602}, 2336a7eba24SJean-Francois Moine {0x04, 0x8501}, 2346a7eba24SJean-Francois Moine 2356a7eba24SJean-Francois Moine {0x07, 0x8201}, /* windows uses 02 */ 2366a7eba24SJean-Francois Moine {0x08, 0x8200}, 2376a7eba24SJean-Francois Moine {0x01, 0x8200}, 2386a7eba24SJean-Francois Moine 2396a7eba24SJean-Francois Moine {0x90, 0x8604}, 2406a7eba24SJean-Francois Moine {0x00, 0x8605}, 2416a7eba24SJean-Francois Moine {0xb0, 0x8603}, 2426a7eba24SJean-Francois Moine 2436a7eba24SJean-Francois Moine /* sensor gains */ 2446c9d3c59SJean-Francois Moine {0x07, 0x8601}, /* white balance - new */ 2456c9d3c59SJean-Francois Moine {0x07, 0x8602}, /* white balance - new */ 2466a7eba24SJean-Francois Moine {0x00, 0x8610}, /* *red */ 2476a7eba24SJean-Francois Moine {0x00, 0x8611}, /* 3f *green */ 2486a7eba24SJean-Francois Moine {0x00, 0x8612}, /* green *blue */ 2496a7eba24SJean-Francois Moine {0x00, 0x8613}, /* blue *green */ 2506c9d3c59SJean-Francois Moine {0x43, 0x8614}, /* green *red - white balance - was 0x35 */ 2516c9d3c59SJean-Francois Moine {0x40, 0x8615}, /* 40 *green - white balance - was 0x35 */ 2526c9d3c59SJean-Francois Moine {0x71, 0x8616}, /* 7a *blue - white balance - was 0x35 */ 2536c9d3c59SJean-Francois Moine {0x40, 0x8617}, /* 40 *green - white balance - was 0x35 */ 2546a7eba24SJean-Francois Moine 2556a7eba24SJean-Francois Moine {0x0c, 0x8620}, /* 0c */ 2566a7eba24SJean-Francois Moine {0xc8, 0x8631}, /* c8 */ 2576a7eba24SJean-Francois Moine {0xc8, 0x8634}, /* c8 */ 2586a7eba24SJean-Francois Moine {0x23, 0x8635}, /* 23 */ 2596a7eba24SJean-Francois Moine {0x1f, 0x8636}, /* 1f */ 2606a7eba24SJean-Francois Moine {0xdd, 0x8637}, /* dd */ 2616a7eba24SJean-Francois Moine {0xe1, 0x8638}, /* e1 */ 2626a7eba24SJean-Francois Moine {0x1d, 0x8639}, /* 1d */ 2636a7eba24SJean-Francois Moine {0x21, 0x863a}, /* 21 */ 2646a7eba24SJean-Francois Moine {0xe3, 0x863b}, /* e3 */ 2656a7eba24SJean-Francois Moine {0xdf, 0x863c}, /* df */ 2666a7eba24SJean-Francois Moine {0xf0, 0x8505}, 2676a7eba24SJean-Francois Moine {0x32, 0x850a}, 2687879d459SJean-Francois Moine /* {0x99, 0x8700}, * - white balance - new (removed) */ 2696b33e5e7SHans de Goede /* HDG we used to do this in stop0, making the init state and the state 2706b33e5e7SHans de Goede after a start / stop different, so do this here instead. */ 2716b33e5e7SHans de Goede {0x29, 0x8118}, 2726a7eba24SJean-Francois Moine {} 2736a7eba24SJean-Francois Moine }; 2746a7eba24SJean-Francois Moine 275c93396e1STheodore Kilgore static void reg_w_val(struct gspca_dev *gspca_dev, __u16 index, __u8 value) 2766a7eba24SJean-Francois Moine { 27735dc1b4cSJean-Francois Moine int ret; 278c93396e1STheodore Kilgore struct usb_device *dev = gspca_dev->dev; 2796a7eba24SJean-Francois Moine 28035dc1b4cSJean-Francois Moine ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 28135dc1b4cSJean-Francois Moine 0, /* request */ 28235dc1b4cSJean-Francois Moine USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 28335dc1b4cSJean-Francois Moine value, index, NULL, 0, 500); 28437d5efb0SJoe Perches gspca_dbg(gspca_dev, D_USBO, "reg write: 0x%02x:0x%02x\n", 28537d5efb0SJoe Perches index, value); 28635dc1b4cSJean-Francois Moine if (ret < 0) 287133a9fe9SJoe Perches pr_err("reg write: error %d\n", ret); 28835dc1b4cSJean-Francois Moine } 28935dc1b4cSJean-Francois Moine 29035dc1b4cSJean-Francois Moine static void write_vector(struct gspca_dev *gspca_dev, 29135dc1b4cSJean-Francois Moine const __u16 data[][2]) 29235dc1b4cSJean-Francois Moine { 29335dc1b4cSJean-Francois Moine int i; 29435dc1b4cSJean-Francois Moine 29535dc1b4cSJean-Francois Moine i = 0; 29635dc1b4cSJean-Francois Moine while (data[i][1] != 0) { 297c93396e1STheodore Kilgore reg_w_val(gspca_dev, data[i][1], data[i][0]); 2986a7eba24SJean-Francois Moine i++; 2996a7eba24SJean-Francois Moine } 3006a7eba24SJean-Francois Moine } 30135dc1b4cSJean-Francois Moine 30235dc1b4cSJean-Francois Moine /* read 'len' bytes to gspca_dev->usb_buf */ 30335dc1b4cSJean-Francois Moine static void reg_r(struct gspca_dev *gspca_dev, 30435dc1b4cSJean-Francois Moine __u16 index, __u16 length) 30535dc1b4cSJean-Francois Moine { 30635dc1b4cSJean-Francois Moine usb_control_msg(gspca_dev->dev, 30735dc1b4cSJean-Francois Moine usb_rcvctrlpipe(gspca_dev->dev, 0), 30835dc1b4cSJean-Francois Moine 0, /* request */ 30935dc1b4cSJean-Francois Moine USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 31035dc1b4cSJean-Francois Moine 0, /* value */ 31135dc1b4cSJean-Francois Moine index, gspca_dev->usb_buf, length, 500); 31235dc1b4cSJean-Francois Moine } 31335dc1b4cSJean-Francois Moine 31435dc1b4cSJean-Francois Moine /* write 'len' bytes from gspca_dev->usb_buf */ 31535dc1b4cSJean-Francois Moine static void reg_w_buf(struct gspca_dev *gspca_dev, 31635dc1b4cSJean-Francois Moine __u16 index, __u16 len) 31735dc1b4cSJean-Francois Moine { 31835dc1b4cSJean-Francois Moine usb_control_msg(gspca_dev->dev, 31935dc1b4cSJean-Francois Moine usb_sndctrlpipe(gspca_dev->dev, 0), 32035dc1b4cSJean-Francois Moine 0, /* request */ 32135dc1b4cSJean-Francois Moine USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 32235dc1b4cSJean-Francois Moine 0, /* value */ 32335dc1b4cSJean-Francois Moine index, gspca_dev->usb_buf, len, 500); 32435dc1b4cSJean-Francois Moine } 32535dc1b4cSJean-Francois Moine 32635dc1b4cSJean-Francois Moine static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg) 32735dc1b4cSJean-Francois Moine { 32835dc1b4cSJean-Francois Moine int retry = 60; 32935dc1b4cSJean-Francois Moine 330c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8801, reg); 331c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8805, value); 332c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8800, value >> 8); 33335dc1b4cSJean-Francois Moine do { 33435dc1b4cSJean-Francois Moine reg_r(gspca_dev, 0x8803, 1); 33535dc1b4cSJean-Francois Moine if (!gspca_dev->usb_buf[0]) 33635dc1b4cSJean-Francois Moine return; 3370dbc2c16SJean-Francois Moine msleep(10); 33835dc1b4cSJean-Francois Moine } while (--retry); 33935dc1b4cSJean-Francois Moine } 34035dc1b4cSJean-Francois Moine 34135dc1b4cSJean-Francois Moine static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) 34235dc1b4cSJean-Francois Moine { 34335dc1b4cSJean-Francois Moine int retry = 60; 34435dc1b4cSJean-Francois Moine __u8 value; 34535dc1b4cSJean-Francois Moine 346c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8804, 0x92); 347c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8801, reg); 348c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8802, mode | 0x01); 34935dc1b4cSJean-Francois Moine do { 35035dc1b4cSJean-Francois Moine reg_r(gspca_dev, 0x8803, 1); 35135dc1b4cSJean-Francois Moine if (!gspca_dev->usb_buf[0]) { 35235dc1b4cSJean-Francois Moine reg_r(gspca_dev, 0x8800, 1); 35335dc1b4cSJean-Francois Moine value = gspca_dev->usb_buf[0]; 35435dc1b4cSJean-Francois Moine reg_r(gspca_dev, 0x8805, 1); 35535dc1b4cSJean-Francois Moine return ((int) value << 8) | gspca_dev->usb_buf[0]; 35635dc1b4cSJean-Francois Moine } 3570dbc2c16SJean-Francois Moine msleep(10); 35835dc1b4cSJean-Francois Moine } while (--retry); 35935dc1b4cSJean-Francois Moine return -1; 36035dc1b4cSJean-Francois Moine } 36135dc1b4cSJean-Francois Moine 36235dc1b4cSJean-Francois Moine static void sensor_mapwrite(struct gspca_dev *gspca_dev, 36335dc1b4cSJean-Francois Moine const __u16 (*sensormap)[2]) 36435dc1b4cSJean-Francois Moine { 36535dc1b4cSJean-Francois Moine while ((*sensormap)[0]) { 36635dc1b4cSJean-Francois Moine gspca_dev->usb_buf[0] = (*sensormap)[1]; 36735dc1b4cSJean-Francois Moine gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8; 36835dc1b4cSJean-Francois Moine reg_w_buf(gspca_dev, (*sensormap)[0], 2); 36935dc1b4cSJean-Francois Moine sensormap++; 37035dc1b4cSJean-Francois Moine } 37135dc1b4cSJean-Francois Moine } 37235dc1b4cSJean-Francois Moine 373f8a04a6fSJean-Francois Moine static void write_sensor_72a(struct gspca_dev *gspca_dev, 374f8a04a6fSJean-Francois Moine const __u16 (*sensor)[2]) 375f8a04a6fSJean-Francois Moine { 376f8a04a6fSJean-Francois Moine while ((*sensor)[0]) { 377f8a04a6fSJean-Francois Moine i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]); 378f8a04a6fSJean-Francois Moine sensor++; 379f8a04a6fSJean-Francois Moine } 380f8a04a6fSJean-Francois Moine } 381f8a04a6fSJean-Francois Moine 3826a7eba24SJean-Francois Moine static void init_161rev12A(struct gspca_dev *gspca_dev) 3836a7eba24SJean-Francois Moine { 3846a7eba24SJean-Francois Moine write_vector(gspca_dev, spca561_161rev12A_data1); 3856a7eba24SJean-Francois Moine sensor_mapwrite(gspca_dev, Pb100_1map8300); 3867879d459SJean-Francois Moine /*fixme: should be in sd_start*/ 3876a7eba24SJean-Francois Moine write_vector(gspca_dev, spca561_161rev12A_data2); 3886a7eba24SJean-Francois Moine sensor_mapwrite(gspca_dev, Pb100_2map8300); 3896a7eba24SJean-Francois Moine } 3906a7eba24SJean-Francois Moine 3916a7eba24SJean-Francois Moine /* this function is called at probe time */ 3926a7eba24SJean-Francois Moine static int sd_config(struct gspca_dev *gspca_dev, 3936a7eba24SJean-Francois Moine const struct usb_device_id *id) 3946a7eba24SJean-Francois Moine { 3956a7eba24SJean-Francois Moine struct sd *sd = (struct sd *) gspca_dev; 3966a7eba24SJean-Francois Moine struct cam *cam; 3976a7eba24SJean-Francois Moine __u16 vendor, product; 3986a7eba24SJean-Francois Moine __u8 data1, data2; 3996a7eba24SJean-Francois Moine 4006a7eba24SJean-Francois Moine /* Read frm global register the USB product and vendor IDs, just to 4016a7eba24SJean-Francois Moine * prove that we can communicate with the device. This works, which 4026a7eba24SJean-Francois Moine * confirms at we are communicating properly and that the device 4036a7eba24SJean-Francois Moine * is a 561. */ 404739570bbSJean-Francois Moine reg_r(gspca_dev, 0x8104, 1); 405739570bbSJean-Francois Moine data1 = gspca_dev->usb_buf[0]; 406739570bbSJean-Francois Moine reg_r(gspca_dev, 0x8105, 1); 407739570bbSJean-Francois Moine data2 = gspca_dev->usb_buf[0]; 4086a7eba24SJean-Francois Moine vendor = (data2 << 8) | data1; 409739570bbSJean-Francois Moine reg_r(gspca_dev, 0x8106, 1); 410739570bbSJean-Francois Moine data1 = gspca_dev->usb_buf[0]; 411739570bbSJean-Francois Moine reg_r(gspca_dev, 0x8107, 1); 412739570bbSJean-Francois Moine data2 = gspca_dev->usb_buf[0]; 4136a7eba24SJean-Francois Moine product = (data2 << 8) | data1; 4146a7eba24SJean-Francois Moine if (vendor != id->idVendor || product != id->idProduct) { 41537d5efb0SJoe Perches gspca_dbg(gspca_dev, D_PROBE, "Bad vendor / product from device\n"); 4166a7eba24SJean-Francois Moine return -EINVAL; 4176a7eba24SJean-Francois Moine } 4189d64fdb1SJean-Francois Moine 4196a7eba24SJean-Francois Moine cam = &gspca_dev->cam; 420eb3fb7c9SHans de Goede cam->needs_full_bandwidth = 1; 4219d64fdb1SJean-Francois Moine 4229d64fdb1SJean-Francois Moine sd->chip_revision = id->driver_info; 423b77c0046SJean-Francois Moine if (sd->chip_revision == Rev012A) { 424b77c0046SJean-Francois Moine cam->cam_mode = sif_012a_mode; 425b77c0046SJean-Francois Moine cam->nmodes = ARRAY_SIZE(sif_012a_mode); 426b77c0046SJean-Francois Moine } else { 427b77c0046SJean-Francois Moine cam->cam_mode = sif_072a_mode; 428b77c0046SJean-Francois Moine cam->nmodes = ARRAY_SIZE(sif_072a_mode); 429b77c0046SJean-Francois Moine } 430d698dc6bSJean-Francois Moine sd->expo12a = EXPO12A_DEF; 4316a7eba24SJean-Francois Moine return 0; 4326a7eba24SJean-Francois Moine } 4336a7eba24SJean-Francois Moine 434012d6b02SJean-Francois Moine /* this function is called at probe and resume time */ 435012d6b02SJean-Francois Moine static int sd_init_12a(struct gspca_dev *gspca_dev) 4366a7eba24SJean-Francois Moine { 43737d5efb0SJoe Perches gspca_dbg(gspca_dev, D_STREAM, "Chip revision: 012a\n"); 4386a7eba24SJean-Francois Moine init_161rev12A(gspca_dev); 4397879d459SJean-Francois Moine return 0; 4406a7eba24SJean-Francois Moine } 441012d6b02SJean-Francois Moine static int sd_init_72a(struct gspca_dev *gspca_dev) 4427879d459SJean-Francois Moine { 44337d5efb0SJoe Perches gspca_dbg(gspca_dev, D_STREAM, "Chip revision: 072a\n"); 4440dbc2c16SJean-Francois Moine write_vector(gspca_dev, rev72a_reset); 4450dbc2c16SJean-Francois Moine msleep(200); 446f8a04a6fSJean-Francois Moine write_vector(gspca_dev, rev72a_init_data1); 447f8a04a6fSJean-Francois Moine write_sensor_72a(gspca_dev, rev72a_init_sensor1); 448f8a04a6fSJean-Francois Moine write_vector(gspca_dev, rev72a_init_data2); 449f8a04a6fSJean-Francois Moine write_sensor_72a(gspca_dev, rev72a_init_sensor2); 450c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8112, 0x30); 4516a7eba24SJean-Francois Moine return 0; 4526a7eba24SJean-Francois Moine } 4536a7eba24SJean-Francois Moine 4543fa24bf5SHans Verkuil static void setbrightness(struct gspca_dev *gspca_dev, s32 val) 4553fa24bf5SHans Verkuil { 456a8931d59SHans de Goede struct sd *sd = (struct sd *) gspca_dev; 457a8931d59SHans de Goede __u16 reg; 4583fa24bf5SHans Verkuil 459a8931d59SHans de Goede if (sd->chip_revision == Rev012A) 460a8931d59SHans de Goede reg = 0x8610; 461a8931d59SHans de Goede else 462a8931d59SHans de Goede reg = 0x8611; 463a8931d59SHans de Goede 464c93396e1STheodore Kilgore reg_w_val(gspca_dev, reg + 0, val); /* R */ 465c93396e1STheodore Kilgore reg_w_val(gspca_dev, reg + 1, val); /* Gr */ 466c93396e1STheodore Kilgore reg_w_val(gspca_dev, reg + 2, val); /* B */ 467c93396e1STheodore Kilgore reg_w_val(gspca_dev, reg + 3, val); /* Gb */ 4683fa24bf5SHans Verkuil } 4693fa24bf5SHans Verkuil 4703fa24bf5SHans Verkuil static void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast) 4716a7eba24SJean-Francois Moine { 4726a7eba24SJean-Francois Moine struct sd *sd = (struct sd *) gspca_dev; 4735b7ed28eSJean-Francois Moine __u8 blue, red; 4745b7ed28eSJean-Francois Moine __u16 reg; 4756c9d3c59SJean-Francois Moine 4766c9d3c59SJean-Francois Moine /* try to emulate MS-win as possible */ 4775b7ed28eSJean-Francois Moine red = 0x20 + white * 3 / 8; 4785b7ed28eSJean-Francois Moine blue = 0x90 - white * 5 / 8; 4795b7ed28eSJean-Francois Moine if (sd->chip_revision == Rev012A) { 4805b7ed28eSJean-Francois Moine reg = 0x8614; 4815b7ed28eSJean-Francois Moine } else { 4825b7ed28eSJean-Francois Moine reg = 0x8651; 4833fa24bf5SHans Verkuil red += contrast - 0x20; 4843fa24bf5SHans Verkuil blue += contrast - 0x20; 485c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8652, contrast + 0x20); /* Gr */ 486c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8654, contrast + 0x20); /* Gb */ 4875b7ed28eSJean-Francois Moine } 488c93396e1STheodore Kilgore reg_w_val(gspca_dev, reg, red); 489c93396e1STheodore Kilgore reg_w_val(gspca_dev, reg + 2, blue); 4906c9d3c59SJean-Francois Moine } 4917879d459SJean-Francois Moine 4927879d459SJean-Francois Moine /* rev 12a only */ 4933fa24bf5SHans Verkuil static void setexposure(struct gspca_dev *gspca_dev, s32 val) 4947879d459SJean-Francois Moine { 495d0848eb2SHans de Goede int i, expo = 0; 4967879d459SJean-Francois Moine 4970fc23d20SHans de Goede /* Register 0x8309 controls exposure for the spca561, 4980fc23d20SHans de Goede the basic exposure setting goes from 1-2047, where 1 is completely 4990fc23d20SHans de Goede dark and 2047 is very bright. It not only influences exposure but 5000fc23d20SHans de Goede also the framerate (to allow for longer exposure) from 1 - 300 it 5010fc23d20SHans de Goede only raises the exposure time then from 300 - 600 it halves the 5020fc23d20SHans de Goede framerate to be able to further raise the exposure time and for every 5030fc23d20SHans de Goede 300 more it halves the framerate again. This allows for a maximum 5040fc23d20SHans de Goede exposure time of circa 0.2 - 0.25 seconds (30 / (2000/3000) fps). 5050fc23d20SHans de Goede Sometimes this is not enough, the 1-2047 uses bits 0-10, bits 11-12 5060fc23d20SHans de Goede configure a divider for the base framerate which us used at the 5070fc23d20SHans de Goede exposure setting of 1-300. These bits configure the base framerate 5080fc23d20SHans de Goede according to the following formula: fps = 60 / (value + 2) */ 509d0848eb2SHans de Goede 510d0848eb2SHans de Goede /* We choose to use the high bits setting the fixed framerate divisor 511d0848eb2SHans de Goede asap, as setting high basic exposure setting without the fixed 512d0848eb2SHans de Goede divider in combination with high gains makes the cam stop */ 513d0848eb2SHans de Goede int table[] = { 0, 450, 550, 625, EXPOSURE_MAX }; 514d0848eb2SHans de Goede 515d0848eb2SHans de Goede for (i = 0; i < ARRAY_SIZE(table) - 1; i++) { 5163fa24bf5SHans Verkuil if (val <= table[i + 1]) { 5173fa24bf5SHans Verkuil expo = val - table[i]; 518d0848eb2SHans de Goede if (i) 519d0848eb2SHans de Goede expo += 300; 520d0848eb2SHans de Goede expo |= i << 11; 521d0848eb2SHans de Goede break; 5220fc23d20SHans de Goede } 523d0848eb2SHans de Goede } 524d0848eb2SHans de Goede 52535dc1b4cSJean-Francois Moine gspca_dev->usb_buf[0] = expo; 52635dc1b4cSJean-Francois Moine gspca_dev->usb_buf[1] = expo >> 8; 52735dc1b4cSJean-Francois Moine reg_w_buf(gspca_dev, 0x8309, 2); 5287879d459SJean-Francois Moine } 5297879d459SJean-Francois Moine 5307879d459SJean-Francois Moine /* rev 12a only */ 5313fa24bf5SHans Verkuil static void setgain(struct gspca_dev *gspca_dev, s32 val) 5327879d459SJean-Francois Moine { 533d0848eb2SHans de Goede /* gain reg low 6 bits 0-63 gain, bit 6 and 7, both double the 534d0848eb2SHans de Goede sensitivity when set, so 31 + one of them set == 63, and 15 535d0848eb2SHans de Goede with both of them set == 63 */ 5363fa24bf5SHans Verkuil if (val < 64) 5373fa24bf5SHans Verkuil gspca_dev->usb_buf[0] = val; 5383fa24bf5SHans Verkuil else if (val < 128) 5393fa24bf5SHans Verkuil gspca_dev->usb_buf[0] = (val / 2) | 0x40; 540d0848eb2SHans de Goede else 5413fa24bf5SHans Verkuil gspca_dev->usb_buf[0] = (val / 4) | 0xc0; 542d0848eb2SHans de Goede 54335dc1b4cSJean-Francois Moine gspca_dev->usb_buf[1] = 0; 54435dc1b4cSJean-Francois Moine reg_w_buf(gspca_dev, 0x8335, 2); 5456c9d3c59SJean-Francois Moine } 5466c9d3c59SJean-Francois Moine 5473fa24bf5SHans Verkuil static void setautogain(struct gspca_dev *gspca_dev, s32 val) 548cebf3b67SJean-Francois Moine { 549cebf3b67SJean-Francois Moine struct sd *sd = (struct sd *) gspca_dev; 550cebf3b67SJean-Francois Moine 5513fa24bf5SHans Verkuil if (val) 552cebf3b67SJean-Francois Moine sd->ag_cnt = AG_CNT_START; 553cebf3b67SJean-Francois Moine else 554cebf3b67SJean-Francois Moine sd->ag_cnt = -1; 555cebf3b67SJean-Francois Moine } 556cebf3b67SJean-Francois Moine 55772ab97ceSJean-Francois Moine static int sd_start_12a(struct gspca_dev *gspca_dev) 5586a7eba24SJean-Francois Moine { 5596a7eba24SJean-Francois Moine int mode; 5605b7ed28eSJean-Francois Moine static const __u8 Reg8391[8] = 5615b7ed28eSJean-Francois Moine {0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00}; 5626a7eba24SJean-Francois Moine 563c2446b3eSJean-Francois Moine mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; 5646a7eba24SJean-Francois Moine if (mode <= 1) { 5656a7eba24SJean-Francois Moine /* Use compression on 320x240 and above */ 566c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8500, 0x10 | mode); 5676a7eba24SJean-Francois Moine } else { 5686a7eba24SJean-Francois Moine /* I couldn't get the compression to work below 320x240 5696a7eba24SJean-Francois Moine * Fortunately at these resolutions the bandwidth 5706a7eba24SJean-Francois Moine * is sufficient to push raw frames at ~20fps */ 571c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8500, mode); 5726a7eba24SJean-Francois Moine } /* -- qq@kuku.eu.org */ 57335dc1b4cSJean-Francois Moine 5745b7ed28eSJean-Francois Moine gspca_dev->usb_buf[0] = 0xaa; 5755b7ed28eSJean-Francois Moine gspca_dev->usb_buf[1] = 0x00; 5765b7ed28eSJean-Francois Moine reg_w_buf(gspca_dev, 0x8307, 2); 5775b7ed28eSJean-Francois Moine /* clock - lower 0x8X values lead to fps > 30 */ 578c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8700, 0x8a); 579739570bbSJean-Francois Moine /* 0x8f 0x85 0x27 clock */ 580c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8112, 0x1e | 0x20); 581c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x850b, 0x03); 5825b7ed28eSJean-Francois Moine memcpy(gspca_dev->usb_buf, Reg8391, 8); 5835b7ed28eSJean-Francois Moine reg_w_buf(gspca_dev, 0x8391, 8); 5845b7ed28eSJean-Francois Moine reg_w_buf(gspca_dev, 0x8390, 8); 5856b33e5e7SHans de Goede 5866b33e5e7SHans de Goede /* Led ON (bit 3 -> 0 */ 587c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8114, 0x00); 58872ab97ceSJean-Francois Moine return 0; 5897879d459SJean-Francois Moine } 59072ab97ceSJean-Francois Moine static int sd_start_72a(struct gspca_dev *gspca_dev) 5917879d459SJean-Francois Moine { 5923fa24bf5SHans Verkuil struct sd *sd = (struct sd *) gspca_dev; 5937879d459SJean-Francois Moine int Clck; 5947879d459SJean-Francois Moine int mode; 5957879d459SJean-Francois Moine 5960dbc2c16SJean-Francois Moine write_vector(gspca_dev, rev72a_reset); 5970dbc2c16SJean-Francois Moine msleep(200); 5980dbc2c16SJean-Francois Moine write_vector(gspca_dev, rev72a_init_data1); 5990dbc2c16SJean-Francois Moine write_sensor_72a(gspca_dev, rev72a_init_sensor1); 6000dbc2c16SJean-Francois Moine 6017879d459SJean-Francois Moine mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; 6027879d459SJean-Francois Moine switch (mode) { 6037879d459SJean-Francois Moine default: 604a48196a2SJean-Francois Moine case 0: 605a48196a2SJean-Francois Moine Clck = 0x27; /* ms-win 0x87 */ 606a48196a2SJean-Francois Moine break; 607a48196a2SJean-Francois Moine case 1: 6087879d459SJean-Francois Moine Clck = 0x25; 6097879d459SJean-Francois Moine break; 6107879d459SJean-Francois Moine case 2: 6117879d459SJean-Francois Moine Clck = 0x22; 6127879d459SJean-Francois Moine break; 6137879d459SJean-Francois Moine case 3: 6147879d459SJean-Francois Moine Clck = 0x21; 6156a7eba24SJean-Francois Moine break; 6166a7eba24SJean-Francois Moine } 617c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8700, Clck); /* 0x27 clock */ 618c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8702, 0x81); 619c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8500, mode); /* mode */ 6200dbc2c16SJean-Francois Moine write_sensor_72a(gspca_dev, rev72a_init_sensor2); 6213fa24bf5SHans Verkuil setwhite(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue), 6223fa24bf5SHans Verkuil v4l2_ctrl_g_ctrl(sd->contrast)); 6235b7ed28eSJean-Francois Moine /* setbrightness(gspca_dev); * fixme: bad values */ 6243fa24bf5SHans Verkuil setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); 625c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8112, 0x10 | 0x20); 62672ab97ceSJean-Francois Moine return 0; 6276a7eba24SJean-Francois Moine } 6286a7eba24SJean-Francois Moine 6296a7eba24SJean-Francois Moine static void sd_stopN(struct gspca_dev *gspca_dev) 6306a7eba24SJean-Francois Moine { 631d698dc6bSJean-Francois Moine struct sd *sd = (struct sd *) gspca_dev; 632d698dc6bSJean-Francois Moine 633d698dc6bSJean-Francois Moine if (sd->chip_revision == Rev012A) { 634c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8112, 0x0e); 6356b33e5e7SHans de Goede /* Led Off (bit 3 -> 1 */ 636c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8114, 0x08); 637d698dc6bSJean-Francois Moine } else { 638c93396e1STheodore Kilgore reg_w_val(gspca_dev, 0x8112, 0x20); 639c93396e1STheodore Kilgore /* reg_w_val(gspca_dev, 0x8102, 0x00); ?? */ 640d698dc6bSJean-Francois Moine } 6416a7eba24SJean-Francois Moine } 6426a7eba24SJean-Francois Moine 643cebf3b67SJean-Francois Moine static void do_autogain(struct gspca_dev *gspca_dev) 6446a7eba24SJean-Francois Moine { 6456a7eba24SJean-Francois Moine struct sd *sd = (struct sd *) gspca_dev; 646cebf3b67SJean-Francois Moine int expotimes; 647cebf3b67SJean-Francois Moine int pixelclk; 648cebf3b67SJean-Francois Moine int gainG; 6496a7eba24SJean-Francois Moine __u8 R, Gr, Gb, B; 6506a7eba24SJean-Francois Moine int y; 6516a7eba24SJean-Francois Moine __u8 luma_mean = 110; 6526a7eba24SJean-Francois Moine __u8 luma_delta = 20; 6536a7eba24SJean-Francois Moine __u8 spring = 4; 6546a7eba24SJean-Francois Moine 655cebf3b67SJean-Francois Moine if (sd->ag_cnt < 0) 656cebf3b67SJean-Francois Moine return; 657cebf3b67SJean-Francois Moine if (--sd->ag_cnt >= 0) 658cebf3b67SJean-Francois Moine return; 659cebf3b67SJean-Francois Moine sd->ag_cnt = AG_CNT_START; 660cebf3b67SJean-Francois Moine 6616a7eba24SJean-Francois Moine switch (sd->chip_revision) { 6626a7eba24SJean-Francois Moine case Rev072A: 663739570bbSJean-Francois Moine reg_r(gspca_dev, 0x8621, 1); 664739570bbSJean-Francois Moine Gr = gspca_dev->usb_buf[0]; 665739570bbSJean-Francois Moine reg_r(gspca_dev, 0x8622, 1); 666739570bbSJean-Francois Moine R = gspca_dev->usb_buf[0]; 667739570bbSJean-Francois Moine reg_r(gspca_dev, 0x8623, 1); 668739570bbSJean-Francois Moine B = gspca_dev->usb_buf[0]; 669739570bbSJean-Francois Moine reg_r(gspca_dev, 0x8624, 1); 670739570bbSJean-Francois Moine Gb = gspca_dev->usb_buf[0]; 6716a7eba24SJean-Francois Moine y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8; 6726a7eba24SJean-Francois Moine /* u= (128*B-(43*(Gr+Gb+R))) >> 8; */ 6736a7eba24SJean-Francois Moine /* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */ 6746a7eba24SJean-Francois Moine 6756a7eba24SJean-Francois Moine if (y < luma_mean - luma_delta || 6766a7eba24SJean-Francois Moine y > luma_mean + luma_delta) { 6776a7eba24SJean-Francois Moine expotimes = i2c_read(gspca_dev, 0x09, 0x10); 6786a7eba24SJean-Francois Moine pixelclk = 0x0800; 6796a7eba24SJean-Francois Moine expotimes = expotimes & 0x07ff; 6806a7eba24SJean-Francois Moine gainG = i2c_read(gspca_dev, 0x35, 0x10); 6816a7eba24SJean-Francois Moine 6826a7eba24SJean-Francois Moine expotimes += (luma_mean - y) >> spring; 6836a7eba24SJean-Francois Moine gainG += (luma_mean - y) / 50; 6846a7eba24SJean-Francois Moine 6856a7eba24SJean-Francois Moine if (gainG > 0x3f) 6866a7eba24SJean-Francois Moine gainG = 0x3f; 68735dc1b4cSJean-Francois Moine else if (gainG < 3) 6886a7eba24SJean-Francois Moine gainG = 3; 6896a7eba24SJean-Francois Moine i2c_write(gspca_dev, gainG, 0x35); 6906a7eba24SJean-Francois Moine 69135dc1b4cSJean-Francois Moine if (expotimes > 0x0256) 6926a7eba24SJean-Francois Moine expotimes = 0x0256; 69335dc1b4cSJean-Francois Moine else if (expotimes < 3) 6946a7eba24SJean-Francois Moine expotimes = 3; 6956a7eba24SJean-Francois Moine i2c_write(gspca_dev, expotimes | pixelclk, 0x09); 6966a7eba24SJean-Francois Moine } 6976a7eba24SJean-Francois Moine break; 6986a7eba24SJean-Francois Moine } 6996a7eba24SJean-Francois Moine } 7006a7eba24SJean-Francois Moine 7016a7eba24SJean-Francois Moine static void sd_pkt_scan(struct gspca_dev *gspca_dev, 70276dd272bSJean-Francois Moine u8 *data, /* isoc packet */ 7036a7eba24SJean-Francois Moine int len) /* iso packet length */ 7046a7eba24SJean-Francois Moine { 7050fc23d20SHans de Goede struct sd *sd = (struct sd *) gspca_dev; 7060fc23d20SHans de Goede 707576ed7b5SJean-Francois Moine len--; 708576ed7b5SJean-Francois Moine switch (*data++) { /* sequence number */ 7096a7eba24SJean-Francois Moine case 0: /* start of frame */ 71076dd272bSJean-Francois Moine gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); 711436c2c53SHans de Goede 712436c2c53SHans de Goede /* This should never happen */ 713436c2c53SHans de Goede if (len < 2) { 71437d5efb0SJoe Perches gspca_err(gspca_dev, "Short SOF packet, ignoring\n\n\n\n\n"); 715436c2c53SHans de Goede gspca_dev->last_packet_type = DISCARD_PACKET; 716436c2c53SHans de Goede return; 717436c2c53SHans de Goede } 718436c2c53SHans de Goede 71960d21563SPeter Senna Tschudin #if IS_ENABLED(CONFIG_INPUT) 720436c2c53SHans de Goede if (data[0] & 0x20) { 721436c2c53SHans de Goede input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); 722436c2c53SHans de Goede input_sync(gspca_dev->input_dev); 723436c2c53SHans de Goede input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); 724436c2c53SHans de Goede input_sync(gspca_dev->input_dev); 725436c2c53SHans de Goede } 726436c2c53SHans de Goede #endif 727436c2c53SHans de Goede 7286a7eba24SJean-Francois Moine if (data[1] & 0x10) { 7296a7eba24SJean-Francois Moine /* compressed bayer */ 73076dd272bSJean-Francois Moine gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); 7316a7eba24SJean-Francois Moine } else { 73254ab92caSHans de Goede /* raw bayer (with a header, which we skip) */ 7330fc23d20SHans de Goede if (sd->chip_revision == Rev012A) { 7340fc23d20SHans de Goede data += 20; 7350fc23d20SHans de Goede len -= 20; 7360fc23d20SHans de Goede } else { 737b77c0046SJean-Francois Moine data += 16; 738b77c0046SJean-Francois Moine len -= 16; 7390fc23d20SHans de Goede } 74076dd272bSJean-Francois Moine gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); 7416a7eba24SJean-Francois Moine } 7426a7eba24SJean-Francois Moine return; 74335dc1b4cSJean-Francois Moine case 0xff: /* drop (empty mpackets) */ 7446a7eba24SJean-Francois Moine return; 7456a7eba24SJean-Francois Moine } 74676dd272bSJean-Francois Moine gspca_frame_add(gspca_dev, INTER_PACKET, data, len); 7476a7eba24SJean-Francois Moine } 7486a7eba24SJean-Francois Moine 7493fa24bf5SHans Verkuil static int sd_s_ctrl(struct v4l2_ctrl *ctrl) 7506a7eba24SJean-Francois Moine { 7513fa24bf5SHans Verkuil struct gspca_dev *gspca_dev = 7523fa24bf5SHans Verkuil container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 7536a7eba24SJean-Francois Moine struct sd *sd = (struct sd *)gspca_dev; 7546a7eba24SJean-Francois Moine 7553fa24bf5SHans Verkuil gspca_dev->usb_err = 0; 7563fa24bf5SHans Verkuil 7573fa24bf5SHans Verkuil if (!gspca_dev->streaming) 7586a7eba24SJean-Francois Moine return 0; 7593fa24bf5SHans Verkuil 7603fa24bf5SHans Verkuil switch (ctrl->id) { 7613fa24bf5SHans Verkuil case V4L2_CID_BRIGHTNESS: 7623fa24bf5SHans Verkuil setbrightness(gspca_dev, ctrl->val); 7633fa24bf5SHans Verkuil break; 7643fa24bf5SHans Verkuil case V4L2_CID_CONTRAST: 7653fa24bf5SHans Verkuil /* hue/contrast control cluster for 72a */ 7663fa24bf5SHans Verkuil setwhite(gspca_dev, sd->hue->val, ctrl->val); 7673fa24bf5SHans Verkuil break; 7683fa24bf5SHans Verkuil case V4L2_CID_HUE: 7693fa24bf5SHans Verkuil /* just plain hue control for 12a */ 7703fa24bf5SHans Verkuil setwhite(gspca_dev, ctrl->val, 0); 7713fa24bf5SHans Verkuil break; 7723fa24bf5SHans Verkuil case V4L2_CID_EXPOSURE: 7733fa24bf5SHans Verkuil setexposure(gspca_dev, ctrl->val); 7743fa24bf5SHans Verkuil break; 7753fa24bf5SHans Verkuil case V4L2_CID_GAIN: 7763fa24bf5SHans Verkuil setgain(gspca_dev, ctrl->val); 7773fa24bf5SHans Verkuil break; 7783fa24bf5SHans Verkuil case V4L2_CID_AUTOGAIN: 7793fa24bf5SHans Verkuil setautogain(gspca_dev, ctrl->val); 7803fa24bf5SHans Verkuil break; 7813fa24bf5SHans Verkuil } 7823fa24bf5SHans Verkuil return gspca_dev->usb_err; 7836a7eba24SJean-Francois Moine } 7846a7eba24SJean-Francois Moine 7853fa24bf5SHans Verkuil static const struct v4l2_ctrl_ops sd_ctrl_ops = { 7863fa24bf5SHans Verkuil .s_ctrl = sd_s_ctrl, 7877879d459SJean-Francois Moine }; 7887879d459SJean-Francois Moine 7893fa24bf5SHans Verkuil static int sd_init_controls_12a(struct gspca_dev *gspca_dev) 7907879d459SJean-Francois Moine { 7913fa24bf5SHans Verkuil struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 7923fa24bf5SHans Verkuil 7933fa24bf5SHans Verkuil gspca_dev->vdev.ctrl_handler = hdl; 7943fa24bf5SHans Verkuil v4l2_ctrl_handler_init(hdl, 3); 7953fa24bf5SHans Verkuil v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 7963fa24bf5SHans Verkuil V4L2_CID_HUE, 1, 0x7f, 1, 0x40); 7973fa24bf5SHans Verkuil v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 798a8931d59SHans de Goede V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); 799a8931d59SHans de Goede v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 8003fa24bf5SHans Verkuil V4L2_CID_EXPOSURE, 1, EXPOSURE_MAX, 1, 700); 8013fa24bf5SHans Verkuil v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 8023fa24bf5SHans Verkuil V4L2_CID_GAIN, 0, 255, 1, 63); 8033fa24bf5SHans Verkuil 8043fa24bf5SHans Verkuil if (hdl->error) { 8053fa24bf5SHans Verkuil pr_err("Could not initialize controls\n"); 8063fa24bf5SHans Verkuil return hdl->error; 8073fa24bf5SHans Verkuil } 8083fa24bf5SHans Verkuil return 0; 8093fa24bf5SHans Verkuil } 8103fa24bf5SHans Verkuil 8113fa24bf5SHans Verkuil static int sd_init_controls_72a(struct gspca_dev *gspca_dev) 8127879d459SJean-Francois Moine { 8133fa24bf5SHans Verkuil struct sd *sd = (struct sd *)gspca_dev; 8143fa24bf5SHans Verkuil struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 8153fa24bf5SHans Verkuil 8163fa24bf5SHans Verkuil gspca_dev->vdev.ctrl_handler = hdl; 8173fa24bf5SHans Verkuil v4l2_ctrl_handler_init(hdl, 4); 8183fa24bf5SHans Verkuil sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 8193fa24bf5SHans Verkuil V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x20); 8203fa24bf5SHans Verkuil sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 8213fa24bf5SHans Verkuil V4L2_CID_HUE, 1, 0x7f, 1, 0x40); 8223fa24bf5SHans Verkuil v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 8233fa24bf5SHans Verkuil V4L2_CID_BRIGHTNESS, 0, 0x3f, 1, 0x20); 8243fa24bf5SHans Verkuil sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 8253fa24bf5SHans Verkuil V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 8263fa24bf5SHans Verkuil 8273fa24bf5SHans Verkuil if (hdl->error) { 8283fa24bf5SHans Verkuil pr_err("Could not initialize controls\n"); 8293fa24bf5SHans Verkuil return hdl->error; 8303fa24bf5SHans Verkuil } 8313fa24bf5SHans Verkuil v4l2_ctrl_cluster(2, &sd->contrast); 8323fa24bf5SHans Verkuil return 0; 8333fa24bf5SHans Verkuil } 8347879d459SJean-Francois Moine 8356a7eba24SJean-Francois Moine /* sub-driver description */ 8367879d459SJean-Francois Moine static const struct sd_desc sd_desc_12a = { 8376a7eba24SJean-Francois Moine .name = MODULE_NAME, 8383fa24bf5SHans Verkuil .init_controls = sd_init_controls_12a, 8396a7eba24SJean-Francois Moine .config = sd_config, 840012d6b02SJean-Francois Moine .init = sd_init_12a, 8417879d459SJean-Francois Moine .start = sd_start_12a, 8427879d459SJean-Francois Moine .stopN = sd_stopN, 8437879d459SJean-Francois Moine .pkt_scan = sd_pkt_scan, 84460d21563SPeter Senna Tschudin #if IS_ENABLED(CONFIG_INPUT) 845436c2c53SHans de Goede .other_input = 1, 846436c2c53SHans de Goede #endif 8477879d459SJean-Francois Moine }; 8487879d459SJean-Francois Moine static const struct sd_desc sd_desc_72a = { 8497879d459SJean-Francois Moine .name = MODULE_NAME, 8503fa24bf5SHans Verkuil .init_controls = sd_init_controls_72a, 8517879d459SJean-Francois Moine .config = sd_config, 852012d6b02SJean-Francois Moine .init = sd_init_72a, 8537879d459SJean-Francois Moine .start = sd_start_72a, 8546a7eba24SJean-Francois Moine .stopN = sd_stopN, 8556a7eba24SJean-Francois Moine .pkt_scan = sd_pkt_scan, 856cebf3b67SJean-Francois Moine .dq_callback = do_autogain, 85760d21563SPeter Senna Tschudin #if IS_ENABLED(CONFIG_INPUT) 858436c2c53SHans de Goede .other_input = 1, 859436c2c53SHans de Goede #endif 8606a7eba24SJean-Francois Moine }; 8617879d459SJean-Francois Moine static const struct sd_desc *sd_desc[2] = { 8627879d459SJean-Francois Moine &sd_desc_12a, 8637879d459SJean-Francois Moine &sd_desc_72a 8647879d459SJean-Francois Moine }; 8656a7eba24SJean-Francois Moine 8666a7eba24SJean-Francois Moine /* -- module initialisation -- */ 86795c967c1SJean-François Moine static const struct usb_device_id device_table[] = { 86887581aa5SJean-Francois Moine {USB_DEVICE(0x041e, 0x401a), .driver_info = Rev072A}, 86987581aa5SJean-Francois Moine {USB_DEVICE(0x041e, 0x403b), .driver_info = Rev012A}, 87087581aa5SJean-Francois Moine {USB_DEVICE(0x0458, 0x7004), .driver_info = Rev072A}, 871f8f73d01SJohn Ellson {USB_DEVICE(0x0461, 0x0815), .driver_info = Rev072A}, 87287581aa5SJean-Francois Moine {USB_DEVICE(0x046d, 0x0928), .driver_info = Rev012A}, 87387581aa5SJean-Francois Moine {USB_DEVICE(0x046d, 0x0929), .driver_info = Rev012A}, 87487581aa5SJean-Francois Moine {USB_DEVICE(0x046d, 0x092a), .driver_info = Rev012A}, 87587581aa5SJean-Francois Moine {USB_DEVICE(0x046d, 0x092b), .driver_info = Rev012A}, 87687581aa5SJean-Francois Moine {USB_DEVICE(0x046d, 0x092c), .driver_info = Rev012A}, 87787581aa5SJean-Francois Moine {USB_DEVICE(0x046d, 0x092d), .driver_info = Rev012A}, 87887581aa5SJean-Francois Moine {USB_DEVICE(0x046d, 0x092e), .driver_info = Rev012A}, 87987581aa5SJean-Francois Moine {USB_DEVICE(0x046d, 0x092f), .driver_info = Rev012A}, 88087581aa5SJean-Francois Moine {USB_DEVICE(0x04fc, 0x0561), .driver_info = Rev072A}, 88187581aa5SJean-Francois Moine {USB_DEVICE(0x060b, 0xa001), .driver_info = Rev072A}, 88287581aa5SJean-Francois Moine {USB_DEVICE(0x10fd, 0x7e50), .driver_info = Rev072A}, 88387581aa5SJean-Francois Moine {USB_DEVICE(0xabcd, 0xcdee), .driver_info = Rev072A}, 8846a7eba24SJean-Francois Moine {} 8856a7eba24SJean-Francois Moine }; 8866a7eba24SJean-Francois Moine 8876a7eba24SJean-Francois Moine MODULE_DEVICE_TABLE(usb, device_table); 8886a7eba24SJean-Francois Moine 8896a7eba24SJean-Francois Moine /* -- device connect -- */ 8906a7eba24SJean-Francois Moine static int sd_probe(struct usb_interface *intf, 8916a7eba24SJean-Francois Moine const struct usb_device_id *id) 8926a7eba24SJean-Francois Moine { 8937879d459SJean-Francois Moine return gspca_dev_probe(intf, id, 8947879d459SJean-Francois Moine sd_desc[id->driver_info], 8957879d459SJean-Francois Moine sizeof(struct sd), 8966a7eba24SJean-Francois Moine THIS_MODULE); 8976a7eba24SJean-Francois Moine } 8986a7eba24SJean-Francois Moine 8996a7eba24SJean-Francois Moine static struct usb_driver sd_driver = { 9006a7eba24SJean-Francois Moine .name = MODULE_NAME, 9016a7eba24SJean-Francois Moine .id_table = device_table, 9026a7eba24SJean-Francois Moine .probe = sd_probe, 9036a7eba24SJean-Francois Moine .disconnect = gspca_disconnect, 9046a709749SJean-Francois Moine #ifdef CONFIG_PM 9056a709749SJean-Francois Moine .suspend = gspca_suspend, 9066a709749SJean-Francois Moine .resume = gspca_resume, 9078bb58964SHans de Goede .reset_resume = gspca_resume, 9086a709749SJean-Francois Moine #endif 9096a7eba24SJean-Francois Moine }; 9106a7eba24SJean-Francois Moine 911ecb3b2b3SGreg Kroah-Hartman module_usb_driver(sd_driver); 912