Using standard driver from RedHat kernel (2.4.9-31 and 2.4.18-0.13) my DC10+ card was unable to show correctly VCR output from VHS (the tuner output was ok). Updating the saa7110 driver the problem disappeared. Apparently the saa7110 driver shipped with those kernels is old, but updated for a clean compilation, and appear to be the same from vanilla 2.4.18. I've merged a newer driver (0.8) with those patches. Hope this helps. Gabriele Turchi turchi@xxxxxxxx P.S.: I'm sorry, my english is alpha version...
--- saa7110.rhl Wed Apr 24 12:36:52 2002 +++ saa7110.c Wed Apr 24 12:45:08 2002 @@ -2,6 +2,10 @@ saa7110 - Philips SAA7110(A) video decoder driver Copyright (C) 1998 Pauline Middelink <middelin@xxxxxxxxxxx> + + Copyright (C) 1999 Wolfgang Scherr <scherr@xxxxxxxxxxx> + Copyright (C) 2000 Serguei Miridonov <mirsev@xxxxxxxxx> + - some corrections for Pinnacle Systems Inc. DC10plus card. 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 @@ -18,6 +22,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/version.h> + #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> @@ -28,7 +34,7 @@ #include <linux/i2c-old.h> #include <linux/videodev.h> -#include "linux/video_decoder.h" +#include <linux/video_decoder.h> #define DEBUG(x...) /* remove when no long debugging */ @@ -42,7 +48,7 @@ struct saa7110 { struct i2c_bus *bus; int addr; - unsigned char reg[36]; + unsigned char reg[54]; int norm; int input; @@ -51,6 +57,8 @@ int contrast; int hue; int sat; + + wait_queue_head_t wq; }; /* ----------------------------------------------------------------------- */ @@ -83,7 +91,7 @@ while (len-- > 0) { if (i2c_sendbyte(decoder->bus,*data,0)) { i2c_stop(decoder->bus); - UNLOCK_I2C_BUS(decoder->bus); + UNLOCK_I2C_BUS(decoder->bus); return -EAGAIN; } decoder->reg[subaddr++] = *data++; @@ -101,8 +109,8 @@ LOCK_I2C_BUS(decoder->bus); i2c_start(decoder->bus); - i2c_sendbyte(decoder->bus, decoder->addr, I2C_DELAY); - i2c_start(decoder->bus); + //i2c_sendbyte(decoder->bus, decoder->addr, I2C_DELAY); + //i2c_start(decoder->bus); i2c_sendbyte(decoder->bus, decoder->addr | 1, I2C_DELAY); data = i2c_readbyte(decoder->bus, 1); i2c_stop(decoder->bus); @@ -113,19 +121,24 @@ /* ----------------------------------------------------------------------- */ /* SAA7110 functions */ /* ----------------------------------------------------------------------- */ + +#define FRESP_06H_COMPST 0x03 //0x13 +#define FRESP_06H_SVIDEO 0x83 //0xC0 + + static int saa7110_selmux(struct i2c_device *device, int chan) { static const unsigned char modes[9][8] = { -/* mode 0 */ { 0x00, 0xD9, 0x17, 0x40, 0x03, 0x44, 0x75, 0x16 }, -/* mode 1 */ { 0x00, 0xD8, 0x17, 0x40, 0x03, 0x44, 0x75, 0x16 }, -/* mode 2 */ { 0x00, 0xBA, 0x07, 0x91, 0x03, 0x60, 0xB5, 0x05 }, -/* mode 3 */ { 0x00, 0xB8, 0x07, 0x91, 0x03, 0x60, 0xB5, 0x05 }, -/* mode 4 */ { 0x00, 0x7C, 0x07, 0xD2, 0x83, 0x60, 0xB5, 0x03 }, -/* mode 5 */ { 0x00, 0x78, 0x07, 0xD2, 0x83, 0x60, 0xB5, 0x03 }, -/* mode 6 */ { 0x80, 0x59, 0x17, 0x42, 0xA3, 0x44, 0x75, 0x12 }, -/* mode 7 */ { 0x80, 0x9A, 0x17, 0xB1, 0x13, 0x60, 0xB5, 0x14 }, -/* mode 8 */ { 0x80, 0x3C, 0x27, 0xC1, 0x23, 0x44, 0x75, 0x21 } }; +/* mode 0 */ { FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03, 0x44, 0x75, 0x16 }, +/* mode 1 */ { FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03, 0x44, 0x75, 0x16 }, +/* mode 2 */ { FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03, 0x60, 0xB5, 0x05 }, +/* mode 3 */ { FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03, 0x60, 0xB5, 0x05 }, +/* mode 4 */ { FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83, 0x60, 0xB5, 0x03 }, +/* mode 5 */ { FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83, 0x60, 0xB5, 0x03 }, +/* mode 6 */ { FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3, 0x44, 0x75, 0x12 }, +/* mode 7 */ { FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13, 0x60, 0xB5, 0x14 }, +/* mode 8 */ { FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23, 0x44, 0x75, 0x21 } }; struct saa7110* decoder = device->data; const unsigned char* ptr = modes[chan]; @@ -137,67 +150,78 @@ saa7110_write(decoder,0x30,ptr[5]); /* ADCs gain control */ saa7110_write(decoder,0x31,ptr[6]); /* Mixer Control #3 */ saa7110_write(decoder,0x21,ptr[7]); /* Analog Control #2 */ + decoder->input = chan; return 0; } +static const unsigned char initseq[] = { + 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00, + 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90, + 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA, + 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F, + 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C, + 0x44, 0x71, 0x02, 0x8C, 0x02}; + static -int determine_norm(struct i2c_device* dev) +int determine_norm(struct i2c_device* device) { - struct saa7110* decoder = dev->data; + struct saa7110* decoder = device->data; int status; /* mode changed, start automatic detection */ + saa7110_write_block(decoder, initseq, sizeof(initseq)); + saa7110_selmux(device, decoder->input); + sleep_on_timeout(&decoder->wq, HZ/4); status = saa7110_read(decoder); + if (status & 0x40) { + DEBUG(printk(KERN_INFO "%s: status=0x%02x (no signal)\n",device->name, status)); + return decoder->norm; // no change + } if ((status & 3) == 0) { - saa7110_write(decoder,0x06,0x80); + saa7110_write(decoder,0x06,0x83); if (status & 0x20) { - DEBUG(printk(KERN_INFO "%s: norm=bw60\n",dev->name)); - saa7110_write(decoder,0x2E,0x81); + DEBUG(printk(KERN_INFO "%s: status=0x%02x (NTSC/no color)\n",device->name, status)); + //saa7110_write(decoder,0x2E,0x81); return VIDEO_MODE_NTSC; } - DEBUG(printk(KERN_INFO "%s: norm=bw50\n",dev->name)); - saa7110_write(decoder,0x2E,0x9A); + DEBUG(printk(KERN_INFO "%s: status=0x%02x (PAL/no color)\n",device->name, status)); + //saa7110_write(decoder,0x2E,0x9A); return VIDEO_MODE_PAL; } - saa7110_write(decoder,0x06,0x00); + //saa7110_write(decoder,0x06,0x03); if (status & 0x20) { /* 60Hz */ - DEBUG(printk(KERN_INFO "%s: norm=ntsc\n",dev->name)); - saa7110_write(decoder,0x0D,0x06); + DEBUG(printk(KERN_INFO "%s: status=0x%02x (NTSC)\n",device->name, status)); + saa7110_write(decoder,0x0D,0x86); + saa7110_write(decoder,0x0F,0x50); saa7110_write(decoder,0x11,0x2C); - saa7110_write(decoder,0x2E,0x81); + //saa7110_write(decoder,0x2E,0x81); return VIDEO_MODE_NTSC; } /* 50Hz -> PAL/SECAM */ - saa7110_write(decoder,0x0D,0x06); + saa7110_write(decoder,0x0D,0x86); + saa7110_write(decoder,0x0F,0x10); saa7110_write(decoder,0x11,0x59); - saa7110_write(decoder,0x2E,0x9A); + //saa7110_write(decoder,0x2E,0x9A); - mdelay(150); /* pause 150 ms */ + sleep_on_timeout(&decoder->wq, HZ/4); status = saa7110_read(decoder); if ((status & 0x03) == 0x01) { - DEBUG(printk(KERN_INFO "%s: norm=secam\n",dev->name)); - saa7110_write(decoder,0x0D,0x07); + DEBUG(printk(KERN_INFO "%s: status=0x%02x (SECAM)\n",device->name, status)); + saa7110_write(decoder,0x0D,0x87); return VIDEO_MODE_SECAM; } - DEBUG(printk(KERN_INFO "%s: norm=pal\n",dev->name)); + DEBUG(printk(KERN_INFO "%s: status=0x%02x (PAL)\n",device->name, status)); return VIDEO_MODE_PAL; } static int saa7110_attach(struct i2c_device *device) { -static const unsigned char initseq[] = { - 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF0, 0x00, 0x00, - 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x06, 0x18, 0x90, - 0x00, 0x2C, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA, - 0xF0, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xD9, 0x17, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F, - 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x81, 0x03, - 0x40, 0x75, 0x01, 0x8C, 0x03}; struct saa7110* decoder; int rv; @@ -219,17 +243,30 @@ decoder->contrast = 32768; decoder->hue = 32768; decoder->sat = 32768; + + init_waitqueue_head(&decoder->wq); rv = saa7110_write_block(decoder, initseq, sizeof(initseq)); if (rv < 0) printk(KERN_ERR "%s_attach: init status %d\n", device->name, rv); else { - saa7110_write(decoder,0x21,0x16); + int ver, status; + saa7110_write(decoder,0x21,0x10); + saa7110_write(decoder,0x0e,0x18); saa7110_write(decoder,0x0D,0x04); - DEBUG(printk(KERN_INFO "%s_attach: chip version %x\n", device->name, saa7110_read(decoder))); + ver = saa7110_read(decoder); saa7110_write(decoder,0x0D,0x06); + //mdelay(150); + status = saa7110_read(decoder); + printk(KERN_INFO "%s_attach: SAA7110A version %x at 0x%02x, status=0x%02x\n", device->name, ver, device->addr, status); + saa7110_write(decoder, 0x0D, 0x86); + saa7110_write(decoder, 0x0F, 0x10); + saa7110_write(decoder, 0x11, 0x59); + //saa7110_write(decoder, 0x2E, 0x9A); } + //saa7110_selmux(device,0); + //determine_norm(device); /* setup and implicit mode 0 select has been performed */ return 0; } @@ -263,8 +300,7 @@ dc->flags = VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC | VIDEO_DECODER_SECAM - | VIDEO_DECODER_AUTO - | VIDEO_DECODER_CCIR; + | VIDEO_DECODER_AUTO; dc->inputs = SAA7110_MAX_INPUT; dc->outputs = SAA7110_MAX_OUTPUT; } @@ -277,7 +313,8 @@ int res = 0; status = i2c_read(device->bus,device->addr|1); - if (status & 0x40) + DEBUG(printk(KERN_INFO "%s: status=0x%02x norm=%d\n",device->name, status, decoder->norm)); + if (!(status & 0x40)) res |= DECODER_STATUS_GOOD; if (status & 0x03) res |= DECODER_STATUS_COLOR; @@ -301,26 +338,33 @@ v = *(int*)arg; if (decoder->norm != v) { decoder->norm = v; - saa7110_write(decoder, 0x06, 0x00); + //saa7110_write(decoder, 0x06, 0x03); switch (v) { case VIDEO_MODE_NTSC: - saa7110_write(decoder, 0x0D, 0x06); + saa7110_write(decoder, 0x0D, 0x86); + saa7110_write(decoder, 0x0F, 0x50); saa7110_write(decoder, 0x11, 0x2C); - saa7110_write(decoder, 0x30, 0x81); - saa7110_write(decoder, 0x2A, 0xDF); + //saa7110_write(decoder, 0x2E, 0x81); + DEBUG(printk(KERN_INFO "%s: switched to NTSC\n",device->name)); break; case VIDEO_MODE_PAL: - saa7110_write(decoder, 0x0D, 0x06); + saa7110_write(decoder, 0x0D, 0x86); + saa7110_write(decoder, 0x0F, 0x10); saa7110_write(decoder, 0x11, 0x59); - saa7110_write(decoder, 0x2E, 0x9A); + //saa7110_write(decoder, 0x2E, 0x9A); + DEBUG(printk(KERN_INFO "%s: switched to PAL\n",device->name)); break; case VIDEO_MODE_SECAM: - saa7110_write(decoder, 0x0D, 0x07); + saa7110_write(decoder, 0x0D, 0x87); + saa7110_write(decoder, 0x0F, 0x10); saa7110_write(decoder, 0x11, 0x59); - saa7110_write(decoder, 0x2E, 0x9A); + //saa7110_write(decoder, 0x2E, 0x9A); + DEBUG(printk(KERN_INFO "%s: switched to SECAM\n",device->name)); break; case VIDEO_MODE_AUTO: - *(int*)arg = determine_norm(device); + DEBUG(printk(KERN_INFO "%s: TV standard detection...\n",device->name)); + decoder->norm = determine_norm(device); + *(int*)arg = decoder->norm; break; default: return -EPERM; @@ -330,11 +374,13 @@ case DECODER_SET_INPUT: v = *(int*)arg; - if (v<0 || v>SAA7110_MAX_INPUT) + if (v<0 || v>SAA7110_MAX_INPUT) { + DEBUG(printk(KERN_INFO "%s: input=%d not available\n",device->name, v)); return -EINVAL; + } if (decoder->input != v) { - decoder->input = v; saa7110_selmux(device, v); + DEBUG(printk(KERN_INFO "%s: switched to input=%d\n",device->name, v)); } break; @@ -349,7 +395,8 @@ v = *(int*)arg; if (decoder->enable != v) { decoder->enable = v; - saa7110_write(decoder,0x0E, v ? 0x18 : 0x00); + saa7110_write(decoder,0x0E, v ? 0x18 : 0x80); + DEBUG(printk(KERN_INFO "%s: YUV %s\n",device->name,v ? "on" : "off")); } break; @@ -381,7 +428,7 @@ break; case DECODER_DUMP: - for (v=0; v<34; v+=16) { + for (v=0; v<0x34; v+=16) { int j; DEBUG(printk(KERN_INFO "%s: %03x\n",device->name,v)); for (j=0; j<16; j++) { @@ -392,7 +439,7 @@ break; default: - DEBUG(printk(KERN_INFO "unknown saa7110_command?(%d)\n",cmd)); + DEBUG(printk(KERN_INFO "unknown saa7110_command??(%d)\n",cmd)); return -EINVAL; } return 0; @@ -400,12 +447,12 @@ /* ----------------------------------------------------------------------- */ -static struct i2c_driver i2c_driver_saa7110 = +struct i2c_driver i2c_driver_saa7110 = { "saa7110", /* name */ - I2C_DRIVERID_VIDEODECODER, /* in i2c.h */ - I2C_SAA7110, I2C_SAA7110+1, /* Addr range */ + I2C_DRIVERID_VIDEODECODER, /* in i2c.h */ + I2C_SAA7110, I2C_SAA7110+3, /* Addr range */ saa7110_attach, saa7110_detach, @@ -414,17 +461,17 @@ EXPORT_NO_SYMBOLS; -static int saa7110_init(void) +int saa7110_init(void) { return i2c_register_driver(&i2c_driver_saa7110); } -static void saa7110_exit(void) +void saa7110_exit(void) { i2c_unregister_driver(&i2c_driver_saa7110); } - module_init(saa7110_init); module_exit(saa7110_exit); MODULE_LICENSE("GPL"); +