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/sound/oss/ad1816.c b/sound/oss/ad1816.c
new file mode 100644
index 0000000..22dae5d
--- /dev/null
+++ b/sound/oss/ad1816.c
@@ -0,0 +1,1369 @@
+/*
+ *
+ * AD1816 lowlevel sound driver for Linux 2.6.0 and above
+ *
+ * Copyright (C) 1998-2003 by Thorsten Knabe <linux@thorsten-knabe.de>
+ *
+ * Based on the CS4232/AD1848 driver Copyright (C) by Hannu Savolainen 1993-1996
+ *
+ *
+ * version: 1.5
+ * status: beta
+ * date: 2003/07/15
+ *
+ * Changes:
+ *	Oleg Drokin: Some cleanup of load/unload functions.	1998/11/24
+ *	
+ *	Thorsten Knabe: attach and unload rewritten, 
+ *	some argument checks added				1998/11/30
+ *
+ *	Thorsten Knabe: Buggy isa bridge workaround added	1999/01/16
+ *	
+ *	David Moews/Thorsten Knabe: Introduced options 
+ *	parameter. Added slightly modified patch from 
+ *	David Moews to disable dsp audio sources by setting 
+ *	bit 0 of options parameter. This seems to be
+ *	required by some Aztech/Newcom SC-16 cards.		1999/04/18
+ *
+ *	Christoph Hellwig: Adapted to module_init/module_exit.	2000/03/03
+ *
+ *	Christoph Hellwig: Added isapnp support			2000/03/15
+ *
+ *	Arnaldo Carvalho de Melo: get rid of check_region	2001/10/07
+ *      
+ *      Thorsten Knabe: Compiling with CONFIG_PNP enabled
+ *	works again. It is now possible to use more than one 
+ *	AD1816 sound card. Sample rate now may be changed during
+ *	playback/capture. printk() uses log levels everywhere.
+ *	SMP fixes. DMA handling fixes.
+ *	Other minor code cleanup.				2003/07/15
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/isapnp.h>
+#include <linux/stddef.h>
+#include <linux/spinlock.h>
+#include "sound_config.h"
+
+#define DEBUGNOISE(x)
+
+#define CHECK_FOR_POWER { int timeout=100; \
+  while (timeout > 0 && (inb(devc->base)&0x80)!= 0x80) {\
+          timeout--; \
+  } \
+  if (timeout==0) {\
+          printk(KERN_WARNING "ad1816: Check for power failed in %s line: %d\n",__FILE__,__LINE__); \
+  } \
+}
+
+/* structure to hold device specific information */
+typedef struct
+{
+        int            base;          /* set in attach */
+	int            irq;
+	int            dma_playback;
+        int            dma_capture;
+  
+	int            opened;         /* open */
+        int            speed;	
+	int            channels;
+	int            audio_format;
+        int            audio_mode; 
+  
+        int            recmask;        /* setup */
+	unsigned char  format_bits;
+	int            supported_devices;
+	int            supported_rec_devices;
+	unsigned short levels[SOUND_MIXER_NRDEVICES];
+					/* misc */
+	struct pnp_dev *pnpdev;	 /* configured via pnp */
+        int            dev_no;   /* this is the # in audio_devs and NOT 
+				    in ad1816_info */
+	spinlock_t	lock;  
+} ad1816_info;
+
+static int nr_ad1816_devs;
+static int ad1816_clockfreq = 33000;
+static int options;
+
+/* supported audio formats */
+static int  ad_format_mask =
+AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW;
+
+/* array of device info structures */
+static ad1816_info dev_info[MAX_AUDIO_DEV];
+
+
+/* ------------------------------------------------------------------- */
+
+/* functions for easier access to inderect registers */
+
+static int ad_read (ad1816_info * devc, int reg)
+{
+	int result;
+	
+	CHECK_FOR_POWER;
+	outb ((unsigned char) (reg & 0x3f), devc->base+0);
+	result = inb(devc->base+2);
+	result+= inb(devc->base+3)<<8;
+	return (result);
+}
+
+
+static void ad_write (ad1816_info * devc, int reg, int data)
+{
+	CHECK_FOR_POWER;
+	outb ((unsigned char) (reg & 0xff), devc->base+0);
+	outb ((unsigned char) (data & 0xff),devc->base+2);
+	outb ((unsigned char) ((data>>8)&0xff),devc->base+3);
+}
+
+/* ------------------------------------------------------------------- */
+
+/* function interface required by struct audio_driver */
+
+static void ad1816_halt_input (int dev)
+{
+	unsigned long flags;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	unsigned char buffer;
+	
+	DEBUGNOISE(printk(KERN_DEBUG "ad1816: halt_input called\n"));
+	
+	spin_lock_irqsave(&devc->lock,flags); 
+	
+	if(!isa_dma_bridge_buggy) {
+	        disable_dma(audio_devs[dev]->dmap_in->dma);
+	}
+	
+	buffer=inb(devc->base+9);
+	if (buffer & 0x01) {
+		/* disable capture */
+		outb(buffer & ~0x01,devc->base+9); 
+	}
+
+	if(!isa_dma_bridge_buggy) {
+	        enable_dma(audio_devs[dev]->dmap_in->dma);
+	}
+
+	/* Clear interrupt status */
+	outb (~0x40, devc->base+1);	
+	
+	devc->audio_mode &= ~PCM_ENABLE_INPUT;
+	spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static void ad1816_halt_output (int dev)
+{
+	unsigned long  flags;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	
+	unsigned char buffer;
+
+	DEBUGNOISE(printk(KERN_DEBUG "ad1816: halt_output called!\n"));
+
+	spin_lock_irqsave(&devc->lock,flags); 
+	/* Mute pcm output */
+	ad_write(devc, 4, ad_read(devc,4)|0x8080);
+
+	if(!isa_dma_bridge_buggy) {
+	        disable_dma(audio_devs[dev]->dmap_out->dma);
+	}
+
+	buffer=inb(devc->base+8);
+	if (buffer & 0x01) {
+		/* disable capture */
+		outb(buffer & ~0x01,devc->base+8); 
+	}
+
+	if(!isa_dma_bridge_buggy) {
+	        enable_dma(audio_devs[dev]->dmap_out->dma);
+	}
+
+	/* Clear interrupt status */
+	outb ((unsigned char)~0x80, devc->base+1);	
+
+	devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+	spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static void ad1816_output_block (int dev, unsigned long buf, 
+				 int count, int intrflag)
+{
+	unsigned long flags;
+	unsigned long cnt;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	
+	DEBUGNOISE(printk(KERN_DEBUG "ad1816: output_block called buf=%ld count=%d flags=%d\n",buf,count,intrflag));
+  
+	cnt = count/4 - 1;
+  
+	spin_lock_irqsave(&devc->lock,flags);
+	
+	/* set transfer count */
+	ad_write (devc, 8, cnt & 0xffff); 
+	
+	devc->audio_mode |= PCM_ENABLE_OUTPUT; 
+	spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+
+static void ad1816_start_input (int dev, unsigned long buf, int count,
+				int intrflag)
+{
+	unsigned long flags;
+	unsigned long  cnt;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	
+	DEBUGNOISE(printk(KERN_DEBUG "ad1816: start_input called buf=%ld count=%d flags=%d\n",buf,count,intrflag));
+
+	cnt = count/4 - 1;
+
+	spin_lock_irqsave(&devc->lock,flags);
+
+	/* set transfer count */
+	ad_write (devc, 10, cnt & 0xffff); 
+	devc->audio_mode |= PCM_ENABLE_INPUT;
+	spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static int ad1816_prepare_for_input (int dev, int bsize, int bcount)
+{
+	unsigned long flags;
+	unsigned int freq;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	unsigned char fmt_bits;
+	
+	DEBUGNOISE(printk(KERN_DEBUG "ad1816: prepare_for_input called: bsize=%d bcount=%d\n",bsize,bcount));
+
+	spin_lock_irqsave(&devc->lock,flags);
+	fmt_bits= (devc->format_bits&0x7)<<3;
+	
+	/* set mono/stereo mode */
+	if (devc->channels > 1) {
+		fmt_bits |=0x4;
+	}
+	/* set Mono/Stereo in playback/capture register */
+	outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); 
+	outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9);
+
+	freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; 
+
+	/* write playback/capture speeds */
+	ad_write (devc, 2, freq & 0xffff);	
+	ad_write (devc, 3, freq & 0xffff);	
+
+	spin_unlock_irqrestore(&devc->lock,flags);
+
+	ad1816_halt_input(dev);
+	return 0;
+}
+
+static int ad1816_prepare_for_output (int dev, int bsize, int bcount)
+{
+	unsigned long flags;
+	unsigned int freq;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	unsigned char fmt_bits;
+
+	DEBUGNOISE(printk(KERN_DEBUG "ad1816: prepare_for_output called: bsize=%d bcount=%d\n",bsize,bcount));
+
+	spin_lock_irqsave(&devc->lock,flags);
+
+	fmt_bits= (devc->format_bits&0x7)<<3;
+	/* set mono/stereo mode */
+	if (devc->channels > 1) {
+		fmt_bits |=0x4;
+	}
+
+	/* write format bits to playback/capture registers */
+	outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); 
+	outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9);
+  
+	freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; 
+	
+	/* write playback/capture speeds */
+	ad_write (devc, 2, freq & 0xffff);
+	ad_write (devc, 3, freq & 0xffff);
+
+	spin_unlock_irqrestore(&devc->lock,flags);
+	
+	ad1816_halt_output(dev);
+	return 0;
+
+}
+
+static void ad1816_trigger (int dev, int state) 
+{
+	unsigned long flags;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+	DEBUGNOISE(printk(KERN_DEBUG "ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base));
+
+	/* mode may have changed */
+
+	spin_lock_irqsave(&devc->lock,flags);
+
+	/* mask out modes not specified on open call */
+	state &= devc->audio_mode; 
+				
+	/* setup soundchip to new io-mode */
+	if (state & PCM_ENABLE_INPUT) {
+		/* enable capture */
+		outb(inb(devc->base+9)|0x01, devc->base+9);
+	} else {
+		/* disable capture */
+		outb(inb(devc->base+9)&~0x01, devc->base+9);
+	}
+
+	if (state & PCM_ENABLE_OUTPUT) {
+		/* enable playback */
+		outb(inb(devc->base+8)|0x01, devc->base+8);
+		/* unmute pcm output */
+		ad_write(devc, 4, ad_read(devc,4)&~0x8080);
+	} else {
+		/* mute pcm output */
+		ad_write(devc, 4, ad_read(devc,4)|0x8080);
+		/* disable capture */
+		outb(inb(devc->base+8)&~0x01, devc->base+8);
+	}
+	spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+
+/* halt input & output */
+static void ad1816_halt (int dev)
+{
+	ad1816_halt_input(dev);
+	ad1816_halt_output(dev);
+}
+
+static void ad1816_reset (int dev)
+{
+	ad1816_halt (dev);
+}
+
+/* set playback speed */
+static int ad1816_set_speed (int dev, int arg)
+{
+	unsigned long flags;
+	unsigned int freq;
+	int ret;
+
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	
+	spin_lock_irqsave(&devc->lock, flags);
+	if (arg == 0) {
+		ret = devc->speed;
+		spin_unlock_irqrestore(&devc->lock, flags);
+		return ret;
+	}
+	/* range checking */
+	if (arg < 4000) {
+		arg = 4000;
+	}
+	if (arg > 55000) {
+		arg = 55000;
+	}
+	devc->speed = arg;
+
+	/* change speed during playback */
+	freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; 
+	/* write playback/capture speeds */
+	ad_write (devc, 2, freq & 0xffff);	
+	ad_write (devc, 3, freq & 0xffff);	
+
+	ret = devc->speed;
+	spin_unlock_irqrestore(&devc->lock, flags);
+	return ret;
+
+}
+
+static unsigned int ad1816_set_bits (int dev, unsigned int arg)
+{
+	unsigned long flags;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	
+	static struct format_tbl {
+		int             format;
+		unsigned char   bits;
+	} format2bits[] = {
+		{ 0, 0 },
+		{ AFMT_MU_LAW, 1 },
+		{ AFMT_A_LAW, 3 },
+		{ AFMT_IMA_ADPCM, 0 },
+		{ AFMT_U8, 0 },
+		{ AFMT_S16_LE, 2 },
+		{ AFMT_S16_BE, 6 },
+		{ AFMT_S8, 0 },
+		{ AFMT_U16_LE, 0 },
+		{ AFMT_U16_BE, 0 }
+  	};
+
+	int  i, n = sizeof (format2bits) / sizeof (struct format_tbl);
+
+	spin_lock_irqsave(&devc->lock, flags);
+	/* return current format */
+	if (arg == 0) {
+	  	arg = devc->audio_format;
+		spin_unlock_irqrestore(&devc->lock, flags);
+		return arg;
+	}
+	devc->audio_format = arg;
+
+	/* search matching format bits */
+	for (i = 0; i < n; i++)
+		if (format2bits[i].format == arg) {
+			devc->format_bits = format2bits[i].bits;
+			devc->audio_format = arg;
+			spin_unlock_irqrestore(&devc->lock, flags);
+			return arg;
+		}
+
+	/* Still hanging here. Something must be terribly wrong */
+	devc->format_bits = 0;
+	devc->audio_format = AFMT_U8;
+	spin_unlock_irqrestore(&devc->lock, flags);
+	return(AFMT_U8); 
+}
+
+static short ad1816_set_channels (int dev, short arg)
+{
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+	if (arg != 1 && arg != 2)
+		return devc->channels;
+
+	devc->channels = arg;
+	return arg;
+}
+
+/* open device */
+static int ad1816_open (int dev, int mode) 
+{
+	ad1816_info    *devc = NULL;
+	unsigned long   flags;
+
+	/* is device number valid ? */
+	if (dev < 0 || dev >= num_audiodevs)
+		return -(ENXIO);
+
+	/* get device info of this dev */
+	devc = (ad1816_info *) audio_devs[dev]->devc; 
+
+	/* make check if device already open atomic */
+	spin_lock_irqsave(&devc->lock,flags);
+
+	if (devc->opened) {
+		spin_unlock_irqrestore(&devc->lock,flags);
+		return -(EBUSY);
+	}
+
+	/* mark device as open */
+	devc->opened = 1; 
+
+	devc->audio_mode = 0;
+	devc->speed = 8000;
+	devc->audio_format=AFMT_U8;
+	devc->channels=1;
+	spin_unlock_irqrestore(&devc->lock,flags);
+	ad1816_reset(devc->dev_no); /* halt all pending output */
+	return 0;
+}
+
+static void ad1816_close (int dev) /* close device */
+{
+	unsigned long flags;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+	/* halt all pending output */
+	ad1816_reset(devc->dev_no); 
+
+	spin_lock_irqsave(&devc->lock,flags);
+	devc->opened = 0;
+	devc->audio_mode = 0;
+	devc->speed = 8000;
+	devc->audio_format=AFMT_U8;
+	devc->format_bits = 0;
+	spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+
+/* ------------------------------------------------------------------- */
+
+/* Audio driver structure */
+
+static struct audio_driver ad1816_audio_driver =
+{
+	.owner			= THIS_MODULE,
+	.open			= ad1816_open,
+	.close			= ad1816_close,
+	.output_block		= ad1816_output_block,
+	.start_input		= ad1816_start_input,
+	.prepare_for_input	= ad1816_prepare_for_input,
+	.prepare_for_output	= ad1816_prepare_for_output,
+	.halt_io		= ad1816_halt,
+	.halt_input		= ad1816_halt_input,
+	.halt_output		= ad1816_halt_output,
+	.trigger		= ad1816_trigger,
+	.set_speed		= ad1816_set_speed,
+	.set_bits		= ad1816_set_bits,
+	.set_channels		= ad1816_set_channels,
+};
+
+
+/* ------------------------------------------------------------------- */
+
+/* Interrupt handler */
+
+
+static irqreturn_t ad1816_interrupt (int irq, void *dev_id, struct pt_regs *dummy)
+{
+	unsigned char	status;
+	ad1816_info	*devc = (ad1816_info *)dev_id;
+	
+	if (irq < 0 || irq > 15) {
+	        printk(KERN_WARNING "ad1816: Got bogus interrupt %d\n", irq);
+		return IRQ_NONE;
+	}
+
+	spin_lock(&devc->lock);
+
+	/* read interrupt register */
+	status = inb (devc->base+1); 
+	/* Clear all interrupt  */
+	outb (~status, devc->base+1);	
+
+	DEBUGNOISE(printk(KERN_DEBUG "ad1816: Got interrupt subclass %d\n",status));
+
+	if (status == 0) {
+		DEBUGNOISE(printk(KERN_DEBUG "ad1816: interrupt: Got interrupt, but no source.\n"));
+		spin_unlock(&devc->lock);
+		return IRQ_NONE;
+	}
+
+	if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT) && (status&64))
+		DMAbuf_inputintr (devc->dev_no);
+
+	if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) && (status & 128))
+		DMAbuf_outputintr (devc->dev_no, 1);
+
+	spin_unlock(&devc->lock);
+	return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------------- */
+
+/* Mixer stuff */
+
+struct mixer_def {
+	unsigned int regno: 7;
+	unsigned int polarity:1;	/* 0=normal, 1=reversed */
+	unsigned int bitpos:4;
+	unsigned int nbits:4;
+};
+
+static char mix_cvt[101] = {
+	 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
+	43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
+	65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
+	80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
+	91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99,
+	100
+};
+
+typedef struct mixer_def mixer_ent;
+
+/*
+ * Most of the mixer entries work in backwards. Setting the polarity field
+ * makes them to work correctly.
+ *
+ * The channel numbering used by individual soundcards is not fixed. Some
+ * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs.
+ * The current version doesn't try to compensate this.
+ */
+
+#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r)	\
+  {{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r}}
+
+
+mixer_ent mix_devices[SOUND_MIXER_NRDEVICES][2] = {
+MIX_ENT(SOUND_MIXER_VOLUME,	14, 1, 8, 5,	14, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,	 5, 1, 8, 6,	 5, 1, 0, 6),
+MIX_ENT(SOUND_MIXER_PCM,	 4, 1, 8, 6,	 4, 1, 0, 6),
+MIX_ENT(SOUND_MIXER_SPEAKER,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	18, 1, 8, 5,	18, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_MIC,	19, 1, 8, 5,	19, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_CD,	 	15, 1, 8, 5,	15, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_IMIX,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	20, 0, 8, 4,	20, 0, 0, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1, 	17, 1, 8, 5,	17, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_LINE2,	16, 1, 8, 5,	16, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_LINE3,      39, 0, 9, 4,    39, 1, 0, 5)
+};
+
+
+static unsigned short default_mixer_levels[SOUND_MIXER_NRDEVICES] =
+{
+	0x4343,		/* Master Volume */
+	0x3232,		/* Bass */
+	0x3232,		/* Treble */
+	0x0000,		/* FM */
+	0x4343,		/* PCM */
+	0x0000,		/* PC Speaker */
+	0x0000,		/* Ext Line */
+	0x0000,		/* Mic */
+	0x0000,		/* CD */
+	0x0000,		/* Recording monitor */
+	0x0000,		/* SB PCM */
+	0x0000,		/* Recording level */
+	0x0000,		/* Input gain */
+	0x0000,		/* Output gain */
+	0x0000,		/* Line1 */
+	0x0000,		/* Line2 */
+	0x0000		/* Line3 (usually line in)*/
+};
+
+#define LEFT_CHN	0
+#define RIGHT_CHN	1
+
+
+
+static int
+ad1816_set_recmask (ad1816_info * devc, int mask)
+{
+  	unsigned long 	flags;
+	unsigned char   recdev;
+	int             i, n;
+	
+	spin_lock_irqsave(&devc->lock, flags);
+	mask &= devc->supported_rec_devices;
+	
+	n = 0;
+	/* Count selected device bits */
+	for (i = 0; i < 32; i++)
+		if (mask & (1 << i))
+			n++;
+	
+	if (n == 0)
+		mask = SOUND_MASK_MIC;
+	else if (n != 1) { /* Too many devices selected */
+		/* Filter out active settings */
+		mask &= ~devc->recmask;	
+		
+		n = 0;
+		/* Count selected device bits */
+		for (i = 0; i < 32; i++) 
+			if (mask & (1 << i))
+				n++;
+		
+		if (n != 1)
+			mask = SOUND_MASK_MIC;
+	}
+	
+	switch (mask) {
+	case SOUND_MASK_MIC:
+		recdev = 5;
+		break;
+		
+	case SOUND_MASK_LINE:
+		recdev = 0;
+		break;
+		
+	case SOUND_MASK_CD:
+		recdev = 2;
+		break;
+		
+	case SOUND_MASK_LINE1:
+		recdev = 4;
+		break;
+		
+	case SOUND_MASK_LINE2:
+		recdev = 3;
+		break;
+		
+	case SOUND_MASK_VOLUME:
+		recdev = 1;
+		break;
+		
+	default:
+		mask = SOUND_MASK_MIC;
+		recdev = 5;
+	}
+	
+	recdev <<= 4;
+	ad_write (devc, 20, 
+		  (ad_read (devc, 20) & 0x8f8f) | recdev | (recdev<<8));
+
+	devc->recmask = mask;
+	spin_unlock_irqrestore(&devc->lock, flags);
+	return mask;
+}
+
+static void
+change_bits (int *regval, int dev, int chn, int newval)
+{
+	unsigned char   mask;
+	int             shift;
+  
+	/* Reverse polarity*/
+
+	if (mix_devices[dev][chn].polarity == 1) 
+		newval = 100 - newval;
+
+	mask = (1 << mix_devices[dev][chn].nbits) - 1;
+	shift = mix_devices[dev][chn].bitpos;
+	/* Scale it */
+	newval = (int) ((newval * mask) + 50) / 100;	
+	/* Clear bits */
+	*regval &= ~(mask << shift);	
+	/* Set new value */
+	*regval |= (newval & mask) << shift;	
+}
+
+static int
+ad1816_mixer_get (ad1816_info * devc, int dev)
+{
+	DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_get called!\n"));
+	
+	/* range check + supported mixer check */
+	if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES )
+	        return (-(EINVAL));
+	if (!((1 << dev) & devc->supported_devices))
+		return -(EINVAL);
+	
+	return devc->levels[dev];
+}
+
+static int
+ad1816_mixer_set (ad1816_info * devc, int dev, int value)
+{
+	int   left = value & 0x000000ff;
+	int   right = (value & 0x0000ff00) >> 8;
+	int   retvol;
+
+	int   regoffs;
+	int   val;
+	int   valmute;
+	unsigned long flags;
+
+	DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_set called!\n"));
+	
+	if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES )
+		return -(EINVAL);
+
+	if (left > 100)
+		left = 100;
+	if (left < 0)
+		left = 0;
+	if (right > 100)
+		right = 100;
+	if (right < 0)
+		right = 0;
+	
+	/* Mono control */
+	if (mix_devices[dev][RIGHT_CHN].nbits == 0) 
+		right = left;
+	retvol = left | (right << 8);
+	
+	/* Scale it */
+	
+	left = mix_cvt[left];
+	right = mix_cvt[right];
+
+	/* reject all mixers that are not supported */
+	if (!(devc->supported_devices & (1 << dev)))
+		return -(EINVAL);
+	
+	/* sanity check */
+	if (mix_devices[dev][LEFT_CHN].nbits == 0)
+		return -(EINVAL);
+	spin_lock_irqsave(&devc->lock, flags);
+
+	/* keep precise volume internal */
+	devc->levels[dev] = retvol;
+
+	/* Set the left channel */
+	regoffs = mix_devices[dev][LEFT_CHN].regno;
+	val = ad_read (devc, regoffs);
+	change_bits (&val, dev, LEFT_CHN, left);
+
+	valmute=val;
+
+	/* Mute bit masking on some registers */
+	if ( regoffs==5 || regoffs==14 || regoffs==15 ||
+	     regoffs==16 || regoffs==17 || regoffs==18 || 
+	     regoffs==19 || regoffs==39) {
+		if (left==0)
+			valmute |= 0x8000;
+		else
+			valmute &= ~0x8000;
+	}
+	ad_write (devc, regoffs, valmute); /* mute */
+
+	/*
+	 * Set the right channel
+	 */
+ 
+	/* Was just a mono channel */
+	if (mix_devices[dev][RIGHT_CHN].nbits == 0) {
+		spin_unlock_irqrestore(&devc->lock, flags);
+		return retvol;		
+	}
+
+	regoffs = mix_devices[dev][RIGHT_CHN].regno;
+	val = ad_read (devc, regoffs);
+	change_bits (&val, dev, RIGHT_CHN, right);
+
+	valmute=val;
+	if ( regoffs==5 || regoffs==14 || regoffs==15 ||
+	     regoffs==16 || regoffs==17 || regoffs==18 || 
+	     regoffs==19 || regoffs==39) {
+		if (right==0)
+			valmute |= 0x80;
+		else
+			valmute &= ~0x80;
+	}
+	ad_write (devc, regoffs, valmute); /* mute */
+	spin_unlock_irqrestore(&devc->lock, flags);
+       	return retvol;
+}
+
+#define MIXER_DEVICES ( SOUND_MASK_VOLUME | \
+			SOUND_MASK_SYNTH | \
+			SOUND_MASK_PCM | \
+			SOUND_MASK_LINE | \
+			SOUND_MASK_LINE1 | \
+			SOUND_MASK_LINE2 | \
+			SOUND_MASK_LINE3 | \
+			SOUND_MASK_MIC | \
+			SOUND_MASK_CD | \
+			SOUND_MASK_RECLEV  \
+			)
+#define REC_DEVICES ( SOUND_MASK_LINE2 |\
+		      SOUND_MASK_LINE |\
+		      SOUND_MASK_LINE1 |\
+		      SOUND_MASK_MIC |\
+		      SOUND_MASK_CD |\
+		      SOUND_MASK_VOLUME \
+		      )
+     
+static void
+ad1816_mixer_reset (ad1816_info * devc)
+{
+	int  i;
+
+	devc->supported_devices = MIXER_DEVICES;
+	
+	devc->supported_rec_devices = REC_DEVICES;
+
+	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+		if (devc->supported_devices & (1 << i))
+			ad1816_mixer_set (devc, i, default_mixer_levels[i]);
+	ad1816_set_recmask (devc, SOUND_MASK_MIC);
+}
+
+static int
+ad1816_mixer_ioctl (int dev, unsigned int cmd, void __user * arg)
+{
+	ad1816_info    *devc = mixer_devs[dev]->devc;
+	int val;
+	int __user *p = arg;
+  
+	DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_ioctl called!\n"));
+  
+	/* Mixer ioctl */
+	if (((cmd >> 8) & 0xff) == 'M') { 
+		
+		/* set ioctl */
+		if (_SIOC_DIR (cmd) & _SIOC_WRITE) { 
+			switch (cmd & 0xff){
+			case SOUND_MIXER_RECSRC:
+				
+				if (get_user(val, p))
+					return -EFAULT;
+				val=ad1816_set_recmask (devc, val);
+				return put_user(val, p);
+				break;
+				
+			default:
+				if (get_user(val, p))
+					return -EFAULT;
+				if ((val=ad1816_mixer_set (devc, cmd & 0xff, val))<0)
+				        return val;
+				else
+				        return put_user(val, p);
+			}
+		} else { 
+			/* read ioctl */
+			switch (cmd & 0xff) {
+				
+			case SOUND_MIXER_RECSRC:
+				val=devc->recmask;
+				return put_user(val, p);
+				break;
+				
+			case SOUND_MIXER_DEVMASK:
+				val=devc->supported_devices;
+				return put_user(val, p);
+				break;
+
+			case SOUND_MIXER_STEREODEVS:
+				val=devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
+				return put_user(val, p);
+				break;
+				
+			case SOUND_MIXER_RECMASK:
+				val=devc->supported_rec_devices;
+				return put_user(val, p);
+				break;
+				
+			case SOUND_MIXER_CAPS:
+				val=SOUND_CAP_EXCL_INPUT;
+				return put_user(val, p);
+				break;
+				
+			default:
+			        if ((val=ad1816_mixer_get (devc, cmd & 0xff))<0)
+				        return val;
+				else
+				        return put_user(val, p);
+			}
+		}
+	} else
+		/* not for mixer */
+		return -(EINVAL);
+}
+
+/* ------------------------------------------------------------------- */
+
+/* Mixer structure */
+
+static struct mixer_operations ad1816_mixer_operations = {
+	.owner	= THIS_MODULE,
+	.id	= "AD1816",
+	.name	= "AD1816 Mixer",
+	.ioctl	= ad1816_mixer_ioctl
+};
+
+
+/* ------------------------------------------------------------------- */
+
+/* stuff for card recognition, init and unloading PNP ...*/
+
+
+/* check if AD1816 present at specified hw_config and register device with OS 
+ * return 1 if initialization was successful, 0 otherwise
+ */
+static int __init ad1816_init_card (struct address_info *hw_config, 
+	struct pnp_dev *pnp)
+{
+	ad1816_info    *devc = NULL;
+	int tmp;
+	int oss_devno = -1;
+
+	printk(KERN_INFO "ad1816: initializing card: io=0x%x, irq=%d, dma=%d, "
+			 "dma2=%d, clockfreq=%d, options=%d isadmabug=%d "
+			 "%s\n",
+	       hw_config->io_base,
+	       hw_config->irq,
+	       hw_config->dma,
+	       hw_config->dma2,
+	       ad1816_clockfreq,
+	       options,
+	       isa_dma_bridge_buggy,
+	       pnp?"(PNP)":"");
+
+	/* ad1816_info structure remaining ? */
+	if (nr_ad1816_devs >= MAX_AUDIO_DEV) {
+		printk(KERN_WARNING "ad1816: no more ad1816_info structures "
+			"left\n");
+		goto out;
+	}
+
+	devc = &dev_info[nr_ad1816_devs];
+	devc->base = hw_config->io_base;
+	devc->irq = hw_config->irq;
+	devc->dma_playback=hw_config->dma;
+	devc->dma_capture=hw_config->dma2;
+	devc->opened = 0;
+	devc->pnpdev = pnp;
+	spin_lock_init(&devc->lock);
+
+	if (!request_region(devc->base, 16, "AD1816 Sound")) {
+		printk(KERN_WARNING "ad1816: I/O port 0x%03x not free\n",
+				    devc->base);
+		goto out;
+	}
+
+	printk(KERN_INFO "ad1816: Examining AD1816 at address 0x%03x.\n", 
+		devc->base);
+	
+
+	/* tests for ad1816 */
+	/* base+0: bit 1 must be set but not 255 */
+	tmp=inb(devc->base);
+	if ( (tmp&0x80)==0 || tmp==255 ) {
+		printk (KERN_INFO "ad1816: Chip is not an AD1816 or chip "
+			"is not active (Test 0)\n");
+		goto out_release_region;
+	}
+
+	/* writes to ireg 8 are copied to ireg 9 */
+	ad_write(devc,8,12345); 
+	if (ad_read(devc,9)!=12345) {
+		printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 1)\n");
+		goto out_release_region;
+	}
+  
+	/* writes to ireg 8 are copied to ireg 9 */
+	ad_write(devc,8,54321); 
+	if (ad_read(devc,9)!=54321) {
+		printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 2)\n");
+		goto out_release_region;
+	}
+
+	/* writes to ireg 10 are copied to ireg 11 */
+	ad_write(devc,10,54321); 
+	if (ad_read(devc,11)!=54321) {
+		printk (KERN_INFO "ad1816: Chip is not an AD1816 (Test 3)\n");
+		goto out_release_region;
+	}
+
+	/* writes to ireg 10 are copied to ireg 11 */
+	ad_write(devc,10,12345); 
+	if (ad_read(devc,11)!=12345) {
+		printk (KERN_INFO "ad1816: Chip is not an AD1816 (Test 4)\n");
+		goto out_release_region;
+	}
+
+	/* bit in base +1 cannot be set to 1 */
+	tmp=inb(devc->base+1);
+	outb(0xff,devc->base+1); 
+	if (inb(devc->base+1)!=tmp) {
+		printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 5)\n");
+		goto out_release_region;
+	}
+  
+	printk(KERN_INFO "ad1816: AD1816 (version %d) successfully detected!\n",
+		ad_read(devc,45));
+
+	/* disable all interrupts */
+	ad_write(devc,1,0);     
+
+	/* Clear pending interrupts */
+	outb (0, devc->base+1);	
+
+	/* allocate irq */
+	if (devc->irq < 0 || devc->irq > 15)
+		goto out_release_region;
+	if (request_irq(devc->irq, ad1816_interrupt,0,
+			"SoundPort", devc) < 0)	{
+	        printk(KERN_WARNING "ad1816: IRQ in use\n");
+		goto out_release_region;
+	}
+
+	/* DMA stuff */
+	if (sound_alloc_dma (devc->dma_playback, "Sound System")) {
+		printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n",
+				    devc->dma_playback);
+		goto out_free_irq;
+	}
+	
+	if ( devc->dma_capture >= 0 && 
+	  	devc->dma_capture != devc->dma_playback) {
+		if (sound_alloc_dma(devc->dma_capture,
+				    "Sound System (capture)")) {
+			printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n",
+					    devc->dma_capture);
+			goto out_free_dma;
+		}
+		devc->audio_mode=DMA_AUTOMODE|DMA_DUPLEX;
+	} else {
+	  	printk(KERN_WARNING "ad1816: Only one DMA channel "
+			"available/configured. No duplex operation possible\n");
+		devc->audio_mode=DMA_AUTOMODE;
+	}
+
+	conf_printf2 ("AD1816 audio driver",
+		      devc->base, devc->irq, devc->dma_playback, 
+		      devc->dma_capture);
+
+	/* register device */
+	if ((oss_devno = sound_install_audiodrv (AUDIO_DRIVER_VERSION,
+					      "AD1816 audio driver",
+					      &ad1816_audio_driver,
+					      sizeof (struct audio_driver),
+					      devc->audio_mode,
+					      ad_format_mask,
+					      devc,
+					      devc->dma_playback, 
+					      devc->dma_capture)) < 0) {
+		printk(KERN_WARNING "ad1816: Can't install sound driver\n");
+		goto out_free_dma_2;
+	}
+
+
+	ad_write(devc,32,0x80f0); /* sound system mode */
+	if (options&1) {
+	        ad_write(devc,33,0); /* disable all audiosources for dsp */
+	} else {
+	        ad_write(devc,33,0x03f8); /* enable all audiosources for dsp */
+	}
+	ad_write(devc,4,0x8080);  /* default values for volumes (muted)*/
+	ad_write(devc,5,0x8080);
+	ad_write(devc,6,0x8080);
+	ad_write(devc,7,0x8080);
+	ad_write(devc,15,0x8888);
+	ad_write(devc,16,0x8888);
+	ad_write(devc,17,0x8888);
+	ad_write(devc,18,0x8888);
+	ad_write(devc,19,0xc888); /* +20db mic active */
+	ad_write(devc,14,0x0000); /* Master volume unmuted */
+	ad_write(devc,39,0x009f); /* 3D effect on 0% phone out muted */
+	ad_write(devc,44,0x0080); /* everything on power, 3d enabled for d/a */
+	outb(0x10,devc->base+8); /* set dma mode */
+	outb(0x10,devc->base+9);
+  
+	/* enable capture + playback interrupt */
+	ad_write(devc,1,0xc000); 
+	
+	/* set mixer defaults */
+	ad1816_mixer_reset (devc); 
+  
+	/* register mixer */
+	if ((audio_devs[oss_devno]->mixer_dev=sound_install_mixer(
+				       MIXER_DRIVER_VERSION,
+				       "AD1816 audio driver",
+				       &ad1816_mixer_operations,
+				       sizeof (struct mixer_operations),
+				       devc)) < 0) {
+	  	printk(KERN_WARNING "Can't install mixer\n");
+	}
+	/* make ad1816_info active */
+	nr_ad1816_devs++;
+	printk(KERN_INFO "ad1816: card successfully installed!\n");
+	return 1;
+	/* error handling */
+out_free_dma_2:
+	if (devc->dma_capture >= 0 && devc->dma_capture != devc->dma_playback)
+	        sound_free_dma(devc->dma_capture);
+out_free_dma:
+	sound_free_dma(devc->dma_playback);
+out_free_irq:
+	free_irq(devc->irq, devc);
+out_release_region:
+	release_region(devc->base, 16);
+out:
+	return 0;
+}
+
+static void __exit unload_card(ad1816_info *devc)
+{
+	int  mixer, dev = 0;
+	
+	if (devc != NULL) {
+		printk("ad1816: Unloading card at address 0x%03x\n",devc->base);
+		
+		dev = devc->dev_no;
+		mixer = audio_devs[dev]->mixer_dev;
+
+		/* unreg mixer*/
+		if(mixer>=0) {
+			sound_unload_mixerdev(mixer);
+		}
+		/* unreg audiodev */
+		sound_unload_audiodev(dev);
+		
+		/* free dma channels */
+		if (devc->dma_capture>=0 && 
+		  	devc->dma_capture != devc->dma_playback) {
+			sound_free_dma(devc->dma_capture);
+		}
+		sound_free_dma (devc->dma_playback);
+		/* free irq */
+		free_irq(devc->irq, devc);
+		/* free io */
+		release_region (devc->base, 16);
+#ifdef __ISAPNP__
+		if (devc->pnpdev) {
+		  	pnp_disable_dev(devc->pnpdev);
+			pnp_device_detach(devc->pnpdev);
+		}
+#endif
+		
+	} else
+		printk(KERN_WARNING "ad1816: no device/card specified\n");
+}
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+static int __initdata dma = -1;
+static int __initdata dma2 = -1;
+
+#ifdef __ISAPNP__
+/* use isapnp for configuration */
+static int isapnp	= 1;
+static int isapnpjump;
+module_param(isapnp, bool, 0);
+module_param(isapnpjump, int, 0);
+#endif
+
+module_param(io, int, 0);
+module_param(irq, int, 0);
+module_param(dma, int, 0);
+module_param(dma2, int, 0);
+module_param(ad1816_clockfreq, int, 0);
+module_param(options, int, 0);
+
+#ifdef __ISAPNP__
+static struct {
+	unsigned short card_vendor, card_device;
+	unsigned short vendor;
+	unsigned short function;
+	struct ad1816_data *data;
+} isapnp_ad1816_list[] __initdata = {
+	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7150), 
+		NULL },
+	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7180),
+		NULL },
+	{0}
+};
+
+MODULE_DEVICE_TABLE(isapnp, isapnp_ad1816_list);
+
+
+static void __init ad1816_config_pnp_card(struct pnp_card *card,
+					  unsigned short vendor,
+					  unsigned short function)
+{
+	struct address_info cfg;
+  	struct pnp_dev *card_dev = pnp_find_dev(card, vendor, function, NULL);
+	if (!card_dev) return;
+	if (pnp_device_attach(card_dev) < 0) {
+	  	printk(KERN_WARNING "ad1816: Failed to attach PnP device\n");
+		return;
+	}
+	if (pnp_activate_dev(card_dev) < 0) {
+		printk(KERN_WARNING "ad1816: Failed to activate PnP device\n");
+		pnp_device_detach(card_dev);
+		return;
+	}
+	cfg.io_base = pnp_port_start(card_dev, 2);
+	cfg.irq = pnp_irq(card_dev, 0);
+	cfg.dma = pnp_irq(card_dev, 0);
+	cfg.dma2 = pnp_irq(card_dev, 1);
+	if (!ad1816_init_card(&cfg, card_dev)) {
+	  	pnp_disable_dev(card_dev);
+		pnp_device_detach(card_dev);
+	}
+}
+
+static void __init ad1816_config_pnp_cards(void)
+{
+	int nr_pnp_cfg;
+	int i;
+	
+	/* Count entries in isapnp_ad1816_list */
+	for (nr_pnp_cfg = 0; isapnp_ad1816_list[nr_pnp_cfg].card_vendor != 0; 
+		nr_pnp_cfg++);
+	/* Check and adjust isapnpjump */
+	if( isapnpjump < 0 || isapnpjump >= nr_pnp_cfg) {
+		printk(KERN_WARNING 
+			"ad1816: Valid range for isapnpjump is 0-%d. "
+			"Adjusted to 0.\n", nr_pnp_cfg-1);
+		isapnpjump = 0;
+	}
+	for (i = isapnpjump; isapnp_ad1816_list[i].card_vendor != 0; i++) {
+	 	struct pnp_card *card = NULL;
+		/* iterate over all pnp cards */		
+		while ((card = pnp_find_card(isapnp_ad1816_list[i].card_vendor,
+		              	isapnp_ad1816_list[i].card_device, card))) 
+			ad1816_config_pnp_card(card, 
+				isapnp_ad1816_list[i].vendor,
+				isapnp_ad1816_list[i].function);
+	}
+}
+#endif
+
+/* module initialization */
+static int __init init_ad1816(void)
+{
+	printk(KERN_INFO "ad1816: AD1816 sounddriver "
+			 "Copyright (C) 1998-2003 by Thorsten Knabe and "
+			 "others\n");
+#ifdef AD1816_CLOCK 
+	/* set ad1816_clockfreq if set during compilation */
+	ad1816_clockfreq=AD1816_CLOCK;
+#endif
+	if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) {
+		ad1816_clockfreq=33000;
+	}
+
+#ifdef __ISAPNP__
+	/* configure PnP cards */
+	if(isapnp) ad1816_config_pnp_cards();
+#endif
+	/* configure card by module params */
+	if (io != -1 && irq != -1 && dma != -1) {
+		struct address_info cfg;
+		cfg.io_base = io;
+		cfg.irq = irq;
+		cfg.dma = dma;
+		cfg.dma2 = dma2;
+		ad1816_init_card(&cfg, NULL);
+	}
+	if (nr_ad1816_devs <= 0)
+	  	return -ENODEV;
+	return 0;
+}
+
+/* module cleanup */
+static void __exit cleanup_ad1816 (void)
+{
+	int          i;
+	ad1816_info  *devc = NULL;
+  
+	/* remove any soundcard */
+	for (i = 0;  i < nr_ad1816_devs; i++) {
+		devc = &dev_info[i];
+		unload_card(devc);
+	}     
+	nr_ad1816_devs=0;
+	printk(KERN_INFO "ad1816: driver unloaded!\n");
+}
+
+module_init(init_ad1816);
+module_exit(cleanup_ad1816);
+
+#ifndef MODULE
+/* kernel command line parameter evaluation */
+static int __init setup_ad1816(char *str)
+{
+	/* io, irq, dma, dma2 */
+	int ints[5];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma2	= ints[4];
+	return 1;
+}
+
+__setup("ad1816=", setup_ad1816);
+#endif
+MODULE_LICENSE("GPL");