11da177e4SLinus Torvalds /* SF16FMI radio driver for Linux radio support 21da177e4SLinus Torvalds * heavily based on rtrack driver... 31da177e4SLinus Torvalds * (c) 1997 M. Kirkwood 41da177e4SLinus Torvalds * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * Fitted to new interface by Alan Cox <alan.cox@linux.org> 71da177e4SLinus Torvalds * Made working and cleaned up functions <mikael.hedin@irf.se> 81da177e4SLinus Torvalds * Support for ISAPnP by Ladislav Michl <ladis@psi.cz> 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * Notes on the hardware 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); 131da177e4SLinus Torvalds * No volume control - only mute/unmute - you have to use line volume 141da177e4SLinus Torvalds * control on SB-part of SF16FMI 151da177e4SLinus Torvalds * 16a2ef73afSMauro Carvalho Chehab * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 171da177e4SLinus Torvalds */ 181da177e4SLinus Torvalds 192cd885aaSAndrew Morton #include <linux/version.h> 201da177e4SLinus Torvalds #include <linux/kernel.h> /* __setup */ 211da177e4SLinus Torvalds #include <linux/module.h> /* Modules */ 221da177e4SLinus Torvalds #include <linux/init.h> /* Initdata */ 23fb911ee8SPeter Osterlund #include <linux/ioport.h> /* request_region */ 241da177e4SLinus Torvalds #include <linux/delay.h> /* udelay */ 25a2ef73afSMauro Carvalho Chehab #include <linux/videodev2.h> /* kernel radio structs */ 265e87efa3SMauro Carvalho Chehab #include <media/v4l2-common.h> 2735ea11ffSHans Verkuil #include <media/v4l2-ioctl.h> 281da177e4SLinus Torvalds #include <linux/isapnp.h> 291da177e4SLinus Torvalds #include <asm/io.h> /* outb, outb_p */ 301da177e4SLinus Torvalds #include <asm/uaccess.h> /* copy to/from user */ 313593cab5SIngo Molnar #include <linux/mutex.h> 321da177e4SLinus Torvalds 33a2ef73afSMauro Carvalho Chehab #define RADIO_VERSION KERNEL_VERSION(0,0,2) 34a2ef73afSMauro Carvalho Chehab 35a2ef73afSMauro Carvalho Chehab static struct v4l2_queryctrl radio_qctrl[] = { 36a2ef73afSMauro Carvalho Chehab { 37a2ef73afSMauro Carvalho Chehab .id = V4L2_CID_AUDIO_MUTE, 38a2ef73afSMauro Carvalho Chehab .name = "Mute", 39a2ef73afSMauro Carvalho Chehab .minimum = 0, 40a2ef73afSMauro Carvalho Chehab .maximum = 1, 41a2ef73afSMauro Carvalho Chehab .default_value = 1, 42a2ef73afSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_BOOLEAN, 43a2ef73afSMauro Carvalho Chehab } 44a2ef73afSMauro Carvalho Chehab }; 45a2ef73afSMauro Carvalho Chehab 461da177e4SLinus Torvalds struct fmi_device 471da177e4SLinus Torvalds { 481da177e4SLinus Torvalds int port; 491da177e4SLinus Torvalds int curvol; /* 1 or 0 */ 501da177e4SLinus Torvalds unsigned long curfreq; /* freq in kHz */ 511da177e4SLinus Torvalds __u32 flags; 521da177e4SLinus Torvalds }; 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds static int io = -1; 551da177e4SLinus Torvalds static int radio_nr = -1; 561da177e4SLinus Torvalds static struct pnp_dev *dev = NULL; 573593cab5SIngo Molnar static struct mutex lock; 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ 601da177e4SLinus Torvalds /* It is only useful to give freq in intervall of 800 (=0.05Mhz), 611da177e4SLinus Torvalds * other bits will be truncated, e.g 92.7400016 -> 92.7, but 621da177e4SLinus Torvalds * 92.7400017 -> 92.75 631da177e4SLinus Torvalds */ 641da177e4SLinus Torvalds #define RSF16_ENCODE(x) ((x)/800+214) 651da177e4SLinus Torvalds #define RSF16_MINFREQ 87*16000 661da177e4SLinus Torvalds #define RSF16_MAXFREQ 108*16000 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds static void outbits(int bits, unsigned int data, int port) 691da177e4SLinus Torvalds { 701da177e4SLinus Torvalds while(bits--) { 711da177e4SLinus Torvalds if(data & 1) { 721da177e4SLinus Torvalds outb(5, port); 731da177e4SLinus Torvalds udelay(6); 741da177e4SLinus Torvalds outb(7, port); 751da177e4SLinus Torvalds udelay(6); 761da177e4SLinus Torvalds } else { 771da177e4SLinus Torvalds outb(1, port); 781da177e4SLinus Torvalds udelay(6); 791da177e4SLinus Torvalds outb(3, port); 801da177e4SLinus Torvalds udelay(6); 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds data>>=1; 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds static inline void fmi_mute(int port) 871da177e4SLinus Torvalds { 883593cab5SIngo Molnar mutex_lock(&lock); 891da177e4SLinus Torvalds outb(0x00, port); 903593cab5SIngo Molnar mutex_unlock(&lock); 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds static inline void fmi_unmute(int port) 941da177e4SLinus Torvalds { 953593cab5SIngo Molnar mutex_lock(&lock); 961da177e4SLinus Torvalds outb(0x08, port); 973593cab5SIngo Molnar mutex_unlock(&lock); 981da177e4SLinus Torvalds } 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds static inline int fmi_setfreq(struct fmi_device *dev) 1011da177e4SLinus Torvalds { 1021da177e4SLinus Torvalds int myport = dev->port; 1031da177e4SLinus Torvalds unsigned long freq = dev->curfreq; 1041da177e4SLinus Torvalds 1053593cab5SIngo Molnar mutex_lock(&lock); 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds outbits(16, RSF16_ENCODE(freq), myport); 1081da177e4SLinus Torvalds outbits(8, 0xC0, myport); 1091da177e4SLinus Torvalds msleep(143); /* was schedule_timeout(HZ/7) */ 1103593cab5SIngo Molnar mutex_unlock(&lock); 1111da177e4SLinus Torvalds if (dev->curvol) fmi_unmute(myport); 1121da177e4SLinus Torvalds return 0; 1131da177e4SLinus Torvalds } 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds static inline int fmi_getsigstr(struct fmi_device *dev) 1161da177e4SLinus Torvalds { 1171da177e4SLinus Torvalds int val; 1181da177e4SLinus Torvalds int res; 1191da177e4SLinus Torvalds int myport = dev->port; 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds 1223593cab5SIngo Molnar mutex_lock(&lock); 1231da177e4SLinus Torvalds val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */ 1241da177e4SLinus Torvalds outb(val, myport); 1251da177e4SLinus Torvalds outb(val | 0x10, myport); 1261da177e4SLinus Torvalds msleep(143); /* was schedule_timeout(HZ/7) */ 1271da177e4SLinus Torvalds res = (int)inb(myport+1); 1281da177e4SLinus Torvalds outb(val, myport); 1291da177e4SLinus Torvalds 1303593cab5SIngo Molnar mutex_unlock(&lock); 1311da177e4SLinus Torvalds return (res & 2) ? 0 : 0xFFFF; 1321da177e4SLinus Torvalds } 1331da177e4SLinus Torvalds 134c123b867SDouglas Landgraf static int vidioc_querycap(struct file *file, void *priv, 135c123b867SDouglas Landgraf struct v4l2_capability *v) 1361da177e4SLinus Torvalds { 137a2ef73afSMauro Carvalho Chehab strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver)); 138a2ef73afSMauro Carvalho Chehab strlcpy(v->card, "SF16-FMx radio", sizeof(v->card)); 139a2ef73afSMauro Carvalho Chehab sprintf(v->bus_info, "ISA"); 140a2ef73afSMauro Carvalho Chehab v->version = RADIO_VERSION; 141a2ef73afSMauro Carvalho Chehab v->capabilities = V4L2_CAP_TUNER; 1421da177e4SLinus Torvalds return 0; 1431da177e4SLinus Torvalds } 144c123b867SDouglas Landgraf 145c123b867SDouglas Landgraf static int vidioc_g_tuner(struct file *file, void *priv, 146c123b867SDouglas Landgraf struct v4l2_tuner *v) 1471da177e4SLinus Torvalds { 1481da177e4SLinus Torvalds int mult; 149c123b867SDouglas Landgraf struct video_device *dev = video_devdata(file); 150c123b867SDouglas Landgraf struct fmi_device *fmi = dev->priv; 1511da177e4SLinus Torvalds 152a2ef73afSMauro Carvalho Chehab if (v->index > 0) 1531da177e4SLinus Torvalds return -EINVAL; 154a2ef73afSMauro Carvalho Chehab 1551da177e4SLinus Torvalds strcpy(v->name, "FM"); 156a2ef73afSMauro Carvalho Chehab v->type = V4L2_TUNER_RADIO; 157a2ef73afSMauro Carvalho Chehab mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; 1581da177e4SLinus Torvalds v->rangelow = RSF16_MINFREQ/mult; 1591da177e4SLinus Torvalds v->rangehigh = RSF16_MAXFREQ/mult; 160a2ef73afSMauro Carvalho Chehab v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; 161fa38ad65SMauro Carvalho Chehab v->capability = fmi->flags&V4L2_TUNER_CAP_LOW; 162a2ef73afSMauro Carvalho Chehab v->audmode = V4L2_TUNER_MODE_STEREO; 1631da177e4SLinus Torvalds v->signal = fmi_getsigstr(fmi); 1641da177e4SLinus Torvalds return 0; 1651da177e4SLinus Torvalds } 166a2ef73afSMauro Carvalho Chehab 167c123b867SDouglas Landgraf static int vidioc_s_tuner(struct file *file, void *priv, 168c123b867SDouglas Landgraf struct v4l2_tuner *v) 169c123b867SDouglas Landgraf { 170a2ef73afSMauro Carvalho Chehab if (v->index > 0) 1711da177e4SLinus Torvalds return -EINVAL; 1721da177e4SLinus Torvalds return 0; 1731da177e4SLinus Torvalds } 174c123b867SDouglas Landgraf 175c123b867SDouglas Landgraf static int vidioc_s_frequency(struct file *file, void *priv, 176c123b867SDouglas Landgraf struct v4l2_frequency *f) 1771da177e4SLinus Torvalds { 178c123b867SDouglas Landgraf struct video_device *dev = video_devdata(file); 179c123b867SDouglas Landgraf struct fmi_device *fmi = dev->priv; 180a2ef73afSMauro Carvalho Chehab 181a2ef73afSMauro Carvalho Chehab if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) 182a2ef73afSMauro Carvalho Chehab f->frequency *= 1000; 183a2ef73afSMauro Carvalho Chehab if (f->frequency < RSF16_MINFREQ || 184a2ef73afSMauro Carvalho Chehab f->frequency > RSF16_MAXFREQ ) 1851da177e4SLinus Torvalds return -EINVAL; 1861da177e4SLinus Torvalds /*rounding in steps of 800 to match th freq 1871da177e4SLinus Torvalds that will be used */ 188a2ef73afSMauro Carvalho Chehab fmi->curfreq = (f->frequency/800)*800; 1891da177e4SLinus Torvalds fmi_setfreq(fmi); 1901da177e4SLinus Torvalds return 0; 1911da177e4SLinus Torvalds } 192c123b867SDouglas Landgraf 193c123b867SDouglas Landgraf static int vidioc_g_frequency(struct file *file, void *priv, 194c123b867SDouglas Landgraf struct v4l2_frequency *f) 1951da177e4SLinus Torvalds { 196c123b867SDouglas Landgraf struct video_device *dev = video_devdata(file); 197c123b867SDouglas Landgraf struct fmi_device *fmi = dev->priv; 198a2ef73afSMauro Carvalho Chehab 199a2ef73afSMauro Carvalho Chehab f->type = V4L2_TUNER_RADIO; 200a2ef73afSMauro Carvalho Chehab f->frequency = fmi->curfreq; 201a2ef73afSMauro Carvalho Chehab if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) 202a2ef73afSMauro Carvalho Chehab f->frequency /= 1000; 2031da177e4SLinus Torvalds return 0; 2041da177e4SLinus Torvalds } 205c123b867SDouglas Landgraf 206c123b867SDouglas Landgraf static int vidioc_queryctrl(struct file *file, void *priv, 207c123b867SDouglas Landgraf struct v4l2_queryctrl *qc) 2081da177e4SLinus Torvalds { 209a2ef73afSMauro Carvalho Chehab int i; 210a2ef73afSMauro Carvalho Chehab 211a2ef73afSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 212a2ef73afSMauro Carvalho Chehab if (qc->id && qc->id == radio_qctrl[i].id) { 213a2ef73afSMauro Carvalho Chehab memcpy(qc, &(radio_qctrl[i]), 214a2ef73afSMauro Carvalho Chehab sizeof(*qc)); 215c123b867SDouglas Landgraf return 0; 216a2ef73afSMauro Carvalho Chehab } 217a2ef73afSMauro Carvalho Chehab } 2181da177e4SLinus Torvalds return -EINVAL; 2191da177e4SLinus Torvalds } 220c123b867SDouglas Landgraf 221c123b867SDouglas Landgraf static int vidioc_g_ctrl(struct file *file, void *priv, 222c123b867SDouglas Landgraf struct v4l2_control *ctrl) 2231da177e4SLinus Torvalds { 224c123b867SDouglas Landgraf struct video_device *dev = video_devdata(file); 225c123b867SDouglas Landgraf struct fmi_device *fmi = dev->priv; 226a2ef73afSMauro Carvalho Chehab 227a2ef73afSMauro Carvalho Chehab switch (ctrl->id) { 228a2ef73afSMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 229a2ef73afSMauro Carvalho Chehab ctrl->value = fmi->curvol; 230c123b867SDouglas Landgraf return 0; 231a2ef73afSMauro Carvalho Chehab } 232a2ef73afSMauro Carvalho Chehab return -EINVAL; 233a2ef73afSMauro Carvalho Chehab } 234c123b867SDouglas Landgraf 235c123b867SDouglas Landgraf static int vidioc_s_ctrl(struct file *file, void *priv, 236c123b867SDouglas Landgraf struct v4l2_control *ctrl) 237a2ef73afSMauro Carvalho Chehab { 238c123b867SDouglas Landgraf struct video_device *dev = video_devdata(file); 239c123b867SDouglas Landgraf struct fmi_device *fmi = dev->priv; 240a2ef73afSMauro Carvalho Chehab 241a2ef73afSMauro Carvalho Chehab switch (ctrl->id) { 242a2ef73afSMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 243a2ef73afSMauro Carvalho Chehab if (ctrl->value) 244a2ef73afSMauro Carvalho Chehab fmi_mute(fmi->port); 245a2ef73afSMauro Carvalho Chehab else 246a2ef73afSMauro Carvalho Chehab fmi_unmute(fmi->port); 247a2ef73afSMauro Carvalho Chehab fmi->curvol = ctrl->value; 248c123b867SDouglas Landgraf return 0; 249a2ef73afSMauro Carvalho Chehab } 250a2ef73afSMauro Carvalho Chehab return -EINVAL; 2511da177e4SLinus Torvalds } 252c123b867SDouglas Landgraf 253c123b867SDouglas Landgraf static int vidioc_g_audio(struct file *file, void *priv, 254c123b867SDouglas Landgraf struct v4l2_audio *a) 255c123b867SDouglas Landgraf { 256c123b867SDouglas Landgraf if (a->index > 1) 257c123b867SDouglas Landgraf return -EINVAL; 258c123b867SDouglas Landgraf 259c123b867SDouglas Landgraf strcpy(a->name, "Radio"); 260c123b867SDouglas Landgraf a->capability = V4L2_AUDCAP_STEREO; 261c123b867SDouglas Landgraf return 0; 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds 264c123b867SDouglas Landgraf static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 2651da177e4SLinus Torvalds { 266c123b867SDouglas Landgraf *i = 0; 267c123b867SDouglas Landgraf return 0; 268c123b867SDouglas Landgraf } 269c123b867SDouglas Landgraf 270c123b867SDouglas Landgraf static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 271c123b867SDouglas Landgraf { 272c123b867SDouglas Landgraf if (i != 0) 273c123b867SDouglas Landgraf return -EINVAL; 274c123b867SDouglas Landgraf return 0; 275c123b867SDouglas Landgraf } 276c123b867SDouglas Landgraf 277c123b867SDouglas Landgraf static int vidioc_s_audio(struct file *file, void *priv, 278c123b867SDouglas Landgraf struct v4l2_audio *a) 279c123b867SDouglas Landgraf { 280c123b867SDouglas Landgraf if (a->index != 0) 281c123b867SDouglas Landgraf return -EINVAL; 282c123b867SDouglas Landgraf return 0; 2831da177e4SLinus Torvalds } 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds static struct fmi_device fmi_unit; 2861da177e4SLinus Torvalds 287fa027c2aSArjan van de Ven static const struct file_operations fmi_fops = { 2881da177e4SLinus Torvalds .owner = THIS_MODULE, 2891da177e4SLinus Torvalds .open = video_exclusive_open, 2901da177e4SLinus Torvalds .release = video_exclusive_release, 291c123b867SDouglas Landgraf .ioctl = video_ioctl2, 292078ff795SDouglas Schilling Landgraf #ifdef CONFIG_COMPAT 2930d0fbf81SArnd Bergmann .compat_ioctl = v4l_compat_ioctl32, 294078ff795SDouglas Schilling Landgraf #endif 2951da177e4SLinus Torvalds .llseek = no_llseek, 2961da177e4SLinus Torvalds }; 2971da177e4SLinus Torvalds 298*a399810cSHans Verkuil static const struct v4l2_ioctl_ops fmi_ioctl_ops = { 299c123b867SDouglas Landgraf .vidioc_querycap = vidioc_querycap, 300c123b867SDouglas Landgraf .vidioc_g_tuner = vidioc_g_tuner, 301c123b867SDouglas Landgraf .vidioc_s_tuner = vidioc_s_tuner, 302c123b867SDouglas Landgraf .vidioc_g_audio = vidioc_g_audio, 303c123b867SDouglas Landgraf .vidioc_s_audio = vidioc_s_audio, 304c123b867SDouglas Landgraf .vidioc_g_input = vidioc_g_input, 305c123b867SDouglas Landgraf .vidioc_s_input = vidioc_s_input, 306c123b867SDouglas Landgraf .vidioc_g_frequency = vidioc_g_frequency, 307c123b867SDouglas Landgraf .vidioc_s_frequency = vidioc_s_frequency, 308c123b867SDouglas Landgraf .vidioc_queryctrl = vidioc_queryctrl, 309c123b867SDouglas Landgraf .vidioc_g_ctrl = vidioc_g_ctrl, 310c123b867SDouglas Landgraf .vidioc_s_ctrl = vidioc_s_ctrl, 3111da177e4SLinus Torvalds }; 3121da177e4SLinus Torvalds 313*a399810cSHans Verkuil static struct video_device fmi_radio = { 314*a399810cSHans Verkuil .owner = THIS_MODULE, 315*a399810cSHans Verkuil .name = "SF16FMx radio", 316*a399810cSHans Verkuil .type = VID_TYPE_TUNER, 317*a399810cSHans Verkuil .fops = &fmi_fops, 318*a399810cSHans Verkuil .ioctl_ops = &fmi_ioctl_ops, 319*a399810cSHans Verkuil }; 320*a399810cSHans Verkuil 3211da177e4SLinus Torvalds /* ladis: this is my card. does any other types exist? */ 3221da177e4SLinus Torvalds static struct isapnp_device_id id_table[] __devinitdata = { 3231da177e4SLinus Torvalds { ISAPNP_ANY_ID, ISAPNP_ANY_ID, 3241da177e4SLinus Torvalds ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0}, 3251da177e4SLinus Torvalds { ISAPNP_CARD_END, }, 3261da177e4SLinus Torvalds }; 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds MODULE_DEVICE_TABLE(isapnp, id_table); 3291da177e4SLinus Torvalds 330a999337bSRandy Dunlap static int __init isapnp_fmi_probe(void) 3311da177e4SLinus Torvalds { 3321da177e4SLinus Torvalds int i = 0; 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds while (id_table[i].card_vendor != 0 && dev == NULL) { 3351da177e4SLinus Torvalds dev = pnp_find_dev(NULL, id_table[i].vendor, 3361da177e4SLinus Torvalds id_table[i].function, NULL); 3371da177e4SLinus Torvalds i++; 3381da177e4SLinus Torvalds } 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds if (!dev) 3411da177e4SLinus Torvalds return -ENODEV; 3421da177e4SLinus Torvalds if (pnp_device_attach(dev) < 0) 3431da177e4SLinus Torvalds return -EAGAIN; 3441da177e4SLinus Torvalds if (pnp_activate_dev(dev) < 0) { 3451da177e4SLinus Torvalds printk ("radio-sf16fmi: PnP configure failed (out of resources?)\n"); 3461da177e4SLinus Torvalds pnp_device_detach(dev); 3471da177e4SLinus Torvalds return -ENOMEM; 3481da177e4SLinus Torvalds } 3491da177e4SLinus Torvalds if (!pnp_port_valid(dev, 0)) { 3501da177e4SLinus Torvalds pnp_device_detach(dev); 3511da177e4SLinus Torvalds return -ENODEV; 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds i = pnp_port_start(dev, 0); 3551da177e4SLinus Torvalds printk ("radio-sf16fmi: PnP reports card at %#x\n", i); 3561da177e4SLinus Torvalds 3571da177e4SLinus Torvalds return i; 3581da177e4SLinus Torvalds } 3591da177e4SLinus Torvalds 3601da177e4SLinus Torvalds static int __init fmi_init(void) 3611da177e4SLinus Torvalds { 3621da177e4SLinus Torvalds if (io < 0) 3631da177e4SLinus Torvalds io = isapnp_fmi_probe(); 3641da177e4SLinus Torvalds if (io < 0) { 3651da177e4SLinus Torvalds printk(KERN_ERR "radio-sf16fmi: No PnP card found.\n"); 3661da177e4SLinus Torvalds return io; 3671da177e4SLinus Torvalds } 3681da177e4SLinus Torvalds if (!request_region(io, 2, "radio-sf16fmi")) { 3691da177e4SLinus Torvalds printk(KERN_ERR "radio-sf16fmi: port 0x%x already in use\n", io); 370e08a8c9dSMauro Carvalho Chehab pnp_device_detach(dev); 3711da177e4SLinus Torvalds return -EBUSY; 3721da177e4SLinus Torvalds } 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds fmi_unit.port = io; 3751da177e4SLinus Torvalds fmi_unit.curvol = 0; 3761da177e4SLinus Torvalds fmi_unit.curfreq = 0; 377a2ef73afSMauro Carvalho Chehab fmi_unit.flags = V4L2_TUNER_CAP_LOW; 3781da177e4SLinus Torvalds fmi_radio.priv = &fmi_unit; 3791da177e4SLinus Torvalds 3803593cab5SIngo Molnar mutex_init(&lock); 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) == -1) { 3831da177e4SLinus Torvalds release_region(io, 2); 3841da177e4SLinus Torvalds return -EINVAL; 3851da177e4SLinus Torvalds } 3861da177e4SLinus Torvalds 3871da177e4SLinus Torvalds printk(KERN_INFO "SF16FMx radio card driver at 0x%x\n", io); 3881da177e4SLinus Torvalds /* mute card - prevents noisy bootups */ 3891da177e4SLinus Torvalds fmi_mute(io); 3901da177e4SLinus Torvalds return 0; 3911da177e4SLinus Torvalds } 3921da177e4SLinus Torvalds 3931da177e4SLinus Torvalds MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood"); 3941da177e4SLinus Torvalds MODULE_DESCRIPTION("A driver for the SF16MI radio."); 3951da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 3961da177e4SLinus Torvalds 3971da177e4SLinus Torvalds module_param(io, int, 0); 3981da177e4SLinus Torvalds MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)"); 3991da177e4SLinus Torvalds module_param(radio_nr, int, 0); 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds static void __exit fmi_cleanup_module(void) 4021da177e4SLinus Torvalds { 4031da177e4SLinus Torvalds video_unregister_device(&fmi_radio); 4041da177e4SLinus Torvalds release_region(io, 2); 4051da177e4SLinus Torvalds if (dev) 4061da177e4SLinus Torvalds pnp_device_detach(dev); 4071da177e4SLinus Torvalds } 4081da177e4SLinus Torvalds 4091da177e4SLinus Torvalds module_init(fmi_init); 4101da177e4SLinus Torvalds module_exit(fmi_cleanup_module); 411