18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher 48c2ecf20Sopenharmony_ci * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland 58c2ecf20Sopenharmony_ci * Copyright (c) 2002, 2003 Tuukka Toivonen 68c2ecf20Sopenharmony_ci * Copyright (c) 2008 Erik Andrén 78c2ecf20Sopenharmony_ci * Copyright (c) 2008 Chia-I Wu 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * P/N 861037: Sensor HDCS1000 ASIC STV0600 108c2ecf20Sopenharmony_ci * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 118c2ecf20Sopenharmony_ci * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express 128c2ecf20Sopenharmony_ci * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam 138c2ecf20Sopenharmony_ci * P/N 861075-0040: Sensor HDCS1000 ASIC 148c2ecf20Sopenharmony_ci * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB 158c2ecf20Sopenharmony_ci * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "stv06xx_hdcs.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic struct v4l2_pix_format hdcs1x00_mode[] = { 238c2ecf20Sopenharmony_ci { 248c2ecf20Sopenharmony_ci HDCS_1X00_DEF_WIDTH, 258c2ecf20Sopenharmony_ci HDCS_1X00_DEF_HEIGHT, 268c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 278c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 288c2ecf20Sopenharmony_ci .sizeimage = 298c2ecf20Sopenharmony_ci HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT, 308c2ecf20Sopenharmony_ci .bytesperline = HDCS_1X00_DEF_WIDTH, 318c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 328c2ecf20Sopenharmony_ci .priv = 1 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic struct v4l2_pix_format hdcs1020_mode[] = { 378c2ecf20Sopenharmony_ci { 388c2ecf20Sopenharmony_ci HDCS_1020_DEF_WIDTH, 398c2ecf20Sopenharmony_ci HDCS_1020_DEF_HEIGHT, 408c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 418c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 428c2ecf20Sopenharmony_ci .sizeimage = 438c2ecf20Sopenharmony_ci HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT, 448c2ecf20Sopenharmony_ci .bytesperline = HDCS_1020_DEF_WIDTH, 458c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 468c2ecf20Sopenharmony_ci .priv = 1 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cienum hdcs_power_state { 518c2ecf20Sopenharmony_ci HDCS_STATE_SLEEP, 528c2ecf20Sopenharmony_ci HDCS_STATE_IDLE, 538c2ecf20Sopenharmony_ci HDCS_STATE_RUN 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* no lock? */ 578c2ecf20Sopenharmony_cistruct hdcs { 588c2ecf20Sopenharmony_ci enum hdcs_power_state state; 598c2ecf20Sopenharmony_ci int w, h; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* visible area of the sensor array */ 628c2ecf20Sopenharmony_ci struct { 638c2ecf20Sopenharmony_ci int left, top; 648c2ecf20Sopenharmony_ci int width, height; 658c2ecf20Sopenharmony_ci int border; 668c2ecf20Sopenharmony_ci } array; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci struct { 698c2ecf20Sopenharmony_ci /* Column timing overhead */ 708c2ecf20Sopenharmony_ci u8 cto; 718c2ecf20Sopenharmony_ci /* Column processing overhead */ 728c2ecf20Sopenharmony_ci u8 cpo; 738c2ecf20Sopenharmony_ci /* Row sample period constant */ 748c2ecf20Sopenharmony_ci u16 rs; 758c2ecf20Sopenharmony_ci /* Exposure reset duration */ 768c2ecf20Sopenharmony_ci u16 er; 778c2ecf20Sopenharmony_ci } exp; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci int psmp; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci u8 regs[I2C_MAX_BYTES * 2]; 858c2ecf20Sopenharmony_ci int i; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (unlikely((len <= 0) || (len >= I2C_MAX_BYTES) || 888c2ecf20Sopenharmony_ci (reg + len > 0xff))) 898c2ecf20Sopenharmony_ci return -EINVAL; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 928c2ecf20Sopenharmony_ci regs[2 * i] = reg; 938c2ecf20Sopenharmony_ci regs[2 * i + 1] = vals[i]; 948c2ecf20Sopenharmony_ci /* All addresses are shifted left one bit 958c2ecf20Sopenharmony_ci * as bit 0 toggles r/w */ 968c2ecf20Sopenharmony_ci reg += 2; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return stv06xx_write_sensor_bytes(sd, regs, len); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int hdcs_set_state(struct sd *sd, enum hdcs_power_state state) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct hdcs *hdcs = sd->sensor_priv; 1058c2ecf20Sopenharmony_ci u8 val; 1068c2ecf20Sopenharmony_ci int ret; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (hdcs->state == state) 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* we need to go idle before running or sleeping */ 1128c2ecf20Sopenharmony_ci if (hdcs->state != HDCS_STATE_IDLE) { 1138c2ecf20Sopenharmony_ci ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0); 1148c2ecf20Sopenharmony_ci if (ret) 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci hdcs->state = HDCS_STATE_IDLE; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (state == HDCS_STATE_IDLE) 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci switch (state) { 1248c2ecf20Sopenharmony_ci case HDCS_STATE_SLEEP: 1258c2ecf20Sopenharmony_ci val = HDCS_SLEEP_MODE; 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci case HDCS_STATE_RUN: 1298c2ecf20Sopenharmony_ci val = HDCS_RUN_ENABLE; 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci default: 1338c2ecf20Sopenharmony_ci return -EINVAL; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* Update the state if the write succeeded */ 1398c2ecf20Sopenharmony_ci if (!ret) 1408c2ecf20Sopenharmony_ci hdcs->state = state; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return ret; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int hdcs_reset(struct sd *sd) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct hdcs *hdcs = sd->sensor_priv; 1488c2ecf20Sopenharmony_ci int err; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 1); 1518c2ecf20Sopenharmony_ci if (err < 0) 1528c2ecf20Sopenharmony_ci return err; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0); 1558c2ecf20Sopenharmony_ci if (err < 0) 1568c2ecf20Sopenharmony_ci hdcs->state = HDCS_STATE_IDLE; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return err; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 1648c2ecf20Sopenharmony_ci struct hdcs *hdcs = sd->sensor_priv; 1658c2ecf20Sopenharmony_ci int rowexp, srowexp; 1668c2ecf20Sopenharmony_ci int max_srowexp; 1678c2ecf20Sopenharmony_ci /* Column time period */ 1688c2ecf20Sopenharmony_ci int ct; 1698c2ecf20Sopenharmony_ci /* Column processing period */ 1708c2ecf20Sopenharmony_ci int cp; 1718c2ecf20Sopenharmony_ci /* Row processing period */ 1728c2ecf20Sopenharmony_ci int rp; 1738c2ecf20Sopenharmony_ci /* Minimum number of column timing periods 1748c2ecf20Sopenharmony_ci within the column processing period */ 1758c2ecf20Sopenharmony_ci int mnct; 1768c2ecf20Sopenharmony_ci int cycles, err; 1778c2ecf20Sopenharmony_ci u8 exp[14]; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci cycles = val * HDCS_CLK_FREQ_MHZ * 257; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); 1828c2ecf20Sopenharmony_ci cp = hdcs->exp.cto + (hdcs->w * ct / 2); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* the cycles one row takes */ 1858c2ecf20Sopenharmony_ci rp = hdcs->exp.rs + cp; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci rowexp = cycles / rp; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* the remaining cycles */ 1908c2ecf20Sopenharmony_ci cycles -= rowexp * rp; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* calculate sub-row exposure */ 1938c2ecf20Sopenharmony_ci if (IS_1020(sd)) { 1948c2ecf20Sopenharmony_ci /* see HDCS-1020 datasheet 3.5.6.4, p. 63 */ 1958c2ecf20Sopenharmony_ci srowexp = hdcs->w - (cycles + hdcs->exp.er + 13) / ct; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci mnct = (hdcs->exp.er + 12 + ct - 1) / ct; 1988c2ecf20Sopenharmony_ci max_srowexp = hdcs->w - mnct; 1998c2ecf20Sopenharmony_ci } else { 2008c2ecf20Sopenharmony_ci /* see HDCS-1000 datasheet 3.4.5.5, p. 61 */ 2018c2ecf20Sopenharmony_ci srowexp = cp - hdcs->exp.er - 6 - cycles; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci mnct = (hdcs->exp.er + 5 + ct - 1) / ct; 2048c2ecf20Sopenharmony_ci max_srowexp = cp - mnct * ct - 1; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (srowexp < 0) 2088c2ecf20Sopenharmony_ci srowexp = 0; 2098c2ecf20Sopenharmony_ci else if (srowexp > max_srowexp) 2108c2ecf20Sopenharmony_ci srowexp = max_srowexp; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (IS_1020(sd)) { 2138c2ecf20Sopenharmony_ci exp[0] = HDCS20_CONTROL; 2148c2ecf20Sopenharmony_ci exp[1] = 0x00; /* Stop streaming */ 2158c2ecf20Sopenharmony_ci exp[2] = HDCS_ROWEXPL; 2168c2ecf20Sopenharmony_ci exp[3] = rowexp & 0xff; 2178c2ecf20Sopenharmony_ci exp[4] = HDCS_ROWEXPH; 2188c2ecf20Sopenharmony_ci exp[5] = rowexp >> 8; 2198c2ecf20Sopenharmony_ci exp[6] = HDCS20_SROWEXP; 2208c2ecf20Sopenharmony_ci exp[7] = (srowexp >> 2) & 0xff; 2218c2ecf20Sopenharmony_ci exp[8] = HDCS20_ERROR; 2228c2ecf20Sopenharmony_ci exp[9] = 0x10; /* Clear exposure error flag*/ 2238c2ecf20Sopenharmony_ci exp[10] = HDCS20_CONTROL; 2248c2ecf20Sopenharmony_ci exp[11] = 0x04; /* Restart streaming */ 2258c2ecf20Sopenharmony_ci err = stv06xx_write_sensor_bytes(sd, exp, 6); 2268c2ecf20Sopenharmony_ci } else { 2278c2ecf20Sopenharmony_ci exp[0] = HDCS00_CONTROL; 2288c2ecf20Sopenharmony_ci exp[1] = 0x00; /* Stop streaming */ 2298c2ecf20Sopenharmony_ci exp[2] = HDCS_ROWEXPL; 2308c2ecf20Sopenharmony_ci exp[3] = rowexp & 0xff; 2318c2ecf20Sopenharmony_ci exp[4] = HDCS_ROWEXPH; 2328c2ecf20Sopenharmony_ci exp[5] = rowexp >> 8; 2338c2ecf20Sopenharmony_ci exp[6] = HDCS00_SROWEXPL; 2348c2ecf20Sopenharmony_ci exp[7] = srowexp & 0xff; 2358c2ecf20Sopenharmony_ci exp[8] = HDCS00_SROWEXPH; 2368c2ecf20Sopenharmony_ci exp[9] = srowexp >> 8; 2378c2ecf20Sopenharmony_ci exp[10] = HDCS_STATUS; 2388c2ecf20Sopenharmony_ci exp[11] = 0x10; /* Clear exposure error flag*/ 2398c2ecf20Sopenharmony_ci exp[12] = HDCS00_CONTROL; 2408c2ecf20Sopenharmony_ci exp[13] = 0x04; /* Restart streaming */ 2418c2ecf20Sopenharmony_ci err = stv06xx_write_sensor_bytes(sd, exp, 7); 2428c2ecf20Sopenharmony_ci if (err < 0) 2438c2ecf20Sopenharmony_ci return err; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Writing exposure %d, rowexp %d, srowexp %d\n", 2468c2ecf20Sopenharmony_ci val, rowexp, srowexp); 2478c2ecf20Sopenharmony_ci return err; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int hdcs_set_gains(struct sd *sd, u8 g) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci int err; 2538c2ecf20Sopenharmony_ci u8 gains[4]; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */ 2568c2ecf20Sopenharmony_ci if (g > 127) 2578c2ecf20Sopenharmony_ci g = 0x80 | (g / 2); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci gains[0] = g; 2608c2ecf20Sopenharmony_ci gains[1] = g; 2618c2ecf20Sopenharmony_ci gains[2] = g; 2628c2ecf20Sopenharmony_ci gains[3] = g; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci err = hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4); 2658c2ecf20Sopenharmony_ci return err; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Writing gain %d\n", val); 2718c2ecf20Sopenharmony_ci return hdcs_set_gains((struct sd *) gspca_dev, 2728c2ecf20Sopenharmony_ci val & 0xff); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int hdcs_set_size(struct sd *sd, 2768c2ecf20Sopenharmony_ci unsigned int width, unsigned int height) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct hdcs *hdcs = sd->sensor_priv; 2798c2ecf20Sopenharmony_ci u8 win[4]; 2808c2ecf20Sopenharmony_ci unsigned int x, y; 2818c2ecf20Sopenharmony_ci int err; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* must be multiple of 4 */ 2848c2ecf20Sopenharmony_ci width = (width + 3) & ~0x3; 2858c2ecf20Sopenharmony_ci height = (height + 3) & ~0x3; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (width > hdcs->array.width) 2888c2ecf20Sopenharmony_ci width = hdcs->array.width; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (IS_1020(sd)) { 2918c2ecf20Sopenharmony_ci /* the borders are also invalid */ 2928c2ecf20Sopenharmony_ci if (height + 2 * hdcs->array.border + HDCS_1020_BOTTOM_Y_SKIP 2938c2ecf20Sopenharmony_ci > hdcs->array.height) 2948c2ecf20Sopenharmony_ci height = hdcs->array.height - 2 * hdcs->array.border - 2958c2ecf20Sopenharmony_ci HDCS_1020_BOTTOM_Y_SKIP; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci y = (hdcs->array.height - HDCS_1020_BOTTOM_Y_SKIP - height) / 2 2988c2ecf20Sopenharmony_ci + hdcs->array.top; 2998c2ecf20Sopenharmony_ci } else { 3008c2ecf20Sopenharmony_ci if (height > hdcs->array.height) 3018c2ecf20Sopenharmony_ci height = hdcs->array.height; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci y = hdcs->array.top + (hdcs->array.height - height) / 2; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci x = hdcs->array.left + (hdcs->array.width - width) / 2; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci win[0] = y / 4; 3098c2ecf20Sopenharmony_ci win[1] = x / 4; 3108c2ecf20Sopenharmony_ci win[2] = (y + height) / 4 - 1; 3118c2ecf20Sopenharmony_ci win[3] = (x + width) / 4 - 1; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci err = hdcs_reg_write_seq(sd, HDCS_FWROW, win, 4); 3148c2ecf20Sopenharmony_ci if (err < 0) 3158c2ecf20Sopenharmony_ci return err; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Update the current width and height */ 3188c2ecf20Sopenharmony_ci hdcs->w = width; 3198c2ecf20Sopenharmony_ci hdcs->h = height; 3208c2ecf20Sopenharmony_ci return err; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int hdcs_s_ctrl(struct v4l2_ctrl *ctrl) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = 3268c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 3278c2ecf20Sopenharmony_ci int err = -EINVAL; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci switch (ctrl->id) { 3308c2ecf20Sopenharmony_ci case V4L2_CID_GAIN: 3318c2ecf20Sopenharmony_ci err = hdcs_set_gain(gspca_dev, ctrl->val); 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE: 3348c2ecf20Sopenharmony_ci err = hdcs_set_exposure(gspca_dev, ctrl->val); 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci return err; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops hdcs_ctrl_ops = { 3418c2ecf20Sopenharmony_ci .s_ctrl = hdcs_s_ctrl, 3428c2ecf20Sopenharmony_ci}; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic int hdcs_init_controls(struct sd *sd) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 2); 3498c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, 3508c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 0xff, 1, HDCS_DEFAULT_EXPOSURE); 3518c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, 3528c2ecf20Sopenharmony_ci V4L2_CID_GAIN, 0, 0xff, 1, HDCS_DEFAULT_GAIN); 3538c2ecf20Sopenharmony_ci return hdl->error; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int hdcs_probe_1x00(struct sd *sd) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct hdcs *hdcs; 3598c2ecf20Sopenharmony_ci u16 sensor; 3608c2ecf20Sopenharmony_ci int ret; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor); 3638c2ecf20Sopenharmony_ci if (ret < 0 || sensor != 0x08) 3648c2ecf20Sopenharmony_ci return -ENODEV; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci pr_info("HDCS-1000/1100 sensor detected\n"); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci sd->gspca_dev.cam.cam_mode = hdcs1x00_mode; 3698c2ecf20Sopenharmony_ci sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); 3728c2ecf20Sopenharmony_ci if (!hdcs) 3738c2ecf20Sopenharmony_ci return -ENOMEM; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci hdcs->array.left = 8; 3768c2ecf20Sopenharmony_ci hdcs->array.top = 8; 3778c2ecf20Sopenharmony_ci hdcs->array.width = HDCS_1X00_DEF_WIDTH; 3788c2ecf20Sopenharmony_ci hdcs->array.height = HDCS_1X00_DEF_HEIGHT; 3798c2ecf20Sopenharmony_ci hdcs->array.border = 4; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci hdcs->exp.cto = 4; 3828c2ecf20Sopenharmony_ci hdcs->exp.cpo = 2; 3838c2ecf20Sopenharmony_ci hdcs->exp.rs = 186; 3848c2ecf20Sopenharmony_ci hdcs->exp.er = 100; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* 3878c2ecf20Sopenharmony_ci * Frame rate on HDCS-1000 with STV600 depends on PSMP: 3888c2ecf20Sopenharmony_ci * 4 = doesn't work at all 3898c2ecf20Sopenharmony_ci * 5 = 7.8 fps, 3908c2ecf20Sopenharmony_ci * 6 = 6.9 fps, 3918c2ecf20Sopenharmony_ci * 8 = 6.3 fps, 3928c2ecf20Sopenharmony_ci * 10 = 5.5 fps, 3938c2ecf20Sopenharmony_ci * 15 = 4.4 fps, 3948c2ecf20Sopenharmony_ci * 31 = 2.8 fps 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci * Frame rate on HDCS-1000 with STV602 depends on PSMP: 3978c2ecf20Sopenharmony_ci * 15 = doesn't work at all 3988c2ecf20Sopenharmony_ci * 18 = doesn't work at all 3998c2ecf20Sopenharmony_ci * 19 = 7.3 fps 4008c2ecf20Sopenharmony_ci * 20 = 7.4 fps 4018c2ecf20Sopenharmony_ci * 21 = 7.4 fps 4028c2ecf20Sopenharmony_ci * 22 = 7.4 fps 4038c2ecf20Sopenharmony_ci * 24 = 6.3 fps 4048c2ecf20Sopenharmony_ci * 30 = 5.4 fps 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci hdcs->psmp = (sd->bridge == BRIDGE_STV602) ? 20 : 5; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci sd->sensor_priv = hdcs; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic int hdcs_probe_1020(struct sd *sd) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct hdcs *hdcs; 4168c2ecf20Sopenharmony_ci u16 sensor; 4178c2ecf20Sopenharmony_ci int ret; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor); 4208c2ecf20Sopenharmony_ci if (ret < 0 || sensor != 0x10) 4218c2ecf20Sopenharmony_ci return -ENODEV; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci pr_info("HDCS-1020 sensor detected\n"); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci sd->gspca_dev.cam.cam_mode = hdcs1020_mode; 4268c2ecf20Sopenharmony_ci sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); 4298c2ecf20Sopenharmony_ci if (!hdcs) 4308c2ecf20Sopenharmony_ci return -ENOMEM; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* 4338c2ecf20Sopenharmony_ci * From Andrey's test image: looks like HDCS-1020 upper-left 4348c2ecf20Sopenharmony_ci * visible pixel is at 24,8 (y maybe even smaller?) and lower-right 4358c2ecf20Sopenharmony_ci * visible pixel at 375,299 (x maybe even larger?) 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_ci hdcs->array.left = 24; 4388c2ecf20Sopenharmony_ci hdcs->array.top = 4; 4398c2ecf20Sopenharmony_ci hdcs->array.width = HDCS_1020_DEF_WIDTH; 4408c2ecf20Sopenharmony_ci hdcs->array.height = 304; 4418c2ecf20Sopenharmony_ci hdcs->array.border = 4; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci hdcs->psmp = 6; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci hdcs->exp.cto = 3; 4468c2ecf20Sopenharmony_ci hdcs->exp.cpo = 3; 4478c2ecf20Sopenharmony_ci hdcs->exp.rs = 155; 4488c2ecf20Sopenharmony_ci hdcs->exp.er = 96; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci sd->sensor_priv = hdcs; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return 0; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic int hdcs_start(struct sd *sd) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Starting stream\n"); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return hdcs_set_state(sd, HDCS_STATE_RUN); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic int hdcs_stop(struct sd *sd) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Halting stream\n"); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return hdcs_set_state(sd, HDCS_STATE_SLEEP); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int hdcs_init(struct sd *sd) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct hdcs *hdcs = sd->sensor_priv; 4768c2ecf20Sopenharmony_ci int i, err = 0; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* Set the STV0602AA in STV0600 emulation mode */ 4798c2ecf20Sopenharmony_ci if (sd->bridge == BRIDGE_STV602) 4808c2ecf20Sopenharmony_ci stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* Execute the bridge init */ 4838c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(stv_bridge_init) && !err; i++) { 4848c2ecf20Sopenharmony_ci err = stv06xx_write_bridge(sd, stv_bridge_init[i][0], 4858c2ecf20Sopenharmony_ci stv_bridge_init[i][1]); 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci if (err < 0) 4888c2ecf20Sopenharmony_ci return err; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* sensor soft reset */ 4918c2ecf20Sopenharmony_ci hdcs_reset(sd); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* Execute the sensor init */ 4948c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(stv_sensor_init) && !err; i++) { 4958c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, stv_sensor_init[i][0], 4968c2ecf20Sopenharmony_ci stv_sensor_init[i][1]); 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci if (err < 0) 4998c2ecf20Sopenharmony_ci return err; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* Enable continuous frame capture, bit 2: stop when frame complete */ 5028c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, HDCS_REG_CONFIG(sd), BIT(3)); 5038c2ecf20Sopenharmony_ci if (err < 0) 5048c2ecf20Sopenharmony_ci return err; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* Set PGA sample duration 5078c2ecf20Sopenharmony_ci (was 0x7E for the STV602, but caused slow framerate with HDCS-1020) */ 5088c2ecf20Sopenharmony_ci if (IS_1020(sd)) 5098c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, HDCS_TCTRL, 5108c2ecf20Sopenharmony_ci (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp); 5118c2ecf20Sopenharmony_ci else 5128c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, HDCS_TCTRL, 5138c2ecf20Sopenharmony_ci (HDCS_ADC_START_SIG_DUR << 5) | hdcs->psmp); 5148c2ecf20Sopenharmony_ci if (err < 0) 5158c2ecf20Sopenharmony_ci return err; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int hdcs_dump(struct sd *sd) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci u16 reg, val; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci pr_info("Dumping sensor registers:\n"); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) { 5278c2ecf20Sopenharmony_ci stv06xx_read_sensor(sd, reg, &val); 5288c2ecf20Sopenharmony_ci pr_info("reg 0x%02x = 0x%02x\n", reg, val); 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci} 532