diff options
author | Mauro Carvalho Chehab <mchehab@brturbo.com.br> | 2005-06-23 22:05:07 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-24 00:06:39 -0700 |
commit | 56fc08ca375491b965cb76fad65bfb98973e80d8 (patch) | |
tree | d19bb1d6e1d139dda8989188fae49cf124f3aaac /drivers/media/video/tuner-core.c | |
parent | b45009b0288a96a3458f4f8e93cb776678d41875 (diff) | |
download | talos-obmc-linux-56fc08ca375491b965cb76fad65bfb98973e80d8.tar.gz talos-obmc-linux-56fc08ca375491b965cb76fad65bfb98973e80d8.zip |
[PATCH] v4l: update for tuner cards and some V4L chips
Tuner improvements and additions. TEA5767 FM tuner added. Several small
fixes.
Signed-off-by: Mauro Carvalho Chehab <mchehab@brturbo.com.br>
Signed-off-by: Nickolay V Shmyrev <nshmyrev@yandex.ru>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/media/video/tuner-core.c')
-rw-r--r-- | drivers/media/video/tuner-core.c | 212 |
1 files changed, 145 insertions, 67 deletions
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 71423ae3b4dd..ba13bfadb523 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -1,5 +1,5 @@ /* - * $Id: tuner-core.c,v 1.7 2005/05/30 02:02:47 mchehab Exp $ + * $Id: tuner-core.c,v 1.15 2005/06/12 01:36:14 mchehab Exp $ * * i2c tv tuner chip device driver * core core, i.e. kernel interfaces, registering and so on @@ -26,15 +26,17 @@ /* * comment line bellow to return to old behavor, where only one I2C device is supported */ -/* #define CONFIG_TUNER_MULTI_I2C */ +#define CONFIG_TUNER_MULTI_I2C /**/ #define UNSET (-1U) /* standard i2c insmod options */ static unsigned short normal_i2c[] = { 0x4b, /* tda8290 */ - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + I2C_CLIENT_END +}; +static unsigned short normal_i2c_range[] = { + 0x60, 0x6f, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -59,7 +61,7 @@ MODULE_LICENSE("GPL"); static int this_adap; #ifdef CONFIG_TUNER_MULTI_I2C -static unsigned short tv_tuner, radio_tuner; +static unsigned short first_tuner, tv_tuner, radio_tuner; #endif static struct i2c_driver driver; @@ -67,7 +69,7 @@ static struct i2c_client client_template; /* ---------------------------------------------------------------------- */ -// Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz +/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */ static void set_tv_freq(struct i2c_client *c, unsigned int freq) { struct tuner *t = i2c_get_clientdata(c); @@ -81,14 +83,26 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) return; } if (freq < tv_range[0]*16 || freq > tv_range[1]*16) { - /* FIXME: better do that chip-specific, but - right now we don't have that in the config - struct and this way is still better than no - check at all */ - tuner_info("TV freq (%d.%02d) out of range (%d-%d)\n", - freq/16,freq%16*100/16,tv_range[0],tv_range[1]); - return; + + if (freq >= tv_range[0]*16364 && freq <= tv_range[1]*16384) { + /* V4L2_TUNER_CAP_LOW frequency */ + + tuner_dbg("V4L2_TUNER_CAP_LOW freq selected for TV. Tuners yet doesn't support converting it to valid freq.\n"); + + t->tv_freq(c,freq>>10); + + return; + } else { + /* FIXME: better do that chip-specific, but + right now we don't have that in the config + struct and this way is still better than no + check at all */ + tuner_info("TV freq (%d.%02d) out of range (%d-%d)\n", + freq/16,freq%16*100/16,tv_range[0],tv_range[1]); + return; + } } + tuner_dbg("62.5 Khz freq step selected for TV.\n"); t->tv_freq(c,freq); } @@ -105,11 +119,29 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) return; } if (freq < radio_range[0]*16 || freq > radio_range[1]*16) { - tuner_info("radio freq (%d.%02d) out of range (%d-%d)\n", + if (freq >= tv_range[0]*16364 && freq <= tv_range[1]*16384) { + /* V4L2_TUNER_CAP_LOW frequency */ + if (t->type == TUNER_TEA5767) { + tuner_info("radio freq step 62.5Hz (%d.%06d)\n",(freq>>14),freq%(1<<14)*10000); + t->radio_freq(c,freq>>10); + return; + } + + tuner_dbg("V4L2_TUNER_CAP_LOW freq selected for Radio. Tuners yet doesn't support converting it to valid freq.\n"); + + tuner_info("radio freq (%d.%06d)\n",(freq>>14),freq%(1<<14)*10000); + + t->radio_freq(c,freq>>10); + return; + + } else { + tuner_info("radio freq (%d.%02d) out of range (%d-%d)\n", freq/16,freq%16*100/16, - radio_range[0],radio_range[1]); - return; + radio_range[0],radio_range[1]); + return; + } } + tuner_dbg("62.5 Khz freq step selected for Radio.\n"); t->radio_freq(c,freq); } @@ -133,34 +165,13 @@ static void set_freq(struct i2c_client *c, unsigned long freq) t->freq = freq; } -#ifdef CONFIG_TUNER_MULTI_I2C -static void set_addr(struct i2c_client *c, struct tuner_addr *tun_addr) -{ - struct tuner *t = i2c_get_clientdata(c); - - switch (tun_addr->type) { - case V4L2_TUNER_RADIO: - radio_tuner=tun_addr->addr; - tuner_dbg("radio tuner set to I2C address 0x%02x\n",radio_tuner<<1); - - break; - default: - tv_tuner=tun_addr->addr; - tuner_dbg("TV tuner set to I2C address 0x%02x\n",tv_tuner<<1); - break; - } -} -#else -#define set_addr(c,tun_addr) \ - tuner_warn("It is recommended to enable CONFIG_TUNER_MULTI_I2C for this card.\n"); -#endif - static void set_type(struct i2c_client *c, unsigned int type) { struct tuner *t = i2c_get_clientdata(c); + tuner_dbg ("I2C addr 0x%02x with type %d\n",c->addr<<1,type); /* sanity check */ - if (type == UNSET || type == TUNER_ABSENT) + if (type == UNSET || type == TUNER_ABSENT) return; if (type >= tuner_count) return; @@ -175,6 +186,7 @@ static void set_type(struct i2c_client *c, unsigned int type) return; t->initialized = 1; + t->type = type; switch (t->type) { case TUNER_MT2032: @@ -189,6 +201,53 @@ static void set_type(struct i2c_client *c, unsigned int type) } } +#ifdef CONFIG_TUNER_MULTI_I2C +#define CHECK_ADDR(tp,cmd,tun) if (client->addr!=tp) { \ + return 0; } else \ + tuner_info ("Cmd %s accepted to "tun"\n",cmd); +#define CHECK_MODE(cmd) if (t->mode == V4L2_TUNER_RADIO) { \ + CHECK_ADDR(radio_tuner,cmd,"radio") } else \ + { CHECK_ADDR(tv_tuner,cmd,"TV"); } +#else +#define CHECK_ADDR(tp,cmd,tun) tuner_info ("Cmd %s accepted to "tun"\n",cmd); +#define CHECK_MODE(cmd) tuner_info ("Cmd %s accepted\n",cmd); +#endif + +#ifdef CONFIG_TUNER_MULTI_I2C + +static void set_addr(struct i2c_client *c, struct tuner_addr *tun_addr) +{ + /* ADDR_UNSET defaults to first available tuner */ + if ( tun_addr->addr == ADDR_UNSET ) { + if (first_tuner != c->addr) + return; + switch (tun_addr->v4l2_tuner) { + case V4L2_TUNER_RADIO: + radio_tuner=c->addr; + break; + default: + tv_tuner=c->addr; + break; + } + } else { + /* Sets tuner to its configured value */ + switch (tun_addr->v4l2_tuner) { + case V4L2_TUNER_RADIO: + radio_tuner=tun_addr->addr; + if ( tun_addr->addr == c->addr ) set_type(c,tun_addr->type); + return; + default: + tv_tuner=tun_addr->addr; + if ( tun_addr->addr == c->addr ) set_type(c,tun_addr->type); + return; + } + } + set_type(c,tun_addr->type); +} +#else +#define set_addr(c,tun_addr) set_type(c,(tun_addr)->type) +#endif + static char pal[] = "-"; module_param_string(pal, pal, sizeof(pal), 0644); @@ -233,6 +292,7 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) #else /* by default, first I2C card is both tv and radio tuner */ if (this_adap == 0) { + first_tuner = addr; tv_tuner = addr; radio_tuner = addr; } @@ -249,11 +309,12 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) memcpy(&t->i2c,&client_template,sizeof(struct i2c_client)); i2c_set_clientdata(&t->i2c, t); t->type = UNSET; - t->radio_if2 = 10700*1000; // 10.7MHz - FM radio + t->radio_if2 = 10700*1000; /* 10.7MHz - FM radio */ i2c_attach_client(&t->i2c); tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name); + set_type(&t->i2c, t->type); return 0; } @@ -261,12 +322,14 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) static int tuner_probe(struct i2c_adapter *adap) { if (0 != addr) { - normal_i2c[0] = addr; - normal_i2c[1] = I2C_CLIENT_END; + normal_i2c[0] = addr; + normal_i2c_range[0] = addr; + normal_i2c_range[1] = addr; } this_adap = 0; #ifdef CONFIG_TUNER_MULTI_I2C + first_tuner = 0; tv_tuner = 0; radio_tuner = 0; #endif @@ -298,17 +361,6 @@ static int tuner_detach(struct i2c_client *client) tuner_info("ignore v4l1 call\n"); \ return 0; } -#ifdef CONFIG_TUNER_MULTI_I2C -#define CHECK_ADDR(tp,cmd) if (client->addr!=tp) { \ - tuner_info ("Cmd %s to addr 0x%02x rejected.\n",cmd,client->addr<<1); \ - return 0; } -#define CHECK_MODE(cmd) if (t->mode == V4L2_TUNER_RADIO) { \ - CHECK_ADDR(radio_tuner,cmd) } else { CHECK_ADDR(tv_tuner,cmd); } -#else -#define CHECK_ADDR(tp,cmd) -#define CHECK_MODE(cmd) -#endif - static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { @@ -320,19 +372,19 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) case TUNER_SET_TYPE: set_type(client,*iarg); break; - case TUNER_SET_ADDR: + case TUNER_SET_TYPE_ADDR: set_addr(client,(struct tuner_addr *)arg); break; case AUDC_SET_RADIO: - CHECK_ADDR(radio_tuner,"AUDC_SET_RADIO"); + t->mode = V4L2_TUNER_RADIO; + CHECK_ADDR(tv_tuner,"AUDC_SET_RADIO","TV"); if (V4L2_TUNER_RADIO != t->mode) { set_tv_freq(client,400 * 16); - t->mode = V4L2_TUNER_RADIO; } break; case AUDC_CONFIG_PINNACLE: - CHECK_ADDR(tv_tuner,"AUDC_CONFIG_PINNACLE"); + CHECK_ADDR(tv_tuner,"AUDC_CONFIG_PINNACLE","TV"); switch (*iarg) { case 2: tuner_dbg("pinnacle pal\n"); @@ -360,9 +412,10 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) }; struct video_channel *vc = arg; - CHECK_ADDR(tv_tuner,"VIDIOCSCHAN"); CHECK_V4L2; t->mode = V4L2_TUNER_ANALOG_TV; + CHECK_ADDR(tv_tuner,"VIDIOCSCHAN","TV"); + if (vc->norm < ARRAY_SIZE(map)) t->std = map[vc->norm]; tuner_fixup_std(t); @@ -383,17 +436,27 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct video_tuner *vt = arg; - CHECK_ADDR(radio_tuner,"VIDIOCGTUNER:"); + CHECK_ADDR(radio_tuner,"VIDIOCGTUNER","radio"); CHECK_V4L2; - if (V4L2_TUNER_RADIO == t->mode && t->has_signal) - vt->signal = t->has_signal(client); + if (V4L2_TUNER_RADIO == t->mode) { + if (t->has_signal) + vt->signal = t->has_signal(client); + if (t->is_stereo) { + if (t->is_stereo(client)) + vt-> flags |= VIDEO_TUNER_STEREO_ON; + else + vt-> flags &= 0xffff ^ VIDEO_TUNER_STEREO_ON; + } + vt->flags |= V4L2_TUNER_CAP_LOW; /* Allow freqs at 62.5 Hz */ + } + return 0; } case VIDIOCGAUDIO: { struct video_audio *va = arg; - CHECK_ADDR(radio_tuner,"VIDIOCGAUDIO"); + CHECK_ADDR(radio_tuner,"VIDIOCGAUDIO","radio"); CHECK_V4L2; if (V4L2_TUNER_RADIO == t->mode && t->is_stereo) va->mode = t->is_stereo(client) @@ -406,9 +469,10 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { v4l2_std_id *id = arg; - CHECK_ADDR(tv_tuner,"VIDIOC_S_STD"); SWITCH_V4L2; t->mode = V4L2_TUNER_ANALOG_TV; + CHECK_ADDR(tv_tuner,"VIDIOC_S_STD","TV"); + t->std = *id; tuner_fixup_std(t); if (t->freq) @@ -444,13 +508,27 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) CHECK_MODE("VIDIOC_G_TUNER"); SWITCH_V4L2; - if (V4L2_TUNER_RADIO == t->mode && t->has_signal) - tuner->signal = t->has_signal(client); + if (V4L2_TUNER_RADIO == t->mode) { + if (t->has_signal) + tuner -> signal = t->has_signal(client); + if (t->is_stereo) { + if (t->is_stereo(client)) { + tuner -> capability |= V4L2_TUNER_CAP_STEREO; + tuner -> rxsubchans |= V4L2_TUNER_SUB_STEREO; + } else { + tuner -> rxsubchans &= 0xffff ^ V4L2_TUNER_SUB_STEREO; + } + } + } + /* Wow to deal with V4L2_TUNER_CAP_LOW ? For now, it accepts from low at 62.5KHz step to high at 62.5 Hz */ tuner->rangelow = tv_range[0] * 16; - tuner->rangehigh = tv_range[1] * 16; +// tuner->rangehigh = tv_range[1] * 16; +// tuner->rangelow = tv_range[0] * 16384; + tuner->rangehigh = tv_range[1] * 16384; break; } default: + tuner_dbg ("Unimplemented IOCTL 0x%08x called to tuner.\n", cmd); /* nothing */ break; } @@ -458,7 +536,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; } -static int tuner_suspend(struct device * dev, pm_message_t state, u32 level) +static int tuner_suspend(struct device * dev, u32 state, u32 level) { struct i2c_client *c = container_of(dev, struct i2c_client, dev); struct tuner *t = i2c_get_clientdata(c); |