1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Syntek DV4000 (STK014) subdriver |
4 | * |
5 | * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) |
6 | */ |
7 | |
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
9 | |
10 | #define MODULE_NAME "stk014" |
11 | |
12 | #include "gspca.h" |
13 | #include "jpeg.h" |
14 | |
15 | MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>" ); |
16 | MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver" ); |
17 | MODULE_LICENSE("GPL" ); |
18 | |
19 | #define QUALITY 50 |
20 | |
21 | /* specific webcam descriptor */ |
22 | struct sd { |
23 | struct gspca_dev gspca_dev; /* !! must be the first item */ |
24 | u8 jpeg_hdr[JPEG_HDR_SZ]; |
25 | }; |
26 | |
27 | static const struct v4l2_pix_format vga_mode[] = { |
28 | {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
29 | .bytesperline = 320, |
30 | .sizeimage = 320 * 240 * 3 / 8 + 590, |
31 | .colorspace = V4L2_COLORSPACE_JPEG, |
32 | .priv = 1}, |
33 | {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
34 | .bytesperline = 640, |
35 | .sizeimage = 640 * 480 * 3 / 8 + 590, |
36 | .colorspace = V4L2_COLORSPACE_JPEG, |
37 | .priv = 0}, |
38 | }; |
39 | |
40 | /* -- read a register -- */ |
41 | static u8 reg_r(struct gspca_dev *gspca_dev, |
42 | __u16 index) |
43 | { |
44 | struct usb_device *dev = gspca_dev->dev; |
45 | int ret; |
46 | |
47 | if (gspca_dev->usb_err < 0) |
48 | return 0; |
49 | ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), |
50 | request: 0x00, |
51 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
52 | value: 0x00, |
53 | index, |
54 | data: gspca_dev->usb_buf, size: 1, |
55 | timeout: 500); |
56 | if (ret < 0) { |
57 | pr_err("reg_r err %d\n" , ret); |
58 | gspca_dev->usb_err = ret; |
59 | return 0; |
60 | } |
61 | return gspca_dev->usb_buf[0]; |
62 | } |
63 | |
64 | /* -- write a register -- */ |
65 | static void reg_w(struct gspca_dev *gspca_dev, |
66 | __u16 index, __u16 value) |
67 | { |
68 | struct usb_device *dev = gspca_dev->dev; |
69 | int ret; |
70 | |
71 | if (gspca_dev->usb_err < 0) |
72 | return; |
73 | ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), |
74 | request: 0x01, |
75 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
76 | value, |
77 | index, |
78 | NULL, |
79 | size: 0, |
80 | timeout: 500); |
81 | if (ret < 0) { |
82 | pr_err("reg_w err %d\n" , ret); |
83 | gspca_dev->usb_err = ret; |
84 | } |
85 | } |
86 | |
87 | /* -- get a bulk value (4 bytes) -- */ |
88 | static void rcv_val(struct gspca_dev *gspca_dev, |
89 | int ads) |
90 | { |
91 | struct usb_device *dev = gspca_dev->dev; |
92 | int alen, ret; |
93 | |
94 | reg_w(gspca_dev, index: 0x634, value: (ads >> 16) & 0xff); |
95 | reg_w(gspca_dev, index: 0x635, value: (ads >> 8) & 0xff); |
96 | reg_w(gspca_dev, index: 0x636, value: ads & 0xff); |
97 | reg_w(gspca_dev, index: 0x637, value: 0); |
98 | reg_w(gspca_dev, index: 0x638, value: 4); /* len & 0xff */ |
99 | reg_w(gspca_dev, index: 0x639, value: 0); /* len >> 8 */ |
100 | reg_w(gspca_dev, index: 0x63a, value: 0); |
101 | reg_w(gspca_dev, index: 0x63b, value: 0); |
102 | reg_w(gspca_dev, index: 0x630, value: 5); |
103 | if (gspca_dev->usb_err < 0) |
104 | return; |
105 | ret = usb_bulk_msg(usb_dev: dev, |
106 | usb_rcvbulkpipe(dev, 0x05), |
107 | data: gspca_dev->usb_buf, |
108 | len: 4, /* length */ |
109 | actual_length: &alen, |
110 | timeout: 500); /* timeout in milliseconds */ |
111 | if (ret < 0) { |
112 | pr_err("rcv_val err %d\n" , ret); |
113 | gspca_dev->usb_err = ret; |
114 | } |
115 | } |
116 | |
117 | /* -- send a bulk value -- */ |
118 | static void snd_val(struct gspca_dev *gspca_dev, |
119 | int ads, |
120 | unsigned int val) |
121 | { |
122 | struct usb_device *dev = gspca_dev->dev; |
123 | int alen, ret; |
124 | __u8 seq = 0; |
125 | |
126 | if (ads == 0x003f08) { |
127 | reg_r(gspca_dev, index: 0x0704); |
128 | seq = reg_r(gspca_dev, index: 0x0705); |
129 | reg_r(gspca_dev, index: 0x0650); |
130 | reg_w(gspca_dev, index: 0x654, value: seq); |
131 | } else { |
132 | reg_w(gspca_dev, index: 0x654, value: (ads >> 16) & 0xff); |
133 | } |
134 | reg_w(gspca_dev, index: 0x655, value: (ads >> 8) & 0xff); |
135 | reg_w(gspca_dev, index: 0x656, value: ads & 0xff); |
136 | reg_w(gspca_dev, index: 0x657, value: 0); |
137 | reg_w(gspca_dev, index: 0x658, value: 0x04); /* size */ |
138 | reg_w(gspca_dev, index: 0x659, value: 0); |
139 | reg_w(gspca_dev, index: 0x65a, value: 0); |
140 | reg_w(gspca_dev, index: 0x65b, value: 0); |
141 | reg_w(gspca_dev, index: 0x650, value: 5); |
142 | if (gspca_dev->usb_err < 0) |
143 | return; |
144 | gspca_dev->usb_buf[0] = val >> 24; |
145 | gspca_dev->usb_buf[1] = val >> 16; |
146 | gspca_dev->usb_buf[2] = val >> 8; |
147 | gspca_dev->usb_buf[3] = val; |
148 | ret = usb_bulk_msg(usb_dev: dev, |
149 | usb_sndbulkpipe(dev, 6), |
150 | data: gspca_dev->usb_buf, |
151 | len: 4, |
152 | actual_length: &alen, |
153 | timeout: 500); /* timeout in milliseconds */ |
154 | if (ret < 0) { |
155 | pr_err("snd_val err %d\n" , ret); |
156 | gspca_dev->usb_err = ret; |
157 | } else { |
158 | if (ads == 0x003f08) { |
159 | seq += 4; |
160 | seq &= 0x3f; |
161 | reg_w(gspca_dev, index: 0x705, value: seq); |
162 | } |
163 | } |
164 | } |
165 | |
166 | /* set a camera parameter */ |
167 | static void set_par(struct gspca_dev *gspca_dev, |
168 | int parval) |
169 | { |
170 | snd_val(gspca_dev, ads: 0x003f08, val: parval); |
171 | } |
172 | |
173 | static void setbrightness(struct gspca_dev *gspca_dev, s32 val) |
174 | { |
175 | int parval; |
176 | |
177 | parval = 0x06000000 /* whiteness */ |
178 | + (val << 16); |
179 | set_par(gspca_dev, parval); |
180 | } |
181 | |
182 | static void setcontrast(struct gspca_dev *gspca_dev, s32 val) |
183 | { |
184 | int parval; |
185 | |
186 | parval = 0x07000000 /* contrast */ |
187 | + (val << 16); |
188 | set_par(gspca_dev, parval); |
189 | } |
190 | |
191 | static void setcolors(struct gspca_dev *gspca_dev, s32 val) |
192 | { |
193 | int parval; |
194 | |
195 | parval = 0x08000000 /* saturation */ |
196 | + (val << 16); |
197 | set_par(gspca_dev, parval); |
198 | } |
199 | |
200 | static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) |
201 | { |
202 | set_par(gspca_dev, parval: val == 1 |
203 | ? 0x33640000 /* 50 Hz */ |
204 | : 0x33780000); /* 60 Hz */ |
205 | } |
206 | |
207 | /* this function is called at probe time */ |
208 | static int sd_config(struct gspca_dev *gspca_dev, |
209 | const struct usb_device_id *id) |
210 | { |
211 | gspca_dev->cam.cam_mode = vga_mode; |
212 | gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); |
213 | return 0; |
214 | } |
215 | |
216 | /* this function is called at probe and resume time */ |
217 | static int sd_init(struct gspca_dev *gspca_dev) |
218 | { |
219 | u8 ret; |
220 | |
221 | /* check if the device responds */ |
222 | usb_set_interface(dev: gspca_dev->dev, ifnum: gspca_dev->iface, alternate: 1); |
223 | ret = reg_r(gspca_dev, index: 0x0740); |
224 | if (gspca_dev->usb_err >= 0) { |
225 | if (ret != 0xff) { |
226 | pr_err("init reg: 0x%02x\n" , ret); |
227 | gspca_dev->usb_err = -EIO; |
228 | } |
229 | } |
230 | return gspca_dev->usb_err; |
231 | } |
232 | |
233 | /* -- start the camera -- */ |
234 | static int sd_start(struct gspca_dev *gspca_dev) |
235 | { |
236 | struct sd *sd = (struct sd *) gspca_dev; |
237 | int ret, value; |
238 | |
239 | /* create the JPEG header */ |
240 | jpeg_define(jpeg_hdr: sd->jpeg_hdr, height: gspca_dev->pixfmt.height, |
241 | width: gspca_dev->pixfmt.width, |
242 | samplesY: 0x22); /* JPEG 411 */ |
243 | jpeg_set_qual(jpeg_hdr: sd->jpeg_hdr, QUALITY); |
244 | |
245 | /* work on alternate 1 */ |
246 | usb_set_interface(dev: gspca_dev->dev, ifnum: gspca_dev->iface, alternate: 1); |
247 | |
248 | set_par(gspca_dev, parval: 0x10000000); |
249 | set_par(gspca_dev, parval: 0x00000000); |
250 | set_par(gspca_dev, parval: 0x8002e001); |
251 | set_par(gspca_dev, parval: 0x14000000); |
252 | if (gspca_dev->pixfmt.width > 320) |
253 | value = 0x8002e001; /* 640x480 */ |
254 | else |
255 | value = 0x4001f000; /* 320x240 */ |
256 | set_par(gspca_dev, parval: value); |
257 | ret = usb_set_interface(dev: gspca_dev->dev, |
258 | ifnum: gspca_dev->iface, |
259 | alternate: gspca_dev->alt); |
260 | if (ret < 0) { |
261 | pr_err("set intf %d %d failed\n" , |
262 | gspca_dev->iface, gspca_dev->alt); |
263 | gspca_dev->usb_err = ret; |
264 | goto out; |
265 | } |
266 | reg_r(gspca_dev, index: 0x0630); |
267 | rcv_val(gspca_dev, ads: 0x000020); /* << (value ff ff ff ff) */ |
268 | reg_r(gspca_dev, index: 0x0650); |
269 | snd_val(gspca_dev, ads: 0x000020, val: 0xffffffff); |
270 | reg_w(gspca_dev, index: 0x0620, value: 0); |
271 | reg_w(gspca_dev, index: 0x0630, value: 0); |
272 | reg_w(gspca_dev, index: 0x0640, value: 0); |
273 | reg_w(gspca_dev, index: 0x0650, value: 0); |
274 | reg_w(gspca_dev, index: 0x0660, value: 0); |
275 | set_par(gspca_dev, parval: 0x09800000); /* Red ? */ |
276 | set_par(gspca_dev, parval: 0x0a800000); /* Green ? */ |
277 | set_par(gspca_dev, parval: 0x0b800000); /* Blue ? */ |
278 | set_par(gspca_dev, parval: 0x0d030000); /* Gamma ? */ |
279 | |
280 | /* start the video flow */ |
281 | set_par(gspca_dev, parval: 0x01000000); |
282 | set_par(gspca_dev, parval: 0x01000000); |
283 | if (gspca_dev->usb_err >= 0) |
284 | gspca_dbg(gspca_dev, D_STREAM, "camera started alt: 0x%02x\n" , |
285 | gspca_dev->alt); |
286 | out: |
287 | return gspca_dev->usb_err; |
288 | } |
289 | |
290 | static void sd_stopN(struct gspca_dev *gspca_dev) |
291 | { |
292 | struct usb_device *dev = gspca_dev->dev; |
293 | |
294 | set_par(gspca_dev, parval: 0x02000000); |
295 | set_par(gspca_dev, parval: 0x02000000); |
296 | usb_set_interface(dev, ifnum: gspca_dev->iface, alternate: 1); |
297 | reg_r(gspca_dev, index: 0x0630); |
298 | rcv_val(gspca_dev, ads: 0x000020); /* << (value ff ff ff ff) */ |
299 | reg_r(gspca_dev, index: 0x0650); |
300 | snd_val(gspca_dev, ads: 0x000020, val: 0xffffffff); |
301 | reg_w(gspca_dev, index: 0x0620, value: 0); |
302 | reg_w(gspca_dev, index: 0x0630, value: 0); |
303 | reg_w(gspca_dev, index: 0x0640, value: 0); |
304 | reg_w(gspca_dev, index: 0x0650, value: 0); |
305 | reg_w(gspca_dev, index: 0x0660, value: 0); |
306 | gspca_dbg(gspca_dev, D_STREAM, "camera stopped\n" ); |
307 | } |
308 | |
309 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, |
310 | u8 *data, /* isoc packet */ |
311 | int len) /* iso packet length */ |
312 | { |
313 | struct sd *sd = (struct sd *) gspca_dev; |
314 | static unsigned char ffd9[] = {0xff, 0xd9}; |
315 | |
316 | /* a frame starts with: |
317 | * - 0xff 0xfe |
318 | * - 0x08 0x00 - length (little endian ?!) |
319 | * - 4 bytes = size of whole frame (BE - including header) |
320 | * - 0x00 0x0c |
321 | * - 0xff 0xd8 |
322 | * - .. JPEG image with escape sequences (ff 00) |
323 | * (without ending - ff d9) |
324 | */ |
325 | if (data[0] == 0xff && data[1] == 0xfe) { |
326 | gspca_frame_add(gspca_dev, packet_type: LAST_PACKET, |
327 | data: ffd9, len: 2); |
328 | |
329 | /* put the JPEG 411 header */ |
330 | gspca_frame_add(gspca_dev, packet_type: FIRST_PACKET, |
331 | data: sd->jpeg_hdr, JPEG_HDR_SZ); |
332 | |
333 | /* beginning of the frame */ |
334 | #define STKHDRSZ 12 |
335 | data += STKHDRSZ; |
336 | len -= STKHDRSZ; |
337 | } |
338 | gspca_frame_add(gspca_dev, packet_type: INTER_PACKET, data, len); |
339 | } |
340 | |
341 | static int sd_s_ctrl(struct v4l2_ctrl *ctrl) |
342 | { |
343 | struct gspca_dev *gspca_dev = |
344 | container_of(ctrl->handler, struct gspca_dev, ctrl_handler); |
345 | |
346 | gspca_dev->usb_err = 0; |
347 | |
348 | if (!gspca_dev->streaming) |
349 | return 0; |
350 | |
351 | switch (ctrl->id) { |
352 | case V4L2_CID_BRIGHTNESS: |
353 | setbrightness(gspca_dev, val: ctrl->val); |
354 | break; |
355 | case V4L2_CID_CONTRAST: |
356 | setcontrast(gspca_dev, val: ctrl->val); |
357 | break; |
358 | case V4L2_CID_SATURATION: |
359 | setcolors(gspca_dev, val: ctrl->val); |
360 | break; |
361 | case V4L2_CID_POWER_LINE_FREQUENCY: |
362 | setlightfreq(gspca_dev, val: ctrl->val); |
363 | break; |
364 | } |
365 | return gspca_dev->usb_err; |
366 | } |
367 | |
368 | static const struct v4l2_ctrl_ops sd_ctrl_ops = { |
369 | .s_ctrl = sd_s_ctrl, |
370 | }; |
371 | |
372 | static int sd_init_controls(struct gspca_dev *gspca_dev) |
373 | { |
374 | struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; |
375 | |
376 | gspca_dev->vdev.ctrl_handler = hdl; |
377 | v4l2_ctrl_handler_init(hdl, 4); |
378 | v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops, |
379 | V4L2_CID_BRIGHTNESS, min: 0, max: 255, step: 1, def: 127); |
380 | v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops, |
381 | V4L2_CID_CONTRAST, min: 0, max: 255, step: 1, def: 127); |
382 | v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops, |
383 | V4L2_CID_SATURATION, min: 0, max: 255, step: 1, def: 127); |
384 | v4l2_ctrl_new_std_menu(hdl, ops: &sd_ctrl_ops, |
385 | V4L2_CID_POWER_LINE_FREQUENCY, |
386 | max: V4L2_CID_POWER_LINE_FREQUENCY_60HZ, mask: 1, |
387 | def: V4L2_CID_POWER_LINE_FREQUENCY_50HZ); |
388 | |
389 | if (hdl->error) { |
390 | pr_err("Could not initialize controls\n" ); |
391 | return hdl->error; |
392 | } |
393 | return 0; |
394 | } |
395 | |
396 | /* sub-driver description */ |
397 | static const struct sd_desc sd_desc = { |
398 | .name = MODULE_NAME, |
399 | .config = sd_config, |
400 | .init = sd_init, |
401 | .init_controls = sd_init_controls, |
402 | .start = sd_start, |
403 | .stopN = sd_stopN, |
404 | .pkt_scan = sd_pkt_scan, |
405 | }; |
406 | |
407 | /* -- module initialisation -- */ |
408 | static const struct usb_device_id device_table[] = { |
409 | {USB_DEVICE(0x05e1, 0x0893)}, |
410 | {} |
411 | }; |
412 | MODULE_DEVICE_TABLE(usb, device_table); |
413 | |
414 | /* -- device connect -- */ |
415 | static int sd_probe(struct usb_interface *intf, |
416 | const struct usb_device_id *id) |
417 | { |
418 | return gspca_dev_probe(intf, id, sd_desc: &sd_desc, dev_size: sizeof(struct sd), |
419 | THIS_MODULE); |
420 | } |
421 | |
422 | static struct usb_driver sd_driver = { |
423 | .name = MODULE_NAME, |
424 | .id_table = device_table, |
425 | .probe = sd_probe, |
426 | .disconnect = gspca_disconnect, |
427 | #ifdef CONFIG_PM |
428 | .suspend = gspca_suspend, |
429 | .resume = gspca_resume, |
430 | .reset_resume = gspca_resume, |
431 | #endif |
432 | }; |
433 | |
434 | module_usb_driver(sd_driver); |
435 | |