[media] radio-terratec: Convert to radio-isa

Tested with v4l2-compliance, but not with actual hardware. Contact the
linux-media mailinglist if you have this card!

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Hans Verkuil 2012-01-16 05:15:09 -03:00 committed by Mauro Carvalho Chehab
parent 8bd7ef5ae6
commit 32c518364a
2 changed files with 64 additions and 325 deletions

View File

@ -363,32 +363,17 @@ config RADIO_SF16FMR2
config RADIO_TERRATEC config RADIO_TERRATEC
tristate "TerraTec ActiveRadio ISA Standalone" tristate "TerraTec ActiveRadio ISA Standalone"
depends on ISA && VIDEO_V4L2 depends on ISA && VIDEO_V4L2
select RADIO_ISA
---help--- ---help---
Choose Y here if you have this FM radio card, and then fill in the Choose Y here if you have this FM radio card.
port address below. (TODO)
Note: This driver is in its early stages. Right now volume and Note: this driver hasn't been tested since a long time due to lack
frequency control and muting works at least for me, but of hardware. If you have this hardware, then please contact the
unfortunately I have not found anybody who wants to use this card linux-media mailinglist.
with Linux. So if it is this what YOU are trying to do right now,
PLEASE DROP ME A NOTE!! Rolf Offermanns <rolf@offermanns.de>.
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux API. Information on
this API and pointers to "v4l" programs may be found at
<file:Documentation/video4linux/API.html>.
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called radio-terratec. module will be called radio-terratec.
config RADIO_TERRATEC_PORT
hex "Terratec i/o port (normally 0x590)"
depends on RADIO_TERRATEC=y
default "590"
help
Fill in the I/O port of your TerraTec FM radio card. If unsure, go
with the default.
config RADIO_TRUST config RADIO_TRUST
tristate "Trust FM radio card" tristate "Trust FM radio card"
depends on ISA && VIDEO_V4L2 depends on ISA && VIDEO_V4L2

View File

@ -16,11 +16,7 @@
* Frequency control is done digitally -- ie out(port,encodefreq(95.8)); * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
* Volume Control is done digitally * Volume Control is done digitally
* *
* there is a I2C controlled RDS decoder (SAA6588) onboard, which i would like to support someday * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
* (as soon i have understand how to get started :)
* If you can help me out with that, please contact me!!
*
*
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/ */
@ -32,41 +28,21 @@
#include <linux/io.h> /* outb, outb_p */ #include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-device.h> #include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include "radio-isa.h"
MODULE_AUTHOR("R.OFFERMANNS & others"); MODULE_AUTHOR("R. Offermans & others");
MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card."); MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_VERSION("0.0.3"); MODULE_VERSION("0.1.99");
#ifndef CONFIG_RADIO_TERRATEC_PORT /* Note: there seems to be only one possible port (0x590), but without
#define CONFIG_RADIO_TERRATEC_PORT 0x590 hardware this is hard to verify. For now, this is the only one we will
#endif support. */
static int io = 0x590;
static int io = CONFIG_RADIO_TERRATEC_PORT;
static int radio_nr = -1; static int radio_nr = -1;
module_param(io, int, 0); module_param(radio_nr, int, 0444);
MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)"); MODULE_PARM_DESC(radio_nr, "Radio device number");
module_param(radio_nr, int, 0);
static struct v4l2_queryctrl radio_qctrl[] = {
{
.id = V4L2_CID_AUDIO_MUTE,
.name = "Mute",
.minimum = 0,
.maximum = 1,
.default_value = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},{
.id = V4L2_CID_AUDIO_VOLUME,
.name = "Volume",
.minimum = 0,
.maximum = 0xff,
.step = 1,
.default_value = 0xff,
.type = V4L2_CTRL_TYPE_INTEGER,
}
};
#define WRT_DIS 0x00 #define WRT_DIS 0x00
#define CLK_OFF 0x00 #define CLK_OFF 0x00
@ -76,63 +52,24 @@ static struct v4l2_queryctrl radio_qctrl[] = {
#define CLK_ON 0x08 #define CLK_ON 0x08
#define WRT_EN 0x10 #define WRT_EN 0x10
struct terratec static struct radio_isa_card *terratec_alloc(void)
{ {
struct v4l2_device v4l2_dev; return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL);
struct video_device vdev; }
int io;
int curvol;
unsigned long curfreq;
int muted;
struct mutex lock;
};
static struct terratec terratec_card; static int terratec_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
/* local things */
static void tt_write_vol(struct terratec *tt, int volume)
{ {
int i; int i;
volume = volume + (volume * 32); /* change both channels */ if (mute)
mutex_lock(&tt->lock); vol = 0;
vol = vol + (vol * 32); /* change both channels */
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
if (volume & (0x80 >> i)) if (vol & (0x80 >> i))
outb(0x80, tt->io + 1); outb(0x80, isa->io + 1);
else else
outb(0x00, tt->io + 1); outb(0x00, isa->io + 1);
} }
mutex_unlock(&tt->lock);
}
static void tt_mute(struct terratec *tt)
{
tt->muted = 1;
tt_write_vol(tt, 0);
}
static int tt_setvol(struct terratec *tt, int vol)
{
if (vol == tt->curvol) { /* requested volume = current */
if (tt->muted) { /* user is unmuting the card */
tt->muted = 0;
tt_write_vol(tt, vol); /* enable card */
}
return 0;
}
if (vol == 0) { /* volume = 0 means mute the card */
tt_write_vol(tt, 0); /* "turn off card" by setting vol to 0 */
tt->curvol = vol; /* track the volume state! */
return 0;
}
tt->muted = 0;
tt_write_vol(tt, vol);
tt->curvol = vol;
return 0; return 0;
} }
@ -140,20 +77,15 @@ static int tt_setvol(struct terratec *tt, int vol)
/* this is the worst part in this driver */ /* this is the worst part in this driver */
/* many more or less strange things are going on here, but hey, it works :) */ /* many more or less strange things are going on here, but hey, it works :) */
static int tt_setfreq(struct terratec *tt, unsigned long freq1) static int terratec_s_frequency(struct radio_isa_card *isa, u32 freq)
{ {
int freq;
int i; int i;
int p; int p;
int temp; int temp;
long rest; long rest;
unsigned char buffer[25]; /* we have to bit shift 25 registers */ unsigned char buffer[25]; /* we have to bit shift 25 registers */
mutex_lock(&tt->lock); freq = freq / 160; /* convert the freq. to a nice to handle value */
tt->curfreq = freq1;
freq = freq1 / 160; /* convert the freq. to a nice to handle value */
memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
rest = freq * 10 + 10700; /* I once had understood what is going on here */ rest = freq * 10 + 10700; /* I once had understood what is going on here */
@ -175,239 +107,61 @@ static int tt_setfreq(struct terratec *tt, unsigned long freq1)
for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */ for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */
if (buffer[i] == 1) { if (buffer[i] == 1) {
outb(WRT_EN | DATA, tt->io); outb(WRT_EN | DATA, isa->io);
outb(WRT_EN | DATA | CLK_ON, tt->io); outb(WRT_EN | DATA | CLK_ON, isa->io);
outb(WRT_EN | DATA, tt->io); outb(WRT_EN | DATA, isa->io);
} else { } else {
outb(WRT_EN | 0x00, tt->io); outb(WRT_EN | 0x00, isa->io);
outb(WRT_EN | 0x00 | CLK_ON, tt->io); outb(WRT_EN | 0x00 | CLK_ON, isa->io);
} }
} }
outb(0x00, tt->io); outb(0x00, isa->io);
mutex_unlock(&tt->lock);
return 0; return 0;
} }
static int tt_getsigstr(struct terratec *tt) static u32 terratec_g_signal(struct radio_isa_card *isa)
{ {
if (inb(tt->io) & 2) /* bit set = no signal present */ /* bit set = no signal present */
return 0; return (inb(isa->io) & 2) ? 0 : 0xffff;
return 1; /* signal present */
} }
static int vidioc_querycap(struct file *file, void *priv, static const struct radio_isa_ops terratec_ops = {
struct v4l2_capability *v) .alloc = terratec_alloc,
{ .s_mute_volume = terratec_s_mute_volume,
strlcpy(v->driver, "radio-terratec", sizeof(v->driver)); .s_frequency = terratec_s_frequency,
strlcpy(v->card, "ActiveRadio", sizeof(v->card)); .g_signal = terratec_g_signal,
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
struct terratec *tt = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
strlcpy(v->name, "FM", sizeof(v->name));
v->type = V4L2_TUNER_RADIO;
v->rangelow = 87 * 16000;
v->rangehigh = 108 * 16000;
v->rxsubchans = V4L2_TUNER_SUB_MONO;
v->capability = V4L2_TUNER_CAP_LOW;
v->audmode = V4L2_TUNER_MODE_MONO;
v->signal = 0xFFFF * tt_getsigstr(tt);
return 0;
}
static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
return v->index ? -EINVAL : 0;
}
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
struct terratec *tt = video_drvdata(file);
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
return -EINVAL;
tt_setfreq(tt, f->frequency);
return 0;
}
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
struct terratec *tt = video_drvdata(file);
if (f->tuner != 0)
return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = tt->curfreq;
return 0;
}
static int vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qc)
{
int i;
for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
if (qc->id && qc->id == radio_qctrl[i].id) {
memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
return 0;
}
}
return -EINVAL;
}
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct terratec *tt = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (tt->muted)
ctrl->value = 1;
else
ctrl->value = 0;
return 0;
case V4L2_CID_AUDIO_VOLUME:
ctrl->value = tt->curvol * 6554;
return 0;
}
return -EINVAL;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct terratec *tt = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value)
tt_mute(tt);
else
tt_setvol(tt,tt->curvol);
return 0;
case V4L2_CID_AUDIO_VOLUME:
tt_setvol(tt,ctrl->value);
return 0;
}
return -EINVAL;
}
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 const struct v4l2_file_operations terratec_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = video_ioctl2,
}; };
static const struct v4l2_ioctl_ops terratec_ioctl_ops = { static const int terratec_ioports[] = { 0x590 };
.vidioc_querycap = vidioc_querycap,
.vidioc_g_tuner = vidioc_g_tuner, static struct radio_isa_driver terratec_driver = {
.vidioc_s_tuner = vidioc_s_tuner, .driver = {
.vidioc_g_frequency = vidioc_g_frequency, .match = radio_isa_match,
.vidioc_s_frequency = vidioc_s_frequency, .probe = radio_isa_probe,
.vidioc_queryctrl = vidioc_queryctrl, .remove = radio_isa_remove,
.vidioc_g_ctrl = vidioc_g_ctrl, .driver = {
.vidioc_s_ctrl = vidioc_s_ctrl, .name = "radio-terratec",
.vidioc_g_audio = vidioc_g_audio, },
.vidioc_s_audio = vidioc_s_audio, },
.vidioc_g_input = vidioc_g_input, .io_params = &io,
.vidioc_s_input = vidioc_s_input, .radio_nr_params = &radio_nr,
.io_ports = terratec_ioports,
.num_of_io_ports = ARRAY_SIZE(terratec_ioports),
.region_size = 2,
.card = "TerraTec ActiveRadio",
.ops = &terratec_ops,
.has_stereo = true,
.max_volume = 10,
}; };
static int __init terratec_init(void) static int __init terratec_init(void)
{ {
struct terratec *tt = &terratec_card; return isa_register_driver(&terratec_driver.driver, 1);
struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
int res;
strlcpy(v4l2_dev->name, "terratec", sizeof(v4l2_dev->name));
tt->io = io;
if (tt->io == -1) {
v4l2_err(v4l2_dev, "you must set an I/O address with io=0x590 or 0x591\n");
return -EINVAL;
}
if (!request_region(tt->io, 2, "terratec")) {
v4l2_err(v4l2_dev, "port 0x%x already in use\n", io);
return -EBUSY;
}
res = v4l2_device_register(NULL, v4l2_dev);
if (res < 0) {
release_region(tt->io, 2);
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
return res;
}
strlcpy(tt->vdev.name, v4l2_dev->name, sizeof(tt->vdev.name));
tt->vdev.v4l2_dev = v4l2_dev;
tt->vdev.fops = &terratec_fops;
tt->vdev.ioctl_ops = &terratec_ioctl_ops;
tt->vdev.release = video_device_release_empty;
video_set_drvdata(&tt->vdev, tt);
mutex_init(&tt->lock);
/* mute card - prevents noisy bootups */
tt_write_vol(tt, 0);
if (video_register_device(&tt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
v4l2_device_unregister(&tt->v4l2_dev);
release_region(tt->io, 2);
return -EINVAL;
}
v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver.\n");
return 0;
} }
static void __exit terratec_exit(void) static void __exit terratec_exit(void)
{ {
struct terratec *tt = &terratec_card; isa_unregister_driver(&terratec_driver.driver);
struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
video_unregister_device(&tt->vdev);
v4l2_device_unregister(&tt->v4l2_dev);
release_region(tt->io, 2);
v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver unloaded.\n");
} }
module_init(terratec_init); module_init(terratec_init);