--- linux-2.6.11-rc5/sound/arm/s3c24xx-iis.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.11-rc5-audio9/sound/arm/s3c24xx-iis.c	2005-03-15 12:23:10.000000000 +0000
@@ -0,0 +1,1189 @@
+/* sound/arm/s3c24xx-iis.c
+ *
+ * (c) 2004-2005 Simtec Electronics
+ *	http://armlinux.simtec.co.uk/
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX ALSA IIS audio driver core
+ *
+ * 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.
+*/
+
+/* Notes:
+ *   The I2S pins that are needed (SCLK,LRCLK,SDO,SDI) are always
+ *   configured when the I2S system is active. SDO and SDI are
+ *   always configured even if the system is not open for both
+ *   capture and playback. This ensures that if the DAC is
+ *   not muted, then the output line is held in place, reducing
+ *   the chance of any random data being picked up. CDCLK is
+ *   only selected when the system is in master mode.
+ *
+ *   When the I2S unit is not being used, all pins are set to
+ *   input, so either the internal resistors need to be configured
+ *   or the lines need to have external pull-up or pull-downs.
+*/
+
+#include <linux/config.h>
+#include <sound/driver.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/hardware.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+
+#include <asm/hardware/clock.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+#include <asm/arch/regs-iis.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/audio.h>
+
+#include "s3c24xx-iis.h"
+
+
+/* debugging macros */
+
+#if 0
+#define DBG(x...)
+#else
+#define DBG(x...) do { printk(KERN_DEBUG x); } while(0)
+#endif
+
+#if 1
+#define DBG_DMA(x...)
+#else
+#define DBG_DMA(x...) do { printk(KERN_DEBUG x); } while(0)
+#endif
+
+static s3c24xx_card_t *our_card;
+
+/* s3c24xx_snd_capture_hw
+ *
+ * the default initialiser for the capture hardware stream,
+ * not to be altered
+*/
+
+static snd_pcm_hardware_t s3c24xx_snd_hw = {
+	.info			= ( SNDRV_PCM_INFO_INTERLEAVED |
+				    SNDRV_PCM_INFO_MMAP | 
+				    SNDRV_PCM_INFO_MMAP_VALID |
+				    SNDRV_PCM_INFO_PAUSE |
+				    SNDRV_PCM_INFO_RESUME ),
+	.formats		= ( SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_U16_LE |
+				    SNDRV_PCM_FMTBIT_U8 |
+				    SNDRV_PCM_FMTBIT_S8 ),
+	.rates			= SNDRV_PCM_RATE_44100,
+	.rate_min		= 8000,
+	.rate_max		= 48000,
+	.channels_min		= 2,
+	.channels_max		= 2,
+	.buffer_bytes_max	= 64*1024,
+	.period_bytes_min	= 4,
+	.period_bytes_max	= PAGE_SIZE,
+	.periods_min		= 2,
+	.periods_max		= 128,
+	.fifo_size		= 32,
+};
+
+
+
+static s3c2410_dma_client_t s3c24xx_snd_playback_client = {
+	.name		= "S3C24XX Audio Playback",
+};
+
+static s3c2410_dma_client_t s3c24xx_snd_capture_client = {
+	.name		= "S3C24XX Audio Capture",
+};
+
+/* debug helpers */
+
+static void s3c24xx_snd_showregs(s3c24xx_card_t *card)
+{
+	printk(KERN_DEBUG "IIS: CON=%08x, MOD=%08x, PSR=%08x, FCON=%08x\n",
+	       readl(card->regs + S3C2410_IISCON),
+	       readl(card->regs + S3C2410_IISMOD),
+	       readl(card->regs + S3C2410_IISPSR),
+	       readl(card->regs + S3C2410_IISFCON));
+}
+
+static void s3c24xx_snd_showdma(s3c24xx_runtime_t *or)
+{
+	dma_addr_t src, dst;
+
+	s3c2410_dma_getposition(or->dma_ch, &src, &dst);
+	DBG("dma position: src=0x%lx, dst=0x%lx\n", (long)src, (long)dst);
+}
+
+/* conversion */
+
+static struct s3c24xx_platdata_iis *to_platdata(s3c24xx_card_t *card)
+{
+	return (card->device == NULL) ? NULL : card->device->platform_data;
+}
+
+/* helpers */
+
+static inline int s3c24xx_snd_is_clkmaster(s3c24xx_card_t *chip)
+{
+	return (readl(chip->regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
+}
+
+/* audio hardware control */
+
+static void s3c24xx_snd_txctrl(s3c24xx_card_t *card, int on)
+{
+	unsigned long iisfcon;
+	unsigned long iiscon;
+	unsigned long iismod;
+
+	iisfcon = readl(card->regs + S3C2410_IISFCON);
+	iiscon  = readl(card->regs + S3C2410_IISCON);
+	iismod  = readl(card->regs + S3C2410_IISMOD);
+
+	if (on) {
+		iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
+		iiscon  |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
+		iiscon  &= ~S3C2410_IISCON_TXIDLE;
+		iismod  |= S3C2410_IISMOD_TXMODE;
+		iismod  |= S3C2410_IISMOD_RXMODE;
+
+		writel(iismod,  card->regs + S3C2410_IISMOD);
+		writel(iisfcon, card->regs + S3C2410_IISFCON);
+		writel(iiscon,  card->regs + S3C2410_IISCON);
+	} else {
+		/* note, we have to disable the FIFOs otherwise bad things
+		 * seem to happen when the DMA stops. According to the 
+		 * Samsung supplied kernel, this should allow the DMA 
+		 * engine and FIFOs to reset. If this isn't allowed, the
+		 * DMA engine will simply freeze randomly.
+                 */
+
+		iisfcon &= ~S3C2410_IISFCON_TXENABLE;
+		iisfcon &= ~S3C2410_IISFCON_TXDMA;
+		iiscon  |=  S3C2410_IISCON_TXIDLE;
+		iiscon  &= ~S3C2410_IISCON_TXDMAEN;
+		//iismod  &= ~S3C2410_IISMOD_TXMODE;
+
+		writel(iiscon,  card->regs + S3C2410_IISCON);
+		writel(iisfcon, card->regs + S3C2410_IISFCON);
+		//writel(iismod,  card->regs + S3C2410_IISMOD);
+	}
+
+	DBG("%s: iismod=0x%08lx, iisfcon=0x%08lx, iiscon=0x%08lx\n",
+	    __FUNCTION__, iismod, iisfcon, iiscon);
+}
+
+static void s3c24xx_snd_rxctrl(s3c24xx_card_t *card, int on)
+{
+	unsigned long iisfcon;
+	unsigned long iiscon;
+	unsigned long iismod;
+
+	iisfcon = readl(card->regs + S3C2410_IISFCON);
+	iiscon  = readl(card->regs + S3C2410_IISCON);
+	iismod  = readl(card->regs + S3C2410_IISMOD);
+
+	if (on) {
+		iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
+		iiscon  |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
+		iiscon  &= ~S3C2410_IISCON_RXIDLE;
+		iismod  |= S3C2410_IISMOD_RXMODE;
+		iismod  |= S3C2410_IISMOD_TXMODE;
+
+		writel(iismod,  card->regs + S3C2410_IISMOD);
+		writel(iisfcon, card->regs + S3C2410_IISFCON);
+		writel(iiscon,  card->regs + S3C2410_IISCON);
+	} else {
+                /* note, we have to disable the FIFOs otherwise bad things
+                 * seem to happen when the DMA stops. According to the 
+                 * Samsung supplied kernel, this should allow the DMA 
+                 * engine and FIFOs to reset. If this isn't allowed, the
+                 * DMA engine will simply freeze randomly.
+                 */
+
+                iisfcon &= ~S3C2410_IISFCON_RXENABLE;
+                iisfcon &= ~S3C2410_IISFCON_RXDMA;
+                iiscon  |= S3C2410_IISCON_RXIDLE;
+                iiscon  &= ~S3C2410_IISCON_RXDMAEN;
+		//iismod  &= ~S3C2410_IISMOD_RXMODE;
+
+		writel(iisfcon, card->regs + S3C2410_IISFCON);
+		writel(iiscon,  card->regs + S3C2410_IISCON);
+		//writel(iismod,  card->regs + S3C2410_IISMOD);
+	}
+
+	DBG("%s: iismod=0x%08lx, iisfcon=0x%08lx, iiscon=0x%08lx\n",
+	    __FUNCTION__, iismod, iisfcon, iiscon);
+}
+
+int s3c24xx_iis_cfgclksrc(s3c24xx_card_t *card, const char *name)
+{
+	struct s3c24xx_platdata_iis *pdata = to_platdata(card);
+	unsigned long iismod;
+
+	if (name == NULL && pdata != NULL)
+		name = pdata->codec_clk;
+
+	if (name == NULL)
+		return -EINVAL;
+
+	DBG("%s: clock source %s\n", __FUNCTION__, name);
+
+	/* alter clock source (master/slave) appropriately */
+
+	iismod = readl(card->regs + S3C2410_IISMOD);
+
+	if (strcmp(name, "pclk") == 0 || strcmp(name, "cdclk") == 0) {
+		iismod &= ~S3C2410_IISMOD_SLAVE;		
+	} else {
+		iismod |= S3C2410_IISMOD_SLAVE;
+	}
+
+	writel(iismod, card->regs + S3C2410_IISMOD);
+	return 0;
+}
+
+
+/* s3c24xx_pcm_enqueue
+ *
+ * place a dma buffer onto the queue for the dma system
+ * to handle.
+*/
+
+static void s3c24xx_pcm_enqueue(s3c24xx_runtime_t *or)
+{
+	dma_addr_t pos = or->dma_pos;
+	int ret;
+
+	DBG_DMA("%s: pos=%lx, period=%x, end=%lx, loaded=%d\n", __FUNCTION__,
+		(long)or->dma_start, or->dma_period,
+		(long)or->dma_end, or->dma_loaded);
+
+	do {
+		DBG_DMA("%s: load buffer %lx\n", __FUNCTION__, (long)pos);
+
+		ret = s3c2410_dma_enqueue(or->dma_ch, or, pos, or->dma_period);
+
+		if (ret == 0) {
+			or->dma_loaded++;
+			pos += or->dma_period;
+			if (pos >= or->dma_end)
+				pos = or->dma_start;
+		}
+	} while (ret == 0 && or->dma_loaded < or->dma_limit);
+
+	or->dma_pos = pos;
+}
+
+
+static void s3c24xx_snd_playback_buffdone(s3c2410_dma_chan_t *ch,
+					  void *bufid, int size,
+					  s3c2410_dma_buffresult_t result)
+{
+	s3c24xx_runtime_t *or = bufid;
+
+	DBG_DMA("%s: %p,%p, or %p, sz %d, res %d\n", 
+		__FUNCTION__, ch, bufid, or, size, result);
+
+	if (or->stream)
+		snd_pcm_period_elapsed(or->stream);
+
+	spin_lock(&or->lock);
+	if (or->state & ST_RUNNING) {
+		or->dma_loaded--;
+		s3c24xx_pcm_enqueue(or);
+	}
+	spin_unlock(&or->lock);
+}
+
+static void s3c24xx_snd_capture_buffdone(s3c2410_dma_chan_t *ch,
+					 void *bufid, int size,
+					 s3c2410_dma_buffresult_t result)
+{
+	s3c24xx_runtime_t *or = bufid;
+
+	DBG_DMA("%s: %p,%p, or %p, sz %d, res %d\n", 
+		__FUNCTION__, ch, bufid, or, size, result);
+
+	if (or->stream)
+		snd_pcm_period_elapsed(or->stream);
+
+	spin_lock(&or->lock);
+	if (or->state & ST_RUNNING) {
+		or->dma_loaded--;
+		s3c24xx_pcm_enqueue(or);
+	}
+	spin_unlock(&or->lock);
+}
+
+
+
+static int call_startup(struct s3c24xx_iis_ops *ops)
+{
+	if (ops && ops->startup)
+		return (ops->startup)(ops);
+
+	return 0;
+}
+
+static int call_open(struct s3c24xx_iis_ops *ops,
+		     snd_pcm_substream_t *substream)
+{
+	if (ops && ops->open)
+		return (ops->open)(ops, substream);
+
+	return 0;
+}
+
+static int s3c24xx_snd_open(snd_pcm_substream_t *substream)
+{
+	s3c24xx_card_t *chip = snd_pcm_substream_chip(substream);
+	s3c24xx_runtime_t *or;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int ret = 0;
+
+	DBG("%s: substream=%p, chip=%p\n", __FUNCTION__, substream, chip);
+
+	runtime->private_data = &chip->playback;
+	runtime->hw	      = s3c24xx_snd_hw;
+
+	down(&chip->sem);
+
+	/* todo - request dma channel nicely */
+
+	DBG("%s: state: playback 0x%x, capture 0x%x\n",
+	    __FUNCTION__, chip->playback.state, chip->capture.state);
+
+	if (chip->playback.state == 0 && chip->capture.state == 0) {
+
+		/* ensure we hold all the modules we need */
+
+		if (chip->base_ops) {
+			if (try_module_get(chip->base_ops->owner))
+				chip->base_ops_claimed = 1;
+			else {
+				dev_err(chip->device, "cannot claim module\n");
+				ret = -EINVAL;
+				goto exit_err;
+			}
+		}
+
+		if (chip->chip_ops) {
+			if (try_module_get(chip->chip_ops->owner))
+				chip->chip_ops_claimed = 1;
+			else {
+				dev_err(chip->device, "cannot claim module\n");
+				ret = -EINVAL;
+				goto exit_err;
+			}
+		}
+
+		/* ensure the chip is started */
+
+		ret = call_startup(chip->base_ops);
+		if (ret)
+			goto exit_err;
+
+		ret = call_startup(chip->chip_ops);
+		if (ret)
+			goto exit_err;
+
+		/* configure the pins for audio (iis) control */
+
+		s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
+		s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
+		s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
+		s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
+		
+		if (s3c24xx_snd_is_clkmaster(chip))
+			s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
+	}
+
+	/* initialise the stream */
+
+	runtime->hw = s3c24xx_snd_hw;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		or = &chip->capture;
+	else
+		or = &chip->playback;
+
+	/* call all the registered helpers */
+
+	ret = call_open(chip->base_ops, substream);
+	if (ret < 0)
+		goto exit_err;
+
+	ret = call_open(chip->chip_ops, substream);
+	if (ret < 0)
+		goto exit_err;
+
+	or->state |= ST_OPENED;
+
+	runtime->private_data = or;
+	or->stream = substream;
+	      
+	up(&chip->sem);
+
+	return ret;
+
+ exit_err:
+	up(&chip->sem);
+	return ret;
+}
+
+
+static void call_shutdown(struct s3c24xx_iis_ops *ops)
+{
+	if (ops && ops->shutdown)
+		(ops->shutdown)(ops);
+}
+
+static int call_close(struct s3c24xx_iis_ops *ops, 
+		      snd_pcm_substream_t *substream)
+{
+	if (ops && ops->close)
+		return (ops->close)(ops, substream);
+	
+	return 0;
+}
+
+/* close callback */
+static int s3c24xx_snd_close(snd_pcm_substream_t *substream)
+{
+	s3c24xx_card_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	s3c24xx_runtime_t *or = runtime->private_data;
+
+	DBG("%s: substream=%p, chip=%p\n", __FUNCTION__, substream, chip);
+		
+	down(&chip->sem); 
+
+	snd_assert(or->state != 0, chip = chip);
+
+	/* mark stream as closed */
+	or->state &= ~ST_OPENED;
+
+	/* close all the devices associated with this */
+	call_close(chip->chip_ops, substream);
+	call_close(chip->base_ops, substream);
+
+	/* are both shut-down? */
+	if (chip->playback.state == 0 && chip->capture.state == 0) {
+		/* call shut-down methods for all */
+				
+		call_shutdown(chip->chip_ops);
+		call_shutdown(chip->base_ops);
+
+		/* de-configure the IIS pins, go to input */
+
+		s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_INP);
+		s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_INP);
+		
+		if (s3c24xx_snd_is_clkmaster(chip))
+			s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_INP);
+		
+		s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_INP);
+		s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_INP);
+
+		/* free any hold we had on the modules */
+
+		if (chip->base_ops_claimed) {
+			chip->base_ops_claimed = 0;
+			module_put(chip->base_ops->owner);
+		}
+
+		if (chip->chip_ops_claimed) {
+			chip->chip_ops_claimed = 0;
+			module_put(chip->chip_ops->owner);
+		}
+	}
+	
+	up(&chip->sem);
+	return 0;
+}
+
+
+/* hw_params callback */
+static int s3c24xx_snd_pcm_hw_params(snd_pcm_substream_t *substream,
+				     snd_pcm_hw_params_t *hw_params)
+{
+	DBG("%s: sub=%p, hwp=%p\n", __FUNCTION__, substream, hw_params);
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	return 0;
+}
+
+/* hw_free callback */
+static int s3c24xx_snd_pcm_hw_free(snd_pcm_substream_t *substream)
+{
+	DBG("%s: substream %p\n", __FUNCTION__, substream);
+
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+}
+
+/* frequency control */
+
+static long s3c24xx_snd_getdiv(s3c24xx_card_t *card, unsigned int rate,
+			       unsigned long *iismod, unsigned long iisdiv)
+{
+	unsigned long clkrate = clk_get_rate(card->clock);
+	long div;
+	long diff;
+
+	if (iisdiv & S3C2410_IISMOD_384FS)
+		clkrate /= 384;
+	else
+		clkrate /= 256;
+
+	div = clkrate / rate;
+
+	/* is divisor in range */
+
+	if (div > 32 || div < 1)
+		return -1L;
+
+	/* is the rate generated near enough our clock rate? */
+
+	diff = (clkrate / div) - rate;
+
+	if (diff > 100 || diff < 100)
+		return -1L;
+	       
+	/* update info and return */
+
+	*iismod &= ~S3C2410_IISMOD_384FS;
+	*iismod |= iisdiv;
+
+	return (div-1) << S3C2410_IISPSR_INTSHIFT;
+}
+
+/* s3c24xx_snd_setrate
+ *
+ * configure the clock rate for when the s3c24xx is in clock master
+ * mode.
+*/
+
+int s3c24xx_snd_setrate(s3c24xx_card_t *chip, snd_pcm_runtime_t *run)
+{
+	unsigned long iismod = readl(chip->regs + S3C2410_IISMOD);
+	unsigned long rate;
+	unsigned long tmp;
+	long prescaler;
+
+	/* if this is set to slave mode, then our clocks are all
+	 * inputs anyway, so we cannot alter the frequency here */
+
+	if (iismod & S3C2410_IISMOD_SLAVE) {
+		printk(KERN_ERR "%s: called with IISMOD as slave\n",
+		       __FUNCTION__);
+		return -EINVAL;
+	}
+
+	if (iismod & S3C2410_IISMOD_16BIT)
+		rate = run->rate * 32;
+	else
+		rate = run->rate * 16;
+
+
+	DBG("%s: clock rate %ld\n", __FUNCTION__, rate);
+
+	prescaler = s3c24xx_snd_getdiv(chip, rate, &iismod,
+				       S3C2410_IISMOD_256FS);
+	if (prescaler < 0)
+		prescaler = s3c24xx_snd_getdiv(chip, rate, &iismod,
+					       S3C2410_IISMOD_384FS);
+
+	if (prescaler < 0) {
+		printk(KERN_ERR "cannot get rate %d Hz\n", run->rate);
+		return -ERANGE;
+	}
+
+	/* update timing registers */
+
+	writel(iismod, chip->regs + S3C2410_IISMOD);
+
+	tmp = readl(chip->regs + S3C2410_IISPSR) & ~S3C2410_IISPSR_INTMASK;
+	tmp |= prescaler;
+	writel(tmp, chip->regs + S3C2410_IISPSR);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(s3c24xx_snd_setrate);
+
+/* s3c24xx_iismod_cfg
+ *
+ * Update the s3c24xx IISMOD register
+*/
+
+void s3c24xx_iismod_cfg(s3c24xx_card_t *chip,
+			unsigned long set,
+			unsigned long mask)
+{
+	unsigned long flags;
+	unsigned long tmp;
+
+	local_irq_save(flags);
+
+	tmp = readl(chip->regs + S3C2410_IISMOD);
+	tmp &= ~mask;
+	tmp |= set;
+
+	DBG("%s: new iismod 0x%08lx\n", __FUNCTION__, tmp);
+
+	writel(tmp, chip->regs + S3C2410_IISMOD);
+	local_irq_restore(flags);
+}
+
+EXPORT_SYMBOL(s3c24xx_iismod_cfg);
+
+static int call_prepare(struct s3c24xx_iis_ops *ops,
+			snd_pcm_substream_t *substream, snd_pcm_runtime_t *rt)
+{
+	if (ops && ops->prepare)
+		return (ops->prepare)(ops, substream, rt);
+
+	return 0;
+}
+
+/* prepare callback */
+
+static int s3c24xx_snd_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	s3c24xx_card_t *chip = snd_pcm_substream_chip(substream);
+	s3c24xx_runtime_t *or = substream->runtime->private_data;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	int ret = 0;
+
+	DBG("%s: substream=%p, chip=%p\n", __FUNCTION__, substream, chip);
+	DBG("%s: r->dma_addr=%lx, r->dma_area=%p\n", __FUNCTION__,
+	    (long)runtime->dma_addr, runtime->dma_area);
+
+	/* prepare DMA */
+
+	spin_lock_irqsave(&or->lock, flags);
+	or->dma_loaded	= 0;
+	or->dma_limit	= runtime->hw.periods_min;
+	or->dma_period	= snd_pcm_lib_period_bytes(substream);
+	or->dma_start	= runtime->dma_addr;
+	or->dma_pos	= or->dma_start;
+	or->dma_end	= or->dma_start + snd_pcm_lib_buffer_bytes(substream);
+	spin_unlock_irqrestore(&or->lock, flags);
+
+	DBG("%s: dma: period=%d, start=%lx, pos=%lx, end=%lx\n", __FUNCTION__,
+	    or->dma_period, (long)or->dma_start,
+	    (long)or->dma_pos, (long)or->dma_end);
+	
+	/* flush the DMA channel */
+
+	s3c2410_dma_ctrl(or->dma_ch, S3C2410_DMAOP_FLUSH);
+
+	/* call the register ops for prepare, to allow things like
+	 * sample rate and format to be set
+	 */
+
+	ret = call_prepare(chip->base_ops, substream, runtime);
+	if (ret < 0)
+		goto exit_err;
+
+	ret = call_prepare(chip->chip_ops, substream, runtime);
+
+ exit_err:
+	return ret;
+}
+
+/* s3c24xx_snd_lrsync
+ *
+ * Wait for the LR signal to allow synchronisation to the L/R clock
+ * from the DAC. May only be needed for slave mode.
+*/
+
+static int s3c24xx_snd_lrsync(s3c24xx_card_t *chip)
+{
+	unsigned long iiscon;
+	int timeout = 10000;
+
+	while (1) {
+		iiscon = readl(chip->regs + S3C2410_IISCON);
+		if (iiscon & S3C2410_IISCON_LRINDEX)
+			break;
+
+		if (--timeout < 0)
+			return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+/* trigger callback */
+static int s3c24xx_snd_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	s3c24xx_runtime_t *or = substream->runtime->private_data;
+	s3c24xx_card_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	DBG("%s: substream=%p, cmd=%d\n", __FUNCTION__, substream, cmd);
+
+	spin_lock_irqsave(&or->lock, flags);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		or->state |= ST_RUNNING;
+
+		/* enqueue dma buffers and start the IIS engine */
+
+		s3c24xx_pcm_enqueue(or);
+		s3c24xx_snd_showregs(chip);
+		s3c2410_dma_ctrl(or->dma_ch, S3C2410_DMAOP_START);
+
+		if (!s3c24xx_snd_is_clkmaster(chip)) {			
+			ret = s3c24xx_snd_lrsync(chip);
+			if (ret)
+				goto exit_err;
+		}
+
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			s3c24xx_snd_rxctrl(chip, 1);
+		else
+			s3c24xx_snd_txctrl(chip, 1);
+
+		ret = 0;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		or->state &= ~ST_RUNNING;
+
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			s3c24xx_snd_rxctrl(chip, 0);
+		else
+			s3c24xx_snd_txctrl(chip, 0);
+		
+		s3c2410_dma_ctrl(or->dma_ch, S3C2410_DMAOP_STOP);
+		s3c24xx_snd_showdma(or);	
+		s3c24xx_snd_showregs(chip);
+
+		ret = 0;
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+ exit_err:
+	spin_unlock_irqrestore(&or->lock, flags);
+	return ret;
+}
+
+
+/* pointer callback */
+static snd_pcm_uframes_t s3c24xx_snd_pcm_pointer(snd_pcm_substream_t *substream)
+{
+	s3c24xx_runtime_t *or = substream->runtime->private_data;
+	unsigned long flags;
+	unsigned long res;
+	dma_addr_t src, dst;
+
+	spin_lock_irqsave(&or->lock, flags);
+	s3c2410_dma_getposition(or->dma_ch, &src, &dst);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		res = dst - or->dma_start;
+	else
+		res = src - or->dma_start;
+
+	spin_unlock_irqrestore(&or->lock, flags);
+
+	/* we seem to be getting the odd error from the pcm library due
+	 * to out-of-bounds pointers. this is maybe due to the dma engine
+	 * not having loaded the new values for the channel before being
+	 * callled... (todo - fix )
+	 */
+
+	if (res > (or->dma_end - or->dma_start))
+		DBG("%s: %lx,%lx (%lx) from %lx,%lx\n", __FUNCTION__,
+		    (long)src, (long)dst, res,
+		    (long)or->dma_start, (long)or->dma_end);
+
+	if (res >= snd_pcm_lib_buffer_bytes(substream)) {
+		if (res > snd_pcm_lib_buffer_bytes(substream))
+			DBG("%s: res %lx >= %lx\n", __FUNCTION__,
+			    res, (long)snd_pcm_lib_buffer_bytes(substream));
+		
+		if (res == snd_pcm_lib_buffer_bytes(substream))
+			res = 0;
+	}
+
+	return bytes_to_frames(substream->runtime, res);
+}
+
+static int s3c24xx_snd_pcm_mmap(snd_pcm_substream_t *substream,
+				struct vm_area_struct *vma)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+				     runtime->dma_area,
+				     runtime->dma_addr,
+				     runtime->dma_bytes);
+}
+
+static int sc24xx_snd_dma_prealloc(snd_pcm_t *pcm, int stream)
+{
+	snd_pcm_substream_t *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = s3c24xx_snd_hw.buffer_bytes_max;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+
+	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+					   &buf->addr, GFP_KERNEL);
+
+	if (buf->area == NULL) {
+		dev_err(pcm->card->dev, "failed to allocate dma buff\n");
+		return -ENOMEM;
+	}
+
+	DBG("%s: stream %d: dma area %p, 0x%lx\n", __FUNCTION__,
+	    stream, buf->area, (long)buf->addr);
+
+	return 0;
+}
+
+static void sc24xx_snd_dma_free(snd_pcm_t *pcm, int stream)
+{
+	snd_pcm_substream_t *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = s3c24xx_snd_hw.buffer_bytes_max;
+
+	dma_free_writecombine(pcm->card->dev, size, buf->area, buf->addr);
+
+	buf->area = NULL;
+	buf->addr = (dma_addr_t)NULL;
+}
+
+/* operators */
+static snd_pcm_ops_t s3c24xx_snd_pcm_ops = {
+	.open		= s3c24xx_snd_open,
+	.close		= s3c24xx_snd_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= s3c24xx_snd_pcm_hw_params,
+	.hw_free	= s3c24xx_snd_pcm_hw_free,
+	.prepare	= s3c24xx_snd_pcm_prepare,
+	.trigger	= s3c24xx_snd_pcm_trigger,
+	.pointer	= s3c24xx_snd_pcm_pointer,
+	.mmap		= s3c24xx_snd_pcm_mmap,
+};
+
+static int snd_card_s3c24xx_pcm(s3c24xx_card_t *card)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	DBG("snd_card_s3c24xx_pcm: card=%p\n", card);
+
+	err = snd_pcm_new(card->card, "S3C24XX-IIS", 0, 1, 1, &pcm);
+	if (err < 0) {
+		printk(KERN_ERR "cannot register new pcm");
+		return err;
+	}
+
+	DBG("snd_card_s3c24xx_pcm: new pcm %p\n", pcm);
+
+	card->pcm	  = pcm;
+	pcm->private_data = card;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &s3c24xx_snd_pcm_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &s3c24xx_snd_pcm_ops);
+
+	/* allocate dma resources */
+
+	err = sc24xx_snd_dma_prealloc(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+	if (err)
+		goto exit_err;
+
+	err = sc24xx_snd_dma_prealloc(pcm, SNDRV_PCM_STREAM_CAPTURE);
+	if (err)
+		goto exit_err;
+
+	strcpy(pcm->name, "s3c24xx");
+
+	DBG("snd_card_s3c24xx_pcm: ok\n");
+
+	return 0;
+
+ exit_err:
+	return err;
+}
+
+static int s3c24xx_iis_free(s3c24xx_card_t *card)
+{
+	sc24xx_snd_dma_free(card->pcm, SNDRV_PCM_STREAM_PLAYBACK);
+	sc24xx_snd_dma_free(card->pcm, SNDRV_PCM_STREAM_CAPTURE);
+}
+
+static int s3c24xx_iis_dma_init(s3c24xx_card_t *card)
+{
+	int err = 0;
+
+	our_card->playback.dma_ch = 2;
+	our_card->capture.dma_ch = 1;
+
+	err = s3c2410_dma_request(our_card->playback.dma_ch,
+				  &s3c24xx_snd_playback_client, NULL);
+	if (err) {
+		printk(KERN_ERR "failed to get dma channel\n");
+		return err;
+	}
+
+	s3c2410_dma_devconfig(our_card->playback.dma_ch,
+			      S3C2410_DMASRC_MEM, 0x03,
+			      S3C2410_PA_IIS + S3C2410_IISFIFO);
+
+	s3c2410_dma_config(our_card->playback.dma_ch,
+			   2, S3C2410_DCON_CH2_I2SSDO|S3C2410_DCON_SYNC_PCLK|S3C2410_DCON_HANDSHAKE);
+
+	s3c2410_dma_set_buffdone_fn(our_card->playback.dma_ch,
+				    s3c24xx_snd_playback_buffdone);
+
+	/* attach capture channel */
+
+	err = s3c2410_dma_request(our_card->capture.dma_ch,
+				  &s3c24xx_snd_capture_client, NULL);
+	if (err) {
+		printk(KERN_ERR "failed to get dma channel\n");
+		return err;
+	}
+
+	s3c2410_dma_config(our_card->capture.dma_ch,
+			   2, S3C2410_DCON_HANDSHAKE |
+			   S3C2410_DCON_SYNC_PCLK |
+			   (2 << S3C2410_DCON_SRCSHIFT));
+
+	s3c2410_dma_devconfig(our_card->capture.dma_ch,
+			      S3C2410_DMASRC_HW, 0x3,
+			      S3C2410_PA_IIS + S3C2410_IISFIFO);
+
+	s3c2410_dma_set_buffdone_fn(our_card->capture.dma_ch,
+				    s3c24xx_snd_capture_buffdone);
+
+	return 0;
+}
+
+static int s3c24xx_iis_dma_free(s3c24xx_card_t *card)
+{
+	s3c2410_dma_free(card->capture.dma_ch, &s3c24xx_snd_capture_client);
+	s3c2410_dma_free(card->playback.dma_ch, &s3c24xx_snd_playback_client);
+}
+
+int s3c24xx_iis_remove(struct device *dev)
+{
+	s3c24xx_card_t *card = dev_get_drvdata(dev);
+
+	card = dev_get_drvdata(dev);
+	if (card == NULL)
+		return 0;
+
+	s3c24xx_iis_dma_free(card);
+	s3c24xx_iis_free(card);
+
+	kfree(card);
+	dev_set_drvdata(dev, NULL);
+
+	return 0;
+}
+
+#if 1
+
+static ssize_t s3c24xx_iis_show_regs(struct device *dev, char *buf)
+{
+	s3c24xx_card_t *our_card = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE,
+			"IISMOD = %08x\n"
+			"IISCON = %08x\n"
+			"IISPSR = %08x\n"
+			"IISFCON= %08x\n",
+			readl(our_card->regs + S3C2410_IISCON),
+			readl(our_card->regs + S3C2410_IISMOD),
+			readl(our_card->regs + S3C2410_IISPSR),
+			readl(our_card->regs + S3C2410_IISFCON));
+}
+
+static DEVICE_ATTR(regs, S_IRUGO, s3c24xx_iis_show_regs, NULL);
+#endif
+
+s3c24xx_card_t *s3c24xx_iis_probe(struct device *dev)
+{
+	struct s3c24xx_platdata_iis *pdata = dev->platform_data;
+	snd_card_t *card;
+	int err;
+
+	dev_info(dev, "probe called\n");
+
+	card = snd_card_new(-1, 0, THIS_MODULE, sizeof(s3c24xx_card_t));
+
+	our_card = (s3c24xx_card_t *)card->private_data;
+
+	if (card == NULL) {
+		printk(KERN_ERR "snd_card_new() failed\n");
+		err = -EINVAL;
+		goto exit_err;
+	}
+	
+	our_card->card     = card;
+	our_card->device   = dev;
+	our_card->base_ops = (pdata) ? pdata->ops : NULL;
+
+	/* get resources such as clocks and IO */
+
+	our_card->clock = clk_get(dev, "iis");
+	DBG("our_card.clock = %p\n", our_card->clock);
+
+	if (IS_ERR(our_card->clock)) {
+		err = PTR_ERR(our_card->clock);
+		goto exit_err;
+	}
+
+	our_card->regs = ioremap(S3C2410_PA_IIS, 0x100);
+	if (our_card->regs == NULL) {
+		err = -ENXIO;
+		goto exit_err;
+	}
+
+	DBG("our_card.regs at %p\n", our_card->regs);
+	
+	clk_enable(our_card->clock);
+
+	init_MUTEX(&our_card->sem);
+	spin_lock_init(&our_card->capture.lock);
+	spin_lock_init(&our_card->playback.lock);
+
+	/* ensure the channels are both shutdown */
+
+	s3c24xx_snd_txctrl(our_card, 0);
+	s3c24xx_snd_rxctrl(our_card, 0);
+
+	/* static dma channel setup for the moment */
+
+	err = s3c24xx_iis_dma_init(our_card);
+	if (err)
+		goto exit_err;
+
+	/* default names for s3c2410 audio driver */
+
+	strcpy(card->driver,    "S3C24XX");
+	strcpy(card->shortname, "S3C24XX");
+	strcpy(card->longname,  "Samsung S3C24XX");
+
+	err = snd_card_s3c24xx_pcm(our_card);
+	if (err) {
+		printk(KERN_ERR "failed to init pcm devices\n");
+		goto exit_err;
+	}
+
+	dev_info(dev, "soundcard attached ok (%p)\n", our_card);
+
+	dev_set_drvdata(dev, our_card);
+	device_create_file(dev, &dev_attr_regs);
+	return our_card;
+
+ exit_err:
+	dev_err(dev, "error initialised card (%d)\n", err);
+	snd_card_free(our_card->card);
+	return ERR_PTR(err);
+}
+
+EXPORT_SYMBOL(s3c24xx_iis_probe);
+
+#ifdef CONFIG_PM
+
+static int call_suspend(struct s3c24xx_iis_ops *ops)
+{
+	if (ops && ops->suspend)
+		return (ops->suspend)(ops);
+
+	return 0;
+}
+
+int s3c24xx_iis_suspend(struct device *dev, u32 state, u32 level)
+{
+	s3c24xx_card_t *card = dev_get_drvdata(dev);
+
+	if (card && level == SUSPEND_DISABLE) {
+		if (card->card->power_state == SNDRV_CTL_POWER_D3cold)
+			goto exit;
+
+		/* inform sound core that we wish to suspend */
+
+		snd_pcm_suspend_all(card->pcm);
+		snd_power_change_state(card->card, SNDRV_CTL_POWER_D3cold);
+
+		/* inform any of our clients */
+
+		call_suspend(card->chip_ops);
+		call_suspend(card->base_ops);
+
+		clk_disable(card->clock);
+	}
+
+ exit:
+	return 0;
+}
+
+static int call_resume(struct s3c24xx_iis_ops *ops)
+{
+	if (ops && ops->resume)
+		return (ops->resume)(ops);
+
+	return 0;
+}
+
+int s3c24xx_iis_resume(struct device *dev, u32 level)
+{
+	s3c24xx_card_t *card = dev_get_drvdata(dev);
+
+	if (card && level == RESUME_ENABLE) {
+		if (card->card->power_state == SNDRV_CTL_POWER_D0)
+			goto exit;
+
+		clk_enable(card->clock);
+
+		/* inform any of our clients */
+
+		call_resume(card->base_ops);
+		call_resume(card->chip_ops);
+
+		/* inform sound core that we wish to resume */
+
+		snd_power_change_state(card->card, SNDRV_CTL_POWER_D0);
+	}
+
+ exit:
+	return 0;
+}
+
+EXPORT_SYMBOL(s3c24xx_iis_suspend);
+EXPORT_SYMBOL(s3c24xx_iis_resume);
+
+#endif /* CONFIG_PM */
--- linux-2.6.11-rc5/sound/arm/s3c24xx-iis.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.11-rc5-audio9/sound/arm/s3c24xx-iis.h	2005-03-15 08:45:39.000000000 +0000
@@ -0,0 +1,62 @@
+/* sound/arms3c24xx-iis.h
+ *
+ * (c) 2004-2005 Simtec Electronics
+ *	http://armlinux.simtec.co.uk/
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX ALSA IIS audio driver core
+ *
+ * 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.
+*/
+
+/* forward declarations */
+
+struct s3c24xx_iis_drv;
+struct s3c24xx_iis_ops;
+
+typedef struct s3c24xx_card s3c24xx_card_t;
+
+#define ST_RUNNING		(1<<0)
+#define ST_OPENED		(1<<1)
+
+typedef struct s3c24xx_runtime {
+	dmach_t			 dma_ch;
+	unsigned int		 state;
+	spinlock_t		 lock;
+
+	unsigned int		 dma_loaded;
+	unsigned int		 dma_limit;
+	unsigned int		 dma_period;
+	dma_addr_t		 dma_start;
+	dma_addr_t		 dma_pos;
+	dma_addr_t		 dma_end;
+
+	snd_pcm_substream_t	*stream;
+} s3c24xx_runtime_t;
+
+struct s3c24xx_card {
+	struct device		*device;
+	struct clk		*clock;
+	struct s3c24xx_iis_ops	*base_ops;
+	struct s3c24xx_iis_ops	*chip_ops;
+	unsigned char		 base_ops_claimed;
+	unsigned char		 chip_ops_claimed;
+	void __iomem		*regs;
+	void			*client;
+	snd_card_t		*card;
+	snd_pcm_t		*pcm;
+	struct semaphore	 sem;
+	s3c24xx_runtime_t	 playback;
+	s3c24xx_runtime_t	 capture;
+};
+
+extern s3c24xx_card_t *s3c24xx_iis_probe(struct device *dev);
+extern int s3c24xx_iis_remove(struct device *dev);
+
+extern s3c24xx_card_t *s3c24xx_iis_runtime_to_card(s3c24xx_runtime_t *rt);
+
+extern int s3c24xx_iis_cfgclksrc(s3c24xx_card_t *, const char *src);
+
+extern void s3c24xx_iismod_cfg(s3c24xx_card_t *, unsigned long set, unsigned long mask);

