]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/media/radio/radio-typhoon.c
V4L/DVB (10892): radio-typhoon: convert to v4l2_device.
[karo-tx-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  * 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 <linux/uaccess.h>      /* copy to/from user              */
41 #include <media/v4l2-device.h>
42 #include <media/v4l2-ioctl.h>
43
44 MODULE_AUTHOR("Dr. Henrik Seidel");
45 MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
46 MODULE_LICENSE("GPL");
47
48 #ifndef CONFIG_RADIO_TYPHOON_PORT
49 #define CONFIG_RADIO_TYPHOON_PORT -1
50 #endif
51
52 #ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
53 #define CONFIG_RADIO_TYPHOON_MUTEFREQ 0
54 #endif
55
56 static int io = CONFIG_RADIO_TYPHOON_PORT;
57 static int radio_nr = -1;
58
59 module_param(io, int, 0);
60 MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
61
62 module_param(radio_nr, int, 0);
63
64 static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ;
65 module_param(mutefreq, ulong, 0);
66 MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
67
68 #define RADIO_VERSION KERNEL_VERSION(0, 1, 1)
69
70 #define BANNER "Typhoon Radio Card driver v0.1.1\n"
71
72 struct typhoon {
73         struct v4l2_device v4l2_dev;
74         struct video_device vdev;
75         int io;
76         int curvol;
77         int muted;
78         unsigned long curfreq;
79         unsigned long mutefreq;
80         struct mutex lock;
81 };
82
83 static struct typhoon typhoon_card;
84
85 static void typhoon_setvol_generic(struct typhoon *dev, int vol)
86 {
87         mutex_lock(&dev->lock);
88         vol >>= 14;                             /* Map 16 bit to 2 bit */
89         vol &= 3;
90         outb_p(vol / 2, dev->io);               /* Set the volume, high bit. */
91         outb_p(vol % 2, dev->io + 2);   /* Set the volume, low bit. */
92         mutex_unlock(&dev->lock);
93 }
94
95 static int typhoon_setfreq_generic(struct typhoon *dev,
96                                    unsigned long frequency)
97 {
98         unsigned long outval;
99         unsigned long x;
100
101         /*
102          * The frequency transfer curve is not linear. The best fit I could
103          * get is
104          *
105          * outval = -155 + exp((f + 15.55) * 0.057))
106          *
107          * where frequency f is in MHz. Since we don't have exp in the kernel,
108          * I approximate this function by a third order polynomial.
109          *
110          */
111
112         mutex_lock(&dev->lock);
113         x = frequency / 160;
114         outval = (x * x + 2500) / 5000;
115         outval = (outval * x + 5000) / 10000;
116         outval -= (10 * x * x + 10433) / 20866;
117         outval += 4 * x - 11505;
118
119         outb_p((outval >> 8) & 0x01, dev->io + 4);
120         outb_p(outval >> 9, dev->io + 6);
121         outb_p(outval & 0xff, dev->io + 8);
122         mutex_unlock(&dev->lock);
123
124         return 0;
125 }
126
127 static int typhoon_setfreq(struct typhoon *dev, unsigned long frequency)
128 {
129         typhoon_setfreq_generic(dev, frequency);
130         dev->curfreq = frequency;
131         return 0;
132 }
133
134 static void typhoon_mute(struct typhoon *dev)
135 {
136         if (dev->muted == 1)
137                 return;
138         typhoon_setvol_generic(dev, 0);
139         typhoon_setfreq_generic(dev, dev->mutefreq);
140         dev->muted = 1;
141 }
142
143 static void typhoon_unmute(struct typhoon *dev)
144 {
145         if (dev->muted == 0)
146                 return;
147         typhoon_setfreq_generic(dev, dev->curfreq);
148         typhoon_setvol_generic(dev, dev->curvol);
149         dev->muted = 0;
150 }
151
152 static int typhoon_setvol(struct typhoon *dev, int vol)
153 {
154         if (dev->muted && vol != 0) {   /* user is unmuting the card */
155                 dev->curvol = vol;
156                 typhoon_unmute(dev);
157                 return 0;
158         }
159         if (vol == dev->curvol)         /* requested volume == current */
160                 return 0;
161
162         if (vol == 0) {                 /* volume == 0 means mute the card */
163                 typhoon_mute(dev);
164                 dev->curvol = vol;
165                 return 0;
166         }
167         typhoon_setvol_generic(dev, vol);
168         dev->curvol = vol;
169         return 0;
170 }
171
172 static int vidioc_querycap(struct file *file, void  *priv,
173                                         struct v4l2_capability *v)
174 {
175         strlcpy(v->driver, "radio-typhoon", sizeof(v->driver));
176         strlcpy(v->card, "Typhoon Radio", sizeof(v->card));
177         strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
178         v->version = RADIO_VERSION;
179         v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
180         return 0;
181 }
182
183 static int vidioc_g_tuner(struct file *file, void *priv,
184                                         struct v4l2_tuner *v)
185 {
186         if (v->index > 0)
187                 return -EINVAL;
188
189         strlcpy(v->name, "FM", sizeof(v->name));
190         v->type = V4L2_TUNER_RADIO;
191         v->rangelow = 87.5 * 16000;
192         v->rangehigh = 108 * 16000;
193         v->rxsubchans = V4L2_TUNER_SUB_MONO;
194         v->capability = V4L2_TUNER_CAP_LOW;
195         v->audmode = V4L2_TUNER_MODE_MONO;
196         v->signal = 0xFFFF;     /* We can't get the signal strength */
197         return 0;
198 }
199
200 static int vidioc_s_tuner(struct file *file, void *priv,
201                                         struct v4l2_tuner *v)
202 {
203         return v->index ? -EINVAL : 0;
204 }
205
206 static int vidioc_g_frequency(struct file *file, void *priv,
207                                         struct v4l2_frequency *f)
208 {
209         struct typhoon *dev = video_drvdata(file);
210
211         f->type = V4L2_TUNER_RADIO;
212         f->frequency = dev->curfreq;
213         return 0;
214 }
215
216 static int vidioc_s_frequency(struct file *file, void *priv,
217                                         struct v4l2_frequency *f)
218 {
219         struct typhoon *dev = video_drvdata(file);
220
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 int typhoon_open(struct file *file)
319 {
320         return 0;
321 }
322
323 static int typhoon_release(struct file *file)
324 {
325         return 0;
326 }
327
328 static const struct v4l2_file_operations typhoon_fops = {
329         .owner          = THIS_MODULE,
330         .open           = typhoon_open,
331         .release        = typhoon_release,
332         .ioctl          = video_ioctl2,
333 };
334
335 static const struct v4l2_ioctl_ops typhoon_ioctl_ops = {
336         .vidioc_log_status  = vidioc_log_status,
337         .vidioc_querycap    = vidioc_querycap,
338         .vidioc_g_tuner     = vidioc_g_tuner,
339         .vidioc_s_tuner     = vidioc_s_tuner,
340         .vidioc_g_audio     = vidioc_g_audio,
341         .vidioc_s_audio     = vidioc_s_audio,
342         .vidioc_g_input     = vidioc_g_input,
343         .vidioc_s_input     = vidioc_s_input,
344         .vidioc_g_frequency = vidioc_g_frequency,
345         .vidioc_s_frequency = vidioc_s_frequency,
346         .vidioc_queryctrl   = vidioc_queryctrl,
347         .vidioc_g_ctrl      = vidioc_g_ctrl,
348         .vidioc_s_ctrl      = vidioc_s_ctrl,
349 };
350
351 static int __init typhoon_init(void)
352 {
353         struct typhoon *dev = &typhoon_card;
354         struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
355         int res;
356
357         strlcpy(v4l2_dev->name, "typhoon", sizeof(v4l2_dev->name));
358         dev->io = io;
359         dev->curfreq = dev->mutefreq = mutefreq;
360
361         if (dev->io == -1) {
362                 v4l2_err(v4l2_dev, "You must set an I/O address with io=0x316 or io=0x336\n");
363                 return -EINVAL;
364         }
365
366         if (dev->mutefreq < 87000 || dev->mutefreq > 108500) {
367                 v4l2_err(v4l2_dev, "You must set a frequency (in kHz) used when muting the card,\n");
368                 v4l2_err(v4l2_dev, "e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
369                 return -EINVAL;
370         }
371
372         mutex_init(&dev->lock);
373         if (!request_region(dev->io, 8, "typhoon")) {
374                 v4l2_err(v4l2_dev, "port 0x%x already in use\n",
375                        dev->io);
376                 return -EBUSY;
377         }
378
379         res = v4l2_device_register(NULL, v4l2_dev);
380         if (res < 0) {
381                 release_region(dev->io, 8);
382                 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
383                 return res;
384         }
385         v4l2_info(v4l2_dev, BANNER);
386
387         strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
388         dev->vdev.v4l2_dev = v4l2_dev;
389         dev->vdev.fops = &typhoon_fops;
390         dev->vdev.ioctl_ops = &typhoon_ioctl_ops;
391         dev->vdev.release = video_device_release_empty;
392         video_set_drvdata(&dev->vdev, dev);
393         if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
394                 v4l2_device_unregister(&dev->v4l2_dev);
395                 release_region(dev->io, 8);
396                 return -EINVAL;
397         }
398         v4l2_info(v4l2_dev, "port 0x%x.\n", dev->io);
399         v4l2_info(v4l2_dev, "mute frequency is %lu kHz.\n", dev->mutefreq);
400         dev->mutefreq <<= 4;
401
402         /* mute card - prevents noisy bootups */
403         typhoon_mute(dev);
404
405         return 0;
406 }
407
408 static void __exit typhoon_exit(void)
409 {
410         struct typhoon *dev = &typhoon_card;
411
412         video_unregister_device(&dev->vdev);
413         v4l2_device_unregister(&dev->v4l2_dev);
414         release_region(dev->io, 8);
415 }
416
417 module_init(typhoon_init);
418 module_exit(typhoon_exit);
419