162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Support for the mpeg transport stream transfers 562306a36Sopenharmony_ci * PCI function #2 of the cx2388x. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (c) 2004 Jelle Foks <jelle@foks.us> 862306a36Sopenharmony_ci * (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au> 962306a36Sopenharmony_ci * (c) 2004 Gerd Knorr <kraxel@bytesex.org> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "cx88.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/device.h> 1862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciMODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards"); 2562306a36Sopenharmony_ciMODULE_AUTHOR("Jelle Foks <jelle@foks.us>"); 2662306a36Sopenharmony_ciMODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); 2762306a36Sopenharmony_ciMODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); 2862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2962306a36Sopenharmony_ciMODULE_VERSION(CX88_VERSION); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic unsigned int debug; 3262306a36Sopenharmony_cimodule_param(debug, int, 0644); 3362306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "enable debug messages [mpeg]"); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define dprintk(level, fmt, arg...) do { \ 3662306a36Sopenharmony_ci if (debug + 1 > level) \ 3762306a36Sopenharmony_ci printk(KERN_DEBUG pr_fmt("%s: mpeg:" fmt), \ 3862306a36Sopenharmony_ci __func__, ##arg); \ 3962306a36Sopenharmony_ci} while (0) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#if defined(CONFIG_MODULES) && defined(MODULE) 4262306a36Sopenharmony_cistatic void request_module_async(struct work_struct *work) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct cx8802_dev *dev = container_of(work, struct cx8802_dev, 4562306a36Sopenharmony_ci request_module_wk); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (dev->core->board.mpeg & CX88_MPEG_DVB) 4862306a36Sopenharmony_ci request_module("cx88-dvb"); 4962306a36Sopenharmony_ci if (dev->core->board.mpeg & CX88_MPEG_BLACKBIRD) 5062306a36Sopenharmony_ci request_module("cx88-blackbird"); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void request_modules(struct cx8802_dev *dev) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci INIT_WORK(&dev->request_module_wk, request_module_async); 5662306a36Sopenharmony_ci schedule_work(&dev->request_module_wk); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void flush_request_modules(struct cx8802_dev *dev) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci flush_work(&dev->request_module_wk); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci#else 6462306a36Sopenharmony_ci#define request_modules(dev) 6562306a36Sopenharmony_ci#define flush_request_modules(dev) 6662306a36Sopenharmony_ci#endif /* CONFIG_MODULES */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic LIST_HEAD(cx8802_devlist); 6962306a36Sopenharmony_cistatic DEFINE_MUTEX(cx8802_mutex); 7062306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ciint cx8802_start_dma(struct cx8802_dev *dev, 7362306a36Sopenharmony_ci struct cx88_dmaqueue *q, 7462306a36Sopenharmony_ci struct cx88_buffer *buf) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct cx88_core *core = dev->core; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci dprintk(1, "w: %d, h: %d, f: %d\n", 7962306a36Sopenharmony_ci core->width, core->height, core->field); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* setup fifo + format */ 8262306a36Sopenharmony_ci cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 8362306a36Sopenharmony_ci dev->ts_packet_size, buf->risc.dma); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* write TS length to chip */ 8662306a36Sopenharmony_ci cx_write(MO_TS_LNGTH, dev->ts_packet_size); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* 8962306a36Sopenharmony_ci * FIXME: this needs a review. 9062306a36Sopenharmony_ci * also: move to cx88-blackbird + cx88-dvb source files? 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci dprintk(1, "core->active_type_id = 0x%08x\n", core->active_type_id); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if ((core->active_type_id == CX88_MPEG_DVB) && 9662306a36Sopenharmony_ci (core->board.mpeg & CX88_MPEG_DVB)) { 9762306a36Sopenharmony_ci dprintk(1, "cx8802_start_dma doing .dvb\n"); 9862306a36Sopenharmony_ci /* negedge driven & software reset */ 9962306a36Sopenharmony_ci cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl); 10062306a36Sopenharmony_ci udelay(100); 10162306a36Sopenharmony_ci cx_write(MO_PINMUX_IO, 0x00); 10262306a36Sopenharmony_ci cx_write(TS_HW_SOP_CNTRL, 0x47 << 16 | 188 << 4 | 0x01); 10362306a36Sopenharmony_ci switch (core->boardnr) { 10462306a36Sopenharmony_ci case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q: 10562306a36Sopenharmony_ci case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T: 10662306a36Sopenharmony_ci case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: 10762306a36Sopenharmony_ci case CX88_BOARD_PCHDTV_HD5500: 10862306a36Sopenharmony_ci cx_write(TS_SOP_STAT, 1 << 13); 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci case CX88_BOARD_SAMSUNG_SMT_7020: 11162306a36Sopenharmony_ci cx_write(TS_SOP_STAT, 0x00); 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: 11462306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: 11562306a36Sopenharmony_ci /* Enable MPEG parallel IO and video signal pins */ 11662306a36Sopenharmony_ci cx_write(MO_PINMUX_IO, 0x88); 11762306a36Sopenharmony_ci udelay(100); 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_HVR1300: 12062306a36Sopenharmony_ci /* Enable MPEG parallel IO and video signal pins */ 12162306a36Sopenharmony_ci cx_write(MO_PINMUX_IO, 0x88); 12262306a36Sopenharmony_ci cx_write(TS_SOP_STAT, 0); 12362306a36Sopenharmony_ci cx_write(TS_VALERR_CNTRL, 0); 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci case CX88_BOARD_PINNACLE_PCTV_HD_800i: 12662306a36Sopenharmony_ci /* Enable MPEG parallel IO and video signal pins */ 12762306a36Sopenharmony_ci cx_write(MO_PINMUX_IO, 0x88); 12862306a36Sopenharmony_ci cx_write(TS_HW_SOP_CNTRL, (0x47 << 16) | (188 << 4)); 12962306a36Sopenharmony_ci dev->ts_gen_cntrl = 5; 13062306a36Sopenharmony_ci cx_write(TS_SOP_STAT, 0); 13162306a36Sopenharmony_ci cx_write(TS_VALERR_CNTRL, 0); 13262306a36Sopenharmony_ci udelay(100); 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci default: 13562306a36Sopenharmony_ci cx_write(TS_SOP_STAT, 0x00); 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl); 13962306a36Sopenharmony_ci udelay(100); 14062306a36Sopenharmony_ci } else if ((core->active_type_id == CX88_MPEG_BLACKBIRD) && 14162306a36Sopenharmony_ci (core->board.mpeg & CX88_MPEG_BLACKBIRD)) { 14262306a36Sopenharmony_ci dprintk(1, "cx8802_start_dma doing .blackbird\n"); 14362306a36Sopenharmony_ci cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* punctured clock TS & posedge driven & software reset */ 14662306a36Sopenharmony_ci cx_write(TS_GEN_CNTRL, 0x46); 14762306a36Sopenharmony_ci udelay(100); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */ 15062306a36Sopenharmony_ci cx_write(TS_VALERR_CNTRL, 0x2000); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* punctured clock TS & posedge driven */ 15362306a36Sopenharmony_ci cx_write(TS_GEN_CNTRL, 0x06); 15462306a36Sopenharmony_ci udelay(100); 15562306a36Sopenharmony_ci } else { 15662306a36Sopenharmony_ci pr_err("%s() Failed. Unsupported value in .mpeg (0x%08x)\n", 15762306a36Sopenharmony_ci __func__, core->board.mpeg); 15862306a36Sopenharmony_ci return -EINVAL; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* reset counter */ 16262306a36Sopenharmony_ci cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET); 16362306a36Sopenharmony_ci q->count = 0; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* clear interrupt status register */ 16662306a36Sopenharmony_ci cx_write(MO_TS_INTSTAT, 0x1f1111); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* enable irqs */ 16962306a36Sopenharmony_ci dprintk(1, "setting the interrupt mask\n"); 17062306a36Sopenharmony_ci cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_TSINT); 17162306a36Sopenharmony_ci cx_set(MO_TS_INTMSK, 0x1f0011); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* start dma */ 17462306a36Sopenharmony_ci cx_set(MO_DEV_CNTRL2, (1 << 5)); 17562306a36Sopenharmony_ci cx_set(MO_TS_DMACNTRL, 0x11); 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ciEXPORT_SYMBOL(cx8802_start_dma); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int cx8802_stop_dma(struct cx8802_dev *dev) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct cx88_core *core = dev->core; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci dprintk(1, "\n"); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* stop dma */ 18762306a36Sopenharmony_ci cx_clear(MO_TS_DMACNTRL, 0x11); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* disable irqs */ 19062306a36Sopenharmony_ci cx_clear(MO_PCI_INTMSK, PCI_INT_TSINT); 19162306a36Sopenharmony_ci cx_clear(MO_TS_INTMSK, 0x1f0011); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Reset the controller */ 19462306a36Sopenharmony_ci cx_write(TS_GEN_CNTRL, 0xcd); 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int cx8802_restart_queue(struct cx8802_dev *dev, 19962306a36Sopenharmony_ci struct cx88_dmaqueue *q) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct cx88_buffer *buf; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci dprintk(1, "\n"); 20462306a36Sopenharmony_ci if (list_empty(&q->active)) 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci buf = list_entry(q->active.next, struct cx88_buffer, list); 20862306a36Sopenharmony_ci dprintk(2, "restart_queue [%p/%d]: restart dma\n", 20962306a36Sopenharmony_ci buf, buf->vb.vb2_buf.index); 21062306a36Sopenharmony_ci cx8802_start_dma(dev, q, buf); 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ciint cx8802_buf_prepare(struct vb2_queue *q, struct cx8802_dev *dev, 21762306a36Sopenharmony_ci struct cx88_buffer *buf) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci int size = dev->ts_packet_size * dev->ts_packet_count; 22062306a36Sopenharmony_ci struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0); 22162306a36Sopenharmony_ci struct cx88_riscmem *risc = &buf->risc; 22262306a36Sopenharmony_ci int rc; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (vb2_plane_size(&buf->vb.vb2_buf, 0) < size) 22562306a36Sopenharmony_ci return -EINVAL; 22662306a36Sopenharmony_ci vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci rc = cx88_risc_databuffer(dev->pci, risc, sgt->sgl, 22962306a36Sopenharmony_ci dev->ts_packet_size, dev->ts_packet_count, 0); 23062306a36Sopenharmony_ci if (rc) { 23162306a36Sopenharmony_ci if (risc->cpu) 23262306a36Sopenharmony_ci dma_free_coherent(&dev->pci->dev, risc->size, 23362306a36Sopenharmony_ci risc->cpu, risc->dma); 23462306a36Sopenharmony_ci memset(risc, 0, sizeof(*risc)); 23562306a36Sopenharmony_ci return rc; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ciEXPORT_SYMBOL(cx8802_buf_prepare); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_civoid cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct cx88_buffer *prev; 24462306a36Sopenharmony_ci struct cx88_dmaqueue *cx88q = &dev->mpegq; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci dprintk(1, "\n"); 24762306a36Sopenharmony_ci /* add jump to start */ 24862306a36Sopenharmony_ci buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 8); 24962306a36Sopenharmony_ci buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC); 25062306a36Sopenharmony_ci buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 8); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (list_empty(&cx88q->active)) { 25362306a36Sopenharmony_ci dprintk(1, "queue is empty - first active\n"); 25462306a36Sopenharmony_ci list_add_tail(&buf->list, &cx88q->active); 25562306a36Sopenharmony_ci dprintk(1, "[%p/%d] %s - first active\n", 25662306a36Sopenharmony_ci buf, buf->vb.vb2_buf.index, __func__); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci } else { 25962306a36Sopenharmony_ci buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); 26062306a36Sopenharmony_ci dprintk(1, "queue is not empty - append to active\n"); 26162306a36Sopenharmony_ci prev = list_entry(cx88q->active.prev, struct cx88_buffer, list); 26262306a36Sopenharmony_ci list_add_tail(&buf->list, &cx88q->active); 26362306a36Sopenharmony_ci prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); 26462306a36Sopenharmony_ci dprintk(1, "[%p/%d] %s - append to active\n", 26562306a36Sopenharmony_ci buf, buf->vb.vb2_buf.index, __func__); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ciEXPORT_SYMBOL(cx8802_buf_queue); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* ----------------------------------------------------------- */ 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void do_cancel_buffers(struct cx8802_dev *dev) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct cx88_dmaqueue *q = &dev->mpegq; 27562306a36Sopenharmony_ci struct cx88_buffer *buf; 27662306a36Sopenharmony_ci unsigned long flags; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 27962306a36Sopenharmony_ci while (!list_empty(&q->active)) { 28062306a36Sopenharmony_ci buf = list_entry(q->active.next, struct cx88_buffer, list); 28162306a36Sopenharmony_ci list_del(&buf->list); 28262306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_civoid cx8802_cancel_buffers(struct cx8802_dev *dev) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci dprintk(1, "\n"); 29062306a36Sopenharmony_ci cx8802_stop_dma(dev); 29162306a36Sopenharmony_ci do_cancel_buffers(dev); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL(cx8802_cancel_buffers); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic const char *cx88_mpeg_irqs[32] = { 29662306a36Sopenharmony_ci "ts_risci1", NULL, NULL, NULL, 29762306a36Sopenharmony_ci "ts_risci2", NULL, NULL, NULL, 29862306a36Sopenharmony_ci "ts_oflow", NULL, NULL, NULL, 29962306a36Sopenharmony_ci "ts_sync", NULL, NULL, NULL, 30062306a36Sopenharmony_ci "opc_err", "par_err", "rip_err", "pci_abort", 30162306a36Sopenharmony_ci "ts_err?", 30262306a36Sopenharmony_ci}; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic void cx8802_mpeg_irq(struct cx8802_dev *dev) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct cx88_core *core = dev->core; 30762306a36Sopenharmony_ci u32 status, mask, count; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci dprintk(1, "\n"); 31062306a36Sopenharmony_ci status = cx_read(MO_TS_INTSTAT); 31162306a36Sopenharmony_ci mask = cx_read(MO_TS_INTMSK); 31262306a36Sopenharmony_ci if (0 == (status & mask)) 31362306a36Sopenharmony_ci return; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci cx_write(MO_TS_INTSTAT, status); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (debug || (status & mask & ~0xff)) 31862306a36Sopenharmony_ci cx88_print_irqbits("irq mpeg ", 31962306a36Sopenharmony_ci cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs), 32062306a36Sopenharmony_ci status, mask); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* risc op code error */ 32362306a36Sopenharmony_ci if (status & (1 << 16)) { 32462306a36Sopenharmony_ci pr_warn("mpeg risc op code error\n"); 32562306a36Sopenharmony_ci cx_clear(MO_TS_DMACNTRL, 0x11); 32662306a36Sopenharmony_ci cx88_sram_channel_dump(dev->core, 32762306a36Sopenharmony_ci &cx88_sram_channels[SRAM_CH28]); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* risc1 y */ 33162306a36Sopenharmony_ci if (status & 0x01) { 33262306a36Sopenharmony_ci dprintk(1, "wake up\n"); 33362306a36Sopenharmony_ci spin_lock(&dev->slock); 33462306a36Sopenharmony_ci count = cx_read(MO_TS_GPCNT); 33562306a36Sopenharmony_ci cx88_wakeup(dev->core, &dev->mpegq, count); 33662306a36Sopenharmony_ci spin_unlock(&dev->slock); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* other general errors */ 34062306a36Sopenharmony_ci if (status & 0x1f0100) { 34162306a36Sopenharmony_ci dprintk(0, "general errors: 0x%08x\n", status & 0x1f0100); 34262306a36Sopenharmony_ci spin_lock(&dev->slock); 34362306a36Sopenharmony_ci cx8802_stop_dma(dev); 34462306a36Sopenharmony_ci spin_unlock(&dev->slock); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci#define MAX_IRQ_LOOP 10 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic irqreturn_t cx8802_irq(int irq, void *dev_id) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct cx8802_dev *dev = dev_id; 35362306a36Sopenharmony_ci struct cx88_core *core = dev->core; 35462306a36Sopenharmony_ci u32 status; 35562306a36Sopenharmony_ci int loop, handled = 0; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci for (loop = 0; loop < MAX_IRQ_LOOP; loop++) { 35862306a36Sopenharmony_ci status = cx_read(MO_PCI_INTSTAT) & 35962306a36Sopenharmony_ci (core->pci_irqmask | PCI_INT_TSINT); 36062306a36Sopenharmony_ci if (status == 0) 36162306a36Sopenharmony_ci goto out; 36262306a36Sopenharmony_ci dprintk(1, "cx8802_irq\n"); 36362306a36Sopenharmony_ci dprintk(1, " loop: %d/%d\n", loop, MAX_IRQ_LOOP); 36462306a36Sopenharmony_ci dprintk(1, " status: %d\n", status); 36562306a36Sopenharmony_ci handled = 1; 36662306a36Sopenharmony_ci cx_write(MO_PCI_INTSTAT, status); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (status & core->pci_irqmask) 36962306a36Sopenharmony_ci cx88_core_irq(core, status); 37062306a36Sopenharmony_ci if (status & PCI_INT_TSINT) 37162306a36Sopenharmony_ci cx8802_mpeg_irq(dev); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci if (loop == MAX_IRQ_LOOP) { 37462306a36Sopenharmony_ci dprintk(0, "clearing mask\n"); 37562306a36Sopenharmony_ci pr_warn("irq loop -- clearing mask\n"); 37662306a36Sopenharmony_ci cx_write(MO_PCI_INTMSK, 0); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci out: 38062306a36Sopenharmony_ci return IRQ_RETVAL(handled); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int cx8802_init_common(struct cx8802_dev *dev) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct cx88_core *core = dev->core; 38662306a36Sopenharmony_ci int err; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* pci init */ 38962306a36Sopenharmony_ci if (pci_enable_device(dev->pci)) 39062306a36Sopenharmony_ci return -EIO; 39162306a36Sopenharmony_ci pci_set_master(dev->pci); 39262306a36Sopenharmony_ci err = dma_set_mask(&dev->pci->dev, DMA_BIT_MASK(32)); 39362306a36Sopenharmony_ci if (err) { 39462306a36Sopenharmony_ci pr_err("Oops: no 32bit PCI DMA ???\n"); 39562306a36Sopenharmony_ci return -EIO; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci dev->pci_rev = dev->pci->revision; 39962306a36Sopenharmony_ci pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat); 40062306a36Sopenharmony_ci pr_info("found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n", 40162306a36Sopenharmony_ci pci_name(dev->pci), dev->pci_rev, dev->pci->irq, 40262306a36Sopenharmony_ci dev->pci_lat, 40362306a36Sopenharmony_ci (unsigned long long)pci_resource_start(dev->pci, 0)); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* initialize driver struct */ 40662306a36Sopenharmony_ci spin_lock_init(&dev->slock); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* init dma queue */ 40962306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->mpegq.active); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* get irq */ 41262306a36Sopenharmony_ci err = request_irq(dev->pci->irq, cx8802_irq, 41362306a36Sopenharmony_ci IRQF_SHARED, dev->core->name, dev); 41462306a36Sopenharmony_ci if (err < 0) { 41562306a36Sopenharmony_ci pr_err("can't get IRQ %d\n", dev->pci->irq); 41662306a36Sopenharmony_ci return err; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci cx_set(MO_PCI_INTMSK, core->pci_irqmask); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* everything worked */ 42162306a36Sopenharmony_ci pci_set_drvdata(dev->pci, dev); 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void cx8802_fini_common(struct cx8802_dev *dev) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci dprintk(2, "\n"); 42862306a36Sopenharmony_ci cx8802_stop_dma(dev); 42962306a36Sopenharmony_ci pci_disable_device(dev->pci); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* unregister stuff */ 43262306a36Sopenharmony_ci free_irq(dev->pci->irq, dev); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/* ----------------------------------------------------------- */ 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct cx8802_dev *dev = pci_get_drvdata(pci_dev); 44062306a36Sopenharmony_ci unsigned long flags; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* stop mpeg dma */ 44362306a36Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 44462306a36Sopenharmony_ci if (!list_empty(&dev->mpegq.active)) { 44562306a36Sopenharmony_ci dprintk(2, "suspend\n"); 44662306a36Sopenharmony_ci pr_info("suspend mpeg\n"); 44762306a36Sopenharmony_ci cx8802_stop_dma(dev); 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* FIXME -- shutdown device */ 45262306a36Sopenharmony_ci cx88_shutdown(dev->core); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci pci_save_state(pci_dev); 45562306a36Sopenharmony_ci if (pci_set_power_state(pci_dev, 45662306a36Sopenharmony_ci pci_choose_state(pci_dev, state)) != 0) { 45762306a36Sopenharmony_ci pci_disable_device(pci_dev); 45862306a36Sopenharmony_ci dev->state.disabled = 1; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic int cx8802_resume_common(struct pci_dev *pci_dev) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct cx8802_dev *dev = pci_get_drvdata(pci_dev); 46662306a36Sopenharmony_ci unsigned long flags; 46762306a36Sopenharmony_ci int err; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (dev->state.disabled) { 47062306a36Sopenharmony_ci err = pci_enable_device(pci_dev); 47162306a36Sopenharmony_ci if (err) { 47262306a36Sopenharmony_ci pr_err("can't enable device\n"); 47362306a36Sopenharmony_ci return err; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci dev->state.disabled = 0; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci err = pci_set_power_state(pci_dev, PCI_D0); 47862306a36Sopenharmony_ci if (err) { 47962306a36Sopenharmony_ci pr_err("can't enable device\n"); 48062306a36Sopenharmony_ci pci_disable_device(pci_dev); 48162306a36Sopenharmony_ci dev->state.disabled = 1; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return err; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci pci_restore_state(pci_dev); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* FIXME: re-initialize hardware */ 48862306a36Sopenharmony_ci cx88_reset(dev->core); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* restart video+vbi capture */ 49162306a36Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 49262306a36Sopenharmony_ci if (!list_empty(&dev->mpegq.active)) { 49362306a36Sopenharmony_ci pr_info("resume mpeg\n"); 49462306a36Sopenharmony_ci cx8802_restart_queue(dev, &dev->mpegq); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistruct cx8802_driver *cx8802_get_driver(struct cx8802_dev *dev, 50262306a36Sopenharmony_ci enum cx88_board_type btype) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct cx8802_driver *d; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci list_for_each_entry(d, &dev->drvlist, drvlist) 50762306a36Sopenharmony_ci if (d->type_id == btype) 50862306a36Sopenharmony_ci return d; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return NULL; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ciEXPORT_SYMBOL(cx8802_get_driver); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci/* Driver asked for hardware access. */ 51562306a36Sopenharmony_cistatic int cx8802_request_acquire(struct cx8802_driver *drv) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct cx88_core *core = drv->core; 51862306a36Sopenharmony_ci unsigned int i; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Fail a request for hardware if the device is busy. */ 52162306a36Sopenharmony_ci if (core->active_type_id != CX88_BOARD_NONE && 52262306a36Sopenharmony_ci core->active_type_id != drv->type_id) 52362306a36Sopenharmony_ci return -EBUSY; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (drv->type_id == CX88_MPEG_DVB) { 52662306a36Sopenharmony_ci /* When switching to DVB, always set the input to the tuner */ 52762306a36Sopenharmony_ci core->last_analog_input = core->input; 52862306a36Sopenharmony_ci core->input = 0; 52962306a36Sopenharmony_ci for (i = 0; 53062306a36Sopenharmony_ci i < ARRAY_SIZE(core->board.input); 53162306a36Sopenharmony_ci i++) { 53262306a36Sopenharmony_ci if (core->board.input[i].type == CX88_VMUX_DVB) { 53362306a36Sopenharmony_ci core->input = i; 53462306a36Sopenharmony_ci break; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (drv->advise_acquire) { 54062306a36Sopenharmony_ci core->active_ref++; 54162306a36Sopenharmony_ci if (core->active_type_id == CX88_BOARD_NONE) { 54262306a36Sopenharmony_ci core->active_type_id = drv->type_id; 54362306a36Sopenharmony_ci drv->advise_acquire(drv); 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci dprintk(1, "Post acquire GPIO=%x\n", cx_read(MO_GP0_IO)); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return 0; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci/* Driver asked to release hardware. */ 55362306a36Sopenharmony_cistatic int cx8802_request_release(struct cx8802_driver *drv) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct cx88_core *core = drv->core; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (drv->advise_release && --core->active_ref == 0) { 55862306a36Sopenharmony_ci if (drv->type_id == CX88_MPEG_DVB) { 55962306a36Sopenharmony_ci /* 56062306a36Sopenharmony_ci * If the DVB driver is releasing, reset the input 56162306a36Sopenharmony_ci * state to the last configured analog input 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_ci core->input = core->last_analog_input; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci drv->advise_release(drv); 56762306a36Sopenharmony_ci core->active_type_id = CX88_BOARD_NONE; 56862306a36Sopenharmony_ci dprintk(1, "Post release GPIO=%x\n", cx_read(MO_GP0_IO)); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int cx8802_check_driver(struct cx8802_driver *drv) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci if (!drv) 57762306a36Sopenharmony_ci return -ENODEV; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if ((drv->type_id != CX88_MPEG_DVB) && 58062306a36Sopenharmony_ci (drv->type_id != CX88_MPEG_BLACKBIRD)) 58162306a36Sopenharmony_ci return -EINVAL; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if ((drv->hw_access != CX8802_DRVCTL_SHARED) && 58462306a36Sopenharmony_ci (drv->hw_access != CX8802_DRVCTL_EXCLUSIVE)) 58562306a36Sopenharmony_ci return -EINVAL; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if ((!drv->probe) || 58862306a36Sopenharmony_ci (!drv->remove) || 58962306a36Sopenharmony_ci (!drv->advise_acquire) || 59062306a36Sopenharmony_ci (!drv->advise_release)) 59162306a36Sopenharmony_ci return -EINVAL; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return 0; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ciint cx8802_register_driver(struct cx8802_driver *drv) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct cx8802_dev *dev; 59962306a36Sopenharmony_ci struct cx8802_driver *driver; 60062306a36Sopenharmony_ci int err, i = 0; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci pr_info("registering cx8802 driver, type: %s access: %s\n", 60362306a36Sopenharmony_ci drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird", 60462306a36Sopenharmony_ci drv->hw_access == CX8802_DRVCTL_SHARED ? 60562306a36Sopenharmony_ci "shared" : "exclusive"); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci err = cx8802_check_driver(drv); 60862306a36Sopenharmony_ci if (err) { 60962306a36Sopenharmony_ci pr_err("cx8802_driver is invalid\n"); 61062306a36Sopenharmony_ci return err; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci mutex_lock(&cx8802_mutex); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci list_for_each_entry(dev, &cx8802_devlist, devlist) { 61662306a36Sopenharmony_ci pr_info("subsystem: %04x:%04x, board: %s [card=%d]\n", 61762306a36Sopenharmony_ci dev->pci->subsystem_vendor, 61862306a36Sopenharmony_ci dev->pci->subsystem_device, dev->core->board.name, 61962306a36Sopenharmony_ci dev->core->boardnr); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* Bring up a new struct for each driver instance */ 62262306a36Sopenharmony_ci driver = kzalloc(sizeof(*drv), GFP_KERNEL); 62362306a36Sopenharmony_ci if (!driver) { 62462306a36Sopenharmony_ci err = -ENOMEM; 62562306a36Sopenharmony_ci goto out; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* Snapshot of the driver registration data */ 62962306a36Sopenharmony_ci drv->core = dev->core; 63062306a36Sopenharmony_ci drv->suspend = cx8802_suspend_common; 63162306a36Sopenharmony_ci drv->resume = cx8802_resume_common; 63262306a36Sopenharmony_ci drv->request_acquire = cx8802_request_acquire; 63362306a36Sopenharmony_ci drv->request_release = cx8802_request_release; 63462306a36Sopenharmony_ci memcpy(driver, drv, sizeof(*driver)); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci mutex_lock(&drv->core->lock); 63762306a36Sopenharmony_ci err = drv->probe(driver); 63862306a36Sopenharmony_ci if (err == 0) { 63962306a36Sopenharmony_ci i++; 64062306a36Sopenharmony_ci list_add_tail(&driver->drvlist, &dev->drvlist); 64162306a36Sopenharmony_ci } else { 64262306a36Sopenharmony_ci pr_err("cx8802 probe failed, err = %d\n", err); 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci mutex_unlock(&drv->core->lock); 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci err = i ? 0 : -ENODEV; 64862306a36Sopenharmony_ciout: 64962306a36Sopenharmony_ci mutex_unlock(&cx8802_mutex); 65062306a36Sopenharmony_ci return err; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ciEXPORT_SYMBOL(cx8802_register_driver); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ciint cx8802_unregister_driver(struct cx8802_driver *drv) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct cx8802_dev *dev; 65762306a36Sopenharmony_ci struct cx8802_driver *d, *dtmp; 65862306a36Sopenharmony_ci int err = 0; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci pr_info("unregistering cx8802 driver, type: %s access: %s\n", 66162306a36Sopenharmony_ci drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird", 66262306a36Sopenharmony_ci drv->hw_access == CX8802_DRVCTL_SHARED ? 66362306a36Sopenharmony_ci "shared" : "exclusive"); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci mutex_lock(&cx8802_mutex); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci list_for_each_entry(dev, &cx8802_devlist, devlist) { 66862306a36Sopenharmony_ci pr_info("subsystem: %04x:%04x, board: %s [card=%d]\n", 66962306a36Sopenharmony_ci dev->pci->subsystem_vendor, 67062306a36Sopenharmony_ci dev->pci->subsystem_device, dev->core->board.name, 67162306a36Sopenharmony_ci dev->core->boardnr); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci mutex_lock(&dev->core->lock); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) { 67662306a36Sopenharmony_ci /* only unregister the correct driver type */ 67762306a36Sopenharmony_ci if (d->type_id != drv->type_id) 67862306a36Sopenharmony_ci continue; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci err = d->remove(d); 68162306a36Sopenharmony_ci if (err == 0) { 68262306a36Sopenharmony_ci list_del(&d->drvlist); 68362306a36Sopenharmony_ci kfree(d); 68462306a36Sopenharmony_ci } else 68562306a36Sopenharmony_ci pr_err("cx8802 driver remove failed (%d)\n", 68662306a36Sopenharmony_ci err); 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci mutex_unlock(&dev->core->lock); 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci mutex_unlock(&cx8802_mutex); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return err; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ciEXPORT_SYMBOL(cx8802_unregister_driver); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci/* ----------------------------------------------------------- */ 69962306a36Sopenharmony_cistatic int cx8802_probe(struct pci_dev *pci_dev, 70062306a36Sopenharmony_ci const struct pci_device_id *pci_id) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct cx8802_dev *dev; 70362306a36Sopenharmony_ci struct cx88_core *core; 70462306a36Sopenharmony_ci int err; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* general setup */ 70762306a36Sopenharmony_ci core = cx88_core_get(pci_dev); 70862306a36Sopenharmony_ci if (!core) 70962306a36Sopenharmony_ci return -EINVAL; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci pr_info("cx2388x 8802 Driver Manager\n"); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci err = -ENODEV; 71462306a36Sopenharmony_ci if (!core->board.mpeg) 71562306a36Sopenharmony_ci goto fail_core; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci err = -ENOMEM; 71862306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 71962306a36Sopenharmony_ci if (!dev) 72062306a36Sopenharmony_ci goto fail_core; 72162306a36Sopenharmony_ci dev->pci = pci_dev; 72262306a36Sopenharmony_ci dev->core = core; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* Maintain a reference so cx88-video can query the 8802 device. */ 72562306a36Sopenharmony_ci core->dvbdev = dev; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci err = cx8802_init_common(dev); 72862306a36Sopenharmony_ci if (err != 0) 72962306a36Sopenharmony_ci goto fail_dev; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->drvlist); 73262306a36Sopenharmony_ci mutex_lock(&cx8802_mutex); 73362306a36Sopenharmony_ci list_add_tail(&dev->devlist, &cx8802_devlist); 73462306a36Sopenharmony_ci mutex_unlock(&cx8802_mutex); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* now autoload cx88-dvb or cx88-blackbird */ 73762306a36Sopenharmony_ci request_modules(dev); 73862306a36Sopenharmony_ci return 0; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci fail_dev: 74162306a36Sopenharmony_ci kfree(dev); 74262306a36Sopenharmony_ci fail_core: 74362306a36Sopenharmony_ci core->dvbdev = NULL; 74462306a36Sopenharmony_ci cx88_core_put(core, pci_dev); 74562306a36Sopenharmony_ci return err; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic void cx8802_remove(struct pci_dev *pci_dev) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci struct cx8802_dev *dev; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci dev = pci_get_drvdata(pci_dev); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci dprintk(1, "%s\n", __func__); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci flush_request_modules(dev); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci mutex_lock(&dev->core->lock); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (!list_empty(&dev->drvlist)) { 76162306a36Sopenharmony_ci struct cx8802_driver *drv, *tmp; 76262306a36Sopenharmony_ci int err; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci pr_warn("Trying to remove cx8802 driver while cx8802 sub-drivers still loaded?!\n"); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) { 76762306a36Sopenharmony_ci err = drv->remove(drv); 76862306a36Sopenharmony_ci if (err == 0) { 76962306a36Sopenharmony_ci list_del(&drv->drvlist); 77062306a36Sopenharmony_ci } else 77162306a36Sopenharmony_ci pr_err("cx8802 driver remove failed (%d)\n", 77262306a36Sopenharmony_ci err); 77362306a36Sopenharmony_ci kfree(drv); 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci mutex_unlock(&dev->core->lock); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* Destroy any 8802 reference. */ 78062306a36Sopenharmony_ci dev->core->dvbdev = NULL; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* common */ 78362306a36Sopenharmony_ci cx8802_fini_common(dev); 78462306a36Sopenharmony_ci cx88_core_put(dev->core, dev->pci); 78562306a36Sopenharmony_ci kfree(dev); 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic const struct pci_device_id cx8802_pci_tbl[] = { 78962306a36Sopenharmony_ci { 79062306a36Sopenharmony_ci .vendor = 0x14f1, 79162306a36Sopenharmony_ci .device = 0x8802, 79262306a36Sopenharmony_ci .subvendor = PCI_ANY_ID, 79362306a36Sopenharmony_ci .subdevice = PCI_ANY_ID, 79462306a36Sopenharmony_ci }, { 79562306a36Sopenharmony_ci /* --- end of list --- */ 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci}; 79862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic struct pci_driver cx8802_pci_driver = { 80162306a36Sopenharmony_ci .name = "cx88-mpeg driver manager", 80262306a36Sopenharmony_ci .id_table = cx8802_pci_tbl, 80362306a36Sopenharmony_ci .probe = cx8802_probe, 80462306a36Sopenharmony_ci .remove = cx8802_remove, 80562306a36Sopenharmony_ci}; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cimodule_pci_driver(cx8802_pci_driver); 808