patch-2.4.4 linux/drivers/sound/cs46xx.c

Next file: linux/drivers/sound/cs46xx_wrapper-24.h
Previous file: linux/drivers/sound/cs461x_image.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/drivers/sound/cs46xx.c linux/drivers/sound/cs46xx.c
@@ -1,7 +1,8 @@
 /*
  *	Crystal SoundFusion CS46xx driver
  *
- *	Copyright 1998-2000 Cirrus Logic Corporation <audio@crystal.cirrus.com>
+ *	Copyright 1998-2001 Cirrus Logic Corporation <pcaudio@crystal.cirrus.com>
+ *						<twoller@crystal.cirrus.com>
  *	Copyright 1999-2000 Jaroslav Kysela <perex@suse.cz>
  *	Copyright 2000 Alan Cox <alan@redhat.com>
  *
@@ -22,7 +23,6 @@
  *	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.
- *
  *	Current maintainers:
  *		Cirrus Logic Corporation, Thomas Woller (tw)
  *			<twoller@crystal.cirrus.com>
@@ -43,17 +43,33 @@
  *			underruns.
  *	20001201-tw	add resyncing of swptr on underruns.
  *	20001205-tw-nf	fixed GETOSPACE ioctl() after open()
- *
+ *	20010113-tw	patch from Hans Grobler general cleanup.
+ *	20010117-tw	2.4.0 pci cleanup, wrapper code for 2.2.16-2.4.0
+ *	20010118-tw	basic PM support for 2.2.16+ and 2.4.0/2.4.2.
+ *	20010228-dh	patch from David Huggins - cs_update_ptr recursion.
  *
  *	Status:
  *	Playback/Capture supported from 8k-48k.
  *	16Bit Signed LE & 8Bit Unsigned, with Mono or Stereo supported.
+ *
+ *	APM/PM - 2.2.x APM is enabled and functioning fine. APM can also
+ *	be enabled for 2.4.x by modifying the CS46XX_ACPI_SUPPORT macro
+ *	definition.
+ *
+ *      Hercules Game Pro XP - the EGPIO2 pin controls the external Amp,
+ *	but the static image can not modify the EGPIO pins, so we can not
+ *	turn on the external amp.
+ *
+ *	VTB Santa Cruz - the GPIO7/GPIO8 on the Secondary Codec control
+ *	the external amplifier for the "back" speakers, since we do not
+ *	support the secondary codec then this external amp is also not
+ *	turned on.
  */
  
-#include <linux/module.h>
+#include <linux/list.h>
 #include <linux/version.h>
+#include <linux/module.h>
 #include <linux/string.h>
-#include <linux/ctype.h>
 #include <linux/ioport.h>
 #include <linux/sched.h>
 #include <linux/delay.h>
@@ -61,22 +77,21 @@
 #include <linux/slab.h>
 #include <linux/soundcard.h>
 #include <linux/pci.h>
-#ifdef CS46XX_PM
-#include <linux/pm.h>
-#endif
+#include <linux/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <linux/init.h>
 #include <linux/poll.h>
-#include <linux/spinlock.h>
-#include <linux/ac97_codec.h>
+#include <linux/smp_lock.h>
 #include <linux/wrapper.h>
 #include <asm/uaccess.h>
 #include <asm/hardirq.h>
+#include <linux/ac97_codec.h>
+#include "cs46xxpm-24.h"
+#include "cs46xx_wrapper-24.h"
 
 #include "cs461x.h"
 
-
 /* MIDI buffer sizes */
 #define CS_MIDIINBUF  500
 #define CS_MIDIOUTBUF 500
@@ -90,6 +105,11 @@
 
 #define CS_TYPE_ADC	1
 #define CS_TYPE_DAC	2
+
+#define CS_TRUE 	1
+#define CS_FALSE 	0
+
+#define CS_DBGBREAKPOINT {__asm__("INT $3");}
 /*
  *	CS461x definitions
  */
@@ -135,14 +155,22 @@
 #define CS_RELEASE	0x00000800		/* all release functions in the driver */
 #define CS_PARMS	0x00001000		/* functional and operational parameters */
 #define CS_IOCTL	0x00002000		/* ioctl (non-mixer) */
+#define CS_PM		0x00004000		/* PM */
 #define CS_TMP		0x10000000		/* tmp debug mask bit */
 
+#define CS_IOCTL_CMD_SUSPEND	0x1	// suspend
+#define CS_IOCTL_CMD_RESUME	0x2	// resume
+
 #if CSDEBUG
 static unsigned long cs_debuglevel=1;			/* levels range from 1-9 */
 MODULE_PARM(cs_debuglevel, "i");
 static unsigned long cs_debugmask=CS_INIT | CS_ERROR;	/* use CS_DBGOUT with various mask values */
 MODULE_PARM(cs_debugmask, "i");
 #endif
+static unsigned long initdelay=700;  /* PM delay in millisecs */
+MODULE_PARM(initdelay, "i");
+static unsigned long powerdown=1;  /* turn on/off powerdown processing in driver */
+MODULE_PARM(powerdown, "i");
 #define DMABUF_DEFAULTORDER 3
 static unsigned long defaultorder=DMABUF_DEFAULTORDER;
 MODULE_PARM(defaultorder, "i");
@@ -153,7 +181,6 @@
 MODULE_PARM(thinkpad, "i");
 
 /* An instance of the 4610 channel */
-
 struct cs_channel 
 {
 	int used;
@@ -161,7 +188,16 @@
 	void *state;
 };
 
-#define DRIVER_VERSION "1.10"
+#define CS46XX_MAJOR_VERSION "1"
+#define CS46XX_MINOR_VERSION "22"
+
+#ifdef __ia64__
+#define CS46XX_ARCH	     	"64"	//architecture key
+#else
+#define CS46XX_ARCH	     	"32"	//architecture key
+#endif
+
+struct list_head cs46xx_devs = { &cs46xx_devs, &cs46xx_devs };
 
 /* magic numbers to protect our data structures */
 #define CS_CARD_MAGIC		0x43525553 /* "CRUS" */
@@ -208,7 +244,8 @@
 		unsigned divisor;
 		unsigned type;
 		void *tmpbuff;			/* tmp buffer for sample conversions */
-		dma_addr_t dma_handle_tmpbuff;
+		dma_addr_t dmaaddr;
+		dma_addr_t dmaaddr_tmpbuff;
 		unsigned buforder_tmpbuff;	/* Log base 2 of size in bytes.. */
 
 		/* our buffer acts like a circular ring */
@@ -239,7 +276,6 @@
 	} dmabuf;
 };
 
-
 struct cs_card {
 	struct cs_channel channel[2];
 	unsigned int magic;
@@ -253,6 +289,7 @@
 
 	/* PCI device stuff */
 	struct pci_dev * pci_dev;
+	struct list_head list;
 
 	unsigned int pctl, cctl;	/* Hardware DMA flag sets */
 
@@ -268,6 +305,7 @@
 	
 	int amplifier;			/* Amplifier control */
 	void (*amplifier_ctrl)(struct cs_card *, int);
+	void (*amp_init)(struct cs_card *);
 	
 	int active;			/* Active clocking */
 	void (*active_ctrl)(struct cs_card *, int);
@@ -309,15 +347,17 @@
 		mode_t open_mode;
 		struct semaphore open_sem;
 	} midi;
+	struct cs46xx_pm pm;
 };
 
-static struct cs_card *devs;
-
 static int cs_open_mixdev(struct inode *inode, struct file *file);
 static int cs_release_mixdev(struct inode *inode, struct file *file);
 static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
 				unsigned long arg);
 static loff_t cs_llseek(struct file *file, loff_t offset, int origin);
+static int cs_hardware_init(struct cs_card *card);
+static int cs46xx_powerup(struct cs_card *card, unsigned int type);
+static int cs461x_powerdown(struct cs_card *card, unsigned int type);
 
 static inline unsigned ld2(unsigned int x)
 {
@@ -352,13 +392,9 @@
 #define SOUND_MIXER_CS_SETDBGLEVEL 	_SIOWR('M',121, int)
 #define SOUND_MIXER_CS_GETDBGMASK 	_SIOWR('M',122, int)
 #define SOUND_MIXER_CS_SETDBGMASK 	_SIOWR('M',123, int)
+#define SOUND_MIXER_CS_APM	 	_SIOWR('M',124, int)
 
-#define SNDCTL_DSP_CS_GETDBGLEVEL 	_SIOWR('P', 50, int)
-#define SNDCTL_DSP_CS_SETDBGLEVEL 	_SIOWR('P', 51, int)
-#define SNDCTL_DSP_CS_GETDBGMASK 	_SIOWR('P', 52, int)
-#define SNDCTL_DSP_CS_SETDBGMASK 	_SIOWR('P', 53, int)
-
-static void printioctl(unsigned int x)
+void printioctl(unsigned int x)
 {
     unsigned int i;
     unsigned char vidx;
@@ -477,18 +513,6 @@
         case SOUND_PCM_READ_FILTER:
 		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER: ") );
 		break;
-        case SNDCTL_DSP_CS_GETDBGMASK:
-		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CS_GETDBGMASK: ") );
-		break;
-        case SNDCTL_DSP_CS_GETDBGLEVEL:
-		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CS_GETDBGLEVEL: ") );
-		break;
-        case SNDCTL_DSP_CS_SETDBGMASK:
-		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CS_SETDBGMASK: ") );
-		break;
-        case SNDCTL_DSP_CS_SETDBGLEVEL:
-		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CS_SETDBGLEVEL: ") );
-		break;
 
         case SOUND_MIXER_PRIVATE1:
 		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1: ") );
@@ -650,6 +674,8 @@
 	unsigned int tmp1, tmp2;
 	unsigned int phiIncr;
 	unsigned int correctionPerGOF, correctionPerSec;
+	unsigned long flags;
+
 	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_dac_rate()+ %d\n",rate) );
 
 	/*
@@ -685,11 +711,11 @@
 	 *  Fill in the SampleRateConverter control block.
 	 */
 	 
-	spin_lock_irq(&state->card->lock);
+	spin_lock_irqsave(&state->card->lock, flags);
 	cs461x_poke(state->card, BA1_PSRC,
 	  ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
 	cs461x_poke(state->card, BA1_PPI, phiIncr);
-	spin_unlock_irq(&state->card->lock);
+	spin_unlock_irqrestore(&state->card->lock, flags);
 	dmabuf->rate = rate;
 	
 	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_dac_rate()- %d\n",rate) );
@@ -704,6 +730,7 @@
 	unsigned int phiIncr, coeffIncr, tmp1, tmp2;
 	unsigned int correctionPerGOF, correctionPerSec, initialDelay;
 	unsigned int frameGroupLength, cnt;
+	unsigned long flags;
 	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_adc_rate()+ %d\n",rate) );
 
 	/*
@@ -766,14 +793,14 @@
 	/*
 	 *  Fill in the VariDecimate control block.
 	 */
-	spin_lock_irq(&card->lock);
+	spin_lock_irqsave(&card->lock, flags);
 	cs461x_poke(card, BA1_CSRC,
 		((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
 	cs461x_poke(card, BA1_CCI, coeffIncr);
 	cs461x_poke(card, BA1_CD,
 		(((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
 	cs461x_poke(card, BA1_CPI, phiIncr);
-	spin_unlock_irq(&card->lock);
+	spin_unlock_irqrestore(&card->lock, flags);
 
 	/*
 	 *  Figure out the frame group length for the write back task.  Basically,
@@ -796,13 +823,13 @@
 	/*
 	 * Fill in the WriteBack control block.
 	 */
-	spin_lock_irq(&card->lock);
+	spin_lock_irqsave(&card->lock, flags);
 	cs461x_poke(card, BA1_CFG1, frameGroupLength);
 	cs461x_poke(card, BA1_CFG2, (0x00800000 | frameGroupLength));
 	cs461x_poke(card, BA1_CCST, 0x0000FFFF);
 	cs461x_poke(card, BA1_CSPB, ((65536 * rate) / 24000));
 	cs461x_poke(card, (BA1_CSPB + 4), 0x0000FFFF);
-	spin_unlock_irq(&card->lock);
+	spin_unlock_irqrestore(&card->lock, flags);
 	dmabuf->rate = rate;
 	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_adc_rate()- %d\n",rate) );
 	return rate;
@@ -904,7 +931,7 @@
 /* get current playback/recording dma buffer pointer (byte offset from LBA),
    called with spinlock held! */
    
-static inline unsigned cs_get_dma_addr(struct cs_state *state)
+extern __inline__ unsigned cs_get_dma_addr(struct cs_state *state)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
 	u32 offset;
@@ -938,18 +965,20 @@
 
 static void resync_dma_ptrs(struct cs_state *state)
 {
-	struct dmabuf *dmabuf = &state->dmabuf;
-	int offset;
+	struct dmabuf *dmabuf;
 	
 	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()+ \n") );
-	offset = 0;
-	dmabuf->hwptr=dmabuf->swptr = 0;
-	dmabuf->pringbuf = 0;
+	if(state)
+	{
+		dmabuf = &state->dmabuf;
+		dmabuf->hwptr=dmabuf->swptr = 0;
+		dmabuf->pringbuf = 0;
+	}
 	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()- \n") );
 }
 	
 /* Stop recording (lock held) */
-static inline void __stop_adc(struct cs_state *state)
+extern __inline__ void __stop_adc(struct cs_state *state)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
 	struct cs_card *card = state->card;
@@ -983,7 +1012,9 @@
 	spin_lock_irqsave(&card->lock, flags);
 	if (!(dmabuf->enable & ADC_RUNNING) && 
 	     ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) 
-	       && dmabuf->ready)) 
+	       && dmabuf->ready) && 
+	       ((card->pm.flags & CS46XX_PM_IDLE) || 
+	        (card->pm.flags & CS46XX_PM_RESUMED)) )
 	{
 		dmabuf->enable |= ADC_RUNNING;
 		cs_set_divisor(dmabuf);
@@ -1032,7 +1063,10 @@
 	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: start_dac()+ \n") );
 	spin_lock_irqsave(&card->lock, flags);
 	if (!(dmabuf->enable & DAC_RUNNING) && 
-	    ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready)) {
+	    ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) &&
+	       ((card->pm.flags & CS46XX_PM_IDLE) || 
+	        (card->pm.flags & CS46XX_PM_RESUMED)) )
+	{
 		dmabuf->enable |= DAC_RUNNING;
 		tmp = cs461x_peek(card, BA1_PCTL);
 		tmp &= 0xFFFF;
@@ -1054,54 +1088,82 @@
  */
 static int alloc_dmabuf(struct cs_state *state)
 {
+
+	struct cs_card *card=state->card;
 	struct dmabuf *dmabuf = &state->dmabuf;
 	void *rawbuf = NULL;
 	void *tmpbuff = NULL;
 	int order;
-	struct page *page, *pend;
-
-	/* alloc as big a chunk as we can */
-	for (order = defaultorder; order >= DMABUF_MINORDER; order--)
-		if((rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order)))
-			break;
+	struct page *map, *mapend;
+	unsigned long df;
+	
+	dmabuf->ready  = dmabuf->mapped = 0;
+	dmabuf->SGok = 0;
+/*
+* check for order within limits, but do not overwrite value.
+*/
+	if((defaultorder > 1) && (defaultorder < 12))
+		df = defaultorder;
+	else
+		df = 2;	
 
-	if (!rawbuf)
+	for (order = df; order >= DMABUF_MINORDER; order--)
+		if ( (rawbuf = (void *) pci_alloc_consistent(
+			card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr)))
+			    break;
+	if (!rawbuf) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+			"cs46xx: alloc_dmabuf(): unable to allocate rawbuf\n"));
 		return -ENOMEM;
-
+	}
 	dmabuf->buforder = order;
 	dmabuf->rawbuf = rawbuf;
+	// Now mark the pages as reserved; otherwise the 
+	// remap_page_range() in cs46xx_mmap doesn't work.
+	// 1. get index to last page in mem_map array for rawbuf.
+	mapend = virt_to_page(dmabuf->rawbuf + 
+		(PAGE_SIZE << dmabuf->buforder) - 1);
+
+	// 2. mark each physical page in range as 'reserved'.
+	for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++)
+		cs4x_mem_map_reserve(map);
 
-	/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
-	pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);
-	for (page = virt_to_page(rawbuf); page <= pend; page++)
-		mem_map_reserve(page);
-
-	CS_DBGOUT(CS_PARMS, 9, printk("cs461x: allocated %ld (order = %d) bytes at %p\n",
+	CS_DBGOUT(CS_PARMS, 9, printk("cs46xx: alloc_dmabuf(): allocated %ld (order = %d) bytes at %p\n",
 	       PAGE_SIZE << order, order, rawbuf) );
+
+/*
+*  only allocate the conversion buffer for the ADC
+*/
+	if(dmabuf->type == CS_TYPE_DAC)
+	{
+		dmabuf->tmpbuff = NULL;
+		dmabuf->buforder_tmpbuff = 0;
+		return 0;
+	}
 /*
  * now the temp buffer for 16/8 conversions
  */
-	for (order = defaultorder; order >= DMABUF_MINORDER; order--)
-		if((tmpbuff = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order)))
-			break;
+
+	tmpbuff = (void *) pci_alloc_consistent(
+		card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr_tmpbuff);
+
 	if (!tmpbuff)
 		return -ENOMEM;
-	CS_DBGOUT(CS_PARMS, 9, printk("cs461x: allocated %ld (order = %d) bytes at %p\n",
+	CS_DBGOUT(CS_PARMS, 9, printk("cs46xx: allocated %ld (order = %d) bytes at %p\n",
 	       PAGE_SIZE << order, order, tmpbuff) );
 
 	dmabuf->tmpbuff = tmpbuff;
 	dmabuf->buforder_tmpbuff = order;
 	
-	/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
-	pend = virt_to_page(tmpbuff + (PAGE_SIZE << order) - 1);
-	for (page = virt_to_page(tmpbuff); page <= pend; page++)
-		mem_map_reserve(page);
-
-	CS_DBGOUT(CS_PARMS, 9, printk("cs461x: allocated %ld (order = %d) bytes at %p\n",
-	       PAGE_SIZE << order, order, tmpbuff) );
-
-	dmabuf->ready  = dmabuf->mapped = 0;
-	dmabuf->SGok = 0;
+	// Now mark the pages as reserved; otherwise the 
+	// remap_page_range() in cs46xx_mmap doesn't work.
+	// 1. get index to last page in mem_map array for rawbuf.
+	mapend = virt_to_page(dmabuf->tmpbuff + 
+		(PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1);
+
+	// 2. mark each physical page in range as 'reserved'.
+	for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++)
+		cs4x_mem_map_reserve(map);
 	return 0;
 }
 
@@ -1109,24 +1171,24 @@
 static void dealloc_dmabuf(struct cs_state *state)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	struct page *page, *pend;
+	struct page *map, *mapend;
 
 	if (dmabuf->rawbuf) {
-		pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);
-		for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++)
-			mem_map_unreserve(page);
-		pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder,
-				    dmabuf->rawbuf, dmabuf->dma_handle);
+		// Undo prog_dmabuf()'s marking the pages as reserved 
+		mapend = virt_to_page(dmabuf->rawbuf + 
+				(PAGE_SIZE << dmabuf->buforder) - 1);
+		for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++)
+			cs4x_mem_map_unreserve(map);
+		free_dmabuf(state->card, dmabuf);
 	}
-	dmabuf->rawbuf = NULL;
 
 	if (dmabuf->tmpbuff) {
-		/* undo marking the pages as reserved */
-		pend = virt_to_page(dmabuf->tmpbuff + (PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1);
-		for (page = virt_to_page(dmabuf->tmpbuff); page <= pend; page++)
-			mem_map_unreserve(page);
-		pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder_tmpbuff,
-				    dmabuf->tmpbuff, dmabuf->dma_handle_tmpbuff);
+		// Undo prog_dmabuf()'s marking the pages as reserved 
+		mapend = virt_to_page(dmabuf->tmpbuff +
+				(PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1);
+		for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++)
+			cs4x_mem_map_unreserve(map);
+		free_dmabuf2(state->card, dmabuf);
 	}
 
 	dmabuf->rawbuf = NULL;
@@ -1197,7 +1259,7 @@
 		dmabuf->ready = 1;
 
 		CS_DBGOUT(CS_PARMS, 4, printk(
-			"cs461x: prog_dmabuf(): CAPTURE rate=%d fmt=0x%x numfrag=%d "
+			"cs46xx: prog_dmabuf(): CAPTURE rate=%d fmt=0x%x numfrag=%d "
 			"fragsize=%d dmasize=%d\n",
 			    dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
 			    dmabuf->fragsize, dmabuf->dmasize) );
@@ -1300,7 +1362,7 @@
 		dmabuf->ready = 1;
 
 		CS_DBGOUT(CS_PARMS, 4, printk(
-			"cs461x: prog_dmabuf(): PLAYBACK rate=%d fmt=0x%x numfrag=%d "
+			"cs46xx: prog_dmabuf(): PLAYBACK rate=%d fmt=0x%x numfrag=%d "
 			"fragsize=%d dmasize=%d\n",
 			    dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
 			    dmabuf->fragsize, dmabuf->dmasize) );
@@ -1358,7 +1420,7 @@
 		tmo += (2048*HZ)/dmabuf->rate;
 		
 		if (!schedule_timeout(tmo ? tmo : 1) && tmo){
-			printk(KERN_ERR "cs461x: drain_dac, dma timeout? %d\n", count);
+			printk(KERN_ERR "cs46xx: drain_dac, dma timeout? %d\n", count);
 			break;
 		}
 	}
@@ -1372,9 +1434,8 @@
 
 
 /* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */
-static void cs_update_ptr(void)
+static void cs_update_ptr(struct cs_card *card, int wake)
 {
-	struct cs_card *card=devs;
 	struct cs_state *state;
 	struct dmabuf *dmabuf;
 	unsigned hwptr;
@@ -1401,11 +1462,11 @@
 
 			if(dmabuf->mapped)
 			{
-				if (dmabuf->count >= (signed)dmabuf->fragsize)
+				if (wake && dmabuf->count >= (signed)dmabuf->fragsize)
 					wake_up(&dmabuf->wait);
 			} else 
 			{
-				if (dmabuf->count > 0)
+				if (wake && dmabuf->count > 0)
 					wake_up(&dmabuf->wait);
 			}
 		}
@@ -1431,7 +1492,7 @@
 			dmabuf->total_bytes += diff;
 			if (dmabuf->mapped) {
 				dmabuf->count += diff;
-				if (dmabuf->count >= (signed)dmabuf->fragsize)
+				if (wake && dmabuf->count >= (signed)dmabuf->fragsize)
 					wake_up(&dmabuf->wait);
 				/*
 				 * other drivers use fragsize, but don't see any sense
@@ -1475,7 +1536,7 @@
 					dmabuf->count = 0;
 					dmabuf->error++;
 				}
-				if (dmabuf->count < (signed)dmabuf->dmasize/2)
+				if (wake && dmabuf->count < (signed)dmabuf->dmasize/2)
 					wake_up(&dmabuf->wait);
 			}
 		}
@@ -1544,7 +1605,7 @@
 	{
 		CS_DBGOUT(CS_INTERRUPT, 8, printk(
 			"cs46xx: cs_interrupt() interrupt bit(s) set (0x%x)\n",status));
-		cs_update_ptr();
+		cs_update_ptr(card, CS_TRUE);
 	}
 
         if( status & HISR_MIDI )
@@ -1681,12 +1742,26 @@
 static int cs_midi_open(struct inode *inode, struct file *file)
 {
         int minor = MINOR(inode->i_rdev);
-        struct cs_card *card = devs;
+        struct cs_card *card=NULL;
         unsigned long flags;
-        while (card && card->dev_midi != minor)
-                card = card->next;
-        if (!card)
-                return -ENODEV;
+	struct list_head *entry;
+
+	list_for_each(entry, &cs46xx_devs)
+	{
+		card = list_entry(entry, struct cs_card, list);
+		if (card->dev_midi == minor)
+			break;
+	}
+
+	if (entry == &cs46xx_devs)
+		return -ENODEV;
+	if (!card)
+	{
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
+			"cs46xx: cs46xx_midi_open(): Error - unable to find card struct\n"));
+		return -ENODEV;
+	}
+
         file->private_data = card;
         /* wait for device to become free */
         down(&card->midi.open_sem);
@@ -1765,13 +1840,13 @@
  *   Midi file operations struct.
  */
 static /*const*/ struct file_operations cs_midi_fops = {
-	owner:		THIS_MODULE,
-        llseek:		cs_llseek,
-        read:		cs_midi_read,
-        write:		cs_midi_write,
-        poll:		cs_midi_poll,
-        open:		cs_midi_open,
-        release:	cs_midi_release,
+	CS_OWNER	CS_THIS_MODULE
+	llseek:		cs_llseek,
+	read:		cs_midi_read,
+	write:		cs_midi_write,
+	poll:		cs_midi_poll,
+	open:		cs_midi_open,
+	release:	cs_midi_release,
 };
 
 static loff_t cs_llseek(struct file *file, loff_t offset, int origin)
@@ -1806,7 +1881,7 @@
     s16 *psDst=(s16 *)dst;
     u8 *pucDst=(u8 *)dst;
 
-    CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs4281: CopySamples()+ ") );
+    CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: CopySamples()+ ") );
     CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
 	" dst=0x%x src=0x%x count=%d fmt=0x%x\n",
 	(unsigned)dst,(unsigned)src,(unsigned)count,(unsigned)fmt) );
@@ -1912,14 +1987,14 @@
         if (copy_to_user(dest, src, cnt))
 	{
 		CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR 
-			"cs4281: cs_copy_to_user()- fault dest=0x%x src=0x%x cnt=%d\n",
+			"cs46xx: cs_copy_to_user()- fault dest=0x%x src=0x%x cnt=%d\n",
 				(unsigned)dest,(unsigned)src,cnt) );
 		*copied = 0;
 		return -EFAULT;
 	}
 	*copied = cnt;
 	CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO 
-		"cs4281: cs_copy_to_user()- copied bytes is %d \n",cnt) );
+		"cs46xx: cs_copy_to_user()- copied bytes is %d \n",cnt) );
 	return 0;
 }
 
@@ -1927,7 +2002,7 @@
    the user's buffer.  it is filled by the dma machine and drained by this loop. */
 static ssize_t cs_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
-	struct cs_card *card=devs;
+	struct cs_card *card = (struct cs_card *) file->private_data;
 	struct cs_state *state;
 	DECLARE_WAITQUEUE(wait, current);
 	struct dmabuf *dmabuf;
@@ -1937,7 +2012,8 @@
 	int cnt;
 	unsigned copied=0;
 
-	CS_DBGOUT(CS_WAVE_READ, 4, printk("cs461x: cs_read()+ %d\n",count) );
+	CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, 
+		printk("cs46xx: cs_read()+ %d\n",count) );
 	state = (struct cs_state *)card->states[0];
 	if(!state)
 		return -ENODEV;
@@ -1951,9 +2027,18 @@
 		return ret;
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
+	ret = 0;
 
 	add_wait_queue(&state->dmabuf.wait, &wait);
 	while (count > 0) {
+		while(!(card->pm.flags & CS46XX_PM_IDLE))
+		{
+			schedule();
+			if (signal_pending(current)) {
+				ret = ret ? ret : -ERESTARTSYS;
+				break;
+			}
+		}
 		spin_lock_irqsave(&state->card->lock, flags);
 		swptr = dmabuf->swptr;
 		cnt = dmabuf->dmasize - swptr;
@@ -1978,7 +2063,7 @@
 				ret = ret ? ret : -ERESTARTSYS;
 				break;
 			}
-			continue;
+ 			continue;
 		}
 
 		CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO 
@@ -2005,7 +2090,8 @@
 	}
 	remove_wait_queue(&state->dmabuf.wait, &wait);
 	set_current_state(TASK_RUNNING);
-	CS_DBGOUT(CS_WAVE_READ, 4, printk("cs461x: cs_read()- %d\n",ret) );
+	CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, 
+		printk("cs46xx: cs_read()- %d\n",ret) );
 	return ret;
 }
 
@@ -2013,17 +2099,17 @@
    the soundcard.  it is drained by the dma machine and filled by this loop. */
 static ssize_t cs_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
-	struct cs_card *card=devs;
+	struct cs_card *card = (struct cs_card *) file->private_data;
 	struct cs_state *state;
 	DECLARE_WAITQUEUE(wait, current);
 	struct dmabuf *dmabuf;
-	ssize_t ret = 0;
+	ssize_t ret;
 	unsigned long flags;
 	unsigned swptr;
 	int cnt;
 
 	CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 4,
-		printk("cs461x: cs_write called, count = %d\n", count) );
+		printk("cs46xx: cs_write called, count = %d\n", count) );
 	state = (struct cs_state *)card->states[1];
 	if(!state)
 		return -ENODEV;
@@ -2038,7 +2124,20 @@
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
 	add_wait_queue(&state->dmabuf.wait, &wait);
+	ret = 0;
+/*
+* Start the loop to read from the user's buffer and write to the dma buffer.
+* check for PM events and underrun/overrun in the loop.
+*/
 	while (count > 0) {
+		while(!(card->pm.flags & CS46XX_PM_IDLE))
+		{
+			schedule();
+			if (signal_pending(current)) {
+				ret = ret ? ret : -ERESTARTSYS;
+				break;
+			}
+		}
 		spin_lock_irqsave(&state->card->lock, flags);
 		if (dmabuf->count < 0) {
 			/* buffer underrun, we are recovering from sleep_on_timeout,
@@ -2052,6 +2151,7 @@
 			dmabuf->hwptr = cs_get_dma_addr(state);
 			dmabuf->swptr = dmabuf->hwptr;
 		}
+
 		swptr = dmabuf->swptr;
 		cnt = dmabuf->dmasize - swptr;
 		if (dmabuf->count + cnt > dmabuf->dmasize)
@@ -2081,10 +2181,8 @@
 			if (!ret) ret = -EFAULT;
 			return ret;
 		}
-
-		swptr = (swptr + cnt) % dmabuf->dmasize;
-
 		spin_lock_irqsave(&state->card->lock, flags);
+		swptr = (swptr + cnt) % dmabuf->dmasize;
 		dmabuf->swptr = swptr;
 		dmabuf->count += cnt;
 		if(dmabuf->count > dmabuf->dmasize)
@@ -2143,7 +2241,7 @@
 	}
 
 	spin_lock_irqsave(&card->lock, flags);
-	cs_update_ptr();
+	cs_update_ptr(card, CS_FALSE);
 	if (file->f_mode & FMODE_READ) {
 		state = card->states[0];
 		if(state)
@@ -2170,7 +2268,8 @@
 	}
 	spin_unlock_irqrestore(&card->lock, flags);
 
-	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()- \n"));
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()- (0x%x) \n",
+		mask));
 	return mask;
 }
 
@@ -2184,7 +2283,7 @@
  
 static int cs_mmap(struct file *file, struct vm_area_struct *vma)
 {
-	struct cs_card *card=devs;
+	struct cs_card *card = (struct cs_card *)file->private_data;
 	struct cs_state *state;
 	struct dmabuf *dmabuf;
 	int ret;
@@ -2199,7 +2298,7 @@
 		if(state)
 		{
 			CS_DBGOUT(CS_OPEN, 2, printk(
-			  "cs46xx: cs_mmap() VM_WRITE - state TRUE prog_dmabuf DAC\n") );
+			  "cs46xx: cs_mmap() VM_WRITE - state CS_TRUE prog_dmabuf DAC\n") );
 			if ((ret = prog_dmabuf(state)) != 0)
 				return ret;
 		}
@@ -2208,7 +2307,7 @@
 		if(state)
 		{
 			CS_DBGOUT(CS_OPEN, 2, printk(
-			  "cs46xx: cs_mmap() VM_READ - state TRUE prog_dmabuf ADC\n") );
+			  "cs46xx: cs_mmap() VM_READ - state CS_TRUE prog_dmabuf ADC\n") );
 			if ((ret = prog_dmabuf(state)) != 0)
 				return ret;
 		}
@@ -2231,7 +2330,7 @@
 		return -EINVAL;
 
 	dmabuf = &state->dmabuf;
-	if (vma->vm_pgoff != 0)
+	if (cs4x_pgoff(vma) != 0)
 		return -EINVAL;
 	size = vma->vm_end - vma->vm_start;
 
@@ -2617,7 +2716,7 @@
 		{
 			dmabuf = &state->dmabuf;
 			spin_lock_irqsave(&state->card->lock, flags);
-			cs_update_ptr();
+			cs_update_ptr(card, CS_TRUE);
 			abinfo.fragsize = dmabuf->fragsize;
 			abinfo.fragstotal = dmabuf->numfrag;
 		/*
@@ -2640,7 +2739,7 @@
 		{
 			dmabuf = &state->dmabuf;
 			spin_lock_irqsave(&state->card->lock, flags);
-			cs_update_ptr();
+			cs_update_ptr(card, CS_TRUE);
 			abinfo.fragsize = dmabuf->fragsize/dmabuf->divisor;
 			abinfo.bytes = dmabuf->count/dmabuf->divisor;
 			abinfo.fragstotal = dmabuf->numfrag;
@@ -2721,7 +2820,7 @@
 		{
 			dmabuf = &state->dmabuf;
 			spin_lock_irqsave(&state->card->lock, flags);
-			cs_update_ptr();
+			cs_update_ptr(card, CS_TRUE);
 			cinfo.bytes = dmabuf->total_bytes/dmabuf->divisor;
 			cinfo.blocks = dmabuf->count/dmabuf->divisor >> dmabuf->fragshift;
 			cinfo.ptr = dmabuf->hwptr/dmabuf->divisor;
@@ -2736,7 +2835,7 @@
 		{
 			dmabuf = &state->dmabuf;
 			spin_lock_irqsave(&state->card->lock, flags);
-			cs_update_ptr();
+			cs_update_ptr(card, CS_TRUE);
 			cinfo.bytes = dmabuf->total_bytes;
 			if (dmabuf->mapped)
 			{
@@ -2772,7 +2871,7 @@
 		{
 			dmabuf = &state->dmabuf;
 			spin_lock_irqsave(&state->card->lock, flags);
-			cs_update_ptr();
+			cs_update_ptr(card, CS_TRUE);
 			val = dmabuf->count;
 			spin_unlock_irqrestore(&state->card->lock, flags);
 		}
@@ -2856,10 +2955,12 @@
 	}
 }
 
-
-
 /*
- *	Untested
+ *	For the VTB Santa Cruz card, the Secondary Codec must have the 
+ *	GPIO pins 7 and 8 manipulated, not the Primary codec.
+ *	Currently, only the primary codec is supported, so the following
+ *	code will not function.  Additionally, slot 12 must be setup
+ *	to allow proper output for 7 and 8 to occur (trw).
  */
  
 static void amp_voyetra_4294(struct cs_card *card, int change)
@@ -2897,9 +2998,9 @@
 	u16 control;
 	u8 pp;
 	unsigned long port;
-	int old=card->amplifier;
+	int old=card->active;
 	
-	card->amplifier+=change;
+	card->active+=change;
 	
 	acpi_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
 	if(acpi_dev == NULL)
@@ -2913,24 +3014,55 @@
 	control=inw(port+0x10);
 
 	/* Flip CLKRUN off while running */
-	if(!card->amplifier && old)
+	if(!card->active && old)
+	{
+		CS_DBGOUT(CS_PARMS , 9, printk(
+			"cs46xx: clkrun() enable clkrun - change=%d active=%d\n",
+				change,card->active));
 		outw(control|0x2000, port+0x10);
-	else if(card->amplifier && !old)
+	}
+	else 
+	{
+	/*
+	* sometimes on a resume the bit is set, so always reset the bit.
+	*/
+		CS_DBGOUT(CS_PARMS , 9, printk(
+			"cs46xx: clkrun() disable clkrun - change=%d active=%d\n",
+				change,card->active));
 		outw(control&~0x2000, port+0x10);
+	}
 }
 
 	
 static int cs_open(struct inode *inode, struct file *file)
 {
-	struct cs_card *card = devs;
+	struct cs_card *card = (struct cs_card *)file->private_data;
 	struct cs_state *state = NULL;
 	struct dmabuf *dmabuf = NULL;
+	struct list_head *entry;
+        int minor = MINOR(inode->i_rdev);
 	int ret=0;
+	unsigned int tmp;
 
 	CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()+ file=0x%x %s %s\n",
 		(unsigned)file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "",
 		file->f_mode & FMODE_READ ? "FMODE_READ" : "") );
 
+	list_for_each(entry, &cs46xx_devs)
+	{
+		card = list_entry(entry, struct cs_card, list);
+
+		if (!((card->dev_audio ^ minor) & ~0xf))
+			break;
+	}
+	if (entry == &cs46xx_devs)
+		return -ENODEV;
+	if (!card) {
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
+			"cs46xx: cs_open(): Error - unable to find audio card struct\n"));
+		return -ENODEV;
+	}
+
 	/*
 	 * hardcode state[0] for capture, [1] for playback
 	 */
@@ -2971,6 +3103,13 @@
 		state->card->active_ctrl(state->card,1);
 		state->card->amplifier_ctrl(state->card,1);
 		
+		if( (tmp = cs46xx_powerup(card, CS_POWER_ADC)) )
+		{
+			CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+				"cs46xx: cs46xx_powerup of ADC failed (0x%x)\n",tmp) );
+			return -EIO;
+		}
+
 		dmabuf->channel->state = state;
 		/* initialize the virtual channel */
 		state->virt = 0;
@@ -3034,6 +3173,13 @@
 		state->card = card;
 		state->card->active_ctrl(state->card,1);
 		state->card->amplifier_ctrl(state->card,1);
+
+		if( (tmp = cs46xx_powerup(card, CS_POWER_DAC)) )
+		{
+			CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+				"cs46xx: cs46xx_powerup of DAC failed (0x%x)\n",tmp) );
+			return -EIO;
+		}
 		
 		dmabuf->channel->state = state;
 		/* initialize the virtual channel */
@@ -3074,6 +3220,7 @@
 	struct cs_card *card = (struct cs_card *)file->private_data;
 	struct dmabuf *dmabuf;
 	struct cs_state *state;
+	unsigned int tmp;
 	CS_DBGOUT(CS_RELEASE | CS_FUNCTION, 2, printk("cs46xx: cs_release()+ file=0x%x %s %s\n",
 		(unsigned)file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "",
 		file->f_mode & FMODE_READ ? "FMODE_READ" : "") );
@@ -3103,6 +3250,13 @@
 			state->card->states[state->virt] = NULL;
 			state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
 
+			if( (tmp = cs461x_powerdown(card, CS_POWER_DAC )) )
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO 
+					"cs46xx: cs_release_mixdev() powerdown DAC failure (0x%x)\n",tmp) );
+				return -EIO;
+			}
+
 			/* Now turn off external AMP if needed */
 			state->card->amplifier_ctrl(state->card, -1);
 			state->card->active_ctrl(state->card, -1);
@@ -3130,6 +3284,13 @@
 			state->card->states[state->virt] = NULL;
 			state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
 
+			if( (tmp = cs461x_powerdown(card, CS_POWER_ADC )) )
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO 
+					"cs46xx: cs_release_mixdev() powerdown ADC failure (0x%x)\n",tmp) );
+				return -EIO;
+			}
+
 			/* Now turn off external AMP if needed */
 			state->card->amplifier_ctrl(state->card, -1);
 			state->card->active_ctrl(state->card, -1);
@@ -3143,127 +3304,494 @@
 	return 0;
 }
 
-static /*const*/ struct file_operations cs461x_fops = {
-	owner:		THIS_MODULE,
-	llseek:		cs_llseek,
-	read:		cs_read,
-	write:		cs_write,
-	poll:		cs_poll,
-	ioctl:		cs_ioctl,
-	mmap:		cs_mmap,
-	open:		cs_open,
-	release:	cs_release,
-};
-
-/* Write AC97 codec registers */
+void printpm(struct cs_card *s)
+{
+	CS_DBGOUT(CS_PM, 9, printk("pm struct:\n"));
+	CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n",
+		(unsigned)s->pm.flags,s->pm.u32CLKCR1_SAVE,s->pm.u32SSPMValue));
+	CS_DBGOUT(CS_PM, 9, printk("u32PPLVCvalue: 0x%x u32PPRVCvalue: 0x%x\n",
+		s->pm.u32PPLVCvalue,s->pm.u32PPRVCvalue));
+	CS_DBGOUT(CS_PM, 9, printk("u32FMLVCvalue: 0x%x u32FMRVCvalue: 0x%x\n",
+		s->pm.u32FMLVCvalue,s->pm.u32FMRVCvalue));
+	CS_DBGOUT(CS_PM, 9, printk("u32GPIORvalue: 0x%x u32JSCTLvalue: 0x%x\n",
+		s->pm.u32GPIORvalue,s->pm.u32JSCTLvalue));
+	CS_DBGOUT(CS_PM, 9, printk("u32SSCR: 0x%x u32SRCSA: 0x%x\n",
+		s->pm.u32SSCR,s->pm.u32SRCSA));
+	CS_DBGOUT(CS_PM, 9, printk("u32DacASR: 0x%x u32AdcASR: 0x%x\n",
+		s->pm.u32DacASR,s->pm.u32AdcASR));
+	CS_DBGOUT(CS_PM, 9, printk("u32DacSR: 0x%x u32AdcSR: 0x%x\n",
+		s->pm.u32DacSR,s->pm.u32AdcSR));
+	CS_DBGOUT(CS_PM, 9, printk("u32MIDCR_Save: 0x%x\n",
+		s->pm.u32MIDCR_Save));
+	CS_DBGOUT(CS_PM, 9, printk("u32AC97_powerdown: 0x%x _general_purpose 0x%x\n",
+		s->pm.u32AC97_powerdown,s->pm.u32AC97_general_purpose));
+	CS_DBGOUT(CS_PM, 9, printk("u32AC97_master_volume: 0x%x\n",
+		s->pm.u32AC97_master_volume));
+	CS_DBGOUT(CS_PM, 9, printk("u32AC97_headphone_volume: 0x%x\n",
+		s->pm.u32AC97_headphone_volume));
+	CS_DBGOUT(CS_PM, 9, printk("u32AC97_master_volume_mono: 0x%x\n",
+		s->pm.u32AC97_master_volume_mono));
+	CS_DBGOUT(CS_PM, 9, printk("u32AC97_pcm_out_volume: 0x%x\n",
+		s->pm.u32AC97_pcm_out_volume));
+	CS_DBGOUT(CS_PM, 9, printk("dmabuf_swptr_play: 0x%x dmabuf_count_play: %d\n",
+		s->pm.dmabuf_swptr_play,s->pm.dmabuf_count_play));
+	CS_DBGOUT(CS_PM, 9, printk("dmabuf_swptr_capture: 0x%x dmabuf_count_capture: %d\n",
+		s->pm.dmabuf_swptr_capture,s->pm.dmabuf_count_capture));
 
+}
 
-static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg)
+/****************************************************************************
+*
+*  Suspend - save the ac97 regs, mute the outputs and power down the part.  
+*
+****************************************************************************/
+void cs46xx_ac97_suspend(struct cs_card *card)
 {
-	struct cs_card *card = dev->private_data;
-	int count;
-	
-	/*
-	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
-	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97 
-	 *  3. Write ACCTL = Control Register = 460h for initiating the write
-	 *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h
-	 *  5. if DCV not cleared, break and return error
-	 *  6. Read ACSTS = Status Register = 464h, check VSTS bit
-	 */
-
+	int Count,i;
+	unsigned int tmp;
+	struct ac97_codec *dev=card->ac97_codec[0];
 
-	cs461x_peekBA0(card, BA0_ACSDA);
+	CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_suspend()+\n"));
 
-	/*
-	 *  Setup the AC97 control registers on the CS461x to send the
-	 *  appropriate command to the AC97 to perform the read.
-	 *  ACCAD = Command Address Register = 46Ch
-	 *  ACCDA = Command Data Register = 470h
-	 *  ACCTL = Control Register = 460h
-	 *  set DCV - will clear when process completed
-	 *  set CRW - Read command
-	 *  set VFRM - valid frame enabled
-	 *  set ESYN - ASYNC generation enabled
-	 *  set RSTN - ARST# inactive, AC97 codec not reset
-	 */
+	if(card->states[1])
+	{
+		stop_dac(card->states[1]);
+		resync_dma_ptrs(card->states[1]);
+	}
+	if(card->states[0])
+	{
+		stop_adc(card->states[0]);
+		resync_dma_ptrs(card->states[0]);
+	}
 
-	cs461x_pokeBA0(card, BA0_ACCAD, reg);
-	cs461x_pokeBA0(card, BA0_ACCDA, 0);
-	cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW |
-					     ACCTL_VFRM | ACCTL_ESYN |
-					     ACCTL_RSTN);
+	for(Count = 0x2, i=0; (Count <= CS46XX_AC97_HIGHESTREGTORESTORE)
+			&& (i < CS46XX_AC97_NUMBER_RESTORE_REGS); 
+		Count += 2, i++)
+	{
+		card->pm.ac97[i] = cs_ac97_get(dev, BA0_AC97_RESET + Count);
+	}
+/*
+* Save the ac97 volume registers as well as the current powerdown state.
+* Now, mute the all the outputs (master, headphone, and mono), as well
+* as the PCM volume, in preparation for powering down the entire part.
+*/ 
+	card->pm.u32AC97_master_volume = (u32)cs_ac97_get( dev, 
+			(u8)BA0_AC97_MASTER_VOLUME); 
+	card->pm.u32AC97_headphone_volume = (u32)cs_ac97_get(dev, 
+			(u8)BA0_AC97_HEADPHONE_VOLUME); 
+	card->pm.u32AC97_master_volume_mono = (u32)cs_ac97_get(dev, 
+			(u8)BA0_AC97_MASTER_VOLUME_MONO); 
+	card->pm.u32AC97_pcm_out_volume = (u32)cs_ac97_get(dev, 
+			(u8)BA0_AC97_PCM_OUT_VOLUME);
+		
+	cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, 0x8000);
+	cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, 0x8000);
+	cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, 0x8000);
+	cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, 0x8000);
 
+	card->pm.u32AC97_powerdown = (u32)cs_ac97_get(dev, (u8)AC97_POWER_CONTROL); 
+	card->pm.u32AC97_general_purpose = (u32)cs_ac97_get(dev, (u8)BA0_AC97_GENERAL_PURPOSE); 
 
-	/*
-	 *  Wait for the read to occur.
-	 */
-	for (count = 0; count < 500; count++) {
-		/*
-		 *  First, we want to wait for a short time.
-	 	 */
-		udelay(10);
-		/*
-		 *  Now, check to see if the read has completed.
-		 *  ACCTL = 460h, DCV should be reset by now and 460h = 17h
-		 */
-		if (!(cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV))
-			break;
+/*
+* And power down everything on the AC97 codec.
+* well, for now, only power down the DAC/ADC and MIXER VREFON components. 
+* trouble with removing VREF.
+*/
+	if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC |
+			CS_POWER_MIXVON )) )
+	{
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+			"cs46xx: cs46xx_ac97_suspend() failure (0x%x)\n",tmp) );
 	}
 
-	/*
-	 *  Make sure the read completed.
-	 */
-	if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV) {
-		printk(KERN_WARNING "cs461x: AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
-		return 0xffff;
-	}
+	CS_DBGOUT(CS_PM, 9, printk("cs4281: cs46xx_ac97_suspend()-\n"));
+}
 
-	/*
-	 *  Wait for the valid status bit to go active.
-	 */
-	for (count = 0; count < 100; count++) {
-		/*
-		 *  Read the AC97 status register.
-		 *  ACSTS = Status Register = 464h
-		 *  VSTS - Valid Status
-		 */
-		if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_VSTS)
-			break;
-		udelay(10);
-	}
-	
-	/*
-	 *  Make sure we got valid status.
-	 */
-	if (!(cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_VSTS)) {
-		printk(KERN_WARNING "cs461x: AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg);
-		return 0xffff;
+/****************************************************************************
+*
+*  Resume - power up the part and restore its registers..  
+*
+****************************************************************************/
+void cs46xx_ac97_resume(struct cs_card *card)
+{
+	int Count,i;
+	struct ac97_codec *dev=card->ac97_codec[0];
+
+	CS_DBGOUT(CS_PM, 9, printk("cs4281: cs46xx_ac97_resume()+\n"));
+
+/*
+* First, we restore the state of the general purpose register.  This
+* contains the mic select (mic1 or mic2) and if we restore this after
+* we restore the mic volume/boost state and mic2 was selected at
+* suspend time, we will end up with a brief period of time where mic1
+* is selected with the volume/boost settings for mic2, causing
+* acoustic feedback.  So we restore the general purpose register
+* first, thereby getting the correct mic selected before we restore
+* the mic volume/boost.
+*/
+	cs_ac97_set(dev, (u8)BA0_AC97_GENERAL_PURPOSE, 
+		(u16)card->pm.u32AC97_general_purpose);
+
+	cs_ac97_set(dev, (u8)AC97_POWER_CONTROL, 
+		(u16)card->pm.u32AC97_powerdown);
+	mdelay(10);
+
+/*
+* Now, while the outputs are still muted, restore the state of power
+* on the AC97 part.
+*/
+	cs_ac97_set(dev, (u8)BA0_AC97_POWERDOWN, (u16)card->pm.u32AC97_powerdown);
+	mdelay(5);
+/*
+* Restore just the first set of registers, from register number
+* 0x02 to the register number that ulHighestRegToRestore specifies.
+*/
+	for(	Count = 0x2, i=0; 
+		(Count <= CS46XX_AC97_HIGHESTREGTORESTORE)
+			&& (i < CS46XX_AC97_NUMBER_RESTORE_REGS); 
+		Count += 2, i++)
+	{
+		cs_ac97_set(dev, (u8)(BA0_AC97_RESET + Count), (u16)card->pm.ac97[i]);
 	}
 
-	/*
-	 *  Read the data returned from the AC97 register.
-	 *  ACSDA = Status Data Register = 474h
-	 */
-#if 0
-	printk("e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg,
-			cs461x_peekBA0(card, BA0_ACSDA),
-			cs461x_peekBA0(card, BA0_ACCAD));
-#endif
-	return cs461x_peekBA0(card, BA0_ACSDA);
+	/* Check if we have to init the amplifier */
+	if(card->amp_init)
+		card->amp_init(card);
+        
+	CS_DBGOUT(CS_PM, 9, printk("cs4281: cs46xx_ac97_resume()-\n"));
 }
 
-static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 val)
+
+static int cs46xx_restart_part(struct cs_card *card)
 {
-	struct cs_card *card = dev->private_data;
-	int count;
-	int val2 = 0;
-	
-	if(reg == AC97_CD_VOL)
-	{
-		val2 = cs_ac97_get(dev, AC97_CD_VOL);
-	}
-	
+	struct dmabuf *dmabuf;
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, 
+		printk( "cs46xx: cs46xx_restart_part()+\n"));
+	if(card->states[1])
+	{
+		dmabuf = &card->states[1]->dmabuf;
+		dmabuf->ready = 0;
+		resync_dma_ptrs(card->states[1]);
+		cs_set_divisor(dmabuf);
+		if(prog_dmabuf(card->states[1]))
+		{
+			CS_DBGOUT(CS_PM | CS_ERROR, 1, 
+				printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() dac error\n"));
+			return -1;
+		}
+		cs_set_dac_rate(card->states[1], dmabuf->rate);
+	}
+	if(card->states[0])
+	{
+		dmabuf = &card->states[0]->dmabuf;
+		dmabuf->ready = 0;
+		resync_dma_ptrs(card->states[0]);
+		cs_set_divisor(dmabuf);
+		if(prog_dmabuf(card->states[0]))
+		{
+			CS_DBGOUT(CS_PM | CS_ERROR, 1, 
+				printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() adc error\n"));
+			return -1;
+		}
+		cs_set_adc_rate(card->states[0], dmabuf->rate);
+	}
+	card->pm.flags |= CS46XX_PM_RESUMED;
+	if(card->states[0])
+		start_adc(card->states[0]);
+	if(card->states[1])
+		start_dac(card->states[1]);
+
+	card->pm.flags |= CS46XX_PM_IDLE;
+	card->pm.flags &= ~(CS46XX_PM_SUSPENDING | CS46XX_PM_SUSPENDED 
+			| CS46XX_PM_RESUMING | CS46XX_PM_RESUMED);
+	if(card->states[0])
+		wake_up(&card->states[0]->dmabuf.wait);
+	if(card->states[1])
+		wake_up(&card->states[1]->dmabuf.wait);
+
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, 
+		printk( "cs46xx: cs46xx_restart_part()-\n"));
+	return 0;
+}
+
+
+static void cs461x_reset(struct cs_card *card);
+static void cs461x_proc_stop(struct cs_card *card);
+int cs46xx_suspend(struct cs_card *card)
+{
+	unsigned int tmp;
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, 
+		printk("cs46xx: cs46xx_suspend()+ flags=%d s=0x%x\n",
+			(unsigned)card->pm.flags,(unsigned)card));
+/*
+* check the current state, only suspend if IDLE
+*/
+	if(!(card->pm.flags & CS46XX_PM_IDLE))
+	{
+		CS_DBGOUT(CS_PM | CS_ERROR, 2, 
+			printk("cs46xx: cs46xx_suspend() unable to suspend, not IDLE\n"));
+		return 1;
+	}
+	card->pm.flags &= ~CS46XX_PM_IDLE;
+	card->pm.flags |= CS46XX_PM_SUSPENDING;
+
+	card->active_ctrl(card,1);
+	
+	tmp = cs461x_peek(card, BA1_PFIE);
+	tmp &= ~0x0000f03f;
+	tmp |=  0x00000010;
+	cs461x_poke(card, BA1_PFIE, tmp);	/* playback interrupt disable */
+
+	tmp = cs461x_peek(card, BA1_CIE);
+	tmp &= ~0x0000003f;
+	tmp |=  0x00000011;
+	cs461x_poke(card, BA1_CIE, tmp);	/* capture interrupt disable */
+
+	/*
+         *  Stop playback DMA.
+	 */
+	tmp = cs461x_peek(card, BA1_PCTL);
+	cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff);
+
+	/*
+         *  Stop capture DMA.
+	 */
+	tmp = cs461x_peek(card, BA1_CCTL);
+	cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000);
+
+	if(card->states[1])
+	{
+		card->pm.dmabuf_swptr_play = card->states[1]->dmabuf.swptr;
+		card->pm.dmabuf_count_play = card->states[1]->dmabuf.count;
+	}
+	if(card->states[0])
+	{
+		card->pm.dmabuf_swptr_capture = card->states[0]->dmabuf.swptr;
+		card->pm.dmabuf_count_capture = card->states[0]->dmabuf.count;
+	}
+
+	cs46xx_ac97_suspend(card);
+
+	/*
+         *  Reset the processor.
+         */
+	cs461x_reset(card);
+
+	cs461x_proc_stop(card);
+
+	/*
+	 *  Power down the DAC and ADC.  For now leave the other areas on.
+	 */
+	cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, 0x0300);
+
+	/*
+	 *  Power down the PLL.
+	 */
+	cs461x_pokeBA0(card, BA0_CLKCR1, 0);
+
+	/*
+	 *  Turn off the Processor by turning off the software clock enable flag in 
+	 *  the clock control register.
+	 */
+	tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE;
+	cs461x_pokeBA0(card, BA0_CLKCR1, tmp);
+
+	card->active_ctrl(card,-1);
+
+	card->pm.flags &= ~CS46XX_PM_SUSPENDING;
+	card->pm.flags |= CS46XX_PM_SUSPENDED;
+
+	printpm(card);
+
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, 
+		printk("cs46xx: cs46xx_suspend()- flags=%d\n",
+			(unsigned)card->pm.flags));
+	return 0;
+}
+
+int cs46xx_resume(struct cs_card *card)
+{
+	int i;
+
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, 
+		printk( "cs46xx: cs46xx_resume()+ flags=%d\n",
+			(unsigned)card->pm.flags));
+	if(!(card->pm.flags & CS46XX_PM_SUSPENDED))
+	{
+		CS_DBGOUT(CS_PM | CS_ERROR, 2, 
+			printk("cs46xx: cs46xx_resume() unable to resume, not SUSPENDED\n"));
+		return 1;
+	}
+	card->pm.flags |= CS46XX_PM_RESUMING;
+	card->pm.flags &= ~CS46XX_PM_SUSPENDED;
+	printpm(card);
+	card->active_ctrl(card, 1);
+
+	for(i=0;i<5;i++)
+	{
+		if (cs_hardware_init(card) != 0)
+		{
+			CS_DBGOUT(CS_PM | CS_ERROR, 4, printk(
+				"cs46xx: cs46xx_resume()- ERROR in cs_hardware_init()\n"));
+			mdelay(10);
+			cs461x_reset(card);
+			continue;
+		}
+		break;
+	}
+	if(i>=4)
+	{
+		CS_DBGOUT(CS_PM | CS_ERROR, 1, printk(
+			"cs46xx: cs46xx_resume()- cs_hardware_init() failed, retried %d times.\n",i));
+		return 0;
+	}
+
+	if(cs46xx_restart_part(card))
+	{
+		CS_DBGOUT(CS_PM | CS_ERROR, 4, printk(
+			"cs46xx: cs46xx_resume(): cs46xx_restart_part() returned error\n"));
+	}
+
+	card->active_ctrl(card, -1);
+
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, printk("cs46xx: cs46xx_resume()- flags=%d\n",
+		(unsigned)card->pm.flags));
+	return 0;
+}
+
+static /*const*/ struct file_operations cs461x_fops = {
+	CS_OWNER	CS_THIS_MODULE
+	llseek:		cs_llseek,
+	read:		cs_read,
+	write:		cs_write,
+	poll:		cs_poll,
+	ioctl:		cs_ioctl,
+	mmap:		cs_mmap,
+	open:		cs_open,
+	release:	cs_release,
+};
+
+/* Write AC97 codec registers */
+
+
+static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg)
+{
+	struct cs_card *card = dev->private_data;
+	int count,loopcnt;
+	unsigned int tmp;
+	
+	/*
+	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97 
+	 *  3. Write ACCTL = Control Register = 460h for initiating the write
+	 *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h
+	 *  5. if DCV not cleared, break and return error
+	 *  6. Read ACSTS = Status Register = 464h, check VSTS bit
+	 */
+
+
+	cs461x_peekBA0(card, BA0_ACSDA);
+
+	/*
+	 *  Setup the AC97 control registers on the CS461x to send the
+	 *  appropriate command to the AC97 to perform the read.
+	 *  ACCAD = Command Address Register = 46Ch
+	 *  ACCDA = Command Data Register = 470h
+	 *  ACCTL = Control Register = 460h
+	 *  set DCV - will clear when process completed
+	 *  set CRW - Read command
+	 *  set VFRM - valid frame enabled
+	 *  set ESYN - ASYNC generation enabled
+	 *  set RSTN - ARST# inactive, AC97 codec not reset
+	 */
+
+	cs461x_pokeBA0(card, BA0_ACCAD, reg);
+	cs461x_pokeBA0(card, BA0_ACCDA, 0);
+	cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW |
+					     ACCTL_VFRM | ACCTL_ESYN |
+					     ACCTL_RSTN);
+
+
+	/*
+	 *  Wait for the read to occur.
+	 */
+	if(!(card->pm.flags & CS46XX_PM_IDLE))
+		loopcnt = 2000;
+	else
+		loopcnt = 500;
+	for (count = 0; count < loopcnt; count++) {
+		/*
+		 *  First, we want to wait for a short time.
+	 	 */
+		udelay(10);
+		/*
+		 *  Now, check to see if the read has completed.
+		 *  ACCTL = 460h, DCV should be reset by now and 460h = 17h
+		 */
+		if (!(cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV))
+			break;
+	}
+
+	/*
+	 *  Make sure the read completed.
+	 */
+	if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+			"cs46xx: AC'97 read problem (ACCTL_DCV), reg = 0x%x returning 0xffff\n", reg));
+		return 0xffff;
+	}
+
+	/*
+	 *  Wait for the valid status bit to go active.
+	 */
+
+	if(!(card->pm.flags & CS46XX_PM_IDLE))
+		loopcnt = 2000;
+	else
+		loopcnt = 100;
+	for (count = 0; count < loopcnt; count++) {
+		/*
+		 *  Read the AC97 status register.
+		 *  ACSTS = Status Register = 464h
+		 *  VSTS - Valid Status
+		 */
+		if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_VSTS)
+			break;
+		udelay(10);
+	}
+	
+	/*
+	 *  Make sure we got valid status.
+	 */
+	if (!( (tmp=cs461x_peekBA0(card, BA0_ACSTS)) & ACSTS_VSTS)) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+			"cs46xx: AC'97 read problem (ACSTS_VSTS), reg = 0x%x val=0x%x \n", reg, tmp));
+		CS_DBGOUT(CS_ERROR, 9, printk(KERN_WARNING "returning 0xffff\n"));
+		return 0xffff;
+	}
+
+	/*
+	 *  Read the data returned from the AC97 register.
+	 *  ACSDA = Status Data Register = 474h
+	 */
+	CS_DBGOUT(CS_FUNCTION, 9, printk(KERN_INFO
+		"cs46xx: cs_ac97_get() reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", 
+			reg, cs461x_peekBA0(card, BA0_ACSDA),
+			cs461x_peekBA0(card, BA0_ACCAD)));
+	return (cs461x_peekBA0(card, BA0_ACSDA));
+}
+
+static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 val)
+{
+	struct cs_card *card = dev->private_data;
+	int count;
+	int val2 = 0;
+	
+	if(reg == AC97_CD_VOL)
+	{
+		val2 = cs_ac97_get(dev, AC97_CD_VOL);
+	}
+	
 	/*
 	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
 	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97
@@ -3286,6 +3814,8 @@
          */
 	cs461x_pokeBA0(card, BA0_ACCAD, reg);
 	cs461x_pokeBA0(card, BA0_ACCDA, val);
+	cs461x_peekBA0(card, BA0_ACCTL);
+	cs461x_pokeBA0(card, BA0_ACCTL, 0 | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
 	cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM |
 				             ACCTL_ESYN | ACCTL_RSTN);
 	for (count = 0; count < 1000; count++) {
@@ -3304,7 +3834,10 @@
 	 *  Make sure the write completed.
 	 */
 	if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV)
-		printk(KERN_WARNING "cs461x: AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val);
+	{
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+			"cs46xx: AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val));
+	}
 
 	/*
 	 *	Adjust power if the mixer is selected/deselected according
@@ -3343,7 +3876,11 @@
 			if(val&0x8000 || val == 0x1f1f)
 				card->amplifier_ctrl(card, -1);
 			else /* Mute off power on */
+			{
+				if(card->amp_init)
+					card->amp_init(card);
 				card->amplifier_ctrl(card, 1);
+			}
 		}
 	}
 }
@@ -3355,49 +3892,87 @@
 {
 	int i=0;
 	int minor = MINOR(inode->i_rdev);
-	struct cs_card *card = devs;
+	struct cs_card *card=NULL;
+	struct list_head *entry;
+	unsigned int tmp;
+
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
+		  printk(KERN_INFO "cs46xx: cs_open_mixdev()+\n"));
 
-	for (card = devs; card != NULL; card = card->next)
+	list_for_each(entry, &cs46xx_devs)
+	{
+		card = list_entry(entry, struct cs_card, list);
 		for (i = 0; i < NR_AC97; i++)
 			if (card->ac97_codec[i] != NULL &&
 			    card->ac97_codec[i]->dev_mixer == minor)
 				goto match;
-
+	}
 	if (!card)
+	{
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2,
+			printk(KERN_INFO "cs46xx: cs46xx_open_mixdev()- -ENODEV\n"));
 		return -ENODEV;
-
+	}
  match:
+	if(!card->ac97_codec[i])
+		return -ENODEV;
 	file->private_data = card->ac97_codec[i];
 
 	card->active_ctrl(card,1);
+	if( (tmp = cs46xx_powerup(card, CS_POWER_MIXVON )) )
+	{
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+			"cs46xx: cs_open_mixdev() powerup failure (0x%x)\n",tmp) );
+		return -EIO;
+	}
 	MOD_INC_USE_COUNT;
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
+		  printk(KERN_INFO "cs46xx: cs_open_mixdev()- 0\n"));
 	return 0;
 }
 
 static int cs_release_mixdev(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
-	struct cs_card *card = devs;
+	struct cs_card *card=NULL;
+	struct list_head *entry;
 	int i;
+	unsigned int tmp;
+	
 	
-	for (card = devs; card != NULL; card = card->next)
+	list_for_each(entry, &cs46xx_devs)
+	{
+		card = list_entry(entry, struct cs_card, list);
 		for (i = 0; i < NR_AC97; i++)
 			if (card->ac97_codec[i] != NULL &&
 			    card->ac97_codec[i]->dev_mixer == minor)
 				goto match;
-
+	}
 	if (!card)
+	{
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2,
+			printk(KERN_INFO "cs46xx: cs46xx_open_mixdev()- -ENODEV\n"));
 		return -ENODEV;
+	}
 match:
+	if( (tmp = cs461x_powerdown(card, CS_POWER_MIXVON )) )
+	{
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+			"cs46xx: cs_release_mixdev() powerdown MIXVON failure (0x%x)\n",tmp) );
+		return -EIO;
+	}
 	card->active_ctrl(card, -1);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
+void __exit cs46xx_cleanup_module(void);
 static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
 				unsigned long arg)
 {
 	struct ac97_codec *codec = (struct ac97_codec *)file->private_data;
+	struct cs_card *card=NULL;
+	struct list_head *entry;
 
 #if CSDEBUG_INTERFACE
         int val;
@@ -3405,7 +3980,8 @@
 	if( 	(cmd == SOUND_MIXER_CS_GETDBGMASK) || 
 		(cmd == SOUND_MIXER_CS_SETDBGMASK) ||
 		(cmd == SOUND_MIXER_CS_GETDBGLEVEL) ||
-		(cmd == SOUND_MIXER_CS_SETDBGLEVEL) )
+		(cmd == SOUND_MIXER_CS_SETDBGLEVEL) ||
+		(cmd == SOUND_MIXER_CS_APM))
 	{
 	    switch(cmd)
 	    {
@@ -3427,9 +4003,38 @@
 				return -EFAULT;
 			cs_debuglevel = val;
 			return 0;
+
+		case SOUND_MIXER_CS_APM:
+			if (get_user(val, (unsigned long *) arg))
+				return -EFAULT;
+			if(val == CS_IOCTL_CMD_SUSPEND) 
+			{
+				list_for_each(entry, &cs46xx_devs)
+				{
+					card = list_entry(entry, struct cs_card, list);
+					cs46xx_suspend(card);
+				}
+
+			}
+			else if(val == CS_IOCTL_CMD_RESUME)
+			{
+				list_for_each(entry, &cs46xx_devs)
+				{
+					card = list_entry(entry, struct cs_card, list);
+					cs46xx_resume(card);
+				}
+			}
+			else
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO
+				    "cs4281: mixer_ioctl(): invalid APM cmd (%d)\n",
+					val));
+			}
+			return 0;
+
 		default:
 			CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO 
-				"cs4281: mixer_ioctl(): ERROR unknown debug cmd\n") );
+				"cs46xx: mixer_ioctl(): ERROR unknown debug cmd\n") );
 			return 0;
 	    }
 	}
@@ -3438,7 +4043,7 @@
 }
 
 static /*const*/ struct file_operations cs_mixer_fops = {
-	owner:		THIS_MODULE,
+	CS_OWNER	CS_THIS_MODULE
 	llseek:		cs_llseek,
 	ioctl:		cs_ioctl_mixdev,
 	open:		cs_open_mixdev,
@@ -3453,6 +4058,9 @@
 	struct ac97_codec *codec;
 	u16 eid;
 
+	CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+		"cs46xx: cs_ac97_init()+\n") );
+
 	for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
 		if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL)
 			return -ENOMEM;
@@ -3467,13 +4075,21 @@
 		codec->codec_write = cs_ac97_set;
 	
 		if (ac97_probe_codec(codec) == 0)
+		{
+			CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+				"cs46xx: cs_ac97_init()- codec number %d not found\n",
+					num_ac97) );
+			card->ac97_codec[num_ac97] = 0;
 			break;
+		}
+		CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+			"cs46xx: cs_ac97_init() found codec %d\n",num_ac97) );
 
 		eid = cs_ac97_get(codec, AC97_EXTENDED_ID);
 		
 		if(eid==0xFFFFFF)
 		{
-			printk(KERN_WARNING "cs461x: no codec attached ?\n");
+			printk(KERN_WARNING "cs46xx: codec %d not present\n",num_ac97);
 			kfree(codec);
 			break;
 		}
@@ -3481,17 +4097,25 @@
 		card->ac97_features = eid;
 			
 		if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1)) < 0) {
-			printk(KERN_ERR "cs461x: couldn't register mixer!\n");
+			printk(KERN_ERR "cs46xx: couldn't register mixer!\n");
 			kfree(codec);
 			break;
 		}
-
 		card->ac97_codec[num_ac97] = codec;
 
+		CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+			"cs46xx: cs_ac97_init() ac97_codec[%d] set to 0x%x\n",
+				(unsigned int)num_ac97,
+				(unsigned int)codec));
 		/* if there is no secondary codec at all, don't probe any more */
 		if (!ready_2nd)
-			return num_ac97+1;
+		{
+			num_ac97 += 1;
+			break;
+		}
 	}
+	CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+		"cs46xx: cs_ac97_init()- %d\n", (unsigned int)num_ac97));
 	return num_ac97;
 }
 
@@ -3599,96 +4223,426 @@
 		 */
 		cs461x_pokeBA0(card, BA0_SERBAD, idx);
 		/*
-		 *  Tell the serial port to load the new value into the FIFO location.
+		 *  Tell the serial port to load the new value into the FIFO location.
+		 */
+		cs461x_pokeBA0(card, BA0_SERBCM, SERBCM_WRC);
+	}
+	/*
+	 *  Now, if we powered up the devices, then power them back down again.
+	 *  This is kinda ugly, but should never happen.
+	 */
+	if (powerdown)
+		cs461x_pokeBA0(card, BA0_CLKCR1, tmp);
+}
+
+
+static int cs461x_powerdown(struct cs_card *card, unsigned int type)
+{
+	int count;
+	unsigned int tmp=0;
+
+	CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO 
+		"cs46xx: cs461x_powerdown()+ type=0x%x\n",type));
+	if(!powerdown)
+	{
+		CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO 
+			"cs46xx: cs461x_powerdown() DISABLED exiting\n"));
+		return 0;
+	}
+	tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+	CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO 
+		"cs46xx: cs461x_powerdown() powerdown reg=0x%x\n",tmp));
+/*
+* if powering down only the VREF, and not powering down the DAC/ADC,
+* then do not power down the VREF, UNLESS both the DAC and ADC are not
+* currently powered down.  If powering down DAC and ADC, then
+* it is possible to power down the VREF (ON).
+*/
+	if (    ((type & CS_POWER_MIXVON) && 
+		 (!(type & CS_POWER_ADC) || (!(type & CS_POWER_DAC))) )
+	      && 
+		((tmp & CS_AC97_POWER_CONTROL_ADC_ON) ||
+		 (tmp & CS_AC97_POWER_CONTROL_DAC_ON) ) )
+	{
+		CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO 
+			"cs46xx: cs461x_powerdown()- 0  unable to powerdown. tmp=0x%x\n",tmp));
+		return 0;
+	}
+	/*
+	 *  Power down indicated areas.
+	 */
+	if(type & CS_POWER_MIXVOFF)
+	{
+
+		CS_DBGOUT(CS_FUNCTION, 4, 
+			printk(KERN_INFO "cs46xx: cs461x_powerdown()+ MIXVOFF\n"));
+		/*
+		 *  Power down the MIXER (VREF ON) on the AC97 card.  
+		 */
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON)
+		{
+			tmp |= CS_AC97_POWER_CONTROL_MIXVOFF;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_MIXVOFF_ON))
+					break;
+			}
+			
+			/*
+			 *  Check the status..
+			 */
+			if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_MIXVOFF_ON)
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerdown MIXVOFF failed\n"));
+				return 1;
+			}
+		}
+	}
+	if(type & CS_POWER_MIXVON)
+	{
+
+		CS_DBGOUT(CS_FUNCTION, 4, 
+			printk(KERN_INFO "cs46xx: cs461x_powerdown()+ MIXVON\n"));
+		/*
+		 *  Power down the MIXER (VREF ON) on the AC97 card.  
+		 */
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (tmp & CS_AC97_POWER_CONTROL_MIXVON_ON)
+		{
+			tmp |= CS_AC97_POWER_CONTROL_MIXVON;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_MIXVON_ON))
+					break;
+			}
+			
+			/*
+			 *  Check the status..
+			 */
+			if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_MIXVON_ON)
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerdown MIXVON failed\n"));
+				return 1;
+			}
+		}
+	}
+	if(type & CS_POWER_ADC)
+	{
+		/*
+		 *  Power down the ADC on the AC97 card.  
+		 */
+		CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs461x_powerdown()+ ADC\n"));
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (tmp & CS_AC97_POWER_CONTROL_ADC_ON)
+		{
+			tmp |= CS_AC97_POWER_CONTROL_ADC;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_ADC_ON))
+					break;
+			}
+
+			/*
+			 *  Check the status..
+			 */
+			if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_ADC_ON)
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerdown ADC failed\n"));
+				return 1;
+			}
+		}
+	}
+	if(type & CS_POWER_DAC)
+	{
+		/*
+		 *  Power down the DAC on the AC97 card.  
 		 */
-		cs461x_pokeBA0(card, BA0_SERBCM, SERBCM_WRC);
+
+		CS_DBGOUT(CS_FUNCTION, 4, 
+			printk(KERN_INFO "cs46xx: cs461x_powerdown()+ DAC\n"));
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (tmp & CS_AC97_POWER_CONTROL_DAC_ON)
+		{
+			tmp |= CS_AC97_POWER_CONTROL_DAC;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_DAC_ON))
+					break;
+			}
+			
+			/*
+			 *  Check the status..
+			 */
+			if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_DAC_ON)
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerdown DAC failed\n"));
+				return 1;
+			}
+		}
 	}
-	/*
-	 *  Now, if we powered up the devices, then power them back down again.
-	 *  This is kinda ugly, but should never happen.
-	 */
-	if (powerdown)
-		cs461x_pokeBA0(card, BA0_CLKCR1, tmp);
+	tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+	CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO 
+		"cs46xx: cs461x_powerdown()- 0 tmp=0x%x\n",tmp));
+	return 0;
 }
 
-static void cs461x_powerup_dac(struct cs_card *card)
+static int cs46xx_powerup(struct cs_card *card, unsigned int type)
 {
 	int count;
-	unsigned int tmp;
+	unsigned int tmp=0;
 
+	CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO 
+		"cs46xx: cs46xx_powerup()+ type=0x%x\n",type));
 	/*
-	 *  Power on the DACs on the AC97 card.  We turn off the DAC
-	 *  powerdown bit and write the new value of the power control
-	 *  register.
-	 */
-	tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
-	if (tmp & 2)	/* already */
-		return;
-	cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp & 0xfdff);
-
+	* check for VREF and powerup if need to.
+	*/
+	if(type & CS_POWER_MIXVON)
+		type |= CS_POWER_MIXVOFF;
+	if(type & (CS_POWER_DAC | CS_POWER_ADC))
+		type |= CS_POWER_MIXVON | CS_POWER_MIXVOFF;
 	/*
-	 *  Now, we wait until we sample a DAC ready state.
+	 *  Power up indicated areas.
 	 */
-	for (count = 0; count < 32; count++) {
+	if(type & CS_POWER_MIXVOFF)
+	{
+
+		CS_DBGOUT(CS_FUNCTION, 4, 
+			printk(KERN_INFO "cs46xx: cs46xx_powerup()+ MIXVOFF\n"));
 		/*
-		 *  First, lets wait a short while to let things settle out a
-		 *  bit, and to prevent retrying the read too quickly.
+		 *  Power up the MIXER (VREF ON) on the AC97 card.  
 		 */
-		udelay(50);
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (!(tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON))
+		{
+			tmp &= ~CS_AC97_POWER_CONTROL_MIXVOFF;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_MIXVOFF_ON)
+					break;
+			}
+			
+			/*
+			 *  Check the status..
+			 */
+			if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_MIXVOFF_ON))
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerup MIXVOFF failed\n"));
+				return 1;
+			}
+		}
+	}
+	if(type & CS_POWER_MIXVON)
+	{
 
+		CS_DBGOUT(CS_FUNCTION, 4, 
+			printk(KERN_INFO "cs46xx: cs46xx_powerup()+ MIXVON\n"));
 		/*
-		 *  Read the current state of the power control register.
+		 *  Power up the MIXER (VREF ON) on the AC97 card.  
 		 */
-		if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 2)
-			break;
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (!(tmp & CS_AC97_POWER_CONTROL_MIXVON_ON))
+		{
+			tmp &= ~CS_AC97_POWER_CONTROL_MIXVON;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_MIXVON_ON)
+					break;
+			}
+			
+			/*
+			 *  Check the status..
+			 */
+			if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_MIXVON_ON))
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerup MIXVON failed\n"));
+				return 1;
+			}
+		}
 	}
-	
-	/*
-	 *  Check the status..
-	 */
-	if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 2))
-		printk(KERN_WARNING "cs461x: powerup DAC failed\n");
-}
+	if(type & CS_POWER_ADC)
+	{
+		/*
+		 *  Power up the ADC on the AC97 card.  
+		 */
+		CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs46xx_powerup()+ ADC\n"));
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (!(tmp & CS_AC97_POWER_CONTROL_ADC_ON))
+		{
+			tmp &= ~CS_AC97_POWER_CONTROL_ADC;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
 
-static void cs461x_powerup_adc(struct cs_card *card)
-{
-	int count;
-	unsigned int tmp;
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
 
-	/*
-	 *  Power on the ADCs on the AC97 card.  We turn off the DAC
-	 *  powerdown bit and write the new value of the power control
-	 *  register.
-	 */
-	tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
-	if (tmp & 1)	/* already */
-		return;
-	cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp & 0xfeff);
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_ADC_ON)
+					break;
+			}
 
-	/*
-	 *  Now, we wait until we sample a ADC ready state.
-	 */
-	for (count = 0; count < 32; count++) {
+			/*
+			 *  Check the status..
+			 */
+			if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_ADC_ON))
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerup ADC failed\n"));
+				return 1;
+			}
+		}
+	}
+	if(type & CS_POWER_DAC)
+	{
 		/*
-		 *  First, lets wait a short while to let things settle out a
-		 *  bit, and to prevent retrying the read too quickly.
+		 *  Power up the DAC on the AC97 card.  
 		 */
-		udelay(50);
 
-		/*
-		 *  Read the current state of the power control register.
-		 */
-		if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 1)
-			break;
-	}
+		CS_DBGOUT(CS_FUNCTION, 4, 
+			printk(KERN_INFO "cs46xx: cs46xx_powerup()+ DAC\n"));
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (!(tmp & CS_AC97_POWER_CONTROL_DAC_ON))
+		{
+			tmp &= ~CS_AC97_POWER_CONTROL_DAC;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
 
-	/*
-	 *  Check the status..
-	 */
-	if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 1))
-		printk(KERN_WARNING "cs461x: powerup ADC failed\n");
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_DAC_ON)
+					break;
+			}
+			
+			/*
+			 *  Check the status..
+			 */
+			if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_DAC_ON))
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerup DAC failed\n"));
+				return 1;
+			}
+		}
+	}
+	tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+	CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO 
+		"cs46xx: cs46xx_powerup()- 0 tmp=0x%x\n",tmp));
+	return 0;
 }
 
+
 static void cs461x_proc_start(struct cs_card *card)
 {
 	int cnt;
@@ -3713,7 +4667,7 @@
 	}
 
 	if (cs461x_peek(card, BA1_SPCR) & SPCR_RUNFR)
-		printk(KERN_WARNING "cs461x: SPCR_RUNFR never reset\n");
+		printk(KERN_WARNING "cs46xx: SPCR_RUNFR never reset\n");
 }
 
 static void cs461x_proc_stop(struct cs_card *card)
@@ -3725,13 +4679,13 @@
 	cs461x_poke(card, BA1_SPCR, 0);
 }
 
-
-
 static int cs_hardware_init(struct cs_card *card)
 {
 	unsigned long end_time;
-	unsigned int tmp;
+	unsigned int tmp,count;
 	
+	CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+		"cs46xx: cs_hardware_init()+\n") );
 	/* 
 	 *  First, blast the clock control register to zero so that the PLL starts
          *  out in a known state, and blast the master serial port control register
@@ -3753,6 +4707,8 @@
          *  there might be logic external to the CS461x that uses the ARST# line
          *  for a reset.
          */
+        cs461x_pokeBA0(card, BA0_ACCTL, 1);
+        udelay(50);
         cs461x_pokeBA0(card, BA0_ACCTL, 0);
         udelay(50);
         cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_RSTN);
@@ -3778,6 +4734,15 @@
 	cs461x_pokeBA0(card, BA0_SERMC1, SERMC1_PTC_AC97);
 
 	/*
+	* The part seems to not be ready for a while after a resume.
+	* so, if we are resuming, then wait for 700 mils.  Note that 600 mils
+	* is not enough for some platforms! tested on an IBM Thinkpads and 
+	* reference cards.
+	*/
+	if(!(card->pm.flags & CS46XX_PM_IDLE))
+		mdelay(initdelay);
+
+	/*
 	 *  Write the selected clock control setup to the hardware.  Do not turn on
 	 *  SWCE yet (if requested), so that the devices clocked by the output of
 	 *  PLL are not clocked until the PLL is stable.
@@ -3823,27 +4788,48 @@
 
 	mdelay(5);		/* Shouldnt be needed ?? */
 	
+/*
+* If we are resuming under 2.2.x then we can not schedule a timeout.
+* so, just spin the CPU.
+*/
+	if(card->pm.flags & CS46XX_PM_IDLE)
+	{
 	/*
 	 * Wait for the card ready signal from the AC97 card.
 	 */
-	end_time = jiffies + 3 * (HZ >> 2);
-	do {
+		end_time = jiffies + 3 * (HZ >> 2);
+		do {
 		/*
 		 *  Read the AC97 status register to see if we've seen a CODEC READY
 		 *  signal from the AC97 card.
 		 */
-		if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY)
-			break;
-		current->state = TASK_UNINTERRUPTIBLE;
-		schedule_timeout(1);
-	} while (time_before(jiffies, end_time));
+			if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY)
+				break;
+			current->state = TASK_UNINTERRUPTIBLE;
+			schedule_timeout(1);
+		} while (time_before(jiffies, end_time));
+	}
+	else
+	{
+		for (count = 0; count < 100; count++) {
+		// First, we want to wait for a short time.
+			udelay(25);
+
+			if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY)
+				break;
+		}
+	}
 
 	/*
 	 *  Make sure CODEC is READY.
 	 */
 	if (!(cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY)) {
-		printk(KERN_WARNING "cs461x: create - never read card ready from AC'97\n");
-		printk(KERN_WARNING "cs461x: it is probably not a bug, try using the CS4232 driver\n");
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING  
+			"cs46xx: create - never read card ready from AC'97\n"));
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING  
+			"cs46xx: probably not a bug, try using the CS4232 driver,\n"));
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING  
+			"cs46xx: or turn off any automatic Power Management support in the BIOS.\n"));
 		return -EIO;
 	}
 
@@ -3853,28 +4839,40 @@
 	 */
 	cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
 
+	if(card->pm.flags & CS46XX_PM_IDLE)
+	{
 	/*
 	 *  Wait until we've sampled input slots 3 and 4 as valid, meaning that
 	 *  the card is pumping ADC data across the AC-link.
 	 */
-	end_time = jiffies + 3 * (HZ >> 2);
-	do {
-		/*
-		 *  Read the input slot valid register and see if input slots 3 and
-		 *  4 are valid yet.
-		 */
-		if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
-			break;
-		current->state = TASK_UNINTERRUPTIBLE;
-		schedule_timeout(1);
-	} while (time_before(jiffies, end_time));
+		end_time = jiffies + 3 * (HZ >> 2);
+		do {
+			/*
+			 *  Read the input slot valid register and see if input slots 3 and
+			 *  4 are valid yet.
+			 */
+			if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
+				break;
+			current->state = TASK_UNINTERRUPTIBLE;
+			schedule_timeout(1);
+		} while (time_before(jiffies, end_time));
+	}
+	else
+	{
+		for (count = 0; count < 100; count++) {
+		// First, we want to wait for a short time.
+			udelay(25);
 
+			if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
+				break;
+		}
+	}
 	/*
 	 *  Make sure input slots 3 and 4 are valid.  If not, then return
 	 *  an error.
 	 */
 	if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) != (ACISV_ISV3 | ACISV_ISV4)) {
-		printk(KERN_WARNING "cs461x: create - never read ISV3 & ISV4 from AC'97\n");
+		printk(KERN_WARNING "cs46xx: create - never read ISV3 & ISV4 from AC'97\n");
 		return -EIO;
 	}
 
@@ -3885,12 +4883,6 @@
 	cs461x_pokeBA0(card, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4);
 
 	/*
-	 *  Power down the DAC and ADC.  We will power them up (if) when we need
-	 *  them.
-	 */
-	/* cs461x_pokeBA0(card, BA0_AC97_POWERDOWN, 0x300); */
-
-	/*
 	 *  Turn off the Processor by turning off the software clock enable flag in 
 	 *  the clock control register.
 	 */
@@ -3923,14 +4915,20 @@
 	cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000);
 
 	/* initialize AC97 codec and register /dev/mixer */
-	if (cs_ac97_init(card) <= 0)
-		return -EIO;
-		
-	mdelay(5);		/* Do we need this ?? */
+	if(card->pm.flags & CS46XX_PM_IDLE)
+	{
+		if (cs_ac97_init(card) <= 0)
+		{
+			CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+				"cs46xx: cs_ac97_init() failure\n") );
+			return -EIO;
+		}
+	}
+	else
+	{
+		cs46xx_ac97_resume(card);
+	}
 	
-	cs461x_powerup_adc(card);
-	cs461x_powerup_dac(card);
-
 	cs461x_proc_start(card);
 
 	/*
@@ -3946,11 +4944,29 @@
 	tmp &= ~0x0000003f;
 	tmp |=  0x00000001;
 	cs461x_poke(card, BA1_CIE, tmp);	/* capture interrupt enable */	
+
+	/*
+	 *  If IDLE then Power down the part.  We will power components up 
+	 *  when we need them.  
+	 */
+	if(card->pm.flags & CS46XX_PM_IDLE)
+	{
+		if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC |
+				CS_POWER_MIXVON )) )
+		{
+			CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+				"cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) );
+			return -EIO;
+		}
+	}
+	CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+		"cs46xx: cs_hardware_init()- 0\n"));
 	return 0;
 }
 
+
 /* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered 
-   untill "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */
+   until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */
    
    
 /*
@@ -3963,65 +4979,77 @@
 	u16 id;
 	char *name;
 	void (*amp)(struct cs_card *, int);
+	void (*amp_init)(struct cs_card *);
 	void (*active)(struct cs_card *, int);
 };
 
-static struct cs_card_type __initdata cards[]={
-	{0x1489, 0x7001, "Genius Soundmaker 128 value", amp_none, NULL},
-	{0x5053, 0x3357, "Voyetra", amp_voyetra, NULL},
-	{0x1071, 0x6003, "Mitac MI6020/21", amp_voyetra, NULL},
+static struct cs_card_type cards[]={
+	{0x1489, 0x7001, "Genius Soundmaker 128 value", amp_none, NULL, NULL},
+	{0x5053, 0x3357, "Voyetra", amp_voyetra, NULL, NULL},
+	{0x1071, 0x6003, "Mitac MI6020/21", amp_voyetra, NULL, NULL},
+	{0x14AF, 0x0050, "Hercules Game Theatre XP", NULL, NULL, NULL},
+	{0x1681, 0x0050, "Hercules Game Theatre XP", NULL, NULL, NULL},
 	/* Not sure if the 570 needs the clkrun hack */
-	{PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", amp_none, clkrun_hack},
-	{PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", amp_none, clkrun_hack},
-	{PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL},
-	{0, 0, "Card without SSID set", NULL, NULL },
+	{PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", amp_none, NULL, clkrun_hack},
+	{PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", amp_none, NULL, clkrun_hack},
+	{PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL, NULL},
+	{0, 0, "Card without SSID set", NULL, NULL, NULL },
 	{0, 0, NULL, NULL, NULL}
 };
 
-#ifdef CS46XX_PM
-static int cs46xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
-{
-struct cs_state *state = (struct cs_state *) dev->data;
-
-	if (state) {
-		switch(rqst) {
-			case PM_RESUME:
-				printk( KERN_DEBUG "cs46xx: PM resume request\n");
-				cs_hardware_init(state->card);
-				break;
-			case PM_SUSPEND:
-				printk( KERN_DEBUG "cs46xx: PM suspend request\n");
-				stop_dac(state);
-				resync_dma_ptrs(state);
-				break;
-		}
-	}
+MODULE_AUTHOR("Alan Cox <alan@redhat.com>, Jaroslav Kysela, <pcaudio@crystal.cirrus.com>");
+MODULE_DESCRIPTION("Crystal SoundFusion Audio Support");
 
-return 0;
-}
-#endif
+static const char cs46xx_banner[] = KERN_INFO "Crystal 4280/46xx + AC97 Audio, version " CS46XX_MAJOR_VERSION "." CS46XX_MINOR_VERSION "." CS46XX_ARCH ", " __TIME__ " " __DATE__ "\n";
+static const char fndmsg[] = KERN_INFO "cs46xx: Found %d audio device(s).\n";
 
-static int __init cs_install(struct pci_dev *pci_dev)
+static int __devinit cs46xx_probe(struct pci_dev *pci_dev,
+				  const struct pci_device_id *pciid)
 {
-	struct cs_card *card;
-	struct cs_card_type *cp = &cards[0];
-#ifdef CS46XX_PM
 	struct pm_dev *pmdev;
-#endif
+	int i,j;
 	u16 ss_card, ss_vendor;
-	
-	
+	struct cs_card *card;
+	dma_addr_t dma_mask;
+	struct cs_card_type *cp = &cards[0];
+
+	CS_DBGOUT(CS_FUNCTION | CS_INIT, 2,
+		  printk(KERN_INFO "cs46xx: probe()+\n"));
+
+	if (!RSRCISMEMORYREGION(pci_dev, 0) ||
+	    !RSRCISMEMORYREGION(pci_dev, 1)) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+			 "cs46xx: probe()- Memory region not assigned\n"));
+		return -1;
+	}
+	if (pci_dev->irq == 0) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+			 "cs46xx: probe() IRQ not assigned\n"));
+		return -1;
+	}
+	if (!pci_dma_supported(pci_dev, 0xffffffff)) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+		      "cs46xx: probe() architecture does not support 32bit PCI busmaster DMA\n"));
+		return -1;
+	}
+	dma_mask = 0xffffffff;	/* this enables playback and recording */
+
 	pci_read_config_word(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor);
 	pci_read_config_word(pci_dev, PCI_SUBSYSTEM_ID, &ss_card);
 
 	if ((card = kmalloc(sizeof(struct cs_card), GFP_KERNEL)) == NULL) {
-		printk(KERN_ERR "cs461x: out of memory\n");
+		printk(KERN_ERR "cs46xx: out of memory\n");
 		return -ENOMEM;
 	}
 	memset(card, 0, sizeof(*card));
 
-	card->ba0_addr = pci_resource_start(pci_dev, 0);
-	card->ba1_addr = pci_resource_start(pci_dev, 1);
+	if (pci_enable_device(pci_dev)) {
+		CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
+			 "cs46xx: pci_enable_device() failed\n"));
+		goto fail2;
+	}
+	card->ba0_addr = RSRCADDRESS(pci_dev, 0);
+	card->ba1_addr = RSRCADDRESS(pci_dev, 1);
 	card->pci_dev = pci_dev;
 	card->irq = pci_dev->irq;
 	card->magic = CS_CARD_MAGIC;
@@ -4029,7 +5057,8 @@
 
 	pci_set_master(pci_dev);
 
-	printk(KERN_INFO "cs461x: Card found at 0x%08lx and 0x%08lx, IRQ %d\n",
+	printk(cs46xx_banner);
+	printk(KERN_INFO "cs46xx: Card found at 0x%08lx and 0x%08lx, IRQ %d\n",
 	       card->ba0_addr, card->ba1_addr, card->irq);
 
 	card->alloc_pcm_channel = cs_alloc_pcm_channel;
@@ -4037,7 +5066,7 @@
 	card->free_pcm_channel = cs_free_pcm_channel;
 	card->amplifier_ctrl = amp_none;
 	card->active_ctrl = amp_none;
-	
+
 	while (cp->name)
 	{
 		if(cp->vendor == ss_vendor && cp->id == ss_card)
@@ -4045,19 +5074,21 @@
 			card->amplifier_ctrl = cp->amp;
 			if(cp->active)
 				card->active_ctrl = cp->active;
+			if(cp->amp_init)
+				card->amp_init = cp->amp_init;
 			break;
 		}
 		cp++;
 	}
 	if (cp->name==NULL)
 	{
-		printk(KERN_INFO "cs461x: Unknown card (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n",
+		printk(KERN_INFO "cs46xx: Unknown card (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n",
 			ss_vendor, ss_card, card->ba0_addr, card->ba1_addr,  card->irq);
 	}
 	else
 	{
-		printk(KERN_INFO "cs461x: %s at 0x%08lx/0x%08lx, IRQ %d\n",
-			cp->name, card->ba0_addr, card->ba1_addr, card->irq);
+		printk(KERN_INFO "cs46xx: %s (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n",
+			cp->name, ss_vendor, ss_card, card->ba0_addr, card->ba1_addr, card->irq);
 	}
 	
 	if (card->amplifier_ctrl==NULL)
@@ -4068,74 +5099,121 @@
 		       
 	if (external_amp == 1)
 	{
-		printk(KERN_INFO "cs461x: Crystal EAPD support forced on.\n");
+		printk(KERN_INFO "cs46xx: Crystal EAPD support forced on.\n");
 		card->amplifier_ctrl = amp_voyetra;
 	}
 
 	if (thinkpad == 1)
 	{
+		printk(KERN_INFO "cs46xx: Activating CLKRUN hack for Thinkpad.\n");
 		card->active_ctrl = clkrun_hack;
-		printk(KERN_INFO "cs461x: Activating CLKRUN hack for Thinkpad.\n");
 	}
 	
 	card->active_ctrl(card, 1);
-	
+
 	/* claim our iospace and irq */
 	
-	card->ba0 = ioremap(card->ba0_addr, CS461X_BA0_SIZE);
-	card->ba1.name.data0 = ioremap(card->ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE);
-	card->ba1.name.data1 = ioremap(card->ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE);
-	card->ba1.name.pmem = ioremap(card->ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE);
-	card->ba1.name.reg = ioremap(card->ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE);
-	
-	CS_DBGOUT(CS_INIT, 4, printk("card->ba0=0x%.08x\n",(unsigned)card->ba0) );
-	CS_DBGOUT(CS_INIT, 4, printk("card->ba1=0x%.08x 0x%.08x 0x%.08x 0x%.08x\n",
-		(unsigned)card->ba1.name.data0,
-		(unsigned)card->ba1.name.data1,
-		(unsigned)card->ba1.name.pmem,
-		(unsigned)card->ba1.name.reg) );
+	card->ba0 = ioremap_nocache(card->ba0_addr, CS461X_BA0_SIZE);
+	card->ba1.name.data0 = ioremap_nocache(card->ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE);
+	card->ba1.name.data1 = ioremap_nocache(card->ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE);
+	card->ba1.name.pmem = ioremap_nocache(card->ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE);
+	card->ba1.name.reg = ioremap_nocache(card->ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE);
+	
+	CS_DBGOUT(CS_INIT, 4, printk(KERN_INFO 
+		"cs46xx: card->ba0=0x%.08x\n",(unsigned)card->ba0) );
+	CS_DBGOUT(CS_INIT, 4, printk(KERN_INFO 
+		"cs46xx: card->ba1=0x%.08x 0x%.08x 0x%.08x 0x%.08x\n",
+			(unsigned)card->ba1.name.data0,
+			(unsigned)card->ba1.name.data1,
+			(unsigned)card->ba1.name.pmem,
+			(unsigned)card->ba1.name.reg) );
 
 	if(card->ba0 == 0 || card->ba1.name.data0 == 0 ||
 		card->ba1.name.data1 == 0 || card->ba1.name.pmem == 0 ||
 		card->ba1.name.reg == 0)
 		goto fail2;
 		
-	if (request_irq(card->irq, &cs_interrupt, SA_SHIRQ, "cs461x", card)) {
-		printk(KERN_ERR "cs461x: unable to allocate irq %d\n", card->irq);
+	if (request_irq(card->irq, &cs_interrupt, SA_SHIRQ, "cs46xx", card)) {
+		printk(KERN_ERR "cs46xx: unable to allocate irq %d\n", card->irq);
 		goto fail2;
 	}
 	/* register /dev/dsp */
 	if ((card->dev_audio = register_sound_dsp(&cs461x_fops, -1)) < 0) {
-		printk(KERN_ERR "cs461x: unable to register dsp\n");
+		printk(KERN_ERR "cs46xx: unable to register dsp\n");
 		goto fail;
 	}
 
         /* register /dev/midi */
         if((card->dev_midi = register_sound_midi(&cs_midi_fops, -1)) < 0)
-                printk(KERN_ERR "cs461x: unable to register midi\n");
+                printk(KERN_ERR "cs46xx: unable to register midi\n");
                 
-        if (cs_hardware_init(card)<0)
-        {
+	card->pm.flags |= CS46XX_PM_IDLE;
+	for(i=0;i<5;i++)
+	{
+		if (cs_hardware_init(card) != 0)
+		{
+			CS_DBGOUT(CS_ERROR, 4, printk(
+				"cs46xx: ERROR in cs_hardware_init()... retrying\n"));
+			for (j = 0; j < NR_AC97; j++)
+				if (card->ac97_codec[j] != NULL) {
+					unregister_sound_mixer(card->ac97_codec[j]->dev_mixer);
+					kfree (card->ac97_codec[j]);
+				}
+			mdelay(10);
+			continue;
+		}
+		break;
+	}
+	if(i>=4)
+	{
+		CS_DBGOUT(CS_PM | CS_ERROR, 1, printk(
+			"cs46xx: cs46xx_probe()- cs_hardware_init() failed, retried %d times.\n",i));
                 unregister_sound_dsp(card->dev_audio);
                 if(card->dev_midi)
                         unregister_sound_midi(card->dev_midi);
                 goto fail;
-        }
+	}
+
         init_waitqueue_head(&card->midi.open_wait);
         init_MUTEX(&card->midi.open_sem);
         init_waitqueue_head(&card->midi.iwait);
         init_waitqueue_head(&card->midi.owait);
-        card->next = devs;
-        devs = card;
         cs461x_pokeBA0(card, BA0_MIDCR, MIDCR_MRST);   
         cs461x_pokeBA0(card, BA0_MIDCR, 0);   
-        
+
+	/* 
+	* Check if we have to init the amplifier, but probably already done
+	* since the CD logic in the ac97 init code will turn on the ext amp.
+	*/
+	if(cp->amp_init)
+		cp->amp_init(card);
         card->active_ctrl(card, -1);
-#ifdef CS46XX_PM
-        pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pci_dev), cs46xx_pm_callback);
-        if (pmdev)
-        	pmdev->data = card;
-#endif
+
+	PCI_SET_DRIVER_DATA(pci_dev, card);
+	PCI_SET_DMA_MASK(pci_dev, dma_mask);
+	list_add(&card->list, &cs46xx_devs);
+
+	pmdev = cs_pm_register(PM_PCI_DEV, PM_PCI_ID(pci_dev), cs46xx_pm_callback);
+	if (pmdev)
+	{
+		CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO
+			 "cs46xx: probe() pm_register() succeeded (0x%x).\n",
+				(unsigned)pmdev));
+		pmdev->data = card;
+	}
+	else
+	{
+		CS_DBGOUT(CS_INIT | CS_PM | CS_ERROR, 2, printk(KERN_INFO
+			 "cs46xx: probe() pm_register() failed (0x%x).\n",
+				(unsigned)pmdev));
+		card->pm.flags |= CS46XX_PM_NOT_REGISTERED;
+	}
+
+	CS_DBGOUT(CS_PM, 9, printk(KERN_INFO "cs46xx: pm.flags=0x%x card=0x%x\n",
+		(unsigned)card->pm.flags,(unsigned)card));
+
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
+		"cs46xx: probe()- device allocated successfully\n"));
         return 0;
 
 fail:
@@ -4152,15 +5230,22 @@
 	if(card->ba1.name.reg)
 		iounmap(card->ba1.name.reg);
 	kfree(card);
+	CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO
+		"cs46xx: probe()- no device allocated\n"));
 	return -ENODEV;
+} // probe_cs46xx
 
-}
+// --------------------------------------------------------------------- 
 
-static void cs_remove(struct cs_card *card)
+static void __devinit cs46xx_remove(struct pci_dev *pci_dev)
 {
+	struct cs_card *card = PCI_GET_DRIVER_DATA(pci_dev);
 	int i;
 	unsigned int tmp;
 	
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
+		 "cs46xx: cs46xx_remove()+\n"));
+
 	card->active_ctrl(card,1);
 	
 	tmp = cs461x_peek(card, BA1_PFIE);
@@ -4196,7 +5281,12 @@
 	 *  Power down the DAC and ADC.  We will power them up (if) when we need
 	 *  them.
 	 */
-	cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, 0x300);
+	if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC |
+			CS_POWER_MIXVON )) )
+	{
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+			"cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) );
+	}
 
 	/*
 	 *  Power down the PLL.
@@ -4230,54 +5320,123 @@
         if(card->dev_midi)
                 unregister_sound_midi(card->dev_midi);
 	kfree(card);
-}
+	PCI_SET_DRIVER_DATA(pci_dev,NULL);
+	list_del(&card->list);
 
-MODULE_AUTHOR("Alan Cox <alan@redhat.com>, Jaroslav Kysela, <audio@crystal.cirrus.com>");
-MODULE_DESCRIPTION("Crystal SoundFusion Audio Support");
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
+		 "cs46xx: cs46xx_remove()-: remove successful\n"));
+}
 
-static char banner[] __initdata = KERN_INFO "Crystal 4280/461x + AC97 Audio, version " DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n";
-static char fndmsg[] __initdata = KERN_INFO "cs461x: Found %d audio device(s).\n";
+enum {
+	CS46XX_4610 = 0,
+	CS46XX_4612,  	/* same as 4624 */
+	CS46XX_4615,  	/* same as 4630 */
+};
 
-static int __init cs_init_driver(void)
-{
-	struct pci_dev *pcidev = NULL;
-	int foundone=0;
+static struct pci_device_id cs46xx_pci_tbl[] __devinitdata = {
+	
+	{PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4610},
+	{PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4612, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4612},
+	{PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4615, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4615},
+	{0,}
+};
 
-	if (!pci_present())   /* No PCI bus in this machine! */
-		return -ENODEV;
+MODULE_DEVICE_TABLE(pci, cs46xx_pci_tbl);
 
-	printk(banner);
+struct pci_driver cs46xx_pci_driver = {
+	name:"cs46xx",
+	id_table:cs46xx_pci_tbl,
+	probe:cs46xx_probe,
+	remove:cs46xx_remove,
+	suspend:CS46XX_SUSPEND_TBL,
+	resume:CS46XX_RESUME_TBL,
+};
 
-	while( (pcidev = pci_find_device(PCI_VENDOR_ID_CIRRUS, 0x6001 , pcidev))!=NULL ) {
-		if (cs_install(pcidev)==0)
-			foundone++;
-	}
-	while( (pcidev = pci_find_device(PCI_VENDOR_ID_CIRRUS, 0x6003 , pcidev))!=NULL ) {
-		if (cs_install(pcidev)==0)
-			foundone++;
+int __init cs46xx_init_module(void)
+{
+	int rtn = 0;
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO 
+		"cs46xx: cs46xx_init_module()+ \n"));
+	if (!pci_present()) {	/* No PCI bus in this machine! */
+		CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
+			"cs46xx: cs46xx_init_module()- no pci bus found\n"));
+		return -ENODEV;
 	}
-	while( (pcidev = pci_find_device(PCI_VENDOR_ID_CIRRUS, 0x6004 , pcidev))!=NULL ) {
-		if (cs_install(pcidev)==0)
-			foundone++;
+	rtn = pci_module_init(&cs46xx_pci_driver);
+
+	if(rtn == -ENODEV)
+	{
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk( 
+			"cs46xx: Unable to detect valid cs46xx device\n"));
 	}
 
-	printk(fndmsg, foundone);
-	return foundone ? 0 : -ENODEV;
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs46xx: cs46xx_init_module()- (%d)\n",rtn));
+	return rtn;
 }
 
-static void __exit cs_exit_driver(void)
+void __exit cs46xx_cleanup_module(void)
 {
-	struct cs_card *next;
-#ifdef CS46XX_PM
-	pm_unregister_all(cs46xx_pm_callback);
-#endif
-	while(devs)
-	{
-		next=devs->next;
-		cs_remove(devs);
-		devs=next;
+	pci_unregister_driver(&cs46xx_pci_driver);
+	cs_pm_unregister_all(cs46xx_pm_callback);
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs46xx: cleanup_cs46xx() finished\n"));
+}
+
+module_init(cs46xx_init_module);
+module_exit(cs46xx_cleanup_module);
+
+int cs46xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	struct cs_card *card;
+
+	CS_DBGOUT(CS_PM, 2, printk(KERN_INFO 
+		"cs46xx: cs46xx_pm_callback dev=0x%x rqst=0x%x card=%d\n",
+			(unsigned)dev,(unsigned)rqst,(unsigned)data));
+	card = (struct cs_card *) dev->data;
+	if (card) {
+		switch(rqst) {
+			case PM_SUSPEND:
+				CS_DBGOUT(CS_PM, 2, printk(KERN_INFO
+					"cs46xx: PM suspend request\n"));
+				if(cs46xx_suspend(card))
+				{
+				    CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
+					"cs46xx: PM suspend request refused\n"));
+					return 1; 
+				}
+				break;
+			case PM_RESUME:
+				CS_DBGOUT(CS_PM, 2, printk(KERN_INFO
+					"cs46xx: PM resume request\n"));
+				if(cs46xx_resume(card))
+				{
+				    CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
+					"cs46xx: PM resume request refused\n"));
+					return 1;
+				}
+				break;
+		}
 	}
+
+	return 0;
+}
+
+static void cs46xx_suspend_tbl(struct pci_dev *pcidev)
+{
+	struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev);
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 2, 
+		printk(KERN_INFO "cs46xx: cs46xx_suspend_tbl request\n"));
+	cs46xx_suspend(s);
+	return;
+}
+
+static void cs46xx_resume_tbl(struct pci_dev *pcidev)
+{
+	struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev);
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 2, 
+		printk(KERN_INFO "cs46xx: cs46xx_resume_tbl request\n"));
+	cs46xx_resume(s);
+	return;
 }
 
-module_init(cs_init_driver);
-module_exit(cs_exit_driver);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)