xref: /OK3568_Linux_fs/kernel/drivers/media/usb/gspca/pac207.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Pixart PAC207BCA library
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2008 Hans de Goede <hdegoede@redhat.com>
6*4882a593Smuzhiyun  * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
7*4882a593Smuzhiyun  * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #define MODULE_NAME "pac207"
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include <linux/input.h>
17*4882a593Smuzhiyun #include "gspca.h"
18*4882a593Smuzhiyun /* Include pac common sof detection functions */
19*4882a593Smuzhiyun #include "pac_common.h"
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
22*4882a593Smuzhiyun MODULE_DESCRIPTION("Pixart PAC207");
23*4882a593Smuzhiyun MODULE_LICENSE("GPL");
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #define PAC207_CTRL_TIMEOUT		100  /* ms */
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #define PAC207_BRIGHTNESS_MIN		0
28*4882a593Smuzhiyun #define PAC207_BRIGHTNESS_MAX		255
29*4882a593Smuzhiyun #define PAC207_BRIGHTNESS_DEFAULT	46
30*4882a593Smuzhiyun #define PAC207_BRIGHTNESS_REG		0x08
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun #define PAC207_EXPOSURE_MIN		3
33*4882a593Smuzhiyun #define PAC207_EXPOSURE_MAX		90 /* 1 sec expo time / 1 fps */
34*4882a593Smuzhiyun #define PAC207_EXPOSURE_DEFAULT		5 /* power on default: 3 */
35*4882a593Smuzhiyun #define PAC207_EXPOSURE_REG		0x02
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #define PAC207_GAIN_MIN			0
38*4882a593Smuzhiyun #define PAC207_GAIN_MAX			31
39*4882a593Smuzhiyun #define PAC207_GAIN_DEFAULT		7 /* power on default: 9 */
40*4882a593Smuzhiyun #define PAC207_GAIN_REG			0x0e
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun #define PAC207_AUTOGAIN_DEADZONE	30
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun /* global parameters */
45*4882a593Smuzhiyun static int led_invert;
46*4882a593Smuzhiyun module_param(led_invert, int, 0644);
47*4882a593Smuzhiyun MODULE_PARM_DESC(led_invert, "Invert led");
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun /* specific webcam descriptor */
50*4882a593Smuzhiyun struct sd {
51*4882a593Smuzhiyun 	struct gspca_dev gspca_dev;		/* !! must be the first item */
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	struct v4l2_ctrl *brightness;
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	u8 mode;
56*4882a593Smuzhiyun 	u8 sof_read;
57*4882a593Smuzhiyun 	u8 header_read;
58*4882a593Smuzhiyun 	u8 autogain_ignore_frames;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	atomic_t avg_lum;
61*4882a593Smuzhiyun };
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun static const struct v4l2_pix_format sif_mode[] = {
64*4882a593Smuzhiyun 	{176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
65*4882a593Smuzhiyun 		.bytesperline = 176,
66*4882a593Smuzhiyun 		.sizeimage = (176 + 2) * 144,
67*4882a593Smuzhiyun 			/* uncompressed, add 2 bytes / line for line header */
68*4882a593Smuzhiyun 		.colorspace = V4L2_COLORSPACE_SRGB,
69*4882a593Smuzhiyun 		.priv = 1},
70*4882a593Smuzhiyun 	{352, 288, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
71*4882a593Smuzhiyun 		.bytesperline = 352,
72*4882a593Smuzhiyun 			/* compressed, but only when needed (not compressed
73*4882a593Smuzhiyun 			   when the framerate is low) */
74*4882a593Smuzhiyun 		.sizeimage = (352 + 2) * 288,
75*4882a593Smuzhiyun 		.colorspace = V4L2_COLORSPACE_SRGB,
76*4882a593Smuzhiyun 		.priv = 0},
77*4882a593Smuzhiyun };
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun static const __u8 pac207_sensor_init[][8] = {
80*4882a593Smuzhiyun 	{0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0x84},
81*4882a593Smuzhiyun 	{0x49, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30},
82*4882a593Smuzhiyun 	{0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00},
83*4882a593Smuzhiyun 	{0x32, 0x00, 0x96, 0x00, 0xa2, 0x02, 0xaf, 0x00},
84*4882a593Smuzhiyun };
85*4882a593Smuzhiyun 
pac207_write_regs(struct gspca_dev * gspca_dev,u16 index,const u8 * buffer,u16 length)86*4882a593Smuzhiyun static void pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
87*4882a593Smuzhiyun 	const u8 *buffer, u16 length)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun 	struct usb_device *udev = gspca_dev->dev;
90*4882a593Smuzhiyun 	int err;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	if (gspca_dev->usb_err < 0)
93*4882a593Smuzhiyun 		return;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	memcpy(gspca_dev->usb_buf, buffer, length);
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01,
98*4882a593Smuzhiyun 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
99*4882a593Smuzhiyun 			0x00, index,
100*4882a593Smuzhiyun 			gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT);
101*4882a593Smuzhiyun 	if (err < 0) {
102*4882a593Smuzhiyun 		pr_err("Failed to write registers to index 0x%04X, error %d\n",
103*4882a593Smuzhiyun 		       index, err);
104*4882a593Smuzhiyun 		gspca_dev->usb_err = err;
105*4882a593Smuzhiyun 	}
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun 
pac207_write_reg(struct gspca_dev * gspca_dev,u16 index,u16 value)108*4882a593Smuzhiyun static void pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun 	struct usb_device *udev = gspca_dev->dev;
111*4882a593Smuzhiyun 	int err;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	if (gspca_dev->usb_err < 0)
114*4882a593Smuzhiyun 		return;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
117*4882a593Smuzhiyun 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
118*4882a593Smuzhiyun 			value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
119*4882a593Smuzhiyun 	if (err) {
120*4882a593Smuzhiyun 		pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
121*4882a593Smuzhiyun 		       index, value, err);
122*4882a593Smuzhiyun 		gspca_dev->usb_err = err;
123*4882a593Smuzhiyun 	}
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun 
pac207_read_reg(struct gspca_dev * gspca_dev,u16 index)126*4882a593Smuzhiyun static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun 	struct usb_device *udev = gspca_dev->dev;
129*4882a593Smuzhiyun 	int res;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	if (gspca_dev->usb_err < 0)
132*4882a593Smuzhiyun 		return 0;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
135*4882a593Smuzhiyun 			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
136*4882a593Smuzhiyun 			0x00, index,
137*4882a593Smuzhiyun 			gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT);
138*4882a593Smuzhiyun 	if (res < 0) {
139*4882a593Smuzhiyun 		pr_err("Failed to read a register (index 0x%04X, error %d)\n",
140*4882a593Smuzhiyun 		       index, res);
141*4882a593Smuzhiyun 		gspca_dev->usb_err = res;
142*4882a593Smuzhiyun 		return 0;
143*4882a593Smuzhiyun 	}
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	return gspca_dev->usb_buf[0];
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun /* this function is called at probe time */
sd_config(struct gspca_dev * gspca_dev,const struct usb_device_id * id)149*4882a593Smuzhiyun static int sd_config(struct gspca_dev *gspca_dev,
150*4882a593Smuzhiyun 			const struct usb_device_id *id)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun 	struct cam *cam;
153*4882a593Smuzhiyun 	u8 idreg[2];
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	idreg[0] = pac207_read_reg(gspca_dev, 0x0000);
156*4882a593Smuzhiyun 	idreg[1] = pac207_read_reg(gspca_dev, 0x0001);
157*4882a593Smuzhiyun 	idreg[0] = ((idreg[0] & 0x0f) << 4) | ((idreg[1] & 0xf0) >> 4);
158*4882a593Smuzhiyun 	idreg[1] = idreg[1] & 0x0f;
159*4882a593Smuzhiyun 	gspca_dbg(gspca_dev, D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X\n",
160*4882a593Smuzhiyun 		  idreg[0], idreg[1]);
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	if (idreg[0] != 0x27) {
163*4882a593Smuzhiyun 		gspca_dbg(gspca_dev, D_PROBE, "Error invalid sensor ID!\n");
164*4882a593Smuzhiyun 		return -ENODEV;
165*4882a593Smuzhiyun 	}
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	gspca_dbg(gspca_dev, D_PROBE,
168*4882a593Smuzhiyun 		  "Pixart PAC207BCA Image Processor and Control Chip detected (vid/pid 0x%04X:0x%04X)\n",
169*4882a593Smuzhiyun 		  id->idVendor, id->idProduct);
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	cam = &gspca_dev->cam;
172*4882a593Smuzhiyun 	cam->cam_mode = sif_mode;
173*4882a593Smuzhiyun 	cam->nmodes = ARRAY_SIZE(sif_mode);
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	return 0;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun /* this function is called at probe and resume time */
sd_init(struct gspca_dev * gspca_dev)179*4882a593Smuzhiyun static int sd_init(struct gspca_dev *gspca_dev)
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun 	u8 mode;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	/* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */
184*4882a593Smuzhiyun 	if (led_invert)
185*4882a593Smuzhiyun 		mode = 0x02;
186*4882a593Smuzhiyun 	else
187*4882a593Smuzhiyun 		mode = 0x00;
188*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x41, mode);
189*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	return gspca_dev->usb_err;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun 
setcontrol(struct gspca_dev * gspca_dev,u16 reg,u16 val)194*4882a593Smuzhiyun static void setcontrol(struct gspca_dev *gspca_dev, u16 reg, u16 val)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, reg, val);
197*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x13, 0x01);	/* Bit 0, auto clear */
198*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x1c, 0x01);	/* not documented */
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun 
sd_s_ctrl(struct v4l2_ctrl * ctrl)201*4882a593Smuzhiyun static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun 	struct gspca_dev *gspca_dev =
204*4882a593Smuzhiyun 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
205*4882a593Smuzhiyun 	struct sd *sd = (struct sd *)gspca_dev;
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	gspca_dev->usb_err = 0;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
210*4882a593Smuzhiyun 		/* when switching to autogain set defaults to make sure
211*4882a593Smuzhiyun 		   we are on a valid point of the autogain gain /
212*4882a593Smuzhiyun 		   exposure knee graph, and give this change time to
213*4882a593Smuzhiyun 		   take effect before doing autogain. */
214*4882a593Smuzhiyun 		gspca_dev->exposure->val    = PAC207_EXPOSURE_DEFAULT;
215*4882a593Smuzhiyun 		gspca_dev->gain->val        = PAC207_GAIN_DEFAULT;
216*4882a593Smuzhiyun 		sd->autogain_ignore_frames  = PAC_AUTOGAIN_IGNORE_FRAMES;
217*4882a593Smuzhiyun 	}
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	if (!gspca_dev->streaming)
220*4882a593Smuzhiyun 		return 0;
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	switch (ctrl->id) {
223*4882a593Smuzhiyun 	case V4L2_CID_BRIGHTNESS:
224*4882a593Smuzhiyun 		setcontrol(gspca_dev, PAC207_BRIGHTNESS_REG, ctrl->val);
225*4882a593Smuzhiyun 		break;
226*4882a593Smuzhiyun 	case V4L2_CID_AUTOGAIN:
227*4882a593Smuzhiyun 		if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
228*4882a593Smuzhiyun 			setcontrol(gspca_dev, PAC207_EXPOSURE_REG,
229*4882a593Smuzhiyun 				   gspca_dev->exposure->val);
230*4882a593Smuzhiyun 		if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
231*4882a593Smuzhiyun 			setcontrol(gspca_dev, PAC207_GAIN_REG,
232*4882a593Smuzhiyun 				   gspca_dev->gain->val);
233*4882a593Smuzhiyun 		break;
234*4882a593Smuzhiyun 	default:
235*4882a593Smuzhiyun 		return -EINVAL;
236*4882a593Smuzhiyun 	}
237*4882a593Smuzhiyun 	return gspca_dev->usb_err;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun static const struct v4l2_ctrl_ops sd_ctrl_ops = {
241*4882a593Smuzhiyun 	.s_ctrl = sd_s_ctrl,
242*4882a593Smuzhiyun };
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun /* this function is called at probe time */
sd_init_controls(struct gspca_dev * gspca_dev)245*4882a593Smuzhiyun static int sd_init_controls(struct gspca_dev *gspca_dev)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun 	struct sd *sd = (struct sd *) gspca_dev;
248*4882a593Smuzhiyun 	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	gspca_dev->vdev.ctrl_handler = hdl;
251*4882a593Smuzhiyun 	v4l2_ctrl_handler_init(hdl, 4);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
254*4882a593Smuzhiyun 				V4L2_CID_BRIGHTNESS,
255*4882a593Smuzhiyun 				PAC207_BRIGHTNESS_MIN, PAC207_BRIGHTNESS_MAX,
256*4882a593Smuzhiyun 				1, PAC207_BRIGHTNESS_DEFAULT);
257*4882a593Smuzhiyun 	gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
258*4882a593Smuzhiyun 				V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
259*4882a593Smuzhiyun 	gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
260*4882a593Smuzhiyun 				V4L2_CID_EXPOSURE,
261*4882a593Smuzhiyun 				PAC207_EXPOSURE_MIN, PAC207_EXPOSURE_MAX,
262*4882a593Smuzhiyun 				1, PAC207_EXPOSURE_DEFAULT);
263*4882a593Smuzhiyun 	gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
264*4882a593Smuzhiyun 				V4L2_CID_GAIN,
265*4882a593Smuzhiyun 				PAC207_GAIN_MIN, PAC207_GAIN_MAX,
266*4882a593Smuzhiyun 				1, PAC207_GAIN_DEFAULT);
267*4882a593Smuzhiyun 	if (hdl->error) {
268*4882a593Smuzhiyun 		pr_err("Could not initialize controls\n");
269*4882a593Smuzhiyun 		return hdl->error;
270*4882a593Smuzhiyun 	}
271*4882a593Smuzhiyun 	v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
272*4882a593Smuzhiyun 	return 0;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun /* -- start the camera -- */
sd_start(struct gspca_dev * gspca_dev)276*4882a593Smuzhiyun static int sd_start(struct gspca_dev *gspca_dev)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun 	struct sd *sd = (struct sd *) gspca_dev;
279*4882a593Smuzhiyun 	__u8 mode;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */
282*4882a593Smuzhiyun 	pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8);
283*4882a593Smuzhiyun 	pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8);
284*4882a593Smuzhiyun 	pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8);
285*4882a593Smuzhiyun 	pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[3], 8);
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	/* Compression Balance */
288*4882a593Smuzhiyun 	if (gspca_dev->pixfmt.width == 176)
289*4882a593Smuzhiyun 		pac207_write_reg(gspca_dev, 0x4a, 0xff);
290*4882a593Smuzhiyun 	else
291*4882a593Smuzhiyun 		pac207_write_reg(gspca_dev, 0x4a, 0x30);
292*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */
293*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x08, v4l2_ctrl_g_ctrl(sd->brightness));
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	/* PGA global gain (Bit 4-0) */
296*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x0e,
297*4882a593Smuzhiyun 		v4l2_ctrl_g_ctrl(gspca_dev->gain));
298*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x02,
299*4882a593Smuzhiyun 		v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	/* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */
302*4882a593Smuzhiyun 	if (led_invert)
303*4882a593Smuzhiyun 		mode = 0x00;
304*4882a593Smuzhiyun 	else
305*4882a593Smuzhiyun 		mode = 0x02;
306*4882a593Smuzhiyun 	if (gspca_dev->pixfmt.width == 176) {	/* 176x144 */
307*4882a593Smuzhiyun 		mode |= 0x01;
308*4882a593Smuzhiyun 		gspca_dbg(gspca_dev, D_STREAM, "pac207_start mode 176x144\n");
309*4882a593Smuzhiyun 	} else {				/* 352x288 */
310*4882a593Smuzhiyun 		gspca_dbg(gspca_dev, D_STREAM, "pac207_start mode 352x288\n");
311*4882a593Smuzhiyun 	}
312*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x41, mode);
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
315*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
316*4882a593Smuzhiyun 	msleep(10);
317*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	sd->sof_read = 0;
320*4882a593Smuzhiyun 	sd->autogain_ignore_frames = 0;
321*4882a593Smuzhiyun 	atomic_set(&sd->avg_lum, -1);
322*4882a593Smuzhiyun 	return gspca_dev->usb_err;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun 
sd_stopN(struct gspca_dev * gspca_dev)325*4882a593Smuzhiyun static void sd_stopN(struct gspca_dev *gspca_dev)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun 	u8 mode;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	/* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */
330*4882a593Smuzhiyun 	if (led_invert)
331*4882a593Smuzhiyun 		mode = 0x02;
332*4882a593Smuzhiyun 	else
333*4882a593Smuzhiyun 		mode = 0x00;
334*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */
335*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x41, mode); /* Turn off LED */
336*4882a593Smuzhiyun 	pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 
pac207_do_auto_gain(struct gspca_dev * gspca_dev)340*4882a593Smuzhiyun static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun 	struct sd *sd = (struct sd *) gspca_dev;
343*4882a593Smuzhiyun 	int avg_lum = atomic_read(&sd->avg_lum);
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	if (avg_lum == -1)
346*4882a593Smuzhiyun 		return;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	if (sd->autogain_ignore_frames > 0)
349*4882a593Smuzhiyun 		sd->autogain_ignore_frames--;
350*4882a593Smuzhiyun 	else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
351*4882a593Smuzhiyun 			90, PAC207_AUTOGAIN_DEADZONE))
352*4882a593Smuzhiyun 		sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun 
sd_pkt_scan(struct gspca_dev * gspca_dev,u8 * data,int len)355*4882a593Smuzhiyun static void sd_pkt_scan(struct gspca_dev *gspca_dev,
356*4882a593Smuzhiyun 			u8 *data,
357*4882a593Smuzhiyun 			int len)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun 	struct sd *sd = (struct sd *) gspca_dev;
360*4882a593Smuzhiyun 	unsigned char *sof;
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len);
363*4882a593Smuzhiyun 	if (sof) {
364*4882a593Smuzhiyun 		int n;
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 		/* finish decoding current frame */
367*4882a593Smuzhiyun 		n = sof - data;
368*4882a593Smuzhiyun 		if (n > sizeof pac_sof_marker)
369*4882a593Smuzhiyun 			n -= sizeof pac_sof_marker;
370*4882a593Smuzhiyun 		else
371*4882a593Smuzhiyun 			n = 0;
372*4882a593Smuzhiyun 		gspca_frame_add(gspca_dev, LAST_PACKET,
373*4882a593Smuzhiyun 				data, n);
374*4882a593Smuzhiyun 		sd->header_read = 0;
375*4882a593Smuzhiyun 		gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
376*4882a593Smuzhiyun 		len -= sof - data;
377*4882a593Smuzhiyun 		data = sof;
378*4882a593Smuzhiyun 	}
379*4882a593Smuzhiyun 	if (sd->header_read < 11) {
380*4882a593Smuzhiyun 		int needed;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 		/* get average lumination from frame header (byte 5) */
383*4882a593Smuzhiyun 		if (sd->header_read < 5) {
384*4882a593Smuzhiyun 			needed = 5 - sd->header_read;
385*4882a593Smuzhiyun 			if (len >= needed)
386*4882a593Smuzhiyun 				atomic_set(&sd->avg_lum, data[needed - 1]);
387*4882a593Smuzhiyun 		}
388*4882a593Smuzhiyun 		/* skip the rest of the header */
389*4882a593Smuzhiyun 		needed = 11 - sd->header_read;
390*4882a593Smuzhiyun 		if (len <= needed) {
391*4882a593Smuzhiyun 			sd->header_read += len;
392*4882a593Smuzhiyun 			return;
393*4882a593Smuzhiyun 		}
394*4882a593Smuzhiyun 		data += needed;
395*4882a593Smuzhiyun 		len -= needed;
396*4882a593Smuzhiyun 		sd->header_read = 11;
397*4882a593Smuzhiyun 	}
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_INPUT)
sd_int_pkt_scan(struct gspca_dev * gspca_dev,u8 * data,int len)403*4882a593Smuzhiyun static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
404*4882a593Smuzhiyun 			u8 *data,		/* interrupt packet data */
405*4882a593Smuzhiyun 			int len)		/* interrupt packet length */
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun 	int ret = -EINVAL;
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	if (len == 2 && data[0] == 0x5a && data[1] == 0x5a) {
410*4882a593Smuzhiyun 		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
411*4882a593Smuzhiyun 		input_sync(gspca_dev->input_dev);
412*4882a593Smuzhiyun 		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
413*4882a593Smuzhiyun 		input_sync(gspca_dev->input_dev);
414*4882a593Smuzhiyun 		ret = 0;
415*4882a593Smuzhiyun 	}
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	return ret;
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun #endif
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun /* sub-driver description */
422*4882a593Smuzhiyun static const struct sd_desc sd_desc = {
423*4882a593Smuzhiyun 	.name = MODULE_NAME,
424*4882a593Smuzhiyun 	.config = sd_config,
425*4882a593Smuzhiyun 	.init = sd_init,
426*4882a593Smuzhiyun 	.init_controls = sd_init_controls,
427*4882a593Smuzhiyun 	.start = sd_start,
428*4882a593Smuzhiyun 	.stopN = sd_stopN,
429*4882a593Smuzhiyun 	.dq_callback = pac207_do_auto_gain,
430*4882a593Smuzhiyun 	.pkt_scan = sd_pkt_scan,
431*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_INPUT)
432*4882a593Smuzhiyun 	.int_pkt_scan = sd_int_pkt_scan,
433*4882a593Smuzhiyun #endif
434*4882a593Smuzhiyun };
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun /* -- module initialisation -- */
437*4882a593Smuzhiyun static const struct usb_device_id device_table[] = {
438*4882a593Smuzhiyun 	{USB_DEVICE(0x041e, 0x4028)},
439*4882a593Smuzhiyun 	{USB_DEVICE(0x093a, 0x2460)},
440*4882a593Smuzhiyun 	{USB_DEVICE(0x093a, 0x2461)},
441*4882a593Smuzhiyun 	{USB_DEVICE(0x093a, 0x2463)},
442*4882a593Smuzhiyun 	{USB_DEVICE(0x093a, 0x2464)},
443*4882a593Smuzhiyun 	{USB_DEVICE(0x093a, 0x2468)},
444*4882a593Smuzhiyun 	{USB_DEVICE(0x093a, 0x2470)},
445*4882a593Smuzhiyun 	{USB_DEVICE(0x093a, 0x2471)},
446*4882a593Smuzhiyun 	{USB_DEVICE(0x093a, 0x2472)},
447*4882a593Smuzhiyun 	{USB_DEVICE(0x093a, 0x2474)},
448*4882a593Smuzhiyun 	{USB_DEVICE(0x093a, 0x2476)},
449*4882a593Smuzhiyun 	{USB_DEVICE(0x145f, 0x013a)},
450*4882a593Smuzhiyun 	{USB_DEVICE(0x2001, 0xf115)},
451*4882a593Smuzhiyun 	{}
452*4882a593Smuzhiyun };
453*4882a593Smuzhiyun MODULE_DEVICE_TABLE(usb, device_table);
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun /* -- device connect -- */
sd_probe(struct usb_interface * intf,const struct usb_device_id * id)456*4882a593Smuzhiyun static int sd_probe(struct usb_interface *intf,
457*4882a593Smuzhiyun 			const struct usb_device_id *id)
458*4882a593Smuzhiyun {
459*4882a593Smuzhiyun 	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
460*4882a593Smuzhiyun 				THIS_MODULE);
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun static struct usb_driver sd_driver = {
464*4882a593Smuzhiyun 	.name = MODULE_NAME,
465*4882a593Smuzhiyun 	.id_table = device_table,
466*4882a593Smuzhiyun 	.probe = sd_probe,
467*4882a593Smuzhiyun 	.disconnect = gspca_disconnect,
468*4882a593Smuzhiyun #ifdef CONFIG_PM
469*4882a593Smuzhiyun 	.suspend = gspca_suspend,
470*4882a593Smuzhiyun 	.resume = gspca_resume,
471*4882a593Smuzhiyun 	.reset_resume = gspca_resume,
472*4882a593Smuzhiyun #endif
473*4882a593Smuzhiyun };
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun module_usb_driver(sd_driver);
476