[alsa-devel] tea575x-tuner improvements & use in maxiradio
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/radio-pci...
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
Regards,
Hans
From: Hans Verkuil hans.verkuil@cisco.com
The tea575x-tuner module has been updated to use the latest V4L2 framework functionality. This also required changes in the drivers that rely on it.
The tea575x changes are:
- The drivers must provide a v4l2_device struct to the tea module. - The radio_nr module parameter must be part of the actual radio driver, and not of the tea module. - Changed the frequency range to the normal 76-108 MHz range instead of 50-150. - Add hardware frequency seek support. - Fix broken rxsubchans/audmode handling. - The application can now select between stereo and mono. - Support polling for control events. - Add V4L2 priority handling.
And radio-sf16fmr2.c now uses the isa bus kernel framework.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com --- drivers/media/radio/radio-sf16fmr2.c | 56 ++++++++++++- include/sound/tea575x-tuner.h | 6 +- sound/i2c/other/tea575x-tuner.c | 143 ++++++++++++++++++---------------- sound/pci/es1968.c | 15 ++++ sound/pci/fm801.c | 20 ++++- 5 files changed, 165 insertions(+), 75 deletions(-)
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index 2dd4859..772a886 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -11,14 +11,20 @@ #include <linux/init.h> /* Initdata */ #include <linux/ioport.h> /* request_region */ #include <linux/io.h> /* outb, outb_p */ +#include <linux/isa.h> #include <sound/tea575x-tuner.h>
MODULE_AUTHOR("Ondrej Zary"); MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver"); MODULE_LICENSE("GPL");
+static int radio_nr = -1; +module_param(radio_nr, int, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device number"); + struct fmr2 { int io; + struct v4l2_device v4l2_dev; struct snd_tea575x tea; struct v4l2_ctrl *volume; struct v4l2_ctrl *balance; @@ -180,26 +186,45 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea) return 0; }
-static int __init fmr2_init(void) +static int __init fmr2_probe(struct device *pdev, unsigned int dev) { struct fmr2 *fmr2 = &fmr2_card; + int err; + + fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL); + if (fmr2 == NULL) + return -ENOMEM;
+ strlcpy(fmr2->v4l2_dev.name, dev_name(pdev), + sizeof(fmr2->v4l2_dev.name)); fmr2->io = FMR2_PORT;
- if (!request_region(fmr2->io, 2, "SF16-FMR2")) { + if (!request_region(fmr2->io, 2, fmr2->v4l2_dev.name)) { printk(KERN_ERR "radio-sf16fmr2: I/O port 0x%x already in use\n", fmr2->io); + kfree(fmr2); return -EBUSY; }
+ err = v4l2_device_register(NULL, &fmr2->v4l2_dev); + if (err < 0) { + v4l2_err(&fmr2->v4l2_dev, "Could not register v4l2_device\n"); + release_region(fmr2->io, 2); + kfree(fmr2); + return err; + } + fmr2->tea.v4l2_dev = &fmr2->v4l2_dev; fmr2->tea.private_data = fmr2; + fmr2->tea.radio_nr = radio_nr; fmr2->tea.ops = &fmr2_tea_ops; fmr2->tea.ext_init = fmr2_tea_ext_init; strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card)); - strcpy(fmr2->tea.bus_info, "ISA"); + snprintf(fmr2->tea.bus_info, sizeof(fmr2->tea.bus_info), "ISA:%s", + fmr2->v4l2_dev.name);
if (snd_tea575x_init(&fmr2->tea)) { printk(KERN_ERR "radio-sf16fmr2: Unable to detect TEA575x tuner\n"); release_region(fmr2->io, 2); + kfree(fmr2); return -ENODEV; }
@@ -207,12 +232,33 @@ static int __init fmr2_init(void) return 0; }
-static void __exit fmr2_exit(void) +static int __exit fmr2_remove(struct device *pdev, unsigned int dev) { - struct fmr2 *fmr2 = &fmr2_card; + struct fmr2 *fmr2 = dev_get_drvdata(pdev);
snd_tea575x_exit(&fmr2->tea); release_region(fmr2->io, 2); + v4l2_device_unregister(&fmr2->v4l2_dev); + kfree(fmr2); + return 0; +} + +struct isa_driver fmr2_driver = { + .probe = fmr2_probe, + .remove = fmr2_remove, + .driver = { + .name = "radio-sf16fmr2", + }, +}; + +static int __init fmr2_init(void) +{ + return isa_register_driver(&fmr2_driver, 1); +} + +static void __exit fmr2_exit(void) +{ + isa_unregister_driver(&fmr2_driver); }
module_init(fmr2_init); diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h index 726e947..ec3f910 100644 --- a/include/sound/tea575x-tuner.h +++ b/include/sound/tea575x-tuner.h @@ -25,6 +25,7 @@ #include <linux/videodev2.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-dev.h> +#include <media/v4l2-device.h>
#define TEA575X_FMIF 10700
@@ -42,13 +43,16 @@ struct snd_tea575x_ops { };
struct snd_tea575x { + struct v4l2_device *v4l2_dev; struct video_device vd; /* video device */ + int radio_nr; /* radio_nr */ bool tea5759; /* 5759 chip is present */ + bool cannot_read_data; /* Device cannot read the data pin */ bool mute; /* Device is muted? */ bool stereo; /* receiving stereo */ bool tuned; /* tuned to a station */ unsigned int val; /* hw value */ - unsigned long freq; /* frequency */ + u32 freq; /* frequency */ struct mutex mutex; struct snd_tea575x_ops *ops; void *private_data; diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 6b68c82..ae4397d 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -25,8 +25,10 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/version.h> +#include <linux/sched.h> +#include <media/v4l2-device.h> #include <media/v4l2-dev.h> +#include <media/v4l2-fh.h> #include <media/v4l2-ioctl.h> #include <sound/tea575x-tuner.h>
@@ -34,12 +36,8 @@ MODULE_AUTHOR("Jaroslav Kysela perex@perex.cz"); MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); MODULE_LICENSE("GPL");
-static int radio_nr = -1; -module_param(radio_nr, int, 0); - -#define RADIO_VERSION KERNEL_VERSION(0, 0, 2) -#define FREQ_LO (50UL * 16000) -#define FREQ_HI (150UL * 16000) +#define FREQ_LO (76U * 16000) +#define FREQ_HI (108U * 16000)
/* * definitions @@ -121,28 +119,10 @@ static unsigned int snd_tea575x_read(struct snd_tea575x *tea) return data; }
-static void snd_tea575x_get_freq(struct snd_tea575x *tea) -{ - unsigned long freq; - - freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK; - /* freq *= 12.5 */ - freq *= 125; - freq /= 10; - /* crystal fixup */ - if (tea->tea5759) - freq += TEA575X_FMIF; - else - freq -= TEA575X_FMIF; - - tea->freq = freq * 16; /* from kHz */ -} - static void snd_tea575x_set_freq(struct snd_tea575x *tea) { - unsigned long freq; + u32 freq = tea->freq;
- freq = clamp(tea->freq, FREQ_LO, FREQ_HI); freq /= 16; /* to kHz */ /* crystal fixup */ if (tea->tea5759) @@ -167,12 +147,14 @@ static int vidioc_querycap(struct file *file, void *priv, { struct snd_tea575x *tea = video_drvdata(file);
- strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver)); + strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver)); strlcpy(v->card, tea->card, sizeof(v->card)); strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card)); strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info)); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + if (!tea->cannot_read_data) + v->device_caps |= V4L2_CAP_HW_FREQ_SEEK; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; }
@@ -191,18 +173,24 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; v->rangelow = FREQ_LO; v->rangehigh = FREQ_HI; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->audmode = tea->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; + v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; + v->audmode = (tea->val & TEA575X_BIT_MONO) ? + V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO; v->signal = tea->tuned ? 0xffff : 0; - return 0; }
static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - if (v->index > 0) + struct snd_tea575x *tea = video_drvdata(file); + + if (v->index) return -EINVAL; + tea->val &= ~TEA575X_BIT_MONO; + if (v->audmode == V4L2_TUNER_MODE_MONO) + tea->val |= TEA575X_BIT_MONO; + snd_tea575x_write(tea, tea->val); return 0; }
@@ -214,7 +202,6 @@ static int vidioc_g_frequency(struct file *file, void *priv, if (f->tuner != 0) return -EINVAL; f->type = V4L2_TUNER_RADIO; - snd_tea575x_get_freq(tea); f->frequency = tea->freq; return 0; } @@ -227,32 +214,45 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) return -EINVAL;
- if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) - return -EINVAL; - - tea->freq = f->frequency; - + tea->val &= ~TEA575X_BIT_SEARCH; + tea->freq = clamp(f->frequency, FREQ_LO, FREQ_HI); snd_tea575x_set_freq(tea); - return 0; }
-static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) +static int vidioc_s_hw_freq_seek(struct file *file, void *fh, + struct v4l2_hw_freq_seek *a) { - if (a->index > 1) - return -EINVAL; - - strcpy(a->name, "Radio"); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} + struct snd_tea575x *tea = video_drvdata(file);
-static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - if (a->index != 0) - return -EINVAL; + if (tea->cannot_read_data) + return -ENOTTY; + tea->val |= TEA575X_BIT_SEARCH; + tea->val &= ~TEA575X_BIT_UPDOWN; + if (a->seek_upward) + tea->val |= TEA575X_BIT_UPDOWN; + snd_tea575x_write(tea, tea->val); + 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 (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; + } + } return 0; }
@@ -273,23 +273,25 @@ static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl) static const struct v4l2_file_operations tea575x_fops = { .owner = THIS_MODULE, .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, };
static const struct v4l2_ioctl_ops tea575x_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek, + .vidioc_log_status = v4l2_ctrl_log_status, };
-static struct video_device tea575x_radio = { - .name = "tea575x-tuner", +static const struct video_device tea575x_radio = { .fops = &tea575x_fops, .ioctl_ops = &tea575x_ioctl_ops, - .release = video_device_release_empty, + .release = video_device_release_empty, };
static const struct v4l2_ctrl_ops tea575x_ctrl_ops = { @@ -303,11 +305,15 @@ int snd_tea575x_init(struct snd_tea575x *tea) { int retval;
- tea->mute = 1; + tea->mute = true;
- snd_tea575x_write(tea, 0x55AA); - if (snd_tea575x_read(tea) != 0x55AA) - return -ENODEV; + /* Not all devices can or know how to read the data back. + Such devices can set cannot_read_data to true. */ + if (!tea->cannot_read_data) { + snd_tea575x_write(tea, 0x55AA); + if (snd_tea575x_read(tea) != 0x55AA) + return -ENODEV; + }
tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40; tea->freq = 90500 * 16; /* 90.5Mhz default */ @@ -316,14 +322,17 @@ int snd_tea575x_init(struct snd_tea575x *tea) tea->vd = tea575x_radio; video_set_drvdata(&tea->vd, tea); mutex_init(&tea->mutex); + strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name)); tea->vd.lock = &tea->mutex; + tea->vd.v4l2_dev = tea->v4l2_dev; + tea->vd.ctrl_handler = &tea->ctrl_handler; + set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags);
v4l2_ctrl_handler_init(&tea->ctrl_handler, 1); - tea->vd.ctrl_handler = &tea->ctrl_handler; v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); retval = tea->ctrl_handler.error; if (retval) { - printk(KERN_ERR "tea575x-tuner: can't initialize controls\n"); + v4l2_err(tea->v4l2_dev, "can't initialize controls\n"); v4l2_ctrl_handler_free(&tea->ctrl_handler); return retval; } @@ -338,9 +347,9 @@ int snd_tea575x_init(struct snd_tea575x *tea)
v4l2_ctrl_handler_setup(&tea->ctrl_handler);
- retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr); + retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->radio_nr); if (retval) { - printk(KERN_ERR "tea575x-tuner: can't register video device!\n"); + v4l2_err(tea->v4l2_dev, "can't register video device!\n"); v4l2_ctrl_handler_free(&tea->ctrl_handler); return retval; } diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index cb557c6..a8faae1 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -142,6 +142,7 @@ static int enable_mpu[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; #ifdef SUPPORT_JOYSTICK static bool joystick[SNDRV_CARDS]; #endif +static int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); @@ -165,6 +166,9 @@ MODULE_PARM_DESC(enable_mpu, "Enable MPU401. (0 = off, 1 = on, 2 = auto)"); module_param_array(joystick, bool, NULL, 0444); MODULE_PARM_DESC(joystick, "Enable joystick."); #endif +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); +
#define NR_APUS 64 @@ -558,6 +562,7 @@ struct es1968 { struct work_struct hwvol_work;
#ifdef CONFIG_SND_ES1968_RADIO + struct v4l2_device v4l2_dev; struct snd_tea575x tea; #endif }; @@ -2613,6 +2618,7 @@ static int snd_es1968_free(struct es1968 *chip)
#ifdef CONFIG_SND_ES1968_RADIO snd_tea575x_exit(&chip->tea); + v4l2_device_unregister(&chip->v4l2_dev); #endif
if (chip->irq >= 0) @@ -2655,6 +2661,7 @@ static int __devinit snd_es1968_create(struct snd_card *card, int capt_streams, int chip_type, int do_pm, + int radio_nr, struct es1968 **chip_ret) { static struct snd_device_ops ops = { @@ -2751,7 +2758,14 @@ static int __devinit snd_es1968_create(struct snd_card *card, snd_card_set_dev(card, &pci->dev);
#ifdef CONFIG_SND_ES1968_RADIO + err = v4l2_device_register(&pci->dev, &chip->v4l2_dev); + if (err < 0) { + snd_es1968_free(chip); + return err; + } + chip->tea.v4l2_dev = &chip->v4l2_dev; chip->tea.private_data = chip; + chip->tea.radio_nr = radio_nr; chip->tea.ops = &snd_es1968_tea_ops; strlcpy(chip->tea.card, "SF64-PCE2", sizeof(chip->tea.card)); sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci)); @@ -2797,6 +2811,7 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci, pcm_substreams_c[dev], pci_id->driver_data, use_pm[dev], + radio_nr[dev], &chip)) < 0) { snd_card_free(card); return err; diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 9597ef1..a416ea8 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -58,6 +58,7 @@ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card * High 16-bits are video (radio) device number + 1 */ static int tea575x_tuner[SNDRV_CARDS]; +static int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the FM801 soundcard."); @@ -67,6 +68,9 @@ module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable FM801 soundcard."); module_param_array(tea575x_tuner, int, NULL, 0444); MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only)."); +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); +
#define TUNER_DISABLED (1<<3) #define TUNER_ONLY (1<<4) @@ -197,6 +201,7 @@ struct fm801 { struct snd_info_entry *proc_entry;
#ifdef CONFIG_SND_FM801_TEA575X_BOOL + struct v4l2_device v4l2_dev; struct snd_tea575x tea; #endif
@@ -1154,8 +1159,10 @@ static int snd_fm801_free(struct fm801 *chip)
__end_hw: #ifdef CONFIG_SND_FM801_TEA575X_BOOL - if (!(chip->tea575x_tuner & TUNER_DISABLED)) + if (!(chip->tea575x_tuner & TUNER_DISABLED)) { snd_tea575x_exit(&chip->tea); + v4l2_device_unregister(&chip->v4l2_dev); + } #endif if (chip->irq >= 0) free_irq(chip->irq, chip); @@ -1175,6 +1182,7 @@ static int snd_fm801_dev_free(struct snd_device *device) static int __devinit snd_fm801_create(struct snd_card *card, struct pci_dev * pci, int tea575x_tuner, + int radio_nr, struct fm801 ** rchip) { struct fm801 *chip; @@ -1234,6 +1242,13 @@ static int __devinit snd_fm801_create(struct snd_card *card, snd_card_set_dev(card, &pci->dev);
#ifdef CONFIG_SND_FM801_TEA575X_BOOL + err = v4l2_device_register(&pci->dev, &chip->v4l2_dev); + if (err < 0) { + snd_fm801_free(chip); + return err; + } + chip->tea.v4l2_dev = &chip->v4l2_dev; + chip->tea.radio_nr = radio_nr; chip->tea.private_data = chip; chip->tea.ops = &snd_fm801_tea_ops; sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci)); @@ -1241,6 +1256,7 @@ static int __devinit snd_fm801_create(struct snd_card *card, (tea575x_tuner & TUNER_TYPE_MASK) < 4) { if (snd_tea575x_init(&chip->tea)) { snd_printk(KERN_ERR "TEA575x radio not found\n"); + snd_fm801_free(chip); return -ENODEV; } } else if ((tea575x_tuner & TUNER_TYPE_MASK) == 0) { @@ -1287,7 +1303,7 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci, err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); if (err < 0) return err; - if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], &chip)) < 0) { + if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], radio_nr[dev], &chip)) < 0) { snd_card_free(card); return err; }
From: Hans Verkuil hans.verkuil@cisco.com
This card is based on the tea575x receiver. Use the tea575x-tuner framework instead of reinventing the wheel.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com --- drivers/media/radio/Kconfig | 2 +- drivers/media/radio/radio-maxiradio.c | 379 ++++++--------------------------- 2 files changed, 65 insertions(+), 316 deletions(-)
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 48747df..6591f8b 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -43,7 +43,7 @@ config USB_DSBR
config RADIO_MAXIRADIO tristate "Guillemot MAXI Radio FM 2000 radio" - depends on VIDEO_V4L2 && PCI + depends on VIDEO_V4L2 && PCI && SND ---help--- Choose Y here if you have this radio card. This card may also be found as Gemtek PCI FM. diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c index f872a54..740a3d5 100644 --- a/drivers/media/radio/radio-maxiradio.c +++ b/drivers/media/radio/radio-maxiradio.c @@ -42,67 +42,37 @@ #include <linux/videodev2.h> #include <linux/io.h> #include <linux/slab.h> +#include <sound/tea575x-tuner.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> - -#define DRIVER_VERSION "0.7.8" - +#include <media/v4l2-fh.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h>
MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net"); -MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio."); +MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000."); MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); +MODULE_VERSION("1.0.0");
static int radio_nr = -1; -module_param(radio_nr, int, 0); - -static int debug; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "activates debug info"); - -#define dprintk(dev, num, fmt, arg...) \ - v4l2_dbg(num, debug, &dev->v4l2_dev, fmt, ## arg) - -#ifndef PCI_VENDOR_ID_GUILLEMOT -#define PCI_VENDOR_ID_GUILLEMOT 0x5046 -#endif - -#ifndef PCI_DEVICE_ID_GUILLEMOT -#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001 -#endif - +module_param(radio_nr, int, 0644); +MODULE_PARM_DESC(radio_nr, "Radio device number");
/* TEA5757 pin mappings */ static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16;
-#define FREQ_LO (87 * 16000) -#define FREQ_HI (108 * 16000) - -#define FREQ_IF 171200 /* 10.7*16000 */ -#define FREQ_STEP 200 /* 12.5*16 */ - -/* (x==fmhz*16*1000) -> bits */ -#define FREQ2BITS(x) \ - ((((unsigned int)(x) + FREQ_IF + (FREQ_STEP << 1)) / (FREQ_STEP << 2)) << 2) - -#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF) +static atomic_t maxiradio_instance = ATOMIC_INIT(0);
+#define PCI_VENDOR_ID_GUILLEMOT 0x5046 +#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
struct maxiradio { + struct snd_tea575x tea; struct v4l2_device v4l2_dev; - struct video_device vdev; struct pci_dev *pdev;
u16 io; /* base of radio io */ - u16 muted; /* VIDEO_AUDIO_MUTE */ - u16 stereo; /* VIDEO_TUNER_STEREO_ON */ - u16 tuned; /* signal strength (0 or 0xffff) */ - - unsigned long freq; - - struct mutex lock; };
static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev) @@ -110,259 +80,41 @@ static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev) return container_of(v4l2_dev, struct maxiradio, v4l2_dev); }
-static void outbit(unsigned long bit, u16 io) -{ - int val = power | wren | (bit ? data : 0); - - outb(val, io); - udelay(4); - outb(val | clk, io); - udelay(4); - outb(val, io); - udelay(4); -} - -static void turn_power(struct maxiradio *dev, int p) -{ - if (p != 0) { - dprintk(dev, 1, "Radio powered on\n"); - outb(power, dev->io); - } else { - dprintk(dev, 1, "Radio powered off\n"); - outb(0, dev->io); - } -} - -static void set_freq(struct maxiradio *dev, u32 freq) -{ - unsigned long int si; - int bl; - int io = dev->io; - int val = FREQ2BITS(freq); - - /* TEA5757 shift register bits (see pdf) */ - - outbit(0, io); /* 24 search */ - outbit(1, io); /* 23 search up/down */ - - outbit(0, io); /* 22 stereo/mono */ - - outbit(0, io); /* 21 band */ - outbit(0, io); /* 20 band (only 00=FM works I think) */ - - outbit(0, io); /* 19 port ? */ - outbit(0, io); /* 18 port ? */ - - outbit(0, io); /* 17 search level */ - outbit(0, io); /* 16 search level */ - - si = 0x8000; - for (bl = 1; bl <= 16; bl++) { - outbit(val & si, io); - si >>= 1; - } - - dprintk(dev, 1, "Radio freq set to %d.%02d MHz\n", - freq / 16000, - freq % 16000 * 100 / 16000); - - turn_power(dev, 1); -} - -static int get_stereo(u16 io) +static void maxiradio_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) { - outb(power,io); - udelay(4); + struct maxiradio *dev = tea->private_data; + u8 bits = 0;
- return !(inb(io) & mo_st); -} + bits |= (pins & TEA575X_DATA) ? data : 0; + bits |= (pins & TEA575X_CLK) ? clk : 0; + bits |= (pins & TEA575X_WREN) ? wren : 0; + bits |= power;
-static int get_tune(u16 io) -{ - outb(power+clk,io); - udelay(4); - - return !(inb(io) & mo_st); -} - - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - struct maxiradio *dev = video_drvdata(file); - - strlcpy(v->driver, "radio-maxiradio", sizeof(v->driver)); - strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof(v->card)); - snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev)); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - return 0; + outb(bits, dev->io); }
-static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) +/* Note: this card cannot read out the data of the shift registers, + only the mono/stereo pin works. */ +static u8 maxiradio_tea575x_get_pins(struct snd_tea575x *tea) { - struct maxiradio *dev = video_drvdata(file); - - if (v->index > 0) - return -EINVAL; - - mutex_lock(&dev->lock); - strlcpy(v->name, "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - v->rangelow = FREQ_LO; - v->rangehigh = FREQ_HI; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - if (get_stereo(dev->io)) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xffff * get_tune(dev->io); - mutex_unlock(&dev->lock); + struct maxiradio *dev = tea->private_data; + u8 bits = inb(dev->io);
- return 0; + return ((bits & data) ? TEA575X_DATA : 0) | + ((bits & mo_st) ? TEA575X_MOST : 0); }
-static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) +static void maxiradio_tea575x_set_direction(struct snd_tea575x *tea, bool output) { - return v->index ? -EINVAL : 0; }
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - - -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct maxiradio *dev = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) { - dprintk(dev, 1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n", - f->frequency / 16000, - f->frequency % 16000 * 100 / 16000, - FREQ_LO / 16000, FREQ_HI / 16000); - - return -EINVAL; - } - - mutex_lock(&dev->lock); - dev->freq = f->frequency; - set_freq(dev, dev->freq); - msleep(125); - mutex_unlock(&dev->lock); - - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct maxiradio *dev = video_drvdata(file); - - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = dev->freq; - - dprintk(dev, 4, "radio freq is %d.%02d MHz", - f->frequency / 16000, - f->frequency % 16000 * 100 / 16000); - - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - } - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct maxiradio *dev = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = dev->muted; - return 0; - } - - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct maxiradio *dev = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - mutex_lock(&dev->lock); - dev->muted = ctrl->value; - if (dev->muted) - turn_power(dev, 0); - else - set_freq(dev, dev->freq); - mutex_unlock(&dev->lock); - return 0; - } - - return -EINVAL; -} - -static const struct v4l2_file_operations maxiradio_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, +static struct snd_tea575x_ops maxiradio_tea_ops = { + .set_pins = maxiradio_tea575x_set_pins, + .get_pins = maxiradio_tea575x_get_pins, + .set_direction = maxiradio_tea575x_set_direction, };
-static const struct v4l2_ioctl_ops maxiradio_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, -}; - -static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +static int __devinit maxiradio_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct maxiradio *dev; struct v4l2_device *v4l2_dev; @@ -375,63 +127,60 @@ static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_d }
v4l2_dev = &dev->v4l2_dev; - mutex_init(&dev->lock); - dev->pdev = pdev; - dev->muted = 1; - dev->freq = FREQ_LO; - - strlcpy(v4l2_dev->name, "maxiradio", sizeof(v4l2_dev->name)); + v4l2_device_set_name(v4l2_dev, "maxiradio", &maxiradio_instance);
retval = v4l2_device_register(&pdev->dev, v4l2_dev); if (retval < 0) { v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); goto errfr; } + dev->tea.private_data = dev; + dev->tea.ops = &maxiradio_tea_ops; + /* The data pin cannot be read. This may be a hardware limitation, or + we just don't know how to read it. */ + dev->tea.cannot_read_data = true; + dev->tea.v4l2_dev = v4l2_dev; + dev->tea.radio_nr = radio_nr; + strlcpy(dev->tea.card, "Maxi Radio FM2000", sizeof(dev->tea.card)); + snprintf(dev->tea.bus_info, sizeof(dev->tea.bus_info), + "PCI:%s", pci_name(pdev)); + + retval = -ENODEV;
if (!request_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) { - v4l2_err(v4l2_dev, "can't reserve I/O ports\n"); - goto err_out; + pci_resource_len(pdev, 0), v4l2_dev->name)) { + dev_err(&pdev->dev, "can't reserve I/O ports\n"); + goto err_hdl; }
if (pci_enable_device(pdev)) goto err_out_free_region;
dev->io = pci_resource_start(pdev, 0); - strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); - dev->vdev.v4l2_dev = v4l2_dev; - dev->vdev.fops = &maxiradio_fops; - dev->vdev.ioctl_ops = &maxiradio_ioctl_ops; - dev->vdev.release = video_device_release_empty; - video_set_drvdata(&dev->vdev, dev); - - if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { - v4l2_err(v4l2_dev, "can't register device!"); + if (snd_tea575x_init(&dev->tea)) { + printk(KERN_ERR "radio-maxiradio: Unable to detect TEA575x tuner\n"); goto err_out_free_region; } - - v4l2_info(v4l2_dev, "version " DRIVER_VERSION "\n"); - - v4l2_info(v4l2_dev, "found Guillemot MAXI Radio device (io = 0x%x)\n", - dev->io); return 0;
err_out_free_region: release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); -err_out: +err_hdl: v4l2_device_unregister(v4l2_dev); errfr: kfree(dev); - return -ENODEV; + return retval; }
-static void __devexit maxiradio_remove_one(struct pci_dev *pdev) +static void __devexit maxiradio_remove(struct pci_dev *pdev) { struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); struct maxiradio *dev = to_maxiradio(v4l2_dev);
- video_unregister_device(&dev->vdev); - v4l2_device_unregister(&dev->v4l2_dev); + snd_tea575x_exit(&dev->tea); + /* Turn off power */ + outb(0, dev->io); + v4l2_device_unregister(v4l2_dev); release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); }
@@ -446,19 +195,19 @@ MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl); static struct pci_driver maxiradio_driver = { .name = "radio-maxiradio", .id_table = maxiradio_pci_tbl, - .probe = maxiradio_init_one, - .remove = __devexit_p(maxiradio_remove_one), + .probe = maxiradio_probe, + .remove = __devexit_p(maxiradio_remove), };
-static int __init maxiradio_radio_init(void) +static int __init maxiradio_init(void) { return pci_register_driver(&maxiradio_driver); }
-static void __exit maxiradio_radio_exit(void) +static void __exit maxiradio_exit(void) { pci_unregister_driver(&maxiradio_driver); }
-module_init(maxiradio_radio_init); -module_exit(maxiradio_radio_exit); +module_init(maxiradio_init); +module_exit(maxiradio_exit);
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/radio-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).
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/radio-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 wonder if you are able to test the ISA radio-sf16fmr2.c driver? I'm not sure if you have the hardware, but since I changed this driver to use the proper isa kernel framework I'd like to have this tested if possible.
Regards,
Hans
On Wednesday 08 February 2012, you 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'll test it with other cards, maybe it will work at least a bit. And also with original (Windows) software - it has some seek functionality, IIRC.
I wonder if you are able to test the ISA radio-sf16fmr2.c driver? I'm not sure if you have the hardware, but since I changed this driver to use the proper isa kernel framework I'd like to have this tested if possible.
Yes, I have the card so I'll test it. All I can say right now is that the driver did not build yesterday (missing #include <linux/slab.h>).
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 wonder if you are able to test the ISA radio-sf16fmr2.c driver? I'm not sure if you have the hardware, but since I changed this driver to use the proper isa kernel framework I'd like to have this tested if possible.
The driver works, provided that linux/slab.h is included. It oopses on rmmod because dev_set_drvdata() call is missing from init. This patch fixes all of that and also removes (now useless) static fmr2_card:
--- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -9,6 +9,7 @@ #include <linux/delay.h> #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ +#include <linux/slab.h> #include <linux/ioport.h> /* request_region */ #include <linux/io.h> /* outb, outb_p */ #include <linux/isa.h> @@ -32,7 +33,6 @@ struct fmr2 {
/* the port is hardwired so no need to support multiple cards */ #define FMR2_PORT 0x384 -static struct fmr2 fmr2_card;
/* TEA575x tuner pins */ #define STR_DATA (1 << 0) @@ -188,7 +188,7 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea)
static int __init fmr2_probe(struct device *pdev, unsigned int dev) { - struct fmr2 *fmr2 = &fmr2_card; + struct fmr2 *fmr2; int err;
fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL); @@ -229,6 +229,7 @@ static int __init fmr2_probe(struct device *pdev, unsigned int dev) }
printk(KERN_INFO "radio-sf16fmr2: SF16-FMR2 radio card at 0x%x.\n", fmr2->io); + dev_set_drvdata(pdev, fmr2); return 0; }
On Wednesday, February 08, 2012 20:57:43 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 wonder if you are able to test the ISA radio-sf16fmr2.c driver? I'm not sure if you have the hardware, but since I changed this driver to use the proper isa kernel framework I'd like to have this tested if possible.
The driver works, provided that linux/slab.h is included. It oopses on rmmod because dev_set_drvdata() call is missing from init. This patch fixes all of that and also removes (now useless) static fmr2_card:
Thanks! I'll apply the changes for the final pull request. That's probably going to be in a week or two.
BTW, do you perhaps have a mirosound pcm20 card (or know someone who does)? I'd like to update the radio-miropcm20 driver as well and it would be nice if my changes can be tested.
Regards,
Hans
--- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -9,6 +9,7 @@ #include <linux/delay.h> #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ +#include <linux/slab.h> #include <linux/ioport.h> /* request_region */ #include <linux/io.h> /* outb, outb_p */ #include <linux/isa.h> @@ -32,7 +33,6 @@ struct fmr2 {
/* the port is hardwired so no need to support multiple cards */ #define FMR2_PORT 0x384 -static struct fmr2 fmr2_card;
/* TEA575x tuner pins */ #define STR_DATA (1 << 0) @@ -188,7 +188,7 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea)
static int __init fmr2_probe(struct device *pdev, unsigned int dev) {
- struct fmr2 *fmr2 = &fmr2_card;
struct fmr2 *fmr2; int err;
fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL);
@@ -229,6 +229,7 @@ static int __init fmr2_probe(struct device *pdev, unsigned int dev) }
printk(KERN_INFO "radio-sf16fmr2: SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
- dev_set_drvdata(pdev, fmr2); return 0;
}
On Wednesday 08 February 2012 21:30:07 Hans Verkuil wrote:
On Wednesday, February 08, 2012 20:57:43 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 wonder if you are able to test the ISA radio-sf16fmr2.c driver? I'm not sure if you have the hardware, but since I changed this driver to use the proper isa kernel framework I'd like to have this tested if possible.
The driver works, provided that linux/slab.h is included. It oopses on rmmod because dev_set_drvdata() call is missing from init. This patch fixes all of that and also removes (now useless) static fmr2_card:
Thanks! I'll apply the changes for the final pull request. That's probably going to be in a week or two.
BTW, do you perhaps have a mirosound pcm20 card (or know someone who does)? I'd like to update the radio-miropcm20 driver as well and it would be nice if my changes can be tested.
Unfortunately not. I only have SF16-FMI, SF16-FMP, SF16-FMR2, SF64-PCR, SF64-PCE2, SF256-PCP. Another card should be on the way, looks like AOpen FX-3D Pro Radio on photo.
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);
participants (2)
-
Hans Verkuil
-
Ondrej Zary