cx88-core.c
来自「trident tm5600的linux驱动」· C语言 代码 · 共 1,130 行 · 第 1/3 页
C
1,130 行
// setup filters value = 0; value |= (1 << 19); // CFILT (default) if (core->tvnorm & V4L2_STD_SECAM) { value |= (1 << 15); value |= (1 << 16); } if (INPUT(core->input).type == CX88_VMUX_SVIDEO) value |= (1 << 13) | (1 << 5); if (V4L2_FIELD_INTERLACED == field) value |= (1 << 3); // VINT (interlaced vertical scaling) if (width < 385) value |= (1 << 0); // 3-tap interpolation if (width < 193) value |= (1 << 1); // 5-tap interpolation if (nocomb) value |= (3 << 5); // disable comb filter cx_write(MO_FILTER_EVEN, value); cx_write(MO_FILTER_ODD, value); dprintk(1,"set_scale: filter 0x%04x\n", value); return 0;}static const u32 xtal = 28636363;static int set_pll(struct cx88_core *core, int prescale, u32 ofreq){ static u32 pre[] = { 0, 0, 0, 3, 2, 1 }; u64 pll; u32 reg; int i; if (prescale < 2) prescale = 2; if (prescale > 5) prescale = 5; pll = ofreq * 8 * prescale * (u64)(1 << 20); do_div(pll,xtal); reg = (pll & 0x3ffffff) | (pre[prescale] << 26); if (((reg >> 20) & 0x3f) < 14) { printk("%s/0: pll out of range\n",core->name); return -1; } dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n", reg, cx_read(MO_PLL_REG), ofreq); cx_write(MO_PLL_REG, reg); for (i = 0; i < 100; i++) { reg = cx_read(MO_DEVICE_STATUS); if (reg & (1<<2)) { dprintk(1,"pll locked [pre=%d,ofreq=%d]\n", prescale,ofreq); return 0; } dprintk(1,"pll not locked yet, waiting ...\n"); msleep(10); } dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq); return -1;}int cx88_start_audio_dma(struct cx88_core *core){ /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */ int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4; /* If downstream RISC is enabled, bail out; ALSA is managing DMA */ if (cx_read(MO_AUD_DMACNTRL) & 0x10) return 0; /* setup fifo + format */ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0); cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0); cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */ cx_write(MO_AUDR_LNGTH, bpl); /* fifo bpl size */ /* start dma */ cx_write(MO_AUD_DMACNTRL, 0x0003); /* Up and Down fifo enable */ return 0;}int cx88_stop_audio_dma(struct cx88_core *core){ /* If downstream RISC is enabled, bail out; ALSA is managing DMA */ if (cx_read(MO_AUD_DMACNTRL) & 0x10) return 0; /* stop dma */ cx_write(MO_AUD_DMACNTRL, 0x0000); return 0;}static int set_tvaudio(struct cx88_core *core){ v4l2_std_id norm = core->tvnorm; if (CX88_VMUX_TELEVISION != INPUT(core->input).type) return 0; if (V4L2_STD_PAL_BG & norm) { core->tvaudio = WW_BG; } else if (V4L2_STD_PAL_DK & norm) { core->tvaudio = WW_DK; } else if (V4L2_STD_PAL_I & norm) { core->tvaudio = WW_I; } else if (V4L2_STD_SECAM_L & norm) { core->tvaudio = WW_L; } else if (V4L2_STD_SECAM_DK & norm) { core->tvaudio = WW_DK; } else if ((V4L2_STD_NTSC_M & norm) || (V4L2_STD_PAL_M & norm)) { core->tvaudio = WW_BTSC; } else if (V4L2_STD_NTSC_M_JP & norm) { core->tvaudio = WW_EIAJ; } else { printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n", core->name, v4l2_norm_to_name(core->tvnorm)); core->tvaudio = 0; return 0; } cx_andor(MO_AFECFG_IO, 0x1f, 0x0); cx88_set_tvaudio(core); /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */#if 1/* This should be needed only on cx88-alsa. It seems that some cx88 chips have bugs and does require DMA enabled for it to work. */ cx88_start_audio_dma(core);#endif return 0;}int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm){ u32 fsc8; u32 adc_clock; u32 vdec_clock; u32 step_db,step_dr; u64 tmp64; u32 bdelay,agcdelay,htotal; u32 cxiformat, cxoformat; core->tvnorm = norm; fsc8 = norm_fsc8(norm); adc_clock = xtal; vdec_clock = fsc8; step_db = fsc8; step_dr = fsc8; if (norm & V4L2_STD_NTSC_M_JP) { cxiformat = VideoFormatNTSCJapan; cxoformat = 0x181f0008; } else if (norm & V4L2_STD_NTSC_443) { cxiformat = VideoFormatNTSC443; cxoformat = 0x181f0008; } else if (norm & V4L2_STD_PAL_M) { cxiformat = VideoFormatPALM; cxoformat = 0x1c1f0008; } else if (norm & V4L2_STD_PAL_N) { cxiformat = VideoFormatPALN; cxoformat = 0x1c1f0008; } else if (norm & V4L2_STD_PAL_Nc) { cxiformat = VideoFormatPALNC; cxoformat = 0x1c1f0008; } else if (norm & V4L2_STD_PAL_60) { cxiformat = VideoFormatPAL60; cxoformat = 0x181f0008; } else if (norm & V4L2_STD_NTSC) { cxiformat = VideoFormatNTSC; cxoformat = 0x181f0008; } else if (norm & V4L2_STD_SECAM) { step_db = 4250000 * 8; step_dr = 4406250 * 8; cxiformat = VideoFormatSECAM; cxoformat = 0x181f0008; } else { /* PAL */ cxiformat = VideoFormatPAL; cxoformat = 0x181f0008; } dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n", v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock, step_db, step_dr); set_pll(core,2,vdec_clock); dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n", cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f); /* Chroma AGC must be disabled if SECAM is used, we enable it by default on PAL and NTSC */ cx_andor(MO_INPUT_FORMAT, 0x40f, norm & V4L2_STD_SECAM ? cxiformat : cxiformat | 0x400);#if 1 // FIXME: as-is from DScaler dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n", cxoformat, cx_read(MO_OUTPUT_FORMAT)); cx_write(MO_OUTPUT_FORMAT, cxoformat);#endif // MO_SCONV_REG = adc clock / video dec clock * 2^17 tmp64 = adc_clock * (u64)(1 << 17); do_div(tmp64, vdec_clock); dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n", (u32)tmp64, cx_read(MO_SCONV_REG)); cx_write(MO_SCONV_REG, (u32)tmp64); // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22 tmp64 = step_db * (u64)(1 << 22); do_div(tmp64, vdec_clock); dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n", (u32)tmp64, cx_read(MO_SUB_STEP)); cx_write(MO_SUB_STEP, (u32)tmp64); // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22 tmp64 = step_dr * (u64)(1 << 22); do_div(tmp64, vdec_clock); dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n", (u32)tmp64, cx_read(MO_SUB_STEP_DR)); cx_write(MO_SUB_STEP_DR, (u32)tmp64); // bdelay + agcdelay bdelay = vdec_clock * 65 / 20000000 + 21; agcdelay = vdec_clock * 68 / 20000000 + 15; dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n", (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay); cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay); // htotal tmp64 = norm_htotal(norm) * (u64)vdec_clock; do_div(tmp64, fsc8); htotal = (u32)tmp64 | (HLNotchFilter4xFsc << 11); dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n", htotal, cx_read(MO_HTOTAL), (u32)tmp64); cx_write(MO_HTOTAL, htotal); // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes // the effective vbi offset ~244 samples, the same as the Bt8x8 cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm)); // this is needed as well to set all tvnorm parameter cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED); // audio set_tvaudio(core); // tell i2c chips cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm); // done return 0;}/* ------------------------------------------------------------------ */struct video_device *cx88_vdev_init(struct cx88_core *core, struct pci_dev *pci, struct video_device *template, char *type){ struct video_device *vfd; vfd = video_device_alloc(); if (NULL == vfd) return NULL; *vfd = *template; vfd->minor = -1; vfd->parent = &pci->dev; vfd->release = video_device_release; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", core->name, type, core->board.name); return vfd;}struct cx88_core* cx88_core_get(struct pci_dev *pci){ struct cx88_core *core; mutex_lock(&devlist); list_for_each_entry(core, &cx88_devlist, devlist) { if (pci->bus->number != core->pci_bus) continue; if (PCI_SLOT(pci->devfn) != core->pci_slot) continue; if (0 != cx88_get_resources(core, pci)) { mutex_unlock(&devlist); return NULL; } atomic_inc(&core->refcount); mutex_unlock(&devlist); return core; } core = cx88_core_create(pci, cx88_devcount); if (NULL != core) { cx88_devcount++; list_add_tail(&core->devlist, &cx88_devlist); } mutex_unlock(&devlist); return core;}void cx88_core_put(struct cx88_core *core, struct pci_dev *pci){ release_mem_region(pci_resource_start(pci,0), pci_resource_len(pci,0)); if (!atomic_dec_and_test(&core->refcount)) return; mutex_lock(&devlist); cx88_ir_fini(core); if (0 == core->i2c_rc) i2c_del_adapter(&core->i2c_adap); list_del(&core->devlist); iounmap(core->lmmio); cx88_devcount--; mutex_unlock(&devlist); kfree(core);}/* ------------------------------------------------------------------ */EXPORT_SYMBOL(cx88_print_irqbits);EXPORT_SYMBOL(cx88_core_irq);EXPORT_SYMBOL(cx88_wakeup);EXPORT_SYMBOL(cx88_reset);EXPORT_SYMBOL(cx88_shutdown);EXPORT_SYMBOL(cx88_risc_buffer);EXPORT_SYMBOL(cx88_risc_databuffer);EXPORT_SYMBOL(cx88_risc_stopper);EXPORT_SYMBOL(cx88_free_buffer);EXPORT_SYMBOL(cx88_sram_channels);EXPORT_SYMBOL(cx88_sram_channel_setup);EXPORT_SYMBOL(cx88_sram_channel_dump);EXPORT_SYMBOL(cx88_set_tvnorm);EXPORT_SYMBOL(cx88_set_scale);EXPORT_SYMBOL(cx88_vdev_init);EXPORT_SYMBOL(cx88_core_get);EXPORT_SYMBOL(cx88_core_put);EXPORT_SYMBOL(cx88_ir_start);EXPORT_SYMBOL(cx88_ir_stop);/* * Local variables: * c-basic-offset: 8 * End: * 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 off */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?