Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / drivers / media / radio / radio-gemtek.c
1 /* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi>
2  *
3  * GemTek hasn't released any specs on the card, so the protocol had to
4  * be reverse engineered with dosemu.
5  *
6  * Besides the protocol changes, this is mostly a copy of:
7  *
8  *    RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
9  *
10  *    Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
11  *    Converted to new API by Alan Cox <Alan.Cox@linux.org>
12  *    Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
13  *
14  * TODO: Allow for more than one of these foolish entities :-)
15  *
16  */
17
18 #include <linux/module.h>       /* Modules                      */
19 #include <linux/init.h>         /* Initdata                     */
20 #include <linux/ioport.h>       /* request_region               */
21 #include <linux/delay.h>        /* udelay                       */
22 #include <asm/io.h>             /* outb, outb_p                 */
23 #include <asm/uaccess.h>        /* copy to/from user            */
24 #include <linux/videodev.h>     /* kernel radio structs         */
25 #include <media/v4l2-common.h>
26 #include <linux/config.h>       /* CONFIG_RADIO_GEMTEK_PORT     */
27 #include <linux/spinlock.h>
28
29 #ifndef CONFIG_RADIO_GEMTEK_PORT
30 #define CONFIG_RADIO_GEMTEK_PORT -1
31 #endif
32
33 static int io = CONFIG_RADIO_GEMTEK_PORT;
34 static int radio_nr = -1;
35 static spinlock_t lock;
36
37 struct gemtek_device
38 {
39         int port;
40         unsigned long curfreq;
41         int muted;
42 };
43
44
45 /* local things */
46
47 /* the correct way to mute the gemtek may be to write the last written
48  * frequency || 0x10, but just writing 0x10 once seems to do it as well
49  */
50 static void gemtek_mute(struct gemtek_device *dev)
51 {
52         if(dev->muted)
53                 return;
54         spin_lock(&lock);
55         outb(0x10, io);
56         spin_unlock(&lock);
57         dev->muted = 1;
58 }
59
60 static void gemtek_unmute(struct gemtek_device *dev)
61 {
62         if(dev->muted == 0)
63                 return;
64         spin_lock(&lock);
65         outb(0x20, io);
66         spin_unlock(&lock);
67         dev->muted = 0;
68 }
69
70 static void zero(void)
71 {
72         outb_p(0x04, io);
73         udelay(5);
74         outb_p(0x05, io);
75         udelay(5);
76 }
77
78 static void one(void)
79 {
80         outb_p(0x06, io);
81         udelay(5);
82         outb_p(0x07, io);
83         udelay(5);
84 }
85
86 static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq)
87 {
88         int i;
89
90 /*        freq = 78.25*((float)freq/16000.0 + 10.52); */
91
92         freq /= 16;
93         freq += 10520;
94         freq *= 7825;
95         freq /= 100000;
96
97         spin_lock(&lock);
98
99         /* 2 start bits */
100         outb_p(0x03, io);
101         udelay(5);
102         outb_p(0x07, io);
103         udelay(5);
104
105         /* 28 frequency bits (lsb first) */
106         for (i = 0; i < 14; i++)
107                 if (freq & (1 << i))
108                         one();
109                 else
110                         zero();
111         /* 36 unknown bits */
112         for (i = 0; i < 11; i++)
113                 zero();
114         one();
115         for (i = 0; i < 4; i++)
116                 zero();
117         one();
118         zero();
119
120         /* 2 end bits */
121         outb_p(0x03, io);
122         udelay(5);
123         outb_p(0x07, io);
124         udelay(5);
125
126         spin_unlock(&lock);
127
128         return 0;
129 }
130
131 static int gemtek_getsigstr(struct gemtek_device *dev)
132 {
133         spin_lock(&lock);
134         inb(io);
135         udelay(5);
136         spin_unlock(&lock);
137         if (inb(io) & 8)                /* bit set = no signal present */
138                 return 0;
139         return 1;               /* signal present */
140 }
141
142 static int gemtek_do_ioctl(struct inode *inode, struct file *file,
143                            unsigned int cmd, void *arg)
144 {
145         struct video_device *dev = video_devdata(file);
146         struct gemtek_device *rt=dev->priv;
147
148         switch(cmd)
149         {
150                 case VIDIOCGCAP:
151                 {
152                         struct video_capability *v = arg;
153                         memset(v,0,sizeof(*v));
154                         v->type=VID_TYPE_TUNER;
155                         v->channels=1;
156                         v->audios=1;
157                         strcpy(v->name, "GemTek");
158                         return 0;
159                 }
160                 case VIDIOCGTUNER:
161                 {
162                         struct video_tuner *v = arg;
163                         if(v->tuner)    /* Only 1 tuner */
164                                 return -EINVAL;
165                         v->rangelow=87*16000;
166                         v->rangehigh=108*16000;
167                         v->flags=VIDEO_TUNER_LOW;
168                         v->mode=VIDEO_MODE_AUTO;
169                         v->signal=0xFFFF*gemtek_getsigstr(rt);
170                         strcpy(v->name, "FM");
171                         return 0;
172                 }
173                 case VIDIOCSTUNER:
174                 {
175                         struct video_tuner *v = arg;
176                         if(v->tuner!=0)
177                                 return -EINVAL;
178                         /* Only 1 tuner so no setting needed ! */
179                         return 0;
180                 }
181                 case VIDIOCGFREQ:
182                 {
183                         unsigned long *freq = arg;
184                         *freq = rt->curfreq;
185                         return 0;
186                 }
187                 case VIDIOCSFREQ:
188                 {
189                         unsigned long *freq = arg;
190                         rt->curfreq = *freq;
191                         /* needs to be called twice in order for getsigstr to work */
192                         gemtek_setfreq(rt, rt->curfreq);
193                         gemtek_setfreq(rt, rt->curfreq);
194                         return 0;
195                 }
196                 case VIDIOCGAUDIO:
197                 {
198                         struct video_audio *v = arg;
199                         memset(v,0, sizeof(*v));
200                         v->flags|=VIDEO_AUDIO_MUTABLE;
201                         v->volume=1;
202                         v->step=65535;
203                         strcpy(v->name, "Radio");
204                         return 0;
205                 }
206                 case VIDIOCSAUDIO:
207                 {
208                         struct video_audio *v = arg;
209                         if(v->audio)
210                                 return -EINVAL;
211
212                         if(v->flags&VIDEO_AUDIO_MUTE)
213                                 gemtek_mute(rt);
214                         else
215                                 gemtek_unmute(rt);
216
217                         return 0;
218                 }
219                 default:
220                         return -ENOIOCTLCMD;
221         }
222 }
223
224 static int gemtek_ioctl(struct inode *inode, struct file *file,
225                         unsigned int cmd, unsigned long arg)
226 {
227         return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl);
228 }
229
230 static struct gemtek_device gemtek_unit;
231
232 static struct file_operations gemtek_fops = {
233         .owner          = THIS_MODULE,
234         .open           = video_exclusive_open,
235         .release        = video_exclusive_release,
236         .ioctl          = gemtek_ioctl,
237         .compat_ioctl   = v4l_compat_ioctl32,
238         .llseek         = no_llseek,
239 };
240
241 static struct video_device gemtek_radio=
242 {
243         .owner          = THIS_MODULE,
244         .name           = "GemTek radio",
245         .type           = VID_TYPE_TUNER,
246         .hardware       = VID_HARDWARE_GEMTEK,
247         .fops           = &gemtek_fops,
248 };
249
250 static int __init gemtek_init(void)
251 {
252         if(io==-1)
253         {
254                 printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (io=0x020c or io=0x248 for the combined sound/radiocard)\n");
255                 return -EINVAL;
256         }
257
258         if (!request_region(io, 4, "gemtek"))
259         {
260                 printk(KERN_ERR "gemtek: port 0x%x already in use\n", io);
261                 return -EBUSY;
262         }
263
264         gemtek_radio.priv=&gemtek_unit;
265
266         if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1)
267         {
268                 release_region(io, 4);
269                 return -EINVAL;
270         }
271         printk(KERN_INFO "GemTek Radio Card driver.\n");
272
273         spin_lock_init(&lock);
274
275         /* this is _maybe_ unnecessary */
276         outb(0x01, io);
277
278         /* mute card - prevents noisy bootups */
279         gemtek_unit.muted = 0;
280         gemtek_mute(&gemtek_unit);
281
282         return 0;
283 }
284
285 MODULE_AUTHOR("Jonas Munsin");
286 MODULE_DESCRIPTION("A driver for the GemTek Radio Card");
287 MODULE_LICENSE("GPL");
288
289 module_param(io, int, 0);
290 MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (0x20c or 0x248 have been reported to work for the combined sound/radiocard)).");
291 module_param(radio_nr, int, 0);
292
293 static void __exit gemtek_cleanup(void)
294 {
295         video_unregister_device(&gemtek_radio);
296         release_region(io,4);
297 }
298
299 module_init(gemtek_init);
300 module_exit(gemtek_cleanup);
301
302 /*
303   Local variables:
304   compile-command: "gcc -c -DMODVERSIONS -D__KERNEL__ -DMODULE -O6 -Wall -Wstrict-prototypes -I /home/blp/tmp/linux-2.1.111-rtrack/include radio-rtrack2.c"
305   End:
306 */