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> 271da177e4SLinus Torvalds #include <linux/isapnp.h> 281da177e4SLinus Torvalds #include <asm/io.h> /* outb, outb_p */ 291da177e4SLinus Torvalds #include <asm/uaccess.h> /* copy to/from user */ 303593cab5SIngo Molnar #include <linux/mutex.h> 311da177e4SLinus Torvalds 32a2ef73afSMauro Carvalho Chehab #define RADIO_VERSION KERNEL_VERSION(0,0,2) 33a2ef73afSMauro Carvalho Chehab 34a2ef73afSMauro Carvalho Chehab static struct v4l2_queryctrl radio_qctrl[] = { 35a2ef73afSMauro Carvalho Chehab { 36a2ef73afSMauro Carvalho Chehab .id = V4L2_CID_AUDIO_MUTE, 37a2ef73afSMauro Carvalho Chehab .name = "Mute", 38a2ef73afSMauro Carvalho Chehab .minimum = 0, 39a2ef73afSMauro Carvalho Chehab .maximum = 1, 40a2ef73afSMauro Carvalho Chehab .default_value = 1, 41a2ef73afSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_BOOLEAN, 42a2ef73afSMauro Carvalho Chehab } 43a2ef73afSMauro Carvalho Chehab }; 44a2ef73afSMauro Carvalho Chehab 451da177e4SLinus Torvalds struct fmi_device 461da177e4SLinus Torvalds { 471da177e4SLinus Torvalds int port; 481da177e4SLinus Torvalds int curvol; /* 1 or 0 */ 491da177e4SLinus Torvalds unsigned long curfreq; /* freq in kHz */ 501da177e4SLinus Torvalds __u32 flags; 511da177e4SLinus Torvalds }; 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds static int io = -1; 541da177e4SLinus Torvalds static int radio_nr = -1; 551da177e4SLinus Torvalds static struct pnp_dev *dev = NULL; 563593cab5SIngo Molnar static struct mutex lock; 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ 591da177e4SLinus Torvalds /* It is only useful to give freq in intervall of 800 (=0.05Mhz), 601da177e4SLinus Torvalds * other bits will be truncated, e.g 92.7400016 -> 92.7, but 611da177e4SLinus Torvalds * 92.7400017 -> 92.75 621da177e4SLinus Torvalds */ 631da177e4SLinus Torvalds #define RSF16_ENCODE(x) ((x)/800+214) 641da177e4SLinus Torvalds #define RSF16_MINFREQ 87*16000 651da177e4SLinus Torvalds #define RSF16_MAXFREQ 108*16000 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds static void outbits(int bits, unsigned int data, int port) 681da177e4SLinus Torvalds { 691da177e4SLinus Torvalds while(bits--) { 701da177e4SLinus Torvalds if(data & 1) { 711da177e4SLinus Torvalds outb(5, port); 721da177e4SLinus Torvalds udelay(6); 731da177e4SLinus Torvalds outb(7, port); 741da177e4SLinus Torvalds udelay(6); 751da177e4SLinus Torvalds } else { 761da177e4SLinus Torvalds outb(1, port); 771da177e4SLinus Torvalds udelay(6); 781da177e4SLinus Torvalds outb(3, port); 791da177e4SLinus Torvalds udelay(6); 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds data>>=1; 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds static inline void fmi_mute(int port) 861da177e4SLinus Torvalds { 873593cab5SIngo Molnar mutex_lock(&lock); 881da177e4SLinus Torvalds outb(0x00, port); 893593cab5SIngo Molnar mutex_unlock(&lock); 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds static inline void fmi_unmute(int port) 931da177e4SLinus Torvalds { 943593cab5SIngo Molnar mutex_lock(&lock); 951da177e4SLinus Torvalds outb(0x08, port); 963593cab5SIngo Molnar mutex_unlock(&lock); 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds static inline int fmi_setfreq(struct fmi_device *dev) 1001da177e4SLinus Torvalds { 1011da177e4SLinus Torvalds int myport = dev->port; 1021da177e4SLinus Torvalds unsigned long freq = dev->curfreq; 1031da177e4SLinus Torvalds 1043593cab5SIngo Molnar mutex_lock(&lock); 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds outbits(16, RSF16_ENCODE(freq), myport); 1071da177e4SLinus Torvalds outbits(8, 0xC0, myport); 1081da177e4SLinus Torvalds msleep(143); /* was schedule_timeout(HZ/7) */ 1093593cab5SIngo Molnar mutex_unlock(&lock); 1101da177e4SLinus Torvalds if (dev->curvol) fmi_unmute(myport); 1111da177e4SLinus Torvalds return 0; 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds static inline int fmi_getsigstr(struct fmi_device *dev) 1151da177e4SLinus Torvalds { 1161da177e4SLinus Torvalds int val; 1171da177e4SLinus Torvalds int res; 1181da177e4SLinus Torvalds int myport = dev->port; 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds 1213593cab5SIngo Molnar mutex_lock(&lock); 1221da177e4SLinus Torvalds val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */ 1231da177e4SLinus Torvalds outb(val, myport); 1241da177e4SLinus Torvalds outb(val | 0x10, myport); 1251da177e4SLinus Torvalds msleep(143); /* was schedule_timeout(HZ/7) */ 1261da177e4SLinus Torvalds res = (int)inb(myport+1); 1271da177e4SLinus Torvalds outb(val, myport); 1281da177e4SLinus Torvalds 1293593cab5SIngo Molnar mutex_unlock(&lock); 1301da177e4SLinus Torvalds return (res & 2) ? 0 : 0xFFFF; 1311da177e4SLinus Torvalds } 1321da177e4SLinus Torvalds 133c123b867SDouglas Landgraf static int vidioc_querycap(struct file *file, void *priv, 134c123b867SDouglas Landgraf struct v4l2_capability *v) 1351da177e4SLinus Torvalds { 136a2ef73afSMauro Carvalho Chehab strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver)); 137a2ef73afSMauro Carvalho Chehab strlcpy(v->card, "SF16-FMx radio", sizeof(v->card)); 138a2ef73afSMauro Carvalho Chehab sprintf(v->bus_info, "ISA"); 139a2ef73afSMauro Carvalho Chehab v->version = RADIO_VERSION; 140a2ef73afSMauro Carvalho Chehab v->capabilities = V4L2_CAP_TUNER; 1411da177e4SLinus Torvalds return 0; 1421da177e4SLinus Torvalds } 143c123b867SDouglas Landgraf 144c123b867SDouglas Landgraf static int vidioc_g_tuner(struct file *file, void *priv, 145c123b867SDouglas Landgraf struct v4l2_tuner *v) 1461da177e4SLinus Torvalds { 1471da177e4SLinus Torvalds int mult; 148c123b867SDouglas Landgraf struct video_device *dev = video_devdata(file); 149c123b867SDouglas Landgraf struct fmi_device *fmi = dev->priv; 1501da177e4SLinus Torvalds 151a2ef73afSMauro Carvalho Chehab if (v->index > 0) 1521da177e4SLinus Torvalds return -EINVAL; 153a2ef73afSMauro Carvalho Chehab 1541da177e4SLinus Torvalds strcpy(v->name, "FM"); 155a2ef73afSMauro Carvalho Chehab v->type = V4L2_TUNER_RADIO; 156a2ef73afSMauro Carvalho Chehab mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; 1571da177e4SLinus Torvalds v->rangelow = RSF16_MINFREQ/mult; 1581da177e4SLinus Torvalds v->rangehigh = RSF16_MAXFREQ/mult; 159a2ef73afSMauro Carvalho Chehab v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; 160fa38ad65SMauro Carvalho Chehab v->capability = fmi->flags&V4L2_TUNER_CAP_LOW; 161a2ef73afSMauro Carvalho Chehab v->audmode = V4L2_TUNER_MODE_STEREO; 1621da177e4SLinus Torvalds v->signal = fmi_getsigstr(fmi); 1631da177e4SLinus Torvalds return 0; 1641da177e4SLinus Torvalds } 165a2ef73afSMauro Carvalho Chehab 166c123b867SDouglas Landgraf static int vidioc_s_tuner(struct file *file, void *priv, 167c123b867SDouglas Landgraf struct v4l2_tuner *v) 168c123b867SDouglas Landgraf { 169a2ef73afSMauro Carvalho Chehab if (v->index > 0) 1701da177e4SLinus Torvalds return -EINVAL; 1711da177e4SLinus Torvalds return 0; 1721da177e4SLinus Torvalds } 173c123b867SDouglas Landgraf 174c123b867SDouglas Landgraf static int vidioc_s_frequency(struct file *file, void *priv, 175c123b867SDouglas Landgraf struct v4l2_frequency *f) 1761da177e4SLinus Torvalds { 177c123b867SDouglas Landgraf struct video_device *dev = video_devdata(file); 178c123b867SDouglas Landgraf struct fmi_device *fmi = dev->priv; 179a2ef73afSMauro Carvalho Chehab 180a2ef73afSMauro Carvalho Chehab if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) 181a2ef73afSMauro Carvalho Chehab f->frequency *= 1000; 182a2ef73afSMauro Carvalho Chehab if (f->frequency < RSF16_MINFREQ || 183a2ef73afSMauro Carvalho Chehab f->frequency > RSF16_MAXFREQ ) 1841da177e4SLinus Torvalds return -EINVAL; 1851da177e4SLinus Torvalds /*rounding in steps of 800 to match th freq 1861da177e4SLinus Torvalds that will be used */ 187a2ef73afSMauro Carvalho Chehab fmi->curfreq = (f->frequency/800)*800; 1881da177e4SLinus Torvalds fmi_setfreq(fmi); 1891da177e4SLinus Torvalds return 0; 1901da177e4SLinus Torvalds } 191c123b867SDouglas Landgraf 192c123b867SDouglas Landgraf static int vidioc_g_frequency(struct file *file, void *priv, 193c123b867SDouglas Landgraf struct v4l2_frequency *f) 1941da177e4SLinus Torvalds { 195c123b867SDouglas Landgraf struct video_device *dev = video_devdata(file); 196c123b867SDouglas Landgraf struct fmi_device *fmi = dev->priv; 197a2ef73afSMauro Carvalho Chehab 198a2ef73afSMauro Carvalho Chehab f->type = V4L2_TUNER_RADIO; 199a2ef73afSMauro Carvalho Chehab f->frequency = fmi->curfreq; 200a2ef73afSMauro Carvalho Chehab if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) 201a2ef73afSMauro Carvalho Chehab f->frequency /= 1000; 2021da177e4SLinus Torvalds return 0; 2031da177e4SLinus Torvalds } 204c123b867SDouglas Landgraf 205c123b867SDouglas Landgraf static int vidioc_queryctrl(struct file *file, void *priv, 206c123b867SDouglas Landgraf struct v4l2_queryctrl *qc) 2071da177e4SLinus Torvalds { 208a2ef73afSMauro Carvalho Chehab int i; 209a2ef73afSMauro Carvalho Chehab 210a2ef73afSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 211a2ef73afSMauro Carvalho Chehab if (qc->id && qc->id == radio_qctrl[i].id) { 212a2ef73afSMauro Carvalho Chehab memcpy(qc, &(radio_qctrl[i]), 213a2ef73afSMauro Carvalho Chehab sizeof(*qc)); 214c123b867SDouglas Landgraf return 0; 215a2ef73afSMauro Carvalho Chehab } 216a2ef73afSMauro Carvalho Chehab } 2171da177e4SLinus Torvalds return -EINVAL; 2181da177e4SLinus Torvalds } 219c123b867SDouglas Landgraf 220c123b867SDouglas Landgraf static int vidioc_g_ctrl(struct file *file, void *priv, 221c123b867SDouglas Landgraf struct v4l2_control *ctrl) 2221da177e4SLinus Torvalds { 223c123b867SDouglas Landgraf struct video_device *dev = video_devdata(file); 224c123b867SDouglas Landgraf struct fmi_device *fmi = dev->priv; 225a2ef73afSMauro Carvalho Chehab 226a2ef73afSMauro Carvalho Chehab switch (ctrl->id) { 227a2ef73afSMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 228a2ef73afSMauro Carvalho Chehab ctrl->value = fmi->curvol; 229c123b867SDouglas Landgraf return 0; 230a2ef73afSMauro Carvalho Chehab } 231a2ef73afSMauro Carvalho Chehab return -EINVAL; 232a2ef73afSMauro Carvalho Chehab } 233c123b867SDouglas Landgraf 234c123b867SDouglas Landgraf static int vidioc_s_ctrl(struct file *file, void *priv, 235c123b867SDouglas Landgraf struct v4l2_control *ctrl) 236a2ef73afSMauro Carvalho Chehab { 237c123b867SDouglas Landgraf struct video_device *dev = video_devdata(file); 238c123b867SDouglas Landgraf struct fmi_device *fmi = dev->priv; 239a2ef73afSMauro Carvalho Chehab 240a2ef73afSMauro Carvalho Chehab switch (ctrl->id) { 241a2ef73afSMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 242a2ef73afSMauro Carvalho Chehab if (ctrl->value) 243a2ef73afSMauro Carvalho Chehab fmi_mute(fmi->port); 244a2ef73afSMauro Carvalho Chehab else 245a2ef73afSMauro Carvalho Chehab fmi_unmute(fmi->port); 246a2ef73afSMauro Carvalho Chehab fmi->curvol = ctrl->value; 247c123b867SDouglas Landgraf return 0; 248a2ef73afSMauro Carvalho Chehab } 249a2ef73afSMauro Carvalho Chehab return -EINVAL; 2501da177e4SLinus Torvalds } 251c123b867SDouglas Landgraf 252c123b867SDouglas Landgraf static int vidioc_g_audio(struct file *file, void *priv, 253c123b867SDouglas Landgraf struct v4l2_audio *a) 254c123b867SDouglas Landgraf { 255c123b867SDouglas Landgraf if (a->index > 1) 256c123b867SDouglas Landgraf return -EINVAL; 257c123b867SDouglas Landgraf 258c123b867SDouglas Landgraf strcpy(a->name, "Radio"); 259c123b867SDouglas Landgraf a->capability = V4L2_AUDCAP_STEREO; 260c123b867SDouglas Landgraf return 0; 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds 263c123b867SDouglas Landgraf static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 2641da177e4SLinus Torvalds { 265c123b867SDouglas Landgraf *i = 0; 266c123b867SDouglas Landgraf return 0; 267c123b867SDouglas Landgraf } 268c123b867SDouglas Landgraf 269c123b867SDouglas Landgraf static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 270c123b867SDouglas Landgraf { 271c123b867SDouglas Landgraf if (i != 0) 272c123b867SDouglas Landgraf return -EINVAL; 273c123b867SDouglas Landgraf return 0; 274c123b867SDouglas Landgraf } 275c123b867SDouglas Landgraf 276c123b867SDouglas Landgraf static int vidioc_s_audio(struct file *file, void *priv, 277c123b867SDouglas Landgraf struct v4l2_audio *a) 278c123b867SDouglas Landgraf { 279c123b867SDouglas Landgraf if (a->index != 0) 280c123b867SDouglas Landgraf return -EINVAL; 281c123b867SDouglas Landgraf return 0; 2821da177e4SLinus Torvalds } 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds static struct fmi_device fmi_unit; 2851da177e4SLinus Torvalds 286fa027c2aSArjan van de Ven static const struct file_operations fmi_fops = { 2871da177e4SLinus Torvalds .owner = THIS_MODULE, 2881da177e4SLinus Torvalds .open = video_exclusive_open, 2891da177e4SLinus Torvalds .release = video_exclusive_release, 290c123b867SDouglas Landgraf .ioctl = video_ioctl2, 2910d0fbf81SArnd Bergmann .compat_ioctl = v4l_compat_ioctl32, 2921da177e4SLinus Torvalds .llseek = no_llseek, 2931da177e4SLinus Torvalds }; 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds static struct video_device fmi_radio= 2961da177e4SLinus Torvalds { 2971da177e4SLinus Torvalds .owner = THIS_MODULE, 2981da177e4SLinus Torvalds .name = "SF16FMx radio", 2991da177e4SLinus Torvalds .type = VID_TYPE_TUNER, 3001da177e4SLinus Torvalds .fops = &fmi_fops, 301c123b867SDouglas Landgraf .vidioc_querycap = vidioc_querycap, 302c123b867SDouglas Landgraf .vidioc_g_tuner = vidioc_g_tuner, 303c123b867SDouglas Landgraf .vidioc_s_tuner = vidioc_s_tuner, 304c123b867SDouglas Landgraf .vidioc_g_audio = vidioc_g_audio, 305c123b867SDouglas Landgraf .vidioc_s_audio = vidioc_s_audio, 306c123b867SDouglas Landgraf .vidioc_g_input = vidioc_g_input, 307c123b867SDouglas Landgraf .vidioc_s_input = vidioc_s_input, 308c123b867SDouglas Landgraf .vidioc_g_frequency = vidioc_g_frequency, 309c123b867SDouglas Landgraf .vidioc_s_frequency = vidioc_s_frequency, 310c123b867SDouglas Landgraf .vidioc_queryctrl = vidioc_queryctrl, 311c123b867SDouglas Landgraf .vidioc_g_ctrl = vidioc_g_ctrl, 312c123b867SDouglas Landgraf .vidioc_s_ctrl = vidioc_s_ctrl, 3131da177e4SLinus Torvalds }; 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds /* ladis: this is my card. does any other types exist? */ 3161da177e4SLinus Torvalds static struct isapnp_device_id id_table[] __devinitdata = { 3171da177e4SLinus Torvalds { ISAPNP_ANY_ID, ISAPNP_ANY_ID, 3181da177e4SLinus Torvalds ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0}, 3191da177e4SLinus Torvalds { ISAPNP_CARD_END, }, 3201da177e4SLinus Torvalds }; 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds MODULE_DEVICE_TABLE(isapnp, id_table); 3231da177e4SLinus Torvalds 324*a999337bSRandy Dunlap static int __init isapnp_fmi_probe(void) 3251da177e4SLinus Torvalds { 3261da177e4SLinus Torvalds int i = 0; 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds while (id_table[i].card_vendor != 0 && dev == NULL) { 3291da177e4SLinus Torvalds dev = pnp_find_dev(NULL, id_table[i].vendor, 3301da177e4SLinus Torvalds id_table[i].function, NULL); 3311da177e4SLinus Torvalds i++; 3321da177e4SLinus Torvalds } 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds if (!dev) 3351da177e4SLinus Torvalds return -ENODEV; 3361da177e4SLinus Torvalds if (pnp_device_attach(dev) < 0) 3371da177e4SLinus Torvalds return -EAGAIN; 3381da177e4SLinus Torvalds if (pnp_activate_dev(dev) < 0) { 3391da177e4SLinus Torvalds printk ("radio-sf16fmi: PnP configure failed (out of resources?)\n"); 3401da177e4SLinus Torvalds pnp_device_detach(dev); 3411da177e4SLinus Torvalds return -ENOMEM; 3421da177e4SLinus Torvalds } 3431da177e4SLinus Torvalds if (!pnp_port_valid(dev, 0)) { 3441da177e4SLinus Torvalds pnp_device_detach(dev); 3451da177e4SLinus Torvalds return -ENODEV; 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds i = pnp_port_start(dev, 0); 3491da177e4SLinus Torvalds printk ("radio-sf16fmi: PnP reports card at %#x\n", i); 3501da177e4SLinus Torvalds 3511da177e4SLinus Torvalds return i; 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds static int __init fmi_init(void) 3551da177e4SLinus Torvalds { 3561da177e4SLinus Torvalds if (io < 0) 3571da177e4SLinus Torvalds io = isapnp_fmi_probe(); 3581da177e4SLinus Torvalds if (io < 0) { 3591da177e4SLinus Torvalds printk(KERN_ERR "radio-sf16fmi: No PnP card found.\n"); 3601da177e4SLinus Torvalds return io; 3611da177e4SLinus Torvalds } 3621da177e4SLinus Torvalds if (!request_region(io, 2, "radio-sf16fmi")) { 3631da177e4SLinus Torvalds printk(KERN_ERR "radio-sf16fmi: port 0x%x already in use\n", io); 3641da177e4SLinus Torvalds return -EBUSY; 3651da177e4SLinus Torvalds } 3661da177e4SLinus Torvalds 3671da177e4SLinus Torvalds fmi_unit.port = io; 3681da177e4SLinus Torvalds fmi_unit.curvol = 0; 3691da177e4SLinus Torvalds fmi_unit.curfreq = 0; 370a2ef73afSMauro Carvalho Chehab fmi_unit.flags = V4L2_TUNER_CAP_LOW; 3711da177e4SLinus Torvalds fmi_radio.priv = &fmi_unit; 3721da177e4SLinus Torvalds 3733593cab5SIngo Molnar mutex_init(&lock); 3741da177e4SLinus Torvalds 3751da177e4SLinus Torvalds if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) == -1) { 3761da177e4SLinus Torvalds release_region(io, 2); 3771da177e4SLinus Torvalds return -EINVAL; 3781da177e4SLinus Torvalds } 3791da177e4SLinus Torvalds 3801da177e4SLinus Torvalds printk(KERN_INFO "SF16FMx radio card driver at 0x%x\n", io); 3811da177e4SLinus Torvalds /* mute card - prevents noisy bootups */ 3821da177e4SLinus Torvalds fmi_mute(io); 3831da177e4SLinus Torvalds return 0; 3841da177e4SLinus Torvalds } 3851da177e4SLinus Torvalds 3861da177e4SLinus Torvalds MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood"); 3871da177e4SLinus Torvalds MODULE_DESCRIPTION("A driver for the SF16MI radio."); 3881da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 3891da177e4SLinus Torvalds 3901da177e4SLinus Torvalds module_param(io, int, 0); 3911da177e4SLinus Torvalds MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)"); 3921da177e4SLinus Torvalds module_param(radio_nr, int, 0); 3931da177e4SLinus Torvalds 3941da177e4SLinus Torvalds static void __exit fmi_cleanup_module(void) 3951da177e4SLinus Torvalds { 3961da177e4SLinus Torvalds video_unregister_device(&fmi_radio); 3971da177e4SLinus Torvalds release_region(io, 2); 3981da177e4SLinus Torvalds if (dev) 3991da177e4SLinus Torvalds pnp_device_detach(dev); 4001da177e4SLinus Torvalds } 4011da177e4SLinus Torvalds 4021da177e4SLinus Torvalds module_init(fmi_init); 4031da177e4SLinus Torvalds module_exit(fmi_cleanup_module); 404