Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/media/radio/radio-miropcm20.c
15112 views
1
/* Miro PCM20 radio driver for Linux radio support
2
* (c) 1998 Ruurd Reitsma <[email protected]>
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
22
static int radio_nr = -1;
23
module_param(radio_nr, int, 0);
24
MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
25
26
static int mono;
27
module_param(mono, bool, 0);
28
MODULE_PARM_DESC(mono, "Force tuner into mono mode.");
29
30
struct 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
struct mutex lock;
37
};
38
39
static struct pcm20 pcm20_card = {
40
.freq = 87*16000,
41
.muted = 1,
42
};
43
44
static int pcm20_mute(struct pcm20 *dev, unsigned char mute)
45
{
46
dev->muted = mute;
47
return snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, mute, -1);
48
}
49
50
static int pcm20_stereo(struct pcm20 *dev, unsigned char stereo)
51
{
52
return snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, !stereo, -1);
53
}
54
55
static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq)
56
{
57
unsigned char freql;
58
unsigned char freqh;
59
struct snd_miro_aci *aci = dev->aci;
60
61
dev->freq = freq;
62
63
freq /= 160;
64
if (!(aci->aci_version == 0x07 || aci->aci_version >= 0xb0))
65
freq /= 10; /* I don't know exactly which version
66
* needs this hack */
67
freql = freq & 0xff;
68
freqh = freq >> 8;
69
70
pcm20_stereo(dev, !mono);
71
return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh);
72
}
73
74
static const struct v4l2_file_operations pcm20_fops = {
75
.owner = THIS_MODULE,
76
.unlocked_ioctl = video_ioctl2,
77
};
78
79
static int vidioc_querycap(struct file *file, void *priv,
80
struct v4l2_capability *v)
81
{
82
strlcpy(v->driver, "Miro PCM20", sizeof(v->driver));
83
strlcpy(v->card, "Miro PCM20", sizeof(v->card));
84
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
85
v->version = 0x1;
86
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
87
return 0;
88
}
89
90
static int vidioc_g_tuner(struct file *file, void *priv,
91
struct v4l2_tuner *v)
92
{
93
if (v->index) /* Only 1 tuner */
94
return -EINVAL;
95
strlcpy(v->name, "FM", sizeof(v->name));
96
v->type = V4L2_TUNER_RADIO;
97
v->rangelow = 87*16000;
98
v->rangehigh = 108*16000;
99
v->signal = 0xffff;
100
v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
101
v->capability = V4L2_TUNER_CAP_LOW;
102
v->audmode = V4L2_TUNER_MODE_MONO;
103
return 0;
104
}
105
106
static int vidioc_s_tuner(struct file *file, void *priv,
107
struct v4l2_tuner *v)
108
{
109
return v->index ? -EINVAL : 0;
110
}
111
112
static int vidioc_g_frequency(struct file *file, void *priv,
113
struct v4l2_frequency *f)
114
{
115
struct pcm20 *dev = video_drvdata(file);
116
117
if (f->tuner != 0)
118
return -EINVAL;
119
120
f->type = V4L2_TUNER_RADIO;
121
f->frequency = dev->freq;
122
return 0;
123
}
124
125
126
static int vidioc_s_frequency(struct file *file, void *priv,
127
struct v4l2_frequency *f)
128
{
129
struct pcm20 *dev = video_drvdata(file);
130
131
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
132
return -EINVAL;
133
134
dev->freq = f->frequency;
135
pcm20_setfreq(dev, f->frequency);
136
return 0;
137
}
138
139
static int vidioc_queryctrl(struct file *file, void *priv,
140
struct v4l2_queryctrl *qc)
141
{
142
switch (qc->id) {
143
case V4L2_CID_AUDIO_MUTE:
144
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
145
}
146
return -EINVAL;
147
}
148
149
static int vidioc_g_ctrl(struct file *file, void *priv,
150
struct v4l2_control *ctrl)
151
{
152
struct pcm20 *dev = video_drvdata(file);
153
154
switch (ctrl->id) {
155
case V4L2_CID_AUDIO_MUTE:
156
ctrl->value = dev->muted;
157
break;
158
default:
159
return -EINVAL;
160
}
161
return 0;
162
}
163
164
static int vidioc_s_ctrl(struct file *file, void *priv,
165
struct v4l2_control *ctrl)
166
{
167
struct pcm20 *dev = video_drvdata(file);
168
169
switch (ctrl->id) {
170
case V4L2_CID_AUDIO_MUTE:
171
pcm20_mute(dev, ctrl->value);
172
break;
173
default:
174
return -EINVAL;
175
}
176
return 0;
177
}
178
179
static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
180
{
181
*i = 0;
182
return 0;
183
}
184
185
static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
186
{
187
return i ? -EINVAL : 0;
188
}
189
190
static int vidioc_g_audio(struct file *file, void *priv,
191
struct v4l2_audio *a)
192
{
193
a->index = 0;
194
strlcpy(a->name, "Radio", sizeof(a->name));
195
a->capability = V4L2_AUDCAP_STEREO;
196
return 0;
197
}
198
199
static int vidioc_s_audio(struct file *file, void *priv,
200
struct v4l2_audio *a)
201
{
202
return a->index ? -EINVAL : 0;
203
}
204
205
static const struct v4l2_ioctl_ops pcm20_ioctl_ops = {
206
.vidioc_querycap = vidioc_querycap,
207
.vidioc_g_tuner = vidioc_g_tuner,
208
.vidioc_s_tuner = vidioc_s_tuner,
209
.vidioc_g_frequency = vidioc_g_frequency,
210
.vidioc_s_frequency = vidioc_s_frequency,
211
.vidioc_queryctrl = vidioc_queryctrl,
212
.vidioc_g_ctrl = vidioc_g_ctrl,
213
.vidioc_s_ctrl = vidioc_s_ctrl,
214
.vidioc_g_audio = vidioc_g_audio,
215
.vidioc_s_audio = vidioc_s_audio,
216
.vidioc_g_input = vidioc_g_input,
217
.vidioc_s_input = vidioc_s_input,
218
};
219
220
static int __init pcm20_init(void)
221
{
222
struct pcm20 *dev = &pcm20_card;
223
struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
224
int res;
225
226
dev->aci = snd_aci_get_aci();
227
if (dev->aci == NULL) {
228
v4l2_err(v4l2_dev,
229
"you must load the snd-miro driver first!\n");
230
return -ENODEV;
231
}
232
strlcpy(v4l2_dev->name, "miropcm20", sizeof(v4l2_dev->name));
233
mutex_init(&dev->lock);
234
235
res = v4l2_device_register(NULL, v4l2_dev);
236
if (res < 0) {
237
v4l2_err(v4l2_dev, "could not register v4l2_device\n");
238
return -EINVAL;
239
}
240
241
strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
242
dev->vdev.v4l2_dev = v4l2_dev;
243
dev->vdev.fops = &pcm20_fops;
244
dev->vdev.ioctl_ops = &pcm20_ioctl_ops;
245
dev->vdev.release = video_device_release_empty;
246
dev->vdev.lock = &dev->lock;
247
video_set_drvdata(&dev->vdev, dev);
248
249
if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
250
goto fail;
251
252
v4l2_info(v4l2_dev, "Mirosound PCM20 Radio tuner\n");
253
return 0;
254
fail:
255
v4l2_device_unregister(v4l2_dev);
256
return -EINVAL;
257
}
258
259
MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt");
260
MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
261
MODULE_LICENSE("GPL");
262
263
static void __exit pcm20_cleanup(void)
264
{
265
struct pcm20 *dev = &pcm20_card;
266
267
video_unregister_device(&dev->vdev);
268
v4l2_device_unregister(&dev->v4l2_dev);
269
}
270
271
module_init(pcm20_init);
272
module_exit(pcm20_cleanup);
273
274