forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
V4L/DVB (10771): tea575x-tuner: convert it to V4L2 API
Signed-off-by: Mauro Carvalho Chehab <[email protected]>
- Loading branch information
Mauro Carvalho Chehab
committed
Mar 30, 2009
1 parent
85f8841
commit 9b76ede
Showing
3 changed files
with
212 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,13 +24,21 @@ | |
#include <linux/delay.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/init.h> | ||
#include <linux/version.h> | ||
#include <sound/core.h> | ||
#include <sound/tea575x-tuner.h> | ||
|
||
MODULE_AUTHOR("Jaroslav Kysela <[email protected]>"); | ||
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 (87 * 16000) | ||
#define FREQ_HI (108 * 16000) | ||
|
||
/* | ||
* definitions | ||
*/ | ||
|
@@ -53,6 +61,17 @@ MODULE_LICENSE("GPL"); | |
#define TEA575X_BIT_DUMMY (1<<15) /* buffer */ | ||
#define TEA575X_BIT_FREQ_MASK 0x7fff | ||
|
||
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, | ||
} | ||
}; | ||
|
||
/* | ||
* lowlevel part | ||
*/ | ||
|
@@ -84,94 +103,146 @@ static void snd_tea575x_set_freq(struct snd_tea575x *tea) | |
* Linux Video interface | ||
*/ | ||
|
||
static long snd_tea575x_ioctl(struct file *file, | ||
unsigned int cmd, unsigned long data) | ||
static int vidioc_querycap(struct file *file, void *priv, | ||
struct v4l2_capability *v) | ||
{ | ||
struct snd_tea575x *tea = video_drvdata(file); | ||
void __user *arg = (void __user *)data; | ||
|
||
switch(cmd) { | ||
case VIDIOCGCAP: | ||
{ | ||
struct video_capability v; | ||
v.type = VID_TYPE_TUNER; | ||
v.channels = 1; | ||
v.audios = 1; | ||
/* No we don't do pictures */ | ||
v.maxwidth = 0; | ||
v.maxheight = 0; | ||
v.minwidth = 0; | ||
v.minheight = 0; | ||
strcpy(v.name, tea->tea5759 ? "TEA5759" : "TEA5757"); | ||
if (copy_to_user(arg,&v,sizeof(v))) | ||
return -EFAULT; | ||
return 0; | ||
} | ||
case VIDIOCGTUNER: | ||
{ | ||
struct video_tuner v; | ||
if (copy_from_user(&v, arg,sizeof(v))!=0) | ||
return -EFAULT; | ||
if (v.tuner) /* Only 1 tuner */ | ||
return -EINVAL; | ||
v.rangelow = (87*16000); | ||
v.rangehigh = (108*16000); | ||
v.flags = VIDEO_TUNER_LOW; | ||
v.mode = VIDEO_MODE_AUTO; | ||
strcpy(v.name, "FM"); | ||
v.signal = 0xFFFF; | ||
if (copy_to_user(arg, &v, sizeof(v))) | ||
return -EFAULT; | ||
return 0; | ||
} | ||
case VIDIOCSTUNER: | ||
{ | ||
struct video_tuner v; | ||
if(copy_from_user(&v, arg, sizeof(v))) | ||
return -EFAULT; | ||
if(v.tuner!=0) | ||
return -EINVAL; | ||
/* Only 1 tuner so no setting needed ! */ | ||
|
||
strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757"); | ||
strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver)); | ||
strlcpy(v->card, "Maestro Radio", sizeof(v->card)); | ||
sprintf(v->bus_info, "PCI"); | ||
v->version = RADIO_VERSION; | ||
v->capabilities = V4L2_CAP_TUNER; | ||
return 0; | ||
} | ||
|
||
static int vidioc_g_tuner(struct file *file, void *priv, | ||
struct v4l2_tuner *v) | ||
{ | ||
if (v->index > 0) | ||
return -EINVAL; | ||
|
||
strcpy(v->name, "FM"); | ||
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; | ||
v->audmode = V4L2_TUNER_MODE_MONO; | ||
v->signal = 0xffff; | ||
return 0; | ||
} | ||
|
||
static int vidioc_s_tuner(struct file *file, void *priv, | ||
struct v4l2_tuner *v) | ||
{ | ||
if (v->index > 0) | ||
return -EINVAL; | ||
return 0; | ||
} | ||
|
||
static int vidioc_g_frequency(struct file *file, void *priv, | ||
struct v4l2_frequency *f) | ||
{ | ||
struct snd_tea575x *tea = video_drvdata(file); | ||
|
||
f->type = V4L2_TUNER_RADIO; | ||
f->frequency = tea->freq; | ||
return 0; | ||
} | ||
|
||
static int vidioc_s_frequency(struct file *file, void *priv, | ||
struct v4l2_frequency *f) | ||
{ | ||
struct snd_tea575x *tea = video_drvdata(file); | ||
|
||
if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) | ||
return -EINVAL; | ||
|
||
tea->freq = f->frequency; | ||
|
||
snd_tea575x_set_freq(tea); | ||
|
||
return 0; | ||
} | ||
|
||
static int vidioc_g_audio(struct file *file, void *priv, | ||
struct v4l2_audio *a) | ||
{ | ||
if (a->index > 1) | ||
return -EINVAL; | ||
|
||
strcpy(a->name, "Radio"); | ||
a->capability = V4L2_AUDCAP_STEREO; | ||
return 0; | ||
} | ||
|
||
static int vidioc_s_audio(struct file *file, void *priv, | ||
struct v4l2_audio *a) | ||
{ | ||
if (a->index != 0) | ||
return -EINVAL; | ||
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; | ||
} | ||
case VIDIOCGFREQ: | ||
if(copy_to_user(arg, &tea->freq, sizeof(tea->freq))) | ||
return -EFAULT; | ||
return 0; | ||
case VIDIOCSFREQ: | ||
if(copy_from_user(&tea->freq, arg, sizeof(tea->freq))) | ||
return -EFAULT; | ||
snd_tea575x_set_freq(tea); | ||
return 0; | ||
case VIDIOCGAUDIO: | ||
{ | ||
struct video_audio v; | ||
memset(&v, 0, sizeof(v)); | ||
strcpy(v.name, "Radio"); | ||
if(copy_to_user(arg,&v, sizeof(v))) | ||
return -EFAULT; | ||
} | ||
return -EINVAL; | ||
} | ||
|
||
static int vidioc_g_ctrl(struct file *file, void *priv, | ||
struct v4l2_control *ctrl) | ||
{ | ||
struct snd_tea575x *tea = video_drvdata(file); | ||
|
||
switch (ctrl->id) { | ||
case V4L2_CID_AUDIO_MUTE: | ||
if (tea->ops->mute) { | ||
ctrl->value = tea->mute; | ||
return 0; | ||
} | ||
case VIDIOCSAUDIO: | ||
{ | ||
struct video_audio v; | ||
if(copy_from_user(&v, arg, sizeof(v))) | ||
return -EFAULT; | ||
if (tea->ops->mute) | ||
tea->ops->mute(tea, | ||
(v.flags & | ||
VIDEO_AUDIO_MUTE) ? 1 : 0); | ||
if(v.audio) | ||
return -EINVAL; | ||
} | ||
return -EINVAL; | ||
} | ||
|
||
static int vidioc_s_ctrl(struct file *file, void *priv, | ||
struct v4l2_control *ctrl) | ||
{ | ||
struct snd_tea575x *tea = video_drvdata(file); | ||
|
||
switch (ctrl->id) { | ||
case V4L2_CID_AUDIO_MUTE: | ||
if (tea->ops->mute) { | ||
tea->ops->mute(tea, ctrl->value); | ||
tea->mute = 1; | ||
return 0; | ||
} | ||
default: | ||
return -ENOIOCTLCMD; | ||
} | ||
return -EINVAL; | ||
} | ||
|
||
static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | ||
{ | ||
*i = 0; | ||
return 0; | ||
} | ||
|
||
static void snd_tea575x_release(struct video_device *vfd) | ||
static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | ||
{ | ||
if (i != 0) | ||
return -EINVAL; | ||
return 0; | ||
} | ||
|
||
static int snd_tea575x_exclusive_open(struct file *file) | ||
|
@@ -189,50 +260,91 @@ static int snd_tea575x_exclusive_release(struct file *file) | |
return 0; | ||
} | ||
|
||
static const struct v4l2_file_operations tea575x_fops = { | ||
.owner = THIS_MODULE, | ||
.open = snd_tea575x_exclusive_open, | ||
.release = snd_tea575x_exclusive_release, | ||
.ioctl = video_ioctl2, | ||
}; | ||
|
||
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_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 struct video_device tea575x_radio = { | ||
.name = "tea575x-tuner", | ||
.fops = &tea575x_fops, | ||
.ioctl_ops = &tea575x_ioctl_ops, | ||
.release = video_device_release, | ||
}; | ||
|
||
/* | ||
* initialize all the tea575x chips | ||
*/ | ||
void snd_tea575x_init(struct snd_tea575x *tea) | ||
{ | ||
int retval; | ||
unsigned int val; | ||
struct video_device *tea575x_radio_inst; | ||
|
||
val = tea->ops->read(tea); | ||
if (val == 0x1ffffff || val == 0) { | ||
snd_printk(KERN_ERR "Cannot find TEA575x chip\n"); | ||
snd_printk(KERN_ERR | ||
"tea575x-tuner: Cannot find TEA575x chip\n"); | ||
return; | ||
} | ||
|
||
memset(&tea->vd, 0, sizeof(tea->vd)); | ||
strcpy(tea->vd.name, tea->tea5759 ? "TEA5759 radio" : "TEA5757 radio"); | ||
tea->vd.release = snd_tea575x_release; | ||
video_set_drvdata(&tea->vd, tea); | ||
tea->vd.fops = &tea->fops; | ||
tea->in_use = 0; | ||
tea->fops.owner = tea->card->module; | ||
tea->fops.open = snd_tea575x_exclusive_open; | ||
tea->fops.release = snd_tea575x_exclusive_release; | ||
tea->fops.ioctl = snd_tea575x_ioctl; | ||
if (video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->dev_nr - 1) < 0) { | ||
snd_printk(KERN_ERR "unable to register tea575x tuner\n"); | ||
tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40; | ||
tea->freq = 90500 * 16; /* 90.5Mhz default */ | ||
|
||
tea575x_radio_inst = video_device_alloc(); | ||
if (tea575x_radio_inst == NULL) { | ||
printk(KERN_ERR "tea575x-tuner: not enough memory\n"); | ||
return; | ||
} | ||
tea->vd_registered = 1; | ||
|
||
tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40; | ||
tea->freq = 90500 * 16; /* 90.5Mhz default */ | ||
memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio)); | ||
|
||
strcpy(tea575x_radio.name, tea->tea5759 ? | ||
"TEA5759 radio" : "TEA5757 radio"); | ||
|
||
video_set_drvdata(tea575x_radio_inst, tea); | ||
|
||
retval = video_register_device(tea575x_radio_inst, | ||
VFL_TYPE_RADIO, radio_nr); | ||
if (retval) { | ||
printk(KERN_ERR "tea575x-tuner: can't register video device!\n"); | ||
kfree(tea575x_radio_inst); | ||
return; | ||
} | ||
|
||
snd_tea575x_set_freq(tea); | ||
|
||
/* mute on init */ | ||
if (tea->ops->mute) | ||
if (tea->ops->mute) { | ||
tea->ops->mute(tea, 1); | ||
tea->mute = 1; | ||
} | ||
tea->vd = tea575x_radio_inst; | ||
} | ||
|
||
void snd_tea575x_exit(struct snd_tea575x *tea) | ||
{ | ||
if (tea->vd_registered) { | ||
video_unregister_device(&tea->vd); | ||
tea->vd_registered = 0; | ||
if (tea->vd) { | ||
video_unregister_device(tea->vd); | ||
tea->vd = NULL; | ||
} | ||
} | ||
|
||
|
Oops, something went wrong.