Merge branch 'topic/snd_card_new-err' into for-linus
[linux-2.6] / drivers / media / radio / radio-aztech.c
1 /* radio-aztech.c - Aztech radio card driver for Linux 2.2
2  *
3  * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
4  * Adapted to support the Video for Linux API by
5  * Russell Kroll <rkroll@exploits.org>.  Based on original tuner code by:
6  *
7  * Quay Ly
8  * Donald Song
9  * Jason Lewis      (jlewis@twilight.vtc.vsc.edu)
10  * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
11  * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
12  *
13  * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
14  * along with more information on the card itself.
15  *
16  * History:
17  * 1999-02-24   Russell Kroll <rkroll@exploits.org>
18  *              Fine tuning/VIDEO_TUNER_LOW
19  *              Range expanded to 87-108 MHz (from 87.9-107.8)
20  *
21  * Notable changes from the original source:
22  * - includes stripped down to the essentials
23  * - for loops used as delays replaced with udelay()
24  * - #defines removed, changed to static values
25  * - tuning structure changed - no more character arrays, other changes
26 */
27
28 #include <linux/module.h>       /* Modules                      */
29 #include <linux/init.h>         /* Initdata                     */
30 #include <linux/ioport.h>       /* request_region               */
31 #include <linux/delay.h>        /* udelay                       */
32 #include <asm/io.h>             /* outb, outb_p                 */
33 #include <asm/uaccess.h>        /* copy to/from user            */
34 #include <linux/videodev2.h>    /* kernel radio structs         */
35 #include <media/v4l2-common.h>
36 #include <media/v4l2-ioctl.h>
37
38 #include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
39 #define RADIO_VERSION KERNEL_VERSION(0,0,2)
40
41 static struct v4l2_queryctrl radio_qctrl[] = {
42         {
43                 .id            = V4L2_CID_AUDIO_MUTE,
44                 .name          = "Mute",
45                 .minimum       = 0,
46                 .maximum       = 1,
47                 .default_value = 1,
48                 .type          = V4L2_CTRL_TYPE_BOOLEAN,
49         },{
50                 .id            = V4L2_CID_AUDIO_VOLUME,
51                 .name          = "Volume",
52                 .minimum       = 0,
53                 .maximum       = 0xff,
54                 .step          = 1,
55                 .default_value = 0xff,
56                 .type          = V4L2_CTRL_TYPE_INTEGER,
57         }
58 };
59
60 /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
61
62 #ifndef CONFIG_RADIO_AZTECH_PORT
63 #define CONFIG_RADIO_AZTECH_PORT -1
64 #endif
65
66 static int io = CONFIG_RADIO_AZTECH_PORT;
67 static int radio_nr = -1;
68 static int radio_wait_time = 1000;
69 static struct mutex lock;
70
71 struct az_device
72 {
73         unsigned long in_use;
74         int curvol;
75         unsigned long curfreq;
76         int stereo;
77 };
78
79 static int volconvert(int level)
80 {
81         level>>=14;             /* Map 16bits down to 2 bit */
82         level&=3;
83
84         /* convert to card-friendly values */
85         switch (level)
86         {
87                 case 0:
88                         return 0;
89                 case 1:
90                         return 1;
91                 case 2:
92                         return 4;
93                 case 3:
94                         return 5;
95         }
96         return 0;       /* Quieten gcc */
97 }
98
99 static void send_0_byte (struct az_device *dev)
100 {
101         udelay(radio_wait_time);
102         outb_p(2+volconvert(dev->curvol), io);
103         outb_p(64+2+volconvert(dev->curvol), io);
104 }
105
106 static void send_1_byte (struct az_device *dev)
107 {
108         udelay (radio_wait_time);
109         outb_p(128+2+volconvert(dev->curvol), io);
110         outb_p(128+64+2+volconvert(dev->curvol), io);
111 }
112
113 static int az_setvol(struct az_device *dev, int vol)
114 {
115         mutex_lock(&lock);
116         outb (volconvert(vol), io);
117         mutex_unlock(&lock);
118         return 0;
119 }
120
121 /* thanks to Michael Dwyer for giving me a dose of clues in
122  * the signal strength department..
123  *
124  * This card has a stereo bit - bit 0 set = mono, not set = stereo
125  * It also has a "signal" bit - bit 1 set = bad signal, not set = good
126  *
127  */
128
129 static int az_getsigstr(struct az_device *dev)
130 {
131         if (inb(io) & 2)        /* bit set = no signal present */
132                 return 0;
133         return 1;               /* signal present */
134 }
135
136 static int az_getstereo(struct az_device *dev)
137 {
138         if (inb(io) & 1)        /* bit set = mono */
139                 return 0;
140         return 1;               /* stereo */
141 }
142
143 static int az_setfreq(struct az_device *dev, unsigned long frequency)
144 {
145         int  i;
146
147         frequency += 171200;            /* Add 10.7 MHz IF              */
148         frequency /= 800;               /* Convert to 50 kHz units      */
149
150         mutex_lock(&lock);
151
152         send_0_byte (dev);              /*  0: LSB of frequency       */
153
154         for (i = 0; i < 13; i++)        /*   : frequency bits (1-13)  */
155                 if (frequency & (1 << i))
156                         send_1_byte (dev);
157                 else
158                         send_0_byte (dev);
159
160         send_0_byte (dev);              /* 14: test bit - always 0    */
161         send_0_byte (dev);              /* 15: test bit - always 0    */
162         send_0_byte (dev);              /* 16: band data 0 - always 0 */
163         if (dev->stereo)                /* 17: stereo (1 to enable)   */
164                 send_1_byte (dev);
165         else
166                 send_0_byte (dev);
167
168         send_1_byte (dev);              /* 18: band data 1 - unknown  */
169         send_0_byte (dev);              /* 19: time base - always 0   */
170         send_0_byte (dev);              /* 20: spacing (0 = 25 kHz)   */
171         send_1_byte (dev);              /* 21: spacing (1 = 25 kHz)   */
172         send_0_byte (dev);              /* 22: spacing (0 = 25 kHz)   */
173         send_1_byte (dev);              /* 23: AM/FM (FM = 1, always) */
174
175         /* latch frequency */
176
177         udelay (radio_wait_time);
178         outb_p(128+64+volconvert(dev->curvol), io);
179
180         mutex_unlock(&lock);
181
182         return 0;
183 }
184
185 static int vidioc_querycap (struct file *file, void  *priv,
186                                         struct v4l2_capability *v)
187 {
188         strlcpy(v->driver, "radio-aztech", sizeof (v->driver));
189         strlcpy(v->card, "Aztech Radio", sizeof (v->card));
190         sprintf(v->bus_info,"ISA");
191         v->version = RADIO_VERSION;
192         v->capabilities = V4L2_CAP_TUNER;
193         return 0;
194 }
195
196 static int vidioc_g_tuner (struct file *file, void *priv,
197                                 struct v4l2_tuner *v)
198 {
199         struct az_device *az = video_drvdata(file);
200
201         if (v->index > 0)
202                 return -EINVAL;
203
204         strcpy(v->name, "FM");
205         v->type = V4L2_TUNER_RADIO;
206
207         v->rangelow=(87*16000);
208         v->rangehigh=(108*16000);
209         v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
210         v->capability=V4L2_TUNER_CAP_LOW;
211         if(az_getstereo(az))
212                 v->audmode = V4L2_TUNER_MODE_STEREO;
213         else
214                 v->audmode = V4L2_TUNER_MODE_MONO;
215         v->signal=0xFFFF*az_getsigstr(az);
216
217         return 0;
218 }
219
220
221 static int vidioc_s_tuner (struct file *file, void *priv,
222                                 struct v4l2_tuner *v)
223 {
224         if (v->index > 0)
225                 return -EINVAL;
226
227         return 0;
228 }
229
230 static int vidioc_g_audio (struct file *file, void *priv,
231                            struct v4l2_audio *a)
232 {
233         if (a->index > 1)
234                 return -EINVAL;
235
236         strcpy(a->name, "Radio");
237         a->capability = V4L2_AUDCAP_STEREO;
238         return 0;
239 }
240
241 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
242 {
243         *i = 0;
244         return 0;
245 }
246
247 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
248 {
249         if (i != 0)
250                 return -EINVAL;
251         return 0;
252 }
253
254
255 static int vidioc_s_audio (struct file *file, void *priv,
256                            struct v4l2_audio *a)
257 {
258         if (a->index != 0)
259                 return -EINVAL;
260
261         return 0;
262 }
263
264 static int vidioc_s_frequency (struct file *file, void *priv,
265                                 struct v4l2_frequency *f)
266 {
267         struct az_device *az = video_drvdata(file);
268
269         az->curfreq = f->frequency;
270         az_setfreq(az, az->curfreq);
271         return 0;
272 }
273
274 static int vidioc_g_frequency (struct file *file, void *priv,
275                                 struct v4l2_frequency *f)
276 {
277         struct az_device *az = video_drvdata(file);
278
279         f->type = V4L2_TUNER_RADIO;
280         f->frequency = az->curfreq;
281
282         return 0;
283 }
284
285 static int vidioc_queryctrl (struct file *file, void *priv,
286                             struct v4l2_queryctrl *qc)
287 {
288         int i;
289
290         for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
291                 if (qc->id && qc->id == radio_qctrl[i].id) {
292                         memcpy(qc, &(radio_qctrl[i]),
293                                                 sizeof(*qc));
294                         return (0);
295                 }
296         }
297         return -EINVAL;
298 }
299
300 static int vidioc_g_ctrl (struct file *file, void *priv,
301                             struct v4l2_control *ctrl)
302 {
303         struct az_device *az = video_drvdata(file);
304
305         switch (ctrl->id) {
306                 case V4L2_CID_AUDIO_MUTE:
307                         if (az->curvol==0)
308                                 ctrl->value=1;
309                         else
310                                 ctrl->value=0;
311                         return (0);
312                 case V4L2_CID_AUDIO_VOLUME:
313                         ctrl->value=az->curvol * 6554;
314                         return (0);
315         }
316         return -EINVAL;
317 }
318
319 static int vidioc_s_ctrl (struct file *file, void *priv,
320                             struct v4l2_control *ctrl)
321 {
322         struct az_device *az = video_drvdata(file);
323
324         switch (ctrl->id) {
325                 case V4L2_CID_AUDIO_MUTE:
326                         if (ctrl->value) {
327                                 az_setvol(az,0);
328                         } else {
329                                 az_setvol(az,az->curvol);
330                         }
331                         return (0);
332                 case V4L2_CID_AUDIO_VOLUME:
333                         az_setvol(az,ctrl->value);
334                         return (0);
335         }
336         return -EINVAL;
337 }
338
339 static struct az_device aztech_unit;
340
341 static int aztech_exclusive_open(struct file *file)
342 {
343         return test_and_set_bit(0, &aztech_unit.in_use) ? -EBUSY : 0;
344 }
345
346 static int aztech_exclusive_release(struct file *file)
347 {
348         clear_bit(0, &aztech_unit.in_use);
349         return 0;
350 }
351
352 static const struct v4l2_file_operations aztech_fops = {
353         .owner          = THIS_MODULE,
354         .open           = aztech_exclusive_open,
355         .release        = aztech_exclusive_release,
356         .ioctl          = video_ioctl2,
357 };
358
359 static const struct v4l2_ioctl_ops aztech_ioctl_ops = {
360         .vidioc_querycap    = vidioc_querycap,
361         .vidioc_g_tuner     = vidioc_g_tuner,
362         .vidioc_s_tuner     = vidioc_s_tuner,
363         .vidioc_g_audio     = vidioc_g_audio,
364         .vidioc_s_audio     = vidioc_s_audio,
365         .vidioc_g_input     = vidioc_g_input,
366         .vidioc_s_input     = vidioc_s_input,
367         .vidioc_g_frequency = vidioc_g_frequency,
368         .vidioc_s_frequency = vidioc_s_frequency,
369         .vidioc_queryctrl   = vidioc_queryctrl,
370         .vidioc_g_ctrl      = vidioc_g_ctrl,
371         .vidioc_s_ctrl      = vidioc_s_ctrl,
372 };
373
374 static struct video_device aztech_radio = {
375         .name           = "Aztech radio",
376         .fops           = &aztech_fops,
377         .ioctl_ops      = &aztech_ioctl_ops,
378         .release        = video_device_release_empty,
379 };
380
381 module_param_named(debug,aztech_radio.debug, int, 0644);
382 MODULE_PARM_DESC(debug,"activates debug info");
383
384 static int __init aztech_init(void)
385 {
386         if(io==-1)
387         {
388                 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
389                 return -EINVAL;
390         }
391
392         if (!request_region(io, 2, "aztech"))
393         {
394                 printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
395                 return -EBUSY;
396         }
397
398         mutex_init(&lock);
399         video_set_drvdata(&aztech_radio, &aztech_unit);
400
401         if (video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
402                 release_region(io,2);
403                 return -EINVAL;
404         }
405
406         printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
407         /* mute card - prevents noisy bootups */
408         outb (0, io);
409         return 0;
410 }
411
412 MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
413 MODULE_DESCRIPTION("A driver for the Aztech radio card.");
414 MODULE_LICENSE("GPL");
415
416 module_param(io, int, 0);
417 module_param(radio_nr, int, 0);
418 MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
419
420 static void __exit aztech_cleanup(void)
421 {
422         video_unregister_device(&aztech_radio);
423         release_region(io,2);
424 }
425
426 module_init(aztech_init);
427 module_exit(aztech_cleanup);