1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip
3*4882a593Smuzhiyun * Subdriver core
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * 2009/09/24 Olivier Lorin <o.lorin@laposte.net>
6*4882a593Smuzhiyun * GSPCA by Jean-Francois Moine <http://moinejf.free.fr>
7*4882a593Smuzhiyun * Thanks BUGabundo and Malmostoso for your amazing help!
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include "gspca.h"
13*4882a593Smuzhiyun #include "gl860.h"
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun MODULE_AUTHOR("Olivier Lorin <o.lorin@laposte.net>");
16*4882a593Smuzhiyun MODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver");
17*4882a593Smuzhiyun MODULE_LICENSE("GPL");
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun /*======================== static function declarations ====================*/
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun static void (*dev_init_settings)(struct gspca_dev *gspca_dev);
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun static int sd_config(struct gspca_dev *gspca_dev,
24*4882a593Smuzhiyun const struct usb_device_id *id);
25*4882a593Smuzhiyun static int sd_init(struct gspca_dev *gspca_dev);
26*4882a593Smuzhiyun static int sd_isoc_init(struct gspca_dev *gspca_dev);
27*4882a593Smuzhiyun static int sd_start(struct gspca_dev *gspca_dev);
28*4882a593Smuzhiyun static void sd_stop0(struct gspca_dev *gspca_dev);
29*4882a593Smuzhiyun static void sd_pkt_scan(struct gspca_dev *gspca_dev,
30*4882a593Smuzhiyun u8 *data, int len);
31*4882a593Smuzhiyun static void sd_callback(struct gspca_dev *gspca_dev);
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
34*4882a593Smuzhiyun u16 vendor_id, u16 product_id);
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun /*============================ driver options ==============================*/
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun static s32 AC50Hz = 0xff;
39*4882a593Smuzhiyun module_param(AC50Hz, int, 0644);
40*4882a593Smuzhiyun MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)");
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun static char sensor[7];
43*4882a593Smuzhiyun module_param_string(sensor, sensor, sizeof(sensor), 0644);
44*4882a593Smuzhiyun MODULE_PARM_DESC(sensor,
45*4882a593Smuzhiyun " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')");
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun /*============================ webcam controls =============================*/
48*4882a593Smuzhiyun
sd_s_ctrl(struct v4l2_ctrl * ctrl)49*4882a593Smuzhiyun static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun struct gspca_dev *gspca_dev =
52*4882a593Smuzhiyun container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
53*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun switch (ctrl->id) {
56*4882a593Smuzhiyun case V4L2_CID_BRIGHTNESS:
57*4882a593Smuzhiyun sd->vcur.brightness = ctrl->val;
58*4882a593Smuzhiyun break;
59*4882a593Smuzhiyun case V4L2_CID_CONTRAST:
60*4882a593Smuzhiyun sd->vcur.contrast = ctrl->val;
61*4882a593Smuzhiyun break;
62*4882a593Smuzhiyun case V4L2_CID_SATURATION:
63*4882a593Smuzhiyun sd->vcur.saturation = ctrl->val;
64*4882a593Smuzhiyun break;
65*4882a593Smuzhiyun case V4L2_CID_HUE:
66*4882a593Smuzhiyun sd->vcur.hue = ctrl->val;
67*4882a593Smuzhiyun break;
68*4882a593Smuzhiyun case V4L2_CID_GAMMA:
69*4882a593Smuzhiyun sd->vcur.gamma = ctrl->val;
70*4882a593Smuzhiyun break;
71*4882a593Smuzhiyun case V4L2_CID_HFLIP:
72*4882a593Smuzhiyun sd->vcur.mirror = ctrl->val;
73*4882a593Smuzhiyun break;
74*4882a593Smuzhiyun case V4L2_CID_VFLIP:
75*4882a593Smuzhiyun sd->vcur.flip = ctrl->val;
76*4882a593Smuzhiyun break;
77*4882a593Smuzhiyun case V4L2_CID_POWER_LINE_FREQUENCY:
78*4882a593Smuzhiyun sd->vcur.AC50Hz = ctrl->val;
79*4882a593Smuzhiyun break;
80*4882a593Smuzhiyun case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
81*4882a593Smuzhiyun sd->vcur.whitebal = ctrl->val;
82*4882a593Smuzhiyun break;
83*4882a593Smuzhiyun case V4L2_CID_SHARPNESS:
84*4882a593Smuzhiyun sd->vcur.sharpness = ctrl->val;
85*4882a593Smuzhiyun break;
86*4882a593Smuzhiyun case V4L2_CID_BACKLIGHT_COMPENSATION:
87*4882a593Smuzhiyun sd->vcur.backlight = ctrl->val;
88*4882a593Smuzhiyun break;
89*4882a593Smuzhiyun default:
90*4882a593Smuzhiyun return -EINVAL;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun if (gspca_dev->streaming)
94*4882a593Smuzhiyun sd->waitSet = 1;
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun return 0;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun static const struct v4l2_ctrl_ops sd_ctrl_ops = {
100*4882a593Smuzhiyun .s_ctrl = sd_s_ctrl,
101*4882a593Smuzhiyun };
102*4882a593Smuzhiyun
sd_init_controls(struct gspca_dev * gspca_dev)103*4882a593Smuzhiyun static int sd_init_controls(struct gspca_dev *gspca_dev)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
106*4882a593Smuzhiyun struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun gspca_dev->vdev.ctrl_handler = hdl;
109*4882a593Smuzhiyun v4l2_ctrl_handler_init(hdl, 11);
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun if (sd->vmax.brightness)
112*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_BRIGHTNESS,
113*4882a593Smuzhiyun 0, sd->vmax.brightness, 1,
114*4882a593Smuzhiyun sd->vcur.brightness);
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun if (sd->vmax.contrast)
117*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_CONTRAST,
118*4882a593Smuzhiyun 0, sd->vmax.contrast, 1,
119*4882a593Smuzhiyun sd->vcur.contrast);
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun if (sd->vmax.saturation)
122*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_SATURATION,
123*4882a593Smuzhiyun 0, sd->vmax.saturation, 1,
124*4882a593Smuzhiyun sd->vcur.saturation);
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun if (sd->vmax.hue)
127*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_HUE,
128*4882a593Smuzhiyun 0, sd->vmax.hue, 1, sd->vcur.hue);
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun if (sd->vmax.gamma)
131*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAMMA,
132*4882a593Smuzhiyun 0, sd->vmax.gamma, 1, sd->vcur.gamma);
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun if (sd->vmax.mirror)
135*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_HFLIP,
136*4882a593Smuzhiyun 0, sd->vmax.mirror, 1, sd->vcur.mirror);
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun if (sd->vmax.flip)
139*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_VFLIP,
140*4882a593Smuzhiyun 0, sd->vmax.flip, 1, sd->vcur.flip);
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun if (sd->vmax.AC50Hz)
143*4882a593Smuzhiyun v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
144*4882a593Smuzhiyun V4L2_CID_POWER_LINE_FREQUENCY,
145*4882a593Smuzhiyun sd->vmax.AC50Hz, 0, sd->vcur.AC50Hz);
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun if (sd->vmax.whitebal)
148*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
149*4882a593Smuzhiyun V4L2_CID_WHITE_BALANCE_TEMPERATURE,
150*4882a593Smuzhiyun 0, sd->vmax.whitebal, 1, sd->vcur.whitebal);
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun if (sd->vmax.sharpness)
153*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_SHARPNESS,
154*4882a593Smuzhiyun 0, sd->vmax.sharpness, 1,
155*4882a593Smuzhiyun sd->vcur.sharpness);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun if (sd->vmax.backlight)
158*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
159*4882a593Smuzhiyun V4L2_CID_BACKLIGHT_COMPENSATION,
160*4882a593Smuzhiyun 0, sd->vmax.backlight, 1,
161*4882a593Smuzhiyun sd->vcur.backlight);
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun if (hdl->error) {
164*4882a593Smuzhiyun pr_err("Could not initialize controls\n");
165*4882a593Smuzhiyun return hdl->error;
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun return 0;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun /*==================== sud-driver structure initialisation =================*/
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun static const struct sd_desc sd_desc_mi1320 = {
174*4882a593Smuzhiyun .name = MODULE_NAME,
175*4882a593Smuzhiyun .config = sd_config,
176*4882a593Smuzhiyun .init = sd_init,
177*4882a593Smuzhiyun .init_controls = sd_init_controls,
178*4882a593Smuzhiyun .isoc_init = sd_isoc_init,
179*4882a593Smuzhiyun .start = sd_start,
180*4882a593Smuzhiyun .stop0 = sd_stop0,
181*4882a593Smuzhiyun .pkt_scan = sd_pkt_scan,
182*4882a593Smuzhiyun .dq_callback = sd_callback,
183*4882a593Smuzhiyun };
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun static const struct sd_desc sd_desc_mi2020 = {
186*4882a593Smuzhiyun .name = MODULE_NAME,
187*4882a593Smuzhiyun .config = sd_config,
188*4882a593Smuzhiyun .init = sd_init,
189*4882a593Smuzhiyun .init_controls = sd_init_controls,
190*4882a593Smuzhiyun .isoc_init = sd_isoc_init,
191*4882a593Smuzhiyun .start = sd_start,
192*4882a593Smuzhiyun .stop0 = sd_stop0,
193*4882a593Smuzhiyun .pkt_scan = sd_pkt_scan,
194*4882a593Smuzhiyun .dq_callback = sd_callback,
195*4882a593Smuzhiyun };
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun static const struct sd_desc sd_desc_ov2640 = {
198*4882a593Smuzhiyun .name = MODULE_NAME,
199*4882a593Smuzhiyun .config = sd_config,
200*4882a593Smuzhiyun .init = sd_init,
201*4882a593Smuzhiyun .init_controls = sd_init_controls,
202*4882a593Smuzhiyun .isoc_init = sd_isoc_init,
203*4882a593Smuzhiyun .start = sd_start,
204*4882a593Smuzhiyun .stop0 = sd_stop0,
205*4882a593Smuzhiyun .pkt_scan = sd_pkt_scan,
206*4882a593Smuzhiyun .dq_callback = sd_callback,
207*4882a593Smuzhiyun };
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun static const struct sd_desc sd_desc_ov9655 = {
210*4882a593Smuzhiyun .name = MODULE_NAME,
211*4882a593Smuzhiyun .config = sd_config,
212*4882a593Smuzhiyun .init = sd_init,
213*4882a593Smuzhiyun .init_controls = sd_init_controls,
214*4882a593Smuzhiyun .isoc_init = sd_isoc_init,
215*4882a593Smuzhiyun .start = sd_start,
216*4882a593Smuzhiyun .stop0 = sd_stop0,
217*4882a593Smuzhiyun .pkt_scan = sd_pkt_scan,
218*4882a593Smuzhiyun .dq_callback = sd_callback,
219*4882a593Smuzhiyun };
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun /*=========================== sub-driver image sizes =======================*/
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun static struct v4l2_pix_format mi2020_mode[] = {
224*4882a593Smuzhiyun { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
225*4882a593Smuzhiyun .bytesperline = 640,
226*4882a593Smuzhiyun .sizeimage = 640 * 480,
227*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
228*4882a593Smuzhiyun .priv = 0
229*4882a593Smuzhiyun },
230*4882a593Smuzhiyun { 800, 598, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
231*4882a593Smuzhiyun .bytesperline = 800,
232*4882a593Smuzhiyun .sizeimage = 800 * 598,
233*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
234*4882a593Smuzhiyun .priv = 1
235*4882a593Smuzhiyun },
236*4882a593Smuzhiyun {1280, 1024, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
237*4882a593Smuzhiyun .bytesperline = 1280,
238*4882a593Smuzhiyun .sizeimage = 1280 * 1024,
239*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
240*4882a593Smuzhiyun .priv = 2
241*4882a593Smuzhiyun },
242*4882a593Smuzhiyun {1600, 1198, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
243*4882a593Smuzhiyun .bytesperline = 1600,
244*4882a593Smuzhiyun .sizeimage = 1600 * 1198,
245*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
246*4882a593Smuzhiyun .priv = 3
247*4882a593Smuzhiyun },
248*4882a593Smuzhiyun };
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun static struct v4l2_pix_format ov2640_mode[] = {
251*4882a593Smuzhiyun { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
252*4882a593Smuzhiyun .bytesperline = 640,
253*4882a593Smuzhiyun .sizeimage = 640 * 480,
254*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
255*4882a593Smuzhiyun .priv = 0
256*4882a593Smuzhiyun },
257*4882a593Smuzhiyun { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
258*4882a593Smuzhiyun .bytesperline = 800,
259*4882a593Smuzhiyun .sizeimage = 800 * 600,
260*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
261*4882a593Smuzhiyun .priv = 1
262*4882a593Smuzhiyun },
263*4882a593Smuzhiyun {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
264*4882a593Smuzhiyun .bytesperline = 1280,
265*4882a593Smuzhiyun .sizeimage = 1280 * 960,
266*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
267*4882a593Smuzhiyun .priv = 2
268*4882a593Smuzhiyun },
269*4882a593Smuzhiyun {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
270*4882a593Smuzhiyun .bytesperline = 1600,
271*4882a593Smuzhiyun .sizeimage = 1600 * 1200,
272*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
273*4882a593Smuzhiyun .priv = 3
274*4882a593Smuzhiyun },
275*4882a593Smuzhiyun };
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun static struct v4l2_pix_format mi1320_mode[] = {
278*4882a593Smuzhiyun { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
279*4882a593Smuzhiyun .bytesperline = 640,
280*4882a593Smuzhiyun .sizeimage = 640 * 480,
281*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
282*4882a593Smuzhiyun .priv = 0
283*4882a593Smuzhiyun },
284*4882a593Smuzhiyun { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
285*4882a593Smuzhiyun .bytesperline = 800,
286*4882a593Smuzhiyun .sizeimage = 800 * 600,
287*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
288*4882a593Smuzhiyun .priv = 1
289*4882a593Smuzhiyun },
290*4882a593Smuzhiyun {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
291*4882a593Smuzhiyun .bytesperline = 1280,
292*4882a593Smuzhiyun .sizeimage = 1280 * 960,
293*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
294*4882a593Smuzhiyun .priv = 2
295*4882a593Smuzhiyun },
296*4882a593Smuzhiyun };
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun static struct v4l2_pix_format ov9655_mode[] = {
299*4882a593Smuzhiyun { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
300*4882a593Smuzhiyun .bytesperline = 640,
301*4882a593Smuzhiyun .sizeimage = 640 * 480,
302*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
303*4882a593Smuzhiyun .priv = 0
304*4882a593Smuzhiyun },
305*4882a593Smuzhiyun {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
306*4882a593Smuzhiyun .bytesperline = 1280,
307*4882a593Smuzhiyun .sizeimage = 1280 * 960,
308*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_SRGB,
309*4882a593Smuzhiyun .priv = 1
310*4882a593Smuzhiyun },
311*4882a593Smuzhiyun };
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun /*========================= sud-driver functions ===========================*/
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun /* This function is called at probe time */
sd_config(struct gspca_dev * gspca_dev,const struct usb_device_id * id)316*4882a593Smuzhiyun static int sd_config(struct gspca_dev *gspca_dev,
317*4882a593Smuzhiyun const struct usb_device_id *id)
318*4882a593Smuzhiyun {
319*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
320*4882a593Smuzhiyun struct cam *cam;
321*4882a593Smuzhiyun u16 vendor_id, product_id;
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun /* Get USB VendorID and ProductID */
324*4882a593Smuzhiyun vendor_id = id->idVendor;
325*4882a593Smuzhiyun product_id = id->idProduct;
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun sd->nbRightUp = 1;
328*4882a593Smuzhiyun sd->nbIm = -1;
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun sd->sensor = 0xff;
331*4882a593Smuzhiyun if (strcmp(sensor, "MI1320") == 0)
332*4882a593Smuzhiyun sd->sensor = ID_MI1320;
333*4882a593Smuzhiyun else if (strcmp(sensor, "OV2640") == 0)
334*4882a593Smuzhiyun sd->sensor = ID_OV2640;
335*4882a593Smuzhiyun else if (strcmp(sensor, "OV9655") == 0)
336*4882a593Smuzhiyun sd->sensor = ID_OV9655;
337*4882a593Smuzhiyun else if (strcmp(sensor, "MI2020") == 0)
338*4882a593Smuzhiyun sd->sensor = ID_MI2020;
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun /* Get sensor and set the suitable init/start/../stop functions */
341*4882a593Smuzhiyun if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1)
342*4882a593Smuzhiyun return -1;
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun cam = &gspca_dev->cam;
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun switch (sd->sensor) {
347*4882a593Smuzhiyun case ID_MI1320:
348*4882a593Smuzhiyun gspca_dev->sd_desc = &sd_desc_mi1320;
349*4882a593Smuzhiyun cam->cam_mode = mi1320_mode;
350*4882a593Smuzhiyun cam->nmodes = ARRAY_SIZE(mi1320_mode);
351*4882a593Smuzhiyun dev_init_settings = mi1320_init_settings;
352*4882a593Smuzhiyun break;
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun case ID_MI2020:
355*4882a593Smuzhiyun gspca_dev->sd_desc = &sd_desc_mi2020;
356*4882a593Smuzhiyun cam->cam_mode = mi2020_mode;
357*4882a593Smuzhiyun cam->nmodes = ARRAY_SIZE(mi2020_mode);
358*4882a593Smuzhiyun dev_init_settings = mi2020_init_settings;
359*4882a593Smuzhiyun break;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun case ID_OV2640:
362*4882a593Smuzhiyun gspca_dev->sd_desc = &sd_desc_ov2640;
363*4882a593Smuzhiyun cam->cam_mode = ov2640_mode;
364*4882a593Smuzhiyun cam->nmodes = ARRAY_SIZE(ov2640_mode);
365*4882a593Smuzhiyun dev_init_settings = ov2640_init_settings;
366*4882a593Smuzhiyun break;
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun case ID_OV9655:
369*4882a593Smuzhiyun gspca_dev->sd_desc = &sd_desc_ov9655;
370*4882a593Smuzhiyun cam->cam_mode = ov9655_mode;
371*4882a593Smuzhiyun cam->nmodes = ARRAY_SIZE(ov9655_mode);
372*4882a593Smuzhiyun dev_init_settings = ov9655_init_settings;
373*4882a593Smuzhiyun break;
374*4882a593Smuzhiyun }
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun dev_init_settings(gspca_dev);
377*4882a593Smuzhiyun if (AC50Hz != 0xff)
378*4882a593Smuzhiyun ((struct sd *) gspca_dev)->vcur.AC50Hz = AC50Hz;
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun return 0;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun /* This function is called at probe time after sd_config */
sd_init(struct gspca_dev * gspca_dev)384*4882a593Smuzhiyun static int sd_init(struct gspca_dev *gspca_dev)
385*4882a593Smuzhiyun {
386*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun return sd->dev_init_at_startup(gspca_dev);
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun /* This function is called before to choose the alt setting */
sd_isoc_init(struct gspca_dev * gspca_dev)392*4882a593Smuzhiyun static int sd_isoc_init(struct gspca_dev *gspca_dev)
393*4882a593Smuzhiyun {
394*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun return sd->dev_configure_alt(gspca_dev);
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun /* This function is called to start the webcam */
sd_start(struct gspca_dev * gspca_dev)400*4882a593Smuzhiyun static int sd_start(struct gspca_dev *gspca_dev)
401*4882a593Smuzhiyun {
402*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun return sd->dev_init_pre_alt(gspca_dev);
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun
407*4882a593Smuzhiyun /* This function is called to stop the webcam */
sd_stop0(struct gspca_dev * gspca_dev)408*4882a593Smuzhiyun static void sd_stop0(struct gspca_dev *gspca_dev)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun if (!sd->gspca_dev.present)
413*4882a593Smuzhiyun return;
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun return sd->dev_post_unset_alt(gspca_dev);
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun /* This function is called when an image is being received */
sd_pkt_scan(struct gspca_dev * gspca_dev,u8 * data,int len)419*4882a593Smuzhiyun static void sd_pkt_scan(struct gspca_dev *gspca_dev,
420*4882a593Smuzhiyun u8 *data, int len)
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
423*4882a593Smuzhiyun static s32 nSkipped;
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun s32 mode = (s32) gspca_dev->curr_mode;
426*4882a593Smuzhiyun s32 nToSkip =
427*4882a593Smuzhiyun sd->swapRB * (gspca_dev->cam.cam_mode[mode].bytesperline + 1);
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun /* Test only against 0202h, so endianness does not matter */
430*4882a593Smuzhiyun switch (*(s16 *) data) {
431*4882a593Smuzhiyun case 0x0202: /* End of frame, start a new one */
432*4882a593Smuzhiyun gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
433*4882a593Smuzhiyun nSkipped = 0;
434*4882a593Smuzhiyun if (sd->nbIm >= 0 && sd->nbIm < 10)
435*4882a593Smuzhiyun sd->nbIm++;
436*4882a593Smuzhiyun gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
437*4882a593Smuzhiyun break;
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun default:
440*4882a593Smuzhiyun data += 2;
441*4882a593Smuzhiyun len -= 2;
442*4882a593Smuzhiyun if (nSkipped + len <= nToSkip)
443*4882a593Smuzhiyun nSkipped += len;
444*4882a593Smuzhiyun else {
445*4882a593Smuzhiyun if (nSkipped < nToSkip && nSkipped + len > nToSkip) {
446*4882a593Smuzhiyun data += nToSkip - nSkipped;
447*4882a593Smuzhiyun len -= nToSkip - nSkipped;
448*4882a593Smuzhiyun nSkipped = nToSkip + 1;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun gspca_frame_add(gspca_dev,
451*4882a593Smuzhiyun INTER_PACKET, data, len);
452*4882a593Smuzhiyun }
453*4882a593Smuzhiyun break;
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun /* This function is called when an image has been read */
458*4882a593Smuzhiyun /* This function is used to monitor webcam orientation */
sd_callback(struct gspca_dev * gspca_dev)459*4882a593Smuzhiyun static void sd_callback(struct gspca_dev *gspca_dev)
460*4882a593Smuzhiyun {
461*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun if (!_OV9655_) {
464*4882a593Smuzhiyun u8 state;
465*4882a593Smuzhiyun u8 upsideDown;
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun /* Probe sensor orientation */
468*4882a593Smuzhiyun ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, (void *)&state);
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun /* C8/40 means upside-down (looking backwards) */
471*4882a593Smuzhiyun /* D8/50 means right-up (looking onwards) */
472*4882a593Smuzhiyun upsideDown = (state == 0xc8 || state == 0x40);
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun if (upsideDown && sd->nbRightUp > -4) {
475*4882a593Smuzhiyun if (sd->nbRightUp > 0)
476*4882a593Smuzhiyun sd->nbRightUp = 0;
477*4882a593Smuzhiyun if (sd->nbRightUp == -3) {
478*4882a593Smuzhiyun sd->mirrorMask = 1;
479*4882a593Smuzhiyun sd->waitSet = 1;
480*4882a593Smuzhiyun }
481*4882a593Smuzhiyun sd->nbRightUp--;
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun if (!upsideDown && sd->nbRightUp < 4) {
484*4882a593Smuzhiyun if (sd->nbRightUp < 0)
485*4882a593Smuzhiyun sd->nbRightUp = 0;
486*4882a593Smuzhiyun if (sd->nbRightUp == 3) {
487*4882a593Smuzhiyun sd->mirrorMask = 0;
488*4882a593Smuzhiyun sd->waitSet = 1;
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun sd->nbRightUp++;
491*4882a593Smuzhiyun }
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun if (sd->waitSet)
495*4882a593Smuzhiyun sd->dev_camera_settings(gspca_dev);
496*4882a593Smuzhiyun }
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun /*=================== USB driver structure initialisation ==================*/
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun static const struct usb_device_id device_table[] = {
501*4882a593Smuzhiyun {USB_DEVICE(0x05e3, 0x0503)},
502*4882a593Smuzhiyun {USB_DEVICE(0x05e3, 0xf191)},
503*4882a593Smuzhiyun {}
504*4882a593Smuzhiyun };
505*4882a593Smuzhiyun
506*4882a593Smuzhiyun MODULE_DEVICE_TABLE(usb, device_table);
507*4882a593Smuzhiyun
sd_probe(struct usb_interface * intf,const struct usb_device_id * id)508*4882a593Smuzhiyun static int sd_probe(struct usb_interface *intf,
509*4882a593Smuzhiyun const struct usb_device_id *id)
510*4882a593Smuzhiyun {
511*4882a593Smuzhiyun return gspca_dev_probe(intf, id,
512*4882a593Smuzhiyun &sd_desc_mi1320, sizeof(struct sd), THIS_MODULE);
513*4882a593Smuzhiyun }
514*4882a593Smuzhiyun
sd_disconnect(struct usb_interface * intf)515*4882a593Smuzhiyun static void sd_disconnect(struct usb_interface *intf)
516*4882a593Smuzhiyun {
517*4882a593Smuzhiyun gspca_disconnect(intf);
518*4882a593Smuzhiyun }
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun static struct usb_driver sd_driver = {
521*4882a593Smuzhiyun .name = MODULE_NAME,
522*4882a593Smuzhiyun .id_table = device_table,
523*4882a593Smuzhiyun .probe = sd_probe,
524*4882a593Smuzhiyun .disconnect = sd_disconnect,
525*4882a593Smuzhiyun #ifdef CONFIG_PM
526*4882a593Smuzhiyun .suspend = gspca_suspend,
527*4882a593Smuzhiyun .resume = gspca_resume,
528*4882a593Smuzhiyun .reset_resume = gspca_resume,
529*4882a593Smuzhiyun #endif
530*4882a593Smuzhiyun };
531*4882a593Smuzhiyun
532*4882a593Smuzhiyun /*====================== Init and Exit module functions ====================*/
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun module_usb_driver(sd_driver);
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun /*==========================================================================*/
537*4882a593Smuzhiyun
gl860_RTx(struct gspca_dev * gspca_dev,unsigned char pref,u32 req,u16 val,u16 index,s32 len,void * pdata)538*4882a593Smuzhiyun int gl860_RTx(struct gspca_dev *gspca_dev,
539*4882a593Smuzhiyun unsigned char pref, u32 req, u16 val, u16 index,
540*4882a593Smuzhiyun s32 len, void *pdata)
541*4882a593Smuzhiyun {
542*4882a593Smuzhiyun struct usb_device *udev = gspca_dev->dev;
543*4882a593Smuzhiyun s32 r = 0;
544*4882a593Smuzhiyun
545*4882a593Smuzhiyun if (pref == 0x40) { /* Send */
546*4882a593Smuzhiyun if (len > 0) {
547*4882a593Smuzhiyun memcpy(gspca_dev->usb_buf, pdata, len);
548*4882a593Smuzhiyun r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
549*4882a593Smuzhiyun req, pref, val, index,
550*4882a593Smuzhiyun gspca_dev->usb_buf,
551*4882a593Smuzhiyun len, 400 + 200 * (len > 1));
552*4882a593Smuzhiyun } else {
553*4882a593Smuzhiyun r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
554*4882a593Smuzhiyun req, pref, val, index, NULL, len, 400);
555*4882a593Smuzhiyun }
556*4882a593Smuzhiyun } else { /* Receive */
557*4882a593Smuzhiyun if (len > 0) {
558*4882a593Smuzhiyun r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
559*4882a593Smuzhiyun req, pref, val, index,
560*4882a593Smuzhiyun gspca_dev->usb_buf,
561*4882a593Smuzhiyun len, 400 + 200 * (len > 1));
562*4882a593Smuzhiyun memcpy(pdata, gspca_dev->usb_buf, len);
563*4882a593Smuzhiyun } else {
564*4882a593Smuzhiyun gspca_err(gspca_dev, "zero-length read request\n");
565*4882a593Smuzhiyun r = -EINVAL;
566*4882a593Smuzhiyun }
567*4882a593Smuzhiyun }
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun if (r < 0)
570*4882a593Smuzhiyun pr_err("ctrl transfer failed %4d [p%02x r%d v%04x i%04x len%d]\n",
571*4882a593Smuzhiyun r, pref, req, val, index, len);
572*4882a593Smuzhiyun else if (len > 1 && r < len)
573*4882a593Smuzhiyun gspca_err(gspca_dev, "short ctrl transfer %d/%d\n", r, len);
574*4882a593Smuzhiyun
575*4882a593Smuzhiyun msleep(1);
576*4882a593Smuzhiyun
577*4882a593Smuzhiyun return r;
578*4882a593Smuzhiyun }
579*4882a593Smuzhiyun
fetch_validx(struct gspca_dev * gspca_dev,struct validx * tbl,int len)580*4882a593Smuzhiyun int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len)
581*4882a593Smuzhiyun {
582*4882a593Smuzhiyun int n;
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun for (n = 0; n < len; n++) {
585*4882a593Smuzhiyun if (tbl[n].idx != 0xffff)
586*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, tbl[n].val,
587*4882a593Smuzhiyun tbl[n].idx, 0, NULL);
588*4882a593Smuzhiyun else if (tbl[n].val == 0xffff)
589*4882a593Smuzhiyun break;
590*4882a593Smuzhiyun else
591*4882a593Smuzhiyun msleep(tbl[n].val);
592*4882a593Smuzhiyun }
593*4882a593Smuzhiyun return n;
594*4882a593Smuzhiyun }
595*4882a593Smuzhiyun
keep_on_fetching_validx(struct gspca_dev * gspca_dev,struct validx * tbl,int len,int n)596*4882a593Smuzhiyun int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl,
597*4882a593Smuzhiyun int len, int n)
598*4882a593Smuzhiyun {
599*4882a593Smuzhiyun while (++n < len) {
600*4882a593Smuzhiyun if (tbl[n].idx != 0xffff)
601*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, tbl[n].idx,
602*4882a593Smuzhiyun 0, NULL);
603*4882a593Smuzhiyun else if (tbl[n].val == 0xffff)
604*4882a593Smuzhiyun break;
605*4882a593Smuzhiyun else
606*4882a593Smuzhiyun msleep(tbl[n].val);
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun return n;
609*4882a593Smuzhiyun }
610*4882a593Smuzhiyun
fetch_idxdata(struct gspca_dev * gspca_dev,struct idxdata * tbl,int len)611*4882a593Smuzhiyun void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len)
612*4882a593Smuzhiyun {
613*4882a593Smuzhiyun int n;
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun for (n = 0; n < len; n++) {
616*4882a593Smuzhiyun if (memcmp(tbl[n].data, "\xff\xff\xff", 3) != 0)
617*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 3, 0x7a00, tbl[n].idx,
618*4882a593Smuzhiyun 3, tbl[n].data);
619*4882a593Smuzhiyun else
620*4882a593Smuzhiyun msleep(tbl[n].idx);
621*4882a593Smuzhiyun }
622*4882a593Smuzhiyun }
623*4882a593Smuzhiyun
gl860_guess_sensor(struct gspca_dev * gspca_dev,u16 vendor_id,u16 product_id)624*4882a593Smuzhiyun static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
625*4882a593Smuzhiyun u16 vendor_id, u16 product_id)
626*4882a593Smuzhiyun {
627*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
628*4882a593Smuzhiyun u8 probe, nb26, nb96, nOV, ntry;
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun if (product_id == 0xf191)
631*4882a593Smuzhiyun sd->sensor = ID_MI1320;
632*4882a593Smuzhiyun
633*4882a593Smuzhiyun if (sd->sensor == 0xff) {
634*4882a593Smuzhiyun ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe);
635*4882a593Smuzhiyun ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe);
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x0000, 0, NULL);
638*4882a593Smuzhiyun msleep(3);
639*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL);
640*4882a593Smuzhiyun msleep(3);
641*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x00c0, 0, NULL);
642*4882a593Smuzhiyun msleep(3);
643*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c1, 0, NULL);
644*4882a593Smuzhiyun msleep(3);
645*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c2, 0, NULL);
646*4882a593Smuzhiyun msleep(3);
647*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, 0x0020, 0x0006, 0, NULL);
648*4882a593Smuzhiyun msleep(3);
649*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL);
650*4882a593Smuzhiyun msleep(56);
651*4882a593Smuzhiyun
652*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE, "probing for sensor MI2020 or OVXXXX\n");
653*4882a593Smuzhiyun nOV = 0;
654*4882a593Smuzhiyun for (ntry = 0; ntry < 4; ntry++) {
655*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
656*4882a593Smuzhiyun msleep(3);
657*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, 0x0063, 0x0006, 0, NULL);
658*4882a593Smuzhiyun msleep(3);
659*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL);
660*4882a593Smuzhiyun msleep(10);
661*4882a593Smuzhiyun ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe);
662*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE, "probe=0x%02x\n", probe);
663*4882a593Smuzhiyun if (probe == 0xff)
664*4882a593Smuzhiyun nOV++;
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun if (nOV) {
668*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE, "0xff -> OVXXXX\n");
669*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE, "probing for sensor OV2640 or OV9655");
670*4882a593Smuzhiyun
671*4882a593Smuzhiyun nb26 = nb96 = 0;
672*4882a593Smuzhiyun for (ntry = 0; ntry < 4; ntry++) {
673*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000,
674*4882a593Smuzhiyun 0, NULL);
675*4882a593Smuzhiyun msleep(3);
676*4882a593Smuzhiyun ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a,
677*4882a593Smuzhiyun 0, NULL);
678*4882a593Smuzhiyun msleep(10);
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun /* Wait for 26(OV2640) or 96(OV9655) */
681*4882a593Smuzhiyun ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a,
682*4882a593Smuzhiyun 1, &probe);
683*4882a593Smuzhiyun
684*4882a593Smuzhiyun if (probe == 0x26 || probe == 0x40) {
685*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE,
686*4882a593Smuzhiyun "probe=0x%02x -> OV2640\n",
687*4882a593Smuzhiyun probe);
688*4882a593Smuzhiyun sd->sensor = ID_OV2640;
689*4882a593Smuzhiyun nb26 += 4;
690*4882a593Smuzhiyun break;
691*4882a593Smuzhiyun }
692*4882a593Smuzhiyun if (probe == 0x96 || probe == 0x55) {
693*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE,
694*4882a593Smuzhiyun "probe=0x%02x -> OV9655\n",
695*4882a593Smuzhiyun probe);
696*4882a593Smuzhiyun sd->sensor = ID_OV9655;
697*4882a593Smuzhiyun nb96 += 4;
698*4882a593Smuzhiyun break;
699*4882a593Smuzhiyun }
700*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE, "probe=0x%02x\n",
701*4882a593Smuzhiyun probe);
702*4882a593Smuzhiyun if (probe == 0x00)
703*4882a593Smuzhiyun nb26++;
704*4882a593Smuzhiyun if (probe == 0xff)
705*4882a593Smuzhiyun nb96++;
706*4882a593Smuzhiyun msleep(3);
707*4882a593Smuzhiyun }
708*4882a593Smuzhiyun if (nb26 < 4 && nb96 < 4)
709*4882a593Smuzhiyun return -1;
710*4882a593Smuzhiyun } else {
711*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE, "Not any 0xff -> MI2020\n");
712*4882a593Smuzhiyun sd->sensor = ID_MI2020;
713*4882a593Smuzhiyun }
714*4882a593Smuzhiyun }
715*4882a593Smuzhiyun
716*4882a593Smuzhiyun if (_MI1320_) {
717*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE, "05e3:f191 sensor MI1320 (1.3M)\n");
718*4882a593Smuzhiyun } else if (_MI2020_) {
719*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE, "05e3:0503 sensor MI2020 (2.0M)\n");
720*4882a593Smuzhiyun } else if (_OV9655_) {
721*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE, "05e3:0503 sensor OV9655 (1.3M)\n");
722*4882a593Smuzhiyun } else if (_OV2640_) {
723*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE, "05e3:0503 sensor OV2640 (2.0M)\n");
724*4882a593Smuzhiyun } else {
725*4882a593Smuzhiyun gspca_dbg(gspca_dev, D_PROBE, "***** Unknown sensor *****\n");
726*4882a593Smuzhiyun return -1;
727*4882a593Smuzhiyun }
728*4882a593Smuzhiyun
729*4882a593Smuzhiyun return 0;
730*4882a593Smuzhiyun }
731