1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Mars-Semi MR97311A library |
4 | * Copyright (C) 2005 <bradlch@hotmail.com> |
5 | * |
6 | * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | |
11 | #define MODULE_NAME "mars" |
12 | |
13 | #include "gspca.h" |
14 | #include "jpeg.h" |
15 | |
16 | MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>" ); |
17 | MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver" ); |
18 | MODULE_LICENSE("GPL" ); |
19 | |
20 | #define QUALITY 50 |
21 | |
22 | /* specific webcam descriptor */ |
23 | struct sd { |
24 | struct gspca_dev gspca_dev; /* !! must be the first item */ |
25 | |
26 | struct v4l2_ctrl *brightness; |
27 | struct v4l2_ctrl *saturation; |
28 | struct v4l2_ctrl *sharpness; |
29 | struct v4l2_ctrl *gamma; |
30 | struct { /* illuminator control cluster */ |
31 | struct v4l2_ctrl *illum_top; |
32 | struct v4l2_ctrl *illum_bottom; |
33 | }; |
34 | u8 jpeg_hdr[JPEG_HDR_SZ]; |
35 | }; |
36 | |
37 | /* V4L2 controls supported by the driver */ |
38 | static void setbrightness(struct gspca_dev *gspca_dev, s32 val); |
39 | static void setcolors(struct gspca_dev *gspca_dev, s32 val); |
40 | static void setgamma(struct gspca_dev *gspca_dev, s32 val); |
41 | static void setsharpness(struct gspca_dev *gspca_dev, s32 val); |
42 | |
43 | static const struct v4l2_pix_format vga_mode[] = { |
44 | {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
45 | .bytesperline = 320, |
46 | .sizeimage = 320 * 240 * 3 / 8 + 590, |
47 | .colorspace = V4L2_COLORSPACE_JPEG, |
48 | .priv = 2}, |
49 | {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
50 | .bytesperline = 640, |
51 | .sizeimage = 640 * 480 * 3 / 8 + 590, |
52 | .colorspace = V4L2_COLORSPACE_JPEG, |
53 | .priv = 1}, |
54 | }; |
55 | |
56 | static const __u8 mi_data[0x20] = { |
57 | /* 01 02 03 04 05 06 07 08 */ |
58 | 0x48, 0x22, 0x01, 0x47, 0x10, 0x00, 0x00, 0x00, |
59 | /* 09 0a 0b 0c 0d 0e 0f 10 */ |
60 | 0x00, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, |
61 | /* 11 12 13 14 15 16 17 18 */ |
62 | 0x30, 0x00, 0x04, 0x00, 0x06, 0x01, 0xe2, 0x02, |
63 | /* 19 1a 1b 1c 1d 1e 1f 20 */ |
64 | 0x82, 0x00, 0x20, 0x17, 0x80, 0x08, 0x0c, 0x00 |
65 | }; |
66 | |
67 | /* write <len> bytes from gspca_dev->usb_buf */ |
68 | static void reg_w(struct gspca_dev *gspca_dev, |
69 | int len) |
70 | { |
71 | int alen, ret; |
72 | |
73 | if (gspca_dev->usb_err < 0) |
74 | return; |
75 | |
76 | ret = usb_bulk_msg(usb_dev: gspca_dev->dev, |
77 | usb_sndbulkpipe(gspca_dev->dev, 4), |
78 | data: gspca_dev->usb_buf, |
79 | len, |
80 | actual_length: &alen, |
81 | timeout: 500); /* timeout in milliseconds */ |
82 | if (ret < 0) { |
83 | pr_err("reg write [%02x] error %d\n" , |
84 | gspca_dev->usb_buf[0], ret); |
85 | gspca_dev->usb_err = ret; |
86 | } |
87 | } |
88 | |
89 | static void mi_w(struct gspca_dev *gspca_dev, |
90 | u8 addr, |
91 | u8 value) |
92 | { |
93 | gspca_dev->usb_buf[0] = 0x1f; |
94 | gspca_dev->usb_buf[1] = 0; /* control byte */ |
95 | gspca_dev->usb_buf[2] = addr; |
96 | gspca_dev->usb_buf[3] = value; |
97 | |
98 | reg_w(gspca_dev, len: 4); |
99 | } |
100 | |
101 | static void setbrightness(struct gspca_dev *gspca_dev, s32 val) |
102 | { |
103 | gspca_dev->usb_buf[0] = 0x61; |
104 | gspca_dev->usb_buf[1] = val; |
105 | reg_w(gspca_dev, len: 2); |
106 | } |
107 | |
108 | static void setcolors(struct gspca_dev *gspca_dev, s32 val) |
109 | { |
110 | gspca_dev->usb_buf[0] = 0x5f; |
111 | gspca_dev->usb_buf[1] = val << 3; |
112 | gspca_dev->usb_buf[2] = ((val >> 2) & 0xf8) | 0x04; |
113 | reg_w(gspca_dev, len: 3); |
114 | } |
115 | |
116 | static void setgamma(struct gspca_dev *gspca_dev, s32 val) |
117 | { |
118 | gspca_dev->usb_buf[0] = 0x06; |
119 | gspca_dev->usb_buf[1] = val * 0x40; |
120 | reg_w(gspca_dev, len: 2); |
121 | } |
122 | |
123 | static void setsharpness(struct gspca_dev *gspca_dev, s32 val) |
124 | { |
125 | gspca_dev->usb_buf[0] = 0x67; |
126 | gspca_dev->usb_buf[1] = val * 4 + 3; |
127 | reg_w(gspca_dev, len: 2); |
128 | } |
129 | |
130 | static void setilluminators(struct gspca_dev *gspca_dev, bool top, bool bottom) |
131 | { |
132 | /* both are off if not streaming */ |
133 | gspca_dev->usb_buf[0] = 0x22; |
134 | if (top) |
135 | gspca_dev->usb_buf[1] = 0x76; |
136 | else if (bottom) |
137 | gspca_dev->usb_buf[1] = 0x7a; |
138 | else |
139 | gspca_dev->usb_buf[1] = 0x7e; |
140 | reg_w(gspca_dev, len: 2); |
141 | } |
142 | |
143 | static int mars_s_ctrl(struct v4l2_ctrl *ctrl) |
144 | { |
145 | struct gspca_dev *gspca_dev = |
146 | container_of(ctrl->handler, struct gspca_dev, ctrl_handler); |
147 | struct sd *sd = (struct sd *)gspca_dev; |
148 | |
149 | gspca_dev->usb_err = 0; |
150 | |
151 | if (ctrl->id == V4L2_CID_ILLUMINATORS_1) { |
152 | /* only one can be on at a time */ |
153 | if (ctrl->is_new && ctrl->val) |
154 | sd->illum_bottom->val = 0; |
155 | if (sd->illum_bottom->is_new && sd->illum_bottom->val) |
156 | sd->illum_top->val = 0; |
157 | } |
158 | |
159 | if (!gspca_dev->streaming) |
160 | return 0; |
161 | |
162 | switch (ctrl->id) { |
163 | case V4L2_CID_BRIGHTNESS: |
164 | setbrightness(gspca_dev, val: ctrl->val); |
165 | break; |
166 | case V4L2_CID_SATURATION: |
167 | setcolors(gspca_dev, val: ctrl->val); |
168 | break; |
169 | case V4L2_CID_GAMMA: |
170 | setgamma(gspca_dev, val: ctrl->val); |
171 | break; |
172 | case V4L2_CID_ILLUMINATORS_1: |
173 | setilluminators(gspca_dev, top: sd->illum_top->val, |
174 | bottom: sd->illum_bottom->val); |
175 | break; |
176 | case V4L2_CID_SHARPNESS: |
177 | setsharpness(gspca_dev, val: ctrl->val); |
178 | break; |
179 | default: |
180 | return -EINVAL; |
181 | } |
182 | return gspca_dev->usb_err; |
183 | } |
184 | |
185 | static const struct v4l2_ctrl_ops mars_ctrl_ops = { |
186 | .s_ctrl = mars_s_ctrl, |
187 | }; |
188 | |
189 | /* this function is called at probe time */ |
190 | static int sd_init_controls(struct gspca_dev *gspca_dev) |
191 | { |
192 | struct sd *sd = (struct sd *) gspca_dev; |
193 | struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; |
194 | |
195 | gspca_dev->vdev.ctrl_handler = hdl; |
196 | v4l2_ctrl_handler_init(hdl, 6); |
197 | sd->brightness = v4l2_ctrl_new_std(hdl, ops: &mars_ctrl_ops, |
198 | V4L2_CID_BRIGHTNESS, min: 0, max: 30, step: 1, def: 15); |
199 | sd->saturation = v4l2_ctrl_new_std(hdl, ops: &mars_ctrl_ops, |
200 | V4L2_CID_SATURATION, min: 0, max: 255, step: 1, def: 200); |
201 | sd->gamma = v4l2_ctrl_new_std(hdl, ops: &mars_ctrl_ops, |
202 | V4L2_CID_GAMMA, min: 0, max: 3, step: 1, def: 1); |
203 | sd->sharpness = v4l2_ctrl_new_std(hdl, ops: &mars_ctrl_ops, |
204 | V4L2_CID_SHARPNESS, min: 0, max: 2, step: 1, def: 1); |
205 | sd->illum_top = v4l2_ctrl_new_std(hdl, ops: &mars_ctrl_ops, |
206 | V4L2_CID_ILLUMINATORS_1, min: 0, max: 1, step: 1, def: 0); |
207 | sd->illum_top->flags |= V4L2_CTRL_FLAG_UPDATE; |
208 | sd->illum_bottom = v4l2_ctrl_new_std(hdl, ops: &mars_ctrl_ops, |
209 | V4L2_CID_ILLUMINATORS_2, min: 0, max: 1, step: 1, def: 0); |
210 | sd->illum_bottom->flags |= V4L2_CTRL_FLAG_UPDATE; |
211 | if (hdl->error) { |
212 | pr_err("Could not initialize controls\n" ); |
213 | return hdl->error; |
214 | } |
215 | v4l2_ctrl_cluster(ncontrols: 2, controls: &sd->illum_top); |
216 | return 0; |
217 | } |
218 | |
219 | /* this function is called at probe time */ |
220 | static int sd_config(struct gspca_dev *gspca_dev, |
221 | const struct usb_device_id *id) |
222 | { |
223 | struct cam *cam; |
224 | |
225 | cam = &gspca_dev->cam; |
226 | cam->cam_mode = vga_mode; |
227 | cam->nmodes = ARRAY_SIZE(vga_mode); |
228 | return 0; |
229 | } |
230 | |
231 | /* this function is called at probe and resume time */ |
232 | static int sd_init(struct gspca_dev *gspca_dev) |
233 | { |
234 | return 0; |
235 | } |
236 | |
237 | static int sd_start(struct gspca_dev *gspca_dev) |
238 | { |
239 | struct sd *sd = (struct sd *) gspca_dev; |
240 | u8 *data; |
241 | int i; |
242 | |
243 | /* create the JPEG header */ |
244 | jpeg_define(jpeg_hdr: sd->jpeg_hdr, height: gspca_dev->pixfmt.height, |
245 | width: gspca_dev->pixfmt.width, |
246 | samplesY: 0x21); /* JPEG 422 */ |
247 | jpeg_set_qual(jpeg_hdr: sd->jpeg_hdr, QUALITY); |
248 | |
249 | data = gspca_dev->usb_buf; |
250 | |
251 | data[0] = 0x01; /* address */ |
252 | data[1] = 0x01; |
253 | reg_w(gspca_dev, len: 2); |
254 | |
255 | /* |
256 | Initialize the MR97113 chip register |
257 | */ |
258 | data[0] = 0x00; /* address */ |
259 | data[1] = 0x0c | 0x01; /* reg 0 */ |
260 | data[2] = 0x01; /* reg 1 */ |
261 | data[3] = gspca_dev->pixfmt.width / 8; /* h_size , reg 2 */ |
262 | data[4] = gspca_dev->pixfmt.height / 8; /* v_size , reg 3 */ |
263 | data[5] = 0x30; /* reg 4, MI, PAS5101 : |
264 | * 0x30 for 24mhz , 0x28 for 12mhz */ |
265 | data[6] = 0x02; /* reg 5, H start - was 0x04 */ |
266 | data[7] = v4l2_ctrl_g_ctrl(ctrl: sd->gamma) * 0x40; /* reg 0x06: gamma */ |
267 | data[8] = 0x01; /* reg 7, V start - was 0x03 */ |
268 | /* if (h_size == 320 ) */ |
269 | /* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */ |
270 | /* else */ |
271 | data[9] = 0x52; /* reg 8, 24MHz, no scale down */ |
272 | /*jfm: from win trace*/ |
273 | data[10] = 0x18; |
274 | |
275 | reg_w(gspca_dev, len: 11); |
276 | |
277 | data[0] = 0x23; /* address */ |
278 | data[1] = 0x09; /* reg 35, append frame header */ |
279 | |
280 | reg_w(gspca_dev, len: 2); |
281 | |
282 | data[0] = 0x3c; /* address */ |
283 | /* if (gspca_dev->width == 1280) */ |
284 | /* data[1] = 200; * reg 60, pc-cam frame size |
285 | * (unit: 4KB) 800KB */ |
286 | /* else */ |
287 | data[1] = 50; /* 50 reg 60, pc-cam frame size |
288 | * (unit: 4KB) 200KB */ |
289 | reg_w(gspca_dev, len: 2); |
290 | |
291 | /* auto dark-gain */ |
292 | data[0] = 0x5e; /* address */ |
293 | data[1] = 0; /* reg 94, Y Gain (auto) */ |
294 | /*jfm: from win trace*/ |
295 | /* reg 0x5f/0x60 (LE) = saturation */ |
296 | /* h (60): xxxx x100 |
297 | * l (5f): xxxx x000 */ |
298 | data[2] = v4l2_ctrl_g_ctrl(ctrl: sd->saturation) << 3; |
299 | data[3] = ((v4l2_ctrl_g_ctrl(ctrl: sd->saturation) >> 2) & 0xf8) | 0x04; |
300 | data[4] = v4l2_ctrl_g_ctrl(ctrl: sd->brightness); /* reg 0x61 = brightness */ |
301 | data[5] = 0x00; |
302 | |
303 | reg_w(gspca_dev, len: 6); |
304 | |
305 | data[0] = 0x67; |
306 | /*jfm: from win trace*/ |
307 | data[1] = v4l2_ctrl_g_ctrl(ctrl: sd->sharpness) * 4 + 3; |
308 | data[2] = 0x14; |
309 | reg_w(gspca_dev, len: 3); |
310 | |
311 | data[0] = 0x69; |
312 | data[1] = 0x2f; |
313 | data[2] = 0x28; |
314 | data[3] = 0x42; |
315 | reg_w(gspca_dev, len: 4); |
316 | |
317 | data[0] = 0x63; |
318 | data[1] = 0x07; |
319 | reg_w(gspca_dev, len: 2); |
320 | /*jfm: win trace - many writes here to reg 0x64*/ |
321 | |
322 | /* initialize the MI sensor */ |
323 | for (i = 0; i < sizeof mi_data; i++) |
324 | mi_w(gspca_dev, addr: i + 1, value: mi_data[i]); |
325 | |
326 | data[0] = 0x00; |
327 | data[1] = 0x4d; /* ISOC transferring enable... */ |
328 | reg_w(gspca_dev, len: 2); |
329 | |
330 | setilluminators(gspca_dev, top: v4l2_ctrl_g_ctrl(ctrl: sd->illum_top), |
331 | bottom: v4l2_ctrl_g_ctrl(ctrl: sd->illum_bottom)); |
332 | |
333 | return gspca_dev->usb_err; |
334 | } |
335 | |
336 | static void sd_stopN(struct gspca_dev *gspca_dev) |
337 | { |
338 | struct sd *sd = (struct sd *) gspca_dev; |
339 | |
340 | if (v4l2_ctrl_g_ctrl(ctrl: sd->illum_top) || |
341 | v4l2_ctrl_g_ctrl(ctrl: sd->illum_bottom)) { |
342 | setilluminators(gspca_dev, top: false, bottom: false); |
343 | msleep(msecs: 20); |
344 | } |
345 | |
346 | gspca_dev->usb_buf[0] = 1; |
347 | gspca_dev->usb_buf[1] = 0; |
348 | reg_w(gspca_dev, len: 2); |
349 | } |
350 | |
351 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, |
352 | u8 *data, /* isoc packet */ |
353 | int len) /* iso packet length */ |
354 | { |
355 | struct sd *sd = (struct sd *) gspca_dev; |
356 | int p; |
357 | |
358 | if (len < 6) { |
359 | /* gspca_dev->last_packet_type = DISCARD_PACKET; */ |
360 | return; |
361 | } |
362 | for (p = 0; p < len - 6; p++) { |
363 | if (data[0 + p] == 0xff |
364 | && data[1 + p] == 0xff |
365 | && data[2 + p] == 0x00 |
366 | && data[3 + p] == 0xff |
367 | && data[4 + p] == 0x96) { |
368 | if (data[5 + p] == 0x64 |
369 | || data[5 + p] == 0x65 |
370 | || data[5 + p] == 0x66 |
371 | || data[5 + p] == 0x67) { |
372 | gspca_dbg(gspca_dev, D_PACK, "sof offset: %d len: %d\n" , |
373 | p, len); |
374 | gspca_frame_add(gspca_dev, packet_type: LAST_PACKET, |
375 | data, len: p); |
376 | |
377 | /* put the JPEG header */ |
378 | gspca_frame_add(gspca_dev, packet_type: FIRST_PACKET, |
379 | data: sd->jpeg_hdr, JPEG_HDR_SZ); |
380 | data += p + 16; |
381 | len -= p + 16; |
382 | break; |
383 | } |
384 | } |
385 | } |
386 | gspca_frame_add(gspca_dev, packet_type: INTER_PACKET, data, len); |
387 | } |
388 | |
389 | /* sub-driver description */ |
390 | static const struct sd_desc sd_desc = { |
391 | .name = MODULE_NAME, |
392 | .config = sd_config, |
393 | .init = sd_init, |
394 | .init_controls = sd_init_controls, |
395 | .start = sd_start, |
396 | .stopN = sd_stopN, |
397 | .pkt_scan = sd_pkt_scan, |
398 | }; |
399 | |
400 | /* -- module initialisation -- */ |
401 | static const struct usb_device_id device_table[] = { |
402 | {USB_DEVICE(0x093a, 0x050f)}, |
403 | {} |
404 | }; |
405 | MODULE_DEVICE_TABLE(usb, device_table); |
406 | |
407 | /* -- device connect -- */ |
408 | static int sd_probe(struct usb_interface *intf, |
409 | const struct usb_device_id *id) |
410 | { |
411 | return gspca_dev_probe(intf, id, sd_desc: &sd_desc, dev_size: sizeof(struct sd), |
412 | THIS_MODULE); |
413 | } |
414 | |
415 | static struct usb_driver sd_driver = { |
416 | .name = MODULE_NAME, |
417 | .id_table = device_table, |
418 | .probe = sd_probe, |
419 | .disconnect = gspca_disconnect, |
420 | #ifdef CONFIG_PM |
421 | .suspend = gspca_suspend, |
422 | .resume = gspca_resume, |
423 | .reset_resume = gspca_resume, |
424 | #endif |
425 | }; |
426 | |
427 | module_usb_driver(sd_driver); |
428 | |