📄 cx88-core.c
字号:
/* * * device driver for Conexant 2388x based TV cards * driver core * * (c) 2003 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/moduleparam.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/kmod.h>#include <linux/sound.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/delay.h>#include "compat.h"#include <linux/videodev2.h>#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15)#include <linux/mutex.h>#endif#include "cx88.h"#include <media/v4l2-common.h>MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");MODULE_LICENSE("GPL");/* ------------------------------------------------------------------ */static unsigned int core_debug = 0;module_param(core_debug,int,0644);MODULE_PARM_DESC(core_debug,"enable debug messages [core]");static unsigned int latency = UNSET;module_param(latency,int,0444);MODULE_PARM_DESC(latency,"pci latency timer");static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };static unsigned int card[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)MODULE_PARM(tuner,"1-" __stringify(CX88_MAXBOARDS) "i");MODULE_PARM(radiox,"1-" __stringify(CX88_MAXBOARDS) "i");MODULE_PARM(card,"1-" __stringify(CX88_MAXBOARDS) "i");#else#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)static int dummy;module_param_array(tuner, int, dummy, 0444);module_param_array(radio, int, dummy, 0444);module_param_array(card, int, dummy, 0444);#elsemodule_param_array(tuner, int, NULL, 0444);module_param_array(radio, int, NULL, 0444);module_param_array(card, int, NULL, 0444);#endif#endifMODULE_PARM_DESC(tuner,"tuner type");MODULE_PARM_DESC(radio,"radio tuner type");MODULE_PARM_DESC(card,"card type");static unsigned int nicam = 0;module_param(nicam,int,0644);MODULE_PARM_DESC(nicam,"tv audio is nicam");static unsigned int nocomb = 0;module_param(nocomb,int,0644);MODULE_PARM_DESC(nocomb,"disable comb filter");#define dprintk(level,fmt, arg...) if (core_debug >= level) \ printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)static unsigned int cx88_devcount;static LIST_HEAD(cx88_devlist);static DEFINE_MUTEX(devlist);#define NO_SYNC_LINE (-1U)static u32* cx88_risc_field(u32 *rp, struct scatterlist *sglist, unsigned int offset, u32 sync_line, unsigned int bpl, unsigned int padding, unsigned int lines){ struct scatterlist *sg; unsigned int line,todo; /* sync instruction */ if (sync_line != NO_SYNC_LINE) *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); /* scan lines */ sg = sglist; for (line = 0; line < lines; line++) { while (offset && offset >= sg_dma_len(sg)) { offset -= sg_dma_len(sg); sg++; } if (bpl <= sg_dma_len(sg)-offset) { /* fits into current chunk */ *(rp++)=cpu_to_le32(RISC_WRITE|RISC_SOL|RISC_EOL|bpl); *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset); offset+=bpl; } else { /* scanline needs to be splitted */ todo = bpl; *(rp++)=cpu_to_le32(RISC_WRITE|RISC_SOL| (sg_dma_len(sg)-offset)); *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset); todo -= (sg_dma_len(sg)-offset); offset = 0; sg++; while (todo > sg_dma_len(sg)) { *(rp++)=cpu_to_le32(RISC_WRITE| sg_dma_len(sg)); *(rp++)=cpu_to_le32(sg_dma_address(sg)); todo -= sg_dma_len(sg); sg++; } *(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo); *(rp++)=cpu_to_le32(sg_dma_address(sg)); offset += todo; } offset += padding; } return rp;}int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines){ u32 instructions,fields; u32 *rp; int rc; fields = 0; if (UNSET != top_offset) fields++; if (UNSET != bottom_offset) fields++; /* estimate risc mem: worst case is one write per page border + one write per scan line + syncs + jump (all 2 dwords). Padding can cause next bpl to start close to a page border. First DMA region may be smaller than PAGE_SIZE */ instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines); instructions += 2; if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0) return rc; /* write risc instructions */ rp = risc->cpu; if (UNSET != top_offset) rp = cx88_risc_field(rp, sglist, top_offset, 0, bpl, padding, lines); if (UNSET != bottom_offset) rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200, bpl, padding, lines); /* save pointer to jmp instruction address */ risc->jmp = rp; BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size); return 0;}int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc, struct scatterlist *sglist, unsigned int bpl, unsigned int lines){ u32 instructions; u32 *rp; int rc; /* estimate risc mem: worst case is one write per page border + one write per scan line + syncs + jump (all 2 dwords). Here there is no padding and no sync. First DMA region may be smaller than PAGE_SIZE */ instructions = 1 + (bpl * lines) / PAGE_SIZE + lines; instructions += 1; if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0) return rc; /* write risc instructions */ rp = risc->cpu; rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines); /* save pointer to jmp instruction address */ risc->jmp = rp; BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size); return 0;}int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, u32 reg, u32 mask, u32 value){ u32 *rp; int rc; if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0) return rc; /* write risc instructions */ rp = risc->cpu; *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2 | RISC_IMM); *(rp++) = cpu_to_le32(reg); *(rp++) = cpu_to_le32(value); *(rp++) = cpu_to_le32(mask); *(rp++) = cpu_to_le32(RISC_JUMP); *(rp++) = cpu_to_le32(risc->dma); return 0;}voidcx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf){ BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); videobuf_dma_unmap(q, &buf->vb.dma); videobuf_dma_free(&buf->vb.dma); btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc); buf->vb.state = STATE_NEEDS_INIT;}/* ------------------------------------------------------------------ *//* our SRAM memory layout *//* we are going to put all thr risc programs into host memory, so we * can use the whole SDRAM for the DMA fifos. To simplify things, we * use a static memory layout. That surely will waste memory in case * we don't use all DMA channels at the same time (which will be the * case most of the time). But that still gives us enougth FIFO space * to be able to deal with insane long pci latencies ... * * FIFO space allocations: * channel 21 (y video) - 10.0k * channel 22 (u video) - 2.0k * channel 23 (v video) - 2.0k * channel 24 (vbi) - 4.0k * channels 25+26 (audio) - 4.0k * channel 28 (mpeg) - 4.0k * TOTAL = 29.0k * * Every channel has 160 bytes control data (64 bytes instruction * queue and 6 CDT entries), which is close to 2k total. * * Address layout: * 0x0000 - 0x03ff CMDs / reserved * 0x0400 - 0x0bff instruction queues + CDs * 0x0c00 - FIFOs */struct sram_channel cx88_sram_channels[] = { [SRAM_CH21] = { .name = "video y / packed", .cmds_start = 0x180040, .ctrl_start = 0x180400, .cdt = 0x180400 + 64, .fifo_start = 0x180c00, .fifo_size = 0x002800, .ptr1_reg = MO_DMA21_PTR1, .ptr2_reg = MO_DMA21_PTR2, .cnt1_reg = MO_DMA21_CNT1, .cnt2_reg = MO_DMA21_CNT2, }, [SRAM_CH22] = { .name = "video u", .cmds_start = 0x180080, .ctrl_start = 0x1804a0, .cdt = 0x1804a0 + 64, .fifo_start = 0x183400, .fifo_size = 0x000800, .ptr1_reg = MO_DMA22_PTR1, .ptr2_reg = MO_DMA22_PTR2, .cnt1_reg = MO_DMA22_CNT1, .cnt2_reg = MO_DMA22_CNT2, }, [SRAM_CH23] = { .name = "video v", .cmds_start = 0x1800c0, .ctrl_start = 0x180540, .cdt = 0x180540 + 64, .fifo_start = 0x183c00, .fifo_size = 0x000800, .ptr1_reg = MO_DMA23_PTR1, .ptr2_reg = MO_DMA23_PTR2, .cnt1_reg = MO_DMA23_CNT1, .cnt2_reg = MO_DMA23_CNT2, }, [SRAM_CH24] = { .name = "vbi", .cmds_start = 0x180100, .ctrl_start = 0x1805e0, .cdt = 0x1805e0 + 64, .fifo_start = 0x184400, .fifo_size = 0x001000, .ptr1_reg = MO_DMA24_PTR1, .ptr2_reg = MO_DMA24_PTR2, .cnt1_reg = MO_DMA24_CNT1, .cnt2_reg = MO_DMA24_CNT2, }, [SRAM_CH25] = { .name = "audio from", .cmds_start = 0x180140, .ctrl_start = 0x180680, .cdt = 0x180680 + 64, .fifo_start = 0x185400, .fifo_size = 0x001000, .ptr1_reg = MO_DMA25_PTR1, .ptr2_reg = MO_DMA25_PTR2, .cnt1_reg = MO_DMA25_CNT1, .cnt2_reg = MO_DMA25_CNT2, }, [SRAM_CH26] = { .name = "audio to", .cmds_start = 0x180180, .ctrl_start = 0x180720, .cdt = 0x180680 + 64, /* same as audio IN */ .fifo_start = 0x185400, /* same as audio IN */ .fifo_size = 0x001000, /* same as audio IN */ .ptr1_reg = MO_DMA26_PTR1, .ptr2_reg = MO_DMA26_PTR2, .cnt1_reg = MO_DMA26_CNT1, .cnt2_reg = MO_DMA26_CNT2, }, [SRAM_CH28] = { .name = "mpeg", .cmds_start = 0x180200, .ctrl_start = 0x1807C0, .cdt = 0x1807C0 + 64, .fifo_start = 0x186400, .fifo_size = 0x001000, .ptr1_reg = MO_DMA28_PTR1, .ptr2_reg = MO_DMA28_PTR2, .cnt1_reg = MO_DMA28_CNT1, .cnt2_reg = MO_DMA28_CNT2, },};int cx88_sram_channel_setup(struct cx88_core *core, struct sram_channel *ch, unsigned int bpl, u32 risc){ unsigned int i,lines; u32 cdt; bpl = (bpl + 7) & ~7; /* alignment */ cdt = ch->cdt; lines = ch->fifo_size / bpl; if (lines > 6) lines = 6; BUG_ON(lines < 2); /* write CDT */ for (i = 0; i < lines; i++) cx_write(cdt + 16*i, ch->fifo_start + bpl*i); /* write CMDS */ cx_write(ch->cmds_start + 0, risc); cx_write(ch->cmds_start + 4, cdt); cx_write(ch->cmds_start + 8, (lines*16) >> 3); cx_write(ch->cmds_start + 12, ch->ctrl_start); cx_write(ch->cmds_start + 16, 64 >> 2); for (i = 20; i < 64; i += 4) cx_write(ch->cmds_start + i, 0); /* fill registers */ cx_write(ch->ptr1_reg, ch->fifo_start); cx_write(ch->ptr2_reg, cdt); cx_write(ch->cnt1_reg, (bpl >> 3) -1); cx_write(ch->cnt2_reg, (lines*16) >> 3); dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines); return 0;}/* ------------------------------------------------------------------ *//* debug helper code */static int cx88_risc_decode(u32 risc){ static char *instr[16] = { [ RISC_SYNC >> 28 ] = "sync", [ RISC_WRITE >> 28 ] = "write", [ RISC_WRITEC >> 28 ] = "writec", [ RISC_READ >> 28 ] = "read", [ RISC_READC >> 28 ] = "readc", [ RISC_JUMP >> 28 ] = "jump", [ RISC_SKIP >> 28 ] = "skip", [ RISC_WRITERM >> 28 ] = "writerm", [ RISC_WRITECM >> 28 ] = "writecm", [ RISC_WRITECR >> 28 ] = "writecr",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -