xref: /OK3568_Linux_fs/kernel/drivers/media/usb/gspca/gl860/gl860.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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