Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/gerg/m68knommu
[linux-2.6] / drivers / media / radio / radio-rtrack2.c
1 /* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
2  *
3  * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
4  * Converted to new API by Alan Cox <Alan.Cox@linux.org>
5  * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
6  *
7  * TODO: Allow for more than one of these foolish entities :-)
8  *
9  * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
10  */
11
12 #include <linux/module.h>       /* Modules                      */
13 #include <linux/init.h>         /* Initdata                     */
14 #include <linux/ioport.h>       /* request_region               */
15 #include <linux/delay.h>        /* udelay                       */
16 #include <asm/io.h>             /* outb, outb_p                 */
17 #include <asm/uaccess.h>        /* copy to/from user            */
18 #include <linux/videodev2.h>    /* kernel radio structs         */
19 #include <media/v4l2-common.h>
20 #include <media/v4l2-ioctl.h>
21 #include <linux/spinlock.h>
22
23 #include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
24 #define RADIO_VERSION KERNEL_VERSION(0,0,2)
25
26 static struct v4l2_queryctrl radio_qctrl[] = {
27         {
28                 .id            = V4L2_CID_AUDIO_MUTE,
29                 .name          = "Mute",
30                 .minimum       = 0,
31                 .maximum       = 1,
32                 .default_value = 1,
33                 .type          = V4L2_CTRL_TYPE_BOOLEAN,
34         },{
35                 .id            = V4L2_CID_AUDIO_VOLUME,
36                 .name          = "Volume",
37                 .minimum       = 0,
38                 .maximum       = 65535,
39                 .step          = 65535,
40                 .default_value = 0xff,
41                 .type          = V4L2_CTRL_TYPE_INTEGER,
42         }
43 };
44
45 #ifndef CONFIG_RADIO_RTRACK2_PORT
46 #define CONFIG_RADIO_RTRACK2_PORT -1
47 #endif
48
49 static int io = CONFIG_RADIO_RTRACK2_PORT;
50 static int radio_nr = -1;
51 static spinlock_t lock;
52
53 struct rt_device
54 {
55         int port;
56         unsigned long curfreq;
57         int muted;
58 };
59
60
61 /* local things */
62
63 static void rt_mute(struct rt_device *dev)
64 {
65         if(dev->muted)
66                 return;
67         spin_lock(&lock);
68         outb(1, io);
69         spin_unlock(&lock);
70         dev->muted = 1;
71 }
72
73 static void rt_unmute(struct rt_device *dev)
74 {
75         if(dev->muted == 0)
76                 return;
77         spin_lock(&lock);
78         outb(0, io);
79         spin_unlock(&lock);
80         dev->muted = 0;
81 }
82
83 static void zero(void)
84 {
85         outb_p(1, io);
86         outb_p(3, io);
87         outb_p(1, io);
88 }
89
90 static void one(void)
91 {
92         outb_p(5, io);
93         outb_p(7, io);
94         outb_p(5, io);
95 }
96
97 static int rt_setfreq(struct rt_device *dev, unsigned long freq)
98 {
99         int i;
100
101         freq = freq / 200 + 856;
102
103         spin_lock(&lock);
104
105         outb_p(0xc8, io);
106         outb_p(0xc9, io);
107         outb_p(0xc9, io);
108
109         for (i = 0; i < 10; i++)
110                 zero ();
111
112         for (i = 14; i >= 0; i--)
113                 if (freq & (1 << i))
114                         one ();
115                 else
116                         zero ();
117
118         outb_p(0xc8, io);
119         if (!dev->muted)
120                 outb_p(0, io);
121
122         spin_unlock(&lock);
123         return 0;
124 }
125
126 static int vidioc_querycap(struct file *file, void *priv,
127                                 struct v4l2_capability *v)
128 {
129         strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver));
130         strlcpy(v->card, "RadioTrack II", sizeof(v->card));
131         sprintf(v->bus_info, "ISA");
132         v->version = RADIO_VERSION;
133         v->capabilities = V4L2_CAP_TUNER;
134         return 0;
135 }
136
137 static int vidioc_s_tuner(struct file *file, void *priv,
138                                 struct v4l2_tuner *v)
139 {
140         if (v->index > 0)
141                 return -EINVAL;
142
143         return 0;
144 }
145
146 static int rt_getsigstr(struct rt_device *dev)
147 {
148         if (inb(io) & 2)        /* bit set = no signal present  */
149                 return 0;
150         return 1;               /* signal present               */
151 }
152
153 static int vidioc_g_tuner(struct file *file, void *priv,
154                                 struct v4l2_tuner *v)
155 {
156         struct video_device *dev = video_devdata(file);
157         struct rt_device *rt = dev->priv;
158
159         if (v->index > 0)
160                 return -EINVAL;
161
162         strcpy(v->name, "FM");
163         v->type = V4L2_TUNER_RADIO;
164         v->rangelow = (88*16000);
165         v->rangehigh = (108*16000);
166         v->rxsubchans = V4L2_TUNER_SUB_MONO;
167         v->capability = V4L2_TUNER_CAP_LOW;
168         v->audmode = V4L2_TUNER_MODE_MONO;
169         v->signal = 0xFFFF*rt_getsigstr(rt);
170         return 0;
171 }
172
173 static int vidioc_s_frequency(struct file *file, void *priv,
174                                 struct v4l2_frequency *f)
175 {
176         struct video_device *dev = video_devdata(file);
177         struct rt_device *rt = dev->priv;
178
179         rt->curfreq = f->frequency;
180         rt_setfreq(rt, rt->curfreq);
181         return 0;
182 }
183
184 static int vidioc_g_frequency(struct file *file, void *priv,
185                                 struct v4l2_frequency *f)
186 {
187         struct video_device *dev = video_devdata(file);
188         struct rt_device *rt = dev->priv;
189
190         f->type = V4L2_TUNER_RADIO;
191         f->frequency = rt->curfreq;
192         return 0;
193 }
194
195 static int vidioc_queryctrl(struct file *file, void *priv,
196                                 struct v4l2_queryctrl *qc)
197 {
198         int i;
199
200         for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
201                 if (qc->id && qc->id == radio_qctrl[i].id) {
202                         memcpy(qc, &(radio_qctrl[i]),
203                                                 sizeof(*qc));
204                         return 0;
205                 }
206         }
207         return -EINVAL;
208 }
209
210 static int vidioc_g_ctrl(struct file *file, void *priv,
211                                 struct v4l2_control *ctrl)
212 {
213         struct video_device *dev = video_devdata(file);
214         struct rt_device *rt = dev->priv;
215
216         switch (ctrl->id) {
217         case V4L2_CID_AUDIO_MUTE:
218                 ctrl->value = rt->muted;
219                 return 0;
220         case V4L2_CID_AUDIO_VOLUME:
221                 if (rt->muted)
222                         ctrl->value = 0;
223                 else
224                         ctrl->value = 65535;
225                 return 0;
226         }
227         return -EINVAL;
228 }
229
230 static int vidioc_s_ctrl(struct file *file, void *priv,
231                                 struct v4l2_control *ctrl)
232 {
233         struct video_device *dev = video_devdata(file);
234         struct rt_device *rt = dev->priv;
235
236         switch (ctrl->id) {
237         case V4L2_CID_AUDIO_MUTE:
238                 if (ctrl->value)
239                         rt_mute(rt);
240                 else
241                         rt_unmute(rt);
242                 return 0;
243         case V4L2_CID_AUDIO_VOLUME:
244                 if (ctrl->value)
245                         rt_unmute(rt);
246                 else
247                         rt_mute(rt);
248                 return 0;
249         }
250         return -EINVAL;
251 }
252
253 static int vidioc_g_audio(struct file *file, void *priv,
254                                 struct v4l2_audio *a)
255 {
256         if (a->index > 1)
257                 return -EINVAL;
258
259         strcpy(a->name, "Radio");
260         a->capability = V4L2_AUDCAP_STEREO;
261         return 0;
262 }
263
264 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
265 {
266         *i = 0;
267         return 0;
268 }
269
270 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
271 {
272         if (i != 0)
273                 return -EINVAL;
274         return 0;
275 }
276
277 static int vidioc_s_audio(struct file *file, void *priv,
278                                 struct v4l2_audio *a)
279 {
280         if (a->index != 0)
281                 return -EINVAL;
282         return 0;
283 }
284
285 static struct rt_device rtrack2_unit;
286
287 static const struct file_operations rtrack2_fops = {
288         .owner          = THIS_MODULE,
289         .open           = video_exclusive_open,
290         .release        = video_exclusive_release,
291         .ioctl          = video_ioctl2,
292 #ifdef CONFIG_COMPAT
293         .compat_ioctl   = v4l_compat_ioctl32,
294 #endif
295         .llseek         = no_llseek,
296 };
297
298 static const struct v4l2_ioctl_ops rtrack2_ioctl_ops = {
299         .vidioc_querycap    = vidioc_querycap,
300         .vidioc_g_tuner     = vidioc_g_tuner,
301         .vidioc_s_tuner     = vidioc_s_tuner,
302         .vidioc_g_frequency = vidioc_g_frequency,
303         .vidioc_s_frequency = vidioc_s_frequency,
304         .vidioc_queryctrl   = vidioc_queryctrl,
305         .vidioc_g_ctrl      = vidioc_g_ctrl,
306         .vidioc_s_ctrl      = vidioc_s_ctrl,
307         .vidioc_g_audio     = vidioc_g_audio,
308         .vidioc_s_audio     = vidioc_s_audio,
309         .vidioc_g_input     = vidioc_g_input,
310         .vidioc_s_input     = vidioc_s_input,
311 };
312
313 static struct video_device rtrack2_radio = {
314         .name           = "RadioTrack II radio",
315         .fops           = &rtrack2_fops,
316         .ioctl_ops      = &rtrack2_ioctl_ops,
317 };
318
319 static int __init rtrack2_init(void)
320 {
321         if(io==-1)
322         {
323                 printk(KERN_ERR "You must set an I/O address with io=0x20c or io=0x30c\n");
324                 return -EINVAL;
325         }
326         if (!request_region(io, 4, "rtrack2"))
327         {
328                 printk(KERN_ERR "rtrack2: port 0x%x already in use\n", io);
329                 return -EBUSY;
330         }
331
332         rtrack2_radio.priv=&rtrack2_unit;
333
334         spin_lock_init(&lock);
335         if(video_register_device(&rtrack2_radio, VFL_TYPE_RADIO, radio_nr)==-1)
336         {
337                 release_region(io, 4);
338                 return -EINVAL;
339         }
340
341         printk(KERN_INFO "AIMSlab Radiotrack II card driver.\n");
342
343         /* mute card - prevents noisy bootups */
344         outb(1, io);
345         rtrack2_unit.muted = 1;
346
347         return 0;
348 }
349
350 MODULE_AUTHOR("Ben Pfaff");
351 MODULE_DESCRIPTION("A driver for the RadioTrack II radio card.");
352 MODULE_LICENSE("GPL");
353
354 module_param(io, int, 0);
355 MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)");
356 module_param(radio_nr, int, 0);
357
358 static void __exit rtrack2_cleanup_module(void)
359 {
360         video_unregister_device(&rtrack2_radio);
361         release_region(io,4);
362 }
363
364 module_init(rtrack2_init);
365 module_exit(rtrack2_cleanup_module);
366
367 /*
368   Local variables:
369   compile-command: "mmake"
370   End:
371 */