diff -Naur linux-2.6.25/sound/arm/ep93xx-ac97.c linux-2.6.25.ep93xx/sound/arm/ep93xx-ac97.c
--- linux-2.6.25/sound/arm/ep93xx-ac97.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.25.ep93xx/sound/arm/ep93xx-ac97.c	2008-09-12 14:50:40.000000000 -0500
@@ -0,0 +1,3492 @@
+/*
+ * linux/sound/arm/ep93xx-i2s.c -- ALSA PCM interface for the edb93xx i2s audio
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/soundcard.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+
+#include <asm/irq.h>
+#include <asm/semaphore.h>
+#include <asm/hardware.h>
+#include <asm/io.h>                                                                                                                             
+#include <asm/arch/dma.h>
+#include "ep93xx-ac97.h"
+
+#define	DRIVER_VERSION	"19/3/2007"
+#define	DRIVER_DESC	"EP93xx AC97 Audio driver"
+static int ac_link_enabled = 0;
+static int codec_supported_mixers;
+
+//#define DEBUG 1
+#ifdef DEBUG
+#define DPRINTK( fmt, arg... )  printk( fmt, ##arg )
+#else
+#define DPRINTK( fmt, arg... )
+#endif
+
+#define WL16 	0
+#define WL24	1
+
+#define AUDIO_NAME              	"ep93xx-i2s"
+#define AUDIO_SAMPLE_RATE_DEFAULT       44100
+#define AUDIO_DEFAULT_VOLUME            0
+#define AUDIO_MAX_VOLUME	        181
+#define AUDIO_DEFAULT_DMACHANNELS       3
+#define PLAYBACK_DEFAULT_DMACHANNELS    3
+#define CAPTURE_DEFAULT_DMACHANNELS     3
+
+#define CHANNEL_FRONT			(1<<0)
+#define CHANNEL_REAR                   	(1<<1)
+#define CHANNEL_CENTER_LFE              (1<<2)
+
+#ifdef CONFIG_CODEC_CS4271
+static int 	mute = 0;
+#endif
+
+static int 	index = SNDRV_DEFAULT_IDX1;          /* Index 0-MAX */
+static char 	*id = SNDRV_DEFAULT_STR1;            /* ID for this card */
+static int 	SSP_Handle = -1;
+
+static void snd_ep93xx_dma_tx_callback(ep93xx_dma_int_t DMAInt,ep93xx_dma_dev_t device,unsigned int user_data);
+static void snd_ep93xx_dma_rx_callback(ep93xx_dma_int_t DMAInt,ep93xx_dma_dev_t device,unsigned int user_data);
+
+static const struct snd_pcm_hardware ep93xx_ac97_pcm_hardware = {
+
+
+    .info		= ( SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE  ),
+    .formats		= ( SNDRV_PCM_FMTBIT_U8     | SNDRV_PCM_FMTBIT_S8     |
+			    SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
+			    SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+			    SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
+			    SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE ),
+    .rates		= ( SNDRV_PCM_RATE_8000  | SNDRV_PCM_RATE_11025 |
+			    SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+			    SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+			    SNDRV_PCM_RATE_48000 ),
+    .rate_min		= 8000,
+    .rate_max		= 48000,
+    .channels_min	= 1,/*2,*/
+    .channels_max	= 2,
+					
+    .period_bytes_min	= 1 * 1024,
+    .period_bytes_max	= 32 * 1024,
+    .periods_min	= 1,
+    .periods_max	= 32,
+    .buffer_bytes_max	= 32 * 1024,
+    .fifo_size		= 0,
+};
+
+static int ep93xx_calc_closest_freq
+(
+    ulong   ulPLLFreq, 
+    ulong   ulRequestedMClkFreq,
+    ulong * pulActualMClkFreq,
+    ulong * pulI2SDiv
+);
+
+static audio_stream_t output_stream;
+static audio_stream_t input_stream;
+								
+static audio_state_t audio_state =
+{
+    .output_stream      	=&output_stream,
+    .output_dma[0]        	=DMATx_AAC1,
+    .output_id[0]          	="Ac97 out",
+	
+    .input_stream       	=&input_stream,
+    .input_dma[0]          	=DMARx_AAC1,
+    .input_id[0]           	="Ac97 in",
+	
+    .sem                    = __SEMAPHORE_INIT(audio_state.sem,1),
+    .codec_set_by_playback  = 0,
+    .codec_set_by_capture   = 0,
+    .DAC_bit_width		 =16,
+    .bCompactMode		 =0,
+};			
+		
+
+
+/*
+ * peek
+ *
+ * Reads an AC97 codec register.  Returns -1 if there was an error.
+ */
+static int peek(unsigned int uiAddress)
+{
+	unsigned int uiAC97RGIS;
+	
+	if( !ac_link_enabled )
+	{
+		printk("ep93xx ac97 peek: attempt to peek before enabling ac-link.\n");
+		return -1;
+	}
+	
+	/*
+	 * Check to make sure that the address is aligned on a word boundary 
+	 * and is 7E or less.
+	 */
+	if( ((uiAddress & 0x1)!=0) || (uiAddress > 0x007e))
+	{
+		return -1;
+	}
+
+	/*
+	 * How it is supposed to work is:
+	 *  - The ac97 controller sends out a read addr in slot 1.
+	 *  - In the next frame, the codec will echo that address back in slot 1
+	 *    and send the data in slot 2.  SLOT2RXVALID will be set to 1.
+	 *
+	 * Read until SLOT2RXVALID goes to 1.  Reading the data in AC97S2DATA
+	 * clears SLOT2RXVALID.
+	 */
+
+	/*
+	 * First, delay one frame in case of back to back peeks/pokes.
+	 */
+	mdelay( 1 );
+
+	/*
+	 * Write the address to AC97S1DATA, delay 1 frame, read the flags.
+	 */
+	outl( uiAddress, AC97S1DATA);
+	udelay( 21 * 4 );
+	uiAC97RGIS = inl( AC97RGIS );
+
+	/*
+	 * Return error if we timed out.
+	 */
+	if( ((uiAC97RGIS & AC97RGIS_SLOT1TXCOMPLETE) == 0 ) &&
+		((uiAC97RGIS & AC97RGIS_SLOT2RXVALID) == 0 ) )
+	{
+		printk( "ep93xx-ac97 - peek failed reading reg 0x%02x.\n", uiAddress ); 
+		return -1;
+	}
+	
+	return ( inl(AC97S2DATA) & 0x000fffff);
+}
+
+/*
+ * poke
+ *
+ * Writes an AC97 codec Register.  Return -1 if error.
+ */
+static int poke(unsigned int uiAddress, unsigned int uiValue)
+{
+	unsigned int uiAC97RGIS;
+
+	if( !ac_link_enabled )
+	{
+		printk("ep93xx ac97 poke: attempt to poke before enabling ac-link.\n");
+		return -1;
+	}
+	
+	/*
+	 * Check to make sure that the address is align on a word boundary and
+	 * is 7E or less.  And that the value is a 16 bit value.
+	 */
+	if( ((uiAddress & 0x1)!=0) || (uiAddress > 0x007e))
+	{
+		printk("ep93xx ac97 poke: address error.\n");
+		return -1;
+	}
+ 	
+	/*stop the audio loop from the input to the output directly*/
+
+	if((uiAddress==AC97_0E_MIC_VOL)||(uiAddress==AC97_10_LINE_IN_VOL))
+	{
+		uiValue = (uiValue | 0x8000);
+	
+	}
+	
+	/*
+	 * First, delay one frame in case of back to back peeks/pokes.
+	 */
+	mdelay( 1 );
+
+	/*
+	 * Write the data to AC97S2DATA, then the address to AC97S1DATA.
+	 */
+	outl( uiValue, AC97S2DATA );
+	outl( uiAddress, AC97S1DATA );
+	
+	/*
+	 * Wait for the tx to complete, get status.
+	 */
+	udelay( 30 );/*21*/
+	uiAC97RGIS = inl(AC97RGIS);
+
+	/*
+	 * Return error if we timed out.
+	 */
+	if( !(inl(AC97RGIS) & AC97RGIS_SLOT1TXCOMPLETE) )
+	{
+		printk( "ep93xx-ac97: poke failed writing reg 0x%02x  value 0x%02x.\n", uiAddress, uiValue ); 
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * When we get to the multichannel case the pre-fill and enable code
+ * will go to the dma driver's start routine.
+ */
+static void ep93xx_audio_enable( int input_or_output_stream )
+{
+	unsigned int uiTemp;
+
+	DPRINTK("ep93xx_audio_enable :%x\n",input_or_output_stream);
+
+	/*
+	 * Enable the rx or tx channel depending on the value of 
+	 * input_or_output_stream
+	 */
+	if( input_or_output_stream )
+	{
+		uiTemp = inl(AC97TXCR1);
+		outl( (uiTemp | AC97TXCR_TEN), AC97TXCR1 );
+	}
+	else
+	{
+		uiTemp = inl(AC97RXCR1);
+		outl( (uiTemp | AC97RXCR_REN), AC97RXCR1 );
+	}
+	
+	
+	//DDEBUG("ep93xx_audio_enable - EXIT\n");
+}
+
+static void ep93xx_audio_disable( int input_or_output_stream )
+{
+	unsigned int uiTemp;
+
+	DPRINTK("ep93xx_audio_disable\n");
+
+	/*
+	 * Disable the rx or tx channel depending on the value of 
+	 * input_or_output_stream
+	 */
+	if( input_or_output_stream )
+	{
+		uiTemp = inl(AC97TXCR1);
+		outl( (uiTemp & ~AC97TXCR_TEN), AC97TXCR1 );
+	}
+	else
+	{
+		uiTemp = inl(AC97RXCR1);
+		outl( (uiTemp & ~AC97RXCR_REN), AC97RXCR1 );
+	}
+
+	//DDEBUG("ep93xx_audio_disable - EXIT\n");
+}
+
+
+
+/*=======================================================================================*/
+/*
+ * ep93xx_setup_src
+ *
+ * Once the ac-link is up and all is good, we want to set the codec to a 
+ * usable mode.
+ */
+static void ep93xx_setup_src(void)
+{
+	int iTemp;
+
+	/*
+	 * Set the VRA bit to enable the SRC.
+	 */
+	iTemp = peek( AC97_2A_EXT_AUDIO_POWER );
+	poke( AC97_2A_EXT_AUDIO_POWER,  (iTemp | 0x1) );
+	
+	/*
+	 * Set the DSRC/ASRC bits to enable the variable rate SRC.
+	 */
+	iTemp = peek( AC97_60_MISC_CRYSTAL_CONTROL  );
+	poke( AC97_60_MISC_CRYSTAL_CONTROL, (iTemp  | 0x0300) );
+}
+
+/*
+ * ep93xx_set_samplerate
+ *
+ *   lFrequency       - Sample Rate in Hz
+ *   bCapture       - 0 to set Tx sample rate; 1 to set Rx sample rate
+ */
+static void ep93xx_set_samplerate( long lSampleRate, int bCapture )
+{
+	unsigned short usDivider, usPhase;
+
+	DPRINTK( "ep93xx_set_samplerate - Fs = %d\n", (int)lSampleRate );
+
+	if( (lSampleRate <  7200) || (lSampleRate > 48000)  )
+	{
+		printk( "ep93xx_set_samplerate - invalid Fs = %d\n", 
+				 (int)lSampleRate );
+		return;
+	}
+
+	/*
+	 * Calculate divider and phase increment.
+	 *
+	 * divider = round( 0x1770000 / lSampleRate )
+	 *  Note that usually rounding is done by adding 0.5 to a floating 
+	 *  value and then truncating.  To do this without using floating
+	 *  point, I multiply the fraction by two, do the division, then add one, 
+	 *  then divide the whole by 2 and then truncate.
+	 *  Same effect, no floating point math.
+	 *
+	 * Ph incr = trunc( (0x1000000 / usDivider) + 1 )
+	 */
+
+	usDivider = (unsigned short)( ((2 * 0x1770000 / lSampleRate) +  1) / 2 );
+
+	usPhase = (0x1000000 / usDivider) + 1;
+
+	/*
+	 * Write them in the registers.  Spec says divider must be
+	 * written after phase incr.
+	 */
+	if(!bCapture)
+	{
+		poke( AC97_2C_PCM_FRONT_DAC_RATE, usDivider);
+		poke( AC97_64_DAC_SRC_PHASE_INCR, usPhase);
+	}
+	else
+	{
+		
+		poke( AC97_32_PCM_LR_ADC_RATE,  usDivider);
+		poke( AC97_66_ADC_SRC_PHASE_INCR, usPhase);
+	}
+	
+	DPRINTK( "ep93xx_set_samplerate - phase = %d,  divider = %d\n",
+				(unsigned int)usPhase, (unsigned int)usDivider );
+
+	/*
+	 * We sorta should report the actual samplerate back to the calling
+	 * application.  But some applications freak out if they don't get
+	 * exactly what they asked for.  So we fudge and tell them what
+	 * they want to hear.
+	 */
+	//audio_samplerate = lSampleRate;
+
+	DPRINTK( "ep93xx_set_samplerate - EXIT\n" );
+}
+
+/*
+ * ep93xx_set_hw_format
+ *
+ * Sets up whether the controller is expecting 20 bit data in 32 bit words
+ * or 16 bit data compacted to have a stereo sample in each 32 bit word.
+ */
+static void ep93xx_set_hw_format( long format,long channel )
+{
+	int bCompactMode;
+	
+	switch( format )
+	{
+		/*
+		 * Here's all the <=16 bit formats.  We can squeeze both L and R
+		 * into one 32 bit sample so use compact mode.
+		 */
+		case SNDRV_PCM_FORMAT_U8:		   
+		case SNDRV_PCM_FORMAT_S8:		   
+		case SNDRV_PCM_FORMAT_S16_LE:
+		case SNDRV_PCM_FORMAT_U16_LE:
+			bCompactMode = 1;
+			break;
+
+		/*
+		 * Add any other >16 bit formats here...
+		 */
+		case SNDRV_PCM_FORMAT_S32_LE:
+		default:
+			bCompactMode = 0;
+			break;
+	}
+	
+	if( bCompactMode )
+	{
+		DPRINTK("ep93xx_set_hw_format - Setting serial mode to 16 bit compact.\n");
+	
+		/*
+		 * Turn on Compact Mode so we can fit each stereo sample into
+		 * a 32 bit word.  Twice as efficent for DMA and FIFOs.
+		 */
+		if(channel==2){
+			outl( 0x00008018, AC97RXCR1 );
+			outl( 0x00008018, AC97TXCR1 );
+		}
+		else {
+		        outl( 0x00008018, AC97RXCR1 );
+                        outl( 0x00008018, AC97TXCR1 );
+                }
+
+
+		audio_state.DAC_bit_width = 16;
+		audio_state.bCompactMode = 1;
+	}
+	else
+	{
+		DPRINTK("ep93xx_set_hw_format - Setting serial mode to 20 bit non-CM.\n");
+	
+		/*
+		 * Turn off Compact Mode so we can do > 16 bits per channel 
+		 */
+		if(channel==2){
+			outl( 0x00004018, AC97RXCR1 );
+			outl( 0x00004018, AC97TXCR1 );
+		}
+		else{
+                        outl( 0x00004018, AC97RXCR1 );
+                        outl( 0x00004018, AC97TXCR1 );
+		}
+
+		audio_state.DAC_bit_width = 20;
+		audio_state.bCompactMode = 0;
+	}
+
+}
+
+/*
+ * ep93xx_stop_loop
+ *
+ * Once the ac-link is up and all is good, we want to set the codec to a
+ * usable mode.
+ */
+static void ep93xx_stop_loop(void)
+{
+        int iTemp;
+                                                                                                                             
+        /*
+         * Set the AC97_0E_MIC_VOL MUTE bit to enable the LOOP.
+         */
+        iTemp = peek( AC97_0E_MIC_VOL );
+        poke( AC97_0E_MIC_VOL,  (iTemp | 0x8000) );
+                                                                                                                             
+        /*
+         * Set the AC97_10_LINE_IN_VOL MUTE bit to enable the LOOP.
+         */
+        iTemp = peek( AC97_10_LINE_IN_VOL  );
+        poke( AC97_10_LINE_IN_VOL, (iTemp  | 0x8000) );
+}
+
+/*
+ * ep93xx_init_ac97_controller
+ *
+ * This routine sets up the Ac'97 Controller.
+ */
+static void ep93xx_init_ac97_controller(void)
+{
+	unsigned int uiDEVCFG, uiTemp;
+
+	DPRINTK("ep93xx_init_ac97_controller - enter\n");
+
+	/*
+	 * Configure the multiplexed Ac'97 pins to be Ac97 not I2s.
+	 * Configure the EGPIO4 and EGPIO6 to be GPIOS, not to be  
+	 * SDOUT's for the second and third I2S controller channels.
+	 */
+	uiDEVCFG = inl(SYSCON_DEVCFG);
+	
+	uiDEVCFG &= ~(SYSCON_DEVCFG_I2SonAC97 | 
+				  SYSCON_DEVCFG_A1onG |
+				  SYSCON_DEVCFG_A2onG);
+		
+	SysconSetLocked(SYSCON_DEVCFG, uiDEVCFG);
+
+	/*
+	 * Disable the AC97 controller internal loopback.  
+	 * Disable Override codec ready.
+	 */
+	outl( 0, AC97GCR );
+
+	/*
+	 * Enable the AC97 Link.
+	 */
+	uiTemp = inl(AC97GCR);
+	outl( (uiTemp | AC97GSR_IFE), AC97GCR );
+
+	/*
+	 * Set the TIMEDRESET bit.  Will cause a > 1uSec reset of the ac-link.
+	 * This bit is self resetting.
+	 */
+	outl( AC97RESET_TIMEDRESET, AC97RESET );
+	
+	/*
+	 *  Delay briefly, but let's not hog the processor.
+	 */
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout( 5 ); /* 50 mSec */
+
+	/*
+	 * Read the AC97 status register to see if we've seen a CODECREADY
+	 * signal from the AC97 codec.
+	 */
+	if( !(inl(AC97RGIS) & AC97RGIS_CODECREADY))
+	{
+		printk( "ep93xx-ac97 - FAIL: CODECREADY still low!\n");
+		return;
+	}
+
+	/*
+	 *  Delay for a second, not hogging the processor
+	 */
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout( HZ ); /* 1 Sec */
+	
+	/*
+	 * Now the Ac-link is up.  We can read and write codec registers.
+	 */
+	ac_link_enabled = 1;
+
+	/*
+	 * Set up the rx and tx channels
+	 * Set the CM bit, data size=16 bits, enable tx slots 3 & 4.
+	 */
+	ep93xx_set_hw_format( EP93XX_DEFAULT_FORMAT,EP93XX_DEFAULT_NUM_CHANNELS );
+
+	DPRINTK( "ep93xx-ac97 -- AC97RXCR1:  %08x\n", inl(AC97RXCR1) ); 
+	DPRINTK( "ep93xx-ac97 -- AC97TXCR1:  %08x\n", inl(AC97TXCR1) ); 
+
+	DPRINTK("ep93xx_init_ac97_controller - EXIT - success\n");
+
+}
+
+#ifdef alsa_ac97_debug
+static void ep93xx_dump_ac97_regs(void)
+{
+	int i;
+	unsigned int reg0, reg1, reg2, reg3, reg4, reg5, reg6, reg7;
+	
+	DPRINTK( "---------------------------------------------\n");
+	DPRINTK( "   :   0    2    4    6    8    A    C    E\n" ); 
+	
+	for( i=0 ; i < 0x80 ; i+=0x10 )
+	{
+		reg0 = 0xffff & (unsigned int)peek( i );
+		reg1 = 0xffff & (unsigned int)peek( i + 0x2 );
+		reg2 = 0xffff & (unsigned int)peek( i + 0x4 );
+		reg3 = 0xffff & (unsigned int)peek( i + 0x6 );
+		reg4 = 0xffff & (unsigned int)peek( i + 0x8 );
+		reg5 = 0xffff & (unsigned int)peek( i + 0xa );
+		reg6 = 0xffff & (unsigned int)peek( i + 0xc );
+		reg7 = 0xffff & (unsigned int)peek( i + 0xe );
+
+		DPRINTK( " %02x : %04x %04x %04x %04x %04x %04x %04x %04x\n", 
+				 i, reg0, reg1, reg2, reg3, reg4, reg5, reg6, reg7);
+	}
+
+	DPRINTK( "---------------------------------------------\n");
+}
+#endif
+
+
+#define supported_mixer(FOO) \
+        ( (FOO >= 0) && \
+        (FOO < SOUND_MIXER_NRDEVICES) && \
+        codec_supported_mixers & (1<<FOO) )
+                                                                                                                             
+/*
+ * Available record sources.
+ * LINE1 refers to AUX in.
+ * IGAIN refers to input gain which means stereo mix.
+ */
+#define AC97_RECORD_MASK \
+        (SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN | SOUND_MASK_VIDEO |\
+        SOUND_MASK_LINE1 | SOUND_MASK_LINE | SOUND_MASK_PHONEIN)
+                                                                                                                             
+#define AC97_STEREO_MASK \
+        (SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_CD | \
+        SOUND_MASK_ALTPCM | SOUND_MASK_IGAIN | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO)
+                                                                                                                             
+#define AC97_SUPPORTED_MASK \
+        (AC97_STEREO_MASK | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \
+        SOUND_MASK_SPEAKER | SOUND_MASK_MIC | \
+        SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT)
+
+
+
+
+/* this table has default mixer values for all OSS mixers. */
+typedef struct  {
+	int mixer;
+	unsigned int value;
+} mixer_defaults_t;
+
+/*
+ * Default mixer settings that are set up during boot.
+ *
+ * These values are 16 bit numbers in which the upper byte is right volume
+ * and the lower byte is left volume or mono volume for mono controls.
+ *
+ * OSS Range for each of left and right volumes is 0 to 100 (0x00 to 0x64).
+ * 
+ */
+static mixer_defaults_t mixer_defaults[SOUND_MIXER_NRDEVICES] = 
+{
+	/* Outputs */
+	{SOUND_MIXER_VOLUME,	0x6464},   /* 0 dB */  /* -46.5dB to  0 dB */
+	{SOUND_MIXER_ALTPCM,	0x6464},   /* 0 dB */  /* -46.5dB to  0 dB */
+	{SOUND_MIXER_PHONEOUT,	0x6464},   /* 0 dB */  /* -46.5dB to  0 dB */
+
+	/* PCM playback gain */
+	{SOUND_MIXER_PCM,		0x4b4b},   /* 0 dB */  /* -34.5dB to +12dB */
+
+	/* Record gain */
+	{SOUND_MIXER_IGAIN,		0x0000},   /* 0 dB */  /* -34.5dB to +12dB */
+
+	/* Inputs */
+	{SOUND_MIXER_MIC,		0x0000},   /* mute */  /* -34.5dB to +12dB */
+	{SOUND_MIXER_LINE,		0x4b4b},   /* 0 dB */  /* -34.5dB to +12dB */
+
+	/* Inputs that are not connected. */
+	{SOUND_MIXER_SPEAKER,	0x0000},   /* mute */  /* -45dB   to   0dB */
+	{SOUND_MIXER_PHONEIN,	0x0000},   /* mute */  /* -34.5dB to +12dB */
+	{SOUND_MIXER_CD,		0x0000},   /* mute */  /* -34.5dB to +12dB */
+	{SOUND_MIXER_VIDEO,		0x0000},   /* mute */  /* -34.5dB to +12dB */
+	{SOUND_MIXER_LINE1,		0x0000},   /* mute */  /* -34.5dB to +12dB */
+
+	{-1,0} /* last entry */
+};
+
+/* table to scale scale from OSS mixer value to AC97 mixer register value */	
+typedef struct {
+	unsigned int offset;
+	int scale;
+} ac97_mixer_hw_t; 
+
+static ac97_mixer_hw_t ac97_hw[SOUND_MIXER_NRDEVICES] = 
+{
+	[SOUND_MIXER_VOLUME]		=  	{AC97_02_MASTER_VOL,	64},
+	[SOUND_MIXER_BASS]			=	{0, 0},
+	[SOUND_MIXER_TREBLE]		=	{0, 0},
+	[SOUND_MIXER_SYNTH]			=  	{0,	0},
+	[SOUND_MIXER_PCM]			=  	{AC97_18_PCM_OUT_VOL,	32},
+	[SOUND_MIXER_SPEAKER]		=  	{AC97_0A_PC_BEEP_VOL,	32},
+	[SOUND_MIXER_LINE]			=  	{AC97_10_LINE_IN_VOL,	32},
+	[SOUND_MIXER_MIC]			=  	{AC97_0E_MIC_VOL,		32},
+	[SOUND_MIXER_CD]			=  	{AC97_12_CD_VOL,		32},
+	[SOUND_MIXER_IMIX]			=  	{0,	0},
+	[SOUND_MIXER_ALTPCM]		=  	{AC97_04_HEADPHONE_VOL,	64},
+	[SOUND_MIXER_RECLEV]		=  	{0,	0},
+	[SOUND_MIXER_IGAIN]			=  	{AC97_1C_RECORD_GAIN,	16},
+	[SOUND_MIXER_OGAIN]			=  	{0,	0},
+	[SOUND_MIXER_LINE1]			=  	{AC97_16_AUX_VOL,		32},
+	[SOUND_MIXER_LINE2]			=  	{0,	0},
+	[SOUND_MIXER_LINE3]			=  	{0,	0},
+	[SOUND_MIXER_DIGITAL1]		=  	{0,	0},
+	[SOUND_MIXER_DIGITAL2]		=  	{0,	0},
+	[SOUND_MIXER_DIGITAL3]		=  	{0,	0},
+	[SOUND_MIXER_PHONEIN]		=  	{AC97_0C_PHONE_VOL,		32},
+	[SOUND_MIXER_PHONEOUT]		=  	{AC97_06_MONO_VOL,		64},
+	[SOUND_MIXER_VIDEO]			=  	{AC97_14_VIDEO_VOL,		32},
+	[SOUND_MIXER_RADIO]			=  	{0,	0},
+	[SOUND_MIXER_MONITOR]		=  	{0,	0},
+};
+
+
+/* the following tables allow us to go from OSS <-> ac97 quickly. */
+enum ac97_recsettings 
+{
+	AC97_REC_MIC=0,
+	AC97_REC_CD,
+	AC97_REC_VIDEO,
+	AC97_REC_AUX,
+	AC97_REC_LINE,
+	AC97_REC_STEREO, /* combination of all enabled outputs..  */
+	AC97_REC_MONO,	      /*.. or the mono equivalent */
+	AC97_REC_PHONE
+};
+
+static const unsigned int ac97_rm2oss[] = 
+{
+	[AC97_REC_MIC] 	 = SOUND_MIXER_MIC,
+	[AC97_REC_CD] 	 = SOUND_MIXER_CD,
+	[AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
+	[AC97_REC_AUX] 	 = SOUND_MIXER_LINE1,
+	[AC97_REC_LINE]  = SOUND_MIXER_LINE,
+	[AC97_REC_STEREO]= SOUND_MIXER_IGAIN,
+	[AC97_REC_PHONE] = SOUND_MIXER_PHONEIN
+};
+
+/* indexed by bit position */
+static const unsigned int ac97_oss_rm[] = 
+{
+	[SOUND_MIXER_MIC] 	= AC97_REC_MIC,
+	[SOUND_MIXER_CD] 	= AC97_REC_CD,
+	[SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
+	[SOUND_MIXER_LINE1] = AC97_REC_AUX,
+	[SOUND_MIXER_LINE] 	= AC97_REC_LINE,
+	[SOUND_MIXER_IGAIN]	= AC97_REC_STEREO,
+	[SOUND_MIXER_PHONEIN] 	= AC97_REC_PHONE
+};
+
+
+/*
+ * ep93xx_write_mixer
+ *
+ */
+static void ep93xx_write_mixer
+( 
+	int oss_channel,
+	unsigned int left, 
+	unsigned int right
+)
+{
+	u16 val = 0;
+	ac97_mixer_hw_t * mh = &ac97_hw[oss_channel];
+
+	DPRINTK("ac97_codec: wrote OSS %2d (ac97 0x%02x), "
+	       "l:%2d, r:%2d:",
+	       oss_channel, mh->offset, left, right);
+
+	if( !mh->scale )
+	{
+		printk( "ep93xx-ac97.c: ep93xx_write_mixer - not a valid OSS channel\n");
+		return;
+	}
+
+	if( AC97_STEREO_MASK & (1 << oss_channel) ) 
+	{
+		/* stereo mixers */
+		if (left == 0 && right == 0) 
+		{
+			val = 0x8000;
+		} 
+		else 
+		{
+			if (oss_channel == SOUND_MIXER_IGAIN) 
+			{
+				right = (right * mh->scale) / 100;
+				left = (left * mh->scale) / 100;
+				if (right >= mh->scale)
+					right = mh->scale-1;
+				if (left >= mh->scale)
+					left = mh->scale-1;
+			} 
+			else 
+			{
+				right = ((100 - right) * mh->scale) / 100;
+				left = ((100 - left) * mh->scale) / 100;
+				if (right >= mh->scale)
+					right = mh->scale-1;
+				if (left >= mh->scale)
+					left = mh->scale-1;
+			}
+			val = (left << 8) | right;
+		}
+	} 
+	else if(left == 0) 
+	{
+		val = 0x8000;
+	} 
+	else if( (oss_channel == SOUND_MIXER_SPEAKER) ||
+			(oss_channel == SOUND_MIXER_PHONEIN) ||
+			(oss_channel == SOUND_MIXER_PHONEOUT) )
+	{
+		left = ((100 - left) * mh->scale) / 100;
+		if (left >= mh->scale)
+			left = mh->scale-1;
+		val = left;
+	} 
+	else if (oss_channel == SOUND_MIXER_MIC) 
+	{
+		val = peek( mh->offset) & ~0x801f;
+		left = ((100 - left) * mh->scale) / 100;
+		if (left >= mh->scale)
+			left = mh->scale-1;
+		val |= left;
+	} 
+	/*  
+	 * For bass and treble, the low bit is optional.  Masking it
+	 * lets us avoid the 0xf 'bypass'.
+	 * Do a read, modify, write as we have two contols in one reg. 
+	 */
+	else if (oss_channel == SOUND_MIXER_BASS) 
+	{
+		val = peek( mh->offset) & ~0x0f00;
+		left = ((100 - left) * mh->scale) / 100;
+		if (left >= mh->scale)
+			left = mh->scale-1;
+		val |= (left << 8) & 0x0e00;
+	} 
+	else if (oss_channel == SOUND_MIXER_TREBLE) 
+	{
+		val = peek( mh->offset) & ~0x000f;
+		left = ((100 - left) * mh->scale) / 100;
+		if (left >= mh->scale)
+			left = mh->scale-1;
+		val |= left & 0x000e;
+	}
+	
+	DPRINTK(" 0x%04x", val);
+
+	poke( mh->offset, val );
+
+#ifdef alsa_ac97_debug
+	val = peek( mh->offset );
+	DEBUG(" -> 0x%04x\n", val);
+#endif
+
+}
+
+/* a thin wrapper for write_mixer */
+static void ep93xx_set_mixer
+(
+	unsigned int oss_mixer, 
+	unsigned int val 
+) 
+{
+	unsigned int left,right;
+
+	/* cleanse input a little */
+	right = ((val >> 8)  & 0xff) ;
+	left = (val  & 0xff) ;
+
+	if (right > 100) right = 100;
+	if (left > 100) left = 100;
+
+	/*mixer_state[oss_mixer] = (right << 8) | left;*/
+	ep93xx_write_mixer( oss_mixer, left, right);
+}
+
+static void ep93xx_init_mixer(void)
+{
+	u16 cap;
+	int i;
+
+	/* mixer masks */
+	codec_supported_mixers 	= AC97_SUPPORTED_MASK;
+	
+	cap = peek( AC97_00_RESET );
+	if( !(cap & 0x04) )
+	{
+		codec_supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
+	}
+	if( !(cap & 0x10) )
+	{
+		codec_supported_mixers &= ~SOUND_MASK_ALTPCM;
+	}
+
+	/* 
+	 * Detect bit resolution of output volume controls by writing to the
+	 * 6th bit (not unmuting yet)
+	 */
+	poke( AC97_02_MASTER_VOL, 0xa020 );
+	if( peek( AC97_02_MASTER_VOL) != 0xa020 )
+	{
+		ac97_hw[SOUND_MIXER_VOLUME].scale = 32;
+	}
+
+	poke( AC97_04_HEADPHONE_VOL, 0xa020 );
+	if( peek( AC97_04_HEADPHONE_VOL) != 0xa020 )
+	{
+		ac97_hw[AC97_04_HEADPHONE_VOL].scale = 32;
+	}
+
+	poke( AC97_06_MONO_VOL, 0x8020 );
+	if( peek( AC97_06_MONO_VOL) != 0x8020 )
+	{
+		ac97_hw[AC97_06_MONO_VOL].scale = 32;
+	}
+
+	/* initialize mixer channel volumes */
+	for( i = 0; 
+		(i < SOUND_MIXER_NRDEVICES) && (mixer_defaults[i].mixer != -1) ; 
+		i++ ) 
+	{
+		if( !supported_mixer( mixer_defaults[i].mixer) )
+		{ 
+			continue;
+		}
+		
+		ep93xx_set_mixer( mixer_defaults[i].mixer, mixer_defaults[i].value);
+	}
+
+}
+
+static int ep93xx_set_recsource( int mask ) 
+{
+	unsigned int val;
+
+	/* Arg contains a bit for each recording source */
+	if( mask == 0 ) 
+	{
+		return 0;
+	}
+	
+	mask &= AC97_RECORD_MASK;
+	
+	if( mask == 0 ) 
+	{
+		return -EINVAL;
+	}
+				
+	/*
+	 * May have more than one bit set.  So clear out currently selected
+	 * record source value first (AC97 supports only 1 input) 
+	 */
+	val = (1 << ac97_rm2oss[peek( AC97_1A_RECORD_SELECT ) & 0x07]);
+	if (mask != val)
+	    mask &= ~val;
+       
+	val = ffs(mask); 
+	val = ac97_oss_rm[val-1];
+	val |= val << 8;  /* set both channels */
+
+	/*
+	 *
+	 */
+        val = peek( AC97_1A_RECORD_SELECT ) & 0x0707;
+        if ((val&0x0404)!=0)
+          val=0x0404;
+        else if((val&0x0000)!=0)
+          val=0x0101;
+
+
+	DPRINTK("ac97_codec: setting ac97 recmask to 0x%04x\n", val);
+
+	poke( AC97_1A_RECORD_SELECT, val);
+
+	return 0;
+}
+
+/*
+ * ep93xx_init_ac97_codec
+ *
+ * Program up the external Ac97 codec.
+ *
+ */
+static void ep93xx_init_ac97_codec( void )
+{
+	DPRINTK("ep93xx_init_ac97_codec - enter\n");
+
+	ep93xx_setup_src();
+	ep93xx_set_samplerate( AUDIO_SAMPLE_RATE_DEFAULT, 0 );
+	ep93xx_set_samplerate( AUDIO_SAMPLE_RATE_DEFAULT, 1 );
+	ep93xx_init_mixer();
+
+	DPRINTK("ep93xx_init_ac97_codec - EXIT\n");
+	
+}
+
+
+/*
+ * ep93xx_audio_init
+ * Audio interface
+ */
+static void ep93xx_audio_init(void)
+{
+	DPRINTK("ep93xx_audio_init - enter\n");
+	/*
+	 * Init the controller, enable the ac-link.
+	 * Initialize the codec.
+	 */	 
+	ep93xx_init_ac97_controller();
+	ep93xx_init_ac97_codec();
+	/*stop the audio loop from the input to the output directly*/
+	ep93xx_stop_loop();
+	
+#ifdef alsa_ac97_debug
+	ep93xx_dump_ac97_regs();
+#endif
+	DPRINTK("ep93xx_audio_init - EXIT\n");
+}
+                     
+/*====================================================================================*/                     
+
+
+static void print_audio_format( long format )
+{
+    switch( format ){
+	case SNDRV_PCM_FORMAT_S8:
+		DPRINTK( "AFMT_S8\n" );		   
+		break;
+			
+	case SNDRV_PCM_FORMAT_U8:		   
+		DPRINTK( "AFMT_U8\n" );		   
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+		DPRINTK( "AFMT_S16_LE\n" );		   
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_BE:
+		DPRINTK( "AFMT_S16_BE\n" );		   
+		break;
+
+	case SNDRV_PCM_FORMAT_U16_LE:
+		DPRINTK( "AFMT_U16_LE\n" );		   
+		break;
+	case SNDRV_PCM_FORMAT_U16_BE:
+		DPRINTK( "AFMT_U16_BE\n" );
+		break;
+			
+	case SNDRV_PCM_FORMAT_S24_LE:
+		DPRINTK( "AFMT_S24_LE\n" );		   
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_BE:
+		DPRINTK( "AFMT_S24_BE\n" );		   
+		break;
+
+	case SNDRV_PCM_FORMAT_U24_LE:
+		DPRINTK( "AFMT_U24_LE\n" );		   
+		break;
+
+	case SNDRV_PCM_FORMAT_U24_BE:
+		DPRINTK( "AFMT_U24_BE\n" );		   
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		DPRINTK( "AFMT_S24_LE\n" );		   
+		break;
+
+	case SNDRV_PCM_FORMAT_S32_BE:
+		DPRINTK( "AFMT_S24_BE\n" );		   
+		break;
+
+	case SNDRV_PCM_FORMAT_U32_LE:
+		DPRINTK( "AFMT_U24_LE\n" );		   
+		break;
+
+	case SNDRV_PCM_FORMAT_U32_BE:
+		DPRINTK( "AFMT_U24_BE\n" );		   
+		break;		
+	default:
+		DPRINTK( "ep93xx_i2s_Unsupported Audio Format\n" );		   
+		break;
+    }
+}
+
+static void audio_set_format( audio_stream_t * s, long val )
+{
+    DPRINTK( "ep93xx_i2s_audio_set_format enter.  Format requested (%d) %d ", 
+				(int)val,SNDRV_PCM_FORMAT_S16_LE);
+    print_audio_format( val );
+	
+    switch( val ){
+	case SNDRV_PCM_FORMAT_S8:
+		s->audio_format = SNDRV_PCM_FORMAT_S8;
+		s->audio_stream_bitwidth = 8;
+		break;
+			
+	case SNDRV_PCM_FORMAT_U8:		   
+		s->audio_format = SNDRV_PCM_FORMAT_U8;
+		s->audio_stream_bitwidth = 8;
+		break;
+			
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_S16_BE:
+		s->audio_format = SNDRV_PCM_FORMAT_S16_LE;
+		s->audio_stream_bitwidth = 16;
+		break;
+
+	case SNDRV_PCM_FORMAT_U16_LE:
+	case SNDRV_PCM_FORMAT_U16_BE:
+		s->audio_format = SNDRV_PCM_FORMAT_U16_LE;
+		s->audio_stream_bitwidth = 16;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_BE:		
+		s->audio_format = SNDRV_PCM_FORMAT_S24_LE;
+		s->audio_stream_bitwidth = 24;
+		break;
+
+	case SNDRV_PCM_FORMAT_U24_LE:
+	case SNDRV_PCM_FORMAT_U24_BE:		
+        	s->audio_format = SNDRV_PCM_FORMAT_U24_LE;
+		s->audio_stream_bitwidth = 24;
+		break;
+		
+	case SNDRV_PCM_FORMAT_U32_LE:
+	case SNDRV_PCM_FORMAT_U32_BE:
+	case SNDRV_PCM_FORMAT_S32_LE:
+	case SNDRV_PCM_FORMAT_S32_BE:				
+        	s->audio_format = SNDRV_PCM_FORMAT_S32_LE;
+		s->audio_stream_bitwidth = 32;
+		break;		
+	default:
+		DPRINTK( "ep93xx_i2s_Unsupported Audio Format\n" );	
+		break;
+    }
+
+    DPRINTK( "ep93xx_i2s_audio_set_format EXIT format set to be (%d) ", (int)s->audio_format );
+    print_audio_format( (long)s->audio_format );
+}
+
+static __inline__ unsigned long copy_to_user_S24_LE
+(
+    audio_stream_t *stream,
+    const char *to, 
+    unsigned long to_count
+)
+{
+    int *dma_buffer_0 = (int *)stream->hwbuf[0];
+    int *dma_buffer_1 = (int *)stream->hwbuf[1];
+    int *dma_buffer_2 = (int *)stream->hwbuf[2];
+			    
+    int total_to_count = to_count;
+    int *user_ptr = (int *)to;	/* 32 bit user buffer */
+    int count;
+    	
+    count = 8 * stream->dma_num_channels;
+	
+    while (to_count > 0){
+    
+	__put_user( (int)( *dma_buffer_0++ ), user_ptr++ );
+	__put_user( (int)( *dma_buffer_0++ ), user_ptr++ );
+	
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __put_user( (int)( *dma_buffer_1++ ), user_ptr++ );
+	    __put_user( (int)( *dma_buffer_1++ ), user_ptr++ );
+	}
+	
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __put_user( (int)( *dma_buffer_2++ ), user_ptr++ );
+	    __put_user( (int)( *dma_buffer_2++ ), user_ptr++ );
+	}
+	to_count -= count;
+    }
+    return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_U24_LE
+(
+    audio_stream_t *stream,
+    const char *to, 
+    unsigned long to_count
+)
+{
+    int *dma_buffer_0 = (int *)stream->hwbuf[0];
+    int *dma_buffer_1 = (int *)stream->hwbuf[1];
+    int *dma_buffer_2 = (int *)stream->hwbuf[2];
+			
+    int total_to_count = to_count;
+    unsigned int * user_ptr = (unsigned int *)to;	/* 32 bit user buffer */
+    int count;
+	
+    count = 8 * stream->dma_num_channels;
+	
+    while (to_count > 0){ 
+	__put_user( ((unsigned int)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
+	__put_user( ((unsigned int)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
+	
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __put_user( ((unsigned int)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
+	    __put_user( ((unsigned int)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
+	}
+	
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __put_user( ((unsigned int)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
+	    __put_user( ((unsigned int)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
+	}
+	to_count -= count;
+    }
+    return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_S16_LE
+(
+    audio_stream_t *stream,
+    const char *to, 
+    unsigned long to_count
+)
+{
+    int *dma_buffer_0 = (int *)stream->hwbuf[0];
+    int *dma_buffer_1 = (int *)stream->hwbuf[1];
+    int *dma_buffer_2 = (int *)stream->hwbuf[2];
+    int total_to_count = to_count;
+    short * user_ptr = (short *)to;	/* 16 bit user buffer */
+    int count;
+    	
+    count = 4 * stream->dma_num_channels;
+		
+    while (to_count > 0){
+
+	__put_user( (short)( *dma_buffer_0++ ), user_ptr++ );
+	__put_user( (short)( *dma_buffer_0++ ), user_ptr++ );
+
+        if( stream->audio_channels_flag & CHANNEL_REAR ){
+	    __put_user( (short)( *dma_buffer_1++ ), user_ptr++ );
+	    __put_user( (short)( *dma_buffer_1++ ), user_ptr++ );
+	}
+
+        if( stream->audio_channels_flag  & CHANNEL_CENTER_LFE ){
+	    __put_user( (short)( *dma_buffer_2++ ), user_ptr++ );
+	    __put_user( (short)( *dma_buffer_2++ ), user_ptr++ );
+	}
+	to_count -= count;
+    }
+    return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_U16_LE
+(
+    audio_stream_t *stream,
+    const char *to, 
+    unsigned long to_count
+)
+{
+    int *dma_buffer_0 = (int *)stream->hwbuf[0];
+    int *dma_buffer_1 = (int *)stream->hwbuf[1];
+    int *dma_buffer_2 = (int *)stream->hwbuf[2];
+    int count;
+    int total_to_count = to_count;
+    short * user_ptr = (short *)to;	/* 16 bit user buffer */
+
+    count = 4 * stream->dma_num_channels;
+		
+    while (to_count > 0){
+
+	__put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
+	__put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
+
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __put_user( ((unsigned short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
+	    __put_user( ((unsigned short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
+	}
+
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __put_user( ((unsigned short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
+	    __put_user( ((unsigned short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
+	}
+	to_count -= count;
+    }
+    return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_S8
+(
+    audio_stream_t *stream,
+    const char *to, 
+    unsigned long to_count
+)
+{
+    char *dma_buffer_0 = (char *)stream->hwbuf[0];
+    char *dma_buffer_1 = (char *)stream->hwbuf[1];
+    char *dma_buffer_2 = (char *)stream->hwbuf[2];
+    int count;
+    int total_to_count = to_count;
+    char * user_ptr = (char *)to;  /*  8 bit user buffer */
+
+    count = 2 * stream->dma_num_channels;
+	
+    dma_buffer_0++;
+    dma_buffer_1++;
+    dma_buffer_2++;
+		
+    while (to_count > 0){
+
+	__put_user( (char)( *dma_buffer_0 ), user_ptr++ );
+	dma_buffer_0 += 4;
+	__put_user( (char)( *dma_buffer_0 ), user_ptr++ );
+	dma_buffer_0 += 4;
+
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __put_user( (char)( *dma_buffer_1 ), user_ptr++ );
+            dma_buffer_1 += 4;
+	    __put_user( (char)( *dma_buffer_1 ), user_ptr++ );
+	    dma_buffer_1 += 4;
+	}
+
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __put_user( (char)( *dma_buffer_2 ), user_ptr++ );
+	    dma_buffer_2 += 4;
+	    __put_user( (char)( *dma_buffer_2 ), user_ptr++ );
+	    dma_buffer_2 += 4;
+	}
+	to_count -= count;
+    }
+    return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_U8
+(
+    audio_stream_t *stream,
+    const char *to, 
+    unsigned long to_count
+)
+{
+    char *dma_buffer_0 = (char *)stream->hwbuf[0];
+    char *dma_buffer_1 = (char *)stream->hwbuf[1];
+    char *dma_buffer_2 = (char *)stream->hwbuf[2];
+    int count;
+    int total_to_count = to_count;
+    char * user_ptr = (char *)to;  /*  8 bit user buffer */
+
+    count = 2 * stream->dma_num_channels;
+		
+    dma_buffer_0++;
+    dma_buffer_1++;
+    dma_buffer_2++;
+	
+    while (to_count > 0){
+	
+	__put_user( (char)( *dma_buffer_0 ) ^ 0x80, user_ptr++ );
+	dma_buffer_0 += 4;
+	__put_user( (char)( *dma_buffer_0 ) ^ 0x80, user_ptr++ );
+	dma_buffer_0 += 4;
+				
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ );
+	    dma_buffer_1 += 4;
+	    __put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ );
+	    dma_buffer_1 += 4;
+	}
+
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ );
+	    dma_buffer_2 += 4;
+	    __put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ );
+	    dma_buffer_2 += 4;
+	}
+	to_count -= count;
+    }
+    return total_to_count;
+}
+
+
+
+
+static __inline__ unsigned long copy_to_user_S16_LE_CM
+(
+    audio_stream_t *stream,
+    const char *to, 
+    unsigned long to_count
+)
+{
+    short *dma_buffer_0 = (short *)stream->hwbuf[0];
+    int *dma_buffer_1 = (int *)stream->hwbuf[1];
+    int *dma_buffer_2 = (int *)stream->hwbuf[2];
+    int total_to_count = to_count;
+    short * user_ptr = (short *)to;	/* 16 bit user buffer */
+    int count;
+    
+    	
+    count = 4 * stream->dma_num_channels;
+		
+    while (to_count > 0){
+    	if(stream->audio_num_channels == 2){
+		__put_user( (short)( *dma_buffer_0++ ), user_ptr++ );
+		__put_user( (short)( *dma_buffer_0++ ), user_ptr++ );
+		to_count -= count;
+	}
+	else{
+		dma_buffer_0++; 
+		__put_user( (short)( *dma_buffer_0++ ), user_ptr++ );
+		to_count -= 2;
+	}
+	
+        if( stream->audio_channels_flag & CHANNEL_REAR ){
+	    __put_user( (short)( *dma_buffer_1++ ), user_ptr++ );
+	    __put_user( (short)( *dma_buffer_1++ ), user_ptr++ );
+	}
+
+        if( stream->audio_channels_flag  & CHANNEL_CENTER_LFE ){
+	    __put_user( (short)( *dma_buffer_2++ ), user_ptr++ );
+	    __put_user( (short)( *dma_buffer_2++ ), user_ptr++ );
+	}
+	//to_count -= count;
+    }
+    return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_U16_LE_CM
+(
+    audio_stream_t *stream,
+    const char *to, 
+    unsigned long to_count
+)
+{
+    unsigned short *dma_buffer_0 = (unsigned short *)stream->hwbuf[0];
+    int *dma_buffer_1 = (int *)stream->hwbuf[1];
+    int *dma_buffer_2 = (int *)stream->hwbuf[2];
+    int count;
+    int total_to_count = to_count;
+    unsigned short * user_ptr = (unsigned short *)to;	/* 16 bit user buffer */
+
+    count = 4 * stream->dma_num_channels;
+		
+    while (to_count > 0){
+
+	if(stream->audio_num_channels == 2){
+		__put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
+		__put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
+		to_count -= count;
+	}
+	else{
+		dma_buffer_0++;
+		__put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
+		to_count -= 2;
+	}
+	
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __put_user( ((unsigned short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
+	    __put_user( ((unsigned short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
+	}
+
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __put_user( ((unsigned short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
+	    __put_user( ((unsigned short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
+	}
+	//to_count -= count;
+    }
+    return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_S8_CM
+(
+    audio_stream_t *stream,
+    const char *to, 
+    unsigned long to_count
+)
+{
+    unsigned short *dma_buffer_0 = (unsigned short *)stream->hwbuf[0];
+    char *dma_buffer_1 = (char *)stream->hwbuf[1];
+    char *dma_buffer_2 = (char *)stream->hwbuf[2];
+    int count;
+    int total_to_count = to_count;
+    char * user_ptr = (char *)to;  /*  8 bit user buffer */
+
+    count = 2 * stream->dma_num_channels;
+	
+    dma_buffer_0++;
+    dma_buffer_1++;
+    dma_buffer_2++;
+		
+    while (to_count > 0){	
+	if(stream->audio_num_channels == 2){
+		__put_user( (char)( *dma_buffer_0++ >> 8), user_ptr++ );
+		//dma_buffer_0 += 4;
+		__put_user( (char)( *dma_buffer_0++ >> 8), user_ptr++ );
+		//dma_buffer_0 += 4;
+		to_count -= count;
+	}
+	else{
+		dma_buffer_0++ ;
+		__put_user( (char)( *dma_buffer_0++ >> 8), user_ptr++ );
+		
+		to_count -= 1;
+	}
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __put_user( (char)( *dma_buffer_1 ), user_ptr++ );
+            dma_buffer_1 += 4;
+	    __put_user( (char)( *dma_buffer_1 ), user_ptr++ );
+	    dma_buffer_1 += 4;
+	}
+
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __put_user( (char)( *dma_buffer_2 ), user_ptr++ );
+	    dma_buffer_2 += 4;
+	    __put_user( (char)( *dma_buffer_2 ), user_ptr++ );
+	    dma_buffer_2 += 4;
+	}
+	//to_count -= count;
+    }
+    return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_U8_CM
+(
+    audio_stream_t *stream,
+    const char *to, 
+    unsigned long to_count
+)
+{
+    unsigned short *dma_buffer_0 = (unsigned short *)stream->hwbuf[0];
+    char *dma_buffer_1 = (char *)stream->hwbuf[1];
+    char *dma_buffer_2 = (char *)stream->hwbuf[2];
+    int count;
+    int total_to_count = to_count;
+    char * user_ptr = (char *)to;  /*  8 bit user buffer */
+
+    count = 2 * stream->dma_num_channels;
+		
+    dma_buffer_0++;
+    dma_buffer_1++;
+    dma_buffer_2++;
+	
+    while (to_count > 0){
+	if(stream->audio_num_channels == 2){
+		__put_user( (char)( *dma_buffer_0++  >>8) ^ 0x80, user_ptr++ );
+		//dma_buffer_0 += 4;
+		__put_user( (char)( *dma_buffer_0++  >>8) ^ 0x80, user_ptr++ );
+		//dma_buffer_0 += 4;
+		to_count -= count;
+	}
+	else{
+		dma_buffer_0++;
+		__put_user( (char)( *dma_buffer_0++  >>8) ^ 0x80, user_ptr++ );
+		//dma_buffer_0 += 4;
+		to_count--;
+	}
+	
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ );
+	    dma_buffer_1 += 4;
+	    __put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ );
+	    dma_buffer_1 += 4;
+	}
+
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ );
+	    dma_buffer_2 += 4;
+	    __put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ );
+	    dma_buffer_2 += 4;
+	}
+	//to_count -= count;
+    }
+    return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_U32
+(
+    audio_stream_t *stream,
+    const char *to, 
+    unsigned long to_count
+)
+{
+    char *dma_buffer_0 = (char *)stream->hwbuf[0];
+
+    if(__copy_to_user( (char *)to, dma_buffer_0, to_count))
+    {
+	return -EFAULT;
+    }
+    return to_count;
+}
+
+static __inline__ int copy_to_user_with_conversion
+(
+    audio_stream_t *stream,
+    const char *to, 
+    int toCount,
+    int bCompactMode
+)
+{
+    int ret = 0;
+	
+    if( toCount == 0 ){
+	DPRINTK("ep93xx_i2s_copy_to_user_with_conversion - nothing to copy!\n");
+    }
+    
+    if( bCompactMode == 1 ){
+        
+        switch( stream->audio_format ){
+
+	case SNDRV_PCM_FORMAT_S8:
+		ret = copy_to_user_S8_CM( stream, to, toCount );
+		break;
+			
+	case SNDRV_PCM_FORMAT_U8:
+		ret = copy_to_user_U8_CM( stream, to, toCount );
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+		ret = copy_to_user_S16_LE_CM( stream, to, toCount );
+		break;
+		
+	case SNDRV_PCM_FORMAT_U16_LE:
+		ret = copy_to_user_U16_LE_CM( stream, to, toCount );
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		//ret = copy_to_user_S24_LE( stream, to, toCount );
+		//break;
+		
+	case SNDRV_PCM_FORMAT_U24_LE:
+		//ret = copy_to_user_U24_LE( stream, to, toCount );
+		//break;
+		
+	case SNDRV_PCM_FORMAT_S32_LE:	
+        default:
+                DPRINTK( "ep93xx_i2s copy to user unsupported audio format %x\n",stream->audio_format );
+		break;
+        }
+        	
+    }
+    else{
+     
+        switch( stream->audio_format ){
+
+	case SNDRV_PCM_FORMAT_S8:
+		ret = copy_to_user_S8( stream, to, toCount );
+		break;
+			
+	case SNDRV_PCM_FORMAT_U8:
+		ret = copy_to_user_U8( stream, to, toCount );
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+		ret = copy_to_user_S16_LE( stream, to, toCount );
+		break;
+		
+	case SNDRV_PCM_FORMAT_U16_LE:
+		ret = copy_to_user_U16_LE( stream, to, toCount );
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		//ret = copy_to_user_S24_LE( stream, to, toCount );
+		//break;
+		
+	case SNDRV_PCM_FORMAT_U24_LE:
+		//ret = copy_to_user_U24_LE( stream, to, toCount );
+		//break;
+		DPRINTK( "ep93xx_i2s copy to user unsupported audio format %x\n",stream->audio_format );
+		break;
+		
+	case SNDRV_PCM_FORMAT_S32_LE:
+	
+		//__copy_to_user( (char *)to, from, toCount);
+		ret = copy_to_user_U32( stream, to, toCount );
+		break;	
+        default:
+                DPRINTK( "ep93xx_i2s copy to user unsupported audio format\n" );
+		break;
+        }
+    
+    }
+    return ret;
+}
+
+static __inline__ int copy_from_user_S24_LE
+(
+    audio_stream_t *stream,
+    const char *from, 
+    int toCount 
+)
+{
+    int *dma_buffer_0 = (int *)stream->hwbuf[0];
+    int *dma_buffer_1 = (int *)stream->hwbuf[1];
+    int *dma_buffer_2 = (int *)stream->hwbuf[2];
+    int count;
+
+    unsigned int * user_buffer = (unsigned int *)from;
+    unsigned int data;
+	
+    int toCount0 = toCount;
+    count = 8 * stream->dma_num_channels;
+	
+    while (toCount > 0){
+
+	__get_user(data, user_buffer++);
+	*dma_buffer_0++ = (unsigned int)data;
+	__get_user(data, user_buffer++);
+	*dma_buffer_0++ = (unsigned int)data;
+
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_1++ = (unsigned int)data;
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_1++ = (unsigned int)data;
+	}
+
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_2++ = (unsigned int)data;
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_2++ = (unsigned int)data;
+        }
+	toCount -= count;
+    }
+    return toCount0 / 2;
+}
+
+static __inline__ int copy_from_user_U24_LE
+(
+    audio_stream_t *stream,
+    const char *from, 
+    int toCount 
+)
+{
+    int *dma_buffer_0 = (int *)stream->hwbuf[0];
+    int *dma_buffer_1 = (int *)stream->hwbuf[1];
+    int *dma_buffer_2 = (int *)stream->hwbuf[2];
+    int count;
+    unsigned int * user_buffer = (unsigned int *)from;
+    unsigned int data;
+	
+    int toCount0 = toCount;
+    count = 8 * stream->dma_num_channels;
+	
+    while (toCount > 0){
+
+	__get_user(data, user_buffer++);
+	*dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
+	__get_user(data, user_buffer++);
+	*dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
+
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
+	}
+
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
+	}
+	toCount -= count;
+    }
+    return toCount0 / 2;
+}
+
+static __inline__ int copy_from_user_S16_LE
+(
+	audio_stream_t *stream,
+	const char *from, 
+	int toCount 
+)
+{
+    int *dma_buffer_0 = (int *)stream->hwbuf[0];
+    int *dma_buffer_1 = (int *)stream->hwbuf[1];
+    int *dma_buffer_2 = (int *)stream->hwbuf[2];
+    unsigned short *user_buffer = (unsigned short *)from;
+    unsigned short data;
+	
+    int toCount0 = toCount;
+    int count;
+    count = 8 * stream->dma_num_channels;
+	
+    while (toCount > 0){
+    
+	__get_user(data, user_buffer++);
+	*dma_buffer_0++ = data;
+	if(stream->audio_num_channels == 2){
+	    __get_user(data, user_buffer++);
+	}
+	*dma_buffer_0++ = data;
+	
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+    	    __get_user(data, user_buffer++);
+	    *dma_buffer_1++ = data;
+    	    __get_user(data, user_buffer++);
+	    *dma_buffer_1++ = data;
+	}
+
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_2++ = data;
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_2++ = data;
+	}
+	toCount -= count;
+    }
+    
+    if(stream->audio_num_channels == 1){
+    	return toCount0 / 4;	
+    }
+    return toCount0 / 2;
+}
+
+static __inline__ int copy_from_user_U16_LE
+(
+    audio_stream_t *stream,
+    const char *from, 
+    int toCount 
+)
+{
+    int *dma_buffer_0 = (int *)stream->hwbuf[0];
+    int *dma_buffer_1 = (int *)stream->hwbuf[1];
+    int *dma_buffer_2 = (int *)stream->hwbuf[2];
+    int count;
+    unsigned short * user_buffer = (unsigned short *)from;
+    unsigned short data;
+	
+    int toCount0 = toCount;
+    count = 8 * stream->dma_num_channels;
+	
+    while (toCount > 0){
+    
+	__get_user(data, user_buffer++);
+	*dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
+	if(stream->audio_num_channels == 2){
+	    __get_user(data, user_buffer++);
+	}
+	*dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
+
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
+    	    __get_user(data, user_buffer++);
+            *dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
+	}
+
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
+	    __get_user(data, user_buffer++);
+    	    *dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
+	}
+	toCount -= count;
+    }
+    
+    if(stream->audio_num_channels == 1){
+        return toCount0 / 4;
+    }
+    return toCount0 / 2;
+}
+
+static __inline__ int copy_from_user_S8
+(
+    audio_stream_t *stream,
+    const char *from, 
+    int toCount 
+)
+{
+    char *dma_buffer_0 = (char *)stream->hwbuf[0];
+    char *dma_buffer_1 = (char *)stream->hwbuf[1];
+    char *dma_buffer_2 = (char *)stream->hwbuf[2];
+    int count;
+    unsigned char * user_buffer = (unsigned char *)from;
+    unsigned char data;
+	
+    int toCount0 = toCount;
+    count = 8 * stream->dma_num_channels;
+
+    dma_buffer_0++;
+    dma_buffer_1++;
+    dma_buffer_2++;
+
+    while (toCount > 0){
+	__get_user(data, user_buffer++);
+	*dma_buffer_0 = data;
+	dma_buffer_0 += 4;
+	if(stream->audio_num_channels == 2){
+	    __get_user(data, user_buffer++);
+	}
+	*dma_buffer_0 = data;
+	dma_buffer_0 += 4;
+
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_1 = data;
+            dma_buffer_1 += 4;
+	    __get_user(data, user_buffer++);
+            *dma_buffer_1 = data;
+	    dma_buffer_1 += 4;
+	}
+	
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_2 = data;
+	    dma_buffer_2 += 4;
+	    __get_user(data, user_buffer++);
+    	    *dma_buffer_2 = data;
+            dma_buffer_2 += 4;
+	}
+	toCount -= count;
+    }
+    
+    if(stream->audio_num_channels == 1){
+    	return toCount0 / 8;
+    }
+    return toCount0 / 4;
+}
+
+static __inline__ int copy_from_user_U8
+(
+    audio_stream_t *stream,
+    const char *from, 
+    int toCount 
+)
+{
+    char *dma_buffer_0 = (char *)stream->hwbuf[0];
+    char *dma_buffer_1 = (char *)stream->hwbuf[1];
+    char *dma_buffer_2 = (char *)stream->hwbuf[2];
+    int count;
+    unsigned char *user_buffer = (unsigned char *)from;
+    unsigned char data;
+	
+    int toCount0 = toCount;
+    count = 8 * stream->dma_num_channels;
+	
+    dma_buffer_0 ++;
+    dma_buffer_1 ++;
+    dma_buffer_2 ++;
+			
+    while (toCount > 0){
+
+	__get_user(data, user_buffer++);
+	*dma_buffer_0 = ((unsigned char)data ^ 0x80);
+	dma_buffer_0 += 4;
+	if(stream->audio_num_channels == 2){
+	    __get_user(data, user_buffer++);
+	}
+	*dma_buffer_0 = ((unsigned char)data ^ 0x80);
+	dma_buffer_0 += 4;
+        
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_1 = ((unsigned char)data ^ 0x80);
+            dma_buffer_1 += 4;
+	    __get_user(data, user_buffer++);
+            *dma_buffer_1 = ((unsigned char)data ^ 0x80);
+            dma_buffer_1 += 4;
+	}
+	
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_2 = ((unsigned char)data ^ 0x80);
+    	    dma_buffer_2 += 4;
+	    __get_user(data, user_buffer++);
+    	    *dma_buffer_2 = ((unsigned char)data ^ 0x80);
+            dma_buffer_2 += 4;
+	}
+	toCount -= count;
+    }
+    
+    if(stream->audio_num_channels == 1){
+    	return toCount0 / 8;
+    }
+    return toCount0 / 4;
+}
+
+static __inline__ int copy_from_user_S16_LE_CM
+(
+	audio_stream_t *stream,
+	const char *from, 
+	int toCount 
+)
+{
+    unsigned int *dma_buffer_0 = (int *)stream->hwbuf[0];
+    unsigned int *dma_buffer_1 = (int *)stream->hwbuf[1];
+    unsigned int *dma_buffer_2 = (int *)stream->hwbuf[2];
+    unsigned short *user_buffer = (unsigned short *)from;
+    short data;
+    unsigned int val;	
+    int toCount0 = toCount;
+    int count;
+    count = 4 * stream->dma_num_channels;
+
+	//printk("count=%x tocount\n",count,toCount);	
+    while (toCount > 0){
+    
+	__get_user(data, user_buffer++);
+	//*dma_buffer_0++ = data;
+	val = (unsigned int)data & 0x0000ffff;
+	if(stream->audio_num_channels == 2){
+	    __get_user(data, user_buffer++);
+        }
+	*dma_buffer_0++ = ((unsigned int)data << 16) | val;
+	
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+    	    __get_user(data, user_buffer++);
+	    //*dma_buffer_1++ = data;
+	    val = (unsigned int)data & 0x0000ffff;
+    	    __get_user(data, user_buffer++);
+	    *dma_buffer_1++ = ((unsigned int)data << 16) | val;
+	}
+
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __get_user(data, user_buffer++);
+	    //*dma_buffer_2++ = data;
+	    val = (unsigned int)data & 0x0000ffff;
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_2++ = ((unsigned int)data << 16) | val;
+	}
+	toCount -= count;
+    }
+    
+    if(stream->audio_num_channels == 1){
+        return toCount0 /2 ;
+    }
+    
+    return toCount0 ;
+}
+
+static __inline__ int copy_from_user_U16_LE_CM
+(
+    audio_stream_t *stream,
+    const char *from, 
+    int toCount 
+)
+{
+    int *dma_buffer_0 = (int *)stream->hwbuf[0];
+    int *dma_buffer_1 = (int *)stream->hwbuf[1];
+    int *dma_buffer_2 = (int *)stream->hwbuf[2];
+    int count;
+    unsigned short * user_buffer = (unsigned short *)from;
+    unsigned short data;
+    unsigned int val;	
+    int toCount0 = toCount;
+    count = 4 * stream->dma_num_channels;
+	
+    while (toCount > 0){
+    
+	__get_user(data, user_buffer++);
+	//*dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
+	val = (unsigned int)data & 0x0000ffff;
+	if(stream->audio_num_channels == 2){
+	    __get_user(data, user_buffer++);
+        }
+	//*dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
+        *dma_buffer_0++ = (((unsigned int)data << 16) | val) ^ 0x80008000;
+
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __get_user(data, user_buffer++);
+	    //*dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
+	    val = (unsigned int)data & 0x0000ffff;
+    	    __get_user(data, user_buffer++);
+            //*dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
+            *dma_buffer_1++ = (((unsigned int)data << 16) | val) ^ 0x80008000;
+	}
+
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __get_user(data, user_buffer++);
+	    //*dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
+	    val = (unsigned int)data & 0x0000ffff;
+	    __get_user(data, user_buffer++);
+    	    //*dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
+    	    *dma_buffer_2++ = (((unsigned int)data << 16) | val) ^ 0x80008000;
+	}
+	toCount -= count;
+    }
+    
+    if(stream->audio_num_channels == 1){
+        return toCount0/2;
+    }
+    return toCount0 ;
+}
+
+static __inline__ int copy_from_user_S8_CM
+(
+    audio_stream_t *stream,
+    const char *from, 
+    int toCount 
+)
+{
+    char *dma_buffer_0 = (char *)stream->hwbuf[0];
+    char *dma_buffer_1 = (char *)stream->hwbuf[1];
+    char *dma_buffer_2 = (char *)stream->hwbuf[2];
+    int count;
+    unsigned char * user_buffer = (unsigned char *)from;
+    unsigned char data;	
+    int toCount0 = toCount;
+    count = 4 * stream->dma_num_channels;
+
+    dma_buffer_0++;
+    dma_buffer_1++;
+    dma_buffer_2++;
+
+    while (toCount > 0){
+	__get_user(data, user_buffer++);
+	*dma_buffer_0 = data;
+	*(dma_buffer_0 +1 ) = 0;
+	dma_buffer_0 += 2;
+	
+	if(stream->audio_num_channels == 2){
+	    __get_user(data, user_buffer++);
+	}
+	*dma_buffer_0 = data;
+	*(dma_buffer_0 +1 ) = 0;
+	dma_buffer_0 += 2;
+
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_1 = data;
+	    dma_buffer_1 += 2;
+	    __get_user(data, user_buffer++);
+            *dma_buffer_1 = data;
+            dma_buffer_1 += 2;
+	}
+	
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_2 = data;
+	    dma_buffer_2 += 2;
+	    __get_user(data, user_buffer++);
+    	    *dma_buffer_2 = data;
+    	    dma_buffer_2 += 2;
+	}
+	toCount -= count;
+    }
+    
+    if(stream->audio_num_channels == 1){
+        return toCount0 / 4;
+    }
+    
+    return toCount0 / 2;
+}
+
+static __inline__ int copy_from_user_U8_CM
+(
+    audio_stream_t *stream,
+    const char *from, 
+    int toCount 
+)
+{
+    unsigned char *dma_buffer_0 = (unsigned char *)stream->hwbuf[0];
+    unsigned char *dma_buffer_1 = (unsigned char *)stream->hwbuf[1];
+    unsigned char *dma_buffer_2 = (unsigned char *)stream->hwbuf[2];
+    int count;
+    unsigned char *user_buffer = (unsigned char *)from;
+    unsigned char data;
+	
+    int toCount0 = toCount;
+    count = 4 * stream->dma_num_channels;
+	
+    dma_buffer_0 ++;
+    dma_buffer_1 ++;
+    dma_buffer_2 ++;
+			
+    while (toCount > 0){
+
+	__get_user(data, user_buffer++);
+	*dma_buffer_0 = ((unsigned char)data ^ 0x80);
+	*(dma_buffer_0 +1 ) = 0;
+	dma_buffer_0 += 2;
+	
+	if(stream->audio_num_channels == 2){	
+	    __get_user(data, user_buffer++);
+	}
+	*dma_buffer_0 = ((unsigned char)data ^ 0x80);
+	*(dma_buffer_0 +1 ) = 0;
+	dma_buffer_0 += 2;
+
+	
+        if(stream->audio_channels_flag & CHANNEL_REAR ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_1 = ((unsigned char)data ^ 0x80);
+	    dma_buffer_1 += 2;
+	    __get_user(data, user_buffer++);
+            *dma_buffer_1 = ((unsigned char)data ^ 0x80);
+            dma_buffer_1 += 2;
+	}
+	
+        if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+	    __get_user(data, user_buffer++);
+	    *dma_buffer_2 = ((unsigned char)data ^ 0x80);
+    	    dma_buffer_2 += 2;
+	    __get_user(data, user_buffer++);
+    	    *dma_buffer_2 = ((unsigned char)data ^ 0x80);
+            dma_buffer_2 += 2;
+	}
+	toCount -= count;
+    }
+    
+    if(stream->audio_num_channels == 1){
+        return toCount0 / 4;
+    }
+    
+    return toCount0 / 2;
+}
+
+static int copy_from_user_U32
+(
+	audio_stream_t *stream,
+	const char *from, 
+	int toCount 
+)
+{
+    char *dma_buffer_0 = (char *)stream->hwbuf[0];
+	
+    if (copy_from_user( (char *)dma_buffer_0, from, toCount)) 
+    {
+	return -EFAULT;
+    }
+
+    return toCount;
+    
+}
+
+/*
+ * Returns negative for error
+ * Returns # of bytes transferred out of the from buffer
+ * for success.
+ */
+static __inline__ int copy_from_user_with_conversion
+(
+    audio_stream_t *stream,
+    const char *from, 
+    int toCount,
+    int bCompactMode 
+)
+{
+    int ret = 0;
+//    DPRINTK("copy_from_user_with_conversion\n");	
+    if( toCount == 0 ){
+    	DPRINTK("ep93xx_i2s_copy_from_user_with_conversion - nothing to copy!\n");
+    }
+
+    if( bCompactMode == 1){
+    	
+    	switch( stream->audio_format ){
+
+		case SNDRV_PCM_FORMAT_S8:
+			DPRINTK("SNDRV_PCM_FORMAT_S8 CM\n");
+			ret = copy_from_user_S8_CM( stream, from, toCount );
+			break;
+			
+		case SNDRV_PCM_FORMAT_U8:
+			DPRINTK("SNDRV_PCM_FORMAT_U8 CM\n");
+			ret = copy_from_user_U8_CM( stream, from, toCount );
+			break;
+
+		case SNDRV_PCM_FORMAT_S16_LE:
+			DPRINTK("SNDRV_PCM_FORMAT_S16_LE CM\n");
+			ret = copy_from_user_S16_LE_CM( stream, from, toCount );
+			break;
+				
+		case SNDRV_PCM_FORMAT_U16_LE:
+			DPRINTK("SNDRV_PCM_FORMAT_U16_LE CM\n");
+			ret = copy_from_user_U16_LE_CM( stream, from, toCount );
+			break;
+
+		case SNDRV_PCM_FORMAT_S24_LE:
+			DPRINTK("SNDRV_PCM_FORMAT_S24_LE CM\n");
+			//ret = copy_from_user_S24_LE( stream, from, toCount );
+			//break;
+		
+		case SNDRV_PCM_FORMAT_U24_LE:
+			DPRINTK("SNDRV_PCM_FORMAT_U24_LE CM\n");
+			//ret = copy_from_user_U24_LE( stream, from, toCount );
+			//break;
+		case SNDRV_PCM_FORMAT_S32_LE:
+			DPRINTK("SNDRV_PCM_FORMAT_S32_LE CM\n");
+			//break;
+        	default:
+                	DPRINTK( "ep93xx_i2s copy from user unsupported audio format\n" );
+			break;			
+    	}
+    }
+    else{
+        switch( stream->audio_format ){
+
+	case SNDRV_PCM_FORMAT_S8:
+		DPRINTK("SNDRV_PCM_FORMAT_S8\n");
+		ret = copy_from_user_S8( stream, from, toCount );
+		break;
+			
+	case SNDRV_PCM_FORMAT_U8:
+		DPRINTK("SNDRV_PCM_FORMAT_U8\n");
+		ret = copy_from_user_U8( stream, from, toCount );
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+		DPRINTK("SNDRV_PCM_FORMAT_S16_LE\n");
+		ret = copy_from_user_S16_LE( stream, from, toCount );
+		break;
+				
+	case SNDRV_PCM_FORMAT_U16_LE:
+		DPRINTK("SNDRV_PCM_FORMAT_U16_LE\n");
+		ret = copy_from_user_U16_LE( stream, from, toCount );
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		DPRINTK("SNDRV_PCM_FORMAT_S24_LE\n");
+		//ret = copy_from_user_S24_LE( stream, from, toCount );
+		//break;
+		
+	case SNDRV_PCM_FORMAT_U24_LE:
+		DPRINTK("SNDRV_PCM_FORMAT_U24_LE\n");
+		//ret = copy_from_user_U24_LE( stream, from, toCount );
+		//break;
+		DPRINTK( "ep93xx_i2s copy from user unsupported audio format\n" );
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		DPRINTK("SNDRV_PCM_FORMAT_S32_LE\n");
+		ret = copy_from_user_U32( stream, from, toCount );
+		break;
+        default:
+                DPRINTK( "ep93xx_i2s copy from user unsupported audio format\n" );
+		break;			
+    	}
+    }
+	
+    return ret;
+}
+
+
+
+/*
+ *  For audio playback, we convert samples of arbitrary format to be 32 bit 
+ *  for our hardware. We're scaling a user buffer to a dma buffer.  So when
+ *  report byte counts, we scale them acording to the ratio of DMA sample
+ *  size to user buffer sample size.  When we report # of DMA fragments,
+ *  we don't scale that.  So:
+ *
+ *  Also adjust the size and number of dma fragments if sample size changed.
+ *
+ *  Input format       Input sample     Output sample size    ratio (out:in)
+ *  bits   channels    size (bytes)       CM   non-CM          CM   non-CM
+ *   8      stereo         2		   4      8            2:1   4:1
+ *   16     stereo         4		   4      8            1:1   2:1
+ *   24     stereo         6		   4      8             X    8:6 not a real case
+ *
+ */
+static void snd_ep93xx_dma2usr_ratio( audio_stream_t * stream,int bCompactMode )
+{
+    unsigned int dma_sample_size, user_sample_size;
+	
+    if(bCompactMode == 1){	    	
+	dma_sample_size = 4;	/* each stereo sample is 2 * 32 bits */    
+    }    
+    else{    	
+    	dma_sample_size = 8;    
+    }
+	
+    // If stereo 16 bit, user sample is 4 bytes.
+    // If stereo  8 bit, user sample is 2 bytes.
+    if(stream->audio_num_channels == 1){
+    	user_sample_size = stream->audio_stream_bitwidth / 8;
+    }
+    else{
+    	user_sample_size = stream->audio_stream_bitwidth / 4;
+    }
+	
+    stream->dma2usr_ratio = dma_sample_size / user_sample_size;
+}
+
+static int snd_ep93xx_dma_free(struct snd_pcm_substream *substream ){
+
+    
+    audio_state_t *state = substream->private_data;
+    audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+                              state->output_stream:state->input_stream;
+    int i;
+    DPRINTK("snd_ep93xx_dma_free - enter\n");
+
+    for( i = 0 ; i < stream->dma_num_channels ;i++ ){
+	ep93xx_dma_free( stream->dmahandles[i] );
+    }
+    DPRINTK("snd_ep93xx_dma_free - exit\n");
+    return 0;	       
+}
+
+static int snd_ep93xx_dma_config(struct snd_pcm_substream *substream ){
+
+    audio_state_t *state = substream->private_data;
+    audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+                               state->output_stream:state->input_stream;
+    int i,err = 0;
+	
+    DPRINTK("snd_ep93xx_dma_config - enter\n");
+
+    for( i = 0 ; i < stream->dma_num_channels ;i++ ){
+    
+        err = ep93xx_dma_request(&stream->dmahandles[i],
+	                        stream->devicename,
+	                        (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+				state->output_dma[i]:state->input_dma[i] );
+        if (err){
+	    printk("snd_ep93xx_dma_config - exit ERROR dma request failed\n");
+	    return err;
+        }
+	err = ep93xx_dma_config( stream->dmahandles[i],
+    				IGNORE_CHANNEL_ERROR,
+				0,
+				(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 
+				snd_ep93xx_dma_tx_callback:snd_ep93xx_dma_rx_callback,
+				(unsigned int)substream );
+        if (err){
+	    printk("snd_ep93xx_dma_config - exit ERROR dma request failed\n");
+	    return err;
+	}
+    }
+
+    DPRINTK("snd_ep93xx_dma_config - enter\n");
+    return err;
+}
+
+static void snd_ep93xx_dma_start( audio_state_t * state, audio_stream_t * stream )
+{
+    int err,i;
+
+    DPRINTK("snd_ep93xx_dma_start - enter\n");
+
+    for(i = 0 ;i < stream->dma_num_channels;i++)
+	err = ep93xx_dma_start( stream->dmahandles[i], 1,(unsigned int *) stream->dmahandles );
+	
+    stream->active = 1;
+    
+    DPRINTK("snd_ep93xx_dma_start - exit\n");
+}
+
+static void snd_ep93xx_dma_pause( audio_state_t * state, audio_stream_t * stream )
+{
+    int i;
+ 
+    DPRINTK("snd_ep93xx_dma_pause - enter\n");
+
+    for(i = 0 ;i < stream->dma_num_channels;i++)
+	ep93xx_dma_pause( stream->dmahandles[i], 1,(unsigned int *)stream->dmahandles );
+
+    stream->active = 0;
+    DPRINTK("snd_ep93xx_dma_pause - exit\n");
+
+}
+
+static void snd_ep93xx_dma_flush( audio_state_t * state, audio_stream_t * stream ){
+
+    int i;
+    
+    DPRINTK("snd_ep93xx_dma_flush - enter\n");
+	
+    for( i = 0 ; i < stream->dma_num_channels ; i++ )
+	ep93xx_dma_flush( stream->dmahandles[i] );
+	   
+    DPRINTK("snd_ep93xx_dma_flush - exit\n");
+}
+
+static void snd_ep93xx_deallocate_buffers(struct snd_pcm_substream *substream, audio_stream_t *stream )
+{
+    int i;
+    audio_channel_t *dma_chan;
+    
+    DPRINTK("snd_ep93xx_deallocate_buffers - enter\n");
+    
+    if( stream->dma_channels ){
+
+        for(i = 0;i < stream->dma_num_channels;i++){
+
+	    dma_chan = &stream->dma_channels[i];
+
+	    if( dma_chan->area ){
+	    	    
+		if( dma_chan->audio_buffers ){
+
+		    kfree(dma_chan->audio_buffers);
+		    dma_chan->audio_buffers = NULL;
+		    
+		}
+
+		kfree(dma_chan->area);
+		dma_chan->area = NULL;
+	    }    
+	}
+	kfree(stream->dma_channels);
+	stream->dma_channels = NULL;
+    }
+    DPRINTK("snd_ep93xx_deallocate_buffers - exit\n");
+}
+
+static int snd_ep93xx_allocate_buffers( struct snd_pcm_substream *substream, audio_stream_t *stream)
+{
+    audio_channel_t *channel;
+    unsigned int size,tmpsize,bufsize,bufextsize;
+    int i,j;
+    
+        
+    DPRINTK("snd_ep93xx_allocate_buffers - enter\n" );
+
+    if (stream->dma_channels){
+	printk("ep93xx_i2s  %s BUSY\n",__FUNCTION__);
+        return -EBUSY;
+    }
+							       
+    stream->dma_channels = (audio_channel_t *)kmalloc(sizeof(audio_channel_t) * stream->dma_num_channels , GFP_KERNEL);
+	    
+    if (!stream->dma_channels){
+	printk(AUDIO_NAME ": unable to allocate dma_channels memory\n");
+	return - ENOMEM;
+    }
+    
+    size = ( stream->dmasize / stream->dma_num_channels ) * stream->dma2usr_ratio; 
+
+    for( i = 0; i < stream->dma_num_channels;i++){
+	channel = &stream->dma_channels[i];
+
+	channel->area = kmalloc( size, GFP_DMA );
+			
+	if(!channel->area){
+	    printk(AUDIO_NAME ": unable to allocate audio memory\n");
+	    return -ENOMEM;
+	}	
+	channel->bytes = size;
+	channel->addr = __virt_to_phys((int) channel->area);
+        memset( channel->area, 0, channel->bytes );
+
+	bufsize = ( stream->fragsize / stream->dma_num_channels ) * stream->dma2usr_ratio;
+	channel->audio_buff_count = size / bufsize;
+	bufextsize = size % bufsize;
+
+	if( bufextsize > 0 ){
+	    channel->audio_buff_count++;
+	}
+	
+	channel->audio_buffers = (audio_buf_t *)kmalloc(sizeof(audio_buf_t) * channel->audio_buff_count , GFP_KERNEL);
+		    
+	if (!channel->audio_buffers){
+	    printk(AUDIO_NAME ": unable to allocate audio memory\n ");
+	    return -ENOMEM;
+	}
+
+	tmpsize = size;
+
+	for( j = 0; j < channel->audio_buff_count; j++){
+
+	    channel->audio_buffers[j].dma_addr = channel->addr + j * bufsize;		
+
+	    if( tmpsize >= bufsize ){
+		tmpsize -= bufsize;
+		channel->audio_buffers[j].bytes = bufsize;
+		channel->audio_buffers[j].reportedbytes = bufsize / stream->dma2usr_ratio; 
+	    }
+	    else{
+                channel->audio_buffers[j].bytes = bufextsize;
+                channel->audio_buffers[j].reportedbytes = bufextsize / stream->dma2usr_ratio;
+	    }
+	}								
+    }
+
+    DPRINTK("snd_ep93xx_allocate_buffers -- exit SUCCESS\n" );
+    return 0;
+}
+
+/*
+ * DMA callback functions
+ */
+ 
+static void snd_ep93xx_dma_tx_callback
+( 
+	ep93xx_dma_int_t DMAInt,
+	ep93xx_dma_dev_t device, 
+	unsigned int user_data 
+)
+{
+    int handle;
+    int i,chan;
+    unsigned int buf_id;
+	    		
+    struct snd_pcm_substream *substream = (struct snd_pcm_substream *)user_data;
+    audio_state_t *state = (audio_state_t *)(substream->private_data);
+    audio_stream_t *stream = state->output_stream;
+    audio_buf_t *buf;
+
+    switch( device )              
+    {
+	case DMATx_I2S3:
+	    DPRINTK( "snd_ep93xx_dma_tx_callback - DMATx_I2S3\n");
+	    i = 2;
+	    break;
+    	case DMATx_I2S2:
+	    DPRINTK( "snd_ep93xx_dma_tx_callback - DMATx_I2S2\n");
+       	    i = 1;
+	    break;
+	case DMATx_I2S1:
+	    default:
+	    DPRINTK( "snd_ep93xx_dma_tx_callback - DMATx_I2S1\n");
+       	    i = 0;
+	    break;
+    }
+    
+    if(stream->audio_num_channels == 1){
+    	chan = 0;
+    }
+    else{
+        chan = stream->audio_num_channels / 2 - 1;
+    } 
+    handle = stream->dmahandles[i];
+
+    if(stream->stopped == 0){
+
+	if( ep93xx_dma_remove_buffer( handle, &buf_id ) >= 0 ){
+
+	    buf = (audio_buf_t *)buf_id;
+            stream->bytecount += buf->reportedbytes;
+	    ep93xx_dma_add_buffer( stream->dmahandles[i],
+				    (unsigned int)buf->dma_addr,
+				    0,
+				    buf->bytes,
+				    0,
+				    (unsigned int) buf );
+
+            if(chan == i)
+	        snd_pcm_period_elapsed(substream);
+	}
+    }
+}
+
+static void snd_ep93xx_dma_rx_callback
+(
+	ep93xx_dma_int_t DMAInt,
+	ep93xx_dma_dev_t device, 
+	unsigned int user_data 
+)
+{
+    int handle,i,chan;
+    unsigned int buf_id;
+    audio_buf_t *buf;
+		
+    struct snd_pcm_substream *substream = (struct snd_pcm_substream *)user_data;
+    audio_state_t *state = (audio_state_t *)(substream->private_data);
+    audio_stream_t *stream = state->input_stream;
+
+    switch( device ){
+		
+	case DMARx_I2S3:
+    	    DPRINTK( "snd_ep93xx_dma_rx_callback - DMARx_I2S3\n");
+	    i = 2;
+	    break;
+    	case DMARx_I2S2:
+          DPRINTK( "snd_ep93xx_dma_rx_callback - DMARx_I2S2\n");
+	    i = 1;
+	    break;
+	case DMARx_I2S1:
+	    default:
+	    DPRINTK( "snd_ep93xx_dma_rx_callback - DMARx_I2S1\n");
+	    i = 0;
+	    break;
+    }
+    
+    if(stream->audio_num_channels == 1){
+    	chan = 0;
+    }
+    else{
+        chan = stream->audio_num_channels / 2 - 1;
+    } 
+    handle = stream->dmahandles[i];
+    
+    if( stream->stopped == 0 ){
+	
+        if( ep93xx_dma_remove_buffer( handle, &buf_id ) >= 0 ){
+
+    	    buf = (audio_buf_t *)buf_id;
+	    stream->bytecount += buf->reportedbytes;
+	    ep93xx_dma_add_buffer( stream->dmahandles[i],
+				    (unsigned int)buf->dma_addr,
+				    0, 
+				    buf->bytes,
+				    0,
+				    (unsigned int) buf );
+
+            if( i == chan )
+                snd_pcm_period_elapsed(substream);
+	}
+    } 
+}
+
+static int snd_ep93xx_release(struct snd_pcm_substream *substream)
+{
+    audio_state_t *state = (audio_state_t *)substream->private_data;
+    audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+                             state->output_stream : state->input_stream;
+    
+    DPRINTK("snd_ep93xx_release - enter\n");
+
+    down(&state->sem);
+    stream->active = 0;
+    stream->stopped = 0;
+    snd_ep93xx_deallocate_buffers(substream, stream);
+    up(&state->sem);
+
+    DPRINTK("snd_ep93xx_release - exit\n");
+
+    return 0;
+}
+
+static int ep93xx_ac97_pcm_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int r;
+	int iTempMasterVol,iTempHeadphoneVol,iTempMonoVol,iTempRecordSelect;
+        /*save the old mixer*/
+      	iTempRecordSelect 	= peek(AC97_1A_RECORD_SELECT);
+        iTempMasterVol		= peek( AC97_02_MASTER_VOL);
+        iTempHeadphoneVol	= peek( AC97_04_HEADPHONE_VOL);
+        iTempMonoVol		= peek( AC97_06_MONO_VOL);
+
+	runtime->hw.channels_min = 1;
+	runtime->hw.channels_max = 2;
+
+ 	ep93xx_audio_init();
+	/*ep93xx_init_ac97_controller();*/
+
+        /*reset the old output mixer*/
+        poke( AC97_02_MASTER_VOL, iTempMasterVol);
+        poke( AC97_04_HEADPHONE_VOL,iTempHeadphoneVol );
+        poke( AC97_06_MONO_VOL, iTempMonoVol);
+	poke( AC97_1A_RECORD_SELECT,iTempRecordSelect);
+		
+	r = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+	    AC97_RATES_FRONT_DAC : AC97_RATES_ADC;
+	
+	DPRINTK(" ep93xx_ac97_pcm_startup=%x\n",r);
+
+		return 0;
+}
+
+
+static int snd_ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+        DPRINTK("snd_ep93xx_pcm_hw_params - enter\n");
+	return snd_pcm_lib_malloc_pages(substream,params_buffer_bytes(params));
+}
+
+static int snd_ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+
+	DPRINTK("snd_ep93xx_pcm_hw_free - enter\n");
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ *snd_ep93xx_pcm_prepare: need to finish these functions as lower
+ *chip_set_sample_format
+ *chip_set_sample_rate
+ *chip_set_channels
+ *chip_set_dma_setup
+ */
+
+static int snd_ep93xx_pcm_prepare_playback( struct snd_pcm_substream *substream)
+{
+    audio_state_t *state = (audio_state_t *) substream->private_data;
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    audio_stream_t *stream = state->output_stream;
+
+    DPRINTK("snd_ep93xx_pcm_prepare_playback - enter\n");
+    
+    ep93xx_audio_disable(1);
+    ep93xx_ac97_pcm_startup(substream);
+	        
+    snd_ep93xx_deallocate_buffers(substream,stream);
+			
+    //if(runtime->channels % 2 != 0)
+    //	return -1;
+		   	
+    DPRINTK("The runtime item : \n");
+    DPRINTK("runtime->dma_addr    = 0x%x\n", runtime->dma_addr);
+    DPRINTK("runtime->dma_area    = 0x%x\n", runtime->dma_area);
+    DPRINTK("runtime->dma_bytes   = %d\n",   runtime->dma_bytes);
+    DPRINTK("runtime->frame_bits  = %d\n",   runtime->frame_bits);
+    DPRINTK("runtime->buffer_size = %d\n",   runtime->buffer_size);
+    DPRINTK("runtime->period_size = %d\n",   runtime->period_size);
+    DPRINTK("runtime->periods     = %d\n",   runtime->periods);
+    DPRINTK("runtime->rate        = %d\n",   runtime->rate);
+    DPRINTK("runtime->format      = %d\n",   runtime->format);
+    DPRINTK("runtime->channels    = %d\n",   runtime->channels);
+	
+    /* set requestd format when available */
+    stream->audio_num_channels = runtime->channels;
+    if(stream->audio_num_channels == 1){
+    	stream->dma_num_channels = 1;
+    }
+    else{
+    	stream->dma_num_channels = runtime->channels / 2;
+    }
+
+    stream->audio_channels_flag = CHANNEL_FRONT;
+    if(stream->dma_num_channels == 2)
+        stream->audio_channels_flag |= CHANNEL_REAR;
+    if(stream->dma_num_channels == 3)
+        stream->audio_channels_flag |= CHANNEL_REAR | CHANNEL_CENTER_LFE;
+			    
+    stream->dmasize = runtime->dma_bytes;
+    stream->nbfrags = runtime->periods;
+    stream->fragsize = frames_to_bytes (runtime, runtime->period_size);
+    stream->bytecount = 0;
+
+    if( !state->codec_set_by_capture ){
+	state->codec_set_by_playback = 1;
+	
+	if( stream->audio_rate != runtime->rate ){
+	    ep93xx_set_samplerate( runtime->rate,0 );
+	}    
+	//if( stream->audio_format != runtime->format ){
+    	//    snd_ep93xx_i2s_init((stream->audio_stream_bitwidth == 24));
+	//}
+    }
+    else{
+        audio_stream_t *s = state->input_stream;
+        if( runtime->format != s->audio_format)
+    	    return -1;
+	if( runtime->rate != s->audio_rate )
+	    return -1;
+    }
+    
+    stream->audio_format = runtime->format ;	
+    ep93xx_set_hw_format(stream->audio_format,stream->audio_num_channels);
+    
+    
+    stream->audio_rate = runtime->rate;
+    audio_set_format( stream, runtime->format );
+    snd_ep93xx_dma2usr_ratio( stream,state->bCompactMode );
+	
+    if( snd_ep93xx_allocate_buffers( substream, stream ) != 0 ){
+        snd_ep93xx_deallocate_buffers( substream, stream );
+        return -1;
+    }
+    
+    ep93xx_audio_enable(1);
+												 
+    DPRINTK("snd_ep93xx_pcm_prepare_playback - exit\n");
+    return 0;	
+}
+
+static int snd_ep93xx_pcm_prepare_capture( struct snd_pcm_substream *substream)
+{
+    audio_state_t *state = (audio_state_t *) substream->private_data;
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    audio_stream_t *stream = state->input_stream;
+    
+    ep93xx_audio_disable(0);
+    ep93xx_ac97_pcm_startup(substream);
+	
+    snd_ep93xx_deallocate_buffers(substream,stream);
+
+    //if(runtime->channels % 2 != 0)
+	//return -1;
+		   		       
+    DPRINTK("snd_ep93xx_pcm_prepare_capture - enter\n");
+			
+//    printk("The runtime item : \n");
+//    printk("runtime->dma_addr    = 0x%x\n", runtime->dma_addr);
+//    printk("runtime->dma_area    = 0x%x\n", runtime->dma_area);
+//    printk("runtime->dma_bytes   = %d\n",   runtime->dma_bytes);
+//    printk("runtime->frame_bits  = %d\n",   runtime->frame_bits);
+//    printk("runtime->buffer_size = %d\n",   runtime->buffer_size);
+//    printk("runtime->period_size = %d\n",   runtime->period_size);
+//    printk("runtime->periods     = %d\n",   runtime->periods);
+//    printk("runtime->rate        = %d\n",   runtime->rate);
+//    printk("runtime->format      = %d\n",   runtime->format);
+//    printk("runtime->channels    = %d\n",   runtime->channels);
+	
+    /* set requestd format when available */
+    stream->audio_num_channels = runtime->channels;
+    if(stream->audio_num_channels == 1){
+    	stream->dma_num_channels = 1;
+    }
+    else{
+    	stream->dma_num_channels = runtime->channels / 2;
+    }
+
+    stream->audio_channels_flag = CHANNEL_FRONT;
+    if(stream->dma_num_channels == 2)
+	stream->audio_channels_flag |= CHANNEL_REAR;
+    if(stream->dma_num_channels == 3)
+	stream->audio_channels_flag |= CHANNEL_REAR | CHANNEL_CENTER_LFE;
+			    
+    stream->dmasize = runtime->dma_bytes;
+    stream->nbfrags = runtime->periods;
+    stream->fragsize = frames_to_bytes (runtime, runtime->period_size);
+    stream->bytecount = 0;
+
+    if( !state->codec_set_by_playback ){
+	state->codec_set_by_capture = 1;
+	
+	/*rate*/
+	if( stream->audio_rate != runtime->rate ){
+    	    ep93xx_set_samplerate( runtime->rate,1 );
+	}
+	
+	/*mixer*/
+	ep93xx_set_recsource(SOUND_MASK_MIC|SOUND_MASK_LINE1 | SOUND_MASK_LINE);
+	poke( AC97_1C_RECORD_GAIN, 0);
+	
+	/*format*/	
+        //if( stream->audio_format != runtime->format ){
+    	//    snd_ep93xx_i2s_init((stream->audio_stream_bitwidth == 24));
+	//}
+    }
+    else{
+        audio_stream_t *s = state->output_stream;
+        if( runtime->format != s->audio_format)
+    	    return -1;
+	if( runtime->rate != s->audio_rate )
+    	    return -1;
+    }
+    
+    stream->audio_format = runtime->format ;	
+    ep93xx_set_hw_format(stream->audio_format,stream->audio_num_channels);
+    
+    
+    stream->audio_rate = runtime->rate;
+    audio_set_format( stream, runtime->format );
+    snd_ep93xx_dma2usr_ratio( stream,state->bCompactMode );
+
+    if( snd_ep93xx_allocate_buffers( substream, stream ) != 0 ){
+        snd_ep93xx_deallocate_buffers( substream, stream );
+	return -1;
+    }
+    
+    ep93xx_audio_enable(0);
+												 
+    DPRINTK("snd_ep93xx_pcm_prepare_capture - exit\n");
+    return 0;	
+}
+/*
+ *start/stop/pause dma translate
+ */
+static int snd_ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{	
+    audio_state_t  *state = (audio_state_t *)substream->private_data;
+    audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+				state->output_stream:state->input_stream;
+    audio_buf_t *buf; 
+    audio_channel_t *dma_channel;
+    int i,count,ret = 0;
+    unsigned long flags;
+
+    DPRINTK("snd_ep93xx_pcm_triger %d - enter \n",cmd);
+					
+    switch (cmd){
+    
+	case SNDRV_PCM_TRIGGER_START:
+				
+	    snd_ep93xx_dma_config( substream );
+
+            stream->stopped = 0;
+		
+            if( !stream->active && !stream->stopped ){
+	        stream->active = 1;
+    		snd_ep93xx_dma_start( state, stream );
+            }
+
+            local_irq_save(flags);
+    
+	    for (i = 0; i < stream->dma_num_channels; i++){
+		dma_channel = &stream->dma_channels[i];
+
+		for(count = 0 ;count < dma_channel->audio_buff_count; count++){
+		
+		    buf = &dma_channel->audio_buffers[count];																	
+    		    ep93xx_dma_add_buffer( stream->dmahandles[i],
+					    (unsigned int)buf->dma_addr,
+		            		    0,
+		                	    buf->bytes,
+					    0,
+					    (unsigned int) buf );
+		}
+	    }	
+						
+	    local_irq_restore(flags);
+	    break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	    stream->stopped = 1;
+	    snd_ep93xx_dma_pause( state, stream );
+	    snd_ep93xx_dma_flush( state, stream );
+	    snd_ep93xx_dma_free( substream );
+	    break;
+
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	    break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	    break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	    break;
+
+	    default:
+	    ret = -EINVAL;
+    }
+    DPRINTK("snd_ep93xx_pcm_triger %d - exit \n",cmd);
+    return ret;
+}
+
+static snd_pcm_uframes_t snd_ep93xx_pcm_pointer_playback(struct snd_pcm_substream *substream)
+{
+    audio_state_t *state = (audio_state_t *)(substream->private_data);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    audio_stream_t *stream = state->output_stream;
+    snd_pcm_uframes_t pointer = 0;
+
+    pointer = bytes_to_frames( runtime,stream->bytecount );
+
+    if (pointer >= runtime->buffer_size){
+	pointer = 0;
+	stream->bytecount = 0;
+    }
+			    
+    DPRINTK("snd_ep93xx_pcm_pointer_playback - exit\n");
+    return pointer;
+}
+
+static snd_pcm_uframes_t snd_ep93xx_pcm_pointer_capture(struct snd_pcm_substream *substream)
+{
+    audio_state_t *state = (audio_state_t *)(substream->private_data);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    audio_stream_t *stream = state->input_stream;
+    snd_pcm_uframes_t pointer = 0;
+	
+    pointer = bytes_to_frames( runtime,stream->bytecount );
+	
+    if (pointer >= runtime->buffer_size){
+	pointer = 0;
+	stream->bytecount = 0;
+    }
+	
+    DPRINTK("snd_ep93xx_pcm_pointer_capture - exit\n");
+    return pointer;
+}
+
+static int snd_ep93xx_pcm_open(struct snd_pcm_substream *substream)
+{
+    audio_state_t *state = substream->private_data;
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+                                state->output_stream:state->input_stream;
+
+    DPRINTK("snd_ep93xx_pcm_open - enter\n");
+
+    down(&state->sem);
+            
+    runtime->hw = ep93xx_ac97_pcm_hardware;
+
+    stream->dma_num_channels = AUDIO_DEFAULT_DMACHANNELS;
+    stream->dma_channels = NULL;
+    stream->audio_rate = 0;
+    stream->audio_stream_bitwidth = 0;
+    	    
+    up(&state->sem);
+	
+    DPRINTK("snd_ep93xx_pcm_open - exit\n");
+    return 0;		
+}
+
+/*
+ *free the HW dma channel
+ *free the HW dma buffer
+ *free the Hw dma decrotion using function :kfree
+ */
+static int snd_ep93xx_pcm_close(struct snd_pcm_substream *substream)
+{
+    audio_state_t *state = (audio_state_t *)(substream->private_data);
+
+    DPRINTK("snd_ep93xx_pcm_close - enter\n");
+
+    snd_ep93xx_release(substream);
+
+    if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+	state->codec_set_by_playback = 0;
+    else
+	state->codec_set_by_capture = 0;
+
+    DPRINTK("snd_ep93xx_pcm_close - exit\n");
+    return 0;
+}
+
+static int snd_ep93xx_pcm_copy_playback(struct snd_pcm_substream * substream,int channel, 
+				snd_pcm_uframes_t pos,void __user *src, snd_pcm_uframes_t count)
+{
+
+    audio_state_t *state = (audio_state_t *)substream->private_data;
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    audio_stream_t *stream = state->output_stream ;
+    audio_channel_t *dma_channel;
+    int i;
+    int tocount = frames_to_bytes(runtime,count);
+    
+    for( i = 0; i < stream->dma_num_channels; i++ ){
+
+	dma_channel = &stream->dma_channels[i];	
+	stream->hwbuf[i] = dma_channel->area + ( frames_to_bytes(runtime,pos) * stream->dma2usr_ratio / stream->dma_num_channels );
+    
+    }
+
+    if(copy_from_user_with_conversion(stream ,(const char*)src,(tocount * stream->dma2usr_ratio),state->bCompactMode) <=0 ){
+	DPRINTK(KERN_ERR "copy_from_user_with_conversion() failed\n");
+	return -EFAULT;
+    }
+					
+    DPRINTK("snd_ep93xx_pcm_copy_playback - exit\n");
+    return 0;
+}
+
+
+static int snd_ep93xx_pcm_copy_capture(struct snd_pcm_substream * substream,int channel, 
+				snd_pcm_uframes_t pos,void __user *src, snd_pcm_uframes_t count)
+{
+    audio_state_t *state = (audio_state_t *)substream->private_data;
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    audio_stream_t *stream = state->input_stream ;
+    audio_channel_t *dma_channel;
+    int i;
+		       
+    int tocount = frames_to_bytes(runtime,count);
+			   
+    for( i = 0; i < stream->dma_num_channels; i++ ){
+  
+	dma_channel = &stream->dma_channels[i];
+	stream->hwbuf[i] = dma_channel->area + ( frames_to_bytes(runtime,pos) * stream->dma2usr_ratio / stream->dma_num_channels );
+
+    }
+
+    if(copy_to_user_with_conversion(stream,(const char*)src,tocount,state->bCompactMode) <=0 ){
+
+	DPRINTK(KERN_ERR "copy_to_user_with_conversion() failed\n");
+	return -EFAULT;
+    }
+										       
+    DPRINTK("snd_ep93xx_pcm_copy_capture - exit\n");
+    return 0;
+}
+
+/*----------------------------------------------------------------------------------*/
+static unsigned short ep93xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+	int val = -1;
+	/*volatile u32 *reg_addr;*/
+
+	DPRINTK(" number of codec:%x reg=%x\n",ac97->num,reg);
+	val=peek(reg);
+	if(val==-1){
+		printk(KERN_ERR "%s: read error (ac97_reg=%d )val=%x\n",
+				__FUNCTION__, reg, val);
+		return 0;
+	}
+
+	return val;
+}
+
+static void ep93xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
+{
+	/*volatile u32 *reg_addr;*/
+	int ret;
+
+	DPRINTK(" number of codec:%x rge=%x val=%x\n",ac97->num,reg,val);
+	ret=poke(reg, val);
+	if(ret!=0){
+		printk(KERN_ERR "%s: write error (ac97_reg=%d val=%x)\n",
+				__FUNCTION__, reg, val);
+	}
+
+}
+
+static void ep93xx_ac97_reset(struct snd_ac97 *ac97)
+{
+
+	DPRINTK(" ep93xx_ac97_reset\n");
+	ep93xx_audio_init();
+
+}
+
+static struct snd_ac97_bus_ops ep93xx_ac97_ops = {
+	.read	= ep93xx_ac97_read,
+	.write	= ep93xx_ac97_write,
+	.reset	= ep93xx_ac97_reset,
+};
+
+static struct snd_pcm *ep93xx_ac97_pcm;
+static struct snd_ac97 *ep93xx_ac97_ac97;																					     
+
+static struct snd_pcm_ops snd_ep93xx_pcm_playback_ops = {
+	.open		= snd_ep93xx_pcm_open,
+	.close		= snd_ep93xx_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_ep93xx_pcm_hw_params,
+	.hw_free	= snd_ep93xx_pcm_hw_free,
+	.prepare	= snd_ep93xx_pcm_prepare_playback,
+	.trigger	= snd_ep93xx_pcm_trigger,
+	.pointer	= snd_ep93xx_pcm_pointer_playback,
+	.copy		= snd_ep93xx_pcm_copy_playback,
+	
+};
+
+static struct snd_pcm_ops snd_ep93xx_pcm_capture_ops = {
+	.open		= snd_ep93xx_pcm_open,
+	.close		= snd_ep93xx_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_ep93xx_pcm_hw_params,
+	.hw_free	= snd_ep93xx_pcm_hw_free,
+	.prepare	= snd_ep93xx_pcm_prepare_capture,
+	.trigger	= snd_ep93xx_pcm_trigger,
+	.pointer	= snd_ep93xx_pcm_pointer_capture,
+	.copy 		= snd_ep93xx_pcm_copy_capture,
+};
+
+/*--------------------------------------------------------------------------*/
+
+
+static int snd_ep93xx_pcm_new(struct snd_card *card, audio_state_t *state, struct snd_pcm **rpcm)
+{
+    struct snd_pcm *pcm;
+    int play = state->output_stream? 1 : 0;/*SNDRV_PCM_STREAM_PLAYBACK*/
+    int capt = state->input_stream ? 1 : 0;/*SNDRV_PCM_STREAM_CAPTURE*/
+    int ret = 0;
+
+    DPRINTK("snd_ep93xx_pcm_new - enter\n");
+	
+    /* Register the new pcm device interface */
+    ret = snd_pcm_new(card, "EP93xx-AC97-PCM", 0, play, capt, &pcm);
+
+    if (ret){
+	DPRINTK("%s--%x:card=%x,play=%x,capt=%x,&pcm=%x\n",__FUNCTION__,ret,(int)card,play,capt,(int)pcm);
+	goto out;
+    }
+
+    /* allocate the pcm(DMA) memory */
+    ret = snd_pcm_lib_preallocate_pages_for_all(pcm, /*SNDRV_DMA_TYPE_DEV,0,*/SNDRV_DMA_TYPE_CONTINUOUS,snd_dma_continuous_data(GFP_KERNEL),128*1024,128*1024);
+
+    DPRINTK("The substream item : \n");
+    DPRINTK("pcm->streams[0].substream->dma_buffer.addr  = 0x%x\n", pcm->streams[0].substream->dma_buffer.addr);
+    DPRINTK("pcm->streams[0].substream->dma_buffer.area  = 0x%x\n", pcm->streams[0].substream->dma_buffer.area);
+    DPRINTK("pcm->streams[0].substream->dma_buffer.bytes = 0x%x\n", pcm->streams[0].substream->dma_buffer.bytes);
+    DPRINTK("pcm->streams[1].substream->dma_buffer.addr  = 0x%x\n", pcm->streams[1].substream->dma_buffer.addr);
+    DPRINTK("pcm->streams[1].substream->dma_buffer.area  = 0x%x\n", pcm->streams[1].substream->dma_buffer.area);
+    DPRINTK("pcm->streams[1].substream->dma_buffer.bytes = 0x%x\n", pcm->streams[1].substream->dma_buffer.bytes);	
+
+    pcm->private_data = state;
+	
+    /* seem to free the pcm data struct-->self dma buffer */
+    pcm->private_free = (void*) snd_pcm_lib_preallocate_free_for_all;
+
+    /* alsa pcm ops setting for SNDRV_PCM_STREAM_PLAYBACK */
+    if (play) {
+	int stream = SNDRV_PCM_STREAM_PLAYBACK;
+	snd_pcm_set_ops(pcm, stream, &snd_ep93xx_pcm_playback_ops);
+    }
+
+    /* alsa pcm ops setting for SNDRV_PCM_STREAM_CAPTURE */	
+    if (capt) {
+	int stream = SNDRV_PCM_STREAM_CAPTURE;
+	snd_pcm_set_ops(pcm, stream, &snd_ep93xx_pcm_capture_ops);
+    }
+
+    if (rpcm)
+	*rpcm = pcm;
+    DPRINTK("snd_ep93xx_pcm_new - exit\n");
+out:
+    return ret;
+}
+
+#ifdef CONFIG_PM
+
+int ep93xx_ac97_do_suspend(struct snd_card *card, pm_message_t state)
+{
+	if (card->power_state != SNDRV_CTL_POWER_D3cold) {
+		snd_pcm_suspend_all(ep93xx_ac97_pcm);
+		snd_ac97_suspend(ep93xx_ac97_ac97);
+		snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
+	}
+
+	return 0;
+}
+
+int ep93xx_ac97_do_resume(struct snd_card *card)
+{
+	if (card->power_state != SNDRV_CTL_POWER_D0) {
+
+		snd_ac97_resume(ep93xx_ac97_ac97);
+		snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	}
+
+	return 0;
+}
+
+int ep93xx_ac97_suspend(struct platform_device *_dev, pm_message_t state)
+{
+	snd_card_t *card = platform_get_drvdata(_dev);
+	int ret = 0;
+
+	if (card /*&& level == SUSPEND_DISABLE*/)
+		ret = ep93xx_ac97_do_suspend(card, PMSG_SUSPEND);
+
+	return ret;
+}
+
+int ep93xx_ac97_resume(struct platform_device *_dev)
+{
+	snd_card_t *card = platform_get_drvdata(_dev);
+	int ret = 0;
+
+	if (card /*&& level == RESUME_ENABLE*/)
+		ret = ep93xx_ac97_do_resume(card, SNDRV_CTL_POWER_D0);
+
+	return ret;
+}
+
+#else
+/*
+#define ep93xx_ac97_do_suspend		NULL
+#define ep93xx_ac97_do_resume		NULL
+#define ep93xx_ac97_suspend		NULL
+#define ep93xx_ac97_resume		NULL
+*/
+
+int ep93xx_ac97_do_suspend(struct snd_card *card, pm_message_t state)
+{                                                                                                                            
+        return 0;
+}
+                                                                                                                             
+int ep93xx_ac97_do_resume(struct snd_card *card)
+{                                                                                                                     
+        return 0;
+}
+
+int ep93xx_ac97_resume(struct platform_device *_dev)
+{
+        struct snd_card *card = platform_get_drvdata(_dev);
+        int ret = 0;
+                                                                                                                             
+        if (card /*&& level == RESUME_ENABLE*/)
+                ret = ep93xx_ac97_do_resume(card);
+                                                                                                                             
+        return ret;
+}
+
+int ep93xx_ac97_suspend(struct platform_device *_dev, pm_message_t state)
+{
+        struct snd_card *card = platform_get_drvdata(_dev);
+        int ret = 0;
+                                                                                                                             
+        if (card /*&& level == SUSPEND_DISABLE*/)
+                ret = ep93xx_ac97_do_suspend(card, PMSG_SUSPEND);
+                                                                                                                             
+        return ret;
+}
+
+#endif
+
+
+
+/* module init & exit */
+static int ep93xx_ac97_probe(struct platform_device *dev)
+{
+    struct snd_card *card;
+    struct ac97_bus *ac97_bus;
+    struct snd_ac97_template ac97_template;
+    int err = -ENOMEM;
+    struct resource *res = NULL;
+            
+    printk("snd_ep93xx_probe - enter\n");
+	
+    /* Enable audio early on, give the DAC time to come up. */ 
+    res = platform_get_resource( dev, IORESOURCE_MEM, 0);
+
+    if(!res) {
+	printk("error : platform_get_resource \n");
+        return -ENODEV;
+    }
+
+    if (!request_mem_region(res->start,res->end - res->start + 1, "snd-ac97-cs4202" )){
+    	printk("error : request_mem_region\n");
+        return -EBUSY;
+    }
+									    
+    /*enable ac97 codec*/
+    ep93xx_audio_init();
+			    
+    /* register the soundcard */
+    card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			    THIS_MODULE, 0);
+    if (!card){
+	printk("AC97: snd_card_new error\n");
+	goto error;
+    }
+    
+    card->dev = &dev->dev;
+    /*regist the new pcm device*/
+    err = snd_ep93xx_pcm_new(card, &audio_state, &ep93xx_ac97_pcm);
+    if (err){
+	printk("AC97: ep93xx_ac97_pcm_new error\n");
+	goto error;
+    }
+    if (card == NULL) {
+	DPRINTK(KERN_ERR "snd_card_new() failed\n");
+	goto error;
+    }
+
+    /*driver name*/
+    strcpy(card->driver, "CS4202A");
+    strcpy(card->shortname, "Cirrus Logic AC97 Audio ");
+    strcpy(card->longname, "Cirrus Logic AC97 Audio with CS4202A");
+
+    /*regist the new ac97 device*/
+    err = snd_ac97_bus(card, 0, &ep93xx_ac97_ops, NULL, &ac97_bus);
+    if (err){
+	printk("AC97: snd_ac97_bus error\n");
+	goto error;
+    }
+    
+    memset(&ac97_template, 0, sizeof(ac97_template));
+    err = snd_ac97_mixer(ac97_bus, &ac97_template, &ep93xx_ac97_ac97);
+    if (err){
+	printk("AC97: snd_ac97_mixer error\n");
+	goto error;
+    }
+   
+    /**/
+    ep93xx_audio_init();	 
+    /*setting the card device callback*/
+    //err = snd_card_set_pm_callback(card, ep93xx_ac97_do_suspend,ep93xx_ac97_do_resume, (void*)NULL);
+    //if(err != 0){
+    //	printk("snd_card_set_pm_callback error\n");
+    //}
+
+    /*regist the new CARD device*/
+    err = snd_card_register(card);
+    if (err == 0) {
+	printk( KERN_INFO "Cirrus Logic ep93xx ac97 audio initialized\n" );
+	platform_set_drvdata(dev,card);
+	DPRINTK("snd_ep93xx_probe - exit\n");
+    	return 0;
+    }
+
+error:
+    snd_card_free(card);
+    printk("snd_ep93xx_probe - error\n");
+    return err;
+
+return 0;
+}
+
+static int ep93xx_ac97_remove(struct platform_device *dev)
+{
+    struct resource *res;
+    struct snd_card *card = platform_get_drvdata(dev);
+
+    res = platform_get_resource( dev, IORESOURCE_MEM, 0);
+    release_mem_region(res->start, res->end - res->start + 1);
+	
+    DPRINTK("snd_ep93xx_ac97_remove - enter\n");
+    
+    if (card) {
+	snd_card_free(card);
+	platform_set_drvdata(dev, NULL);
+    }
+    DPRINTK("snd_ep93xx_remove - exit\n");
+
+return 0;
+}
+
+
+static struct platform_driver ep93xx_ac97_driver = {
+	.probe		= ep93xx_ac97_probe,
+	.remove		= ep93xx_ac97_remove,
+	.suspend	= ep93xx_ac97_suspend,
+	.resume		= ep93xx_ac97_resume,
+	.driver		= {
+		.name	= "ep93xx-ac97",
+	},
+};						
+						
+
+static int __init ep93xx_ac97_init(void)
+{
+    int ret;
+    
+    DPRINTK(KERN_INFO "%s: version %s\n", DRIVER_DESC, DRIVER_VERSION);
+    DPRINTK("snd_ep93xx_AC97_init - enter\n");	
+    ret = platform_driver_register(&ep93xx_ac97_driver);
+    DPRINTK("snd_ep93xx_AC97_init - exit\n");
+    return ret;										
+}
+
+static void __exit ep93xx_ac97_exit(void)
+{
+    DPRINTK("ep93xx_ac97_exit  - enter\n");
+    return platform_driver_unregister(&ep93xx_ac97_driver);
+}
+
+module_init(ep93xx_ac97_init);
+module_exit(ep93xx_ac97_exit);
+
+MODULE_DESCRIPTION("Cirrus Logic audio module");
+MODULE_LICENSE("GPL");
diff -Naur linux-2.6.25/sound/arm/ep93xx-ac97.h linux-2.6.25.ep93xx/sound/arm/ep93xx-ac97.h
--- linux-2.6.25/sound/arm/ep93xx-ac97.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.25.ep93xx/sound/arm/ep93xx-ac97.h	2008-09-12 14:50:40.000000000 -0500
@@ -0,0 +1,89 @@
+/*
+ * linux/sound/arm/ep93xx-i2s.c -- ALSA PCM interface for the edb93xx i2s audio
+ *
+ * Author:      Fred Wei
+ * Created:     July 19, 2005
+ * Copyright:   Cirrus Logic, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define EP93XX_DEFAULT_NUM_CHANNELS     2
+#define EP93XX_DEFAULT_FORMAT           SNDRV_PCM_FORMAT_S16_LE
+#define EP93XX_DEFAULT_BIT_WIDTH        16
+#define MAX_DEVICE_NAME 		20
+
+/*
+ * Buffer Management
+ */
+				
+typedef struct {
+
+    unsigned char	*area;    	/* virtual pointer */
+    dma_addr_t 		dma_addr;       /* physical address */
+    size_t 		bytes;      
+    size_t 		reportedbytes;	/* buffer size */
+    int 		sent;		/* indicates that dma has the buf */
+    char		*start;		/* points to actual buffer */
+
+} audio_buf_t;
+
+
+typedef struct {
+
+    unsigned char	*area;  		/* virtual pointer */
+    dma_addr_t 		addr;        		/* physical address */
+    size_t 		bytes;          	/* buffer size in bytes */
+    unsigned char      	*buff_pos;              /* virtual pointer */
+    audio_buf_t        	*audio_buffers; 	/* array of audio buffer structures */
+    int 		audio_buff_count;
+		
+
+} audio_channel_t;
+
+typedef struct audio_stream_s {
+
+    /* dma stuff */
+    int			dmahandles[3];		/* handles for dma driver instances */
+    char		devicename[MAX_DEVICE_NAME]; /* string - name of device */
+    int			dma_num_channels;		/* 1, 2, or 3 DMA channels */
+    audio_channel_t	*dma_channels;
+    u_int 		nbfrags;		/* nbr of fragments i.e. buffers */
+    u_int		fragsize;		/* fragment i.e. buffer size */
+    u_int		dmasize;
+    int 		bytecount;		/* nbr of processed bytes */
+    int 		externedbytecount;	/* nbr of processed bytes */
+    volatile int        active;                 /* actually in progress                 */
+    volatile int        stopped;                /* might be active but stopped          */
+    char 		*hwbuf[3];
+    long		audio_rate;
+    long 		audio_num_channels;		/* Range: 1 to 6 */
+    int			audio_channels_flag;
+    long 		audio_format;
+    long 		audio_stream_bitwidth;		/* Range: 8, 16, 24 */
+    int			dma2usr_ratio;
+
+} audio_stream_t;
+
+
+/*
+ * State structure for one instance
+ */
+typedef struct {
+	    
+    audio_stream_t 	*output_stream;
+    audio_stream_t 	*input_stream;
+    ep93xx_dma_dev_t	output_dma[3];
+    ep93xx_dma_dev_t	input_dma[3];
+    char 		*output_id[3];
+    char 		*input_id[3];
+    struct              semaphore sem;          /* to protect against races in attach() */
+    int			codec_set_by_playback;
+    int                 codec_set_by_capture;
+    int                 DAC_bit_width;          /* 16, 20, 24 bits */
+    int                 bCompactMode;           /* set if 32bits = a stereo sample */
+	
+} audio_state_t;
+
diff -Naur linux-2.6.25/sound/arm/ep93xx-i2s.c linux-2.6.25.ep93xx/sound/arm/ep93xx-i2s.c
--- linux-2.6.25/sound/arm/ep93xx-i2s.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.25.ep93xx/sound/arm/ep93xx-i2s.c	2008-12-08 09:47:45.000000000 -0600
@@ -0,0 +1,3069 @@
+/*
+ * linux/sound/arm/ep93xx-i2s.c -- ALSA PCM interface for the edb93xx i2s audio
+ */
+
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+#include <asm/irq.h>
+#include <asm/semaphore.h>
+#include <asm/hardware.h>
+#include <asm/io.h>                                                                                                                             
+#include <asm/arch/dma.h>
+#include <asm/arch/regs_i2s.h>
+#include <asm/arch/ssp.h>
+
+#include <linux/spi/spi.h>
+
+#include "ep93xx-i2s.h"
+
+
+//#define DEBUG 1
+#ifdef DEBUG
+#define DPRINTK( fmt, arg... )  printk(KERN_ERR fmt, ##arg )
+#else
+#define DPRINTK( fmt, arg... )
+#endif
+
+#define WL16 	0
+#define WL24	1
+
+#define AUDIO_NAME              	"ep93xx-i2s"
+#define AUDIO_SAMPLE_RATE_DEFAULT       44100
+#define AUDIO_DEFAULT_VOLUME            0
+#define AUDIO_MAX_VOLUME	        181
+#define AUDIO_DEFAULT_DMACHANNELS       3
+#define PLAYBACK_DEFAULT_DMACHANNELS    3
+#define CAPTURE_DEFAULT_DMACHANNELS     3
+
+#define CHANNEL_FRONT			(1<<0)
+#define CHANNEL_REAR                   	(1<<1)
+#define CHANNEL_CENTER_LFE              (1<<2)
+
+#ifdef CONFIG_CODEC_CS4271
+static int 	mute = 0;
+#endif
+
+static int 	index = SNDRV_DEFAULT_IDX1;          /* Index 0-MAX */
+static char 	*id = SNDRV_DEFAULT_STR1;            /* ID for this card */
+//static int 	SSP_Handle = -1;
+
+static void snd_ep93xx_dma_tx_callback( ep93xx_dma_int_t DMAInt,
+		ep93xx_dma_dev_t device,
+		unsigned int user_data);
+static void snd_ep93xx_dma_rx_callback( ep93xx_dma_int_t DMAInt,
+		ep93xx_dma_dev_t device,
+		unsigned int user_data);
+
+static const struct snd_pcm_hardware ep93xx_i2s_pcm_hardware = {
+
+
+	.info		= ( SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE  ),
+	.formats	= ( SNDRV_PCM_FMTBIT_U8     | SNDRV_PCM_FMTBIT_S8     |
+			SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+			SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
+			SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE ),
+	.rates		= ( SNDRV_PCM_RATE_8000  | SNDRV_PCM_RATE_11025 |
+			SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000),
+	.rate_min		= 8000,
+	.rate_max		= 96000,
+	.channels_min	= 1,
+#ifdef CONFIG_CODEC_CS4271
+	.channels_max	= 2,
+#else//CONFIG_CODEC_CS4228A
+	.channels_max	= 6,
+#endif
+	.period_bytes_min	= 1 * 1024,
+	.period_bytes_max	= 32 * 1024,
+	.periods_min	= 1,
+	.periods_max	= 32,
+	.buffer_bytes_max	= 32 * 1024,
+	.fifo_size		= 0,
+};
+
+static int ep93xx_calc_closest_freq
+(
+ ulong   ulPLLFreq, 
+ ulong   ulRequestedMClkFreq,
+ ulong * pulActualMClkFreq,
+ ulong * pulI2SDiv
+ );
+
+static audio_stream_t output_stream;
+static audio_stream_t input_stream;
+
+static audio_state_t audio_state =
+{
+	.output_stream          = &output_stream,
+	.output_dma[0]          = DMATx_I2S1,
+	.output_id[0]           = "I2S_OUT1",
+	.output_dma[1]          = DMATx_I2S2,
+	.output_id[1]           = "I2S_OUT2",
+	.output_dma[2]          = DMATx_I2S3,
+	.output_id[2]           = "I2S_OUT3",
+
+
+	.input_stream           = &input_stream,
+	.input_dma[0]           = DMARx_I2S1,
+	.input_id[0]            = "I2S_IN1",
+	.input_dma[1]           = DMARx_I2S2,
+	.input_id[1]            = "I2S_IN2",
+	.input_dma[2]           = DMARx_I2S3,
+	.input_id[2]            = "I2S_IN3",
+
+	.sem                    = __SEMAPHORE_INIT(audio_state.sem,1),
+	.codec_set_by_playback  = 0,
+	.codec_set_by_capture   = 0,
+};			
+
+static struct snd_pcm *ep93xx_i2s_pcm;
+
+typedef struct {
+	ulong   ulTotalDiv;
+	ulong   ulI2SDiv;
+} DIV_TABLE;
+
+static const DIV_TABLE I2SDivTable[] =
+{
+	{   6, SYSCON_I2SDIV_PDIV_2  | (  2 & SYSCON_I2SDIV_MDIV_MASK) },
+	{   8, SYSCON_I2SDIV_PDIV_2  | (  2 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  10, SYSCON_I2SDIV_PDIV_25 | (  2 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  12, SYSCON_I2SDIV_PDIV_3  | (  2 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  15, SYSCON_I2SDIV_PDIV_25 | (  3 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  16, SYSCON_I2SDIV_PDIV_2  | (  4 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  18, SYSCON_I2SDIV_PDIV_3  | (  3 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  20, SYSCON_I2SDIV_PDIV_25 | (  4 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  24, SYSCON_I2SDIV_PDIV_3  | (  4 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  25, SYSCON_I2SDIV_PDIV_25 | (  5 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  28, SYSCON_I2SDIV_PDIV_2  | (  7 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  30, SYSCON_I2SDIV_PDIV_3  | (  5 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  32, SYSCON_I2SDIV_PDIV_2  | (  8 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  35, SYSCON_I2SDIV_PDIV_25 | (  7 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  36, SYSCON_I2SDIV_PDIV_3  | (  6 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  40, SYSCON_I2SDIV_PDIV_25 | (  8 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  42, SYSCON_I2SDIV_PDIV_3  | (  7 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  44, SYSCON_I2SDIV_PDIV_2  | ( 11 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  45, SYSCON_I2SDIV_PDIV_25 | (  9 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  48, SYSCON_I2SDIV_PDIV_3  | (  8 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  50, SYSCON_I2SDIV_PDIV_25 | ( 10 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  52, SYSCON_I2SDIV_PDIV_2  | ( 13 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  54, SYSCON_I2SDIV_PDIV_3  | (  9 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  55, SYSCON_I2SDIV_PDIV_25 | ( 11 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  56, SYSCON_I2SDIV_PDIV_2  | ( 14 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  60, SYSCON_I2SDIV_PDIV_3  | ( 10 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  64, SYSCON_I2SDIV_PDIV_2  | ( 16 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  65, SYSCON_I2SDIV_PDIV_25 | ( 13 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  66, SYSCON_I2SDIV_PDIV_3  | ( 11 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  68, SYSCON_I2SDIV_PDIV_2  | ( 17 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  70, SYSCON_I2SDIV_PDIV_25 | ( 14 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  72, SYSCON_I2SDIV_PDIV_3  | ( 12 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  75, SYSCON_I2SDIV_PDIV_25 | ( 15 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  76, SYSCON_I2SDIV_PDIV_2  | ( 19 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  78, SYSCON_I2SDIV_PDIV_3  | ( 13 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  80, SYSCON_I2SDIV_PDIV_25 | ( 16 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  84, SYSCON_I2SDIV_PDIV_3  | ( 14 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  85, SYSCON_I2SDIV_PDIV_25 | ( 17 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  88, SYSCON_I2SDIV_PDIV_2  | ( 22 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  90, SYSCON_I2SDIV_PDIV_3  | ( 15 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  92, SYSCON_I2SDIV_PDIV_2  | ( 23 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  95, SYSCON_I2SDIV_PDIV_25 | ( 19 & SYSCON_I2SDIV_MDIV_MASK) },
+	{  96, SYSCON_I2SDIV_PDIV_3  | ( 16 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 100, SYSCON_I2SDIV_PDIV_25 | ( 20 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 102, SYSCON_I2SDIV_PDIV_3  | ( 17 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 104, SYSCON_I2SDIV_PDIV_2  | ( 26 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 105, SYSCON_I2SDIV_PDIV_25 | ( 21 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 108, SYSCON_I2SDIV_PDIV_3  | ( 18 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 110, SYSCON_I2SDIV_PDIV_25 | ( 22 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 112, SYSCON_I2SDIV_PDIV_2  | ( 28 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 114, SYSCON_I2SDIV_PDIV_3  | ( 19 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 115, SYSCON_I2SDIV_PDIV_25 | ( 23 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 116, SYSCON_I2SDIV_PDIV_2  | ( 29 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 120, SYSCON_I2SDIV_PDIV_3  | ( 20 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 124, SYSCON_I2SDIV_PDIV_2  | ( 31 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 125, SYSCON_I2SDIV_PDIV_25 | ( 25 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 126, SYSCON_I2SDIV_PDIV_3  | ( 21 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 128, SYSCON_I2SDIV_PDIV_2  | ( 32 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 130, SYSCON_I2SDIV_PDIV_25 | ( 26 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 132, SYSCON_I2SDIV_PDIV_3  | ( 22 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 135, SYSCON_I2SDIV_PDIV_25 | ( 27 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 136, SYSCON_I2SDIV_PDIV_2  | ( 34 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 138, SYSCON_I2SDIV_PDIV_3  | ( 23 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 140, SYSCON_I2SDIV_PDIV_25 | ( 28 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 144, SYSCON_I2SDIV_PDIV_3  | ( 24 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 145, SYSCON_I2SDIV_PDIV_25 | ( 29 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 148, SYSCON_I2SDIV_PDIV_2  | ( 37 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 150, SYSCON_I2SDIV_PDIV_3  | ( 25 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 152, SYSCON_I2SDIV_PDIV_2  | ( 38 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 155, SYSCON_I2SDIV_PDIV_25 | ( 31 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 156, SYSCON_I2SDIV_PDIV_3  | ( 26 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 160, SYSCON_I2SDIV_PDIV_25 | ( 32 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 162, SYSCON_I2SDIV_PDIV_3  | ( 27 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 164, SYSCON_I2SDIV_PDIV_2  | ( 41 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 165, SYSCON_I2SDIV_PDIV_25 | ( 33 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 168, SYSCON_I2SDIV_PDIV_3  | ( 28 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 170, SYSCON_I2SDIV_PDIV_25 | ( 34 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 172, SYSCON_I2SDIV_PDIV_2  | ( 43 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 174, SYSCON_I2SDIV_PDIV_3  | ( 29 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 175, SYSCON_I2SDIV_PDIV_25 | ( 35 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 176, SYSCON_I2SDIV_PDIV_2  | ( 44 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 180, SYSCON_I2SDIV_PDIV_3  | ( 30 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 184, SYSCON_I2SDIV_PDIV_2  | ( 46 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 185, SYSCON_I2SDIV_PDIV_25 | ( 37 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 186, SYSCON_I2SDIV_PDIV_3  | ( 31 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 188, SYSCON_I2SDIV_PDIV_2  | ( 47 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 190, SYSCON_I2SDIV_PDIV_25 | ( 38 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 192, SYSCON_I2SDIV_PDIV_3  | ( 32 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 195, SYSCON_I2SDIV_PDIV_25 | ( 39 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 196, SYSCON_I2SDIV_PDIV_2  | ( 49 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 198, SYSCON_I2SDIV_PDIV_3  | ( 33 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 200, SYSCON_I2SDIV_PDIV_25 | ( 40 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 204, SYSCON_I2SDIV_PDIV_3  | ( 34 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 205, SYSCON_I2SDIV_PDIV_25 | ( 41 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 208, SYSCON_I2SDIV_PDIV_2  | ( 52 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 210, SYSCON_I2SDIV_PDIV_3  | ( 35 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 212, SYSCON_I2SDIV_PDIV_2  | ( 53 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 215, SYSCON_I2SDIV_PDIV_25 | ( 43 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 216, SYSCON_I2SDIV_PDIV_3  | ( 36 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 220, SYSCON_I2SDIV_PDIV_25 | ( 44 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 222, SYSCON_I2SDIV_PDIV_3  | ( 37 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 224, SYSCON_I2SDIV_PDIV_2  | ( 56 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 225, SYSCON_I2SDIV_PDIV_25 | ( 45 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 228, SYSCON_I2SDIV_PDIV_3  | ( 38 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 230, SYSCON_I2SDIV_PDIV_25 | ( 46 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 232, SYSCON_I2SDIV_PDIV_2  | ( 58 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 234, SYSCON_I2SDIV_PDIV_3  | ( 39 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 235, SYSCON_I2SDIV_PDIV_25 | ( 47 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 236, SYSCON_I2SDIV_PDIV_2  | ( 59 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 240, SYSCON_I2SDIV_PDIV_3  | ( 40 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 244, SYSCON_I2SDIV_PDIV_2  | ( 61 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 245, SYSCON_I2SDIV_PDIV_25 | ( 49 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 246, SYSCON_I2SDIV_PDIV_3  | ( 41 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 248, SYSCON_I2SDIV_PDIV_2  | ( 62 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 250, SYSCON_I2SDIV_PDIV_25 | ( 50 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 252, SYSCON_I2SDIV_PDIV_3  | ( 42 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 255, SYSCON_I2SDIV_PDIV_25 | ( 51 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 256, SYSCON_I2SDIV_PDIV_2  | ( 64 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 258, SYSCON_I2SDIV_PDIV_3  | ( 43 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 260, SYSCON_I2SDIV_PDIV_25 | ( 52 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 264, SYSCON_I2SDIV_PDIV_3  | ( 44 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 265, SYSCON_I2SDIV_PDIV_25 | ( 53 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 268, SYSCON_I2SDIV_PDIV_2  | ( 67 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 270, SYSCON_I2SDIV_PDIV_3  | ( 45 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 272, SYSCON_I2SDIV_PDIV_2  | ( 68 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 275, SYSCON_I2SDIV_PDIV_25 | ( 55 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 276, SYSCON_I2SDIV_PDIV_3  | ( 46 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 280, SYSCON_I2SDIV_PDIV_25 | ( 56 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 282, SYSCON_I2SDIV_PDIV_3  | ( 47 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 284, SYSCON_I2SDIV_PDIV_2  | ( 71 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 285, SYSCON_I2SDIV_PDIV_25 | ( 57 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 288, SYSCON_I2SDIV_PDIV_3  | ( 48 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 290, SYSCON_I2SDIV_PDIV_25 | ( 58 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 292, SYSCON_I2SDIV_PDIV_2  | ( 73 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 294, SYSCON_I2SDIV_PDIV_3  | ( 49 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 295, SYSCON_I2SDIV_PDIV_25 | ( 59 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 296, SYSCON_I2SDIV_PDIV_2  | ( 74 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 300, SYSCON_I2SDIV_PDIV_3  | ( 50 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 304, SYSCON_I2SDIV_PDIV_2  | ( 76 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 305, SYSCON_I2SDIV_PDIV_25 | ( 61 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 306, SYSCON_I2SDIV_PDIV_3  | ( 51 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 308, SYSCON_I2SDIV_PDIV_2  | ( 77 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 310, SYSCON_I2SDIV_PDIV_25 | ( 62 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 312, SYSCON_I2SDIV_PDIV_3  | ( 52 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 315, SYSCON_I2SDIV_PDIV_25 | ( 63 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 316, SYSCON_I2SDIV_PDIV_2  | ( 79 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 318, SYSCON_I2SDIV_PDIV_3  | ( 53 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 320, SYSCON_I2SDIV_PDIV_25 | ( 64 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 324, SYSCON_I2SDIV_PDIV_3  | ( 54 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 325, SYSCON_I2SDIV_PDIV_25 | ( 65 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 328, SYSCON_I2SDIV_PDIV_2  | ( 82 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 330, SYSCON_I2SDIV_PDIV_3  | ( 55 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 332, SYSCON_I2SDIV_PDIV_2  | ( 83 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 335, SYSCON_I2SDIV_PDIV_25 | ( 67 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 336, SYSCON_I2SDIV_PDIV_3  | ( 56 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 340, SYSCON_I2SDIV_PDIV_25 | ( 68 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 342, SYSCON_I2SDIV_PDIV_3  | ( 57 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 344, SYSCON_I2SDIV_PDIV_2  | ( 86 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 345, SYSCON_I2SDIV_PDIV_25 | ( 69 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 348, SYSCON_I2SDIV_PDIV_3  | ( 58 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 350, SYSCON_I2SDIV_PDIV_25 | ( 70 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 352, SYSCON_I2SDIV_PDIV_2  | ( 88 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 354, SYSCON_I2SDIV_PDIV_3  | ( 59 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 355, SYSCON_I2SDIV_PDIV_25 | ( 71 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 356, SYSCON_I2SDIV_PDIV_2  | ( 89 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 360, SYSCON_I2SDIV_PDIV_3  | ( 60 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 364, SYSCON_I2SDIV_PDIV_2  | ( 91 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 365, SYSCON_I2SDIV_PDIV_25 | ( 73 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 366, SYSCON_I2SDIV_PDIV_3  | ( 61 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 368, SYSCON_I2SDIV_PDIV_2  | ( 92 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 370, SYSCON_I2SDIV_PDIV_25 | ( 74 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 372, SYSCON_I2SDIV_PDIV_3  | ( 62 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 375, SYSCON_I2SDIV_PDIV_25 | ( 75 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 376, SYSCON_I2SDIV_PDIV_2  | ( 94 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 378, SYSCON_I2SDIV_PDIV_3  | ( 63 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 380, SYSCON_I2SDIV_PDIV_25 | ( 76 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 384, SYSCON_I2SDIV_PDIV_3  | ( 64 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 385, SYSCON_I2SDIV_PDIV_25 | ( 77 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 388, SYSCON_I2SDIV_PDIV_2  | ( 97 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 390, SYSCON_I2SDIV_PDIV_3  | ( 65 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 392, SYSCON_I2SDIV_PDIV_2  | ( 98 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 395, SYSCON_I2SDIV_PDIV_25 | ( 79 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 396, SYSCON_I2SDIV_PDIV_3  | ( 66 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 400, SYSCON_I2SDIV_PDIV_25 | ( 80 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 402, SYSCON_I2SDIV_PDIV_3  | ( 67 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 404, SYSCON_I2SDIV_PDIV_2  | (101 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 405, SYSCON_I2SDIV_PDIV_25 | ( 81 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 408, SYSCON_I2SDIV_PDIV_3  | ( 68 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 410, SYSCON_I2SDIV_PDIV_25 | ( 82 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 412, SYSCON_I2SDIV_PDIV_2  | (103 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 414, SYSCON_I2SDIV_PDIV_3  | ( 69 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 415, SYSCON_I2SDIV_PDIV_25 | ( 83 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 416, SYSCON_I2SDIV_PDIV_2  | (104 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 420, SYSCON_I2SDIV_PDIV_3  | ( 70 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 424, SYSCON_I2SDIV_PDIV_2  | (106 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 425, SYSCON_I2SDIV_PDIV_25 | ( 85 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 426, SYSCON_I2SDIV_PDIV_3  | ( 71 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 428, SYSCON_I2SDIV_PDIV_2  | (107 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 430, SYSCON_I2SDIV_PDIV_25 | ( 86 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 432, SYSCON_I2SDIV_PDIV_3  | ( 72 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 435, SYSCON_I2SDIV_PDIV_25 | ( 87 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 436, SYSCON_I2SDIV_PDIV_2  | (109 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 438, SYSCON_I2SDIV_PDIV_3  | ( 73 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 440, SYSCON_I2SDIV_PDIV_25 | ( 88 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 444, SYSCON_I2SDIV_PDIV_3  | ( 74 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 445, SYSCON_I2SDIV_PDIV_25 | ( 89 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 448, SYSCON_I2SDIV_PDIV_2  | (112 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 450, SYSCON_I2SDIV_PDIV_3  | ( 75 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 452, SYSCON_I2SDIV_PDIV_2  | (113 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 455, SYSCON_I2SDIV_PDIV_25 | ( 91 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 456, SYSCON_I2SDIV_PDIV_3  | ( 76 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 460, SYSCON_I2SDIV_PDIV_25 | ( 92 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 462, SYSCON_I2SDIV_PDIV_3  | ( 77 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 464, SYSCON_I2SDIV_PDIV_2  | (116 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 465, SYSCON_I2SDIV_PDIV_25 | ( 93 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 468, SYSCON_I2SDIV_PDIV_3  | ( 78 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 470, SYSCON_I2SDIV_PDIV_25 | ( 94 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 472, SYSCON_I2SDIV_PDIV_2  | (118 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 474, SYSCON_I2SDIV_PDIV_3  | ( 79 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 475, SYSCON_I2SDIV_PDIV_25 | ( 95 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 476, SYSCON_I2SDIV_PDIV_2  | (119 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 480, SYSCON_I2SDIV_PDIV_3  | ( 80 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 484, SYSCON_I2SDIV_PDIV_2  | (121 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 485, SYSCON_I2SDIV_PDIV_25 | ( 97 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 486, SYSCON_I2SDIV_PDIV_3  | ( 81 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 488, SYSCON_I2SDIV_PDIV_2  | (122 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 490, SYSCON_I2SDIV_PDIV_25 | ( 98 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 492, SYSCON_I2SDIV_PDIV_3  | ( 82 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 495, SYSCON_I2SDIV_PDIV_25 | ( 99 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 496, SYSCON_I2SDIV_PDIV_2  | (124 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 498, SYSCON_I2SDIV_PDIV_3  | ( 83 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 500, SYSCON_I2SDIV_PDIV_25 | (100 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 504, SYSCON_I2SDIV_PDIV_3  | ( 84 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 505, SYSCON_I2SDIV_PDIV_25 | (101 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 508, SYSCON_I2SDIV_PDIV_2  | (127 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 510, SYSCON_I2SDIV_PDIV_3  | ( 85 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 515, SYSCON_I2SDIV_PDIV_25 | (103 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 516, SYSCON_I2SDIV_PDIV_3  | ( 86 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 520, SYSCON_I2SDIV_PDIV_25 | (104 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 522, SYSCON_I2SDIV_PDIV_3  | ( 87 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 525, SYSCON_I2SDIV_PDIV_25 | (105 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 528, SYSCON_I2SDIV_PDIV_3  | ( 88 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 530, SYSCON_I2SDIV_PDIV_25 | (106 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 534, SYSCON_I2SDIV_PDIV_3  | ( 89 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 535, SYSCON_I2SDIV_PDIV_25 | (107 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 540, SYSCON_I2SDIV_PDIV_3  | ( 90 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 545, SYSCON_I2SDIV_PDIV_25 | (109 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 546, SYSCON_I2SDIV_PDIV_3  | ( 91 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 550, SYSCON_I2SDIV_PDIV_25 | (110 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 552, SYSCON_I2SDIV_PDIV_3  | ( 92 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 555, SYSCON_I2SDIV_PDIV_25 | (111 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 558, SYSCON_I2SDIV_PDIV_3  | ( 93 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 560, SYSCON_I2SDIV_PDIV_25 | (112 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 564, SYSCON_I2SDIV_PDIV_3  | ( 94 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 565, SYSCON_I2SDIV_PDIV_25 | (113 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 570, SYSCON_I2SDIV_PDIV_3  | ( 95 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 575, SYSCON_I2SDIV_PDIV_25 | (115 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 576, SYSCON_I2SDIV_PDIV_3  | ( 96 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 580, SYSCON_I2SDIV_PDIV_25 | (116 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 582, SYSCON_I2SDIV_PDIV_3  | ( 97 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 585, SYSCON_I2SDIV_PDIV_25 | (117 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 588, SYSCON_I2SDIV_PDIV_3  | ( 98 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 590, SYSCON_I2SDIV_PDIV_25 | (118 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 594, SYSCON_I2SDIV_PDIV_3  | ( 99 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 595, SYSCON_I2SDIV_PDIV_25 | (119 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 600, SYSCON_I2SDIV_PDIV_3  | (100 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 605, SYSCON_I2SDIV_PDIV_25 | (121 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 606, SYSCON_I2SDIV_PDIV_3  | (101 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 610, SYSCON_I2SDIV_PDIV_25 | (122 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 612, SYSCON_I2SDIV_PDIV_3  | (102 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 615, SYSCON_I2SDIV_PDIV_25 | (123 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 618, SYSCON_I2SDIV_PDIV_3  | (103 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 620, SYSCON_I2SDIV_PDIV_25 | (124 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 624, SYSCON_I2SDIV_PDIV_3  | (104 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 625, SYSCON_I2SDIV_PDIV_25 | (125 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 630, SYSCON_I2SDIV_PDIV_3  | (105 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 635, SYSCON_I2SDIV_PDIV_25 | (127 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 636, SYSCON_I2SDIV_PDIV_3  | (106 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 642, SYSCON_I2SDIV_PDIV_3  | (107 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 648, SYSCON_I2SDIV_PDIV_3  | (108 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 654, SYSCON_I2SDIV_PDIV_3  | (109 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 660, SYSCON_I2SDIV_PDIV_3  | (110 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 666, SYSCON_I2SDIV_PDIV_3  | (111 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 672, SYSCON_I2SDIV_PDIV_3  | (112 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 678, SYSCON_I2SDIV_PDIV_3  | (113 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 684, SYSCON_I2SDIV_PDIV_3  | (114 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 690, SYSCON_I2SDIV_PDIV_3  | (115 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 696, SYSCON_I2SDIV_PDIV_3  | (116 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 702, SYSCON_I2SDIV_PDIV_3  | (117 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 708, SYSCON_I2SDIV_PDIV_3  | (118 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 714, SYSCON_I2SDIV_PDIV_3  | (119 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 720, SYSCON_I2SDIV_PDIV_3  | (120 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 726, SYSCON_I2SDIV_PDIV_3  | (121 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 732, SYSCON_I2SDIV_PDIV_3  | (122 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 738, SYSCON_I2SDIV_PDIV_3  | (123 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 744, SYSCON_I2SDIV_PDIV_3  | (124 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 750, SYSCON_I2SDIV_PDIV_3  | (125 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 756, SYSCON_I2SDIV_PDIV_3  | (126 & SYSCON_I2SDIV_MDIV_MASK) },
+	{ 762, SYSCON_I2SDIV_PDIV_3  | (127 & SYSCON_I2SDIV_MDIV_MASK) }
+};
+
+u8	spi_wbuffer[2];
+u8	spi_rbuffer[2];
+u8	reg_image[18];
+struct spi_device *spi_global;
+
+static int spi_write_reg(u8 reg, u8 val)
+{
+	struct spi_message msg;
+	struct spi_transfer msg_xfer = {
+		.len		= 3,
+		.cs_change	= 0,
+	};
+	int retval;
+	
+	DPRINTK("spi_write_reg called val = %d.\n",val);
+	spi_message_init(&msg);
+
+	spi_wbuffer[0] = 0x20;
+	spi_wbuffer[1] = reg;
+	spi_wbuffer[2] = val;
+
+	msg_xfer.tx_buf = spi_wbuffer;
+	msg_xfer.rx_buf = spi_rbuffer;
+	spi_message_add_tail(&msg_xfer, &msg);
+
+	retval = spi_sync(spi_global, &msg);
+
+	if (!retval)
+		reg_image[reg] = val;
+	
+	udelay(10);
+
+	return retval;
+}
+
+/*
+ * When we get to the multichannel case the pre-fill and enable code
+ * will go to the dma driver's start routine.
+ */
+static void snd_ep93xx_i2s_enable_transmit(void){
+
+	int i;
+	unsigned long ulTemp;
+
+	outl( 0, I2STX0En );
+	outl( 0, I2STX1En );
+	outl( 0, I2STX2En );
+	ulTemp = inl( I2STX2En ); /* input to push the outl's past the wrapper */
+
+	for( i = 0 ; i < 8 ; i++ ){
+		outl( 0, I2STX0Lft );
+		outl( 0, I2STX0Rt );
+		outl( 0, I2STX1Lft );
+		outl( 0, I2STX1Rt );
+		outl( 0, I2STX2Lft );
+		outl( 0, I2STX2Rt );
+		ulTemp = inl( I2SRX2En ); /* input to push the outl's past the wrapper */
+	}
+	outl( 1, I2STX0En );
+	outl( 1, I2STX1En );
+	outl( 1, I2STX2En );
+	ulTemp = inl( I2STX2En ); /* input to push the outl's past the wrapper */
+}
+
+static void snd_ep93xx_i2s_disable_transmit( void )
+{
+	unsigned long ulTemp;
+
+	outl( 0, I2STX0En );
+	outl( 0, I2STX1En );
+	outl( 0, I2STX2En );
+	ulTemp = inl( I2SRX2En ); /* input to push the outl's past the wrapper */
+
+}
+
+
+static void snd_ep93xx_i2s_enable_receive(void)
+{
+	unsigned long ulTemp;
+
+	outl( 1, I2SRX0En );
+	outl( 1, I2SRX1En );
+	outl( 1, I2SRX2En );
+	ulTemp = inl( I2SRX2En );
+
+}
+
+static void snd_ep93xx_i2s_disable_receive( void )
+{
+	unsigned long ulTemp;
+
+	outl( 0, I2SRX0En );
+	outl( 0, I2SRX1En );
+	outl( 0, I2SRX2En );
+	ulTemp = inl( I2SRX2En );
+}
+
+static void snd_ep93xx_i2s_enable( void )
+{
+	unsigned long ulTemp;
+
+	outl(1, I2SGlCtrl );
+	ulTemp = inl( I2SGlCtrl );
+}
+
+static void snd_ep93xx_i2s_disable( void )
+{
+	unsigned long ulTemp;
+
+	outl(0, I2SGlCtrl );
+	ulTemp = inl( I2SGlCtrl );
+}
+
+
+/*
+ * ep93xx_calc_closest_freq
+ * 
+ *   Return            0 - Failure
+ *                     1 - Success
+ */
+static int ep93xx_calc_closest_freq
+(
+ ulong   ulPLLFreq, 
+ ulong   ulRequestedMClkFreq,
+ ulong * pulActualMClkFreq,
+ ulong * pulI2SDiv
+ )
+{
+	ulong   ulLower;
+	ulong   ulUpper;
+	ulong   ulDiv;
+	int     x;
+
+	/* Calculate the closest divisor. */
+	ulDiv =  (ulPLLFreq * 2)/ ulRequestedMClkFreq;
+
+	for(x = 1; x < sizeof(I2SDivTable)/sizeof(DIV_TABLE); x++){
+
+		/* Calculate the next greater and lower value. */
+		ulLower = I2SDivTable[x - 1].ulTotalDiv;     
+		ulUpper = I2SDivTable[x].ulTotalDiv;     
+
+		/* Check to see if it is in between the two values. */
+		if(ulLower <= ulDiv && ulDiv < ulUpper)
+			break;
+	}
+
+	/* Return if we did not find a divisor. */
+	if(x == sizeof(I2SDivTable)/sizeof(DIV_TABLE)){
+
+		DPRINTK("ep93xx_i2s couldn't find a divisor.\n");
+
+		*pulActualMClkFreq  = 0;
+		*pulI2SDiv          = 0;
+		return -1;
+	}
+
+	/* See which is closer, the upper or the lower case. */
+	if(ulUpper * ulRequestedMClkFreq - ulPLLFreq * 2 >  
+			ulPLLFreq * 2 - ulLower * ulRequestedMClkFreq){
+		x-=1;
+	}
+
+	*pulActualMClkFreq  = (ulPLLFreq * 2)/ I2SDivTable[x].ulTotalDiv;
+	*pulI2SDiv          = I2SDivTable[x].ulI2SDiv;
+
+	return 0;
+}
+
+/*
+ * ep93xx_get_PLL_frequency
+ *
+ * Given a value for ClkSet1 or ClkSet2, calculate the PLL1 or PLL2 frequency.
+ */
+static ulong ep93xx_get_PLL_frequency( ulong ulCLKSET )
+{
+	ulong ulX1FBD, ulX2FBD, ulX2IPD, ulPS, ulPLL_Freq;
+
+	ulPS = (ulCLKSET & SYSCON_CLKSET1_PLL1_PS_MASK) >> SYSCON_CLKSET1_PLL1_PS_SHIFT;
+	ulX1FBD = (ulCLKSET & SYSCON_CLKSET1_PLL1_X1FBD1_MASK) >> SYSCON_CLKSET1_PLL1_X1FBD1_SHIFT;
+	ulX2FBD = (ulCLKSET & SYSCON_CLKSET1_PLL1_X2FBD2_MASK) >> SYSCON_CLKSET1_PLL1_X2FBD2_SHIFT;
+	ulX2IPD = (ulCLKSET & SYSCON_CLKSET1_PLL1_X2IPD_MASK) >> SYSCON_CLKSET1_PLL1_X2IPD_SHIFT;
+
+	ulPLL_Freq = (((0x00e10000 * (ulX1FBD+1)) / (ulX2IPD+1)) * (ulX2FBD+1)) >> ulPS;
+	return ulPLL_Freq;
+}
+
+/*
+ * SetSampleRate
+ * disables i2s channels and sets up i2s divisors
+ * in syscon for the requested sample rate.
+ * lFrequency - Sample Rate in Hz
+ */
+static void ep93xx_set_samplerate(long lFrequency)
+{
+	ulong ulRequestedMClkFreq, ulPLL1_CLOCK, ulPLL2_CLOCK;
+	ulong ulMClkFreq1, ulMClkFreq2, ulClkSet1, ulClkSet2;
+	ulong ulI2SDiv, ulI2SDiv1, ulI2SDiv2, ulI2SDIV, actual_samplerate;
+
+	/* Clock ratios: MCLK to SCLK and SCLK to LRCK */
+	ulong ulM2SClock  = 4;
+	ulong ulS2LRClock = 64;
+
+	DPRINTK( "ep93xx_set_samplerate = %d Hz.\n", (int)lFrequency );
+	/*
+	 * Read CLKSET1 and CLKSET2 in the System Controller and calculate
+	 * the PLL frequencies from that.
+	 */
+	ulClkSet1 =	inl(SYSCON_CLKSET1);
+	ulClkSet2 =	inl(SYSCON_CLKSET2);
+	ulPLL1_CLOCK = ep93xx_get_PLL_frequency( ulClkSet1 );
+	ulPLL2_CLOCK = ep93xx_get_PLL_frequency( ulClkSet2 );
+
+	DPRINTK( "ep93xx_i2s_ClkSet1=0x%08x Clkset2=0x%08x  PLL1=%d Hz  PLL2=%d Hz\n", 
+			(int)ulClkSet1, (int)ulClkSet2, (int)ulPLL1_CLOCK, (int)ulPLL2_CLOCK );
+
+	ulRequestedMClkFreq = ( lFrequency * ulM2SClock * ulS2LRClock);
+
+	ep93xx_calc_closest_freq
+		(
+		 ulPLL1_CLOCK, 
+		 ulRequestedMClkFreq,
+		 &ulMClkFreq1,
+		 &ulI2SDiv1
+		);
+	ep93xx_calc_closest_freq
+		(
+		 ulPLL2_CLOCK, 
+		 ulRequestedMClkFreq,
+		 &ulMClkFreq2,
+		 &ulI2SDiv2
+		);
+
+	/* See which is closer, MClk rate 1 or MClk rate 2. */
+	if(abs(ulMClkFreq1 - ulRequestedMClkFreq) < abs(ulMClkFreq2 -ulRequestedMClkFreq)){
+		ulI2SDiv = ulI2SDiv1;
+		actual_samplerate = ulMClkFreq1/ (ulM2SClock * ulS2LRClock);
+		DPRINTK( "ep93xx_set_samplerate - using PLL1\n" );
+	}
+	else{
+		ulI2SDiv = ulI2SDiv2 | SYSCON_I2SDIV_PSEL;
+		actual_samplerate = ulMClkFreq1 / (ulM2SClock * ulS2LRClock);
+		DPRINTK( "ep93xx_set_samplerate - using PLL2\n" );
+	}
+
+	/* Calculate the new I2SDIV register value and write it out. */
+	ulI2SDIV = ulI2SDiv | SYSCON_I2SDIV_SENA |  SYSCON_I2SDIV_ORIDE |
+		SYSCON_I2SDIV_SPOL| SYSCON_I2SDIV_LRDIV_64 |
+		SYSCON_I2SDIV_SDIV | SYSCON_I2SDIV_MENA | SYSCON_I2SDIV_ESEL;
+
+	DPRINTK("I2SDIV set to 0x%08x\n", (unsigned int)ulI2SDIV );									
+	SysconSetLocked(SYSCON_I2SDIV, ulI2SDIV);
+	DPRINTK("I2SDIV set to 0x%08x\n", inl(SYSCON_I2SDIV));
+}
+
+/*
+ * BringUpI2S
+ * This routine sets up the I2S Controller.
+ */
+static void snd_ep93xx_i2s_init( char wordlenght )
+{
+	unsigned int uiDEVCFG;
+
+	DPRINTK("snd_ep93xx_i2s_init - enter\n");
+
+	if(wordlenght)
+		DPRINTK("i2s_controller set to 24bit lenght\n");
+	else
+		DPRINTK("i2s_controller set to 16bit lenght\n");
+
+	/*
+	 * Configure 
+	 * EGPIO[4,5,6,13] to be SDIN's and SDOUT's for the second and third
+	 * I2S stereo channels if the codec is a 6 channel codec.
+	 */
+	uiDEVCFG = inl(SYSCON_DEVCFG/*EP93XX_SYSCON_DEVICE_CONFIG*/);
+	DPRINTK("snd_ep93xx_i2s_init =%x - 0\n",uiDEVCFG);
+#ifdef CONFIG_CODEC_CS4271
+	uiDEVCFG |= SYSCON_DEVCFG_I2SonAC97;
+	DPRINTK("snd_ep93xx_i2s_init =%x- 0 0\n",uiDEVCFG);
+#else /* CONFIG_CODEC_CS4228A */
+	uiDEVCFG |= SYSCON_DEVCFG_I2SonAC97 | SYSCON_DEVCFG_A1onG | SYSCON_DEVCFG_A2onG;
+#endif
+	DPRINTK("snd_ep93xx_i2s_init - 0 1\n");
+	SysconSetLocked(SYSCON_DEVCFG/*EP93XX_SYSCON_DEVICE_CONFIG*/, uiDEVCFG);
+
+	uiDEVCFG = inl(SYSCON_DEVCFG/*EP93XX_SYSCON_DEVICE_CONFIG*/);
+	DPRINTK("snd_ep93xx_i2s_init=%x - 1\n",uiDEVCFG);
+	/* Configure I2S Tx channel */
+	/* Justify Left, MSB first */
+	outl( 0, I2STXLinCtrlData );
+
+	/* WL = 24 bits */
+	outl( wordlenght, I2STXWrdLen );
+
+	/*
+	 * Set the I2S control block to master mode.
+	 * Tx bit clk rate determined by word legnth 
+	 * Do gate off last 8 clks (24 bit samples in a 32 bit field)
+	 * LRclk = I2S timing; LRck = 0 for left
+	 */
+
+	outl( ( i2s_txcc_mstr | i2s_txcc_trel), I2STxClkCfg );
+	/* Configure I2S rx channel */
+	/* First, clear all config bits. */
+	outl( 0, I2SRXLinCtrlData );
+	outl( wordlenght, I2SRXWrdLen );
+
+	/* 
+	 * Set the I2S control block to master mode.
+	 * Rx bit clk rate determined by word legnth 
+	 * Do gate off last 8 clks 
+	 * setting i2s_rxcc_rrel gives us I2S timing
+	 * clearing i2s_rlrs gives us LRck = 0 for left, 1 for right
+	 * setting i2s_rxcc_nbcg specifies to not gate off extra 8 clocks 
+	 */
+	outl( (i2s_rxcc_bcr_64x | i2s_rxcc_nbcg |i2s_rxcc_mstr | i2s_rxcc_rrel), I2SRxClkCfg );
+
+	/* Do an input to push the outl's past the wrapper */
+	uiDEVCFG = inl(SYSCON_DEVCFG/*EP93XX_SYSCON_DEVICE_CONFIG*/);
+
+	DPRINTK("snd_ep93xx_i2s_init =%x- EXIT\n",uiDEVCFG);
+}
+
+/*
+ * ep93xx_init_i2s_codec
+ *
+ * Note that codec must be getting i2s clocks for any codec
+ * register writes to work.
+ */
+static void snd_ep93xx_codec_init( void )
+{
+
+#if defined(CONFIG_MACH_EDB9301) || defined(CONFIG_MACH_EDB9302)
+	unsigned int uiPADR, uiPADDR;
+#endif
+
+#if defined(CONFIG_MACH_EDB9315A)
+	unsigned int uiPBDR, uiPBDDR;
+#endif
+
+#if defined(CONFIG_MACH_EDB9307A) || defined(CONFIG_MACH_EDB9302A)
+	unsigned int uiPHDR, uiPHDDR, uiDEVCFG;;
+#endif
+
+
+#ifdef CONFIG_CODEC_CS4271
+	/*
+	 * Some EDB9301 boards use EGPIO1 (port 1, bit 1) for the I2S reset.
+	 * EGPIO1 has a pulldown so if it isn't configured as an output, it is low.
+	 */
+#if defined(CONFIG_MACH_EDB9301) || defined(CONFIG_MACH_EDB9302)
+	uiPADR  = inl(GPIO_PADR);
+	uiPADDR = inl(GPIO_PADDR);
+	DPRINTK("%s 01 02\n",__FUNCTION__); 
+	/* Clear bit 1 of the data register */
+	outl( (uiPADR & 0xfd), GPIO_PADR );
+	uiPADR  = inl(GPIO_PADR);
+
+	/* Set bit 1 of the DDR to set it to output
+	 * Now we are driving the reset pin low.
+	 */
+	outl( (uiPADDR | 0x02), GPIO_PADDR );
+	uiPADDR = inl(GPIO_PADDR);
+
+	udelay( 2 );  /* plenty of time */
+
+	/* Set bit 1 of the data reg.  Now we drive the reset pin high. */
+	outl( (uiPADR | 0x02),  GPIO_PADR );
+	uiPADR  = inl(GPIO_PADR);
+#endif
+
+#if defined(CONFIG_MACH_EDB9315A)
+	DPRINTK("%s EDB15A\n",__FUNCTION__);
+	//outl( 0xa3, GPIO_PBDR );
+	//outl( 0,GPIO_PBDDR);
+	uiPBDR  = inl(GPIO_PBDR);
+	uiPBDDR = inl(GPIO_PBDDR);
+	DPRINTK("uiPBDR=%x,uiPBDDR=%x\n",uiPBDR,uiPBDDR); 
+	/* Clear bit 1 of the data register */
+	outl( (uiPBDR & 0xbf), GPIO_PBDR );
+	uiPBDR  = inl(GPIO_PBDR);
+
+	/* Set bit 1 of the DDR to set it to output
+	 * Now we are driving the reset pin low.
+	 */
+	outl( (uiPBDDR | 0x40), GPIO_PBDDR );
+	uiPBDDR = inl(GPIO_PBDDR);
+
+	//uiPBDR  = inl(GPIO_PBDR);
+	//uiPBDDR = inl(GPIO_PBDDR);
+	//DPRINTK("uiPBDR=%x,uiPBDDR=%x\n",uiPBDR,uiPBDDR);
+	udelay( 2 );  /* plenty of time */
+
+	/* Set bit 1 of the data reg.  Now we drive the reset pin high. */
+	outl( (uiPBDR | 0x40),  GPIO_PBDR );
+	uiPBDR  = inl(GPIO_PBDR);
+
+	//uiPBDR  = inl(GPIO_PBDR);
+	//uiPBDDR = inl(GPIO_PBDDR);
+	//DPRINTK("uiPBDR=%x,uiPBDDR=%x\n",uiPBDR,uiPBDDR);
+#endif
+
+#if defined(CONFIG_MACH_EDB9307A) || defined(CONFIG_MACH_EDB9302A)
+
+	/* Setup bit 11 in DEV_CONFIG for Port HonIDE to do GPIO */
+	uiDEVCFG = inl(SYSCON_DEVCFG);
+	uiDEVCFG |= SYSCON_DEVCFG_HonIDE;
+
+	/* Unlock SYSCON_DEVCFG */
+	SysconSetLocked(SYSCON_DEVCFG, uiDEVCFG);
+
+	uiPHDR  = inl(GPIO_PHDR);
+	uiPHDDR = inl(GPIO_PHDDR);
+
+	/* Clear bit 3 of the data register */
+	outl( (uiPHDR & 0xfb), GPIO_PHDR );
+	uiPHDR  = inl(GPIO_PHDR);
+
+	/* Set bit 3 of the DDR to set it to output
+	 * Now we are driving the reset pin low.
+	 */
+	outl( (uiPHDDR | 0x04), GPIO_PHDDR );
+	uiPHDDR = inl(GPIO_PHDDR);
+
+	udelay( 2 );  /* plenty of time */
+
+	/* Set bit 3 of the data reg.  Now we drive the reset pin high. */
+	outl( (uiPHDR | 0x04),  GPIO_PHDR );
+	uiPHDR  = inl(GPIO_PHDR);
+#endif
+	/*
+	 * Write to the control port, setting the enable control port bit
+	 * so that we can write to the control port.  OK?
+	 */
+//	SSPDriver->Write( SSP_Handle, 7, 0x02 );
+	spi_write_reg( 7, 0x03 );
+
+	/* Select slave, 24Bit I2S serial mode */
+//	SSPDriver->Write( SSP_Handle, 1, 0x01 );
+	spi_write_reg( 1, 0x01 );
+//	SSPDriver->Write( SSP_Handle, 6, 0x10 );
+	spi_write_reg( 6, 0x10 );
+	/* Set AMUTE (auto-mute) bit. */
+//	SSPDriver->Write( SSP_Handle, 2, 0x00 );
+	spi_write_reg( 2, 0x00 );
+	spi_write_reg( 7, 0x02 );
+
+#else // CONFIG_CODEC_CS4228A
+//	SSPDriver->Write( SSP_Handle, 0x01, 0x04 );
+//	SSPDriver->Write( SSP_Handle, 0x0D, 0x84 );
+	spi_write_reg( 0x01, 0x04 );
+	spi_write_reg( 0x0D, 0x84 );
+
+#endif
+
+}
+
+static int snd_ep93xx_codec_setvolume( int value )
+{
+#ifdef CONFIG_CODEC_CS4271
+	int iMute = 0;
+#endif
+
+	DPRINTK("snd_ep93xx_codec_setvolume %x- enter\n",value);
+
+#ifdef CONFIG_CODEC_CS4271
+	/*
+	 * CS4271 DAC attenuation is 0 to 127 dB in 1 dB steps
+	 */
+	if( mute )
+	{
+		iMute = 0x80;
+	}
+	DPRINTK("%x---%x\n",iMute,mute);
+	/*
+	 * Write the volume for DAC1 and DAC2 (reg 4 and 5)
+	 */
+	//SSPDriver->Write( SSP_Handle, 4, (value | iMute) );
+	spi_write_reg( 4, (value | iMute) );
+	//SSPDriver->Write( SSP_Handle, 5, (value | iMute) );
+	spi_write_reg( 5, (value | iMute) );
+
+
+#else // CONFIG_CODEC_CS4228A
+	/*
+	 * CS4228A DAC attenuation is 0 to 90.5 dB in 0.5 dB steps
+	 */
+	//SSPDriver->Write( SSP_Handle, 0x07, value );
+	//SSPDriver->Write( SSP_Handle, 0x08, value );
+	//SSPDriver->Write( SSP_Handle, 0x09, value );
+	//SSPDriver->Write( SSP_Handle, 0x0A, value );
+	//SSPDriver->Write( SSP_Handle, 0x0B, value );
+	//SSPDriver->Write( SSP_Handle, 0x0C, value );
+	spi_write_reg(0x07, value);
+	spi_write_reg(0x08, value);
+	spi_write_reg(0x09, value);
+	spi_write_reg(0x0A, value);
+	spi_write_reg(0x0B, value);
+	spi_write_reg(0x0C, value);
+
+#endif
+	DPRINTK("snd_ep93xx_codec_setvolume - exit\n");
+	return 0;
+}
+
+/*
+ * ep93xx_automute_i2s_codec
+ *
+ * Note that codec must be getting i2s clocks for any codec
+ * register writes to work.
+ */
+static void snd_ep93xx_codec_automute (audio_state_t *state)
+{
+	DPRINTK("snd_ep93xx_codec_automute - enter\n");	
+#ifdef CONFIG_CODEC_CS4271
+	/*
+	 * The automute bit is set by default for the CS4271.
+	 * Clear the driver's mute flag and use the set_volume routine
+	 * to write the current volume out with the mute bit cleared.
+	 */
+	mute = 0;
+	snd_ep93xx_codec_setvolume(0);
+
+	state->playback_volume[0][0] = 127 ;
+	state->playback_volume[0][0] = 127 ;
+
+
+#else // CONFIG_CODEC_CS4228A	
+	//SSPDriver->Write( SSP_Handle, 4, 0 );
+	spi_write_reg(4, 0);
+
+	/* Unmute the DACs */
+	//SSPDriver->Write( SSP_Handle, 5, 0x80 );
+	spi_write_reg(5, 0x80);
+	/* Unmute the MUTEC pin, turn on automute. */
+	//SSPDriver->Write( SSP_Handle, 5, 0x40 );
+	spi_write_reg(5, 0x40);
+
+	state->playback_volume[0][0] = 181 ;
+	state->playback_volume[0][1] = 181 ;
+	state->playback_volume[1][0] = 181 ;
+	state->playback_volume[1][1] = 181 ;
+	state->playback_volume[2][0] = 181 ;
+	state->playback_volume[2][1] = 181 ;
+
+#endif    
+	DPRINTK("snd_ep93xx_codec_automute - exit\n");
+}
+
+
+/*
+ * ep93xx_audio_init
+ * Note that the codec powers up with its DAC's muted and
+ * the serial format set to 24 bit I2S mode.
+ */
+static void snd_ep93xx_audio_init(audio_state_t *state)
+{
+	DPRINTK("snd_ep93xx_audio_init - enter\n");
+	/* Mute the DACs. Disable all audio channels. */  
+	/* Must do this to change sample rate. */
+
+	snd_ep93xx_i2s_disable_transmit();
+	snd_ep93xx_i2s_disable_receive();
+	snd_ep93xx_i2s_disable();
+
+	/*Set up the i2s clocks in syscon.  Enable them. */ 
+	ep93xx_set_samplerate( AUDIO_SAMPLE_RATE_DEFAULT );
+
+	/* Set i2s' controller serial format, and enable */ 
+	snd_ep93xx_i2s_init(WL16);
+
+	/* Initialize codec serial format, etc. */
+	snd_ep93xx_codec_init();
+
+	/* Clear the fifo and enable the tx0 channel. */
+	snd_ep93xx_i2s_enable_transmit();
+	snd_ep93xx_i2s_enable_receive();
+	snd_ep93xx_i2s_enable();
+
+	/* Set the volume for the first time. */
+	snd_ep93xx_codec_setvolume( AUDIO_DEFAULT_VOLUME );
+
+	/* Unmute the DAC and set the mute pin MUTEC to automute. */
+	snd_ep93xx_codec_automute(state);
+
+	DPRINTK("snd_ep93xx_audio_init - exit\n");
+}
+
+static void print_audio_format( long format )
+{
+	switch( format ){
+		case SNDRV_PCM_FORMAT_S8:
+			DPRINTK( "AFMT_S8\n" );		   
+			break;
+
+		case SNDRV_PCM_FORMAT_U8:		   
+			DPRINTK( "AFMT_U8\n" );		   
+			break;
+
+		case SNDRV_PCM_FORMAT_S16_LE:
+			DPRINTK( "AFMT_S16_LE\n" );		   
+			break;
+
+		case SNDRV_PCM_FORMAT_S16_BE:
+			DPRINTK( "AFMT_S16_BE\n" );		   
+			break;
+
+		case SNDRV_PCM_FORMAT_U16_LE:
+			DPRINTK( "AFMT_U16_LE\n" );		   
+			break;
+		case SNDRV_PCM_FORMAT_U16_BE:
+			DPRINTK( "AFMT_U16_BE\n" );
+			break;
+
+		case SNDRV_PCM_FORMAT_S24_LE:
+			DPRINTK( "AFMT_S24_LE\n" );		   
+			break;
+
+		case SNDRV_PCM_FORMAT_S24_BE:
+			DPRINTK( "AFMT_S24_BE\n" );		   
+			break;
+
+		case SNDRV_PCM_FORMAT_U24_LE:
+			DPRINTK( "AFMT_U24_LE\n" );		   
+			break;
+
+		case SNDRV_PCM_FORMAT_U24_BE:
+			DPRINTK( "AFMT_U24_BE\n" );		   
+			break;
+		default:
+			DPRINTK( "ep93xx_i2s_Unsupported Audio Format\n" );		   
+			break;
+	}
+}
+
+static void audio_set_format( audio_stream_t * s, long val )
+{
+	DPRINTK( "ep93xx_i2s_audio_set_format enter.  Format requested (%d) %d ", 
+			(int)val,SNDRV_PCM_FORMAT_S16_LE);
+	print_audio_format( val );
+
+	switch( val ){
+		case SNDRV_PCM_FORMAT_S8:
+			s->audio_format = SNDRV_PCM_FORMAT_S8;
+			s->audio_stream_bitwidth = 8;
+			break;
+
+		case SNDRV_PCM_FORMAT_U8:		   
+			s->audio_format = SNDRV_PCM_FORMAT_U8;
+			s->audio_stream_bitwidth = 8;
+			break;
+
+		case SNDRV_PCM_FORMAT_S16_LE:
+		case SNDRV_PCM_FORMAT_S16_BE:
+			s->audio_format = SNDRV_PCM_FORMAT_S16_LE;
+			s->audio_stream_bitwidth = 16;
+			break;
+
+		case SNDRV_PCM_FORMAT_U16_LE:
+		case SNDRV_PCM_FORMAT_U16_BE:
+			s->audio_format = SNDRV_PCM_FORMAT_U16_LE;
+			s->audio_stream_bitwidth = 16;
+			break;
+
+		case SNDRV_PCM_FORMAT_S24_LE:
+		case SNDRV_PCM_FMTBIT_S24_BE:		
+			s->audio_format = SNDRV_PCM_FORMAT_S24_LE;
+			s->audio_stream_bitwidth = 24;
+			break;
+
+		case SNDRV_PCM_FORMAT_U24_LE:
+		case SNDRV_PCM_FMTBIT_U24_BE:		
+		default:
+			s->audio_format = SNDRV_PCM_FORMAT_U24_LE;
+			s->audio_stream_bitwidth = 24;
+			break;
+	}
+
+	DPRINTK( "ep93xx_i2s_audio_set_format EXIT format set to be (%d) ", (int)s->audio_format );
+	print_audio_format( (long)s->audio_format );
+}
+
+static __inline__ unsigned long copy_to_user_S24_LE
+(
+ audio_stream_t *stream,
+ const char *to, 
+ unsigned long to_count
+ )
+{
+	int *dma_buffer_0 = (int *)stream->hwbuf[0];
+	int *dma_buffer_1 = (int *)stream->hwbuf[1];
+	int *dma_buffer_2 = (int *)stream->hwbuf[2];
+
+	int total_to_count = to_count;
+	int *user_ptr = (int *)to;	/* 32 bit user buffer */
+	int count;
+
+	count = 8 * stream->dma_num_channels;
+
+	while (to_count > 0){
+
+		__put_user( (int)( *dma_buffer_0++ ), user_ptr++ );
+		__put_user( (int)( *dma_buffer_0++ ), user_ptr++ );
+
+		if(stream->audio_channels_flag & CHANNEL_REAR ){
+			__put_user( (int)( *dma_buffer_1++ ), user_ptr++ );
+			__put_user( (int)( *dma_buffer_1++ ), user_ptr++ );
+		}
+
+		if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+			__put_user( (int)( *dma_buffer_2++ ), user_ptr++ );
+			__put_user( (int)( *dma_buffer_2++ ), user_ptr++ );
+		}
+		to_count -= count;
+	}
+	return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_U24_LE
+(
+ audio_stream_t *stream,
+ const char *to, 
+ unsigned long to_count
+ )
+{
+	int *dma_buffer_0 = (int *)stream->hwbuf[0];
+	int *dma_buffer_1 = (int *)stream->hwbuf[1];
+	int *dma_buffer_2 = (int *)stream->hwbuf[2];
+
+	int total_to_count = to_count;
+	unsigned int * user_ptr = (unsigned int *)to;	/* 32 bit user buffer */
+	int count;
+
+	count = 8 * stream->dma_num_channels;
+
+	while (to_count > 0){ 
+		__put_user( ((unsigned int)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
+		__put_user( ((unsigned int)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
+
+		if(stream->audio_channels_flag & CHANNEL_REAR ){
+			__put_user( ((unsigned int)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
+			__put_user( ((unsigned int)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
+		}
+
+		if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+			__put_user( ((unsigned int)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
+			__put_user( ((unsigned int)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
+		}
+		to_count -= count;
+	}
+	return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_S16_LE
+(
+ audio_stream_t *stream,
+ const char *to, 
+ unsigned long to_count
+ )
+{
+	int *dma_buffer_0 = (int *)stream->hwbuf[0];
+	int *dma_buffer_1 = (int *)stream->hwbuf[1];
+	int *dma_buffer_2 = (int *)stream->hwbuf[2];
+	int total_to_count = to_count;
+	short * user_ptr = (short *)to;	/* 16 bit user buffer */
+	int count;
+
+	count = 4 * stream->dma_num_channels;
+
+	while (to_count > 0){
+
+		__put_user( (short)( *dma_buffer_0++ ), user_ptr++ );
+		__put_user( (short)( *dma_buffer_0++ ), user_ptr++ );
+
+		if( stream->audio_channels_flag & CHANNEL_REAR ){
+			__put_user( (short)( *dma_buffer_1++ ), user_ptr++ );
+			__put_user( (short)( *dma_buffer_1++ ), user_ptr++ );
+		}
+
+		if( stream->audio_channels_flag  & CHANNEL_CENTER_LFE ){
+			__put_user( (short)( *dma_buffer_2++ ), user_ptr++ );
+			__put_user( (short)( *dma_buffer_2++ ), user_ptr++ );
+		}
+		to_count -= count;
+	}
+	return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_U16_LE
+(
+ audio_stream_t *stream,
+ const char *to, 
+ unsigned long to_count
+ )
+{
+	int *dma_buffer_0 = (int *)stream->hwbuf[0];
+	int *dma_buffer_1 = (int *)stream->hwbuf[1];
+	int *dma_buffer_2 = (int *)stream->hwbuf[2];
+	int count;
+	int total_to_count = to_count;
+	short * user_ptr = (short *)to;	/* 16 bit user buffer */
+
+	count = 4 * stream->dma_num_channels;
+
+	while (to_count > 0){
+
+		__put_user( ((short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
+		__put_user( ((short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
+
+		if(stream->audio_channels_flag & CHANNEL_REAR ){
+			__put_user( ((short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
+			__put_user( ((short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
+		}
+
+		if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+			__put_user( ((short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
+			__put_user( ((short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
+		}
+		to_count -= count;
+	}
+	return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_S8
+(
+ audio_stream_t *stream,
+ const char *to, 
+ unsigned long to_count
+ )
+{
+	char *dma_buffer_0 = (char *)stream->hwbuf[0];
+	char *dma_buffer_1 = (char *)stream->hwbuf[1];
+	char *dma_buffer_2 = (char *)stream->hwbuf[2];
+	int count;
+	int total_to_count = to_count;
+	char * user_ptr = (char *)to;  /*  8 bit user buffer */
+
+	count = 2 * stream->dma_num_channels;
+
+	dma_buffer_0++;
+	dma_buffer_1++;
+	dma_buffer_2++;
+
+	while (to_count > 0){
+
+		__put_user( (char)( *dma_buffer_0 ), user_ptr++ );
+		dma_buffer_0 += 4;
+		__put_user( (char)( *dma_buffer_0 ), user_ptr++ );
+		dma_buffer_0 += 4;
+
+		if(stream->audio_channels_flag & CHANNEL_REAR ){
+			__put_user( (char)( *dma_buffer_1 ), user_ptr++ );
+			dma_buffer_1 += 4;
+			__put_user( (char)( *dma_buffer_1 ), user_ptr++ );
+			dma_buffer_1 += 4;
+		}
+
+		if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+			__put_user( (char)( *dma_buffer_2 ), user_ptr++ );
+			dma_buffer_2 += 4;
+			__put_user( (char)( *dma_buffer_2 ), user_ptr++ );
+			dma_buffer_2 += 4;
+		}
+		to_count -= count;
+	}
+	return total_to_count;
+}
+
+static __inline__ unsigned long copy_to_user_U8
+(
+ audio_stream_t *stream,
+ const char *to, 
+ unsigned long to_count
+ )
+{
+	char *dma_buffer_0 = (char *)stream->hwbuf[0];
+	char *dma_buffer_1 = (char *)stream->hwbuf[1];
+	char *dma_buffer_2 = (char *)stream->hwbuf[2];
+	int count;
+	int total_to_count = to_count;
+	char * user_ptr = (char *)to;  /*  8 bit user buffer */
+
+	count = 2 * stream->dma_num_channels;
+
+	dma_buffer_0++;
+	dma_buffer_1++;
+	dma_buffer_2++;
+
+	while (to_count > 0){
+
+		__put_user( (char)( *dma_buffer_0 ) ^ 0x80, user_ptr++ );
+		dma_buffer_0 += 4;
+		__put_user( (char)( *dma_buffer_0 ) ^ 0x80, user_ptr++ );
+		dma_buffer_0 += 4;
+
+		if(stream->audio_channels_flag & CHANNEL_REAR ){
+			__put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ );
+			dma_buffer_1 += 4;
+			__put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ );
+			dma_buffer_1 += 4;
+		}
+
+		if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+			__put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ );
+			dma_buffer_2 += 4;
+			__put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ );
+			dma_buffer_2 += 4;
+		}
+		to_count -= count;
+	}
+	return total_to_count;
+}
+
+static __inline__ int copy_to_user_with_conversion
+(
+ audio_stream_t *stream,
+ const char *to, 
+ int toCount
+ )
+{
+	int ret = 0;
+
+	if( toCount == 0 ){
+		DPRINTK("ep93xx_i2s_copy_to_user_with_conversion - nothing to copy!\n");
+	}
+
+	switch( stream->audio_format ){
+
+		case SNDRV_PCM_FORMAT_S8:
+			ret = copy_to_user_S8( stream, to, toCount );
+			break;
+
+		case SNDRV_PCM_FORMAT_U8:
+			ret = copy_to_user_U8( stream, to, toCount );
+			break;
+
+		case SNDRV_PCM_FORMAT_S16_LE:
+			ret = copy_to_user_S16_LE( stream, to, toCount );
+			break;
+
+		case SNDRV_PCM_FORMAT_U16_LE:
+			ret = copy_to_user_U16_LE( stream, to, toCount );
+			break;
+
+		case SNDRV_PCM_FORMAT_S24_LE:
+			ret = copy_to_user_S24_LE( stream, to, toCount );
+			break;
+
+		case SNDRV_PCM_FORMAT_U24_LE:
+			ret = copy_to_user_U24_LE( stream, to, toCount );
+			break;
+		default:
+			DPRINTK( "ep93xx_i2s copy to user unsupported audio format\n" );
+			break;
+	}
+	return ret;
+}
+
+static __inline__ int copy_from_user_S24_LE
+(
+ audio_stream_t *stream,
+ const char *from, 
+ int toCount 
+ )
+{
+	int *dma_buffer_0 = (int *)stream->hwbuf[0];
+	int *dma_buffer_1 = (int *)stream->hwbuf[1];
+	int *dma_buffer_2 = (int *)stream->hwbuf[2];
+	int count;
+
+	unsigned int * user_buffer = (unsigned int *)from;
+	unsigned int data;
+
+	int toCount0 = toCount;
+	count = 8 * stream->dma_num_channels;
+
+	while (toCount > 0){
+
+		__get_user(data, user_buffer++);
+		*dma_buffer_0++ = (unsigned int)data;
+		__get_user(data, user_buffer++);
+		*dma_buffer_0++ = (unsigned int)data;
+
+		if(stream->audio_channels_flag & CHANNEL_REAR ){
+			__get_user(data, user_buffer++);
+			*dma_buffer_1++ = (unsigned int)data;
+			__get_user(data, user_buffer++);
+			*dma_buffer_1++ = (unsigned int)data;
+		}
+
+		if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+			__get_user(data, user_buffer++);
+			*dma_buffer_2++ = (unsigned int)data;
+			__get_user(data, user_buffer++);
+			*dma_buffer_2++ = (unsigned int)data;
+		}
+		toCount -= count;
+	}
+	return toCount0 / 2;
+}
+
+static __inline__ int copy_from_user_U24_LE
+(
+ audio_stream_t *stream,
+ const char *from, 
+ int toCount 
+ )
+{
+	int *dma_buffer_0 = (int *)stream->hwbuf[0];
+	int *dma_buffer_1 = (int *)stream->hwbuf[1];
+	int *dma_buffer_2 = (int *)stream->hwbuf[2];
+	int count;
+	unsigned int * user_buffer = (unsigned int *)from;
+	unsigned int data;
+
+	int toCount0 = toCount;
+	count = 8 * stream->dma_num_channels;
+
+	while (toCount > 0){
+
+		__get_user(data, user_buffer++);
+		*dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
+		__get_user(data, user_buffer++);
+		*dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
+
+		if(stream->audio_channels_flag & CHANNEL_REAR ){
+			__get_user(data, user_buffer++);
+			*dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
+			__get_user(data, user_buffer++);
+			*dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
+		}
+
+		if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+			__get_user(data, user_buffer++);
+			*dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
+			__get_user(data, user_buffer++);
+			*dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
+		}
+		toCount -= count;
+	}
+	return toCount0 / 2;
+}
+
+static __inline__ int copy_from_user_S16_LE
+(
+ audio_stream_t *stream,
+ const char *from, 
+ int toCount 
+ )
+{
+	int *dma_buffer_0 = (int *)stream->hwbuf[0];
+	int *dma_buffer_1 = (int *)stream->hwbuf[1];
+	int *dma_buffer_2 = (int *)stream->hwbuf[2];
+	unsigned short *user_buffer = (unsigned short *)from;
+	unsigned short data;
+
+	int toCount0 = toCount;
+	int count;
+	count = 8 * stream->dma_num_channels;
+
+	while (toCount > 0){
+
+		__get_user(data, user_buffer++);
+		*dma_buffer_0++ = data;
+		__get_user(data, user_buffer++);
+		*dma_buffer_0++ = data;
+
+		if(stream->audio_channels_flag & CHANNEL_REAR ){
+			__get_user(data, user_buffer++);
+			*dma_buffer_1++ = data;
+			__get_user(data, user_buffer++);
+			*dma_buffer_1++ = data;
+		}
+
+		if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+			__get_user(data, user_buffer++);
+			*dma_buffer_2++ = data;
+			__get_user(data, user_buffer++);
+			*dma_buffer_2++ = data;
+		}
+		toCount -= count;
+	}
+	return toCount0 / 2;
+}
+
+static __inline__ int copy_from_user_U16_LE
+(
+ audio_stream_t *stream,
+ const char *from, 
+ int toCount 
+ )
+{
+	int *dma_buffer_0 = (int *)stream->hwbuf[0];
+	int *dma_buffer_1 = (int *)stream->hwbuf[1];
+	int *dma_buffer_2 = (int *)stream->hwbuf[2];
+	int count;
+	unsigned short * user_buffer = (unsigned short *)from;
+	unsigned short data;
+
+	int toCount0 = toCount;
+	count = 8 * stream->dma_num_channels;
+
+	while (toCount > 0){
+
+		__get_user(data, user_buffer++);
+		*dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
+		__get_user(data, user_buffer++);
+		*dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
+
+		if(stream->audio_channels_flag & CHANNEL_REAR ){
+			__get_user(data, user_buffer++);
+			*dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
+			__get_user(data, user_buffer++);
+			*dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
+		}
+
+		if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+			__get_user(data, user_buffer++);
+			*dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
+			__get_user(data, user_buffer++);
+			*dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
+		}
+		toCount -= count;
+	}
+	return toCount0 / 2;
+}
+
+static __inline__ int copy_from_user_S8
+(
+ audio_stream_t *stream,
+ const char *from, 
+ int toCount 
+ )
+{
+	char *dma_buffer_0 = (char *)stream->hwbuf[0];
+	char *dma_buffer_1 = (char *)stream->hwbuf[1];
+	char *dma_buffer_2 = (char *)stream->hwbuf[2];
+	int count;
+	unsigned char * user_buffer = (unsigned char *)from;
+	unsigned char data;
+
+	int toCount0 = toCount;
+	count = 8 * stream->dma_num_channels;
+
+	dma_buffer_0++;
+	dma_buffer_1++;
+	dma_buffer_2++;
+
+	while (toCount > 0){
+		__get_user(data, user_buffer++);
+		*dma_buffer_0 = data;
+		dma_buffer_0 += 4;
+		__get_user(data, user_buffer++);
+		*dma_buffer_0 = data;
+		dma_buffer_0 += 4;
+
+		if(stream->audio_channels_flag & CHANNEL_REAR ){
+			__get_user(data, user_buffer++);
+			*dma_buffer_1 = data;
+			dma_buffer_1 += 4;
+			__get_user(data, user_buffer++);
+			*dma_buffer_1 = data;
+			dma_buffer_1 += 4;
+		}
+
+		if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+			__get_user(data, user_buffer++);
+			*dma_buffer_2 = data;
+			dma_buffer_2 += 4;
+			__get_user(data, user_buffer++);
+			*dma_buffer_2 = data;
+			dma_buffer_2 += 4;
+		}
+		toCount -= count;
+	}
+	return toCount0 / 4;
+}
+
+static __inline__ int copy_from_user_U8
+(
+ audio_stream_t *stream,
+ const char *from, 
+ int toCount 
+ )
+{
+	char *dma_buffer_0 = (char *)stream->hwbuf[0];
+	char *dma_buffer_1 = (char *)stream->hwbuf[1];
+	char *dma_buffer_2 = (char *)stream->hwbuf[2];
+	int count;
+	unsigned char *user_buffer = (unsigned char *)from;
+	unsigned char data;
+
+	int toCount0 = toCount;
+	count = 8 * stream->dma_num_channels;
+
+	dma_buffer_0 ++;
+	dma_buffer_1 ++;
+	dma_buffer_2 ++;
+
+	while (toCount > 0){
+
+		__get_user(data, user_buffer++);
+		*dma_buffer_0 = ((unsigned char)data ^ 0x80);
+		dma_buffer_0 += 4;
+		__get_user(data, user_buffer++);
+		*dma_buffer_0 = ((unsigned char)data ^ 0x80);
+		dma_buffer_0 += 4;
+
+		if(stream->audio_channels_flag & CHANNEL_REAR ){
+			__get_user(data, user_buffer++);
+			*dma_buffer_1 = ((unsigned char)data ^ 0x80);
+			dma_buffer_1 += 4;
+			__get_user(data, user_buffer++);
+			*dma_buffer_1 = ((unsigned char)data ^ 0x80);
+			dma_buffer_1 += 4;
+		}
+
+		if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
+			__get_user(data, user_buffer++);
+			*dma_buffer_2 = ((unsigned char)data ^ 0x80);
+			dma_buffer_2 += 4;
+			__get_user(data, user_buffer++);
+			*dma_buffer_2 = ((unsigned char)data ^ 0x80);
+			dma_buffer_2 += 4;
+		}
+		toCount -= count;
+	}
+	return toCount0 / 4;
+}
+
+/*
+ * Returns negative for error
+ * Returns # of bytes transferred out of the from buffer
+ * for success.
+ */
+static __inline__ int copy_from_user_with_conversion
+(
+ audio_stream_t *stream,
+ const char *from, 
+ int toCount 
+ )
+{
+	int ret = 0;
+	//    DPRINTK("copy_from_user_with_conversion\n");	
+	if( toCount == 0 ){
+		DPRINTK("ep93xx_i2s_copy_from_user_with_conversion - nothing to copy!\n");
+	}
+
+	switch( stream->audio_format ){
+
+		case SNDRV_PCM_FORMAT_S8:
+			//DPRINTK("SNDRV_PCM_FORMAT_S8\n");
+			ret = copy_from_user_S8( stream, from, toCount );
+			break;
+
+		case SNDRV_PCM_FORMAT_U8:
+			//DPRINTK("SNDRV_PCM_FORMAT_U8\n");
+			ret = copy_from_user_U8( stream, from, toCount );
+			break;
+
+		case SNDRV_PCM_FORMAT_S16_LE:
+			//DPRINTK("SNDRV_PCM_FORMAT_S16_LE\n");
+			ret = copy_from_user_S16_LE( stream, from, toCount );
+			break;
+
+		case SNDRV_PCM_FORMAT_U16_LE:
+			//DPRINTK("SNDRV_PCM_FORMAT_U16_LE\n");
+			ret = copy_from_user_U16_LE( stream, from, toCount );
+			break;
+
+		case SNDRV_PCM_FORMAT_S24_LE:
+			//DPRINTK("SNDRV_PCM_FORMAT_S24_LE\n");
+			ret = copy_from_user_S24_LE( stream, from, toCount );
+			break;
+
+		case SNDRV_PCM_FORMAT_U24_LE:
+			//DPRINTK("SNDRV_PCM_FORMAT_U24_LE\n");
+			ret = copy_from_user_U24_LE( stream, from, toCount );
+			break;
+		default:
+			DPRINTK( "ep93xx_i2s copy from user unsupported audio format\n" );
+			break;			
+	}
+
+	return ret;
+}
+
+/*
+ *  For audio playback, we convert samples of arbitrary format to be 32 bit 
+ *  for our hardware. We're scaling a user buffer to a dma buffer.  So when
+ *  report byte counts, we scale them acording to the ratio of DMA sample
+ *  size to user buffer sample size.  When we report # of DMA fragments,
+ *  we don't scale that.  So:
+ *
+ *  Also adjust the size and number of dma fragments if sample size changed.
+ *
+ *  Input format       Input sample     Output sample size    ratio (out:in)
+ *  bits   channels    size (bytes)       CM   non-CM          CM   non-CM
+ *   8      stereo         2		   4      8            2:1   4:1
+ *   16     stereo         4		   4      8            1:1   2:1
+ *   24     stereo         6		   4      8             X    8:6 not a real case
+ *
+ */
+static void snd_ep93xx_dma2usr_ratio( audio_stream_t * stream )
+{
+	unsigned int dma_sample_size, user_sample_size;
+
+	dma_sample_size = 8;	/* each stereo sample is 2 * 32 bits */
+
+	// If stereo 16 bit, user sample is 4 bytes.
+	// If stereo  8 bit, user sample is 2 bytes.
+
+	user_sample_size = stream->audio_stream_bitwidth / 4;
+
+	stream->dma2usr_ratio = dma_sample_size / user_sample_size;
+}
+
+/*---------------------------------------------------------------------------------------------*/
+
+static int snd_ep93xx_dma_free(struct snd_pcm_substream *substream ){
+
+
+	audio_state_t *state = substream->private_data;
+	audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		state->output_stream:state->input_stream;
+	int i;
+	DPRINTK("snd_ep93xx_dma_free - enter\n");
+
+	for( i = 0 ; i < stream->dma_num_channels ;i++ ){
+		DPRINTK("snd_ep93xx_dma_free %d\n",i);
+		ep93xx_dma_free( stream->dmahandles[i] );
+	}
+	DPRINTK("snd_ep93xx_dma_free - exit\n");
+	return 0;	       
+}
+
+static int snd_ep93xx_dma_config(struct snd_pcm_substream *substream ){
+
+	audio_state_t *state = substream->private_data;
+	audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		state->output_stream:state->input_stream;
+	int i,err = 0;
+
+	DPRINTK("snd_ep93xx_dma_config - enter\n");
+
+	for( i = 0 ; i < stream->dma_num_channels ;i++ ){
+
+		err = ep93xx_dma_request(&stream->dmahandles[i],
+				stream->devicename,
+				(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+				state->output_dma[i]:state->input_dma[i] );
+		if (err){
+			printk("snd_ep93xx_dma_config - exit ERROR dma request failed\n");
+			return err;
+		}
+		err = ep93xx_dma_config( stream->dmahandles[i],
+				IGNORE_CHANNEL_ERROR,
+				0,
+				(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 
+				snd_ep93xx_dma_tx_callback:snd_ep93xx_dma_rx_callback,
+				(unsigned int)substream );
+		if (err){
+			printk("snd_ep93xx_dma_config - exit ERROR dma request failed\n");
+			return err;
+		}
+	}
+
+	DPRINTK("snd_ep93xx_dma_config - enter\n");
+	return err;
+}
+
+static void snd_ep93xx_dma_start( audio_state_t * state, audio_stream_t * stream )
+{
+	int err,i;
+
+	DPRINTK("snd_ep93xx_dma_start - enter\n");
+
+	for(i = 0 ;i < stream->dma_num_channels;i++)
+		err = ep93xx_dma_start( stream->dmahandles[i], 1,(unsigned int *) stream->dmahandles );
+
+	stream->active = 1;
+
+	DPRINTK("snd_ep93xx_dma_start - exit\n");
+}
+
+static void snd_ep93xx_dma_pause( audio_state_t * state, audio_stream_t * stream )
+{
+	int i;
+
+	DPRINTK("snd_ep93xx_dma_pause - enter\n");
+
+	for(i = 0 ;i < stream->dma_num_channels;i++)
+		ep93xx_dma_pause( stream->dmahandles[i], 1,(unsigned int *)stream->dmahandles );
+
+	stream->active = 0;
+	DPRINTK("snd_ep93xx_dma_pause - exit\n");
+
+}
+
+static void snd_ep93xx_dma_flush( audio_state_t * state, audio_stream_t * stream ){
+
+	int i;
+
+	DPRINTK("snd_ep93xx_dma_flush - enter\n");
+
+	for( i = 0 ; i < stream->dma_num_channels ; i++ )
+		ep93xx_dma_flush( stream->dmahandles[i] );
+
+	DPRINTK("snd_ep93xx_dma_flush - exit\n");
+}
+
+static void snd_ep93xx_deallocate_buffers(struct snd_pcm_substream *substream, audio_stream_t *stream )
+{
+	int i;
+	audio_channel_t *dma_chan;
+
+	DPRINTK("snd_ep93xx_deallocate_buffers - enter\n");
+
+	if( stream->dma_channels ){
+
+		for(i = 0;i < stream->dma_num_channels;i++){
+
+			dma_chan = &stream->dma_channels[i];
+
+			if( dma_chan->area ){
+
+				if( dma_chan->audio_buffers ){
+
+					kfree(dma_chan->audio_buffers);
+					dma_chan->audio_buffers = NULL;
+
+				}
+
+				//kfree(dma_chan->area);
+				dma_free_writecombine(0, dma_chan->bytes,
+						dma_chan->area, dma_chan->addr);
+				dma_chan->area = NULL;
+			}    
+		}
+		kfree(stream->dma_channels);
+		stream->dma_channels = NULL;
+	}
+	DPRINTK("snd_ep93xx_deallocate_buffers - exit\n");
+}
+
+static int snd_ep93xx_allocate_buffers(struct snd_pcm_substream *substream, audio_stream_t *stream)
+{
+	audio_channel_t *channel;
+	unsigned int size,tmpsize,bufsize,bufextsize;
+	int i,j;
+
+
+	DPRINTK("snd_ep93xx_allocate_buffers - enter\n" );
+
+	if (stream->dma_channels){
+		printk("ep93xx_i2s  %s BUSY\n",__FUNCTION__);
+		return -EBUSY;
+	}
+
+	stream->dma_channels = (audio_channel_t *)kmalloc(sizeof(audio_channel_t) * stream->dma_num_channels , GFP_KERNEL);
+
+	if (!stream->dma_channels){
+		printk(AUDIO_NAME ": unable to allocate dma_channels memory\n");
+		return - ENOMEM;
+	}
+
+	size = ( stream->dmasize / stream->dma_num_channels ) * stream->dma2usr_ratio; 
+
+	for( i = 0; i < stream->dma_num_channels;i++){
+		channel = &stream->dma_channels[i];
+
+		//channel->area = kmalloc( size, GFP_DMA );
+		channel->area = dma_alloc_writecombine(0, size,&channel->addr,GFP_KERNEL);		
+		if(!channel->area){
+			printk(AUDIO_NAME ": unable to allocate audio memory\n");
+			return -ENOMEM;
+		}	
+		channel->bytes = size;
+		//channel->addr = __virt_to_phys((int) channel->area);
+		memset( channel->area, 0, channel->bytes );
+
+		bufsize = ( stream->fragsize / stream->dma_num_channels ) * stream->dma2usr_ratio;
+		channel->audio_buff_count = size / bufsize;
+		bufextsize = size % bufsize;
+
+		if( bufextsize > 0 ){
+			channel->audio_buff_count++;
+		}
+
+		channel->audio_buffers = (audio_buf_t *)kmalloc(sizeof(audio_buf_t) * channel->audio_buff_count , GFP_KERNEL);
+
+		if (!channel->audio_buffers){
+			printk(AUDIO_NAME ": unable to allocate audio memory\n ");
+			return -ENOMEM;
+		}
+
+		tmpsize = size;
+
+		for( j = 0; j < channel->audio_buff_count; j++){
+
+			channel->audio_buffers[j].dma_addr = channel->addr + j * bufsize;		
+
+			if( tmpsize >= bufsize ){
+				tmpsize -= bufsize;
+				channel->audio_buffers[j].bytes = bufsize;
+				channel->audio_buffers[j].reportedbytes = bufsize / stream->dma2usr_ratio; 
+			}
+			else{
+				channel->audio_buffers[j].bytes = bufextsize;
+				channel->audio_buffers[j].reportedbytes = bufextsize / stream->dma2usr_ratio;
+			}
+		}								
+	}
+
+	DPRINTK("snd_ep93xx_allocate_buffers -- exit SUCCESS\n" );
+	return 0;
+}
+
+/*
+ * DMA callback functions
+ */
+
+static void snd_ep93xx_dma_tx_callback
+( 
+ ep93xx_dma_int_t DMAInt,
+ ep93xx_dma_dev_t device, 
+ unsigned int user_data 
+ )
+{
+	int handle;
+	int i,chan;
+	unsigned int buf_id;
+
+	struct snd_pcm_substream *substream = (struct snd_pcm_substream *)user_data;
+	audio_state_t *state = (audio_state_t *)(substream->private_data);
+	audio_stream_t *stream = state->output_stream;
+	audio_buf_t *buf;
+
+	switch( device )              
+	{
+		case DMATx_I2S3:
+			//	    DPRINTK( "snd_ep93xx_dma_tx_callback - DMATx_I2S3\n");
+			i = 2;
+			break;
+		case DMATx_I2S2:
+			//	    DPRINTK( "snd_ep93xx_dma_tx_callback - DMATx_I2S2\n");
+			i = 1;
+			break;
+		case DMATx_I2S1:
+		default:
+			//	    DPRINTK( "snd_ep93xx_dma_tx_callback - DMATx_I2S1\n");
+			i = 0;
+			break;
+	}
+
+	chan = stream->audio_num_channels / 2 - 1;
+	handle = stream->dmahandles[i];
+
+	if(stream->stopped == 0){
+
+		if( ep93xx_dma_remove_buffer( handle, &buf_id ) >= 0 ){
+
+			buf = (audio_buf_t *)buf_id;
+			stream->bytecount += buf->reportedbytes;
+			ep93xx_dma_add_buffer( stream->dmahandles[i],
+					(unsigned int)buf->dma_addr,
+					0,
+					buf->bytes,
+					0,
+					(unsigned int) buf );
+
+			if(chan == i)
+				snd_pcm_period_elapsed(substream);
+		}
+	}
+}
+
+static void snd_ep93xx_dma_rx_callback
+(
+ ep93xx_dma_int_t DMAInt,
+ ep93xx_dma_dev_t device, 
+ unsigned int user_data 
+ )
+{
+	int handle,i,chan;
+	unsigned int buf_id;
+	audio_buf_t *buf;
+
+	struct snd_pcm_substream *substream = (struct snd_pcm_substream *)user_data;
+	audio_state_t *state = (audio_state_t *)(substream->private_data);
+	audio_stream_t *stream = state->input_stream;
+
+	switch( device ){
+
+		case DMARx_I2S3:
+			//    	    DPRINTK( "snd_ep93xx_dma_rx_callback - DMARx_I2S3\n");
+			i = 2;
+			break;
+		case DMARx_I2S2:
+			//          DPRINTK( "snd_ep93xx_dma_rx_callback - DMARx_I2S2\n");
+			i = 1;
+			break;
+		case DMARx_I2S1:
+		default:
+			//	    DPRINTK( "snd_ep93xx_dma_rx_callback - DMARx_I2S1\n");
+			i = 0;
+			break;
+	}
+	chan = stream->audio_num_channels / 2 - 1;
+	handle = stream->dmahandles[i];
+
+	if( stream->stopped == 0 ){
+
+		if( ep93xx_dma_remove_buffer( handle, &buf_id ) >= 0 ){
+
+			buf = (audio_buf_t *)buf_id;
+			stream->bytecount += buf->reportedbytes;
+			ep93xx_dma_add_buffer( stream->dmahandles[i],
+					(unsigned int)buf->dma_addr,
+					0, 
+					buf->bytes,
+					0,
+					(unsigned int) buf );
+
+			if( i == chan )
+				snd_pcm_period_elapsed(substream);
+		}
+	} 
+}
+
+static int snd_ep93xx_release(struct snd_pcm_substream *substream)
+{
+	audio_state_t *state = (audio_state_t *)substream->private_data;
+	audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		state->output_stream : state->input_stream;
+
+	DPRINTK("snd_ep93xx_release - enter\n");
+
+	down(&state->sem);
+	stream->active = 0;
+	stream->stopped = 0;
+	snd_ep93xx_deallocate_buffers(substream, stream);
+	up(&state->sem);
+
+	DPRINTK("snd_ep93xx_release - exit\n");
+
+	return 0;
+}
+
+static int snd_ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	DPRINTK("snd_ep93xx_pcm_hw_params - enter\n");
+	return snd_pcm_lib_malloc_pages(substream,params_buffer_bytes(params));
+}
+
+static int snd_ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+
+	DPRINTK("snd_ep93xx_pcm_hw_free - enter\n");
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ *snd_ep93xx_pcm_prepare: need to finish these functions as lower
+ *chip_set_sample_format
+ *chip_set_sample_rate
+ *chip_set_channels
+ *chip_set_dma_setup
+ */
+
+static int snd_ep93xx_pcm_prepare_playback(struct snd_pcm_substream *substream)
+{
+	audio_state_t *state = (audio_state_t *) substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	audio_stream_t *stream = state->output_stream;
+
+	DPRINTK("snd_ep93xx_pcm_prepare_playback - enter\n");
+
+	snd_ep93xx_i2s_disable_transmit();
+
+	snd_ep93xx_deallocate_buffers(substream,stream);
+
+	if(runtime->channels % 2 != 0)
+		return -1;
+
+	//    printk("The runtime item : \n");
+	//    printk("runtime->dma_addr    = 0x%x\n", runtime->dma_addr);
+	//    printk("runtime->dma_area    = 0x%x\n", runtime->dma_area);
+	//    printk("runtime->dma_bytes   = %d\n",   runtime->dma_bytes);
+	//    printk("runtime->frame_bits  = %d\n",   runtime->frame_bits);
+	//    printk("runtime->buffer_size = %d\n",   runtime->buffer_size);
+	//    printk("runtime->period_size = %d\n",   runtime->period_size);
+	//    printk("runtime->periods     = %d\n",   runtime->periods);
+	//    printk("runtime->rate        = %d\n",   runtime->rate);
+	//    printk("runtime->format      = %d\n",   runtime->format);
+	//    printk("runtime->channels    = %d\n",   runtime->channels);
+
+	/* set requestd format when available */
+	stream->audio_num_channels = runtime->channels;
+	stream->dma_num_channels = runtime->channels / 2;
+
+
+	stream->audio_channels_flag = CHANNEL_FRONT;
+	if(stream->dma_num_channels == 2)
+		stream->audio_channels_flag |= CHANNEL_REAR;
+	if(stream->dma_num_channels == 3)
+		stream->audio_channels_flag |= CHANNEL_REAR | CHANNEL_CENTER_LFE;
+
+	stream->dmasize = runtime->dma_bytes;
+	stream->nbfrags = runtime->periods;
+	stream->fragsize = frames_to_bytes (runtime, runtime->period_size);
+	stream->bytecount = 0;
+
+	if( !state->codec_set_by_capture ){
+		state->codec_set_by_playback = 1;
+
+		if( stream->audio_rate != runtime->rate ){
+			ep93xx_set_samplerate( runtime->rate );
+		}    
+		if( stream->audio_format != runtime->format ){
+			snd_ep93xx_i2s_init((stream->audio_stream_bitwidth == 24));
+		}
+	}
+	else{
+		audio_stream_t *s = state->input_stream;
+		if( runtime->format != s->audio_format)
+			return -1;
+		if( runtime->rate != s->audio_rate )
+			return -1;
+	}
+
+	stream->audio_rate = runtime->rate;
+	audio_set_format( stream, runtime->format );
+	snd_ep93xx_dma2usr_ratio( stream );
+
+	if( snd_ep93xx_allocate_buffers( substream, stream ) != 0 ){
+		snd_ep93xx_deallocate_buffers( substream, stream );
+		return -1;
+	}
+
+	snd_ep93xx_i2s_enable_transmit();
+
+	DPRINTK("snd_ep93xx_pcm_prepare_playback - exit\n");
+	return 0;	
+}
+
+static int snd_ep93xx_pcm_prepare_capture(struct snd_pcm_substream *substream)
+{
+	audio_state_t *state = (audio_state_t *) substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	audio_stream_t *stream = state->input_stream;
+
+	snd_ep93xx_i2s_disable_receive();
+
+	snd_ep93xx_deallocate_buffers(substream,stream);
+
+	if(runtime->channels % 2 != 0)
+		return -1;
+
+	DPRINTK("snd_ep93xx_pcm_prepare_capture - enter\n");
+
+	//    printk("The runtime item : \n");
+	//    printk("runtime->dma_addr    = 0x%x\n", runtime->dma_addr);
+	//    printk("runtime->dma_area    = 0x%x\n", runtime->dma_area);
+	//    printk("runtime->dma_bytes   = %d\n",   runtime->dma_bytes);
+	//    printk("runtime->frame_bits  = %d\n",   runtime->frame_bits);
+	//    printk("runtime->buffer_size = %d\n",   runtime->buffer_size);
+	//    printk("runtime->period_size = %d\n",   runtime->period_size);
+	//    printk("runtime->periods     = %d\n",   runtime->periods);
+	//    printk("runtime->rate        = %d\n",   runtime->rate);
+	//    printk("runtime->format      = %d\n",   runtime->format);
+	//    printk("runtime->channels    = %d\n",   runtime->channels);
+
+	/* set requestd format when available */
+	stream->audio_num_channels = runtime->channels;
+	stream->dma_num_channels = runtime->channels / 2;
+
+	stream->audio_channels_flag = CHANNEL_FRONT;
+	if(stream->dma_num_channels == 2)
+		stream->audio_channels_flag |= CHANNEL_REAR;
+	if(stream->dma_num_channels == 3)
+		stream->audio_channels_flag |= CHANNEL_REAR | CHANNEL_CENTER_LFE;
+
+	stream->dmasize = runtime->dma_bytes;
+	stream->nbfrags = runtime->periods;
+	stream->fragsize = frames_to_bytes (runtime, runtime->period_size);
+	stream->bytecount = 0;
+
+	if( !state->codec_set_by_playback ){
+		state->codec_set_by_capture = 1;
+
+		if( stream->audio_rate != runtime->rate ){
+			ep93xx_set_samplerate( runtime->rate );
+		}
+		if( stream->audio_format != runtime->format ){
+			snd_ep93xx_i2s_init((stream->audio_stream_bitwidth == 24));
+		}
+	}
+	else{
+		audio_stream_t *s = state->output_stream;
+		if( runtime->format != s->audio_format)
+			return -1;
+		if( runtime->rate != s->audio_rate )
+			return -1;
+	}
+
+	stream->audio_rate = runtime->rate;
+	audio_set_format( stream, runtime->format );
+	snd_ep93xx_dma2usr_ratio( stream );
+
+	if( snd_ep93xx_allocate_buffers( substream, stream ) != 0 ){
+		snd_ep93xx_deallocate_buffers( substream, stream );
+		return -1;
+	}
+
+	snd_ep93xx_i2s_enable_receive();
+
+	DPRINTK("snd_ep93xx_pcm_prepare_capture - exit\n");
+	return 0;	
+}
+/*
+ *start/stop/pause dma translate
+ */
+static int snd_ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{	
+	audio_state_t  *state = (audio_state_t *)substream->private_data;
+	audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		state->output_stream:state->input_stream;
+	audio_buf_t *buf; 
+	audio_channel_t *dma_channel;
+	int i,count,ret = 0;
+	unsigned long flags;
+
+	DPRINTK("snd_ep93xx_pcm_triger %d - enter \n",cmd);
+
+	switch (cmd){
+
+		case SNDRV_PCM_TRIGGER_START:
+
+			snd_ep93xx_dma_config( substream );
+
+			stream->stopped = 0;
+
+			if( !stream->active && !stream->stopped ){
+				stream->active = 1;
+				snd_ep93xx_dma_start( state, stream );
+			}
+
+			local_irq_save(flags);
+
+			for (i = 0; i < stream->dma_num_channels; i++){
+				dma_channel = &stream->dma_channels[i];
+
+				memset(dma_channel->area,0,dma_channel->bytes);
+
+				for(count = 0 ;count < dma_channel->audio_buff_count; count++){
+
+					buf = &dma_channel->audio_buffers[count];																	
+					ep93xx_dma_add_buffer( stream->dmahandles[i],
+							(unsigned int)buf->dma_addr,
+							0,
+							buf->bytes,
+							0,
+							(unsigned int) buf );
+				}
+			}	
+
+			local_irq_restore(flags);
+			break;
+
+		case SNDRV_PCM_TRIGGER_STOP:
+			stream->stopped = 1;
+			snd_ep93xx_dma_pause( state, stream );
+			snd_ep93xx_dma_flush( state, stream );
+			snd_ep93xx_dma_free( substream );
+			break;
+
+		case SNDRV_PCM_TRIGGER_SUSPEND:
+			break;
+		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+			break;
+		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+			break;
+
+		default:
+			ret = -EINVAL;
+	}
+	DPRINTK("snd_ep93xx_pcm_triger %d - exit \n",cmd);
+	return ret;
+}
+
+static snd_pcm_uframes_t snd_ep93xx_pcm_pointer_playback(struct snd_pcm_substream *substream)
+{
+	audio_state_t *state = (audio_state_t *)(substream->private_data);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	audio_stream_t *stream = state->output_stream;
+	snd_pcm_uframes_t pointer = 0;
+
+	pointer = bytes_to_frames( runtime,stream->bytecount );
+
+	if (pointer >= runtime->buffer_size){
+		pointer = 0;
+		stream->bytecount = 0;
+	}
+
+	DPRINTK("snd_ep93xx_pcm_pointer_playback - exit\n");
+	return pointer;
+}
+
+static snd_pcm_uframes_t snd_ep93xx_pcm_pointer_capture(struct snd_pcm_substream *substream)
+{
+	audio_state_t *state = (audio_state_t *)(substream->private_data);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	audio_stream_t *stream = state->input_stream;
+	snd_pcm_uframes_t pointer = 0;
+
+	pointer = bytes_to_frames( runtime,stream->bytecount );
+
+	if (pointer >= runtime->buffer_size){
+		pointer = 0;
+		stream->bytecount = 0;
+	}
+
+	DPRINTK("snd_ep93xx_pcm_pointer_capture - exit\n");
+	return pointer;
+}
+
+static int snd_ep93xx_pcm_open(struct snd_pcm_substream *substream)
+{
+	audio_state_t *state = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		state->output_stream:state->input_stream;
+
+	DPRINTK("snd_ep93xx_pcm_open - enter\n");
+
+	down(&state->sem);
+
+	runtime->hw = ep93xx_i2s_pcm_hardware;
+
+	stream->dma_num_channels = AUDIO_DEFAULT_DMACHANNELS;
+	stream->dma_channels = NULL;
+	stream->audio_rate = 0;
+	stream->audio_stream_bitwidth = 0;
+
+	up(&state->sem);
+
+	DPRINTK("snd_ep93xx_pcm_open - exit\n");
+	return 0;		
+}
+
+/*
+ *free the HW dma channel
+ *free the HW dma buffer
+ *free the Hw dma decrotion using function :kfree
+ */
+static int snd_ep93xx_pcm_close(struct snd_pcm_substream *substream)
+{
+	audio_state_t *state = (audio_state_t *)(substream->private_data);
+
+	DPRINTK("snd_ep93xx_pcm_close - enter\n");
+
+	snd_ep93xx_release(substream);
+
+	if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		state->codec_set_by_playback = 0;
+	else
+		state->codec_set_by_capture = 0;
+
+	ep93xx_set_samplerate( AUDIO_SAMPLE_RATE_DEFAULT );
+
+	DPRINTK("snd_ep93xx_pcm_close - exit\n");
+	return 0;
+}
+
+static int snd_ep93xx_pcm_copy_playback(struct snd_pcm_substream * substream,int channel, 
+		snd_pcm_uframes_t pos,void __user *src, snd_pcm_uframes_t count)
+{
+
+	audio_state_t *state = (audio_state_t *)substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	audio_stream_t *stream = state->output_stream ;
+	audio_channel_t *dma_channel;
+	int i;
+	int tocount = frames_to_bytes(runtime,count);
+
+	for( i = 0; i < stream->dma_num_channels; i++ ){
+
+		dma_channel = &stream->dma_channels[i];	
+		stream->hwbuf[i] = dma_channel->area + ( frames_to_bytes(runtime,pos) * stream->dma2usr_ratio / stream->dma_num_channels );
+
+	}
+
+	if(copy_from_user_with_conversion(stream ,(const char*)src,(tocount * stream->dma2usr_ratio)) <=0 ){
+		DPRINTK(KERN_ERR "copy_from_user_with_conversion() failed\n");
+		return -EFAULT;
+	}
+
+	//printk("snd_ep93xx_pcm_copy_playback %x %x %x- exit\n",stream->dma_num_channels,stream->dma2usr_ratio,frames_to_bytes(runtime,pos));
+
+	DPRINTK("snd_ep93xx_pcm_copy_playback - exit\n");
+	return 0;
+}
+
+
+static int snd_ep93xx_pcm_copy_capture(struct snd_pcm_substream * substream,int channel, 
+		snd_pcm_uframes_t pos,void __user *src, snd_pcm_uframes_t count)
+{
+	audio_state_t *state = (audio_state_t *)substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	audio_stream_t *stream = state->input_stream ;
+	audio_channel_t *dma_channel;
+	int i;
+
+	int tocount = frames_to_bytes(runtime,count);
+
+	for( i = 0; i < stream->dma_num_channels; i++ ){
+
+		dma_channel = &stream->dma_channels[i];
+		stream->hwbuf[i] = dma_channel->area + ( frames_to_bytes(runtime,pos) * stream->dma2usr_ratio / stream->dma_num_channels );
+
+	}
+
+	if(copy_to_user_with_conversion(stream,(const char*)src,tocount) <=0 ){
+
+		DPRINTK(KERN_ERR "copy_to_user_with_conversion() failed\n");
+		return -EFAULT;
+	}
+
+	DPRINTK("snd_ep93xx_pcm_copy_capture - exit\n");
+	return 0;
+}
+
+static struct snd_pcm_ops snd_ep93xx_pcm_playback_ops = {
+	.open		= snd_ep93xx_pcm_open,
+	.close		= snd_ep93xx_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_ep93xx_pcm_hw_params,
+	.hw_free	= snd_ep93xx_pcm_hw_free,
+	.prepare	= snd_ep93xx_pcm_prepare_playback,
+	.trigger	= snd_ep93xx_pcm_trigger,
+	.pointer	= snd_ep93xx_pcm_pointer_playback,
+	.copy		= snd_ep93xx_pcm_copy_playback,
+
+};
+
+static struct snd_pcm_ops snd_ep93xx_pcm_capture_ops = {
+	.open		= snd_ep93xx_pcm_open,
+	.close		= snd_ep93xx_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_ep93xx_pcm_hw_params,
+	.hw_free	= snd_ep93xx_pcm_hw_free,
+	.prepare	= snd_ep93xx_pcm_prepare_capture,
+	.trigger	= snd_ep93xx_pcm_trigger,
+	.pointer	= snd_ep93xx_pcm_pointer_capture,
+	.copy 		= snd_ep93xx_pcm_copy_capture,
+};
+
+static int snd_ep93xx_pcm_new(struct snd_card *card, audio_state_t *state, struct snd_pcm **rpcm)
+{
+	struct snd_pcm *pcm;
+	int play = state->output_stream? 1 : 0;/*SNDRV_PCM_STREAM_PLAYBACK*/
+	int capt = state->input_stream ? 1 : 0;/*SNDRV_PCM_STREAM_CAPTURE*/
+	int ret = 0;
+
+	DPRINTK("snd_ep93xx_pcm_new - enter\n");
+
+	/* Register the new pcm device interface */
+	ret = snd_pcm_new(card, "EP93XX", 0, play, capt, &pcm);
+
+	if (ret){
+		DPRINTK("%s--%x:card=%x,play=%x,capt=%x,&pcm=%x\n",__FUNCTION__,ret,(int)card,play,capt,(int)pcm);
+		goto out;
+	}
+
+	/* allocate the pcm(DMA) memory */
+	ret = snd_pcm_lib_preallocate_pages_for_all(pcm, /*SNDRV_DMA_TYPE_DEV,0,*/SNDRV_DMA_TYPE_CONTINUOUS,snd_dma_continuous_data(GFP_KERNEL),128*1024,128*1024);
+
+	DPRINTK("The substream item : \n");
+	DPRINTK("pcm->streams[0].substream->dma_buffer.addr  = 0x%x\n", pcm->streams[0].substream->dma_buffer.addr);
+	DPRINTK("pcm->streams[0].substream->dma_buffer.area  = 0x%x\n", pcm->streams[0].substream->dma_buffer.area);
+	DPRINTK("pcm->streams[0].substream->dma_buffer.bytes = 0x%x\n", pcm->streams[0].substream->dma_buffer.bytes);
+	DPRINTK("pcm->streams[1].substream->dma_buffer.addr  = 0x%x\n", pcm->streams[1].substream->dma_buffer.addr);
+	DPRINTK("pcm->streams[1].substream->dma_buffer.area  = 0x%x\n", pcm->streams[1].substream->dma_buffer.area);
+	DPRINTK("pcm->streams[1].substream->dma_buffer.bytes = 0x%x\n", pcm->streams[1].substream->dma_buffer.bytes);	
+
+	pcm->private_data = state;
+
+	/* seem to free the pcm data struct-->self dma buffer */
+	pcm->private_free = (void*) snd_pcm_lib_preallocate_free_for_all;
+
+	/* alsa pcm ops setting for SNDRV_PCM_STREAM_PLAYBACK */
+	if (play) {
+		int stream = SNDRV_PCM_STREAM_PLAYBACK;
+		snd_pcm_set_ops(pcm, stream, &snd_ep93xx_pcm_playback_ops);
+	}
+
+	/* alsa pcm ops setting for SNDRV_PCM_STREAM_CAPTURE */	
+	if (capt) {
+		int stream = SNDRV_PCM_STREAM_CAPTURE;
+		snd_pcm_set_ops(pcm, stream, &snd_ep93xx_pcm_capture_ops);
+	}
+
+	if (rpcm)
+		*rpcm = pcm;
+	DPRINTK("snd_ep93xx_pcm_new - exit\n");
+out:
+	return ret;
+}
+
+/* mixer */
+
+
+#ifdef CONFIG_CODEC_CS4228A
+
+static int ep93xx_i2s_volume_info(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info * uinfo)
+{
+	int mask = 181;
+
+	DPRINTK("%s \n",__FUNCTION__);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	uinfo->value.integer.step = 2;	       
+	return 0;
+}
+
+static int ep93xx_i2s_volume_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value * ucontrol)
+{
+	audio_state_t * state = (audio_state_t *)kcontrol->private_data;
+	int max = 181;
+
+	DPRINTK("%s ucontrol->value.integer.value[0]=%x ucontrol->value.integer.value[1]=%x\n",__FUNCTION__,state->playback_volume[0][0],state->playback_volume[0][1]);
+
+	ucontrol->value.integer.value[0] = state->playback_volume[0][0] % (max + 1);/*state->playback_volume[0][0];*/
+	ucontrol->value.integer.value[1] = state->playback_volume[0][1] % (max + 1);/*state->playback_volume[0][1];*/
+	ucontrol->value.integer.value[2] = state->playback_volume[1][0] % (max + 1);/*state->playback_volume[0][0];*/
+	ucontrol->value.integer.value[3] = state->playback_volume[1][1] % (max + 1);/*state->playback_volume[0][1];*/
+	ucontrol->value.integer.value[4] = state->playback_volume[2][0] % (max + 1);
+	ucontrol->value.integer.value[5] = state->playback_volume[2][1] % (max + 1);
+
+	return 0;
+}
+
+static int ep93xx_i2s_volume_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value * ucontrol)
+{
+	int i;
+	audio_state_t *state = (audio_state_t *) kcontrol->private_data;
+	int valL,valR;
+	int reg = 0x07;
+	int max = 181;
+
+	DPRINTK("%s \n",__FUNCTION__);
+
+	state->playback_volume[0][0] = ucontrol->value.integer.value[0];
+	state->playback_volume[0][1] = ucontrol->value.integer.value[1];
+	state->playback_volume[1][0] = ucontrol->value.integer.value[2];
+	state->playback_volume[1][1] = ucontrol->value.integer.value[3];
+	state->playback_volume[2][0] = ucontrol->value.integer.value[4];
+	state->playback_volume[2][1] = ucontrol->value.integer.value[5];
+
+	for( i = 0; i < 3; i++ ){
+		if(state->playback_volume[i][0]>=max){
+			valL = 0;
+			state->playback_volume[i][0] = max;
+		}
+		else{
+			valL = max - state->playback_volume[i][0];
+		}
+
+		if(state->playback_volume[i][1]>=max){
+			valR = 0;
+			state->playback_volume[i][1] = max;
+		}
+		else{
+			valR = max - state->playback_volume[i][1];
+		}
+
+
+		DPRINTK("%x  channle :L=%x,R=%x reg=%x\n",i,valL,valR,reg);
+		//SSPDriver->Write( SSP_Handle, reg++, valL );
+		spi_write_reg(reg++, valL);
+		DPRINTK("reg=%x\n",reg);
+		//SSPDriver->Write( SSP_Handle, reg++, valR );
+		spi_write_reg(reg++, valR);
+	}
+
+	return 0;
+}
+
+#endif
+
+
+
+static int ep93xx_i2s_volume_info_double(struct snd_kcontrol *kcontrol, 
+		struct snd_ctl_elem_info * uinfo)
+{
+
+#ifdef CONFIG_CODEC_CS4271
+	int mask = (kcontrol->private_value >> 16) & 0x7f;
+
+	DPRINTK("%s %x\n",__FUNCTION__,mask);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	uinfo->value.integer.step = 1;
+#else // CONFIG_ARCH_CS4228A
+	int mask = (kcontrol->private_value >> 16);
+
+	DPRINTK("%s max=%x\n",__FUNCTION__,mask); 
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	uinfo->value.integer.step = 2;
+#endif    
+	return 0;
+}
+
+static int ep93xx_i2s_volume_get_double(struct snd_kcontrol * kcontrol,
+		struct snd_ctl_elem_value * ucontrol)
+{
+
+	audio_state_t * state = (audio_state_t *)kcontrol->private_data;
+#ifdef CONFIG_CODEC_CS4271
+	int mask = (kcontrol->private_value >> 16) & 0x7f;
+	int val = 0;
+
+	DPRINTK("%s %x\n",__FUNCTION__,mask);	
+	val = state->playback_volume[0][0];
+	ucontrol->value.integer.value[0] = val & mask;
+
+#else // CONFIG_ARCH_CS4228A
+	unsigned int idx = ucontrol->id.numid -2;
+	int max = (kcontrol->private_value >> 16);
+
+	DPRINTK("%s max=%x idex=%x %x %x\n",__FUNCTION__,max,idx,state->playback_volume[idx][0],state->playback_volume[idx][1]);
+
+	ucontrol->value.integer.value[0] = state->playback_volume[idx][0] % (max + 1);
+	ucontrol->value.integer.value[1] = state->playback_volume[idx][1] % (max + 1);
+#endif
+
+	return 0;
+}
+
+static int ep93xx_i2s_volume_put_double(struct snd_kcontrol * kcontrol,
+		struct snd_ctl_elem_value * ucontrol)
+{
+	int reg1 = (kcontrol->private_value) & 0xf;
+	int reg2 = (kcontrol->private_value >> 4) & 0xf;
+	audio_state_t *state = (audio_state_t *) kcontrol->private_data;	
+#ifdef CONFIG_CODEC_CS4271
+	int mask = (kcontrol->private_value >> 16) & 0x7f;
+	int val = 0;
+	val = mask - (ucontrol->value.integer.value[0] & mask);
+	state->playback_volume[0][0] = ucontrol->value.integer.value[0] & mask;
+
+	DPRINTK("%s %x\n",__FUNCTION__,mask);
+	DPRINTK("reg1 = 0x%x\n", reg1);
+	DPRINTK("reg2 = 0x%x\n", reg2);
+	DPRINTK("mask = %d\n", mask);
+	DPRINTK("val = %d\n", val);
+	DPRINTK("volume = %d\n", state->playback_volume[0][0]);
+
+
+	//SSPDriver->Write( SSP_Handle, reg1, val );
+	spi_write_reg(reg1, val);
+	//SSPDriver->Write( SSP_Handle, reg2, val );
+	spi_write_reg(reg2, val);
+
+#else // CONFIG_ARCH_CS4228A
+	//audio_state_t *state = (audio_state_t *) kcontrol->private_data;
+	unsigned int idx = ucontrol->id.numid -2;
+	int max = (kcontrol->private_value >> 16);
+	int valL=0,valR=0;
+
+	state->playback_volume[idx][0] = ucontrol->value.integer.value[0];
+	state->playback_volume[idx][1] = ucontrol->value.integer.value[1];
+
+	DPRINTK("%s max=%x idex=%x\n",__FUNCTION__,max,idx);
+	DPRINTK("reg1 = 0x%x\n", reg1);
+	DPRINTK("reg2 = 0x%x\n", reg2);
+
+	if(state->playback_volume[idx][0]>=max){
+		valL = 0;
+		state->playback_volume[idx][0] = max;
+	}
+	else{
+		valL = max - state->playback_volume[idx][0];
+	}
+
+	if(state->playback_volume[idx][1]>=max){
+		valR = 0;
+		state->playback_volume[idx][1] = max;
+	}
+	else{
+		valR = max - state->playback_volume[idx][1];
+	}
+
+	//SSPDriver->Write( SSP_Handle, reg1, valL );
+	spi_write_reg(reg1, valL);
+	//SSPDriver->Write( SSP_Handle, reg2, valR );
+	spi_write_reg(reg2, valR);
+#endif
+
+	return 0;
+}
+
+#ifdef CONFIG_CODEC_CS4228A
+
+static int ep93xx_i2s_volume_info_single(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16);
+	DPRINTK("%s \n",__FUNCTION__);				
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	uinfo->value.integer.step = 1;
+	return 0;
+}
+
+static int ep93xx_i2s_volume_get_single(struct snd_kcontrol * kcontrol,
+		struct snd_ctl_elem_value * ucontrol)
+{
+	audio_state_t *state = (audio_state_t *) kcontrol->private_data;
+	int idx = (kcontrol->private_value >> 4) & 0xf;
+	int max = (kcontrol->private_value >> 16);
+
+	DPRINTK("%s idex=%x\n",__FUNCTION__,idx);
+	ucontrol->value.integer.value[0] = state->playback_volume[2][idx] % (max + 1);
+
+	return 0;
+}				       
+
+static int ep93xx_i2s_volume_put_single(struct snd_kcontrol * kcontrol,
+		struct snd_ctl_elem_value * ucontrol)
+{
+	audio_state_t *state = (audio_state_t *) kcontrol->private_data;
+	int reg = (kcontrol->private_value) & 0xf;
+	int idx = (kcontrol->private_value >> 4) & 0xf;
+	int max = (kcontrol->private_value >> 16);
+	int valL=0;
+
+	DPRINTK("%s \n",__FUNCTION__);
+
+	state->playback_volume[2][idx] = ucontrol->value.integer.value[0];
+
+	if(state->playback_volume[2][idx]>=max){
+		valL = 0;
+		state->playback_volume[2][idx] = max;
+	}
+	else{
+		valL = max - state->playback_volume[2][idx];
+	}
+
+	//SSPDriver->Write( SSP_Handle, reg, valL );
+	spi_write_reg(reg, valL);
+
+	return 0;
+}				       
+#endif
+
+
+static struct snd_kcontrol_new snd_ep93xx_i2s_controls[]  = {
+
+#ifdef CONFIG_CODEC_CS4271
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master",
+		.info = ep93xx_i2s_volume_info_double,
+		.get = ep93xx_i2s_volume_get_double,
+		.put = ep93xx_i2s_volume_put_double,
+		.private_value = ( 0x04 ) | ( 0x05 << 4 )| (0x7f << 16)
+	}
+#else // CONFIG_ARCH_CS4228A      
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master",
+		.info = ep93xx_i2s_volume_info,
+		.get = ep93xx_i2s_volume_get,
+		.put = ep93xx_i2s_volume_put,
+	},
+		{
+			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+			.name = "PCM",
+			.info = ep93xx_i2s_volume_info_double,
+			.get = ep93xx_i2s_volume_get_double,
+			.put = ep93xx_i2s_volume_put_double,
+			.private_value = ( 0x07 ) | ( 0x08 << 4 ) | (0xb5 << 16)
+		},
+		{
+			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+			.name = "Surround",
+			.info = ep93xx_i2s_volume_info_double,
+			.get = ep93xx_i2s_volume_get_double,
+			.put = ep93xx_i2s_volume_put_double,
+			.private_value = ( 0x09 ) | ( 0x0A << 4 ) | (0xb5 << 16)
+		},
+		{
+			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+			.name = "Center",
+			.info = ep93xx_i2s_volume_info_single,
+			.get = ep93xx_i2s_volume_get_single,
+			.put = ep93xx_i2s_volume_put_single,
+			.private_value =  0x0B  | (0xb5 <<16)
+		},
+		{
+			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+			.name = "LFE",
+			.info = ep93xx_i2s_volume_info_single,
+			.get = ep93xx_i2s_volume_get_single,
+			.put = ep93xx_i2s_volume_put_single,
+			.private_value = 0x0C | ( 1 << 4 ) | (0xb5 <<16)
+		}
+#endif							    
+};
+
+static int __init snd_ep93xx_mixer_new(struct snd_card *card, audio_state_t *state)
+{
+	int idx, err;
+
+	snd_assert(card != NULL, return -EINVAL);
+
+	strcpy(card->mixername, "Cirrus Logic Mixer");
+
+	for (idx = 0; idx < ARRAY_SIZE(snd_ep93xx_i2s_controls); idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ep93xx_i2s_controls[idx], state))) < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/* module init & exit */
+static int snd_ep93xx_probe(struct platform_device *dev)
+{
+	struct snd_card *card;
+	int err = -ENOMEM;
+	struct resource *res = NULL;
+
+	DPRINTK("snd_ep93xx_probe - enter\n");
+
+	/* Open an instance of the SSP driver for I2S codec. */
+//	SSP_Handle = SSPDriver->Open(I2S_CODEC,0);
+
+//	if( SSP_Handle < 1 ){
+//		printk( KERN_ERR "Couldn't open SSP driver!\n");
+//		return -ENODEV;
+//	}
+	/* Enable audio early on, give the DAC time to come up. */ 
+	res = platform_get_resource( dev, IORESOURCE_MEM, 0);
+
+	if(!res) {
+		printk("error : platform_get_resource \n");
+		return -ENODEV;
+	}
+
+	if (!request_mem_region(res->start,res->end - res->start + 1, "snd-cs4228a" ))
+		return -EBUSY;
+
+
+	snd_ep93xx_audio_init(&audio_state);
+
+	/* register the soundcard */
+	card = snd_card_new(index, id, THIS_MODULE, 0);
+
+	card->dev = &dev->dev;
+
+	if (card == NULL) {
+		DPRINTK(KERN_ERR "snd_card_new() failed\n");
+		goto error;
+	}
+
+#ifdef CONFIG_CODEC_CS4271
+	strcpy(card->driver, "CS4271");
+	strcpy(card->shortname, "Cirrus Logic I2S Audio ");
+	strcpy(card->longname, "Cirrus Logic I2S Audio with CS4271");
+#else
+	strcpy(card->driver, "CS4228A");
+	strcpy(card->shortname, "Cirrus Logic I2S Audio ");
+	strcpy(card->longname, "Cirrus Logic I2S Audio with CS4228A");
+#endif
+
+	/* pcm */
+	if (snd_ep93xx_pcm_new(card, &audio_state, &ep93xx_i2s_pcm) < 0)
+		goto error;
+
+	/* mixer */
+	if (snd_ep93xx_mixer_new(card, &audio_state) < 0)
+		goto error;
+
+	if ((err = snd_card_register(card)) == 0) {
+		printk( KERN_INFO "Cirrus Logic ep93xx i2s audio initialized\n" );
+		platform_set_drvdata(dev,card);
+		DPRINTK("snd_ep93xx_probe - exit\n");
+		return 0;
+	}
+
+error:
+	snd_card_free(card);
+	printk("snd_ep93xx_probe - error\n");
+	return err;
+
+	return 0;
+}
+
+static int snd_ep93xx_remove(struct platform_device *dev)
+{
+	struct resource *res;
+	struct snd_card *card = platform_get_drvdata(dev);
+
+	res = platform_get_resource( dev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, res->end - res->start + 1);
+
+	DPRINTK("snd_ep93xx_remove - enter\n");
+//	SSPDriver->Close(SSP_Handle);
+
+	if (card) {
+		snd_card_free(card);
+		platform_set_drvdata(dev, NULL);
+	}
+	DPRINTK("snd_ep93xx_remove - exit\n");
+
+	return 0;
+}
+
+static int snd_cs4271_probe(struct spi_device *spi)
+{
+	DPRINTK("snd_cs4271_probe called\n");
+	spi_global = spi;
+	return 0;
+}
+
+static int __devexit snd_cs4271_remove(struct spi_device *spi)
+{
+	DPRINTK("snd_cs4271_remove called\n");
+	dev_set_drvdata(&spi->dev, NULL);
+	return 0;
+}
+
+static struct resource ep93xx_i2s_resources = {
+	.start          = I2S_PHYS_BASE,
+	.end            = I2S_PHYS_BASE + 0x6C,
+	.flags          = IORESOURCE_MEM,
+};
+
+
+
+static struct platform_driver snd_ep93xx_driver = {
+	.probe      = snd_ep93xx_probe,
+	.remove     = snd_ep93xx_remove,
+	.driver	= {
+		.name = AUDIO_NAME,
+		.bus  = &platform_bus_type,
+	},
+};
+
+static struct spi_driver cs4271_driver = {
+	.driver		= {
+		.name	= "cs4271",
+		.bus =		&spi_bus_type,
+		.owner =	THIS_MODULE,		
+	},
+	.probe		= snd_cs4271_probe,
+	.remove		= snd_cs4271_remove,
+};
+
+static void snd_ep93xx_platform_release(struct device *device)
+{
+	// This is called when the reference count goes to zero.
+}
+
+static struct platform_device snd_ep93xx_device = {
+	.name   	= AUDIO_NAME,
+	.id     	= -1,
+	.dev    	= {
+		.release = snd_ep93xx_platform_release,
+	},
+	.num_resources  = 1,
+	.resource 	= &ep93xx_i2s_resources,
+
+};						
+
+
+static int __init snd_ep93xx_init(void)
+{
+	int ret;
+
+	DPRINTK("snd_ep93xx_init - enter\n");	
+	ret = platform_driver_register(&snd_ep93xx_driver);
+	if (!ret) {
+		ret = spi_register_driver(&cs4271_driver);
+		if (!ret) {
+			ret = platform_device_register(&snd_ep93xx_device);
+			if (ret)
+				platform_driver_unregister(&snd_ep93xx_driver);
+		}
+	}
+	DPRINTK("snd_ep93xx_init - exit return %d\n",ret);
+	return ret;										
+}
+
+static void __exit snd_ep93xx_exit(void)
+{
+	DPRINTK("snd_ep93xx_exit - enter\n");
+	platform_device_unregister(&snd_ep93xx_device);
+	platform_driver_unregister(&snd_ep93xx_driver);
+	spi_unregister_driver(&cs4271_driver);
+	DPRINTK("snd_ep93xx_exit - exit\n");
+
+}
+
+module_init(snd_ep93xx_init);
+module_exit(snd_ep93xx_exit);
+
+MODULE_DESCRIPTION("Cirrus Logic audio module");
+MODULE_LICENSE("GPL");
diff -Naur linux-2.6.25/sound/arm/ep93xx-i2s.h linux-2.6.25.ep93xx/sound/arm/ep93xx-i2s.h
--- linux-2.6.25/sound/arm/ep93xx-i2s.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.25.ep93xx/sound/arm/ep93xx-i2s.h	2008-09-12 14:50:40.000000000 -0500
@@ -0,0 +1,87 @@
+/*
+ * linux/sound/arm/ep93xx-i2s.c -- ALSA PCM interface for the edb93xx i2s audio
+ *
+ * Author:      Fred Wei
+ * Created:     July 19, 2005
+ * Copyright:   Cirrus Logic, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define EP93XX_DEFAULT_NUM_CHANNELS     2
+#define EP93XX_DEFAULT_FORMAT           SNDRV_PCM_FORMAT_S16_LE
+#define EP93XX_DEFAULT_BIT_WIDTH        16
+#define MAX_DEVICE_NAME 		20
+
+/*
+ * Buffer Management
+ */
+				
+typedef struct {
+
+    unsigned char	*area;    	/* virtual pointer */
+    dma_addr_t 		dma_addr;       /* physical address */
+    size_t 		bytes;      
+    size_t 		reportedbytes;	/* buffer size */
+    int 		sent;		/* indicates that dma has the buf */
+    char		*start;		/* points to actual buffer */
+
+} audio_buf_t;
+
+
+typedef struct {
+
+    unsigned char	*area;  		/* virtual pointer */
+    dma_addr_t 		addr;        		/* physical address */
+    size_t 		bytes;          	/* buffer size in bytes */
+    unsigned char      	*buff_pos;              /* virtual pointer */
+    audio_buf_t        	*audio_buffers; 	/* array of audio buffer structures */
+    int 		audio_buff_count;
+		
+
+} audio_channel_t;
+
+typedef struct audio_stream_s {
+
+    /* dma stuff */
+    int			dmahandles[3];		/* handles for dma driver instances */
+    char		devicename[MAX_DEVICE_NAME]; /* string - name of device */
+    int			dma_num_channels;		/* 1, 2, or 3 DMA channels */
+    audio_channel_t	*dma_channels;
+    u_int 		nbfrags;		/* nbr of fragments i.e. buffers */
+    u_int		fragsize;		/* fragment i.e. buffer size */
+    u_int		dmasize;
+    int 		bytecount;		/* nbr of processed bytes */
+    int 		externedbytecount;	/* nbr of processed bytes */
+    volatile int        active;                 /* actually in progress                 */
+    volatile int        stopped;                /* might be active but stopped          */
+    char 		*hwbuf[3];
+    long		audio_rate;
+    long 		audio_num_channels;		/* Range: 1 to 6 */
+    int			audio_channels_flag;
+    long 		audio_format;
+    long 		audio_stream_bitwidth;		/* Range: 8, 16, 24 */
+    int			dma2usr_ratio;
+
+} audio_stream_t;
+
+
+/*
+ * State structure for one instance
+ */
+typedef struct {
+	    
+    audio_stream_t 	*output_stream;
+    audio_stream_t 	*input_stream;
+    ep93xx_dma_dev_t	output_dma[3];
+    ep93xx_dma_dev_t	input_dma[3];
+    char 		*output_id[3];
+    char 		*input_id[3];
+    struct              semaphore sem;          /* to protect against races in attach() */
+    int			codec_set_by_playback;
+    int                 codec_set_by_capture;
+    unsigned char   	playback_volume[5][2];    		
+} audio_state_t;
+
diff -Naur linux-2.6.25/sound/arm/Kconfig linux-2.6.25.ep93xx/sound/arm/Kconfig
--- linux-2.6.25/sound/arm/Kconfig	2008-04-16 21:49:44.000000000 -0500
+++ linux-2.6.25.ep93xx/sound/arm/Kconfig	2008-09-12 14:50:40.000000000 -0500
@@ -14,6 +14,45 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-sa11xx-uda1341.
 
+config SND_EP93XX_IIS
+	tristate "EP93xx ALSA iis audio driver"
+	depends on ARCH_EP93XX && SND
+	select SND_PCM
+	help
+	  Say Y or M if you have a EP93xx board and want to use iis audio and
+	  to use CS4228A and CS4271.
+
+config CODEC_CS4228A
+	tristate "Cirrus cs4228a 6-channels sound"
+	depends on MACH_EDB9312 || MACH_EDB9315 || MACH_EDB9307
+	help
+	  This module drives the Cirrus Logic device CS4281A when wired
+	  as native sound drivers with I2S codecs. It olny work on
+	  EDB9307/12/15 with disable CS4202 driver.
+
+config CODEC_CS4271
+	tristate "Cirrus cs4271 2-channels sound"
+	depends on (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9315A || MACH_EDB9307A || MACH_EDB9302A || MACH_PPCE7 || MACH_SOM9307M)
+	help
+	  This module drives the Cirrus Logic device CS4271 when wired
+	  as native sound drivers with I2S codecs. It olny work on
+	  EDB9301/02/15A with disable CS4202 driver.
+
+config SND_EP93XX_AC97
+	tristate "AC97 driver for the Cirrus EP93xx chip"
+	depends on ARCH_EP93XX && SND
+	select SND_EP93XX_PCM
+	select SND_AC97_CODEC
+	help
+	  Say Y or M if you want to support any AC97 codec attached to
+	  the EP93xx AC97 interface.
+
+config SND_EP93XX_PCM
+	tristate
+	select SND_PCM
+	help
+	  Generic PCM module for EP93xx
+
 config SND_ARMAACI
 	tristate "ARM PrimeCell PL041 AC Link support"
 	depends on SND && ARM_AMBA
diff -Naur linux-2.6.25/sound/arm/Makefile linux-2.6.25.ep93xx/sound/arm/Makefile
--- linux-2.6.25/sound/arm/Makefile	2008-04-16 21:49:44.000000000 -0500
+++ linux-2.6.25.ep93xx/sound/arm/Makefile	2008-09-12 14:50:40.000000000 -0500
@@ -13,3 +13,9 @@
 
 obj-$(CONFIG_SND_PXA2XX_AC97)	+= snd-pxa2xx-ac97.o
 snd-pxa2xx-ac97-objs		:= pxa2xx-ac97.o
+
+obj-$(CONFIG_SND_EP93XX_IIS) 	+= snd-ep93xx-i2s.o
+snd-ep93xx-i2s-objs 		:= ep93xx-i2s.o
+
+obj-$(CONFIG_SND_EP93XX_AC97)   += snd-ep93xx-ac97.o
+snd-ep93xx-ac97-objs            := ep93xx-ac97.o
diff -Naur linux-2.6.25/sound/spi/cs4271.c linux-2.6.25.ep93xx/sound/spi/cs4271.c
--- linux-2.6.25/sound/spi/cs4271.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.25.ep93xx/sound/spi/cs4271.c	2008-08-18 14:05:42.000000000 -0500
@@ -0,0 +1,945 @@
+/*
+ * Driver for CS4271 24-bit stereo Codec connected to Atmel SSC
+ *
+ * Copyright (C) 2006-2007 Atmel Norway
+ * Copyright (C) 2008 EMAC Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include <linux/atmel-ssc.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/cs4271.h>
+
+#include "cs4271.h"
+
+#define BITRATE_MIN	 8000 /* Hardware limit? */
+#define BITRATE_TARGET	CONFIG_SND_CS4271_TARGET_BITRATE
+#define BITRATE_MAX	50000 /* Hardware limit. */
+
+/* Initial (hardware reset) CS4271 register values. */
+static u8 snd_cs4271_original_image[9] =
+{
+	0x00,	/* 00 - NONE      */
+	0x00,	/* 01 - MODE1     */
+	0x80,	/* 02 - DAC_CTRL  */
+	0x29,	/* 03 - VOL_MIX   */
+	0x00,	/* 04 - VOL_CHANA */
+	0x00,	/* 05 - VOL_CHANB */
+	0x00,	/* 06 - ADC_CTRL  */
+	0x00,	/* 07 - MODE2     */
+	0x00,	/* 08 - DEVICE    */
+};
+
+struct snd_cs4271 {
+	struct snd_card			*card;
+	struct snd_pcm			*pcm;
+	struct snd_pcm_substream	*substream;
+	struct cs4271_board_info	*board;
+	int				irq;
+	int				period;
+	unsigned long			bitrate;
+	struct clk			*bitclk;
+	struct ssc_device		*ssc;
+	struct spi_device		*spi;
+	u8				spi_wbuffer[3];
+	u8				spi_rbuffer[3];
+	/* Image of the SPI registers in CS4271. */
+	u8				reg_image[9];
+	/* Protect SSC registers against concurrent access. */
+	spinlock_t			lock;
+	/* Protect mixer registers against concurrent access. */
+	struct mutex			mixer_lock;
+};
+
+#define get_chip(card) ((struct snd_cs4271 *)card->private_data)
+
+static int
+snd_cs4271_write_reg(struct snd_cs4271 *chip, u8 reg, u8 val)
+{
+	struct spi_message msg;
+	struct spi_transfer msg_xfer = {
+		.len		= 3,
+		.cs_change	= 0,
+	};
+	int retval;
+
+	spi_message_init(&msg);
+
+	chip->spi_wbuffer[0] = 0x20;
+	chip->spi_wbuffer[1] = reg;
+	chip->spi_wbuffer[2] = val;
+
+	msg_xfer.tx_buf = chip->spi_wbuffer;
+	msg_xfer.rx_buf = chip->spi_rbuffer;
+	spi_message_add_tail(&msg_xfer, &msg);
+
+	retval = spi_sync(chip->spi, &msg);
+
+	if (!retval)
+		chip->reg_image[reg] = val;
+
+	udelay(10);
+
+	return retval;
+}
+
+static struct snd_pcm_hardware snd_cs4271_capture_hw = {
+	.info		= SNDRV_PCM_INFO_INTERLEAVED |
+			  SNDRV_PCM_INFO_BLOCK_TRANSFER,
+#ifdef __BIG_ENDIAN
+	.formats	= SNDRV_PCM_FMTBIT_S16_BE,
+#else
+	.formats	= SNDRV_PCM_FMTBIT_S16_LE,
+#endif
+	.rates		= (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+			   SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
+			   SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+			   SNDRV_PCM_RATE_KNOT),
+	.rate_min	= 8000,  /* Replaced by chip->bitrate later. */
+	.rate_max	= 48000, /* Replaced by chip->bitrate later. */
+	.channels_min	= 1,
+	.channels_max	= 2,
+	.buffer_bytes_max = 64 * 1024 - 1,
+	.period_bytes_min = 512,
+	.period_bytes_max = 64 * 1024 - 1,
+	.periods_min	= 4,
+	.periods_max	= 1024,
+};
+
+static struct snd_pcm_hardware snd_cs4271_playback_hw = {
+	.info		= SNDRV_PCM_INFO_INTERLEAVED |
+			  SNDRV_PCM_INFO_BLOCK_TRANSFER,
+#ifdef __BIG_ENDIAN
+	.formats	= SNDRV_PCM_FMTBIT_S16_BE,
+#else
+	.formats	= SNDRV_PCM_FMTBIT_S16_LE,
+#endif
+	.rates		= (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+			   SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
+			   SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+			   SNDRV_PCM_RATE_KNOT),
+	.rate_min	= 8000,  /* Replaced by chip->bitrate later. */
+	.rate_max	= 48000, /* Replaced by chip->bitrate later. */
+	.channels_min	= 1,
+	.channels_max	= 2,
+	.buffer_bytes_max = 64 * 1024 - 1,
+	.period_bytes_min = 512,
+	.period_bytes_max = 64 * 1024 - 1,
+	.periods_min	= 4,
+	.periods_max	= 1024,
+};
+
+/*
+ * Calculate and set bitrate and divisions.
+ */
+static int snd_cs4271_set_bitrate(struct snd_cs4271 *chip)
+{
+	unsigned long ssc_rate = clk_get_rate(chip->ssc->clk);
+	unsigned long dac_rate_new, ssc_div, status;
+	unsigned long ssc_div_max, ssc_div_min;
+	int max_tries;
+
+	/*
+	 * We connect two clocks here, picking divisors so the I2S clocks
+	 * out data at the same rate the DAC clocks it in ... and as close
+	 * as practical to the desired target rate.
+	 *
+	 * The DAC master clock (MCLK) is programmable, and is either 256
+	 * or (not here) 384 times the I2S output clock (BCLK).
+	 */
+
+	/* SSC clock / (bitrate * stereo * 16-bit). */
+	ssc_div = ssc_rate / (BITRATE_TARGET * 2 * 16);
+	ssc_div_min = ssc_rate / (BITRATE_MAX * 2 * 16);
+	ssc_div_max = ssc_rate / (BITRATE_MIN * 2 * 16);
+	max_tries = (ssc_div_max - ssc_div_min) / 2;
+
+	if (max_tries < 1)
+		max_tries = 1;
+
+	/* ssc_div must be a power of 2. */
+	ssc_div = (ssc_div + 1) & ~1UL;
+
+	if ((ssc_rate / (ssc_div * 2 * 16)) < BITRATE_MIN) {
+		ssc_div -= 2;
+		if ((ssc_rate / (ssc_div * 2 * 16)) > BITRATE_MAX)
+			return -ENXIO;
+	}
+
+	/* Search for a possible bitrate. */
+	
+	do {
+		/* SSC clock / (ssc divider * 16-bit * stereo). */
+		if ((ssc_rate / (ssc_div * 2 * 16)) < BITRATE_MIN)
+			return -ENXIO;
+
+		/* 256 / (2 * 16) = 8 */
+		dac_rate_new = 8 * (ssc_rate / ssc_div);
+
+		status = clk_round_rate(chip->board->dac_clk, dac_rate_new);
+		if (status < 0)
+			return status;
+
+		/* Ignore difference smaller than 256 Hz. */
+		if ((status/256) == (dac_rate_new/256))
+			goto set_rate;
+
+		ssc_div += 2;
+	} while (--max_tries);
+
+	/* Not able to find a valid bitrate. */
+	return -ENXIO;
+
+set_rate:
+	status = clk_set_rate(chip->board->dac_clk, status);
+	printk(KERN_ERR "MCLK rate %luHz\n", clk_get_rate(chip->board->dac_clk));
+	if (status < 0)
+		return status;
+
+	/* Set divider in SSC device. */
+	ssc_writel(chip->ssc->regs, CMR, ssc_div/2);
+
+	/* SSC clock / (ssc divider * 16-bit * stereo). */
+	chip->bitrate = ssc_rate / (ssc_div * 16 * 2); 
+
+	dev_info(&chip->spi->dev,
+			"cs4271: supported bitrate is %lu (%lu divider)\n",
+			chip->bitrate, ssc_div);
+
+	return 0;
+}
+
+static int snd_cs4271_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_cs4271 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int stream_id = substream->pstr->stream;
+
+	snd_cs4271_playback_hw.rate_min = chip->bitrate;
+	snd_cs4271_playback_hw.rate_max = chip->bitrate;
+
+	snd_cs4271_capture_hw.rate_min = chip->bitrate;
+	snd_cs4271_capture_hw.rate_max = chip->bitrate;
+
+	if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
+		runtime->hw = snd_cs4271_playback_hw;
+	else 
+		runtime->hw = snd_cs4271_capture_hw;
+
+	chip->substream = substream;
+
+	return 0;
+}
+
+static int snd_cs4271_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_cs4271 *chip = snd_pcm_substream_chip(substream);
+	
+	chip->substream = NULL;
+	return 0;
+}
+
+static int snd_cs4271_pcm_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+static int snd_cs4271_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_cs4271_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_cs4271 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int stream_id = substream->pstr->stream;
+	int block_size;
+
+	block_size = frames_to_bytes(runtime, runtime->period_size);
+
+	chip->period = 0;
+
+	if (stream_id == SNDRV_PCM_STREAM_CAPTURE)
+	{
+		ssc_writel(chip->ssc->regs, PDC_RPR,
+				(long)runtime->dma_addr);
+		ssc_writel(chip->ssc->regs, PDC_RCR, runtime->period_size * 2);
+		ssc_writel(chip->ssc->regs, PDC_RNPR,
+				(long)runtime->dma_addr + block_size);
+		ssc_writel(chip->ssc->regs, PDC_RNCR, runtime->period_size * 2);
+	}
+	else
+	{
+		ssc_writel(chip->ssc->regs, PDC_TPR,
+			(long)runtime->dma_addr);
+		ssc_writel(chip->ssc->regs, PDC_TCR, runtime->period_size * 2);
+		ssc_writel(chip->ssc->regs, PDC_TNPR,
+			(long)runtime->dma_addr + block_size);
+		ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2);
+	}
+
+	return 0;
+}
+
+static int snd_cs4271_pcm_trigger(struct snd_pcm_substream *substream,
+				   int cmd)
+{
+	struct snd_cs4271 *chip = snd_pcm_substream_chip(substream);
+	int stream_id = substream->pstr->stream;
+	int retval = 0;
+
+	spin_lock(&chip->lock);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (stream_id == SNDRV_PCM_STREAM_CAPTURE)
+		{
+			ssc_writel(chip->ssc->regs, IER, SSC_BIT(IER_ENDRX));
+			ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_RXTEN));
+		}
+		else
+		{
+			ssc_writel(chip->ssc->regs, IER, SSC_BIT(IER_ENDTX));
+			ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_TXTEN));
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (stream_id == SNDRV_PCM_STREAM_CAPTURE)
+		{
+			ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_RXTDIS));
+			ssc_writel(chip->ssc->regs, IDR, SSC_BIT(IDR_ENDRX));		
+		}
+		else
+		{
+			ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_TXTDIS));
+			ssc_writel(chip->ssc->regs, IDR, SSC_BIT(IDR_ENDTX));
+		}
+		break;
+	default:
+		dev_dbg(&chip->spi->dev, "spurious command %x\n", cmd);
+		retval = -EINVAL;
+		break;
+	}
+
+	spin_unlock(&chip->lock);
+
+	return retval;
+}
+
+static snd_pcm_uframes_t
+snd_cs4271_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_cs4271 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int stream_id = substream->pstr->stream;
+	snd_pcm_uframes_t pos;
+	unsigned long bytes;
+
+	if(stream_id == SNDRV_PCM_STREAM_CAPTURE)
+	{
+		bytes = ssc_readl(chip->ssc->regs, PDC_RPR)
+			- (unsigned long)runtime->dma_addr;
+	}
+	else
+	{
+		bytes = ssc_readl(chip->ssc->regs, PDC_TPR)
+			- (unsigned long)runtime->dma_addr;
+	}
+
+	pos = bytes_to_frames(runtime, bytes);
+	if (pos >= runtime->buffer_size)
+		pos -= runtime->buffer_size;
+
+	return pos;
+}
+
+static struct snd_pcm_ops cs4271_capture_ops = {
+	.open		= snd_cs4271_pcm_open,
+	.close		= snd_cs4271_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_cs4271_pcm_hw_params,
+	.hw_free	= snd_cs4271_pcm_hw_free,
+	.prepare	= snd_cs4271_pcm_prepare,
+	.trigger	= snd_cs4271_pcm_trigger,
+	.pointer	= snd_cs4271_pcm_pointer,
+};
+
+static struct snd_pcm_ops cs4271_playback_ops = {
+	.open		= snd_cs4271_pcm_open,
+	.close		= snd_cs4271_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_cs4271_pcm_hw_params,
+	.hw_free	= snd_cs4271_pcm_hw_free,
+	.prepare	= snd_cs4271_pcm_prepare,
+	.trigger	= snd_cs4271_pcm_trigger,
+	.pointer	= snd_cs4271_pcm_pointer,
+};
+
+static void snd_cs4271_pcm_free(struct snd_pcm *pcm)
+{
+	struct snd_cs4271 *chip = snd_pcm_chip(pcm);
+	if (chip->pcm) {
+		snd_pcm_lib_preallocate_free_for_all(chip->pcm);
+		chip->pcm = NULL;
+	}
+}
+
+static int __devinit snd_cs4271_pcm_new(struct snd_cs4271 *chip, int device)
+{
+	struct snd_pcm *pcm;
+	int retval;
+
+	retval = snd_pcm_new(chip->card, chip->card->shortname,
+			device, 1, 1, &pcm);
+	if (retval < 0)
+		goto out;
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_cs4271_pcm_free;
+	pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER;
+	strcpy(pcm->name, "cs4271");
+	chip->pcm = pcm;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &cs4271_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &cs4271_capture_ops);
+
+	retval = snd_pcm_lib_preallocate_pages_for_all(chip->pcm,
+			SNDRV_DMA_TYPE_DEV, &chip->ssc->pdev->dev,
+			64 * 1024, 64 * 1024);
+out:
+	return retval;
+}
+
+static irqreturn_t snd_cs4271_interrupt(int irq, void *dev_id)
+{
+	struct snd_cs4271 *chip = dev_id;
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+	u32 status;
+	int offset;
+	int block_size;
+	int next_period;
+	int retval = IRQ_NONE;
+
+	spin_lock(&chip->lock);
+
+	block_size = frames_to_bytes(runtime, runtime->period_size);
+	status = ssc_readl(chip->ssc->regs, IMR);
+
+	if (status & SSC_BIT(IMR_ENDTX)) 
+	{
+		chip->period++;
+		if (chip->period == runtime->periods)
+			chip->period = 0;
+		next_period = chip->period + 1;
+		if (next_period == runtime->periods)
+			next_period = 0;
+
+		offset = block_size * next_period;
+
+		ssc_writel(chip->ssc->regs, PDC_TNPR,
+				(long)runtime->dma_addr + offset);
+		ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2);
+		retval = IRQ_HANDLED;
+	}
+	else if (status & SSC_BIT(IMR_ENDRX)) 
+	{
+		chip->period++;
+		if (chip->period == runtime->periods)
+			chip->period = 0;
+		next_period = chip->period + 1;
+		if (next_period == runtime->periods)
+			next_period = 0;
+
+		offset = block_size * next_period;
+
+		ssc_writel(chip->ssc->regs, PDC_RNPR,
+				(long)runtime->dma_addr + offset);
+		ssc_writel(chip->ssc->regs, PDC_RNCR, runtime->period_size * 2);
+		retval = IRQ_HANDLED;
+	}
+	else	
+	{
+		printk(KERN_WARNING
+				"Spurious SSC interrupt, status = 0x%08lx\n",
+				(unsigned long)status);
+		ssc_writel(chip->ssc->regs, IDR, status);
+	}
+
+	ssc_readl(chip->ssc->regs, IMR);
+	spin_unlock(&chip->lock);
+
+	if ((status & SSC_BIT(IMR_ENDTX))||(status & SSC_BIT(IMR_ENDRX)))
+		snd_pcm_period_elapsed(chip->substream);
+
+	return retval;
+}
+
+/*
+ * Mixer functions.
+ */
+static int snd_cs4271_stereo_info(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_info *uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	if (mask == 1)
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	else
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+
+	return 0;
+}
+
+static int snd_cs4271_stereo_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs4271 *chip = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+
+	mutex_lock(&chip->mixer_lock);
+
+	ucontrol->value.integer.value[0] =
+		(chip->reg_image[left_reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] =
+		(chip->reg_image[right_reg] >> shift_right) & mask;
+
+	if (invert) {
+		ucontrol->value.integer.value[0] =
+			mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] =
+			mask - ucontrol->value.integer.value[1];
+	}
+
+	mutex_unlock(&chip->mixer_lock);
+
+	return 0;
+}
+
+static int snd_cs4271_stereo_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs4271 *chip = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change, retval;
+	unsigned short val1, val2;
+
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+
+	mutex_lock(&chip->mixer_lock);
+
+	val1 = (chip->reg_image[left_reg] & ~(mask << shift_left)) | val1;
+	val2 = (chip->reg_image[right_reg] & ~(mask << shift_right)) | val2;
+	change = val1 != chip->reg_image[left_reg]
+		|| val2 != chip->reg_image[right_reg];
+	retval = snd_cs4271_write_reg(chip, left_reg, val1);
+	if (retval) {
+		mutex_unlock(&chip->mixer_lock);
+		goto out;
+	}
+	retval = snd_cs4271_write_reg(chip, right_reg, val2);
+	if (retval) {
+		mutex_unlock(&chip->mixer_lock);
+		goto out;
+	}
+
+	mutex_unlock(&chip->mixer_lock);
+
+	return change;
+
+out:
+	return retval;
+}
+
+#define CS4271_STEREO(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{									\
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,				\
+	.name = xname,							\
+	.index = xindex,						\
+	.info = snd_cs4271_stereo_info,					\
+	.get = snd_cs4271_stereo_get,					\
+	.put = snd_cs4271_stereo_put,					\
+	.private_value = (left_reg | (right_reg << 8)			\
+			| (shift_left << 16) | (shift_right << 19)	\
+			| (mask << 24) | (invert << 22))		\
+}
+
+static struct snd_kcontrol_new snd_cs4271_controls[] __devinitdata = {
+CS4271_STEREO("PCM Playback Volume", 0, DAC_VOL_CHANA_REG, DAC_VOL_CHANB_REG, 0, 0, 0x7f, 1),
+};
+
+static int __devinit snd_cs4271_mixer(struct snd_cs4271 *chip)
+{
+	struct snd_card *card;
+	int errval, idx;
+
+	if (chip == NULL || chip->pcm == NULL)
+		return -EINVAL;
+
+	card = chip->card;
+
+	strcpy(card->mixername, chip->pcm->name);
+
+	for (idx = 0; idx < ARRAY_SIZE(snd_cs4271_controls); idx++) {
+		errval = snd_ctl_add(card,
+				snd_ctl_new1(&snd_cs4271_controls[idx],
+					chip));
+		if (errval < 0)
+			goto cleanup;
+	}
+
+	return 0;
+
+cleanup:
+	for (idx = 1; idx < ARRAY_SIZE(snd_cs4271_controls) + 1; idx++) {
+		struct snd_kcontrol *kctl;
+		kctl = snd_ctl_find_numid(card, idx);
+		if (kctl)
+			snd_ctl_remove(card, kctl);
+	}
+	return errval;
+}
+
+/*
+ * Device functions
+ */
+static int snd_cs4271_ssc_init(struct snd_cs4271 *chip)
+{
+	ssc_writel(chip->ssc->regs, TCMR,
+			SSC_BF(TCMR_CKO, 1)
+			| SSC_BF(TCMR_START, 7)
+			| SSC_BF(TCMR_STTDLY, 0)
+			| SSC_BF(TCMR_PERIOD, 16 - 1));
+
+	ssc_writel(chip->ssc->regs, RCMR,
+			SSC_BF(RCMR_CKO, 1)
+			| SSC_BF(RCMR_CKI, 1)
+			| SSC_BF(RCMR_START, 0)
+			| SSC_BF(RCMR_STTDLY, 0)
+			| SSC_BF(RCMR_PERIOD, 16 - 1));
+	
+	ssc_writel(chip->ssc->regs, TFMR,
+			SSC_BF(TFMR_DATLEN, 16 - 1)
+			| SSC_BIT(TFMR_MSBF)
+			| SSC_BF(TFMR_DATNB, 1)
+			| SSC_BF(TFMR_FSLEN, 16 - 1)
+			| SSC_BF(TFMR_FSOS, 1));
+
+	ssc_writel(chip->ssc->regs, RFMR,
+			SSC_BF(RFMR_DATLEN, 16 - 1)
+			| SSC_BIT(RFMR_MSBF)
+			| SSC_BF(RFMR_DATNB, 1)
+			| SSC_BF(RFMR_FSLEN, 16 - 1)
+			| SSC_BF(RFMR_FSOS, 1));
+	return 0;
+}
+
+static int snd_cs4271_chip_init(struct snd_cs4271 *chip)
+{
+	int retval;
+
+	retval = snd_cs4271_set_bitrate(chip);
+	if (retval)
+		goto out;
+
+	/* Enable DAC master clock. */
+	clk_enable(chip->board->dac_clk);
+	
+	/* Enable I2S device, i.e. clock output. */
+	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN));
+	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_RXEN));
+
+	/* Enable control */
+	snd_cs4271_write_reg(chip, MODE2_REG, 0x03);
+
+	/* up to 24 bit I2S mode */
+	//snd_cs4271_write_reg(chip, MODE1_REG, 0x01);
+
+	/* Left justified format (seems to work better) */
+	snd_cs4271_write_reg(chip, MODE1_REG, 0x00);
+
+	/* I2S format / 16 bit dither */
+	//snd_cs4271_write_reg(chip, ADC_CTRL_REG, 0x30);
+
+	/* Left justified format / 16 bit dither */
+	snd_cs4271_write_reg(chip, ADC_CTRL_REG, 0x20);
+
+	/* Turn auto-mute off */
+	snd_cs4271_write_reg(chip, DAC_CTRL_REG, 0x00);
+
+	/* Volume 100% */
+	snd_cs4271_write_reg(chip, DAC_VOL_CHANA_REG, 0);
+	snd_cs4271_write_reg(chip, DAC_VOL_CHANB_REG, 0);
+
+	/* Enable the CODEC */
+	snd_cs4271_write_reg(chip, MODE2_REG, 0x02);
+
+	goto out;
+
+out_clk:
+	clk_disable(chip->board->dac_clk);
+out:
+	return retval;
+}
+
+static int snd_cs4271_dev_free(struct snd_device *device)
+{
+	struct snd_cs4271 *chip = device->device_data;
+
+	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
+	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_RXDIS));
+
+	if (chip->irq >= 0) {
+		free_irq(chip->irq, chip);
+		chip->irq = -1;
+	}
+
+	return 0;
+}
+
+static int __devinit snd_cs4271_dev_init(struct snd_card *card,
+					 struct spi_device *spi)
+{
+	static struct snd_device_ops ops = {
+		.dev_free	= snd_cs4271_dev_free,
+	};
+	struct snd_cs4271 *chip = get_chip(card);
+	int irq, retval;
+
+	irq = chip->ssc->irq;
+	if (irq < 0)
+		return irq;
+
+	spin_lock_init(&chip->lock);
+	mutex_init(&chip->mixer_lock);
+	chip->card = card;
+	chip->irq = -1;
+
+	retval = request_irq(irq, snd_cs4271_interrupt, 0, "cs4271", chip);
+	if (retval) {
+		dev_dbg(&chip->spi->dev, "unable to request irq %d\n", irq);
+		goto out;
+	}
+	chip->irq = irq;
+
+	memcpy(&chip->reg_image, &snd_cs4271_original_image,
+			sizeof(snd_cs4271_original_image));
+
+	retval = snd_cs4271_ssc_init(chip);
+	if (retval)
+		goto out_irq;
+
+	retval = snd_cs4271_chip_init(chip);
+	if (retval)
+		goto out_irq;
+
+	retval = snd_cs4271_pcm_new(chip, 0);
+	if (retval)
+		goto out_irq;
+
+	retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (retval)
+		goto out_irq;
+
+	retval = snd_cs4271_mixer(chip);
+	if (retval)
+		goto out_snd_dev;
+
+	snd_card_set_dev(card, &spi->dev);
+
+	goto out;
+
+out_snd_dev:
+	snd_device_free(card, chip);
+out_irq:
+	free_irq(chip->irq, chip);
+	chip->irq = -1;
+out:
+	return retval;
+}
+
+static int snd_cs4271_probe(struct spi_device *spi)
+{
+	struct snd_card			*card;
+	struct snd_cs4271		*chip;
+	struct cs4271_board_info	*board;
+	int				retval;
+	char				id[16];
+
+	board = spi->dev.platform_data;
+	if (!board) {
+		dev_dbg(&spi->dev, "no platform_data\n");
+		return -ENXIO;
+	}
+
+	if (!board->dac_clk) {
+		dev_dbg(&spi->dev, "no DAC clk\n");
+		return -ENXIO;
+	}
+
+	if (IS_ERR(board->dac_clk)) {
+		dev_dbg(&spi->dev, "no DAC clk\n");
+		return PTR_ERR(board->dac_clk);
+	}
+
+	retval = -ENOMEM;
+
+	/* Allocate "card" using some unused identifiers. */
+	snprintf(id, sizeof id, "cs4271_%d", board->ssc_id);
+	card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct snd_cs4271));
+	if (!card)
+		goto out;
+
+	chip = card->private_data;
+	chip->spi = spi;
+	chip->board = board;
+
+	chip->ssc = ssc_request(board->ssc_id);
+	if (IS_ERR(chip->ssc)) {
+		dev_dbg(&spi->dev, "could not get ssc%d device\n",
+				board->ssc_id);
+		retval = PTR_ERR(chip->ssc);
+		goto out_card;
+	}
+
+	retval = snd_cs4271_dev_init(card, spi);
+	if (retval)
+		goto out_ssc;
+
+	strcpy(card->driver, "cs4271");
+	strcpy(card->shortname, board->shortname);
+	sprintf(card->longname, "%s on irq %d", card->shortname, chip->irq);
+
+	retval = snd_card_register(card);
+	if (retval)
+		goto out_ssc;
+
+	dev_set_drvdata(&spi->dev, card);
+
+	goto out;
+
+out_ssc:
+	ssc_free(chip->ssc);
+out_card:
+	snd_card_free(card);
+out:
+	return retval;
+}
+
+static int __devexit snd_cs4271_remove(struct spi_device *spi)
+{
+	struct snd_card *card = dev_get_drvdata(&spi->dev);
+	struct snd_cs4271 *chip = card->private_data;
+	int retval;
+
+	/* Stop playback. */
+	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
+	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_RXDIS));
+
+	clk_disable(chip->board->dac_clk);
+
+	ssc_free(chip->ssc);
+	snd_card_free(card);
+	dev_set_drvdata(&spi->dev, NULL);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int snd_cs4271_suspend(struct spi_device *spi, pm_message_t msg)
+{
+	struct snd_card *card = dev_get_drvdata(&spi->dev);
+	struct snd_cs4271 *chip = card->private_data;
+
+	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
+	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_RXDIS));
+	clk_disable(chip->board->dac_clk);
+
+	return 0;
+}
+
+static int snd_cs4271_resume(struct spi_device *spi)
+{
+	struct snd_card *card = dev_get_drvdata(&spi->dev);
+	struct snd_cs4271 *chip = card->private_data;
+
+	clk_enable(chip->board->dac_clk);
+	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN));
+	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_RXEN));	
+
+	return 0;
+}
+#else
+#define snd_cs4271_suspend NULL
+#define snd_cs4271_resume NULL
+#endif
+
+static struct spi_driver cs4271_driver = {
+	.driver		= {
+		.name	= "cs4271",
+	},
+	.probe		= snd_cs4271_probe,
+	.suspend	= snd_cs4271_suspend,
+	.resume		= snd_cs4271_resume,
+	.remove		= __devexit_p(snd_cs4271_remove),
+};
+
+static int __init cs4271_init(void)
+{
+	return spi_register_driver(&cs4271_driver);
+}
+module_init(cs4271_init);
+
+static void __exit cs4271_exit(void)
+{
+	spi_unregister_driver(&cs4271_driver);
+}
+module_exit(cs4271_exit);
+
+MODULE_AUTHOR("Michael Welling");
+MODULE_DESCRIPTION("Sound driver for CS4271 with Atmel SSC");
+MODULE_LICENSE("GPL");
diff -Naur linux-2.6.25/sound/spi/cs4271.h linux-2.6.25.ep93xx/sound/spi/cs4271.h
--- linux-2.6.25/sound/spi/cs4271.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.25.ep93xx/sound/spi/cs4271.h	2008-08-18 14:05:42.000000000 -0500
@@ -0,0 +1,50 @@
+/*
+ * Driver for the CS4271 16-bit stereo DAC
+ *
+ * Copyright (C) 2006 Atmel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this
+ * distribution in the file called COPYING.
+ */
+
+#ifndef SND_CS4271_MIXER_H_
+#define SND_CS4271_MIXER_H_
+
+/* Mode Control Register 1 */
+#define MODE1_REG 0x01
+
+/* DAC Control Register */
+#define DAC_CTRL_REG 0x02
+
+/* DAC Volume/ Mixing Control Register */
+#define DAC_VOL_MIX_REG 0x03
+
+/* DAC Ch. A Volume Control Register */
+#define DAC_VOL_CHANA_REG 0x04
+
+/* DAC Ch. B Volume Control Register */
+#define DAC_VOL_CHANB_REG 0x05
+
+/* ADC Control Register */
+#define ADC_CTRL_REG 0x06
+
+/* Mode Control Register 2 */
+#define MODE2_REG 0x07
+
+#endif
+
diff -Naur linux-2.6.25/sound/spi/Kconfig linux-2.6.25.ep93xx/sound/spi/Kconfig
--- linux-2.6.25/sound/spi/Kconfig	2008-04-16 21:49:44.000000000 -0500
+++ linux-2.6.25.ep93xx/sound/spi/Kconfig	2008-08-18 14:05:42.000000000 -0500
@@ -28,4 +28,25 @@
 
 	  Set to 48000 Hz by default.
 
+config SND_CS4271
+	tristate "CS4271 driver"
+	depends on ATMEL_SSC
+	select SND_PCM
+	help
+	  Say Y here if you want to use the CS4271 codec. This
+	  codec can be found on the EMAC Inc SOM-150 carrier.
+
+	  This driver requires the Atmel SSC driver for sound sink, a
+	  peripheral found on most AT91 and AVR32 microprocessors.
+
+config SND_CS4271_TARGET_BITRATE
+	int "Target bitrate for CS4271"
+	depends on SND_CS4271
+	default "48000"
+	range 8000 50000
+	help
+	  Sets the target bitrate for the bitrate calculator in the driver.
+	  Limited by hardware to be between 8000 Hz and 50000 Hz.
+
+	  Set to 48000 Hz by default.
 endmenu
diff -Naur linux-2.6.25/sound/spi/Makefile linux-2.6.25.ep93xx/sound/spi/Makefile
--- linux-2.6.25/sound/spi/Makefile	2008-04-16 21:49:44.000000000 -0500
+++ linux-2.6.25.ep93xx/sound/spi/Makefile	2008-08-18 14:05:42.000000000 -0500
@@ -3,3 +3,7 @@
 snd-at73c213-objs		:= at73c213.o
 
 obj-$(CONFIG_SND_AT73C213)	+= snd-at73c213.o
+
+snd-cs4271-objs			:= cs4271.o
+
+obj-$(CONFIG_SND_CS4271)	+= snd-cs4271.o
