diff options
Diffstat (limited to 'drivers/media/video/tm6000/tm6000-dvb.c')
-rw-r--r-- | drivers/media/video/tm6000/tm6000-dvb.c | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/drivers/media/video/tm6000/tm6000-dvb.c b/drivers/media/video/tm6000/tm6000-dvb.c new file mode 100644 index 00000000000..5e6c129a4be --- /dev/null +++ b/drivers/media/video/tm6000/tm6000-dvb.c | |||
@@ -0,0 +1,452 @@ | |||
1 | /* | ||
2 | * tm6000-dvb.c - dvb-t support for TM5600/TM6000/TM6010 USB video capture devices | ||
3 | * | ||
4 | * Copyright (C) 2007 Michel Ludwig <michel.ludwig@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 version 2 | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/usb.h> | ||
23 | |||
24 | #include "tm6000.h" | ||
25 | #include "tm6000-regs.h" | ||
26 | |||
27 | #include "zl10353.h" | ||
28 | |||
29 | #include <media/tuner.h> | ||
30 | |||
31 | #include "tuner-xc2028.h" | ||
32 | #include "xc5000.h" | ||
33 | |||
34 | MODULE_DESCRIPTION("DVB driver extension module for tm5600/6000/6010 based TV cards"); | ||
35 | MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); | ||
36 | MODULE_LICENSE("GPL"); | ||
37 | |||
38 | MODULE_SUPPORTED_DEVICE("{{Trident, tm5600}," | ||
39 | "{{Trident, tm6000}," | ||
40 | "{{Trident, tm6010}"); | ||
41 | |||
42 | static int debug; | ||
43 | |||
44 | module_param(debug, int, 0644); | ||
45 | MODULE_PARM_DESC(debug, "enable debug message"); | ||
46 | |||
47 | static inline void print_err_status(struct tm6000_core *dev, | ||
48 | int packet, int status) | ||
49 | { | ||
50 | char *errmsg = "Unknown"; | ||
51 | |||
52 | switch (status) { | ||
53 | case -ENOENT: | ||
54 | errmsg = "unlinked synchronuously"; | ||
55 | break; | ||
56 | case -ECONNRESET: | ||
57 | errmsg = "unlinked asynchronuously"; | ||
58 | break; | ||
59 | case -ENOSR: | ||
60 | errmsg = "Buffer error (overrun)"; | ||
61 | break; | ||
62 | case -EPIPE: | ||
63 | errmsg = "Stalled (device not responding)"; | ||
64 | break; | ||
65 | case -EOVERFLOW: | ||
66 | errmsg = "Babble (bad cable?)"; | ||
67 | break; | ||
68 | case -EPROTO: | ||
69 | errmsg = "Bit-stuff error (bad cable?)"; | ||
70 | break; | ||
71 | case -EILSEQ: | ||
72 | errmsg = "CRC/Timeout (could be anything)"; | ||
73 | break; | ||
74 | case -ETIME: | ||
75 | errmsg = "Device does not respond"; | ||
76 | break; | ||
77 | } | ||
78 | if (packet < 0) { | ||
79 | dprintk(dev, 1, "URB status %d [%s].\n", | ||
80 | status, errmsg); | ||
81 | } else { | ||
82 | dprintk(dev, 1, "URB packet %d, status %d [%s].\n", | ||
83 | packet, status, errmsg); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | static void tm6000_urb_received(struct urb *urb) | ||
88 | { | ||
89 | int ret; | ||
90 | struct tm6000_core *dev = urb->context; | ||
91 | |||
92 | if (urb->status != 0) | ||
93 | print_err_status(dev, 0, urb->status); | ||
94 | else if (urb->actual_length > 0) | ||
95 | dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, | ||
96 | urb->actual_length); | ||
97 | |||
98 | if (dev->dvb->streams > 0) { | ||
99 | ret = usb_submit_urb(urb, GFP_ATOMIC); | ||
100 | if (ret < 0) { | ||
101 | printk(KERN_ERR "tm6000: error %s\n", __func__); | ||
102 | kfree(urb->transfer_buffer); | ||
103 | usb_free_urb(urb); | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | static int tm6000_start_stream(struct tm6000_core *dev) | ||
109 | { | ||
110 | int ret; | ||
111 | unsigned int pipe, size; | ||
112 | struct tm6000_dvb *dvb = dev->dvb; | ||
113 | |||
114 | printk(KERN_INFO "tm6000: got start stream request %s\n", __func__); | ||
115 | |||
116 | if (dev->mode != TM6000_MODE_DIGITAL) { | ||
117 | tm6000_init_digital_mode(dev); | ||
118 | dev->mode = TM6000_MODE_DIGITAL; | ||
119 | } | ||
120 | |||
121 | dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
122 | if (dvb->bulk_urb == NULL) { | ||
123 | printk(KERN_ERR "tm6000: couldn't allocate urb\n"); | ||
124 | return -ENOMEM; | ||
125 | } | ||
126 | |||
127 | pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in.endp->desc.bEndpointAddress | ||
128 | & USB_ENDPOINT_NUMBER_MASK); | ||
129 | |||
130 | size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); | ||
131 | size = size * 15; /* 512 x 8 or 12 or 15 */ | ||
132 | |||
133 | dvb->bulk_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); | ||
134 | if (dvb->bulk_urb->transfer_buffer == NULL) { | ||
135 | usb_free_urb(dvb->bulk_urb); | ||
136 | printk(KERN_ERR "tm6000: couldn't allocate transfer buffer!\n"); | ||
137 | return -ENOMEM; | ||
138 | } | ||
139 | |||
140 | usb_fill_bulk_urb(dvb->bulk_urb, dev->udev, pipe, | ||
141 | dvb->bulk_urb->transfer_buffer, | ||
142 | size, | ||
143 | tm6000_urb_received, dev); | ||
144 | |||
145 | ret = usb_clear_halt(dev->udev, pipe); | ||
146 | if (ret < 0) { | ||
147 | printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n", | ||
148 | ret, __func__); | ||
149 | return ret; | ||
150 | } else | ||
151 | printk(KERN_ERR "tm6000: pipe resetted\n"); | ||
152 | |||
153 | /* mutex_lock(&tm6000_driver.open_close_mutex); */ | ||
154 | ret = usb_submit_urb(dvb->bulk_urb, GFP_KERNEL); | ||
155 | |||
156 | /* mutex_unlock(&tm6000_driver.open_close_mutex); */ | ||
157 | if (ret) { | ||
158 | printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n", | ||
159 | ret); | ||
160 | |||
161 | kfree(dvb->bulk_urb->transfer_buffer); | ||
162 | usb_free_urb(dvb->bulk_urb); | ||
163 | return ret; | ||
164 | } | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static void tm6000_stop_stream(struct tm6000_core *dev) | ||
170 | { | ||
171 | struct tm6000_dvb *dvb = dev->dvb; | ||
172 | |||
173 | if (dvb->bulk_urb) { | ||
174 | printk(KERN_INFO "urb killing\n"); | ||
175 | usb_kill_urb(dvb->bulk_urb); | ||
176 | printk(KERN_INFO "urb buffer free\n"); | ||
177 | kfree(dvb->bulk_urb->transfer_buffer); | ||
178 | usb_free_urb(dvb->bulk_urb); | ||
179 | dvb->bulk_urb = NULL; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | static int tm6000_start_feed(struct dvb_demux_feed *feed) | ||
184 | { | ||
185 | struct dvb_demux *demux = feed->demux; | ||
186 | struct tm6000_core *dev = demux->priv; | ||
187 | struct tm6000_dvb *dvb = dev->dvb; | ||
188 | printk(KERN_INFO "tm6000: got start feed request %s\n", __func__); | ||
189 | |||
190 | mutex_lock(&dvb->mutex); | ||
191 | if (dvb->streams == 0) { | ||
192 | dvb->streams = 1; | ||
193 | /* mutex_init(&tm6000_dev->streming_mutex); */ | ||
194 | tm6000_start_stream(dev); | ||
195 | } else | ||
196 | ++(dvb->streams); | ||
197 | mutex_unlock(&dvb->mutex); | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int tm6000_stop_feed(struct dvb_demux_feed *feed) | ||
203 | { | ||
204 | struct dvb_demux *demux = feed->demux; | ||
205 | struct tm6000_core *dev = demux->priv; | ||
206 | struct tm6000_dvb *dvb = dev->dvb; | ||
207 | |||
208 | printk(KERN_INFO "tm6000: got stop feed request %s\n", __func__); | ||
209 | |||
210 | mutex_lock(&dvb->mutex); | ||
211 | |||
212 | printk(KERN_INFO "stream %#x\n", dvb->streams); | ||
213 | --(dvb->streams); | ||
214 | if (dvb->streams == 0) { | ||
215 | printk(KERN_INFO "stop stream\n"); | ||
216 | tm6000_stop_stream(dev); | ||
217 | /* mutex_destroy(&tm6000_dev->streaming_mutex); */ | ||
218 | } | ||
219 | mutex_unlock(&dvb->mutex); | ||
220 | /* mutex_destroy(&tm6000_dev->streaming_mutex); */ | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int tm6000_dvb_attach_frontend(struct tm6000_core *dev) | ||
226 | { | ||
227 | struct tm6000_dvb *dvb = dev->dvb; | ||
228 | |||
229 | if (dev->caps.has_zl10353) { | ||
230 | struct zl10353_config config = { | ||
231 | .demod_address = dev->demod_addr, | ||
232 | .no_tuner = 1, | ||
233 | .parallel_ts = 1, | ||
234 | .if2 = 45700, | ||
235 | .disable_i2c_gate_ctrl = 1, | ||
236 | }; | ||
237 | |||
238 | dvb->frontend = dvb_attach(zl10353_attach, &config, | ||
239 | &dev->i2c_adap); | ||
240 | } else { | ||
241 | printk(KERN_ERR "tm6000: no frontend defined for the device!\n"); | ||
242 | return -1; | ||
243 | } | ||
244 | |||
245 | return (!dvb->frontend) ? -1 : 0; | ||
246 | } | ||
247 | |||
248 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | ||
249 | |||
250 | static int register_dvb(struct tm6000_core *dev) | ||
251 | { | ||
252 | int ret = -1; | ||
253 | struct tm6000_dvb *dvb = dev->dvb; | ||
254 | |||
255 | mutex_init(&dvb->mutex); | ||
256 | |||
257 | dvb->streams = 0; | ||
258 | |||
259 | /* attach the frontend */ | ||
260 | ret = tm6000_dvb_attach_frontend(dev); | ||
261 | if (ret < 0) { | ||
262 | printk(KERN_ERR "tm6000: couldn't attach the frontend!\n"); | ||
263 | goto err; | ||
264 | } | ||
265 | |||
266 | ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T", | ||
267 | THIS_MODULE, &dev->udev->dev, adapter_nr); | ||
268 | dvb->adapter.priv = dev; | ||
269 | |||
270 | if (dvb->frontend) { | ||
271 | switch (dev->tuner_type) { | ||
272 | case TUNER_XC2028: { | ||
273 | struct xc2028_config cfg = { | ||
274 | .i2c_adap = &dev->i2c_adap, | ||
275 | .i2c_addr = dev->tuner_addr, | ||
276 | }; | ||
277 | |||
278 | dvb->frontend->callback = tm6000_tuner_callback; | ||
279 | ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); | ||
280 | if (ret < 0) { | ||
281 | printk(KERN_ERR | ||
282 | "tm6000: couldn't register frontend\n"); | ||
283 | goto adapter_err; | ||
284 | } | ||
285 | |||
286 | if (!dvb_attach(xc2028_attach, dvb->frontend, &cfg)) { | ||
287 | printk(KERN_ERR "tm6000: couldn't register " | ||
288 | "frontend (xc3028)\n"); | ||
289 | ret = -EINVAL; | ||
290 | goto frontend_err; | ||
291 | } | ||
292 | printk(KERN_INFO "tm6000: XC2028/3028 asked to be " | ||
293 | "attached to frontend!\n"); | ||
294 | break; | ||
295 | } | ||
296 | case TUNER_XC5000: { | ||
297 | struct xc5000_config cfg = { | ||
298 | .i2c_address = dev->tuner_addr, | ||
299 | }; | ||
300 | |||
301 | dvb->frontend->callback = tm6000_xc5000_callback; | ||
302 | ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); | ||
303 | if (ret < 0) { | ||
304 | printk(KERN_ERR | ||
305 | "tm6000: couldn't register frontend\n"); | ||
306 | goto adapter_err; | ||
307 | } | ||
308 | |||
309 | if (!dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, &cfg)) { | ||
310 | printk(KERN_ERR "tm6000: couldn't register " | ||
311 | "frontend (xc5000)\n"); | ||
312 | ret = -EINVAL; | ||
313 | goto frontend_err; | ||
314 | } | ||
315 | printk(KERN_INFO "tm6000: XC5000 asked to be " | ||
316 | "attached to frontend!\n"); | ||
317 | break; | ||
318 | } | ||
319 | } | ||
320 | } else | ||
321 | printk(KERN_ERR "tm6000: no frontend found\n"); | ||
322 | |||
323 | dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | ||
324 | | DMX_MEMORY_BASED_FILTERING; | ||
325 | dvb->demux.priv = dev; | ||
326 | dvb->demux.filternum = 8; | ||
327 | dvb->demux.feednum = 8; | ||
328 | dvb->demux.start_feed = tm6000_start_feed; | ||
329 | dvb->demux.stop_feed = tm6000_stop_feed; | ||
330 | dvb->demux.write_to_decoder = NULL; | ||
331 | ret = dvb_dmx_init(&dvb->demux); | ||
332 | if (ret < 0) { | ||
333 | printk(KERN_ERR "tm6000: dvb_dmx_init failed (errno = %d)\n", ret); | ||
334 | goto frontend_err; | ||
335 | } | ||
336 | |||
337 | dvb->dmxdev.filternum = dev->dvb->demux.filternum; | ||
338 | dvb->dmxdev.demux = &dev->dvb->demux.dmx; | ||
339 | dvb->dmxdev.capabilities = 0; | ||
340 | |||
341 | ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); | ||
342 | if (ret < 0) { | ||
343 | printk(KERN_ERR "tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret); | ||
344 | goto dvb_dmx_err; | ||
345 | } | ||
346 | |||
347 | return 0; | ||
348 | |||
349 | dvb_dmx_err: | ||
350 | dvb_dmx_release(&dvb->demux); | ||
351 | frontend_err: | ||
352 | if (dvb->frontend) { | ||
353 | dvb_frontend_detach(dvb->frontend); | ||
354 | dvb_unregister_frontend(dvb->frontend); | ||
355 | } | ||
356 | adapter_err: | ||
357 | dvb_unregister_adapter(&dvb->adapter); | ||
358 | err: | ||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | static void unregister_dvb(struct tm6000_core *dev) | ||
363 | { | ||
364 | struct tm6000_dvb *dvb = dev->dvb; | ||
365 | |||
366 | if (dvb->bulk_urb != NULL) { | ||
367 | struct urb *bulk_urb = dvb->bulk_urb; | ||
368 | |||
369 | kfree(bulk_urb->transfer_buffer); | ||
370 | bulk_urb->transfer_buffer = NULL; | ||
371 | usb_unlink_urb(bulk_urb); | ||
372 | usb_free_urb(bulk_urb); | ||
373 | } | ||
374 | |||
375 | /* mutex_lock(&tm6000_driver.open_close_mutex); */ | ||
376 | if (dvb->frontend) { | ||
377 | dvb_frontend_detach(dvb->frontend); | ||
378 | dvb_unregister_frontend(dvb->frontend); | ||
379 | } | ||
380 | |||
381 | dvb_dmxdev_release(&dvb->dmxdev); | ||
382 | dvb_dmx_release(&dvb->demux); | ||
383 | dvb_unregister_adapter(&dvb->adapter); | ||
384 | mutex_destroy(&dvb->mutex); | ||
385 | /* mutex_unlock(&tm6000_driver.open_close_mutex); */ | ||
386 | } | ||
387 | |||
388 | static int dvb_init(struct tm6000_core *dev) | ||
389 | { | ||
390 | struct tm6000_dvb *dvb; | ||
391 | int rc; | ||
392 | |||
393 | if (!dev) | ||
394 | return 0; | ||
395 | |||
396 | if (!dev->caps.has_dvb) | ||
397 | return 0; | ||
398 | |||
399 | dvb = kzalloc(sizeof(struct tm6000_dvb), GFP_KERNEL); | ||
400 | if (!dvb) { | ||
401 | printk(KERN_INFO "Cannot allocate memory\n"); | ||
402 | return -ENOMEM; | ||
403 | } | ||
404 | |||
405 | dev->dvb = dvb; | ||
406 | |||
407 | rc = register_dvb(dev); | ||
408 | if (rc < 0) { | ||
409 | kfree(dvb); | ||
410 | dev->dvb = NULL; | ||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | static int dvb_fini(struct tm6000_core *dev) | ||
418 | { | ||
419 | if (!dev) | ||
420 | return 0; | ||
421 | |||
422 | if (!dev->caps.has_dvb) | ||
423 | return 0; | ||
424 | |||
425 | if (dev->dvb) { | ||
426 | unregister_dvb(dev); | ||
427 | kfree(dev->dvb); | ||
428 | dev->dvb = NULL; | ||
429 | } | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | static struct tm6000_ops dvb_ops = { | ||
435 | .type = TM6000_DVB, | ||
436 | .name = "TM6000 dvb Extension", | ||
437 | .init = dvb_init, | ||
438 | .fini = dvb_fini, | ||
439 | }; | ||
440 | |||
441 | static int __init tm6000_dvb_register(void) | ||
442 | { | ||
443 | return tm6000_register_extension(&dvb_ops); | ||
444 | } | ||
445 | |||
446 | static void __exit tm6000_dvb_unregister(void) | ||
447 | { | ||
448 | tm6000_unregister_extension(&dvb_ops); | ||
449 | } | ||
450 | |||
451 | module_init(tm6000_dvb_register); | ||
452 | module_exit(tm6000_dvb_unregister); | ||