Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
new file mode 100644
index 0000000..d318be3
--- /dev/null
+++ b/drivers/media/radio/Kconfig
@@ -0,0 +1,354 @@
+#
+# Multimedia Video device configuration
+#
+
+menu "Radio Adapters"
+	depends on VIDEO_DEV!=n
+
+config RADIO_CADET
+	tristate "ADS Cadet AM/FM Tuner"
+	depends on ISA && VIDEO_DEV
+	---help---
+	  Choose Y here if you have one of these AM/FM radio cards, and then
+	  fill in the port address below.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  Further documentation on this driver can be found on the WWW at
+	  <http://linux.blackhawke.net/cadet/>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-cadet.
+
+config RADIO_RTRACK
+	tristate "AIMSlab RadioTrack (aka RadioReveal) support"
+	depends on ISA && VIDEO_DEV
+	---help---
+	  Choose Y here if you have one of these FM radio cards, and then fill
+	  in the port address below.
+
+	  Note that newer AIMSlab RadioTrack cards have a different chipset
+	  and are not supported by this driver.  For these cards, use the
+	  RadioTrack II driver below.
+
+	  If you have a GemTeks combined (PnP) sound- and radio card you must
+	  use this driver as a module and setup the card with isapnptools.
+	  You must also pass the module a suitable io parameter, 0x248 has
+	  been reported to be used by these cards.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>. More information is
+	  contained in the file
+	  <file:Documentation/video4linux/radiotrack.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-aimslab.
+
+config RADIO_RTRACK_PORT
+	hex "RadioTrack i/o port (0x20f or 0x30f)"
+	depends on RADIO_RTRACK=y
+	default "20f"
+	help
+	  Enter either 0x30f or 0x20f here.  The card default is 0x30f, if you
+	  haven't changed the jumper setting on the card.
+
+config RADIO_RTRACK2
+	tristate "AIMSlab RadioTrack II support"
+	depends on ISA && VIDEO_DEV
+	---help---
+	  Choose Y here if you have this FM radio card, and then fill in the
+	  port address below.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-rtrack2.
+
+config RADIO_RTRACK2_PORT
+	hex "RadioTrack II i/o port (0x20c or 0x30c)"
+	depends on RADIO_RTRACK2=y
+	default "30c"
+	help
+	  Enter either 0x30c or 0x20c here.  The card default is 0x30c, if you
+	  haven't changed the jumper setting on the card.
+
+config RADIO_AZTECH
+	tristate "Aztech/Packard Bell Radio"
+	depends on ISA && VIDEO_DEV
+	---help---
+	  Choose Y here if you have one of these FM radio cards, and then fill
+	  in the port address below.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-aztech.
+
+config RADIO_AZTECH_PORT
+	hex "Aztech/Packard Bell I/O port (0x350 or 0x358)"
+	depends on RADIO_AZTECH=y
+	default "350"
+	help
+	  Enter either 0x350 or 0x358 here.  The card default is 0x350, if you
+	  haven't changed the setting of jumper JP3 on the card.  Removing the
+	  jumper sets the card to 0x358.
+
+config RADIO_GEMTEK
+	tristate "GemTek Radio Card support"
+	depends on ISA && VIDEO_DEV
+	---help---
+	  Choose Y here if you have this FM radio card, and then fill in the
+	  port address below.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-gemtek.
+
+config RADIO_GEMTEK_PORT
+	hex "GemTek i/o port (0x20c, 0x30c, 0x24c or 0x34c)"
+	depends on RADIO_GEMTEK=y
+	default "34c"
+	help
+	  Enter either 0x20c, 0x30c, 0x24c or 0x34c here. The card default is
+	  0x34c, if you haven't changed the jumper setting on the card. On
+	  Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O
+	  port is 0x28c.
+
+config RADIO_GEMTEK_PCI
+	tristate "GemTek PCI Radio Card support"
+	depends on VIDEO_DEV && PCI
+	---help---
+	  Choose Y here if you have this PCI FM radio card.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video for Linux API.  Information on 
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-gemtek-pci.
+
+config RADIO_MAXIRADIO
+	tristate "Guillemot MAXI Radio FM 2000 radio"
+	depends on VIDEO_DEV && PCI
+	---help---
+	  Choose Y here if you have this radio card.  This card may also be
+	  found as Gemtek PCI FM.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-maxiradio.
+
+config RADIO_MAESTRO
+	tristate "Maestro on board radio"
+	depends on VIDEO_DEV
+	---help---
+	  Say Y here to directly support the on-board radio tuner on the
+	  Maestro 2 or 2E sound card.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-maestro.
+
+config RADIO_MIROPCM20
+	tristate "miroSOUND PCM20 radio"
+	depends on ISA && VIDEO_DEV && SOUND_ACI_MIXER
+	---help---
+	  Choose Y here if you have this FM radio card. You also need to say Y
+	  to "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio)" (in "Sound")
+	  for this to work.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called miropcm20.
+
+config RADIO_MIROPCM20_RDS
+	tristate "miroSOUND PCM20 radio RDS user interface (EXPERIMENTAL)"
+	depends on RADIO_MIROPCM20 && EXPERIMENTAL
+	---help---
+	  Choose Y here if you want to see RDS/RBDS information like
+	  RadioText, Programme Service name, Clock Time and date, Programme
+	  TYpe and Traffic Announcement/Programme identification.  You also
+	  need to say Y to "miroSOUND PCM20 radio" and devfs!
+
+	  It's not possible to read the raw RDS packets from the device, so
+	  the driver cant provide an V4L interface for this.  But the
+	  availability of RDS is reported over V4L by the basic driver
+	  already.  Here RDS can be read from files in /dev/v4l/rds.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called miropcm20-rds.
+
+config RADIO_SF16FMI
+	tristate "SF16FMI Radio"
+	depends on ISA && VIDEO_DEV
+	---help---
+	  Choose Y here if you have one of these FM radio cards.  If you
+	  compile the driver into the kernel and your card is not PnP one, you
+	  have to add "sf16fm=<io>" to the kernel command line (I/O address is
+	  0x284 or 0x384).
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-sf16fmi.
+
+config RADIO_SF16FMR2
+	tristate "SF16FMR2 Radio"
+	depends on ISA && VIDEO_DEV
+	---help---
+	  Choose Y here if you have one of these FM radio cards.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found on the WWW at
+	  <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-sf16fmr2.
+
+config RADIO_TERRATEC
+	tristate "TerraTec ActiveRadio ISA Standalone"
+	depends on ISA && VIDEO_DEV
+	---help---
+	  Choose Y here if you have this FM radio card, and then fill in the
+	  port address below. (TODO)
+
+	  Note: This driver is in its early stages.  Right now volume and
+	  frequency control and muting works at least for me, but
+	  unfortunately I have not found anybody who wants to use this card
+	  with Linux.  So if it is this what YOU are trying to do right now,
+	  PLEASE DROP ME A NOTE!!  Rolf Offermanns <rolf@offermanns.de>.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-terratec.
+
+config RADIO_TERRATEC_PORT
+	hex "Terratec i/o port (normally 0x590)"
+	depends on RADIO_TERRATEC=y
+	default "590"
+	help
+	  Fill in the I/O port of your TerraTec FM radio card. If unsure, go
+	  with the default.
+
+config RADIO_TRUST
+	tristate "Trust FM radio card"
+	depends on ISA && VIDEO_DEV
+	help
+	  This is a driver for the Trust FM radio cards. Say Y if you have
+	  such a card and want to use it under Linux.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-trust.
+
+config RADIO_TRUST_PORT
+	hex "Trust i/o port (usually 0x350 or 0x358)"
+	depends on RADIO_TRUST=y
+	default "350"
+	help
+	  Enter the I/O port of your Trust FM radio card. If unsure, try the
+	  values "0x350" or "0x358".
+
+config RADIO_TYPHOON
+	tristate "Typhoon Radio (a.k.a. EcoRadio)"
+	depends on ISA && VIDEO_DEV
+	---help---
+	  Choose Y here if you have one of these FM radio cards, and then fill
+	  in the port address and the frequency used for muting below.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-typhoon.
+
+config RADIO_TYPHOON_PROC_FS
+	bool "Support for /proc/radio-typhoon"
+	depends on PROC_FS && RADIO_TYPHOON
+	help
+	  Say Y here if you want the typhoon radio card driver to write
+	  status information (frequency, volume, muted, mute frequency,
+	  base address) to /proc/radio-typhoon. The file can be viewed with
+	  your favorite pager (i.e. use "more /proc/radio-typhoon" or "less
+	  /proc/radio-typhoon" or simply "cat /proc/radio-typhoon").
+
+config RADIO_TYPHOON_PORT
+	hex "Typhoon I/O port (0x316 or 0x336)"
+	depends on RADIO_TYPHOON=y
+	default "316"
+	help
+	  Enter the I/O port of your Typhoon or EcoRadio radio card.
+
+config RADIO_TYPHOON_MUTEFREQ
+	int "Typhoon frequency set when muting the device (kHz)"
+	depends on RADIO_TYPHOON=y
+	default "87500"
+	help
+	  Enter the frequency used for muting the radio. The device is never
+	  completely silent. If the volume is just turned down, you can still
+	  hear silent voices and music. For that reason, the frequency of the
+	  radio device is set to the frequency you can enter here whenever
+	  the device is muted. There should be no local radio station at that
+	  frequency.
+
+config RADIO_ZOLTRIX
+	tristate "Zoltrix Radio"
+	depends on ISA && VIDEO_DEV
+	---help---
+	  Choose Y here if you have one of these FM radio cards, and then fill
+	  in the port address below.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-zoltrix.
+
+config RADIO_ZOLTRIX_PORT
+	hex "ZOLTRIX I/O port (0x20c or 0x30c)"
+	depends on RADIO_ZOLTRIX=y
+	default "20c"
+	help
+	  Enter the I/O port of your Zoltrix radio card.
+
+endmenu
+
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
new file mode 100644
index 0000000..8b35194
--- /dev/null
+++ b/drivers/media/radio/Makefile
@@ -0,0 +1,22 @@
+#
+# Makefile for the kernel character device drivers.
+#
+
+miropcm20-objs	:= miropcm20-rds-core.o miropcm20-radio.o
+
+obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o
+obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o
+obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o
+obj-$(CONFIG_RADIO_SF16FMR2) += radio-sf16fmr2.o
+obj-$(CONFIG_RADIO_CADET) += radio-cadet.o
+obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o
+obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o
+obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o
+obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o
+obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o
+obj-$(CONFIG_RADIO_MIROPCM20) += miropcm20.o
+obj-$(CONFIG_RADIO_MIROPCM20_RDS) += miropcm20-rds.o
+obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o
+obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o
+obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
+obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
diff --git a/drivers/media/radio/miropcm20-radio.c b/drivers/media/radio/miropcm20-radio.c
new file mode 100644
index 0000000..c2ebe87
--- /dev/null
+++ b/drivers/media/radio/miropcm20-radio.c
@@ -0,0 +1,264 @@
+/* Miro PCM20 radio driver for Linux radio support
+ * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
+ * Thanks to Norberto Pellici for the ACI device interface specification
+ * The API part is based on the radiotrack driver by M. Kirkwood
+ * This driver relies on the aci mixer (drivers/sound/aci.c)
+ * Look there for further info...
+ */
+
+/* Revision history:
+ *
+ *   1998        Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
+ *   2000-09-05  Robert Siemer <Robert.Siemer@gmx.de>
+ *        removed unfinished volume control (maybe adding it later again)
+ *        use OSS-mixer; added stereo control
+ */
+
+/* What ever you think about the ACI, version 0x07 is not very well!
+ * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono
+ * conditions...                Robert 
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/videodev.h>
+#include "../../../sound/oss/aci.h"
+#include "miropcm20-rds-core.h"
+
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+
+struct pcm20_device {
+	unsigned long freq;
+	int muted;
+	int stereo;
+};
+
+
+static int pcm20_mute(struct pcm20_device *dev, unsigned char mute)
+{
+	dev->muted = mute;
+	return aci_write_cmd(ACI_SET_TUNERMUTE, mute);
+}
+
+static int pcm20_stereo(struct pcm20_device *dev, unsigned char stereo)
+{
+	dev->stereo = stereo;
+	return aci_write_cmd(ACI_SET_TUNERMONO, !stereo);
+}
+
+static int pcm20_setfreq(struct pcm20_device *dev, unsigned long freq)
+{
+	unsigned char freql;
+	unsigned char freqh;
+
+	dev->freq=freq;
+
+	freq /= 160;
+	if (!(aci_version==0x07 || aci_version>=0xb0))
+		freq /= 10;  /* I don't know exactly which version
+			      * needs this hack */
+	freql = freq & 0xff;
+	freqh = freq >> 8;
+
+	aci_rds_cmd(RDS_RESET, NULL, 0);
+	pcm20_stereo(dev, 1);
+
+	return aci_rw_cmd(ACI_WRITE_TUNE, freql, freqh);
+}
+
+static int pcm20_getflags(struct pcm20_device *dev, __u32 *flags, __u16 *signal)
+{
+	/* okay, check for signal, stereo and rds here... */
+	int i;
+	unsigned char buf;
+
+	if ((i=aci_rw_cmd(ACI_READ_TUNERSTATION, -1, -1))<0)
+		return i;
+	pr_debug("check_sig: 0x%x\n", i);
+	if (i & 0x80) {
+		/* no signal from tuner */
+		*flags=0;
+		*signal=0;
+		return 0;
+	} else
+		*signal=0xffff;
+
+	if ((i=aci_rw_cmd(ACI_READ_TUNERSTEREO, -1, -1))<0)
+		return i;
+	if (i & 0x40) {
+		*flags=0;
+	} else {
+		/* stereo */
+		*flags=VIDEO_TUNER_STEREO_ON;
+		/* I can't see stereo, when forced to mono */
+		dev->stereo=1;
+	}
+
+	if ((i=aci_rds_cmd(RDS_STATUS, &buf, 1))<0)
+		return i;
+	if (buf & 1)
+		/* RDS available */
+		*flags|=VIDEO_TUNER_RDS_ON;
+	else
+		return 0;
+
+	if ((i=aci_rds_cmd(RDS_RXVALUE, &buf, 1))<0)
+		return i;
+	pr_debug("rds-signal: %d\n", buf);
+	if (buf > 15) {
+		printk("miropcm20-radio: RX strengths unexpected high...\n");
+		buf=15;
+	}
+	/* refine signal */
+	if ((*signal=SCALE(15, 0xffff, buf))==0)
+		*signal = 1;
+
+	return 0;
+}
+
+static int pcm20_do_ioctl(struct inode *inode, struct file *file,
+			  unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct pcm20_device *pcm20 = dev->priv;
+	int i;
+	
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability *v = arg;
+			memset(v,0,sizeof(*v));
+			v->type=VID_TYPE_TUNER;
+			strcpy(v->name, "Miro PCM20");
+			v->channels=1;
+			v->audios=1;
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner)	/* Only 1 tuner */ 
+				return -EINVAL;
+			v->rangelow=87*16000;
+			v->rangehigh=108*16000;
+			pcm20_getflags(pcm20, &v->flags, &v->signal);
+			v->flags|=VIDEO_TUNER_LOW;
+			v->mode=VIDEO_MODE_AUTO;
+			strcpy(v->name, "FM");
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner!=0)
+				return -EINVAL;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+		case VIDIOCGFREQ:
+		{
+			unsigned long *freq = arg;
+			*freq = pcm20->freq;
+			return 0;
+		}
+		case VIDIOCSFREQ:
+		{
+			unsigned long *freq = arg;
+			pcm20->freq = *freq;
+			i=pcm20_setfreq(pcm20, pcm20->freq);
+			pr_debug("First view (setfreq): 0x%x\n", i);
+			return i;
+		}
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio *v = arg;
+			memset(v,0, sizeof(*v));
+			v->flags=VIDEO_AUDIO_MUTABLE;
+			if (pcm20->muted)
+				v->flags|=VIDEO_AUDIO_MUTE;
+			v->mode=VIDEO_SOUND_STEREO;
+			if (pcm20->stereo)
+				v->mode|=VIDEO_SOUND_MONO;
+			/* v->step=2048; */
+			strcpy(v->name, "Radio");
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio *v = arg;
+			if(v->audio) 
+				return -EINVAL;
+
+			pcm20_mute(pcm20, !!(v->flags&VIDEO_AUDIO_MUTE));
+			if(v->flags&VIDEO_SOUND_MONO)
+				pcm20_stereo(pcm20, 0);
+			if(v->flags&VIDEO_SOUND_STEREO)
+				pcm20_stereo(pcm20, 1);
+
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int pcm20_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, pcm20_do_ioctl);
+}
+
+static struct pcm20_device pcm20_unit = {
+	.freq   = 87*16000,
+	.muted  = 1,
+};
+
+static struct file_operations pcm20_fops = {
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl		= pcm20_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device pcm20_radio = {
+	.owner		= THIS_MODULE,
+	.name		= "Miro PCM 20 radio",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_RTRACK,
+	.fops           = &pcm20_fops,
+	.priv		= &pcm20_unit
+};
+
+static int __init pcm20_init(void)
+{
+	if(video_register_device(&pcm20_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+		goto video_register_device;
+		
+	if(attach_aci_rds()<0)
+		goto attach_aci_rds;
+
+	printk(KERN_INFO "Miro PCM20 radio card driver.\n");
+
+	return 0;
+
+ attach_aci_rds:
+	video_unregister_device(&pcm20_radio);
+ video_register_device:
+	return -EINVAL;
+}
+
+MODULE_AUTHOR("Ruurd Reitsma");
+MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
+MODULE_LICENSE("GPL");
+
+static void __exit pcm20_cleanup(void)
+{
+	unload_aci_rds();
+	video_unregister_device(&pcm20_radio);
+}
+
+module_init(pcm20_init);
+module_exit(pcm20_cleanup);
diff --git a/drivers/media/radio/miropcm20-rds-core.c b/drivers/media/radio/miropcm20-rds-core.c
new file mode 100644
index 0000000..a917a90
--- /dev/null
+++ b/drivers/media/radio/miropcm20-rds-core.c
@@ -0,0 +1,210 @@
+/*
+ *  Many thanks to Fred Seidel <seidel@metabox.de>, the
+ *  designer of the RDS decoder hardware. With his help
+ *  I was able to code this driver.
+ *  Thanks also to Norberto Pellicci, Dominic Mounteney
+ *  <DMounteney@pinnaclesys.com> and www.teleauskunft.de
+ *  for good hints on finding Fred. It was somewhat hard
+ *  to locate him here in Germany... [:
+ *
+ * Revision history:
+ *
+ *   2000-08-09  Robert Siemer <Robert.Siemer@gmx.de>
+ *        RDS support for MiroSound PCM20 radio
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+#include <asm/io.h>
+#include "../../../sound/oss/aci.h"
+#include "miropcm20-rds-core.h"
+
+#define DEBUG 0
+
+static struct semaphore aci_rds_sem;
+
+#define RDS_DATASHIFT          2   /* Bit 2 */
+#define RDS_DATAMASK        (1 << RDS_DATASHIFT)
+#define RDS_BUSYMASK        0x10   /* Bit 4 */
+#define RDS_CLOCKMASK       0x08   /* Bit 3 */
+
+#define RDS_DATA(x)         (((x) >> RDS_DATASHIFT) & 1) 
+
+
+#if DEBUG
+static void print_matrix(char array[], unsigned int length)
+{
+        int i, j;
+
+        for (i=0; i<length; i++) {
+                printk(KERN_DEBUG "aci-rds: ");
+                for (j=7; j>=0; j--) {
+                        printk("%d", (array[i] >> j) & 0x1);
+                }
+                if (i%8 == 0)
+                        printk(" byte-border\n");
+                else
+                        printk("\n");
+        }
+}
+#endif /* DEBUG */
+
+static int byte2trans(unsigned char byte, unsigned char sendbuffer[], int size)
+{
+	int i;
+
+	if (size != 8)
+		return -1;
+	for (i = 7; i >= 0; i--)
+		sendbuffer[7-i] = (byte & (1 << i)) ? RDS_DATAMASK : 0;
+	sendbuffer[0] |= RDS_CLOCKMASK;
+
+	return 0;
+}
+
+static int rds_waitread(void)
+{
+	unsigned char byte;
+	int i=2000;
+
+	do {
+		byte=inb(RDS_REGISTER);
+		i--;
+	}
+	while ((byte & RDS_BUSYMASK) && i);
+
+	if (i) {
+		#if DEBUG
+		printk(KERN_DEBUG "rds_waitread()");
+		print_matrix(&byte, 1);
+		#endif
+		return (byte);
+	} else {
+		printk(KERN_WARNING "aci-rds: rds_waitread() timeout...\n");
+		return -1;
+	}
+}
+
+/* don't use any ..._nowait() function if you are not sure what you do... */
+
+static inline void rds_rawwrite_nowait(unsigned char byte)
+{
+	#if DEBUG
+	printk(KERN_DEBUG "rds_rawwrite()");
+	print_matrix(&byte, 1);
+	#endif
+	outb(byte, RDS_REGISTER);
+}
+
+static int rds_rawwrite(unsigned char byte)
+{
+	if (rds_waitread() >= 0) {
+		rds_rawwrite_nowait(byte);
+		return 0;
+	} else
+		return -1;
+}
+
+static int rds_write(unsigned char cmd)
+{
+	unsigned char sendbuffer[8];
+	int i;
+	
+	if (byte2trans(cmd, sendbuffer, 8) != 0){
+		return -1;
+	} else {
+		for (i=0; i<8; i++) {
+			rds_rawwrite(sendbuffer[i]);
+		}
+	}
+	return 0;
+}
+
+static int rds_readcycle_nowait(void)
+{
+	rds_rawwrite_nowait(0);
+	return rds_waitread();
+}
+
+static int rds_readcycle(void)
+{
+	if (rds_rawwrite(0) < 0)
+		return -1;
+	return rds_waitread();
+}
+
+static int rds_read(unsigned char databuffer[], int datasize)
+{
+	#define READSIZE (8*datasize)
+
+	int i,j;
+
+	if (datasize < 1)  /* nothing to read */
+		return 0;
+
+	/* to be able to use rds_readcycle_nowait()
+	   I have to waitread() here */
+	if (rds_waitread() < 0)
+		return -1;
+	
+	memset(databuffer, 0, datasize);
+
+	for (i=0; i< READSIZE; i++)
+		if((j=rds_readcycle_nowait()) < 0) {
+			return -1;
+		} else {
+			databuffer[i/8]|=(RDS_DATA(j) << (7-(i%8)));
+		}
+
+	return 0;
+}
+
+static int rds_ack(void)
+{
+	int i=rds_readcycle();
+
+	if (i < 0)
+		return -1;
+	if (i & RDS_DATAMASK) {
+		return 0;  /* ACK  */
+	} else {
+		printk(KERN_DEBUG "aci-rds: NACK\n");
+		return 1;  /* NACK */
+	}
+}
+
+int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize)
+{
+	int ret;
+
+	if (down_interruptible(&aci_rds_sem))
+		return -EINTR;
+
+	rds_write(cmd);
+
+	/* RDS_RESET doesn't need further processing */
+	if (cmd!=RDS_RESET && (rds_ack() || rds_read(databuffer, datasize)))
+		ret = -1;
+	else
+		ret = 0;
+
+	up(&aci_rds_sem);
+	
+	return ret;
+}
+EXPORT_SYMBOL(aci_rds_cmd);
+
+int __init attach_aci_rds(void)
+{
+	init_MUTEX(&aci_rds_sem);
+	return 0;
+}
+
+void __exit unload_aci_rds(void)
+{
+}
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/radio/miropcm20-rds-core.h b/drivers/media/radio/miropcm20-rds-core.h
new file mode 100644
index 0000000..aeb5761
--- /dev/null
+++ b/drivers/media/radio/miropcm20-rds-core.h
@@ -0,0 +1,19 @@
+#ifndef _MIROPCM20_RDS_CORE_H_
+#define _MIROPCM20_RDS_CORE_H_
+
+extern int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize);
+
+#define RDS_STATUS      0x01
+#define RDS_STATIONNAME 0x02
+#define RDS_TEXT        0x03
+#define RDS_ALTFREQ     0x04
+#define RDS_TIMEDATE    0x05
+#define RDS_PI_CODE     0x06
+#define RDS_PTYTATP     0x07
+#define RDS_RESET       0x08
+#define RDS_RXVALUE     0x09
+
+extern void __exit unload_aci_rds(void);
+extern int __init attach_aci_rds(void);
+
+#endif /* _MIROPCM20_RDS_CORE_H_ */
diff --git a/drivers/media/radio/miropcm20-rds.c b/drivers/media/radio/miropcm20-rds.c
new file mode 100644
index 0000000..df79d5e
--- /dev/null
+++ b/drivers/media/radio/miropcm20-rds.c
@@ -0,0 +1,133 @@
+/* MiroSOUND PCM20 radio rds interface driver
+ * (c) 2001 Robert Siemer <Robert.Siemer@gmx.de>
+ * Thanks to Fred Seidel. See miropcm20-rds-core.c for further information.
+ */
+
+/* Revision history:
+ *
+ *   2001-04-18  Robert Siemer <Robert.Siemer@gmx.de>
+ *        separate file for user interface driver
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include "miropcm20-rds-core.h"
+
+static char * text_buffer;
+static int rds_users = 0;
+
+
+static int rds_f_open(struct inode *in, struct file *fi)
+{
+	if (rds_users)
+		return -EBUSY;
+
+	rds_users++;
+	if ((text_buffer=kmalloc(66, GFP_KERNEL)) == 0) {
+		rds_users--;
+		printk(KERN_NOTICE "aci-rds: Out of memory by open()...\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int rds_f_release(struct inode *in, struct file *fi)
+{
+	kfree(text_buffer);
+
+	rds_users--;
+	return 0;
+}
+
+static void print_matrix(char *ch, char out[])
+{
+        int j;
+
+	for (j=7; j>=0; j--) {
+		 out[7-j] = ((*ch >> j) & 0x1) + '0';
+	}
+}
+
+static ssize_t rds_f_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)
+{
+//	i = sprintf(text_buffer, "length: %d, offset: %d\n", length, *offset);
+
+	char c;
+	char bits[8];
+
+	msleep(2000);
+	aci_rds_cmd(RDS_STATUS, &c, 1);
+	print_matrix(&c, bits);
+	if (copy_to_user(buffer, bits, 8))
+		return -EFAULT;
+
+/*	if ((c >> 3) & 1) {
+		aci_rds_cmd(RDS_STATIONNAME, text_buffer+1, 8);
+		text_buffer[0]  = ' ' ;
+		text_buffer[9]  = '\n';
+		return copy_to_user(buffer+8, text_buffer, 10) ? -EFAULT: 18;
+	}
+*/
+/*	if ((c >> 6) & 1) {
+		aci_rds_cmd(RDS_PTYTATP, &c, 1);
+		if ( c & 1)
+			sprintf(text_buffer, " M");
+		else
+			sprintf(text_buffer, " S");
+		if ((c >> 1) & 1)
+			sprintf(text_buffer+2, " TA");
+		else
+			sprintf(text_buffer+2, " --");
+		if ((c >> 7) & 1)
+			sprintf(text_buffer+5, " TP");
+		else
+			sprintf(text_buffer+5, " --");
+		sprintf(text_buffer+8, " %2d\n", (c >> 2) & 0x1f);
+		return copy_to_user(buffer+8, text_buffer, 12) ? -EFAULT: 20;
+	}
+*/
+
+	if ((c >> 4) & 1) {
+		aci_rds_cmd(RDS_TEXT, text_buffer, 65);
+		text_buffer[0]  = ' ' ;
+		text_buffer[65] = '\n';
+		return copy_to_user(buffer+8, text_buffer,66) ? -EFAULT : 66+8;
+	} else {
+		put_user('\n', buffer+8);
+		return 9;
+	}
+}
+
+static struct file_operations rds_fops = {
+	.owner		= THIS_MODULE,
+	.read		= rds_f_read,
+	.open		= rds_f_open,
+	.release	= rds_f_release
+};
+
+static struct miscdevice rds_miscdev = {
+	.minor		= MISC_DYNAMIC_MINOR,
+	.name		= "radiotext",
+	.devfs_name	= "v4l/rds/radiotext",
+	.fops		= &rds_fops,
+};
+
+static int __init miropcm20_rds_init(void)
+{
+	return misc_register(&rds_miscdev);
+}
+
+static void __exit miropcm20_rds_cleanup(void)
+{
+	misc_deregister(&rds_miscdev);
+}
+
+module_init(miropcm20_rds_init);
+module_exit(miropcm20_rds_cleanup);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
new file mode 100644
index 0000000..8b4ad70
--- /dev/null
+++ b/drivers/media/radio/radio-aimslab.c
@@ -0,0 +1,368 @@
+/* radiotrack (radioreveal) driver for Linux radio support
+ * (c) 1997 M. Kirkwood
+ * Converted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
+ *
+ * History:
+ * 1999-02-24	Russell Kroll <rkroll@exploits.org>
+ * 		Fine tuning/VIDEO_TUNER_LOW
+ *		Frequency range expanded to start at 87 MHz
+ *
+ * TODO: Allow for more than one of these foolish entities :-)
+ *
+ * Notes on the hardware (reverse engineered from other peoples'
+ * reverse engineering of AIMS' code :-)
+ *
+ *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ *
+ *  The signal strength query is unsurprisingly inaccurate.  And it seems
+ *  to indicate that (on my card, at least) the frequency setting isn't
+ *  too great.  (I have to tune up .025MHz from what the freq should be
+ *  to get a report that the thing is tuned.)
+ *
+ *  Volume control is (ugh) analogue:
+ *   out(port, start_increasing_volume);
+ *   wait(a_wee_while);
+ *   out(port, stop_changing_the_volume);
+ *  
+ */
+
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/ioport.h>	/* check_region, request_region	*/
+#include <linux/delay.h>	/* udelay			*/
+#include <asm/io.h>		/* outb, outb_p			*/
+#include <asm/uaccess.h>	/* copy to/from user		*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <linux/config.h>	/* CONFIG_RADIO_RTRACK_PORT 	*/
+#include <asm/semaphore.h>	/* Lock for the I/O 		*/
+
+#ifndef CONFIG_RADIO_RTRACK_PORT
+#define CONFIG_RADIO_RTRACK_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_RTRACK_PORT; 
+static int radio_nr = -1;
+static struct semaphore lock;
+
+struct rt_device
+{
+	int port;
+	int curvol;
+	unsigned long curfreq;
+	int muted;
+};
+
+
+/* local things */
+
+static void sleep_delay(long n)
+{
+	/* Sleep nicely for 'n' uS */
+	int d=n/(1000000/HZ);
+	if(!d)
+		udelay(n);
+	else
+		msleep(jiffies_to_msecs(d));
+}
+
+static void rt_decvol(void)
+{
+	outb(0x58, io);		/* volume down + sigstr + on	*/
+	sleep_delay(100000);
+	outb(0xd8, io);		/* volume steady + sigstr + on	*/
+}
+
+static void rt_incvol(void)
+{
+	outb(0x98, io);		/* volume up + sigstr + on	*/
+	sleep_delay(100000);
+	outb(0xd8, io);		/* volume steady + sigstr + on	*/
+}
+
+static void rt_mute(struct rt_device *dev)
+{
+	dev->muted = 1;
+	down(&lock);
+	outb(0xd0, io);			/* volume steady, off		*/
+	up(&lock);
+}
+
+static int rt_setvol(struct rt_device *dev, int vol)
+{
+	int i;
+
+	down(&lock);
+	
+	if(vol == dev->curvol) {	/* requested volume = current */
+		if (dev->muted) {	/* user is unmuting the card  */
+			dev->muted = 0;
+			outb (0xd8, io);	/* enable card */
+		}	
+		up(&lock);
+		return 0;
+	}
+
+	if(vol == 0) {			/* volume = 0 means mute the card */
+		outb(0x48, io);		/* volume down but still "on"	*/
+		sleep_delay(2000000);	/* make sure it's totally down	*/
+		outb(0xd0, io);		/* volume steady, off		*/
+		dev->curvol = 0;	/* track the volume state!	*/
+		up(&lock);
+		return 0;
+	}
+
+	dev->muted = 0;
+	if(vol > dev->curvol)
+		for(i = dev->curvol; i < vol; i++) 
+			rt_incvol();
+	else
+		for(i = dev->curvol; i > vol; i--) 
+			rt_decvol();
+
+	dev->curvol = vol;
+	up(&lock);
+	return 0;
+}
+
+/* the 128+64 on these outb's is to keep the volume stable while tuning 
+ * without them, the volume _will_ creep up with each frequency change
+ * and bit 4 (+16) is to keep the signal strength meter enabled
+ */
+
+static void send_0_byte(int port, struct rt_device *dev)
+{
+	if ((dev->curvol == 0) || (dev->muted)) {
+		outb_p(128+64+16+  1, port);   /* wr-enable + data low */
+		outb_p(128+64+16+2+1, port);   /* clock */
+	}
+	else {
+		outb_p(128+64+16+8+  1, port);  /* on + wr-enable + data low */
+		outb_p(128+64+16+8+2+1, port);  /* clock */
+	}
+	sleep_delay(1000); 
+}
+
+static void send_1_byte(int port, struct rt_device *dev)
+{
+	if ((dev->curvol == 0) || (dev->muted)) {
+		outb_p(128+64+16+4  +1, port);   /* wr-enable+data high */
+		outb_p(128+64+16+4+2+1, port);   /* clock */
+	} 
+	else {
+		outb_p(128+64+16+8+4  +1, port); /* on+wr-enable+data high */
+		outb_p(128+64+16+8+4+2+1, port); /* clock */
+	}
+
+	sleep_delay(1000); 
+}
+
+static int rt_setfreq(struct rt_device *dev, unsigned long freq)
+{
+	int i;
+
+	/* adapted from radio-aztech.c */
+
+	/* now uses VIDEO_TUNER_LOW for fine tuning */
+
+	freq += 171200;			/* Add 10.7 MHz IF 		*/
+	freq /= 800;			/* Convert to 50 kHz units	*/
+	
+	down(&lock);			/* Stop other ops interfering */
+	 
+	send_0_byte (io, dev);		/*  0: LSB of frequency		*/
+
+	for (i = 0; i < 13; i++)	/*   : frequency bits (1-13)	*/
+		if (freq & (1 << i))
+			send_1_byte (io, dev);
+		else
+			send_0_byte (io, dev);
+
+	send_0_byte (io, dev);		/* 14: test bit - always 0    */
+	send_0_byte (io, dev);		/* 15: test bit - always 0    */
+
+	send_0_byte (io, dev);		/* 16: band data 0 - always 0 */
+	send_0_byte (io, dev);		/* 17: band data 1 - always 0 */
+	send_0_byte (io, dev);		/* 18: band data 2 - always 0 */
+	send_0_byte (io, dev);		/* 19: time base - always 0   */
+
+	send_0_byte (io, dev);		/* 20: spacing (0 = 25 kHz)   */
+	send_1_byte (io, dev);		/* 21: spacing (1 = 25 kHz)   */
+	send_0_byte (io, dev);		/* 22: spacing (0 = 25 kHz)   */
+	send_1_byte (io, dev);		/* 23: AM/FM (FM = 1, always) */
+
+	if ((dev->curvol == 0) || (dev->muted))
+		outb (0xd0, io);	/* volume steady + sigstr */
+	else
+		outb (0xd8, io);	/* volume steady + sigstr + on */
+		
+	up(&lock);
+
+	return 0;
+}
+
+static int rt_getsigstr(struct rt_device *dev)
+{
+	if (inb(io) & 2)	/* bit set = no signal present	*/
+		return 0;
+	return 1;		/* signal present		*/
+}
+
+static int rt_do_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct rt_device *rt=dev->priv;
+	
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability *v = arg;
+			memset(v,0,sizeof(*v));
+			v->type=VID_TYPE_TUNER;
+			v->channels=1;
+			v->audios=1;
+			strcpy(v->name, "RadioTrack");
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner)	/* Only 1 tuner */ 
+				return -EINVAL;
+			v->rangelow=(87*16000);
+			v->rangehigh=(108*16000);
+			v->flags=VIDEO_TUNER_LOW;
+			v->mode=VIDEO_MODE_AUTO;
+			strcpy(v->name, "FM");
+			v->signal=0xFFFF*rt_getsigstr(rt);
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner!=0)
+				return -EINVAL;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+		case VIDIOCGFREQ:
+		{
+			unsigned long *freq = arg;
+			*freq = rt->curfreq;
+			return 0;
+		}
+		case VIDIOCSFREQ:
+		{
+			unsigned long *freq = arg;
+			rt->curfreq = *freq;
+			rt_setfreq(rt, rt->curfreq);
+			return 0;
+		}
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio *v = arg;
+			memset(v,0, sizeof(*v));
+			v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
+			v->volume=rt->curvol * 6554;
+			v->step=6554;
+			strcpy(v->name, "Radio");
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio *v = arg;
+			if(v->audio) 
+				return -EINVAL;
+			if(v->flags&VIDEO_AUDIO_MUTE) 
+				rt_mute(rt);
+			else
+				rt_setvol(rt,v->volume/6554);
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int rt_ioctl(struct inode *inode, struct file *file,
+		    unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, rt_do_ioctl);
+}
+
+static struct rt_device rtrack_unit;
+
+static struct file_operations rtrack_fops = {
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl	        = rt_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device rtrack_radio=
+{
+	.owner		= THIS_MODULE,
+	.name		= "RadioTrack radio",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_RTRACK,
+	.fops           = &rtrack_fops,
+};
+
+static int __init rtrack_init(void)
+{
+	if(io==-1)
+	{
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+
+	if (!request_region(io, 2, "rtrack")) 
+	{
+		printk(KERN_ERR "rtrack: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	rtrack_radio.priv=&rtrack_unit;
+	
+	if(video_register_device(&rtrack_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+	{
+		release_region(io, 2);
+		return -EINVAL;
+	}
+	printk(KERN_INFO "AIMSlab RadioTrack/RadioReveal card driver.\n");
+
+	/* Set up the I/O locking */
+	
+	init_MUTEX(&lock);
+	
+ 	/* mute card - prevents noisy bootups */
+
+	/* this ensures that the volume is all the way down  */
+	outb(0x48, io);		/* volume down but still "on"	*/
+	sleep_delay(2000000);	/* make sure it's totally down	*/
+	outb(0xc0, io);		/* steady volume, mute card	*/
+	rtrack_unit.curvol = 0;
+
+	return 0;
+}
+
+MODULE_AUTHOR("M.Kirkwood");
+MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
+module_param(radio_nr, int, 0);
+
+static void __exit cleanup_rtrack_module(void)
+{
+	video_unregister_device(&rtrack_radio);
+	release_region(io,2);
+}
+
+module_init(rtrack_init);
+module_exit(cleanup_rtrack_module);
+
diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c
new file mode 100644
index 0000000..013c835
--- /dev/null
+++ b/drivers/media/radio/radio-aztech.c
@@ -0,0 +1,315 @@
+/* radio-aztech.c - Aztech radio card driver for Linux 2.2 
+ *
+ * Adapted to support the Video for Linux API by 
+ * Russell Kroll <rkroll@exploits.org>.  Based on original tuner code by:
+ *
+ * Quay Ly
+ * Donald Song
+ * Jason Lewis      (jlewis@twilight.vtc.vsc.edu) 
+ * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
+ * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
+ *
+ * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
+ * along with more information on the card itself.
+ *
+ * History:
+ * 1999-02-24	Russell Kroll <rkroll@exploits.org>
+ *		Fine tuning/VIDEO_TUNER_LOW
+ * 		Range expanded to 87-108 MHz (from 87.9-107.8)
+ *
+ * Notable changes from the original source:
+ * - includes stripped down to the essentials
+ * - for loops used as delays replaced with udelay()
+ * - #defines removed, changed to static values
+ * - tuning structure changed - no more character arrays, other changes
+*/
+
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/ioport.h>	/* check_region, request_region	*/
+#include <linux/delay.h>	/* udelay			*/
+#include <asm/io.h>		/* outb, outb_p			*/
+#include <asm/uaccess.h>	/* copy to/from user		*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <linux/config.h>	/* CONFIG_RADIO_AZTECH_PORT 	*/
+
+/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
+
+#ifndef CONFIG_RADIO_AZTECH_PORT
+#define CONFIG_RADIO_AZTECH_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_AZTECH_PORT; 
+static int radio_nr = -1;
+static int radio_wait_time = 1000;
+static struct semaphore lock;
+
+struct az_device
+{
+	int curvol;
+	unsigned long curfreq;
+	int stereo;
+};
+
+static int volconvert(int level)
+{
+	level>>=14;	 	/* Map 16bits down to 2 bit */
+ 	level&=3;
+	
+	/* convert to card-friendly values */
+	switch (level) 
+	{
+		case 0: 
+			return 0;
+		case 1: 
+			return 1;
+		case 2:
+			return 4;
+		case 3:
+			return 5;
+	}
+	return 0;	/* Quieten gcc */
+}
+
+static void send_0_byte (struct az_device *dev)
+{
+	udelay(radio_wait_time);
+	outb_p(2+volconvert(dev->curvol), io);
+	outb_p(64+2+volconvert(dev->curvol), io);
+}
+
+static void send_1_byte (struct az_device *dev)
+{
+	udelay (radio_wait_time);
+	outb_p(128+2+volconvert(dev->curvol), io);
+	outb_p(128+64+2+volconvert(dev->curvol), io);
+}
+
+static int az_setvol(struct az_device *dev, int vol)
+{
+	down(&lock);
+	outb (volconvert(vol), io);
+	up(&lock);
+	return 0;
+}
+
+/* thanks to Michael Dwyer for giving me a dose of clues in
+ * the signal strength department..
+ *
+ * This card has a stereo bit - bit 0 set = mono, not set = stereo
+ * It also has a "signal" bit - bit 1 set = bad signal, not set = good
+ *
+ */
+
+static int az_getsigstr(struct az_device *dev)
+{
+	if (inb(io) & 2)	/* bit set = no signal present */
+		return 0;
+	return 1;		/* signal present */
+}
+
+static int az_getstereo(struct az_device *dev)
+{
+	if (inb(io) & 1) 	/* bit set = mono */
+		return 0;
+	return 1;		/* stereo */
+}
+
+static int az_setfreq(struct az_device *dev, unsigned long frequency)
+{
+	int  i;
+
+	frequency += 171200;		/* Add 10.7 MHz IF		*/
+	frequency /= 800;		/* Convert to 50 kHz units	*/
+					
+	down(&lock);
+	
+	send_0_byte (dev);		/*  0: LSB of frequency       */
+
+	for (i = 0; i < 13; i++)	/*   : frequency bits (1-13)  */
+		if (frequency & (1 << i))
+			send_1_byte (dev);
+		else
+			send_0_byte (dev);
+
+	send_0_byte (dev);		/* 14: test bit - always 0    */
+	send_0_byte (dev);		/* 15: test bit - always 0    */
+	send_0_byte (dev);		/* 16: band data 0 - always 0 */
+	if (dev->stereo)		/* 17: stereo (1 to enable)   */
+		send_1_byte (dev);
+	else
+		send_0_byte (dev);
+
+	send_1_byte (dev);		/* 18: band data 1 - unknown  */
+	send_0_byte (dev);		/* 19: time base - always 0   */
+	send_0_byte (dev);		/* 20: spacing (0 = 25 kHz)   */
+	send_1_byte (dev);		/* 21: spacing (1 = 25 kHz)   */
+	send_0_byte (dev);		/* 22: spacing (0 = 25 kHz)   */
+	send_1_byte (dev);		/* 23: AM/FM (FM = 1, always) */
+
+	/* latch frequency */
+
+	udelay (radio_wait_time);
+	outb_p(128+64+volconvert(dev->curvol), io);
+	
+	up(&lock);
+
+	return 0;
+}
+
+static int az_do_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct az_device *az = dev->priv;
+	
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability *v = arg;
+			memset(v,0,sizeof(*v));
+			v->type=VID_TYPE_TUNER;
+			v->channels=1;
+			v->audios=1;
+			strcpy(v->name, "Aztech Radio");
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner)	/* Only 1 tuner */ 
+				return -EINVAL;
+			v->rangelow=(87*16000);
+			v->rangehigh=(108*16000);
+			v->flags=VIDEO_TUNER_LOW;
+			v->mode=VIDEO_MODE_AUTO;
+			v->signal=0xFFFF*az_getsigstr(az);
+			if(az_getstereo(az))
+				v->flags|=VIDEO_TUNER_STEREO_ON;
+			strcpy(v->name, "FM");
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner!=0)
+				return -EINVAL;
+			return 0;
+		}
+		case VIDIOCGFREQ:
+		{
+			unsigned long *freq = arg;
+			*freq = az->curfreq;
+			return 0;
+		}
+		case VIDIOCSFREQ:
+		{
+			unsigned long *freq = arg;
+			az->curfreq = *freq;
+			az_setfreq(az, az->curfreq);
+			return 0;
+		}
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio *v = arg;
+			memset(v,0, sizeof(*v));
+			v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
+			if(az->stereo)
+				v->mode=VIDEO_SOUND_STEREO;
+			else
+				v->mode=VIDEO_SOUND_MONO;
+			v->volume=az->curvol;
+			v->step=16384;
+			strcpy(v->name, "Radio");
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio *v = arg;
+			if(v->audio) 
+				return -EINVAL;
+			az->curvol=v->volume;
+
+			az->stereo=(v->mode&VIDEO_SOUND_STEREO)?1:0;
+			if(v->flags&VIDEO_AUDIO_MUTE) 
+				az_setvol(az,0);
+			else
+				az_setvol(az,az->curvol);
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int az_ioctl(struct inode *inode, struct file *file,
+		    unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, az_do_ioctl);
+}
+
+static struct az_device aztech_unit;
+
+static struct file_operations aztech_fops = {
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl		= az_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device aztech_radio=
+{
+	.owner		= THIS_MODULE,
+	.name		= "Aztech radio",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_AZTECH,
+	.fops           = &aztech_fops,
+};
+
+static int __init aztech_init(void)
+{
+	if(io==-1)
+	{
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+
+	if (!request_region(io, 2, "aztech")) 
+	{
+		printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	init_MUTEX(&lock);
+	aztech_radio.priv=&aztech_unit;
+	
+	if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+	{
+		release_region(io,2);
+		return -EINVAL;
+	}
+		
+	printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
+	/* mute card - prevents noisy bootups */
+	outb (0, io);
+	return 0;
+}
+
+MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
+MODULE_DESCRIPTION("A driver for the Aztech radio card.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
+
+static void __exit aztech_cleanup(void)
+{
+	video_unregister_device(&aztech_radio);
+	release_region(io,2);
+}
+
+module_init(aztech_init);
+module_exit(aztech_cleanup);
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
new file mode 100644
index 0000000..53d399b
--- /dev/null
+++ b/drivers/media/radio/radio-cadet.c
@@ -0,0 +1,620 @@
+/* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card
+ *
+ * by Fred Gleason <fredg@wava.com>
+ * Version 0.3.3
+ *
+ * (Loosely) based on code for the Aztech radio card by
+ *
+ * Russell Kroll    (rkroll@exploits.org)
+ * Quay Ly
+ * Donald Song
+ * Jason Lewis      (jlewis@twilight.vtc.vsc.edu) 
+ * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
+ * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
+ *
+ * History:
+ * 2000-04-29	Russell Kroll <rkroll@exploits.org>
+ *		Added ISAPnP detection for Linux 2.3/2.4
+ *
+ * 2001-01-10	Russell Kroll <rkroll@exploits.org>
+ *		Removed dead CONFIG_RADIO_CADET_PORT code
+ *		PnP detection on load is now default (no args necessary)
+ *
+ * 2002-01-17	Adam Belay <ambx1@neo.rr.com>
+ *		Updated to latest pnp code
+ *
+ * 2003-01-31	Alan Cox <alan@redhat.com>
+ *		Cleaned up locking, delay code, general odds and ends
+ */
+
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/ioport.h>	/* check_region, request_region	*/
+#include <linux/delay.h>	/* udelay			*/
+#include <asm/io.h>		/* outb, outb_p			*/
+#include <asm/uaccess.h>	/* copy to/from user		*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <linux/param.h>
+#include <linux/pnp.h>
+
+#define RDS_BUFFER 256
+
+static int io=-1;		/* default to isapnp activation */
+static int radio_nr = -1;
+static int users=0;
+static int curtuner=0;
+static int tunestat=0;
+static int sigstrength=0;
+static wait_queue_head_t read_queue;
+static struct timer_list readtimer;
+static __u8 rdsin=0,rdsout=0,rdsstat=0;
+static unsigned char rdsbuf[RDS_BUFFER];
+static spinlock_t cadet_io_lock;
+
+static int cadet_probe(void);
+
+/*
+ * Signal Strength Threshold Values
+ * The V4L API spec does not define any particular unit for the signal 
+ * strength value.  These values are in microvolts of RF at the tuner's input.
+ */
+static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}};
+
+static int cadet_getrds(void)
+{
+        int rdsstat=0;
+
+	spin_lock(&cadet_io_lock);
+        outb(3,io);                 /* Select Decoder Control/Status */
+	outb(inb(io+1)&0x7f,io+1);  /* Reset RDS detection */
+	spin_unlock(&cadet_io_lock);
+	
+	msleep(100);
+
+	spin_lock(&cadet_io_lock);	
+        outb(3,io);                 /* Select Decoder Control/Status */
+	if((inb(io+1)&0x80)!=0) {
+	        rdsstat|=VIDEO_TUNER_RDS_ON;
+	}
+	if((inb(io+1)&0x10)!=0) {
+	        rdsstat|=VIDEO_TUNER_MBS_ON;
+	}
+	spin_unlock(&cadet_io_lock);
+	return rdsstat;
+}
+
+static int cadet_getstereo(void)
+{
+	int ret = 0;
+        if(curtuner != 0)	/* Only FM has stereo capability! */
+	        return 0;
+
+	spin_lock(&cadet_io_lock);
+        outb(7,io);          /* Select tuner control */
+	if( (inb(io+1) & 0x40) == 0)
+        	ret = 1;
+        spin_unlock(&cadet_io_lock);
+        return ret;
+}
+
+static unsigned cadet_gettune(void)
+{
+        int curvol,i;
+	unsigned fifo=0;
+
+        /*
+         * Prepare for read
+         */
+
+	spin_lock(&cadet_io_lock);
+	
+        outb(7,io);       /* Select tuner control */
+        curvol=inb(io+1); /* Save current volume/mute setting */
+        outb(0x00,io+1);  /* Ensure WRITE-ENABLE is LOW */
+	tunestat=0xffff;
+
+        /*
+         * Read the shift register
+         */
+        for(i=0;i<25;i++) {
+                fifo=(fifo<<1)|((inb(io+1)>>7)&0x01);
+                if(i<24) {
+                        outb(0x01,io+1);
+			tunestat&=inb(io+1);
+                        outb(0x00,io+1);
+                }
+        }
+
+        /*
+         * Restore volume/mute setting
+         */
+        outb(curvol,io+1);
+	spin_unlock(&cadet_io_lock);
+
+	return fifo;
+}
+
+static unsigned cadet_getfreq(void)
+{
+        int i;
+        unsigned freq=0,test,fifo=0;
+
+	/*
+	 * Read current tuning
+	 */
+	fifo=cadet_gettune();
+
+        /*
+         * Convert to actual frequency
+         */
+	if(curtuner==0) {    /* FM */
+	        test=12500;
+                for(i=0;i<14;i++) {
+                        if((fifo&0x01)!=0) {
+                                freq+=test;
+                        }
+                        test=test<<1;
+                        fifo=fifo>>1;
+                }
+                freq-=10700000;           /* IF frequency is 10.7 MHz */
+                freq=(freq*16)/1000000;   /* Make it 1/16 MHz */
+	}
+	if(curtuner==1) {    /* AM */
+	        freq=((fifo&0x7fff)-2010)*16;
+	}
+
+        return freq;
+}
+
+static void cadet_settune(unsigned fifo)
+{
+        int i;
+	unsigned test;  
+
+	spin_lock(&cadet_io_lock);
+	
+	outb(7,io);                /* Select tuner control */
+	/*
+	 * Write the shift register
+	 */
+	test=0;
+	test=(fifo>>23)&0x02;      /* Align data for SDO */
+	test|=0x1c;                /* SDM=1, SWE=1, SEN=1, SCK=0 */
+	outb(7,io);                /* Select tuner control */
+	outb(test,io+1);           /* Initialize for write */
+	for(i=0;i<25;i++) {
+   	        test|=0x01;              /* Toggle SCK High */
+		outb(test,io+1);
+		test&=0xfe;              /* Toggle SCK Low */
+		outb(test,io+1);
+		fifo=fifo<<1;            /* Prepare the next bit */
+		test=0x1c|((fifo>>23)&0x02);
+		outb(test,io+1);
+	}
+	spin_unlock(&cadet_io_lock);
+}
+
+static void cadet_setfreq(unsigned freq)
+{
+        unsigned fifo;
+        int i,j,test;
+        int curvol;
+
+        /* 
+         * Formulate a fifo command
+         */
+	fifo=0;
+	if(curtuner==0) {    /* FM */
+        	test=102400;
+                freq=(freq*1000)/16;       /* Make it kHz */
+                freq+=10700;               /* IF is 10700 kHz */
+                for(i=0;i<14;i++) {
+                        fifo=fifo<<1;
+                        if(freq>=test) {
+                                fifo|=0x01;
+                                freq-=test;
+                        }
+                        test=test>>1;
+                }
+	}
+	if(curtuner==1) {    /* AM */
+                fifo=(freq/16)+2010;            /* Make it kHz */
+		fifo|=0x100000;            /* Select AM Band */
+	}
+
+        /*
+         * Save current volume/mute setting
+         */
+
+	spin_lock(&cadet_io_lock);
+	outb(7,io);                /* Select tuner control */
+        curvol=inb(io+1); 
+        spin_unlock(&cadet_io_lock);
+
+	/*
+	 * Tune the card
+	 */
+	for(j=3;j>-1;j--) {
+	        cadet_settune(fifo|(j<<16));
+	        
+	        spin_lock(&cadet_io_lock);
+		outb(7,io);         /* Select tuner control */
+		outb(curvol,io+1);
+		spin_unlock(&cadet_io_lock);
+		
+		msleep(100);
+
+		cadet_gettune();
+		if((tunestat & 0x40) == 0) {   /* Tuned */
+		        sigstrength=sigtable[curtuner][j];
+			return;
+		}
+	}
+	sigstrength=0;
+}
+
+
+static int cadet_getvol(void)
+{
+	int ret = 0;
+	
+	spin_lock(&cadet_io_lock);
+	
+        outb(7,io);                /* Select tuner control */
+        if((inb(io + 1) & 0x20) != 0)
+        	ret = 0xffff;
+        
+        spin_unlock(&cadet_io_lock);
+        return ret;
+}
+
+
+static void cadet_setvol(int vol)
+{
+	spin_lock(&cadet_io_lock);
+        outb(7,io);                /* Select tuner control */
+        if(vol>0)
+                outb(0x20,io+1);
+        else
+                outb(0x00,io+1);
+	spin_unlock(&cadet_io_lock);
+}  
+
+static void cadet_handler(unsigned long data)
+{
+	/*
+	 * Service the RDS fifo
+	 */
+
+	if(spin_trylock(&cadet_io_lock))
+	{
+	        outb(0x3,io);       /* Select RDS Decoder Control */
+		if((inb(io+1)&0x20)!=0) {
+		        printk(KERN_CRIT "cadet: RDS fifo overflow\n");
+		}
+		outb(0x80,io);      /* Select RDS fifo */
+		while((inb(io)&0x80)!=0) {
+		        rdsbuf[rdsin]=inb(io+1);
+			if(rdsin==rdsout)
+			        printk(KERN_WARNING "cadet: RDS buffer overflow\n");
+			else
+				rdsin++;
+		}
+		spin_unlock(&cadet_io_lock);
+	}
+
+	/*
+	 * Service pending read
+	 */
+	if( rdsin!=rdsout)
+	        wake_up_interruptible(&read_queue);
+
+	/* 
+	 * Clean up and exit
+	 */
+	init_timer(&readtimer);
+	readtimer.function=cadet_handler;
+	readtimer.data=(unsigned long)0;
+	readtimer.expires=jiffies+(HZ/20);
+	add_timer(&readtimer);
+}
+
+
+
+static ssize_t cadet_read(struct file *file, char __user *data,
+			  size_t count, loff_t *ppos)
+{
+        int i=0;
+	unsigned char readbuf[RDS_BUFFER];
+
+        if(rdsstat==0) {
+		spin_lock(&cadet_io_lock);
+	        rdsstat=1;
+		outb(0x80,io);        /* Select RDS fifo */
+		spin_unlock(&cadet_io_lock);
+		init_timer(&readtimer);
+		readtimer.function=cadet_handler;
+		readtimer.data=(unsigned long)0;
+		readtimer.expires=jiffies+(HZ/20);
+		add_timer(&readtimer);
+	}
+	if(rdsin==rdsout) {
+  	        if (file->f_flags & O_NONBLOCK)
+		        return -EWOULDBLOCK;
+	        interruptible_sleep_on(&read_queue);
+	}		
+	while( i<count && rdsin!=rdsout)
+	        readbuf[i++]=rdsbuf[rdsout++];
+
+	if (copy_to_user(data,readbuf,i))
+	        return -EFAULT;
+	return i;
+}
+
+
+
+static int cadet_do_ioctl(struct inode *inode, struct file *file,
+			  unsigned int cmd, void *arg)
+{
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability *v = arg;
+			memset(v,0,sizeof(*v));
+			v->type=VID_TYPE_TUNER;
+			v->channels=2;
+			v->audios=1;
+			strcpy(v->name, "ADS Cadet");
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+			if((v->tuner<0)||(v->tuner>1)) {
+				return -EINVAL;
+			}
+			switch(v->tuner) {
+			        case 0:
+			        strcpy(v->name,"FM");
+			        v->rangelow=1400;     /* 87.5 MHz */
+			        v->rangehigh=1728;    /* 108.0 MHz */
+			        v->flags=0;
+			        v->mode=0;
+			        v->mode|=VIDEO_MODE_AUTO;
+			        v->signal=sigstrength;
+			        if(cadet_getstereo()==1) {
+				        v->flags|=VIDEO_TUNER_STEREO_ON;
+			        }
+				v->flags|=cadet_getrds();
+			        break;
+			        case 1:
+			        strcpy(v->name,"AM");
+			        v->rangelow=8320;      /* 520 kHz */
+			        v->rangehigh=26400;    /* 1650 kHz */
+			        v->flags=0;
+			        v->flags|=VIDEO_TUNER_LOW;
+			        v->mode=0;
+			        v->mode|=VIDEO_MODE_AUTO;
+			        v->signal=sigstrength;
+			        break;
+			}
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner *v = arg;
+			if((v->tuner<0)||(v->tuner>1)) {
+				return -EINVAL;
+			}
+			curtuner=v->tuner;	
+			return 0;
+		}
+		case VIDIOCGFREQ:
+		{
+		        unsigned long *freq = arg;
+			*freq = cadet_getfreq();
+			return 0;
+		}
+		case VIDIOCSFREQ:
+		{
+		        unsigned long *freq = arg;
+			if((curtuner==0)&&((*freq<1400)||(*freq>1728))) {
+			        return -EINVAL;
+			}
+			if((curtuner==1)&&((*freq<8320)||(*freq>26400))) {
+			        return -EINVAL;
+			}
+			cadet_setfreq(*freq);
+			return 0;
+		}
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio *v = arg;
+			memset(v,0, sizeof(*v));
+			v->flags=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
+			if(cadet_getstereo()==0) {
+			        v->mode=VIDEO_SOUND_MONO;
+			} else {
+				v->mode=VIDEO_SOUND_STEREO;
+			}
+			v->volume=cadet_getvol();
+			v->step=0xffff;
+			strcpy(v->name, "Radio");
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio *v = arg;
+			if(v->audio) 
+				return -EINVAL;
+			cadet_setvol(v->volume);
+			if(v->flags&VIDEO_AUDIO_MUTE) 
+				cadet_setvol(0);
+			else
+				cadet_setvol(0xffff);
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int cadet_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, cadet_do_ioctl);
+}
+
+static int cadet_open(struct inode *inode, struct file *file)
+{
+	if(users)
+		return -EBUSY;
+	users++;
+	init_waitqueue_head(&read_queue);
+	return 0;
+}
+
+static int cadet_release(struct inode *inode, struct file *file)
+{
+	del_timer_sync(&readtimer);
+	rdsstat=0;
+	users--;
+	return 0;
+}
+
+
+static struct file_operations cadet_fops = {
+	.owner		= THIS_MODULE,
+	.open		= cadet_open,
+	.release       	= cadet_release,
+	.read		= cadet_read,
+	.ioctl		= cadet_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device cadet_radio=
+{
+	.owner		= THIS_MODULE,
+	.name		= "Cadet radio",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_CADET,
+	.fops           = &cadet_fops,
+};
+
+static struct pnp_device_id cadet_pnp_devices[] = {
+	/* ADS Cadet AM/FM Radio Card */
+	{.id = "MSM0c24", .driver_data = 0},
+	{.id = ""}
+};
+
+MODULE_DEVICE_TABLE(pnp, cadet_pnp_devices);
+
+static int cadet_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id)
+{
+	if (!dev)
+		return -ENODEV;
+	/* only support one device */
+	if (io > 0)
+		return -EBUSY;
+
+	if (!pnp_port_valid(dev, 0)) {
+		return -ENODEV;
+	}
+
+	io = pnp_port_start(dev, 0);
+
+	printk ("radio-cadet: PnP reports device at %#x\n", io);
+
+	return io;
+}
+
+static struct pnp_driver cadet_pnp_driver = {
+	.name		= "radio-cadet",
+	.id_table	= cadet_pnp_devices,
+	.probe		= cadet_pnp_probe,
+	.remove		= NULL,
+};
+
+static int cadet_probe(void)
+{
+        static int iovals[8]={0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e};
+	int i;
+
+	for(i=0;i<8;i++) {
+	        io=iovals[i];
+	        if(request_region(io,2, "cadet-probe")>=0) {
+		        cadet_setfreq(1410);
+			if(cadet_getfreq()==1410) {
+				release_region(io, 2);
+			        return io;
+			}
+			release_region(io, 2);
+		}
+	}
+	return -1;
+}
+
+/* 
+ * io should only be set if the user has used something like
+ * isapnp (the userspace program) to initialize this card for us
+ */
+
+static int __init cadet_init(void)
+{
+	spin_lock_init(&cadet_io_lock);
+	
+	/*
+	 *	If a probe was requested then probe ISAPnP first (safest)
+	 */
+	if (io < 0)
+		pnp_register_driver(&cadet_pnp_driver);
+	/*
+	 *	If that fails then probe unsafely if probe is requested
+	 */
+	if(io < 0)
+		io = cadet_probe ();
+
+	/*
+	 *	Else we bail out
+	 */
+	 
+        if(io < 0) {
+#ifdef MODULE        
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+#endif
+	        goto fail;
+	}
+	if (!request_region(io,2,"cadet"))
+		goto fail;
+	if(video_register_device(&cadet_radio,VFL_TYPE_RADIO,radio_nr)==-1) {
+		release_region(io,2);
+		goto fail;
+	}
+	printk(KERN_INFO "ADS Cadet Radio Card at 0x%x\n",io);
+	return 0;
+fail:
+	pnp_unregister_driver(&cadet_pnp_driver);
+	return -1;
+}
+
+
+
+MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
+MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)");
+module_param(radio_nr, int, 0);
+
+static void __exit cadet_cleanup_module(void)
+{
+	video_unregister_device(&cadet_radio);
+	release_region(io,2);
+	pnp_unregister_driver(&cadet_pnp_driver);
+}
+
+module_init(cadet_init);
+module_exit(cadet_cleanup_module);
+
diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c
new file mode 100644
index 0000000..630cc78
--- /dev/null
+++ b/drivers/media/radio/radio-gemtek-pci.c
@@ -0,0 +1,416 @@
+/*
+ ***************************************************************************
+ *     
+ *     radio-gemtek-pci.c - Gemtek PCI Radio driver
+ *     (C) 2001 Vladimir Shebordaev <vshebordaev@mail.ru>
+ *
+ ***************************************************************************
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public
+ *     License along with this program; if not, write to the Free
+ *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ ***************************************************************************
+ *
+ *     Gemtek Corp still silently refuses to release any specifications
+ *     of their multimedia devices, so the protocol still has to be
+ *     reverse engineered.
+ *
+ *     The v4l code was inspired by Jonas Munsin's  Gemtek serial line
+ *     radio device driver.
+ *
+ *     Please, let me know if this piece of code was useful :)
+ * 
+ *     TODO: multiple device support and portability were not tested
+ *
+ ***************************************************************************
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/videodev.h>
+#include <linux/errno.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#ifndef PCI_VENDOR_ID_GEMTEK
+#define PCI_VENDOR_ID_GEMTEK 0x5046
+#endif
+
+#ifndef PCI_DEVICE_ID_GEMTEK_PR103
+#define PCI_DEVICE_ID_GEMTEK_PR103 0x1001
+#endif
+
+#ifndef GEMTEK_PCI_RANGE_LOW
+#define GEMTEK_PCI_RANGE_LOW (87*16000)
+#endif
+
+#ifndef GEMTEK_PCI_RANGE_HIGH
+#define GEMTEK_PCI_RANGE_HIGH (108*16000)
+#endif
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE 
+#define FALSE (0)
+#endif
+
+struct gemtek_pci_card {
+	struct video_device *videodev;
+	
+	u32 iobase;
+	u32 length;
+	u8  chiprev;
+	u16 model;
+	
+	u32 current_frequency;
+	u8  mute;
+};
+
+static const char rcsid[] = "$Id: radio-gemtek-pci.c,v 1.1 2001/07/23 08:08:16 ted Exp ted $";
+
+static int nr_radio = -1;
+
+static inline u8 gemtek_pci_out( u16 value, u32 port )
+{
+	outw( value, port );
+
+	return (u8)value;
+}
+
+#define _b0( v ) *((u8 *)&v)  
+static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep )
+{
+	register u8 byte = *last_byte;
+
+	if ( !value ) {
+		if ( !keep )
+			value = (u16)port;
+		byte &= 0xfd;	
+	} else
+		byte |= 2;
+
+	_b0( value ) = byte;
+	outw( value, port );
+	byte |= 1;
+	_b0( value ) = byte;
+	outw( value, port );
+	byte &= 0xfe;
+	_b0( value ) = byte;
+	outw( value, port );
+	
+	*last_byte = byte;
+}
+
+static inline void gemtek_pci_nil( u32 port, u8 *last_byte )
+{
+	__gemtek_pci_cmd( 0x00, port, last_byte, FALSE );
+}
+
+static inline void gemtek_pci_cmd( u16 cmd, u32 port, u8 *last_byte )
+{
+	__gemtek_pci_cmd( cmd, port, last_byte, TRUE );
+}
+
+static void gemtek_pci_setfrequency( struct gemtek_pci_card *card, unsigned long frequency )
+{
+	register int i;
+	register u32 value = frequency / 200 + 856;
+	register u16 mask = 0x8000;
+	u8 last_byte;
+	u32 port = card->iobase;
+
+	last_byte = gemtek_pci_out( 0x06, port );
+
+	i = 0;
+	do {
+		gemtek_pci_nil( port, &last_byte );
+		i++;
+	} while ( i < 9 );
+
+	i = 0;
+	do {
+		gemtek_pci_cmd( value & mask, port, &last_byte );
+		mask >>= 1;
+		i++;
+	} while ( i < 16 );
+
+	outw( 0x10, port );
+}
+
+
+static inline void gemtek_pci_mute( struct gemtek_pci_card *card )
+{
+	outb( 0x1f, card->iobase );
+	card->mute = TRUE;
+}
+
+static inline void gemtek_pci_unmute( struct gemtek_pci_card *card )
+{
+	if ( card->mute ) {
+		gemtek_pci_setfrequency( card, card->current_frequency );
+		card->mute = FALSE;
+	}
+}
+
+static inline unsigned int gemtek_pci_getsignal( struct gemtek_pci_card *card )
+{
+	return ( inb( card->iobase ) & 0x08 ) ? 0 : 1;
+}
+
+static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file,
+			       unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct gemtek_pci_card *card = dev->priv;
+
+	switch ( cmd ) {
+		case VIDIOCGCAP:
+		{
+			struct video_capability *c = arg;
+
+			memset(c,0,sizeof(*c));
+			c->type = VID_TYPE_TUNER;
+			c->channels = 1;
+			c->audios = 1;
+			strcpy( c->name, "Gemtek PCI Radio" );
+			return 0;
+		} 
+
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner *t = arg;
+
+			if ( t->tuner ) 
+				return -EINVAL;
+
+			t->rangelow = GEMTEK_PCI_RANGE_LOW;
+			t->rangehigh = GEMTEK_PCI_RANGE_HIGH;
+			t->flags = VIDEO_TUNER_LOW;
+			t->mode = VIDEO_MODE_AUTO;
+			t->signal = 0xFFFF * gemtek_pci_getsignal( card );
+			strcpy( t->name, "FM" );
+			return 0;
+		}
+
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner *t = arg;
+			if ( t->tuner )
+				return -EINVAL;
+			return 0;
+		}
+
+		case VIDIOCGFREQ:
+		{
+			unsigned long *freq = arg;
+			*freq = card->current_frequency;
+			return 0;
+		}
+		case VIDIOCSFREQ:
+		{
+			unsigned long *freq = arg;
+	 
+			if ( (*freq < GEMTEK_PCI_RANGE_LOW) ||
+			     (*freq > GEMTEK_PCI_RANGE_HIGH) )
+				return -EINVAL;
+
+			gemtek_pci_setfrequency( card, *freq );
+			card->current_frequency = *freq;
+			card->mute = FALSE;
+
+			return 0;
+		}
+  
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio *a = arg;
+
+			memset( a, 0, sizeof( *a ) );
+			a->flags |= VIDEO_AUDIO_MUTABLE;
+			a->volume = 1;
+			a->step = 65535;
+			strcpy( a->name, "Radio" );
+			return 0;			
+		}
+
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio *a = arg;
+
+			if ( a->audio ) 
+				return -EINVAL;
+
+			if ( a->flags & VIDEO_AUDIO_MUTE ) 
+				gemtek_pci_mute( card );
+			else
+				gemtek_pci_unmute( card );
+			return 0;
+		}
+
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int gemtek_pci_ioctl(struct inode *inode, struct file *file,
+			    unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, gemtek_pci_do_ioctl);
+}
+
+enum {
+	GEMTEK_PR103
+};
+
+static char *card_names[] __devinitdata = {
+	"GEMTEK_PR103"
+};
+
+static struct pci_device_id gemtek_pci_id[] =
+{
+	{ PCI_VENDOR_ID_GEMTEK, PCI_DEVICE_ID_GEMTEK_PR103,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, GEMTEK_PR103 },
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE( pci, gemtek_pci_id );
+
+static int mx = 1;
+
+static struct file_operations gemtek_pci_fops = {
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl		= gemtek_pci_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device vdev_template = {
+	.owner         = THIS_MODULE,
+	.name          = "Gemtek PCI Radio",
+	.type          = VID_TYPE_TUNER,
+	.hardware      = VID_HARDWARE_GEMTEK,
+	.fops          = &gemtek_pci_fops,
+};
+
+static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id )
+{
+	struct gemtek_pci_card *card;
+	struct video_device *devradio;
+
+	if ( (card = kmalloc( sizeof( struct gemtek_pci_card ), GFP_KERNEL )) == NULL ) {
+		printk( KERN_ERR "gemtek_pci: out of memory\n" );
+		return -ENOMEM;
+	}
+	memset( card, 0, sizeof( struct gemtek_pci_card ) );
+
+	if ( pci_enable_device( pci_dev ) ) 
+		goto err_pci;
+	
+	card->iobase = pci_resource_start( pci_dev, 0 );
+	card->length = pci_resource_len( pci_dev, 0 );
+
+	if ( request_region( card->iobase, card->length, card_names[pci_id->driver_data] ) == NULL ) {
+		printk( KERN_ERR "gemtek_pci: i/o port already in use\n" );
+		goto err_pci;
+	}
+
+	pci_read_config_byte( pci_dev, PCI_REVISION_ID, &card->chiprev );
+	pci_read_config_word( pci_dev, PCI_SUBSYSTEM_ID, &card->model );
+
+	pci_set_drvdata( pci_dev, card );
+ 
+	if ( (devradio = kmalloc( sizeof( struct video_device ), GFP_KERNEL )) == NULL ) {
+		printk( KERN_ERR "gemtek_pci: out of memory\n" );
+		goto err_video;
+	}
+	*devradio = vdev_template;
+
+	if ( video_register_device( devradio, VFL_TYPE_RADIO , nr_radio) == -1 ) {
+		kfree( devradio );
+		goto err_video;
+	}
+
+	card->videodev = devradio;
+	devradio->priv = card;
+	gemtek_pci_mute( card );
+
+	printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n", 
+		card->chiprev, card->iobase, card->iobase + card->length - 1 );
+
+	return 0;
+
+err_video:
+	release_region( card->iobase, card->length );
+
+err_pci:
+	kfree( card );
+	return -ENODEV;        
+}
+
+static void __devexit gemtek_pci_remove( struct pci_dev *pci_dev )
+{
+	struct gemtek_pci_card *card = pci_get_drvdata( pci_dev );
+
+	video_unregister_device( card->videodev );
+	kfree( card->videodev );
+
+	release_region( card->iobase, card->length );
+	
+	if ( mx )
+		gemtek_pci_mute( card );
+
+	kfree( card );
+	
+	pci_set_drvdata( pci_dev, NULL );
+}
+
+static struct pci_driver gemtek_pci_driver =
+{
+	.name		= "gemtek_pci",
+	.id_table	= gemtek_pci_id,
+	.probe		= gemtek_pci_probe,
+	.remove		= __devexit_p(gemtek_pci_remove),
+};
+
+static int __init gemtek_pci_init_module( void )
+{
+	return pci_module_init( &gemtek_pci_driver );
+}
+
+static void __exit gemtek_pci_cleanup_module( void )
+{
+	return pci_unregister_driver( &gemtek_pci_driver );
+}
+
+MODULE_AUTHOR( "Vladimir Shebordaev <vshebordaev@mail.ru>" );
+MODULE_DESCRIPTION( "The video4linux driver for the Gemtek PCI Radio Card" );
+MODULE_LICENSE("GPL");
+
+module_param(mx, bool, 0);
+MODULE_PARM_DESC( mx, "single digit: 1 - turn off the turner upon module exit (default), 0 - do not" );
+module_param(nr_radio, int, 0);
+MODULE_PARM_DESC( nr_radio, "video4linux device number to use");
+
+module_init( gemtek_pci_init_module );
+module_exit( gemtek_pci_cleanup_module );
+
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
new file mode 100644
index 0000000..202bfe6
--- /dev/null
+++ b/drivers/media/radio/radio-gemtek.c
@@ -0,0 +1,304 @@
+/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi>
+ *
+ * GemTek hasn't released any specs on the card, so the protocol had to
+ * be reverse engineered with dosemu.
+ *
+ * Besides the protocol changes, this is mostly a copy of:
+ *
+ *    RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
+ * 
+ *    Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
+ *    Converted to new API by Alan Cox <Alan.Cox@linux.org>
+ *    Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
+ *
+ * TODO: Allow for more than one of these foolish entities :-)
+ *
+ */
+
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/ioport.h>	/* check_region, request_region	*/
+#include <linux/delay.h>	/* udelay			*/
+#include <asm/io.h>		/* outb, outb_p			*/
+#include <asm/uaccess.h>	/* copy to/from user		*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <linux/config.h>	/* CONFIG_RADIO_GEMTEK_PORT 	*/
+#include <linux/spinlock.h>
+
+#ifndef CONFIG_RADIO_GEMTEK_PORT
+#define CONFIG_RADIO_GEMTEK_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_GEMTEK_PORT; 
+static int radio_nr = -1;
+static spinlock_t lock;
+
+struct gemtek_device
+{
+	int port;
+	unsigned long curfreq;
+	int muted;
+};
+
+
+/* local things */
+
+/* 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
+ */
+static void gemtek_mute(struct gemtek_device *dev)
+{
+        if(dev->muted)
+		return;
+	spin_lock(&lock);
+	outb(0x10, io);
+	spin_unlock(&lock);
+	dev->muted = 1;
+}
+
+static void gemtek_unmute(struct gemtek_device *dev)
+{
+	if(dev->muted == 0)
+		return;
+	spin_lock(&lock);
+	outb(0x20, io);
+	spin_unlock(&lock);
+	dev->muted = 0;
+}
+
+static void zero(void)
+{
+	outb_p(0x04, io);
+	udelay(5);
+	outb_p(0x05, io);
+	udelay(5);
+}
+
+static void one(void)
+{
+	outb_p(0x06, io);
+	udelay(5);
+	outb_p(0x07, io);
+	udelay(5);
+}
+
+static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq)
+{
+	int i;
+
+/*        freq = 78.25*((float)freq/16000.0 + 10.52); */
+
+	freq /= 16;
+	freq += 10520;
+	freq *= 7825;
+	freq /= 100000;
+
+	spin_lock(&lock);
+	
+	/* 2 start bits */
+	outb_p(0x03, io);
+	udelay(5);
+	outb_p(0x07, io);
+	udelay(5);
+
+        /* 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);
+
+	spin_unlock(&lock);
+	
+	return 0;
+}
+
+static int gemtek_getsigstr(struct gemtek_device *dev)
+{
+	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 */
+}
+
+static int gemtek_do_ioctl(struct inode *inode, struct file *file,
+			   unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct gemtek_device *rt=dev->priv;
+
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability *v = arg;
+			memset(v,0,sizeof(*v));
+			v->type=VID_TYPE_TUNER;
+			v->channels=1;
+			v->audios=1;
+			strcpy(v->name, "GemTek");
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner)	/* Only 1 tuner */ 
+				return -EINVAL;
+			v->rangelow=87*16000;
+			v->rangehigh=108*16000;
+			v->flags=VIDEO_TUNER_LOW;
+			v->mode=VIDEO_MODE_AUTO;
+			v->signal=0xFFFF*gemtek_getsigstr(rt);
+			strcpy(v->name, "FM");
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner!=0)
+				return -EINVAL;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+		case VIDIOCGFREQ:
+		{
+			unsigned long *freq = arg;
+			*freq = rt->curfreq;
+			return 0;
+		}
+		case VIDIOCSFREQ:
+		{
+			unsigned long *freq = arg;
+			rt->curfreq = *freq;
+			/* needs to be called twice in order for getsigstr to work */
+			gemtek_setfreq(rt, rt->curfreq);
+			gemtek_setfreq(rt, rt->curfreq);
+			return 0;
+		}
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio *v = arg;
+			memset(v,0, sizeof(*v));
+			v->flags|=VIDEO_AUDIO_MUTABLE;
+			v->volume=1;
+			v->step=65535;
+			strcpy(v->name, "Radio");
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio *v = arg;
+			if(v->audio) 
+				return -EINVAL;
+
+			if(v->flags&VIDEO_AUDIO_MUTE) 
+				gemtek_mute(rt);
+			else
+			        gemtek_unmute(rt);
+
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int gemtek_ioctl(struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl);
+}
+
+static struct gemtek_device gemtek_unit;
+
+static struct file_operations gemtek_fops = {
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl		= gemtek_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device gemtek_radio=
+{
+	.owner		= THIS_MODULE,
+	.name		= "GemTek radio",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_GEMTEK,
+	.fops           = &gemtek_fops,
+};
+
+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;
+	}
+
+	if (!request_region(io, 4, "gemtek")) 
+	{
+		printk(KERN_ERR "gemtek: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	gemtek_radio.priv=&gemtek_unit;
+	
+	if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+	{
+		release_region(io, 4);
+		return -EINVAL;
+	}
+	printk(KERN_INFO "GemTek Radio Card driver.\n");
+
+	spin_lock_init(&lock);
+
+	/* this is _maybe_ unnecessary */
+	outb(0x01, io);
+
+ 	/* mute card - prevents noisy bootups */
+	gemtek_unit.muted = 0;
+	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)
+{
+	video_unregister_device(&gemtek_radio);
+	release_region(io,4);
+}
+
+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:
+*/
diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c
new file mode 100644
index 0000000..e62147e
--- /dev/null
+++ b/drivers/media/radio/radio-maestro.c
@@ -0,0 +1,332 @@
+/* Maestro PCI sound card radio driver for Linux support
+ * (c) 2000 A. Tlalka, atlka@pg.gda.pl
+ * Notes on the hardware
+ *
+ *  + Frequency control is done digitally 
+ *  + No volume control - only mute/unmute - you have to use Aux line volume
+ *  control on Maestro card to set the volume
+ *  + Radio status (tuned/not_tuned and stereo/mono) is valid some time after
+ *  frequency setting (>100ms) and only when the radio is unmuted.
+ *  version 0.02
+ *  + io port is automatically detected - only the first radio is used
+ *  version 0.03
+ *  + thread access locking additions
+ *  version 0.04
+ * + code improvements
+ * + VIDEO_TUNER_LOW is permanent
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <linux/pci.h>
+#include <linux/videodev.h>
+
+#define DRIVER_VERSION	"0.04"
+
+#define PCI_VENDOR_ESS                  0x125D
+#define PCI_DEVICE_ID_ESS_ESS1968       0x1968          /* Maestro 2    */
+#define PCI_DEVICE_ID_ESS_ESS1978       0x1978          /* Maestro 2E   */
+
+#define GPIO_DATA       0x60   /* port offset from ESS_IO_BASE */
+
+#define IO_MASK		4      /* mask      register offset from GPIO_DATA
+				bits 1=unmask write to given bit */
+#define IO_DIR		8      /* direction register offset from GPIO_DATA
+				bits 0/1=read/write direction */
+
+#define GPIO6           0x0040 /* mask bits for GPIO lines */
+#define GPIO7           0x0080
+#define GPIO8           0x0100
+#define GPIO9           0x0200
+
+#define STR_DATA        GPIO6  /* radio TEA5757 pins and GPIO bits */
+#define STR_CLK         GPIO7
+#define STR_WREN        GPIO8
+#define STR_MOST        GPIO9
+
+#define FREQ_LO		 50*16000
+#define FREQ_HI		150*16000
+
+#define FREQ_IF         171200 /* 10.7*16000   */
+#define FREQ_STEP       200    /* 12.5*16      */
+
+#define FREQ2BITS(x)	((((unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\
+			/(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */
+
+#define BITS2FREQ(x)	((x) * FREQ_STEP - FREQ_IF)
+
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+
+static int radio_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg);
+
+static struct file_operations maestro_fops = {
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl		= radio_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device maestro_radio=
+{
+	.owner		= THIS_MODULE,
+	.name		= "Maestro radio",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_SF16MI,
+	.fops           = &maestro_fops,
+};
+
+static struct radio_device
+{
+	__u16	io,	/* base of Maestro card radio io (GPIO_DATA)*/
+		muted,	/* VIDEO_AUDIO_MUTE */
+		stereo,	/* VIDEO_TUNER_STEREO_ON */	
+		tuned;	/* signal strength (0 or 0xffff) */
+	struct  semaphore lock;
+} radio_unit = {0, 0, 0, 0, };
+
+static __u32 radio_bits_get(struct radio_device *dev)
+{
+	register __u16 io=dev->io, l, rdata;
+	register __u32 data=0;
+	__u16 omask;
+	omask = inw(io + IO_MASK);
+	outw(~(STR_CLK | STR_WREN), io + IO_MASK);
+	outw(0, io);
+	udelay(16);
+
+	for (l=24;l--;) {
+		outw(STR_CLK, io);		/* HI state */
+		udelay(2);
+		if(!l) 
+			dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff;
+		outw(0, io);			/* LO state */
+		udelay(2);
+		data <<= 1;			/* shift data */
+		rdata = inw(io);
+		if(!l)
+			dev->stereo =  rdata & STR_MOST ? 
+			0 : VIDEO_TUNER_STEREO_ON;
+		else
+			if(rdata & STR_DATA)
+				data++;
+		udelay(2);
+	}
+	if(dev->muted)
+		outw(STR_WREN, io);
+	udelay(4);
+	outw(omask, io + IO_MASK);
+	return data & 0x3ffe;
+}
+
+static void radio_bits_set(struct radio_device *dev, __u32 data)
+{
+	register __u16 io=dev->io, l, bits;
+	__u16 omask, odir;
+	omask = inw(io + IO_MASK);
+	odir  = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
+	outw(odir | STR_DATA, io + IO_DIR);
+	outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
+	udelay(16);
+	for (l=25;l;l--) {
+		bits = ((data >> 18) & STR_DATA) | STR_WREN ;
+		data <<= 1;			/* shift data */
+		outw(bits, io);			/* start strobe */
+		udelay(2);
+		outw(bits | STR_CLK, io);	/* HI level */
+		udelay(2);
+		outw(bits, io);			/* LO level */
+		udelay(4);
+	}
+	if(!dev->muted)
+		outw(0, io);
+	udelay(4);
+	outw(omask, io + IO_MASK);
+	outw(odir, io + IO_DIR);
+	msleep(125);
+}
+
+inline static int radio_function(struct inode *inode, struct file *file,
+				 unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct radio_device *card=dev->priv;
+	
+	switch(cmd) {
+		case VIDIOCGCAP: {
+			struct video_capability *v = arg;
+			memset(v,0,sizeof(*v));
+			strcpy(v->name, "Maestro radio");
+			v->type=VID_TYPE_TUNER;
+			v->channels=v->audios=1;
+			return 0;
+		}
+		case VIDIOCGTUNER: {
+			struct video_tuner *v = arg;
+			if(v->tuner)
+				return -EINVAL;
+			(void)radio_bits_get(card);
+			v->flags = VIDEO_TUNER_LOW | card->stereo;
+			v->signal = card->tuned;
+			strcpy(v->name, "FM");
+			v->rangelow = FREQ_LO;
+			v->rangehigh = FREQ_HI;
+			v->mode = VIDEO_MODE_AUTO;
+		        return 0;
+		}
+		case VIDIOCSTUNER: {
+			struct video_tuner *v = arg;
+			if(v->tuner!=0)
+				return -EINVAL;
+			return 0;
+		}
+		case VIDIOCGFREQ: {
+			unsigned long *freq = arg;
+			*freq = BITS2FREQ(radio_bits_get(card));
+			return 0;
+		}
+		case VIDIOCSFREQ: {
+			unsigned long *freq = arg;
+			if (*freq<FREQ_LO || *freq>FREQ_HI )
+				return -EINVAL;
+			radio_bits_set(card, FREQ2BITS(*freq));
+			return 0;
+		}
+		case VIDIOCGAUDIO: {	
+			struct video_audio *v = arg;
+			memset(v,0,sizeof(*v));
+			strcpy(v->name, "Radio");
+			v->flags=VIDEO_AUDIO_MUTABLE | card->muted;
+			v->mode=VIDEO_SOUND_STEREO;
+			return 0;		
+		}
+		case VIDIOCSAUDIO: {
+			struct video_audio *v = arg;
+			if(v->audio)
+				return -EINVAL;
+			{
+				register __u16 io=card->io;
+				register __u16 omask = inw(io + IO_MASK);
+				outw(~STR_WREN, io + IO_MASK);
+				outw((card->muted = v->flags & VIDEO_AUDIO_MUTE)
+				     ? STR_WREN : 0, io);
+				udelay(4);
+				outw(omask, io + IO_MASK);
+				msleep(125);
+				return 0;
+			}
+		}
+		case VIDIOCGUNIT: {
+			struct video_unit *v = arg;
+			v->video=VIDEO_NO_UNIT;
+			v->vbi=VIDEO_NO_UNIT;
+			v->radio=dev->minor;
+			v->audio=0;
+			v->teletext=VIDEO_NO_UNIT;
+			return 0;		
+		}
+		default: return -ENOIOCTLCMD;
+	}
+}
+
+static int radio_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct radio_device *card=dev->priv;
+	int ret;
+
+	down(&card->lock);
+	ret = video_usercopy(inode, file, cmd, arg, radio_function);
+	up(&card->lock);
+	return ret;
+}
+
+static __u16 radio_install(struct pci_dev *pcidev);
+
+MODULE_AUTHOR("Adam Tlalka, atlka@pg.gda.pl");
+MODULE_DESCRIPTION("Radio driver for the Maestro PCI sound card radio.");
+MODULE_LICENSE("GPL");
+
+static void __exit maestro_radio_exit(void)
+{
+	video_unregister_device(&maestro_radio);
+}
+
+static int __init maestro_radio_init(void)
+{
+	register __u16 found=0;
+	struct pci_dev *pcidev = NULL;
+	while(!found && (pcidev = pci_find_device(PCI_VENDOR_ESS, 
+						  PCI_DEVICE_ID_ESS_ESS1968,
+						  pcidev)))
+		found |= radio_install(pcidev);
+	while(!found && (pcidev = pci_find_device(PCI_VENDOR_ESS,
+						  PCI_DEVICE_ID_ESS_ESS1978, 
+						  pcidev)))
+		found |= radio_install(pcidev);
+	if(!found) {
+		printk(KERN_INFO "radio-maestro: no devices found.\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+module_init(maestro_radio_init);
+module_exit(maestro_radio_exit);
+
+inline static __u16 radio_power_on(struct radio_device *dev)
+{
+	register __u16 io=dev->io;
+	register __u32 ofreq;
+	__u16 omask, odir;
+	omask = inw(io + IO_MASK);
+	odir  = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
+	outw(odir & ~STR_WREN, io + IO_DIR);
+	dev->muted = inw(io) & STR_WREN ? 0 : VIDEO_AUDIO_MUTE;
+	outw(odir, io + IO_DIR);
+	outw(~(STR_WREN | STR_CLK), io + IO_MASK);
+	outw(dev->muted ? 0 : STR_WREN, io);
+	udelay(16);
+	outw(omask, io + IO_MASK);
+	ofreq = radio_bits_get(dev);
+	if((ofreq<FREQ2BITS(FREQ_LO)) || (ofreq>FREQ2BITS(FREQ_HI)))
+		ofreq = FREQ2BITS(FREQ_LO);
+	radio_bits_set(dev, ofreq);
+	return (ofreq == radio_bits_get(dev));
+}
+
+static __u16 radio_install(struct pci_dev *pcidev)
+{
+	if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO)
+		return 0;
+	
+	radio_unit.io = pcidev->resource[0].start + GPIO_DATA;
+	maestro_radio.priv = &radio_unit;
+	init_MUTEX(&radio_unit.lock);
+	
+	if(radio_power_on(&radio_unit)) {
+		if(video_register_device(&maestro_radio, VFL_TYPE_RADIO, radio_nr)==-1) {
+			printk("radio-maestro: can't register device!");
+			return 0;
+		}
+		printk(KERN_INFO "radio-maestro: version "
+		       DRIVER_VERSION 
+		       " time " 
+		       __TIME__ "  "
+		       __DATE__
+		       "\n");
+		printk(KERN_INFO "radio-maestro: radio chip initialized\n");
+		return 1;
+	} else
+		return 0;   
+}
+
diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c
new file mode 100644
index 0000000..5b748a4
--- /dev/null
+++ b/drivers/media/radio/radio-maxiradio.c
@@ -0,0 +1,349 @@
+/* 
+ * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux 
+ * (C) 2001 Dimitromanolakis Apostolos <apdim@grecian.net>
+ *
+ * Based in the radio Maestro PCI driver. Actually it uses the same chip
+ * for radio but different pci controller.
+ *
+ * I didn't have any specs I reversed engineered the protocol from
+ * the windows driver (radio.dll). 
+ *
+ * The card uses the TEA5757 chip that includes a search function but it
+ * is useless as I haven't found any way to read back the frequency. If 
+ * anybody does please mail me.
+ *
+ * For the pdf file see:
+ * http://www.semiconductors.philips.com/pip/TEA5757H/V1
+ *
+ *
+ * CHANGES:
+ *   0.75b
+ *     - better pci interface thanks to Francois Romieu <romieu@cogenit.fr>
+ *
+ *   0.75
+ *     - tiding up
+ *     - removed support for multiple devices as it didn't work anyway
+ *
+ * BUGS: 
+ *   - card unmutes if you change frequency
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <linux/pci.h>
+#include <linux/videodev.h>
+
+/* version 0.75      Sun Feb  4 22:51:27 EET 2001 */
+#define DRIVER_VERSION	"0.75"
+
+#ifndef PCI_VENDOR_ID_GUILLEMOT
+#define PCI_VENDOR_ID_GUILLEMOT 0x5046
+#endif
+
+#ifndef PCI_DEVICE_ID_GUILLEMOT
+#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
+#endif
+
+
+/* TEA5757 pin mappings */
+static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16 ;
+
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+
+
+#define FREQ_LO		 50*16000
+#define FREQ_HI		150*16000
+
+#define FREQ_IF         171200 /* 10.7*16000   */
+#define FREQ_STEP       200    /* 12.5*16      */
+
+#define FREQ2BITS(x)	((( (unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\
+			/(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */
+
+#define BITS2FREQ(x)	((x) * FREQ_STEP - FREQ_IF)
+
+
+static int radio_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg);
+
+static struct file_operations maxiradio_fops = {
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl	        = radio_ioctl,
+	.llseek         = no_llseek,
+};
+static struct video_device maxiradio_radio =
+{
+	.owner		= THIS_MODULE,
+	.name		= "Maxi Radio FM2000 radio",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_SF16MI,
+	.fops           = &maxiradio_fops,
+};
+
+static struct radio_device
+{
+	__u16	io,	/* base of radio io */
+		muted,	/* VIDEO_AUDIO_MUTE */
+		stereo,	/* VIDEO_TUNER_STEREO_ON */	
+		tuned;	/* signal strength (0 or 0xffff) */
+		
+	unsigned long freq;
+	
+	struct  semaphore lock;
+} radio_unit = {0, 0, 0, 0, };
+
+
+static void outbit(unsigned long bit, __u16 io)
+{
+	if(bit != 0)
+		{
+			outb(  power|wren|data     ,io); udelay(4);
+			outb(  power|wren|data|clk ,io); udelay(4);
+			outb(  power|wren|data     ,io); udelay(4);
+		}
+	else	
+		{
+			outb(  power|wren          ,io); udelay(4);
+			outb(  power|wren|clk      ,io); udelay(4);
+			outb(  power|wren          ,io); udelay(4);
+		}
+}
+
+static void turn_power(__u16 io, int p)
+{
+	if(p != 0) outb(power, io); else outb(0,io);
+}
+
+
+static void set_freq(__u16 io, __u32 data)
+{
+	unsigned long int si;
+	int bl;
+	
+	/* TEA5757 shift register bits (see pdf) */
+
+	outbit(0,io); // 24  search 
+	outbit(1,io); // 23  search up/down
+	
+	outbit(0,io); // 22  stereo/mono
+
+	outbit(0,io); // 21  band
+	outbit(0,io); // 20  band (only 00=FM works I think)
+
+	outbit(0,io); // 19  port ?
+	outbit(0,io); // 18  port ?
+	
+	outbit(0,io); // 17  search level
+	outbit(0,io); // 16  search level
+ 
+	si = 0x8000;
+	for(bl = 1; bl <= 16 ; bl++) { outbit(data & si,io); si >>=1; }
+	
+	outb(power,io);
+}
+
+static int get_stereo(__u16 io)
+{	
+	outb(power,io); udelay(4);
+	return !(inb(io) & mo_st);
+}
+
+static int get_tune(__u16 io)
+{	
+	outb(power+clk,io); udelay(4);
+	return !(inb(io) & mo_st);
+}
+
+
+inline static int radio_function(struct inode *inode, struct file *file,
+				 unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct radio_device *card=dev->priv;
+
+	switch(cmd) {
+		case VIDIOCGCAP: {
+			struct video_capability *v = arg;
+			
+			memset(v,0,sizeof(*v));
+			strcpy(v->name, "Maxi Radio FM2000 radio");
+			v->type=VID_TYPE_TUNER;
+			v->channels=v->audios=1;
+			return 0;
+		}
+		case VIDIOCGTUNER: {
+			struct video_tuner *v = arg;
+			
+			if(v->tuner)
+				return -EINVAL;
+				
+			card->stereo = 0xffff * get_stereo(card->io);
+			card->tuned = 0xffff * get_tune(card->io);
+			
+			v->flags = VIDEO_TUNER_LOW | card->stereo;
+			v->signal = card->tuned;
+			
+			strcpy(v->name, "FM");
+			
+			v->rangelow = FREQ_LO;
+			v->rangehigh = FREQ_HI;
+			v->mode = VIDEO_MODE_AUTO;
+			
+			return 0;
+		}
+		case VIDIOCSTUNER: {
+			struct video_tuner *v = arg;
+			if(v->tuner!=0)
+				return -EINVAL;
+			return 0;
+		}
+		case VIDIOCGFREQ: {
+			unsigned long *freq = arg;
+			
+			*freq = card->freq;
+			return 0;
+		}
+		case VIDIOCSFREQ: {
+			unsigned long *freq = arg;
+			
+			if (*freq < FREQ_LO || *freq > FREQ_HI)
+				return -EINVAL;
+			card->freq = *freq;
+			set_freq(card->io, FREQ2BITS(card->freq));
+			msleep(125);
+			return 0;
+		}
+		case VIDIOCGAUDIO: {	
+			struct video_audio *v = arg;
+			memset(v,0,sizeof(*v));
+			strcpy(v->name, "Radio");
+			v->flags=VIDEO_AUDIO_MUTABLE | card->muted;
+			v->mode=VIDEO_SOUND_STEREO;
+			return 0;		
+		}
+		
+		case VIDIOCSAUDIO: {
+			struct video_audio *v = arg;
+			
+			if(v->audio)
+				return -EINVAL;
+			card->muted = v->flags & VIDEO_AUDIO_MUTE;
+			if(card->muted)
+				turn_power(card->io, 0);
+			else
+				set_freq(card->io, FREQ2BITS(card->freq));
+			return 0;
+		}
+		case VIDIOCGUNIT: {
+			struct video_unit *v = arg;
+			
+			v->video=VIDEO_NO_UNIT;
+			v->vbi=VIDEO_NO_UNIT;
+			v->radio=dev->minor;
+			v->audio=0;
+			v->teletext=VIDEO_NO_UNIT;
+			return 0;		
+		}
+		default: return -ENOIOCTLCMD;
+	}
+}
+
+static int radio_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct radio_device *card=dev->priv;
+	int ret;
+	
+	down(&card->lock);
+	ret = video_usercopy(inode, file, cmd, arg, radio_function);
+	up(&card->lock);
+	return ret;
+}
+
+MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
+MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
+MODULE_LICENSE("GPL");
+
+
+static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	if(!request_region(pci_resource_start(pdev, 0),
+	                   pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) {
+	        printk(KERN_ERR "radio-maxiradio: can't reserve I/O ports\n");
+	        goto err_out;
+	}
+
+	if (pci_enable_device(pdev))
+	        goto err_out_free_region;
+
+	radio_unit.io = pci_resource_start(pdev, 0);
+	init_MUTEX(&radio_unit.lock);
+	maxiradio_radio.priv = &radio_unit;
+
+	if(video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr)==-1) {
+	        printk("radio-maxiradio: can't register device!");
+	        goto err_out_free_region;
+	}
+
+	printk(KERN_INFO "radio-maxiradio: version "
+	       DRIVER_VERSION
+	       " time "
+	       __TIME__ "  "
+	       __DATE__
+	       "\n");
+
+	printk(KERN_INFO "radio-maxiradio: found Guillemot MAXI Radio device (io = 0x%x)\n",
+	       radio_unit.io);
+	return 0;
+
+err_out_free_region:
+	release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+err_out:
+	return -ENODEV;
+}
+
+static void __devexit maxiradio_remove_one(struct pci_dev *pdev)
+{
+	video_unregister_device(&maxiradio_radio);
+	release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+}
+
+static struct pci_device_id maxiradio_pci_tbl[] = {
+	{ PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO,
+		PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0,}
+};
+
+MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl);
+
+static struct pci_driver maxiradio_driver = {
+	.name		= "radio-maxiradio",
+	.id_table	= maxiradio_pci_tbl,
+	.probe		= maxiradio_init_one,
+	.remove		= __devexit_p(maxiradio_remove_one),
+};
+
+static int __init maxiradio_radio_init(void)
+{
+	return pci_module_init(&maxiradio_driver);
+}
+
+static void __exit maxiradio_radio_exit(void)
+{
+	pci_unregister_driver(&maxiradio_driver);
+}
+
+module_init(maxiradio_radio_init);
+module_exit(maxiradio_radio_exit);
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
new file mode 100644
index 0000000..c00245d
--- /dev/null
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -0,0 +1,266 @@
+/* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
+ * 
+ * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
+ * Converted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
+ *
+ * TODO: Allow for more than one of these foolish entities :-)
+ *
+ */
+
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/ioport.h>	/* check_region, request_region	*/
+#include <linux/delay.h>	/* udelay			*/
+#include <asm/io.h>		/* outb, outb_p			*/
+#include <asm/uaccess.h>	/* copy to/from user		*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <linux/config.h>	/* CONFIG_RADIO_RTRACK2_PORT 	*/
+#include <linux/spinlock.h>
+
+#ifndef CONFIG_RADIO_RTRACK2_PORT
+#define CONFIG_RADIO_RTRACK2_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_RTRACK2_PORT; 
+static int radio_nr = -1;
+static spinlock_t lock;
+
+struct rt_device
+{
+	int port;
+	unsigned long curfreq;
+	int muted;
+};
+
+
+/* local things */
+
+static void rt_mute(struct rt_device *dev)
+{
+        if(dev->muted)
+		return;
+	spin_lock(&lock);
+	outb(1, io);
+	spin_unlock(&lock);
+	dev->muted = 1;
+}
+
+static void rt_unmute(struct rt_device *dev)
+{
+	if(dev->muted == 0)
+		return;
+	spin_lock(&lock);
+	outb(0, io);
+	spin_unlock(&lock);
+	dev->muted = 0;
+}
+
+static void zero(void)
+{
+        outb_p(1, io);
+	outb_p(3, io);
+	outb_p(1, io);
+}
+
+static void one(void)
+{
+        outb_p(5, io);
+	outb_p(7, io);
+	outb_p(5, io);
+}
+
+static int rt_setfreq(struct rt_device *dev, unsigned long freq)
+{
+	int i;
+
+	freq = freq / 200 + 856;
+	
+	spin_lock(&lock);
+
+	outb_p(0xc8, io);
+	outb_p(0xc9, io);
+	outb_p(0xc9, io);
+
+	for (i = 0; i < 10; i++)
+		zero ();
+
+	for (i = 14; i >= 0; i--)
+		if (freq & (1 << i))
+			one ();
+		else
+			zero ();
+
+	outb_p(0xc8, io);
+	if (!dev->muted)
+		outb_p(0, io);
+		
+	spin_unlock(&lock);
+	return 0;
+}
+
+static int rt_getsigstr(struct rt_device *dev)
+{
+	if (inb(io) & 2)	/* bit set = no signal present	*/
+		return 0;
+	return 1;		/* signal present		*/
+}
+
+static int rt_do_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct rt_device *rt=dev->priv;
+
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability *v = arg;
+			memset(v,0,sizeof(*v));
+			v->type=VID_TYPE_TUNER;
+			v->channels=1;
+			v->audios=1;
+			strcpy(v->name, "RadioTrack II");
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner)	/* Only 1 tuner */ 
+				return -EINVAL;
+			v->rangelow=88*16000;
+			v->rangehigh=108*16000;
+			v->flags=VIDEO_TUNER_LOW;
+			v->mode=VIDEO_MODE_AUTO;
+			v->signal=0xFFFF*rt_getsigstr(rt);
+			strcpy(v->name, "FM");
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner!=0)
+				return -EINVAL;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+		case VIDIOCGFREQ:
+		{
+			unsigned long *freq = arg;
+			*freq = rt->curfreq;
+			return 0;
+		}
+		case VIDIOCSFREQ:
+		{
+			unsigned long *freq = arg;
+			rt->curfreq = *freq;
+			rt_setfreq(rt, rt->curfreq);
+			return 0;
+		}
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio *v = arg;
+			memset(v,0, sizeof(*v));
+			v->flags|=VIDEO_AUDIO_MUTABLE;
+			v->volume=1;
+			v->step=65535;
+			strcpy(v->name, "Radio");
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio *v = arg;
+			if(v->audio) 
+				return -EINVAL;
+
+			if(v->flags&VIDEO_AUDIO_MUTE) 
+				rt_mute(rt);
+			else
+			        rt_unmute(rt);
+
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int rt_ioctl(struct inode *inode, struct file *file,
+		    unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, rt_do_ioctl);
+}
+
+static struct rt_device rtrack2_unit;
+
+static struct file_operations rtrack2_fops = {
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl		= rt_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device rtrack2_radio=
+{
+	.owner		= THIS_MODULE,
+	.name		= "RadioTrack II radio",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_RTRACK2,
+	.fops           = &rtrack2_fops,
+};
+
+static int __init rtrack2_init(void)
+{
+	if(io==-1)
+	{
+		printk(KERN_ERR "You must set an I/O address with io=0x20c or io=0x30c\n");
+		return -EINVAL;
+	}
+	if (!request_region(io, 4, "rtrack2")) 
+	{
+		printk(KERN_ERR "rtrack2: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	rtrack2_radio.priv=&rtrack2_unit;
+
+	spin_lock_init(&lock);	
+	if(video_register_device(&rtrack2_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+	{
+		release_region(io, 4);
+		return -EINVAL;
+	}
+		
+	printk(KERN_INFO "AIMSlab Radiotrack II card driver.\n");
+
+ 	/* mute card - prevents noisy bootups */
+	outb(1, io);
+	rtrack2_unit.muted = 1;
+
+	return 0;
+}
+
+MODULE_AUTHOR("Ben Pfaff");
+MODULE_DESCRIPTION("A driver for the RadioTrack II radio card.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)");
+module_param(radio_nr, int, 0);
+
+static void __exit rtrack2_cleanup_module(void)
+{
+	video_unregister_device(&rtrack2_radio);
+	release_region(io,4);
+}
+
+module_init(rtrack2_init);
+module_exit(rtrack2_cleanup_module);
+
+/*
+  Local variables:
+  compile-command: "mmake"
+  End:
+*/
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
new file mode 100644
index 0000000..3a464a0
--- /dev/null
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -0,0 +1,328 @@
+/* SF16FMI radio driver for Linux radio support
+ * heavily based on rtrack driver...
+ * (c) 1997 M. Kirkwood
+ * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
+ *
+ * Fitted to new interface by Alan Cox <alan.cox@linux.org>
+ * Made working and cleaned up functions <mikael.hedin@irf.se>
+ * Support for ISAPnP by Ladislav Michl <ladis@psi.cz>
+ *
+ * Notes on the hardware
+ *
+ *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ *  No volume control - only mute/unmute - you have to use line volume
+ *  control on SB-part of SF16FMI
+ *  
+ */
+
+#include <linux/kernel.h>	/* __setup			*/
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/ioport.h>	/* check_region, request_region	*/
+#include <linux/delay.h>	/* udelay			*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <linux/isapnp.h>
+#include <asm/io.h>		/* outb, outb_p			*/
+#include <asm/uaccess.h>	/* copy to/from user		*/
+#include <asm/semaphore.h>
+
+struct fmi_device
+{
+	int port;
+        int curvol; /* 1 or 0 */
+        unsigned long curfreq; /* freq in kHz */
+        __u32 flags;
+};
+
+static int io = -1; 
+static int radio_nr = -1;
+static struct pnp_dev *dev = NULL;
+static struct semaphore lock;
+
+/* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
+/* It is only useful to give freq in intervall of 800 (=0.05Mhz),
+ * other bits will be truncated, e.g 92.7400016 -> 92.7, but 
+ * 92.7400017 -> 92.75
+ */
+#define RSF16_ENCODE(x)	((x)/800+214)
+#define RSF16_MINFREQ 87*16000
+#define RSF16_MAXFREQ 108*16000
+
+static void outbits(int bits, unsigned int data, int port)
+{
+	while(bits--) {
+ 		if(data & 1) {
+			outb(5, port);
+			udelay(6);
+			outb(7, port);
+			udelay(6);
+		} else {
+			outb(1, port);
+			udelay(6);
+			outb(3, port);
+			udelay(6);
+		}
+		data>>=1;
+	}
+}
+
+static inline void fmi_mute(int port)
+{
+	down(&lock);
+	outb(0x00, port);
+	up(&lock);
+}
+
+static inline void fmi_unmute(int port)
+{
+	down(&lock);
+	outb(0x08, port);
+	up(&lock);
+}
+
+static inline int fmi_setfreq(struct fmi_device *dev)
+{
+	int myport = dev->port;
+	unsigned long freq = dev->curfreq;
+
+	down(&lock);
+
+	outbits(16, RSF16_ENCODE(freq), myport);
+	outbits(8, 0xC0, myport);
+	msleep(143);		/* was schedule_timeout(HZ/7) */
+	up(&lock);
+	if (dev->curvol) fmi_unmute(myport);
+	return 0;
+}
+
+static inline int fmi_getsigstr(struct fmi_device *dev)
+{
+	int val;
+	int res;
+	int myport = dev->port;
+
+	
+	down(&lock);
+	val = dev->curvol ? 0x08 : 0x00;	/* unmute/mute */
+	outb(val, myport);
+	outb(val | 0x10, myport);
+	msleep(143); 		/* was schedule_timeout(HZ/7) */
+	res = (int)inb(myport+1);
+	outb(val, myport);
+	
+	up(&lock);
+	return (res & 2) ? 0 : 0xFFFF;
+}
+
+static int fmi_do_ioctl(struct inode *inode, struct file *file,
+			unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct fmi_device *fmi=dev->priv;
+	
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability *v = arg;
+			memset(v,0,sizeof(*v));
+			strcpy(v->name, "SF16-FMx radio");
+			v->type=VID_TYPE_TUNER;
+			v->channels=1;
+			v->audios=1;
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+			int mult;
+
+			if(v->tuner)	/* Only 1 tuner */
+				return -EINVAL;
+			strcpy(v->name, "FM");
+			mult = (fmi->flags & VIDEO_TUNER_LOW) ? 1 : 1000;
+			v->rangelow = RSF16_MINFREQ/mult;
+			v->rangehigh = RSF16_MAXFREQ/mult;
+			v->flags=fmi->flags;
+			v->mode=VIDEO_MODE_AUTO;
+			v->signal = fmi_getsigstr(fmi);
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner!=0)
+				return -EINVAL;
+			fmi->flags = v->flags & VIDEO_TUNER_LOW;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+		case VIDIOCGFREQ:
+		{
+			unsigned long *freq = arg;
+			*freq = fmi->curfreq;
+			if (!(fmi->flags & VIDEO_TUNER_LOW))
+			    *freq /= 1000;
+			return 0;
+		}
+		case VIDIOCSFREQ:
+		{
+			unsigned long *freq = arg;
+			if (!(fmi->flags & VIDEO_TUNER_LOW))
+				*freq *= 1000;
+			if (*freq < RSF16_MINFREQ || *freq > RSF16_MAXFREQ )
+				return -EINVAL;
+			/*rounding in steps of 800 to match th freq
+			  that will be used */
+			fmi->curfreq = (*freq/800)*800; 
+			fmi_setfreq(fmi);
+			return 0;
+		}
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio *v = arg;
+			memset(v,0,sizeof(*v));
+			v->flags=( (!fmi->curvol)*VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE);
+			strcpy(v->name, "Radio");
+			v->mode=VIDEO_SOUND_STEREO;
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio *v = arg;
+			if(v->audio)
+				return -EINVAL;
+			fmi->curvol= v->flags&VIDEO_AUDIO_MUTE ? 0 : 1;
+			fmi->curvol ? 
+				fmi_unmute(fmi->port) : fmi_mute(fmi->port);
+			return 0;
+		}
+	        case VIDIOCGUNIT:
+		{
+               		struct video_unit *v = arg;
+			v->video=VIDEO_NO_UNIT;
+			v->vbi=VIDEO_NO_UNIT;
+			v->radio=dev->minor;
+			v->audio=0; /* How do we find out this??? */
+			v->teletext=VIDEO_NO_UNIT;
+			return 0;			
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int fmi_ioctl(struct inode *inode, struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, fmi_do_ioctl);
+}
+
+static struct fmi_device fmi_unit;
+
+static struct file_operations fmi_fops = {
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl		= fmi_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device fmi_radio=
+{
+	.owner		= THIS_MODULE,
+	.name		= "SF16FMx radio",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_SF16MI,
+	.fops           = &fmi_fops,
+};
+
+/* ladis: this is my card. does any other types exist? */
+static struct isapnp_device_id id_table[] __devinitdata = {
+	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0},
+	{	ISAPNP_CARD_END, },
+};
+
+MODULE_DEVICE_TABLE(isapnp, id_table);
+
+static int isapnp_fmi_probe(void)
+{
+	int i = 0;
+
+	while (id_table[i].card_vendor != 0 && dev == NULL) {
+		dev = pnp_find_dev(NULL, id_table[i].vendor,
+				   id_table[i].function, NULL);
+		i++;
+	}
+
+	if (!dev)
+		return -ENODEV;
+	if (pnp_device_attach(dev) < 0)
+		return -EAGAIN;
+	if (pnp_activate_dev(dev) < 0) {
+		printk ("radio-sf16fmi: PnP configure failed (out of resources?)\n");
+		pnp_device_detach(dev);
+		return -ENOMEM;
+	}
+	if (!pnp_port_valid(dev, 0)) {
+		pnp_device_detach(dev);
+		return -ENODEV;
+	}
+
+	i = pnp_port_start(dev, 0);
+	printk ("radio-sf16fmi: PnP reports card at %#x\n", i);
+
+	return i;
+}
+
+static int __init fmi_init(void)
+{
+	if (io < 0)
+		io = isapnp_fmi_probe();
+	if (io < 0) {
+		printk(KERN_ERR "radio-sf16fmi: No PnP card found.\n");
+		return io;
+	}
+	if (!request_region(io, 2, "radio-sf16fmi")) {
+		printk(KERN_ERR "radio-sf16fmi: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	fmi_unit.port = io;
+	fmi_unit.curvol = 0;
+	fmi_unit.curfreq = 0;
+	fmi_unit.flags = VIDEO_TUNER_LOW;
+	fmi_radio.priv = &fmi_unit;
+	
+	init_MUTEX(&lock);
+	
+	if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) == -1) {
+		release_region(io, 2);
+		return -EINVAL;
+	}
+		
+	printk(KERN_INFO "SF16FMx radio card driver at 0x%x\n", io);
+	/* mute card - prevents noisy bootups */
+	fmi_mute(io);
+	return 0;
+}
+
+MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
+MODULE_DESCRIPTION("A driver for the SF16MI radio.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)");
+module_param(radio_nr, int, 0);
+
+static void __exit fmi_cleanup_module(void)
+{
+	video_unregister_device(&fmi_radio);
+	release_region(io, 2);
+	if (dev)
+		pnp_device_detach(dev);
+}
+
+module_init(fmi_init);
+module_exit(fmi_cleanup_module);
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
new file mode 100644
index 0000000..0732efd
--- /dev/null
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -0,0 +1,434 @@
+/* SF16FMR2 radio driver for Linux radio support
+ * heavily based on fmi driver...
+ * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+ *
+ * Notes on the hardware
+ *
+ *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ *  No volume control - only mute/unmute - you have to use line volume
+ *
+ *  For read stereo/mono you must wait 0.1 sec after set frequency and
+ *  card unmuted so I set frequency on unmute
+ *  Signal handling seem to work only on autoscanning (not implemented)
+ */
+
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/ioport.h>	/* check_region, request_region	*/
+#include <linux/delay.h>	/* udelay			*/
+#include <asm/io.h>		/* outb, outb_p			*/
+#include <asm/uaccess.h>	/* copy to/from user		*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <asm/semaphore.h>
+
+static struct semaphore lock;
+
+#undef DEBUG
+//#define DEBUG 1
+
+#ifdef DEBUG
+# define  debug_print(s) printk s
+#else
+# define  debug_print(s)
+#endif
+
+/* this should be static vars for module size */
+struct fmr2_device
+{
+	int port;
+	int curvol; /* 0-65535, if not volume 0 or 65535 */
+	int mute;
+	int stereo; /* card is producing stereo audio */
+	unsigned long curfreq; /* freq in kHz */
+	int card_type;
+	__u32 flags;
+};
+
+static int io = 0x384;
+static int radio_nr = -1;
+
+/* hw precision is 12.5 kHz
+ * It is only usefull to give freq in intervall of 200 (=0.0125Mhz),
+ * other bits will be truncated
+ */
+#define RSF16_ENCODE(x)	((x)/200+856)
+#define RSF16_MINFREQ 87*16000
+#define RSF16_MAXFREQ 108*16000
+
+static inline void wait(int n,int port)
+{
+	for (;n;--n) inb(port);
+}
+
+static void outbits(int bits, unsigned int data, int nWait, int port)
+{
+	int bit;
+	for(;--bits>=0;) {
+		bit = (data>>bits) & 1;
+		outb(bit,port);
+		wait(nWait,port);
+		outb(bit|2,port);
+		wait(nWait,port);
+		outb(bit,port);
+		wait(nWait,port);
+	}
+}
+
+static inline void fmr2_mute(int port)
+{
+	outb(0x00, port);
+	wait(4,port);
+}
+
+static inline void fmr2_unmute(int port)
+{
+	outb(0x04, port);
+	wait(4,port);
+}
+
+static inline int fmr2_stereo_mode(int port)
+{
+	int n = inb(port);
+	outb(6,port);
+	inb(port);
+	n = ((n>>3)&1)^1;
+	debug_print((KERN_DEBUG "stereo: %d\n", n));
+	return n;
+}
+
+static int fmr2_product_info(struct fmr2_device *dev)
+{
+	int n = inb(dev->port);
+	n &= 0xC1;
+	if (n == 0)
+	{
+		/* this should support volume set */
+		dev->card_type = 12;
+		return 0;
+	}
+	/* not volume (mine is 11) */
+	dev->card_type = (n==128)?11:0;
+	return n;
+}
+
+static inline int fmr2_getsigstr(struct fmr2_device *dev)
+{
+	/* !!! work only if scanning freq */
+	int port = dev->port, res = 0xffff;
+	outb(5,port);
+	wait(4,port);
+	if (!(inb(port)&1)) res = 0;
+	debug_print((KERN_DEBUG "signal: %d\n", res));
+	return res;
+}
+
+/* set frequency and unmute card */
+static int fmr2_setfreq(struct fmr2_device *dev)
+{
+	int port = dev->port;
+	unsigned long freq = dev->curfreq;
+
+	fmr2_mute(port);
+
+	/* 0x42 for mono output
+	 * 0x102 forward scanning
+	 * 0x182 scansione avanti
+	 */
+	outbits(9,0x2,3,port);
+	outbits(16,RSF16_ENCODE(freq),2,port);
+
+	fmr2_unmute(port);
+
+	/* wait 0.11 sec */
+	msleep(110);
+
+	/* NOTE if mute this stop radio
+	   you must set freq on unmute */
+	dev->stereo = fmr2_stereo_mode(port);
+	return 0;
+}
+
+/* !!! not tested, in my card this does't work !!! */
+static int fmr2_setvolume(struct fmr2_device *dev)
+{
+	int i,a,n, port = dev->port;
+
+	if (dev->card_type != 11) return 1;
+
+	switch( (dev->curvol+(1<<11)) >> 12 )
+	{
+	case 0: case 1: n = 0x21; break;
+	case 2: n = 0x84; break;
+	case 3: n = 0x90; break;
+	case 4: n = 0x104; break;
+	case 5: n = 0x110; break;
+	case 6: n = 0x204; break;
+	case 7: n = 0x210; break;
+	case 8: n = 0x402; break;
+	case 9: n = 0x404; break;
+	default:
+	case 10: n = 0x408; break;
+	case 11: n = 0x410; break;
+	case 12: n = 0x801; break;
+	case 13: n = 0x802; break;
+	case 14: n = 0x804; break;
+	case 15: n = 0x808; break;
+	case 16: n = 0x810; break;
+	}
+	for(i=12;--i>=0;)
+	{
+		a = ((n >> i) & 1) << 6; /* if (a=0) a= 0; else a= 0x40; */
+		outb(a|4, port);
+		wait(4,port);
+		outb(a|0x24, port);
+		wait(4,port);
+		outb(a|4, port);
+		wait(4,port);
+	}
+	for(i=6;--i>=0;)
+	{
+		a = ((0x18 >> i) & 1) << 6;
+		outb(a|4, port);
+		wait(4,port);
+		outb(a|0x24, port);
+		wait(4,port);
+		outb(a|4, port);
+		wait(4,port);
+	}
+	wait(4,port);
+	outb(0x14, port);
+
+	return 0;
+}
+
+static int fmr2_do_ioctl(struct inode *inode, struct file *file,
+                      unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct fmr2_device *fmr2 = dev->priv;
+	debug_print((KERN_DEBUG "freq %ld flags %d vol %d mute %d "
+		"stereo %d type %d\n",
+		fmr2->curfreq, fmr2->flags, fmr2->curvol, fmr2->mute,
+		fmr2->stereo, fmr2->card_type));
+
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability *v = arg;
+			memset(v,0,sizeof(*v));
+			strcpy(v->name, "SF16-FMR2 radio");
+			v->type=VID_TYPE_TUNER;
+			v->channels=1;
+			v->audios=1;
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+			int mult;
+
+			if(v->tuner)     /* Only 1 tuner */
+				return -EINVAL;
+			strcpy(v->name, "FM");
+			mult = (fmr2->flags & VIDEO_TUNER_LOW) ? 1 : 1000;
+			v->rangelow = RSF16_MINFREQ/mult;
+			v->rangehigh = RSF16_MAXFREQ/mult;
+			v->flags = fmr2->flags | VIDEO_AUDIO_MUTABLE;
+			if (fmr2->mute)
+				v->flags |= VIDEO_AUDIO_MUTE;
+			v->mode=VIDEO_MODE_AUTO;
+			down(&lock);
+			v->signal = fmr2_getsigstr(fmr2);
+			up(&lock);
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner *v = arg;
+			if (v->tuner!=0)
+				return -EINVAL;
+			fmr2->flags = v->flags & VIDEO_TUNER_LOW;
+			return 0;
+		}
+		case VIDIOCGFREQ:
+		{
+			unsigned long *freq = arg;
+			*freq = fmr2->curfreq;
+			if (!(fmr2->flags & VIDEO_TUNER_LOW))
+				*freq /= 1000;
+			return 0;
+		}
+		case VIDIOCSFREQ:
+		{
+			unsigned long *freq = arg;
+			if (!(fmr2->flags & VIDEO_TUNER_LOW))
+				*freq *= 1000;
+			if ( *freq < RSF16_MINFREQ || *freq > RSF16_MAXFREQ )
+				return -EINVAL;
+			/* rounding in steps of 200 to match th freq
+			 * that will be used
+			 */
+			fmr2->curfreq = (*freq/200)*200;
+
+			/* set card freq (if not muted) */
+			if (fmr2->curvol && !fmr2->mute)
+			{
+				down(&lock);
+				fmr2_setfreq(fmr2);
+				up(&lock);
+			}
+			return 0;
+		}
+		case VIDIOCGAUDIO:
+		{
+			struct video_audio *v = arg;
+			memset(v,0,sizeof(*v));
+			/* !!! do not return VIDEO_AUDIO_MUTE */
+			v->flags = VIDEO_AUDIO_MUTABLE;
+			strcpy(v->name, "Radio");
+			/* get current stereo mode */
+			v->mode = fmr2->stereo ? VIDEO_SOUND_STEREO: VIDEO_SOUND_MONO;
+			/* volume supported ? */
+			if (fmr2->card_type == 11)
+			{
+				v->flags |= VIDEO_AUDIO_VOLUME;
+				v->step = 1 << 12;
+				v->volume = fmr2->curvol;
+			}
+			debug_print((KERN_DEBUG "Get flags %d vol %d\n", v->flags, v->volume));
+			return 0;
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio *v = arg;
+			if(v->audio)
+				return -EINVAL;
+			debug_print((KERN_DEBUG "Set flags %d vol %d\n", v->flags, v->volume));
+			/* set volume */
+			if (v->flags & VIDEO_AUDIO_VOLUME)
+				fmr2->curvol = v->volume; /* !!! set with precision */
+			if (fmr2->card_type != 11) fmr2->curvol = 65535;
+			fmr2->mute = 0;
+			if (v->flags & VIDEO_AUDIO_MUTE)
+				fmr2->mute = 1;
+#ifdef DEBUG
+			if (fmr2->curvol && !fmr2->mute)
+				printk(KERN_DEBUG "unmute\n");
+			else
+				printk(KERN_DEBUG "mute\n");
+#endif
+			down(&lock);
+			if (fmr2->curvol && !fmr2->mute)
+			{
+				fmr2_setvolume(fmr2);
+				fmr2_setfreq(fmr2);
+			}
+			else fmr2_mute(fmr2->port);
+			up(&lock);
+			return 0;
+		}
+		case VIDIOCGUNIT:
+		{
+			struct video_unit *v = arg;
+			v->video=VIDEO_NO_UNIT;
+			v->vbi=VIDEO_NO_UNIT;
+			v->radio=dev->minor;
+			v->audio=0; /* How do we find out this??? */
+			v->teletext=VIDEO_NO_UNIT;
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int fmr2_ioctl(struct inode *inode, struct file *file,
+                      unsigned int cmd, unsigned long arg)
+ {
+	return video_usercopy(inode, file, cmd, arg, fmr2_do_ioctl);
+}
+
+static struct fmr2_device fmr2_unit;
+
+static struct file_operations fmr2_fops = {
+	.owner          = THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl          = fmr2_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device fmr2_radio=
+{
+	.owner		= THIS_MODULE,
+	.name		= "SF16FMR2 radio",
+	. type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_SF16FMR2,
+	.fops		= &fmr2_fops,
+};
+
+static int __init fmr2_init(void)
+{
+	fmr2_unit.port = io;
+	fmr2_unit.curvol = 0;
+	fmr2_unit.mute = 0;
+	fmr2_unit.curfreq = 0;
+	fmr2_unit.stereo = 1;
+	fmr2_unit.flags = VIDEO_TUNER_LOW;
+	fmr2_unit.card_type = 0;
+	fmr2_radio.priv = &fmr2_unit;
+
+	init_MUTEX(&lock);
+
+	if (request_region(io, 2, "sf16fmr2"))
+	{
+		printk(KERN_ERR "fmr2: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	if(video_register_device(&fmr2_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+	{
+		release_region(io, 2);
+		return -EINVAL;
+	}
+
+	printk(KERN_INFO "SF16FMR2 radio card driver at 0x%x.\n", io);
+	debug_print((KERN_DEBUG "Mute %d Low %d\n",VIDEO_AUDIO_MUTE,VIDEO_TUNER_LOW));
+	/* mute card - prevents noisy bootups */
+	down(&lock);
+	fmr2_mute(io);
+	fmr2_product_info(&fmr2_unit);
+	up(&lock);
+	debug_print((KERN_DEBUG "card_type %d\n", fmr2_unit.card_type));
+	return 0;
+}
+
+MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
+MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
+module_param(radio_nr, int, 0);
+
+static void __exit fmr2_cleanup_module(void)
+{
+	video_unregister_device(&fmr2_radio);
+	release_region(io,2);
+}
+
+module_init(fmr2_init);
+module_exit(fmr2_cleanup_module);
+
+#ifndef MODULE
+
+static int __init fmr2_setup_io(char *str)
+{
+	get_option(&str, &io);
+	return 1;
+}
+
+__setup("sf16fmr2=", fmr2_setup_io);
+
+#endif
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
new file mode 100644
index 0000000..248d67f
--- /dev/null
+++ b/drivers/media/radio/radio-terratec.c
@@ -0,0 +1,341 @@
+/* Terratec ActiveRadio ISA Standalone card driver for Linux radio support
+ * (c) 1999 R. Offermanns (rolf@offermanns.de)
+ * based on the aimslab radio driver from M. Kirkwood
+ * many thanks to Michael Becker and Friedhelm Birth (from TerraTec)
+ * 
+ *
+ * History:
+ * 1999-05-21	First preview release
+ * 
+ *  Notes on the hardware:
+ *  There are two "main" chips on the card:
+ *  - Philips OM5610 (http://www-us.semiconductors.philips.com/acrobat/datasheets/OM5610_2.pdf)
+ *  - Philips SAA6588 (http://www-us.semiconductors.philips.com/acrobat/datasheets/SAA6588_1.pdf)
+ *  (you can get the datasheet at the above links)
+ *
+ *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ *  Volume Control is done digitally
+ *
+ *  there is a I2C controlled RDS decoder (SAA6588)  onboard, which i would like to support someday
+ *  (as soon i have understand how to get started :)
+ *  If you can help me out with that, please contact me!!
+ *
+ *  
+ */
+
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/ioport.h>	/* check_region, request_region	*/
+#include <linux/delay.h>	/* udelay			*/
+#include <asm/io.h>		/* outb, outb_p			*/
+#include <asm/uaccess.h>	/* copy to/from user		*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <linux/config.h>	/* CONFIG_RADIO_TERRATEC_PORT 	*/
+#include <linux/spinlock.h>
+
+#ifndef CONFIG_RADIO_TERRATEC_PORT
+#define CONFIG_RADIO_TERRATEC_PORT 0x590
+#endif
+
+/**************** this ones are for the terratec *******************/
+#define BASEPORT 	0x590
+#define VOLPORT 	0x591
+#define WRT_DIS 	0x00
+#define CLK_OFF		0x00
+#define IIC_DATA	0x01
+#define IIC_CLK		0x02
+#define DATA		0x04
+#define CLK_ON 		0x08
+#define WRT_EN		0x10
+/*******************************************************************/
+
+static int io = CONFIG_RADIO_TERRATEC_PORT; 
+static int radio_nr = -1;
+static spinlock_t lock;
+
+struct tt_device
+{
+	int port;
+	int curvol;
+	unsigned long curfreq;
+	int muted;
+};
+
+
+/* local things */
+
+static void cardWriteVol(int volume)
+{
+	int i;
+	volume = volume+(volume * 32); // change both channels
+	spin_lock(&lock);
+	for (i=0;i<8;i++)
+	{
+		if (volume & (0x80>>i))
+			outb(0x80, VOLPORT);
+		else outb(0x00, VOLPORT);
+	}
+	spin_unlock(&lock);
+}
+
+
+
+static void tt_mute(struct tt_device *dev)
+{
+	dev->muted = 1;
+	cardWriteVol(0);
+}
+
+static int tt_setvol(struct tt_device *dev, int vol)
+{
+	
+//	printk(KERN_ERR "setvol called, vol = %d\n", vol);
+
+	if(vol == dev->curvol) {	/* requested volume = current */
+		if (dev->muted) {	/* user is unmuting the card  */
+			dev->muted = 0;
+			cardWriteVol(vol);	/* enable card */
+		}	
+	
+		return 0;
+	}
+
+	if(vol == 0) {			/* volume = 0 means mute the card */
+		cardWriteVol(0);	/* "turn off card" by setting vol to 0 */
+		dev->curvol = vol;	/* track the volume state!	*/
+		return 0;
+	}
+
+	dev->muted = 0;
+	
+	cardWriteVol(vol);
+	 
+	dev->curvol = vol;
+
+	return 0;
+
+}
+
+
+/* this is the worst part in this driver */
+/* many more or less strange things are going on here, but hey, it works :) */
+
+static int tt_setfreq(struct tt_device *dev, unsigned long freq1)
+{	
+	int freq;
+	int i;
+	int p;
+	int  temp;
+	long rest;
+     
+	unsigned char buffer[25];		/* we have to bit shift 25 registers */
+	freq = freq1/160;			/* convert the freq. to a nice to handle value */
+	for(i=24;i>-1;i--)
+		buffer[i]=0;
+
+	rest = freq*10+10700;		/* i once had understood what is going on here */
+					/* maybe some wise guy (friedhelm?) can comment this stuff */
+	i=13;
+	p=10;
+	temp=102400;
+	while (rest!=0)
+	{
+		if (rest%temp  == rest)
+			buffer[i] = 0;
+		else 
+		{
+			buffer[i] = 1; 
+			rest = rest-temp;
+		}
+		i--;
+		p--;
+		temp = temp/2;
+       }
+
+	spin_lock(&lock);
+	
+	for (i=24;i>-1;i--)			/* bit shift the values to the radiocard */
+	{
+		if (buffer[i]==1) 
+		{
+			outb(WRT_EN|DATA, BASEPORT);
+			outb(WRT_EN|DATA|CLK_ON  , BASEPORT);
+			outb(WRT_EN|DATA, BASEPORT);
+		}
+		else
+		{
+			outb(WRT_EN|0x00, BASEPORT);
+			outb(WRT_EN|0x00|CLK_ON  , BASEPORT);
+		}
+	}
+	outb(0x00, BASEPORT);     
+	
+	spin_unlock(&lock);
+  
+  	return 0;
+}
+
+static int tt_getsigstr(struct tt_device *dev)		/* TODO */
+{
+	if (inb(io) & 2)	/* bit set = no signal present	*/
+		return 0;
+	return 1;		/* signal present		*/
+}
+
+
+/* implement the video4linux api */
+
+static int tt_do_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct tt_device *tt=dev->priv;
+	
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability *v = arg;
+			memset(v,0,sizeof(*v));
+			v->type=VID_TYPE_TUNER;
+			v->channels=1;
+			v->audios=1;
+			strcpy(v->name, "ActiveRadio");
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner)	/* Only 1 tuner */ 
+				return -EINVAL;
+			v->rangelow=(87*16000);
+			v->rangehigh=(108*16000);
+			v->flags=VIDEO_TUNER_LOW;
+			v->mode=VIDEO_MODE_AUTO;
+			strcpy(v->name, "FM");
+			v->signal=0xFFFF*tt_getsigstr(tt);
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner!=0)
+				return -EINVAL;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+		case VIDIOCGFREQ:
+		{
+			unsigned long *freq = arg;
+			*freq = tt->curfreq;
+			return 0;
+		}
+		case VIDIOCSFREQ:
+		{
+			unsigned long *freq = arg;
+			tt->curfreq = *freq;
+			tt_setfreq(tt, tt->curfreq);
+			return 0;
+		}
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio *v = arg;
+			memset(v,0, sizeof(*v));
+			v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
+			v->volume=tt->curvol * 6554;
+			v->step=6554;
+			strcpy(v->name, "Radio");
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio *v = arg;
+			if(v->audio) 
+				return -EINVAL;
+			if(v->flags&VIDEO_AUDIO_MUTE) 
+				tt_mute(tt);
+			else
+				tt_setvol(tt,v->volume/6554);
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int tt_ioctl(struct inode *inode, struct file *file,
+		    unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, tt_do_ioctl);
+}
+
+static struct tt_device terratec_unit;
+
+static struct file_operations terratec_fops = {
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl		= tt_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device terratec_radio=
+{
+	.owner		= THIS_MODULE,
+	.name		= "TerraTec ActiveRadio",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_TERRATEC,
+	.fops           = &terratec_fops,
+};
+
+static int __init terratec_init(void)
+{
+	if(io==-1)
+	{
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+	if (!request_region(io, 2, "terratec")) 
+	{
+		printk(KERN_ERR "TerraTec: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	terratec_radio.priv=&terratec_unit;
+	
+	spin_lock_init(&lock);
+	
+	if(video_register_device(&terratec_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+	{
+		release_region(io,2);
+		return -EINVAL;
+	}
+		
+	printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver.\n");
+
+ 	/* mute card - prevents noisy bootups */
+
+	/* this ensures that the volume is all the way down  */
+	cardWriteVol(0);
+	terratec_unit.curvol = 0;
+
+	return 0;
+}
+
+MODULE_AUTHOR("R.OFFERMANNS & others");
+MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
+MODULE_LICENSE("GPL");
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
+module_param(radio_nr, int, 0);
+
+static void __exit terratec_cleanup_module(void)
+{
+	video_unregister_device(&terratec_radio);
+	release_region(io,2);
+	printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver unloaded.\n");	
+}
+
+module_init(terratec_init);
+module_exit(terratec_cleanup_module);
+
diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c
new file mode 100644
index 0000000..b300bed
--- /dev/null
+++ b/drivers/media/radio/radio-trust.c
@@ -0,0 +1,320 @@
+/* radio-trust.c - Trust FM Radio card driver for Linux 2.2 
+ * by Eric Lammerts <eric@scintilla.utwente.nl>
+ *
+ * Based on radio-aztech.c. Original notes:
+ *
+ * Adapted to support the Video for Linux API by 
+ * Russell Kroll <rkroll@exploits.org>.  Based on original tuner code by:
+ *
+ * Quay Ly
+ * Donald Song
+ * Jason Lewis      (jlewis@twilight.vtc.vsc.edu) 
+ * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
+ * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
+ *
+ * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
+ */
+
+#include <stdarg.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/videodev.h>
+#include <linux/config.h>	/* CONFIG_RADIO_TRUST_PORT 	*/
+
+/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
+
+#ifndef CONFIG_RADIO_TRUST_PORT
+#define CONFIG_RADIO_TRUST_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_TRUST_PORT; 
+static int radio_nr = -1;
+static int ioval = 0xf;
+static __u16 curvol;
+static __u16 curbass;
+static __u16 curtreble;
+static unsigned long curfreq;
+static int curstereo;
+static int curmute;
+
+/* i2c addresses */
+#define TDA7318_ADDR 0x88
+#define TSA6060T_ADDR 0xc4
+
+#define TR_DELAY do { inb(io); inb(io); inb(io); } while(0)
+#define TR_SET_SCL outb(ioval |= 2, io)
+#define TR_CLR_SCL outb(ioval &= 0xfd, io)
+#define TR_SET_SDA outb(ioval |= 1, io)
+#define TR_CLR_SDA outb(ioval &= 0xfe, io)
+
+static void write_i2c(int n, ...)
+{
+	unsigned char val, mask;
+	va_list args;
+
+	va_start(args, n);
+
+	/* start condition */
+	TR_SET_SDA;
+	TR_SET_SCL;
+	TR_DELAY;
+	TR_CLR_SDA;
+	TR_CLR_SCL;
+	TR_DELAY;
+
+	for(; n; n--) {
+		val = va_arg(args, unsigned);
+		for(mask = 0x80; mask; mask >>= 1) {
+			if(val & mask)
+				TR_SET_SDA;
+			else
+				TR_CLR_SDA;
+			TR_SET_SCL;
+			TR_DELAY;
+			TR_CLR_SCL;
+			TR_DELAY;
+		}
+		/* acknowledge bit */
+		TR_SET_SDA;
+		TR_SET_SCL;
+		TR_DELAY;
+		TR_CLR_SCL;
+		TR_DELAY;
+	}
+
+	/* stop condition */
+	TR_CLR_SDA;
+	TR_DELAY;
+	TR_SET_SCL;
+	TR_DELAY;
+	TR_SET_SDA;
+	TR_DELAY;
+
+	va_end(args);
+}
+
+static void tr_setvol(__u16 vol)
+{
+	curvol = vol / 2048;
+	write_i2c(2, TDA7318_ADDR, curvol ^ 0x1f);
+}
+
+static int basstreble2chip[15] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8
+};
+
+static void tr_setbass(__u16 bass)
+{
+	curbass = bass / 4370;
+	write_i2c(2, TDA7318_ADDR, 0x60 | basstreble2chip[curbass]);
+}
+
+static void tr_settreble(__u16 treble)
+{
+	curtreble = treble / 4370;
+	write_i2c(2, TDA7318_ADDR, 0x70 | basstreble2chip[curtreble]);
+}
+
+static void tr_setstereo(int stereo)
+{
+	curstereo = !!stereo;
+	ioval = (ioval & 0xfb) | (!curstereo << 2);
+	outb(ioval, io);
+}
+
+static void tr_setmute(int mute)
+{
+	curmute = !!mute;
+	ioval = (ioval & 0xf7) | (curmute << 3);
+	outb(ioval, io);
+}
+
+static int tr_getsigstr(void)
+{
+	int i, v;
+	
+	for(i = 0, v = 0; i < 100; i++) v |= inb(io);
+	return (v & 1)? 0 : 0xffff;
+}
+
+static int tr_getstereo(void)
+{
+	/* don't know how to determine it, just return the setting */
+	return curstereo;
+}
+
+static void tr_setfreq(unsigned long f)
+{
+	f /= 160;	/* Convert to 10 kHz units	*/
+	f += 1070;	/* Add 10.7 MHz IF			*/
+
+	write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0);
+}
+
+static int tr_do_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, void *arg)
+{
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability *v = arg;
+
+			memset(v,0,sizeof(*v));
+			v->type=VID_TYPE_TUNER;
+			v->channels=1;
+			v->audios=1;
+			strcpy(v->name, "Trust FM Radio");
+
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+
+			if(v->tuner)	/* Only 1 tuner */ 
+				return -EINVAL;
+
+			v->rangelow = 87500 * 16;
+			v->rangehigh = 108000 * 16;
+			v->flags = VIDEO_TUNER_LOW;
+			v->mode = VIDEO_MODE_AUTO;
+
+			v->signal = tr_getsigstr();
+			if(tr_getstereo())
+				v->flags |= VIDEO_TUNER_STEREO_ON;
+
+			strcpy(v->name, "FM");
+
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner *v = arg;
+			if(v->tuner != 0)
+				return -EINVAL;
+			return 0;
+		}
+		case VIDIOCGFREQ:
+		{
+			unsigned long *freq = arg;
+			*freq = curfreq;
+			return 0;
+		}
+		case VIDIOCSFREQ:
+		{
+			unsigned long *freq = arg;
+			tr_setfreq(*freq);
+			return 0;
+		}
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio *v = arg;
+
+			memset(v,0, sizeof(*v));
+			v->flags = VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME |
+			          VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE;
+			v->mode = curstereo? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
+			v->volume = curvol * 2048;
+			v->step = 2048;
+			v->bass = curbass * 4370;
+			v->treble = curtreble * 4370;
+			
+			strcpy(v->name, "Trust FM Radio");
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio *v = arg;
+
+			if(v->audio) 
+				return -EINVAL;
+			tr_setvol(v->volume);					
+			tr_setbass(v->bass);
+			tr_settreble(v->treble);
+			tr_setstereo(v->mode & VIDEO_SOUND_STEREO);
+			tr_setmute(v->flags & VIDEO_AUDIO_MUTE);
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int tr_ioctl(struct inode *inode, struct file *file,
+		    unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, tr_do_ioctl);
+}
+
+static struct file_operations trust_fops = {
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl		= tr_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device trust_radio=
+{
+	.owner		= THIS_MODULE,
+	.name		= "Trust FM Radio",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_TRUST,
+	.fops           = &trust_fops,
+};
+
+static int __init trust_init(void)
+{
+	if(io == -1) {
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+	if(!request_region(io, 2, "Trust FM Radio")) {
+		printk(KERN_ERR "trust: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+	if(video_register_device(&trust_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+	{
+		release_region(io, 2);
+		return -EINVAL;
+	}
+
+	printk(KERN_INFO "Trust FM Radio card driver v1.0.\n");
+
+	write_i2c(2, TDA7318_ADDR, 0x80);	/* speaker att. LF = 0 dB */
+	write_i2c(2, TDA7318_ADDR, 0xa0);	/* speaker att. RF = 0 dB */
+	write_i2c(2, TDA7318_ADDR, 0xc0);	/* speaker att. LR = 0 dB */
+	write_i2c(2, TDA7318_ADDR, 0xe0);	/* speaker att. RR = 0 dB */
+	write_i2c(2, TDA7318_ADDR, 0x40);	/* stereo 1 input, gain = 18.75 dB */
+
+	tr_setvol(0x8000);					
+	tr_setbass(0x8000);
+	tr_settreble(0x8000);
+	tr_setstereo(1);
+
+	/* mute card - prevents noisy bootups */
+	tr_setmute(1);
+
+	return 0;
+}
+
+MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
+MODULE_DESCRIPTION("A driver for the Trust FM Radio card.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)");
+module_param(radio_nr, int, 0);
+
+static void __exit cleanup_trust_module(void)
+{
+	video_unregister_device(&trust_radio);
+	release_region(io, 2);
+}
+
+module_init(trust_init);
+module_exit(cleanup_trust_module);
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
new file mode 100644
index 0000000..d7da901
--- /dev/null
+++ b/drivers/media/radio/radio-typhoon.c
@@ -0,0 +1,383 @@
+/* Typhoon Radio Card driver for radio support
+ * (c) 1999 Dr. Henrik Seidel <Henrik.Seidel@gmx.de>
+ *
+ * Card manufacturer:
+ * http://194.18.155.92/idc/prod2.idc?nr=50753&lang=e
+ *
+ * Notes on the hardware
+ *
+ * This card has two output sockets, one for speakers and one for line.
+ * The speaker output has volume control, but only in four discrete
+ * steps. The line output has neither volume control nor mute.
+ *
+ * The card has auto-stereo according to its manual, although it all
+ * sounds mono to me (even with the Win/DOS drivers). Maybe it's my
+ * antenna - I really don't know for sure.
+ *
+ * Frequency control is done digitally.
+ *
+ * Volume control is done digitally, but there are only four different
+ * possible values. So you should better always turn the volume up and
+ * use line control. I got the best results by connecting line output
+ * to the sound card microphone input. For such a configuration the
+ * volume control has no effect, since volume control only influences
+ * the speaker output.
+ *
+ * There is no explicit mute/unmute. So I set the radio frequency to a
+ * value where I do expect just noise and turn the speaker volume down.
+ * The frequency change is necessary since the card never seems to be
+ * completely silent.
+ */
+
+#include <linux/module.h>	/* Modules                        */
+#include <linux/init.h>		/* Initdata                       */
+#include <linux/ioport.h>	/* check_region, request_region   */
+#include <linux/proc_fs.h>	/* radio card status report	  */
+#include <asm/io.h>		/* outb, outb_p                   */
+#include <asm/uaccess.h>	/* copy to/from user              */
+#include <linux/videodev.h>	/* kernel radio structs           */
+#include <linux/config.h>	/* CONFIG_RADIO_TYPHOON_*         */
+
+#define BANNER "Typhoon Radio Card driver v0.1\n"
+
+#ifndef CONFIG_RADIO_TYPHOON_PORT
+#define CONFIG_RADIO_TYPHOON_PORT -1
+#endif
+
+#ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
+#define CONFIG_RADIO_TYPHOON_MUTEFREQ 0
+#endif
+
+#ifndef CONFIG_PROC_FS
+#undef CONFIG_RADIO_TYPHOON_PROC_FS
+#endif
+
+struct typhoon_device {
+	int users;
+	int iobase;
+	int curvol;
+	int muted;
+	unsigned long curfreq;
+	unsigned long mutefreq;
+	struct semaphore lock;
+};
+
+static void typhoon_setvol_generic(struct typhoon_device *dev, int vol);
+static int typhoon_setfreq_generic(struct typhoon_device *dev,
+				   unsigned long frequency);
+static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency);
+static void typhoon_mute(struct typhoon_device *dev);
+static void typhoon_unmute(struct typhoon_device *dev);
+static int typhoon_setvol(struct typhoon_device *dev, int vol);
+static int typhoon_ioctl(struct inode *inode, struct file *file,
+			 unsigned int cmd, unsigned long arg);
+#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
+static int typhoon_get_info(char *buf, char **start, off_t offset, int len);
+#endif
+
+static void typhoon_setvol_generic(struct typhoon_device *dev, int vol)
+{
+	down(&dev->lock);
+	vol >>= 14;				/* Map 16 bit to 2 bit */
+	vol &= 3;
+	outb_p(vol / 2, dev->iobase);		/* Set the volume, high bit. */
+	outb_p(vol % 2, dev->iobase + 2);	/* Set the volume, low bit. */
+	up(&dev->lock);
+}
+
+static int typhoon_setfreq_generic(struct typhoon_device *dev,
+				   unsigned long frequency)
+{
+	unsigned long outval;
+	unsigned long x;
+
+	/*
+	 * The frequency transfer curve is not linear. The best fit I could
+	 * get is
+	 *
+	 * outval = -155 + exp((f + 15.55) * 0.057))
+	 *
+	 * where frequency f is in MHz. Since we don't have exp in the kernel,
+	 * I approximate this function by a third order polynomial.
+	 *
+	 */
+
+	down(&dev->lock);
+	x = frequency / 160;
+	outval = (x * x + 2500) / 5000;
+	outval = (outval * x + 5000) / 10000;
+	outval -= (10 * x * x + 10433) / 20866;
+	outval += 4 * x - 11505;
+
+	outb_p((outval >> 8) & 0x01, dev->iobase + 4);
+	outb_p(outval >> 9, dev->iobase + 6);
+	outb_p(outval & 0xff, dev->iobase + 8);
+	up(&dev->lock);
+
+	return 0;
+}
+
+static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency)
+{
+	typhoon_setfreq_generic(dev, frequency);
+	dev->curfreq = frequency;
+	return 0;
+}
+
+static void typhoon_mute(struct typhoon_device *dev)
+{
+	if (dev->muted == 1)
+		return;
+	typhoon_setvol_generic(dev, 0);
+	typhoon_setfreq_generic(dev, dev->mutefreq);
+	dev->muted = 1;
+}
+
+static void typhoon_unmute(struct typhoon_device *dev)
+{
+	if (dev->muted == 0)
+		return;
+	typhoon_setfreq_generic(dev, dev->curfreq);
+	typhoon_setvol_generic(dev, dev->curvol);
+	dev->muted = 0;
+}
+
+static int typhoon_setvol(struct typhoon_device *dev, int vol)
+{
+	if (dev->muted && vol != 0) {	/* user is unmuting the card */
+		dev->curvol = vol;
+		typhoon_unmute(dev);
+		return 0;
+	}
+	if (vol == dev->curvol)		/* requested volume == current */
+		return 0;
+
+	if (vol == 0) {			/* volume == 0 means mute the card */
+		typhoon_mute(dev);
+		dev->curvol = vol;
+		return 0;
+	}
+	typhoon_setvol_generic(dev, vol);
+	dev->curvol = vol;
+	return 0;
+}
+
+
+static int typhoon_do_ioctl(struct inode *inode, struct file *file,
+			    unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct typhoon_device *typhoon = dev->priv;
+
+	switch (cmd) {
+	case VIDIOCGCAP:
+		{
+			struct video_capability *v = arg;
+			memset(v,0,sizeof(*v));
+			v->type = VID_TYPE_TUNER;
+			v->channels = 1;
+			v->audios = 1;
+			strcpy(v->name, "Typhoon Radio");
+			return 0;
+		}
+	case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+			if (v->tuner)	/* Only 1 tuner */
+				return -EINVAL;
+			v->rangelow = 875 * 1600;
+			v->rangehigh = 1080 * 1600;
+			v->flags = VIDEO_TUNER_LOW;
+			v->mode = VIDEO_MODE_AUTO;
+			v->signal = 0xFFFF;	/* We can't get the signal strength */
+			strcpy(v->name, "FM");
+			return 0;
+		}
+	case VIDIOCSTUNER:
+		{
+			struct video_tuner *v = arg;
+			if (v->tuner != 0)
+				return -EINVAL;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+	case VIDIOCGFREQ:
+	{
+		unsigned long *freq = arg;
+		*freq = typhoon->curfreq;
+		return 0;
+	}
+	case VIDIOCSFREQ:
+	{
+		unsigned long *freq = arg;
+		typhoon->curfreq = *freq;
+		typhoon_setfreq(typhoon, typhoon->curfreq);
+		return 0;
+	}
+	case VIDIOCGAUDIO:
+		{
+			struct video_audio *v = arg;
+			memset(v, 0, sizeof(*v));
+			v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME;
+			v->mode |= VIDEO_SOUND_MONO;
+			v->volume = typhoon->curvol;
+			v->step = 1 << 14;
+			strcpy(v->name, "Typhoon Radio");
+			return 0;
+		}
+	case VIDIOCSAUDIO:
+		{
+			struct video_audio *v = arg;
+			if (v->audio)
+				return -EINVAL;
+			if (v->flags & VIDEO_AUDIO_MUTE)
+				typhoon_mute(typhoon);
+			else
+				typhoon_unmute(typhoon);
+			if (v->flags & VIDEO_AUDIO_VOLUME)
+				typhoon_setvol(typhoon, v->volume);
+			return 0;
+		}
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+static int typhoon_ioctl(struct inode *inode, struct file *file,
+			 unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, typhoon_do_ioctl);
+}
+
+static struct typhoon_device typhoon_unit =
+{
+	.iobase		= CONFIG_RADIO_TYPHOON_PORT,
+	.curfreq	= CONFIG_RADIO_TYPHOON_MUTEFREQ,
+	.mutefreq	= CONFIG_RADIO_TYPHOON_MUTEFREQ,
+};
+
+static struct file_operations typhoon_fops = {
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl		= typhoon_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device typhoon_radio =
+{
+	.owner		= THIS_MODULE,
+	.name		= "Typhoon Radio",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_TYPHOON,
+	.fops           = &typhoon_fops,
+};
+
+#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
+
+static int typhoon_get_info(char *buf, char **start, off_t offset, int len)
+{
+	char *out = buf;
+
+	#ifdef MODULE
+	    #define MODULEPROCSTRING "Driver loaded as a module"
+	#else
+	    #define MODULEPROCSTRING "Driver compiled into kernel"
+	#endif
+
+	/* output must be kept under PAGE_SIZE */
+	out += sprintf(out, BANNER);
+	out += sprintf(out, "Load type: " MODULEPROCSTRING "\n\n");
+	out += sprintf(out, "frequency = %lu kHz\n",
+		typhoon_unit.curfreq >> 4);
+	out += sprintf(out, "volume = %d\n", typhoon_unit.curvol);
+	out += sprintf(out, "mute = %s\n", typhoon_unit.muted ?
+		"on" : "off");
+	out += sprintf(out, "iobase = 0x%x\n", typhoon_unit.iobase);
+	out += sprintf(out, "mute frequency = %lu kHz\n",
+		typhoon_unit.mutefreq >> 4);
+	return out - buf;
+}
+
+#endif /* CONFIG_RADIO_TYPHOON_PROC_FS */
+
+MODULE_AUTHOR("Dr. Henrik Seidel");
+MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
+MODULE_LICENSE("GPL");
+
+static int io = -1;
+static int radio_nr = -1;
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
+module_param(radio_nr, int, 0);
+
+#ifdef MODULE
+static unsigned long mutefreq = 0;
+module_param(mutefreq, ulong, 0);
+MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
+#endif
+
+static int __init typhoon_init(void)
+{
+#ifdef MODULE
+	if (io == -1) {
+		printk(KERN_ERR "radio-typhoon: You must set an I/O address with io=0x316 or io=0x336\n");
+		return -EINVAL;
+	}
+	typhoon_unit.iobase = io;
+
+	if (mutefreq < 87000 || mutefreq > 108500) {
+		printk(KERN_ERR "radio-typhoon: You must set a frequency (in kHz) used when muting the card,\n");
+		printk(KERN_ERR "radio-typhoon: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
+		return -EINVAL;
+	}
+	typhoon_unit.mutefreq = mutefreq;
+#endif /* MODULE */
+
+	printk(KERN_INFO BANNER);
+	init_MUTEX(&typhoon_unit.lock);
+	io = typhoon_unit.iobase;
+	if (!request_region(io, 8, "typhoon")) {
+		printk(KERN_ERR "radio-typhoon: port 0x%x already in use\n",
+		       typhoon_unit.iobase);
+		return -EBUSY;
+	}
+
+	typhoon_radio.priv = &typhoon_unit;
+	if (video_register_device(&typhoon_radio, VFL_TYPE_RADIO, radio_nr) == -1)
+	{
+		release_region(io, 8);
+		return -EINVAL;
+	}
+	printk(KERN_INFO "radio-typhoon: port 0x%x.\n", typhoon_unit.iobase);
+	printk(KERN_INFO "radio-typhoon: mute frequency is %lu kHz.\n",
+	       typhoon_unit.mutefreq);
+	typhoon_unit.mutefreq <<= 4;
+
+	/* mute card - prevents noisy bootups */
+	typhoon_mute(&typhoon_unit);
+
+#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
+	if (!create_proc_info_entry("driver/radio-typhoon", 0, NULL,
+				    typhoon_get_info)) 
+	    	printk(KERN_ERR "radio-typhoon: registering /proc/driver/radio-typhoon failed\n");
+#endif
+
+	return 0;
+}
+
+static void __exit typhoon_cleanup_module(void)
+{
+
+#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
+	remove_proc_entry("driver/radio-typhoon", NULL);
+#endif
+
+	video_unregister_device(&typhoon_radio);
+	release_region(io, 8);
+}
+
+module_init(typhoon_init);
+module_exit(typhoon_cleanup_module);
+
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c
new file mode 100644
index 0000000..342f92d
--- /dev/null
+++ b/drivers/media/radio/radio-zoltrix.c
@@ -0,0 +1,385 @@
+/* zoltrix radio plus driver for Linux radio support
+ * (c) 1998 C. van Schaik <carl@leg.uct.ac.za>
+ *
+ * BUGS  
+ *  Due to the inconsistency in reading from the signal flags
+ *  it is difficult to get an accurate tuned signal.
+ *
+ *  It seems that the card is not linear to 0 volume. It cuts off
+ *  at a low volume, and it is not possible (at least I have not found)
+ *  to get fine volume control over the low volume range.
+ *
+ *  Some code derived from code by Romolo Manfredini
+ *				   romolo@bicnet.it
+ *
+ * 1999-05-06 - (C. van Schaik)
+ *	      - Make signal strength and stereo scans
+ *	        kinder to cpu while in delay
+ * 1999-01-05 - (C. van Schaik)
+ *	      - Changed tuning to 1/160Mhz accuracy
+ *	      - Added stereo support
+ *		(card defaults to stereo)
+ *		(can explicitly force mono on the card)
+ *		(can detect if station is in stereo)
+ *	      - Added unmute function
+ *	      - Reworked ioctl functions
+ * 2002-07-15 - Fix Stereo typo
+ */
+
+#include <linux/module.h>	/* Modules                        */
+#include <linux/init.h>		/* Initdata                       */
+#include <linux/ioport.h>	/* check_region, request_region   */
+#include <linux/delay.h>	/* udelay, msleep                 */
+#include <asm/io.h>		/* outb, outb_p                   */
+#include <asm/uaccess.h>	/* copy to/from user              */
+#include <linux/videodev.h>	/* kernel radio structs           */
+#include <linux/config.h>	/* CONFIG_RADIO_ZOLTRIX_PORT      */
+
+#ifndef CONFIG_RADIO_ZOLTRIX_PORT
+#define CONFIG_RADIO_ZOLTRIX_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_ZOLTRIX_PORT;
+static int radio_nr = -1;
+
+struct zol_device {
+	int port;
+	int curvol;
+	unsigned long curfreq;
+	int muted;
+	unsigned int stereo;
+	struct semaphore lock;
+};
+
+static int zol_setvol(struct zol_device *dev, int vol)
+{
+	dev->curvol = vol;
+	if (dev->muted)
+		return 0;
+
+	down(&dev->lock);
+	if (vol == 0) {
+		outb(0, io);
+		outb(0, io);
+		inb(io + 3);    /* Zoltrix needs to be read to confirm */
+		up(&dev->lock);
+		return 0;
+	}
+
+	outb(dev->curvol-1, io);
+	msleep(10);
+	inb(io + 2);
+	up(&dev->lock);
+	return 0;
+}
+
+static void zol_mute(struct zol_device *dev)
+{
+	dev->muted = 1;
+	down(&dev->lock);
+	outb(0, io);
+	outb(0, io);
+	inb(io + 3);            /* Zoltrix needs to be read to confirm */
+	up(&dev->lock);
+}
+
+static void zol_unmute(struct zol_device *dev)
+{
+	dev->muted = 0;
+	zol_setvol(dev, dev->curvol);
+}
+
+static int zol_setfreq(struct zol_device *dev, unsigned long freq)
+{
+	/* tunes the radio to the desired frequency */
+	unsigned long long bitmask, f, m;
+	unsigned int stereo = dev->stereo;
+	int i;
+
+	if (freq == 0)
+		return 1;
+	m = (freq / 160 - 8800) * 2;
+	f = (unsigned long long) m + 0x4d1c;
+
+	bitmask = 0xc480402c10080000ull;
+	i = 45;
+
+	down(&dev->lock);
+	
+	outb(0, io);
+	outb(0, io);
+	inb(io + 3);            /* Zoltrix needs to be read to confirm */
+
+	outb(0x40, io);
+	outb(0xc0, io);
+
+	bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ ( stereo << 31));
+	while (i--) {
+		if ((bitmask & 0x8000000000000000ull) != 0) {
+			outb(0x80, io);
+			udelay(50);
+			outb(0x00, io);
+			udelay(50);
+			outb(0x80, io);
+			udelay(50);
+		} else {
+			outb(0xc0, io);
+			udelay(50);
+			outb(0x40, io);
+			udelay(50);
+			outb(0xc0, io);
+			udelay(50);
+		}
+		bitmask *= 2;
+	}
+	/* termination sequence */
+	outb(0x80, io);
+	outb(0xc0, io);
+	outb(0x40, io);
+	udelay(1000);
+	inb(io+2);
+
+        udelay(1000);
+        
+	if (dev->muted)
+	{
+		outb(0, io);
+		outb(0, io);
+		inb(io + 3);
+		udelay(1000);
+	}
+	
+	up(&dev->lock);
+	
+	if(!dev->muted)
+	{
+	        zol_setvol(dev, dev->curvol);
+	}
+	return 0;
+}
+
+/* Get signal strength */
+
+static int zol_getsigstr(struct zol_device *dev)
+{
+	int a, b;
+
+	down(&dev->lock);
+	outb(0x00, io);         /* This stuff I found to do nothing */
+	outb(dev->curvol, io);
+	msleep(20);
+
+	a = inb(io);
+	msleep(10);
+	b = inb(io);
+
+	up(&dev->lock);
+	
+	if (a != b)
+		return (0);
+
+        if ((a == 0xcf) || (a == 0xdf)  /* I found this out by playing */
+		|| (a == 0xef))       /* with a binary scanner on the card io */
+		return (1);
+ 	return (0);
+}
+
+static int zol_is_stereo (struct zol_device *dev)
+{
+	int x1, x2;
+
+	down(&dev->lock);
+	
+	outb(0x00, io);
+	outb(dev->curvol, io);
+	msleep(20);
+
+	x1 = inb(io);
+	msleep(10);
+	x2 = inb(io);
+
+	up(&dev->lock);
+	
+	if ((x1 == x2) && (x1 == 0xcf))
+		return 1;
+	return 0;
+}
+
+static int zol_do_ioctl(struct inode *inode, struct file *file,
+			unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct zol_device *zol = dev->priv;
+
+	switch (cmd) {
+	case VIDIOCGCAP:
+		{
+			struct video_capability *v = arg;
+
+			memset(v,0,sizeof(*v));
+			v->type = VID_TYPE_TUNER;
+			v->channels = 1 + zol->stereo;
+			v->audios = 1;
+			strcpy(v->name, "Zoltrix Radio");
+			return 0;
+		}
+	case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+			if (v->tuner)	
+				return -EINVAL;
+			strcpy(v->name, "FM");
+			v->rangelow = (int) (88.0 * 16000);
+			v->rangehigh = (int) (108.0 * 16000);
+			v->flags = zol_is_stereo(zol)
+					? VIDEO_TUNER_STEREO_ON : 0;
+			v->flags |= VIDEO_TUNER_LOW;
+			v->mode = VIDEO_MODE_AUTO;
+			v->signal = 0xFFFF * zol_getsigstr(zol);
+			return 0;
+		}
+	case VIDIOCSTUNER:
+		{
+			struct video_tuner *v = arg;
+			if (v->tuner != 0)
+				return -EINVAL;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+	case VIDIOCGFREQ:
+	{
+		unsigned long *freq = arg;
+		*freq = zol->curfreq;
+		return 0;
+	}
+	case VIDIOCSFREQ:
+	{
+		unsigned long *freq = arg;
+		zol->curfreq = *freq;
+		zol_setfreq(zol, zol->curfreq);
+		return 0;
+	}
+	case VIDIOCGAUDIO:
+		{
+			struct video_audio *v = arg;
+			memset(v, 0, sizeof(*v));
+			v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME;
+			v->mode |= zol_is_stereo(zol)
+				? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
+			v->volume = zol->curvol * 4096;
+			v->step = 4096;
+			strcpy(v->name, "Zoltrix Radio");
+			return 0;
+		}
+	case VIDIOCSAUDIO:
+		{
+			struct video_audio *v = arg;
+			if (v->audio)
+				return -EINVAL;
+
+			if (v->flags & VIDEO_AUDIO_MUTE)
+				zol_mute(zol);
+			else {
+				zol_unmute(zol);
+				zol_setvol(zol, v->volume / 4096);
+			}
+
+			if (v->mode & VIDEO_SOUND_STEREO) {
+				zol->stereo = 1;
+				zol_setfreq(zol, zol->curfreq);
+			}
+			if (v->mode & VIDEO_SOUND_MONO) {
+				zol->stereo = 0;
+				zol_setfreq(zol, zol->curfreq);
+			}
+			return 0;
+		}
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+static int zol_ioctl(struct inode *inode, struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, zol_do_ioctl);
+}
+
+static struct zol_device zoltrix_unit;
+
+static struct file_operations zoltrix_fops =
+{
+	.owner		= THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release,
+	.ioctl		= zol_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device zoltrix_radio =
+{
+	.owner		= THIS_MODULE,
+	.name		= "Zoltrix Radio Plus",
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_ZOLTRIX,
+	.fops           = &zoltrix_fops,
+};
+
+static int __init zoltrix_init(void)
+{
+	if (io == -1) {
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+	if ((io != 0x20c) && (io != 0x30c)) {
+		printk(KERN_ERR "zoltrix: invalid port, try 0x20c or 0x30c\n");
+		return -ENXIO;
+	}
+
+	zoltrix_radio.priv = &zoltrix_unit;
+	if (!request_region(io, 2, "zoltrix")) {
+		printk(KERN_ERR "zoltrix: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	if (video_register_device(&zoltrix_radio, VFL_TYPE_RADIO, radio_nr) == -1)
+	{
+		release_region(io, 2);
+		return -EINVAL;
+	}
+	printk(KERN_INFO "Zoltrix Radio Plus card driver.\n");
+
+	init_MUTEX(&zoltrix_unit.lock);
+	
+	/* mute card - prevents noisy bootups */
+
+	/* this ensures that the volume is all the way down  */
+
+	outb(0, io);
+	outb(0, io);
+	msleep(20);
+	inb(io + 3);
+
+	zoltrix_unit.curvol = 0;
+	zoltrix_unit.stereo = 1;
+
+	return 0;
+}
+
+MODULE_AUTHOR("C.van Schaik");
+MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
+module_param(radio_nr, int, 0);
+
+static void __exit zoltrix_cleanup_module(void)
+{
+	video_unregister_device(&zoltrix_radio);
+	release_region(io, 2);
+}
+
+module_init(zoltrix_init);
+module_exit(zoltrix_cleanup_module);
+