Merge git://git.infradead.org/mtd-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  * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
17  */
18
19 #include <linux/module.h>       /* Modules                      */
20 #include <linux/init.h>         /* Initdata                     */
21 #include <linux/ioport.h>       /* request_region               */
22 #include <linux/delay.h>        /* udelay                       */
23 #include <asm/io.h>             /* outb, outb_p                 */
24 #include <asm/uaccess.h>        /* copy to/from user            */
25 #include <linux/videodev2.h>    /* kernel radio structs         */
26 #include <media/v4l2-common.h>
27 #include <linux/spinlock.h>
28
29 #include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
30 #define RADIO_VERSION KERNEL_VERSION(0,0,2)
31
32 static struct v4l2_queryctrl radio_qctrl[] = {
33         {
34                 .id            = V4L2_CID_AUDIO_MUTE,
35                 .name          = "Mute",
36                 .minimum       = 0,
37                 .maximum       = 1,
38                 .default_value = 1,
39                 .type          = V4L2_CTRL_TYPE_BOOLEAN,
40         },{
41                 .id            = V4L2_CID_AUDIO_VOLUME,
42                 .name          = "Volume",
43                 .minimum       = 0,
44                 .maximum       = 65535,
45                 .step          = 65535,
46                 .default_value = 0xff,
47                 .type          = V4L2_CTRL_TYPE_INTEGER,
48         }
49 };
50
51 #ifndef CONFIG_RADIO_GEMTEK_PORT
52 #define CONFIG_RADIO_GEMTEK_PORT -1
53 #endif
54
55 static int io = CONFIG_RADIO_GEMTEK_PORT;
56 static int radio_nr = -1;
57 static spinlock_t lock;
58
59 struct gemtek_device
60 {
61         int port;
62         unsigned long curfreq;
63         int muted;
64 };
65
66
67 /* local things */
68
69 /* the correct way to mute the gemtek may be to write the last written
70  * frequency || 0x10, but just writing 0x10 once seems to do it as well
71  */
72 static void gemtek_mute(struct gemtek_device *dev)
73 {
74         if(dev->muted)
75                 return;
76         spin_lock(&lock);
77         outb(0x10, io);
78         spin_unlock(&lock);
79         dev->muted = 1;
80 }
81
82 static void gemtek_unmute(struct gemtek_device *dev)
83 {
84         if(dev->muted == 0)
85                 return;
86         spin_lock(&lock);
87         outb(0x20, io);
88         spin_unlock(&lock);
89         dev->muted = 0;
90 }
91
92 static void zero(void)
93 {
94         outb_p(0x04, io);
95         udelay(5);
96         outb_p(0x05, io);
97         udelay(5);
98 }
99
100 static void one(void)
101 {
102         outb_p(0x06, io);
103         udelay(5);
104         outb_p(0x07, io);
105         udelay(5);
106 }
107
108 static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq)
109 {
110         int i;
111
112 /*        freq = 78.25*((float)freq/16000.0 + 10.52); */
113
114         freq /= 16;
115         freq += 10520;
116         freq *= 7825;
117         freq /= 100000;
118
119         spin_lock(&lock);
120
121         /* 2 start bits */
122         outb_p(0x03, io);
123         udelay(5);
124         outb_p(0x07, io);
125         udelay(5);
126
127         /* 28 frequency bits (lsb first) */
128         for (i = 0; i < 14; i++)
129                 if (freq & (1 << i))
130                         one();
131                 else
132                         zero();
133         /* 36 unknown bits */
134         for (i = 0; i < 11; i++)
135                 zero();
136         one();
137         for (i = 0; i < 4; i++)
138                 zero();
139         one();
140         zero();
141
142         /* 2 end bits */
143         outb_p(0x03, io);
144         udelay(5);
145         outb_p(0x07, io);
146         udelay(5);
147
148         spin_unlock(&lock);
149
150         return 0;
151 }
152
153 static int gemtek_getsigstr(struct gemtek_device *dev)
154 {
155         spin_lock(&lock);
156         inb(io);
157         udelay(5);
158         spin_unlock(&lock);
159         if (inb(io) & 8)                /* bit set = no signal present */
160                 return 0;
161         return 1;               /* signal present */
162 }
163
164 static int gemtek_do_ioctl(struct inode *inode, struct file *file,
165                            unsigned int cmd, void *arg)
166 {
167         struct video_device *dev = video_devdata(file);
168         struct gemtek_device *rt=dev->priv;
169
170         switch(cmd)
171         {
172                 case VIDIOC_QUERYCAP:
173                 {
174                         struct v4l2_capability *v = arg;
175                         memset(v,0,sizeof(*v));
176                         strlcpy(v->driver, "radio-gemtek", sizeof (v->driver));
177                         strlcpy(v->card, "GemTek", sizeof (v->card));
178                         sprintf(v->bus_info,"ISA");
179                         v->version = RADIO_VERSION;
180                         v->capabilities = V4L2_CAP_TUNER;
181
182                         return 0;
183                 }
184                 case VIDIOC_G_TUNER:
185                 {
186                         struct v4l2_tuner *v = arg;
187
188                         if (v->index > 0)
189                                 return -EINVAL;
190
191                         memset(v,0,sizeof(*v));
192                         strcpy(v->name, "FM");
193                         v->type = V4L2_TUNER_RADIO;
194
195                         v->rangelow=(87*16000);
196                         v->rangehigh=(108*16000);
197                         v->rxsubchans =V4L2_TUNER_SUB_MONO;
198                         v->capability=V4L2_TUNER_CAP_LOW;
199                         v->audmode = V4L2_TUNER_MODE_MONO;
200                         v->signal=0xFFFF*gemtek_getsigstr(rt);
201
202                         return 0;
203                 }
204                 case VIDIOC_S_TUNER:
205                 {
206                         struct v4l2_tuner *v = arg;
207
208                         if (v->index > 0)
209                                 return -EINVAL;
210
211                         return 0;
212                 }
213                 case VIDIOC_S_FREQUENCY:
214                 {
215                         struct v4l2_frequency *f = arg;
216
217                         rt->curfreq = f->frequency;
218                         /* needs to be called twice in order for getsigstr to work */
219                         gemtek_setfreq(rt, rt->curfreq);
220                         gemtek_setfreq(rt, rt->curfreq);
221                         return 0;
222                 }
223                 case VIDIOC_G_FREQUENCY:
224                 {
225                         struct v4l2_frequency *f = arg;
226
227                         f->type = V4L2_TUNER_RADIO;
228                         f->frequency = rt->curfreq;
229
230                         return 0;
231                 }
232                 case VIDIOC_QUERYCTRL:
233                 {
234                         struct v4l2_queryctrl *qc = arg;
235                         int i;
236
237                         for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
238                                 if (qc->id && qc->id == radio_qctrl[i].id) {
239                                         memcpy(qc, &(radio_qctrl[i]),
240                                                                 sizeof(*qc));
241                                         return (0);
242                                 }
243                         }
244                         return -EINVAL;
245                 }
246                 case VIDIOC_G_CTRL:
247                 {
248                         struct v4l2_control *ctrl= arg;
249
250                         switch (ctrl->id) {
251                                 case V4L2_CID_AUDIO_MUTE:
252                                         ctrl->value=rt->muted;
253                                         return (0);
254                                 case V4L2_CID_AUDIO_VOLUME:
255                                         if (rt->muted)
256                                                 ctrl->value=0;
257                                         else
258                                                 ctrl->value=65535;
259                                         return (0);
260                         }
261                         return -EINVAL;
262                 }
263                 case VIDIOC_S_CTRL:
264                 {
265                         struct v4l2_control *ctrl= arg;
266
267                         switch (ctrl->id) {
268                                 case V4L2_CID_AUDIO_MUTE:
269                                         if (ctrl->value) {
270                                                 gemtek_mute(rt);
271                                         } else {
272                                                 gemtek_unmute(rt);
273                                         }
274                                         return (0);
275                                 case V4L2_CID_AUDIO_VOLUME:
276                                         if (ctrl->value) {
277                                                 gemtek_unmute(rt);
278                                         } else {
279                                                 gemtek_mute(rt);
280                                         }
281                                         return (0);
282                         }
283                         return -EINVAL;
284                 }
285                 default:
286                         return v4l_compat_translate_ioctl(inode,file,cmd,arg,
287                                                           gemtek_do_ioctl);
288         }
289 }
290
291 static int gemtek_ioctl(struct inode *inode, struct file *file,
292                         unsigned int cmd, unsigned long arg)
293 {
294         return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl);
295 }
296
297 static struct gemtek_device gemtek_unit;
298
299 static const struct file_operations gemtek_fops = {
300         .owner          = THIS_MODULE,
301         .open           = video_exclusive_open,
302         .release        = video_exclusive_release,
303         .ioctl          = gemtek_ioctl,
304         .compat_ioctl   = v4l_compat_ioctl32,
305         .llseek         = no_llseek,
306 };
307
308 static struct video_device gemtek_radio=
309 {
310         .owner          = THIS_MODULE,
311         .name           = "GemTek radio",
312         .type           = VID_TYPE_TUNER,
313         .hardware       = 0,
314         .fops           = &gemtek_fops,
315 };
316
317 static int __init gemtek_init(void)
318 {
319         if(io==-1)
320         {
321                 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");
322                 return -EINVAL;
323         }
324
325         if (!request_region(io, 4, "gemtek"))
326         {
327                 printk(KERN_ERR "gemtek: port 0x%x already in use\n", io);
328                 return -EBUSY;
329         }
330
331         gemtek_radio.priv=&gemtek_unit;
332
333         if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1)
334         {
335                 release_region(io, 4);
336                 return -EINVAL;
337         }
338         printk(KERN_INFO "GemTek Radio Card driver.\n");
339
340         spin_lock_init(&lock);
341
342         /* this is _maybe_ unnecessary */
343         outb(0x01, io);
344
345         /* mute card - prevents noisy bootups */
346         gemtek_unit.muted = 0;
347         gemtek_mute(&gemtek_unit);
348
349         return 0;
350 }
351
352 MODULE_AUTHOR("Jonas Munsin");
353 MODULE_DESCRIPTION("A driver for the GemTek Radio Card");
354 MODULE_LICENSE("GPL");
355
356 module_param(io, int, 0);
357 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)).");
358 module_param(radio_nr, int, 0);
359
360 static void __exit gemtek_cleanup(void)
361 {
362         video_unregister_device(&gemtek_radio);
363         release_region(io,4);
364 }
365
366 module_init(gemtek_init);
367 module_exit(gemtek_cleanup);
368
369 /*
370   Local variables:
371   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"
372   End:
373 */