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