|
@@ -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);
|