Re: [alsa-devel] tea575x-tuner improvements & use in maxiradio
On Wednesday 08 February 2012 08:29:25 Hans Verkuil wrote:
On Tuesday, February 07, 2012 23:20:19 Ondrej Zary wrote:
On Sunday 05 February 2012 14:17:05 Hans Verkuil wrote:
These patches improve the tea575x-tuner module to make it up to date with the latest V4L2 frameworks.
The maxiradio driver has also been converted to use the tea575x-tuner and I've used that card to test it.
Unfortunately, this card can't read the data pin, so the new hardware seek functionality has been tested only partially (yes, it seeks, but when it finds a channel I can't read back the frequency).
Ondrej, are you able to test these patches for the sound cards that use this tea575x tuner?
Note that these two patches rely on other work that I did and that hasn't been merged yet. So it is best to pull from my git tree:
http://git.linuxtv.org/hverkuil/media_tree.git/shortlog/refs/heads/radi o-pc i2
You can use the v4l-utils repository (http://git.linuxtv.org/v4l-utils.git) to test the drivers: the v4l2-compliance test should succeed and with v4l2-ctl you can test the hardware seek:
To seek down:
v4l2-ctl -d /dev/radio0 --freq-seek=dir=0
To seek up:
v4l2-ctl -d /dev/radio0 --freq-seek=dir=1
To do the compliance test:
v4l2-compliance -r /dev/radio0
It seems to work (tested with SF64-PCR - snd_fm801) but the seek is severely broken. Reading the frequency immediately after seek does not work, it always returns the old value (haven't found a delay that works). Reading it later (copied back snd_tea575x_get_freq function) works. The chip seeks randomly up or down, ignoring UP/DOWN flag and often stops at wrong place (only noise) or even outside the FM range.
So I strongly suggest not to enable this (mis-)feature. The HW seems to be completely broken (unless there's some weird bug in the code).
Well, it seemed like a good idea at the time :-) I'll remove this 'feature', it's really not worth our time to try and make this work for these old cards.
I fixed it somehow. Now it works most of the time. The important things: - a delay must be present after search start and before first register read or the seek does weird things - when the search stops, the new frequency is not available immediately, we must wait until it appears in the register (fortunately, we can clear the frequency bits when starting the search as it starts at the frequency currently set, not from the value written) - sometimes, seek remains on the current frequency (or moves only a little), so repeat it until it moves by at least 50 kHz
--- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -89,7 +89,7 @@ static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val) tea->ops->set_pins(tea, 0); }
-static unsigned int snd_tea575x_read(struct snd_tea575x *tea) +static u32 snd_tea575x_read(struct snd_tea575x *tea) { u16 l, rdata; u32 data = 0; @@ -120,6 +120,27 @@ static unsigned int snd_tea575x_read(struct snd_tea575x *tea) return data; }
+static void snd_tea575x_get_freq(struct snd_tea575x *tea) +{ + u32 freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK; + + if (freq == 0) { + tea->freq = 0; + return; + } + + /* freq *= 12.5 */ + freq *= 125; + freq /= 10; + /* crystal fixup */ + if (tea->tea5759) + freq += TEA575X_FMIF; + else + freq -= TEA575X_FMIF; + + tea->freq = clamp(freq * 16, FREQ_LO, FREQ_HI); /* from kHz */ +} + static void snd_tea575x_set_freq(struct snd_tea575x *tea) { u32 freq = tea->freq; @@ -203,6 +224,8 @@ static int vidioc_g_frequency(struct file *file, void *priv, if (f->tuner != 0) return -EINVAL; f->type = V4L2_TUNER_RADIO; + if (!tea->cannot_read_data) + snd_tea575x_get_freq(tea); f->frequency = tea->freq; return 0; } @@ -225,36 +248,50 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *fh, struct v4l2_hw_freq_seek *a) { struct snd_tea575x *tea = video_drvdata(file); + int i, old_freq; + unsigned long timeout;
if (tea->cannot_read_data) return -ENOTTY; + + snd_tea575x_get_freq(tea); + old_freq = tea->freq; + /* clear the frequency, HW will fill it in */ + tea->val &= ~TEA575X_BIT_FREQ_MASK; tea->val |= TEA575X_BIT_SEARCH; - tea->val &= ~TEA575X_BIT_UPDOWN; if (a->seek_upward) tea->val |= TEA575X_BIT_UPDOWN; + else + tea->val &= ~TEA575X_BIT_UPDOWN; snd_tea575x_write(tea, tea->val); + timeout = jiffies + msecs_to_jiffies(10000); for (;;) { - unsigned val = snd_tea575x_read(tea); - - if (!(val & TEA575X_BIT_SEARCH)) { - /* Found a frequency */ - val &= TEA575X_BIT_FREQ_MASK; - val = (val * 10) / 125; - if (tea->tea5759) - val += TEA575X_FMIF; - else - val -= TEA575X_FMIF; - tea->freq = clamp(val * 16, FREQ_LO, FREQ_HI); - return 0; - } + if (time_after(jiffies, timeout)) + break; if (schedule_timeout_interruptible(msecs_to_jiffies(10))) { /* some signal arrived, stop search */ tea->val &= ~TEA575X_BIT_SEARCH; snd_tea575x_write(tea, tea->val); return -ERESTARTSYS; } + if (!(snd_tea575x_read(tea) & TEA575X_BIT_SEARCH)) { + /* Found a frequency, wait until it can be read */ + for (i = 0; i < 100; i++) { + msleep(10); + snd_tea575x_get_freq(tea); + if (tea->freq != 0) /* available */ + break; + } + /* if we moved by less than 50 kHz, continue seeking */ + if (abs(old_freq - tea->freq) < 16 * 500) { + snd_tea575x_write(tea, tea->val); + continue; + } + tea->val &= ~TEA575X_BIT_SEARCH; + return 0; + } } - return 0; + return -ETIMEDOUT; }
static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl) @@ -318,7 +355,7 @@ int snd_tea575x_init(struct snd_tea575x *tea) return -ENODEV; }
- tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40; + tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_5_28; tea->freq = 90500 * 16; /* 90.5Mhz default */ snd_tea575x_set_freq(tea);
On Friday, February 17, 2012 21:13:40 Ondrej Zary wrote:
On Wednesday 08 February 2012 08:29:25 Hans Verkuil wrote:
On Tuesday, February 07, 2012 23:20:19 Ondrej Zary wrote:
On Sunday 05 February 2012 14:17:05 Hans Verkuil wrote:
These patches improve the tea575x-tuner module to make it up to date with the latest V4L2 frameworks.
The maxiradio driver has also been converted to use the tea575x-tuner and I've used that card to test it.
Unfortunately, this card can't read the data pin, so the new hardware seek functionality has been tested only partially (yes, it seeks, but when it finds a channel I can't read back the frequency).
Ondrej, are you able to test these patches for the sound cards that use this tea575x tuner?
Note that these two patches rely on other work that I did and that hasn't been merged yet. So it is best to pull from my git tree:
http://git.linuxtv.org/hverkuil/media_tree.git/shortlog/refs/heads/radi o-pc i2
You can use the v4l-utils repository (http://git.linuxtv.org/v4l-utils.git) to test the drivers: the v4l2-compliance test should succeed and with v4l2-ctl you can test the hardware seek:
To seek down:
v4l2-ctl -d /dev/radio0 --freq-seek=dir=0
To seek up:
v4l2-ctl -d /dev/radio0 --freq-seek=dir=1
To do the compliance test:
v4l2-compliance -r /dev/radio0
It seems to work (tested with SF64-PCR - snd_fm801) but the seek is severely broken. Reading the frequency immediately after seek does not work, it always returns the old value (haven't found a delay that works). Reading it later (copied back snd_tea575x_get_freq function) works. The chip seeks randomly up or down, ignoring UP/DOWN flag and often stops at wrong place (only noise) or even outside the FM range.
So I strongly suggest not to enable this (mis-)feature. The HW seems to be completely broken (unless there's some weird bug in the code).
Well, it seemed like a good idea at the time :-) I'll remove this 'feature', it's really not worth our time to try and make this work for these old cards.
I fixed it somehow. Now it works most of the time. The important things:
- a delay must be present after search start and before first register read or the seek does weird things
- when the search stops, the new frequency is not available immediately, we must wait until it appears in the register (fortunately, we can clear the frequency bits when starting the search as it starts at the frequency currently set, not from the value written)
- sometimes, seek remains on the current frequency (or moves only a little), so repeat it until it moves by at least 50 kHz
Thanks! And I'm impressed that you spent all that time on this old hardware :-)
Can I get your Signed-off-by line for this patch?
Regards,
Hans
--- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -89,7 +89,7 @@ static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val) tea->ops->set_pins(tea, 0); }
-static unsigned int snd_tea575x_read(struct snd_tea575x *tea) +static u32 snd_tea575x_read(struct snd_tea575x *tea) { u16 l, rdata; u32 data = 0; @@ -120,6 +120,27 @@ static unsigned int snd_tea575x_read(struct snd_tea575x *tea) return data; }
+static void snd_tea575x_get_freq(struct snd_tea575x *tea) +{
- u32 freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK;
- if (freq == 0) {
tea->freq = 0;
return;
- }
- /* freq *= 12.5 */
- freq *= 125;
- freq /= 10;
- /* crystal fixup */
- if (tea->tea5759)
freq += TEA575X_FMIF;
- else
freq -= TEA575X_FMIF;
- tea->freq = clamp(freq * 16, FREQ_LO, FREQ_HI); /* from kHz */
+}
static void snd_tea575x_set_freq(struct snd_tea575x *tea) { u32 freq = tea->freq; @@ -203,6 +224,8 @@ static int vidioc_g_frequency(struct file *file, void *priv, if (f->tuner != 0) return -EINVAL; f->type = V4L2_TUNER_RADIO;
- if (!tea->cannot_read_data)
f->frequency = tea->freq; return 0;snd_tea575x_get_freq(tea);
} @@ -225,36 +248,50 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *fh, struct v4l2_hw_freq_seek *a) { struct snd_tea575x *tea = video_drvdata(file);
int i, old_freq;
unsigned long timeout;
if (tea->cannot_read_data) return -ENOTTY;
snd_tea575x_get_freq(tea);
old_freq = tea->freq;
/* clear the frequency, HW will fill it in */
tea->val &= ~TEA575X_BIT_FREQ_MASK; tea->val |= TEA575X_BIT_SEARCH;
- tea->val &= ~TEA575X_BIT_UPDOWN; if (a->seek_upward) tea->val |= TEA575X_BIT_UPDOWN;
- else
snd_tea575x_write(tea, tea->val);tea->val &= ~TEA575X_BIT_UPDOWN;
- timeout = jiffies + msecs_to_jiffies(10000); for (;;) {
unsigned val = snd_tea575x_read(tea);
if (!(val & TEA575X_BIT_SEARCH)) {
/* Found a frequency */
val &= TEA575X_BIT_FREQ_MASK;
val = (val * 10) / 125;
if (tea->tea5759)
val += TEA575X_FMIF;
else
val -= TEA575X_FMIF;
tea->freq = clamp(val * 16, FREQ_LO, FREQ_HI);
return 0;
}
if (time_after(jiffies, timeout))
if (schedule_timeout_interruptible(msecs_to_jiffies(10))) { /* some signal arrived, stop search */ tea->val &= ~TEA575X_BIT_SEARCH; snd_tea575x_write(tea, tea->val); return -ERESTARTSYS; }break;
if (!(snd_tea575x_read(tea) & TEA575X_BIT_SEARCH)) {
/* Found a frequency, wait until it can be read */
for (i = 0; i < 100; i++) {
msleep(10);
snd_tea575x_get_freq(tea);
if (tea->freq != 0) /* available */
break;
}
/* if we moved by less than 50 kHz, continue seeking */
if (abs(old_freq - tea->freq) < 16 * 500) {
snd_tea575x_write(tea, tea->val);
continue;
}
tea->val &= ~TEA575X_BIT_SEARCH;
return 0;
}}
- return 0;
- return -ETIMEDOUT;
}
static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl) @@ -318,7 +355,7 @@ int snd_tea575x_init(struct snd_tea575x *tea) return -ENODEV; }
- tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
- tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_5_28; tea->freq = 90500 * 16; /* 90.5Mhz default */ snd_tea575x_set_freq(tea);
participants (2)
-
Hans Verkuil
-
Ondrej Zary