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
[EMAIL PROTECTED]
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 <[EMAIL PROTECTED]>
+
+ Copyright (C) 1999 Wolfgang Scherr <[EMAIL PROTECTED]>
+ Copyright (C) 2000 Serguei Miridonov <[EMAIL PROTECTED]>
+ - 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");
+