OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [media/] [radio/] [radio-zoltrix.c] - Blame information for rev 78

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/* zoltrix radio plus driver for Linux radio support
2
 * (c) 1998 C. van Schaik <carl@leg.uct.ac.za>
3
 *
4
 * BUGS
5
 *  Due to the inconsistency in reading from the signal flags
6
 *  it is difficult to get an accurate tuned signal.
7
 *
8
 *  It seems that the card is not linear to 0 volume. It cuts off
9
 *  at a low volume, and it is not possible (at least I have not found)
10
 *  to get fine volume control over the low volume range.
11
 *
12
 *  Some code derived from code by Romolo Manfredini
13
 *                                 romolo@bicnet.it
14
 *
15
 * 1999-05-06 - (C. van Schaik)
16
 *            - Make signal strength and stereo scans
17
 *              kinder to cpu while in delay
18
 * 1999-01-05 - (C. van Schaik)
19
 *            - Changed tuning to 1/160Mhz accuracy
20
 *            - Added stereo support
21
 *              (card defaults to stereo)
22
 *              (can explicitly force mono on the card)
23
 *              (can detect if station is in stereo)
24
 *            - Added unmute function
25
 *            - Reworked ioctl functions
26
 * 2002-07-15 - Fix Stereo typo
27
 *
28
 * 2006-07-24 - Converted to V4L2 API
29
 *              by Mauro Carvalho Chehab <mchehab@infradead.org>
30
 */
31
 
32
#include <linux/module.h>       /* Modules                        */
33
#include <linux/init.h>         /* Initdata                       */
34
#include <linux/ioport.h>       /* request_region                 */
35
#include <linux/delay.h>        /* udelay, msleep                 */
36
#include <asm/io.h>             /* outb, outb_p                   */
37
#include <asm/uaccess.h>        /* copy to/from user              */
38
#include <linux/videodev2.h>    /* kernel radio structs           */
39
#include <media/v4l2-common.h>
40
 
41
#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
42
#define RADIO_VERSION KERNEL_VERSION(0,0,2)
43
 
44
static struct v4l2_queryctrl radio_qctrl[] = {
45
        {
46
                .id            = V4L2_CID_AUDIO_MUTE,
47
                .name          = "Mute",
48
                .minimum       = 0,
49
                .maximum       = 1,
50
                .default_value = 1,
51
                .type          = V4L2_CTRL_TYPE_BOOLEAN,
52
        },{
53
                .id            = V4L2_CID_AUDIO_VOLUME,
54
                .name          = "Volume",
55
                .minimum       = 0,
56
                .maximum       = 65535,
57
                .step          = 4096,
58
                .default_value = 0xff,
59
                .type          = V4L2_CTRL_TYPE_INTEGER,
60
        }
61
};
62
 
63
#ifndef CONFIG_RADIO_ZOLTRIX_PORT
64
#define CONFIG_RADIO_ZOLTRIX_PORT -1
65
#endif
66
 
67
static int io = CONFIG_RADIO_ZOLTRIX_PORT;
68
static int radio_nr = -1;
69
 
70
struct zol_device {
71
        int port;
72
        int curvol;
73
        unsigned long curfreq;
74
        int muted;
75
        unsigned int stereo;
76
        struct mutex lock;
77
};
78
 
79
static int zol_setvol(struct zol_device *dev, int vol)
80
{
81
        dev->curvol = vol;
82
        if (dev->muted)
83
                return 0;
84
 
85
        mutex_lock(&dev->lock);
86
        if (vol == 0) {
87
                outb(0, io);
88
                outb(0, io);
89
                inb(io + 3);    /* Zoltrix needs to be read to confirm */
90
                mutex_unlock(&dev->lock);
91
                return 0;
92
        }
93
 
94
        outb(dev->curvol-1, io);
95
        msleep(10);
96
        inb(io + 2);
97
        mutex_unlock(&dev->lock);
98
        return 0;
99
}
100
 
101
static void zol_mute(struct zol_device *dev)
102
{
103
        dev->muted = 1;
104
        mutex_lock(&dev->lock);
105
        outb(0, io);
106
        outb(0, io);
107
        inb(io + 3);            /* Zoltrix needs to be read to confirm */
108
        mutex_unlock(&dev->lock);
109
}
110
 
111
static void zol_unmute(struct zol_device *dev)
112
{
113
        dev->muted = 0;
114
        zol_setvol(dev, dev->curvol);
115
}
116
 
117
static int zol_setfreq(struct zol_device *dev, unsigned long freq)
118
{
119
        /* tunes the radio to the desired frequency */
120
        unsigned long long bitmask, f, m;
121
        unsigned int stereo = dev->stereo;
122
        int i;
123
 
124
        if (freq == 0)
125
                return 1;
126
        m = (freq / 160 - 8800) * 2;
127
        f = (unsigned long long) m + 0x4d1c;
128
 
129
        bitmask = 0xc480402c10080000ull;
130
        i = 45;
131
 
132
        mutex_lock(&dev->lock);
133
 
134
        outb(0, io);
135
        outb(0, io);
136
        inb(io + 3);            /* Zoltrix needs to be read to confirm */
137
 
138
        outb(0x40, io);
139
        outb(0xc0, io);
140
 
141
        bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ ( stereo << 31));
142
        while (i--) {
143
                if ((bitmask & 0x8000000000000000ull) != 0) {
144
                        outb(0x80, io);
145
                        udelay(50);
146
                        outb(0x00, io);
147
                        udelay(50);
148
                        outb(0x80, io);
149
                        udelay(50);
150
                } else {
151
                        outb(0xc0, io);
152
                        udelay(50);
153
                        outb(0x40, io);
154
                        udelay(50);
155
                        outb(0xc0, io);
156
                        udelay(50);
157
                }
158
                bitmask *= 2;
159
        }
160
        /* termination sequence */
161
        outb(0x80, io);
162
        outb(0xc0, io);
163
        outb(0x40, io);
164
        udelay(1000);
165
        inb(io+2);
166
 
167
        udelay(1000);
168
 
169
        if (dev->muted)
170
        {
171
                outb(0, io);
172
                outb(0, io);
173
                inb(io + 3);
174
                udelay(1000);
175
        }
176
 
177
        mutex_unlock(&dev->lock);
178
 
179
        if(!dev->muted)
180
        {
181
                zol_setvol(dev, dev->curvol);
182
        }
183
        return 0;
184
}
185
 
186
/* Get signal strength */
187
 
188
static int zol_getsigstr(struct zol_device *dev)
189
{
190
        int a, b;
191
 
192
        mutex_lock(&dev->lock);
193
        outb(0x00, io);         /* This stuff I found to do nothing */
194
        outb(dev->curvol, io);
195
        msleep(20);
196
 
197
        a = inb(io);
198
        msleep(10);
199
        b = inb(io);
200
 
201
        mutex_unlock(&dev->lock);
202
 
203
        if (a != b)
204
                return (0);
205
 
206
        if ((a == 0xcf) || (a == 0xdf)  /* I found this out by playing */
207
                || (a == 0xef))       /* with a binary scanner on the card io */
208
                return (1);
209
        return (0);
210
}
211
 
212
static int zol_is_stereo (struct zol_device *dev)
213
{
214
        int x1, x2;
215
 
216
        mutex_lock(&dev->lock);
217
 
218
        outb(0x00, io);
219
        outb(dev->curvol, io);
220
        msleep(20);
221
 
222
        x1 = inb(io);
223
        msleep(10);
224
        x2 = inb(io);
225
 
226
        mutex_unlock(&dev->lock);
227
 
228
        if ((x1 == x2) && (x1 == 0xcf))
229
                return 1;
230
        return 0;
231
}
232
 
233
static int vidioc_querycap(struct file *file, void  *priv,
234
                                        struct v4l2_capability *v)
235
{
236
        strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver));
237
        strlcpy(v->card, "Zoltrix Radio", sizeof(v->card));
238
        sprintf(v->bus_info, "ISA");
239
        v->version = RADIO_VERSION;
240
        v->capabilities = V4L2_CAP_TUNER;
241
        return 0;
242
}
243
 
244
static int vidioc_g_tuner(struct file *file, void *priv,
245
                                        struct v4l2_tuner *v)
246
{
247
        struct video_device *dev = video_devdata(file);
248
        struct zol_device *zol = dev->priv;
249
 
250
        if (v->index > 0)
251
                return -EINVAL;
252
 
253
        strcpy(v->name, "FM");
254
        v->type = V4L2_TUNER_RADIO;
255
        v->rangelow = (88*16000);
256
        v->rangehigh = (108*16000);
257
        v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
258
        v->capability = V4L2_TUNER_CAP_LOW;
259
        if (zol_is_stereo(zol))
260
                v->audmode = V4L2_TUNER_MODE_STEREO;
261
        else
262
                v->audmode = V4L2_TUNER_MODE_MONO;
263
        v->signal = 0xFFFF*zol_getsigstr(zol);
264
        return 0;
265
}
266
 
267
static int vidioc_s_tuner(struct file *file, void *priv,
268
                                        struct v4l2_tuner *v)
269
{
270
        if (v->index > 0)
271
                return -EINVAL;
272
        return 0;
273
}
274
 
275
static int vidioc_s_frequency(struct file *file, void *priv,
276
                                        struct v4l2_frequency *f)
277
{
278
        struct video_device *dev = video_devdata(file);
279
        struct zol_device *zol = dev->priv;
280
 
281
        zol->curfreq = f->frequency;
282
        zol_setfreq(zol, zol->curfreq);
283
        return 0;
284
}
285
 
286
static int vidioc_g_frequency(struct file *file, void *priv,
287
                                        struct v4l2_frequency *f)
288
{
289
        struct video_device *dev = video_devdata(file);
290
        struct zol_device *zol = dev->priv;
291
 
292
        f->type = V4L2_TUNER_RADIO;
293
        f->frequency = zol->curfreq;
294
        return 0;
295
}
296
 
297
static int vidioc_queryctrl(struct file *file, void *priv,
298
                                        struct v4l2_queryctrl *qc)
299
{
300
        int i;
301
 
302
        for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
303
                if (qc->id && qc->id == radio_qctrl[i].id) {
304
                        memcpy(qc, &(radio_qctrl[i]),
305
                                                sizeof(*qc));
306
                        return 0;
307
                }
308
        }
309
        return -EINVAL;
310
}
311
 
312
static int vidioc_g_ctrl(struct file *file, void *priv,
313
                                struct v4l2_control *ctrl)
314
{
315
        struct video_device *dev = video_devdata(file);
316
        struct zol_device *zol = dev->priv;
317
 
318
        switch (ctrl->id) {
319
        case V4L2_CID_AUDIO_MUTE:
320
                ctrl->value = zol->muted;
321
                return 0;
322
        case V4L2_CID_AUDIO_VOLUME:
323
                ctrl->value = zol->curvol * 4096;
324
                return 0;
325
        }
326
        return -EINVAL;
327
}
328
 
329
static int vidioc_s_ctrl(struct file *file, void *priv,
330
                                struct v4l2_control *ctrl)
331
{
332
        struct video_device *dev = video_devdata(file);
333
        struct zol_device *zol = dev->priv;
334
 
335
        switch (ctrl->id) {
336
        case V4L2_CID_AUDIO_MUTE:
337
                if (ctrl->value)
338
                        zol_mute(zol);
339
                else {
340
                        zol_unmute(zol);
341
                        zol_setvol(zol,zol->curvol);
342
                }
343
                return 0;
344
        case V4L2_CID_AUDIO_VOLUME:
345
                zol_setvol(zol,ctrl->value/4096);
346
                return 0;
347
        }
348
        zol->stereo = 1;
349
        zol_setfreq(zol, zol->curfreq);
350
#if 0
351
/* FIXME: Implement stereo/mono switch on V4L2 */
352
                        if (v->mode & VIDEO_SOUND_STEREO) {
353
                                zol->stereo = 1;
354
                                zol_setfreq(zol, zol->curfreq);
355
                        }
356
                        if (v->mode & VIDEO_SOUND_MONO) {
357
                                zol->stereo = 0;
358
                                zol_setfreq(zol, zol->curfreq);
359
                        }
360
#endif
361
        return -EINVAL;
362
}
363
 
364
static int vidioc_g_audio(struct file *file, void *priv,
365
                                        struct v4l2_audio *a)
366
{
367
        if (a->index > 1)
368
                return -EINVAL;
369
 
370
        strcpy(a->name, "Radio");
371
        a->capability = V4L2_AUDCAP_STEREO;
372
        return 0;
373
}
374
 
375
static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
376
{
377
        *i = 0;
378
        return 0;
379
}
380
 
381
static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
382
{
383
        if (i != 0)
384
                return -EINVAL;
385
        return 0;
386
}
387
 
388
static int vidioc_s_audio(struct file *file, void *priv,
389
                                        struct v4l2_audio *a)
390
{
391
        if (a->index != 0)
392
                return -EINVAL;
393
        return 0;
394
}
395
 
396
static struct zol_device zoltrix_unit;
397
 
398
static const struct file_operations zoltrix_fops =
399
{
400
        .owner          = THIS_MODULE,
401
        .open           = video_exclusive_open,
402
        .release        = video_exclusive_release,
403
        .ioctl          = video_ioctl2,
404
        .compat_ioctl   = v4l_compat_ioctl32,
405
        .llseek         = no_llseek,
406
};
407
 
408
static struct video_device zoltrix_radio =
409
{
410
        .owner          = THIS_MODULE,
411
        .name           = "Zoltrix Radio Plus",
412
        .type           = VID_TYPE_TUNER,
413
        .fops           = &zoltrix_fops,
414
        .vidioc_querycap    = vidioc_querycap,
415
        .vidioc_g_tuner     = vidioc_g_tuner,
416
        .vidioc_s_tuner     = vidioc_s_tuner,
417
        .vidioc_g_audio     = vidioc_g_audio,
418
        .vidioc_s_audio     = vidioc_s_audio,
419
        .vidioc_g_input     = vidioc_g_input,
420
        .vidioc_s_input     = vidioc_s_input,
421
        .vidioc_g_frequency = vidioc_g_frequency,
422
        .vidioc_s_frequency = vidioc_s_frequency,
423
        .vidioc_queryctrl   = vidioc_queryctrl,
424
        .vidioc_g_ctrl      = vidioc_g_ctrl,
425
        .vidioc_s_ctrl      = vidioc_s_ctrl,
426
};
427
 
428
static int __init zoltrix_init(void)
429
{
430
        if (io == -1) {
431
                printk(KERN_ERR "You must set an I/O address with io=0x???\n");
432
                return -EINVAL;
433
        }
434
        if ((io != 0x20c) && (io != 0x30c)) {
435
                printk(KERN_ERR "zoltrix: invalid port, try 0x20c or 0x30c\n");
436
                return -ENXIO;
437
        }
438
 
439
        zoltrix_radio.priv = &zoltrix_unit;
440
        if (!request_region(io, 2, "zoltrix")) {
441
                printk(KERN_ERR "zoltrix: port 0x%x already in use\n", io);
442
                return -EBUSY;
443
        }
444
 
445
        if (video_register_device(&zoltrix_radio, VFL_TYPE_RADIO, radio_nr) == -1)
446
        {
447
                release_region(io, 2);
448
                return -EINVAL;
449
        }
450
        printk(KERN_INFO "Zoltrix Radio Plus card driver.\n");
451
 
452
        mutex_init(&zoltrix_unit.lock);
453
 
454
        /* mute card - prevents noisy bootups */
455
 
456
        /* this ensures that the volume is all the way down  */
457
 
458
        outb(0, io);
459
        outb(0, io);
460
        msleep(20);
461
        inb(io + 3);
462
 
463
        zoltrix_unit.curvol = 0;
464
        zoltrix_unit.stereo = 1;
465
 
466
        return 0;
467
}
468
 
469
MODULE_AUTHOR("C.van Schaik");
470
MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
471
MODULE_LICENSE("GPL");
472
 
473
module_param(io, int, 0);
474
MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
475
module_param(radio_nr, int, 0);
476
 
477
static void __exit zoltrix_cleanup_module(void)
478
{
479
        video_unregister_device(&zoltrix_radio);
480
        release_region(io, 2);
481
}
482
 
483
module_init(zoltrix_init);
484
module_exit(zoltrix_cleanup_module);
485
 

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.