162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Mars MR97310A library 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is 662306a36Sopenharmony_ci * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+ 962306a36Sopenharmony_ci * and for the routines for detecting and classifying these various cameras, 1062306a36Sopenharmony_ci * is Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Support for the control settings for the CIF cameras is 1362306a36Sopenharmony_ci * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com> and 1462306a36Sopenharmony_ci * Thomas Kaiser <thomas@kaiser-linux.li> 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Support for the control settings for the VGA cameras is 1762306a36Sopenharmony_ci * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu> 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Several previously unsupported cameras are owned and have been tested by 2062306a36Sopenharmony_ci * Hans de Goede <hdegoede@redhat.com> and 2162306a36Sopenharmony_ci * Thomas Kaiser <thomas@kaiser-linux.li> and 2262306a36Sopenharmony_ci * Theodore Kilgore <kilgota@auburn.edu> and 2362306a36Sopenharmony_ci * Edmond Rodriguez <erodrig_97@yahoo.com> and 2462306a36Sopenharmony_ci * Aurelien Jacobs <aurel@gnuage.org> 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * The MR97311A support in gspca/mars.c has been helpful in understanding some 2762306a36Sopenharmony_ci * of the registers in these cameras. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define MODULE_NAME "mr97310a" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "gspca.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define CAM_TYPE_CIF 0 3762306a36Sopenharmony_ci#define CAM_TYPE_VGA 1 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define MR97310A_BRIGHTNESS_DEFAULT 0 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define MR97310A_EXPOSURE_MIN 0 4262306a36Sopenharmony_ci#define MR97310A_EXPOSURE_MAX 4095 4362306a36Sopenharmony_ci#define MR97310A_EXPOSURE_DEFAULT 1000 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define MR97310A_GAIN_MIN 0 4662306a36Sopenharmony_ci#define MR97310A_GAIN_MAX 31 4762306a36Sopenharmony_ci#define MR97310A_GAIN_DEFAULT 25 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define MR97310A_CONTRAST_MIN 0 5062306a36Sopenharmony_ci#define MR97310A_CONTRAST_MAX 31 5162306a36Sopenharmony_ci#define MR97310A_CONTRAST_DEFAULT 23 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define MR97310A_CS_GAIN_MIN 0 5462306a36Sopenharmony_ci#define MR97310A_CS_GAIN_MAX 0x7ff 5562306a36Sopenharmony_ci#define MR97310A_CS_GAIN_DEFAULT 0x110 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0x1000) 5862306a36Sopenharmony_ci#define MR97310A_MIN_CLOCKDIV_MIN 3 5962306a36Sopenharmony_ci#define MR97310A_MIN_CLOCKDIV_MAX 8 6062306a36Sopenharmony_ci#define MR97310A_MIN_CLOCKDIV_DEFAULT 3 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciMODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>,Theodore Kilgore <kilgota@auburn.edu>"); 6362306a36Sopenharmony_ciMODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver"); 6462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* global parameters */ 6762306a36Sopenharmony_cistatic int force_sensor_type = -1; 6862306a36Sopenharmony_cimodule_param(force_sensor_type, int, 0644); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)"); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* specific webcam descriptor */ 7262306a36Sopenharmony_cistruct sd { 7362306a36Sopenharmony_ci struct gspca_dev gspca_dev; /* !! must be the first item */ 7462306a36Sopenharmony_ci struct { /* exposure/min_clockdiv control cluster */ 7562306a36Sopenharmony_ci struct v4l2_ctrl *exposure; 7662306a36Sopenharmony_ci struct v4l2_ctrl *min_clockdiv; 7762306a36Sopenharmony_ci }; 7862306a36Sopenharmony_ci u8 sof_read; 7962306a36Sopenharmony_ci u8 cam_type; /* 0 is CIF and 1 is VGA */ 8062306a36Sopenharmony_ci u8 sensor_type; /* We use 0 and 1 here, too. */ 8162306a36Sopenharmony_ci u8 do_lcd_stop; 8262306a36Sopenharmony_ci u8 adj_colors; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistruct sensor_w_data { 8662306a36Sopenharmony_ci u8 reg; 8762306a36Sopenharmony_ci u8 flags; 8862306a36Sopenharmony_ci u8 data[16]; 8962306a36Sopenharmony_ci int len; 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic const struct v4l2_pix_format vga_mode[] = { 9562306a36Sopenharmony_ci {160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, 9662306a36Sopenharmony_ci .bytesperline = 160, 9762306a36Sopenharmony_ci .sizeimage = 160 * 120, 9862306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 9962306a36Sopenharmony_ci .priv = 4}, 10062306a36Sopenharmony_ci {176, 144, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, 10162306a36Sopenharmony_ci .bytesperline = 176, 10262306a36Sopenharmony_ci .sizeimage = 176 * 144, 10362306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 10462306a36Sopenharmony_ci .priv = 3}, 10562306a36Sopenharmony_ci {320, 240, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, 10662306a36Sopenharmony_ci .bytesperline = 320, 10762306a36Sopenharmony_ci .sizeimage = 320 * 240, 10862306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 10962306a36Sopenharmony_ci .priv = 2}, 11062306a36Sopenharmony_ci {352, 288, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, 11162306a36Sopenharmony_ci .bytesperline = 352, 11262306a36Sopenharmony_ci .sizeimage = 352 * 288, 11362306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 11462306a36Sopenharmony_ci .priv = 1}, 11562306a36Sopenharmony_ci {640, 480, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, 11662306a36Sopenharmony_ci .bytesperline = 640, 11762306a36Sopenharmony_ci .sizeimage = 640 * 480, 11862306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 11962306a36Sopenharmony_ci .priv = 0}, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* the bytes to write are in gspca_dev->usb_buf */ 12362306a36Sopenharmony_cistatic int mr_write(struct gspca_dev *gspca_dev, int len) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci int rc; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci rc = usb_bulk_msg(gspca_dev->dev, 12862306a36Sopenharmony_ci usb_sndbulkpipe(gspca_dev->dev, 4), 12962306a36Sopenharmony_ci gspca_dev->usb_buf, len, NULL, 500); 13062306a36Sopenharmony_ci if (rc < 0) 13162306a36Sopenharmony_ci pr_err("reg write [%02x] error %d\n", 13262306a36Sopenharmony_ci gspca_dev->usb_buf[0], rc); 13362306a36Sopenharmony_ci return rc; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* the bytes are read into gspca_dev->usb_buf */ 13762306a36Sopenharmony_cistatic int mr_read(struct gspca_dev *gspca_dev, int len) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci int rc; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci rc = usb_bulk_msg(gspca_dev->dev, 14262306a36Sopenharmony_ci usb_rcvbulkpipe(gspca_dev->dev, 3), 14362306a36Sopenharmony_ci gspca_dev->usb_buf, len, NULL, 500); 14462306a36Sopenharmony_ci if (rc < 0) 14562306a36Sopenharmony_ci pr_err("reg read [%02x] error %d\n", 14662306a36Sopenharmony_ci gspca_dev->usb_buf[0], rc); 14762306a36Sopenharmony_ci return rc; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int sensor_write_reg(struct gspca_dev *gspca_dev, u8 reg, u8 flags, 15162306a36Sopenharmony_ci const u8 *data, int len) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci gspca_dev->usb_buf[0] = 0x1f; 15462306a36Sopenharmony_ci gspca_dev->usb_buf[1] = flags; 15562306a36Sopenharmony_ci gspca_dev->usb_buf[2] = reg; 15662306a36Sopenharmony_ci memcpy(gspca_dev->usb_buf + 3, data, len); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return mr_write(gspca_dev, len + 3); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int sensor_write_regs(struct gspca_dev *gspca_dev, 16262306a36Sopenharmony_ci const struct sensor_w_data *data, int len) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci int i, rc; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci for (i = 0; i < len; i++) { 16762306a36Sopenharmony_ci rc = sensor_write_reg(gspca_dev, data[i].reg, data[i].flags, 16862306a36Sopenharmony_ci data[i].data, data[i].len); 16962306a36Sopenharmony_ci if (rc < 0) 17062306a36Sopenharmony_ci return rc; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 17962306a36Sopenharmony_ci u8 buf, confirm_reg; 18062306a36Sopenharmony_ci int rc; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci buf = data; 18362306a36Sopenharmony_ci if (sd->cam_type == CAM_TYPE_CIF) { 18462306a36Sopenharmony_ci rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1); 18562306a36Sopenharmony_ci confirm_reg = sd->sensor_type ? 0x13 : 0x11; 18662306a36Sopenharmony_ci } else { 18762306a36Sopenharmony_ci rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1); 18862306a36Sopenharmony_ci confirm_reg = 0x11; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci if (rc < 0) 19162306a36Sopenharmony_ci return rc; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci buf = 0x01; 19462306a36Sopenharmony_ci rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1); 19562306a36Sopenharmony_ci if (rc < 0) 19662306a36Sopenharmony_ci return rc; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci int err_code; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci gspca_dev->usb_buf[0] = reg; 20662306a36Sopenharmony_ci err_code = mr_write(gspca_dev, 1); 20762306a36Sopenharmony_ci if (err_code < 0) 20862306a36Sopenharmony_ci return err_code; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci err_code = mr_read(gspca_dev, 16); 21162306a36Sopenharmony_ci if (err_code < 0) 21262306a36Sopenharmony_ci return err_code; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (verbose) 21562306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "Register: %02x reads %02x%02x%02x\n", 21662306a36Sopenharmony_ci reg, 21762306a36Sopenharmony_ci gspca_dev->usb_buf[0], 21862306a36Sopenharmony_ci gspca_dev->usb_buf[1], 21962306a36Sopenharmony_ci gspca_dev->usb_buf[2]); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int zero_the_pointer(struct gspca_dev *gspca_dev) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci __u8 *data = gspca_dev->usb_buf; 22762306a36Sopenharmony_ci int err_code; 22862306a36Sopenharmony_ci u8 status = 0; 22962306a36Sopenharmony_ci int tries = 0; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x21, 0); 23262306a36Sopenharmony_ci if (err_code < 0) 23362306a36Sopenharmony_ci return err_code; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci data[0] = 0x19; 23662306a36Sopenharmony_ci data[1] = 0x51; 23762306a36Sopenharmony_ci err_code = mr_write(gspca_dev, 2); 23862306a36Sopenharmony_ci if (err_code < 0) 23962306a36Sopenharmony_ci return err_code; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x21, 0); 24262306a36Sopenharmony_ci if (err_code < 0) 24362306a36Sopenharmony_ci return err_code; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci data[0] = 0x19; 24662306a36Sopenharmony_ci data[1] = 0xba; 24762306a36Sopenharmony_ci err_code = mr_write(gspca_dev, 2); 24862306a36Sopenharmony_ci if (err_code < 0) 24962306a36Sopenharmony_ci return err_code; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x21, 0); 25262306a36Sopenharmony_ci if (err_code < 0) 25362306a36Sopenharmony_ci return err_code; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci data[0] = 0x19; 25662306a36Sopenharmony_ci data[1] = 0x00; 25762306a36Sopenharmony_ci err_code = mr_write(gspca_dev, 2); 25862306a36Sopenharmony_ci if (err_code < 0) 25962306a36Sopenharmony_ci return err_code; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x21, 0); 26262306a36Sopenharmony_ci if (err_code < 0) 26362306a36Sopenharmony_ci return err_code; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci data[0] = 0x19; 26662306a36Sopenharmony_ci data[1] = 0x00; 26762306a36Sopenharmony_ci err_code = mr_write(gspca_dev, 2); 26862306a36Sopenharmony_ci if (err_code < 0) 26962306a36Sopenharmony_ci return err_code; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci while (status != 0x0a && tries < 256) { 27262306a36Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x21, 0); 27362306a36Sopenharmony_ci status = data[0]; 27462306a36Sopenharmony_ci tries++; 27562306a36Sopenharmony_ci if (err_code < 0) 27662306a36Sopenharmony_ci return err_code; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci if (status != 0x0a) 27962306a36Sopenharmony_ci gspca_err(gspca_dev, "status is %02x\n", status); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci tries = 0; 28262306a36Sopenharmony_ci while (tries < 4) { 28362306a36Sopenharmony_ci data[0] = 0x19; 28462306a36Sopenharmony_ci data[1] = 0x00; 28562306a36Sopenharmony_ci err_code = mr_write(gspca_dev, 2); 28662306a36Sopenharmony_ci if (err_code < 0) 28762306a36Sopenharmony_ci return err_code; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x21, 0); 29062306a36Sopenharmony_ci tries++; 29162306a36Sopenharmony_ci if (err_code < 0) 29262306a36Sopenharmony_ci return err_code; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci data[0] = 0x19; 29662306a36Sopenharmony_ci err_code = mr_write(gspca_dev, 1); 29762306a36Sopenharmony_ci if (err_code < 0) 29862306a36Sopenharmony_ci return err_code; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci err_code = mr_read(gspca_dev, 16); 30162306a36Sopenharmony_ci if (err_code < 0) 30262306a36Sopenharmony_ci return err_code; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int stream_start(struct gspca_dev *gspca_dev) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci gspca_dev->usb_buf[0] = 0x01; 31062306a36Sopenharmony_ci gspca_dev->usb_buf[1] = 0x01; 31162306a36Sopenharmony_ci return mr_write(gspca_dev, 2); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void stream_stop(struct gspca_dev *gspca_dev) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci gspca_dev->usb_buf[0] = 0x01; 31762306a36Sopenharmony_ci gspca_dev->usb_buf[1] = 0x00; 31862306a36Sopenharmony_ci if (mr_write(gspca_dev, 2) < 0) 31962306a36Sopenharmony_ci gspca_err(gspca_dev, "Stream Stop failed\n"); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void lcd_stop(struct gspca_dev *gspca_dev) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci gspca_dev->usb_buf[0] = 0x19; 32562306a36Sopenharmony_ci gspca_dev->usb_buf[1] = 0x54; 32662306a36Sopenharmony_ci if (mr_write(gspca_dev, 2) < 0) 32762306a36Sopenharmony_ci gspca_err(gspca_dev, "LCD Stop failed\n"); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int isoc_enable(struct gspca_dev *gspca_dev) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci gspca_dev->usb_buf[0] = 0x00; 33362306a36Sopenharmony_ci gspca_dev->usb_buf[1] = 0x4d; /* ISOC transferring enable... */ 33462306a36Sopenharmony_ci return mr_write(gspca_dev, 2); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci/* This function is called at probe time */ 33862306a36Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 33962306a36Sopenharmony_ci const struct usb_device_id *id) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 34262306a36Sopenharmony_ci struct cam *cam; 34362306a36Sopenharmony_ci int err_code; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci cam = &gspca_dev->cam; 34662306a36Sopenharmony_ci cam->cam_mode = vga_mode; 34762306a36Sopenharmony_ci cam->nmodes = ARRAY_SIZE(vga_mode); 34862306a36Sopenharmony_ci sd->do_lcd_stop = 0; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Several of the supported CIF cameras share the same USB ID but 35162306a36Sopenharmony_ci * require different initializations and different control settings. 35262306a36Sopenharmony_ci * The same is true of the VGA cameras. Therefore, we are forced 35362306a36Sopenharmony_ci * to start the initialization process in order to determine which 35462306a36Sopenharmony_ci * camera is present. Some of the supported cameras require the 35562306a36Sopenharmony_ci * memory pointer to be set to 0 as the very first item of business 35662306a36Sopenharmony_ci * or else they will not stream. So we do that immediately. 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ci err_code = zero_the_pointer(gspca_dev); 35962306a36Sopenharmony_ci if (err_code < 0) 36062306a36Sopenharmony_ci return err_code; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci err_code = stream_start(gspca_dev); 36362306a36Sopenharmony_ci if (err_code < 0) 36462306a36Sopenharmony_ci return err_code; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Now, the query for sensor type. */ 36762306a36Sopenharmony_ci err_code = cam_get_response16(gspca_dev, 0x07, 1); 36862306a36Sopenharmony_ci if (err_code < 0) 36962306a36Sopenharmony_ci return err_code; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (id->idProduct == 0x0110 || id->idProduct == 0x010e) { 37262306a36Sopenharmony_ci sd->cam_type = CAM_TYPE_CIF; 37362306a36Sopenharmony_ci cam->nmodes--; 37462306a36Sopenharmony_ci /* 37562306a36Sopenharmony_ci * All but one of the known CIF cameras share the same USB ID, 37662306a36Sopenharmony_ci * but two different init routines are in use, and the control 37762306a36Sopenharmony_ci * settings are different, too. We need to detect which camera 37862306a36Sopenharmony_ci * of the two known varieties is connected! 37962306a36Sopenharmony_ci * 38062306a36Sopenharmony_ci * A list of known CIF cameras follows. They all report either 38162306a36Sopenharmony_ci * 0200 for type 0 or 0300 for type 1. 38262306a36Sopenharmony_ci * If you have another to report, please do 38362306a36Sopenharmony_ci * 38462306a36Sopenharmony_ci * Name sd->sensor_type reported by 38562306a36Sopenharmony_ci * 38662306a36Sopenharmony_ci * Sakar 56379 Spy-shot 0 T. Kilgore 38762306a36Sopenharmony_ci * Innovage 0 T. Kilgore 38862306a36Sopenharmony_ci * Vivitar Mini 0 H. De Goede 38962306a36Sopenharmony_ci * Vivitar Mini 0 E. Rodriguez 39062306a36Sopenharmony_ci * Vivitar Mini 1 T. Kilgore 39162306a36Sopenharmony_ci * Elta-Media 8212dc 1 T. Kaiser 39262306a36Sopenharmony_ci * Philips dig. keych. 1 T. Kilgore 39362306a36Sopenharmony_ci * Trust Spyc@m 100 1 A. Jacobs 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_ci switch (gspca_dev->usb_buf[0]) { 39662306a36Sopenharmony_ci case 2: 39762306a36Sopenharmony_ci sd->sensor_type = 0; 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci case 3: 40062306a36Sopenharmony_ci sd->sensor_type = 1; 40162306a36Sopenharmony_ci break; 40262306a36Sopenharmony_ci default: 40362306a36Sopenharmony_ci pr_err("Unknown CIF Sensor id : %02x\n", 40462306a36Sopenharmony_ci gspca_dev->usb_buf[1]); 40562306a36Sopenharmony_ci return -ENODEV; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "MR97310A CIF camera detected, sensor: %d\n", 40862306a36Sopenharmony_ci sd->sensor_type); 40962306a36Sopenharmony_ci } else { 41062306a36Sopenharmony_ci sd->cam_type = CAM_TYPE_VGA; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * Here is a table of the responses to the query for sensor 41462306a36Sopenharmony_ci * type, from the known MR97310A VGA cameras. Six different 41562306a36Sopenharmony_ci * cameras of which five share the same USB ID. 41662306a36Sopenharmony_ci * 41762306a36Sopenharmony_ci * Name gspca_dev->usb_buf[] sd->sensor_type 41862306a36Sopenharmony_ci * sd->do_lcd_stop 41962306a36Sopenharmony_ci * Aiptek Pencam VGA+ 0300 0 1 42062306a36Sopenharmony_ci * ION digital 0300 0 1 42162306a36Sopenharmony_ci * Argus DC-1620 0450 1 0 42262306a36Sopenharmony_ci * Argus QuickClix 0420 1 1 42362306a36Sopenharmony_ci * Sakar 77379 Digital 0350 0 1 42462306a36Sopenharmony_ci * Sakar 1638x CyberPix 0120 0 2 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * Based upon these results, we assume default settings 42762306a36Sopenharmony_ci * and then correct as necessary, as follows. 42862306a36Sopenharmony_ci * 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci sd->sensor_type = 1; 43262306a36Sopenharmony_ci sd->do_lcd_stop = 0; 43362306a36Sopenharmony_ci sd->adj_colors = 0; 43462306a36Sopenharmony_ci if (gspca_dev->usb_buf[0] == 0x01) { 43562306a36Sopenharmony_ci sd->sensor_type = 2; 43662306a36Sopenharmony_ci } else if ((gspca_dev->usb_buf[0] != 0x03) && 43762306a36Sopenharmony_ci (gspca_dev->usb_buf[0] != 0x04)) { 43862306a36Sopenharmony_ci pr_err("Unknown VGA Sensor id Byte 0: %02x\n", 43962306a36Sopenharmony_ci gspca_dev->usb_buf[0]); 44062306a36Sopenharmony_ci pr_err("Defaults assumed, may not work\n"); 44162306a36Sopenharmony_ci pr_err("Please report this\n"); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci /* Sakar Digital color needs to be adjusted. */ 44462306a36Sopenharmony_ci if ((gspca_dev->usb_buf[0] == 0x03) && 44562306a36Sopenharmony_ci (gspca_dev->usb_buf[1] == 0x50)) 44662306a36Sopenharmony_ci sd->adj_colors = 1; 44762306a36Sopenharmony_ci if (gspca_dev->usb_buf[0] == 0x04) { 44862306a36Sopenharmony_ci sd->do_lcd_stop = 1; 44962306a36Sopenharmony_ci switch (gspca_dev->usb_buf[1]) { 45062306a36Sopenharmony_ci case 0x50: 45162306a36Sopenharmony_ci sd->sensor_type = 0; 45262306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "sensor_type corrected to 0\n"); 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci case 0x20: 45562306a36Sopenharmony_ci /* Nothing to do here. */ 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci default: 45862306a36Sopenharmony_ci pr_err("Unknown VGA Sensor id Byte 1: %02x\n", 45962306a36Sopenharmony_ci gspca_dev->usb_buf[1]); 46062306a36Sopenharmony_ci pr_err("Defaults assumed, may not work\n"); 46162306a36Sopenharmony_ci pr_err("Please report this\n"); 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "MR97310A VGA camera detected, sensor: %d\n", 46562306a36Sopenharmony_ci sd->sensor_type); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci /* Stop streaming as we've started it only to probe the sensor type. */ 46862306a36Sopenharmony_ci sd_stopN(gspca_dev); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (force_sensor_type != -1) { 47162306a36Sopenharmony_ci sd->sensor_type = !!force_sensor_type; 47262306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "Forcing sensor type to: %d\n", 47362306a36Sopenharmony_ci sd->sensor_type); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/* this function is called at probe and resume time */ 48062306a36Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci return 0; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int start_cif_cam(struct gspca_dev *gspca_dev) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 48862306a36Sopenharmony_ci __u8 *data = gspca_dev->usb_buf; 48962306a36Sopenharmony_ci int err_code; 49062306a36Sopenharmony_ci static const __u8 startup_string[] = { 49162306a36Sopenharmony_ci 0x00, 49262306a36Sopenharmony_ci 0x0d, 49362306a36Sopenharmony_ci 0x01, 49462306a36Sopenharmony_ci 0x00, /* Hsize/8 for 352 or 320 */ 49562306a36Sopenharmony_ci 0x00, /* Vsize/4 for 288 or 240 */ 49662306a36Sopenharmony_ci 0x13, /* or 0xbb, depends on sensor */ 49762306a36Sopenharmony_ci 0x00, /* Hstart, depends on res. */ 49862306a36Sopenharmony_ci 0x00, /* reserved ? */ 49962306a36Sopenharmony_ci 0x00, /* Vstart, depends on res. and sensor */ 50062306a36Sopenharmony_ci 0x50, /* 0x54 to get 176 or 160 */ 50162306a36Sopenharmony_ci 0xc0 50262306a36Sopenharmony_ci }; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* Note: Some of the above descriptions guessed from MR97113A driver */ 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci memcpy(data, startup_string, 11); 50762306a36Sopenharmony_ci if (sd->sensor_type) 50862306a36Sopenharmony_ci data[5] = 0xbb; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci switch (gspca_dev->pixfmt.width) { 51162306a36Sopenharmony_ci case 160: 51262306a36Sopenharmony_ci data[9] |= 0x04; /* reg 8, 2:1 scale down from 320 */ 51362306a36Sopenharmony_ci fallthrough; 51462306a36Sopenharmony_ci case 320: 51562306a36Sopenharmony_ci default: 51662306a36Sopenharmony_ci data[3] = 0x28; /* reg 2, H size/8 */ 51762306a36Sopenharmony_ci data[4] = 0x3c; /* reg 3, V size/4 */ 51862306a36Sopenharmony_ci data[6] = 0x14; /* reg 5, H start */ 51962306a36Sopenharmony_ci data[8] = 0x1a + sd->sensor_type; /* reg 7, V start */ 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci case 176: 52262306a36Sopenharmony_ci data[9] |= 0x04; /* reg 8, 2:1 scale down from 352 */ 52362306a36Sopenharmony_ci fallthrough; 52462306a36Sopenharmony_ci case 352: 52562306a36Sopenharmony_ci data[3] = 0x2c; /* reg 2, H size/8 */ 52662306a36Sopenharmony_ci data[4] = 0x48; /* reg 3, V size/4 */ 52762306a36Sopenharmony_ci data[6] = 0x06; /* reg 5, H start */ 52862306a36Sopenharmony_ci data[8] = 0x06 - sd->sensor_type; /* reg 7, V start */ 52962306a36Sopenharmony_ci break; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci err_code = mr_write(gspca_dev, 11); 53262306a36Sopenharmony_ci if (err_code < 0) 53362306a36Sopenharmony_ci return err_code; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (!sd->sensor_type) { 53662306a36Sopenharmony_ci static const struct sensor_w_data cif_sensor0_init_data[] = { 53762306a36Sopenharmony_ci {0x02, 0x00, {0x03, 0x5a, 0xb5, 0x01, 53862306a36Sopenharmony_ci 0x0f, 0x14, 0x0f, 0x10}, 8}, 53962306a36Sopenharmony_ci {0x0c, 0x00, {0x04, 0x01, 0x01, 0x00, 0x1f}, 5}, 54062306a36Sopenharmony_ci {0x12, 0x00, {0x07}, 1}, 54162306a36Sopenharmony_ci {0x1f, 0x00, {0x06}, 1}, 54262306a36Sopenharmony_ci {0x27, 0x00, {0x04}, 1}, 54362306a36Sopenharmony_ci {0x29, 0x00, {0x0c}, 1}, 54462306a36Sopenharmony_ci {0x40, 0x00, {0x40, 0x00, 0x04}, 3}, 54562306a36Sopenharmony_ci {0x50, 0x00, {0x60}, 1}, 54662306a36Sopenharmony_ci {0x60, 0x00, {0x06}, 1}, 54762306a36Sopenharmony_ci {0x6b, 0x00, {0x85, 0x85, 0xc8, 0xc8, 0xc8, 0xc8}, 6}, 54862306a36Sopenharmony_ci {0x72, 0x00, {0x1e, 0x56}, 2}, 54962306a36Sopenharmony_ci {0x75, 0x00, {0x58, 0x40, 0xa2, 0x02, 0x31, 0x02, 55062306a36Sopenharmony_ci 0x31, 0x80, 0x00}, 9}, 55162306a36Sopenharmony_ci {0x11, 0x00, {0x01}, 1}, 55262306a36Sopenharmony_ci {0, 0, {0}, 0} 55362306a36Sopenharmony_ci }; 55462306a36Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, cif_sensor0_init_data, 55562306a36Sopenharmony_ci ARRAY_SIZE(cif_sensor0_init_data)); 55662306a36Sopenharmony_ci } else { /* sd->sensor_type = 1 */ 55762306a36Sopenharmony_ci static const struct sensor_w_data cif_sensor1_init_data[] = { 55862306a36Sopenharmony_ci /* Reg 3,4, 7,8 get set by the controls */ 55962306a36Sopenharmony_ci {0x02, 0x00, {0x10}, 1}, 56062306a36Sopenharmony_ci {0x05, 0x01, {0x22}, 1}, /* 5/6 also seen as 65h/32h */ 56162306a36Sopenharmony_ci {0x06, 0x01, {0x00}, 1}, 56262306a36Sopenharmony_ci {0x09, 0x02, {0x0e}, 1}, 56362306a36Sopenharmony_ci {0x0a, 0x02, {0x05}, 1}, 56462306a36Sopenharmony_ci {0x0b, 0x02, {0x05}, 1}, 56562306a36Sopenharmony_ci {0x0c, 0x02, {0x0f}, 1}, 56662306a36Sopenharmony_ci {0x0d, 0x02, {0x07}, 1}, 56762306a36Sopenharmony_ci {0x0e, 0x02, {0x0c}, 1}, 56862306a36Sopenharmony_ci {0x0f, 0x00, {0x00}, 1}, 56962306a36Sopenharmony_ci {0x10, 0x00, {0x06}, 1}, 57062306a36Sopenharmony_ci {0x11, 0x00, {0x07}, 1}, 57162306a36Sopenharmony_ci {0x12, 0x00, {0x00}, 1}, 57262306a36Sopenharmony_ci {0x13, 0x00, {0x01}, 1}, 57362306a36Sopenharmony_ci {0, 0, {0}, 0} 57462306a36Sopenharmony_ci }; 57562306a36Sopenharmony_ci /* Without this command the cam won't work with USB-UHCI */ 57662306a36Sopenharmony_ci gspca_dev->usb_buf[0] = 0x0a; 57762306a36Sopenharmony_ci gspca_dev->usb_buf[1] = 0x00; 57862306a36Sopenharmony_ci err_code = mr_write(gspca_dev, 2); 57962306a36Sopenharmony_ci if (err_code < 0) 58062306a36Sopenharmony_ci return err_code; 58162306a36Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data, 58262306a36Sopenharmony_ci ARRAY_SIZE(cif_sensor1_init_data)); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci return err_code; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic int start_vga_cam(struct gspca_dev *gspca_dev) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 59062306a36Sopenharmony_ci __u8 *data = gspca_dev->usb_buf; 59162306a36Sopenharmony_ci int err_code; 59262306a36Sopenharmony_ci static const __u8 startup_string[] = 59362306a36Sopenharmony_ci {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00, 59462306a36Sopenharmony_ci 0x00, 0x50, 0xc0}; 59562306a36Sopenharmony_ci /* What some of these mean is explained in start_cif_cam(), above */ 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci memcpy(data, startup_string, 11); 59862306a36Sopenharmony_ci if (!sd->sensor_type) { 59962306a36Sopenharmony_ci data[5] = 0x00; 60062306a36Sopenharmony_ci data[10] = 0x91; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci if (sd->sensor_type == 2) { 60362306a36Sopenharmony_ci data[5] = 0x00; 60462306a36Sopenharmony_ci data[10] = 0x18; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci switch (gspca_dev->pixfmt.width) { 60862306a36Sopenharmony_ci case 160: 60962306a36Sopenharmony_ci data[9] |= 0x0c; /* reg 8, 4:1 scale down */ 61062306a36Sopenharmony_ci fallthrough; 61162306a36Sopenharmony_ci case 320: 61262306a36Sopenharmony_ci data[9] |= 0x04; /* reg 8, 2:1 scale down */ 61362306a36Sopenharmony_ci fallthrough; 61462306a36Sopenharmony_ci case 640: 61562306a36Sopenharmony_ci default: 61662306a36Sopenharmony_ci data[3] = 0x50; /* reg 2, H size/8 */ 61762306a36Sopenharmony_ci data[4] = 0x78; /* reg 3, V size/4 */ 61862306a36Sopenharmony_ci data[6] = 0x04; /* reg 5, H start */ 61962306a36Sopenharmony_ci data[8] = 0x03; /* reg 7, V start */ 62062306a36Sopenharmony_ci if (sd->sensor_type == 2) { 62162306a36Sopenharmony_ci data[6] = 2; 62262306a36Sopenharmony_ci data[8] = 1; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci if (sd->do_lcd_stop) 62562306a36Sopenharmony_ci data[8] = 0x04; /* Bayer tile shifted */ 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci case 176: 62962306a36Sopenharmony_ci data[9] |= 0x04; /* reg 8, 2:1 scale down */ 63062306a36Sopenharmony_ci fallthrough; 63162306a36Sopenharmony_ci case 352: 63262306a36Sopenharmony_ci data[3] = 0x2c; /* reg 2, H size */ 63362306a36Sopenharmony_ci data[4] = 0x48; /* reg 3, V size */ 63462306a36Sopenharmony_ci data[6] = 0x94; /* reg 5, H start */ 63562306a36Sopenharmony_ci data[8] = 0x63; /* reg 7, V start */ 63662306a36Sopenharmony_ci if (sd->do_lcd_stop) 63762306a36Sopenharmony_ci data[8] = 0x64; /* Bayer tile shifted */ 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci err_code = mr_write(gspca_dev, 11); 64262306a36Sopenharmony_ci if (err_code < 0) 64362306a36Sopenharmony_ci return err_code; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (!sd->sensor_type) { 64662306a36Sopenharmony_ci static const struct sensor_w_data vga_sensor0_init_data[] = { 64762306a36Sopenharmony_ci {0x01, 0x00, {0x0c, 0x00, 0x04}, 3}, 64862306a36Sopenharmony_ci {0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4}, 64962306a36Sopenharmony_ci {0x20, 0x00, {0x00, 0x80, 0x00, 0x08}, 4}, 65062306a36Sopenharmony_ci {0x25, 0x00, {0x03, 0xa9, 0x80}, 3}, 65162306a36Sopenharmony_ci {0x30, 0x00, {0x30, 0x18, 0x10, 0x18}, 4}, 65262306a36Sopenharmony_ci {0, 0, {0}, 0} 65362306a36Sopenharmony_ci }; 65462306a36Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data, 65562306a36Sopenharmony_ci ARRAY_SIZE(vga_sensor0_init_data)); 65662306a36Sopenharmony_ci } else if (sd->sensor_type == 1) { 65762306a36Sopenharmony_ci static const struct sensor_w_data color_adj[] = { 65862306a36Sopenharmony_ci {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, 65962306a36Sopenharmony_ci /* adjusted blue, green, red gain correct 66062306a36Sopenharmony_ci too much blue from the Sakar Digital */ 66162306a36Sopenharmony_ci 0x05, 0x01, 0x04}, 8} 66262306a36Sopenharmony_ci }; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci static const struct sensor_w_data color_no_adj[] = { 66562306a36Sopenharmony_ci {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, 66662306a36Sopenharmony_ci /* default blue, green, red gain settings */ 66762306a36Sopenharmony_ci 0x07, 0x00, 0x01}, 8} 66862306a36Sopenharmony_ci }; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci static const struct sensor_w_data vga_sensor1_init_data[] = { 67162306a36Sopenharmony_ci {0x11, 0x04, {0x01}, 1}, 67262306a36Sopenharmony_ci {0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, 67362306a36Sopenharmony_ci /* These settings may be better for some cameras */ 67462306a36Sopenharmony_ci /* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */ 67562306a36Sopenharmony_ci 0x00, 0x0a}, 7}, 67662306a36Sopenharmony_ci {0x11, 0x04, {0x01}, 1}, 67762306a36Sopenharmony_ci {0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6}, 67862306a36Sopenharmony_ci {0x11, 0x04, {0x01}, 1}, 67962306a36Sopenharmony_ci {0, 0, {0}, 0} 68062306a36Sopenharmony_ci }; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (sd->adj_colors) 68362306a36Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, color_adj, 68462306a36Sopenharmony_ci ARRAY_SIZE(color_adj)); 68562306a36Sopenharmony_ci else 68662306a36Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, color_no_adj, 68762306a36Sopenharmony_ci ARRAY_SIZE(color_no_adj)); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (err_code < 0) 69062306a36Sopenharmony_ci return err_code; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data, 69362306a36Sopenharmony_ci ARRAY_SIZE(vga_sensor1_init_data)); 69462306a36Sopenharmony_ci } else { /* sensor type == 2 */ 69562306a36Sopenharmony_ci static const struct sensor_w_data vga_sensor2_init_data[] = { 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci {0x01, 0x00, {0x48}, 1}, 69862306a36Sopenharmony_ci {0x02, 0x00, {0x22}, 1}, 69962306a36Sopenharmony_ci /* Reg 3 msb and 4 is lsb of the exposure setting*/ 70062306a36Sopenharmony_ci {0x05, 0x00, {0x10}, 1}, 70162306a36Sopenharmony_ci {0x06, 0x00, {0x00}, 1}, 70262306a36Sopenharmony_ci {0x07, 0x00, {0x00}, 1}, 70362306a36Sopenharmony_ci {0x08, 0x00, {0x00}, 1}, 70462306a36Sopenharmony_ci {0x09, 0x00, {0x00}, 1}, 70562306a36Sopenharmony_ci /* The following are used in the gain control 70662306a36Sopenharmony_ci * which is BTW completely borked in the OEM driver 70762306a36Sopenharmony_ci * The values for each color go from 0 to 0x7ff 70862306a36Sopenharmony_ci *{0x0a, 0x00, {0x01}, 1}, green1 gain msb 70962306a36Sopenharmony_ci *{0x0b, 0x00, {0x10}, 1}, green1 gain lsb 71062306a36Sopenharmony_ci *{0x0c, 0x00, {0x01}, 1}, red gain msb 71162306a36Sopenharmony_ci *{0x0d, 0x00, {0x10}, 1}, red gain lsb 71262306a36Sopenharmony_ci *{0x0e, 0x00, {0x01}, 1}, blue gain msb 71362306a36Sopenharmony_ci *{0x0f, 0x00, {0x10}, 1}, blue gain lsb 71462306a36Sopenharmony_ci *{0x10, 0x00, {0x01}, 1}, green2 gain msb 71562306a36Sopenharmony_ci *{0x11, 0x00, {0x10}, 1}, green2 gain lsb 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_ci {0x12, 0x00, {0x00}, 1}, 71862306a36Sopenharmony_ci {0x13, 0x00, {0x04}, 1}, /* weird effect on colors */ 71962306a36Sopenharmony_ci {0x14, 0x00, {0x00}, 1}, 72062306a36Sopenharmony_ci {0x15, 0x00, {0x06}, 1}, 72162306a36Sopenharmony_ci {0x16, 0x00, {0x01}, 1}, 72262306a36Sopenharmony_ci {0x17, 0x00, {0xe2}, 1}, /* vertical alignment */ 72362306a36Sopenharmony_ci {0x18, 0x00, {0x02}, 1}, 72462306a36Sopenharmony_ci {0x19, 0x00, {0x82}, 1}, /* don't mess with */ 72562306a36Sopenharmony_ci {0x1a, 0x00, {0x00}, 1}, 72662306a36Sopenharmony_ci {0x1b, 0x00, {0x20}, 1}, 72762306a36Sopenharmony_ci /* {0x1c, 0x00, {0x17}, 1}, contrast control */ 72862306a36Sopenharmony_ci {0x1d, 0x00, {0x80}, 1}, /* moving causes a mess */ 72962306a36Sopenharmony_ci {0x1e, 0x00, {0x08}, 1}, /* moving jams the camera */ 73062306a36Sopenharmony_ci {0x1f, 0x00, {0x0c}, 1}, 73162306a36Sopenharmony_ci {0x20, 0x00, {0x00}, 1}, 73262306a36Sopenharmony_ci {0, 0, {0}, 0} 73362306a36Sopenharmony_ci }; 73462306a36Sopenharmony_ci err_code = sensor_write_regs(gspca_dev, vga_sensor2_init_data, 73562306a36Sopenharmony_ci ARRAY_SIZE(vga_sensor2_init_data)); 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci return err_code; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 74362306a36Sopenharmony_ci int err_code; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci sd->sof_read = 0; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* Some of the VGA cameras require the memory pointer 74862306a36Sopenharmony_ci * to be set to 0 again. We have been forced to start the 74962306a36Sopenharmony_ci * stream in sd_config() to detect the hardware, and closed it. 75062306a36Sopenharmony_ci * Thus, we need here to do a completely fresh and clean start. */ 75162306a36Sopenharmony_ci err_code = zero_the_pointer(gspca_dev); 75262306a36Sopenharmony_ci if (err_code < 0) 75362306a36Sopenharmony_ci return err_code; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci err_code = stream_start(gspca_dev); 75662306a36Sopenharmony_ci if (err_code < 0) 75762306a36Sopenharmony_ci return err_code; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (sd->cam_type == CAM_TYPE_CIF) { 76062306a36Sopenharmony_ci err_code = start_cif_cam(gspca_dev); 76162306a36Sopenharmony_ci } else { 76262306a36Sopenharmony_ci err_code = start_vga_cam(gspca_dev); 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci if (err_code < 0) 76562306a36Sopenharmony_ci return err_code; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci return isoc_enable(gspca_dev); 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci stream_stop(gspca_dev); 77562306a36Sopenharmony_ci /* Not all the cams need this, but even if not, probably a good idea */ 77662306a36Sopenharmony_ci zero_the_pointer(gspca_dev); 77762306a36Sopenharmony_ci if (sd->do_lcd_stop) 77862306a36Sopenharmony_ci lcd_stop(gspca_dev); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic void setbrightness(struct gspca_dev *gspca_dev, s32 val) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 78462306a36Sopenharmony_ci u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */ 78562306a36Sopenharmony_ci u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */ 78662306a36Sopenharmony_ci static const u8 quick_clix_table[] = 78762306a36Sopenharmony_ci /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ 78862306a36Sopenharmony_ci { 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15}; 78962306a36Sopenharmony_ci if (sd->cam_type == CAM_TYPE_VGA) { 79062306a36Sopenharmony_ci sign_reg += 4; 79162306a36Sopenharmony_ci value_reg += 4; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* Note register 7 is also seen as 0x8x or 0xCx in some dumps */ 79562306a36Sopenharmony_ci if (val > 0) { 79662306a36Sopenharmony_ci sensor_write1(gspca_dev, sign_reg, 0x00); 79762306a36Sopenharmony_ci } else { 79862306a36Sopenharmony_ci sensor_write1(gspca_dev, sign_reg, 0x01); 79962306a36Sopenharmony_ci val = 257 - val; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci /* Use lookup table for funky Argus QuickClix brightness */ 80262306a36Sopenharmony_ci if (sd->do_lcd_stop) 80362306a36Sopenharmony_ci val = quick_clix_table[val]; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci sensor_write1(gspca_dev, value_reg, val); 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 81162306a36Sopenharmony_ci int exposure = MR97310A_EXPOSURE_DEFAULT; 81262306a36Sopenharmony_ci u8 buf[2]; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) { 81562306a36Sopenharmony_ci /* This cam does not like exposure settings < 300, 81662306a36Sopenharmony_ci so scale 0 - 4095 to 300 - 4095 */ 81762306a36Sopenharmony_ci exposure = (expo * 9267) / 10000 + 300; 81862306a36Sopenharmony_ci sensor_write1(gspca_dev, 3, exposure >> 4); 81962306a36Sopenharmony_ci sensor_write1(gspca_dev, 4, exposure & 0x0f); 82062306a36Sopenharmony_ci } else if (sd->sensor_type == 2) { 82162306a36Sopenharmony_ci exposure = expo; 82262306a36Sopenharmony_ci exposure >>= 3; 82362306a36Sopenharmony_ci sensor_write1(gspca_dev, 3, exposure >> 8); 82462306a36Sopenharmony_ci sensor_write1(gspca_dev, 4, exposure & 0xff); 82562306a36Sopenharmony_ci } else { 82662306a36Sopenharmony_ci /* We have both a clock divider and an exposure register. 82762306a36Sopenharmony_ci We first calculate the clock divider, as that determines 82862306a36Sopenharmony_ci the maximum exposure and then we calculate the exposure 82962306a36Sopenharmony_ci register setting (which goes from 0 - 511). 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci Note our 0 - 4095 exposure is mapped to 0 - 511 83262306a36Sopenharmony_ci milliseconds exposure time */ 83362306a36Sopenharmony_ci u8 clockdiv = (60 * expo + 7999) / 8000; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* Limit framerate to not exceed usb bandwidth */ 83662306a36Sopenharmony_ci if (clockdiv < min_clockdiv && gspca_dev->pixfmt.width >= 320) 83762306a36Sopenharmony_ci clockdiv = min_clockdiv; 83862306a36Sopenharmony_ci else if (clockdiv < 2) 83962306a36Sopenharmony_ci clockdiv = 2; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4) 84262306a36Sopenharmony_ci clockdiv = 4; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* Frame exposure time in ms = 1000 * clockdiv / 60 -> 84562306a36Sopenharmony_ci exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */ 84662306a36Sopenharmony_ci exposure = (60 * 511 * expo) / (8000 * clockdiv); 84762306a36Sopenharmony_ci if (exposure > 511) 84862306a36Sopenharmony_ci exposure = 511; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* exposure register value is reversed! */ 85162306a36Sopenharmony_ci exposure = 511 - exposure; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci buf[0] = exposure & 0xff; 85462306a36Sopenharmony_ci buf[1] = exposure >> 8; 85562306a36Sopenharmony_ci sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2); 85662306a36Sopenharmony_ci sensor_write1(gspca_dev, 0x02, clockdiv); 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic void setgain(struct gspca_dev *gspca_dev, s32 val) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 86362306a36Sopenharmony_ci u8 gainreg; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) 86662306a36Sopenharmony_ci sensor_write1(gspca_dev, 0x0e, val); 86762306a36Sopenharmony_ci else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2) 86862306a36Sopenharmony_ci for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) { 86962306a36Sopenharmony_ci sensor_write1(gspca_dev, gainreg, val >> 8); 87062306a36Sopenharmony_ci sensor_write1(gspca_dev, gainreg + 1, val & 0xff); 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci else 87362306a36Sopenharmony_ci sensor_write1(gspca_dev, 0x10, val); 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic void setcontrast(struct gspca_dev *gspca_dev, s32 val) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci sensor_write1(gspca_dev, 0x1c, val); 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct gspca_dev *gspca_dev = 88462306a36Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 88562306a36Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci gspca_dev->usb_err = 0; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (!gspca_dev->streaming) 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci switch (ctrl->id) { 89362306a36Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 89462306a36Sopenharmony_ci setbrightness(gspca_dev, ctrl->val); 89562306a36Sopenharmony_ci break; 89662306a36Sopenharmony_ci case V4L2_CID_CONTRAST: 89762306a36Sopenharmony_ci setcontrast(gspca_dev, ctrl->val); 89862306a36Sopenharmony_ci break; 89962306a36Sopenharmony_ci case V4L2_CID_EXPOSURE: 90062306a36Sopenharmony_ci setexposure(gspca_dev, sd->exposure->val, 90162306a36Sopenharmony_ci sd->min_clockdiv ? sd->min_clockdiv->val : 0); 90262306a36Sopenharmony_ci break; 90362306a36Sopenharmony_ci case V4L2_CID_GAIN: 90462306a36Sopenharmony_ci setgain(gspca_dev, ctrl->val); 90562306a36Sopenharmony_ci break; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci return gspca_dev->usb_err; 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = { 91162306a36Sopenharmony_ci .s_ctrl = sd_s_ctrl, 91262306a36Sopenharmony_ci}; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 91762306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 91862306a36Sopenharmony_ci static const struct v4l2_ctrl_config clockdiv = { 91962306a36Sopenharmony_ci .ops = &sd_ctrl_ops, 92062306a36Sopenharmony_ci .id = MR97310A_CID_CLOCKDIV, 92162306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 92262306a36Sopenharmony_ci .name = "Minimum Clock Divider", 92362306a36Sopenharmony_ci .min = MR97310A_MIN_CLOCKDIV_MIN, 92462306a36Sopenharmony_ci .max = MR97310A_MIN_CLOCKDIV_MAX, 92562306a36Sopenharmony_ci .step = 1, 92662306a36Sopenharmony_ci .def = MR97310A_MIN_CLOCKDIV_DEFAULT, 92762306a36Sopenharmony_ci }; 92862306a36Sopenharmony_ci bool has_brightness = false; 92962306a36Sopenharmony_ci bool has_argus_brightness = false; 93062306a36Sopenharmony_ci bool has_contrast = false; 93162306a36Sopenharmony_ci bool has_gain = false; 93262306a36Sopenharmony_ci bool has_cs_gain = false; 93362306a36Sopenharmony_ci bool has_exposure = false; 93462306a36Sopenharmony_ci bool has_clockdiv = false; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci gspca_dev->vdev.ctrl_handler = hdl; 93762306a36Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 4); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* Setup controls depending on camera type */ 94062306a36Sopenharmony_ci if (sd->cam_type == CAM_TYPE_CIF) { 94162306a36Sopenharmony_ci /* No brightness for sensor_type 0 */ 94262306a36Sopenharmony_ci if (sd->sensor_type == 0) 94362306a36Sopenharmony_ci has_exposure = has_gain = has_clockdiv = true; 94462306a36Sopenharmony_ci else 94562306a36Sopenharmony_ci has_exposure = has_gain = has_brightness = true; 94662306a36Sopenharmony_ci } else { 94762306a36Sopenharmony_ci /* All controls need to be disabled if VGA sensor_type is 0 */ 94862306a36Sopenharmony_ci if (sd->sensor_type == 0) 94962306a36Sopenharmony_ci ; /* no controls! */ 95062306a36Sopenharmony_ci else if (sd->sensor_type == 2) 95162306a36Sopenharmony_ci has_exposure = has_cs_gain = has_contrast = true; 95262306a36Sopenharmony_ci else if (sd->do_lcd_stop) 95362306a36Sopenharmony_ci has_exposure = has_gain = has_argus_brightness = 95462306a36Sopenharmony_ci has_clockdiv = true; 95562306a36Sopenharmony_ci else 95662306a36Sopenharmony_ci has_exposure = has_gain = has_brightness = 95762306a36Sopenharmony_ci has_clockdiv = true; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci /* Separate brightness control description for Argus QuickClix as it has 96162306a36Sopenharmony_ci * different limits from the other mr97310a cameras, and separate gain 96262306a36Sopenharmony_ci * control for Sakar CyberPix camera. */ 96362306a36Sopenharmony_ci /* 96462306a36Sopenharmony_ci * This control is disabled for CIF type 1 and VGA type 0 cameras. 96562306a36Sopenharmony_ci * It does not quite act linearly for the Argus QuickClix camera, 96662306a36Sopenharmony_ci * but it does control brightness. The values are 0 - 15 only, and 96762306a36Sopenharmony_ci * the table above makes them act consecutively. 96862306a36Sopenharmony_ci */ 96962306a36Sopenharmony_ci if (has_brightness) 97062306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 97162306a36Sopenharmony_ci V4L2_CID_BRIGHTNESS, -254, 255, 1, 97262306a36Sopenharmony_ci MR97310A_BRIGHTNESS_DEFAULT); 97362306a36Sopenharmony_ci else if (has_argus_brightness) 97462306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 97562306a36Sopenharmony_ci V4L2_CID_BRIGHTNESS, 0, 15, 1, 97662306a36Sopenharmony_ci MR97310A_BRIGHTNESS_DEFAULT); 97762306a36Sopenharmony_ci if (has_contrast) 97862306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 97962306a36Sopenharmony_ci V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN, 98062306a36Sopenharmony_ci MR97310A_CONTRAST_MAX, 1, MR97310A_CONTRAST_DEFAULT); 98162306a36Sopenharmony_ci if (has_gain) 98262306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 98362306a36Sopenharmony_ci V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX, 98462306a36Sopenharmony_ci 1, MR97310A_GAIN_DEFAULT); 98562306a36Sopenharmony_ci else if (has_cs_gain) 98662306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN, 98762306a36Sopenharmony_ci MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX, 98862306a36Sopenharmony_ci 1, MR97310A_CS_GAIN_DEFAULT); 98962306a36Sopenharmony_ci if (has_exposure) 99062306a36Sopenharmony_ci sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 99162306a36Sopenharmony_ci V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN, 99262306a36Sopenharmony_ci MR97310A_EXPOSURE_MAX, 1, MR97310A_EXPOSURE_DEFAULT); 99362306a36Sopenharmony_ci if (has_clockdiv) 99462306a36Sopenharmony_ci sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (hdl->error) { 99762306a36Sopenharmony_ci pr_err("Could not initialize controls\n"); 99862306a36Sopenharmony_ci return hdl->error; 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci if (has_exposure && has_clockdiv) 100162306a36Sopenharmony_ci v4l2_ctrl_cluster(2, &sd->exposure); 100262306a36Sopenharmony_ci return 0; 100362306a36Sopenharmony_ci} 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci/* Include pac common sof detection functions */ 100662306a36Sopenharmony_ci#include "pac_common.h" 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, 100962306a36Sopenharmony_ci u8 *data, /* isoc packet */ 101062306a36Sopenharmony_ci int len) /* iso packet length */ 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 101362306a36Sopenharmony_ci unsigned char *sof; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len); 101662306a36Sopenharmony_ci if (sof) { 101762306a36Sopenharmony_ci int n; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci /* finish decoding current frame */ 102062306a36Sopenharmony_ci n = sof - data; 102162306a36Sopenharmony_ci if (n > sizeof pac_sof_marker) 102262306a36Sopenharmony_ci n -= sizeof pac_sof_marker; 102362306a36Sopenharmony_ci else 102462306a36Sopenharmony_ci n = 0; 102562306a36Sopenharmony_ci gspca_frame_add(gspca_dev, LAST_PACKET, 102662306a36Sopenharmony_ci data, n); 102762306a36Sopenharmony_ci /* Start next frame. */ 102862306a36Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, 102962306a36Sopenharmony_ci pac_sof_marker, sizeof pac_sof_marker); 103062306a36Sopenharmony_ci len -= sof - data; 103162306a36Sopenharmony_ci data = sof; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci gspca_frame_add(gspca_dev, INTER_PACKET, data, len); 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci/* sub-driver description */ 103762306a36Sopenharmony_cistatic const struct sd_desc sd_desc = { 103862306a36Sopenharmony_ci .name = MODULE_NAME, 103962306a36Sopenharmony_ci .config = sd_config, 104062306a36Sopenharmony_ci .init = sd_init, 104162306a36Sopenharmony_ci .init_controls = sd_init_controls, 104262306a36Sopenharmony_ci .start = sd_start, 104362306a36Sopenharmony_ci .stopN = sd_stopN, 104462306a36Sopenharmony_ci .pkt_scan = sd_pkt_scan, 104562306a36Sopenharmony_ci}; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci/* -- module initialisation -- */ 104862306a36Sopenharmony_cistatic const struct usb_device_id device_table[] = { 104962306a36Sopenharmony_ci {USB_DEVICE(0x08ca, 0x0110)}, /* Trust Spyc@m 100 */ 105062306a36Sopenharmony_ci {USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */ 105162306a36Sopenharmony_ci {USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */ 105262306a36Sopenharmony_ci {USB_DEVICE(0x093a, 0x010e)}, /* All known MR97310A CIF cams */ 105362306a36Sopenharmony_ci {} 105462306a36Sopenharmony_ci}; 105562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci/* -- device connect -- */ 105862306a36Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, 105962306a36Sopenharmony_ci const struct usb_device_id *id) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), 106262306a36Sopenharmony_ci THIS_MODULE); 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_cistatic struct usb_driver sd_driver = { 106662306a36Sopenharmony_ci .name = MODULE_NAME, 106762306a36Sopenharmony_ci .id_table = device_table, 106862306a36Sopenharmony_ci .probe = sd_probe, 106962306a36Sopenharmony_ci .disconnect = gspca_disconnect, 107062306a36Sopenharmony_ci#ifdef CONFIG_PM 107162306a36Sopenharmony_ci .suspend = gspca_suspend, 107262306a36Sopenharmony_ci .resume = gspca_resume, 107362306a36Sopenharmony_ci .reset_resume = gspca_resume, 107462306a36Sopenharmony_ci#endif 107562306a36Sopenharmony_ci}; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cimodule_usb_driver(sd_driver); 1078