📄 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 <linux/videodev2.h>#include "cx88.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 };module_param_array(tuner, int, NULL, 0444);module_param_array(radio, int, NULL, 0444);module_param_array(card, int, NULL, 0444);MODULE_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 DECLARE_MUTEX(devlist);/* ------------------------------------------------------------------ *//* debug help functions */static const char *v4l1_ioctls[] = { "0", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE", "GVBIFMT", "SVBIFMT" };#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls)static const char *v4l2_ioctls[] = { "QUERYCAP", "1", "ENUM_PIXFMT", "ENUM_FBUFFMT", "G_FMT", "S_FMT", "G_COMP", "S_COMP", "REQBUFS", "QUERYBUF", "G_FBUF", "S_FBUF", "G_WIN", "S_WIN", "PREVIEW", "QBUF", "16", "DQBUF", "STREAMON", "STREAMOFF", "G_PERF", "G_PARM", "S_PARM", "G_STD", "S_STD", "ENUMSTD", "ENUMINPUT", "G_CTRL", "S_CTRL", "G_TUNER", "S_TUNER", "G_FREQ", "S_FREQ", "G_AUDIO", "S_AUDIO", "35", "QUERYCTRL", "QUERYMENU", "G_INPUT", "S_INPUT", "ENUMCVT", "41", "42", "43", "44", "45", "G_OUTPUT", "S_OUTPUT", "ENUMOUTPUT", "G_AUDOUT", "S_AUDOUT", "ENUMFX", "G_EFFECT", "S_EFFECT", "G_MODULATOR", "S_MODULATOR"};#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)void cx88_print_ioctl(char *name, unsigned int cmd){ char *dir; switch (_IOC_DIR(cmd)) { case _IOC_NONE: dir = "--"; break; case _IOC_READ: dir = "r-"; break; case _IOC_WRITE: dir = "-w"; break; case _IOC_READ | _IOC_WRITE: dir = "rw"; break; default: dir = "??"; break; } switch (_IOC_TYPE(cmd)) { case 'v': printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l1, %s, VIDIOC%s)\n", name, cmd, dir, (_IOC_NR(cmd) < V4L1_IOCTLS) ? v4l1_ioctls[_IOC_NR(cmd)] : "???"); break; case 'V': printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l2, %s, VIDIOC_%s)\n", name, cmd, dir, (_IOC_NR(cmd) < V4L2_IOCTLS) ? v4l2_ioctls[_IOC_NR(cmd)] : "???"); break; default: printk(KERN_DEBUG "%s: ioctl 0x%08x (???, %s, #%d)\n", name, cmd, dir, _IOC_NR(cmd)); }}/* ------------------------------------------------------------------ */#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) */ instructions = (bpl * lines * fields) / PAGE_SIZE + lines * fields; instructions += 3 + 4; 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) / 4 > 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) */ instructions = (bpl * lines) / PAGE_SIZE + lines; instructions += 3 + 4; 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) / 4 > 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 pci_dev *pci, struct cx88_buffer *buf){ if (in_interrupt()) BUG(); videobuf_waiton(&buf->vb,0,0); videobuf_dma_pci_unmap(pci, &buf->vb.dma); videobuf_dma_free(&buf->vb.dma); btcx_riscmem_free(pci, &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) - 0.5k * channel 28 (mpeg) - 4.0k * TOTAL = 25.5k * * 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 = 0x000200, .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 = 0x000200, /* 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 = 0x185600, .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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -