aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-mtx-2-2.4.27/44-dbdma-and-au1550_psc.diff
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/linux/linux-mtx-2-2.4.27/44-dbdma-and-au1550_psc.diff')
-rw-r--r--recipes/linux/linux-mtx-2-2.4.27/44-dbdma-and-au1550_psc.diff1491
1 files changed, 1491 insertions, 0 deletions
diff --git a/recipes/linux/linux-mtx-2-2.4.27/44-dbdma-and-au1550_psc.diff b/recipes/linux/linux-mtx-2-2.4.27/44-dbdma-and-au1550_psc.diff
new file mode 100644
index 0000000000..af249dbe58
--- /dev/null
+++ b/recipes/linux/linux-mtx-2-2.4.27/44-dbdma-and-au1550_psc.diff
@@ -0,0 +1,1491 @@
+--- linux-old/include/asm-mips/au1xxx_dbdma.h 2006-05-09 18:24:17.000000000 +0200
++++ linux/include/asm-mips/au1xxx_dbdma.h 2006-05-09 14:05:48.000000000 +0200
+@@ -34,6 +34,8 @@
+ #ifndef _AU1000_DBDMA_H_
+ #define _AU1000_DBDMA_H_
+
++#include <linux/config.h>
++
+ #ifndef _LANGUAGE_ASSEMBLY
+
+ /* The DMA base addresses.
+@@ -43,7 +45,7 @@
+ #define DDMA_GLOBAL_BASE 0xb4003000
+ #define DDMA_CHANNEL_BASE 0xb4002000
+
+-typedef struct dbdma_global {
++typedef volatile struct dbdma_global {
+ u32 ddma_config;
+ u32 ddma_intstat;
+ u32 ddma_throttle;
+@@ -60,7 +62,7 @@
+
+ /* The structure of a DMA Channel.
+ */
+-typedef struct au1xxx_dma_channel {
++typedef volatile struct au1xxx_dma_channel {
+ u32 ddma_cfg; /* See below */
+ u32 ddma_desptr; /* 32-byte aligned pointer to descriptor */
+ u32 ddma_statptr; /* word aligned pointer to status word */
+@@ -96,7 +98,7 @@
+ /* "Standard" DDMA Descriptor.
+ * Must be 32-byte aligned.
+ */
+-typedef struct au1xxx_ddma_desc {
++typedef volatile struct au1xxx_ddma_desc {
+ u32 dscr_cmd0; /* See below */
+ u32 dscr_cmd1; /* See below */
+ u32 dscr_source0; /* source phys address */
+@@ -105,6 +107,12 @@
+ u32 dscr_dest1; /* See below */
+ u32 dscr_stat; /* completion status */
+ u32 dscr_nxtptr; /* Next descriptor pointer (mostly) */
++ /* First 32bytes are HW specific!!!
++ Lets have some SW data following.. make sure its 32bytes
++ */
++ u32 sw_status;
++ u32 sw_context;
++ u32 sw_reserved[6];
+ } au1x_ddma_desc_t;
+
+ #define DSCR_CMD0_V (1 << 31) /* Descriptor valid */
+@@ -123,6 +131,8 @@
+ #define DSCR_CMD0_CV (0x1 << 2) /* Clear Valid when done */
+ #define DSCR_CMD0_ST_MASK (0x3 << 0) /* Status instruction */
+
++#define SW_STATUS_INUSE (1<<0)
++
+ /* Command 0 device IDs.
+ */
+ #ifdef CONFIG_SOC_AU1550
+@@ -169,8 +179,8 @@
+ #define DSCR_CMD0_SDMS_RX0 9
+ #define DSCR_CMD0_SDMS_TX1 10
+ #define DSCR_CMD0_SDMS_RX1 11
+-#define DSCR_CMD0_AES_TX 12
+-#define DSCR_CMD0_AES_RX 13
++#define DSCR_CMD0_AES_TX 13
++#define DSCR_CMD0_AES_RX 12
+ #define DSCR_CMD0_PSC0_TX 14
+ #define DSCR_CMD0_PSC0_RX 15
+ #define DSCR_CMD0_PSC1_TX 16
+@@ -189,6 +199,10 @@
+ #define DSCR_CMD0_THROTTLE 30
+ #define DSCR_CMD0_ALWAYS 31
+ #define DSCR_NDEV_IDS 32
++/* THis macro is used to find/create custom device types */
++#define DSCR_DEV2CUSTOM_ID(x,d) (((((x)&0xFFFF)<<8)|0x32000000)|((d)&0xFF))
++#define DSCR_CUSTOM2DEV_ID(x) ((x)&0xFF)
++
+
+ #define DSCR_CMD0_SID(x) (((x) & 0x1f) << 25)
+ #define DSCR_CMD0_DID(x) (((x) & 0x1f) << 20)
+@@ -277,6 +291,43 @@
+ */
+ #define NUM_DBDMA_CHANS 16
+
++/*
++ * Ddma API definitions
++ * FIXME: may not fit to this header file
++ */
++typedef struct dbdma_device_table {
++ u32 dev_id;
++ u32 dev_flags;
++ u32 dev_tsize;
++ u32 dev_devwidth;
++ u32 dev_physaddr; /* If FIFO */
++ u32 dev_intlevel;
++ u32 dev_intpolarity;
++} dbdev_tab_t;
++
++
++typedef struct dbdma_chan_config {
++ spinlock_t lock;
++
++ u32 chan_flags;
++ u32 chan_index;
++ dbdev_tab_t *chan_src;
++ dbdev_tab_t *chan_dest;
++ au1x_dma_chan_t *chan_ptr;
++ au1x_ddma_desc_t *chan_desc_base;
++ au1x_ddma_desc_t *get_ptr, *put_ptr, *cur_ptr;
++ void *chan_callparam;
++ void (*chan_callback)(int, void *, struct pt_regs *);
++} chan_tab_t;
++
++#define DEV_FLAGS_INUSE (1 << 0)
++#define DEV_FLAGS_ANYUSE (1 << 1)
++#define DEV_FLAGS_OUT (1 << 2)
++#define DEV_FLAGS_IN (1 << 3)
++#define DEV_FLAGS_BURSTABLE (1 << 4)
++#define DEV_FLAGS_SYNC (1 << 5)
++/* end Ddma API definitions */
++
+ /* External functions for drivers to use.
+ */
+ /* Use this to allocate a dbdma channel. The device ids are one of the
+@@ -299,8 +350,8 @@
+
+ /* Put buffers on source/destination descriptors.
+ */
+-u32 au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes);
+-u32 au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes);
++u32 _au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes, u32 flags);
++u32 _au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes, u32 flags);
+
+ /* Get a buffer from the destination descriptor.
+ */
+@@ -314,5 +365,29 @@
+ void au1xxx_dbdma_chan_free(u32 chanid);
+ void au1xxx_dbdma_dump(u32 chanid);
+
++u32 au1xxx_dbdma_put_dscr(u32 chanid, au1x_ddma_desc_t *dscr );
++
++u32 au1xxx_ddma_add_device( dbdev_tab_t *dev );
++void * au1xxx_ddma_get_nextptr_virt(au1x_ddma_desc_t *dp);
++
++/*
++ Some compatibilty macros --
++ Needed to make changes to API without breaking existing drivers
++*/
++#define au1xxx_dbdma_put_source(chanid,buf,nbytes)_au1xxx_dbdma_put_source(chanid, buf, nbytes, DDMA_FLAGS_IE)
++#define au1xxx_dbdma_put_source_flags(chanid,buf,nbytes,flags) _au1xxx_dbdma_put_source(chanid, buf, nbytes, flags)
++#define put_source_flags(chanid,buf,nbytes,flags) au1xxx_dbdma_put_source_flags(chanid,buf,nbytes,flags)
++
++
++#define au1xxx_dbdma_put_dest(chanid,buf,nbytes) _au1xxx_dbdma_put_dest(chanid, buf, nbytes, DDMA_FLAGS_IE)
++#define au1xxx_dbdma_put_dest_flags(chanid,buf,nbytes,flags) _au1xxx_dbdma_put_dest(chanid, buf, nbytes, flags)
++#define put_dest_flags(chanid,buf,nbytes,flags) au1xxx_dbdma_put_dest_flags(chanid,buf,nbytes,flags)
++
++/*
++ * Flags for the put_source/put_dest functions.
++ */
++#define DDMA_FLAGS_IE (1<<0)
++#define DDMA_FLAGS_NOIE (1<<1)
++
+ #endif /* _LANGUAGE_ASSEMBLY */
+ #endif /* _AU1000_DBDMA_H_ */
+--- linux-old/arch/mips/au1000/common/dbdma.c 2006-05-09 18:23:18.000000000 +0200
++++ linux/arch/mips/au1000/common/dbdma.c 2006-05-09 14:04:44.000000000 +0200
+@@ -30,6 +30,7 @@
+ *
+ */
+
++#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+@@ -37,13 +38,14 @@
+ #include <linux/spinlock.h>
+ #include <linux/string.h>
+ #include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
+ #include <asm/au1000.h>
+ #include <asm/au1xxx_dbdma.h>
+ #include <asm/system.h>
+
+-#include <linux/module.h>
+
+-#if 1 // defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200)
++#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200)
+
+ /*
+ * The Descriptor Based DMA supports up to 16 channels.
+@@ -62,37 +64,10 @@
+ */
+ #define ALIGN_ADDR(x, a) ((((u32)(x)) + (a-1)) & ~(a-1))
+
+-static volatile dbdma_global_t *dbdma_gptr = (dbdma_global_t *)DDMA_GLOBAL_BASE;
+-static int dbdma_initialized;
++static dbdma_global_t *dbdma_gptr = (dbdma_global_t *)DDMA_GLOBAL_BASE;
++static int dbdma_initialized=0;
+ static void au1xxx_dbdma_init(void);
+
+-typedef struct dbdma_device_table {
+- u32 dev_id;
+- u32 dev_flags;
+- u32 dev_tsize;
+- u32 dev_devwidth;
+- u32 dev_physaddr; /* If FIFO */
+- u32 dev_intlevel;
+- u32 dev_intpolarity;
+-} dbdev_tab_t;
+-
+-typedef struct dbdma_chan_config {
+- u32 chan_flags;
+- u32 chan_index;
+- dbdev_tab_t *chan_src;
+- dbdev_tab_t *chan_dest;
+- au1x_dma_chan_t *chan_ptr;
+- au1x_ddma_desc_t *chan_desc_base;
+- au1x_ddma_desc_t *get_ptr, *put_ptr, *cur_ptr;
+- void *chan_callparam;
+- void (*chan_callback)(int, void *, struct pt_regs *);
+-} chan_tab_t;
+-
+-#define DEV_FLAGS_INUSE (1 << 0)
+-#define DEV_FLAGS_ANYUSE (1 << 1)
+-#define DEV_FLAGS_OUT (1 << 2)
+-#define DEV_FLAGS_IN (1 << 3)
+-
+ static dbdev_tab_t dbdev_tab[] = {
+ #ifdef CONFIG_SOC_AU1550
+ /* UARTS */
+@@ -158,25 +133,25 @@
+ { DSCR_CMD0_MAE_BOTH, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
+ { DSCR_CMD0_LCD, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
+
+- { DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 },
+- { DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
+- { DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 },
+- { DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
++ { DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 4, 8, 0x10600000, 0, 0 },
++ { DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 4, 8, 0x10600004, 0, 0 },
++ { DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 4, 8, 0x10680000, 0, 0 },
++ { DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 4, 8, 0x10680004, 0, 0 },
+
+- { DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 },
+- { DSCR_CMD0_AES_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
++ { DSCR_CMD0_AES_RX, DEV_FLAGS_IN , 4, 32, 0x10300008, 0, 0 },
++ { DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 4, 32, 0x10300004, 0, 0 },
+
+- { DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 0, 0x11a0001c, 0, 0 },
+- { DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 0, 0x11a0001c, 0, 0 },
++ { DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 16, 0x11a0001c, 0, 0 },
++ { DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 16, 0x11a0001c, 0, 0 },
+ { DSCR_CMD0_PSC0_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
+
+- { DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 0, 0x11b0001c, 0, 0 },
+- { DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 0, 0x11b0001c, 0, 0 },
++ { DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 16, 0x11b0001c, 0, 0 },
++ { DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 16, 0x11b0001c, 0, 0 },
+ { DSCR_CMD0_PSC1_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
+
+- { DSCR_CMD0_CIM_RXA, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
+- { DSCR_CMD0_CIM_RXB, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
+- { DSCR_CMD0_CIM_RXC, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
++ { DSCR_CMD0_CIM_RXA, DEV_FLAGS_IN, 0, 32, 0x14004020, 0, 0 },
++ { DSCR_CMD0_CIM_RXB, DEV_FLAGS_IN, 0, 32, 0x14004040, 0, 0 },
++ { DSCR_CMD0_CIM_RXC, DEV_FLAGS_IN, 0, 32, 0x14004060, 0, 0 },
+ { DSCR_CMD0_CIM_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
+
+ { DSCR_CMD0_NAND_FLASH, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
+@@ -185,6 +160,24 @@
+
+ { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
+ { DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
++
++ /* Provide 16 user definable device types */
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
++ { 0, 0, 0, 0, 0, 0, 0 },
+ };
+
+ #define DBDEV_TAB_SIZE (sizeof(dbdev_tab) / sizeof(dbdev_tab_t))
+@@ -204,6 +197,36 @@
+ return NULL;
+ }
+
++void * au1xxx_ddma_get_nextptr_virt(au1x_ddma_desc_t *dp)
++{
++ return phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
++}
++EXPORT_SYMBOL(au1xxx_ddma_get_nextptr_virt);
++
++u32
++au1xxx_ddma_add_device(dbdev_tab_t *dev)
++{
++ u32 ret = 0;
++ dbdev_tab_t *p=NULL;
++ static u16 new_id=0x1000;
++
++ p = find_dbdev_id(0);
++ if ( NULL != p )
++ {
++ memcpy(p, dev, sizeof(dbdev_tab_t));
++ p->dev_id = DSCR_DEV2CUSTOM_ID(new_id,dev->dev_id);
++ ret = p->dev_id;
++ new_id++;
++#if 0
++ printk("add_device: id:%x flags:%x padd:%x\n",
++ p->dev_id, p->dev_flags, p->dev_physaddr );
++#endif
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL(au1xxx_ddma_add_device);
++
+ /* Allocate a channel and return a non-zero descriptor if successful.
+ */
+ u32
+@@ -216,7 +239,7 @@
+ int i;
+ dbdev_tab_t *stp, *dtp;
+ chan_tab_t *ctp;
+- volatile au1x_dma_chan_t *cp;
++ au1x_dma_chan_t *cp;
+
+ /* We do the intialization on the first channel allocation.
+ * We have to wait because of the interrupt handler initialization
+@@ -226,9 +249,6 @@
+ au1xxx_dbdma_init();
+ dbdma_initialized = 1;
+
+- if ((srcid > DSCR_NDEV_IDS) || (destid > DSCR_NDEV_IDS))
+- return 0;
+-
+ if ((stp = find_dbdev_id(srcid)) == NULL) return 0;
+ if ((dtp = find_dbdev_id(destid)) == NULL) return 0;
+
+@@ -240,7 +260,7 @@
+ spin_lock_irqsave(&au1xxx_dbdma_spin_lock, flags);
+ if (!(stp->dev_flags & DEV_FLAGS_INUSE) ||
+ (stp->dev_flags & DEV_FLAGS_ANYUSE)) {
+- /* Got source */
++ /* Got source */
+ stp->dev_flags |= DEV_FLAGS_INUSE;
+ if (!(dtp->dev_flags & DEV_FLAGS_INUSE) ||
+ (dtp->dev_flags & DEV_FLAGS_ANYUSE)) {
+@@ -270,9 +290,8 @@
+ /* If kmalloc fails, it is caught below same
+ * as a channel not available.
+ */
+- ctp = (chan_tab_t *)kmalloc(sizeof(chan_tab_t), GFP_KERNEL);
++ ctp = kmalloc(sizeof(chan_tab_t), GFP_KERNEL);
+ chan_tab_ptr[i] = ctp;
+- ctp->chan_index = chan = i;
+ break;
+ }
+ }
+@@ -280,10 +299,11 @@
+
+ if (ctp != NULL) {
+ memset(ctp, 0, sizeof(chan_tab_t));
++ ctp->chan_index = chan = i;
+ dcp = DDMA_CHANNEL_BASE;
+ dcp += (0x0100 * chan);
+ ctp->chan_ptr = (au1x_dma_chan_t *)dcp;
+- cp = (volatile au1x_dma_chan_t *)dcp;
++ cp = (au1x_dma_chan_t *)dcp;
+ ctp->chan_src = stp;
+ ctp->chan_dest = dtp;
+ ctp->chan_callback = callback;
+@@ -300,6 +320,9 @@
+ i |= DDMA_CFG_DED;
+ if (dtp->dev_intpolarity)
+ i |= DDMA_CFG_DP;
++ if ((stp->dev_flags & DEV_FLAGS_SYNC) ||
++ (dtp->dev_flags & DEV_FLAGS_SYNC))
++ i |= DDMA_CFG_SYNC;
+ cp->ddma_cfg = i;
+ au_sync();
+
+@@ -310,14 +333,14 @@
+ rv = (u32)(&chan_tab_ptr[chan]);
+ }
+ else {
+- /* Release devices.
+- */
++ /* Release devices */
+ stp->dev_flags &= ~DEV_FLAGS_INUSE;
+ dtp->dev_flags &= ~DEV_FLAGS_INUSE;
+ }
+ }
+ return rv;
+ }
++EXPORT_SYMBOL(au1xxx_dbdma_chan_alloc);
+
+ /* Set the device width if source or destination is a FIFO.
+ * Should be 8, 16, or 32 bits.
+@@ -345,6 +368,7 @@
+
+ return rv;
+ }
++EXPORT_SYMBOL(au1xxx_dbdma_set_devwidth);
+
+ /* Allocate a descriptor ring, initializing as much as possible.
+ */
+@@ -371,10 +395,11 @@
+ * and if we try that first we are likely to not waste larger
+ * slabs of memory.
+ */
+- desc_base = (u32)kmalloc(entries * sizeof(au1x_ddma_desc_t), GFP_KERNEL);
++ desc_base = (u32)kmalloc(entries * sizeof(au1x_ddma_desc_t),
++ GFP_KERNEL|GFP_DMA);
+ if (desc_base == 0)
+ return 0;
+-
++
+ if (desc_base & 0x1f) {
+ /* Lost....do it again, allocate extra, and round
+ * the address base.
+@@ -382,7 +407,7 @@
+ kfree((const void *)desc_base);
+ i = entries * sizeof(au1x_ddma_desc_t);
+ i += (sizeof(au1x_ddma_desc_t) - 1);
+- if ((desc_base = (u32)kmalloc(i, GFP_KERNEL)) == 0)
++ if ((desc_base = (u32)kmalloc(i, GFP_KERNEL|GFP_DMA)) == 0)
+ return 0;
+
+ desc_base = ALIGN_ADDR(desc_base, sizeof(au1x_ddma_desc_t));
+@@ -404,7 +429,13 @@
+ cmd0 |= DSCR_CMD0_SID(srcid);
+ cmd0 |= DSCR_CMD0_DID(destid);
+ cmd0 |= DSCR_CMD0_IE | DSCR_CMD0_CV;
+- cmd0 |= DSCR_CMD0_ST(DSCR_CMD0_ST_CURRENT);
++ cmd0 |= DSCR_CMD0_ST(DSCR_CMD0_ST_NOCHANGE);
++
++ /* is it mem to mem transfer? */
++ if(((DSCR_CUSTOM2DEV_ID(srcid) == DSCR_CMD0_THROTTLE) || (DSCR_CUSTOM2DEV_ID(srcid) == DSCR_CMD0_ALWAYS)) &&
++ ((DSCR_CUSTOM2DEV_ID(destid) == DSCR_CMD0_THROTTLE) || (DSCR_CUSTOM2DEV_ID(destid) == DSCR_CMD0_ALWAYS))) {
++ cmd0 |= DSCR_CMD0_MEM;
++ }
+
+ switch (stp->dev_devwidth) {
+ case 8:
+@@ -462,9 +493,14 @@
+ /* If source input is fifo, set static address.
+ */
+ if (stp->dev_flags & DEV_FLAGS_IN) {
+- src0 = stp->dev_physaddr;
++ if ( stp->dev_flags & DEV_FLAGS_BURSTABLE )
++ src1 |= DSCR_SRC1_SAM(DSCR_xAM_BURST);
++ else
+ src1 |= DSCR_SRC1_SAM(DSCR_xAM_STATIC);
++
+ }
++ if (stp->dev_physaddr)
++ src0 = stp->dev_physaddr;
+
+ /* Set up dest1. For now, assume no stride and increment.
+ * A channel attribute update can change this later.
+@@ -488,10 +524,18 @@
+ /* If destination output is fifo, set static address.
+ */
+ if (dtp->dev_flags & DEV_FLAGS_OUT) {
+- dest0 = dtp->dev_physaddr;
++ if ( dtp->dev_flags & DEV_FLAGS_BURSTABLE )
++ dest1 |= DSCR_DEST1_DAM(DSCR_xAM_BURST);
++ else
+ dest1 |= DSCR_DEST1_DAM(DSCR_xAM_STATIC);
+ }
+-
++ if (dtp->dev_physaddr)
++ dest0 = dtp->dev_physaddr;
++
++#if 0
++ printk("did:%x sid:%x cmd0:%x cmd1:%x source0:%x source1:%x dest0:%x dest1:%x\n",
++ dtp->dev_id, stp->dev_id, cmd0, cmd1, src0, src1, dest0, dest1 );
++#endif
+ for (i=0; i<entries; i++) {
+ dp->dscr_cmd0 = cmd0;
+ dp->dscr_cmd1 = cmd1;
+@@ -500,10 +544,12 @@
+ dp->dscr_dest0 = dest0;
+ dp->dscr_dest1 = dest1;
+ dp->dscr_stat = 0;
++ dp->sw_context = 0;
++ dp->sw_status = 0;
+ dp->dscr_nxtptr = DSCR_NXTPTR(virt_to_phys(dp + 1));
+ dp++;
+ }
+-
++
+ /* Make last descrptor point to the first.
+ */
+ dp--;
+@@ -512,13 +558,14 @@
+
+ return (u32)(ctp->chan_desc_base);
+ }
++EXPORT_SYMBOL(au1xxx_dbdma_ring_alloc);
+
+ /* Put a source buffer into the DMA ring.
+ * This updates the source pointer and byte count. Normally used
+ * for memory to fifo transfers.
+ */
+ u32
+-au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes)
++_au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes, u32 flags)
+ {
+ chan_tab_t *ctp;
+ au1x_ddma_desc_t *dp;
+@@ -540,14 +587,30 @@
+ if (dp->dscr_cmd0 & DSCR_CMD0_V) {
+ return 0;
+ }
+-
++
+ /* Load up buffer address and byte count.
+ */
+ dp->dscr_source0 = virt_to_phys(buf);
+ dp->dscr_cmd1 = nbytes;
+- dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */
+- ctp->chan_ptr->ddma_dbell = 0xffffffff; /* Make it go */
+-
++ /* Check flags */
++ if (flags & DDMA_FLAGS_IE)
++ dp->dscr_cmd0 |= DSCR_CMD0_IE;
++ if (flags & DDMA_FLAGS_NOIE)
++ dp->dscr_cmd0 &= ~DSCR_CMD0_IE;
++
++ /*
++ * There is an errata on the Au1200/Au1550 parts that could result
++ * in "stale" data being DMA'd. It has to do with the snoop logic on
++ * the dache eviction buffer. NONCOHERENT_IO is on by default for
++ * these parts. If it is fixedin the future, these dma_cache_inv will
++ * just be nothing more than empty macros. See io.h.
++ * */
++ dma_cache_wback_inv((unsigned long)buf, nbytes);
++ dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */
++ au_sync();
++ dma_cache_wback_inv((unsigned long)dp, sizeof(dp));
++ ctp->chan_ptr->ddma_dbell = 0;
++
+ /* Get next descriptor pointer.
+ */
+ ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+@@ -556,13 +619,14 @@
+ */
+ return nbytes;
+ }
++EXPORT_SYMBOL(_au1xxx_dbdma_put_source);
+
+ /* Put a destination buffer into the DMA ring.
+ * This updates the destination pointer and byte count. Normally used
+ * to place an empty buffer into the ring for fifo to memory transfers.
+ */
+ u32
+-au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes)
++_au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes, u32 flags)
+ {
+ chan_tab_t *ctp;
+ au1x_ddma_desc_t *dp;
+@@ -581,15 +645,38 @@
+ /* If the descriptor is valid, we are way ahead of the DMA
+ * engine, so just return an error condition.
+ */
+- if (dp->dscr_cmd0 & DSCR_CMD0_V)
++ if (dp->dscr_cmd0 & DSCR_CMD0_V) {
+ return 0;
+-
+- /* Load up buffer address and byte count.
+- */
++ }
++
++ /* Load up buffer address and byte count */
++
++ /* Check flags */
++ if (flags & DDMA_FLAGS_IE)
++ dp->dscr_cmd0 |= DSCR_CMD0_IE;
++ if (flags & DDMA_FLAGS_NOIE)
++ dp->dscr_cmd0 &= ~DSCR_CMD0_IE;
++
+ dp->dscr_dest0 = virt_to_phys(buf);
+ dp->dscr_cmd1 = nbytes;
++#if 0
++ printk("cmd0:%x cmd1:%x source0:%x source1:%x dest0:%x dest1:%x\n",
++ dp->dscr_cmd0, dp->dscr_cmd1, dp->dscr_source0,
++ dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1 );
++#endif
++ /*
++ * There is an errata on the Au1200/Au1550 parts that could result in
++ * "stale" data being DMA'd. It has to do with the snoop logic on the
++ * dache eviction buffer. NONCOHERENT_IO is on by default for these
++ * parts. If it is fixedin the future, these dma_cache_inv will just
++ * be nothing more than empty macros. See io.h.
++ * */
++ dma_cache_inv((unsigned long)buf,nbytes);
+ dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */
+-
++ au_sync();
++ dma_cache_wback_inv((unsigned long)dp, sizeof(dp));
++ ctp->chan_ptr->ddma_dbell = 0;
++
+ /* Get next descriptor pointer.
+ */
+ ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+@@ -598,6 +685,7 @@
+ */
+ return nbytes;
+ }
++EXPORT_SYMBOL(_au1xxx_dbdma_put_dest);
+
+ /* Get a destination buffer into the DMA ring.
+ * Normally used to get a full buffer from the ring during fifo
+@@ -625,29 +713,31 @@
+ /* If the descriptor is valid, we are way ahead of the DMA
+ * engine, so just return an error condition.
+ */
+- if (dp->dscr_cmd0 & DSCR_CMD0_V)
++ if (dp->dscr_cmd0 & DSCR_CMD0_V) {
+ return 0;
+-
++ }
++
+ /* Return buffer address and byte count.
+ */
+ *buf = (void *)(phys_to_virt(dp->dscr_dest0));
+ *nbytes = dp->dscr_cmd1;
+ rv = dp->dscr_stat;
+-
++
+ /* Get next descriptor pointer.
+ */
+ ctp->get_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+
+ /* return something not zero.
+ */
+- return rv;
++ return *nbytes;
+ }
++EXPORT_SYMBOL(au1xxx_dbdma_get_dest);
+
+ void
+ au1xxx_dbdma_stop(u32 chanid)
+ {
+ chan_tab_t *ctp;
+- volatile au1x_dma_chan_t *cp;
++ au1x_dma_chan_t *cp;
+ int halt_timeout = 0;
+
+ ctp = *((chan_tab_t **)chanid);
+@@ -667,6 +757,7 @@
+ cp->ddma_stat |= (DDMA_STAT_DB | DDMA_STAT_V);
+ au_sync();
+ }
++EXPORT_SYMBOL(au1xxx_dbdma_stop);
+
+ /* Start using the current descriptor pointer. If the dbdma encounters
+ * a not valid descriptor, it will stop. In this case, we can just
+@@ -676,17 +767,17 @@
+ au1xxx_dbdma_start(u32 chanid)
+ {
+ chan_tab_t *ctp;
+- volatile au1x_dma_chan_t *cp;
++ au1x_dma_chan_t *cp;
+
+ ctp = *((chan_tab_t **)chanid);
+-
+ cp = ctp->chan_ptr;
+ cp->ddma_desptr = virt_to_phys(ctp->cur_ptr);
+ cp->ddma_cfg |= DDMA_CFG_EN; /* Enable channel */
+ au_sync();
+- cp->ddma_dbell = 0xffffffff; /* Make it go */
++ cp->ddma_dbell = 0;
+ au_sync();
+ }
++EXPORT_SYMBOL(au1xxx_dbdma_start);
+
+ void
+ au1xxx_dbdma_reset(u32 chanid)
+@@ -705,15 +796,21 @@
+
+ do {
+ dp->dscr_cmd0 &= ~DSCR_CMD0_V;
++ /* reset our SW status -- this is used to determine
++ * if a descriptor is in use by upper level SW. Since
++ * posting can reset 'V' bit.
++ */
++ dp->sw_status = 0;
+ dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+ } while (dp != ctp->chan_desc_base);
+ }
++EXPORT_SYMBOL(au1xxx_dbdma_reset);
+
+ u32
+ au1xxx_get_dma_residue(u32 chanid)
+ {
+ chan_tab_t *ctp;
+- volatile au1x_dma_chan_t *cp;
++ au1x_dma_chan_t *cp;
+ u32 rv;
+
+ ctp = *((chan_tab_t **)chanid);
+@@ -726,6 +823,7 @@
+
+ return rv;
+ }
++EXPORT_SYMBOL(au1xxx_get_dma_residue);
+
+ void
+ au1xxx_dbdma_chan_free(u32 chanid)
+@@ -739,35 +837,35 @@
+
+ au1xxx_dbdma_stop(chanid);
+
+- if (ctp->chan_desc_base != NULL)
+- kfree(ctp->chan_desc_base);
+-
++ kfree((void *)ctp->chan_desc_base);
++
+ stp->dev_flags &= ~DEV_FLAGS_INUSE;
+ dtp->dev_flags &= ~DEV_FLAGS_INUSE;
+ chan_tab_ptr[ctp->chan_index] = NULL;
+
+ kfree(ctp);
+ }
++EXPORT_SYMBOL(au1xxx_dbdma_chan_free);
+
+-static void
++static irqreturn_t
+ dbdma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+ {
+- u32 intstat;
+- u32 chan_index;
++ u32 intstat;
++ u32 chan_index;
+ chan_tab_t *ctp;
+ au1x_ddma_desc_t *dp;
+- volatile au1x_dma_chan_t *cp;
++ au1x_dma_chan_t *cp;
+
+ intstat = dbdma_gptr->ddma_intstat;
+ au_sync();
+ chan_index = au_ffs(intstat) - 1;
+
+ ctp = chan_tab_ptr[chan_index];
+ cp = ctp->chan_ptr;
+ dp = ctp->cur_ptr;
+
+ /* Reset interrupt.
+- */
++ */
+ cp->ddma_irq = 0;
+ au_sync();
+
+@@ -775,18 +875,28 @@
+ (ctp->chan_callback)(irq, ctp->chan_callparam, regs);
+
+ ctp->cur_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+-
++
++ return IRQ_RETVAL(1);
+ }
+
+-static void
+-au1xxx_dbdma_init(void)
++static void au1xxx_dbdma_init(void)
+ {
++ int irq_nr;
++
+ dbdma_gptr->ddma_config = 0;
+ dbdma_gptr->ddma_throttle = 0;
+ dbdma_gptr->ddma_inten = 0xffff;
+ au_sync();
+
+- if (request_irq(AU1550_DDMA_INT, dbdma_interrupt, SA_INTERRUPT,
++#if defined(CONFIG_SOC_AU1550)
++ irq_nr = AU1550_DDMA_INT;
++#elif defined(CONFIG_SOC_AU1200)
++ irq_nr = AU1200_DDMA_INT;
++#else
++ #error Unknown Au1x00 SOC
++#endif
++
++ if (request_irq(irq_nr, dbdma_interrupt, SA_INTERRUPT,
+ "Au1xxx dbdma", (void *)dbdma_gptr))
+ printk("Can't get 1550 dbdma irq");
+ }
+@@ -797,7 +907,8 @@
+ chan_tab_t *ctp;
+ au1x_ddma_desc_t *dp;
+ dbdev_tab_t *stp, *dtp;
+- volatile au1x_dma_chan_t *cp;
++ au1x_dma_chan_t *cp;
++ u32 i = 0;
+
+ ctp = *((chan_tab_t **)chanid);
+ stp = ctp->chan_src;
+@@ -809,7 +920,7 @@
+ printk("desc base %x, get %x, put %x, cur %x\n",
+ (u32)(ctp->chan_desc_base), (u32)(ctp->get_ptr),
+ (u32)(ctp->put_ptr), (u32)(ctp->cur_ptr));
+-
++
+ printk("dbdma chan %x\n", (u32)cp);
+ printk("cfg %08x, desptr %08x, statptr %08x\n",
+ cp->ddma_cfg, cp->ddma_desptr, cp->ddma_statptr);
+@@ -822,28 +933,65 @@
+ dp = ctp->chan_desc_base;
+
+ do {
+- printk("dp %08x, cmd0 %08x, cmd1 %08x\n",
+- (u32)dp, dp->dscr_cmd0, dp->dscr_cmd1);
+- printk("src0 %08x, src1 %08x, dest0 %08x\n",
+- dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0);
+- printk("dest1 %08x, stat %08x, nxtptr %08x\n",
+- dp->dscr_dest1, dp->dscr_stat, dp->dscr_nxtptr);
++ printk("Dp[%d]= %08x, cmd0 %08x, cmd1 %08x\n",
++ i++, (u32)dp, dp->dscr_cmd0, dp->dscr_cmd1);
++ printk("src0 %08x, src1 %08x, dest0 %08x, dest1 %08x\n",
++ dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1);
++ printk("stat %08x, nxtptr %08x\n",
++ dp->dscr_stat, dp->dscr_nxtptr);
+ dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+ } while (dp != ctp->chan_desc_base);
+ }
++EXPORT_SYMBOL(au1xxx_dbdma_dump);
+
++/* Put a descriptor into the DMA ring.
++ * This updates the source/destination pointers and byte count.
++ */
++u32
++au1xxx_dbdma_put_dscr(u32 chanid, au1x_ddma_desc_t *dscr )
++{
++ chan_tab_t *ctp;
++ au1x_ddma_desc_t *dp;
++ u32 nbytes=0;
+
+-EXPORT_SYMBOL(au1xxx_dbdma_dump);
+-EXPORT_SYMBOL(au1xxx_dbdma_put_source);
+-EXPORT_SYMBOL(au1xxx_dbdma_put_dest);
+-EXPORT_SYMBOL(au1xxx_dbdma_ring_alloc);
+-EXPORT_SYMBOL(au1xxx_dbdma_start);
+-EXPORT_SYMBOL(au1xxx_dbdma_get_dest);
+-EXPORT_SYMBOL(au1xxx_dbdma_chan_alloc);
+-EXPORT_SYMBOL(au1xxx_get_dma_residue);
+-EXPORT_SYMBOL(au1xxx_dbdma_set_devwidth);
+-EXPORT_SYMBOL(au1xxx_dbdma_chan_free);
+-EXPORT_SYMBOL(au1xxx_dbdma_reset);
++ /* I guess we could check this to be within the
++ * range of the table......
++ */
++ ctp = *((chan_tab_t **)chanid);
+
+-#endif /* defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) */
++ /* We should have multiple callers for a particular channel,
++ * an interrupt doesn't affect this pointer nor the descriptor,
++ * so no locking should be needed.
++ */
++ dp = ctp->put_ptr;
++
++ /* If the descriptor is valid, we are way ahead of the DMA
++ * engine, so just return an error condition.
++ */
++ if (dp->dscr_cmd0 & DSCR_CMD0_V)
++ return 0;
+
++ /* Load up buffer addresses and byte count.
++ */
++ dp->dscr_dest0 = dscr->dscr_dest0;
++ dp->dscr_source0 = dscr->dscr_source0;
++ dp->dscr_dest1 = dscr->dscr_dest1;
++ dp->dscr_source1 = dscr->dscr_source1;
++ dp->dscr_cmd1 = dscr->dscr_cmd1;
++ nbytes = dscr->dscr_cmd1;
++ /* Allow the caller to specifiy if an interrupt is generated */
++ dp->dscr_cmd0 &= ~DSCR_CMD0_IE;
++ dp->dscr_cmd0 |= dscr->dscr_cmd0 | DSCR_CMD0_V;
++ ctp->chan_ptr->ddma_dbell = 0;
++
++ /* Get next descriptor pointer.
++ */
++ ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
++
++ /* return something not zero.
++ */
++ return nbytes;
++}
++EXPORT_SYMBOL(au1xxx_dbdma_put_dscr);
++
++#endif /* defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) */
+--- linux-old/drivers/sound/au1550_psc.c 2006-05-09 14:49:35.000000000 +0200
++++ linux/drivers/sound/au1550_psc.c 2006-05-09 18:19:01.000000000 +0200
+@@ -82,6 +83,11 @@
+
+ #define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
+ #define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
++#ifdef AU1000_VERBOSE_DEBUG
++#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif /* AU1000_VERBOSE_DEBUG */
+
+ /* Boot options
+ * 0 = no VRA, 1 = use VRA if codec supports it
+@@ -127,7 +129,7 @@
+ unsigned fragshift;
+ void *nextIn;
+ void *nextOut;
+- int count;
++ volatile int count;
+ unsigned total_bytes;
+ unsigned error;
+ wait_queue_head_t wait;
+@@ -198,7 +200,7 @@
+ struct au1550_state *s = (struct au1550_state *)codec->private_data;
+ unsigned long flags;
+ u32 cmd, val;
+- u16 data;
++ u16 data=0;
+ int i;
+
+ spin_lock_irqsave(&s->lock, flags);
+@@ -210,26 +212,21 @@
+ break;
+ }
+ if (i == POLL_COUNT)
+- err("rdcodec: codec cmd pending expired!");
++ err("rdcodec: codec cmd pending expired! %i %i", val, addr);
++
++ val = au_readl(PSC_AC97EVNT);
++ if (val & PSC_AC97EVNT_CD) {
++ err("rdcodec: command done is set! %i %i", val, addr);
++ au_readl(PSC_AC97CDC); // manual says: you must read CDC[DATA] before resetting EVNT[CD], if command was read; and we don't know here what the last command was
++ au_sync();
++ au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
++ }
+
+ cmd = (u32)PSC_AC97CDC_INDX(addr);
+ cmd |= PSC_AC97CDC_RD; /* read command */
+ au_writel(cmd, PSC_AC97CDC);
+ au_sync();
+
+- /* now wait for the data
+- */
+- for (i = 0; i < POLL_COUNT; i++) {
+- val = au_readl(PSC_AC97STAT);
+- au_sync();
+- if (!(val & PSC_AC97STAT_CP))
+- break;
+- }
+- if (i == POLL_COUNT) {
+- err("rdcodec: read poll expired!");
+- return 0;
+- }
+-
+ /* wait for command done?
+ */
+ for (i = 0; i < POLL_COUNT; i++) {
+@@ -239,8 +236,8 @@
+ break;
+ }
+ if (i == POLL_COUNT) {
+- err("rdcodec: read cmdwait expired!");
+- return 0;
++ err("rdcodec: read cmdwait expired! %i %i", val, addr);
++ goto rcodec_out;
+ }
+
+ data = au_readl(PSC_AC97CDC) & 0xffff;
+@@ -251,8 +248,11 @@
+ au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
+ au_sync();
+
++ rcodec_out:
+ spin_unlock_irqrestore(&s->lock, flags);
+
++ dbg("rdcodec %x -> %x", addr, data);
++
+ return data;
+ }
+
+@@ -274,7 +274,15 @@
+ break;
+ }
+ if (i == POLL_COUNT)
+- err("wrcodec: codec cmd pending expired!");
++ err("wrcodec: codec cmd pending expired! %i %i", val, addr);
++
++ val = au_readl(PSC_AC97EVNT);
++ if (val & PSC_AC97EVNT_CD) {
++ err("wrcodec: command done is set! %i %i", val, addr);
++ au_readl(PSC_AC97CDC); // manual says: you must read CDC[DATA] before resetting EVNT[CD], if command was read; and we don't know here what the last command was
++ au_sync();
++ au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
++ }
+
+ cmd = (u32)PSC_AC97CDC_INDX(addr);
+ cmd |= (u32)data;
+@@ -282,22 +290,13 @@
+ au_sync();
+
+ for (i = 0; i < POLL_COUNT; i++) {
+- val = au_readl(PSC_AC97STAT);
+- au_sync();
+- if (!(val & PSC_AC97STAT_CP))
+- break;
+- }
+- if (i == POLL_COUNT)
+- err("wrcodec: codec cmd pending expired!");
+-
+- for (i = 0; i < POLL_COUNT; i++) {
+ val = au_readl(PSC_AC97EVNT);
+ au_sync();
+ if (val & PSC_AC97EVNT_CD)
+ break;
+ }
+ if (i == POLL_COUNT)
+- err("wrcodec: read cmdwait expired!");
++ err("wrcodec: read cmdwait expired! %i %i", val, addr);
+
+ /* Clear command done event.
+ */
+@@ -305,6 +304,8 @@
+ au_sync();
+
+ spin_unlock_irqrestore(&s->lock, flags);
++
++ dbg("wrcodec %x -> %x done", data, addr);
+ }
+
+ static void
+@@ -392,7 +393,7 @@
+ adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
+
+ #ifdef AU1000_VERBOSE_DEBUG
+- dbg(__FUNCTION__ ": set to %d Hz", adc_rate);
++ dbg("%s : set to %d Hz", __FUNCTION__, adc_rate);
+ #endif
+
+ /* some codec's don't allow unequal DAC and ADC rates, in which case
+@@ -452,7 +453,7 @@
+ dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
+
+ #ifdef AU1000_VERBOSE_DEBUG
+- dbg(__FUNCTION__ ": set to %d Hz", dac_rate);
++ dbg("%s : set to %d Hz", __FUNCTION__, dac_rate);
+ #endif
+
+ /* some codec's don't allow unequal DAC and ADC rates, in which case
+@@ -489,6 +490,9 @@
+ au1xxx_dbdma_reset(db->dmanr);
+
+ db->stopped = 1;
++ db->count = 0;
++ db->dma_qcount = 0;
++ db->nextIn = db->nextOut = db->rawbuf;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+ }
+@@ -518,6 +522,9 @@
+ au1xxx_dbdma_reset(db->dmanr);
+
+ db->stopped = 1;
++ db->count = 0;
++ db->dma_qcount = 0;
++ db->nextIn = db->nextOut = db->rawbuf;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+ }
+@@ -609,7 +616,8 @@
+
+ spin_lock_irqsave(&s->lock, flags);
+
+- set_xmit_slots(db->num_channels);
++ au1xxx_dbdma_reset(db->dmanr);
++
+ au_writel(PSC_AC97PCR_TC, PSC_AC97PCR);
+ au_sync();
+ au_writel(PSC_AC97PCR_TS, PSC_AC97PCR);
+@@ -634,17 +642,20 @@
+
+ spin_lock_irqsave(&s->lock, flags);
+
++ au1xxx_dbdma_reset(db->dmanr);
++
+ /* Put two buffers on the ring to get things started.
+ */
+- for (i=0; i<2; i++) {
+- au1xxx_dbdma_put_dest(db->dmanr, db->nextIn, db->dma_fragsize);
++ for (i=0; i<NUM_DBDMA_DESCRIPTORS; i++) {
++ if ( au1xxx_dbdma_put_dest(db->dmanr, db->nextIn, db->dma_fragsize) ) {
+
+- db->nextIn += db->dma_fragsize;
+- if (db->nextIn >= db->rawbuf + db->dmasize)
+- db->nextIn -= db->dmasize;
++ db->nextIn += db->dma_fragsize;
++ if (db->nextIn >= db->rawbuf + db->dmasize)
++ db->nextIn -= db->dmasize;
++ } else
++ info("Cannot put dest %i", i);
+ }
+
+- set_recv_slots(db->num_channels);
+ au1xxx_dbdma_start(db->dmanr);
+ au_writel(PSC_AC97PCR_RC, PSC_AC97PCR);
+ au_sync();
+@@ -665,9 +676,12 @@
+ if (!db->rawbuf) {
+ db->ready = db->mapped = 0;
+ db->buforder = 5; /* 32 * PAGE_SIZE */
++ bufs = PAGE_SIZE << db->buforder;
+ db->rawbuf = kmalloc((PAGE_SIZE << db->buforder), GFP_KERNEL);
+ if (!db->rawbuf)
+ return -ENOMEM;
++ } else {
++ bufs = PAGE_SIZE << db->buforder;
+ }
+
+ db->cnt_factor = 1;
+@@ -686,7 +700,6 @@
+ 2 : db->num_channels);
+
+ user_bytes_per_sec = rate * db->user_bytes_per_sample;
+- bufs = PAGE_SIZE << db->buforder;
+ if (db->ossfragshift) {
+ if ((1000 << db->ossfragshift) < user_bytes_per_sec)
+ db->fragshift = ld2(user_bytes_per_sec/1000);
+@@ -731,6 +744,7 @@
+ static int
+ prog_dmabuf_adc(struct au1550_state *s)
+ {
++ dbg("prog_dmabuf_adc s%i c%i", s->dma_adc.sample_size, s->dma_adc.num_channels);
+ stop_adc(s);
+ return prog_dmabuf(s, &s->dma_adc);
+
+@@ -739,6 +753,8 @@
+ static int
+ prog_dmabuf_dac(struct au1550_state *s)
+ {
++ dbg("prog_dmabuf_dac s%i c%i", s->dma_dac.sample_size, s->dma_dac.num_channels);
++
+ stop_dac(s);
+ return prog_dmabuf(s, &s->dma_dac);
+ }
+@@ -752,20 +768,20 @@
+ {
+ struct au1550_state *s = (struct au1550_state *) dev_id;
+ struct dmabuf *db = &s->dma_dac;
++#ifdef AU1000_VERBOSE_DEBUG
+ u32 ac97c_stat;
++#endif
+
+- ac97c_stat = au_readl(PSC_AC97STAT);
+ #ifdef AU1000_VERBOSE_DEBUG
+- if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE))
+- dbg("AC97C status = 0x%08x", ac97c_stat);
++// if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE))
++// dbg("AC97C status = 0x%08x", ac97c_stat);
+ #endif
+ db->dma_qcount--;
+
+- if (db->count >= db->fragsize) {
+- if (au1xxx_dbdma_put_source(db->dmanr, db->nextOut,
+- db->fragsize) == 0) {
+- err("qcount < 2 and no ring room!");
+- }
++ // put source buffers as long as we have data and free dma descr
++ while ( (db->count >= db->fragsize) &&
++ (au1xxx_dbdma_put_source(db->dmanr, db->nextOut,
++ db->fragsize) ) ) {
+ db->nextOut += db->fragsize;
+ if (db->nextOut >= db->rawbuf + db->dmasize)
+ db->nextOut -= db->dmasize;
+@@ -785,30 +799,44 @@
+ struct dmabuf *dp = &s->dma_adc;
+ u32 obytes;
+ char *obuf;
++#ifdef AU1000_VERBOSE_DEBUG
++ u32 ac97c_stat;
++#endif
+
+- /* Pull the buffer from the dma queue.
+- */
+- au1xxx_dbdma_get_dest(dp->dmanr, (void *)(&obuf), &obytes);
+-
+- if ((dp->count + obytes) > dp->dmasize) {
+- /* Overrun. Stop ADC and log the error
+- */
+- stop_adc(s);
+- dp->error++;
+- err("adc overrun");
+- return;
+- }
++#ifdef AU1000_VERBOSE_DEBUG
++// if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE))
++// dbg("AC97C status = 0x%08x", ac97c_stat);
++#endif
+
+- /* Put a new empty buffer on the destination DMA.
++ /* Pull completed buffer(s) from the dma queue.
+ */
+- au1xxx_dbdma_put_dest(dp->dmanr, dp->nextIn, dp->dma_fragsize);
++ int cnt=0;
++ while ( au1xxx_dbdma_get_dest(dp->dmanr, (void *)(&obuf), &obytes) ) {
++ if ((dp->count + obytes) > dp->dmasize) {
++ /* Overrun. Stop ADC and log the error
++ */
++ stop_adc(s);
++ dp->error++;
++ err("adc overrun");
++ break;
++ }
+
+- dp->nextIn += dp->dma_fragsize;
+- if (dp->nextIn >= dp->rawbuf + dp->dmasize)
+- dp->nextIn -= dp->dmasize;
++ dp->count += obytes;
++ dp->total_bytes += obytes;
+
+- dp->count += obytes;
+- dp->total_bytes += obytes;
++ /* Put new empty buffer(s) on the destination DMA.
++ */
++ while ( au1xxx_dbdma_put_dest(dp->dmanr, dp->nextIn, dp->dma_fragsize) ) {
++ dp->nextIn += dp->dma_fragsize;
++ if (dp->nextIn >= dp->rawbuf + dp->dmasize)
++ dp->nextIn -= dp->dmasize;
++ }
++
++ if ( ++cnt>3 ) {
++ // get max 4 buffers at once here
++ break;
++ }
++ }
+
+ /* wake up anybody listening
+ */
+@@ -1078,13 +1104,10 @@
+ /* wait for samples in ADC dma buffer
+ */
+ do {
+- if (db->stopped)
++ if (db->stopped)
+ start_adc(s);
+- spin_lock_irqsave(&s->lock, flags);
++ set_current_state(TASK_INTERRUPTIBLE);
+ avail = db->count;
+- if (avail <= 0)
+- __set_current_state(TASK_INTERRUPTIBLE);
+- spin_unlock_irqrestore(&s->lock, flags);
+ if (avail <= 0) {
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+@@ -1163,11 +1189,8 @@
+ /* wait for space in playback buffer
+ */
+ do {
+- spin_lock_irqsave(&s->lock, flags);
++ set_current_state(TASK_INTERRUPTIBLE);
+ avail = (int) db->dmasize - db->count;
+- if (avail <= 0)
+- __set_current_state(TASK_INTERRUPTIBLE);
+- spin_unlock_irqrestore(&s->lock, flags);
+ if (avail <= 0) {
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+@@ -1190,6 +1214,7 @@
+ if ((cnt = copy_dmabuf_user(db, (char *) buffer,
+ count > avail ?
+ avail : count, 0)) < 0) {
++ err("copy_dmabuf_user error %i", cnt);
+ if (!ret)
+ ret = -EFAULT;
+ goto out;
+@@ -1213,6 +1228,7 @@
+ err("qcount < 2 and no ring room!");
+ }
+ db->nextOut += db->fragsize;
++ db->count -= db->fragsize;
+ if (db->nextOut >= db->rawbuf + db->dmasize)
+ db->nextOut -= db->dmasize;
+ db->total_bytes += db->dma_fragsize;
+@@ -1244,13 +1269,20 @@
+ unsigned int mask = 0;
+
+ if (file->f_mode & FMODE_WRITE) {
+- if (!s->dma_dac.ready)
++ if (!s->dma_dac.ready) {
++ err("poll: dma_dac not ready");
+ return 0;
++ }
+ poll_wait(file, &s->dma_dac.wait, wait);
+ }
+ if (file->f_mode & FMODE_READ) {
+- if (!s->dma_adc.ready)
++ if (!s->dma_adc.ready) {
++ err("poll: dma_adc not ready");
+ return 0;
++ }
++ if ( s->dma_adc.stopped ) {
++ start_adc(s);
++ }
+ poll_wait(file, &s->dma_adc.wait, wait);
+ }
+
+@@ -1388,7 +1420,7 @@
+ break;
+ }
+ if (count < sizeof(ioctl_str) / sizeof(ioctl_str[0]))
+- dbg("ioctl %s, arg=0x%lx", ioctl_str[count].str, arg);
++ dbg("ioctl %s(%x), arg=0x%lx", ioctl_str[count].str, cmd, arg);
+ else
+ dbg("ioctl 0x%x unknown, arg=0x%lx", cmd, arg);
+ #endif
+@@ -1456,12 +1488,14 @@
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ s->dma_adc.num_channels = val ? 2 : 1;
++ set_recv_slots(s->dma_adc.num_channels);
+ if ((ret = prog_dmabuf_adc(s)))
+ return ret;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ s->dma_dac.num_channels = val ? 2 : 1;
++ set_xmit_slots(s->dma_dac.num_channels);
+ if (s->codec_ext_caps & AC97_EXT_DACS) {
+ /* disable surround and center/lfe in AC'97
+ */
+@@ -1486,6 +1520,7 @@
+ return -EINVAL;
+ stop_adc(s);
+ s->dma_adc.num_channels = val;
++ set_recv_slots(s->dma_adc.num_channels);
+ if ((ret = prog_dmabuf_adc(s)))
+ return ret;
+ }
+@@ -1543,6 +1578,7 @@
+ }
+
+ s->dma_dac.num_channels = val;
++ set_xmit_slots(s->dma_dac.num_channels);
+ if ((ret = prog_dmabuf_dac(s)))
+ return ret;
+ }
+@@ -1799,6 +1835,7 @@
+ return -EINVAL;
+ }
+
++ info("mixdev_ioctl(%i)", cmd);
+ return mixdev_ioctl(s->codec, cmd, arg);
+ }
+
+@@ -1813,9 +1850,9 @@
+
+ #ifdef AU1000_VERBOSE_DEBUG
+ if (file->f_flags & O_NONBLOCK)
+- dbg(__FUNCTION__ ": non-blocking");
++ info("%s : non-blocking", __FUNCTION__);
+ else
+- dbg(__FUNCTION__ ": blocking");
++ info("%s : blocking", __FUNCTION__);
+ #endif
+
+ file->private_data = s;
+@@ -1846,6 +1883,7 @@
+ s->dma_adc.num_channels = 1;
+ s->dma_adc.sample_size = 8;
+ set_adc_rate(s, 8000);
++ set_recv_slots(s->dma_adc.num_channels);
+ if ((minor & 0xf) == SND_DEV_DSP16)
+ s->dma_adc.sample_size = 16;
+ }
+@@ -1856,22 +1894,31 @@
+ s->dma_dac.num_channels = 1;
+ s->dma_dac.sample_size = 8;
+ set_dac_rate(s, 8000);
++ set_xmit_slots(s->dma_dac.num_channels);
+ if ((minor & 0xf) == SND_DEV_DSP16)
+ s->dma_dac.sample_size = 16;
+ }
+
+ if (file->f_mode & FMODE_READ) {
+- if ((ret = prog_dmabuf_adc(s)))
++ if ((ret = prog_dmabuf_adc(s))) {
++ err("prog_dmabuf_adc failed");
++ up(&s->open_sem);
+ return ret;
++ }
++
+ }
+ if (file->f_mode & FMODE_WRITE) {
+- if ((ret = prog_dmabuf_dac(s)))
++ if ((ret = prog_dmabuf_dac(s))) {
++ err("prog_dmabuf_dac failed");
++ up(&s->open_sem);
+ return ret;
++ }
+ }
+
+ s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+ up(&s->open_sem);
+ init_MUTEX(&s->sem);
++
+ return 0;
+ }
+
+@@ -1954,12 +2001,13 @@
+ err("AC'97 ports in use");
+ }
+
++
+ /* Allocate the DMA Channels
+ */
+ if ((s->dma_dac.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_MEM_CHAN,
+ DBDMA_AC97_TX_CHAN, dac_dma_interrupt, (void *)s)) == 0) {
+ err("Can't get DAC DMA");
+- goto err_dma1;
++ goto err_dev1;
+ }
+ au1xxx_dbdma_set_devwidth(s->dma_dac.dmanr, 16);
+ if (au1xxx_dbdma_ring_alloc(s->dma_dac.dmanr,
+@@ -1968,10 +2016,11 @@
+ goto err_dma1;
+ }
+
++
+ if ((s->dma_adc.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_AC97_RX_CHAN,
+- DBDMA_MEM_CHAN, adc_dma_interrupt, (void *)s)) == 0) {
++ DBDMA_MEM_CHAN, adc_dma_interrupt, (void *)s)) == 0) {
+ err("Can't get ADC DMA");
+- goto err_dma2;
++ goto err_dma1;
+ }
+ au1xxx_dbdma_set_devwidth(s->dma_adc.dmanr, 16);
+ if (au1xxx_dbdma_ring_alloc(s->dma_adc.dmanr,
+@@ -1980,7 +2029,7 @@
+
+ #ifdef AU1550_DEBUG
+ /* intialize the debug proc device */
+- s->ps = create_proc_read_entry(AU1000_MODULE_NAME, 0, NULL,
++ s->ps = create_proc_read_entry(AU1550_MODULE_NAME, 0, NULL,
+ proc_au1550_dump, NULL);
+ #endif /* AU1550_DEBUG */
+
+@@ -2102,11 +2150,11 @@
+ unregister_sound_mixer(s->codec->dev_mixer);
+ err_dev2:
+ unregister_sound_dsp(s->dev_audio);
+- err_dev1:
+- au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
+ err_dma2:
+- au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
++ au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
+ err_dma1:
++ au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
++ err_dev1:
+ release_region(PHYSADDR(AC97_PSC_SEL), 0x30);
+
+ ac97_release_codec(s->codec);
+@@ -2125,11 +2173,11 @@
+ remove_proc_entry(AU1000_MODULE_NAME, NULL);
+ #endif /* AU1000_DEBUG */
+ synchronize_irq();
++ unregister_sound_dsp(s->dev_audio);
++ unregister_sound_mixer(s->codec->dev_mixer);
+ au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
+ au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
+ release_region(PHYSADDR(AC97_PSC_SEL), 0x30);
+- unregister_sound_dsp(s->dev_audio);
+- unregister_sound_mixer(s->codec->dev_mixer);
+ ac97_release_codec(s->codec);
+ }
+