From: Pekka Seppanen Date: Mon, 1 Oct 2007 03:27:55 +0000 (-0300) Subject: V4L/DVB (6244): [PATCH 1/2] GemTek Radio card X-Git-Tag: v2.6.24-rc1~1463^2~58 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4753647e6422341a091e729b9d81a9a5e7fe6179;p=linux-2.6 V4L/DVB (6244): [PATCH 1/2] GemTek Radio card Code cleanup for GemTek Radio card driver. Removed unnecessary / invalid I/O commands and rewrote code for tuning on-board BU2614FS chip. Adds several new module params for power users. Includes automatic device probing. Signed-off-by: Pekka Seppanen Signed-off-by: Douglas Schilling Landgraf Reviewed-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab --- diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index eab8c80a2e..f959bb71c4 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -26,143 +26,413 @@ #include #include -#include /* for KERNEL_VERSION MACRO */ -#define RADIO_VERSION KERNEL_VERSION(0,0,2) +#include /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,0,3) +#define RADIO_BANNER "GemTek Radio card driver: v0.0.3" -static struct v4l2_queryctrl radio_qctrl[] = { - { - .id = V4L2_CID_AUDIO_MUTE, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .default_value = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = 0, - .maximum = 65535, - .step = 65535, - .default_value = 0xff, - .type = V4L2_CTRL_TYPE_INTEGER, - } -}; +/* + * Module info. + */ + +MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen "); +MODULE_DESCRIPTION("A driver for the GemTek Radio card."); +MODULE_LICENSE("GPL"); + +/* + * Module params. + */ #ifndef CONFIG_RADIO_GEMTEK_PORT #define CONFIG_RADIO_GEMTEK_PORT -1 #endif +#ifndef CONFIG_RADIO_GEMTEK_PROBE +#define CONFIG_RADIO_GEMTEK_PROBE 1 +#endif -static int io = CONFIG_RADIO_GEMTEK_PORT; -static int radio_nr = -1; -static spinlock_t lock; +static int io = CONFIG_RADIO_GEMTEK_PORT; +static int probe = CONFIG_RADIO_GEMTEK_PROBE; +static int hardmute; +static int shutdown = 1; +static int keepmuted = 1; +static int initmute = 1; +static int radio_nr = -1; -struct gemtek_device -{ - int port; - unsigned long curfreq; +module_param(io, int, 0444); +MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic" + "probing is disabled or fails. The most common I/O ports are: 0x20c " + "0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to " + " work for the combined sound/radiocard)."); + +module_param(probe, bool, 0444); +MODULE_PARM_DESC(probe, "Enable automatic device probing. Note: only the most " + "common I/O ports used by the card are probed."); + +module_param(hardmute, bool, 0644); +MODULE_PARM_DESC(hardmute, "Enable `hard muting' by shutting down PLL, may " + "reduce static noise."); + +module_param(shutdown, bool, 0644); +MODULE_PARM_DESC(shutdown, "Enable shutting down PLL and muting line when " + "module is unloaded."); + +module_param(keepmuted, bool, 0644); +MODULE_PARM_DESC(keepmuted, "Keep card muted even when frequency is changed."); + +module_param(initmute, bool, 0444); +MODULE_PARM_DESC(initmute, "Mute card when module is loaded."); + +module_param(radio_nr, int, 0444); + +/* + * Functions for controlling the card. + */ +#define GEMTEK_LOWFREQ (87*16000) +#define GEMTEK_HIGHFREQ (108*16000) + +#define GEMTEK_CK 0x01 /* Clock signal */ +#define GEMTEK_DA 0x02 /* Serial data */ +#define GEMTEK_CE 0x04 /* Chip enable */ +#define GEMTEK_NS 0x08 /* No signal */ +#define GEMTEK_MT 0x10 /* Line mute */ +#define GEMTEK_STDF_3_125_KHZ 0x01 /* Standard frequency 3.125 kHz */ +#define GEMTEK_PLL_OFF 0x07 /* PLL off */ + +#define BU2614_BUS_SIZE 32 /* BU2614 / BU2614FS bus size */ +#define BU2614_NOPS 8 /* Number of supported operations */ + +#define SHORT_DELAY 5 /* usec */ +#define LONG_DELAY 75 /* usec */ + +struct gemtek_device { + unsigned long lastfreq; int muted; + unsigned long bu2614data[BU2614_NOPS]; }; +enum { + BU2614_VOID, + BU2614_FREQ, /* D0..D15, Frequency data */ + BU2614_PORT, /* P0..P2, Output port control data */ + BU2614_FMES, /* CT, Frequency measurement beginning data */ + BU2614_STDF, /* R0..R2, Standard frequency data */ + BU2614_SWIN, /* S, Switch between FMIN / AMIN */ + BU2614_SWAL, /* PS, Swallow counter division (AMIN only) */ + BU2614_FMUN, /* GT, Frequency measurement time and unlock */ + BU2614_TEST /* TS, Test data is input */ +}; + +struct bu2614_op { + int op; /* Operation */ + int size; /* Data size */ +}; + +static struct gemtek_device gemtek_unit; + +static struct bu2614_op bu2614ops[] = { + {.op = BU2614_FREQ, + .size = 0x10}, + {.op = BU2614_PORT, + .size = 0x03}, + {.op = BU2614_VOID, + .size = 0x04}, + {.op = BU2614_FMES, + .size = 0x01}, + {.op = BU2614_STDF, + .size = 0x03}, + {.op = BU2614_SWIN, + .size = 0x01}, + {.op = BU2614_SWAL, + .size = 0x01}, + {.op = BU2614_VOID, + .size = 0x01}, + {.op = BU2614_FMUN, + .size = 0x01}, + {.op = BU2614_TEST, + .size = 0x01} +}; -/* local things */ +static spinlock_t lock; -/* the correct way to mute the gemtek may be to write the last written - * frequency || 0x10, but just writing 0x10 once seems to do it as well +/* + * Set data which will be sent to BU2614FS. */ -static void gemtek_mute(struct gemtek_device *dev) +static void gemtek_bu2614_set(struct gemtek_device *dev, int op, + unsigned long data) { - if(dev->muted) - return; + int i, q; + + for (i = 0, q = 0; q < ARRAY_SIZE(dev->bu2614data); ++i) { + if (bu2614ops[i].op == op) { + dev->bu2614data[q] = + data & ((1 << bu2614ops[i].size) - 1); + return; + } + + if (bu2614ops[i].op != BU2614_VOID) + ++q; + } +} + +/* + * Transmit settings to BU2614FS over GemTek IC. + */ +static void gemtek_bu2614_transmit(struct gemtek_device *dev) +{ + int i, bit, q, mute; + spin_lock(&lock); - outb(0x10, io); + + mute = dev->muted ? GEMTEK_MT : 0x00; + + outb_p(mute | GEMTEK_DA | GEMTEK_CK, io); + udelay(SHORT_DELAY); + outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, io); + udelay(LONG_DELAY); + + for (i = 0, q = 0; q < ARRAY_SIZE(dev->bu2614data); ++i) { + for (bit = 0; bit < bu2614ops[i].size; ++bit) { + if (bu2614ops[i].op != BU2614_VOID && + dev->bu2614data[q] & (1 << bit)) { + outb_p(mute | GEMTEK_CE | GEMTEK_DA, io); + udelay(SHORT_DELAY); + outb_p(mute | GEMTEK_CE | GEMTEK_DA | + GEMTEK_CK, io); + udelay(SHORT_DELAY); + } else { + outb_p(mute | GEMTEK_CE, io); + udelay(SHORT_DELAY); + outb_p(mute | GEMTEK_CE | GEMTEK_CK, io); + udelay(SHORT_DELAY); + } + } + + if (bu2614ops[i].op != BU2614_VOID) + ++q; + } + + outb_p(mute | GEMTEK_DA | GEMTEK_CK, io); + udelay(SHORT_DELAY); + outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, io); + udelay(LONG_DELAY); + spin_unlock(&lock); - dev->muted = 1; } -static void gemtek_unmute(struct gemtek_device *dev) +/* + * Convert FM-frequency for BU2614FS (3.125 KHz STDF expected). + */ +static inline void gemtek_convfreq(unsigned long *freq) { - if(dev->muted == 0) + (*freq) /= 160; + (*freq) += 1052; /* FMIN, 10.52 MHz */ + (*freq) *= 1565; /* STDF, 1 / 156.5 = 0.00639 */ + (*freq) /= 1000; +} + +/* + * Set FM-frequency. + */ +static void gemtek_setfreq(struct gemtek_device *dev, unsigned long freq) +{ + + if (keepmuted && hardmute && dev->muted) return; - spin_lock(&lock); - outb(0x20, io); - spin_unlock(&lock); + + if (freq < GEMTEK_LOWFREQ) + freq = GEMTEK_LOWFREQ; + else if (freq > GEMTEK_HIGHFREQ) + freq = GEMTEK_HIGHFREQ; + + dev->lastfreq = freq; dev->muted = 0; + + gemtek_bu2614_set(dev, BU2614_PORT, 0); + gemtek_bu2614_set(dev, BU2614_FMES, 0); + gemtek_bu2614_set(dev, BU2614_SWIN, 0); /* FM-mode */ + gemtek_bu2614_set(dev, BU2614_SWAL, 0); + gemtek_bu2614_set(dev, BU2614_FMUN, 1); /* GT bit set */ + gemtek_bu2614_set(dev, BU2614_TEST, 0); + + gemtek_convfreq(&freq); + + gemtek_bu2614_set(dev, BU2614_STDF, GEMTEK_STDF_3_125_KHZ); + gemtek_bu2614_set(dev, BU2614_FREQ, freq); + + gemtek_bu2614_transmit(dev); } -static void zero(void) +/* + * Set mute flag. + */ +static void gemtek_mute(struct gemtek_device *dev) { - outb_p(0x04, io); - udelay(5); - outb_p(0x05, io); - udelay(5); + int i; + dev->muted = 1; + + if (hardmute) { + /* Turn off PLL, disable data output */ + gemtek_bu2614_set(dev, BU2614_PORT, 0); + gemtek_bu2614_set(dev, BU2614_FMES, 0); /* CT bit off */ + gemtek_bu2614_set(dev, BU2614_SWIN, 0); /* FM-mode */ + gemtek_bu2614_set(dev, BU2614_SWAL, 0); + gemtek_bu2614_set(dev, BU2614_FMUN, 0); /* GT bit off */ + gemtek_bu2614_set(dev, BU2614_TEST, 0); + gemtek_bu2614_set(dev, BU2614_STDF, GEMTEK_PLL_OFF); + gemtek_bu2614_set(dev, BU2614_FREQ, 0); + gemtek_bu2614_transmit(dev); + } else { + spin_lock(&lock); + + /* Read bus contents (CE, CK and DA). */ + i = inb_p(io); + /* Write it back with mute flag set. */ + outb_p((i >> 5) | GEMTEK_MT, io); + udelay(SHORT_DELAY); + + spin_unlock(&lock); + } } -static void one(void) +/* + * Unset mute flag. + */ +static void gemtek_unmute(struct gemtek_device *dev) { - outb_p(0x06, io); - udelay(5); - outb_p(0x07, io); - udelay(5); + int i; + dev->muted = 0; + + if (hardmute) { + /* Turn PLL back on. */ + gemtek_setfreq(dev, dev->lastfreq); + } else { + spin_lock(&lock); + + i = inb_p(io); + outb_p(i >> 5, io); + udelay(SHORT_DELAY); + + spin_unlock(&lock); + } } -static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq) +/* + * Get signal strength (= stereo status). + */ +static inline int gemtek_getsigstr(void) { - int i; + return inb_p(io) & GEMTEK_NS ? 0 : 1; +} -/* freq = 78.25*((float)freq/16000.0 + 10.52); */ +/* + * Check if requested card acts like GemTek Radio card. + */ +static int gemtek_verify(int port) +{ + static int verified = -1; + int i, q; - freq /= 16; - freq += 10520; - freq *= 7825; - freq /= 100000; + if (verified == port) + return 1; spin_lock(&lock); - /* 2 start bits */ - outb_p(0x03, io); - udelay(5); - outb_p(0x07, io); - udelay(5); + q = inb_p(port); /* Read bus contents before probing. */ + /* Try to turn on CE, CK and DA respectively and check if card responds + properly. */ + for (i = 0; i < 3; ++i) { + outb_p(1 << i, port); + udelay(SHORT_DELAY); - /* 28 frequency bits (lsb first) */ - for (i = 0; i < 14; i++) - if (freq & (1 << i)) - one(); - else - zero(); - /* 36 unknown bits */ - for (i = 0; i < 11; i++) - zero(); - one(); - for (i = 0; i < 4; i++) - zero(); - one(); - zero(); - - /* 2 end bits */ - outb_p(0x03, io); - udelay(5); - outb_p(0x07, io); - udelay(5); + if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) { + spin_unlock(&lock); + return 0; + } + } + outb_p(q >> 5, port); /* Write bus contents back. */ + udelay(SHORT_DELAY); spin_unlock(&lock); + verified = port; - return 0; + return 1; } -static int gemtek_getsigstr(struct gemtek_device *dev) +/* + * Automatic probing for card. + */ +static int gemtek_probe(void) { - spin_lock(&lock); - inb(io); - udelay(5); - spin_unlock(&lock); - if (inb(io) & 8) /* bit set = no signal present */ - return 0; - return 1; /* signal present */ + int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c }; + int i; + + if (!probe) { + printk(KERN_INFO "Automatic device probing disabled.\n"); + return -1; + } + + printk(KERN_INFO "Automatic device probing enabled.\n"); + + for (i = 0; i < ARRAY_SIZE(ioports); ++i) { + printk(KERN_INFO "Trying I/O port 0x%x...\n", ioports[i]); + + if (!request_region(ioports[i], 1, "gemtek-probe")) { + printk(KERN_WARNING "I/O port 0x%x busy!\n", + ioports[i]); + continue; + } + + if (gemtek_verify(ioports[i])) { + printk(KERN_INFO "Card found from I/O port " + "0x%x!\n", ioports[i]); + + release_region(ioports[i], 1); + + io = ioports[i]; + return io; + } + + release_region(ioports[i], 1); + } + + printk(KERN_ERR "Automatic probing failed!\n"); + + return -1; } -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) +/* + * Video 4 Linux stuff. + */ + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535, + .default_value = 0xff, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + +static struct file_operations gemtek_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = video_ioctl2, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek +}; + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) { strlcpy(v->driver, "radio-gemtek", sizeof(v->driver)); strlcpy(v->card, "GemTek", sizeof(v->card)); @@ -172,28 +442,29 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) +static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct video_device *dev = video_devdata(file); - struct gemtek_device *rt = dev->priv; - if (v->index > 0) return -EINVAL; strcpy(v->name, "FM"); v->type = V4L2_TUNER_RADIO; - v->rangelow = (87*16000); - v->rangehigh = (108*16000); - v->rxsubchans = V4L2_TUNER_SUB_MONO; - v->capability = V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xffff*gemtek_getsigstr(rt); + v->rangelow = GEMTEK_LOWFREQ; + v->rangehigh = GEMTEK_HIGHFREQ; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + v->signal = 0xffff * gemtek_getsigstr(); + if (v->signal) { + v->audmode = V4L2_TUNER_MODE_STEREO; + v->rxsubchans = V4L2_TUNER_SUB_STEREO; + } else { + v->audmode = V4L2_TUNER_MODE_MONO; + v->rxsubchans = V4L2_TUNER_SUB_MONO; + } + return 0; } -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) +static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { if (v->index > 0) return -EINVAL; @@ -201,38 +472,35 @@ static int vidioc_s_tuner(struct file *file, void *priv, } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + struct v4l2_frequency *f) { struct video_device *dev = video_devdata(file); struct gemtek_device *rt = dev->priv; - rt->curfreq = f->frequency; - /* needs to be called twice in order for getsigstr to work */ - gemtek_setfreq(rt, rt->curfreq); - gemtek_setfreq(rt, rt->curfreq); + gemtek_setfreq(rt, f->frequency); + return 0; } static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + struct v4l2_frequency *f) { struct video_device *dev = video_devdata(file); struct gemtek_device *rt = dev->priv; f->type = V4L2_TUNER_RADIO; - f->frequency = rt->curfreq; + f->frequency = rt->lastfreq; return 0; } static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) + struct v4l2_queryctrl *qc) { int i; - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + for (i = 0; i < ARRAY_SIZE(radio_qctrl); ++i) { if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); + memcpy(qc, &(radio_qctrl[i]), sizeof(*qc)); return 0; } } @@ -240,7 +508,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, } static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) + struct v4l2_control *ctrl) { struct video_device *dev = video_devdata(file); struct gemtek_device *rt = dev->priv; @@ -260,7 +528,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, } static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) + struct v4l2_control *ctrl) { struct video_device *dev = video_devdata(file); struct gemtek_device *rt = dev->priv; @@ -282,8 +550,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, return -EINVAL; } -static int vidioc_g_audio (struct file *file, void *priv, - struct v4l2_audio *a) +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) { if (a->index > 1) return -EINVAL; @@ -306,99 +573,105 @@ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) return 0; } -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) { if (a->index != 0) return -EINVAL; return 0; } -static struct gemtek_device gemtek_unit; - -static const struct file_operations gemtek_fops = { - .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, - .ioctl = video_ioctl2, - .compat_ioctl = v4l_compat_ioctl32, - .llseek = no_llseek, +static struct video_device gemtek_radio = { + .owner = THIS_MODULE, + .name = "GemTek Radio card", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_GEMTEK, + .fops = &gemtek_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl }; -static struct video_device gemtek_radio= -{ - .owner = THIS_MODULE, - .name = "GemTek radio", - .type = VID_TYPE_TUNER, - .fops = &gemtek_fops, - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, -}; +/* + * Initialization / cleanup related stuff. + */ +/* + * Initilize card. + */ static int __init gemtek_init(void) { - if(io==-1) - { - printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (io=0x020c or io=0x248 for the combined sound/radiocard)\n"); - return -EINVAL; - } + int i; - if (!request_region(io, 4, "gemtek")) - { - printk(KERN_ERR "gemtek: port 0x%x already in use\n", io); - return -EBUSY; - } + printk(KERN_INFO RADIO_BANNER "\n"); - gemtek_radio.priv=&gemtek_unit; + spin_lock_init(&lock); - if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1) - { - release_region(io, 4); + gemtek_probe(); + if (io) { + if (!request_region(io, 1, "gemtek")) { + printk(KERN_ERR "I/O port 0x%x already in use.\n", io); + return -EBUSY; + } + + if (!gemtek_verify(io)) + printk(KERN_WARNING "Card at I/O port 0x%x does not " + "respond properly, check your " + "configuration.\n", io); + else + printk(KERN_INFO "Using I/O port 0x%x.\n", io); + } else if (probe) { + printk(KERN_ERR "Automatic probing failed and no " + "fixed I/O port defined.\n"); + return -ENODEV; + } else { + printk(KERN_ERR "Automatic probing disabled but no fixed " + "I/O port defined."); return -EINVAL; } - printk(KERN_INFO "GemTek Radio Card driver.\n"); - spin_lock_init(&lock); + gemtek_radio.priv = &gemtek_unit; + + if (video_register_device(&gemtek_radio, VFL_TYPE_RADIO, + radio_nr) == -1) { + release_region(io, 1); + return -EBUSY; + } - /* this is _maybe_ unnecessary */ - outb(0x01, io); + /* Set defaults */ + gemtek_unit.lastfreq = GEMTEK_LOWFREQ; + for (i = 0; i < ARRAY_SIZE(gemtek_unit.bu2614data); ++i) + gemtek_unit.bu2614data[i] = 0; - /* mute card - prevents noisy bootups */ - gemtek_unit.muted = 0; - gemtek_mute(&gemtek_unit); + if (initmute) + gemtek_mute(&gemtek_unit); return 0; } -MODULE_AUTHOR("Jonas Munsin"); -MODULE_DESCRIPTION("A driver for the GemTek Radio Card"); -MODULE_LICENSE("GPL"); - -module_param(io, int, 0); -MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (0x20c or 0x248 have been reported to work for the combined sound/radiocard))."); -module_param(radio_nr, int, 0); - -static void __exit gemtek_cleanup(void) +/* + * Module cleanup + */ +static void __exit gemtek_exit(void) { + if (shutdown) { + hardmute = 1; /* Turn off PLL */ + gemtek_mute(&gemtek_unit); + } else { + printk(KERN_INFO "Module unloaded but card not muted!\n"); + } + video_unregister_device(&gemtek_radio); - release_region(io,4); + release_region(io, 1); } module_init(gemtek_init); -module_exit(gemtek_cleanup); - -/* - Local variables: - compile-command: "gcc -c -DMODVERSIONS -D__KERNEL__ -DMODULE -O6 -Wall -Wstrict-prototypes -I /home/blp/tmp/linux-2.1.111-rtrack/include radio-rtrack2.c" - End: -*/ +module_exit(gemtek_exit);