File: /usr/src/linux/drivers/media/radio/radio-sf16fmi.c
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
5 *
6 * Fitted to new interface by Alan Cox <alan.cox@linux.org>
7 * Made working and cleaned up functions <mikael.hedin@irf.se>
8 * Support for ISAPnP by Ladislav Michl <ladis@psi.cz>
9 *
10 * Notes on the hardware
11 *
12 * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
13 * No volume control - only mute/unmute - you have to use line volume
14 * control on SB-part of SF16FMI
15 *
16 */
17
18 #include <linux/kernel.h> /* __setup */
19 #include <linux/module.h> /* Modules */
20 #include <linux/init.h> /* Initdata */
21 #include <linux/ioport.h> /* check_region, request_region */
22 #include <linux/delay.h> /* udelay */
23 #include <linux/videodev.h> /* kernel radio structs */
24 #include <linux/isapnp.h>
25 #include <asm/io.h> /* outb, outb_p */
26 #include <asm/uaccess.h> /* copy to/from user */
27 #include <asm/semaphore.h>
28
29 struct fmi_device
30 {
31 int port;
32 int curvol; /* 1 or 0 */
33 unsigned long curfreq; /* freq in kHz */
34 __u32 flags;
35 };
36
37 static int io = -1;
38 static int radio_nr = -1;
39 static int users = 0;
40 static struct pci_dev *dev = NULL;
41 static struct semaphore lock;
42
43 /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
44 /* It is only useful 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
47 */
48 #define RSF16_ENCODE(x) ((x)/800+214)
49 #define RSF16_MINFREQ 87*16000
50 #define RSF16_MAXFREQ 108*16000
51
52 static void outbits(int bits, unsigned int data, int port)
53 {
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);
65 }
66 data>>=1;
67 }
68 }
69
70 static inline void fmi_mute(int port)
71 {
72 down(&lock);
73 outb(0x00, port);
74 up(&lock);
75 }
76
77 static inline void fmi_unmute(int port)
78 {
79 down(&lock);
80 outb(0x08, port);
81 up(&lock);
82 }
83
84 static inline int fmi_setfreq(struct fmi_device *dev)
85 {
86 int myport = dev->port;
87 unsigned long freq = dev->curfreq;
88 int i;
89
90 down(&lock);
91
92 outbits(16, RSF16_ENCODE(freq), myport);
93 outbits(8, 0xC0, myport);
94 for(i=0; i< 100; i++)
95 {
96 udelay(1400);
97 if(current->need_resched)
98 schedule();
99 }
100 /* If this becomes allowed use it ...
101 current->state = TASK_UNINTERRUPTIBLE;
102 schedule_timeout(HZ/7);
103 */
104
105 up(&lock);
106 if (dev->curvol) fmi_unmute(myport);
107 return 0;
108 }
109
110 static inline int fmi_getsigstr(struct fmi_device *dev)
111 {
112 int val;
113 int res;
114 int myport = dev->port;
115 int i;
116
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++)
122 {
123 udelay(1400);
124 if(current->need_resched)
125 schedule();
126 }
127 /* If this becomes allowed use it ...
128 current->state = TASK_UNINTERRUPTIBLE;
129 schedule_timeout(HZ/7);
130 */
131 res = (int)inb(myport+1);
132 outb(val, myport);
133
134 up(&lock);
135 return (res & 2) ? 0 : 0xFFFF;
136 }
137
138 static int fmi_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
139 {
140 struct fmi_device *fmi=dev->priv;
141
142 switch(cmd)
143 {
144 case VIDIOCGCAP:
145 {
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;
159 }
160 case VIDIOCGTUNER:
161 {
162 struct video_tuner v;
163 int mult;
164
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;
179 }
180 case VIDIOCSTUNER:
181 {
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;
190 }
191 case VIDIOCGFREQ:
192 {
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;
199 }
200 case VIDIOCSFREQ:
201 {
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;
214 }
215 case VIDIOCGAUDIO:
216 {
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;
230 }
231 case VIDIOCSAUDIO:
232 {
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;
242 }
243 case VIDIOCGUNIT:
244 {
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;
254 }
255 default:
256 return -ENOIOCTLCMD;
257 }
258 }
259
260 static int fmi_open(struct video_device *dev, int flags)
261 {
262 if(users)
263 return -EBUSY;
264 users++;
265 return 0;
266 }
267
268 static void fmi_close(struct video_device *dev)
269 {
270 users--;
271 }
272
273 static struct fmi_device fmi_unit;
274
275 static struct video_device fmi_radio=
276 {
277 owner: THIS_MODULE,
278 name: "SF16FMx radio",
279 type: VID_TYPE_TUNER,
280 hardware: VID_HARDWARE_SF16MI,
281 open: fmi_open,
282 close: fmi_close,
283 ioctl: fmi_ioctl,
284 };
285
286 /* ladis: this is my card. does any other types exist? */
287 static struct isapnp_device_id id_table[] __devinitdata = {
288 { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
289 ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0},
290 { ISAPNP_CARD_END, },
291 };
292
293 MODULE_DEVICE_TABLE(isapnp, id_table);
294
295 static int isapnp_fmi_probe(void)
296 {
297 int i = 0;
298
299 while (id_table[i].card_vendor != 0 && dev == NULL) {
300 dev = isapnp_find_dev(NULL, id_table[i].vendor,
301 id_table[i].function, NULL);
302 }
303
304 if (!dev)
305 return -ENODEV;
306 if (dev->prepare(dev) < 0)
307 return -EAGAIN;
308 if (!(dev->resource[0].flags & IORESOURCE_IO))
309 return -ENODEV;
310 if (dev->activate(dev) < 0) {
311 printk ("radio-sf16fmi: ISAPnP configure failed (out of resources?)\n");
312 return -ENOMEM;
313 }
314
315 i = dev->resource[0].start;
316 printk ("radio-sf16fmi: ISAPnP reports card at %#x\n", i);
317
318 return i;
319 }
320
321 static int __init fmi_init(void)
322 {
323 if (io < 0)
324 io = isapnp_fmi_probe();
325 if (io < 0) {
326 printk(KERN_ERR "radio-sf16fmi: No PnP card found.");
327 return io;
328 }
329 if (!request_region(io, 2, "radio-sf16fmi")) {
330 printk(KERN_ERR "radio-sf16fmi: port 0x%x already in use\n", io);
331 return -EBUSY;
332 }
333
334 fmi_unit.port = io;
335 fmi_unit.curvol = 0;
336 fmi_unit.curfreq = 0;
337 fmi_unit.flags = VIDEO_TUNER_LOW;
338 fmi_radio.priv = &fmi_unit;
339
340 init_MUTEX(&lock);
341
342 if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) == -1) {
343 release_region(io, 2);
344 return -EINVAL;
345 }
346
347 printk(KERN_INFO "SF16FMx radio card driver at 0x%x\n", io);
348 /* mute card - prevents noisy bootups */
349 fmi_mute(io);
350 return 0;
351 }
352
353 MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
354 MODULE_DESCRIPTION("A driver for the SF16MI radio.");
355 MODULE_PARM(io, "i");
356 MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)");
357 MODULE_PARM(radio_nr, "i");
358
359 EXPORT_NO_SYMBOLS;
360
361 static void __exit fmi_cleanup_module(void)
362 {
363 video_unregister_device(&fmi_radio);
364 release_region(io, 2);
365 if (dev)
366 dev->deactivate(dev);
367 }
368
369 module_init(fmi_init);
370 module_exit(fmi_cleanup_module);
371
372 #ifndef MODULE
373 static int __init fmi_setup_io(char *str)
374 {
375 get_option(&str, &io);
376 return 1;
377 }
378
379 __setup("sf16fm=", fmi_setup_io);
380 #endif
381