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 * 6d9b01449SAlan Cox * Fitted to new interface by Alan Cox <alan@lxorguk.ukuu.org.uk> 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 { 483ca685aaSHans Verkuil unsigned long in_use; 491da177e4SLinus Torvalds int port; 501da177e4SLinus Torvalds int curvol; /* 1 or 0 */ 511da177e4SLinus Torvalds unsigned long curfreq; /* freq in kHz */ 521da177e4SLinus Torvalds __u32 flags; 531da177e4SLinus Torvalds }; 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds static int io = -1; 561da177e4SLinus Torvalds static int radio_nr = -1; 571da177e4SLinus Torvalds static struct pnp_dev *dev = NULL; 583593cab5SIngo Molnar static struct mutex lock; 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ 611da177e4SLinus Torvalds /* It is only useful to give freq in intervall of 800 (=0.05Mhz), 621da177e4SLinus Torvalds * other bits will be truncated, e.g 92.7400016 -> 92.7, but 631da177e4SLinus Torvalds * 92.7400017 -> 92.75 641da177e4SLinus Torvalds */ 651da177e4SLinus Torvalds #define RSF16_ENCODE(x) ((x)/800+214) 661da177e4SLinus Torvalds #define RSF16_MINFREQ 87*16000 671da177e4SLinus Torvalds #define RSF16_MAXFREQ 108*16000 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds static void outbits(int bits, unsigned int data, int port) 701da177e4SLinus Torvalds { 711da177e4SLinus Torvalds while(bits--) { 721da177e4SLinus Torvalds if(data & 1) { 731da177e4SLinus Torvalds outb(5, port); 741da177e4SLinus Torvalds udelay(6); 751da177e4SLinus Torvalds outb(7, port); 761da177e4SLinus Torvalds udelay(6); 771da177e4SLinus Torvalds } else { 781da177e4SLinus Torvalds outb(1, port); 791da177e4SLinus Torvalds udelay(6); 801da177e4SLinus Torvalds outb(3, port); 811da177e4SLinus Torvalds udelay(6); 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds data>>=1; 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds static inline void fmi_mute(int port) 881da177e4SLinus Torvalds { 893593cab5SIngo Molnar mutex_lock(&lock); 901da177e4SLinus Torvalds outb(0x00, port); 913593cab5SIngo Molnar mutex_unlock(&lock); 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds static inline void fmi_unmute(int port) 951da177e4SLinus Torvalds { 963593cab5SIngo Molnar mutex_lock(&lock); 971da177e4SLinus Torvalds outb(0x08, port); 983593cab5SIngo Molnar mutex_unlock(&lock); 991da177e4SLinus Torvalds } 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds static inline int fmi_setfreq(struct fmi_device *dev) 1021da177e4SLinus Torvalds { 1031da177e4SLinus Torvalds int myport = dev->port; 1041da177e4SLinus Torvalds unsigned long freq = dev->curfreq; 1051da177e4SLinus Torvalds 1063593cab5SIngo Molnar mutex_lock(&lock); 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds outbits(16, RSF16_ENCODE(freq), myport); 1091da177e4SLinus Torvalds outbits(8, 0xC0, myport); 1101da177e4SLinus Torvalds msleep(143); /* was schedule_timeout(HZ/7) */ 1113593cab5SIngo Molnar mutex_unlock(&lock); 1121da177e4SLinus Torvalds if (dev->curvol) fmi_unmute(myport); 1131da177e4SLinus Torvalds return 0; 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds static inline int fmi_getsigstr(struct fmi_device *dev) 1171da177e4SLinus Torvalds { 1181da177e4SLinus Torvalds int val; 1191da177e4SLinus Torvalds int res; 1201da177e4SLinus Torvalds int myport = dev->port; 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds 1233593cab5SIngo Molnar mutex_lock(&lock); 1241da177e4SLinus Torvalds val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */ 1251da177e4SLinus Torvalds outb(val, myport); 1261da177e4SLinus Torvalds outb(val | 0x10, myport); 1271da177e4SLinus Torvalds msleep(143); /* was schedule_timeout(HZ/7) */ 1281da177e4SLinus Torvalds res = (int)inb(myport+1); 1291da177e4SLinus Torvalds outb(val, myport); 1301da177e4SLinus Torvalds 1313593cab5SIngo Molnar mutex_unlock(&lock); 1321da177e4SLinus Torvalds return (res & 2) ? 0 : 0xFFFF; 1331da177e4SLinus Torvalds } 1341da177e4SLinus Torvalds 135c123b867SDouglas Landgraf static int vidioc_querycap(struct file *file, void *priv, 136c123b867SDouglas Landgraf struct v4l2_capability *v) 1371da177e4SLinus Torvalds { 138a2ef73afSMauro Carvalho Chehab strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver)); 139a2ef73afSMauro Carvalho Chehab strlcpy(v->card, "SF16-FMx radio", sizeof(v->card)); 140a2ef73afSMauro Carvalho Chehab sprintf(v->bus_info, "ISA"); 141a2ef73afSMauro Carvalho Chehab v->version = RADIO_VERSION; 142a2ef73afSMauro Carvalho Chehab v->capabilities = V4L2_CAP_TUNER; 1431da177e4SLinus Torvalds return 0; 1441da177e4SLinus Torvalds } 145c123b867SDouglas Landgraf 146c123b867SDouglas Landgraf static int vidioc_g_tuner(struct file *file, void *priv, 147c123b867SDouglas Landgraf struct v4l2_tuner *v) 1481da177e4SLinus Torvalds { 1491da177e4SLinus Torvalds int mult; 150c170ecf4SHans Verkuil struct fmi_device *fmi = video_drvdata(file); 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 { 178c170ecf4SHans Verkuil struct fmi_device *fmi = video_drvdata(file); 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 { 195c170ecf4SHans Verkuil struct fmi_device *fmi = video_drvdata(file); 196a2ef73afSMauro Carvalho Chehab 197a2ef73afSMauro Carvalho Chehab f->type = V4L2_TUNER_RADIO; 198a2ef73afSMauro Carvalho Chehab f->frequency = fmi->curfreq; 199a2ef73afSMauro Carvalho Chehab if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) 200a2ef73afSMauro Carvalho Chehab f->frequency /= 1000; 2011da177e4SLinus Torvalds return 0; 2021da177e4SLinus Torvalds } 203c123b867SDouglas Landgraf 204c123b867SDouglas Landgraf static int vidioc_queryctrl(struct file *file, void *priv, 205c123b867SDouglas Landgraf struct v4l2_queryctrl *qc) 2061da177e4SLinus Torvalds { 207a2ef73afSMauro Carvalho Chehab int i; 208a2ef73afSMauro Carvalho Chehab 209a2ef73afSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 210a2ef73afSMauro Carvalho Chehab if (qc->id && qc->id == radio_qctrl[i].id) { 211a2ef73afSMauro Carvalho Chehab memcpy(qc, &(radio_qctrl[i]), 212a2ef73afSMauro Carvalho Chehab sizeof(*qc)); 213c123b867SDouglas Landgraf return 0; 214a2ef73afSMauro Carvalho Chehab } 215a2ef73afSMauro Carvalho Chehab } 2161da177e4SLinus Torvalds return -EINVAL; 2171da177e4SLinus Torvalds } 218c123b867SDouglas Landgraf 219c123b867SDouglas Landgraf static int vidioc_g_ctrl(struct file *file, void *priv, 220c123b867SDouglas Landgraf struct v4l2_control *ctrl) 2211da177e4SLinus Torvalds { 222c170ecf4SHans Verkuil struct fmi_device *fmi = video_drvdata(file); 223a2ef73afSMauro Carvalho Chehab 224a2ef73afSMauro Carvalho Chehab switch (ctrl->id) { 225a2ef73afSMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 226a2ef73afSMauro Carvalho Chehab ctrl->value = fmi->curvol; 227c123b867SDouglas Landgraf return 0; 228a2ef73afSMauro Carvalho Chehab } 229a2ef73afSMauro Carvalho Chehab return -EINVAL; 230a2ef73afSMauro Carvalho Chehab } 231c123b867SDouglas Landgraf 232c123b867SDouglas Landgraf static int vidioc_s_ctrl(struct file *file, void *priv, 233c123b867SDouglas Landgraf struct v4l2_control *ctrl) 234a2ef73afSMauro Carvalho Chehab { 235c170ecf4SHans Verkuil struct fmi_device *fmi = video_drvdata(file); 236a2ef73afSMauro Carvalho Chehab 237a2ef73afSMauro Carvalho Chehab switch (ctrl->id) { 238a2ef73afSMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 239a2ef73afSMauro Carvalho Chehab if (ctrl->value) 240a2ef73afSMauro Carvalho Chehab fmi_mute(fmi->port); 241a2ef73afSMauro Carvalho Chehab else 242a2ef73afSMauro Carvalho Chehab fmi_unmute(fmi->port); 243a2ef73afSMauro Carvalho Chehab fmi->curvol = ctrl->value; 244c123b867SDouglas Landgraf return 0; 245a2ef73afSMauro Carvalho Chehab } 246a2ef73afSMauro Carvalho Chehab return -EINVAL; 2471da177e4SLinus Torvalds } 248c123b867SDouglas Landgraf 249c123b867SDouglas Landgraf static int vidioc_g_audio(struct file *file, void *priv, 250c123b867SDouglas Landgraf struct v4l2_audio *a) 251c123b867SDouglas Landgraf { 252c123b867SDouglas Landgraf if (a->index > 1) 253c123b867SDouglas Landgraf return -EINVAL; 254c123b867SDouglas Landgraf 255c123b867SDouglas Landgraf strcpy(a->name, "Radio"); 256c123b867SDouglas Landgraf a->capability = V4L2_AUDCAP_STEREO; 257c123b867SDouglas Landgraf return 0; 2581da177e4SLinus Torvalds } 2591da177e4SLinus Torvalds 260c123b867SDouglas Landgraf static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 2611da177e4SLinus Torvalds { 262c123b867SDouglas Landgraf *i = 0; 263c123b867SDouglas Landgraf return 0; 264c123b867SDouglas Landgraf } 265c123b867SDouglas Landgraf 266c123b867SDouglas Landgraf static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 267c123b867SDouglas Landgraf { 268c123b867SDouglas Landgraf if (i != 0) 269c123b867SDouglas Landgraf return -EINVAL; 270c123b867SDouglas Landgraf return 0; 271c123b867SDouglas Landgraf } 272c123b867SDouglas Landgraf 273c123b867SDouglas Landgraf static int vidioc_s_audio(struct file *file, void *priv, 274c123b867SDouglas Landgraf struct v4l2_audio *a) 275c123b867SDouglas Landgraf { 276c123b867SDouglas Landgraf if (a->index != 0) 277c123b867SDouglas Landgraf return -EINVAL; 278c123b867SDouglas Landgraf return 0; 2791da177e4SLinus Torvalds } 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds static struct fmi_device fmi_unit; 2821da177e4SLinus Torvalds 283*bec43661SHans Verkuil static int fmi_exclusive_open(struct file *file) 2843ca685aaSHans Verkuil { 2853ca685aaSHans Verkuil return test_and_set_bit(0, &fmi_unit.in_use) ? -EBUSY : 0; 2863ca685aaSHans Verkuil } 2873ca685aaSHans Verkuil 288*bec43661SHans Verkuil static int fmi_exclusive_release(struct file *file) 2893ca685aaSHans Verkuil { 2903ca685aaSHans Verkuil clear_bit(0, &fmi_unit.in_use); 2913ca685aaSHans Verkuil return 0; 2923ca685aaSHans Verkuil } 2933ca685aaSHans Verkuil 294*bec43661SHans Verkuil static const struct v4l2_file_operations fmi_fops = { 2951da177e4SLinus Torvalds .owner = THIS_MODULE, 2963ca685aaSHans Verkuil .open = fmi_exclusive_open, 2973ca685aaSHans Verkuil .release = fmi_exclusive_release, 298c123b867SDouglas Landgraf .ioctl = video_ioctl2, 2991da177e4SLinus Torvalds }; 3001da177e4SLinus Torvalds 301a399810cSHans Verkuil static const struct v4l2_ioctl_ops fmi_ioctl_ops = { 302c123b867SDouglas Landgraf .vidioc_querycap = vidioc_querycap, 303c123b867SDouglas Landgraf .vidioc_g_tuner = vidioc_g_tuner, 304c123b867SDouglas Landgraf .vidioc_s_tuner = vidioc_s_tuner, 305c123b867SDouglas Landgraf .vidioc_g_audio = vidioc_g_audio, 306c123b867SDouglas Landgraf .vidioc_s_audio = vidioc_s_audio, 307c123b867SDouglas Landgraf .vidioc_g_input = vidioc_g_input, 308c123b867SDouglas Landgraf .vidioc_s_input = vidioc_s_input, 309c123b867SDouglas Landgraf .vidioc_g_frequency = vidioc_g_frequency, 310c123b867SDouglas Landgraf .vidioc_s_frequency = vidioc_s_frequency, 311c123b867SDouglas Landgraf .vidioc_queryctrl = vidioc_queryctrl, 312c123b867SDouglas Landgraf .vidioc_g_ctrl = vidioc_g_ctrl, 313c123b867SDouglas Landgraf .vidioc_s_ctrl = vidioc_s_ctrl, 3141da177e4SLinus Torvalds }; 3151da177e4SLinus Torvalds 316a399810cSHans Verkuil static struct video_device fmi_radio = { 317a399810cSHans Verkuil .name = "SF16FMx radio", 318a399810cSHans Verkuil .fops = &fmi_fops, 319a399810cSHans Verkuil .ioctl_ops = &fmi_ioctl_ops, 320aa5e90afSHans Verkuil .release = video_device_release_empty, 321a399810cSHans Verkuil }; 322a399810cSHans Verkuil 3231da177e4SLinus Torvalds /* ladis: this is my card. does any other types exist? */ 3241da177e4SLinus Torvalds static struct isapnp_device_id id_table[] __devinitdata = { 3251da177e4SLinus Torvalds { ISAPNP_ANY_ID, ISAPNP_ANY_ID, 3261da177e4SLinus Torvalds ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0}, 3271da177e4SLinus Torvalds { ISAPNP_CARD_END, }, 3281da177e4SLinus Torvalds }; 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds MODULE_DEVICE_TABLE(isapnp, id_table); 3311da177e4SLinus Torvalds 332a999337bSRandy Dunlap static int __init isapnp_fmi_probe(void) 3331da177e4SLinus Torvalds { 3341da177e4SLinus Torvalds int i = 0; 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds while (id_table[i].card_vendor != 0 && dev == NULL) { 3371da177e4SLinus Torvalds dev = pnp_find_dev(NULL, id_table[i].vendor, 3381da177e4SLinus Torvalds id_table[i].function, NULL); 3391da177e4SLinus Torvalds i++; 3401da177e4SLinus Torvalds } 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds if (!dev) 3431da177e4SLinus Torvalds return -ENODEV; 3441da177e4SLinus Torvalds if (pnp_device_attach(dev) < 0) 3451da177e4SLinus Torvalds return -EAGAIN; 3461da177e4SLinus Torvalds if (pnp_activate_dev(dev) < 0) { 3471da177e4SLinus Torvalds printk ("radio-sf16fmi: PnP configure failed (out of resources?)\n"); 3481da177e4SLinus Torvalds pnp_device_detach(dev); 3491da177e4SLinus Torvalds return -ENOMEM; 3501da177e4SLinus Torvalds } 3511da177e4SLinus Torvalds if (!pnp_port_valid(dev, 0)) { 3521da177e4SLinus Torvalds pnp_device_detach(dev); 3531da177e4SLinus Torvalds return -ENODEV; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds 3561da177e4SLinus Torvalds i = pnp_port_start(dev, 0); 3571da177e4SLinus Torvalds printk ("radio-sf16fmi: PnP reports card at %#x\n", i); 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds return i; 3601da177e4SLinus Torvalds } 3611da177e4SLinus Torvalds 3621da177e4SLinus Torvalds static int __init fmi_init(void) 3631da177e4SLinus Torvalds { 3641da177e4SLinus Torvalds if (io < 0) 3651da177e4SLinus Torvalds io = isapnp_fmi_probe(); 3661da177e4SLinus Torvalds if (io < 0) { 3671da177e4SLinus Torvalds printk(KERN_ERR "radio-sf16fmi: No PnP card found.\n"); 3681da177e4SLinus Torvalds return io; 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds if (!request_region(io, 2, "radio-sf16fmi")) { 3711da177e4SLinus Torvalds printk(KERN_ERR "radio-sf16fmi: port 0x%x already in use\n", io); 372e08a8c9dSMauro Carvalho Chehab pnp_device_detach(dev); 3731da177e4SLinus Torvalds return -EBUSY; 3741da177e4SLinus Torvalds } 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds fmi_unit.port = io; 3771da177e4SLinus Torvalds fmi_unit.curvol = 0; 3781da177e4SLinus Torvalds fmi_unit.curfreq = 0; 379a2ef73afSMauro Carvalho Chehab fmi_unit.flags = V4L2_TUNER_CAP_LOW; 380601e9444SHans Verkuil video_set_drvdata(&fmi_radio, &fmi_unit); 3811da177e4SLinus Torvalds 3823593cab5SIngo Molnar mutex_init(&lock); 3831da177e4SLinus Torvalds 384cba99ae8SHans Verkuil if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) < 0) { 3851da177e4SLinus Torvalds release_region(io, 2); 3861da177e4SLinus Torvalds return -EINVAL; 3871da177e4SLinus Torvalds } 3881da177e4SLinus Torvalds 3891da177e4SLinus Torvalds printk(KERN_INFO "SF16FMx radio card driver at 0x%x\n", io); 3901da177e4SLinus Torvalds /* mute card - prevents noisy bootups */ 3911da177e4SLinus Torvalds fmi_mute(io); 3921da177e4SLinus Torvalds return 0; 3931da177e4SLinus Torvalds } 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood"); 3961da177e4SLinus Torvalds MODULE_DESCRIPTION("A driver for the SF16MI radio."); 3971da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 3981da177e4SLinus Torvalds 3991da177e4SLinus Torvalds module_param(io, int, 0); 4001da177e4SLinus Torvalds MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)"); 4011da177e4SLinus Torvalds module_param(radio_nr, int, 0); 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds static void __exit fmi_cleanup_module(void) 4041da177e4SLinus Torvalds { 4051da177e4SLinus Torvalds video_unregister_device(&fmi_radio); 4061da177e4SLinus Torvalds release_region(io, 2); 4071da177e4SLinus Torvalds if (dev) 4081da177e4SLinus Torvalds pnp_device_detach(dev); 4091da177e4SLinus Torvalds } 4101da177e4SLinus Torvalds 4111da177e4SLinus Torvalds module_init(fmi_init); 4121da177e4SLinus Torvalds module_exit(fmi_cleanup_module); 413