Path: blob/master/drivers/media/video/cx88/cx88-mpeg.c
17687 views
/*1*2* Support for the mpeg transport stream transfers3* PCI function #2 of the cx2388x.4*5* (c) 2004 Jelle Foks <[email protected]>6* (c) 2004 Chris Pascoe <[email protected]>7* (c) 2004 Gerd Knorr <[email protected]>8*9* This program is free software; you can redistribute it and/or modify10* it under the terms of the GNU General Public License as published by11* the Free Software Foundation; either version 2 of the License, or12* (at your option) any later version.13*14* This program is distributed in the hope that it will be useful,15* but WITHOUT ANY WARRANTY; without even the implied warranty of16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the17* GNU General Public License for more details.18*19* You should have received a copy of the GNU General Public License20* along with this program; if not, write to the Free Software21* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.22*/2324#include <linux/module.h>25#include <linux/slab.h>26#include <linux/init.h>27#include <linux/device.h>28#include <linux/dma-mapping.h>29#include <linux/interrupt.h>30#include <asm/delay.h>3132#include "cx88.h"3334/* ------------------------------------------------------------------ */3536MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards");37MODULE_AUTHOR("Jelle Foks <[email protected]>");38MODULE_AUTHOR("Chris Pascoe <[email protected]>");39MODULE_AUTHOR("Gerd Knorr <[email protected]> [SuSE Labs]");40MODULE_LICENSE("GPL");4142static unsigned int debug;43module_param(debug,int,0644);44MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");4546#define dprintk(level,fmt, arg...) if (debug >= level) \47printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg)4849#define mpeg_dbg(level,fmt, arg...) if (debug >= level) \50printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg)5152#if defined(CONFIG_MODULES) && defined(MODULE)53static void request_module_async(struct work_struct *work)54{55struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk);5657if (dev->core->board.mpeg & CX88_MPEG_DVB)58request_module("cx88-dvb");59if (dev->core->board.mpeg & CX88_MPEG_BLACKBIRD)60request_module("cx88-blackbird");61}6263static void request_modules(struct cx8802_dev *dev)64{65INIT_WORK(&dev->request_module_wk, request_module_async);66schedule_work(&dev->request_module_wk);67}6869static void flush_request_modules(struct cx8802_dev *dev)70{71flush_work_sync(&dev->request_module_wk);72}73#else74#define request_modules(dev)75#define flush_request_modules(dev)76#endif /* CONFIG_MODULES */777879static LIST_HEAD(cx8802_devlist);80static DEFINE_MUTEX(cx8802_mutex);81/* ------------------------------------------------------------------ */8283static int cx8802_start_dma(struct cx8802_dev *dev,84struct cx88_dmaqueue *q,85struct cx88_buffer *buf)86{87struct cx88_core *core = dev->core;8889dprintk(1, "cx8802_start_dma w: %d, h: %d, f: %d\n",90buf->vb.width, buf->vb.height, buf->vb.field);9192/* setup fifo + format */93cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28],94dev->ts_packet_size, buf->risc.dma);9596/* write TS length to chip */97cx_write(MO_TS_LNGTH, buf->vb.width);9899/* FIXME: this needs a review.100* also: move to cx88-blackbird + cx88-dvb source files? */101102dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id);103104if ( (core->active_type_id == CX88_MPEG_DVB) &&105(core->board.mpeg & CX88_MPEG_DVB) ) {106107dprintk( 1, "cx8802_start_dma doing .dvb\n");108/* negedge driven & software reset */109cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);110udelay(100);111cx_write(MO_PINMUX_IO, 0x00);112cx_write(TS_HW_SOP_CNTRL, 0x47<<16|188<<4|0x01);113switch (core->boardnr) {114case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:115case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:116case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:117case CX88_BOARD_PCHDTV_HD5500:118cx_write(TS_SOP_STAT, 1<<13);119break;120case CX88_BOARD_SAMSUNG_SMT_7020:121cx_write(TS_SOP_STAT, 0x00);122break;123case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:124case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:125cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */126udelay(100);127break;128case CX88_BOARD_HAUPPAUGE_HVR1300:129/* Enable MPEG parallel IO and video signal pins */130cx_write(MO_PINMUX_IO, 0x88);131cx_write(TS_SOP_STAT, 0);132cx_write(TS_VALERR_CNTRL, 0);133break;134case CX88_BOARD_PINNACLE_PCTV_HD_800i:135/* Enable MPEG parallel IO and video signal pins */136cx_write(MO_PINMUX_IO, 0x88);137cx_write(TS_HW_SOP_CNTRL, (0x47 << 16) | (188 << 4));138dev->ts_gen_cntrl = 5;139cx_write(TS_SOP_STAT, 0);140cx_write(TS_VALERR_CNTRL, 0);141udelay(100);142break;143default:144cx_write(TS_SOP_STAT, 0x00);145break;146}147cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl);148udelay(100);149} else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) &&150(core->board.mpeg & CX88_MPEG_BLACKBIRD) ) {151dprintk( 1, "cx8802_start_dma doing .blackbird\n");152cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */153154cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */155udelay(100);156157cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */158cx_write(TS_VALERR_CNTRL, 0x2000);159160cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */161udelay(100);162} else {163printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __func__,164core->board.mpeg );165return -EINVAL;166}167168/* reset counter */169cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET);170q->count = 1;171172/* enable irqs */173dprintk( 1, "setting the interrupt mask\n" );174cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_TSINT);175cx_set(MO_TS_INTMSK, 0x1f0011);176177/* start dma */178cx_set(MO_DEV_CNTRL2, (1<<5));179cx_set(MO_TS_DMACNTRL, 0x11);180return 0;181}182183static int cx8802_stop_dma(struct cx8802_dev *dev)184{185struct cx88_core *core = dev->core;186dprintk( 1, "cx8802_stop_dma\n" );187188/* stop dma */189cx_clear(MO_TS_DMACNTRL, 0x11);190191/* disable irqs */192cx_clear(MO_PCI_INTMSK, PCI_INT_TSINT);193cx_clear(MO_TS_INTMSK, 0x1f0011);194195/* Reset the controller */196cx_write(TS_GEN_CNTRL, 0xcd);197return 0;198}199200static int cx8802_restart_queue(struct cx8802_dev *dev,201struct cx88_dmaqueue *q)202{203struct cx88_buffer *buf;204205dprintk( 1, "cx8802_restart_queue\n" );206if (list_empty(&q->active))207{208struct cx88_buffer *prev;209prev = NULL;210211dprintk(1, "cx8802_restart_queue: queue is empty\n" );212213for (;;) {214if (list_empty(&q->queued))215return 0;216buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);217if (NULL == prev) {218list_del(&buf->vb.queue);219list_add_tail(&buf->vb.queue,&q->active);220cx8802_start_dma(dev, q, buf);221buf->vb.state = VIDEOBUF_ACTIVE;222buf->count = q->count++;223mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);224dprintk(1,"[%p/%d] restart_queue - first active\n",225buf,buf->vb.i);226227} else if (prev->vb.width == buf->vb.width &&228prev->vb.height == buf->vb.height &&229prev->fmt == buf->fmt) {230list_del(&buf->vb.queue);231list_add_tail(&buf->vb.queue,&q->active);232buf->vb.state = VIDEOBUF_ACTIVE;233buf->count = q->count++;234prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);235dprintk(1,"[%p/%d] restart_queue - move to active\n",236buf,buf->vb.i);237} else {238return 0;239}240prev = buf;241}242return 0;243}244245buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);246dprintk(2,"restart_queue [%p/%d]: restart dma\n",247buf, buf->vb.i);248cx8802_start_dma(dev, q, buf);249list_for_each_entry(buf, &q->active, vb.queue)250buf->count = q->count++;251mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);252return 0;253}254255/* ------------------------------------------------------------------ */256257int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev,258struct cx88_buffer *buf, enum v4l2_field field)259{260int size = dev->ts_packet_size * dev->ts_packet_count;261struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);262int rc;263264dprintk(1, "%s: %p\n", __func__, buf);265if (0 != buf->vb.baddr && buf->vb.bsize < size)266return -EINVAL;267268if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {269buf->vb.width = dev->ts_packet_size;270buf->vb.height = dev->ts_packet_count;271buf->vb.size = size;272buf->vb.field = field /*V4L2_FIELD_TOP*/;273274if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))275goto fail;276cx88_risc_databuffer(dev->pci, &buf->risc,277dma->sglist,278buf->vb.width, buf->vb.height, 0);279}280buf->vb.state = VIDEOBUF_PREPARED;281return 0;282283fail:284cx88_free_buffer(q,buf);285return rc;286}287288void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)289{290struct cx88_buffer *prev;291struct cx88_dmaqueue *cx88q = &dev->mpegq;292293dprintk( 1, "cx8802_buf_queue\n" );294/* add jump to stopper */295buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);296buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma);297298if (list_empty(&cx88q->active)) {299dprintk( 1, "queue is empty - first active\n" );300list_add_tail(&buf->vb.queue,&cx88q->active);301cx8802_start_dma(dev, cx88q, buf);302buf->vb.state = VIDEOBUF_ACTIVE;303buf->count = cx88q->count++;304mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT);305dprintk(1,"[%p/%d] %s - first active\n",306buf, buf->vb.i, __func__);307308} else {309dprintk( 1, "queue is not empty - append to active\n" );310prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue);311list_add_tail(&buf->vb.queue,&cx88q->active);312buf->vb.state = VIDEOBUF_ACTIVE;313buf->count = cx88q->count++;314prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);315dprintk( 1, "[%p/%d] %s - append to active\n",316buf, buf->vb.i, __func__);317}318}319320/* ----------------------------------------------------------- */321322static void do_cancel_buffers(struct cx8802_dev *dev, const char *reason, int restart)323{324struct cx88_dmaqueue *q = &dev->mpegq;325struct cx88_buffer *buf;326unsigned long flags;327328spin_lock_irqsave(&dev->slock,flags);329while (!list_empty(&q->active)) {330buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);331list_del(&buf->vb.queue);332buf->vb.state = VIDEOBUF_ERROR;333wake_up(&buf->vb.done);334dprintk(1,"[%p/%d] %s - dma=0x%08lx\n",335buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);336}337if (restart)338{339dprintk(1, "restarting queue\n" );340cx8802_restart_queue(dev,q);341}342spin_unlock_irqrestore(&dev->slock,flags);343}344345void cx8802_cancel_buffers(struct cx8802_dev *dev)346{347struct cx88_dmaqueue *q = &dev->mpegq;348349dprintk( 1, "cx8802_cancel_buffers" );350del_timer_sync(&q->timeout);351cx8802_stop_dma(dev);352do_cancel_buffers(dev,"cancel",0);353}354355static void cx8802_timeout(unsigned long data)356{357struct cx8802_dev *dev = (struct cx8802_dev*)data;358359dprintk(1, "%s\n",__func__);360361if (debug)362cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);363cx8802_stop_dma(dev);364do_cancel_buffers(dev,"timeout",1);365}366367static const char * cx88_mpeg_irqs[32] = {368"ts_risci1", NULL, NULL, NULL,369"ts_risci2", NULL, NULL, NULL,370"ts_oflow", NULL, NULL, NULL,371"ts_sync", NULL, NULL, NULL,372"opc_err", "par_err", "rip_err", "pci_abort",373"ts_err?",374};375376static void cx8802_mpeg_irq(struct cx8802_dev *dev)377{378struct cx88_core *core = dev->core;379u32 status, mask, count;380381dprintk( 1, "cx8802_mpeg_irq\n" );382status = cx_read(MO_TS_INTSTAT);383mask = cx_read(MO_TS_INTMSK);384if (0 == (status & mask))385return;386387cx_write(MO_TS_INTSTAT, status);388389if (debug || (status & mask & ~0xff))390cx88_print_irqbits(core->name, "irq mpeg ",391cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs),392status, mask);393394/* risc op code error */395if (status & (1 << 16)) {396printk(KERN_WARNING "%s: mpeg risc op code error\n",core->name);397cx_clear(MO_TS_DMACNTRL, 0x11);398cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);399}400401/* risc1 y */402if (status & 0x01) {403dprintk( 1, "wake up\n" );404spin_lock(&dev->slock);405count = cx_read(MO_TS_GPCNT);406cx88_wakeup(dev->core, &dev->mpegq, count);407spin_unlock(&dev->slock);408}409410/* risc2 y */411if (status & 0x10) {412spin_lock(&dev->slock);413cx8802_restart_queue(dev,&dev->mpegq);414spin_unlock(&dev->slock);415}416417/* other general errors */418if (status & 0x1f0100) {419dprintk( 0, "general errors: 0x%08x\n", status & 0x1f0100 );420spin_lock(&dev->slock);421cx8802_stop_dma(dev);422cx8802_restart_queue(dev,&dev->mpegq);423spin_unlock(&dev->slock);424}425}426427#define MAX_IRQ_LOOP 10428429static irqreturn_t cx8802_irq(int irq, void *dev_id)430{431struct cx8802_dev *dev = dev_id;432struct cx88_core *core = dev->core;433u32 status;434int loop, handled = 0;435436for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {437status = cx_read(MO_PCI_INTSTAT) &438(core->pci_irqmask | PCI_INT_TSINT);439if (0 == status)440goto out;441dprintk( 1, "cx8802_irq\n" );442dprintk( 1, " loop: %d/%d\n", loop, MAX_IRQ_LOOP );443dprintk( 1, " status: %d\n", status );444handled = 1;445cx_write(MO_PCI_INTSTAT, status);446447if (status & core->pci_irqmask)448cx88_core_irq(core,status);449if (status & PCI_INT_TSINT)450cx8802_mpeg_irq(dev);451};452if (MAX_IRQ_LOOP == loop) {453dprintk( 0, "clearing mask\n" );454printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n",455core->name);456cx_write(MO_PCI_INTMSK,0);457}458459out:460return IRQ_RETVAL(handled);461}462463static int cx8802_init_common(struct cx8802_dev *dev)464{465struct cx88_core *core = dev->core;466int err;467468/* pci init */469if (pci_enable_device(dev->pci))470return -EIO;471pci_set_master(dev->pci);472if (!pci_dma_supported(dev->pci,DMA_BIT_MASK(32))) {473printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name);474return -EIO;475}476477dev->pci_rev = dev->pci->revision;478pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat);479printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, "480"latency: %d, mmio: 0x%llx\n", dev->core->name,481pci_name(dev->pci), dev->pci_rev, dev->pci->irq,482dev->pci_lat,(unsigned long long)pci_resource_start(dev->pci,0));483484/* initialize driver struct */485spin_lock_init(&dev->slock);486487/* init dma queue */488INIT_LIST_HEAD(&dev->mpegq.active);489INIT_LIST_HEAD(&dev->mpegq.queued);490dev->mpegq.timeout.function = cx8802_timeout;491dev->mpegq.timeout.data = (unsigned long)dev;492init_timer(&dev->mpegq.timeout);493cx88_risc_stopper(dev->pci,&dev->mpegq.stopper,494MO_TS_DMACNTRL,0x11,0x00);495496/* get irq */497err = request_irq(dev->pci->irq, cx8802_irq,498IRQF_SHARED | IRQF_DISABLED, dev->core->name, dev);499if (err < 0) {500printk(KERN_ERR "%s: can't get IRQ %d\n",501dev->core->name, dev->pci->irq);502return err;503}504cx_set(MO_PCI_INTMSK, core->pci_irqmask);505506/* everything worked */507pci_set_drvdata(dev->pci,dev);508return 0;509}510511static void cx8802_fini_common(struct cx8802_dev *dev)512{513dprintk( 2, "cx8802_fini_common\n" );514cx8802_stop_dma(dev);515pci_disable_device(dev->pci);516517/* unregister stuff */518free_irq(dev->pci->irq, dev);519pci_set_drvdata(dev->pci, NULL);520521/* free memory */522btcx_riscmem_free(dev->pci,&dev->mpegq.stopper);523}524525/* ----------------------------------------------------------- */526527static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)528{529struct cx8802_dev *dev = pci_get_drvdata(pci_dev);530struct cx88_core *core = dev->core;531532/* stop mpeg dma */533spin_lock(&dev->slock);534if (!list_empty(&dev->mpegq.active)) {535dprintk( 2, "suspend\n" );536printk("%s: suspend mpeg\n", core->name);537cx8802_stop_dma(dev);538del_timer(&dev->mpegq.timeout);539}540spin_unlock(&dev->slock);541542/* FIXME -- shutdown device */543cx88_shutdown(dev->core);544545pci_save_state(pci_dev);546if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {547pci_disable_device(pci_dev);548dev->state.disabled = 1;549}550return 0;551}552553static int cx8802_resume_common(struct pci_dev *pci_dev)554{555struct cx8802_dev *dev = pci_get_drvdata(pci_dev);556struct cx88_core *core = dev->core;557int err;558559if (dev->state.disabled) {560err=pci_enable_device(pci_dev);561if (err) {562printk(KERN_ERR "%s: can't enable device\n",563dev->core->name);564return err;565}566dev->state.disabled = 0;567}568err=pci_set_power_state(pci_dev, PCI_D0);569if (err) {570printk(KERN_ERR "%s: can't enable device\n",571dev->core->name);572pci_disable_device(pci_dev);573dev->state.disabled = 1;574575return err;576}577pci_restore_state(pci_dev);578579/* FIXME: re-initialize hardware */580cx88_reset(dev->core);581582/* restart video+vbi capture */583spin_lock(&dev->slock);584if (!list_empty(&dev->mpegq.active)) {585printk("%s: resume mpeg\n", core->name);586cx8802_restart_queue(dev,&dev->mpegq);587}588spin_unlock(&dev->slock);589590return 0;591}592593struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype)594{595struct cx8802_driver *d;596597list_for_each_entry(d, &dev->drvlist, drvlist)598if (d->type_id == btype)599return d;600601return NULL;602}603604/* Driver asked for hardware access. */605static int cx8802_request_acquire(struct cx8802_driver *drv)606{607struct cx88_core *core = drv->core;608unsigned int i;609610/* Fail a request for hardware if the device is busy. */611if (core->active_type_id != CX88_BOARD_NONE &&612core->active_type_id != drv->type_id)613return -EBUSY;614615core->input = 0;616for (i = 0;617i < (sizeof(core->board.input) / sizeof(struct cx88_input));618i++) {619if (core->board.input[i].type == CX88_VMUX_DVB) {620core->input = i;621break;622}623}624625if (drv->advise_acquire)626{627core->active_ref++;628if (core->active_type_id == CX88_BOARD_NONE) {629core->active_type_id = drv->type_id;630drv->advise_acquire(drv);631}632633mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __func__, cx_read(MO_GP0_IO));634}635636return 0;637}638639/* Driver asked to release hardware. */640static int cx8802_request_release(struct cx8802_driver *drv)641{642struct cx88_core *core = drv->core;643644if (drv->advise_release && --core->active_ref == 0)645{646drv->advise_release(drv);647core->active_type_id = CX88_BOARD_NONE;648mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO));649}650651return 0;652}653654static int cx8802_check_driver(struct cx8802_driver *drv)655{656if (drv == NULL)657return -ENODEV;658659if ((drv->type_id != CX88_MPEG_DVB) &&660(drv->type_id != CX88_MPEG_BLACKBIRD))661return -EINVAL;662663if ((drv->hw_access != CX8802_DRVCTL_SHARED) &&664(drv->hw_access != CX8802_DRVCTL_EXCLUSIVE))665return -EINVAL;666667if ((drv->probe == NULL) ||668(drv->remove == NULL) ||669(drv->advise_acquire == NULL) ||670(drv->advise_release == NULL))671return -EINVAL;672673return 0;674}675676int cx8802_register_driver(struct cx8802_driver *drv)677{678struct cx8802_dev *dev;679struct cx8802_driver *driver;680int err, i = 0;681682printk(KERN_INFO683"cx88/2: registering cx8802 driver, type: %s access: %s\n",684drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",685drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");686687if ((err = cx8802_check_driver(drv)) != 0) {688printk(KERN_ERR "cx88/2: cx8802_driver is invalid\n");689return err;690}691692mutex_lock(&cx8802_mutex);693694list_for_each_entry(dev, &cx8802_devlist, devlist) {695printk(KERN_INFO696"%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n",697dev->core->name, dev->pci->subsystem_vendor,698dev->pci->subsystem_device, dev->core->board.name,699dev->core->boardnr);700701/* Bring up a new struct for each driver instance */702driver = kzalloc(sizeof(*drv),GFP_KERNEL);703if (driver == NULL) {704err = -ENOMEM;705goto out;706}707708/* Snapshot of the driver registration data */709drv->core = dev->core;710drv->suspend = cx8802_suspend_common;711drv->resume = cx8802_resume_common;712drv->request_acquire = cx8802_request_acquire;713drv->request_release = cx8802_request_release;714memcpy(driver, drv, sizeof(*driver));715716mutex_lock(&drv->core->lock);717err = drv->probe(driver);718if (err == 0) {719i++;720list_add_tail(&driver->drvlist, &dev->drvlist);721} else {722printk(KERN_ERR723"%s/2: cx8802 probe failed, err = %d\n",724dev->core->name, err);725}726mutex_unlock(&drv->core->lock);727}728729err = i ? 0 : -ENODEV;730out:731mutex_unlock(&cx8802_mutex);732return err;733}734735int cx8802_unregister_driver(struct cx8802_driver *drv)736{737struct cx8802_dev *dev;738struct cx8802_driver *d, *dtmp;739int err = 0;740741printk(KERN_INFO742"cx88/2: unregistering cx8802 driver, type: %s access: %s\n",743drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",744drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");745746mutex_lock(&cx8802_mutex);747748list_for_each_entry(dev, &cx8802_devlist, devlist) {749printk(KERN_INFO750"%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n",751dev->core->name, dev->pci->subsystem_vendor,752dev->pci->subsystem_device, dev->core->board.name,753dev->core->boardnr);754755mutex_lock(&dev->core->lock);756757list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) {758/* only unregister the correct driver type */759if (d->type_id != drv->type_id)760continue;761762err = d->remove(d);763if (err == 0) {764list_del(&d->drvlist);765kfree(d);766} else767printk(KERN_ERR "%s/2: cx8802 driver remove "768"failed (%d)\n", dev->core->name, err);769}770771mutex_unlock(&dev->core->lock);772}773774mutex_unlock(&cx8802_mutex);775776return err;777}778779/* ----------------------------------------------------------- */780static int __devinit cx8802_probe(struct pci_dev *pci_dev,781const struct pci_device_id *pci_id)782{783struct cx8802_dev *dev;784struct cx88_core *core;785int err;786787/* general setup */788core = cx88_core_get(pci_dev);789if (NULL == core)790return -EINVAL;791792printk("%s/2: cx2388x 8802 Driver Manager\n", core->name);793794err = -ENODEV;795if (!core->board.mpeg)796goto fail_core;797798err = -ENOMEM;799dev = kzalloc(sizeof(*dev),GFP_KERNEL);800if (NULL == dev)801goto fail_core;802dev->pci = pci_dev;803dev->core = core;804805/* Maintain a reference so cx88-video can query the 8802 device. */806core->dvbdev = dev;807808err = cx8802_init_common(dev);809if (err != 0)810goto fail_free;811812INIT_LIST_HEAD(&dev->drvlist);813mutex_lock(&cx8802_mutex);814list_add_tail(&dev->devlist,&cx8802_devlist);815mutex_unlock(&cx8802_mutex);816817/* now autoload cx88-dvb or cx88-blackbird */818request_modules(dev);819return 0;820821fail_free:822kfree(dev);823fail_core:824core->dvbdev = NULL;825cx88_core_put(core,pci_dev);826return err;827}828829static void __devexit cx8802_remove(struct pci_dev *pci_dev)830{831struct cx8802_dev *dev;832833dev = pci_get_drvdata(pci_dev);834835dprintk( 1, "%s\n", __func__);836837flush_request_modules(dev);838839mutex_lock(&dev->core->lock);840841if (!list_empty(&dev->drvlist)) {842struct cx8802_driver *drv, *tmp;843int err;844845printk(KERN_WARNING "%s/2: Trying to remove cx8802 driver "846"while cx8802 sub-drivers still loaded?!\n",847dev->core->name);848849list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) {850err = drv->remove(drv);851if (err == 0) {852list_del(&drv->drvlist);853} else854printk(KERN_ERR "%s/2: cx8802 driver remove "855"failed (%d)\n", dev->core->name, err);856kfree(drv);857}858}859860mutex_unlock(&dev->core->lock);861862/* Destroy any 8802 reference. */863dev->core->dvbdev = NULL;864865/* common */866cx8802_fini_common(dev);867cx88_core_put(dev->core,dev->pci);868kfree(dev);869}870871static const struct pci_device_id cx8802_pci_tbl[] = {872{873.vendor = 0x14f1,874.device = 0x8802,875.subvendor = PCI_ANY_ID,876.subdevice = PCI_ANY_ID,877},{878/* --- end of list --- */879}880};881MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);882883static struct pci_driver cx8802_pci_driver = {884.name = "cx88-mpeg driver manager",885.id_table = cx8802_pci_tbl,886.probe = cx8802_probe,887.remove = __devexit_p(cx8802_remove),888};889890static int __init cx8802_init(void)891{892printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %d.%d.%d loaded\n",893(CX88_VERSION_CODE >> 16) & 0xff,894(CX88_VERSION_CODE >> 8) & 0xff,895CX88_VERSION_CODE & 0xff);896#ifdef SNAPSHOT897printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",898SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);899#endif900return pci_register_driver(&cx8802_pci_driver);901}902903static void __exit cx8802_fini(void)904{905pci_unregister_driver(&cx8802_pci_driver);906}907908module_init(cx8802_init);909module_exit(cx8802_fini);910EXPORT_SYMBOL(cx8802_buf_prepare);911EXPORT_SYMBOL(cx8802_buf_queue);912EXPORT_SYMBOL(cx8802_cancel_buffers);913914EXPORT_SYMBOL(cx8802_register_driver);915EXPORT_SYMBOL(cx8802_unregister_driver);916EXPORT_SYMBOL(cx8802_get_driver);917/* ----------------------------------------------------------- */918/*919* Local variables:920* c-basic-offset: 8921* End:922* 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 off923*/924925926