diff options
author | Kyle Guinn <elyk03@gmail.com> | 2009-01-16 03:36:14 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-03-30 11:42:32 -0400 |
commit | d661e62205498ce6518b9859bc30444e59737d8b (patch) | |
tree | a73119ae42513fc6a2b60ff1d3dad33be078daa5 /drivers/media/video/gspca/mr97310a.c | |
parent | 553d0d839b93550780d1b46e6bcd01a3c5c5883e (diff) |
V4L/DVB (10366): gspca - mr97310a: New subdriver.
This patch adds support for USB webcams based on the MR97310A chip. It was
tested with an Aiptek PenCam VGA+ webcam.
Signed-off-by: Kyle Guinn <elyk03@gmail.com>
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/gspca/mr97310a.c')
-rw-r--r-- | drivers/media/video/gspca/mr97310a.c | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c new file mode 100644 index 000000000000..24180bf0cdab --- /dev/null +++ b/drivers/media/video/gspca/mr97310a.c | |||
@@ -0,0 +1,378 @@ | |||
1 | /* | ||
2 | * Mars MR97310A library | ||
3 | * | ||
4 | * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #define MODULE_NAME "mr97310a" | ||
22 | |||
23 | #include "gspca.h" | ||
24 | |||
25 | MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>"); | ||
26 | MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver"); | ||
27 | MODULE_LICENSE("GPL"); | ||
28 | |||
29 | /* specific webcam descriptor */ | ||
30 | struct sd { | ||
31 | struct gspca_dev gspca_dev; /* !! must be the first item */ | ||
32 | |||
33 | u8 sof_read; | ||
34 | u8 header_read; | ||
35 | }; | ||
36 | |||
37 | /* V4L2 controls supported by the driver */ | ||
38 | static struct ctrl sd_ctrls[] = { | ||
39 | }; | ||
40 | |||
41 | static const struct v4l2_pix_format vga_mode[] = { | ||
42 | {160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, | ||
43 | .bytesperline = 160, | ||
44 | .sizeimage = 160 * 120, | ||
45 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
46 | .priv = 4}, | ||
47 | {176, 144, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, | ||
48 | .bytesperline = 176, | ||
49 | .sizeimage = 176 * 144, | ||
50 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
51 | .priv = 3}, | ||
52 | {320, 240, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, | ||
53 | .bytesperline = 320, | ||
54 | .sizeimage = 320 * 240, | ||
55 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
56 | .priv = 2}, | ||
57 | {352, 288, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, | ||
58 | .bytesperline = 352, | ||
59 | .sizeimage = 352 * 288, | ||
60 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
61 | .priv = 1}, | ||
62 | {640, 480, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, | ||
63 | .bytesperline = 640, | ||
64 | .sizeimage = 640 * 480, | ||
65 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
66 | .priv = 0}, | ||
67 | }; | ||
68 | |||
69 | /* the bytes to write are in gspca_dev->usb_buf */ | ||
70 | static int reg_w(struct gspca_dev *gspca_dev, int len) | ||
71 | { | ||
72 | int rc; | ||
73 | |||
74 | rc = usb_bulk_msg(gspca_dev->dev, | ||
75 | usb_sndbulkpipe(gspca_dev->dev, 4), | ||
76 | gspca_dev->usb_buf, len, 0, 500); | ||
77 | if (rc < 0) | ||
78 | PDEBUG(D_ERR, "reg write [%02x] error %d", | ||
79 | gspca_dev->usb_buf[0], rc); | ||
80 | return rc; | ||
81 | } | ||
82 | |||
83 | /* this function is called at probe time */ | ||
84 | static int sd_config(struct gspca_dev *gspca_dev, | ||
85 | const struct usb_device_id *id) | ||
86 | { | ||
87 | struct cam *cam; | ||
88 | |||
89 | cam = &gspca_dev->cam; | ||
90 | cam->cam_mode = vga_mode; | ||
91 | cam->nmodes = ARRAY_SIZE(vga_mode); | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | /* this function is called at probe and resume time */ | ||
96 | static int sd_init(struct gspca_dev *gspca_dev) | ||
97 | { | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | static int sd_start(struct gspca_dev *gspca_dev) | ||
102 | { | ||
103 | struct sd *sd = (struct sd *) gspca_dev; | ||
104 | __u8 *data = gspca_dev->usb_buf; | ||
105 | int err_code; | ||
106 | |||
107 | sd->sof_read = 0; | ||
108 | |||
109 | /* Note: register descriptions guessed from MR97113A driver */ | ||
110 | |||
111 | data[0] = 0x01; | ||
112 | data[1] = 0x01; | ||
113 | err_code = reg_w(gspca_dev, 2); | ||
114 | if (err_code < 0) | ||
115 | return err_code; | ||
116 | |||
117 | data[0] = 0x00; | ||
118 | data[1] = 0x0d; | ||
119 | data[2] = 0x01; | ||
120 | data[5] = 0x2b; | ||
121 | data[7] = 0x00; | ||
122 | data[9] = 0x50; /* reg 8, no scale down */ | ||
123 | data[10] = 0xc0; | ||
124 | |||
125 | switch (gspca_dev->width) { | ||
126 | case 160: | ||
127 | data[9] |= 0x0c; /* reg 8, 4:1 scale down */ | ||
128 | /* fall thru */ | ||
129 | case 320: | ||
130 | data[9] |= 0x04; /* reg 8, 2:1 scale down */ | ||
131 | /* fall thru */ | ||
132 | case 640: | ||
133 | default: | ||
134 | data[3] = 0x50; /* reg 2, H size */ | ||
135 | data[4] = 0x78; /* reg 3, V size */ | ||
136 | data[6] = 0x04; /* reg 5, H start */ | ||
137 | data[8] = 0x03; /* reg 7, V start */ | ||
138 | break; | ||
139 | |||
140 | case 176: | ||
141 | data[9] |= 0x04; /* reg 8, 2:1 scale down */ | ||
142 | /* fall thru */ | ||
143 | case 352: | ||
144 | data[3] = 0x2c; /* reg 2, H size */ | ||
145 | data[4] = 0x48; /* reg 3, V size */ | ||
146 | data[6] = 0x94; /* reg 5, H start */ | ||
147 | data[8] = 0x63; /* reg 7, V start */ | ||
148 | break; | ||
149 | } | ||
150 | |||
151 | err_code = reg_w(gspca_dev, 11); | ||
152 | if (err_code < 0) | ||
153 | return err_code; | ||
154 | |||
155 | data[0] = 0x0a; | ||
156 | data[1] = 0x80; | ||
157 | err_code = reg_w(gspca_dev, 2); | ||
158 | if (err_code < 0) | ||
159 | return err_code; | ||
160 | |||
161 | data[0] = 0x14; | ||
162 | data[1] = 0x0a; | ||
163 | err_code = reg_w(gspca_dev, 2); | ||
164 | if (err_code < 0) | ||
165 | return err_code; | ||
166 | |||
167 | data[0] = 0x1b; | ||
168 | data[1] = 0x00; | ||
169 | err_code = reg_w(gspca_dev, 2); | ||
170 | if (err_code < 0) | ||
171 | return err_code; | ||
172 | |||
173 | data[0] = 0x15; | ||
174 | data[1] = 0x16; | ||
175 | err_code = reg_w(gspca_dev, 2); | ||
176 | if (err_code < 0) | ||
177 | return err_code; | ||
178 | |||
179 | data[0] = 0x16; | ||
180 | data[1] = 0x10; | ||
181 | err_code = reg_w(gspca_dev, 2); | ||
182 | if (err_code < 0) | ||
183 | return err_code; | ||
184 | |||
185 | data[0] = 0x17; | ||
186 | data[1] = 0x3a; | ||
187 | err_code = reg_w(gspca_dev, 2); | ||
188 | if (err_code < 0) | ||
189 | return err_code; | ||
190 | |||
191 | data[0] = 0x18; | ||
192 | data[1] = 0x68; | ||
193 | err_code = reg_w(gspca_dev, 2); | ||
194 | if (err_code < 0) | ||
195 | return err_code; | ||
196 | |||
197 | data[0] = 0x1f; | ||
198 | data[1] = 0x00; | ||
199 | data[2] = 0x02; | ||
200 | data[3] = 0x06; | ||
201 | data[4] = 0x59; | ||
202 | data[5] = 0x0c; | ||
203 | data[6] = 0x16; | ||
204 | data[7] = 0x00; | ||
205 | data[8] = 0x07; | ||
206 | data[9] = 0x00; | ||
207 | data[10] = 0x01; | ||
208 | err_code = reg_w(gspca_dev, 11); | ||
209 | if (err_code < 0) | ||
210 | return err_code; | ||
211 | |||
212 | data[0] = 0x1f; | ||
213 | data[1] = 0x04; | ||
214 | data[2] = 0x11; | ||
215 | data[3] = 0x01; | ||
216 | err_code = reg_w(gspca_dev, 4); | ||
217 | if (err_code < 0) | ||
218 | return err_code; | ||
219 | |||
220 | data[0] = 0x1f; | ||
221 | data[1] = 0x00; | ||
222 | data[2] = 0x0a; | ||
223 | data[3] = 0x00; | ||
224 | data[4] = 0x01; | ||
225 | data[5] = 0x00; | ||
226 | data[6] = 0x00; | ||
227 | data[7] = 0x01; | ||
228 | data[8] = 0x00; | ||
229 | data[9] = 0x0a; | ||
230 | err_code = reg_w(gspca_dev, 10); | ||
231 | if (err_code < 0) | ||
232 | return err_code; | ||
233 | |||
234 | data[0] = 0x1f; | ||
235 | data[1] = 0x04; | ||
236 | data[2] = 0x11; | ||
237 | data[3] = 0x01; | ||
238 | err_code = reg_w(gspca_dev, 4); | ||
239 | if (err_code < 0) | ||
240 | return err_code; | ||
241 | |||
242 | data[0] = 0x1f; | ||
243 | data[1] = 0x00; | ||
244 | data[2] = 0x12; | ||
245 | data[3] = 0x00; | ||
246 | data[4] = 0x63; | ||
247 | data[5] = 0x00; | ||
248 | data[6] = 0x70; | ||
249 | data[7] = 0x00; | ||
250 | data[8] = 0x01; | ||
251 | err_code = reg_w(gspca_dev, 10); | ||
252 | if (err_code < 0) | ||
253 | return err_code; | ||
254 | |||
255 | data[0] = 0x1f; | ||
256 | data[1] = 0x04; | ||
257 | data[2] = 0x11; | ||
258 | data[3] = 0x01; | ||
259 | err_code = reg_w(gspca_dev, 4); | ||
260 | if (err_code < 0) | ||
261 | return err_code; | ||
262 | |||
263 | data[0] = 0x00; | ||
264 | data[1] = 0x4d; /* ISOC transfering enable... */ | ||
265 | err_code = reg_w(gspca_dev, 2); | ||
266 | return err_code; | ||
267 | } | ||
268 | |||
269 | static void sd_stopN(struct gspca_dev *gspca_dev) | ||
270 | { | ||
271 | int result; | ||
272 | |||
273 | gspca_dev->usb_buf[0] = 1; | ||
274 | gspca_dev->usb_buf[1] = 0; | ||
275 | result = reg_w(gspca_dev, 2); | ||
276 | if (result < 0) | ||
277 | PDEBUG(D_ERR, "Camera Stop failed"); | ||
278 | } | ||
279 | |||
280 | /* Include pac common sof detection functions */ | ||
281 | #include "pac_common.h" | ||
282 | |||
283 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, | ||
284 | struct gspca_frame *frame, /* target */ | ||
285 | __u8 *data, /* isoc packet */ | ||
286 | int len) /* iso packet length */ | ||
287 | { | ||
288 | struct sd *sd = (struct sd *) gspca_dev; | ||
289 | unsigned char *sof; | ||
290 | |||
291 | sof = pac_find_sof(gspca_dev, data, len); | ||
292 | if (sof) { | ||
293 | int n; | ||
294 | |||
295 | /* finish decoding current frame */ | ||
296 | n = sof - data; | ||
297 | if (n > sizeof pac_sof_marker) | ||
298 | n -= sizeof pac_sof_marker; | ||
299 | else | ||
300 | n = 0; | ||
301 | frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, | ||
302 | data, n); | ||
303 | sd->header_read = 0; | ||
304 | gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); | ||
305 | len -= sof - data; | ||
306 | data = sof; | ||
307 | } | ||
308 | if (sd->header_read < 7) { | ||
309 | int needed; | ||
310 | |||
311 | /* skip the rest of the header */ | ||
312 | needed = 7 - sd->header_read; | ||
313 | if (len <= needed) { | ||
314 | sd->header_read += len; | ||
315 | return; | ||
316 | } | ||
317 | data += needed; | ||
318 | len -= needed; | ||
319 | sd->header_read = 7; | ||
320 | } | ||
321 | |||
322 | gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); | ||
323 | } | ||
324 | |||
325 | /* sub-driver description */ | ||
326 | static const struct sd_desc sd_desc = { | ||
327 | .name = MODULE_NAME, | ||
328 | .ctrls = sd_ctrls, | ||
329 | .nctrls = ARRAY_SIZE(sd_ctrls), | ||
330 | .config = sd_config, | ||
331 | .init = sd_init, | ||
332 | .start = sd_start, | ||
333 | .stopN = sd_stopN, | ||
334 | .pkt_scan = sd_pkt_scan, | ||
335 | }; | ||
336 | |||
337 | /* -- module initialisation -- */ | ||
338 | static const __devinitdata struct usb_device_id device_table[] = { | ||
339 | {USB_DEVICE(0x08ca, 0x0111)}, | ||
340 | {} | ||
341 | }; | ||
342 | MODULE_DEVICE_TABLE(usb, device_table); | ||
343 | |||
344 | /* -- device connect -- */ | ||
345 | static int sd_probe(struct usb_interface *intf, | ||
346 | const struct usb_device_id *id) | ||
347 | { | ||
348 | return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), | ||
349 | THIS_MODULE); | ||
350 | } | ||
351 | |||
352 | static struct usb_driver sd_driver = { | ||
353 | .name = MODULE_NAME, | ||
354 | .id_table = device_table, | ||
355 | .probe = sd_probe, | ||
356 | .disconnect = gspca_disconnect, | ||
357 | #ifdef CONFIG_PM | ||
358 | .suspend = gspca_suspend, | ||
359 | .resume = gspca_resume, | ||
360 | #endif | ||
361 | }; | ||
362 | |||
363 | /* -- module insert / remove -- */ | ||
364 | static int __init sd_mod_init(void) | ||
365 | { | ||
366 | if (usb_register(&sd_driver) < 0) | ||
367 | return -1; | ||
368 | PDEBUG(D_PROBE, "registered"); | ||
369 | return 0; | ||
370 | } | ||
371 | static void __exit sd_mod_exit(void) | ||
372 | { | ||
373 | usb_deregister(&sd_driver); | ||
374 | PDEBUG(D_PROBE, "deregistered"); | ||
375 | } | ||
376 | |||
377 | module_init(sd_mod_init); | ||
378 | module_exit(sd_mod_exit); | ||