1 /* SF16FMI radio driver for Linux radio support
2 * heavily based on rtrack driver...
4 * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
6 * Fitted to new interface by Alan Cox <alan.cox@linux.org>
8 * Notes on the hardware
10 * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
11 * No volume control - only mute/unmute - you have to use line volume
12 * control on SB-part of SF16FMI
16 #include <linux/module.h> /* Modules */
17 #include <linux/init.h> /* Initdata */
18 #include <linux/ioport.h> /* check_region, request_region */
19 #include <linux/delay.h> /* udelay */
20 #include <asm/io.h> /* outb, outb_p */
21 #include <asm/uaccess.h> /* copy to/from user */
22 #include <linux/videodev.h> /* kernel radio structs */
23 #include <linux/config.h> /* CONFIG_RADIO_SF16MI_PORT */
28 int curvol
; /* 1 or 0 */
29 unsigned long curfreq
; /* RSF16_PREC * freq in MHz */
33 #ifndef CONFIG_RADIO_SF16FMI_PORT
34 #define CONFIG_RADIO_SF16FMI_PORT -1
37 static int io
= CONFIG_RADIO_SF16FMI_PORT
;
40 /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
41 /* It is only usefull to give freq in intervall of 800 (=0.05Mhz),
42 * other bits will be truncated, e.g 92.7400016 -> 92.7, but
45 #define RSF16_ENCODE(x) ((x)/800+214)
46 #define RSF16_MINFREQ 88*16000
47 #define RSF16_MAXFREQ 108*16000
49 static void outbits(int bits
, unsigned int data
, int port
)
67 static void fmi_mute(int port
)
72 static void fmi_unmute(int port
)
77 static int fmi_setfreq(struct fmi_device
*dev
, unsigned long freq
)
79 int myport
= dev
->port
;
81 outbits(16, RSF16_ENCODE(freq
), myport
);
82 outbits(8, 0xC0, myport
);
83 /* we should wait here... */
87 static int fmi_getsigstr(struct fmi_device
*dev
)
91 int myport
= dev
->port
;
93 val
= dev
->curvol
? 0x08 : 0x00; /* unmute/mute */
95 outb(val
| 0x10, myport
);
97 res
= (int)inb(myport
+1);
99 return (res
& 2) ? 0 : 1;
102 static int fmi_ioctl(struct video_device
*dev
, unsigned int cmd
, void *arg
)
104 struct fmi_device
*fmi
=dev
->priv
;
110 struct video_capability v
;
111 strcpy(v
.name
, "SF16-FMx radio");
112 v
.type
=VID_TYPE_TUNER
;
115 /* No we don't do pictures */
120 if(copy_to_user(arg
,&v
,sizeof(v
)))
126 struct video_tuner v
;
129 if(copy_from_user(&v
, arg
,sizeof(v
))!=0)
131 if(v
.tuner
) /* Only 1 tuner */
133 strcpy(v
.name
, "FM");
134 mult
= (fmi
->flags
& VIDEO_TUNER_LOW
) ? 1 : 1000;
135 v
.rangelow
= RSF16_MINFREQ
/mult
;
136 v
.rangehigh
= RSF16_MAXFREQ
/mult
;
138 v
.mode
=VIDEO_MODE_AUTO
;
139 v
.signal
=0xFFFF*fmi_getsigstr(fmi
);
140 if(copy_to_user(arg
,&v
, sizeof(v
)))
146 struct video_tuner v
;
147 if(copy_from_user(&v
, arg
, sizeof(v
)))
151 fmi
->flags
= v
.flags
& VIDEO_TUNER_LOW
;
152 /* Only 1 tuner so no setting needed ! */
157 unsigned long tmp
= fmi
->curfreq
;
158 if (!(fmi
->flags
& VIDEO_TUNER_LOW
))
160 if(copy_to_user(arg
, &tmp
, sizeof(tmp
)))
167 if(copy_from_user(&tmp
, arg
, sizeof(tmp
)))
169 if (!(fmi
->flags
& VIDEO_TUNER_LOW
))
171 if ( tmp
<RSF16_MINFREQ
|| tmp
>RSF16_MAXFREQ
)
174 fmi_setfreq(fmi
, fmi
->curfreq
);
179 struct video_audio v
;
184 v
.flags
=( (!fmi
->curvol
)*VIDEO_AUDIO_MUTE
| VIDEO_AUDIO_MUTABLE
);
185 strcpy(v
.name
, "Radio");
186 v
.mode
=VIDEO_SOUND_MONO
;
188 v
.step
=0; /* No volume, just (un)mute */
189 if(copy_to_user(arg
,&v
, sizeof(v
)))
195 struct video_audio v
;
196 if(copy_from_user(&v
, arg
, sizeof(v
)))
200 fmi
->curvol
= v
.flags
&VIDEO_AUDIO_MUTE
? 0 : 1;
202 fmi_unmute(fmi
->port
) : fmi_mute(fmi
->port
);
208 v
.video
=VIDEO_NO_UNIT
;
211 v
.audio
=0; /* How do we find out this??? */
212 v
.teletext
=VIDEO_NO_UNIT
;
213 if(copy_to_user(arg
, &v
, sizeof(v
)))
222 static int fmi_open(struct video_device
*dev
, int flags
)
231 static void fmi_close(struct video_device
*dev
)
237 static struct fmi_device fmi_unit
;
239 static struct video_device fmi_radio
=
246 NULL
, /* Can't read (no capture ability) */
247 NULL
, /* Can't write */
248 NULL
, /* Can't poll */
254 __initfunc(int fmi_init(struct video_init
*v
))
256 if (check_region(io
, 2))
258 printk(KERN_ERR
"fmi: port 0x%x already in use\n", io
);
264 fmi_unit
.curfreq
= 0;
265 fmi_unit
.flags
= VIDEO_TUNER_LOW
;
266 fmi_radio
.priv
= &fmi_unit
;
268 if(video_register_device(&fmi_radio
, VFL_TYPE_RADIO
)==-1)
271 request_region(io
, 2, "fmi");
272 printk(KERN_INFO
"SF16FMx radio card driver at 0x%x.\n", io
);
273 printk(KERN_INFO
"(c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz.\n");
274 /* mute card - prevents noisy bootups */
281 MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
282 MODULE_DESCRIPTION("A driver for the SF16MI radio.");
283 MODULE_PARM(io
, "i");
284 MODULE_PARM_DESC(io
, "I/O address of the SF16MI card (0x284 or 0x384)");
288 int init_module(void)
292 printk(KERN_ERR
"You must set an I/O address with io=0x???\n");
295 return fmi_init(NULL
);
298 void cleanup_module(void)
300 video_unregister_device(&fmi_radio
);
301 release_region(io
,2);