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