Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / media / radio / radio-aztech.c
1 /* radio-aztech.c - Aztech radio card driver for Linux 2.2
2  *
3  * Adapted to support the Video for Linux API by
4  * Russell Kroll <rkroll@exploits.org>.  Based on original tuner code by:
5  *
6  * Quay Ly
7  * Donald Song
8  * Jason Lewis      (jlewis@twilight.vtc.vsc.edu)
9  * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
10  * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
11  *
12  * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
13  * along with more information on the card itself.
14  *
15  * History:
16  * 1999-02-24   Russell Kroll <rkroll@exploits.org>
17  *              Fine tuning/VIDEO_TUNER_LOW
18  *              Range expanded to 87-108 MHz (from 87.9-107.8)
19  *
20  * Notable changes from the original source:
21  * - includes stripped down to the essentials
22  * - for loops used as delays replaced with udelay()
23  * - #defines removed, changed to static values
24  * - tuning structure changed - no more character arrays, other changes
25 */
26
27 #include <linux/module.h>       /* Modules                      */
28 #include <linux/init.h>         /* Initdata                     */
29 #include <linux/ioport.h>       /* request_region               */
30 #include <linux/delay.h>        /* udelay                       */
31 #include <asm/io.h>             /* outb, outb_p                 */
32 #include <asm/uaccess.h>        /* copy to/from user            */
33 #include <linux/videodev.h>     /* kernel radio structs         */
34 #include <media/v4l2-common.h>
35 #include <linux/config.h>       /* CONFIG_RADIO_AZTECH_PORT     */
36
37 /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
38
39 #ifndef CONFIG_RADIO_AZTECH_PORT
40 #define CONFIG_RADIO_AZTECH_PORT -1
41 #endif
42
43 static int io = CONFIG_RADIO_AZTECH_PORT;
44 static int radio_nr = -1;
45 static int radio_wait_time = 1000;
46 static struct mutex lock;
47
48 struct az_device
49 {
50         int curvol;
51         unsigned long curfreq;
52         int stereo;
53 };
54
55 static int volconvert(int level)
56 {
57         level>>=14;             /* Map 16bits down to 2 bit */
58         level&=3;
59
60         /* convert to card-friendly values */
61         switch (level)
62         {
63                 case 0:
64                         return 0;
65                 case 1:
66                         return 1;
67                 case 2:
68                         return 4;
69                 case 3:
70                         return 5;
71         }
72         return 0;       /* Quieten gcc */
73 }
74
75 static void send_0_byte (struct az_device *dev)
76 {
77         udelay(radio_wait_time);
78         outb_p(2+volconvert(dev->curvol), io);
79         outb_p(64+2+volconvert(dev->curvol), io);
80 }
81
82 static void send_1_byte (struct az_device *dev)
83 {
84         udelay (radio_wait_time);
85         outb_p(128+2+volconvert(dev->curvol), io);
86         outb_p(128+64+2+volconvert(dev->curvol), io);
87 }
88
89 static int az_setvol(struct az_device *dev, int vol)
90 {
91         mutex_lock(&lock);
92         outb (volconvert(vol), io);
93         mutex_unlock(&lock);
94         return 0;
95 }
96
97 /* thanks to Michael Dwyer for giving me a dose of clues in
98  * the signal strength department..
99  *
100  * This card has a stereo bit - bit 0 set = mono, not set = stereo
101  * It also has a "signal" bit - bit 1 set = bad signal, not set = good
102  *
103  */
104
105 static int az_getsigstr(struct az_device *dev)
106 {
107         if (inb(io) & 2)        /* bit set = no signal present */
108                 return 0;
109         return 1;               /* signal present */
110 }
111
112 static int az_getstereo(struct az_device *dev)
113 {
114         if (inb(io) & 1)        /* bit set = mono */
115                 return 0;
116         return 1;               /* stereo */
117 }
118
119 static int az_setfreq(struct az_device *dev, unsigned long frequency)
120 {
121         int  i;
122
123         frequency += 171200;            /* Add 10.7 MHz IF              */
124         frequency /= 800;               /* Convert to 50 kHz units      */
125
126         mutex_lock(&lock);
127
128         send_0_byte (dev);              /*  0: LSB of frequency       */
129
130         for (i = 0; i < 13; i++)        /*   : frequency bits (1-13)  */
131                 if (frequency & (1 << i))
132                         send_1_byte (dev);
133                 else
134                         send_0_byte (dev);
135
136         send_0_byte (dev);              /* 14: test bit - always 0    */
137         send_0_byte (dev);              /* 15: test bit - always 0    */
138         send_0_byte (dev);              /* 16: band data 0 - always 0 */
139         if (dev->stereo)                /* 17: stereo (1 to enable)   */
140                 send_1_byte (dev);
141         else
142                 send_0_byte (dev);
143
144         send_1_byte (dev);              /* 18: band data 1 - unknown  */
145         send_0_byte (dev);              /* 19: time base - always 0   */
146         send_0_byte (dev);              /* 20: spacing (0 = 25 kHz)   */
147         send_1_byte (dev);              /* 21: spacing (1 = 25 kHz)   */
148         send_0_byte (dev);              /* 22: spacing (0 = 25 kHz)   */
149         send_1_byte (dev);              /* 23: AM/FM (FM = 1, always) */
150
151         /* latch frequency */
152
153         udelay (radio_wait_time);
154         outb_p(128+64+volconvert(dev->curvol), io);
155
156         mutex_unlock(&lock);
157
158         return 0;
159 }
160
161 static int az_do_ioctl(struct inode *inode, struct file *file,
162                        unsigned int cmd, void *arg)
163 {
164         struct video_device *dev = video_devdata(file);
165         struct az_device *az = dev->priv;
166
167         switch(cmd)
168         {
169                 case VIDIOCGCAP:
170                 {
171                         struct video_capability *v = arg;
172                         memset(v,0,sizeof(*v));
173                         v->type=VID_TYPE_TUNER;
174                         v->channels=1;
175                         v->audios=1;
176                         strcpy(v->name, "Aztech Radio");
177                         return 0;
178                 }
179                 case VIDIOCGTUNER:
180                 {
181                         struct video_tuner *v = arg;
182                         if(v->tuner)    /* Only 1 tuner */
183                                 return -EINVAL;
184                         v->rangelow=(87*16000);
185                         v->rangehigh=(108*16000);
186                         v->flags=VIDEO_TUNER_LOW;
187                         v->mode=VIDEO_MODE_AUTO;
188                         v->signal=0xFFFF*az_getsigstr(az);
189                         if(az_getstereo(az))
190                                 v->flags|=VIDEO_TUNER_STEREO_ON;
191                         strcpy(v->name, "FM");
192                         return 0;
193                 }
194                 case VIDIOCSTUNER:
195                 {
196                         struct video_tuner *v = arg;
197                         if(v->tuner!=0)
198                                 return -EINVAL;
199                         return 0;
200                 }
201                 case VIDIOCGFREQ:
202                 {
203                         unsigned long *freq = arg;
204                         *freq = az->curfreq;
205                         return 0;
206                 }
207                 case VIDIOCSFREQ:
208                 {
209                         unsigned long *freq = arg;
210                         az->curfreq = *freq;
211                         az_setfreq(az, az->curfreq);
212                         return 0;
213                 }
214                 case VIDIOCGAUDIO:
215                 {
216                         struct video_audio *v = arg;
217                         memset(v,0, sizeof(*v));
218                         v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
219                         if(az->stereo)
220                                 v->mode=VIDEO_SOUND_STEREO;
221                         else
222                                 v->mode=VIDEO_SOUND_MONO;
223                         v->volume=az->curvol;
224                         v->step=16384;
225                         strcpy(v->name, "Radio");
226                         return 0;
227                 }
228                 case VIDIOCSAUDIO:
229                 {
230                         struct video_audio *v = arg;
231                         if(v->audio)
232                                 return -EINVAL;
233                         az->curvol=v->volume;
234
235                         az->stereo=(v->mode&VIDEO_SOUND_STEREO)?1:0;
236                         if(v->flags&VIDEO_AUDIO_MUTE)
237                                 az_setvol(az,0);
238                         else
239                                 az_setvol(az,az->curvol);
240                         return 0;
241                 }
242                 default:
243                         return -ENOIOCTLCMD;
244         }
245 }
246
247 static int az_ioctl(struct inode *inode, struct file *file,
248                     unsigned int cmd, unsigned long arg)
249 {
250         return video_usercopy(inode, file, cmd, arg, az_do_ioctl);
251 }
252
253 static struct az_device aztech_unit;
254
255 static struct file_operations aztech_fops = {
256         .owner          = THIS_MODULE,
257         .open           = video_exclusive_open,
258         .release        = video_exclusive_release,
259         .ioctl          = az_ioctl,
260         .compat_ioctl   = v4l_compat_ioctl32,
261         .llseek         = no_llseek,
262 };
263
264 static struct video_device aztech_radio=
265 {
266         .owner          = THIS_MODULE,
267         .name           = "Aztech radio",
268         .type           = VID_TYPE_TUNER,
269         .hardware       = VID_HARDWARE_AZTECH,
270         .fops           = &aztech_fops,
271 };
272
273 static int __init aztech_init(void)
274 {
275         if(io==-1)
276         {
277                 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
278                 return -EINVAL;
279         }
280
281         if (!request_region(io, 2, "aztech"))
282         {
283                 printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
284                 return -EBUSY;
285         }
286
287         mutex_init(&lock);
288         aztech_radio.priv=&aztech_unit;
289
290         if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1)
291         {
292                 release_region(io,2);
293                 return -EINVAL;
294         }
295
296         printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
297         /* mute card - prevents noisy bootups */
298         outb (0, io);
299         return 0;
300 }
301
302 MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
303 MODULE_DESCRIPTION("A driver for the Aztech radio card.");
304 MODULE_LICENSE("GPL");
305
306 module_param(io, int, 0);
307 module_param(radio_nr, int, 0);
308 MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
309
310 static void __exit aztech_cleanup(void)
311 {
312         video_unregister_device(&aztech_radio);
313         release_region(io,2);
314 }
315
316 module_init(aztech_init);
317 module_exit(aztech_cleanup);