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