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