- Peter Anvin: more P4 configuration parsing
[davej-history.git] / drivers / media / radio / radio-sf16fmi.c
blobe2fa902ff1110f28ee753368871a4653f7497677
1 /* SF16FMI radio driver for Linux radio support
2 * heavily based on rtrack driver...
3 * (c) 1997 M. Kirkwood
4 * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
6 * Fitted to new interface by Alan Cox <alan.cox@linux.org>
7 * Made working and cleaned up functions <mikael.hedin@irf.se>
9 * Notes on the hardware
11 * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
12 * No volume control - only mute/unmute - you have to use line volume
13 * control on SB-part of SF16FMI
17 #include <linux/module.h> /* Modules */
18 #include <linux/init.h> /* Initdata */
19 #include <linux/ioport.h> /* check_region, request_region */
20 #include <linux/delay.h> /* udelay */
21 #include <asm/io.h> /* outb, outb_p */
22 #include <asm/uaccess.h> /* copy to/from user */
23 #include <linux/videodev.h> /* kernel radio structs */
24 #include <linux/config.h> /* CONFIG_RADIO_SF16MI_PORT */
25 #include <asm/semaphore.h>
27 struct fmi_device
29 int port;
30 int curvol; /* 1 or 0 */
31 unsigned long curfreq; /* freq in kHz */
32 __u32 flags;
35 #ifndef CONFIG_RADIO_SF16FMI_PORT
36 #define CONFIG_RADIO_SF16FMI_PORT -1
37 #endif
39 static int io = CONFIG_RADIO_SF16FMI_PORT;
40 static int users = 0;
41 static struct semaphore lock;
43 /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
44 /* It is only usefull to give freq in intervall of 800 (=0.05Mhz),
45 * other bits will be truncated, e.g 92.7400016 -> 92.7, but
46 * 92.7400017 -> 92.75
48 #define RSF16_ENCODE(x) ((x)/800+214)
49 #define RSF16_MINFREQ 87*16000
50 #define RSF16_MAXFREQ 108*16000
52 static void outbits(int bits, unsigned int data, int port)
54 while(bits--) {
55 if(data & 1) {
56 outb(5, port);
57 udelay(6);
58 outb(7, port);
59 udelay(6);
60 } else {
61 outb(1, port);
62 udelay(6);
63 outb(3, port);
64 udelay(6);
66 data>>=1;
70 static inline void fmi_mute(int port)
72 down(&lock);
73 outb(0x00, port);
74 up(&lock);
77 static inline void fmi_unmute(int port)
79 down(&lock);
80 outb(0x08, port);
81 up(&lock);
84 static inline int fmi_setfreq(struct fmi_device *dev)
86 int myport = dev->port;
87 unsigned long freq = dev->curfreq;
88 int i;
90 down(&lock);
92 outbits(16, RSF16_ENCODE(freq), myport);
93 outbits(8, 0xC0, myport);
94 for(i=0; i< 100; i++)
96 udelay(1400);
97 if(current->need_resched)
98 schedule();
100 /* If this becomes allowed use it ...
101 current->state = TASK_UNINTERRUPTIBLE;
102 schedule_timeout(HZ/7);
105 up(&lock);
106 if (dev->curvol) fmi_unmute(myport);
107 return 0;
110 static inline int fmi_getsigstr(struct fmi_device *dev)
112 int val;
113 int res;
114 int myport = dev->port;
115 int i;
117 down(&lock);
118 val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */
119 outb(val, myport);
120 outb(val | 0x10, myport);
121 for(i=0; i< 100; i++)
123 udelay(1400);
124 if(current->need_resched)
125 schedule();
127 /* If this becomes allowed use it ...
128 current->state = TASK_UNINTERRUPTIBLE;
129 schedule_timeout(HZ/7);
131 res = (int)inb(myport+1);
132 outb(val, myport);
134 up(&lock);
135 return (res & 2) ? 0 : 0xFFFF;
138 static int fmi_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
140 struct fmi_device *fmi=dev->priv;
142 switch(cmd)
144 case VIDIOCGCAP:
146 struct video_capability v;
147 strcpy(v.name, "SF16-FMx radio");
148 v.type=VID_TYPE_TUNER;
149 v.channels=1;
150 v.audios=1;
151 /* No we don't do pictures */
152 v.maxwidth=0;
153 v.maxheight=0;
154 v.minwidth=0;
155 v.minheight=0;
156 if(copy_to_user(arg,&v,sizeof(v)))
157 return -EFAULT;
158 return 0;
160 case VIDIOCGTUNER:
162 struct video_tuner v;
163 int mult;
165 if(copy_from_user(&v, arg,sizeof(v))!=0)
166 return -EFAULT;
167 if(v.tuner) /* Only 1 tuner */
168 return -EINVAL;
169 strcpy(v.name, "FM");
170 mult = (fmi->flags & VIDEO_TUNER_LOW) ? 1 : 1000;
171 v.rangelow = RSF16_MINFREQ/mult;
172 v.rangehigh = RSF16_MAXFREQ/mult;
173 v.flags=fmi->flags;
174 v.mode=VIDEO_MODE_AUTO;
175 v.signal = fmi_getsigstr(fmi);
176 if(copy_to_user(arg,&v, sizeof(v)))
177 return -EFAULT;
178 return 0;
180 case VIDIOCSTUNER:
182 struct video_tuner v;
183 if(copy_from_user(&v, arg, sizeof(v)))
184 return -EFAULT;
185 if(v.tuner!=0)
186 return -EINVAL;
187 fmi->flags = v.flags & VIDEO_TUNER_LOW;
188 /* Only 1 tuner so no setting needed ! */
189 return 0;
191 case VIDIOCGFREQ:
193 unsigned long tmp = fmi->curfreq;
194 if (!(fmi->flags & VIDEO_TUNER_LOW))
195 tmp /= 1000;
196 if(copy_to_user(arg, &tmp, sizeof(tmp)))
197 return -EFAULT;
198 return 0;
200 case VIDIOCSFREQ:
202 unsigned long tmp;
203 if(copy_from_user(&tmp, arg, sizeof(tmp)))
204 return -EFAULT;
205 if (!(fmi->flags & VIDEO_TUNER_LOW))
206 tmp *= 1000;
207 if ( tmp<RSF16_MINFREQ || tmp>RSF16_MAXFREQ )
208 return -EINVAL;
209 /*rounding in steps of 800 to match th freq
210 that will be used */
211 fmi->curfreq = (tmp/800)*800;
212 fmi_setfreq(fmi);
213 return 0;
215 case VIDIOCGAUDIO:
217 struct video_audio v;
218 v.audio=0;
219 v.volume=0;
220 v.bass=0;
221 v.treble=0;
222 v.flags=( (!fmi->curvol)*VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE);
223 strcpy(v.name, "Radio");
224 v.mode=VIDEO_SOUND_STEREO;
225 v.balance=0;
226 v.step=0; /* No volume, just (un)mute */
227 if(copy_to_user(arg,&v, sizeof(v)))
228 return -EFAULT;
229 return 0;
231 case VIDIOCSAUDIO:
233 struct video_audio v;
234 if(copy_from_user(&v, arg, sizeof(v)))
235 return -EFAULT;
236 if(v.audio)
237 return -EINVAL;
238 fmi->curvol= v.flags&VIDEO_AUDIO_MUTE ? 0 : 1;
239 fmi->curvol ?
240 fmi_unmute(fmi->port) : fmi_mute(fmi->port);
241 return 0;
243 case VIDIOCGUNIT:
245 struct video_unit v;
246 v.video=VIDEO_NO_UNIT;
247 v.vbi=VIDEO_NO_UNIT;
248 v.radio=dev->minor;
249 v.audio=0; /* How do we find out this??? */
250 v.teletext=VIDEO_NO_UNIT;
251 if(copy_to_user(arg, &v, sizeof(v)))
252 return -EFAULT;
253 return 0;
255 default:
256 return -ENOIOCTLCMD;
260 static int fmi_open(struct video_device *dev, int flags)
262 if(users)
263 return -EBUSY;
264 users++;
265 MOD_INC_USE_COUNT;
266 return 0;
269 static void fmi_close(struct video_device *dev)
271 users--;
272 MOD_DEC_USE_COUNT;
275 static struct fmi_device fmi_unit;
277 static struct video_device fmi_radio=
279 name: "SF16FMx radio",
280 type: VID_TYPE_TUNER,
281 hardware: VID_HARDWARE_SF16MI,
282 open: fmi_open,
283 close: fmi_close,
284 ioctl: fmi_ioctl,
287 static int __init fmi_init(void)
289 if(io==-1)
291 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
292 return -EINVAL;
294 if (request_region(io, 2, "fmi"))
296 printk(KERN_ERR "fmi: port 0x%x already in use\n", io);
297 return -EBUSY;
300 fmi_unit.port = io;
301 fmi_unit.curvol = 0;
302 fmi_unit.curfreq = 0;
303 fmi_unit.flags = VIDEO_TUNER_LOW;
304 fmi_radio.priv = &fmi_unit;
306 init_MUTEX(&lock);
308 if(video_register_device(&fmi_radio, VFL_TYPE_RADIO)==-1)
310 release_region(io, 2);
311 return -EINVAL;
314 printk(KERN_INFO "SF16FMx radio card driver at 0x%x.\n", io);
315 printk(KERN_INFO "(c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz.\n");
316 /* mute card - prevents noisy bootups */
317 fmi_mute(io);
318 return 0;
321 MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
322 MODULE_DESCRIPTION("A driver for the SF16MI radio.");
323 MODULE_PARM(io, "i");
324 MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)");
326 EXPORT_NO_SYMBOLS;
328 static void __exit fmi_cleanup_module(void)
330 video_unregister_device(&fmi_radio);
331 release_region(io,2);
334 module_init(fmi_init);
335 module_exit(fmi_cleanup_module);