11da177e4SLinus Torvalds /* radiotrack (radioreveal) driver for Linux radio support 21da177e4SLinus Torvalds * (c) 1997 M. Kirkwood 346ff2c72SMauro Carvalho Chehab * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 41da177e4SLinus Torvalds * Converted to new API by Alan Cox <Alan.Cox@linux.org> 51da177e4SLinus Torvalds * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * History: 81da177e4SLinus Torvalds * 1999-02-24 Russell Kroll <rkroll@exploits.org> 91da177e4SLinus Torvalds * Fine tuning/VIDEO_TUNER_LOW 101da177e4SLinus Torvalds * Frequency range expanded to start at 87 MHz 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * TODO: Allow for more than one of these foolish entities :-) 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * Notes on the hardware (reverse engineered from other peoples' 151da177e4SLinus Torvalds * reverse engineering of AIMS' code :-) 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); 181da177e4SLinus Torvalds * 191da177e4SLinus Torvalds * The signal strength query is unsurprisingly inaccurate. And it seems 201da177e4SLinus Torvalds * to indicate that (on my card, at least) the frequency setting isn't 211da177e4SLinus Torvalds * too great. (I have to tune up .025MHz from what the freq should be 221da177e4SLinus Torvalds * to get a report that the thing is tuned.) 231da177e4SLinus Torvalds * 241da177e4SLinus Torvalds * Volume control is (ugh) analogue: 251da177e4SLinus Torvalds * out(port, start_increasing_volume); 261da177e4SLinus Torvalds * wait(a_wee_while); 271da177e4SLinus Torvalds * out(port, stop_changing_the_volume); 281da177e4SLinus Torvalds * 291da177e4SLinus Torvalds */ 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #include <linux/module.h> /* Modules */ 321da177e4SLinus Torvalds #include <linux/init.h> /* Initdata */ 33fb911ee8SPeter Osterlund #include <linux/ioport.h> /* request_region */ 341da177e4SLinus Torvalds #include <linux/delay.h> /* udelay */ 351da177e4SLinus Torvalds #include <asm/io.h> /* outb, outb_p */ 361da177e4SLinus Torvalds #include <asm/uaccess.h> /* copy to/from user */ 3746ff2c72SMauro Carvalho Chehab #include <linux/videodev2.h> /* kernel radio structs */ 385e87efa3SMauro Carvalho Chehab #include <media/v4l2-common.h> 3935ea11ffSHans Verkuil #include <media/v4l2-ioctl.h> 401da177e4SLinus Torvalds 4146ff2c72SMauro Carvalho Chehab #include <linux/version.h> /* for KERNEL_VERSION MACRO */ 4246ff2c72SMauro Carvalho Chehab #define RADIO_VERSION KERNEL_VERSION(0,0,2) 4346ff2c72SMauro Carvalho Chehab 441da177e4SLinus Torvalds #ifndef CONFIG_RADIO_RTRACK_PORT 451da177e4SLinus Torvalds #define CONFIG_RADIO_RTRACK_PORT -1 461da177e4SLinus Torvalds #endif 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds static int io = CONFIG_RADIO_RTRACK_PORT; 491da177e4SLinus Torvalds static int radio_nr = -1; 503593cab5SIngo Molnar static struct mutex lock; 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds struct rt_device 531da177e4SLinus Torvalds { 541da177e4SLinus Torvalds int port; 551da177e4SLinus Torvalds int curvol; 561da177e4SLinus Torvalds unsigned long curfreq; 571da177e4SLinus Torvalds int muted; 581da177e4SLinus Torvalds }; 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds /* local things */ 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds static void sleep_delay(long n) 641da177e4SLinus Torvalds { 651da177e4SLinus Torvalds /* Sleep nicely for 'n' uS */ 66a2d66a37SMauro Carvalho Chehab int d=n/msecs_to_jiffies(1000); 671da177e4SLinus Torvalds if(!d) 681da177e4SLinus Torvalds udelay(n); 691da177e4SLinus Torvalds else 701da177e4SLinus Torvalds msleep(jiffies_to_msecs(d)); 711da177e4SLinus Torvalds } 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds static void rt_decvol(void) 741da177e4SLinus Torvalds { 751da177e4SLinus Torvalds outb(0x58, io); /* volume down + sigstr + on */ 761da177e4SLinus Torvalds sleep_delay(100000); 771da177e4SLinus Torvalds outb(0xd8, io); /* volume steady + sigstr + on */ 781da177e4SLinus Torvalds } 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds static void rt_incvol(void) 811da177e4SLinus Torvalds { 821da177e4SLinus Torvalds outb(0x98, io); /* volume up + sigstr + on */ 831da177e4SLinus Torvalds sleep_delay(100000); 841da177e4SLinus Torvalds outb(0xd8, io); /* volume steady + sigstr + on */ 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds static void rt_mute(struct rt_device *dev) 881da177e4SLinus Torvalds { 891da177e4SLinus Torvalds dev->muted = 1; 903593cab5SIngo Molnar mutex_lock(&lock); 911da177e4SLinus Torvalds outb(0xd0, io); /* volume steady, off */ 923593cab5SIngo Molnar mutex_unlock(&lock); 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds static int rt_setvol(struct rt_device *dev, int vol) 961da177e4SLinus Torvalds { 971da177e4SLinus Torvalds int i; 981da177e4SLinus Torvalds 993593cab5SIngo Molnar mutex_lock(&lock); 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds if(vol == dev->curvol) { /* requested volume = current */ 1021da177e4SLinus Torvalds if (dev->muted) { /* user is unmuting the card */ 1031da177e4SLinus Torvalds dev->muted = 0; 1041da177e4SLinus Torvalds outb (0xd8, io); /* enable card */ 1051da177e4SLinus Torvalds } 1063593cab5SIngo Molnar mutex_unlock(&lock); 1071da177e4SLinus Torvalds return 0; 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds if(vol == 0) { /* volume = 0 means mute the card */ 1111da177e4SLinus Torvalds outb(0x48, io); /* volume down but still "on" */ 1121da177e4SLinus Torvalds sleep_delay(2000000); /* make sure it's totally down */ 1131da177e4SLinus Torvalds outb(0xd0, io); /* volume steady, off */ 1141da177e4SLinus Torvalds dev->curvol = 0; /* track the volume state! */ 1153593cab5SIngo Molnar mutex_unlock(&lock); 1161da177e4SLinus Torvalds return 0; 1171da177e4SLinus Torvalds } 1181da177e4SLinus Torvalds 1191da177e4SLinus Torvalds dev->muted = 0; 1201da177e4SLinus Torvalds if(vol > dev->curvol) 1211da177e4SLinus Torvalds for(i = dev->curvol; i < vol; i++) 1221da177e4SLinus Torvalds rt_incvol(); 1231da177e4SLinus Torvalds else 1241da177e4SLinus Torvalds for(i = dev->curvol; i > vol; i--) 1251da177e4SLinus Torvalds rt_decvol(); 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds dev->curvol = vol; 1283593cab5SIngo Molnar mutex_unlock(&lock); 1291da177e4SLinus Torvalds return 0; 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds /* the 128+64 on these outb's is to keep the volume stable while tuning 1331da177e4SLinus Torvalds * without them, the volume _will_ creep up with each frequency change 1341da177e4SLinus Torvalds * and bit 4 (+16) is to keep the signal strength meter enabled 1351da177e4SLinus Torvalds */ 1361da177e4SLinus Torvalds 1371da177e4SLinus Torvalds static void send_0_byte(int port, struct rt_device *dev) 1381da177e4SLinus Torvalds { 1391da177e4SLinus Torvalds if ((dev->curvol == 0) || (dev->muted)) { 1401da177e4SLinus Torvalds outb_p(128+64+16+ 1, port); /* wr-enable + data low */ 1411da177e4SLinus Torvalds outb_p(128+64+16+2+1, port); /* clock */ 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds else { 1441da177e4SLinus Torvalds outb_p(128+64+16+8+ 1, port); /* on + wr-enable + data low */ 1451da177e4SLinus Torvalds outb_p(128+64+16+8+2+1, port); /* clock */ 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds sleep_delay(1000); 1481da177e4SLinus Torvalds } 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds static void send_1_byte(int port, struct rt_device *dev) 1511da177e4SLinus Torvalds { 1521da177e4SLinus Torvalds if ((dev->curvol == 0) || (dev->muted)) { 1531da177e4SLinus Torvalds outb_p(128+64+16+4 +1, port); /* wr-enable+data high */ 1541da177e4SLinus Torvalds outb_p(128+64+16+4+2+1, port); /* clock */ 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds else { 1571da177e4SLinus Torvalds outb_p(128+64+16+8+4 +1, port); /* on+wr-enable+data high */ 1581da177e4SLinus Torvalds outb_p(128+64+16+8+4+2+1, port); /* clock */ 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds sleep_delay(1000); 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds static int rt_setfreq(struct rt_device *dev, unsigned long freq) 1651da177e4SLinus Torvalds { 1661da177e4SLinus Torvalds int i; 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds /* adapted from radio-aztech.c */ 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds /* now uses VIDEO_TUNER_LOW for fine tuning */ 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds freq += 171200; /* Add 10.7 MHz IF */ 1731da177e4SLinus Torvalds freq /= 800; /* Convert to 50 kHz units */ 1741da177e4SLinus Torvalds 1753593cab5SIngo Molnar mutex_lock(&lock); /* Stop other ops interfering */ 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds send_0_byte (io, dev); /* 0: LSB of frequency */ 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ 1801da177e4SLinus Torvalds if (freq & (1 << i)) 1811da177e4SLinus Torvalds send_1_byte (io, dev); 1821da177e4SLinus Torvalds else 1831da177e4SLinus Torvalds send_0_byte (io, dev); 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds send_0_byte (io, dev); /* 14: test bit - always 0 */ 1861da177e4SLinus Torvalds send_0_byte (io, dev); /* 15: test bit - always 0 */ 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds send_0_byte (io, dev); /* 16: band data 0 - always 0 */ 1891da177e4SLinus Torvalds send_0_byte (io, dev); /* 17: band data 1 - always 0 */ 1901da177e4SLinus Torvalds send_0_byte (io, dev); /* 18: band data 2 - always 0 */ 1911da177e4SLinus Torvalds send_0_byte (io, dev); /* 19: time base - always 0 */ 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds send_0_byte (io, dev); /* 20: spacing (0 = 25 kHz) */ 1941da177e4SLinus Torvalds send_1_byte (io, dev); /* 21: spacing (1 = 25 kHz) */ 1951da177e4SLinus Torvalds send_0_byte (io, dev); /* 22: spacing (0 = 25 kHz) */ 1961da177e4SLinus Torvalds send_1_byte (io, dev); /* 23: AM/FM (FM = 1, always) */ 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds if ((dev->curvol == 0) || (dev->muted)) 1991da177e4SLinus Torvalds outb (0xd0, io); /* volume steady + sigstr */ 2001da177e4SLinus Torvalds else 2011da177e4SLinus Torvalds outb (0xd8, io); /* volume steady + sigstr + on */ 2021da177e4SLinus Torvalds 2033593cab5SIngo Molnar mutex_unlock(&lock); 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds return 0; 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds static int rt_getsigstr(struct rt_device *dev) 2091da177e4SLinus Torvalds { 2101da177e4SLinus Torvalds if (inb(io) & 2) /* bit set = no signal present */ 2111da177e4SLinus Torvalds return 0; 2121da177e4SLinus Torvalds return 1; /* signal present */ 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 21546ff2c72SMauro Carvalho Chehab static struct v4l2_queryctrl radio_qctrl[] = { 21646ff2c72SMauro Carvalho Chehab { 21746ff2c72SMauro Carvalho Chehab .id = V4L2_CID_AUDIO_MUTE, 21846ff2c72SMauro Carvalho Chehab .name = "Mute", 21946ff2c72SMauro Carvalho Chehab .minimum = 0, 22046ff2c72SMauro Carvalho Chehab .maximum = 1, 22146ff2c72SMauro Carvalho Chehab .default_value = 1, 22246ff2c72SMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_BOOLEAN, 22346ff2c72SMauro Carvalho Chehab },{ 22446ff2c72SMauro Carvalho Chehab .id = V4L2_CID_AUDIO_VOLUME, 22546ff2c72SMauro Carvalho Chehab .name = "Volume", 22646ff2c72SMauro Carvalho Chehab .minimum = 0, 22746ff2c72SMauro Carvalho Chehab .maximum = 0xff, 22846ff2c72SMauro Carvalho Chehab .step = 1, 22946ff2c72SMauro Carvalho Chehab .default_value = 0xff, 23046ff2c72SMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER, 23146ff2c72SMauro Carvalho Chehab } 23246ff2c72SMauro Carvalho Chehab }; 23346ff2c72SMauro Carvalho Chehab 234385e8d8fSDouglas Landgraf static int vidioc_querycap(struct file *file, void *priv, 235385e8d8fSDouglas Landgraf struct v4l2_capability *v) 2361da177e4SLinus Torvalds { 23746ff2c72SMauro Carvalho Chehab strlcpy(v->driver, "radio-aimslab", sizeof(v->driver)); 23846ff2c72SMauro Carvalho Chehab strlcpy(v->card, "RadioTrack", sizeof(v->card)); 23946ff2c72SMauro Carvalho Chehab sprintf(v->bus_info, "ISA"); 24046ff2c72SMauro Carvalho Chehab v->version = RADIO_VERSION; 24146ff2c72SMauro Carvalho Chehab v->capabilities = V4L2_CAP_TUNER; 2421da177e4SLinus Torvalds return 0; 2431da177e4SLinus Torvalds } 244385e8d8fSDouglas Landgraf 245385e8d8fSDouglas Landgraf static int vidioc_g_tuner(struct file *file, void *priv, 246385e8d8fSDouglas Landgraf struct v4l2_tuner *v) 2471da177e4SLinus Torvalds { 248385e8d8fSDouglas Landgraf struct video_device *dev = video_devdata(file); 249385e8d8fSDouglas Landgraf struct rt_device *rt = dev->priv; 25046ff2c72SMauro Carvalho Chehab 25146ff2c72SMauro Carvalho Chehab if (v->index > 0) 2521da177e4SLinus Torvalds return -EINVAL; 25346ff2c72SMauro Carvalho Chehab 25446ff2c72SMauro Carvalho Chehab strcpy(v->name, "FM"); 25546ff2c72SMauro Carvalho Chehab v->type = V4L2_TUNER_RADIO; 2561da177e4SLinus Torvalds v->rangelow = (87*16000); 2571da177e4SLinus Torvalds v->rangehigh = (108*16000); 25846ff2c72SMauro Carvalho Chehab v->rxsubchans = V4L2_TUNER_SUB_MONO; 25946ff2c72SMauro Carvalho Chehab v->capability = V4L2_TUNER_CAP_LOW; 26046ff2c72SMauro Carvalho Chehab v->audmode = V4L2_TUNER_MODE_MONO; 261385e8d8fSDouglas Landgraf v->signal = 0xffff*rt_getsigstr(rt); 2621da177e4SLinus Torvalds return 0; 2631da177e4SLinus Torvalds } 26446ff2c72SMauro Carvalho Chehab 265385e8d8fSDouglas Landgraf static int vidioc_s_tuner(struct file *file, void *priv, 266385e8d8fSDouglas Landgraf struct v4l2_tuner *v) 267385e8d8fSDouglas Landgraf { 26846ff2c72SMauro Carvalho Chehab if (v->index > 0) 2691da177e4SLinus Torvalds return -EINVAL; 2701da177e4SLinus Torvalds return 0; 2711da177e4SLinus Torvalds } 272385e8d8fSDouglas Landgraf 273385e8d8fSDouglas Landgraf static int vidioc_s_frequency(struct file *file, void *priv, 274385e8d8fSDouglas Landgraf struct v4l2_frequency *f) 2751da177e4SLinus Torvalds { 276385e8d8fSDouglas Landgraf struct video_device *dev = video_devdata(file); 277385e8d8fSDouglas Landgraf struct rt_device *rt = dev->priv; 27846ff2c72SMauro Carvalho Chehab 27946ff2c72SMauro Carvalho Chehab rt->curfreq = f->frequency; 2801da177e4SLinus Torvalds rt_setfreq(rt, rt->curfreq); 2811da177e4SLinus Torvalds return 0; 2821da177e4SLinus Torvalds } 283385e8d8fSDouglas Landgraf 284385e8d8fSDouglas Landgraf static int vidioc_g_frequency(struct file *file, void *priv, 285385e8d8fSDouglas Landgraf struct v4l2_frequency *f) 2861da177e4SLinus Torvalds { 287385e8d8fSDouglas Landgraf struct video_device *dev = video_devdata(file); 288385e8d8fSDouglas Landgraf struct rt_device *rt = dev->priv; 28946ff2c72SMauro Carvalho Chehab 29046ff2c72SMauro Carvalho Chehab f->type = V4L2_TUNER_RADIO; 29146ff2c72SMauro Carvalho Chehab f->frequency = rt->curfreq; 2921da177e4SLinus Torvalds return 0; 2931da177e4SLinus Torvalds } 294385e8d8fSDouglas Landgraf 295385e8d8fSDouglas Landgraf static int vidioc_queryctrl(struct file *file, void *priv, 296385e8d8fSDouglas Landgraf struct v4l2_queryctrl *qc) 2971da177e4SLinus Torvalds { 29846ff2c72SMauro Carvalho Chehab int i; 29946ff2c72SMauro Carvalho Chehab 30046ff2c72SMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 30146ff2c72SMauro Carvalho Chehab if (qc->id && qc->id == radio_qctrl[i].id) { 30246ff2c72SMauro Carvalho Chehab memcpy(qc, &(radio_qctrl[i]), 30346ff2c72SMauro Carvalho Chehab sizeof(*qc)); 304385e8d8fSDouglas Landgraf return 0; 30546ff2c72SMauro Carvalho Chehab } 30646ff2c72SMauro Carvalho Chehab } 3071da177e4SLinus Torvalds return -EINVAL; 3081da177e4SLinus Torvalds } 309385e8d8fSDouglas Landgraf 310385e8d8fSDouglas Landgraf static int vidioc_g_ctrl(struct file *file, void *priv, 311385e8d8fSDouglas Landgraf struct v4l2_control *ctrl) 31246ff2c72SMauro Carvalho Chehab { 313385e8d8fSDouglas Landgraf struct video_device *dev = video_devdata(file); 314385e8d8fSDouglas Landgraf struct rt_device *rt = dev->priv; 31546ff2c72SMauro Carvalho Chehab 31646ff2c72SMauro Carvalho Chehab switch (ctrl->id) { 31746ff2c72SMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 31846ff2c72SMauro Carvalho Chehab ctrl->value = rt->muted; 319385e8d8fSDouglas Landgraf return 0; 32046ff2c72SMauro Carvalho Chehab case V4L2_CID_AUDIO_VOLUME: 32146ff2c72SMauro Carvalho Chehab ctrl->value = rt->curvol * 6554; 322385e8d8fSDouglas Landgraf return 0; 32346ff2c72SMauro Carvalho Chehab } 32446ff2c72SMauro Carvalho Chehab return -EINVAL; 32546ff2c72SMauro Carvalho Chehab } 326385e8d8fSDouglas Landgraf 327385e8d8fSDouglas Landgraf static int vidioc_s_ctrl(struct file *file, void *priv, 328385e8d8fSDouglas Landgraf struct v4l2_control *ctrl) 32946ff2c72SMauro Carvalho Chehab { 330385e8d8fSDouglas Landgraf struct video_device *dev = video_devdata(file); 331385e8d8fSDouglas Landgraf struct rt_device *rt = dev->priv; 33246ff2c72SMauro Carvalho Chehab 33346ff2c72SMauro Carvalho Chehab switch (ctrl->id) { 33446ff2c72SMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 335385e8d8fSDouglas Landgraf if (ctrl->value) 33646ff2c72SMauro Carvalho Chehab rt_mute(rt); 337385e8d8fSDouglas Landgraf else 33846ff2c72SMauro Carvalho Chehab rt_setvol(rt,rt->curvol); 339385e8d8fSDouglas Landgraf return 0; 34046ff2c72SMauro Carvalho Chehab case V4L2_CID_AUDIO_VOLUME: 34146ff2c72SMauro Carvalho Chehab rt_setvol(rt,ctrl->value); 342385e8d8fSDouglas Landgraf return 0; 34346ff2c72SMauro Carvalho Chehab } 34446ff2c72SMauro Carvalho Chehab return -EINVAL; 34546ff2c72SMauro Carvalho Chehab } 34646ff2c72SMauro Carvalho Chehab 347385e8d8fSDouglas Landgraf static int vidioc_g_audio (struct file *file, void *priv, 348385e8d8fSDouglas Landgraf struct v4l2_audio *a) 349385e8d8fSDouglas Landgraf { 350385e8d8fSDouglas Landgraf if (a->index > 1) 351385e8d8fSDouglas Landgraf return -EINVAL; 352385e8d8fSDouglas Landgraf 353385e8d8fSDouglas Landgraf strcpy(a->name, "Radio"); 354385e8d8fSDouglas Landgraf a->capability = V4L2_AUDCAP_STEREO; 355385e8d8fSDouglas Landgraf return 0; 3561da177e4SLinus Torvalds } 3571da177e4SLinus Torvalds 358385e8d8fSDouglas Landgraf static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 3591da177e4SLinus Torvalds { 360385e8d8fSDouglas Landgraf *i = 0; 361385e8d8fSDouglas Landgraf return 0; 362385e8d8fSDouglas Landgraf } 363385e8d8fSDouglas Landgraf 364385e8d8fSDouglas Landgraf static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 365385e8d8fSDouglas Landgraf { 366385e8d8fSDouglas Landgraf if (i != 0) 367385e8d8fSDouglas Landgraf return -EINVAL; 368385e8d8fSDouglas Landgraf return 0; 369385e8d8fSDouglas Landgraf } 370385e8d8fSDouglas Landgraf 371385e8d8fSDouglas Landgraf static int vidioc_s_audio(struct file *file, void *priv, 372385e8d8fSDouglas Landgraf struct v4l2_audio *a) 373385e8d8fSDouglas Landgraf { 374385e8d8fSDouglas Landgraf if (a->index != 0) 375385e8d8fSDouglas Landgraf return -EINVAL; 376385e8d8fSDouglas Landgraf return 0; 3771da177e4SLinus Torvalds } 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds static struct rt_device rtrack_unit; 3801da177e4SLinus Torvalds 381fa027c2aSArjan van de Ven static const struct file_operations rtrack_fops = { 3821da177e4SLinus Torvalds .owner = THIS_MODULE, 3831da177e4SLinus Torvalds .open = video_exclusive_open, 3841da177e4SLinus Torvalds .release = video_exclusive_release, 385385e8d8fSDouglas Landgraf .ioctl = video_ioctl2, 386078ff795SDouglas Schilling Landgraf #ifdef CONFIG_COMPAT 3870d0fbf81SArnd Bergmann .compat_ioctl = v4l_compat_ioctl32, 388078ff795SDouglas Schilling Landgraf #endif 3891da177e4SLinus Torvalds .llseek = no_llseek, 3901da177e4SLinus Torvalds }; 3911da177e4SLinus Torvalds 3921da177e4SLinus Torvalds static struct video_device rtrack_radio= 3931da177e4SLinus Torvalds { 3941da177e4SLinus Torvalds .owner = THIS_MODULE, 3951da177e4SLinus Torvalds .name = "RadioTrack radio", 3961da177e4SLinus Torvalds .type = VID_TYPE_TUNER, 3971da177e4SLinus Torvalds .fops = &rtrack_fops, 398385e8d8fSDouglas Landgraf .vidioc_querycap = vidioc_querycap, 399385e8d8fSDouglas Landgraf .vidioc_g_tuner = vidioc_g_tuner, 400385e8d8fSDouglas Landgraf .vidioc_s_tuner = vidioc_s_tuner, 401385e8d8fSDouglas Landgraf .vidioc_g_audio = vidioc_g_audio, 402385e8d8fSDouglas Landgraf .vidioc_s_audio = vidioc_s_audio, 403385e8d8fSDouglas Landgraf .vidioc_g_input = vidioc_g_input, 404385e8d8fSDouglas Landgraf .vidioc_s_input = vidioc_s_input, 405385e8d8fSDouglas Landgraf .vidioc_g_frequency = vidioc_g_frequency, 406385e8d8fSDouglas Landgraf .vidioc_s_frequency = vidioc_s_frequency, 407385e8d8fSDouglas Landgraf .vidioc_queryctrl = vidioc_queryctrl, 408385e8d8fSDouglas Landgraf .vidioc_g_ctrl = vidioc_g_ctrl, 409385e8d8fSDouglas Landgraf .vidioc_s_ctrl = vidioc_s_ctrl, 4101da177e4SLinus Torvalds }; 4111da177e4SLinus Torvalds 4121da177e4SLinus Torvalds static int __init rtrack_init(void) 4131da177e4SLinus Torvalds { 4141da177e4SLinus Torvalds if(io==-1) 4151da177e4SLinus Torvalds { 4161da177e4SLinus Torvalds printk(KERN_ERR "You must set an I/O address with io=0x???\n"); 4171da177e4SLinus Torvalds return -EINVAL; 4181da177e4SLinus Torvalds } 4191da177e4SLinus Torvalds 4201da177e4SLinus Torvalds if (!request_region(io, 2, "rtrack")) 4211da177e4SLinus Torvalds { 4221da177e4SLinus Torvalds printk(KERN_ERR "rtrack: port 0x%x already in use\n", io); 4231da177e4SLinus Torvalds return -EBUSY; 4241da177e4SLinus Torvalds } 4251da177e4SLinus Torvalds 4261da177e4SLinus Torvalds rtrack_radio.priv=&rtrack_unit; 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds if(video_register_device(&rtrack_radio, VFL_TYPE_RADIO, radio_nr)==-1) 4291da177e4SLinus Torvalds { 4301da177e4SLinus Torvalds release_region(io, 2); 4311da177e4SLinus Torvalds return -EINVAL; 4321da177e4SLinus Torvalds } 4331da177e4SLinus Torvalds printk(KERN_INFO "AIMSlab RadioTrack/RadioReveal card driver.\n"); 4341da177e4SLinus Torvalds 4351da177e4SLinus Torvalds /* Set up the I/O locking */ 4361da177e4SLinus Torvalds 4373593cab5SIngo Molnar mutex_init(&lock); 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds /* mute card - prevents noisy bootups */ 4401da177e4SLinus Torvalds 4411da177e4SLinus Torvalds /* this ensures that the volume is all the way down */ 4421da177e4SLinus Torvalds outb(0x48, io); /* volume down but still "on" */ 4431da177e4SLinus Torvalds sleep_delay(2000000); /* make sure it's totally down */ 4441da177e4SLinus Torvalds outb(0xc0, io); /* steady volume, mute card */ 4451da177e4SLinus Torvalds rtrack_unit.curvol = 0; 4461da177e4SLinus Torvalds 4471da177e4SLinus Torvalds return 0; 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds 4501da177e4SLinus Torvalds MODULE_AUTHOR("M.Kirkwood"); 4511da177e4SLinus Torvalds MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card."); 4521da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 4531da177e4SLinus Torvalds 4541da177e4SLinus Torvalds module_param(io, int, 0); 4551da177e4SLinus Torvalds MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)"); 4561da177e4SLinus Torvalds module_param(radio_nr, int, 0); 4571da177e4SLinus Torvalds 4581da177e4SLinus Torvalds static void __exit cleanup_rtrack_module(void) 4591da177e4SLinus Torvalds { 4601da177e4SLinus Torvalds video_unregister_device(&rtrack_radio); 4611da177e4SLinus Torvalds release_region(io,2); 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds 4641da177e4SLinus Torvalds module_init(rtrack_init); 4651da177e4SLinus Torvalds module_exit(cleanup_rtrack_module); 4661da177e4SLinus Torvalds 467