Path: blob/master/drivers/media/dvb/b2c2/flexcop-pci.c
15111 views
/*1* Linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III2* flexcop-pci.c - covers the PCI part including DMA transfers3* see flexcop.c for copyright information4*/56#define FC_LOG_PREFIX "flexcop-pci"7#include "flexcop-common.h"89static int enable_pid_filtering = 1;10module_param(enable_pid_filtering, int, 0444);11MODULE_PARM_DESC(enable_pid_filtering,12"enable hardware pid filtering: supported values: 0 (fullts), 1");1314static int irq_chk_intv = 100;15module_param(irq_chk_intv, int, 0644);16MODULE_PARM_DESC(irq_chk_intv, "set the interval for IRQ streaming watchdog.");1718#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG19#define dprintk(level,args...) \20do { if ((debug & level)) printk(args); } while (0)21#define DEBSTATUS ""22#else23#define dprintk(level,args...)24#define DEBSTATUS " (debugging is not enabled)"25#endif2627#define deb_info(args...) dprintk(0x01, args)28#define deb_reg(args...) dprintk(0x02, args)29#define deb_ts(args...) dprintk(0x04, args)30#define deb_irq(args...) dprintk(0x08, args)31#define deb_chk(args...) dprintk(0x10, args)3233static int debug;34module_param(debug, int, 0644);35MODULE_PARM_DESC(debug,36"set debug level (1=info,2=regs,4=TS,8=irqdma,16=check (|-able))."37DEBSTATUS);3839#define DRIVER_VERSION "0.1"40#define DRIVER_NAME "flexcop-pci"41#define DRIVER_AUTHOR "Patrick Boettcher <[email protected]>"4243struct flexcop_pci {44struct pci_dev *pdev;4546#define FC_PCI_INIT 0x0147#define FC_PCI_DMA_INIT 0x0248int init_state;4950void __iomem *io_mem;51u32 irq;52/* buffersize (at least for DMA1, need to be % 188 == 0,53* this logic is required */54#define FC_DEFAULT_DMA1_BUFSIZE (1280 * 188)55#define FC_DEFAULT_DMA2_BUFSIZE (10 * 188)56struct flexcop_dma dma[2];5758int active_dma1_addr; /* 0 = addr0 of dma1; 1 = addr1 of dma1 */59u32 last_dma1_cur_pos;60/* position of the pointer last time the timer/packet irq occurred */61int count;62int count_prev;63int stream_problem;6465spinlock_t irq_lock;66unsigned long last_irq;6768struct delayed_work irq_check_work;69struct flexcop_device *fc_dev;70};7172static int lastwreg, lastwval, lastrreg, lastrval;7374static flexcop_ibi_value flexcop_pci_read_ibi_reg(struct flexcop_device *fc,75flexcop_ibi_register r)76{77struct flexcop_pci *fc_pci = fc->bus_specific;78flexcop_ibi_value v;79v.raw = readl(fc_pci->io_mem + r);8081if (lastrreg != r || lastrval != v.raw) {82lastrreg = r; lastrval = v.raw;83deb_reg("new rd: %3x: %08x\n", r, v.raw);84}8586return v;87}8889static int flexcop_pci_write_ibi_reg(struct flexcop_device *fc,90flexcop_ibi_register r, flexcop_ibi_value v)91{92struct flexcop_pci *fc_pci = fc->bus_specific;9394if (lastwreg != r || lastwval != v.raw) {95lastwreg = r; lastwval = v.raw;96deb_reg("new wr: %3x: %08x\n", r, v.raw);97}9899writel(v.raw, fc_pci->io_mem + r);100return 0;101}102103static void flexcop_pci_irq_check_work(struct work_struct *work)104{105struct flexcop_pci *fc_pci =106container_of(work, struct flexcop_pci, irq_check_work.work);107struct flexcop_device *fc = fc_pci->fc_dev;108109if (fc->feedcount) {110111if (fc_pci->count == fc_pci->count_prev) {112deb_chk("no IRQ since the last check\n");113if (fc_pci->stream_problem++ == 3) {114struct dvb_demux_feed *feed;115deb_info("flexcop-pci: stream problem, resetting pid filter\n");116117spin_lock_irq(&fc->demux.lock);118list_for_each_entry(feed, &fc->demux.feed_list,119list_head) {120flexcop_pid_feed_control(fc, feed, 0);121}122123list_for_each_entry(feed, &fc->demux.feed_list,124list_head) {125flexcop_pid_feed_control(fc, feed, 1);126}127spin_unlock_irq(&fc->demux.lock);128129fc_pci->stream_problem = 0;130}131} else {132fc_pci->stream_problem = 0;133fc_pci->count_prev = fc_pci->count;134}135}136137schedule_delayed_work(&fc_pci->irq_check_work,138msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv));139}140141/* When PID filtering is turned on, we use the timer IRQ, because small amounts142* of data need to be passed to the user space instantly as well. When PID143* filtering is turned off, we use the page-change-IRQ */144static irqreturn_t flexcop_pci_isr(int irq, void *dev_id)145{146struct flexcop_pci *fc_pci = dev_id;147struct flexcop_device *fc = fc_pci->fc_dev;148unsigned long flags;149flexcop_ibi_value v;150irqreturn_t ret = IRQ_HANDLED;151152spin_lock_irqsave(&fc_pci->irq_lock, flags);153v = fc->read_ibi_reg(fc, irq_20c);154155/* errors */156if (v.irq_20c.Data_receiver_error)157deb_chk("data receiver error\n");158if (v.irq_20c.Continuity_error_flag)159deb_chk("Contunuity error flag is set\n");160if (v.irq_20c.LLC_SNAP_FLAG_set)161deb_chk("LLC_SNAP_FLAG_set is set\n");162if (v.irq_20c.Transport_Error)163deb_chk("Transport error\n");164165if ((fc_pci->count % 1000) == 0)166deb_chk("%d valid irq took place so far\n", fc_pci->count);167168if (v.irq_20c.DMA1_IRQ_Status == 1) {169if (fc_pci->active_dma1_addr == 0)170flexcop_pass_dmx_packets(fc_pci->fc_dev,171fc_pci->dma[0].cpu_addr0,172fc_pci->dma[0].size / 188);173else174flexcop_pass_dmx_packets(fc_pci->fc_dev,175fc_pci->dma[0].cpu_addr1,176fc_pci->dma[0].size / 188);177178deb_irq("page change to page: %d\n",!fc_pci->active_dma1_addr);179fc_pci->active_dma1_addr = !fc_pci->active_dma1_addr;180/* for the timer IRQ we only can use buffer dmx feeding, because we don't have181* complete TS packets when reading from the DMA memory */182} else if (v.irq_20c.DMA1_Timer_Status == 1) {183dma_addr_t cur_addr =184fc->read_ibi_reg(fc,dma1_008).dma_0x8.dma_cur_addr << 2;185u32 cur_pos = cur_addr - fc_pci->dma[0].dma_addr0;186187deb_irq("%u irq: %08x cur_addr: %llx: cur_pos: %08x, "188"last_cur_pos: %08x ",189jiffies_to_usecs(jiffies - fc_pci->last_irq),190v.raw, (unsigned long long)cur_addr, cur_pos,191fc_pci->last_dma1_cur_pos);192fc_pci->last_irq = jiffies;193194/* buffer end was reached, restarted from the beginning195* pass the data from last_cur_pos to the buffer end to the demux196*/197if (cur_pos < fc_pci->last_dma1_cur_pos) {198deb_irq(" end was reached: passing %d bytes ",199(fc_pci->dma[0].size*2 - 1) -200fc_pci->last_dma1_cur_pos);201flexcop_pass_dmx_data(fc_pci->fc_dev,202fc_pci->dma[0].cpu_addr0 +203fc_pci->last_dma1_cur_pos,204(fc_pci->dma[0].size*2) -205fc_pci->last_dma1_cur_pos);206fc_pci->last_dma1_cur_pos = 0;207}208209if (cur_pos > fc_pci->last_dma1_cur_pos) {210deb_irq(" passing %d bytes ",211cur_pos - fc_pci->last_dma1_cur_pos);212flexcop_pass_dmx_data(fc_pci->fc_dev,213fc_pci->dma[0].cpu_addr0 +214fc_pci->last_dma1_cur_pos,215cur_pos - fc_pci->last_dma1_cur_pos);216}217deb_irq("\n");218219fc_pci->last_dma1_cur_pos = cur_pos;220fc_pci->count++;221} else {222deb_irq("isr for flexcop called, "223"apparently without reason (%08x)\n", v.raw);224ret = IRQ_NONE;225}226227spin_unlock_irqrestore(&fc_pci->irq_lock, flags);228return ret;229}230231static int flexcop_pci_stream_control(struct flexcop_device *fc, int onoff)232{233struct flexcop_pci *fc_pci = fc->bus_specific;234if (onoff) {235flexcop_dma_config(fc, &fc_pci->dma[0], FC_DMA_1);236flexcop_dma_config(fc, &fc_pci->dma[1], FC_DMA_2);237flexcop_dma_config_timer(fc, FC_DMA_1, 0);238flexcop_dma_xfer_control(fc, FC_DMA_1,239FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 1);240deb_irq("DMA xfer enabled\n");241242fc_pci->last_dma1_cur_pos = 0;243flexcop_dma_control_timer_irq(fc, FC_DMA_1, 1);244deb_irq("IRQ enabled\n");245fc_pci->count_prev = fc_pci->count;246} else {247flexcop_dma_control_timer_irq(fc, FC_DMA_1, 0);248deb_irq("IRQ disabled\n");249250flexcop_dma_xfer_control(fc, FC_DMA_1,251FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 0);252deb_irq("DMA xfer disabled\n");253}254return 0;255}256257static int flexcop_pci_dma_init(struct flexcop_pci *fc_pci)258{259int ret;260ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[0],261FC_DEFAULT_DMA1_BUFSIZE);262if (ret != 0)263return ret;264265ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[1],266FC_DEFAULT_DMA2_BUFSIZE);267if (ret != 0) {268flexcop_dma_free(&fc_pci->dma[0]);269return ret;270}271272flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_MEDIA |273FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1);274flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_CAO |275FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2);276fc_pci->init_state |= FC_PCI_DMA_INIT;277return ret;278}279280static void flexcop_pci_dma_exit(struct flexcop_pci *fc_pci)281{282if (fc_pci->init_state & FC_PCI_DMA_INIT) {283flexcop_dma_free(&fc_pci->dma[0]);284flexcop_dma_free(&fc_pci->dma[1]);285}286fc_pci->init_state &= ~FC_PCI_DMA_INIT;287}288289static int flexcop_pci_init(struct flexcop_pci *fc_pci)290{291int ret;292293info("card revision %x", fc_pci->pdev->revision);294295if ((ret = pci_enable_device(fc_pci->pdev)) != 0)296return ret;297pci_set_master(fc_pci->pdev);298299if ((ret = pci_request_regions(fc_pci->pdev, DRIVER_NAME)) != 0)300goto err_pci_disable_device;301302fc_pci->io_mem = pci_iomap(fc_pci->pdev, 0, 0x800);303304if (!fc_pci->io_mem) {305err("cannot map io memory\n");306ret = -EIO;307goto err_pci_release_regions;308}309310pci_set_drvdata(fc_pci->pdev, fc_pci);311spin_lock_init(&fc_pci->irq_lock);312if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr,313IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0)314goto err_pci_iounmap;315316fc_pci->init_state |= FC_PCI_INIT;317return ret;318319err_pci_iounmap:320pci_iounmap(fc_pci->pdev, fc_pci->io_mem);321pci_set_drvdata(fc_pci->pdev, NULL);322err_pci_release_regions:323pci_release_regions(fc_pci->pdev);324err_pci_disable_device:325pci_disable_device(fc_pci->pdev);326return ret;327}328329static void flexcop_pci_exit(struct flexcop_pci *fc_pci)330{331if (fc_pci->init_state & FC_PCI_INIT) {332free_irq(fc_pci->pdev->irq, fc_pci);333pci_iounmap(fc_pci->pdev, fc_pci->io_mem);334pci_set_drvdata(fc_pci->pdev, NULL);335pci_release_regions(fc_pci->pdev);336pci_disable_device(fc_pci->pdev);337}338fc_pci->init_state &= ~FC_PCI_INIT;339}340341static int flexcop_pci_probe(struct pci_dev *pdev,342const struct pci_device_id *ent)343{344struct flexcop_device *fc;345struct flexcop_pci *fc_pci;346int ret = -ENOMEM;347348if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_pci))) == NULL) {349err("out of memory\n");350return -ENOMEM;351}352353/* general flexcop init */354fc_pci = fc->bus_specific;355fc_pci->fc_dev = fc;356357fc->read_ibi_reg = flexcop_pci_read_ibi_reg;358fc->write_ibi_reg = flexcop_pci_write_ibi_reg;359fc->i2c_request = flexcop_i2c_request;360fc->get_mac_addr = flexcop_eeprom_check_mac_addr;361fc->stream_control = flexcop_pci_stream_control;362363if (enable_pid_filtering)364info("will use the HW PID filter.");365else366info("will pass the complete TS to the demuxer.");367368fc->pid_filtering = enable_pid_filtering;369fc->bus_type = FC_PCI;370fc->dev = &pdev->dev;371fc->owner = THIS_MODULE;372373/* bus specific part */374fc_pci->pdev = pdev;375if ((ret = flexcop_pci_init(fc_pci)) != 0)376goto err_kfree;377378/* init flexcop */379if ((ret = flexcop_device_initialize(fc)) != 0)380goto err_pci_exit;381382/* init dma */383if ((ret = flexcop_pci_dma_init(fc_pci)) != 0)384goto err_fc_exit;385386INIT_DELAYED_WORK(&fc_pci->irq_check_work, flexcop_pci_irq_check_work);387388if (irq_chk_intv > 0)389schedule_delayed_work(&fc_pci->irq_check_work,390msecs_to_jiffies(irq_chk_intv < 100 ?391100 :392irq_chk_intv));393return ret;394395err_fc_exit:396flexcop_device_exit(fc);397err_pci_exit:398flexcop_pci_exit(fc_pci);399err_kfree:400flexcop_device_kfree(fc);401return ret;402}403404/* in theory every _exit function should be called exactly two times,405* here and in the bail-out-part of the _init-function406*/407static void flexcop_pci_remove(struct pci_dev *pdev)408{409struct flexcop_pci *fc_pci = pci_get_drvdata(pdev);410411if (irq_chk_intv > 0)412cancel_delayed_work(&fc_pci->irq_check_work);413414flexcop_pci_dma_exit(fc_pci);415flexcop_device_exit(fc_pci->fc_dev);416flexcop_pci_exit(fc_pci);417flexcop_device_kfree(fc_pci->fc_dev);418}419420static struct pci_device_id flexcop_pci_tbl[] = {421{ PCI_DEVICE(0x13d0, 0x2103) },422{ },423};424425MODULE_DEVICE_TABLE(pci, flexcop_pci_tbl);426427static struct pci_driver flexcop_pci_driver = {428.name = "b2c2_flexcop_pci",429.id_table = flexcop_pci_tbl,430.probe = flexcop_pci_probe,431.remove = flexcop_pci_remove,432};433434static int __init flexcop_pci_module_init(void)435{436return pci_register_driver(&flexcop_pci_driver);437}438439static void __exit flexcop_pci_module_exit(void)440{441pci_unregister_driver(&flexcop_pci_driver);442}443444module_init(flexcop_pci_module_init);445module_exit(flexcop_pci_module_exit);446447MODULE_AUTHOR(DRIVER_AUTHOR);448MODULE_DESCRIPTION(DRIVER_NAME);449MODULE_LICENSE("GPL");450451452