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 * 88c2ecf20Sopenharmony_ci * P/N 861037: Sensor HDCS1000 ASIC STV0600 98c2ecf20Sopenharmony_ci * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 108c2ecf20Sopenharmony_ci * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express 118c2ecf20Sopenharmony_ci * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam 128c2ecf20Sopenharmony_ci * P/N 861075-0040: Sensor HDCS1000 ASIC 138c2ecf20Sopenharmony_ci * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB 148c2ecf20Sopenharmony_ci * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "stv06xx_vv6410.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic struct v4l2_pix_format vv6410_mode[] = { 228c2ecf20Sopenharmony_ci { 238c2ecf20Sopenharmony_ci 356, 248c2ecf20Sopenharmony_ci 292, 258c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 268c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 278c2ecf20Sopenharmony_ci .sizeimage = 356 * 292, 288c2ecf20Sopenharmony_ci .bytesperline = 356, 298c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 308c2ecf20Sopenharmony_ci .priv = 0 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int vv6410_s_ctrl(struct v4l2_ctrl *ctrl) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = 378c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 388c2ecf20Sopenharmony_ci int err = -EINVAL; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci switch (ctrl->id) { 418c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 428c2ecf20Sopenharmony_ci if (!gspca_dev->streaming) 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci err = vv6410_set_hflip(gspca_dev, ctrl->val); 458c2ecf20Sopenharmony_ci break; 468c2ecf20Sopenharmony_ci case V4L2_CID_VFLIP: 478c2ecf20Sopenharmony_ci if (!gspca_dev->streaming) 488c2ecf20Sopenharmony_ci return 0; 498c2ecf20Sopenharmony_ci err = vv6410_set_vflip(gspca_dev, ctrl->val); 508c2ecf20Sopenharmony_ci break; 518c2ecf20Sopenharmony_ci case V4L2_CID_GAIN: 528c2ecf20Sopenharmony_ci err = vv6410_set_analog_gain(gspca_dev, ctrl->val); 538c2ecf20Sopenharmony_ci break; 548c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE: 558c2ecf20Sopenharmony_ci err = vv6410_set_exposure(gspca_dev, ctrl->val); 568c2ecf20Sopenharmony_ci break; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci return err; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops vv6410_ctrl_ops = { 628c2ecf20Sopenharmony_ci .s_ctrl = vv6410_s_ctrl, 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int vv6410_probe(struct sd *sd) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci u16 data; 688c2ecf20Sopenharmony_ci int err; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data); 718c2ecf20Sopenharmony_ci if (err < 0) 728c2ecf20Sopenharmony_ci return -ENODEV; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (data != 0x19) 758c2ecf20Sopenharmony_ci return -ENODEV; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci pr_info("vv6410 sensor detected\n"); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci sd->gspca_dev.cam.cam_mode = vv6410_mode; 808c2ecf20Sopenharmony_ci sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode); 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int vv6410_init_controls(struct sd *sd) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 2); 898c2ecf20Sopenharmony_ci /* Disable the hardware VFLIP and HFLIP as we currently lack a 908c2ecf20Sopenharmony_ci mechanism to adjust the image offset in such a way that 918c2ecf20Sopenharmony_ci we don't need to renegotiate the announced format */ 928c2ecf20Sopenharmony_ci /* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */ 938c2ecf20Sopenharmony_ci /* V4L2_CID_HFLIP, 0, 1, 1, 0); */ 948c2ecf20Sopenharmony_ci /* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */ 958c2ecf20Sopenharmony_ci /* V4L2_CID_VFLIP, 0, 1, 1, 0); */ 968c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, 978c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 32768, 1, 20000); 988c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, 998c2ecf20Sopenharmony_ci V4L2_CID_GAIN, 0, 15, 1, 10); 1008c2ecf20Sopenharmony_ci return hdl->error; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int vv6410_init(struct sd *sd) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci int err = 0, i; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) 1088c2ecf20Sopenharmony_ci stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init, 1118c2ecf20Sopenharmony_ci ARRAY_SIZE(vv6410_sensor_init)); 1128c2ecf20Sopenharmony_ci return (err < 0) ? err : 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int vv6410_start(struct sd *sd) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci int err; 1188c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 1198c2ecf20Sopenharmony_ci struct cam *cam = &sd->gspca_dev.cam; 1208c2ecf20Sopenharmony_ci u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (priv & VV6410_SUBSAMPLE) { 1238c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Enabling subsampling\n"); 1248c2ecf20Sopenharmony_ci stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); 1258c2ecf20Sopenharmony_ci stv06xx_write_bridge(sd, STV_X_CTRL, 0x06); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10); 1288c2ecf20Sopenharmony_ci } else { 1298c2ecf20Sopenharmony_ci stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); 1308c2ecf20Sopenharmony_ci stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); 1318c2ecf20Sopenharmony_ci stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x00); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Turn on LED */ 1368c2ecf20Sopenharmony_ci err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON); 1378c2ecf20Sopenharmony_ci if (err < 0) 1388c2ecf20Sopenharmony_ci return err; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0); 1418c2ecf20Sopenharmony_ci if (err < 0) 1428c2ecf20Sopenharmony_ci return err; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Starting stream\n"); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int vv6410_stop(struct sd *sd) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 1528c2ecf20Sopenharmony_ci int err; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* Turn off LED */ 1558c2ecf20Sopenharmony_ci err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF); 1568c2ecf20Sopenharmony_ci if (err < 0) 1578c2ecf20Sopenharmony_ci return err; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE); 1608c2ecf20Sopenharmony_ci if (err < 0) 1618c2ecf20Sopenharmony_ci return err; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Halting stream\n"); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int vv6410_dump(struct sd *sd) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci u8 i; 1718c2ecf20Sopenharmony_ci int err = 0; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci pr_info("Dumping all vv6410 sensor registers\n"); 1748c2ecf20Sopenharmony_ci for (i = 0; i < 0xff && !err; i++) { 1758c2ecf20Sopenharmony_ci u16 data; 1768c2ecf20Sopenharmony_ci err = stv06xx_read_sensor(sd, i, &data); 1778c2ecf20Sopenharmony_ci pr_info("Register 0x%x contained 0x%x\n", i, data); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci return (err < 0) ? err : 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci int err; 1858c2ecf20Sopenharmony_ci u16 i2c_data; 1868c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); 1898c2ecf20Sopenharmony_ci if (err < 0) 1908c2ecf20Sopenharmony_ci return err; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (val) 1938c2ecf20Sopenharmony_ci i2c_data |= VV6410_HFLIP; 1948c2ecf20Sopenharmony_ci else 1958c2ecf20Sopenharmony_ci i2c_data &= ~VV6410_HFLIP; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set horizontal flip to %d\n", val); 1988c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return (err < 0) ? err : 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci int err; 2068c2ecf20Sopenharmony_ci u16 i2c_data; 2078c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); 2108c2ecf20Sopenharmony_ci if (err < 0) 2118c2ecf20Sopenharmony_ci return err; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (val) 2148c2ecf20Sopenharmony_ci i2c_data |= VV6410_VFLIP; 2158c2ecf20Sopenharmony_ci else 2168c2ecf20Sopenharmony_ci i2c_data &= ~VV6410_VFLIP; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set vertical flip to %d\n", val); 2198c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return (err < 0) ? err : 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci int err; 2278c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set analog gain to %d\n", val); 2308c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf)); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return (err < 0) ? err : 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci int err; 2388c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 2398c2ecf20Sopenharmony_ci unsigned int fine, coarse; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci val = (val * val >> 14) + val / 4; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci fine = val % VV6410_CIF_LINELENGTH; 2448c2ecf20Sopenharmony_ci coarse = min(512, val / VV6410_CIF_LINELENGTH); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set coarse exposure to %d, fine exposure to %d\n", 2478c2ecf20Sopenharmony_ci coarse, fine); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_FINEH, fine >> 8); 2508c2ecf20Sopenharmony_ci if (err < 0) 2518c2ecf20Sopenharmony_ci goto out; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_FINEL, fine & 0xff); 2548c2ecf20Sopenharmony_ci if (err < 0) 2558c2ecf20Sopenharmony_ci goto out; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_COARSEH, coarse >> 8); 2588c2ecf20Sopenharmony_ci if (err < 0) 2598c2ecf20Sopenharmony_ci goto out; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_COARSEL, coarse & 0xff); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ciout: 2648c2ecf20Sopenharmony_ci return err; 2658c2ecf20Sopenharmony_ci} 266