18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Mars MR97310A library 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is 68c2ecf20Sopenharmony_ci * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+ 98c2ecf20Sopenharmony_ci * and for the routines for detecting and classifying these various cameras, 108c2ecf20Sopenharmony_ci * is Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Support for the control settings for the CIF cameras is 138c2ecf20Sopenharmony_ci * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com> and 148c2ecf20Sopenharmony_ci * Thomas Kaiser <thomas@kaiser-linux.li> 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Support for the control settings for the VGA cameras is 178c2ecf20Sopenharmony_ci * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu> 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Several previously unsupported cameras are owned and have been tested by 208c2ecf20Sopenharmony_ci * Hans de Goede <hdegoede@redhat.com> and 218c2ecf20Sopenharmony_ci * Thomas Kaiser <thomas@kaiser-linux.li> and 228c2ecf20Sopenharmony_ci * Theodore Kilgore <kilgota@auburn.edu> and 238c2ecf20Sopenharmony_ci * Edmond Rodriguez <erodrig_97@yahoo.com> and 248c2ecf20Sopenharmony_ci * Aurelien Jacobs <aurel@gnuage.org> 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * The MR97311A support in gspca/mars.c has been helpful in understanding some 278c2ecf20Sopenharmony_ci * of the registers in these cameras. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define MODULE_NAME "mr97310a" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "gspca.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define CAM_TYPE_CIF 0 378c2ecf20Sopenharmony_ci#define CAM_TYPE_VGA 1 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define MR97310A_BRIGHTNESS_DEFAULT 0 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define MR97310A_EXPOSURE_MIN 0 428c2ecf20Sopenharmony_ci#define MR97310A_EXPOSURE_MAX 4095 438c2ecf20Sopenharmony_ci#define MR97310A_EXPOSURE_DEFAULT 1000 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define MR97310A_GAIN_MIN 0 468c2ecf20Sopenharmony_ci#define MR97310A_GAIN_MAX 31 478c2ecf20Sopenharmony_ci#define MR97310A_GAIN_DEFAULT 25 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define MR97310A_CONTRAST_MIN 0 508c2ecf20Sopenharmony_ci#define MR97310A_CONTRAST_MAX 31 518c2ecf20Sopenharmony_ci#define MR97310A_CONTRAST_DEFAULT 23 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define MR97310A_CS_GAIN_MIN 0 548c2ecf20Sopenharmony_ci#define MR97310A_CS_GAIN_MAX 0x7ff 558c2ecf20Sopenharmony_ci#define MR97310A_CS_GAIN_DEFAULT 0x110 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0x1000) 588c2ecf20Sopenharmony_ci#define MR97310A_MIN_CLOCKDIV_MIN 3 598c2ecf20Sopenharmony_ci#define MR97310A_MIN_CLOCKDIV_MAX 8 608c2ecf20Sopenharmony_ci#define MR97310A_MIN_CLOCKDIV_DEFAULT 3 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>,Theodore Kilgore <kilgota@auburn.edu>"); 638c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver"); 648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* global parameters */ 678c2ecf20Sopenharmony_cistatic int force_sensor_type = -1; 688c2ecf20Sopenharmony_cimodule_param(force_sensor_type, int, 0644); 698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)"); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* specific webcam descriptor */ 728c2ecf20Sopenharmony_cistruct sd { 738c2ecf20Sopenharmony_ci struct gspca_dev gspca_dev; /* !! must be the first item */ 748c2ecf20Sopenharmony_ci struct { /* exposure/min_clockdiv control cluster */ 758c2ecf20Sopenharmony_ci struct v4l2_ctrl *exposure; 768c2ecf20Sopenharmony_ci struct v4l2_ctrl *min_clockdiv; 778c2ecf20Sopenharmony_ci }; 788c2ecf20Sopenharmony_ci u8 sof_read; 798c2ecf20Sopenharmony_ci u8 cam_type; /* 0 is CIF and 1 is VGA */ 808c2ecf20Sopenharmony_ci u8 sensor_type; /* We use 0 and 1 here, too. */ 818c2ecf20Sopenharmony_ci u8 do_lcd_stop; 828c2ecf20Sopenharmony_ci u8 adj_colors; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct sensor_w_data { 868c2ecf20Sopenharmony_ci u8 reg; 878c2ecf20Sopenharmony_ci u8 flags; 888c2ecf20Sopenharmony_ci u8 data[16]; 898c2ecf20Sopenharmony_ci int len; 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format vga_mode[] = { 958c2ecf20Sopenharmony_ci {160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, 968c2ecf20Sopenharmony_ci .bytesperline = 160, 978c2ecf20Sopenharmony_ci .sizeimage = 160 * 120, 988c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 998c2ecf20Sopenharmony_ci .priv = 4}, 1008c2ecf20Sopenharmony_ci {176, 144, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, 1018c2ecf20Sopenharmony_ci .bytesperline = 176, 1028c2ecf20Sopenharmony_ci .sizeimage = 176 * 144, 1038c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 1048c2ecf20Sopenharmony_ci .priv = 3}, 1058c2ecf20Sopenharmony_ci {320, 240, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, 1068c2ecf20Sopenharmony_ci .bytesperline = 320, 1078c2ecf20Sopenharmony_ci .sizeimage = 320 * 240, 1088c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 1098c2ecf20Sopenharmony_ci .priv = 2}, 1108c2ecf20Sopenharmony_ci {352, 288, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, 1118c2ecf20Sopenharmony_ci .bytesperline = 352, 1128c2ecf20Sopenharmony_ci .sizeimage = 352 * 288, 1138c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 1148c2ecf20Sopenharmony_ci .priv = 1}, 1158c2ecf20Sopenharmony_ci {640, 480, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, 1168c2ecf20Sopenharmony_ci .bytesperline = 640, 1178c2ecf20Sopenharmony_ci .sizeimage = 640 * 480, 1188c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 1198c2ecf20Sopenharmony_ci .priv = 0}, 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* the bytes to write are in gspca_dev->usb_buf */ 1238c2ecf20Sopenharmony_cistatic int mr_write(struct gspca_dev *gspca_dev, int len) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci int rc; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci rc = usb_bulk_msg(gspca_dev->dev, 1288c2ecf20Sopenharmony_ci usb_sndbulkpipe(gspca_dev->dev, 4), 1298c2ecf20Sopenharmony_ci gspca_dev->usb_buf, len, NULL, 500); 1308c2ecf20Sopenharmony_ci if (rc < 0) 1318c2ecf20Sopenharmony_ci pr_err("reg write [%02x] error %d\n", 1328c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0], rc); 1338c2ecf20Sopenharmony_ci return rc; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* the bytes are read into gspca_dev->usb_buf */ 1378c2ecf20Sopenharmony_cistatic int mr_read(struct gspca_dev *gspca_dev, int len) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci int rc; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci rc = usb_bulk_msg(gspca_dev->dev, 1428c2ecf20Sopenharmony_ci usb_rcvbulkpipe(gspca_dev->dev, 3), 1438c2ecf20Sopenharmony_ci gspca_dev->usb_buf, len, NULL, 500); 1448c2ecf20Sopenharmony_ci if (rc < 0) 1458c2ecf20Sopenharmony_ci pr_err("reg read [%02x] error %d\n", 1468c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0], rc); 1478c2ecf20Sopenharmony_ci return rc; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int sensor_write_reg(struct gspca_dev *gspca_dev, u8 reg, u8 flags, 1518c2ecf20Sopenharmony_ci const u8 *data, int len) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0] = 0x1f; 1548c2ecf20Sopenharmony_ci gspca_dev->usb_buf[1] = flags; 1558c2ecf20Sopenharmony_ci gspca_dev->usb_buf[2] = reg; 1568c2ecf20Sopenharmony_ci memcpy(gspca_dev->usb_buf + 3, data, len); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return mr_write(gspca_dev, len + 3); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int sensor_write_regs(struct gspca_dev *gspca_dev, 1628c2ecf20Sopenharmony_ci const struct sensor_w_data *data, int len) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci int i, rc; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 1678c2ecf20Sopenharmony_ci rc = sensor_write_reg(gspca_dev, data[i].reg, data[i].flags, 1688c2ecf20Sopenharmony_ci data[i].data, data[i].len); 1698c2ecf20Sopenharmony_ci if (rc < 0) 1708c2ecf20Sopenharmony_ci return rc; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 1798c2ecf20Sopenharmony_ci u8 buf, confirm_reg; 1808c2ecf20Sopenharmony_ci int rc; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci buf = data; 1838c2ecf20Sopenharmony_ci if (sd->cam_type == CAM_TYPE_CIF) { 1848c2ecf20Sopenharmony_ci rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1); 1858c2ecf20Sopenharmony_ci confirm_reg = sd->sensor_type ? 0x13 : 0x11; 1868c2ecf20Sopenharmony_ci } else { 1878c2ecf20Sopenharmony_ci rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1); 1888c2ecf20Sopenharmony_ci confirm_reg = 0x11; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci if (rc < 0) 1918c2ecf20Sopenharmony_ci return rc; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci buf = 0x01; 1948c2ecf20Sopenharmony_ci rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1); 1958c2ecf20Sopenharmony_ci if (rc < 0) 1968c2ecf20Sopenharmony_ci return rc; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci int err_code; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0] = reg; 2068c2ecf20Sopenharmony_ci err_code = mr_write(gspca_dev, 1); 2078c2ecf20Sopenharmony_ci if (err_code < 0) 2088c2ecf20Sopenharmony_ci return err_code; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci err_code = mr_read(gspca_dev, 16); 2118c2ecf20Sopenharmony_ci if (err_code < 0) 2128c2ecf20Sopenharmony_ci return err_code; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (verbose) 2158c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "Register: %02x reads %02x%02x%02x\n", 2168c2ecf20Sopenharmony_ci reg, 2178c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0], 2188c2ecf20Sopenharmony_ci gspca_dev->usb_buf[1], 2198c2ecf20Sopenharmony_ci gspca_dev->usb_buf[2]); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int zero_the_pointer(struct gspca_dev *gspca_dev) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci __u8 *data = gspca_dev->usb_buf; 2278c2ecf20Sopenharmony_ci int err_code; 2288c2ecf20Sopenharmony_ci u8 status = 0; 2298c2ecf20Sopenharmony_ci int tries = 0; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x21, 0); 2328c2ecf20Sopenharmony_ci if (err_code < 0) 2338c2ecf20Sopenharmony_ci return err_code; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci data[0] = 0x19; 2368c2ecf20Sopenharmony_ci data[1] = 0x51; 2378c2ecf20Sopenharmony_ci err_code = mr_write(gspca_dev, 2); 2388c2ecf20Sopenharmony_ci if (err_code < 0) 2398c2ecf20Sopenharmony_ci return err_code; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x21, 0); 2428c2ecf20Sopenharmony_ci if (err_code < 0) 2438c2ecf20Sopenharmony_ci return err_code; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci data[0] = 0x19; 2468c2ecf20Sopenharmony_ci data[1] = 0xba; 2478c2ecf20Sopenharmony_ci err_code = mr_write(gspca_dev, 2); 2488c2ecf20Sopenharmony_ci if (err_code < 0) 2498c2ecf20Sopenharmony_ci return err_code; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x21, 0); 2528c2ecf20Sopenharmony_ci if (err_code < 0) 2538c2ecf20Sopenharmony_ci return err_code; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci data[0] = 0x19; 2568c2ecf20Sopenharmony_ci data[1] = 0x00; 2578c2ecf20Sopenharmony_ci err_code = mr_write(gspca_dev, 2); 2588c2ecf20Sopenharmony_ci if (err_code < 0) 2598c2ecf20Sopenharmony_ci return err_code; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x21, 0); 2628c2ecf20Sopenharmony_ci if (err_code < 0) 2638c2ecf20Sopenharmony_ci return err_code; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci data[0] = 0x19; 2668c2ecf20Sopenharmony_ci data[1] = 0x00; 2678c2ecf20Sopenharmony_ci err_code = mr_write(gspca_dev, 2); 2688c2ecf20Sopenharmony_ci if (err_code < 0) 2698c2ecf20Sopenharmony_ci return err_code; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci while (status != 0x0a && tries < 256) { 2728c2ecf20Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x21, 0); 2738c2ecf20Sopenharmony_ci status = data[0]; 2748c2ecf20Sopenharmony_ci tries++; 2758c2ecf20Sopenharmony_ci if (err_code < 0) 2768c2ecf20Sopenharmony_ci return err_code; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci if (status != 0x0a) 2798c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "status is %02x\n", status); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci tries = 0; 2828c2ecf20Sopenharmony_ci while (tries < 4) { 2838c2ecf20Sopenharmony_ci data[0] = 0x19; 2848c2ecf20Sopenharmony_ci data[1] = 0x00; 2858c2ecf20Sopenharmony_ci err_code = mr_write(gspca_dev, 2); 2868c2ecf20Sopenharmony_ci if (err_code < 0) 2878c2ecf20Sopenharmony_ci return err_code; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x21, 0); 2908c2ecf20Sopenharmony_ci tries++; 2918c2ecf20Sopenharmony_ci if (err_code < 0) 2928c2ecf20Sopenharmony_ci return err_code; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci data[0] = 0x19; 2968c2ecf20Sopenharmony_ci err_code = mr_write(gspca_dev, 1); 2978c2ecf20Sopenharmony_ci if (err_code < 0) 2988c2ecf20Sopenharmony_ci return err_code; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci err_code = mr_read(gspca_dev, 16); 3018c2ecf20Sopenharmony_ci if (err_code < 0) 3028c2ecf20Sopenharmony_ci return err_code; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int stream_start(struct gspca_dev *gspca_dev) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0] = 0x01; 3108c2ecf20Sopenharmony_ci gspca_dev->usb_buf[1] = 0x01; 3118c2ecf20Sopenharmony_ci return mr_write(gspca_dev, 2); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void stream_stop(struct gspca_dev *gspca_dev) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0] = 0x01; 3178c2ecf20Sopenharmony_ci gspca_dev->usb_buf[1] = 0x00; 3188c2ecf20Sopenharmony_ci if (mr_write(gspca_dev, 2) < 0) 3198c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "Stream Stop failed\n"); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void lcd_stop(struct gspca_dev *gspca_dev) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0] = 0x19; 3258c2ecf20Sopenharmony_ci gspca_dev->usb_buf[1] = 0x54; 3268c2ecf20Sopenharmony_ci if (mr_write(gspca_dev, 2) < 0) 3278c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "LCD Stop failed\n"); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int isoc_enable(struct gspca_dev *gspca_dev) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0] = 0x00; 3338c2ecf20Sopenharmony_ci gspca_dev->usb_buf[1] = 0x4d; /* ISOC transferring enable... */ 3348c2ecf20Sopenharmony_ci return mr_write(gspca_dev, 2); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/* This function is called at probe time */ 3388c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 3398c2ecf20Sopenharmony_ci const struct usb_device_id *id) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 3428c2ecf20Sopenharmony_ci struct cam *cam; 3438c2ecf20Sopenharmony_ci int err_code; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci cam = &gspca_dev->cam; 3468c2ecf20Sopenharmony_ci cam->cam_mode = vga_mode; 3478c2ecf20Sopenharmony_ci cam->nmodes = ARRAY_SIZE(vga_mode); 3488c2ecf20Sopenharmony_ci sd->do_lcd_stop = 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Several of the supported CIF cameras share the same USB ID but 3518c2ecf20Sopenharmony_ci * require different initializations and different control settings. 3528c2ecf20Sopenharmony_ci * The same is true of the VGA cameras. Therefore, we are forced 3538c2ecf20Sopenharmony_ci * to start the initialization process in order to determine which 3548c2ecf20Sopenharmony_ci * camera is present. Some of the supported cameras require the 3558c2ecf20Sopenharmony_ci * memory pointer to be set to 0 as the very first item of business 3568c2ecf20Sopenharmony_ci * or else they will not stream. So we do that immediately. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci err_code = zero_the_pointer(gspca_dev); 3598c2ecf20Sopenharmony_ci if (err_code < 0) 3608c2ecf20Sopenharmony_ci return err_code; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci err_code = stream_start(gspca_dev); 3638c2ecf20Sopenharmony_ci if (err_code < 0) 3648c2ecf20Sopenharmony_ci return err_code; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* Now, the query for sensor type. */ 3678c2ecf20Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x07, 1); 3688c2ecf20Sopenharmony_ci if (err_code < 0) 3698c2ecf20Sopenharmony_ci return err_code; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (id->idProduct == 0x0110 || id->idProduct == 0x010e) { 3728c2ecf20Sopenharmony_ci sd->cam_type = CAM_TYPE_CIF; 3738c2ecf20Sopenharmony_ci cam->nmodes--; 3748c2ecf20Sopenharmony_ci /* 3758c2ecf20Sopenharmony_ci * All but one of the known CIF cameras share the same USB ID, 3768c2ecf20Sopenharmony_ci * but two different init routines are in use, and the control 3778c2ecf20Sopenharmony_ci * settings are different, too. We need to detect which camera 3788c2ecf20Sopenharmony_ci * of the two known varieties is connected! 3798c2ecf20Sopenharmony_ci * 3808c2ecf20Sopenharmony_ci * A list of known CIF cameras follows. They all report either 3818c2ecf20Sopenharmony_ci * 0200 for type 0 or 0300 for type 1. 3828c2ecf20Sopenharmony_ci * If you have another to report, please do 3838c2ecf20Sopenharmony_ci * 3848c2ecf20Sopenharmony_ci * Name sd->sensor_type reported by 3858c2ecf20Sopenharmony_ci * 3868c2ecf20Sopenharmony_ci * Sakar 56379 Spy-shot 0 T. Kilgore 3878c2ecf20Sopenharmony_ci * Innovage 0 T. Kilgore 3888c2ecf20Sopenharmony_ci * Vivitar Mini 0 H. De Goede 3898c2ecf20Sopenharmony_ci * Vivitar Mini 0 E. Rodriguez 3908c2ecf20Sopenharmony_ci * Vivitar Mini 1 T. Kilgore 3918c2ecf20Sopenharmony_ci * Elta-Media 8212dc 1 T. Kaiser 3928c2ecf20Sopenharmony_ci * Philips dig. keych. 1 T. Kilgore 3938c2ecf20Sopenharmony_ci * Trust Spyc@m 100 1 A. Jacobs 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_ci switch (gspca_dev->usb_buf[0]) { 3968c2ecf20Sopenharmony_ci case 2: 3978c2ecf20Sopenharmony_ci sd->sensor_type = 0; 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci case 3: 4008c2ecf20Sopenharmony_ci sd->sensor_type = 1; 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci default: 4038c2ecf20Sopenharmony_ci pr_err("Unknown CIF Sensor id : %02x\n", 4048c2ecf20Sopenharmony_ci gspca_dev->usb_buf[1]); 4058c2ecf20Sopenharmony_ci return -ENODEV; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "MR97310A CIF camera detected, sensor: %d\n", 4088c2ecf20Sopenharmony_ci sd->sensor_type); 4098c2ecf20Sopenharmony_ci } else { 4108c2ecf20Sopenharmony_ci sd->cam_type = CAM_TYPE_VGA; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* 4138c2ecf20Sopenharmony_ci * Here is a table of the responses to the query for sensor 4148c2ecf20Sopenharmony_ci * type, from the known MR97310A VGA cameras. Six different 4158c2ecf20Sopenharmony_ci * cameras of which five share the same USB ID. 4168c2ecf20Sopenharmony_ci * 4178c2ecf20Sopenharmony_ci * Name gspca_dev->usb_buf[] sd->sensor_type 4188c2ecf20Sopenharmony_ci * sd->do_lcd_stop 4198c2ecf20Sopenharmony_ci * Aiptek Pencam VGA+ 0300 0 1 4208c2ecf20Sopenharmony_ci * ION digital 0300 0 1 4218c2ecf20Sopenharmony_ci * Argus DC-1620 0450 1 0 4228c2ecf20Sopenharmony_ci * Argus QuickClix 0420 1 1 4238c2ecf20Sopenharmony_ci * Sakar 77379 Digital 0350 0 1 4248c2ecf20Sopenharmony_ci * Sakar 1638x CyberPix 0120 0 2 4258c2ecf20Sopenharmony_ci * 4268c2ecf20Sopenharmony_ci * Based upon these results, we assume default settings 4278c2ecf20Sopenharmony_ci * and then correct as necessary, as follows. 4288c2ecf20Sopenharmony_ci * 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci sd->sensor_type = 1; 4328c2ecf20Sopenharmony_ci sd->do_lcd_stop = 0; 4338c2ecf20Sopenharmony_ci sd->adj_colors = 0; 4348c2ecf20Sopenharmony_ci if (gspca_dev->usb_buf[0] == 0x01) { 4358c2ecf20Sopenharmony_ci sd->sensor_type = 2; 4368c2ecf20Sopenharmony_ci } else if ((gspca_dev->usb_buf[0] != 0x03) && 4378c2ecf20Sopenharmony_ci (gspca_dev->usb_buf[0] != 0x04)) { 4388c2ecf20Sopenharmony_ci pr_err("Unknown VGA Sensor id Byte 0: %02x\n", 4398c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0]); 4408c2ecf20Sopenharmony_ci pr_err("Defaults assumed, may not work\n"); 4418c2ecf20Sopenharmony_ci pr_err("Please report this\n"); 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci /* Sakar Digital color needs to be adjusted. */ 4448c2ecf20Sopenharmony_ci if ((gspca_dev->usb_buf[0] == 0x03) && 4458c2ecf20Sopenharmony_ci (gspca_dev->usb_buf[1] == 0x50)) 4468c2ecf20Sopenharmony_ci sd->adj_colors = 1; 4478c2ecf20Sopenharmony_ci if (gspca_dev->usb_buf[0] == 0x04) { 4488c2ecf20Sopenharmony_ci sd->do_lcd_stop = 1; 4498c2ecf20Sopenharmony_ci switch (gspca_dev->usb_buf[1]) { 4508c2ecf20Sopenharmony_ci case 0x50: 4518c2ecf20Sopenharmony_ci sd->sensor_type = 0; 4528c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "sensor_type corrected to 0\n"); 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci case 0x20: 4558c2ecf20Sopenharmony_ci /* Nothing to do here. */ 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci default: 4588c2ecf20Sopenharmony_ci pr_err("Unknown VGA Sensor id Byte 1: %02x\n", 4598c2ecf20Sopenharmony_ci gspca_dev->usb_buf[1]); 4608c2ecf20Sopenharmony_ci pr_err("Defaults assumed, may not work\n"); 4618c2ecf20Sopenharmony_ci pr_err("Please report this\n"); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "MR97310A VGA camera detected, sensor: %d\n", 4658c2ecf20Sopenharmony_ci sd->sensor_type); 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci /* Stop streaming as we've started it only to probe the sensor type. */ 4688c2ecf20Sopenharmony_ci sd_stopN(gspca_dev); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (force_sensor_type != -1) { 4718c2ecf20Sopenharmony_ci sd->sensor_type = !!force_sensor_type; 4728c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "Forcing sensor type to: %d\n", 4738c2ecf20Sopenharmony_ci sd->sensor_type); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return 0; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci/* this function is called at probe and resume time */ 4808c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int start_cif_cam(struct gspca_dev *gspca_dev) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4888c2ecf20Sopenharmony_ci __u8 *data = gspca_dev->usb_buf; 4898c2ecf20Sopenharmony_ci int err_code; 4908c2ecf20Sopenharmony_ci static const __u8 startup_string[] = { 4918c2ecf20Sopenharmony_ci 0x00, 4928c2ecf20Sopenharmony_ci 0x0d, 4938c2ecf20Sopenharmony_ci 0x01, 4948c2ecf20Sopenharmony_ci 0x00, /* Hsize/8 for 352 or 320 */ 4958c2ecf20Sopenharmony_ci 0x00, /* Vsize/4 for 288 or 240 */ 4968c2ecf20Sopenharmony_ci 0x13, /* or 0xbb, depends on sensor */ 4978c2ecf20Sopenharmony_ci 0x00, /* Hstart, depends on res. */ 4988c2ecf20Sopenharmony_ci 0x00, /* reserved ? */ 4998c2ecf20Sopenharmony_ci 0x00, /* Vstart, depends on res. and sensor */ 5008c2ecf20Sopenharmony_ci 0x50, /* 0x54 to get 176 or 160 */ 5018c2ecf20Sopenharmony_ci 0xc0 5028c2ecf20Sopenharmony_ci }; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* Note: Some of the above descriptions guessed from MR97113A driver */ 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci memcpy(data, startup_string, 11); 5078c2ecf20Sopenharmony_ci if (sd->sensor_type) 5088c2ecf20Sopenharmony_ci data[5] = 0xbb; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci switch (gspca_dev->pixfmt.width) { 5118c2ecf20Sopenharmony_ci case 160: 5128c2ecf20Sopenharmony_ci data[9] |= 0x04; /* reg 8, 2:1 scale down from 320 */ 5138c2ecf20Sopenharmony_ci fallthrough; 5148c2ecf20Sopenharmony_ci case 320: 5158c2ecf20Sopenharmony_ci default: 5168c2ecf20Sopenharmony_ci data[3] = 0x28; /* reg 2, H size/8 */ 5178c2ecf20Sopenharmony_ci data[4] = 0x3c; /* reg 3, V size/4 */ 5188c2ecf20Sopenharmony_ci data[6] = 0x14; /* reg 5, H start */ 5198c2ecf20Sopenharmony_ci data[8] = 0x1a + sd->sensor_type; /* reg 7, V start */ 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci case 176: 5228c2ecf20Sopenharmony_ci data[9] |= 0x04; /* reg 8, 2:1 scale down from 352 */ 5238c2ecf20Sopenharmony_ci fallthrough; 5248c2ecf20Sopenharmony_ci case 352: 5258c2ecf20Sopenharmony_ci data[3] = 0x2c; /* reg 2, H size/8 */ 5268c2ecf20Sopenharmony_ci data[4] = 0x48; /* reg 3, V size/4 */ 5278c2ecf20Sopenharmony_ci data[6] = 0x06; /* reg 5, H start */ 5288c2ecf20Sopenharmony_ci data[8] = 0x06 - sd->sensor_type; /* reg 7, V start */ 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci err_code = mr_write(gspca_dev, 11); 5328c2ecf20Sopenharmony_ci if (err_code < 0) 5338c2ecf20Sopenharmony_ci return err_code; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (!sd->sensor_type) { 5368c2ecf20Sopenharmony_ci static const struct sensor_w_data cif_sensor0_init_data[] = { 5378c2ecf20Sopenharmony_ci {0x02, 0x00, {0x03, 0x5a, 0xb5, 0x01, 5388c2ecf20Sopenharmony_ci 0x0f, 0x14, 0x0f, 0x10}, 8}, 5398c2ecf20Sopenharmony_ci {0x0c, 0x00, {0x04, 0x01, 0x01, 0x00, 0x1f}, 5}, 5408c2ecf20Sopenharmony_ci {0x12, 0x00, {0x07}, 1}, 5418c2ecf20Sopenharmony_ci {0x1f, 0x00, {0x06}, 1}, 5428c2ecf20Sopenharmony_ci {0x27, 0x00, {0x04}, 1}, 5438c2ecf20Sopenharmony_ci {0x29, 0x00, {0x0c}, 1}, 5448c2ecf20Sopenharmony_ci {0x40, 0x00, {0x40, 0x00, 0x04}, 3}, 5458c2ecf20Sopenharmony_ci {0x50, 0x00, {0x60}, 1}, 5468c2ecf20Sopenharmony_ci {0x60, 0x00, {0x06}, 1}, 5478c2ecf20Sopenharmony_ci {0x6b, 0x00, {0x85, 0x85, 0xc8, 0xc8, 0xc8, 0xc8}, 6}, 5488c2ecf20Sopenharmony_ci {0x72, 0x00, {0x1e, 0x56}, 2}, 5498c2ecf20Sopenharmony_ci {0x75, 0x00, {0x58, 0x40, 0xa2, 0x02, 0x31, 0x02, 5508c2ecf20Sopenharmony_ci 0x31, 0x80, 0x00}, 9}, 5518c2ecf20Sopenharmony_ci {0x11, 0x00, {0x01}, 1}, 5528c2ecf20Sopenharmony_ci {0, 0, {0}, 0} 5538c2ecf20Sopenharmony_ci }; 5548c2ecf20Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, cif_sensor0_init_data, 5558c2ecf20Sopenharmony_ci ARRAY_SIZE(cif_sensor0_init_data)); 5568c2ecf20Sopenharmony_ci } else { /* sd->sensor_type = 1 */ 5578c2ecf20Sopenharmony_ci static const struct sensor_w_data cif_sensor1_init_data[] = { 5588c2ecf20Sopenharmony_ci /* Reg 3,4, 7,8 get set by the controls */ 5598c2ecf20Sopenharmony_ci {0x02, 0x00, {0x10}, 1}, 5608c2ecf20Sopenharmony_ci {0x05, 0x01, {0x22}, 1}, /* 5/6 also seen as 65h/32h */ 5618c2ecf20Sopenharmony_ci {0x06, 0x01, {0x00}, 1}, 5628c2ecf20Sopenharmony_ci {0x09, 0x02, {0x0e}, 1}, 5638c2ecf20Sopenharmony_ci {0x0a, 0x02, {0x05}, 1}, 5648c2ecf20Sopenharmony_ci {0x0b, 0x02, {0x05}, 1}, 5658c2ecf20Sopenharmony_ci {0x0c, 0x02, {0x0f}, 1}, 5668c2ecf20Sopenharmony_ci {0x0d, 0x02, {0x07}, 1}, 5678c2ecf20Sopenharmony_ci {0x0e, 0x02, {0x0c}, 1}, 5688c2ecf20Sopenharmony_ci {0x0f, 0x00, {0x00}, 1}, 5698c2ecf20Sopenharmony_ci {0x10, 0x00, {0x06}, 1}, 5708c2ecf20Sopenharmony_ci {0x11, 0x00, {0x07}, 1}, 5718c2ecf20Sopenharmony_ci {0x12, 0x00, {0x00}, 1}, 5728c2ecf20Sopenharmony_ci {0x13, 0x00, {0x01}, 1}, 5738c2ecf20Sopenharmony_ci {0, 0, {0}, 0} 5748c2ecf20Sopenharmony_ci }; 5758c2ecf20Sopenharmony_ci /* Without this command the cam won't work with USB-UHCI */ 5768c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0] = 0x0a; 5778c2ecf20Sopenharmony_ci gspca_dev->usb_buf[1] = 0x00; 5788c2ecf20Sopenharmony_ci err_code = mr_write(gspca_dev, 2); 5798c2ecf20Sopenharmony_ci if (err_code < 0) 5808c2ecf20Sopenharmony_ci return err_code; 5818c2ecf20Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data, 5828c2ecf20Sopenharmony_ci ARRAY_SIZE(cif_sensor1_init_data)); 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci return err_code; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic int start_vga_cam(struct gspca_dev *gspca_dev) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5908c2ecf20Sopenharmony_ci __u8 *data = gspca_dev->usb_buf; 5918c2ecf20Sopenharmony_ci int err_code; 5928c2ecf20Sopenharmony_ci static const __u8 startup_string[] = 5938c2ecf20Sopenharmony_ci {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00, 5948c2ecf20Sopenharmony_ci 0x00, 0x50, 0xc0}; 5958c2ecf20Sopenharmony_ci /* What some of these mean is explained in start_cif_cam(), above */ 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci memcpy(data, startup_string, 11); 5988c2ecf20Sopenharmony_ci if (!sd->sensor_type) { 5998c2ecf20Sopenharmony_ci data[5] = 0x00; 6008c2ecf20Sopenharmony_ci data[10] = 0x91; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci if (sd->sensor_type == 2) { 6038c2ecf20Sopenharmony_ci data[5] = 0x00; 6048c2ecf20Sopenharmony_ci data[10] = 0x18; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci switch (gspca_dev->pixfmt.width) { 6088c2ecf20Sopenharmony_ci case 160: 6098c2ecf20Sopenharmony_ci data[9] |= 0x0c; /* reg 8, 4:1 scale down */ 6108c2ecf20Sopenharmony_ci fallthrough; 6118c2ecf20Sopenharmony_ci case 320: 6128c2ecf20Sopenharmony_ci data[9] |= 0x04; /* reg 8, 2:1 scale down */ 6138c2ecf20Sopenharmony_ci fallthrough; 6148c2ecf20Sopenharmony_ci case 640: 6158c2ecf20Sopenharmony_ci default: 6168c2ecf20Sopenharmony_ci data[3] = 0x50; /* reg 2, H size/8 */ 6178c2ecf20Sopenharmony_ci data[4] = 0x78; /* reg 3, V size/4 */ 6188c2ecf20Sopenharmony_ci data[6] = 0x04; /* reg 5, H start */ 6198c2ecf20Sopenharmony_ci data[8] = 0x03; /* reg 7, V start */ 6208c2ecf20Sopenharmony_ci if (sd->sensor_type == 2) { 6218c2ecf20Sopenharmony_ci data[6] = 2; 6228c2ecf20Sopenharmony_ci data[8] = 1; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci if (sd->do_lcd_stop) 6258c2ecf20Sopenharmony_ci data[8] = 0x04; /* Bayer tile shifted */ 6268c2ecf20Sopenharmony_ci break; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci case 176: 6298c2ecf20Sopenharmony_ci data[9] |= 0x04; /* reg 8, 2:1 scale down */ 6308c2ecf20Sopenharmony_ci fallthrough; 6318c2ecf20Sopenharmony_ci case 352: 6328c2ecf20Sopenharmony_ci data[3] = 0x2c; /* reg 2, H size */ 6338c2ecf20Sopenharmony_ci data[4] = 0x48; /* reg 3, V size */ 6348c2ecf20Sopenharmony_ci data[6] = 0x94; /* reg 5, H start */ 6358c2ecf20Sopenharmony_ci data[8] = 0x63; /* reg 7, V start */ 6368c2ecf20Sopenharmony_ci if (sd->do_lcd_stop) 6378c2ecf20Sopenharmony_ci data[8] = 0x64; /* Bayer tile shifted */ 6388c2ecf20Sopenharmony_ci break; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci err_code = mr_write(gspca_dev, 11); 6428c2ecf20Sopenharmony_ci if (err_code < 0) 6438c2ecf20Sopenharmony_ci return err_code; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (!sd->sensor_type) { 6468c2ecf20Sopenharmony_ci static const struct sensor_w_data vga_sensor0_init_data[] = { 6478c2ecf20Sopenharmony_ci {0x01, 0x00, {0x0c, 0x00, 0x04}, 3}, 6488c2ecf20Sopenharmony_ci {0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4}, 6498c2ecf20Sopenharmony_ci {0x20, 0x00, {0x00, 0x80, 0x00, 0x08}, 4}, 6508c2ecf20Sopenharmony_ci {0x25, 0x00, {0x03, 0xa9, 0x80}, 3}, 6518c2ecf20Sopenharmony_ci {0x30, 0x00, {0x30, 0x18, 0x10, 0x18}, 4}, 6528c2ecf20Sopenharmony_ci {0, 0, {0}, 0} 6538c2ecf20Sopenharmony_ci }; 6548c2ecf20Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data, 6558c2ecf20Sopenharmony_ci ARRAY_SIZE(vga_sensor0_init_data)); 6568c2ecf20Sopenharmony_ci } else if (sd->sensor_type == 1) { 6578c2ecf20Sopenharmony_ci static const struct sensor_w_data color_adj[] = { 6588c2ecf20Sopenharmony_ci {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, 6598c2ecf20Sopenharmony_ci /* adjusted blue, green, red gain correct 6608c2ecf20Sopenharmony_ci too much blue from the Sakar Digital */ 6618c2ecf20Sopenharmony_ci 0x05, 0x01, 0x04}, 8} 6628c2ecf20Sopenharmony_ci }; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci static const struct sensor_w_data color_no_adj[] = { 6658c2ecf20Sopenharmony_ci {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, 6668c2ecf20Sopenharmony_ci /* default blue, green, red gain settings */ 6678c2ecf20Sopenharmony_ci 0x07, 0x00, 0x01}, 8} 6688c2ecf20Sopenharmony_ci }; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci static const struct sensor_w_data vga_sensor1_init_data[] = { 6718c2ecf20Sopenharmony_ci {0x11, 0x04, {0x01}, 1}, 6728c2ecf20Sopenharmony_ci {0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, 6738c2ecf20Sopenharmony_ci /* These settings may be better for some cameras */ 6748c2ecf20Sopenharmony_ci /* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */ 6758c2ecf20Sopenharmony_ci 0x00, 0x0a}, 7}, 6768c2ecf20Sopenharmony_ci {0x11, 0x04, {0x01}, 1}, 6778c2ecf20Sopenharmony_ci {0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6}, 6788c2ecf20Sopenharmony_ci {0x11, 0x04, {0x01}, 1}, 6798c2ecf20Sopenharmony_ci {0, 0, {0}, 0} 6808c2ecf20Sopenharmony_ci }; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (sd->adj_colors) 6838c2ecf20Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, color_adj, 6848c2ecf20Sopenharmony_ci ARRAY_SIZE(color_adj)); 6858c2ecf20Sopenharmony_ci else 6868c2ecf20Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, color_no_adj, 6878c2ecf20Sopenharmony_ci ARRAY_SIZE(color_no_adj)); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (err_code < 0) 6908c2ecf20Sopenharmony_ci return err_code; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data, 6938c2ecf20Sopenharmony_ci ARRAY_SIZE(vga_sensor1_init_data)); 6948c2ecf20Sopenharmony_ci } else { /* sensor type == 2 */ 6958c2ecf20Sopenharmony_ci static const struct sensor_w_data vga_sensor2_init_data[] = { 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci {0x01, 0x00, {0x48}, 1}, 6988c2ecf20Sopenharmony_ci {0x02, 0x00, {0x22}, 1}, 6998c2ecf20Sopenharmony_ci /* Reg 3 msb and 4 is lsb of the exposure setting*/ 7008c2ecf20Sopenharmony_ci {0x05, 0x00, {0x10}, 1}, 7018c2ecf20Sopenharmony_ci {0x06, 0x00, {0x00}, 1}, 7028c2ecf20Sopenharmony_ci {0x07, 0x00, {0x00}, 1}, 7038c2ecf20Sopenharmony_ci {0x08, 0x00, {0x00}, 1}, 7048c2ecf20Sopenharmony_ci {0x09, 0x00, {0x00}, 1}, 7058c2ecf20Sopenharmony_ci /* The following are used in the gain control 7068c2ecf20Sopenharmony_ci * which is BTW completely borked in the OEM driver 7078c2ecf20Sopenharmony_ci * The values for each color go from 0 to 0x7ff 7088c2ecf20Sopenharmony_ci *{0x0a, 0x00, {0x01}, 1}, green1 gain msb 7098c2ecf20Sopenharmony_ci *{0x0b, 0x00, {0x10}, 1}, green1 gain lsb 7108c2ecf20Sopenharmony_ci *{0x0c, 0x00, {0x01}, 1}, red gain msb 7118c2ecf20Sopenharmony_ci *{0x0d, 0x00, {0x10}, 1}, red gain lsb 7128c2ecf20Sopenharmony_ci *{0x0e, 0x00, {0x01}, 1}, blue gain msb 7138c2ecf20Sopenharmony_ci *{0x0f, 0x00, {0x10}, 1}, blue gain lsb 7148c2ecf20Sopenharmony_ci *{0x10, 0x00, {0x01}, 1}, green2 gain msb 7158c2ecf20Sopenharmony_ci *{0x11, 0x00, {0x10}, 1}, green2 gain lsb 7168c2ecf20Sopenharmony_ci */ 7178c2ecf20Sopenharmony_ci {0x12, 0x00, {0x00}, 1}, 7188c2ecf20Sopenharmony_ci {0x13, 0x00, {0x04}, 1}, /* weird effect on colors */ 7198c2ecf20Sopenharmony_ci {0x14, 0x00, {0x00}, 1}, 7208c2ecf20Sopenharmony_ci {0x15, 0x00, {0x06}, 1}, 7218c2ecf20Sopenharmony_ci {0x16, 0x00, {0x01}, 1}, 7228c2ecf20Sopenharmony_ci {0x17, 0x00, {0xe2}, 1}, /* vertical alignment */ 7238c2ecf20Sopenharmony_ci {0x18, 0x00, {0x02}, 1}, 7248c2ecf20Sopenharmony_ci {0x19, 0x00, {0x82}, 1}, /* don't mess with */ 7258c2ecf20Sopenharmony_ci {0x1a, 0x00, {0x00}, 1}, 7268c2ecf20Sopenharmony_ci {0x1b, 0x00, {0x20}, 1}, 7278c2ecf20Sopenharmony_ci /* {0x1c, 0x00, {0x17}, 1}, contrast control */ 7288c2ecf20Sopenharmony_ci {0x1d, 0x00, {0x80}, 1}, /* moving causes a mess */ 7298c2ecf20Sopenharmony_ci {0x1e, 0x00, {0x08}, 1}, /* moving jams the camera */ 7308c2ecf20Sopenharmony_ci {0x1f, 0x00, {0x0c}, 1}, 7318c2ecf20Sopenharmony_ci {0x20, 0x00, {0x00}, 1}, 7328c2ecf20Sopenharmony_ci {0, 0, {0}, 0} 7338c2ecf20Sopenharmony_ci }; 7348c2ecf20Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, vga_sensor2_init_data, 7358c2ecf20Sopenharmony_ci ARRAY_SIZE(vga_sensor2_init_data)); 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci return err_code; 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 7438c2ecf20Sopenharmony_ci int err_code; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci sd->sof_read = 0; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* Some of the VGA cameras require the memory pointer 7488c2ecf20Sopenharmony_ci * to be set to 0 again. We have been forced to start the 7498c2ecf20Sopenharmony_ci * stream in sd_config() to detect the hardware, and closed it. 7508c2ecf20Sopenharmony_ci * Thus, we need here to do a completely fresh and clean start. */ 7518c2ecf20Sopenharmony_ci err_code = zero_the_pointer(gspca_dev); 7528c2ecf20Sopenharmony_ci if (err_code < 0) 7538c2ecf20Sopenharmony_ci return err_code; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci err_code = stream_start(gspca_dev); 7568c2ecf20Sopenharmony_ci if (err_code < 0) 7578c2ecf20Sopenharmony_ci return err_code; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (sd->cam_type == CAM_TYPE_CIF) { 7608c2ecf20Sopenharmony_ci err_code = start_cif_cam(gspca_dev); 7618c2ecf20Sopenharmony_ci } else { 7628c2ecf20Sopenharmony_ci err_code = start_vga_cam(gspca_dev); 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci if (err_code < 0) 7658c2ecf20Sopenharmony_ci return err_code; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci return isoc_enable(gspca_dev); 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci stream_stop(gspca_dev); 7758c2ecf20Sopenharmony_ci /* Not all the cams need this, but even if not, probably a good idea */ 7768c2ecf20Sopenharmony_ci zero_the_pointer(gspca_dev); 7778c2ecf20Sopenharmony_ci if (sd->do_lcd_stop) 7788c2ecf20Sopenharmony_ci lcd_stop(gspca_dev); 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic void setbrightness(struct gspca_dev *gspca_dev, s32 val) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 7848c2ecf20Sopenharmony_ci u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */ 7858c2ecf20Sopenharmony_ci u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */ 7868c2ecf20Sopenharmony_ci static const u8 quick_clix_table[] = 7878c2ecf20Sopenharmony_ci /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ 7888c2ecf20Sopenharmony_ci { 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15}; 7898c2ecf20Sopenharmony_ci if (sd->cam_type == CAM_TYPE_VGA) { 7908c2ecf20Sopenharmony_ci sign_reg += 4; 7918c2ecf20Sopenharmony_ci value_reg += 4; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* Note register 7 is also seen as 0x8x or 0xCx in some dumps */ 7958c2ecf20Sopenharmony_ci if (val > 0) { 7968c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, sign_reg, 0x00); 7978c2ecf20Sopenharmony_ci } else { 7988c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, sign_reg, 0x01); 7998c2ecf20Sopenharmony_ci val = 257 - val; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci /* Use lookup table for funky Argus QuickClix brightness */ 8028c2ecf20Sopenharmony_ci if (sd->do_lcd_stop) 8038c2ecf20Sopenharmony_ci val = quick_clix_table[val]; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, value_reg, val); 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_cistatic void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 8118c2ecf20Sopenharmony_ci int exposure = MR97310A_EXPOSURE_DEFAULT; 8128c2ecf20Sopenharmony_ci u8 buf[2]; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) { 8158c2ecf20Sopenharmony_ci /* This cam does not like exposure settings < 300, 8168c2ecf20Sopenharmony_ci so scale 0 - 4095 to 300 - 4095 */ 8178c2ecf20Sopenharmony_ci exposure = (expo * 9267) / 10000 + 300; 8188c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, 3, exposure >> 4); 8198c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, 4, exposure & 0x0f); 8208c2ecf20Sopenharmony_ci } else if (sd->sensor_type == 2) { 8218c2ecf20Sopenharmony_ci exposure = expo; 8228c2ecf20Sopenharmony_ci exposure >>= 3; 8238c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, 3, exposure >> 8); 8248c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, 4, exposure & 0xff); 8258c2ecf20Sopenharmony_ci } else { 8268c2ecf20Sopenharmony_ci /* We have both a clock divider and an exposure register. 8278c2ecf20Sopenharmony_ci We first calculate the clock divider, as that determines 8288c2ecf20Sopenharmony_ci the maximum exposure and then we calculate the exposure 8298c2ecf20Sopenharmony_ci register setting (which goes from 0 - 511). 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci Note our 0 - 4095 exposure is mapped to 0 - 511 8328c2ecf20Sopenharmony_ci milliseconds exposure time */ 8338c2ecf20Sopenharmony_ci u8 clockdiv = (60 * expo + 7999) / 8000; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* Limit framerate to not exceed usb bandwidth */ 8368c2ecf20Sopenharmony_ci if (clockdiv < min_clockdiv && gspca_dev->pixfmt.width >= 320) 8378c2ecf20Sopenharmony_ci clockdiv = min_clockdiv; 8388c2ecf20Sopenharmony_ci else if (clockdiv < 2) 8398c2ecf20Sopenharmony_ci clockdiv = 2; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4) 8428c2ecf20Sopenharmony_ci clockdiv = 4; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* Frame exposure time in ms = 1000 * clockdiv / 60 -> 8458c2ecf20Sopenharmony_ci exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */ 8468c2ecf20Sopenharmony_ci exposure = (60 * 511 * expo) / (8000 * clockdiv); 8478c2ecf20Sopenharmony_ci if (exposure > 511) 8488c2ecf20Sopenharmony_ci exposure = 511; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci /* exposure register value is reversed! */ 8518c2ecf20Sopenharmony_ci exposure = 511 - exposure; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci buf[0] = exposure & 0xff; 8548c2ecf20Sopenharmony_ci buf[1] = exposure >> 8; 8558c2ecf20Sopenharmony_ci sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2); 8568c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, 0x02, clockdiv); 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic void setgain(struct gspca_dev *gspca_dev, s32 val) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 8638c2ecf20Sopenharmony_ci u8 gainreg; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) 8668c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, 0x0e, val); 8678c2ecf20Sopenharmony_ci else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2) 8688c2ecf20Sopenharmony_ci for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) { 8698c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, gainreg, val >> 8); 8708c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, gainreg + 1, val & 0xff); 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci else 8738c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, 0x10, val); 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic void setcontrast(struct gspca_dev *gspca_dev, s32 val) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci sensor_write1(gspca_dev, 0x1c, val); 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = 8848c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 8858c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci gspca_dev->usb_err = 0; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (!gspca_dev->streaming) 8908c2ecf20Sopenharmony_ci return 0; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci switch (ctrl->id) { 8938c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 8948c2ecf20Sopenharmony_ci setbrightness(gspca_dev, ctrl->val); 8958c2ecf20Sopenharmony_ci break; 8968c2ecf20Sopenharmony_ci case V4L2_CID_CONTRAST: 8978c2ecf20Sopenharmony_ci setcontrast(gspca_dev, ctrl->val); 8988c2ecf20Sopenharmony_ci break; 8998c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE: 9008c2ecf20Sopenharmony_ci setexposure(gspca_dev, sd->exposure->val, 9018c2ecf20Sopenharmony_ci sd->min_clockdiv ? sd->min_clockdiv->val : 0); 9028c2ecf20Sopenharmony_ci break; 9038c2ecf20Sopenharmony_ci case V4L2_CID_GAIN: 9048c2ecf20Sopenharmony_ci setgain(gspca_dev, ctrl->val); 9058c2ecf20Sopenharmony_ci break; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = { 9118c2ecf20Sopenharmony_ci .s_ctrl = sd_s_ctrl, 9128c2ecf20Sopenharmony_ci}; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 9178c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 9188c2ecf20Sopenharmony_ci static const struct v4l2_ctrl_config clockdiv = { 9198c2ecf20Sopenharmony_ci .ops = &sd_ctrl_ops, 9208c2ecf20Sopenharmony_ci .id = MR97310A_CID_CLOCKDIV, 9218c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 9228c2ecf20Sopenharmony_ci .name = "Minimum Clock Divider", 9238c2ecf20Sopenharmony_ci .min = MR97310A_MIN_CLOCKDIV_MIN, 9248c2ecf20Sopenharmony_ci .max = MR97310A_MIN_CLOCKDIV_MAX, 9258c2ecf20Sopenharmony_ci .step = 1, 9268c2ecf20Sopenharmony_ci .def = MR97310A_MIN_CLOCKDIV_DEFAULT, 9278c2ecf20Sopenharmony_ci }; 9288c2ecf20Sopenharmony_ci bool has_brightness = false; 9298c2ecf20Sopenharmony_ci bool has_argus_brightness = false; 9308c2ecf20Sopenharmony_ci bool has_contrast = false; 9318c2ecf20Sopenharmony_ci bool has_gain = false; 9328c2ecf20Sopenharmony_ci bool has_cs_gain = false; 9338c2ecf20Sopenharmony_ci bool has_exposure = false; 9348c2ecf20Sopenharmony_ci bool has_clockdiv = false; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci gspca_dev->vdev.ctrl_handler = hdl; 9378c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 4); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* Setup controls depending on camera type */ 9408c2ecf20Sopenharmony_ci if (sd->cam_type == CAM_TYPE_CIF) { 9418c2ecf20Sopenharmony_ci /* No brightness for sensor_type 0 */ 9428c2ecf20Sopenharmony_ci if (sd->sensor_type == 0) 9438c2ecf20Sopenharmony_ci has_exposure = has_gain = has_clockdiv = true; 9448c2ecf20Sopenharmony_ci else 9458c2ecf20Sopenharmony_ci has_exposure = has_gain = has_brightness = true; 9468c2ecf20Sopenharmony_ci } else { 9478c2ecf20Sopenharmony_ci /* All controls need to be disabled if VGA sensor_type is 0 */ 9488c2ecf20Sopenharmony_ci if (sd->sensor_type == 0) 9498c2ecf20Sopenharmony_ci ; /* no controls! */ 9508c2ecf20Sopenharmony_ci else if (sd->sensor_type == 2) 9518c2ecf20Sopenharmony_ci has_exposure = has_cs_gain = has_contrast = true; 9528c2ecf20Sopenharmony_ci else if (sd->do_lcd_stop) 9538c2ecf20Sopenharmony_ci has_exposure = has_gain = has_argus_brightness = 9548c2ecf20Sopenharmony_ci has_clockdiv = true; 9558c2ecf20Sopenharmony_ci else 9568c2ecf20Sopenharmony_ci has_exposure = has_gain = has_brightness = 9578c2ecf20Sopenharmony_ci has_clockdiv = true; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* Separate brightness control description for Argus QuickClix as it has 9618c2ecf20Sopenharmony_ci * different limits from the other mr97310a cameras, and separate gain 9628c2ecf20Sopenharmony_ci * control for Sakar CyberPix camera. */ 9638c2ecf20Sopenharmony_ci /* 9648c2ecf20Sopenharmony_ci * This control is disabled for CIF type 1 and VGA type 0 cameras. 9658c2ecf20Sopenharmony_ci * It does not quite act linearly for the Argus QuickClix camera, 9668c2ecf20Sopenharmony_ci * but it does control brightness. The values are 0 - 15 only, and 9678c2ecf20Sopenharmony_ci * the table above makes them act consecutively. 9688c2ecf20Sopenharmony_ci */ 9698c2ecf20Sopenharmony_ci if (has_brightness) 9708c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 9718c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, -254, 255, 1, 9728c2ecf20Sopenharmony_ci MR97310A_BRIGHTNESS_DEFAULT); 9738c2ecf20Sopenharmony_ci else if (has_argus_brightness) 9748c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 9758c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, 0, 15, 1, 9768c2ecf20Sopenharmony_ci MR97310A_BRIGHTNESS_DEFAULT); 9778c2ecf20Sopenharmony_ci if (has_contrast) 9788c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 9798c2ecf20Sopenharmony_ci V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN, 9808c2ecf20Sopenharmony_ci MR97310A_CONTRAST_MAX, 1, MR97310A_CONTRAST_DEFAULT); 9818c2ecf20Sopenharmony_ci if (has_gain) 9828c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 9838c2ecf20Sopenharmony_ci V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX, 9848c2ecf20Sopenharmony_ci 1, MR97310A_GAIN_DEFAULT); 9858c2ecf20Sopenharmony_ci else if (has_cs_gain) 9868c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN, 9878c2ecf20Sopenharmony_ci MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX, 9888c2ecf20Sopenharmony_ci 1, MR97310A_CS_GAIN_DEFAULT); 9898c2ecf20Sopenharmony_ci if (has_exposure) 9908c2ecf20Sopenharmony_ci sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 9918c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN, 9928c2ecf20Sopenharmony_ci MR97310A_EXPOSURE_MAX, 1, MR97310A_EXPOSURE_DEFAULT); 9938c2ecf20Sopenharmony_ci if (has_clockdiv) 9948c2ecf20Sopenharmony_ci sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci if (hdl->error) { 9978c2ecf20Sopenharmony_ci pr_err("Could not initialize controls\n"); 9988c2ecf20Sopenharmony_ci return hdl->error; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci if (has_exposure && has_clockdiv) 10018c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(2, &sd->exposure); 10028c2ecf20Sopenharmony_ci return 0; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci/* Include pac common sof detection functions */ 10068c2ecf20Sopenharmony_ci#include "pac_common.h" 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, 10098c2ecf20Sopenharmony_ci u8 *data, /* isoc packet */ 10108c2ecf20Sopenharmony_ci int len) /* iso packet length */ 10118c2ecf20Sopenharmony_ci{ 10128c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 10138c2ecf20Sopenharmony_ci unsigned char *sof; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len); 10168c2ecf20Sopenharmony_ci if (sof) { 10178c2ecf20Sopenharmony_ci int n; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* finish decoding current frame */ 10208c2ecf20Sopenharmony_ci n = sof - data; 10218c2ecf20Sopenharmony_ci if (n > sizeof pac_sof_marker) 10228c2ecf20Sopenharmony_ci n -= sizeof pac_sof_marker; 10238c2ecf20Sopenharmony_ci else 10248c2ecf20Sopenharmony_ci n = 0; 10258c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, LAST_PACKET, 10268c2ecf20Sopenharmony_ci data, n); 10278c2ecf20Sopenharmony_ci /* Start next frame. */ 10288c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, 10298c2ecf20Sopenharmony_ci pac_sof_marker, sizeof pac_sof_marker); 10308c2ecf20Sopenharmony_ci len -= sof - data; 10318c2ecf20Sopenharmony_ci data = sof; 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, INTER_PACKET, data, len); 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci/* sub-driver description */ 10378c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = { 10388c2ecf20Sopenharmony_ci .name = MODULE_NAME, 10398c2ecf20Sopenharmony_ci .config = sd_config, 10408c2ecf20Sopenharmony_ci .init = sd_init, 10418c2ecf20Sopenharmony_ci .init_controls = sd_init_controls, 10428c2ecf20Sopenharmony_ci .start = sd_start, 10438c2ecf20Sopenharmony_ci .stopN = sd_stopN, 10448c2ecf20Sopenharmony_ci .pkt_scan = sd_pkt_scan, 10458c2ecf20Sopenharmony_ci}; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci/* -- module initialisation -- */ 10488c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = { 10498c2ecf20Sopenharmony_ci {USB_DEVICE(0x08ca, 0x0110)}, /* Trust Spyc@m 100 */ 10508c2ecf20Sopenharmony_ci {USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */ 10518c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */ 10528c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x010e)}, /* All known MR97310A CIF cams */ 10538c2ecf20Sopenharmony_ci {} 10548c2ecf20Sopenharmony_ci}; 10558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci/* -- device connect -- */ 10588c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, 10598c2ecf20Sopenharmony_ci const struct usb_device_id *id) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), 10628c2ecf20Sopenharmony_ci THIS_MODULE); 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = { 10668c2ecf20Sopenharmony_ci .name = MODULE_NAME, 10678c2ecf20Sopenharmony_ci .id_table = device_table, 10688c2ecf20Sopenharmony_ci .probe = sd_probe, 10698c2ecf20Sopenharmony_ci .disconnect = gspca_disconnect, 10708c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 10718c2ecf20Sopenharmony_ci .suspend = gspca_suspend, 10728c2ecf20Sopenharmony_ci .resume = gspca_resume, 10738c2ecf20Sopenharmony_ci .reset_resume = gspca_resume, 10748c2ecf20Sopenharmony_ci#endif 10758c2ecf20Sopenharmony_ci}; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver); 1078