18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ToupTek UCMOS / AmScope MU series camera driver 48c2ecf20Sopenharmony_ci * TODO: contrast with ScopeTek / AmScope MDC cameras 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2012-2014 John McMaster <JohnDMcMaster@gmail.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Special thanks to Bushing for helping with the decrypt algorithm and 98c2ecf20Sopenharmony_ci * Sean O'Sullivan / the Rensselaer Center for Open Source 108c2ecf20Sopenharmony_ci * Software (RCOS) for helping me learn kernel development 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "gspca.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define MODULE_NAME "touptek" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ciMODULE_AUTHOR("John McMaster"); 188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ToupTek UCMOS / Amscope MU microscope camera driver"); 198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * Exposure reg is linear with exposure time 238c2ecf20Sopenharmony_ci * Exposure (sec), E (reg) 248c2ecf20Sopenharmony_ci * 0.000400, 0x0002 258c2ecf20Sopenharmony_ci * 0.001000, 0x0005 268c2ecf20Sopenharmony_ci * 0.005000, 0x0019 278c2ecf20Sopenharmony_ci * 0.020000, 0x0064 288c2ecf20Sopenharmony_ci * 0.080000, 0x0190 298c2ecf20Sopenharmony_ci * 0.400000, 0x07D0 308c2ecf20Sopenharmony_ci * 1.000000, 0x1388 318c2ecf20Sopenharmony_ci * 2.000000, 0x2710 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * Three gain stages 348c2ecf20Sopenharmony_ci * 0x1000: master channel enable bit 358c2ecf20Sopenharmony_ci * 0x007F: low gain bits 368c2ecf20Sopenharmony_ci * 0x0080: medium gain bit 378c2ecf20Sopenharmony_ci * 0x0100: high gain bit 388c2ecf20Sopenharmony_ci * gain = enable * (1 + regH) * (1 + regM) * z * regL 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * Gain implementation 418c2ecf20Sopenharmony_ci * Want to do something similar to mt9v011.c's set_balance 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * Gain does not vary with resolution (checked 640x480 vs 1600x1200) 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * Constant derivation: 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * Raw data: 488c2ecf20Sopenharmony_ci * Gain, GTOP, B, R, GBOT 498c2ecf20Sopenharmony_ci * 1.00, 0x105C, 0x1068, 0x10C8, 0x105C 508c2ecf20Sopenharmony_ci * 1.20, 0x106E, 0x107E, 0x10D6, 0x106E 518c2ecf20Sopenharmony_ci * 1.40, 0x10C0, 0x10CA, 0x10E5, 0x10C0 528c2ecf20Sopenharmony_ci * 1.60, 0x10C9, 0x10D4, 0x10F3, 0x10C9 538c2ecf20Sopenharmony_ci * 1.80, 0x10D2, 0x10DE, 0x11C1, 0x10D2 548c2ecf20Sopenharmony_ci * 2.00, 0x10DC, 0x10E9, 0x11C8, 0x10DC 558c2ecf20Sopenharmony_ci * 2.20, 0x10E5, 0x10F3, 0x11CF, 0x10E5 568c2ecf20Sopenharmony_ci * 2.40, 0x10EE, 0x10FE, 0x11D7, 0x10EE 578c2ecf20Sopenharmony_ci * 2.60, 0x10F7, 0x11C4, 0x11DE, 0x10F7 588c2ecf20Sopenharmony_ci * 2.80, 0x11C0, 0x11CA, 0x11E5, 0x11C0 598c2ecf20Sopenharmony_ci * 3.00, 0x11C5, 0x11CF, 0x11ED, 0x11C5 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * zR = 0.0069605943152454778 628c2ecf20Sopenharmony_ci * about 3/431 = 0.0069605568445475635 638c2ecf20Sopenharmony_ci * zB = 0.0095695970695970703 648c2ecf20Sopenharmony_ci * about 6/627 = 0.0095693779904306216 658c2ecf20Sopenharmony_ci * zG = 0.010889328063241107 668c2ecf20Sopenharmony_ci * about 6/551 = 0.010889292196007259 678c2ecf20Sopenharmony_ci * about 10 bits for constant + 7 bits for value => at least 17 bit 688c2ecf20Sopenharmony_ci * intermediate with 32 bit ints should be fine for overflow etc 698c2ecf20Sopenharmony_ci * Essentially gains are in range 0-0x001FF 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * However, V4L expects a main gain channel + R and B balance 728c2ecf20Sopenharmony_ci * To keep things simple for now saturate the values of balance is too high/low 738c2ecf20Sopenharmony_ci * This isn't really ideal but easy way to fit the Linux model 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * Converted using gain model turns out to be quite linear: 768c2ecf20Sopenharmony_ci * Gain, GTOP, B, R, GBOT 778c2ecf20Sopenharmony_ci * 1.00, 92, 104, 144, 92 788c2ecf20Sopenharmony_ci * 1.20, 110, 126, 172, 110 798c2ecf20Sopenharmony_ci * 1.40, 128, 148, 202, 128 808c2ecf20Sopenharmony_ci * 1.60, 146, 168, 230, 146 818c2ecf20Sopenharmony_ci * 1.80, 164, 188, 260, 164 828c2ecf20Sopenharmony_ci * 2.00, 184, 210, 288, 184 838c2ecf20Sopenharmony_ci * 2.20, 202, 230, 316, 202 848c2ecf20Sopenharmony_ci * 2.40, 220, 252, 348, 220 858c2ecf20Sopenharmony_ci * 2.60, 238, 272, 376, 238 868c2ecf20Sopenharmony_ci * 2.80, 256, 296, 404, 256 878c2ecf20Sopenharmony_ci * 3.00, 276, 316, 436, 276 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * Maximum gain is 0x7FF * 2 * 2 => 0x1FFC (8188) 908c2ecf20Sopenharmony_ci * or about 13 effective bits of gain 918c2ecf20Sopenharmony_ci * The highest the commercial driver goes in my setup 436 928c2ecf20Sopenharmony_ci * However, because could *maybe* damage circuits 938c2ecf20Sopenharmony_ci * limit the gain until have a reason to go higher 948c2ecf20Sopenharmony_ci * Solution: gain clipped and warning emitted 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci#define GAIN_MAX 511 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Frame sync is a short read */ 998c2ecf20Sopenharmony_ci#define BULK_SIZE 0x4000 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* MT9E001 reg names to give a rough approximation */ 1028c2ecf20Sopenharmony_ci#define REG_COARSE_INTEGRATION_TIME_ 0x3012 1038c2ecf20Sopenharmony_ci#define REG_GROUPED_PARAMETER_HOLD_ 0x3022 1048c2ecf20Sopenharmony_ci#define REG_MODE_SELECT 0x0100 1058c2ecf20Sopenharmony_ci#define REG_OP_SYS_CLK_DIV 0x030A 1068c2ecf20Sopenharmony_ci#define REG_VT_SYS_CLK_DIV 0x0302 1078c2ecf20Sopenharmony_ci#define REG_PRE_PLL_CLK_DIV 0x0304 1088c2ecf20Sopenharmony_ci#define REG_VT_PIX_CLK_DIV 0x0300 1098c2ecf20Sopenharmony_ci#define REG_OP_PIX_CLK_DIV 0x0308 1108c2ecf20Sopenharmony_ci#define REG_PLL_MULTIPLIER 0x0306 1118c2ecf20Sopenharmony_ci#define REG_COARSE_INTEGRATION_TIME_ 0x3012 1128c2ecf20Sopenharmony_ci#define REG_FRAME_LENGTH_LINES 0x0340 1138c2ecf20Sopenharmony_ci#define REG_FRAME_LENGTH_LINES_ 0x300A 1148c2ecf20Sopenharmony_ci#define REG_GREEN1_GAIN 0x3056 1158c2ecf20Sopenharmony_ci#define REG_GREEN2_GAIN 0x305C 1168c2ecf20Sopenharmony_ci#define REG_GROUPED_PARAMETER_HOLD 0x0104 1178c2ecf20Sopenharmony_ci#define REG_LINE_LENGTH_PCK_ 0x300C 1188c2ecf20Sopenharmony_ci#define REG_MODE_SELECT 0x0100 1198c2ecf20Sopenharmony_ci#define REG_PLL_MULTIPLIER 0x0306 1208c2ecf20Sopenharmony_ci#define REG_READ_MODE 0x3040 1218c2ecf20Sopenharmony_ci#define REG_BLUE_GAIN 0x3058 1228c2ecf20Sopenharmony_ci#define REG_RED_GAIN 0x305A 1238c2ecf20Sopenharmony_ci#define REG_RESET_REGISTER 0x301A 1248c2ecf20Sopenharmony_ci#define REG_SCALE_M 0x0404 1258c2ecf20Sopenharmony_ci#define REG_SCALING_MODE 0x0400 1268c2ecf20Sopenharmony_ci#define REG_SOFTWARE_RESET 0x0103 1278c2ecf20Sopenharmony_ci#define REG_X_ADDR_END 0x0348 1288c2ecf20Sopenharmony_ci#define REG_X_ADDR_START 0x0344 1298c2ecf20Sopenharmony_ci#define REG_X_ADDR_START 0x0344 1308c2ecf20Sopenharmony_ci#define REG_X_OUTPUT_SIZE 0x034C 1318c2ecf20Sopenharmony_ci#define REG_Y_ADDR_END 0x034A 1328c2ecf20Sopenharmony_ci#define REG_Y_ADDR_START 0x0346 1338c2ecf20Sopenharmony_ci#define REG_Y_OUTPUT_SIZE 0x034E 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* specific webcam descriptor */ 1378c2ecf20Sopenharmony_cistruct sd { 1388c2ecf20Sopenharmony_ci struct gspca_dev gspca_dev; /* !! must be the first item */ 1398c2ecf20Sopenharmony_ci /* How many bytes this frame */ 1408c2ecf20Sopenharmony_ci unsigned int this_f; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci Device has separate gains for each Bayer quadrant 1448c2ecf20Sopenharmony_ci V4L supports master gain which is referenced to G1/G2 and supplies 1458c2ecf20Sopenharmony_ci individual balance controls for R/B 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci struct v4l2_ctrl *blue; 1488c2ecf20Sopenharmony_ci struct v4l2_ctrl *red; 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* Used to simplify reg write error handling */ 1528c2ecf20Sopenharmony_cistruct cmd { 1538c2ecf20Sopenharmony_ci u16 value; 1548c2ecf20Sopenharmony_ci u16 index; 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format vga_mode[] = { 1588c2ecf20Sopenharmony_ci {800, 600, 1598c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 1608c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 1618c2ecf20Sopenharmony_ci .bytesperline = 800, 1628c2ecf20Sopenharmony_ci .sizeimage = 800 * 600, 1638c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB}, 1648c2ecf20Sopenharmony_ci {1600, 1200, 1658c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 1668c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 1678c2ecf20Sopenharmony_ci .bytesperline = 1600, 1688c2ecf20Sopenharmony_ci .sizeimage = 1600 * 1200, 1698c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB}, 1708c2ecf20Sopenharmony_ci {3264, 2448, 1718c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 1728c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 1738c2ecf20Sopenharmony_ci .bytesperline = 3264, 1748c2ecf20Sopenharmony_ci .sizeimage = 3264 * 2448, 1758c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB}, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* 1798c2ecf20Sopenharmony_ci * As there's no known frame sync, the only way to keep synced is to try hard 1808c2ecf20Sopenharmony_ci * to never miss any packets 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci#if MAX_NURBS < 4 1838c2ecf20Sopenharmony_ci#error "Not enough URBs in the gspca table" 1848c2ecf20Sopenharmony_ci#endif 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int val_reply(struct gspca_dev *gspca_dev, const char *reply, int rc) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci if (rc < 0) { 1898c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "reply has error %d\n", rc); 1908c2ecf20Sopenharmony_ci return -EIO; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci if (rc != 1) { 1938c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "Bad reply size %d\n", rc); 1948c2ecf20Sopenharmony_ci return -EIO; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci if (reply[0] != 0x08) { 1978c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "Bad reply 0x%02x\n", (int)reply[0]); 1988c2ecf20Sopenharmony_ci return -EIO; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci char *buff = gspca_dev->usb_buf; 2068c2ecf20Sopenharmony_ci int rc; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_USBO, 2098c2ecf20Sopenharmony_ci "reg_w bReq=0x0B, bReqT=0xC0, wVal=0x%04X, wInd=0x%04X\n\n", 2108c2ecf20Sopenharmony_ci value, index); 2118c2ecf20Sopenharmony_ci rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), 2128c2ecf20Sopenharmony_ci 0x0B, 0xC0, value, index, buff, 1, 500); 2138c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_USBO, "rc=%d, ret={0x%02x}\n", rc, (int)buff[0]); 2148c2ecf20Sopenharmony_ci if (rc < 0) { 2158c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "Failed reg_w(0x0B, 0xC0, 0x%04X, 0x%04X) w/ rc %d\n", 2168c2ecf20Sopenharmony_ci value, index, rc); 2178c2ecf20Sopenharmony_ci gspca_dev->usb_err = rc; 2188c2ecf20Sopenharmony_ci return; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci if (val_reply(gspca_dev, buff, rc)) { 2218c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "Bad reply to reg_w(0x0B, 0xC0, 0x%04X, 0x%04X\n", 2228c2ecf20Sopenharmony_ci value, index); 2238c2ecf20Sopenharmony_ci gspca_dev->usb_err = -EIO; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic void reg_w_buf(struct gspca_dev *gspca_dev, 2288c2ecf20Sopenharmony_ci const struct cmd *p, int l) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci do { 2318c2ecf20Sopenharmony_ci reg_w(gspca_dev, p->value, p->index); 2328c2ecf20Sopenharmony_ci p++; 2338c2ecf20Sopenharmony_ci } while (--l > 0); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void setexposure(struct gspca_dev *gspca_dev, s32 val) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci u16 value; 2398c2ecf20Sopenharmony_ci unsigned int w = gspca_dev->pixfmt.width; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (w == 800) 2428c2ecf20Sopenharmony_ci value = val * 5; 2438c2ecf20Sopenharmony_ci else if (w == 1600) 2448c2ecf20Sopenharmony_ci value = val * 3; 2458c2ecf20Sopenharmony_ci else if (w == 3264) 2468c2ecf20Sopenharmony_ci value = val * 3 / 2; 2478c2ecf20Sopenharmony_ci else { 2488c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "Invalid width %u\n", w); 2498c2ecf20Sopenharmony_ci gspca_dev->usb_err = -EINVAL; 2508c2ecf20Sopenharmony_ci return; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "exposure: 0x%04X ms\n\n", value); 2538c2ecf20Sopenharmony_ci /* Wonder if there's a good reason for sending it twice */ 2548c2ecf20Sopenharmony_ci /* probably not but leave it in because...why not */ 2558c2ecf20Sopenharmony_ci reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_); 2568c2ecf20Sopenharmony_ci reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int gainify(int in) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci /* 2628c2ecf20Sopenharmony_ci * TODO: check if there are any issues with corner cases 2638c2ecf20Sopenharmony_ci * 0x000 (0):0x07F (127): regL 2648c2ecf20Sopenharmony_ci * 0x080 (128) - 0x0FF (255): regM, regL 2658c2ecf20Sopenharmony_ci * 0x100 (256) - max: regH, regM, regL 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci if (in <= 0x7F) 2688c2ecf20Sopenharmony_ci return 0x1000 | in; 2698c2ecf20Sopenharmony_ci else if (in <= 0xFF) 2708c2ecf20Sopenharmony_ci return 0x1080 | in / 2; 2718c2ecf20Sopenharmony_ci else 2728c2ecf20Sopenharmony_ci return 0x1180 | in / 4; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic void setggain(struct gspca_dev *gspca_dev, u16 global_gain) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci u16 normalized; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci normalized = gainify(global_gain); 2808c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "gain G1/G2 (0x%04X): 0x%04X (src 0x%04X)\n\n", 2818c2ecf20Sopenharmony_ci REG_GREEN1_GAIN, 2828c2ecf20Sopenharmony_ci normalized, global_gain); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci reg_w(gspca_dev, normalized, REG_GREEN1_GAIN); 2858c2ecf20Sopenharmony_ci reg_w(gspca_dev, normalized, REG_GREEN2_GAIN); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic void setbgain(struct gspca_dev *gspca_dev, 2898c2ecf20Sopenharmony_ci u16 gain, u16 global_gain) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci u16 normalized; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci normalized = global_gain + 2948c2ecf20Sopenharmony_ci ((u32)global_gain) * gain / GAIN_MAX; 2958c2ecf20Sopenharmony_ci if (normalized > GAIN_MAX) { 2968c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Truncating blue 0x%04X w/ value 0x%04X\n\n", 2978c2ecf20Sopenharmony_ci GAIN_MAX, normalized); 2988c2ecf20Sopenharmony_ci normalized = GAIN_MAX; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci normalized = gainify(normalized); 3018c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "gain B (0x%04X): 0x%04X w/ source 0x%04X\n\n", 3028c2ecf20Sopenharmony_ci REG_BLUE_GAIN, normalized, gain); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci reg_w(gspca_dev, normalized, REG_BLUE_GAIN); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic void setrgain(struct gspca_dev *gspca_dev, 3088c2ecf20Sopenharmony_ci u16 gain, u16 global_gain) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci u16 normalized; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci normalized = global_gain + 3138c2ecf20Sopenharmony_ci ((u32)global_gain) * gain / GAIN_MAX; 3148c2ecf20Sopenharmony_ci if (normalized > GAIN_MAX) { 3158c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Truncating gain 0x%04X w/ value 0x%04X\n\n", 3168c2ecf20Sopenharmony_ci GAIN_MAX, normalized); 3178c2ecf20Sopenharmony_ci normalized = GAIN_MAX; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci normalized = gainify(normalized); 3208c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "gain R (0x%04X): 0x%04X w / source 0x%04X\n\n", 3218c2ecf20Sopenharmony_ci REG_RED_GAIN, normalized, gain); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci reg_w(gspca_dev, normalized, REG_RED_GAIN); 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic void configure_wh(struct gspca_dev *gspca_dev) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci unsigned int w = gspca_dev->pixfmt.width; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "configure_wh\n\n"); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (w == 800) { 3338c2ecf20Sopenharmony_ci static const struct cmd reg_init_res[] = { 3348c2ecf20Sopenharmony_ci {0x0060, REG_X_ADDR_START}, 3358c2ecf20Sopenharmony_ci {0x0CD9, REG_X_ADDR_END}, 3368c2ecf20Sopenharmony_ci {0x0036, REG_Y_ADDR_START}, 3378c2ecf20Sopenharmony_ci {0x098F, REG_Y_ADDR_END}, 3388c2ecf20Sopenharmony_ci {0x07C7, REG_READ_MODE}, 3398c2ecf20Sopenharmony_ci }; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci reg_w_buf(gspca_dev, 3428c2ecf20Sopenharmony_ci reg_init_res, ARRAY_SIZE(reg_init_res)); 3438c2ecf20Sopenharmony_ci } else if (w == 1600) { 3448c2ecf20Sopenharmony_ci static const struct cmd reg_init_res[] = { 3458c2ecf20Sopenharmony_ci {0x009C, REG_X_ADDR_START}, 3468c2ecf20Sopenharmony_ci {0x0D19, REG_X_ADDR_END}, 3478c2ecf20Sopenharmony_ci {0x0068, REG_Y_ADDR_START}, 3488c2ecf20Sopenharmony_ci {0x09C5, REG_Y_ADDR_END}, 3498c2ecf20Sopenharmony_ci {0x06C3, REG_READ_MODE}, 3508c2ecf20Sopenharmony_ci }; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci reg_w_buf(gspca_dev, 3538c2ecf20Sopenharmony_ci reg_init_res, ARRAY_SIZE(reg_init_res)); 3548c2ecf20Sopenharmony_ci } else if (w == 3264) { 3558c2ecf20Sopenharmony_ci static const struct cmd reg_init_res[] = { 3568c2ecf20Sopenharmony_ci {0x00E8, REG_X_ADDR_START}, 3578c2ecf20Sopenharmony_ci {0x0DA7, REG_X_ADDR_END}, 3588c2ecf20Sopenharmony_ci {0x009E, REG_Y_ADDR_START}, 3598c2ecf20Sopenharmony_ci {0x0A2D, REG_Y_ADDR_END}, 3608c2ecf20Sopenharmony_ci {0x0241, REG_READ_MODE}, 3618c2ecf20Sopenharmony_ci }; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci reg_w_buf(gspca_dev, 3648c2ecf20Sopenharmony_ci reg_init_res, ARRAY_SIZE(reg_init_res)); 3658c2ecf20Sopenharmony_ci } else { 3668c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "bad width %u\n", w); 3678c2ecf20Sopenharmony_ci gspca_dev->usb_err = -EINVAL; 3688c2ecf20Sopenharmony_ci return; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0000, REG_SCALING_MODE); 3728c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0010, REG_SCALE_M); 3738c2ecf20Sopenharmony_ci reg_w(gspca_dev, w, REG_X_OUTPUT_SIZE); 3748c2ecf20Sopenharmony_ci reg_w(gspca_dev, gspca_dev->pixfmt.height, REG_Y_OUTPUT_SIZE); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (w == 800) { 3778c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0384, REG_FRAME_LENGTH_LINES_); 3788c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0960, REG_LINE_LENGTH_PCK_); 3798c2ecf20Sopenharmony_ci } else if (w == 1600) { 3808c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0640, REG_FRAME_LENGTH_LINES_); 3818c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0FA0, REG_LINE_LENGTH_PCK_); 3828c2ecf20Sopenharmony_ci } else if (w == 3264) { 3838c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0B4B, REG_FRAME_LENGTH_LINES_); 3848c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x1F40, REG_LINE_LENGTH_PCK_); 3858c2ecf20Sopenharmony_ci } else { 3868c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "bad width %u\n", w); 3878c2ecf20Sopenharmony_ci gspca_dev->usb_err = -EINVAL; 3888c2ecf20Sopenharmony_ci return; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/* Packets that were encrypted, no idea if the grouping is significant */ 3938c2ecf20Sopenharmony_cistatic void configure_encrypted(struct gspca_dev *gspca_dev) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci static const struct cmd reg_init_begin[] = { 3968c2ecf20Sopenharmony_ci {0x0100, REG_SOFTWARE_RESET}, 3978c2ecf20Sopenharmony_ci {0x0000, REG_MODE_SELECT}, 3988c2ecf20Sopenharmony_ci {0x0100, REG_GROUPED_PARAMETER_HOLD}, 3998c2ecf20Sopenharmony_ci {0x0004, REG_VT_PIX_CLK_DIV}, 4008c2ecf20Sopenharmony_ci {0x0001, REG_VT_SYS_CLK_DIV}, 4018c2ecf20Sopenharmony_ci {0x0008, REG_OP_PIX_CLK_DIV}, 4028c2ecf20Sopenharmony_ci {0x0001, REG_OP_SYS_CLK_DIV}, 4038c2ecf20Sopenharmony_ci {0x0004, REG_PRE_PLL_CLK_DIV}, 4048c2ecf20Sopenharmony_ci {0x0040, REG_PLL_MULTIPLIER}, 4058c2ecf20Sopenharmony_ci {0x0000, REG_GROUPED_PARAMETER_HOLD}, 4068c2ecf20Sopenharmony_ci {0x0100, REG_GROUPED_PARAMETER_HOLD}, 4078c2ecf20Sopenharmony_ci }; 4088c2ecf20Sopenharmony_ci static const struct cmd reg_init_end[] = { 4098c2ecf20Sopenharmony_ci {0x0000, REG_GROUPED_PARAMETER_HOLD}, 4108c2ecf20Sopenharmony_ci {0x0301, 0x31AE}, 4118c2ecf20Sopenharmony_ci {0x0805, 0x3064}, 4128c2ecf20Sopenharmony_ci {0x0071, 0x3170}, 4138c2ecf20Sopenharmony_ci {0x10DE, REG_RESET_REGISTER}, 4148c2ecf20Sopenharmony_ci {0x0000, REG_MODE_SELECT}, 4158c2ecf20Sopenharmony_ci {0x0010, REG_PLL_MULTIPLIER}, 4168c2ecf20Sopenharmony_ci {0x0100, REG_MODE_SELECT}, 4178c2ecf20Sopenharmony_ci }; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Encrypted begin, w = %u\n\n", 4208c2ecf20Sopenharmony_ci gspca_dev->pixfmt.width); 4218c2ecf20Sopenharmony_ci reg_w_buf(gspca_dev, reg_init_begin, ARRAY_SIZE(reg_init_begin)); 4228c2ecf20Sopenharmony_ci configure_wh(gspca_dev); 4238c2ecf20Sopenharmony_ci reg_w_buf(gspca_dev, reg_init_end, ARRAY_SIZE(reg_init_end)); 4248c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0100, REG_GROUPED_PARAMETER_HOLD); 4258c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0000, REG_GROUPED_PARAMETER_HOLD); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Encrypted end\n\n"); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int configure(struct gspca_dev *gspca_dev) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci int rc; 4338c2ecf20Sopenharmony_ci char *buff = gspca_dev->usb_buf; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "configure()\n\n"); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* 4388c2ecf20Sopenharmony_ci * First driver sets a sort of encryption key 4398c2ecf20Sopenharmony_ci * A number of futur requests of this type have wValue and wIndex 4408c2ecf20Sopenharmony_ci * encrypted as follows: 4418c2ecf20Sopenharmony_ci * -Compute key = this wValue rotate left by 4 bits 4428c2ecf20Sopenharmony_ci * (decrypt.py rotates right because we are decrypting) 4438c2ecf20Sopenharmony_ci * -Later packets encrypt packets by XOR'ing with key 4448c2ecf20Sopenharmony_ci * XOR encrypt/decrypt is symmetrical 4458c2ecf20Sopenharmony_ci * wValue, and wIndex are encrypted 4468c2ecf20Sopenharmony_ci * bRequest is not and bRequestType is always 0xC0 4478c2ecf20Sopenharmony_ci * This allows resyncing if key is unknown? 4488c2ecf20Sopenharmony_ci * By setting 0 we XOR with 0 and the shifting and XOR drops out 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_ci rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), 4518c2ecf20Sopenharmony_ci 0x16, 0xC0, 0x0000, 0x0000, buff, 2, 500); 4528c2ecf20Sopenharmony_ci if (val_reply(gspca_dev, buff, rc)) { 4538c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "failed key req\n"); 4548c2ecf20Sopenharmony_ci return -EIO; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * Next does some sort of 2 packet challenge / response 4598c2ecf20Sopenharmony_ci * evidence suggests its an Atmel I2C crypto part but nobody cares to 4608c2ecf20Sopenharmony_ci * look 4618c2ecf20Sopenharmony_ci * (to make sure its not cloned hardware?) 4628c2ecf20Sopenharmony_ci * Ignore: I want to work with their hardware, not clone it 4638c2ecf20Sopenharmony_ci * 16 bytes out challenge, requestType: 0x40 4648c2ecf20Sopenharmony_ci * 16 bytes in response, requestType: 0xC0 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 4688c2ecf20Sopenharmony_ci 0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500); 4698c2ecf20Sopenharmony_ci if (rc < 0) { 4708c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "failed to replay packet 176 w/ rc %d\n", 4718c2ecf20Sopenharmony_ci rc); 4728c2ecf20Sopenharmony_ci return rc; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 4768c2ecf20Sopenharmony_ci 0x01, 0x40, 0x0000, 0x000F, NULL, 0, 500); 4778c2ecf20Sopenharmony_ci if (rc < 0) { 4788c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "failed to replay packet 178 w/ rc %d\n", 4798c2ecf20Sopenharmony_ci rc); 4808c2ecf20Sopenharmony_ci return rc; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 4848c2ecf20Sopenharmony_ci 0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500); 4858c2ecf20Sopenharmony_ci if (rc < 0) { 4868c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "failed to replay packet 180 w/ rc %d\n", 4878c2ecf20Sopenharmony_ci rc); 4888c2ecf20Sopenharmony_ci return rc; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* 4928c2ecf20Sopenharmony_ci * Serial number? Doesn't seem to be required 4938c2ecf20Sopenharmony_ci * cam1: \xE6\x0D\x00\x00, cam2: \x70\x19\x00\x00 4948c2ecf20Sopenharmony_ci * rc = usb_control_msg(gspca_dev->dev, 4958c2ecf20Sopenharmony_ci * usb_rcvctrlpipe(gspca_dev->dev, 0), 4968c2ecf20Sopenharmony_ci * 0x20, 0xC0, 0x0000, 0x0000, buff, 4, 500); 4978c2ecf20Sopenharmony_ci */ 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* Large (EEPROM?) read, skip it since no idea what to do with it */ 5008c2ecf20Sopenharmony_ci gspca_dev->usb_err = 0; 5018c2ecf20Sopenharmony_ci configure_encrypted(gspca_dev); 5028c2ecf20Sopenharmony_ci if (gspca_dev->usb_err) 5038c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* Omitted this by accident, does not work without it */ 5068c2ecf20Sopenharmony_ci rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 5078c2ecf20Sopenharmony_ci 0x01, 0x40, 0x0003, 0x000F, NULL, 0, 500); 5088c2ecf20Sopenharmony_ci if (rc < 0) { 5098c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "failed to replay final packet w/ rc %d\n", 5108c2ecf20Sopenharmony_ci rc); 5118c2ecf20Sopenharmony_ci return rc; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Configure complete\n\n"); 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 5198c2ecf20Sopenharmony_ci const struct usb_device_id *id) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci gspca_dev->cam.cam_mode = vga_mode; 5228c2ecf20Sopenharmony_ci gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* Yes we want URBs and we want them now! */ 5258c2ecf20Sopenharmony_ci gspca_dev->cam.no_urb_create = 0; 5268c2ecf20Sopenharmony_ci gspca_dev->cam.bulk_nurbs = 4; 5278c2ecf20Sopenharmony_ci /* Largest size the windows driver uses */ 5288c2ecf20Sopenharmony_ci gspca_dev->cam.bulk_size = BULK_SIZE; 5298c2ecf20Sopenharmony_ci /* Def need to use bulk transfers */ 5308c2ecf20Sopenharmony_ci gspca_dev->cam.bulk = 1; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5388c2ecf20Sopenharmony_ci int rc; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci sd->this_f = 0; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci rc = configure(gspca_dev); 5438c2ecf20Sopenharmony_ci if (rc < 0) { 5448c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "Failed configure\n"); 5458c2ecf20Sopenharmony_ci return rc; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci /* First two frames have messed up gains 5488c2ecf20Sopenharmony_ci Drop them to avoid special cases in user apps? */ 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, 5538c2ecf20Sopenharmony_ci u8 *data, /* isoc packet */ 5548c2ecf20Sopenharmony_ci int len) /* iso packet length */ 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (len != BULK_SIZE) { 5598c2ecf20Sopenharmony_ci /* can we finish a frame? */ 5608c2ecf20Sopenharmony_ci if (sd->this_f + len == gspca_dev->pixfmt.sizeimage) { 5618c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, LAST_PACKET, data, len); 5628c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_FRAM, "finish frame sz %u/%u w/ len %u\n\n", 5638c2ecf20Sopenharmony_ci sd->this_f, gspca_dev->pixfmt.sizeimage, len); 5648c2ecf20Sopenharmony_ci /* lost some data, discard the frame */ 5658c2ecf20Sopenharmony_ci } else { 5668c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, DISCARD_PACKET, NULL, 0); 5678c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_FRAM, "abort frame sz %u/%u w/ len %u\n\n", 5688c2ecf20Sopenharmony_ci sd->this_f, gspca_dev->pixfmt.sizeimage, len); 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci sd->this_f = 0; 5718c2ecf20Sopenharmony_ci } else { 5728c2ecf20Sopenharmony_ci if (sd->this_f == 0) 5738c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); 5748c2ecf20Sopenharmony_ci else 5758c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, INTER_PACKET, data, len); 5768c2ecf20Sopenharmony_ci sd->this_f += len; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = 5888c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 5898c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci gspca_dev->usb_err = 0; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (!gspca_dev->streaming) 5948c2ecf20Sopenharmony_ci return 0; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci switch (ctrl->id) { 5978c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE: 5988c2ecf20Sopenharmony_ci setexposure(gspca_dev, ctrl->val); 5998c2ecf20Sopenharmony_ci break; 6008c2ecf20Sopenharmony_ci case V4L2_CID_GAIN: 6018c2ecf20Sopenharmony_ci /* gspca_dev->gain automatically updated */ 6028c2ecf20Sopenharmony_ci setggain(gspca_dev, gspca_dev->gain->val); 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci case V4L2_CID_BLUE_BALANCE: 6058c2ecf20Sopenharmony_ci sd->blue->val = ctrl->val; 6068c2ecf20Sopenharmony_ci setbgain(gspca_dev, sd->blue->val, gspca_dev->gain->val); 6078c2ecf20Sopenharmony_ci break; 6088c2ecf20Sopenharmony_ci case V4L2_CID_RED_BALANCE: 6098c2ecf20Sopenharmony_ci sd->red->val = ctrl->val; 6108c2ecf20Sopenharmony_ci setrgain(gspca_dev, sd->red->val, gspca_dev->gain->val); 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = { 6178c2ecf20Sopenharmony_ci .s_ctrl = sd_s_ctrl, 6188c2ecf20Sopenharmony_ci}; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 6238c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci gspca_dev->vdev.ctrl_handler = hdl; 6268c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 4); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6298c2ecf20Sopenharmony_ci /* Mostly limited by URB timeouts */ 6308c2ecf20Sopenharmony_ci /* XXX: make dynamic based on frame rate? */ 6318c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 800, 1, 350); 6328c2ecf20Sopenharmony_ci gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6338c2ecf20Sopenharmony_ci V4L2_CID_GAIN, 0, 511, 1, 128); 6348c2ecf20Sopenharmony_ci sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6358c2ecf20Sopenharmony_ci V4L2_CID_BLUE_BALANCE, 0, 1023, 1, 80); 6368c2ecf20Sopenharmony_ci sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6378c2ecf20Sopenharmony_ci V4L2_CID_RED_BALANCE, 0, 1023, 1, 295); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (hdl->error) { 6408c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "Could not initialize controls\n"); 6418c2ecf20Sopenharmony_ci return hdl->error; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci return 0; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci/* sub-driver description */ 6478c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = { 6488c2ecf20Sopenharmony_ci .name = MODULE_NAME, 6498c2ecf20Sopenharmony_ci .config = sd_config, 6508c2ecf20Sopenharmony_ci .init = sd_init, 6518c2ecf20Sopenharmony_ci .init_controls = sd_init_controls, 6528c2ecf20Sopenharmony_ci .start = sd_start, 6538c2ecf20Sopenharmony_ci .pkt_scan = sd_pkt_scan, 6548c2ecf20Sopenharmony_ci}; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci/* Table of supported USB devices */ 6578c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = { 6588c2ecf20Sopenharmony_ci /* Commented out devices should be related */ 6598c2ecf20Sopenharmony_ci /* AS: AmScope, TT: ToupTek */ 6608c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6035) }, TT UCMOS00350KPA */ 6618c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6130) }, TT UCMOS01300KPA */ 6628c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6200) }, TT UCMOS02000KPA */ 6638c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6310) }, TT UCMOS03100KPA */ 6648c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6510) }, TT UCMOS05100KPA */ 6658c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6800) }, TT UCMOS08000KPA */ 6668c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6801) }, TT UCMOS08000KPB */ 6678c2ecf20Sopenharmony_ci { USB_DEVICE(0x0547, 0x6801) }, /* TT UCMOS08000KPB, AS MU800 */ 6688c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6900) }, TT UCMOS09000KPA */ 6698c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6901) }, TT UCMOS09000KPB */ 6708c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6010) }, TT UCMOS10000KPA */ 6718c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6014) }, TT UCMOS14000KPA */ 6728c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6131) }, TT UCMOS01300KMA */ 6738c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6511) }, TT UCMOS05100KMA */ 6748c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8080) }, TT UHCCD00800KPA */ 6758c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8140) }, TT UHCCD01400KPA */ 6768c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8141) }, TT EXCCD01400KPA */ 6778c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8200) }, TT UHCCD02000KPA */ 6788c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8201) }, TT UHCCD02000KPB */ 6798c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8310) }, TT UHCCD03100KPA */ 6808c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8500) }, TT UHCCD05000KPA */ 6818c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8510) }, TT UHCCD05100KPA */ 6828c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8600) }, TT UHCCD06000KPA */ 6838c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8800) }, TT UHCCD08000KPA */ 6848c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8315) }, TT UHCCD03150KPA */ 6858c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x7800) }, TT UHCCD00800KMA */ 6868c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x7140) }, TT UHCCD01400KMA */ 6878c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x7141) }, TT UHCCD01400KMB */ 6888c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x7200) }, TT UHCCD02000KMA */ 6898c2ecf20Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x7315) }, TT UHCCD03150KMA */ 6908c2ecf20Sopenharmony_ci { } 6918c2ecf20Sopenharmony_ci}; 6928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, 6958c2ecf20Sopenharmony_ci const struct usb_device_id *id) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), 6988c2ecf20Sopenharmony_ci THIS_MODULE); 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = { 7028c2ecf20Sopenharmony_ci .name = MODULE_NAME, 7038c2ecf20Sopenharmony_ci .id_table = device_table, 7048c2ecf20Sopenharmony_ci .probe = sd_probe, 7058c2ecf20Sopenharmony_ci .disconnect = gspca_disconnect, 7068c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 7078c2ecf20Sopenharmony_ci .suspend = gspca_suspend, 7088c2ecf20Sopenharmony_ci .resume = gspca_resume, 7098c2ecf20Sopenharmony_ci#endif 7108c2ecf20Sopenharmony_ci}; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic int __init sd_mod_init(void) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci int ret; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci ret = usb_register(&sd_driver); 7178c2ecf20Sopenharmony_ci if (ret < 0) 7188c2ecf20Sopenharmony_ci return ret; 7198c2ecf20Sopenharmony_ci return 0; 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_cistatic void __exit sd_mod_exit(void) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci usb_deregister(&sd_driver); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cimodule_init(sd_mod_init); 7278c2ecf20Sopenharmony_cimodule_exit(sd_mod_exit); 728