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