aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio/radio-miropcm20.c
diff options
context:
space:
mode:
authorKrzysztof Helt <krzysztof.h1@wp.pl>2009-11-27 05:24:13 -0500
committerTakashi Iwai <tiwai@suse.de>2009-11-27 07:26:23 -0500
commit8366fc390865bfb1497fe19a518fe5713f96ba3b (patch)
treeb4b47555a3d77e24b14ca08d0567456d8cfed24b /drivers/media/radio/radio-miropcm20.c
parent8700055e0a30b3f67c1474b09200b59c32dd3796 (diff)
media/radio: New driver for the radio FM module on Miro PCM20 sound card
This is recreated driver for the FM module found on Miro PCM20 sound cards. This driver was removed around the 2.6.2x kernels because it relied on the removed OSS module. Now, it uses a current ALSA module (snd-miro) and is adapted to v4l2 layer. It provides only basic functionality: frequency changing and FM module muting. Signed-off-by: Krzysztof Helt <krzysztof.h1@wp.pl> Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl> Acked-by: Mauro Carvalho Chehab <mchehab@redhat.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'drivers/media/radio/radio-miropcm20.c')
-rw-r--r--drivers/media/radio/radio-miropcm20.c270
1 files changed, 270 insertions, 0 deletions
diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c
new file mode 100644
index 000000000000..4ff885445fd4
--- /dev/null
+++ b/drivers/media/radio/radio-miropcm20.c
@@ -0,0 +1,270 @@
1/* Miro PCM20 radio driver for Linux radio support
2 * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
3 * Thanks to Norberto Pellici for the ACI device interface specification
4 * The API part is based on the radiotrack driver by M. Kirkwood
5 * This driver relies on the aci mixer provided by the snd-miro
6 * ALSA driver.
7 * Look there for further info...
8 */
9
10/* What ever you think about the ACI, version 0x07 is not very well!
11 * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono
12 * conditions... Robert
13 */
14
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/videodev2.h>
18#include <media/v4l2-device.h>
19#include <media/v4l2-ioctl.h>
20#include <sound/aci.h>
21
22static int radio_nr = -1;
23module_param(radio_nr, int, 0);
24MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
25
26static int mono;
27module_param(mono, bool, 0);
28MODULE_PARM_DESC(mono, "Force tuner into mono mode.");
29
30struct pcm20 {
31 struct v4l2_device v4l2_dev;
32 struct video_device vdev;
33 unsigned long freq;
34 int muted;
35 struct snd_miro_aci *aci;
36};
37
38static struct pcm20 pcm20_card = {
39 .freq = 87*16000,
40 .muted = 1,
41};
42
43static int pcm20_mute(struct pcm20 *dev, unsigned char mute)
44{
45 dev->muted = mute;
46 return snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, mute, -1);
47}
48
49static int pcm20_stereo(struct pcm20 *dev, unsigned char stereo)
50{
51 return snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, !stereo, -1);
52}
53
54static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq)
55{
56 unsigned char freql;
57 unsigned char freqh;
58 struct snd_miro_aci *aci = dev->aci;
59
60 dev->freq = freq;
61
62 freq /= 160;
63 if (!(aci->aci_version == 0x07 || aci->aci_version >= 0xb0))
64 freq /= 10; /* I don't know exactly which version
65 * needs this hack */
66 freql = freq & 0xff;
67 freqh = freq >> 8;
68
69 pcm20_stereo(dev, !mono);
70 return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh);
71}
72
73static const struct v4l2_file_operations pcm20_fops = {
74 .owner = THIS_MODULE,
75 .ioctl = video_ioctl2,
76};
77
78static int vidioc_querycap(struct file *file, void *priv,
79 struct v4l2_capability *v)
80{
81 strlcpy(v->driver, "Miro PCM20", sizeof(v->driver));
82 strlcpy(v->card, "Miro PCM20", sizeof(v->card));
83 strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
84 v->version = 0x1;
85 v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
86 return 0;
87}
88
89static int vidioc_g_tuner(struct file *file, void *priv,
90 struct v4l2_tuner *v)
91{
92 if (v->index) /* Only 1 tuner */
93 return -EINVAL;
94 strlcpy(v->name, "FM", sizeof(v->name));
95 v->type = V4L2_TUNER_RADIO;
96 v->rangelow = 87*16000;
97 v->rangehigh = 108*16000;
98 v->signal = 0xffff;
99 v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
100 v->capability = V4L2_TUNER_CAP_LOW;
101 v->audmode = V4L2_TUNER_MODE_MONO;
102 return 0;
103}
104
105static int vidioc_s_tuner(struct file *file, void *priv,
106 struct v4l2_tuner *v)
107{
108 return v->index ? -EINVAL : 0;
109}
110
111static int vidioc_g_frequency(struct file *file, void *priv,
112 struct v4l2_frequency *f)
113{
114 struct pcm20 *dev = video_drvdata(file);
115
116 if (f->tuner != 0)
117 return -EINVAL;
118
119 f->type = V4L2_TUNER_RADIO;
120 f->frequency = dev->freq;
121 return 0;
122}
123
124
125static int vidioc_s_frequency(struct file *file, void *priv,
126 struct v4l2_frequency *f)
127{
128 struct pcm20 *dev = video_drvdata(file);
129
130 if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
131 return -EINVAL;
132
133 dev->freq = f->frequency;
134 pcm20_setfreq(dev, f->frequency);
135 return 0;
136}
137
138static int vidioc_queryctrl(struct file *file, void *priv,
139 struct v4l2_queryctrl *qc)
140{
141 switch (qc->id) {
142 case V4L2_CID_AUDIO_MUTE:
143 return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
144 }
145 return -EINVAL;
146}
147
148static int vidioc_g_ctrl(struct file *file, void *priv,
149 struct v4l2_control *ctrl)
150{
151 struct pcm20 *dev = video_drvdata(file);
152
153 switch (ctrl->id) {
154 case V4L2_CID_AUDIO_MUTE:
155 ctrl->value = dev->muted;
156 break;
157 default:
158 return -EINVAL;
159 }
160 return 0;
161}
162
163static int vidioc_s_ctrl(struct file *file, void *priv,
164 struct v4l2_control *ctrl)
165{
166 struct pcm20 *dev = video_drvdata(file);
167
168 switch (ctrl->id) {
169 case V4L2_CID_AUDIO_MUTE:
170 pcm20_mute(dev, ctrl->value);
171 break;
172 default:
173 return -EINVAL;
174 }
175 return 0;
176}
177
178static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
179{
180 *i = 0;
181 return 0;
182}
183
184static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
185{
186 return i ? -EINVAL : 0;
187}
188
189static int vidioc_g_audio(struct file *file, void *priv,
190 struct v4l2_audio *a)
191{
192 a->index = 0;
193 strlcpy(a->name, "Radio", sizeof(a->name));
194 a->capability = V4L2_AUDCAP_STEREO;
195 return 0;
196}
197
198static int vidioc_s_audio(struct file *file, void *priv,
199 struct v4l2_audio *a)
200{
201 return a->index ? -EINVAL : 0;
202}
203
204static const struct v4l2_ioctl_ops pcm20_ioctl_ops = {
205 .vidioc_querycap = vidioc_querycap,
206 .vidioc_g_tuner = vidioc_g_tuner,
207 .vidioc_s_tuner = vidioc_s_tuner,
208 .vidioc_g_frequency = vidioc_g_frequency,
209 .vidioc_s_frequency = vidioc_s_frequency,
210 .vidioc_queryctrl = vidioc_queryctrl,
211 .vidioc_g_ctrl = vidioc_g_ctrl,
212 .vidioc_s_ctrl = vidioc_s_ctrl,
213 .vidioc_g_audio = vidioc_g_audio,
214 .vidioc_s_audio = vidioc_s_audio,
215 .vidioc_g_input = vidioc_g_input,
216 .vidioc_s_input = vidioc_s_input,
217};
218
219static int __init pcm20_init(void)
220{
221 struct pcm20 *dev = &pcm20_card;
222 struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
223 int res;
224
225 dev->aci = snd_aci_get_aci();
226 if (dev->aci == NULL) {
227 v4l2_err(v4l2_dev,
228 "you must load the snd-miro driver first!\n");
229 return -ENODEV;
230 }
231 strlcpy(v4l2_dev->name, "miropcm20", sizeof(v4l2_dev->name));
232
233
234 res = v4l2_device_register(NULL, v4l2_dev);
235 if (res < 0) {
236 v4l2_err(v4l2_dev, "could not register v4l2_device\n");
237 return -EINVAL;
238 }
239
240 strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
241 dev->vdev.v4l2_dev = v4l2_dev;
242 dev->vdev.fops = &pcm20_fops;
243 dev->vdev.ioctl_ops = &pcm20_ioctl_ops;
244 dev->vdev.release = video_device_release_empty;
245 video_set_drvdata(&dev->vdev, dev);
246
247 if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
248 goto fail;
249
250 v4l2_info(v4l2_dev, "Mirosound PCM20 Radio tuner\n");
251 return 0;
252fail:
253 v4l2_device_unregister(v4l2_dev);
254 return -EINVAL;
255}
256
257MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt");
258MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
259MODULE_LICENSE("GPL");
260
261static void __exit pcm20_cleanup(void)
262{
263 struct pcm20 *dev = &pcm20_card;
264
265 video_unregister_device(&dev->vdev);
266 v4l2_device_unregister(&dev->v4l2_dev);
267}
268
269module_init(pcm20_init);
270module_exit(pcm20_cleanup);