92d923c7f3603e68fea5932b828697f1becb08bb
[linux-2.6.git] / drivers / media / radio / radio-typhoon.c
1 /* Typhoon Radio Card driver for radio support
2  * (c) 1999 Dr. Henrik Seidel <Henrik.Seidel@gmx.de>
3  *
4  * Card manufacturer:
5  * http://194.18.155.92/idc/prod2.idc?nr=50753&lang=e
6  *
7  * Notes on the hardware
8  *
9  * This card has two output sockets, one for speakers and one for line.
10  * The speaker output has volume control, but only in four discrete
11  * steps. The line output has neither volume control nor mute.
12  *
13  * The card has auto-stereo according to its manual, although it all
14  * sounds mono to me (even with the Win/DOS drivers). Maybe it's my
15  * antenna - I really don't know for sure.
16  *
17  * Frequency control is done digitally.
18  *
19  * Volume control is done digitally, but there are only four different
20  * possible values. So you should better always turn the volume up and
21  * use line control. I got the best results by connecting line output
22  * to the sound card microphone input. For such a configuration the
23  * volume control has no effect, since volume control only influences
24  * the speaker output.
25  *
26  * There is no explicit mute/unmute. So I set the radio frequency to a
27  * value where I do expect just noise and turn the speaker volume down.
28  * The frequency change is necessary since the card never seems to be
29  * completely silent.
30  *
31  * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
32  */
33
34 #include <linux/module.h>       /* Modules                        */
35 #include <linux/init.h>         /* Initdata                       */
36 #include <linux/ioport.h>       /* request_region                 */
37 #include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
38 #include <linux/videodev2.h>    /* kernel radio structs           */
39 #include <linux/io.h>           /* outb, outb_p                   */
40 #include <media/v4l2-device.h>
41 #include <media/v4l2-ioctl.h>
42
43 MODULE_AUTHOR("Dr. Henrik Seidel");
44 MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
45 MODULE_LICENSE("GPL");
46
47 #ifndef CONFIG_RADIO_TYPHOON_PORT
48 #define CONFIG_RADIO_TYPHOON_PORT -1
49 #endif
50
51 #ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
52 #define CONFIG_RADIO_TYPHOON_MUTEFREQ 0
53 #endif
54
55 static int io = CONFIG_RADIO_TYPHOON_PORT;
56 static int radio_nr = -1;
57
58 module_param(io, int, 0);
59 MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
60
61 module_param(radio_nr, int, 0);
62
63 static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ;
64 module_param(mutefreq, ulong, 0);
65 MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
66
67 #define RADIO_VERSION KERNEL_VERSION(0, 1, 1)
68
69 #define BANNER "Typhoon Radio Card driver v0.1.1\n"
70
71 struct typhoon {
72         struct v4l2_device v4l2_dev;
73         struct video_device vdev;
74         int io;
75         int curvol;
76         int muted;
77         unsigned long curfreq;
78         unsigned long mutefreq;
79         struct mutex lock;
80 };
81
82 static struct typhoon typhoon_card;
83
84 static void typhoon_setvol_generic(struct typhoon *dev, int vol)
85 {
86         mutex_lock(&dev->lock);
87         vol >>= 14;                             /* Map 16 bit to 2 bit */
88         vol &= 3;
89         outb_p(vol / 2, dev->io);               /* Set the volume, high bit. */
90         outb_p(vol % 2, dev->io + 2);   /* Set the volume, low bit. */
91         mutex_unlock(&dev->lock);
92 }
93
94 static int typhoon_setfreq_generic(struct typhoon *dev,
95                                    unsigned long frequency)
96 {
97         unsigned long outval;
98         unsigned long x;
99
100         /*
101          * The frequency transfer curve is not linear. The best fit I could
102          * get is
103          *
104          * outval = -155 + exp((f + 15.55) * 0.057))
105          *
106          * where frequency f is in MHz. Since we don't have exp in the kernel,
107          * I approximate this function by a third order polynomial.
108          *
109          */
110
111         mutex_lock(&dev->lock);
112         x = frequency / 160;
113         outval = (x * x + 2500) / 5000;
114         outval = (outval * x + 5000) / 10000;
115         outval -= (10 * x * x + 10433) / 20866;
116         outval += 4 * x - 11505;
117
118         outb_p((outval >> 8) & 0x01, dev->io + 4);
119         outb_p(outval >> 9, dev->io + 6);
120         outb_p(outval & 0xff, dev->io + 8);
121         mutex_unlock(&dev->lock);
122
123         return 0;
124 }
125
126 static int typhoon_setfreq(struct typhoon *dev, unsigned long frequency)
127 {
128         typhoon_setfreq_generic(dev, frequency);
129         dev->curfreq = frequency;
130         return 0;
131 }
132
133 static void typhoon_mute(struct typhoon *dev)
134 {
135         if (dev->muted == 1)
136                 return;
137         typhoon_setvol_generic(dev, 0);
138         typhoon_setfreq_generic(dev, dev->mutefreq);
139         dev->muted = 1;
140 }
141
142 static void typhoon_unmute(struct typhoon *dev)
143 {
144         if (dev->muted == 0)
145                 return;
146         typhoon_setfreq_generic(dev, dev->curfreq);
147         typhoon_setvol_generic(dev, dev->curvol);
148         dev->muted = 0;
149 }
150
151 static int typhoon_setvol(struct typhoon *dev, int vol)
152 {
153         if (dev->muted && vol != 0) {   /* user is unmuting the card */
154                 dev->curvol = vol;
155                 typhoon_unmute(dev);
156                 return 0;
157         }
158         if (vol == dev->curvol)         /* requested volume == current */
159                 return 0;
160
161         if (vol == 0) {                 /* volume == 0 means mute the card */
162                 typhoon_mute(dev);
163                 dev->curvol = vol;
164                 return 0;
165         }
166         typhoon_setvol_generic(dev, vol);
167         dev->curvol = vol;
168         return 0;
169 }
170
171 static int vidioc_querycap(struct file *file, void  *priv,
172                                         struct v4l2_capability *v)
173 {
174         strlcpy(v->driver, "radio-typhoon", sizeof(v->driver));
175         strlcpy(v->card, "Typhoon Radio", sizeof(v->card));
176         strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
177         v->version = RADIO_VERSION;
178         v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
179         return 0;
180 }
181
182 static int vidioc_g_tuner(struct file *file, void *priv,
183                                         struct v4l2_tuner *v)
184 {
185         if (v->index > 0)
186                 return -EINVAL;
187
188         strlcpy(v->name, "FM", sizeof(v->name));
189         v->type = V4L2_TUNER_RADIO;
190         v->rangelow = 87.5 * 16000;
191         v->rangehigh = 108 * 16000;
192         v->rxsubchans = V4L2_TUNER_SUB_MONO;
193         v->capability = V4L2_TUNER_CAP_LOW;
194         v->audmode = V4L2_TUNER_MODE_MONO;
195         v->signal = 0xFFFF;     /* We can't get the signal strength */
196         return 0;
197 }
198
199 static int vidioc_s_tuner(struct file *file, void *priv,
200                                         struct v4l2_tuner *v)
201 {
202         return v->index ? -EINVAL : 0;
203 }
204
205 static int vidioc_g_frequency(struct file *file, void *priv,
206                                         struct v4l2_frequency *f)
207 {
208         struct typhoon *dev = video_drvdata(file);
209
210         f->type = V4L2_TUNER_RADIO;
211         f->frequency = dev->curfreq;
212         return 0;
213 }
214
215 static int vidioc_s_frequency(struct file *file, void *priv,
216                                         struct v4l2_frequency *f)
217 {
218         struct typhoon *dev = video_drvdata(file);
219
220         dev->curfreq = f->frequency;
221         typhoon_setfreq(dev, dev->curfreq);
222         return 0;
223 }
224
225 static int vidioc_queryctrl(struct file *file, void *priv,
226                                         struct v4l2_queryctrl *qc)
227 {
228         switch (qc->id) {
229         case V4L2_CID_AUDIO_MUTE:
230                 return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
231         case V4L2_CID_AUDIO_VOLUME:
232                 return v4l2_ctrl_query_fill(qc, 0, 65535, 16384, 65535);
233         }
234         return -EINVAL;
235 }
236
237 static int vidioc_g_ctrl(struct file *file, void *priv,
238                                         struct v4l2_control *ctrl)
239 {
240         struct typhoon *dev = video_drvdata(file);
241
242         switch (ctrl->id) {
243         case V4L2_CID_AUDIO_MUTE:
244                 ctrl->value = dev->muted;
245                 return 0;
246         case V4L2_CID_AUDIO_VOLUME:
247                 ctrl->value = dev->curvol;
248                 return 0;
249         }
250         return -EINVAL;
251 }
252
253 static int vidioc_s_ctrl (struct file *file, void *priv,
254                                         struct v4l2_control *ctrl)
255 {
256         struct typhoon *dev = video_drvdata(file);
257
258         switch (ctrl->id) {
259         case V4L2_CID_AUDIO_MUTE:
260                 if (ctrl->value)
261                         typhoon_mute(dev);
262                 else
263                         typhoon_unmute(dev);
264                 return 0;
265         case V4L2_CID_AUDIO_VOLUME:
266                 typhoon_setvol(dev, ctrl->value);
267                 return 0;
268         }
269         return -EINVAL;
270 }
271
272 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
273 {
274         *i = 0;
275         return 0;
276 }
277
278 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
279 {
280         return i ? -EINVAL : 0;
281 }
282
283 static int vidioc_g_audio(struct file *file, void *priv,
284                                         struct v4l2_audio *a)
285 {
286         a->index = 0;
287         strlcpy(a->name, "Radio", sizeof(a->name));
288         a->capability = V4L2_AUDCAP_STEREO;
289         return 0;
290 }
291
292 static int vidioc_s_audio(struct file *file, void *priv,
293                                         struct v4l2_audio *a)
294 {
295         return a->index ? -EINVAL : 0;
296 }
297
298 static int vidioc_log_status(struct file *file, void *priv)
299 {
300         struct typhoon *dev = video_drvdata(file);
301         struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
302
303         v4l2_info(v4l2_dev, BANNER);
304 #ifdef MODULE
305         v4l2_info(v4l2_dev, "Load type: Driver loaded as a module\n\n");
306 #else
307         v4l2_info(v4l2_dev, "Load type: Driver compiled into kernel\n\n");
308 #endif
309         v4l2_info(v4l2_dev, "frequency = %lu kHz\n", dev->curfreq >> 4);
310         v4l2_info(v4l2_dev, "volume = %d\n", dev->curvol);
311         v4l2_info(v4l2_dev, "mute = %s\n", dev->muted ?  "on" : "off");
312         v4l2_info(v4l2_dev, "io = 0x%x\n", dev->io);
313         v4l2_info(v4l2_dev, "mute frequency = %lu kHz\n", dev->mutefreq >> 4);
314         return 0;
315 }
316
317 static int typhoon_open(struct file *file)
318 {
319         return 0;
320 }
321
322 static int typhoon_release(struct file *file)
323 {
324         return 0;
325 }
326
327 static const struct v4l2_file_operations typhoon_fops = {
328         .owner          = THIS_MODULE,
329         .open           = typhoon_open,
330         .release        = typhoon_release,
331         .ioctl          = video_ioctl2,
332 };
333
334 static const struct v4l2_ioctl_ops typhoon_ioctl_ops = {
335         .vidioc_log_status  = vidioc_log_status,
336         .vidioc_querycap    = vidioc_querycap,
337         .vidioc_g_tuner     = vidioc_g_tuner,
338         .vidioc_s_tuner     = vidioc_s_tuner,
339         .vidioc_g_audio     = vidioc_g_audio,
340         .vidioc_s_audio     = vidioc_s_audio,
341         .vidioc_g_input     = vidioc_g_input,
342         .vidioc_s_input     = vidioc_s_input,
343         .vidioc_g_frequency = vidioc_g_frequency,
344         .vidioc_s_frequency = vidioc_s_frequency,
345         .vidioc_queryctrl   = vidioc_queryctrl,
346         .vidioc_g_ctrl      = vidioc_g_ctrl,
347         .vidioc_s_ctrl      = vidioc_s_ctrl,
348 };
349
350 static int __init typhoon_init(void)
351 {
352         struct typhoon *dev = &typhoon_card;
353         struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
354         int res;
355
356         strlcpy(v4l2_dev->name, "typhoon", sizeof(v4l2_dev->name));
357         dev->io = io;
358         dev->curfreq = dev->mutefreq = mutefreq;
359
360         if (dev->io == -1) {
361                 v4l2_err(v4l2_dev, "You must set an I/O address with io=0x316 or io=0x336\n");
362                 return -EINVAL;
363         }
364
365         if (dev->mutefreq < 87000 || dev->mutefreq > 108500) {
366                 v4l2_err(v4l2_dev, "You must set a frequency (in kHz) used when muting the card,\n");
367                 v4l2_err(v4l2_dev, "e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
368                 return -EINVAL;
369         }
370
371         mutex_init(&dev->lock);
372         if (!request_region(dev->io, 8, "typhoon")) {
373                 v4l2_err(v4l2_dev, "port 0x%x already in use\n",
374                        dev->io);
375                 return -EBUSY;
376         }
377
378         res = v4l2_device_register(NULL, v4l2_dev);
379         if (res < 0) {
380                 release_region(dev->io, 8);
381                 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
382                 return res;
383         }
384         v4l2_info(v4l2_dev, BANNER);
385
386         strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
387         dev->vdev.v4l2_dev = v4l2_dev;
388         dev->vdev.fops = &typhoon_fops;
389         dev->vdev.ioctl_ops = &typhoon_ioctl_ops;
390         dev->vdev.release = video_device_release_empty;
391         video_set_drvdata(&dev->vdev, dev);
392         if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
393                 v4l2_device_unregister(&dev->v4l2_dev);
394                 release_region(dev->io, 8);
395                 return -EINVAL;
396         }
397         v4l2_info(v4l2_dev, "port 0x%x.\n", dev->io);
398         v4l2_info(v4l2_dev, "mute frequency is %lu kHz.\n", dev->mutefreq);
399         dev->mutefreq <<= 4;
400
401         /* mute card - prevents noisy bootups */
402         typhoon_mute(dev);
403
404         return 0;
405 }
406
407 static void __exit typhoon_exit(void)
408 {
409         struct typhoon *dev = &typhoon_card;
410
411         video_unregister_device(&dev->vdev);
412         v4l2_device_unregister(&dev->v4l2_dev);
413         release_region(dev->io, 8);
414 }
415
416 module_init(typhoon_init);
417 module_exit(typhoon_exit);
418