summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video/cx88/cx88-video.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drivers/media/video/cx88/cx88-video.c')
-rw-r--r--linux/drivers/media/video/cx88/cx88-video.c364
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);