diff options
Diffstat (limited to 'packages/linux/linux-mtx-2-2.4.27/44-dbdma-and-au1550_psc.diff')
-rw-r--r-- | packages/linux/linux-mtx-2-2.4.27/44-dbdma-and-au1550_psc.diff | 1491 |
1 files changed, 1491 insertions, 0 deletions
diff --git a/packages/linux/linux-mtx-2-2.4.27/44-dbdma-and-au1550_psc.diff b/packages/linux/linux-mtx-2-2.4.27/44-dbdma-and-au1550_psc.diff new file mode 100644 index 0000000000..af249dbe58 --- /dev/null +++ b/packages/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); + } + |