diff options
Diffstat (limited to 'linux/drivers/media/video/cx88/cx88-video.c')
-rw-r--r-- | linux/drivers/media/video/cx88/cx88-video.c | 364 |
1 files changed, 27 insertions, 337 deletions
diff --git a/linux/drivers/media/video/cx88/cx88-video.c b/linux/drivers/media/video/cx88/cx88-video.c index fe87431a6..31df4ae75 100644 --- a/linux/drivers/media/video/cx88/cx88-video.c +++ b/linux/drivers/media/video/cx88/cx88-video.c @@ -1,5 +1,5 @@ /* - * $Id: cx88-video.c,v 1.31 2004/07/30 13:43:39 kraxel Exp $ + * $Id: cx88-video.c,v 1.32 2004/08/25 14:47:53 kraxel Exp $ * * device driver for Conexant 2388x based TV cards * video4linux video interface @@ -28,12 +28,12 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/kthread.h> #include <asm/div64.h> #include "cx88.h" -#define V4L2_I2C_CLIENTS 1 - MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); @@ -64,10 +64,6 @@ static unsigned int vid_limit = 16; MODULE_PARM(vid_limit,"i"); MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes"); -static unsigned int nicam = 0; -MODULE_PARM(nicam,"i"); -MODULE_PARM_DESC(nicam,"tv audio is nicam"); - #define dprintk(level,fmt, arg...) if (video_debug >= level) \ printk(KERN_DEBUG "%s/0: " fmt, dev->core->name , ## arg) @@ -78,58 +74,7 @@ static LIST_HEAD(cx8800_devlist); /* ------------------------------------------------------------------- */ /* static data */ -static unsigned int inline norm_swidth(struct cx8800_tvnorm *norm) -{ - return (norm->id & V4L2_STD_625_50) ? 922 : 754; -} - -static unsigned int inline norm_hdelay(struct cx8800_tvnorm *norm) -{ - return (norm->id & V4L2_STD_625_50) ? 186 : 135; -} - -static unsigned int inline norm_vdelay(struct cx8800_tvnorm *norm) -{ - return (norm->id & V4L2_STD_625_50) ? 0x24 : 0x18; -} - -static unsigned int inline norm_maxw(struct cx8800_tvnorm *norm) -{ - return (norm->id & V4L2_STD_625_50) ? 768 : 640; -// return (norm->id & V4L2_STD_625_50) ? 720 : 640; -} - -static unsigned int inline norm_maxh(struct cx8800_tvnorm *norm) -{ - return (norm->id & V4L2_STD_625_50) ? 576 : 480; -} - -static unsigned int inline norm_fsc8(struct cx8800_tvnorm *norm) -{ - static const unsigned int ntsc = 28636360; - static const unsigned int pal = 35468950; - - return (norm->id & V4L2_STD_625_50) ? pal : ntsc; -} - -static unsigned int inline norm_notchfilter(struct cx8800_tvnorm *norm) -{ - return (norm->id & V4L2_STD_625_50) - ? HLNotchFilter135PAL - : HLNotchFilter135NTSC; -} - -static unsigned int inline norm_htotal(struct cx8800_tvnorm *norm) -{ - return (norm->id & V4L2_STD_625_50) ? 1135 : 910; -} - -static unsigned int inline norm_vbipack(struct cx8800_tvnorm *norm) -{ - return (norm->id & V4L2_STD_625_50) ? 511 : 288; -} - -static struct cx8800_tvnorm tvnorms[] = { +static struct cx88_tvnorm tvnorms[] = { { .name = "NTSC-M", .id = V4L2_STD_NTSC_M, @@ -432,261 +377,6 @@ void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits) /* ------------------------------------------------------------------ */ -static const u32 xtal = 28636363; - -static int set_pll(struct cx8800_dev *dev, int prescale, u32 ofreq) -{ - static u32 pre[] = { 0, 0, 0, 3, 2, 1 }; - struct cx88_core *core = dev->core; - 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 < 10; 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(100); - } - dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq); - return -1; -} - -static int set_tvaudio(struct cx8800_dev *dev) -{ - struct cx88_core *core = dev->core; - - if (CX88_VMUX_TELEVISION != INPUT(dev->input)->type) - return 0; - - if (V4L2_STD_PAL_BG & dev->tvnorm->id) { - dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_BG; - - } else if (V4L2_STD_PAL_DK & dev->tvnorm->id) { - dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_DK; - - } else if (V4L2_STD_PAL_I & dev->tvnorm->id) { - dev->tvaudio = WW_NICAM_I; - - } else if (V4L2_STD_SECAM_L & dev->tvnorm->id) { - dev->tvaudio = WW_SYSTEM_L_AM; - - } else if (V4L2_STD_SECAM_DK & dev->tvnorm->id) { - dev->tvaudio = WW_A2_DK; - - } else if ((V4L2_STD_NTSC_M & dev->tvnorm->id) || - (V4L2_STD_PAL_M & dev->tvnorm->id)) { - dev->tvaudio = WW_BTSC; - - } else if (V4L2_STD_NTSC_M_JP & dev->tvnorm->id) { - dev->tvaudio = WW_EIAJ; - - } else { - printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n", - core->name, dev->tvnorm->name); - dev->tvaudio = 0; - return 0; - } - - cx_andor(MO_AFECFG_IO, 0x1f, 0x0); - cx88_set_tvaudio(dev); - // cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); - - cx_write(MO_AUDD_LNGTH, 128/8); /* fifo size */ - cx_write(MO_AUDR_LNGTH, 128/8); /* fifo size */ - cx_write(MO_AUD_DMACNTRL, 0x03); /* need audio fifo */ - return 0; -} - -static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm) -{ - struct cx88_core *core = dev->core; - u32 fsc8; - u32 adc_clock; - u32 vdec_clock; - u32 step_db,step_dr; - u64 tmp64; - u32 bdelay,agcdelay,htotal; - - dev->tvnorm = norm; - fsc8 = norm_fsc8(norm); - adc_clock = xtal; - vdec_clock = fsc8; - step_db = fsc8; - step_dr = fsc8; - - if (norm->id & V4L2_STD_SECAM) { - step_db = 4250000 * 8; - step_dr = 4406250 * 8; - } - - dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n", - norm->name, fsc8, adc_clock, vdec_clock, step_db, step_dr); - set_pll(dev,2,vdec_clock); - - dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n", - norm->cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f); - cx_andor(MO_INPUT_FORMAT, 0xf, norm->cxiformat); - -#if 1 - // FIXME: as-is from DScaler - dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n", - norm->cxoformat, cx_read(MO_OUTPUT_FORMAT)); - cx_write(MO_OUTPUT_FORMAT, norm->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 | (norm_notchfilter(norm) << 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 - cx_write(MO_VBI_PACKET, ((1 << 11) | /* (norm_vdelay(norm) << 11) | */ - norm_vbipack(norm))); - - // audio - set_tvaudio(dev); - - // tell i2c chips -#ifdef V4L2_I2C_CLIENTS - cx88_call_i2c_clients(dev->core,VIDIOC_S_STD,&norm->id); -#else - { - struct video_channel c; - memset(&c,0,sizeof(c)); - c.channel = dev->input; - c.norm = VIDEO_MODE_PAL; - if ((norm->id & (V4L2_STD_NTSC_M|V4L2_STD_NTSC_M_JP))) - c.norm = VIDEO_MODE_NTSC; - if (norm->id & V4L2_STD_SECAM) - c.norm = VIDEO_MODE_SECAM; - cx88_call_i2c_clients(dev->core,VIDIOCSCHAN,&c); - } -#endif - - // done - return 0; -} - -static int set_scale(struct cx8800_dev *dev, unsigned int width, unsigned int height, - enum v4l2_field field) -{ - struct cx88_core *core = dev->core; - unsigned int swidth = norm_swidth(dev->tvnorm); - unsigned int sheight = norm_maxh(dev->tvnorm); - u32 value; - - dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height, - V4L2_FIELD_HAS_TOP(field) ? "T" : "", - V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "", - dev->tvnorm->name); - if (!V4L2_FIELD_HAS_BOTH(field)) - height *= 2; - - // recalc H delay and scale registers - value = (width * norm_hdelay(dev->tvnorm)) / swidth; - value &= 0x3fe; - cx_write(MO_HDELAY_EVEN, value); - cx_write(MO_HDELAY_ODD, value); - dprintk(1,"set_scale: hdelay 0x%04x\n", value); - - value = (swidth * 4096 / width) - 4096; - cx_write(MO_HSCALE_EVEN, value); - cx_write(MO_HSCALE_ODD, value); - dprintk(1,"set_scale: hscale 0x%04x\n", value); - - cx_write(MO_HACTIVE_EVEN, width); - cx_write(MO_HACTIVE_ODD, width); - dprintk(1,"set_scale: hactive 0x%04x\n", width); - - // recalc V scale Register (delay is constant) - cx_write(MO_VDELAY_EVEN, norm_vdelay(dev->tvnorm)); - cx_write(MO_VDELAY_ODD, norm_vdelay(dev->tvnorm)); - dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(dev->tvnorm)); - - value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff; - cx_write(MO_VSCALE_EVEN, value); - cx_write(MO_VSCALE_ODD, value); - dprintk(1,"set_scale: vscale 0x%04x\n", value); - - cx_write(MO_VACTIVE_EVEN, sheight); - cx_write(MO_VACTIVE_ODD, sheight); - dprintk(1,"set_scale: vactive 0x%04x\n", sheight); - - // setup filters - value = 0; - value |= (1 << 19); // CFILT (default) - if (dev->tvnorm->id & V4L2_STD_SECAM) { - value |= (1 << 15); - value |= (1 << 16); - } - if (INPUT(dev->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 - - cx_write(MO_FILTER_EVEN, value); - cx_write(MO_FILTER_ODD, value); - dprintk(1,"set_scale: filter 0x%04x\n", value); - - return 0; -} - static int video_mux(struct cx8800_dev *dev, unsigned int input) { struct cx88_core *core = dev->core; @@ -695,7 +385,7 @@ static int video_mux(struct cx8800_dev *dev, unsigned int input) input, INPUT(input)->vmux, INPUT(input)->gpio0,INPUT(input)->gpio1, INPUT(input)->gpio2,INPUT(input)->gpio3); - dev->input = input; + dev->core->input = input; cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input)->vmux << 14); cx_write(MO_GP0_IO, INPUT(input)->gpio0); cx_write(MO_GP1_IO, INPUT(input)->gpio1); @@ -730,7 +420,7 @@ static int start_video_dma(struct cx8800_dev *dev, /* setup fifo + format */ cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH21], buf->bpl, buf->risc.dma); - set_scale(dev, buf->vb.width, buf->vb.height, buf->vb.field); + cx88_set_scale(dev->core, buf->vb.width, buf->vb.height, buf->vb.field); cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma); /* reset counter */ @@ -827,8 +517,8 @@ buffer_prepare(struct file *file, struct videobuf_buffer *vb, int rc, init_buffer = 0; BUG_ON(NULL == fh->fmt); - if (fh->width < 48 || fh->width > norm_maxw(dev->tvnorm) || - fh->height < 32 || fh->height > norm_maxh(dev->tvnorm)) + if (fh->width < 48 || fh->width > norm_maxw(dev->core->tvnorm) || + fh->height < 32 || fh->height > norm_maxh(dev->core->tvnorm)) return -EINVAL; buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) @@ -1092,8 +782,8 @@ static int verify_window(struct cx8800_dev *dev, struct v4l2_window *win) return -EINVAL; field = win->field; - maxw = norm_maxw(dev->tvnorm); - maxh = norm_maxh(dev->tvnorm); + maxw = norm_maxw(core->tvnorm); + maxh = norm_maxh(core->tvnorm); if (V4L2_FIELD_ANY == field) { field = (win->w.height > maxh/2) @@ -1286,9 +976,9 @@ static int video_open(struct inode *inode, struct file *file) cx_write(MO_GP1_IO, cx88_boards[board].radio.gpio1); cx_write(MO_GP2_IO, cx88_boards[board].radio.gpio2); cx_write(MO_GP3_IO, cx88_boards[board].radio.gpio3); - dev->tvaudio = WW_FM; - cx88_set_tvaudio(dev); - cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); + dev->core->tvaudio = WW_FM; + cx88_set_tvaudio(core); + cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO); cx88_call_i2c_clients(dev->core,AUDC_SET_RADIO,NULL); } @@ -1503,8 +1193,8 @@ static int cx8800_try_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, return -EINVAL; field = f->fmt.pix.field; - maxw = norm_maxw(dev->tvnorm); - maxh = norm_maxh(dev->tvnorm); + maxw = norm_maxw(dev->core->tvnorm); + maxh = norm_maxh(dev->core->tvnorm); if (V4L2_FIELD_ANY == field) { field = (f->fmt.pix.height > maxh/2) @@ -1636,7 +1326,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, { v4l2_std_id *id = arg; - *id = dev->tvnorm->id; + *id = core->tvnorm->id; return 0; } case VIDIOC_S_STD: @@ -1651,7 +1341,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, return -EINVAL; down(&dev->lock); - set_tvnorm(dev,&tvnorms[i]); + cx88_set_tvnorm(dev->core,&tvnorms[i]); up(&dev->lock); return 0; } @@ -1691,7 +1381,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, { unsigned int *i = arg; - *i = dev->input; + *i = dev->core->input; return 0; } case VIDIOC_S_INPUT: @@ -1787,7 +1477,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, t->capability = V4L2_TUNER_CAP_NORM; t->rangehigh = 0xffffffffUL; - cx88_get_stereo(dev ,t); + cx88_get_stereo(core ,t); reg = cx_read(MO_DEVICE_STATUS); t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; return 0; @@ -1800,7 +1490,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, return -EINVAL; if (0 != t->index) return -EINVAL; - cx88_set_stereo(dev,t->audmode); + cx88_set_stereo(core, t->audmode); return 0; } case VIDIOC_G_FREQUENCY: @@ -2266,7 +1956,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, /* initialize driver struct */ init_MUTEX(&dev->lock); dev->slock = SPIN_LOCK_UNLOCKED; - dev->tvnorm = tvnorms; + core->tvnorm = tvnorms; /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); @@ -2350,13 +2040,12 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, /* initial device configuration */ down(&dev->lock); init_controls(dev); - set_tvnorm(dev,tvnorms); + cx88_set_tvnorm(dev->core,tvnorms); video_mux(dev,0); up(&dev->lock); /* start tvaudio thread */ - init_completion(&dev->texit); - dev->tpid = kernel_thread(cx88_audio_thread, dev, 0); + core->kthread = kthread_run(cx88_audio_thread, core, "cx88 tvaudio"); return 0; fail_unreg: @@ -2374,9 +2063,10 @@ static void __devexit cx8800_finidev(struct pci_dev *pci_dev) struct cx8800_dev *dev = pci_get_drvdata(pci_dev); /* stop thread */ - dev->shutdown = 1; - if (dev->tpid >= 0) - wait_for_completion(&dev->texit); + if (dev->core->kthread) { + kthread_stop(dev->core->kthread); + dev->core->kthread = NULL; + } cx88_shutdown(dev->core); /* FIXME */ pci_disable_device(pci_dev); |