11da177e4SLinus Torvalds /* radio-aztech.c - Aztech radio card driver for Linux 2.2 21da177e4SLinus Torvalds * 3a4366af4SMauro Carvalho Chehab * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 41da177e4SLinus Torvalds * Adapted to support the Video for Linux API by 51da177e4SLinus Torvalds * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by: 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Quay Ly 81da177e4SLinus Torvalds * Donald Song 91da177e4SLinus Torvalds * Jason Lewis (jlewis@twilight.vtc.vsc.edu) 101da177e4SLinus Torvalds * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) 111da177e4SLinus Torvalds * William McGrath (wmcgrath@twilight.vtc.vsc.edu) 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ 141da177e4SLinus Torvalds * along with more information on the card itself. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * History: 171da177e4SLinus Torvalds * 1999-02-24 Russell Kroll <rkroll@exploits.org> 181da177e4SLinus Torvalds * Fine tuning/VIDEO_TUNER_LOW 191da177e4SLinus Torvalds * Range expanded to 87-108 MHz (from 87.9-107.8) 201da177e4SLinus Torvalds * 211da177e4SLinus Torvalds * Notable changes from the original source: 221da177e4SLinus Torvalds * - includes stripped down to the essentials 231da177e4SLinus Torvalds * - for loops used as delays replaced with udelay() 241da177e4SLinus Torvalds * - #defines removed, changed to static values 251da177e4SLinus Torvalds * - tuning structure changed - no more character arrays, other changes 261da177e4SLinus Torvalds */ 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds #include <linux/module.h> /* Modules */ 291da177e4SLinus Torvalds #include <linux/init.h> /* Initdata */ 30fb911ee8SPeter Osterlund #include <linux/ioport.h> /* request_region */ 311da177e4SLinus Torvalds #include <linux/delay.h> /* udelay */ 32a4366af4SMauro Carvalho Chehab #include <linux/videodev2.h> /* kernel radio structs */ 33e697e12eSHans Verkuil #include <linux/version.h> /* for KERNEL_VERSION MACRO */ 34e697e12eSHans Verkuil #include <linux/io.h> /* outb, outb_p */ 35e697e12eSHans Verkuil #include <linux/uaccess.h> /* copy to/from user */ 36e697e12eSHans Verkuil #include <media/v4l2-device.h> 3735ea11ffSHans Verkuil #include <media/v4l2-ioctl.h> 381da177e4SLinus Torvalds 39e697e12eSHans Verkuil MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); 40e697e12eSHans Verkuil MODULE_DESCRIPTION("A driver for the Aztech radio card."); 41e697e12eSHans Verkuil MODULE_LICENSE("GPL"); 42a4366af4SMauro Carvalho Chehab 431da177e4SLinus Torvalds /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds #ifndef CONFIG_RADIO_AZTECH_PORT 461da177e4SLinus Torvalds #define CONFIG_RADIO_AZTECH_PORT -1 471da177e4SLinus Torvalds #endif 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds static int io = CONFIG_RADIO_AZTECH_PORT; 501da177e4SLinus Torvalds static int radio_nr = -1; 511da177e4SLinus Torvalds static int radio_wait_time = 1000; 521da177e4SLinus Torvalds 53e697e12eSHans Verkuil module_param(io, int, 0); 54e697e12eSHans Verkuil module_param(radio_nr, int, 0); 55e697e12eSHans Verkuil MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)"); 56e697e12eSHans Verkuil 57e697e12eSHans Verkuil #define RADIO_VERSION KERNEL_VERSION(0, 0, 2) 58e697e12eSHans Verkuil 59e697e12eSHans Verkuil struct aztech 601da177e4SLinus Torvalds { 61e697e12eSHans Verkuil struct v4l2_device v4l2_dev; 62e697e12eSHans Verkuil struct video_device vdev; 63e697e12eSHans Verkuil int io; 641da177e4SLinus Torvalds int curvol; 651da177e4SLinus Torvalds unsigned long curfreq; 661da177e4SLinus Torvalds int stereo; 67e697e12eSHans Verkuil struct mutex lock; 681da177e4SLinus Torvalds }; 691da177e4SLinus Torvalds 70e697e12eSHans Verkuil static struct aztech aztech_card; 71e697e12eSHans Verkuil 721da177e4SLinus Torvalds static int volconvert(int level) 731da177e4SLinus Torvalds { 741da177e4SLinus Torvalds level >>= 14; /* Map 16bits down to 2 bit */ 751da177e4SLinus Torvalds level &= 3; 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds /* convert to card-friendly values */ 78e697e12eSHans Verkuil switch (level) { 791da177e4SLinus Torvalds case 0: 801da177e4SLinus Torvalds return 0; 811da177e4SLinus Torvalds case 1: 821da177e4SLinus Torvalds return 1; 831da177e4SLinus Torvalds case 2: 841da177e4SLinus Torvalds return 4; 851da177e4SLinus Torvalds case 3: 861da177e4SLinus Torvalds return 5; 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds return 0; /* Quieten gcc */ 891da177e4SLinus Torvalds } 901da177e4SLinus Torvalds 91e697e12eSHans Verkuil static void send_0_byte(struct aztech *az) 921da177e4SLinus Torvalds { 931da177e4SLinus Torvalds udelay(radio_wait_time); 94e697e12eSHans Verkuil outb_p(2 + volconvert(az->curvol), az->io); 95e697e12eSHans Verkuil outb_p(64 + 2 + volconvert(az->curvol), az->io); 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds 98e697e12eSHans Verkuil static void send_1_byte(struct aztech *az) 991da177e4SLinus Torvalds { 1001da177e4SLinus Torvalds udelay (radio_wait_time); 101e697e12eSHans Verkuil outb_p(128 + 2 + volconvert(az->curvol), az->io); 102e697e12eSHans Verkuil outb_p(128 + 64 + 2 + volconvert(az->curvol), az->io); 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds 105e697e12eSHans Verkuil static int az_setvol(struct aztech *az, int vol) 1061da177e4SLinus Torvalds { 107e697e12eSHans Verkuil mutex_lock(&az->lock); 108e697e12eSHans Verkuil outb(volconvert(vol), az->io); 109e697e12eSHans Verkuil mutex_unlock(&az->lock); 1101da177e4SLinus Torvalds return 0; 1111da177e4SLinus Torvalds } 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds /* thanks to Michael Dwyer for giving me a dose of clues in 1141da177e4SLinus Torvalds * the signal strength department.. 1151da177e4SLinus Torvalds * 1161da177e4SLinus Torvalds * This card has a stereo bit - bit 0 set = mono, not set = stereo 1171da177e4SLinus Torvalds * It also has a "signal" bit - bit 1 set = bad signal, not set = good 1181da177e4SLinus Torvalds * 1191da177e4SLinus Torvalds */ 1201da177e4SLinus Torvalds 121e697e12eSHans Verkuil static int az_getsigstr(struct aztech *az) 1221da177e4SLinus Torvalds { 123e697e12eSHans Verkuil int sig = 1; 124e697e12eSHans Verkuil 125e697e12eSHans Verkuil mutex_lock(&az->lock); 126e697e12eSHans Verkuil if (inb(az->io) & 2) /* bit set = no signal present */ 127e697e12eSHans Verkuil sig = 0; 128e697e12eSHans Verkuil mutex_unlock(&az->lock); 129e697e12eSHans Verkuil return sig; 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 132e697e12eSHans Verkuil static int az_getstereo(struct aztech *az) 1331da177e4SLinus Torvalds { 134e697e12eSHans Verkuil int stereo = 1; 135e697e12eSHans Verkuil 136e697e12eSHans Verkuil mutex_lock(&az->lock); 137e697e12eSHans Verkuil if (inb(az->io) & 1) /* bit set = mono */ 138e697e12eSHans Verkuil stereo = 0; 139e697e12eSHans Verkuil mutex_unlock(&az->lock); 140e697e12eSHans Verkuil return stereo; 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds 143e697e12eSHans Verkuil static int az_setfreq(struct aztech *az, unsigned long frequency) 1441da177e4SLinus Torvalds { 1451da177e4SLinus Torvalds int i; 1461da177e4SLinus Torvalds 147e697e12eSHans Verkuil mutex_lock(&az->lock); 148e697e12eSHans Verkuil 149e697e12eSHans Verkuil az->curfreq = frequency; 1501da177e4SLinus Torvalds frequency += 171200; /* Add 10.7 MHz IF */ 1511da177e4SLinus Torvalds frequency /= 800; /* Convert to 50 kHz units */ 1521da177e4SLinus Torvalds 153e697e12eSHans Verkuil send_0_byte(az); /* 0: LSB of frequency */ 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ 1561da177e4SLinus Torvalds if (frequency & (1 << i)) 157e697e12eSHans Verkuil send_1_byte(az); 1581da177e4SLinus Torvalds else 159e697e12eSHans Verkuil send_0_byte(az); 1601da177e4SLinus Torvalds 161e697e12eSHans Verkuil send_0_byte(az); /* 14: test bit - always 0 */ 162e697e12eSHans Verkuil send_0_byte(az); /* 15: test bit - always 0 */ 163e697e12eSHans Verkuil send_0_byte(az); /* 16: band data 0 - always 0 */ 164e697e12eSHans Verkuil if (az->stereo) /* 17: stereo (1 to enable) */ 165e697e12eSHans Verkuil send_1_byte(az); 1661da177e4SLinus Torvalds else 167e697e12eSHans Verkuil send_0_byte(az); 1681da177e4SLinus Torvalds 169e697e12eSHans Verkuil send_1_byte(az); /* 18: band data 1 - unknown */ 170e697e12eSHans Verkuil send_0_byte(az); /* 19: time base - always 0 */ 171e697e12eSHans Verkuil send_0_byte(az); /* 20: spacing (0 = 25 kHz) */ 172e697e12eSHans Verkuil send_1_byte(az); /* 21: spacing (1 = 25 kHz) */ 173e697e12eSHans Verkuil send_0_byte(az); /* 22: spacing (0 = 25 kHz) */ 174e697e12eSHans Verkuil send_1_byte(az); /* 23: AM/FM (FM = 1, always) */ 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds /* latch frequency */ 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds udelay(radio_wait_time); 179e697e12eSHans Verkuil outb_p(128 + 64 + volconvert(az->curvol), az->io); 1801da177e4SLinus Torvalds 181e697e12eSHans Verkuil mutex_unlock(&az->lock); 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds return 0; 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds 18699218fe4SMauro Carvalho Chehab static int vidioc_querycap(struct file *file, void *priv, 18799218fe4SMauro Carvalho Chehab struct v4l2_capability *v) 1881da177e4SLinus Torvalds { 189a4366af4SMauro Carvalho Chehab strlcpy(v->driver, "radio-aztech", sizeof(v->driver)); 190a4366af4SMauro Carvalho Chehab strlcpy(v->card, "Aztech Radio", sizeof(v->card)); 191e697e12eSHans Verkuil strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); 192a4366af4SMauro Carvalho Chehab v->version = RADIO_VERSION; 193e697e12eSHans Verkuil v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; 1941da177e4SLinus Torvalds return 0; 1951da177e4SLinus Torvalds } 19699218fe4SMauro Carvalho Chehab 19799218fe4SMauro Carvalho Chehab static int vidioc_g_tuner(struct file *file, void *priv, 19899218fe4SMauro Carvalho Chehab struct v4l2_tuner *v) 1991da177e4SLinus Torvalds { 200e697e12eSHans Verkuil struct aztech *az = video_drvdata(file); 201a4366af4SMauro Carvalho Chehab 202a4366af4SMauro Carvalho Chehab if (v->index > 0) 2031da177e4SLinus Torvalds return -EINVAL; 204a4366af4SMauro Carvalho Chehab 205e697e12eSHans Verkuil strlcpy(v->name, "FM", sizeof(v->name)); 206a4366af4SMauro Carvalho Chehab v->type = V4L2_TUNER_RADIO; 207a4366af4SMauro Carvalho Chehab 208e697e12eSHans Verkuil v->rangelow = 87 * 16000; 209e697e12eSHans Verkuil v->rangehigh = 108 * 16000; 210a4366af4SMauro Carvalho Chehab v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; 211a4366af4SMauro Carvalho Chehab v->capability = V4L2_TUNER_CAP_LOW; 2121da177e4SLinus Torvalds if (az_getstereo(az)) 213a4366af4SMauro Carvalho Chehab v->audmode = V4L2_TUNER_MODE_STEREO; 214a4366af4SMauro Carvalho Chehab else 215a4366af4SMauro Carvalho Chehab v->audmode = V4L2_TUNER_MODE_MONO; 216a4366af4SMauro Carvalho Chehab v->signal = 0xFFFF * az_getsigstr(az); 217a4366af4SMauro Carvalho Chehab 2181da177e4SLinus Torvalds return 0; 2191da177e4SLinus Torvalds } 220a4366af4SMauro Carvalho Chehab 22199218fe4SMauro Carvalho Chehab static int vidioc_s_tuner(struct file *file, void *priv, 22299218fe4SMauro Carvalho Chehab struct v4l2_tuner *v) 22399218fe4SMauro Carvalho Chehab { 224e697e12eSHans Verkuil return v->index ? -EINVAL : 0; 225676b0ac7SMauro Carvalho Chehab } 226676b0ac7SMauro Carvalho Chehab 227a0c05ab9SMauro Carvalho Chehab static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 228a0c05ab9SMauro Carvalho Chehab { 229a0c05ab9SMauro Carvalho Chehab *i = 0; 230a0c05ab9SMauro Carvalho Chehab return 0; 231a0c05ab9SMauro Carvalho Chehab } 232a0c05ab9SMauro Carvalho Chehab 233a0c05ab9SMauro Carvalho Chehab static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 234a0c05ab9SMauro Carvalho Chehab { 235e697e12eSHans Verkuil return i ? -EINVAL : 0; 236a0c05ab9SMauro Carvalho Chehab } 237a0c05ab9SMauro Carvalho Chehab 238e697e12eSHans Verkuil static int vidioc_g_audio(struct file *file, void *priv, 239e697e12eSHans Verkuil struct v4l2_audio *a) 240e697e12eSHans Verkuil { 241e697e12eSHans Verkuil a->index = 0; 242e697e12eSHans Verkuil strlcpy(a->name, "Radio", sizeof(a->name)); 243e697e12eSHans Verkuil a->capability = V4L2_AUDCAP_STEREO; 244e697e12eSHans Verkuil return 0; 245e697e12eSHans Verkuil } 246a0c05ab9SMauro Carvalho Chehab 247676b0ac7SMauro Carvalho Chehab static int vidioc_s_audio(struct file *file, void *priv, 248676b0ac7SMauro Carvalho Chehab struct v4l2_audio *a) 249676b0ac7SMauro Carvalho Chehab { 250e697e12eSHans Verkuil return a->index ? -EINVAL : 0; 251676b0ac7SMauro Carvalho Chehab } 252676b0ac7SMauro Carvalho Chehab 25399218fe4SMauro Carvalho Chehab static int vidioc_s_frequency(struct file *file, void *priv, 25499218fe4SMauro Carvalho Chehab struct v4l2_frequency *f) 2551da177e4SLinus Torvalds { 256e697e12eSHans Verkuil struct aztech *az = video_drvdata(file); 257a4366af4SMauro Carvalho Chehab 258e697e12eSHans Verkuil az_setfreq(az, f->frequency); 2591da177e4SLinus Torvalds return 0; 2601da177e4SLinus Torvalds } 26199218fe4SMauro Carvalho Chehab 26299218fe4SMauro Carvalho Chehab static int vidioc_g_frequency(struct file *file, void *priv, 26399218fe4SMauro Carvalho Chehab struct v4l2_frequency *f) 2641da177e4SLinus Torvalds { 265e697e12eSHans Verkuil struct aztech *az = video_drvdata(file); 2661da177e4SLinus Torvalds 267a4366af4SMauro Carvalho Chehab f->type = V4L2_TUNER_RADIO; 268a4366af4SMauro Carvalho Chehab f->frequency = az->curfreq; 2691da177e4SLinus Torvalds return 0; 2701da177e4SLinus Torvalds } 271a4366af4SMauro Carvalho Chehab 27299218fe4SMauro Carvalho Chehab static int vidioc_queryctrl(struct file *file, void *priv, 27399218fe4SMauro Carvalho Chehab struct v4l2_queryctrl *qc) 274a4366af4SMauro Carvalho Chehab { 275e697e12eSHans Verkuil switch (qc->id) { 276e697e12eSHans Verkuil case V4L2_CID_AUDIO_MUTE: 277e697e12eSHans Verkuil return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); 278e697e12eSHans Verkuil case V4L2_CID_AUDIO_VOLUME: 279e697e12eSHans Verkuil return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff); 280a4366af4SMauro Carvalho Chehab } 281a4366af4SMauro Carvalho Chehab return -EINVAL; 282a4366af4SMauro Carvalho Chehab } 28399218fe4SMauro Carvalho Chehab 28499218fe4SMauro Carvalho Chehab static int vidioc_g_ctrl(struct file *file, void *priv, 28599218fe4SMauro Carvalho Chehab struct v4l2_control *ctrl) 286a4366af4SMauro Carvalho Chehab { 287e697e12eSHans Verkuil struct aztech *az = video_drvdata(file); 288a4366af4SMauro Carvalho Chehab 289a4366af4SMauro Carvalho Chehab switch (ctrl->id) { 290a4366af4SMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 291a4366af4SMauro Carvalho Chehab if (az->curvol == 0) 292a4366af4SMauro Carvalho Chehab ctrl->value = 1; 293a4366af4SMauro Carvalho Chehab else 294a4366af4SMauro Carvalho Chehab ctrl->value = 0; 295e697e12eSHans Verkuil return 0; 296a4366af4SMauro Carvalho Chehab case V4L2_CID_AUDIO_VOLUME: 297a4366af4SMauro Carvalho Chehab ctrl->value = az->curvol * 6554; 298e697e12eSHans Verkuil return 0; 299a4366af4SMauro Carvalho Chehab } 300a4366af4SMauro Carvalho Chehab return -EINVAL; 301a4366af4SMauro Carvalho Chehab } 30299218fe4SMauro Carvalho Chehab 30399218fe4SMauro Carvalho Chehab static int vidioc_s_ctrl(struct file *file, void *priv, 30499218fe4SMauro Carvalho Chehab struct v4l2_control *ctrl) 305a4366af4SMauro Carvalho Chehab { 306e697e12eSHans Verkuil struct aztech *az = video_drvdata(file); 307a4366af4SMauro Carvalho Chehab 308a4366af4SMauro Carvalho Chehab switch (ctrl->id) { 309a4366af4SMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 310e697e12eSHans Verkuil if (ctrl->value) 311a4366af4SMauro Carvalho Chehab az_setvol(az, 0); 312e697e12eSHans Verkuil else 313a4366af4SMauro Carvalho Chehab az_setvol(az, az->curvol); 314e697e12eSHans Verkuil return 0; 315a4366af4SMauro Carvalho Chehab case V4L2_CID_AUDIO_VOLUME: 316a4366af4SMauro Carvalho Chehab az_setvol(az, ctrl->value); 317e697e12eSHans Verkuil return 0; 318a4366af4SMauro Carvalho Chehab } 319a4366af4SMauro Carvalho Chehab return -EINVAL; 320a4366af4SMauro Carvalho Chehab } 321a4366af4SMauro Carvalho Chehab 322e697e12eSHans Verkuil static int aztech_open(struct file *file) 3233ca685aaSHans Verkuil { 324e697e12eSHans Verkuil return 0; 3253ca685aaSHans Verkuil } 3263ca685aaSHans Verkuil 327e697e12eSHans Verkuil static int aztech_release(struct file *file) 3283ca685aaSHans Verkuil { 3293ca685aaSHans Verkuil return 0; 3303ca685aaSHans Verkuil } 3313ca685aaSHans Verkuil 332bec43661SHans Verkuil static const struct v4l2_file_operations aztech_fops = { 3331da177e4SLinus Torvalds .owner = THIS_MODULE, 334e697e12eSHans Verkuil .open = aztech_open, 335e697e12eSHans Verkuil .release = aztech_release, 33699218fe4SMauro Carvalho Chehab .ioctl = video_ioctl2, 3371da177e4SLinus Torvalds }; 3381da177e4SLinus Torvalds 339a399810cSHans Verkuil static const struct v4l2_ioctl_ops aztech_ioctl_ops = { 34099218fe4SMauro Carvalho Chehab .vidioc_querycap = vidioc_querycap, 34199218fe4SMauro Carvalho Chehab .vidioc_g_tuner = vidioc_g_tuner, 34299218fe4SMauro Carvalho Chehab .vidioc_s_tuner = vidioc_s_tuner, 343676b0ac7SMauro Carvalho Chehab .vidioc_g_audio = vidioc_g_audio, 344676b0ac7SMauro Carvalho Chehab .vidioc_s_audio = vidioc_s_audio, 345a0c05ab9SMauro Carvalho Chehab .vidioc_g_input = vidioc_g_input, 346a0c05ab9SMauro Carvalho Chehab .vidioc_s_input = vidioc_s_input, 34799218fe4SMauro Carvalho Chehab .vidioc_g_frequency = vidioc_g_frequency, 34899218fe4SMauro Carvalho Chehab .vidioc_s_frequency = vidioc_s_frequency, 34999218fe4SMauro Carvalho Chehab .vidioc_queryctrl = vidioc_queryctrl, 35099218fe4SMauro Carvalho Chehab .vidioc_g_ctrl = vidioc_g_ctrl, 35199218fe4SMauro Carvalho Chehab .vidioc_s_ctrl = vidioc_s_ctrl, 3521da177e4SLinus Torvalds }; 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds static int __init aztech_init(void) 3551da177e4SLinus Torvalds { 356e697e12eSHans Verkuil struct aztech *az = &aztech_card; 357e697e12eSHans Verkuil struct v4l2_device *v4l2_dev = &az->v4l2_dev; 358e697e12eSHans Verkuil int res; 359e697e12eSHans Verkuil 360e697e12eSHans Verkuil strlcpy(v4l2_dev->name, "aztech", sizeof(v4l2_dev->name)); 361e697e12eSHans Verkuil az->io = io; 362e697e12eSHans Verkuil 363e697e12eSHans Verkuil if (az->io == -1) { 364e697e12eSHans Verkuil v4l2_err(v4l2_dev, "you must set an I/O address with io=0x???\n"); 3651da177e4SLinus Torvalds return -EINVAL; 3661da177e4SLinus Torvalds } 3671da177e4SLinus Torvalds 368e697e12eSHans Verkuil if (!request_region(az->io, 2, "aztech")) { 369e697e12eSHans Verkuil v4l2_err(v4l2_dev, "port 0x%x already in use\n", az->io); 3701da177e4SLinus Torvalds return -EBUSY; 3711da177e4SLinus Torvalds } 3721da177e4SLinus Torvalds 373e697e12eSHans Verkuil res = v4l2_device_register(NULL, v4l2_dev); 374e697e12eSHans Verkuil if (res < 0) { 375e697e12eSHans Verkuil release_region(az->io, 2); 376e697e12eSHans Verkuil v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); 377e697e12eSHans Verkuil return res; 378e697e12eSHans Verkuil } 3791da177e4SLinus Torvalds 380e697e12eSHans Verkuil mutex_init(&az->lock); 381e697e12eSHans Verkuil strlcpy(az->vdev.name, v4l2_dev->name, sizeof(az->vdev.name)); 382e697e12eSHans Verkuil az->vdev.v4l2_dev = v4l2_dev; 383e697e12eSHans Verkuil az->vdev.fops = &aztech_fops; 384e697e12eSHans Verkuil az->vdev.ioctl_ops = &aztech_ioctl_ops; 385e697e12eSHans Verkuil az->vdev.release = video_device_release_empty; 386e697e12eSHans Verkuil video_set_drvdata(&az->vdev, az); 387e697e12eSHans Verkuil 388e697e12eSHans Verkuil if (video_register_device(&az->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { 389e697e12eSHans Verkuil v4l2_device_unregister(v4l2_dev); 390e697e12eSHans Verkuil release_region(az->io, 2); 3911da177e4SLinus Torvalds return -EINVAL; 3921da177e4SLinus Torvalds } 3931da177e4SLinus Torvalds 394e697e12eSHans Verkuil v4l2_info(v4l2_dev, "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n"); 3951da177e4SLinus Torvalds /* mute card - prevents noisy bootups */ 396e697e12eSHans Verkuil outb(0, az->io); 3971da177e4SLinus Torvalds return 0; 3981da177e4SLinus Torvalds } 3991da177e4SLinus Torvalds 400e697e12eSHans Verkuil static void __exit aztech_exit(void) 4011da177e4SLinus Torvalds { 402e697e12eSHans Verkuil struct aztech *az = &aztech_card; 403e697e12eSHans Verkuil 404e697e12eSHans Verkuil video_unregister_device(&az->vdev); 405e697e12eSHans Verkuil v4l2_device_unregister(&az->v4l2_dev); 406e697e12eSHans Verkuil release_region(az->io, 2); 4071da177e4SLinus Torvalds } 4081da177e4SLinus Torvalds 4091da177e4SLinus Torvalds module_init(aztech_init); 410e697e12eSHans Verkuil module_exit(aztech_exit); 411