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-cadet.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
/* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card
2
 *
3
 * by Fred Gleason <fredg@wava.com>
4
 * Version 0.3.3
5
 *
6
 * (Loosely) based on code for the Aztech radio card by
7
 *
8
 * Russell Kroll    (rkroll@exploits.org)
9
 * Quay Ly
10
 * Donald Song
11
 * Jason Lewis      (jlewis@twilight.vtc.vsc.edu)
12
 * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
13
 * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
14
 *
15
 * History:
16
 * 2000-04-29   Russell Kroll <rkroll@exploits.org>
17
 *              Added ISAPnP detection for Linux 2.3/2.4
18
 *
19
 * 2001-01-10   Russell Kroll <rkroll@exploits.org>
20
 *              Removed dead CONFIG_RADIO_CADET_PORT code
21
 *              PnP detection on load is now default (no args necessary)
22
 *
23
 * 2002-01-17   Adam Belay <ambx1@neo.rr.com>
24
 *              Updated to latest pnp code
25
 *
26
 * 2003-01-31   Alan Cox <alan@redhat.com>
27
 *              Cleaned up locking, delay code, general odds and ends
28
 *
29
 * 2006-07-30   Hans J. Koch <koch@hjk-az.de>
30
 *              Changed API to V4L2
31
 */
32
 
33
#include <linux/version.h>
34
#include <linux/module.h>       /* Modules                      */
35
#include <linux/init.h>         /* Initdata                     */
36
#include <linux/ioport.h>       /* request_region               */
37
#include <linux/delay.h>        /* udelay                       */
38
#include <asm/io.h>             /* outb, outb_p                 */
39
#include <asm/uaccess.h>        /* copy to/from user            */
40
#include <linux/videodev2.h>    /* V4L2 API defs                */
41
#include <media/v4l2-common.h>
42
#include <linux/param.h>
43
#include <linux/pnp.h>
44
 
45
#define RDS_BUFFER 256
46
#define RDS_RX_FLAG 1
47
#define MBS_RX_FLAG 2
48
 
49
#define CADET_VERSION KERNEL_VERSION(0,3,3)
50
 
51
static struct v4l2_queryctrl radio_qctrl[] = {
52
        {
53
                .id            = V4L2_CID_AUDIO_MUTE,
54
                .name          = "Mute",
55
                .minimum       = 0,
56
                .maximum       = 1,
57
                .default_value = 1,
58
                .type          = V4L2_CTRL_TYPE_BOOLEAN,
59
        },{
60
                .id            = V4L2_CID_AUDIO_VOLUME,
61
                .name          = "Volume",
62
                .minimum       = 0,
63
                .maximum       = 0xff,
64
                .step          = 1,
65
                .default_value = 0xff,
66
                .type          = V4L2_CTRL_TYPE_INTEGER,
67
        }
68
};
69
 
70
static int io=-1;               /* default to isapnp activation */
71
static int radio_nr = -1;
72
static int users=0;
73
static int curtuner=0;
74
static int tunestat=0;
75
static int sigstrength=0;
76
static wait_queue_head_t read_queue;
77
static struct timer_list readtimer;
78
static __u8 rdsin=0,rdsout=0,rdsstat=0;
79
static unsigned char rdsbuf[RDS_BUFFER];
80
static spinlock_t cadet_io_lock;
81
 
82
static int cadet_probe(void);
83
 
84
/*
85
 * Signal Strength Threshold Values
86
 * The V4L API spec does not define any particular unit for the signal
87
 * strength value.  These values are in microvolts of RF at the tuner's input.
88
 */
89
static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}};
90
 
91
 
92
static int
93
cadet_getstereo(void)
94
{
95
        int ret = V4L2_TUNER_SUB_MONO;
96
        if(curtuner != 0)        /* Only FM has stereo capability! */
97
                return V4L2_TUNER_SUB_MONO;
98
 
99
        spin_lock(&cadet_io_lock);
100
        outb(7,io);          /* Select tuner control */
101
        if( (inb(io+1) & 0x40) == 0)
102
                ret = V4L2_TUNER_SUB_STEREO;
103
        spin_unlock(&cadet_io_lock);
104
        return ret;
105
}
106
 
107
static unsigned
108
cadet_gettune(void)
109
{
110
        int curvol,i;
111
        unsigned fifo=0;
112
 
113
        /*
114
         * Prepare for read
115
         */
116
 
117
        spin_lock(&cadet_io_lock);
118
 
119
        outb(7,io);       /* Select tuner control */
120
        curvol=inb(io+1); /* Save current volume/mute setting */
121
        outb(0x00,io+1);  /* Ensure WRITE-ENABLE is LOW */
122
        tunestat=0xffff;
123
 
124
        /*
125
         * Read the shift register
126
         */
127
        for(i=0;i<25;i++) {
128
                fifo=(fifo<<1)|((inb(io+1)>>7)&0x01);
129
                if(i<24) {
130
                        outb(0x01,io+1);
131
                        tunestat&=inb(io+1);
132
                        outb(0x00,io+1);
133
                }
134
        }
135
 
136
        /*
137
         * Restore volume/mute setting
138
         */
139
        outb(curvol,io+1);
140
        spin_unlock(&cadet_io_lock);
141
 
142
        return fifo;
143
}
144
 
145
static unsigned
146
cadet_getfreq(void)
147
{
148
        int i;
149
        unsigned freq=0,test,fifo=0;
150
 
151
        /*
152
         * Read current tuning
153
         */
154
        fifo=cadet_gettune();
155
 
156
        /*
157
         * Convert to actual frequency
158
         */
159
        if(curtuner==0) {    /* FM */
160
                test=12500;
161
                for(i=0;i<14;i++) {
162
                        if((fifo&0x01)!=0) {
163
                                freq+=test;
164
                        }
165
                        test=test<<1;
166
                        fifo=fifo>>1;
167
                }
168
                freq-=10700000;           /* IF frequency is 10.7 MHz */
169
                freq=(freq*16)/1000000;   /* Make it 1/16 MHz */
170
        }
171
        if(curtuner==1) {    /* AM */
172
                freq=((fifo&0x7fff)-2010)*16;
173
        }
174
 
175
        return freq;
176
}
177
 
178
static void
179
cadet_settune(unsigned fifo)
180
{
181
        int i;
182
        unsigned test;
183
 
184
        spin_lock(&cadet_io_lock);
185
 
186
        outb(7,io);                /* Select tuner control */
187
        /*
188
         * Write the shift register
189
         */
190
        test=0;
191
        test=(fifo>>23)&0x02;      /* Align data for SDO */
192
        test|=0x1c;                /* SDM=1, SWE=1, SEN=1, SCK=0 */
193
        outb(7,io);                /* Select tuner control */
194
        outb(test,io+1);           /* Initialize for write */
195
        for(i=0;i<25;i++) {
196
                test|=0x01;              /* Toggle SCK High */
197
                outb(test,io+1);
198
                test&=0xfe;              /* Toggle SCK Low */
199
                outb(test,io+1);
200
                fifo=fifo<<1;            /* Prepare the next bit */
201
                test=0x1c|((fifo>>23)&0x02);
202
                outb(test,io+1);
203
        }
204
        spin_unlock(&cadet_io_lock);
205
}
206
 
207
static void
208
cadet_setfreq(unsigned freq)
209
{
210
        unsigned fifo;
211
        int i,j,test;
212
        int curvol;
213
 
214
        /*
215
         * Formulate a fifo command
216
         */
217
        fifo=0;
218
        if(curtuner==0) {    /* FM */
219
                test=102400;
220
                freq=(freq*1000)/16;       /* Make it kHz */
221
                freq+=10700;               /* IF is 10700 kHz */
222
                for(i=0;i<14;i++) {
223
                        fifo=fifo<<1;
224
                        if(freq>=test) {
225
                                fifo|=0x01;
226
                                freq-=test;
227
                        }
228
                        test=test>>1;
229
                }
230
        }
231
        if(curtuner==1) {    /* AM */
232
                fifo=(freq/16)+2010;            /* Make it kHz */
233
                fifo|=0x100000;            /* Select AM Band */
234
        }
235
 
236
        /*
237
         * Save current volume/mute setting
238
         */
239
 
240
        spin_lock(&cadet_io_lock);
241
        outb(7,io);                /* Select tuner control */
242
        curvol=inb(io+1);
243
        spin_unlock(&cadet_io_lock);
244
 
245
        /*
246
         * Tune the card
247
         */
248
        for(j=3;j>-1;j--) {
249
                cadet_settune(fifo|(j<<16));
250
 
251
                spin_lock(&cadet_io_lock);
252
                outb(7,io);         /* Select tuner control */
253
                outb(curvol,io+1);
254
                spin_unlock(&cadet_io_lock);
255
 
256
                msleep(100);
257
 
258
                cadet_gettune();
259
                if((tunestat & 0x40) == 0) {   /* Tuned */
260
                        sigstrength=sigtable[curtuner][j];
261
                        return;
262
                }
263
        }
264
        sigstrength=0;
265
}
266
 
267
 
268
static int
269
cadet_getvol(void)
270
{
271
        int ret = 0;
272
 
273
        spin_lock(&cadet_io_lock);
274
 
275
        outb(7,io);                /* Select tuner control */
276
        if((inb(io + 1) & 0x20) != 0)
277
                ret = 0xffff;
278
 
279
        spin_unlock(&cadet_io_lock);
280
        return ret;
281
}
282
 
283
 
284
static void
285
cadet_setvol(int vol)
286
{
287
        spin_lock(&cadet_io_lock);
288
        outb(7,io);                /* Select tuner control */
289
        if(vol>0)
290
                outb(0x20,io+1);
291
        else
292
                outb(0x00,io+1);
293
        spin_unlock(&cadet_io_lock);
294
}
295
 
296
static void
297
cadet_handler(unsigned long data)
298
{
299
        /*
300
         * Service the RDS fifo
301
         */
302
 
303
        if(spin_trylock(&cadet_io_lock))
304
        {
305
                outb(0x3,io);       /* Select RDS Decoder Control */
306
                if((inb(io+1)&0x20)!=0) {
307
                        printk(KERN_CRIT "cadet: RDS fifo overflow\n");
308
                }
309
                outb(0x80,io);      /* Select RDS fifo */
310
                while((inb(io)&0x80)!=0) {
311
                        rdsbuf[rdsin]=inb(io+1);
312
                        if(rdsin==rdsout)
313
                                printk(KERN_WARNING "cadet: RDS buffer overflow\n");
314
                        else
315
                                rdsin++;
316
                }
317
                spin_unlock(&cadet_io_lock);
318
        }
319
 
320
        /*
321
         * Service pending read
322
         */
323
        if( rdsin!=rdsout)
324
                wake_up_interruptible(&read_queue);
325
 
326
        /*
327
         * Clean up and exit
328
         */
329
        init_timer(&readtimer);
330
        readtimer.function=cadet_handler;
331
        readtimer.data=(unsigned long)0;
332
        readtimer.expires=jiffies+msecs_to_jiffies(50);
333
        add_timer(&readtimer);
334
}
335
 
336
 
337
 
338
static ssize_t
339
cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
340
{
341
        int i=0;
342
        unsigned char readbuf[RDS_BUFFER];
343
 
344
        if(rdsstat==0) {
345
                spin_lock(&cadet_io_lock);
346
                rdsstat=1;
347
                outb(0x80,io);        /* Select RDS fifo */
348
                spin_unlock(&cadet_io_lock);
349
                init_timer(&readtimer);
350
                readtimer.function=cadet_handler;
351
                readtimer.data=(unsigned long)0;
352
                readtimer.expires=jiffies+msecs_to_jiffies(50);
353
                add_timer(&readtimer);
354
        }
355
        if(rdsin==rdsout) {
356
                if (file->f_flags & O_NONBLOCK)
357
                        return -EWOULDBLOCK;
358
                interruptible_sleep_on(&read_queue);
359
        }
360
        while( i<count && rdsin!=rdsout)
361
                readbuf[i++]=rdsbuf[rdsout++];
362
 
363
        if (copy_to_user(data,readbuf,i))
364
                return -EFAULT;
365
        return i;
366
}
367
 
368
 
369
static int vidioc_querycap(struct file *file, void *priv,
370
                                struct v4l2_capability *v)
371
{
372
        v->capabilities =
373
                V4L2_CAP_TUNER |
374
                V4L2_CAP_READWRITE;
375
        v->version = CADET_VERSION;
376
        strcpy(v->driver, "ADS Cadet");
377
        strcpy(v->card, "ADS Cadet");
378
        return 0;
379
}
380
 
381
static int vidioc_g_tuner(struct file *file, void *priv,
382
                                struct v4l2_tuner *v)
383
{
384
        v->type = V4L2_TUNER_RADIO;
385
        switch (v->index) {
386
        case 0:
387
                strcpy(v->name, "FM");
388
                v->capability = V4L2_TUNER_CAP_STEREO;
389
                v->rangelow = 1400;     /* 87.5 MHz */
390
                v->rangehigh = 1728;    /* 108.0 MHz */
391
                v->rxsubchans=cadet_getstereo();
392
                switch (v->rxsubchans){
393
                case V4L2_TUNER_SUB_MONO:
394
                        v->audmode = V4L2_TUNER_MODE_MONO;
395
                        break;
396
                case V4L2_TUNER_SUB_STEREO:
397
                        v->audmode = V4L2_TUNER_MODE_STEREO;
398
                        break;
399
                default: ;
400
                }
401
                break;
402
        case 1:
403
                strcpy(v->name, "AM");
404
                v->capability = V4L2_TUNER_CAP_LOW;
405
                v->rangelow = 8320;      /* 520 kHz */
406
                v->rangehigh = 26400;    /* 1650 kHz */
407
                v->rxsubchans = V4L2_TUNER_SUB_MONO;
408
                v->audmode = V4L2_TUNER_MODE_MONO;
409
                break;
410
        default:
411
                return -EINVAL;
412
        }
413
        v->signal = sigstrength; /* We might need to modify scaling of this */
414
        return 0;
415
}
416
 
417
static int vidioc_s_tuner(struct file *file, void *priv,
418
                                struct v4l2_tuner *v)
419
{
420
        if((v->index != 0)&&(v->index != 1))
421
                return -EINVAL;
422
        curtuner = v->index;
423
        return 0;
424
}
425
 
426
static int vidioc_g_frequency(struct file *file, void *priv,
427
                                struct v4l2_frequency *f)
428
{
429
        f->tuner = curtuner;
430
        f->type = V4L2_TUNER_RADIO;
431
        f->frequency = cadet_getfreq();
432
        return 0;
433
}
434
 
435
 
436
static int vidioc_s_frequency(struct file *file, void *priv,
437
                                struct v4l2_frequency *f)
438
{
439
        if (f->type != V4L2_TUNER_RADIO)
440
                return -EINVAL;
441
        if((curtuner==0)&&((f->frequency<1400)||(f->frequency>1728)))
442
                return -EINVAL;
443
        if((curtuner==1)&&((f->frequency<8320)||(f->frequency>26400)))
444
                return -EINVAL;
445
        cadet_setfreq(f->frequency);
446
        return 0;
447
}
448
 
449
static int vidioc_queryctrl(struct file *file, void *priv,
450
                                struct v4l2_queryctrl *qc)
451
{
452
        int i;
453
 
454
        for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
455
                if (qc->id && qc->id == radio_qctrl[i].id) {
456
                        memcpy(qc, &(radio_qctrl[i]),
457
                                                sizeof(*qc));
458
                        return 0;
459
                }
460
        }
461
        return -EINVAL;
462
}
463
 
464
static int vidioc_g_ctrl(struct file *file, void *priv,
465
                                struct v4l2_control *ctrl)
466
{
467
        switch (ctrl->id){
468
        case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
469
                ctrl->value = (cadet_getvol() == 0);
470
                break;
471
        case V4L2_CID_AUDIO_VOLUME:
472
                ctrl->value = cadet_getvol();
473
                break;
474
        default:
475
                return -EINVAL;
476
        }
477
        return 0;
478
}
479
 
480
static int vidioc_s_ctrl(struct file *file, void *priv,
481
                                struct v4l2_control *ctrl)
482
{
483
        switch (ctrl->id){
484
        case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
485
                if (ctrl->value)
486
                        cadet_setvol(0);
487
                else
488
                        cadet_setvol(0xffff);
489
                break;
490
        case V4L2_CID_AUDIO_VOLUME:
491
                cadet_setvol(ctrl->value);
492
                break;
493
        default:
494
                return -EINVAL;
495
        }
496
        return 0;
497
}
498
 
499
static int vidioc_g_audio(struct file *file, void *priv,
500
                                struct v4l2_audio *a)
501
{
502
        if (a->index > 1)
503
                return -EINVAL;
504
        strcpy(a->name, "Radio");
505
        a->capability = V4L2_AUDCAP_STEREO;
506
        return 0;
507
}
508
 
509
static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
510
{
511
        *i = 0;
512
        return 0;
513
}
514
 
515
static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
516
{
517
        if (i != 0)
518
                return -EINVAL;
519
        return 0;
520
}
521
 
522
static int vidioc_s_audio(struct file *file, void *priv,
523
                                struct v4l2_audio *a)
524
{
525
        if (a->index != 0)
526
                return -EINVAL;
527
        return 0;
528
}
529
 
530
static int
531
cadet_open(struct inode *inode, struct file *file)
532
{
533
        users++;
534
        if (1 == users) init_waitqueue_head(&read_queue);
535
        return 0;
536
}
537
 
538
static int
539
cadet_release(struct inode *inode, struct file *file)
540
{
541
        users--;
542
        if (0 == users){
543
                del_timer_sync(&readtimer);
544
                rdsstat=0;
545
        }
546
        return 0;
547
}
548
 
549
static unsigned int
550
cadet_poll(struct file *file, struct poll_table_struct *wait)
551
{
552
        poll_wait(file,&read_queue,wait);
553
        if(rdsin != rdsout)
554
                return POLLIN | POLLRDNORM;
555
        return 0;
556
}
557
 
558
 
559
static const struct file_operations cadet_fops = {
560
        .owner          = THIS_MODULE,
561
        .open           = cadet_open,
562
        .release        = cadet_release,
563
        .read           = cadet_read,
564
        .ioctl          = video_ioctl2,
565
        .poll           = cadet_poll,
566
        .compat_ioctl   = v4l_compat_ioctl32,
567
        .llseek         = no_llseek,
568
};
569
 
570
static struct video_device cadet_radio=
571
{
572
        .owner          = THIS_MODULE,
573
        .name           = "Cadet radio",
574
        .type           = VID_TYPE_TUNER,
575
        .fops           = &cadet_fops,
576
        .vidioc_querycap    = vidioc_querycap,
577
        .vidioc_g_tuner     = vidioc_g_tuner,
578
        .vidioc_s_tuner     = vidioc_s_tuner,
579
        .vidioc_g_frequency = vidioc_g_frequency,
580
        .vidioc_s_frequency = vidioc_s_frequency,
581
        .vidioc_queryctrl   = vidioc_queryctrl,
582
        .vidioc_g_ctrl      = vidioc_g_ctrl,
583
        .vidioc_s_ctrl      = vidioc_s_ctrl,
584
        .vidioc_g_audio     = vidioc_g_audio,
585
        .vidioc_s_audio     = vidioc_s_audio,
586
        .vidioc_g_input     = vidioc_g_input,
587
        .vidioc_s_input     = vidioc_s_input,
588
};
589
 
590
static struct pnp_device_id cadet_pnp_devices[] = {
591
        /* ADS Cadet AM/FM Radio Card */
592
        {.id = "MSM0c24", .driver_data = 0},
593
        {.id = ""}
594
};
595
 
596
MODULE_DEVICE_TABLE(pnp, cadet_pnp_devices);
597
 
598
static int cadet_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id)
599
{
600
        if (!dev)
601
                return -ENODEV;
602
        /* only support one device */
603
        if (io > 0)
604
                return -EBUSY;
605
 
606
        if (!pnp_port_valid(dev, 0)) {
607
                return -ENODEV;
608
        }
609
 
610
        io = pnp_port_start(dev, 0);
611
 
612
        printk ("radio-cadet: PnP reports device at %#x\n", io);
613
 
614
        return io;
615
}
616
 
617
static struct pnp_driver cadet_pnp_driver = {
618
        .name           = "radio-cadet",
619
        .id_table       = cadet_pnp_devices,
620
        .probe          = cadet_pnp_probe,
621
        .remove         = NULL,
622
};
623
 
624
static int cadet_probe(void)
625
{
626
        static int iovals[8]={0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e};
627
        int i;
628
 
629
        for(i=0;i<8;i++) {
630
                io=iovals[i];
631
                if (request_region(io, 2, "cadet-probe")) {
632
                        cadet_setfreq(1410);
633
                        if(cadet_getfreq()==1410) {
634
                                release_region(io, 2);
635
                                return io;
636
                        }
637
                        release_region(io, 2);
638
                }
639
        }
640
        return -1;
641
}
642
 
643
/*
644
 * io should only be set if the user has used something like
645
 * isapnp (the userspace program) to initialize this card for us
646
 */
647
 
648
static int __init cadet_init(void)
649
{
650
        spin_lock_init(&cadet_io_lock);
651
 
652
        /*
653
         *      If a probe was requested then probe ISAPnP first (safest)
654
         */
655
        if (io < 0)
656
                pnp_register_driver(&cadet_pnp_driver);
657
        /*
658
         *      If that fails then probe unsafely if probe is requested
659
         */
660
        if(io < 0)
661
                io = cadet_probe ();
662
 
663
        /*
664
         *      Else we bail out
665
         */
666
 
667
        if(io < 0) {
668
#ifdef MODULE
669
                printk(KERN_ERR "You must set an I/O address with io=0x???\n");
670
#endif
671
                goto fail;
672
        }
673
        if (!request_region(io,2,"cadet"))
674
                goto fail;
675
        if(video_register_device(&cadet_radio,VFL_TYPE_RADIO,radio_nr)==-1) {
676
                release_region(io,2);
677
                goto fail;
678
        }
679
        printk(KERN_INFO "ADS Cadet Radio Card at 0x%x\n",io);
680
        return 0;
681
fail:
682
        pnp_unregister_driver(&cadet_pnp_driver);
683
        return -1;
684
}
685
 
686
 
687
 
688
MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
689
MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
690
MODULE_LICENSE("GPL");
691
 
692
module_param(io, int, 0);
693
MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)");
694
module_param(radio_nr, int, 0);
695
 
696
static void __exit cadet_cleanup_module(void)
697
{
698
        video_unregister_device(&cadet_radio);
699
        release_region(io,2);
700
        pnp_unregister_driver(&cadet_pnp_driver);
701
}
702
 
703
module_init(cadet_init);
704
module_exit(cadet_cleanup_module);
705
 

powered by: WebSVN 2.1.0

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