aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/media/radio/Kconfig18
-rw-r--r--drivers/media/radio/Makefile1
-rw-r--r--drivers/media/radio/radio-miropcm20.c270
3 files changed, 289 insertions, 0 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index a87a477c87f2..b134553eb3b5 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -195,6 +195,24 @@ config RADIO_MAESTRO
195 To compile this driver as a module, choose M here: the 195 To compile this driver as a module, choose M here: the
196 module will be called radio-maestro. 196 module will be called radio-maestro.
197 197
198config RADIO_MIROPCM20
199 tristate "miroSOUND PCM20 radio"
200 depends on ISA && VIDEO_V4L2
201 select SND_MIRO
202 ---help---
203 Choose Y here if you have this FM radio card. You also need to enable
204 the ALSA sound system. This choice automatically selects the ALSA
205 sound card driver "Miro miroSOUND PCM1pro/PCM12/PCM20radio" as this
206 is required for the radio-miropcm20.
207
208 In order to control your radio card, you will need to use programs
209 that are compatible with the Video For Linux API. Information on
210 this API and pointers to "v4l" programs may be found at
211 <file:Documentation/video4linux/API.html>.
212
213 To compile this driver as a module, choose M here: the
214 module will be called radio-miropcm20.
215
198config RADIO_SF16FMI 216config RADIO_SF16FMI
199 tristate "SF16FMI Radio" 217 tristate "SF16FMI Radio"
200 depends on ISA && VIDEO_V4L2 218 depends on ISA && VIDEO_V4L2
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index 2a1be3bf4f7c..8a63d543ae41 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
18obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o 18obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o
19obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o 19obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o
20obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o 20obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
21obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o
21obj-$(CONFIG_USB_DSBR) += dsbr100.o 22obj-$(CONFIG_USB_DSBR) += dsbr100.o
22obj-$(CONFIG_RADIO_SI470X) += si470x/ 23obj-$(CONFIG_RADIO_SI470X) += si470x/
23obj-$(CONFIG_USB_MR800) += radio-mr800.o 24obj-$(CONFIG_USB_MR800) += radio-mr800.o
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);