diff options
author | Michael Hunold <devnull@localhost> | 2003-06-03 13:07:01 +0000 |
---|---|---|
committer | Michael Hunold <devnull@localhost> | 2003-06-03 13:07:01 +0000 |
commit | a9f1c290863f5aabdb9f36dfe7a1499ae05a9d45 (patch) | |
tree | 1aa18bc04b9c658202b16ad670bebac13c8b8689 /linux/drivers/media/dvb/ttpci/av7110.c | |
parent | dfa0bc1ee855af7954733faad2a7593b04aa9768 (diff) | |
download | mediapointer-dvb-s2-a9f1c290863f5aabdb9f36dfe7a1499ae05a9d45.tar.gz mediapointer-dvb-s2-a9f1c290863f5aabdb9f36dfe7a1499ae05a9d45.tar.bz2 |
Here comes the "dvb-c analog module hack"
- if the analog module is detected, the saa7113 is initialized and
some more v4l2 ioctls are available. you can use "xawtv" now to
switch between "dvb" and "analog" input. when you are one the
"analog" input, you can tune in analog channels with the cursor
keys via v4l2.
currently, this is a big hack -- tuning is not mutually exclusive,
so "szap" and v4l2 tuning can interfere with each other. the
demodulator address is hardcoded to 0x09.
Other changes:
- changed the saa7146 ioctl parameters, give out the data of the
current device open "fh" (=> "file handle"), not the pointer to the
device structure "dev". It is "dev = fh->dev".
Some stuff does not work:
- analog audio does not work. does the msp3400 need to be reprogrammed?
- one field is "missing", so the picture is very bad and
capturing does not work neither. this needs to be investigated by
looking at the programming the windows driver uses for the saa7146
Diffstat (limited to 'linux/drivers/media/dvb/ttpci/av7110.c')
-rw-r--r-- | linux/drivers/media/dvb/ttpci/av7110.c | 312 |
1 files changed, 291 insertions, 21 deletions
diff --git a/linux/drivers/media/dvb/ttpci/av7110.c b/linux/drivers/media/dvb/ttpci/av7110.c index 99714ad94..37f799543 100644 --- a/linux/drivers/media/dvb/ttpci/av7110.c +++ b/linux/drivers/media/dvb/ttpci/av7110.c @@ -1112,7 +1112,7 @@ static int OutCommand(struct av7110 *av7110, u16* buf, int length) u32 stat; #endif - DEB_EE(("av7110: %p\n",av7110)); +// DEB_EE(("av7110: %p\n",av7110)); if (!av7110->arm_ready) { DEB_D(("arm not ready.\n")); @@ -1190,7 +1190,7 @@ SOutCommand(struct av7110 *av7110, u16* buf, int length) { int ret; - DEB_EE(("av7110: %p\n",av7110)); +// DEB_EE(("av7110: %p\n",av7110)); if (!av7110->arm_ready) { DEB_D(("arm not ready.\n")); @@ -1214,7 +1214,7 @@ static int outcom(struct av7110 *av7110, int type, int com, int num, ...) u16 buf[num+2]; int i, ret; - DEB_EE(("av7110: %p\n",av7110)); +// DEB_EE(("av7110: %p\n",av7110)); buf[0]=(( type << 8 ) | com); buf[1]=num; @@ -1359,7 +1359,7 @@ msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val) inline static int SendDAC(struct av7110 *av7110, u8 addr, u8 data) { - DEB_EE(("av7110: %p\n",av7110)); +// DEB_EE(("av7110: %p\n",av7110)); return outcom(av7110, COMTYPE_AUDIODAC, AudioDAC, 2, addr, data); } @@ -2132,14 +2132,14 @@ SetMode(struct av7110 *av7110, int mode) inline static void TestMode(struct av7110 *av7110, int mode) { - DEB_EE(("av7110: %p\n",av7110)); +// DEB_EE(("av7110: %p\n",av7110)); outcom(av7110, COMTYPE_ENCODER, SetTestMode, 1, mode); } inline static void VidMode(struct av7110 *av7110, int mode) { - DEB_EE(("av7110: %p\n",av7110)); +// DEB_EE(("av7110: %p\n",av7110)); outcom(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode); } @@ -2147,7 +2147,7 @@ VidMode(struct av7110 *av7110, int mode) static int inline vidcom(struct av7110 *av7110, u32 com, u32 arg) { - DEB_EE(("av7110: %p\n",av7110)); +// DEB_EE(("av7110: %p\n",av7110)); return outcom(av7110, 0x80, 0x02, 4, (com>>16), (com&0xffff), (arg>>16), (arg&0xffff)); @@ -2156,7 +2156,7 @@ vidcom(struct av7110 *av7110, u32 com, u32 arg) static int inline audcom(struct av7110 *av7110, u32 com) { - DEB_EE(("av7110: %p\n",av7110)); +// DEB_EE(("av7110: %p\n",av7110)); return outcom(av7110, 0x80, 0x03, 4, (com>>16), (com&0xffff)); } @@ -2641,38 +2641,275 @@ void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, * V4L SECTION ****************************************************************************/ -int av7110_ioctl(struct saa7146_dev *dev, unsigned int cmd, void *arg) +static struct v4l2_input inputs[2] = { + { 0, "DVB", V4L2_INPUT_TYPE_CAMERA, 1, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 }, + { 1, "ANALOG", V4L2_INPUT_TYPE_TUNER, 2, 1, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 }, +}; + +/* taken from ves1820.c */ +static int ves1820_writereg(struct saa7146_dev *dev, u8 reg, u8 data) { - DEB_EE(("saa7146_dev: %p\n",dev)); + u8 addr = 0x09; + u8 buf[] = { 0x00, reg, data }; + struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; + + DEB_EE(("av7710: dev: %p\n",dev)); + + if( 1 != saa7146_i2c_transfer(dev, &msg, 1, 1)) { + return -1; + } + return 0; +} + +static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) +{ + struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; + + DEB_EE(("av7710: dev: %p\n",dev)); + + if( 1 != saa7146_i2c_transfer(dev, &msg, 1, 1)) { + return -1; + } + return 0; +} + + +/** + * set up the downconverter frequency divisor for a + * reference clock comparision frequency of 62.5 kHz. + */ +static int tuner_set_tv_freq (struct saa7146_dev *dev, u32 freq) +{ + u32 div; + u8 config; + u8 buf [4]; + + DEB_EE(("av7710: freq: 0x%08x\n",freq)); + + /* magic number: 56. tuning with the frequency given by v4l2 + is always off by 56*62.5 kHz...*/ + div = freq + 56; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x8e; + + if (freq < 16*168.25 ) + config = 0xa0; + else if (freq < 16*447.25) + config = 0x90; + else + config = 0x30; + config &= ~0x02; + + buf[3] = config; + + return tuner_write (dev, 0x61, buf); +} + +static struct saa7146_standard analog_standard[]; +static struct saa7146_standard dvb_standard[]; +static struct saa7146_standard standard[]; + +int av7110_dvb_c_switch(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct av7110 *av7110 = (struct av7110*)dev->ext_priv; + u16 buf[3] = { ((COMTYPE_AUDIODAC << 8) + ADSwitch), 1, 1 }; + + u8 band = 0; + int source, sync; + + DEB_EE(("av7110: %p\n",av7110)); + + struct saa7146_fh *ov_fh = NULL; + int restart_overlay = 0; + + if( vv->ov_data != NULL ) { + ov_fh = vv->ov_data->fh; + saa7146_stop_preview(ov_fh); + restart_overlay = 1; + } + + if( 0 != av7110->current_input ) { + buf[2] = 0; + band = 0x68; /* analog band */ + source = SAA7146_HPS_SOURCE_PORT_B; + sync = SAA7146_HPS_SYNC_PORT_B; + memcpy(standard,analog_standard,sizeof(struct saa7146_standard)*2); + } else { + buf[2] = 1; + band = 0x28; /* digital band */ + source = SAA7146_HPS_SOURCE_PORT_A; + sync = SAA7146_HPS_SYNC_PORT_A; + memcpy(standard,dvb_standard,sizeof(struct saa7146_standard)*2); + } + + /* hmm, this does not do anything!? */ + if (OutCommand(av7110, buf, 3)) { + printk("ADSwitch error\n"); + } + + if( 0 != ves1820_writereg(dev, 0x0f, band )) { + printk("setting band in demodulator failed.\n"); + } + saa7146_set_hps_source_and_sync(dev, source, sync); + + /* restart overlay if it was active before */ + if( 0 != restart_overlay ) { + saa7146_start_preview(ov_fh); + } + + return 0; +} + +int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) +{ + struct saa7146_dev *dev = fh->dev; + struct av7110 *av7110 = (struct av7110*)dev->ext_priv; + DEB_EE(("saa7146_dev: %p\n",dev)); switch(cmd) { - case VIDIOC_ENUMINPUT: + case VIDIOC_G_TUNER: { - struct v4l2_input *i = arg; + struct v4l2_tuner *t = arg; + + DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index)); + + if( 0 == av7110->has_analog_tuner || av7110->current_input != 1 ) { + return -EINVAL; + } + + memset(t,0,sizeof(*t)); + strcpy(t->name, "Television"); + + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */ + t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */ + /* FIXME: add the real signal strength here */ + t->signal = 0xffff; + t->afc = 0; + /* fixme: real autodetection here */ + t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + + return 0; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *t = arg; - if( i->index != 0 ) { + DEB_EE(("VIDIOC_S_TUNER: %d\n", t->index)); + + if( 0 == av7110->has_analog_tuner || av7110->current_input != 1 ) { + return -EINVAL; + } + + + switch(t->audmode) { + case V4L2_TUNER_MODE_STEREO: { + DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n")); + break; + } + case V4L2_TUNER_MODE_LANG1: { + DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n")); + break; + } + case V4L2_TUNER_MODE_LANG2: { + DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n")); + break; + } + default: { /* case V4L2_TUNER_MODE_MONO: {*/ + DEB_D(("VIDIOC_S_TUNER: TDA9840_SET_MONO\n")); + break; + } + } + + return 0; + } + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", f->frequency)); + + if( 0 == av7110->has_analog_tuner || av7110->current_input != 1 ) { + return -EINVAL; + } + + memset(f,0,sizeof(*f)); + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = av7110->current_freq; + + return 0; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n",f->frequency)); + + if( 0 == av7110->has_analog_tuner || av7110->current_input != 1 ) { return -EINVAL; } - memset(i,0,sizeof(*i)); - i->index = 0; - strcpy(i->name, "DVB"); - i->type = V4L2_INPUT_TYPE_CAMERA; - i->audioset = 1; + if (V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + + /* tune in desired frequency */ + tuner_set_tv_freq(dev, f->frequency); + av7110->current_freq = f->frequency; + + return 0; + } + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *i = arg; + + DEB_EE(("VIDIOC_ENUMINPUT: %d\n", i->index)); + + if( 0 != av7110->has_analog_tuner ) { + if( i->index < 0 || i->index >= 2) { + return -EINVAL; + } + } else { + if( i->index != 0 ) { + return -EINVAL; + } + } + + memcpy(i, &inputs[i->index], sizeof(struct v4l2_input)); return 0; } case VIDIOC_G_INPUT: { int *input = (int *)arg; - *input = 0; + *input = av7110->current_input; + DEB_EE(("VIDIOC_G_INPUT: %d\n", *input)); return 0; } case VIDIOC_S_INPUT: { - return 0; + int input = *(int *)arg; + + DEB_EE(("VIDIOC_S_INPUT: %d\n", input)); + + if( 0 == av7110->has_analog_tuner ) { + return 0; + } + + if( input < 0 || input >= 2) { + return -EINVAL; + } + + /* fixme: switch inputs here */ + av7110->current_input = input; + return av7110_dvb_c_switch(fh); } default: + printk("no such ioctl\n"); return -ENOIOCTLCMD; } return 0; @@ -4148,6 +4385,10 @@ struct saa7146_extension_ioctls ioctls[] = { { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE }, { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE }, { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE }, + { VIDIOC_G_FREQUENCY, SAA7146_EXCLUSIVE }, + { VIDIOC_S_FREQUENCY, SAA7146_EXCLUSIVE }, + { VIDIOC_G_TUNER, SAA7146_EXCLUSIVE }, + { VIDIOC_S_TUNER, SAA7146_EXCLUSIVE }, { 0, 0 } }; @@ -4287,6 +4528,8 @@ int av7110_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *p VidMode(av7110, vidmode); /* remaining inits according to card and frontend type */ + av7110->has_analog_tuner = 0; + av7110->current_input = 0; if (i2c_writereg(av7110, 0x20, 0x00, 0x00)==1) { printk ("av7110(%d): Crystal audio DAC detected\n", av7110->dvb_adapter->num); @@ -4313,6 +4556,23 @@ int av7110_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *p msp_writereg(av7110, 0x12, 0x000a, 0x0220); // SCART 1 source msp_writereg(av7110, 0x12, 0x0007, 0x7f00); // SCART 1 volume msp_writereg(av7110, 0x12, 0x000d, 0x4800); // prescale SCART + + if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) { + INFO(("saa7113 not accessible.\n")); + } else { + av7110->has_analog_tuner = 1; + /* init the saa7113 */ + i2c_writereg(av7110, 0x48, 0x02, 0xd0); i2c_writereg(av7110, 0x48, 0x03, 0x23); i2c_writereg(av7110, 0x48, 0x04, 0x00); + i2c_writereg(av7110, 0x48, 0x05, 0x00); i2c_writereg(av7110, 0x48, 0x06, 0xe9); i2c_writereg(av7110, 0x48, 0x07, 0x0d); + i2c_writereg(av7110, 0x48, 0x08, 0x98); i2c_writereg(av7110, 0x48, 0x09, 0x02); i2c_writereg(av7110, 0x48, 0x0a, 0x80); + i2c_writereg(av7110, 0x48, 0x0b, 0x40); i2c_writereg(av7110, 0x48, 0x0c, 0x40); i2c_writereg(av7110, 0x48, 0x0d, 0x00); + i2c_writereg(av7110, 0x48, 0x0e, 0x01); i2c_writereg(av7110, 0x48, 0x0f, 0x7c); i2c_writereg(av7110, 0x48, 0x10, 0x48); + i2c_writereg(av7110, 0x48, 0x11, 0x0c); i2c_writereg(av7110, 0x48, 0x12, 0x8b); i2c_writereg(av7110, 0x48, 0x13, 0x10); + i2c_writereg(av7110, 0x48, 0x14, 0x00); i2c_writereg(av7110, 0x48, 0x15, 0x00); i2c_writereg(av7110, 0x48, 0x16, 0x00); + i2c_writereg(av7110, 0x48, 0x17, 0x00); i2c_writereg(av7110, 0x48, 0x18, 0x00); i2c_writereg(av7110, 0x48, 0x19, 0x00); + i2c_writereg(av7110, 0x48, 0x1a, 0x00); i2c_writereg(av7110, 0x48, 0x1b, 0x00); i2c_writereg(av7110, 0x48, 0x1c, 0x00); + i2c_writereg(av7110, 0x48, 0x1d, 0x00); i2c_writereg(av7110, 0x48, 0x1e, 0x00); + } } else if (dev->pci->subsystem_vendor == 0x110a) { printk("av7110(%d): DVB-C w/o analog module detected\n", av7110->dvb_adapter->num); @@ -4421,6 +4681,16 @@ struct saa7146_standard standard[] = { { "NTSC", V4L2_STD_NTSC, 0x10, 244, 480, 0x40, 708, 709, 480, 640 }, }; +static struct saa7146_standard analog_standard[] = { + { "PAL", V4L2_STD_PAL, 0x17, 288, 576, 0x4b, 708, 709, 576, 768 }, + { "NTSC", V4L2_STD_NTSC, 0x10, 244, 480, 0x40, 708, 709, 480, 640 }, +}; + +static struct saa7146_standard dvb_standard[] = { + { "PAL", V4L2_STD_PAL, 0x15, 288, 576, 0x4a, 708, 709, 576, 768 }, + { "NTSC", V4L2_STD_NTSC, 0x10, 244, 480, 0x40, 708, 709, 480, 640 }, +}; + static struct saa7146_extension av7110_extension; @@ -4484,7 +4754,7 @@ static struct saa7146_ext_vv av7110_vv_data = { .inputs = 1, .audios = 1, - .capabilities = 0, + .capabilities = V4L2_CAP_TUNER, .flags = SAA7146_EXT_SWAP_ODD_EVEN, .stds = &standard[0], |