📄 saa7134-core.c
字号:
/* * * device driver for philips saa7134 based TV cards * driver core * * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/init.h>#include <linux/list.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/kmod.h>#include <linux/sound.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/mutex.h>#include <linux/dma-mapping.h>#include <linux/pm.h>#include "saa7134-reg.h"#include "saa7134.h"MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards");MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");MODULE_LICENSE("GPL");/* ------------------------------------------------------------------ */static unsigned int irq_debug;module_param(irq_debug, int, 0644);MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]");static unsigned int core_debug;module_param(core_debug, int, 0644);MODULE_PARM_DESC(core_debug,"enable debug messages [core]");static unsigned int gpio_tracking;module_param(gpio_tracking, int, 0644);MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]");static unsigned int alsa;module_param(alsa, int, 0644);MODULE_PARM_DESC(alsa,"enable ALSA DMA sound [dmasound]");static unsigned int oss;module_param(oss, int, 0644);MODULE_PARM_DESC(oss,"enable OSS DMA sound [dmasound]");static unsigned int latency = UNSET;module_param(latency, int, 0444);MODULE_PARM_DESC(latency,"pci latency timer");int saa7134_no_overlay=-1;module_param_named(no_overlay, saa7134_no_overlay, int, 0444);MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)" " [some VIA/SIS chipsets are known to have problem with overlay]");static unsigned int video_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };static unsigned int vbi_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };static unsigned int radio_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };static unsigned int tuner[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };static unsigned int card[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };module_param_array(video_nr, int, NULL, 0444);module_param_array(vbi_nr, int, NULL, 0444);module_param_array(radio_nr, int, NULL, 0444);module_param_array(tuner, int, NULL, 0444);module_param_array(card, int, NULL, 0444);MODULE_PARM_DESC(video_nr, "video device number");MODULE_PARM_DESC(vbi_nr, "vbi device number");MODULE_PARM_DESC(radio_nr, "radio device number");MODULE_PARM_DESC(tuner, "tuner type");MODULE_PARM_DESC(card, "card type");static DEFINE_MUTEX(devlist_lock);LIST_HEAD(saa7134_devlist);static LIST_HEAD(mops_list);static unsigned int saa7134_devcount;int (*saa7134_dmasound_init)(struct saa7134_dev *dev);int (*saa7134_dmasound_exit)(struct saa7134_dev *dev);#define dprintk(fmt, arg...) if (core_debug) \ printk(KERN_DEBUG "%s/core: " fmt, dev->name , ## arg)void saa7134_track_gpio(struct saa7134_dev *dev, char *msg){ unsigned long mode,status; if (!gpio_tracking) return; /* rising SAA7134_GPIO_GPRESCAN reads the status */ saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,0); saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,SAA7134_GPIO_GPRESCAN); mode = saa_readl(SAA7134_GPIO_GPMODE0 >> 2) & 0xfffffff; status = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & 0xfffffff; printk(KERN_DEBUG "%s: gpio: mode=0x%07lx in=0x%07lx out=0x%07lx [%s]\n", dev->name, mode, (~mode) & status, mode & status, msg);}void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value){ u32 index, bitval; index = 1 << bit_no; switch (value) { case 0: /* static value */ case 1: dprintk("setting GPIO%d to static %d\n", bit_no, value); /* turn sync mode off if necessary */ if (index & 0x00c00000) saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x00); if (value) bitval = index; else bitval = 0; saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, index); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, index, bitval); break; case 3: /* tristate */ dprintk("setting GPIO%d to tristate\n", bit_no); saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, 0); break; }}/* ------------------------------------------------------------------ */#if 0static char *dec1_bits[8] = { "DCSTD0", "DCSCT1", "WIPA", "GLIMB", "GLIMT", "SLTCA", "HLCK"};static char *dec2_bits[8] = { "RDCAP", "COPRO", "COLSTR", "TYPE3", NULL, "FIDT", "HLVLN", "INTL"};static char *scale1_bits[8] = { "VID_A", "VBI_A", NULL, NULL, "VID_B", "VBI_B"};static char *scale2_bits[8] = { "TRERR", "CFERR", "LDERR", "WASRST", "FIDSCI", "FIDSCO", "D6^D5", "TASK"};static void dump_statusreg(struct saa7134_dev *dev, int reg, char *regname, char **bits){ int value,i; value = saa_readb(reg); printk(KERN_DEBUG "%s: %s:", dev->name, regname); for (i = 7; i >= 0; i--) { if (NULL == bits[i]) continue; printk(" %s=%d", bits[i], (value & (1 << i)) ? 1 : 0); } printk("\n");}static void dump_statusregs(struct saa7134_dev *dev){ dump_statusreg(dev,SAA7134_STATUS_VIDEO1,"dec1",dec1_bits); dump_statusreg(dev,SAA7134_STATUS_VIDEO2,"dec2",dec2_bits); dump_statusreg(dev,SAA7134_SCALER_STATUS0,"scale0",scale1_bits); dump_statusreg(dev,SAA7134_SCALER_STATUS1,"scale1",scale2_bits);}#endif/* ----------------------------------------------------------- *//* delayed request_module */#if defined(CONFIG_MODULES) && defined(MODULE)#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)static void request_module_async(void *ptr){ struct saa7134_dev* dev=(struct saa7134_dev*)ptr;#elsestatic void request_module_async(struct work_struct *work){ struct saa7134_dev* dev = container_of(work, struct saa7134_dev, request_module_wk);#endif if (card_is_empress(dev)) request_module("saa7134-empress"); if (card_is_dvb(dev)) request_module("saa7134-dvb"); if (alsa) request_module("saa7134-alsa"); if (oss) request_module("saa7134-oss");}static void request_submodules(struct saa7134_dev *dev){#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) INIT_WORK(&dev->request_module_wk, request_module_async, (void*)dev);#else INIT_WORK(&dev->request_module_wk, request_module_async);#endif schedule_work(&dev->request_module_wk);}#else#define request_submodules(dev)#endif /* CONFIG_MODULES *//* ------------------------------------------------------------------ *//* nr of (saa7134-)pages for the given buffer size */static int saa7134_buffer_pages(int size){ size = PAGE_ALIGN(size); size += PAGE_SIZE; /* for non-page-aligned buffers */ size /= 4096; return size;}/* calc max # of buffers from size (must not exceed the 4MB virtual * address space per DMA channel) */int saa7134_buffer_count(unsigned int size, unsigned int count){ unsigned int maxcount; maxcount = 1024 / saa7134_buffer_pages(size); if (count > maxcount) count = maxcount; return count;}int saa7134_buffer_startpage(struct saa7134_buf *buf){ return saa7134_buffer_pages(buf->vb.bsize) * buf->vb.i;}unsigned long saa7134_buffer_base(struct saa7134_buf *buf){ unsigned long base; struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); base = saa7134_buffer_startpage(buf) * 4096; base += dma->sglist[0].offset; return base;}/* ------------------------------------------------------------------ */int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt){ __le32 *cpu; dma_addr_t dma_addr = 0; cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr); if (NULL == cpu) return -ENOMEM; pt->size = SAA7134_PGTABLE_SIZE; pt->cpu = cpu; pt->dma = dma_addr; return 0;}int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt, struct scatterlist *list, unsigned int length, unsigned int startpage){ __le32 *ptr; unsigned int i,p; BUG_ON(NULL == pt || NULL == pt->cpu); ptr = pt->cpu + startpage; for (i = 0; i < length; i++, list++) for (p = 0; p * 4096 < list->length; p++, ptr++) *ptr = cpu_to_le32(sg_dma_address(list) - list->offset); return 0;}void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt){ if (NULL == pt->cpu) return; pci_free_consistent(pci, pt->size, pt->cpu, pt->dma); pt->cpu = NULL;}/* ------------------------------------------------------------------ */void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf){ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); videobuf_dma_unmap(q, dma); videobuf_dma_free(dma); buf->vb.state = VIDEOBUF_NEEDS_INIT;}/* ------------------------------------------------------------------ */int saa7134_buffer_queue(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, struct saa7134_buf *buf){ struct saa7134_buf *next = NULL; assert_spin_locked(&dev->slock); dprintk("buffer_queue %p\n",buf); if (NULL == q->curr) { if (!q->need_two) { q->curr = buf; buf->activate(dev,buf,NULL); } else if (list_empty(&q->queue)) { list_add_tail(&buf->vb.queue,&q->queue); buf->vb.state = VIDEOBUF_QUEUED; } else { next = list_entry(q->queue.next,struct saa7134_buf, vb.queue); q->curr = buf; buf->activate(dev,buf,next); } } else { list_add_tail(&buf->vb.queue,&q->queue); buf->vb.state = VIDEOBUF_QUEUED; } return 0;}void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, unsigned int state){ assert_spin_locked(&dev->slock); dprintk("buffer_finish %p\n",q->curr); /* finish current buffer */ q->curr->vb.state = state; do_gettimeofday(&q->curr->vb.ts); wake_up(&q->curr->vb.done); q->curr = NULL;}void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q){ struct saa7134_buf *buf,*next = NULL; assert_spin_locked(&dev->slock); BUG_ON(NULL != q->curr); if (!list_empty(&q->queue)) { /* activate next one from queue */ buf = list_entry(q->queue.next,struct saa7134_buf,vb.queue); dprintk("buffer_next %p [prev=%p/next=%p]\n", buf,q->queue.prev,q->queue.next); list_del(&buf->vb.queue); if (!list_empty(&q->queue)) next = list_entry(q->queue.next,struct saa7134_buf, vb.queue); q->curr = buf; buf->activate(dev,buf,next); dprintk("buffer_next #2 prev=%p/next=%p\n", q->queue.prev,q->queue.next); } else { /* nothing to do -- just stop DMA */ dprintk("buffer_next %p\n",NULL); saa7134_set_dmabits(dev); del_timer(&q->timeout); }}void saa7134_buffer_timeout(unsigned long data){ struct saa7134_dmaqueue *q = (struct saa7134_dmaqueue*)data; struct saa7134_dev *dev = q->dev; unsigned long flags; spin_lock_irqsave(&dev->slock,flags); /* try to reset the hardware (SWRST) */ saa_writeb(SAA7134_REGION_ENABLE, 0x00); saa_writeb(SAA7134_REGION_ENABLE, 0x80); saa_writeb(SAA7134_REGION_ENABLE, 0x00); /* flag current buffer as failed, try to start over with the next one. */ if (q->curr) { dprintk("timeout on %p\n",q->curr); saa7134_buffer_finish(dev,q,VIDEOBUF_ERROR); } saa7134_buffer_next(dev,q); spin_unlock_irqrestore(&dev->slock,flags);}/* ------------------------------------------------------------------ */int saa7134_set_dmabits(struct saa7134_dev *dev){ u32 split, task=0, ctrl=0, irq=0; enum v4l2_field cap = V4L2_FIELD_ANY; enum v4l2_field ov = V4L2_FIELD_ANY; assert_spin_locked(&dev->slock); if (dev->insuspend) return 0; /* video capture -- dma 0 + video task A */ if (dev->video_q.curr) { task |= 0x01; ctrl |= SAA7134_MAIN_CTRL_TE0; irq |= SAA7134_IRQ1_INTE_RA0_1 | SAA7134_IRQ1_INTE_RA0_0; cap = dev->video_q.curr->vb.field; } /* video capture -- dma 1+2 (planar modes) */ if (dev->video_q.curr && dev->video_q.curr->fmt->planar) { ctrl |= SAA7134_MAIN_CTRL_TE4 | SAA7134_MAIN_CTRL_TE5; } /* screen overlay -- dma 0 + video task B */ if (dev->ovenable) { task |= 0x10; ctrl |= SAA7134_MAIN_CTRL_TE1; ov = dev->ovfield; } /* vbi capture -- dma 0 + vbi task A+B */ if (dev->vbi_q.curr) { task |= 0x22; ctrl |= SAA7134_MAIN_CTRL_TE2 | SAA7134_MAIN_CTRL_TE3; irq |= SAA7134_IRQ1_INTE_RA0_7 | SAA7134_IRQ1_INTE_RA0_6 | SAA7134_IRQ1_INTE_RA0_5 | SAA7134_IRQ1_INTE_RA0_4; } /* audio capture -- dma 3 */ if (dev->dmasound.dma_running) { ctrl |= SAA7134_MAIN_CTRL_TE6; irq |= SAA7134_IRQ1_INTE_RA3_1 | SAA7134_IRQ1_INTE_RA3_0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -