Merge branch 'nfs-for-2.6.38' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
[pandora-kernel.git] / drivers / media / radio / radio-aimslab.c
1 /* radiotrack (radioreveal) driver for Linux radio support
2  * (c) 1997 M. Kirkwood
3  * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
4  * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
5  * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
6  *
7  * History:
8  * 1999-02-24   Russell Kroll <rkroll@exploits.org>
9  *              Fine tuning/VIDEO_TUNER_LOW
10  *              Frequency range expanded to start at 87 MHz
11  *
12  * TODO: Allow for more than one of these foolish entities :-)
13  *
14  * Notes on the hardware (reverse engineered from other peoples'
15  * reverse engineering of AIMS' code :-)
16  *
17  *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
18  *
19  *  The signal strength query is unsurprisingly inaccurate.  And it seems
20  *  to indicate that (on my card, at least) the frequency setting isn't
21  *  too great.  (I have to tune up .025MHz from what the freq should be
22  *  to get a report that the thing is tuned.)
23  *
24  *  Volume control is (ugh) analogue:
25  *   out(port, start_increasing_volume);
26  *   wait(a_wee_while);
27  *   out(port, stop_changing_the_volume);
28  *
29  */
30
31 #include <linux/module.h>       /* Modules                      */
32 #include <linux/init.h>         /* Initdata                     */
33 #include <linux/ioport.h>       /* request_region               */
34 #include <linux/videodev2.h>    /* kernel radio structs         */
35 #include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
36 #include <linux/io.h>           /* outb, outb_p                 */
37 #include <media/v4l2-device.h>
38 #include <media/v4l2-ioctl.h>
39
40 MODULE_AUTHOR("M.Kirkwood");
41 MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
42 MODULE_LICENSE("GPL");
43
44 #ifndef CONFIG_RADIO_RTRACK_PORT
45 #define CONFIG_RADIO_RTRACK_PORT -1
46 #endif
47
48 static int io = CONFIG_RADIO_RTRACK_PORT;
49 static int radio_nr = -1;
50
51 module_param(io, int, 0);
52 MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
53 module_param(radio_nr, int, 0);
54
55 #define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
56
57 struct rtrack
58 {
59         struct v4l2_device v4l2_dev;
60         struct video_device vdev;
61         int port;
62         int curvol;
63         unsigned long curfreq;
64         int muted;
65         int io;
66         struct mutex lock;
67 };
68
69 static struct rtrack rtrack_card;
70
71 /* local things */
72
73 static void rt_decvol(struct rtrack *rt)
74 {
75         outb(0x58, rt->io);             /* volume down + sigstr + on    */
76         msleep(100);
77         outb(0xd8, rt->io);             /* volume steady + sigstr + on  */
78 }
79
80 static void rt_incvol(struct rtrack *rt)
81 {
82         outb(0x98, rt->io);             /* volume up + sigstr + on      */
83         msleep(100);
84         outb(0xd8, rt->io);             /* volume steady + sigstr + on  */
85 }
86
87 static void rt_mute(struct rtrack *rt)
88 {
89         rt->muted = 1;
90         mutex_lock(&rt->lock);
91         outb(0xd0, rt->io);             /* volume steady, off           */
92         mutex_unlock(&rt->lock);
93 }
94
95 static int rt_setvol(struct rtrack *rt, int vol)
96 {
97         int i;
98
99         mutex_lock(&rt->lock);
100
101         if (vol == rt->curvol) {        /* requested volume = current */
102                 if (rt->muted) {        /* user is unmuting the card  */
103                         rt->muted = 0;
104                         outb(0xd8, rt->io);     /* enable card */
105                 }
106                 mutex_unlock(&rt->lock);
107                 return 0;
108         }
109
110         if (vol == 0) {                 /* volume = 0 means mute the card */
111                 outb(0x48, rt->io);     /* volume down but still "on"   */
112                 msleep(2000);   /* make sure it's totally down  */
113                 outb(0xd0, rt->io);     /* volume steady, off           */
114                 rt->curvol = 0;         /* track the volume state!      */
115                 mutex_unlock(&rt->lock);
116                 return 0;
117         }
118
119         rt->muted = 0;
120         if (vol > rt->curvol)
121                 for (i = rt->curvol; i < vol; i++)
122                         rt_incvol(rt);
123         else
124                 for (i = rt->curvol; i > vol; i--)
125                         rt_decvol(rt);
126
127         rt->curvol = vol;
128         mutex_unlock(&rt->lock);
129         return 0;
130 }
131
132 /* the 128+64 on these outb's is to keep the volume stable while tuning
133  * without them, the volume _will_ creep up with each frequency change
134  * and bit 4 (+16) is to keep the signal strength meter enabled
135  */
136
137 static void send_0_byte(struct rtrack *rt)
138 {
139         if (rt->curvol == 0 || rt->muted) {
140                 outb_p(128+64+16+  1, rt->io);   /* wr-enable + data low */
141                 outb_p(128+64+16+2+1, rt->io);   /* clock */
142         }
143         else {
144                 outb_p(128+64+16+8+  1, rt->io);  /* on + wr-enable + data low */
145                 outb_p(128+64+16+8+2+1, rt->io);  /* clock */
146         }
147         msleep(1);
148 }
149
150 static void send_1_byte(struct rtrack *rt)
151 {
152         if (rt->curvol == 0 || rt->muted) {
153                 outb_p(128+64+16+4  +1, rt->io);   /* wr-enable+data high */
154                 outb_p(128+64+16+4+2+1, rt->io);   /* clock */
155         }
156         else {
157                 outb_p(128+64+16+8+4  +1, rt->io); /* on+wr-enable+data high */
158                 outb_p(128+64+16+8+4+2+1, rt->io); /* clock */
159         }
160
161         msleep(1);
162 }
163
164 static int rt_setfreq(struct rtrack *rt, unsigned long freq)
165 {
166         int i;
167
168         mutex_lock(&rt->lock);                  /* Stop other ops interfering */
169
170         rt->curfreq = freq;
171
172         /* now uses VIDEO_TUNER_LOW for fine tuning */
173
174         freq += 171200;                 /* Add 10.7 MHz IF              */
175         freq /= 800;                    /* Convert to 50 kHz units      */
176
177         send_0_byte(rt);                /*  0: LSB of frequency         */
178
179         for (i = 0; i < 13; i++)        /*   : frequency bits (1-13)    */
180                 if (freq & (1 << i))
181                         send_1_byte(rt);
182                 else
183                         send_0_byte(rt);
184
185         send_0_byte(rt);                /* 14: test bit - always 0    */
186         send_0_byte(rt);                /* 15: test bit - always 0    */
187
188         send_0_byte(rt);                /* 16: band data 0 - always 0 */
189         send_0_byte(rt);                /* 17: band data 1 - always 0 */
190         send_0_byte(rt);                /* 18: band data 2 - always 0 */
191         send_0_byte(rt);                /* 19: time base - always 0   */
192
193         send_0_byte(rt);                /* 20: spacing (0 = 25 kHz)   */
194         send_1_byte(rt);                /* 21: spacing (1 = 25 kHz)   */
195         send_0_byte(rt);                /* 22: spacing (0 = 25 kHz)   */
196         send_1_byte(rt);                /* 23: AM/FM (FM = 1, always) */
197
198         if (rt->curvol == 0 || rt->muted)
199                 outb(0xd0, rt->io);     /* volume steady + sigstr */
200         else
201                 outb(0xd8, rt->io);     /* volume steady + sigstr + on */
202
203         mutex_unlock(&rt->lock);
204
205         return 0;
206 }
207
208 static int rt_getsigstr(struct rtrack *rt)
209 {
210         int sig = 1;
211
212         mutex_lock(&rt->lock);
213         if (inb(rt->io) & 2)    /* bit set = no signal present  */
214                 sig = 0;
215         mutex_unlock(&rt->lock);
216         return sig;
217 }
218
219 static int vidioc_querycap(struct file *file, void  *priv,
220                                         struct v4l2_capability *v)
221 {
222         strlcpy(v->driver, "radio-aimslab", sizeof(v->driver));
223         strlcpy(v->card, "RadioTrack", sizeof(v->card));
224         strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
225         v->version = RADIO_VERSION;
226         v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
227         return 0;
228 }
229
230 static int vidioc_g_tuner(struct file *file, void *priv,
231                                         struct v4l2_tuner *v)
232 {
233         struct rtrack *rt = video_drvdata(file);
234
235         if (v->index > 0)
236                 return -EINVAL;
237
238         strlcpy(v->name, "FM", sizeof(v->name));
239         v->type = V4L2_TUNER_RADIO;
240         v->rangelow = 87 * 16000;
241         v->rangehigh = 108 * 16000;
242         v->rxsubchans = V4L2_TUNER_SUB_MONO;
243         v->capability = V4L2_TUNER_CAP_LOW;
244         v->audmode = V4L2_TUNER_MODE_MONO;
245         v->signal = 0xffff * rt_getsigstr(rt);
246         return 0;
247 }
248
249 static int vidioc_s_tuner(struct file *file, void *priv,
250                                         struct v4l2_tuner *v)
251 {
252         return v->index ? -EINVAL : 0;
253 }
254
255 static int vidioc_s_frequency(struct file *file, void *priv,
256                                         struct v4l2_frequency *f)
257 {
258         struct rtrack *rt = video_drvdata(file);
259
260         if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
261                 return -EINVAL;
262         rt_setfreq(rt, f->frequency);
263         return 0;
264 }
265
266 static int vidioc_g_frequency(struct file *file, void *priv,
267                                         struct v4l2_frequency *f)
268 {
269         struct rtrack *rt = video_drvdata(file);
270
271         if (f->tuner != 0)
272                 return -EINVAL;
273         f->type = V4L2_TUNER_RADIO;
274         f->frequency = rt->curfreq;
275         return 0;
276 }
277
278 static int vidioc_queryctrl(struct file *file, void *priv,
279                                         struct v4l2_queryctrl *qc)
280 {
281         switch (qc->id) {
282         case V4L2_CID_AUDIO_MUTE:
283                 return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
284         case V4L2_CID_AUDIO_VOLUME:
285                 return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
286         }
287         return -EINVAL;
288 }
289
290 static int vidioc_g_ctrl(struct file *file, void *priv,
291                                         struct v4l2_control *ctrl)
292 {
293         struct rtrack *rt = video_drvdata(file);
294
295         switch (ctrl->id) {
296         case V4L2_CID_AUDIO_MUTE:
297                 ctrl->value = rt->muted;
298                 return 0;
299         case V4L2_CID_AUDIO_VOLUME:
300                 ctrl->value = rt->curvol;
301                 return 0;
302         }
303         return -EINVAL;
304 }
305
306 static int vidioc_s_ctrl(struct file *file, void *priv,
307                                         struct v4l2_control *ctrl)
308 {
309         struct rtrack *rt = video_drvdata(file);
310
311         switch (ctrl->id) {
312         case V4L2_CID_AUDIO_MUTE:
313                 if (ctrl->value)
314                         rt_mute(rt);
315                 else
316                         rt_setvol(rt, rt->curvol);
317                 return 0;
318         case V4L2_CID_AUDIO_VOLUME:
319                 rt_setvol(rt, ctrl->value);
320                 return 0;
321         }
322         return -EINVAL;
323 }
324
325 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
326 {
327         *i = 0;
328         return 0;
329 }
330
331 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
332 {
333         return i ? -EINVAL : 0;
334 }
335
336 static int vidioc_g_audio(struct file *file, void *priv,
337                                         struct v4l2_audio *a)
338 {
339         a->index = 0;
340         strlcpy(a->name, "Radio", sizeof(a->name));
341         a->capability = V4L2_AUDCAP_STEREO;
342         return 0;
343 }
344
345 static int vidioc_s_audio(struct file *file, void *priv,
346                                         struct v4l2_audio *a)
347 {
348         return a->index ? -EINVAL : 0;
349 }
350
351 static const struct v4l2_file_operations rtrack_fops = {
352         .owner          = THIS_MODULE,
353         .unlocked_ioctl = video_ioctl2,
354 };
355
356 static const struct v4l2_ioctl_ops rtrack_ioctl_ops = {
357         .vidioc_querycap    = vidioc_querycap,
358         .vidioc_g_tuner     = vidioc_g_tuner,
359         .vidioc_s_tuner     = vidioc_s_tuner,
360         .vidioc_g_audio     = vidioc_g_audio,
361         .vidioc_s_audio     = vidioc_s_audio,
362         .vidioc_g_input     = vidioc_g_input,
363         .vidioc_s_input     = vidioc_s_input,
364         .vidioc_g_frequency = vidioc_g_frequency,
365         .vidioc_s_frequency = vidioc_s_frequency,
366         .vidioc_queryctrl   = vidioc_queryctrl,
367         .vidioc_g_ctrl      = vidioc_g_ctrl,
368         .vidioc_s_ctrl      = vidioc_s_ctrl,
369 };
370
371 static int __init rtrack_init(void)
372 {
373         struct rtrack *rt = &rtrack_card;
374         struct v4l2_device *v4l2_dev = &rt->v4l2_dev;
375         int res;
376
377         strlcpy(v4l2_dev->name, "rtrack", sizeof(v4l2_dev->name));
378         rt->io = io;
379
380         if (rt->io == -1) {
381                 v4l2_err(v4l2_dev, "you must set an I/O address with io=0x20f or 0x30f\n");
382                 return -EINVAL;
383         }
384
385         if (!request_region(rt->io, 2, "rtrack")) {
386                 v4l2_err(v4l2_dev, "port 0x%x already in use\n", rt->io);
387                 return -EBUSY;
388         }
389
390         res = v4l2_device_register(NULL, v4l2_dev);
391         if (res < 0) {
392                 release_region(rt->io, 2);
393                 v4l2_err(v4l2_dev, "could not register v4l2_device\n");
394                 return res;
395         }
396
397         strlcpy(rt->vdev.name, v4l2_dev->name, sizeof(rt->vdev.name));
398         rt->vdev.v4l2_dev = v4l2_dev;
399         rt->vdev.fops = &rtrack_fops;
400         rt->vdev.ioctl_ops = &rtrack_ioctl_ops;
401         rt->vdev.release = video_device_release_empty;
402         video_set_drvdata(&rt->vdev, rt);
403
404         /* Set up the I/O locking */
405
406         mutex_init(&rt->lock);
407
408         /* mute card - prevents noisy bootups */
409
410         /* this ensures that the volume is all the way down  */
411         outb(0x48, rt->io);             /* volume down but still "on"   */
412         msleep(2000);   /* make sure it's totally down  */
413         outb(0xc0, rt->io);             /* steady volume, mute card     */
414
415         if (video_register_device(&rt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
416                 v4l2_device_unregister(&rt->v4l2_dev);
417                 release_region(rt->io, 2);
418                 return -EINVAL;
419         }
420         v4l2_info(v4l2_dev, "AIMSlab RadioTrack/RadioReveal card driver.\n");
421
422         return 0;
423 }
424
425 static void __exit rtrack_exit(void)
426 {
427         struct rtrack *rt = &rtrack_card;
428
429         video_unregister_device(&rt->vdev);
430         v4l2_device_unregister(&rt->v4l2_dev);
431         release_region(rt->io, 2);
432 }
433
434 module_init(rtrack_init);
435 module_exit(rtrack_exit);
436