Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/media/radio/radio-sf16fmi.c
15112 views
1
/* SF16-FMI and SF16-FMP radio driver for Linux radio support
2
* heavily based on rtrack driver...
3
* (c) 1997 M. Kirkwood
4
* (c) 1998 Petr Vandrovec, [email protected]
5
*
6
* Fitted to new interface by Alan Cox <[email protected]>
7
* Made working and cleaned up functions <[email protected]>
8
* Support for ISAPnP by Ladislav Michl <[email protected]>
9
*
10
* Notes on the hardware
11
*
12
* Frequency control is done digitally -- ie out(port,encodefreq(95.8));
13
* No volume control - only mute/unmute - you have to use line volume
14
* control on SB-part of SF16-FMI/SF16-FMP
15
*
16
* Converted to V4L2 API by Mauro Carvalho Chehab <[email protected]>
17
*/
18
19
#include <linux/version.h>
20
#include <linux/kernel.h> /* __setup */
21
#include <linux/module.h> /* Modules */
22
#include <linux/init.h> /* Initdata */
23
#include <linux/ioport.h> /* request_region */
24
#include <linux/delay.h> /* udelay */
25
#include <linux/isapnp.h>
26
#include <linux/mutex.h>
27
#include <linux/videodev2.h> /* kernel radio structs */
28
#include <linux/io.h> /* outb, outb_p */
29
#include <media/v4l2-device.h>
30
#include <media/v4l2-ioctl.h>
31
32
MODULE_AUTHOR("Petr Vandrovec, [email protected] and M. Kirkwood");
33
MODULE_DESCRIPTION("A driver for the SF16-FMI and SF16-FMP radio.");
34
MODULE_LICENSE("GPL");
35
36
static int io = -1;
37
static int radio_nr = -1;
38
39
module_param(io, int, 0);
40
MODULE_PARM_DESC(io, "I/O address of the SF16-FMI or SF16-FMP card (0x284 or 0x384)");
41
module_param(radio_nr, int, 0);
42
43
#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
44
45
struct fmi
46
{
47
struct v4l2_device v4l2_dev;
48
struct video_device vdev;
49
int io;
50
bool mute;
51
unsigned long curfreq; /* freq in kHz */
52
struct mutex lock;
53
};
54
55
static struct fmi fmi_card;
56
static struct pnp_dev *dev;
57
bool pnp_attached;
58
59
/* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
60
/* It is only useful to give freq in interval of 800 (=0.05Mhz),
61
* other bits will be truncated, e.g 92.7400016 -> 92.7, but
62
* 92.7400017 -> 92.75
63
*/
64
#define RSF16_ENCODE(x) ((x) / 800 + 214)
65
#define RSF16_MINFREQ (87 * 16000)
66
#define RSF16_MAXFREQ (108 * 16000)
67
68
static void outbits(int bits, unsigned int data, int io)
69
{
70
while (bits--) {
71
if (data & 1) {
72
outb(5, io);
73
udelay(6);
74
outb(7, io);
75
udelay(6);
76
} else {
77
outb(1, io);
78
udelay(6);
79
outb(3, io);
80
udelay(6);
81
}
82
data >>= 1;
83
}
84
}
85
86
static inline void fmi_mute(struct fmi *fmi)
87
{
88
mutex_lock(&fmi->lock);
89
outb(0x00, fmi->io);
90
mutex_unlock(&fmi->lock);
91
}
92
93
static inline void fmi_unmute(struct fmi *fmi)
94
{
95
mutex_lock(&fmi->lock);
96
outb(0x08, fmi->io);
97
mutex_unlock(&fmi->lock);
98
}
99
100
static inline int fmi_setfreq(struct fmi *fmi, unsigned long freq)
101
{
102
mutex_lock(&fmi->lock);
103
fmi->curfreq = freq;
104
105
outbits(16, RSF16_ENCODE(freq), fmi->io);
106
outbits(8, 0xC0, fmi->io);
107
msleep(143); /* was schedule_timeout(HZ/7) */
108
mutex_unlock(&fmi->lock);
109
if (!fmi->mute)
110
fmi_unmute(fmi);
111
return 0;
112
}
113
114
static inline int fmi_getsigstr(struct fmi *fmi)
115
{
116
int val;
117
int res;
118
119
mutex_lock(&fmi->lock);
120
val = fmi->mute ? 0x00 : 0x08; /* mute/unmute */
121
outb(val, fmi->io);
122
outb(val | 0x10, fmi->io);
123
msleep(143); /* was schedule_timeout(HZ/7) */
124
res = (int)inb(fmi->io + 1);
125
outb(val, fmi->io);
126
127
mutex_unlock(&fmi->lock);
128
return (res & 2) ? 0 : 0xFFFF;
129
}
130
131
static int vidioc_querycap(struct file *file, void *priv,
132
struct v4l2_capability *v)
133
{
134
strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver));
135
strlcpy(v->card, "SF16-FMx radio", sizeof(v->card));
136
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
137
v->version = RADIO_VERSION;
138
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
139
return 0;
140
}
141
142
static int vidioc_g_tuner(struct file *file, void *priv,
143
struct v4l2_tuner *v)
144
{
145
struct fmi *fmi = video_drvdata(file);
146
147
if (v->index > 0)
148
return -EINVAL;
149
150
strlcpy(v->name, "FM", sizeof(v->name));
151
v->type = V4L2_TUNER_RADIO;
152
v->rangelow = RSF16_MINFREQ;
153
v->rangehigh = RSF16_MAXFREQ;
154
v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
155
v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
156
v->audmode = V4L2_TUNER_MODE_STEREO;
157
v->signal = fmi_getsigstr(fmi);
158
return 0;
159
}
160
161
static int vidioc_s_tuner(struct file *file, void *priv,
162
struct v4l2_tuner *v)
163
{
164
return v->index ? -EINVAL : 0;
165
}
166
167
static int vidioc_s_frequency(struct file *file, void *priv,
168
struct v4l2_frequency *f)
169
{
170
struct fmi *fmi = video_drvdata(file);
171
172
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
173
return -EINVAL;
174
if (f->frequency < RSF16_MINFREQ ||
175
f->frequency > RSF16_MAXFREQ)
176
return -EINVAL;
177
/* rounding in steps of 800 to match the freq
178
that will be used */
179
fmi_setfreq(fmi, (f->frequency / 800) * 800);
180
return 0;
181
}
182
183
static int vidioc_g_frequency(struct file *file, void *priv,
184
struct v4l2_frequency *f)
185
{
186
struct fmi *fmi = video_drvdata(file);
187
188
if (f->tuner != 0)
189
return -EINVAL;
190
f->type = V4L2_TUNER_RADIO;
191
f->frequency = fmi->curfreq;
192
return 0;
193
}
194
195
static int vidioc_queryctrl(struct file *file, void *priv,
196
struct v4l2_queryctrl *qc)
197
{
198
switch (qc->id) {
199
case V4L2_CID_AUDIO_MUTE:
200
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
201
}
202
return -EINVAL;
203
}
204
205
static int vidioc_g_ctrl(struct file *file, void *priv,
206
struct v4l2_control *ctrl)
207
{
208
struct fmi *fmi = video_drvdata(file);
209
210
switch (ctrl->id) {
211
case V4L2_CID_AUDIO_MUTE:
212
ctrl->value = fmi->mute;
213
return 0;
214
}
215
return -EINVAL;
216
}
217
218
static int vidioc_s_ctrl(struct file *file, void *priv,
219
struct v4l2_control *ctrl)
220
{
221
struct fmi *fmi = video_drvdata(file);
222
223
switch (ctrl->id) {
224
case V4L2_CID_AUDIO_MUTE:
225
if (ctrl->value)
226
fmi_mute(fmi);
227
else
228
fmi_unmute(fmi);
229
fmi->mute = ctrl->value;
230
return 0;
231
}
232
return -EINVAL;
233
}
234
235
static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
236
{
237
*i = 0;
238
return 0;
239
}
240
241
static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
242
{
243
return i ? -EINVAL : 0;
244
}
245
246
static int vidioc_g_audio(struct file *file, void *priv,
247
struct v4l2_audio *a)
248
{
249
a->index = 0;
250
strlcpy(a->name, "Radio", sizeof(a->name));
251
a->capability = V4L2_AUDCAP_STEREO;
252
return 0;
253
}
254
255
static int vidioc_s_audio(struct file *file, void *priv,
256
struct v4l2_audio *a)
257
{
258
return a->index ? -EINVAL : 0;
259
}
260
261
static const struct v4l2_file_operations fmi_fops = {
262
.owner = THIS_MODULE,
263
.unlocked_ioctl = video_ioctl2,
264
};
265
266
static const struct v4l2_ioctl_ops fmi_ioctl_ops = {
267
.vidioc_querycap = vidioc_querycap,
268
.vidioc_g_tuner = vidioc_g_tuner,
269
.vidioc_s_tuner = vidioc_s_tuner,
270
.vidioc_g_audio = vidioc_g_audio,
271
.vidioc_s_audio = vidioc_s_audio,
272
.vidioc_g_input = vidioc_g_input,
273
.vidioc_s_input = vidioc_s_input,
274
.vidioc_g_frequency = vidioc_g_frequency,
275
.vidioc_s_frequency = vidioc_s_frequency,
276
.vidioc_queryctrl = vidioc_queryctrl,
277
.vidioc_g_ctrl = vidioc_g_ctrl,
278
.vidioc_s_ctrl = vidioc_s_ctrl,
279
};
280
281
/* ladis: this is my card. does any other types exist? */
282
static struct isapnp_device_id id_table[] __devinitdata = {
283
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,
284
ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0},
285
{ ISAPNP_CARD_END, },
286
};
287
288
MODULE_DEVICE_TABLE(isapnp, id_table);
289
290
static int __init isapnp_fmi_probe(void)
291
{
292
int i = 0;
293
294
while (id_table[i].card_vendor != 0 && dev == NULL) {
295
dev = pnp_find_dev(NULL, id_table[i].vendor,
296
id_table[i].function, NULL);
297
i++;
298
}
299
300
if (!dev)
301
return -ENODEV;
302
if (pnp_device_attach(dev) < 0)
303
return -EAGAIN;
304
if (pnp_activate_dev(dev) < 0) {
305
printk(KERN_ERR "radio-sf16fmi: PnP configure failed (out of resources?)\n");
306
pnp_device_detach(dev);
307
return -ENOMEM;
308
}
309
if (!pnp_port_valid(dev, 0)) {
310
pnp_device_detach(dev);
311
return -ENODEV;
312
}
313
314
i = pnp_port_start(dev, 0);
315
printk(KERN_INFO "radio-sf16fmi: PnP reports card at %#x\n", i);
316
317
return i;
318
}
319
320
static int __init fmi_init(void)
321
{
322
struct fmi *fmi = &fmi_card;
323
struct v4l2_device *v4l2_dev = &fmi->v4l2_dev;
324
int res, i;
325
int probe_ports[] = { 0, 0x284, 0x384 };
326
327
if (io < 0) {
328
for (i = 0; i < ARRAY_SIZE(probe_ports); i++) {
329
io = probe_ports[i];
330
if (io == 0) {
331
io = isapnp_fmi_probe();
332
if (io < 0)
333
continue;
334
pnp_attached = 1;
335
}
336
if (!request_region(io, 2, "radio-sf16fmi")) {
337
if (pnp_attached)
338
pnp_device_detach(dev);
339
io = -1;
340
continue;
341
}
342
if (pnp_attached ||
343
((inb(io) & 0xf9) == 0xf9 && (inb(io) & 0x4) == 0))
344
break;
345
release_region(io, 2);
346
io = -1;
347
}
348
} else {
349
if (!request_region(io, 2, "radio-sf16fmi")) {
350
printk(KERN_ERR "radio-sf16fmi: port %#x already in use\n", io);
351
return -EBUSY;
352
}
353
if (inb(io) == 0xff) {
354
printk(KERN_ERR "radio-sf16fmi: card not present at %#x\n", io);
355
release_region(io, 2);
356
return -ENODEV;
357
}
358
}
359
if (io < 0) {
360
printk(KERN_ERR "radio-sf16fmi: no cards found\n");
361
return -ENODEV;
362
}
363
364
strlcpy(v4l2_dev->name, "sf16fmi", sizeof(v4l2_dev->name));
365
fmi->io = io;
366
367
res = v4l2_device_register(NULL, v4l2_dev);
368
if (res < 0) {
369
release_region(fmi->io, 2);
370
if (pnp_attached)
371
pnp_device_detach(dev);
372
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
373
return res;
374
}
375
376
strlcpy(fmi->vdev.name, v4l2_dev->name, sizeof(fmi->vdev.name));
377
fmi->vdev.v4l2_dev = v4l2_dev;
378
fmi->vdev.fops = &fmi_fops;
379
fmi->vdev.ioctl_ops = &fmi_ioctl_ops;
380
fmi->vdev.release = video_device_release_empty;
381
video_set_drvdata(&fmi->vdev, fmi);
382
383
mutex_init(&fmi->lock);
384
385
/* mute card - prevents noisy bootups */
386
fmi_mute(fmi);
387
388
if (video_register_device(&fmi->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
389
v4l2_device_unregister(v4l2_dev);
390
release_region(fmi->io, 2);
391
if (pnp_attached)
392
pnp_device_detach(dev);
393
return -EINVAL;
394
}
395
396
v4l2_info(v4l2_dev, "card driver at 0x%x\n", fmi->io);
397
return 0;
398
}
399
400
static void __exit fmi_exit(void)
401
{
402
struct fmi *fmi = &fmi_card;
403
404
video_unregister_device(&fmi->vdev);
405
v4l2_device_unregister(&fmi->v4l2_dev);
406
release_region(fmi->io, 2);
407
if (dev && pnp_attached)
408
pnp_device_detach(dev);
409
}
410
411
module_init(fmi_init);
412
module_exit(fmi_exit);
413
414