162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip 362306a36Sopenharmony_ci * Subdriver core 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * 2009/09/24 Olivier Lorin <o.lorin@laposte.net> 662306a36Sopenharmony_ci * GSPCA by Jean-Francois Moine <http://moinejf.free.fr> 762306a36Sopenharmony_ci * Thanks BUGabundo and Malmostoso for your amazing help! 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "gspca.h" 1362306a36Sopenharmony_ci#include "gl860.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciMODULE_AUTHOR("Olivier Lorin <o.lorin@laposte.net>"); 1662306a36Sopenharmony_ciMODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver"); 1762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/*======================== static function declarations ====================*/ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic void (*dev_init_settings)(struct gspca_dev *gspca_dev); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 2462306a36Sopenharmony_ci const struct usb_device_id *id); 2562306a36Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev); 2662306a36Sopenharmony_cistatic int sd_isoc_init(struct gspca_dev *gspca_dev); 2762306a36Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev); 2862306a36Sopenharmony_cistatic void sd_stop0(struct gspca_dev *gspca_dev); 2962306a36Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, 3062306a36Sopenharmony_ci u8 *data, int len); 3162306a36Sopenharmony_cistatic void sd_callback(struct gspca_dev *gspca_dev); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int gl860_guess_sensor(struct gspca_dev *gspca_dev, 3462306a36Sopenharmony_ci u16 vendor_id, u16 product_id); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/*============================ driver options ==============================*/ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic s32 AC50Hz = 0xff; 3962306a36Sopenharmony_cimodule_param(AC50Hz, int, 0644); 4062306a36Sopenharmony_ciMODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)"); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic char sensor[7]; 4362306a36Sopenharmony_cimodule_param_string(sensor, sensor, sizeof(sensor), 0644); 4462306a36Sopenharmony_ciMODULE_PARM_DESC(sensor, 4562306a36Sopenharmony_ci " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')"); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/*============================ webcam controls =============================*/ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct gspca_dev *gspca_dev = 5262306a36Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 5362306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci switch (ctrl->id) { 5662306a36Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 5762306a36Sopenharmony_ci sd->vcur.brightness = ctrl->val; 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci case V4L2_CID_CONTRAST: 6062306a36Sopenharmony_ci sd->vcur.contrast = ctrl->val; 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci case V4L2_CID_SATURATION: 6362306a36Sopenharmony_ci sd->vcur.saturation = ctrl->val; 6462306a36Sopenharmony_ci break; 6562306a36Sopenharmony_ci case V4L2_CID_HUE: 6662306a36Sopenharmony_ci sd->vcur.hue = ctrl->val; 6762306a36Sopenharmony_ci break; 6862306a36Sopenharmony_ci case V4L2_CID_GAMMA: 6962306a36Sopenharmony_ci sd->vcur.gamma = ctrl->val; 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci case V4L2_CID_HFLIP: 7262306a36Sopenharmony_ci sd->vcur.mirror = ctrl->val; 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci case V4L2_CID_VFLIP: 7562306a36Sopenharmony_ci sd->vcur.flip = ctrl->val; 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci case V4L2_CID_POWER_LINE_FREQUENCY: 7862306a36Sopenharmony_ci sd->vcur.AC50Hz = ctrl->val; 7962306a36Sopenharmony_ci break; 8062306a36Sopenharmony_ci case V4L2_CID_WHITE_BALANCE_TEMPERATURE: 8162306a36Sopenharmony_ci sd->vcur.whitebal = ctrl->val; 8262306a36Sopenharmony_ci break; 8362306a36Sopenharmony_ci case V4L2_CID_SHARPNESS: 8462306a36Sopenharmony_ci sd->vcur.sharpness = ctrl->val; 8562306a36Sopenharmony_ci break; 8662306a36Sopenharmony_ci case V4L2_CID_BACKLIGHT_COMPENSATION: 8762306a36Sopenharmony_ci sd->vcur.backlight = ctrl->val; 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci default: 9062306a36Sopenharmony_ci return -EINVAL; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (gspca_dev->streaming) 9462306a36Sopenharmony_ci sd->waitSet = 1; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = { 10062306a36Sopenharmony_ci .s_ctrl = sd_s_ctrl, 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 10662306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci gspca_dev->vdev.ctrl_handler = hdl; 10962306a36Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 11); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (sd->vmax.brightness) 11262306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_BRIGHTNESS, 11362306a36Sopenharmony_ci 0, sd->vmax.brightness, 1, 11462306a36Sopenharmony_ci sd->vcur.brightness); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (sd->vmax.contrast) 11762306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_CONTRAST, 11862306a36Sopenharmony_ci 0, sd->vmax.contrast, 1, 11962306a36Sopenharmony_ci sd->vcur.contrast); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (sd->vmax.saturation) 12262306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_SATURATION, 12362306a36Sopenharmony_ci 0, sd->vmax.saturation, 1, 12462306a36Sopenharmony_ci sd->vcur.saturation); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (sd->vmax.hue) 12762306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_HUE, 12862306a36Sopenharmony_ci 0, sd->vmax.hue, 1, sd->vcur.hue); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (sd->vmax.gamma) 13162306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAMMA, 13262306a36Sopenharmony_ci 0, sd->vmax.gamma, 1, sd->vcur.gamma); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (sd->vmax.mirror) 13562306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_HFLIP, 13662306a36Sopenharmony_ci 0, sd->vmax.mirror, 1, sd->vcur.mirror); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (sd->vmax.flip) 13962306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_VFLIP, 14062306a36Sopenharmony_ci 0, sd->vmax.flip, 1, sd->vcur.flip); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (sd->vmax.AC50Hz) 14362306a36Sopenharmony_ci v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, 14462306a36Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY, 14562306a36Sopenharmony_ci sd->vmax.AC50Hz, 0, sd->vcur.AC50Hz); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (sd->vmax.whitebal) 14862306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 14962306a36Sopenharmony_ci V4L2_CID_WHITE_BALANCE_TEMPERATURE, 15062306a36Sopenharmony_ci 0, sd->vmax.whitebal, 1, sd->vcur.whitebal); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (sd->vmax.sharpness) 15362306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_SHARPNESS, 15462306a36Sopenharmony_ci 0, sd->vmax.sharpness, 1, 15562306a36Sopenharmony_ci sd->vcur.sharpness); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (sd->vmax.backlight) 15862306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 15962306a36Sopenharmony_ci V4L2_CID_BACKLIGHT_COMPENSATION, 16062306a36Sopenharmony_ci 0, sd->vmax.backlight, 1, 16162306a36Sopenharmony_ci sd->vcur.backlight); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (hdl->error) { 16462306a36Sopenharmony_ci pr_err("Could not initialize controls\n"); 16562306a36Sopenharmony_ci return hdl->error; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/*==================== sud-driver structure initialisation =================*/ 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic const struct sd_desc sd_desc_mi1320 = { 17462306a36Sopenharmony_ci .name = MODULE_NAME, 17562306a36Sopenharmony_ci .config = sd_config, 17662306a36Sopenharmony_ci .init = sd_init, 17762306a36Sopenharmony_ci .init_controls = sd_init_controls, 17862306a36Sopenharmony_ci .isoc_init = sd_isoc_init, 17962306a36Sopenharmony_ci .start = sd_start, 18062306a36Sopenharmony_ci .stop0 = sd_stop0, 18162306a36Sopenharmony_ci .pkt_scan = sd_pkt_scan, 18262306a36Sopenharmony_ci .dq_callback = sd_callback, 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic const struct sd_desc sd_desc_mi2020 = { 18662306a36Sopenharmony_ci .name = MODULE_NAME, 18762306a36Sopenharmony_ci .config = sd_config, 18862306a36Sopenharmony_ci .init = sd_init, 18962306a36Sopenharmony_ci .init_controls = sd_init_controls, 19062306a36Sopenharmony_ci .isoc_init = sd_isoc_init, 19162306a36Sopenharmony_ci .start = sd_start, 19262306a36Sopenharmony_ci .stop0 = sd_stop0, 19362306a36Sopenharmony_ci .pkt_scan = sd_pkt_scan, 19462306a36Sopenharmony_ci .dq_callback = sd_callback, 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic const struct sd_desc sd_desc_ov2640 = { 19862306a36Sopenharmony_ci .name = MODULE_NAME, 19962306a36Sopenharmony_ci .config = sd_config, 20062306a36Sopenharmony_ci .init = sd_init, 20162306a36Sopenharmony_ci .init_controls = sd_init_controls, 20262306a36Sopenharmony_ci .isoc_init = sd_isoc_init, 20362306a36Sopenharmony_ci .start = sd_start, 20462306a36Sopenharmony_ci .stop0 = sd_stop0, 20562306a36Sopenharmony_ci .pkt_scan = sd_pkt_scan, 20662306a36Sopenharmony_ci .dq_callback = sd_callback, 20762306a36Sopenharmony_ci}; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic const struct sd_desc sd_desc_ov9655 = { 21062306a36Sopenharmony_ci .name = MODULE_NAME, 21162306a36Sopenharmony_ci .config = sd_config, 21262306a36Sopenharmony_ci .init = sd_init, 21362306a36Sopenharmony_ci .init_controls = sd_init_controls, 21462306a36Sopenharmony_ci .isoc_init = sd_isoc_init, 21562306a36Sopenharmony_ci .start = sd_start, 21662306a36Sopenharmony_ci .stop0 = sd_stop0, 21762306a36Sopenharmony_ci .pkt_scan = sd_pkt_scan, 21862306a36Sopenharmony_ci .dq_callback = sd_callback, 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/*=========================== sub-driver image sizes =======================*/ 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic struct v4l2_pix_format mi2020_mode[] = { 22462306a36Sopenharmony_ci { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 22562306a36Sopenharmony_ci .bytesperline = 640, 22662306a36Sopenharmony_ci .sizeimage = 640 * 480, 22762306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 22862306a36Sopenharmony_ci .priv = 0 22962306a36Sopenharmony_ci }, 23062306a36Sopenharmony_ci { 800, 598, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 23162306a36Sopenharmony_ci .bytesperline = 800, 23262306a36Sopenharmony_ci .sizeimage = 800 * 598, 23362306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 23462306a36Sopenharmony_ci .priv = 1 23562306a36Sopenharmony_ci }, 23662306a36Sopenharmony_ci {1280, 1024, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 23762306a36Sopenharmony_ci .bytesperline = 1280, 23862306a36Sopenharmony_ci .sizeimage = 1280 * 1024, 23962306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 24062306a36Sopenharmony_ci .priv = 2 24162306a36Sopenharmony_ci }, 24262306a36Sopenharmony_ci {1600, 1198, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 24362306a36Sopenharmony_ci .bytesperline = 1600, 24462306a36Sopenharmony_ci .sizeimage = 1600 * 1198, 24562306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 24662306a36Sopenharmony_ci .priv = 3 24762306a36Sopenharmony_ci }, 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic struct v4l2_pix_format ov2640_mode[] = { 25162306a36Sopenharmony_ci { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 25262306a36Sopenharmony_ci .bytesperline = 640, 25362306a36Sopenharmony_ci .sizeimage = 640 * 480, 25462306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 25562306a36Sopenharmony_ci .priv = 0 25662306a36Sopenharmony_ci }, 25762306a36Sopenharmony_ci { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 25862306a36Sopenharmony_ci .bytesperline = 800, 25962306a36Sopenharmony_ci .sizeimage = 800 * 600, 26062306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 26162306a36Sopenharmony_ci .priv = 1 26262306a36Sopenharmony_ci }, 26362306a36Sopenharmony_ci {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 26462306a36Sopenharmony_ci .bytesperline = 1280, 26562306a36Sopenharmony_ci .sizeimage = 1280 * 960, 26662306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 26762306a36Sopenharmony_ci .priv = 2 26862306a36Sopenharmony_ci }, 26962306a36Sopenharmony_ci {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 27062306a36Sopenharmony_ci .bytesperline = 1600, 27162306a36Sopenharmony_ci .sizeimage = 1600 * 1200, 27262306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 27362306a36Sopenharmony_ci .priv = 3 27462306a36Sopenharmony_ci }, 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic struct v4l2_pix_format mi1320_mode[] = { 27862306a36Sopenharmony_ci { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 27962306a36Sopenharmony_ci .bytesperline = 640, 28062306a36Sopenharmony_ci .sizeimage = 640 * 480, 28162306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 28262306a36Sopenharmony_ci .priv = 0 28362306a36Sopenharmony_ci }, 28462306a36Sopenharmony_ci { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 28562306a36Sopenharmony_ci .bytesperline = 800, 28662306a36Sopenharmony_ci .sizeimage = 800 * 600, 28762306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 28862306a36Sopenharmony_ci .priv = 1 28962306a36Sopenharmony_ci }, 29062306a36Sopenharmony_ci {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 29162306a36Sopenharmony_ci .bytesperline = 1280, 29262306a36Sopenharmony_ci .sizeimage = 1280 * 960, 29362306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 29462306a36Sopenharmony_ci .priv = 2 29562306a36Sopenharmony_ci }, 29662306a36Sopenharmony_ci}; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic struct v4l2_pix_format ov9655_mode[] = { 29962306a36Sopenharmony_ci { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 30062306a36Sopenharmony_ci .bytesperline = 640, 30162306a36Sopenharmony_ci .sizeimage = 640 * 480, 30262306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 30362306a36Sopenharmony_ci .priv = 0 30462306a36Sopenharmony_ci }, 30562306a36Sopenharmony_ci {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, 30662306a36Sopenharmony_ci .bytesperline = 1280, 30762306a36Sopenharmony_ci .sizeimage = 1280 * 960, 30862306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 30962306a36Sopenharmony_ci .priv = 1 31062306a36Sopenharmony_ci }, 31162306a36Sopenharmony_ci}; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci/*========================= sud-driver functions ===========================*/ 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/* This function is called at probe time */ 31662306a36Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 31762306a36Sopenharmony_ci const struct usb_device_id *id) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 32062306a36Sopenharmony_ci struct cam *cam; 32162306a36Sopenharmony_ci u16 vendor_id, product_id; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Get USB VendorID and ProductID */ 32462306a36Sopenharmony_ci vendor_id = id->idVendor; 32562306a36Sopenharmony_ci product_id = id->idProduct; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci sd->nbRightUp = 1; 32862306a36Sopenharmony_ci sd->nbIm = -1; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci sd->sensor = 0xff; 33162306a36Sopenharmony_ci if (strcmp(sensor, "MI1320") == 0) 33262306a36Sopenharmony_ci sd->sensor = ID_MI1320; 33362306a36Sopenharmony_ci else if (strcmp(sensor, "OV2640") == 0) 33462306a36Sopenharmony_ci sd->sensor = ID_OV2640; 33562306a36Sopenharmony_ci else if (strcmp(sensor, "OV9655") == 0) 33662306a36Sopenharmony_ci sd->sensor = ID_OV9655; 33762306a36Sopenharmony_ci else if (strcmp(sensor, "MI2020") == 0) 33862306a36Sopenharmony_ci sd->sensor = ID_MI2020; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Get sensor and set the suitable init/start/../stop functions */ 34162306a36Sopenharmony_ci if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1) 34262306a36Sopenharmony_ci return -1; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci cam = &gspca_dev->cam; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci switch (sd->sensor) { 34762306a36Sopenharmony_ci case ID_MI1320: 34862306a36Sopenharmony_ci gspca_dev->sd_desc = &sd_desc_mi1320; 34962306a36Sopenharmony_ci cam->cam_mode = mi1320_mode; 35062306a36Sopenharmony_ci cam->nmodes = ARRAY_SIZE(mi1320_mode); 35162306a36Sopenharmony_ci dev_init_settings = mi1320_init_settings; 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci case ID_MI2020: 35562306a36Sopenharmony_ci gspca_dev->sd_desc = &sd_desc_mi2020; 35662306a36Sopenharmony_ci cam->cam_mode = mi2020_mode; 35762306a36Sopenharmony_ci cam->nmodes = ARRAY_SIZE(mi2020_mode); 35862306a36Sopenharmony_ci dev_init_settings = mi2020_init_settings; 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci case ID_OV2640: 36262306a36Sopenharmony_ci gspca_dev->sd_desc = &sd_desc_ov2640; 36362306a36Sopenharmony_ci cam->cam_mode = ov2640_mode; 36462306a36Sopenharmony_ci cam->nmodes = ARRAY_SIZE(ov2640_mode); 36562306a36Sopenharmony_ci dev_init_settings = ov2640_init_settings; 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci case ID_OV9655: 36962306a36Sopenharmony_ci gspca_dev->sd_desc = &sd_desc_ov9655; 37062306a36Sopenharmony_ci cam->cam_mode = ov9655_mode; 37162306a36Sopenharmony_ci cam->nmodes = ARRAY_SIZE(ov9655_mode); 37262306a36Sopenharmony_ci dev_init_settings = ov9655_init_settings; 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci dev_init_settings(gspca_dev); 37762306a36Sopenharmony_ci if (AC50Hz != 0xff) 37862306a36Sopenharmony_ci ((struct sd *) gspca_dev)->vcur.AC50Hz = AC50Hz; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* This function is called at probe time after sd_config */ 38462306a36Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return sd->dev_init_at_startup(gspca_dev); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* This function is called before to choose the alt setting */ 39262306a36Sopenharmony_cistatic int sd_isoc_init(struct gspca_dev *gspca_dev) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return sd->dev_configure_alt(gspca_dev); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/* This function is called to start the webcam */ 40062306a36Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return sd->dev_init_pre_alt(gspca_dev); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* This function is called to stop the webcam */ 40862306a36Sopenharmony_cistatic void sd_stop0(struct gspca_dev *gspca_dev) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (!sd->gspca_dev.present) 41362306a36Sopenharmony_ci return; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return sd->dev_post_unset_alt(gspca_dev); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/* This function is called when an image is being received */ 41962306a36Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, 42062306a36Sopenharmony_ci u8 *data, int len) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 42362306a36Sopenharmony_ci static s32 nSkipped; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci s32 mode = (s32) gspca_dev->curr_mode; 42662306a36Sopenharmony_ci s32 nToSkip = 42762306a36Sopenharmony_ci sd->swapRB * (gspca_dev->cam.cam_mode[mode].bytesperline + 1); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* Test only against 0202h, so endianness does not matter */ 43062306a36Sopenharmony_ci switch (*(s16 *) data) { 43162306a36Sopenharmony_ci case 0x0202: /* End of frame, start a new one */ 43262306a36Sopenharmony_ci gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); 43362306a36Sopenharmony_ci nSkipped = 0; 43462306a36Sopenharmony_ci if (sd->nbIm >= 0 && sd->nbIm < 10) 43562306a36Sopenharmony_ci sd->nbIm++; 43662306a36Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci default: 44062306a36Sopenharmony_ci data += 2; 44162306a36Sopenharmony_ci len -= 2; 44262306a36Sopenharmony_ci if (nSkipped + len <= nToSkip) 44362306a36Sopenharmony_ci nSkipped += len; 44462306a36Sopenharmony_ci else { 44562306a36Sopenharmony_ci if (nSkipped < nToSkip && nSkipped + len > nToSkip) { 44662306a36Sopenharmony_ci data += nToSkip - nSkipped; 44762306a36Sopenharmony_ci len -= nToSkip - nSkipped; 44862306a36Sopenharmony_ci nSkipped = nToSkip + 1; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci gspca_frame_add(gspca_dev, 45162306a36Sopenharmony_ci INTER_PACKET, data, len); 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci/* This function is called when an image has been read */ 45862306a36Sopenharmony_ci/* This function is used to monitor webcam orientation */ 45962306a36Sopenharmony_cistatic void sd_callback(struct gspca_dev *gspca_dev) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (!_OV9655_) { 46462306a36Sopenharmony_ci u8 state; 46562306a36Sopenharmony_ci u8 upsideDown; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Probe sensor orientation */ 46862306a36Sopenharmony_ci ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, (void *)&state); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* C8/40 means upside-down (looking backwards) */ 47162306a36Sopenharmony_ci /* D8/50 means right-up (looking onwards) */ 47262306a36Sopenharmony_ci upsideDown = (state == 0xc8 || state == 0x40); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (upsideDown && sd->nbRightUp > -4) { 47562306a36Sopenharmony_ci if (sd->nbRightUp > 0) 47662306a36Sopenharmony_ci sd->nbRightUp = 0; 47762306a36Sopenharmony_ci if (sd->nbRightUp == -3) { 47862306a36Sopenharmony_ci sd->mirrorMask = 1; 47962306a36Sopenharmony_ci sd->waitSet = 1; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci sd->nbRightUp--; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci if (!upsideDown && sd->nbRightUp < 4) { 48462306a36Sopenharmony_ci if (sd->nbRightUp < 0) 48562306a36Sopenharmony_ci sd->nbRightUp = 0; 48662306a36Sopenharmony_ci if (sd->nbRightUp == 3) { 48762306a36Sopenharmony_ci sd->mirrorMask = 0; 48862306a36Sopenharmony_ci sd->waitSet = 1; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci sd->nbRightUp++; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (sd->waitSet) 49562306a36Sopenharmony_ci sd->dev_camera_settings(gspca_dev); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/*=================== USB driver structure initialisation ==================*/ 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic const struct usb_device_id device_table[] = { 50162306a36Sopenharmony_ci {USB_DEVICE(0x05e3, 0x0503)}, 50262306a36Sopenharmony_ci {USB_DEVICE(0x05e3, 0xf191)}, 50362306a36Sopenharmony_ci {} 50462306a36Sopenharmony_ci}; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, 50962306a36Sopenharmony_ci const struct usb_device_id *id) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci return gspca_dev_probe(intf, id, 51262306a36Sopenharmony_ci &sd_desc_mi1320, sizeof(struct sd), THIS_MODULE); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic void sd_disconnect(struct usb_interface *intf) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci gspca_disconnect(intf); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic struct usb_driver sd_driver = { 52162306a36Sopenharmony_ci .name = MODULE_NAME, 52262306a36Sopenharmony_ci .id_table = device_table, 52362306a36Sopenharmony_ci .probe = sd_probe, 52462306a36Sopenharmony_ci .disconnect = sd_disconnect, 52562306a36Sopenharmony_ci#ifdef CONFIG_PM 52662306a36Sopenharmony_ci .suspend = gspca_suspend, 52762306a36Sopenharmony_ci .resume = gspca_resume, 52862306a36Sopenharmony_ci .reset_resume = gspca_resume, 52962306a36Sopenharmony_ci#endif 53062306a36Sopenharmony_ci}; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci/*====================== Init and Exit module functions ====================*/ 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cimodule_usb_driver(sd_driver); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/*==========================================================================*/ 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ciint gl860_RTx(struct gspca_dev *gspca_dev, 53962306a36Sopenharmony_ci unsigned char pref, u32 req, u16 val, u16 index, 54062306a36Sopenharmony_ci s32 len, void *pdata) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct usb_device *udev = gspca_dev->dev; 54362306a36Sopenharmony_ci s32 r = 0; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (pref == 0x40) { /* Send */ 54662306a36Sopenharmony_ci if (len > 0) { 54762306a36Sopenharmony_ci memcpy(gspca_dev->usb_buf, pdata, len); 54862306a36Sopenharmony_ci r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 54962306a36Sopenharmony_ci req, pref, val, index, 55062306a36Sopenharmony_ci gspca_dev->usb_buf, 55162306a36Sopenharmony_ci len, 400 + 200 * (len > 1)); 55262306a36Sopenharmony_ci } else { 55362306a36Sopenharmony_ci r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 55462306a36Sopenharmony_ci req, pref, val, index, NULL, len, 400); 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci } else { /* Receive */ 55762306a36Sopenharmony_ci if (len > 0) { 55862306a36Sopenharmony_ci r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 55962306a36Sopenharmony_ci req, pref, val, index, 56062306a36Sopenharmony_ci gspca_dev->usb_buf, 56162306a36Sopenharmony_ci len, 400 + 200 * (len > 1)); 56262306a36Sopenharmony_ci memcpy(pdata, gspca_dev->usb_buf, len); 56362306a36Sopenharmony_ci } else { 56462306a36Sopenharmony_ci gspca_err(gspca_dev, "zero-length read request\n"); 56562306a36Sopenharmony_ci r = -EINVAL; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (r < 0) 57062306a36Sopenharmony_ci pr_err("ctrl transfer failed %4d [p%02x r%d v%04x i%04x len%d]\n", 57162306a36Sopenharmony_ci r, pref, req, val, index, len); 57262306a36Sopenharmony_ci else if (len > 1 && r < len) 57362306a36Sopenharmony_ci gspca_err(gspca_dev, "short ctrl transfer %d/%d\n", r, len); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci msleep(1); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return r; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ciint fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci int n; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci for (n = 0; n < len; n++) { 58562306a36Sopenharmony_ci if (tbl[n].idx != 0xffff) 58662306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, 58762306a36Sopenharmony_ci tbl[n].idx, 0, NULL); 58862306a36Sopenharmony_ci else if (tbl[n].val == 0xffff) 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci else 59162306a36Sopenharmony_ci msleep(tbl[n].val); 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci return n; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ciint keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl, 59762306a36Sopenharmony_ci int len, int n) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci while (++n < len) { 60062306a36Sopenharmony_ci if (tbl[n].idx != 0xffff) 60162306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, tbl[n].idx, 60262306a36Sopenharmony_ci 0, NULL); 60362306a36Sopenharmony_ci else if (tbl[n].val == 0xffff) 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci else 60662306a36Sopenharmony_ci msleep(tbl[n].val); 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci return n; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_civoid fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci int n; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci for (n = 0; n < len; n++) { 61662306a36Sopenharmony_ci if (memcmp(tbl[n].data, "\xff\xff\xff", 3) != 0) 61762306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 3, 0x7a00, tbl[n].idx, 61862306a36Sopenharmony_ci 3, tbl[n].data); 61962306a36Sopenharmony_ci else 62062306a36Sopenharmony_ci msleep(tbl[n].idx); 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic int gl860_guess_sensor(struct gspca_dev *gspca_dev, 62562306a36Sopenharmony_ci u16 vendor_id, u16 product_id) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 62862306a36Sopenharmony_ci u8 probe, nb26, nb96, nOV, ntry; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (product_id == 0xf191) 63162306a36Sopenharmony_ci sd->sensor = ID_MI1320; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (sd->sensor == 0xff) { 63462306a36Sopenharmony_ci ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); 63562306a36Sopenharmony_ci ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x0000, 0, NULL); 63862306a36Sopenharmony_ci msleep(3); 63962306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); 64062306a36Sopenharmony_ci msleep(3); 64162306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x00c0, 0, NULL); 64262306a36Sopenharmony_ci msleep(3); 64362306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c1, 0, NULL); 64462306a36Sopenharmony_ci msleep(3); 64562306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c2, 0, NULL); 64662306a36Sopenharmony_ci msleep(3); 64762306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, 0x0020, 0x0006, 0, NULL); 64862306a36Sopenharmony_ci msleep(3); 64962306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); 65062306a36Sopenharmony_ci msleep(56); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "probing for sensor MI2020 or OVXXXX\n"); 65362306a36Sopenharmony_ci nOV = 0; 65462306a36Sopenharmony_ci for (ntry = 0; ntry < 4; ntry++) { 65562306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); 65662306a36Sopenharmony_ci msleep(3); 65762306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, 0x0063, 0x0006, 0, NULL); 65862306a36Sopenharmony_ci msleep(3); 65962306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); 66062306a36Sopenharmony_ci msleep(10); 66162306a36Sopenharmony_ci ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe); 66262306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "probe=0x%02x\n", probe); 66362306a36Sopenharmony_ci if (probe == 0xff) 66462306a36Sopenharmony_ci nOV++; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (nOV) { 66862306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "0xff -> OVXXXX\n"); 66962306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "probing for sensor OV2640 or OV9655"); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci nb26 = nb96 = 0; 67262306a36Sopenharmony_ci for (ntry = 0; ntry < 4; ntry++) { 67362306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 67462306a36Sopenharmony_ci 0, NULL); 67562306a36Sopenharmony_ci msleep(3); 67662306a36Sopenharmony_ci ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a, 67762306a36Sopenharmony_ci 0, NULL); 67862306a36Sopenharmony_ci msleep(10); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* Wait for 26(OV2640) or 96(OV9655) */ 68162306a36Sopenharmony_ci ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a, 68262306a36Sopenharmony_ci 1, &probe); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (probe == 0x26 || probe == 0x40) { 68562306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, 68662306a36Sopenharmony_ci "probe=0x%02x -> OV2640\n", 68762306a36Sopenharmony_ci probe); 68862306a36Sopenharmony_ci sd->sensor = ID_OV2640; 68962306a36Sopenharmony_ci nb26 += 4; 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci if (probe == 0x96 || probe == 0x55) { 69362306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, 69462306a36Sopenharmony_ci "probe=0x%02x -> OV9655\n", 69562306a36Sopenharmony_ci probe); 69662306a36Sopenharmony_ci sd->sensor = ID_OV9655; 69762306a36Sopenharmony_ci nb96 += 4; 69862306a36Sopenharmony_ci break; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "probe=0x%02x\n", 70162306a36Sopenharmony_ci probe); 70262306a36Sopenharmony_ci if (probe == 0x00) 70362306a36Sopenharmony_ci nb26++; 70462306a36Sopenharmony_ci if (probe == 0xff) 70562306a36Sopenharmony_ci nb96++; 70662306a36Sopenharmony_ci msleep(3); 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci if (nb26 < 4 && nb96 < 4) 70962306a36Sopenharmony_ci return -1; 71062306a36Sopenharmony_ci } else { 71162306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "Not any 0xff -> MI2020\n"); 71262306a36Sopenharmony_ci sd->sensor = ID_MI2020; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (_MI1320_) { 71762306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "05e3:f191 sensor MI1320 (1.3M)\n"); 71862306a36Sopenharmony_ci } else if (_MI2020_) { 71962306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "05e3:0503 sensor MI2020 (2.0M)\n"); 72062306a36Sopenharmony_ci } else if (_OV9655_) { 72162306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "05e3:0503 sensor OV9655 (1.3M)\n"); 72262306a36Sopenharmony_ci } else if (_OV2640_) { 72362306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "05e3:0503 sensor OV2640 (2.0M)\n"); 72462306a36Sopenharmony_ci } else { 72562306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "***** Unknown sensor *****\n"); 72662306a36Sopenharmony_ci return -1; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci return 0; 73062306a36Sopenharmony_ci} 731