diff options
Diffstat (limited to 'drivers/media/pci/cx88/cx88-mpeg.c')
-rw-r--r-- | drivers/media/pci/cx88/cx88-mpeg.c | 929 |
1 files changed, 929 insertions, 0 deletions
diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c new file mode 100644 index 000000000000..cd5386ee210c --- /dev/null +++ b/drivers/media/pci/cx88/cx88-mpeg.c | |||
@@ -0,0 +1,929 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Support for the mpeg transport stream transfers | ||
4 | * PCI function #2 of the cx2388x. | ||
5 | * | ||
6 | * (c) 2004 Jelle Foks <jelle@foks.us> | ||
7 | * (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au> | ||
8 | * (c) 2004 Gerd Knorr <kraxel@bytesex.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | */ | ||
24 | |||
25 | #include <linux/module.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <linux/dma-mapping.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <asm/delay.h> | ||
32 | |||
33 | #include "cx88.h" | ||
34 | |||
35 | /* ------------------------------------------------------------------ */ | ||
36 | |||
37 | MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards"); | ||
38 | MODULE_AUTHOR("Jelle Foks <jelle@foks.us>"); | ||
39 | MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); | ||
40 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | ||
41 | MODULE_LICENSE("GPL"); | ||
42 | MODULE_VERSION(CX88_VERSION); | ||
43 | |||
44 | static unsigned int debug; | ||
45 | module_param(debug,int,0644); | ||
46 | MODULE_PARM_DESC(debug,"enable debug messages [mpeg]"); | ||
47 | |||
48 | #define dprintk(level,fmt, arg...) if (debug >= level) \ | ||
49 | printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg) | ||
50 | |||
51 | #define mpeg_dbg(level,fmt, arg...) if (debug >= level) \ | ||
52 | printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg) | ||
53 | |||
54 | #if defined(CONFIG_MODULES) && defined(MODULE) | ||
55 | static void request_module_async(struct work_struct *work) | ||
56 | { | ||
57 | struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk); | ||
58 | |||
59 | if (dev->core->board.mpeg & CX88_MPEG_DVB) | ||
60 | request_module("cx88-dvb"); | ||
61 | if (dev->core->board.mpeg & CX88_MPEG_BLACKBIRD) | ||
62 | request_module("cx88-blackbird"); | ||
63 | } | ||
64 | |||
65 | static void request_modules(struct cx8802_dev *dev) | ||
66 | { | ||
67 | INIT_WORK(&dev->request_module_wk, request_module_async); | ||
68 | schedule_work(&dev->request_module_wk); | ||
69 | } | ||
70 | |||
71 | static void flush_request_modules(struct cx8802_dev *dev) | ||
72 | { | ||
73 | flush_work_sync(&dev->request_module_wk); | ||
74 | } | ||
75 | #else | ||
76 | #define request_modules(dev) | ||
77 | #define flush_request_modules(dev) | ||
78 | #endif /* CONFIG_MODULES */ | ||
79 | |||
80 | |||
81 | static LIST_HEAD(cx8802_devlist); | ||
82 | static DEFINE_MUTEX(cx8802_mutex); | ||
83 | /* ------------------------------------------------------------------ */ | ||
84 | |||
85 | static int cx8802_start_dma(struct cx8802_dev *dev, | ||
86 | struct cx88_dmaqueue *q, | ||
87 | struct cx88_buffer *buf) | ||
88 | { | ||
89 | struct cx88_core *core = dev->core; | ||
90 | |||
91 | dprintk(1, "cx8802_start_dma w: %d, h: %d, f: %d\n", | ||
92 | buf->vb.width, buf->vb.height, buf->vb.field); | ||
93 | |||
94 | /* setup fifo + format */ | ||
95 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], | ||
96 | dev->ts_packet_size, buf->risc.dma); | ||
97 | |||
98 | /* write TS length to chip */ | ||
99 | cx_write(MO_TS_LNGTH, buf->vb.width); | ||
100 | |||
101 | /* FIXME: this needs a review. | ||
102 | * also: move to cx88-blackbird + cx88-dvb source files? */ | ||
103 | |||
104 | dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id); | ||
105 | |||
106 | if ( (core->active_type_id == CX88_MPEG_DVB) && | ||
107 | (core->board.mpeg & CX88_MPEG_DVB) ) { | ||
108 | |||
109 | dprintk( 1, "cx8802_start_dma doing .dvb\n"); | ||
110 | /* negedge driven & software reset */ | ||
111 | cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl); | ||
112 | udelay(100); | ||
113 | cx_write(MO_PINMUX_IO, 0x00); | ||
114 | cx_write(TS_HW_SOP_CNTRL, 0x47<<16|188<<4|0x01); | ||
115 | switch (core->boardnr) { | ||
116 | case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q: | ||
117 | case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T: | ||
118 | case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: | ||
119 | case CX88_BOARD_PCHDTV_HD5500: | ||
120 | cx_write(TS_SOP_STAT, 1<<13); | ||
121 | break; | ||
122 | case CX88_BOARD_SAMSUNG_SMT_7020: | ||
123 | cx_write(TS_SOP_STAT, 0x00); | ||
124 | break; | ||
125 | case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: | ||
126 | case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: | ||
127 | cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */ | ||
128 | udelay(100); | ||
129 | break; | ||
130 | case CX88_BOARD_HAUPPAUGE_HVR1300: | ||
131 | /* Enable MPEG parallel IO and video signal pins */ | ||
132 | cx_write(MO_PINMUX_IO, 0x88); | ||
133 | cx_write(TS_SOP_STAT, 0); | ||
134 | cx_write(TS_VALERR_CNTRL, 0); | ||
135 | break; | ||
136 | case CX88_BOARD_PINNACLE_PCTV_HD_800i: | ||
137 | /* Enable MPEG parallel IO and video signal pins */ | ||
138 | cx_write(MO_PINMUX_IO, 0x88); | ||
139 | cx_write(TS_HW_SOP_CNTRL, (0x47 << 16) | (188 << 4)); | ||
140 | dev->ts_gen_cntrl = 5; | ||
141 | cx_write(TS_SOP_STAT, 0); | ||
142 | cx_write(TS_VALERR_CNTRL, 0); | ||
143 | udelay(100); | ||
144 | break; | ||
145 | default: | ||
146 | cx_write(TS_SOP_STAT, 0x00); | ||
147 | break; | ||
148 | } | ||
149 | cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl); | ||
150 | udelay(100); | ||
151 | } else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) && | ||
152 | (core->board.mpeg & CX88_MPEG_BLACKBIRD) ) { | ||
153 | dprintk( 1, "cx8802_start_dma doing .blackbird\n"); | ||
154 | cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */ | ||
155 | |||
156 | cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */ | ||
157 | udelay(100); | ||
158 | |||
159 | cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */ | ||
160 | cx_write(TS_VALERR_CNTRL, 0x2000); | ||
161 | |||
162 | cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */ | ||
163 | udelay(100); | ||
164 | } else { | ||
165 | printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __func__, | ||
166 | core->board.mpeg ); | ||
167 | return -EINVAL; | ||
168 | } | ||
169 | |||
170 | /* reset counter */ | ||
171 | cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET); | ||
172 | q->count = 1; | ||
173 | |||
174 | /* enable irqs */ | ||
175 | dprintk( 1, "setting the interrupt mask\n" ); | ||
176 | cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_TSINT); | ||
177 | cx_set(MO_TS_INTMSK, 0x1f0011); | ||
178 | |||
179 | /* start dma */ | ||
180 | cx_set(MO_DEV_CNTRL2, (1<<5)); | ||
181 | cx_set(MO_TS_DMACNTRL, 0x11); | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static int cx8802_stop_dma(struct cx8802_dev *dev) | ||
186 | { | ||
187 | struct cx88_core *core = dev->core; | ||
188 | dprintk( 1, "cx8802_stop_dma\n" ); | ||
189 | |||
190 | /* stop dma */ | ||
191 | cx_clear(MO_TS_DMACNTRL, 0x11); | ||
192 | |||
193 | /* disable irqs */ | ||
194 | cx_clear(MO_PCI_INTMSK, PCI_INT_TSINT); | ||
195 | cx_clear(MO_TS_INTMSK, 0x1f0011); | ||
196 | |||
197 | /* Reset the controller */ | ||
198 | cx_write(TS_GEN_CNTRL, 0xcd); | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int cx8802_restart_queue(struct cx8802_dev *dev, | ||
203 | struct cx88_dmaqueue *q) | ||
204 | { | ||
205 | struct cx88_buffer *buf; | ||
206 | |||
207 | dprintk( 1, "cx8802_restart_queue\n" ); | ||
208 | if (list_empty(&q->active)) | ||
209 | { | ||
210 | struct cx88_buffer *prev; | ||
211 | prev = NULL; | ||
212 | |||
213 | dprintk(1, "cx8802_restart_queue: queue is empty\n" ); | ||
214 | |||
215 | for (;;) { | ||
216 | if (list_empty(&q->queued)) | ||
217 | return 0; | ||
218 | buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue); | ||
219 | if (NULL == prev) { | ||
220 | list_del(&buf->vb.queue); | ||
221 | list_add_tail(&buf->vb.queue,&q->active); | ||
222 | cx8802_start_dma(dev, q, buf); | ||
223 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
224 | buf->count = q->count++; | ||
225 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
226 | dprintk(1,"[%p/%d] restart_queue - first active\n", | ||
227 | buf,buf->vb.i); | ||
228 | |||
229 | } else if (prev->vb.width == buf->vb.width && | ||
230 | prev->vb.height == buf->vb.height && | ||
231 | prev->fmt == buf->fmt) { | ||
232 | list_del(&buf->vb.queue); | ||
233 | list_add_tail(&buf->vb.queue,&q->active); | ||
234 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
235 | buf->count = q->count++; | ||
236 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
237 | dprintk(1,"[%p/%d] restart_queue - move to active\n", | ||
238 | buf,buf->vb.i); | ||
239 | } else { | ||
240 | return 0; | ||
241 | } | ||
242 | prev = buf; | ||
243 | } | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
248 | dprintk(2,"restart_queue [%p/%d]: restart dma\n", | ||
249 | buf, buf->vb.i); | ||
250 | cx8802_start_dma(dev, q, buf); | ||
251 | list_for_each_entry(buf, &q->active, vb.queue) | ||
252 | buf->count = q->count++; | ||
253 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | /* ------------------------------------------------------------------ */ | ||
258 | |||
259 | int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev, | ||
260 | struct cx88_buffer *buf, enum v4l2_field field) | ||
261 | { | ||
262 | int size = dev->ts_packet_size * dev->ts_packet_count; | ||
263 | struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); | ||
264 | int rc; | ||
265 | |||
266 | dprintk(1, "%s: %p\n", __func__, buf); | ||
267 | if (0 != buf->vb.baddr && buf->vb.bsize < size) | ||
268 | return -EINVAL; | ||
269 | |||
270 | if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { | ||
271 | buf->vb.width = dev->ts_packet_size; | ||
272 | buf->vb.height = dev->ts_packet_count; | ||
273 | buf->vb.size = size; | ||
274 | buf->vb.field = field /*V4L2_FIELD_TOP*/; | ||
275 | |||
276 | if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL))) | ||
277 | goto fail; | ||
278 | cx88_risc_databuffer(dev->pci, &buf->risc, | ||
279 | dma->sglist, | ||
280 | buf->vb.width, buf->vb.height, 0); | ||
281 | } | ||
282 | buf->vb.state = VIDEOBUF_PREPARED; | ||
283 | return 0; | ||
284 | |||
285 | fail: | ||
286 | cx88_free_buffer(q,buf); | ||
287 | return rc; | ||
288 | } | ||
289 | |||
290 | void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf) | ||
291 | { | ||
292 | struct cx88_buffer *prev; | ||
293 | struct cx88_dmaqueue *cx88q = &dev->mpegq; | ||
294 | |||
295 | dprintk( 1, "cx8802_buf_queue\n" ); | ||
296 | /* add jump to stopper */ | ||
297 | buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); | ||
298 | buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma); | ||
299 | |||
300 | if (list_empty(&cx88q->active)) { | ||
301 | dprintk( 1, "queue is empty - first active\n" ); | ||
302 | list_add_tail(&buf->vb.queue,&cx88q->active); | ||
303 | cx8802_start_dma(dev, cx88q, buf); | ||
304 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
305 | buf->count = cx88q->count++; | ||
306 | mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT); | ||
307 | dprintk(1,"[%p/%d] %s - first active\n", | ||
308 | buf, buf->vb.i, __func__); | ||
309 | |||
310 | } else { | ||
311 | dprintk( 1, "queue is not empty - append to active\n" ); | ||
312 | prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue); | ||
313 | list_add_tail(&buf->vb.queue,&cx88q->active); | ||
314 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
315 | buf->count = cx88q->count++; | ||
316 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
317 | dprintk( 1, "[%p/%d] %s - append to active\n", | ||
318 | buf, buf->vb.i, __func__); | ||
319 | } | ||
320 | } | ||
321 | |||
322 | /* ----------------------------------------------------------- */ | ||
323 | |||
324 | static void do_cancel_buffers(struct cx8802_dev *dev, const char *reason, int restart) | ||
325 | { | ||
326 | struct cx88_dmaqueue *q = &dev->mpegq; | ||
327 | struct cx88_buffer *buf; | ||
328 | unsigned long flags; | ||
329 | |||
330 | spin_lock_irqsave(&dev->slock,flags); | ||
331 | while (!list_empty(&q->active)) { | ||
332 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
333 | list_del(&buf->vb.queue); | ||
334 | buf->vb.state = VIDEOBUF_ERROR; | ||
335 | wake_up(&buf->vb.done); | ||
336 | dprintk(1,"[%p/%d] %s - dma=0x%08lx\n", | ||
337 | buf, buf->vb.i, reason, (unsigned long)buf->risc.dma); | ||
338 | } | ||
339 | if (restart) | ||
340 | { | ||
341 | dprintk(1, "restarting queue\n" ); | ||
342 | cx8802_restart_queue(dev,q); | ||
343 | } | ||
344 | spin_unlock_irqrestore(&dev->slock,flags); | ||
345 | } | ||
346 | |||
347 | void cx8802_cancel_buffers(struct cx8802_dev *dev) | ||
348 | { | ||
349 | struct cx88_dmaqueue *q = &dev->mpegq; | ||
350 | |||
351 | dprintk( 1, "cx8802_cancel_buffers" ); | ||
352 | del_timer_sync(&q->timeout); | ||
353 | cx8802_stop_dma(dev); | ||
354 | do_cancel_buffers(dev,"cancel",0); | ||
355 | } | ||
356 | |||
357 | static void cx8802_timeout(unsigned long data) | ||
358 | { | ||
359 | struct cx8802_dev *dev = (struct cx8802_dev*)data; | ||
360 | |||
361 | dprintk(1, "%s\n",__func__); | ||
362 | |||
363 | if (debug) | ||
364 | cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); | ||
365 | cx8802_stop_dma(dev); | ||
366 | do_cancel_buffers(dev,"timeout",1); | ||
367 | } | ||
368 | |||
369 | static const char * cx88_mpeg_irqs[32] = { | ||
370 | "ts_risci1", NULL, NULL, NULL, | ||
371 | "ts_risci2", NULL, NULL, NULL, | ||
372 | "ts_oflow", NULL, NULL, NULL, | ||
373 | "ts_sync", NULL, NULL, NULL, | ||
374 | "opc_err", "par_err", "rip_err", "pci_abort", | ||
375 | "ts_err?", | ||
376 | }; | ||
377 | |||
378 | static void cx8802_mpeg_irq(struct cx8802_dev *dev) | ||
379 | { | ||
380 | struct cx88_core *core = dev->core; | ||
381 | u32 status, mask, count; | ||
382 | |||
383 | dprintk( 1, "cx8802_mpeg_irq\n" ); | ||
384 | status = cx_read(MO_TS_INTSTAT); | ||
385 | mask = cx_read(MO_TS_INTMSK); | ||
386 | if (0 == (status & mask)) | ||
387 | return; | ||
388 | |||
389 | cx_write(MO_TS_INTSTAT, status); | ||
390 | |||
391 | if (debug || (status & mask & ~0xff)) | ||
392 | cx88_print_irqbits(core->name, "irq mpeg ", | ||
393 | cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs), | ||
394 | status, mask); | ||
395 | |||
396 | /* risc op code error */ | ||
397 | if (status & (1 << 16)) { | ||
398 | printk(KERN_WARNING "%s: mpeg risc op code error\n",core->name); | ||
399 | cx_clear(MO_TS_DMACNTRL, 0x11); | ||
400 | cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); | ||
401 | } | ||
402 | |||
403 | /* risc1 y */ | ||
404 | if (status & 0x01) { | ||
405 | dprintk( 1, "wake up\n" ); | ||
406 | spin_lock(&dev->slock); | ||
407 | count = cx_read(MO_TS_GPCNT); | ||
408 | cx88_wakeup(dev->core, &dev->mpegq, count); | ||
409 | spin_unlock(&dev->slock); | ||
410 | } | ||
411 | |||
412 | /* risc2 y */ | ||
413 | if (status & 0x10) { | ||
414 | spin_lock(&dev->slock); | ||
415 | cx8802_restart_queue(dev,&dev->mpegq); | ||
416 | spin_unlock(&dev->slock); | ||
417 | } | ||
418 | |||
419 | /* other general errors */ | ||
420 | if (status & 0x1f0100) { | ||
421 | dprintk( 0, "general errors: 0x%08x\n", status & 0x1f0100 ); | ||
422 | spin_lock(&dev->slock); | ||
423 | cx8802_stop_dma(dev); | ||
424 | cx8802_restart_queue(dev,&dev->mpegq); | ||
425 | spin_unlock(&dev->slock); | ||
426 | } | ||
427 | } | ||
428 | |||
429 | #define MAX_IRQ_LOOP 10 | ||
430 | |||
431 | static irqreturn_t cx8802_irq(int irq, void *dev_id) | ||
432 | { | ||
433 | struct cx8802_dev *dev = dev_id; | ||
434 | struct cx88_core *core = dev->core; | ||
435 | u32 status; | ||
436 | int loop, handled = 0; | ||
437 | |||
438 | for (loop = 0; loop < MAX_IRQ_LOOP; loop++) { | ||
439 | status = cx_read(MO_PCI_INTSTAT) & | ||
440 | (core->pci_irqmask | PCI_INT_TSINT); | ||
441 | if (0 == status) | ||
442 | goto out; | ||
443 | dprintk( 1, "cx8802_irq\n" ); | ||
444 | dprintk( 1, " loop: %d/%d\n", loop, MAX_IRQ_LOOP ); | ||
445 | dprintk( 1, " status: %d\n", status ); | ||
446 | handled = 1; | ||
447 | cx_write(MO_PCI_INTSTAT, status); | ||
448 | |||
449 | if (status & core->pci_irqmask) | ||
450 | cx88_core_irq(core,status); | ||
451 | if (status & PCI_INT_TSINT) | ||
452 | cx8802_mpeg_irq(dev); | ||
453 | }; | ||
454 | if (MAX_IRQ_LOOP == loop) { | ||
455 | dprintk( 0, "clearing mask\n" ); | ||
456 | printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", | ||
457 | core->name); | ||
458 | cx_write(MO_PCI_INTMSK,0); | ||
459 | } | ||
460 | |||
461 | out: | ||
462 | return IRQ_RETVAL(handled); | ||
463 | } | ||
464 | |||
465 | static int cx8802_init_common(struct cx8802_dev *dev) | ||
466 | { | ||
467 | struct cx88_core *core = dev->core; | ||
468 | int err; | ||
469 | |||
470 | /* pci init */ | ||
471 | if (pci_enable_device(dev->pci)) | ||
472 | return -EIO; | ||
473 | pci_set_master(dev->pci); | ||
474 | if (!pci_dma_supported(dev->pci,DMA_BIT_MASK(32))) { | ||
475 | printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name); | ||
476 | return -EIO; | ||
477 | } | ||
478 | |||
479 | dev->pci_rev = dev->pci->revision; | ||
480 | pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat); | ||
481 | printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, " | ||
482 | "latency: %d, mmio: 0x%llx\n", dev->core->name, | ||
483 | pci_name(dev->pci), dev->pci_rev, dev->pci->irq, | ||
484 | dev->pci_lat,(unsigned long long)pci_resource_start(dev->pci,0)); | ||
485 | |||
486 | /* initialize driver struct */ | ||
487 | spin_lock_init(&dev->slock); | ||
488 | |||
489 | /* init dma queue */ | ||
490 | INIT_LIST_HEAD(&dev->mpegq.active); | ||
491 | INIT_LIST_HEAD(&dev->mpegq.queued); | ||
492 | dev->mpegq.timeout.function = cx8802_timeout; | ||
493 | dev->mpegq.timeout.data = (unsigned long)dev; | ||
494 | init_timer(&dev->mpegq.timeout); | ||
495 | cx88_risc_stopper(dev->pci,&dev->mpegq.stopper, | ||
496 | MO_TS_DMACNTRL,0x11,0x00); | ||
497 | |||
498 | /* get irq */ | ||
499 | err = request_irq(dev->pci->irq, cx8802_irq, | ||
500 | IRQF_SHARED | IRQF_DISABLED, dev->core->name, dev); | ||
501 | if (err < 0) { | ||
502 | printk(KERN_ERR "%s: can't get IRQ %d\n", | ||
503 | dev->core->name, dev->pci->irq); | ||
504 | return err; | ||
505 | } | ||
506 | cx_set(MO_PCI_INTMSK, core->pci_irqmask); | ||
507 | |||
508 | /* everything worked */ | ||
509 | pci_set_drvdata(dev->pci,dev); | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static void cx8802_fini_common(struct cx8802_dev *dev) | ||
514 | { | ||
515 | dprintk( 2, "cx8802_fini_common\n" ); | ||
516 | cx8802_stop_dma(dev); | ||
517 | pci_disable_device(dev->pci); | ||
518 | |||
519 | /* unregister stuff */ | ||
520 | free_irq(dev->pci->irq, dev); | ||
521 | pci_set_drvdata(dev->pci, NULL); | ||
522 | |||
523 | /* free memory */ | ||
524 | btcx_riscmem_free(dev->pci,&dev->mpegq.stopper); | ||
525 | } | ||
526 | |||
527 | /* ----------------------------------------------------------- */ | ||
528 | |||
529 | static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state) | ||
530 | { | ||
531 | struct cx8802_dev *dev = pci_get_drvdata(pci_dev); | ||
532 | struct cx88_core *core = dev->core; | ||
533 | |||
534 | /* stop mpeg dma */ | ||
535 | spin_lock(&dev->slock); | ||
536 | if (!list_empty(&dev->mpegq.active)) { | ||
537 | dprintk( 2, "suspend\n" ); | ||
538 | printk("%s: suspend mpeg\n", core->name); | ||
539 | cx8802_stop_dma(dev); | ||
540 | del_timer(&dev->mpegq.timeout); | ||
541 | } | ||
542 | spin_unlock(&dev->slock); | ||
543 | |||
544 | /* FIXME -- shutdown device */ | ||
545 | cx88_shutdown(dev->core); | ||
546 | |||
547 | pci_save_state(pci_dev); | ||
548 | if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) { | ||
549 | pci_disable_device(pci_dev); | ||
550 | dev->state.disabled = 1; | ||
551 | } | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | static int cx8802_resume_common(struct pci_dev *pci_dev) | ||
556 | { | ||
557 | struct cx8802_dev *dev = pci_get_drvdata(pci_dev); | ||
558 | struct cx88_core *core = dev->core; | ||
559 | int err; | ||
560 | |||
561 | if (dev->state.disabled) { | ||
562 | err=pci_enable_device(pci_dev); | ||
563 | if (err) { | ||
564 | printk(KERN_ERR "%s: can't enable device\n", | ||
565 | dev->core->name); | ||
566 | return err; | ||
567 | } | ||
568 | dev->state.disabled = 0; | ||
569 | } | ||
570 | err=pci_set_power_state(pci_dev, PCI_D0); | ||
571 | if (err) { | ||
572 | printk(KERN_ERR "%s: can't enable device\n", | ||
573 | dev->core->name); | ||
574 | pci_disable_device(pci_dev); | ||
575 | dev->state.disabled = 1; | ||
576 | |||
577 | return err; | ||
578 | } | ||
579 | pci_restore_state(pci_dev); | ||
580 | |||
581 | /* FIXME: re-initialize hardware */ | ||
582 | cx88_reset(dev->core); | ||
583 | |||
584 | /* restart video+vbi capture */ | ||
585 | spin_lock(&dev->slock); | ||
586 | if (!list_empty(&dev->mpegq.active)) { | ||
587 | printk("%s: resume mpeg\n", core->name); | ||
588 | cx8802_restart_queue(dev,&dev->mpegq); | ||
589 | } | ||
590 | spin_unlock(&dev->slock); | ||
591 | |||
592 | return 0; | ||
593 | } | ||
594 | |||
595 | struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype) | ||
596 | { | ||
597 | struct cx8802_driver *d; | ||
598 | |||
599 | list_for_each_entry(d, &dev->drvlist, drvlist) | ||
600 | if (d->type_id == btype) | ||
601 | return d; | ||
602 | |||
603 | return NULL; | ||
604 | } | ||
605 | |||
606 | /* Driver asked for hardware access. */ | ||
607 | static int cx8802_request_acquire(struct cx8802_driver *drv) | ||
608 | { | ||
609 | struct cx88_core *core = drv->core; | ||
610 | unsigned int i; | ||
611 | |||
612 | /* Fail a request for hardware if the device is busy. */ | ||
613 | if (core->active_type_id != CX88_BOARD_NONE && | ||
614 | core->active_type_id != drv->type_id) | ||
615 | return -EBUSY; | ||
616 | |||
617 | if (drv->type_id == CX88_MPEG_DVB) { | ||
618 | /* When switching to DVB, always set the input to the tuner */ | ||
619 | core->last_analog_input = core->input; | ||
620 | core->input = 0; | ||
621 | for (i = 0; | ||
622 | i < (sizeof(core->board.input) / sizeof(struct cx88_input)); | ||
623 | i++) { | ||
624 | if (core->board.input[i].type == CX88_VMUX_DVB) { | ||
625 | core->input = i; | ||
626 | break; | ||
627 | } | ||
628 | } | ||
629 | } | ||
630 | |||
631 | if (drv->advise_acquire) | ||
632 | { | ||
633 | core->active_ref++; | ||
634 | if (core->active_type_id == CX88_BOARD_NONE) { | ||
635 | core->active_type_id = drv->type_id; | ||
636 | drv->advise_acquire(drv); | ||
637 | } | ||
638 | |||
639 | mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __func__, cx_read(MO_GP0_IO)); | ||
640 | } | ||
641 | |||
642 | return 0; | ||
643 | } | ||
644 | |||
645 | /* Driver asked to release hardware. */ | ||
646 | static int cx8802_request_release(struct cx8802_driver *drv) | ||
647 | { | ||
648 | struct cx88_core *core = drv->core; | ||
649 | |||
650 | if (drv->advise_release && --core->active_ref == 0) | ||
651 | { | ||
652 | if (drv->type_id == CX88_MPEG_DVB) { | ||
653 | /* If the DVB driver is releasing, reset the input | ||
654 | state to the last configured analog input */ | ||
655 | core->input = core->last_analog_input; | ||
656 | } | ||
657 | |||
658 | drv->advise_release(drv); | ||
659 | core->active_type_id = CX88_BOARD_NONE; | ||
660 | mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO)); | ||
661 | } | ||
662 | |||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static int cx8802_check_driver(struct cx8802_driver *drv) | ||
667 | { | ||
668 | if (drv == NULL) | ||
669 | return -ENODEV; | ||
670 | |||
671 | if ((drv->type_id != CX88_MPEG_DVB) && | ||
672 | (drv->type_id != CX88_MPEG_BLACKBIRD)) | ||
673 | return -EINVAL; | ||
674 | |||
675 | if ((drv->hw_access != CX8802_DRVCTL_SHARED) && | ||
676 | (drv->hw_access != CX8802_DRVCTL_EXCLUSIVE)) | ||
677 | return -EINVAL; | ||
678 | |||
679 | if ((drv->probe == NULL) || | ||
680 | (drv->remove == NULL) || | ||
681 | (drv->advise_acquire == NULL) || | ||
682 | (drv->advise_release == NULL)) | ||
683 | return -EINVAL; | ||
684 | |||
685 | return 0; | ||
686 | } | ||
687 | |||
688 | int cx8802_register_driver(struct cx8802_driver *drv) | ||
689 | { | ||
690 | struct cx8802_dev *dev; | ||
691 | struct cx8802_driver *driver; | ||
692 | int err, i = 0; | ||
693 | |||
694 | printk(KERN_INFO | ||
695 | "cx88/2: registering cx8802 driver, type: %s access: %s\n", | ||
696 | drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird", | ||
697 | drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive"); | ||
698 | |||
699 | if ((err = cx8802_check_driver(drv)) != 0) { | ||
700 | printk(KERN_ERR "cx88/2: cx8802_driver is invalid\n"); | ||
701 | return err; | ||
702 | } | ||
703 | |||
704 | mutex_lock(&cx8802_mutex); | ||
705 | |||
706 | list_for_each_entry(dev, &cx8802_devlist, devlist) { | ||
707 | printk(KERN_INFO | ||
708 | "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n", | ||
709 | dev->core->name, dev->pci->subsystem_vendor, | ||
710 | dev->pci->subsystem_device, dev->core->board.name, | ||
711 | dev->core->boardnr); | ||
712 | |||
713 | /* Bring up a new struct for each driver instance */ | ||
714 | driver = kzalloc(sizeof(*drv),GFP_KERNEL); | ||
715 | if (driver == NULL) { | ||
716 | err = -ENOMEM; | ||
717 | goto out; | ||
718 | } | ||
719 | |||
720 | /* Snapshot of the driver registration data */ | ||
721 | drv->core = dev->core; | ||
722 | drv->suspend = cx8802_suspend_common; | ||
723 | drv->resume = cx8802_resume_common; | ||
724 | drv->request_acquire = cx8802_request_acquire; | ||
725 | drv->request_release = cx8802_request_release; | ||
726 | memcpy(driver, drv, sizeof(*driver)); | ||
727 | |||
728 | mutex_lock(&drv->core->lock); | ||
729 | err = drv->probe(driver); | ||
730 | if (err == 0) { | ||
731 | i++; | ||
732 | list_add_tail(&driver->drvlist, &dev->drvlist); | ||
733 | } else { | ||
734 | printk(KERN_ERR | ||
735 | "%s/2: cx8802 probe failed, err = %d\n", | ||
736 | dev->core->name, err); | ||
737 | } | ||
738 | mutex_unlock(&drv->core->lock); | ||
739 | } | ||
740 | |||
741 | err = i ? 0 : -ENODEV; | ||
742 | out: | ||
743 | mutex_unlock(&cx8802_mutex); | ||
744 | return err; | ||
745 | } | ||
746 | |||
747 | int cx8802_unregister_driver(struct cx8802_driver *drv) | ||
748 | { | ||
749 | struct cx8802_dev *dev; | ||
750 | struct cx8802_driver *d, *dtmp; | ||
751 | int err = 0; | ||
752 | |||
753 | printk(KERN_INFO | ||
754 | "cx88/2: unregistering cx8802 driver, type: %s access: %s\n", | ||
755 | drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird", | ||
756 | drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive"); | ||
757 | |||
758 | mutex_lock(&cx8802_mutex); | ||
759 | |||
760 | list_for_each_entry(dev, &cx8802_devlist, devlist) { | ||
761 | printk(KERN_INFO | ||
762 | "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n", | ||
763 | dev->core->name, dev->pci->subsystem_vendor, | ||
764 | dev->pci->subsystem_device, dev->core->board.name, | ||
765 | dev->core->boardnr); | ||
766 | |||
767 | mutex_lock(&dev->core->lock); | ||
768 | |||
769 | list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) { | ||
770 | /* only unregister the correct driver type */ | ||
771 | if (d->type_id != drv->type_id) | ||
772 | continue; | ||
773 | |||
774 | err = d->remove(d); | ||
775 | if (err == 0) { | ||
776 | list_del(&d->drvlist); | ||
777 | kfree(d); | ||
778 | } else | ||
779 | printk(KERN_ERR "%s/2: cx8802 driver remove " | ||
780 | "failed (%d)\n", dev->core->name, err); | ||
781 | } | ||
782 | |||
783 | mutex_unlock(&dev->core->lock); | ||
784 | } | ||
785 | |||
786 | mutex_unlock(&cx8802_mutex); | ||
787 | |||
788 | return err; | ||
789 | } | ||
790 | |||
791 | /* ----------------------------------------------------------- */ | ||
792 | static int __devinit cx8802_probe(struct pci_dev *pci_dev, | ||
793 | const struct pci_device_id *pci_id) | ||
794 | { | ||
795 | struct cx8802_dev *dev; | ||
796 | struct cx88_core *core; | ||
797 | int err; | ||
798 | |||
799 | /* general setup */ | ||
800 | core = cx88_core_get(pci_dev); | ||
801 | if (NULL == core) | ||
802 | return -EINVAL; | ||
803 | |||
804 | printk("%s/2: cx2388x 8802 Driver Manager\n", core->name); | ||
805 | |||
806 | err = -ENODEV; | ||
807 | if (!core->board.mpeg) | ||
808 | goto fail_core; | ||
809 | |||
810 | err = -ENOMEM; | ||
811 | dev = kzalloc(sizeof(*dev),GFP_KERNEL); | ||
812 | if (NULL == dev) | ||
813 | goto fail_core; | ||
814 | dev->pci = pci_dev; | ||
815 | dev->core = core; | ||
816 | |||
817 | /* Maintain a reference so cx88-video can query the 8802 device. */ | ||
818 | core->dvbdev = dev; | ||
819 | |||
820 | err = cx8802_init_common(dev); | ||
821 | if (err != 0) | ||
822 | goto fail_free; | ||
823 | |||
824 | INIT_LIST_HEAD(&dev->drvlist); | ||
825 | mutex_lock(&cx8802_mutex); | ||
826 | list_add_tail(&dev->devlist,&cx8802_devlist); | ||
827 | mutex_unlock(&cx8802_mutex); | ||
828 | |||
829 | /* now autoload cx88-dvb or cx88-blackbird */ | ||
830 | request_modules(dev); | ||
831 | return 0; | ||
832 | |||
833 | fail_free: | ||
834 | kfree(dev); | ||
835 | fail_core: | ||
836 | core->dvbdev = NULL; | ||
837 | cx88_core_put(core,pci_dev); | ||
838 | return err; | ||
839 | } | ||
840 | |||
841 | static void __devexit cx8802_remove(struct pci_dev *pci_dev) | ||
842 | { | ||
843 | struct cx8802_dev *dev; | ||
844 | |||
845 | dev = pci_get_drvdata(pci_dev); | ||
846 | |||
847 | dprintk( 1, "%s\n", __func__); | ||
848 | |||
849 | flush_request_modules(dev); | ||
850 | |||
851 | mutex_lock(&dev->core->lock); | ||
852 | |||
853 | if (!list_empty(&dev->drvlist)) { | ||
854 | struct cx8802_driver *drv, *tmp; | ||
855 | int err; | ||
856 | |||
857 | printk(KERN_WARNING "%s/2: Trying to remove cx8802 driver " | ||
858 | "while cx8802 sub-drivers still loaded?!\n", | ||
859 | dev->core->name); | ||
860 | |||
861 | list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) { | ||
862 | err = drv->remove(drv); | ||
863 | if (err == 0) { | ||
864 | list_del(&drv->drvlist); | ||
865 | } else | ||
866 | printk(KERN_ERR "%s/2: cx8802 driver remove " | ||
867 | "failed (%d)\n", dev->core->name, err); | ||
868 | kfree(drv); | ||
869 | } | ||
870 | } | ||
871 | |||
872 | mutex_unlock(&dev->core->lock); | ||
873 | |||
874 | /* Destroy any 8802 reference. */ | ||
875 | dev->core->dvbdev = NULL; | ||
876 | |||
877 | /* common */ | ||
878 | cx8802_fini_common(dev); | ||
879 | cx88_core_put(dev->core,dev->pci); | ||
880 | kfree(dev); | ||
881 | } | ||
882 | |||
883 | static const struct pci_device_id cx8802_pci_tbl[] = { | ||
884 | { | ||
885 | .vendor = 0x14f1, | ||
886 | .device = 0x8802, | ||
887 | .subvendor = PCI_ANY_ID, | ||
888 | .subdevice = PCI_ANY_ID, | ||
889 | },{ | ||
890 | /* --- end of list --- */ | ||
891 | } | ||
892 | }; | ||
893 | MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); | ||
894 | |||
895 | static struct pci_driver cx8802_pci_driver = { | ||
896 | .name = "cx88-mpeg driver manager", | ||
897 | .id_table = cx8802_pci_tbl, | ||
898 | .probe = cx8802_probe, | ||
899 | .remove = __devexit_p(cx8802_remove), | ||
900 | }; | ||
901 | |||
902 | static int __init cx8802_init(void) | ||
903 | { | ||
904 | printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %s loaded\n", | ||
905 | CX88_VERSION); | ||
906 | return pci_register_driver(&cx8802_pci_driver); | ||
907 | } | ||
908 | |||
909 | static void __exit cx8802_fini(void) | ||
910 | { | ||
911 | pci_unregister_driver(&cx8802_pci_driver); | ||
912 | } | ||
913 | |||
914 | module_init(cx8802_init); | ||
915 | module_exit(cx8802_fini); | ||
916 | EXPORT_SYMBOL(cx8802_buf_prepare); | ||
917 | EXPORT_SYMBOL(cx8802_buf_queue); | ||
918 | EXPORT_SYMBOL(cx8802_cancel_buffers); | ||
919 | |||
920 | EXPORT_SYMBOL(cx8802_register_driver); | ||
921 | EXPORT_SYMBOL(cx8802_unregister_driver); | ||
922 | EXPORT_SYMBOL(cx8802_get_driver); | ||
923 | /* ----------------------------------------------------------- */ | ||
924 | /* | ||
925 | * Local variables: | ||
926 | * c-basic-offset: 8 | ||
927 | * End: | ||
928 | * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off | ||
929 | */ | ||