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