Merge git://git.infradead.org/~dwmw2/cafe-2.6
[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
37 #include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
38 #define RADIO_VERSION KERNEL_VERSION(0,0,2)
39
40 static struct v4l2_queryctrl radio_qctrl[] = {
41         {
42                 .id            = V4L2_CID_AUDIO_MUTE,
43                 .name          = "Mute",
44                 .minimum       = 0,
45                 .maximum       = 1,
46                 .default_value = 1,
47                 .type          = V4L2_CTRL_TYPE_BOOLEAN,
48         },{
49                 .id            = V4L2_CID_AUDIO_VOLUME,
50                 .name          = "Volume",
51                 .minimum       = 0,
52                 .maximum       = 0xff,
53                 .step          = 1,
54                 .default_value = 0xff,
55                 .type          = V4L2_CTRL_TYPE_INTEGER,
56         }
57 };
58
59 /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
60
61 #ifndef CONFIG_RADIO_AZTECH_PORT
62 #define CONFIG_RADIO_AZTECH_PORT -1
63 #endif
64
65 static int io = CONFIG_RADIO_AZTECH_PORT;
66 static int radio_nr = -1;
67 static int radio_wait_time = 1000;
68 static struct mutex lock;
69
70 struct az_device
71 {
72         int curvol;
73         unsigned long curfreq;
74         int stereo;
75 };
76
77 static int volconvert(int level)
78 {
79         level>>=14;             /* Map 16bits down to 2 bit */
80         level&=3;
81
82         /* convert to card-friendly values */
83         switch (level)
84         {
85                 case 0:
86                         return 0;
87                 case 1:
88                         return 1;
89                 case 2:
90                         return 4;
91                 case 3:
92                         return 5;
93         }
94         return 0;       /* Quieten gcc */
95 }
96
97 static void send_0_byte (struct az_device *dev)
98 {
99         udelay(radio_wait_time);
100         outb_p(2+volconvert(dev->curvol), io);
101         outb_p(64+2+volconvert(dev->curvol), io);
102 }
103
104 static void send_1_byte (struct az_device *dev)
105 {
106         udelay (radio_wait_time);
107         outb_p(128+2+volconvert(dev->curvol), io);
108         outb_p(128+64+2+volconvert(dev->curvol), io);
109 }
110
111 static int az_setvol(struct az_device *dev, int vol)
112 {
113         mutex_lock(&lock);
114         outb (volconvert(vol), io);
115         mutex_unlock(&lock);
116         return 0;
117 }
118
119 /* thanks to Michael Dwyer for giving me a dose of clues in
120  * the signal strength department..
121  *
122  * This card has a stereo bit - bit 0 set = mono, not set = stereo
123  * It also has a "signal" bit - bit 1 set = bad signal, not set = good
124  *
125  */
126
127 static int az_getsigstr(struct az_device *dev)
128 {
129         if (inb(io) & 2)        /* bit set = no signal present */
130                 return 0;
131         return 1;               /* signal present */
132 }
133
134 static int az_getstereo(struct az_device *dev)
135 {
136         if (inb(io) & 1)        /* bit set = mono */
137                 return 0;
138         return 1;               /* stereo */
139 }
140
141 static int az_setfreq(struct az_device *dev, unsigned long frequency)
142 {
143         int  i;
144
145         frequency += 171200;            /* Add 10.7 MHz IF              */
146         frequency /= 800;               /* Convert to 50 kHz units      */
147
148         mutex_lock(&lock);
149
150         send_0_byte (dev);              /*  0: LSB of frequency       */
151
152         for (i = 0; i < 13; i++)        /*   : frequency bits (1-13)  */
153                 if (frequency & (1 << i))
154                         send_1_byte (dev);
155                 else
156                         send_0_byte (dev);
157
158         send_0_byte (dev);              /* 14: test bit - always 0    */
159         send_0_byte (dev);              /* 15: test bit - always 0    */
160         send_0_byte (dev);              /* 16: band data 0 - always 0 */
161         if (dev->stereo)                /* 17: stereo (1 to enable)   */
162                 send_1_byte (dev);
163         else
164                 send_0_byte (dev);
165
166         send_1_byte (dev);              /* 18: band data 1 - unknown  */
167         send_0_byte (dev);              /* 19: time base - always 0   */
168         send_0_byte (dev);              /* 20: spacing (0 = 25 kHz)   */
169         send_1_byte (dev);              /* 21: spacing (1 = 25 kHz)   */
170         send_0_byte (dev);              /* 22: spacing (0 = 25 kHz)   */
171         send_1_byte (dev);              /* 23: AM/FM (FM = 1, always) */
172
173         /* latch frequency */
174
175         udelay (radio_wait_time);
176         outb_p(128+64+volconvert(dev->curvol), io);
177
178         mutex_unlock(&lock);
179
180         return 0;
181 }
182
183 static int az_do_ioctl(struct inode *inode, struct file *file,
184                        unsigned int cmd, void *arg)
185 {
186         struct video_device *dev = video_devdata(file);
187         struct az_device *az = dev->priv;
188
189         switch(cmd)
190         {
191                 case VIDIOC_QUERYCAP:
192                 {
193                         struct v4l2_capability *v = arg;
194                         memset(v,0,sizeof(*v));
195                         strlcpy(v->driver, "radio-aztech", sizeof (v->driver));
196                         strlcpy(v->card, "Aztech Radio", sizeof (v->card));
197                         sprintf(v->bus_info,"ISA");
198                         v->version = RADIO_VERSION;
199                         v->capabilities = V4L2_CAP_TUNER;
200
201                         return 0;
202                 }
203                 case VIDIOC_G_TUNER:
204                 {
205                         struct v4l2_tuner *v = arg;
206
207                         if (v->index > 0)
208                                 return -EINVAL;
209
210                         memset(v,0,sizeof(*v));
211                         strcpy(v->name, "FM");
212                         v->type = V4L2_TUNER_RADIO;
213
214                         v->rangelow=(87*16000);
215                         v->rangehigh=(108*16000);
216                         v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
217                         v->capability=V4L2_TUNER_CAP_LOW;
218                         if(az_getstereo(az))
219                                 v->audmode = V4L2_TUNER_MODE_STEREO;
220                         else
221                                 v->audmode = V4L2_TUNER_MODE_MONO;
222                         v->signal=0xFFFF*az_getsigstr(az);
223
224                         return 0;
225                 }
226                 case VIDIOC_S_TUNER:
227                 {
228                         struct v4l2_tuner *v = arg;
229
230                         if (v->index > 0)
231                                 return -EINVAL;
232
233                         return 0;
234                 }
235                 case VIDIOC_S_FREQUENCY:
236                 {
237                         struct v4l2_frequency *f = arg;
238
239                         az->curfreq = f->frequency;
240                         az_setfreq(az, az->curfreq);
241                         return 0;
242                 }
243                 case VIDIOC_G_FREQUENCY:
244                 {
245                         struct v4l2_frequency *f = arg;
246
247                         f->type = V4L2_TUNER_RADIO;
248                         f->frequency = az->curfreq;
249
250                         return 0;
251                 }
252
253                 case VIDIOC_QUERYCTRL:
254                 {
255                         struct v4l2_queryctrl *qc = arg;
256                         int i;
257
258                         for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
259                                 if (qc->id && qc->id == radio_qctrl[i].id) {
260                                         memcpy(qc, &(radio_qctrl[i]),
261                                                                 sizeof(*qc));
262                                         return (0);
263                                 }
264                         }
265                         return -EINVAL;
266                 }
267                 case VIDIOC_G_CTRL:
268                 {
269                         struct v4l2_control *ctrl= arg;
270
271                         switch (ctrl->id) {
272                                 case V4L2_CID_AUDIO_MUTE:
273                                         if (az->curvol==0)
274                                                 ctrl->value=1;
275                                         else
276                                                 ctrl->value=0;
277                                         return (0);
278                                 case V4L2_CID_AUDIO_VOLUME:
279                                         ctrl->value=az->curvol * 6554;
280                                         return (0);
281                         }
282                         return -EINVAL;
283                 }
284                 case VIDIOC_S_CTRL:
285                 {
286                         struct v4l2_control *ctrl= arg;
287
288                         switch (ctrl->id) {
289                                 case V4L2_CID_AUDIO_MUTE:
290                                         if (ctrl->value) {
291                                                 az_setvol(az,0);
292                                         } else {
293                                                 az_setvol(az,az->curvol);
294                                         }
295                                         return (0);
296                                 case V4L2_CID_AUDIO_VOLUME:
297                                         az_setvol(az,ctrl->value);
298                                         return (0);
299                         }
300                         return -EINVAL;
301                 }
302
303                 default:
304                         return v4l_compat_translate_ioctl(inode,file,cmd,arg,
305                                                           az_do_ioctl);
306         }
307 }
308
309 static int az_ioctl(struct inode *inode, struct file *file,
310                     unsigned int cmd, unsigned long arg)
311 {
312         return video_usercopy(inode, file, cmd, arg, az_do_ioctl);
313 }
314
315 static struct az_device aztech_unit;
316
317 static struct file_operations aztech_fops = {
318         .owner          = THIS_MODULE,
319         .open           = video_exclusive_open,
320         .release        = video_exclusive_release,
321         .ioctl          = az_ioctl,
322         .compat_ioctl   = v4l_compat_ioctl32,
323         .llseek         = no_llseek,
324 };
325
326 static struct video_device aztech_radio=
327 {
328         .owner          = THIS_MODULE,
329         .name           = "Aztech radio",
330         .type           = VID_TYPE_TUNER,
331         .hardware       = 0,
332         .fops           = &aztech_fops,
333 };
334
335 static int __init aztech_init(void)
336 {
337         if(io==-1)
338         {
339                 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
340                 return -EINVAL;
341         }
342
343         if (!request_region(io, 2, "aztech"))
344         {
345                 printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
346                 return -EBUSY;
347         }
348
349         mutex_init(&lock);
350         aztech_radio.priv=&aztech_unit;
351
352         if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1)
353         {
354                 release_region(io,2);
355                 return -EINVAL;
356         }
357
358         printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
359         /* mute card - prevents noisy bootups */
360         outb (0, io);
361         return 0;
362 }
363
364 MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
365 MODULE_DESCRIPTION("A driver for the Aztech radio card.");
366 MODULE_LICENSE("GPL");
367
368 module_param(io, int, 0);
369 module_param(radio_nr, int, 0);
370 MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
371
372 static void __exit aztech_cleanup(void)
373 {
374         video_unregister_device(&aztech_radio);
375         release_region(io,2);
376 }
377
378 module_init(aztech_init);
379 module_exit(aztech_cleanup);