From 5ae0421a753316ca75f49f2d0c0780608515509a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 5 Jan 2010 03:18:01 +0100 Subject: [PATCH] jz4740: new dma api. cleanup audio pcm module --- .../arch/mips/include/asm/mach-jz4740/dma.h | 296 +---- .../files-2.6.31/arch/mips/jz4740/dma.c | 1099 +++++------------ .../sound/soc/jz4740/jz4740-i2s.c | 30 - .../sound/soc/jz4740/jz4740-pcm.c | 582 +++------ .../sound/soc/jz4740/jz4740-pcm.h | 19 - 5 files changed, 473 insertions(+), 1553 deletions(-) diff --git a/target/linux/xburst/files-2.6.31/arch/mips/include/asm/mach-jz4740/dma.h b/target/linux/xburst/files-2.6.31/arch/mips/include/asm/mach-jz4740/dma.h index 821eca90e..3f4dacffd 100644 --- a/target/linux/xburst/files-2.6.31/arch/mips/include/asm/mach-jz4740/dma.h +++ b/target/linux/xburst/files-2.6.31/arch/mips/include/asm/mach-jz4740/dma.h @@ -15,251 +15,75 @@ #ifndef __ASM_JZ4740_DMA_H__ #define __ASM_JZ4740_DMA_H__ -#include -#include /* need byte IO */ -#include /* And spinlocks */ -#include -#include -#include +struct jz4740_dma_chan; -/* - * Descriptor structure for JZ4740 DMA engine - * Note: this structure must always be aligned to a 16-bytes boundary. - */ - -typedef struct { - volatile u32 dcmd; /* DCMD value for the current transfer */ - volatile u32 dsadr; /* DSAR value for the current transfer */ - volatile u32 dtadr; /* DTAR value for the current transfer */ - volatile u32 ddadr; /* Points to the next descriptor + transfer count */ -} jz_dma_desc; - - -/* DMA Device ID's follow */ -enum { - DMA_ID_UART0_TX = 0, - DMA_ID_UART0_RX, - DMA_ID_SSI_TX, - DMA_ID_SSI_RX, - DMA_ID_AIC_TX, - DMA_ID_AIC_RX, - DMA_ID_MSC_TX, - DMA_ID_MSC_RX, - DMA_ID_TCU_OVERFLOW, - DMA_ID_AUTO, - DMA_ID_RAW_SET, - DMA_ID_MAX +enum jz4740_dma_request_type { + JZ4740_DMA_TYPE_AUTO_REQUEST = 8, + JZ4740_DMA_TYPE_UART_TRANSMIT = 20, + JZ4740_DMA_TYPE_UART_RECEIVE = 21, + JZ4740_DMA_TYPE_SPI_TRANSMIT = 22, + JZ4740_DMA_TYPE_SPI_RECEIVE = 23, + JZ4740_DMA_TYPE_AIC_TRANSMIT = 24, + JZ4740_DMA_TYPE_AIC_RECEIVE = 25, + JZ4740_DMA_TYPE_MMC_TRANSMIT = 26, + JZ4740_DMA_TYPE_MMC_RECEIVE = 27, + JZ4740_DMA_TYPE_TCU = 28, + JZ4740_DMA_TYPE_SADC = 29, + JZ4740_DMA_TYPE_SLCD = 30, }; -/* DMA modes, simulated by sw */ -#define DMA_MODE_READ 0x0 /* I/O to memory, no autoinit, increment, single mode */ -#define DMA_MODE_WRITE 0x1 /* memory to I/O, no autoinit, increment, single mode */ -#define DMA_AUTOINIT 0x2 -#define DMA_MODE_MASK 0x3 - -struct jz_dma_chan { - int dev_id; /* DMA ID: this channel is allocated if >=0, free otherwise */ - unsigned int io; /* DMA channel number */ - const char *dev_str; /* string describes the DMA channel */ - int irq; /* DMA irq number */ - void *irq_dev; /* DMA private device structure */ - unsigned int fifo_addr; /* physical fifo address of the requested device */ - unsigned int cntl; /* DMA controll */ - unsigned int mode; /* DMA configuration */ - unsigned int source; /* DMA request source */ +enum jz4740_dma_width { + JZ4740_DMA_WIDTH_8BIT, + JZ4740_DMA_WIDTH_16BIT, + JZ4740_DMA_WIDTH_32BIT, }; -extern struct jz_dma_chan jz_dma_table[]; +enum jz4740_dma_transfer_size { + JZ4740_DMA_TRANSFER_SIZE_4BYTE = 0, + JZ4740_DMA_TRANSFER_SIZE_1BYTE = 1, + JZ4740_DMA_TRANSFER_SIZE_2BYTE = 2, + JZ4740_DMA_TRANSFER_SIZE_16BYTE = 3, + JZ4740_DMA_TRANSFER_SIZE_32BYTE = 4, +}; + +enum jz4740_dma_flags { + JZ4740_DMA_SRC_AUTOINC = 0x2, + JZ4740_DMA_DST_AUTOINC = 0x1, +}; + +enum jz4740_dma_mode { + JZ4740_DMA_MODE_SINGLE = 0, + JZ4740_DMA_MODE_BLOCK = 1, +}; + +struct jz4740_dma_config { + enum jz4740_dma_width src_width; + enum jz4740_dma_width dst_width; + enum jz4740_dma_transfer_size transfer_size; + enum jz4740_dma_request_type request_type; + enum jz4740_dma_flags flags; + enum jz4740_dma_mode mode; +}; + +typedef void (*jz4740_dma_complete_callback_t)(struct jz4740_dma_chan *, int , void *); + +struct jz4740_dma_chan* jz4740_dma_request(void *dev, const char *name); +void jz4740_dma_free(struct jz4740_dma_chan *dma); + +void jz4740_dma_configure(struct jz4740_dma_chan *dma, + const struct jz4740_dma_config *config); -#define DMA_8BIT_RX_CMD \ - DMAC_DCMD_DAI | \ - DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \ - DMAC_DCMD_DS_8BIT | DMAC_DCMD_RDIL_IGN +void jz4740_dma_enable(struct jz4740_dma_chan *dma); +void jz4740_dma_disable(struct jz4740_dma_chan *dma); -#define DMA_8BIT_TX_CMD \ - DMAC_DCMD_SAI | \ - DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \ - DMAC_DCMD_DS_8BIT | DMAC_DCMD_RDIL_IGN +void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src); +void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst); +void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count); -#define DMA_16BIT_RX_CMD \ - DMAC_DCMD_DAI | \ - DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_32 | \ - DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN +uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma); -#define DMA_16BIT_TX_CMD \ - DMAC_DCMD_SAI | \ - DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_16 | \ - DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN - -#define DMA_32BIT_RX_CMD \ - DMAC_DCMD_DAI | \ - DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ - DMAC_DCMD_DS_32BIT | DMAC_DCMD_RDIL_IGN - -#define DMA_32BIT_TX_CMD \ - DMAC_DCMD_SAI | \ - DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ - DMAC_DCMD_DS_32BIT | DMAC_DCMD_RDIL_IGN - -#define DMA_16BYTE_RX_CMD \ - DMAC_DCMD_DAI | \ - DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \ - DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN - -#define DMA_16BYTE_TX_CMD \ - DMAC_DCMD_SAI | \ - DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \ - DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN - -#define DMA_32BYTE_RX_CMD \ - DMAC_DCMD_DAI | \ - DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \ - DMAC_DCMD_DS_32BYTE | DMAC_DCMD_RDIL_IGN - -#define DMA_32BYTE_TX_CMD \ - DMAC_DCMD_SAI | \ - DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \ - DMAC_DCMD_DS_32BYTE | DMAC_DCMD_RDIL_IGN - -#define DMA_AIC_32_16BYTE_TX_CMD \ - DMAC_DCMD_SAI | \ - DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ - DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN - -#define DMA_AIC_32_16BYTE_RX_CMD \ - DMAC_DCMD_DAI | \ - DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ - DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN - -#define DMA_AIC_16BIT_TX_CMD \ - DMAC_DCMD_SAI | \ - DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ - DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN - -#define DMA_AIC_16BIT_RX_CMD \ - DMAC_DCMD_DAI | \ - DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ - DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN - -#define DMA_AIC_16BYTE_RX_CMD \ - DMAC_DCMD_DAI | \ - DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ - DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN - -#define DMA_AIC_16BYTE_TX_CMD \ - DMAC_DCMD_SAI | \ - DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ - DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN - -extern int jz_request_dma(int dev_id, - const char *dev_str, - irqreturn_t (*irqhandler)(int, void *), - unsigned long irqflags, - void *irq_dev_id); -extern void jz_free_dma(unsigned int dmanr); - -extern int jz_dma_read_proc(char *buf, char **start, off_t fpos, - int length, int *eof, void *data); -extern void dump_jz_dma_channel(unsigned int dmanr); - -extern void enable_dma(unsigned int dmanr); -extern void disable_dma(unsigned int dmanr); -extern void set_dma_addr(unsigned int dmanr, unsigned int phyaddr); -extern void set_dma_count(unsigned int dmanr, unsigned int bytecnt); -extern void set_dma_mode(unsigned int dmanr, unsigned int mode); -extern void jz_set_alsa_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt); -extern unsigned int get_dma_residue(unsigned int dmanr); - -extern spinlock_t dma_spin_lock; - -static __inline__ unsigned long claim_dma_lock(void) -{ - unsigned long flags; - spin_lock_irqsave(&dma_spin_lock, flags); - return flags; -} - -static __inline__ void release_dma_lock(unsigned long flags) -{ - spin_unlock_irqrestore(&dma_spin_lock, flags); -} - -/* Clear the 'DMA Pointer Flip Flop'. - * Write 0 for LSB/MSB, 1 for MSB/LSB access. - */ -#define clear_dma_ff(channel) - -static __inline__ struct jz_dma_chan *get_dma_chan(unsigned int dmanr) -{ - if (dmanr > MAX_DMA_NUM - || jz_dma_table[dmanr].dev_id < 0) - return NULL; - return &jz_dma_table[dmanr]; -} - -static __inline__ int dma_halted(unsigned int dmanr) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - if (!chan) - return 1; - return __dmac_channel_transmit_halt_detected(dmanr) ? 1 : 0; -} - -static __inline__ unsigned int get_dma_mode(unsigned int dmanr) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - if (!chan) - return 0; - return chan->mode; -} - -static __inline__ void clear_dma_done(unsigned int dmanr) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - if (!chan) - return; - REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); -} - -static __inline__ void clear_dma_halt(unsigned int dmanr) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - if (!chan) - return; - REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT); - REG_DMAC_DMACR &= ~(DMAC_DMACR_HLT); -} - -static __inline__ void clear_dma_flag(unsigned int dmanr) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - if (!chan) - return; - REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); - REG_DMAC_DMACR &= ~(DMAC_DMACR_HLT | DMAC_DMACR_AR); -} - -static __inline__ void set_dma_page(unsigned int dmanr, char pagenr) -{ -} - -static __inline__ unsigned int get_dma_done_status(unsigned int dmanr) -{ - unsigned long dccsr; - struct jz_dma_chan *chan = get_dma_chan(dmanr); - if (!chan) - return 0; - dccsr = REG_DMAC_DCCSR(chan->io); - return dccsr & (DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); -} - -static __inline__ int get_dma_done_irq(unsigned int dmanr) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - if (!chan) - return -1; - return chan->irq; -} +void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma, + jz4740_dma_complete_callback_t cb); #endif /* __ASM_JZ4740_DMA_H__ */ diff --git a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/dma.c b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/dma.c index 24038a233..ce2c5cb9e 100644 --- a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/dma.c +++ b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/dma.c @@ -1,888 +1,339 @@ /* - * linux/arch/mips/jz4740/dma.c + * Copyright (C) 2010, Lars-Peter Clausen + * JZ4740 SoC DMA support * - * Support functions for the JZ4740 internal DMA channels. - * No-descriptor transfer only. - * Descriptor transfer should also call jz_request_dma() to get a free - * channel and call jz_free_dma() to free the channel. And driver should - * build the DMA descriptor and setup the DMA channel by itself. + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. * - * Copyright (C) 2006 Ingenic Semiconductor Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. * */ -#include #include -#include -#include +#include #include -#include #include -#include -#include #include -#include +#include #include +#include + +#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20) +#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20) +#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20) +#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20) +#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20) +#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20) +#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20) -#define JZ_REG_DMA_SRC_ADDR(x) ((x) * 0x20 + 0x00) -#define JZ_REG_DMA_DEST_ADDR(x) ((x) * 0x20 + 0x04) -#define JZ_REG_DMA_COUNT(x) ((x) * 0x20 + 0x08) -#define JZ_REG_DMA_TYPE(x) ((x) * 0x20 + 0x0c) -#define JZ_REG_DMA_STATUS(x) ((x) * 0x20 + 0x10) -#define JZ_REG_DMA_CMD(x) ((x) * 0x20 + 0x14) -#define JZ_REG_DMA_DESC_ADDR(x) ((x) * 0x20 + 0x18) #define JZ_REG_DMA_CTRL 0x300 #define JZ_REG_DMA_IRQ 0x304 #define JZ_REG_DMA_DOORBELL 0x308 #define JZ_REG_DMA_DOORBELL_SET 0x30C -#define JZ_DMA_STATUS_NO_DESC BIT(31) -#define JZ_DMA_STATUS_CDOA_MASK (0xff << 16) -#define JZ_DMA_STATUS_INV_DESC BIT(6) -#define JZ_DMA_STATUS_ADDR_ERROR BIT(4) -#define JZ_DMA_STATUS_TERMINATE_TRANSFER BIT(3) -#define JZ_DMA_STATUS_HALT BIT(2) -#define JZ_DMA_STATUS_CT BIT(1) -#define JZ_DMA_STATUS_ENABLE BIT(0) +#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31) +#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6) +#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4) +#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3) +#define JZ_DMA_STATUS_CTRL_HALT BIT(2) +#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1) +#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0) -#define JZ_DMA_CMD_SAI BIT(23) -#define JZ_DMA_CMD_DAI BIT(22) -#define JZ_DMA_CMD_RDIL_MASK (0xf << 16) -#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14) -#define JZ_DMA_CMD_DEST_WIDTH_MASK (0x3 << 12) -#define JZ_DMA_CMD_TRANSFER_SIZE_MASK (0x7 << 8) -#define JZ_DMA_CMD_BLOCK_MODE BIT(7) -#define JZ_DMA_CMD_VALID BIT(4) -#define JZ_DMA_CMD_VALID_MODE BIT(3) -#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2) -#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1) -#define JZ_DMA_CMD_LINK BIT(0) +#define JZ_DMA_CMD_SRC_INC BIT(23) +#define JZ_DMA_CMD_DST_INC BIT(22) +#define JZ_DMA_CMD_RDIL_MASK (0xf << 16) +#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14) +#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12) +#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8) +#define JZ_DMA_CMD_BLOCK_MODE BIT(7) +#define JZ_DMA_CMD_DESC_VALID BIT(4) +#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3) +#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2) +#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1) +#define JZ_DMA_CMD_LINK_ENABLE BIT(0) + +#define JZ_DMA_CMD_FLAGS_OFFSET 22 +#define JZ_DMA_CMD_RDIL_OFFSET 16 +#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14 +#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12 +#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8 +#define JZ_DMA_CMD_MODE_OFFSET 7 + +#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8) +#define JZ_DMA_CTRL_HALT BIT(3) +#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2) +#define JZ_DMA_CTRL_ENABLE BIT(0) -static void __iomem *jz_dma_base; -static spinlock_t jz_dma_lock; +static void __iomem *jz4740_dma_base; +static spinlock_t jz4740_dma_lock; -static inline uint32_t jz_dma_read(size_t reg) +static inline uint32_t jz4740_dma_read(size_t reg) { - return readl(jz_dma_base + reg); + return readl(jz4740_dma_base + reg); } -static inline void jz_dma_write(size_t reg, uint32_t val) +static inline void jz4740_dma_write(size_t reg, uint32_t val) { - writel(val, jz_dma_base + reg); + writel(val, jz4740_dma_base + reg); } - - -/* - * A note on resource allocation: - * - * All drivers needing DMA channels, should allocate and release them - * through the public routines `jz_request_dma()' and `jz_free_dma()'. - * - * In order to avoid problems, all processes should allocate resources in - * the same sequence and release them in the reverse order. - * - * So, when allocating DMAs and IRQs, first allocate the DMA, then the IRQ. - * When releasing them, first release the IRQ, then release the DMA. The - * main reason for this order is that, if you are requesting the DMA buffer - * done interrupt, you won't know the irq number until the DMA channel is - * returned from jz_request_dma(). - */ - -struct jz_dma_chan jz_dma_table[MAX_DMA_NUM] = { - {dev_id:-1,}, - {dev_id:-1,}, - {dev_id:-1,}, - {dev_id:-1,}, - {dev_id:-1,}, - {dev_id:-1,}, -}; - -// Device FIFO addresses and default DMA modes -static const struct { - unsigned int fifo_addr; - unsigned int dma_mode; - unsigned int dma_source; -} dma_dev_table[DMA_ID_MAX] = { - {CPHYSADDR(UART0_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART0OUT}, - {CPHYSADDR(UART0_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART0IN}, - {CPHYSADDR(SSI_DR), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSIOUT}, - {CPHYSADDR(SSI_DR), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSIIN}, - {CPHYSADDR(AIC_DR), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_AICOUT}, - {CPHYSADDR(AIC_DR), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_AICIN}, - {CPHYSADDR(MSC_TXFIFO), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSCOUT}, - {CPHYSADDR(MSC_RXFIFO), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSCIN}, - {0, DMA_AUTOINIT, DMAC_DRSR_RS_TCU}, - {0, DMA_AUTOINIT, DMAC_DRSR_RS_AUTO}, - {}, -}; - - -int jz_dma_read_proc(char *buf, char **start, off_t fpos, - int length, int *eof, void *data) +static inline void jz4740_dma_write_mask(size_t reg, uint32_t val, uint32_t mask) { - int i, len = 0; - struct jz_dma_chan *chan; - - for (i = 0; i < MAX_DMA_NUM; i++) { - if ((chan = get_dma_chan(i)) != NULL) { - len += sprintf(buf + len, "%2d: %s\n", - i, chan->dev_str); - } - } - - if (fpos >= len) { - *start = buf; - *eof = 1; - return 0; - } - *start = buf + fpos; - if ((len -= fpos) > length) - return length; - *eof = 1; - return len; + uint32_t val2; + val2 = jz4740_dma_read(reg); + val2 &= ~mask; + val2 |= val; + jz4740_dma_write(reg, val2); } +struct jz4740_dma_chan { + unsigned int id; + void *dev; + const char *name; -void dump_jz_dma_channel(unsigned int dmanr) -{ - struct jz_dma_chan *chan; + enum jz4740_dma_flags flags; + uint32_t transfer_shift; - if (dmanr > MAX_DMA_NUM) - return; - chan = &jz_dma_table[dmanr]; - - printk("DMA%d Registers:\n", dmanr); - printk(" DMACR = 0x%08x\n", REG_DMAC_DMACR); - printk(" DSAR = 0x%08x\n", REG_DMAC_DSAR(dmanr)); - printk(" DTAR = 0x%08x\n", REG_DMAC_DTAR(dmanr)); - printk(" DTCR = 0x%08x\n", REG_DMAC_DTCR(dmanr)); - printk(" DRSR = 0x%08x\n", REG_DMAC_DRSR(dmanr)); - printk(" DCCSR = 0x%08x\n", REG_DMAC_DCCSR(dmanr)); - printk(" DCMD = 0x%08x\n", REG_DMAC_DCMD(dmanr)); - printk(" DDA = 0x%08x\n", REG_DMAC_DDA(dmanr)); - printk(" DMADBR = 0x%08x\n", REG_DMAC_DMADBR); -} - - -/** - * jz_request_dma - dynamically allcate an idle DMA channel to return - * @dev_id: the specified dma device id or DMA_ID_RAW_SET - * @dev_str: the specified dma device string name - * @irqhandler: the irq handler, or NULL - * @irqflags: the irq handler flags - * @irq_dev_id: the irq handler device id for shared irq - * - * Finds a free channel, and binds the requested device to it. - * Returns the allocated channel number, or negative on error. - * Requests the DMA done IRQ if irqhandler != NULL. - * -*/ -/*int jz_request_dma(int dev_id, const char *dev_str, - void (*irqhandler)(int, void *, struct pt_regs *), - unsigned long irqflags, - void *irq_dev_id) -*/ - -int jz_request_dma(int dev_id, const char *dev_str, - irqreturn_t (*irqhandler)(int, void *), - unsigned long irqflags, - void *irq_dev_id) -{ - struct jz_dma_chan *chan; - int i, ret; - - if (dev_id < 0 || dev_id >= DMA_ID_MAX) - return -EINVAL; - - for (i = 0; i < MAX_DMA_NUM; i++) { - if (jz_dma_table[i].dev_id < 0) - break; - } - if (i == MAX_DMA_NUM) /* no free channel */ - return -ENODEV; - - /* we got a free channel */ - chan = &jz_dma_table[i]; - - if (irqhandler) { - chan->irq = JZ_IRQ_DMA(i); // allocate irq number - chan->irq_dev = irq_dev_id; - if ((ret = request_irq(chan->irq, irqhandler, irqflags, - dev_str, chan->irq_dev))) { - chan->irq = -1; - chan->irq_dev = NULL; - return ret; - } - } else { - chan->irq = -1; - chan->irq_dev = NULL; - } - - // fill it in - chan->io = i; - chan->dev_id = dev_id; - chan->dev_str = dev_str; - chan->fifo_addr = dma_dev_table[dev_id].fifo_addr; - chan->mode = dma_dev_table[dev_id].dma_mode; - chan->source = dma_dev_table[dev_id].dma_source; - - return i; -} - -void jz_free_dma(unsigned int dmanr) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - - if (!chan) { - printk("Trying to free DMA%d\n", dmanr); - return; - } - - disable_dma(dmanr); - if (chan->irq) - free_irq(chan->irq, chan->irq_dev); - - chan->irq = -1; - chan->irq_dev = NULL; - chan->dev_id = -1; -} - -void jz_set_dma_dest_width(int dmanr, int nbit) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - - if (!chan) - return; - - chan->mode &= ~DMAC_DCMD_DWDH_MASK; - switch (nbit) { - case 8: - chan->mode |= DMAC_DCMD_DWDH_8; - break; - case 16: - chan->mode |= DMAC_DCMD_DWDH_16; - break; - case 32: - chan->mode |= DMAC_DCMD_DWDH_32; - break; - } -} - -void jz_set_dma_src_width(int dmanr, int nbit) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - - if (!chan) - return; - - chan->mode &= ~DMAC_DCMD_SWDH_MASK; - switch (nbit) { - case 8: - chan->mode |= DMAC_DCMD_SWDH_8; - break; - case 16: - chan->mode |= DMAC_DCMD_SWDH_16; - break; - case 32: - chan->mode |= DMAC_DCMD_SWDH_32; - break; - } -} - -void jz_set_dma_block_size(int dmanr, int nbyte) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - - if (!chan) - return; - - chan->mode &= ~DMAC_DCMD_DS_MASK; - switch (nbyte) { - case 1: - chan->mode |= DMAC_DCMD_DS_8BIT; - break; - case 2: - chan->mode |= DMAC_DCMD_DS_16BIT; - break; - case 4: - chan->mode |= DMAC_DCMD_DS_32BIT; - break; - case 16: - chan->mode |= DMAC_DCMD_DS_16BYTE; - break; - case 32: - chan->mode |= DMAC_DCMD_DS_32BYTE; - break; - } -} - -unsigned int jz_get_dma_command(int dmanr) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - return chan->mode; -} - -/** - * jz_set_dma_mode - do the raw settings for the specified DMA channel - * @dmanr: the specified DMA channel - * @mode: dma operate mode, DMA_MODE_READ or DMA_MODE_WRITE - * @dma_mode: dma raw mode - * @dma_source: dma raw request source - * @fifo_addr: dma raw device fifo address - * - * Ensure call jz_request_dma(DMA_ID_RAW_SET, ...) first, then call - * jz_set_dma_mode() rather than set_dma_mode() if you work with - * and external request dma device. - * - * NOTE: Don not dynamically allocate dma channel if one external request - * dma device will occupy this channel. -*/ -int jz_set_dma_mode(unsigned int dmanr, unsigned int mode, - unsigned int dma_mode, unsigned int dma_source, - unsigned int fifo_addr) -{ - int dev_id, i; - struct jz_dma_chan *chan; - - if (dmanr > MAX_DMA_NUM) - return -ENODEV; - - for (i = 0; i < MAX_DMA_NUM; i++) { - if (jz_dma_table[i].dev_id < 0) - break; - } - if (i == MAX_DMA_NUM) - return -ENODEV; - - chan = &jz_dma_table[dmanr]; - dev_id = chan->dev_id; - if (dev_id > 0) { - printk(KERN_DEBUG "%s sets the allocated DMA channel %d!\n", - __FUNCTION__, dmanr); - return -ENODEV; - } - - /* clone it from the dynamically allocated. */ - if (i != dmanr) { - chan->irq = jz_dma_table[i].irq; - chan->irq_dev = jz_dma_table[i].irq_dev; - chan->dev_str = jz_dma_table[i].dev_str; - jz_dma_table[i].irq = 0; - jz_dma_table[i].irq_dev = NULL; - jz_dma_table[i].dev_id = -1; - } - chan->dev_id = DMA_ID_RAW_SET; - chan->io = dmanr; - chan->fifo_addr = fifo_addr; - chan->mode = dma_mode; - chan->source = dma_source; - - set_dma_mode(dmanr, dma_mode); - - return dmanr; -} - -void enable_dma(unsigned int dmanr) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - - if (!chan) - return; - - REG_DMAC_DCCSR(dmanr) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); - REG_DMAC_DCCSR(dmanr) |= DMAC_DCCSR_NDES; /* No-descriptor transfer */ - __dmac_enable_channel(dmanr); - if (chan->irq) - __dmac_channel_enable_irq(dmanr); -} - -#define DMA_DISABLE_POLL 0x10000 - -void disable_dma(unsigned int dmanr) -{ - int i; - struct jz_dma_chan *chan = get_dma_chan(dmanr); - - if (!chan) - return; - - if (!__dmac_channel_enabled(dmanr)) - return; - - for (i = 0; i < DMA_DISABLE_POLL; i++) - if (__dmac_channel_transmit_end_detected(dmanr)) - break; -#if 0 - if (i == DMA_DISABLE_POLL) - printk(KERN_INFO "disable_dma: poll expired!\n"); -#endif - - __dmac_disable_channel(dmanr); - if (chan->irq) - __dmac_channel_disable_irq(dmanr); -} - -/* Note: DMA_MODE_MASK is simulated by sw */ -void set_dma_mode(unsigned int dmanr, unsigned int mode) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - - if (!chan) - return; - - chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); - mode &= DMA_MODE_MASK; - if (mode == DMA_MODE_READ) { - chan->mode |= DMAC_DCMD_DAI; - chan->mode &= ~DMAC_DCMD_SAI; - } else if (mode == DMA_MODE_WRITE) { - chan->mode |= DMAC_DCMD_SAI; - chan->mode &= ~DMAC_DCMD_DAI; - } else { - printk(KERN_DEBUG "set_dma_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); - } - jz_dma_write(JZ_REG_DMA_CMD(chan->io), chan->mode & ~DMA_MODE_MASK); - jz_dma_write(JZ_REG_DMA_TYPE(chan->io), chan->source); -} - -void set_dma_addr(unsigned int dmanr, unsigned int phyaddr) -{ - unsigned int mode; - struct jz_dma_chan *chan = get_dma_chan(dmanr); - - if (!chan) - return; - - mode = chan->mode & DMA_MODE_MASK; - if (mode == DMA_MODE_READ) { - jz_dma_write(JZ_REG_DMA_SRC_ADDR(chan->io), chan->fifo_addr); - jz_dma_write(JZ_REG_DMA_DEST_ADDR(chan->io), phyaddr); - } else if (mode == DMA_MODE_WRITE) { - jz_dma_write(JZ_REG_DMA_SRC_ADDR(chan->io), phyaddr); - jz_dma_write(JZ_REG_DMA_DEST_ADDR(chan->io), chan->fifo_addr); - } else - printk(KERN_DEBUG "Driver should call set_dma_mode() ahead set_dma_addr()!\n"); -} - -void set_dma_count(unsigned int dmanr, unsigned int bytecnt) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - int dma_ds[] = {4, 1, 2, 16, 32}; - unsigned int ds; - - if (!chan) - return; - - ds = (chan->mode & DMAC_DCMD_DS_MASK) >> DMAC_DCMD_DS_BIT; - - jz_dma_write(JZ_REG_DMA_COUNT(chan->io), bytecnt / dma_ds[ds]); -} - -unsigned int get_dma_residue(unsigned int dmanr) -{ - unsigned int count, ds; - int dma_ds[] = {4, 1, 2, 16, 32}; - struct jz_dma_chan *chan = get_dma_chan(dmanr); - if (!chan) - return 0; - - ds = (chan->mode & DMAC_DCMD_DS_MASK) >> DMAC_DCMD_DS_BIT; - count = jz_dma_read(JZ_REG_DMA_COUNT(chan->io)); - count = count * dma_ds[ds]; - - return count; -} - - -void jz_set_alsa_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt) -{ - struct jz_dma_chan *chan = get_dma_chan(dmanr); - - if (!chan) - return; - - switch (audio_fmt) { - case 8: - /* SNDRV_PCM_FORMAT_S8 burst mode : 32BIT */ - break; - case 16: - /* SNDRV_PCM_FORMAT_S16_LE burst mode : 16BYTE */ - if (mode == DMA_MODE_READ) { - chan->mode = DMA_AIC_16BYTE_RX_CMD | DMA_MODE_READ; - chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); - mode &= DMA_MODE_MASK; - chan->mode |= DMAC_DCMD_DAI; - chan->mode &= ~DMAC_DCMD_SAI; - } else if (mode == DMA_MODE_WRITE) { - chan->mode = DMA_AIC_16BYTE_TX_CMD | DMA_MODE_WRITE; - chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); - mode &= DMA_MODE_MASK; - chan->mode |= DMAC_DCMD_SAI; - chan->mode &= ~DMAC_DCMD_DAI; - } else { - printk("alsa_dma_burst_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); - } - - jz_dma_write(JZ_REG_DMA_CMD(chan->io), chan->mode & ~DMA_MODE_MASK); - jz_dma_write(JZ_REG_DMA_TYPE(chan->io), chan->source); - - break; - } -} - -#undef JZ4740_DMAC_TEST_ENABLE - -#ifdef JZ4740_DMAC_TEST_ENABLE - -/* - * DMA test: external address <--> external address - */ -#define TEST_DMA_SIZE 16*1024 - -static jz_dma_desc *dma_desc; - -static int dma_chan; -static dma_addr_t dma_desc_phys_addr; -static unsigned int dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr; - -static int dma_check_result(void *src, void *dst, int size) -{ - unsigned int addr1, addr2, i, err = 0; - - addr1 = (unsigned int)src; - addr2 = (unsigned int)dst; - - for (i = 0; i < size; i += 4) { - if (*(volatile unsigned int *)addr1 != *(volatile unsigned int *)addr2) { - err++; - printk("wrong data at 0x%08x: src 0x%08x dst 0x%08x\n", addr2, *(volatile unsigned int *)addr1, *(volatile unsigned int *)addr2); - } - addr1 += 4; - addr2 += 4; - } - printk("check DMA result err=%d\n", err); - return err; -} - -static void jz4740_dma_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - printk("jz4740_dma_irq %d\n", irq); - - REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ - - if (__dmac_channel_transmit_halt_detected(dma_chan)) { - printk("DMA HALT\n"); - __dmac_channel_clear_transmit_halt(dma_chan); - } - - if (__dmac_channel_address_error_detected(dma_chan)) { - printk("DMA ADDR ERROR\n"); - __dmac_channel_clear_address_error(dma_chan); - } - - if (__dmac_channel_descriptor_invalid_detected(dma_chan)) { - printk("DMA DESC INVALID\n"); - __dmac_channel_clear_descriptor_invalid(dma_chan); - } - - if (__dmac_channel_count_terminated_detected(dma_chan)) { - printk("DMA CT\n"); - __dmac_channel_clear_count_terminated(dma_chan); - } - - if (__dmac_channel_transmit_end_detected(dma_chan)) { - printk("DMA TT\n"); - __dmac_channel_clear_transmit_end(dma_chan); - dump_jz_dma_channel(dma_chan); - dma_check_result((void *)dma_src_addr, (void *)dma_dst_addr, TEST_DMA_SIZE); - } - - /* free buffers */ - printk("free DMA buffers\n"); - free_pages(dma_src_addr, 2); - free_pages(dma_dst_addr, 2); - - if (dma_desc) - free_pages((unsigned int)dma_desc, 0); - - /* free dma */ - jz_free_dma(dma_chan); -} - -void dma_nodesc_test(void) -{ - unsigned int addr, i; - - printk("dma_nodesc_test\n"); - - /* Request DMA channel and setup irq handler */ - dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", jz4740_dma_irq, - SA_INTERRUPT, NULL); - if (dma_chan < 0) { - printk("Setup irq failed\n"); - return; - } - - printk("Requested DMA channel = %d\n", dma_chan); - - /* Allocate DMA buffers */ - dma_src_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ - dma_dst_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ - - dma_src_phys_addr = CPHYSADDR(dma_src_addr); - dma_dst_phys_addr = CPHYSADDR(dma_dst_addr); - - printk("Buffer addresses: 0x%08x 0x%08x 0x%08x 0x%08x\n", - dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr); - - /* Prepare data for source buffer */ - addr = (unsigned int)dma_src_addr; - for (i = 0; i < TEST_DMA_SIZE; i += 4) { - *(volatile unsigned int *)addr = addr; - addr += 4; - } - dma_cache_wback((unsigned long)dma_src_addr, TEST_DMA_SIZE); - - /* Init target buffer */ - memset((void *)dma_dst_addr, 0, TEST_DMA_SIZE); - dma_cache_wback((unsigned long)dma_dst_addr, TEST_DMA_SIZE); - - /* Init DMA module */ - printk("Starting DMA\n"); - REG_DMAC_DMACR = 0; - REG_DMAC_DCCSR(dma_chan) = 0; - REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; - REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; - REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; - REG_DMAC_DTCR(dma_chan) = 512; - REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TIE; - REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; - REG_DMAC_DMACR = DMAC_DMACR_DMAE; /* global DMA enable bit */ - - printk("DMA started. IMR=%08x\n", REG_INTC_IMR); -} - -void dma_desc_test(void) -{ - unsigned int next, addr, i; - static jz_dma_desc *desc; - - printk("dma_desc_test\n"); - - /* Request DMA channel and setup irq handler */ - dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", jz4740_dma_irq, - SA_INTERRUPT, NULL); - if (dma_chan < 0) { - printk("Setup irq failed\n"); - return; - } - - printk("Requested DMA channel = %d\n", dma_chan); - - /* Allocate DMA buffers */ - dma_src_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ - dma_dst_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ - - dma_src_phys_addr = CPHYSADDR(dma_src_addr); - dma_dst_phys_addr = CPHYSADDR(dma_dst_addr); - - printk("Buffer addresses: 0x%08x 0x%08x 0x%08x 0x%08x\n", - dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr); - - /* Prepare data for source buffer */ - addr = (unsigned int)dma_src_addr; - for (i = 0; i < TEST_DMA_SIZE; i += 4) { - *(volatile unsigned int *)addr = addr; - addr += 4; - } - dma_cache_wback((unsigned long)dma_src_addr, TEST_DMA_SIZE); - - /* Init target buffer */ - memset((void *)dma_dst_addr, 0, TEST_DMA_SIZE); - dma_cache_wback((unsigned long)dma_dst_addr, TEST_DMA_SIZE); - - /* Allocate DMA descriptors */ - dma_desc = (jz_dma_desc *)__get_free_pages(GFP_KERNEL, 0); - dma_desc_phys_addr = CPHYSADDR((unsigned long)dma_desc); - - printk("DMA descriptor address: 0x%08x 0x%08x\n", (u32)dma_desc, dma_desc_phys_addr); - - /* Setup DMA descriptors */ - desc = dma_desc; - next = (dma_desc_phys_addr + (sizeof(jz_dma_desc))) >> 4; - - desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK; - desc->dsadr = dma_src_phys_addr; /* DMA source address */ - desc->dtadr = dma_dst_phys_addr; /* DMA target address */ - desc->ddadr = (next << 24) + 128; /* size: 128*32 bytes = 4096 bytes */ - - desc++; - next = (dma_desc_phys_addr + 2*(sizeof(jz_dma_desc))) >> 4; - - desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK; - desc->dsadr = dma_src_phys_addr + 4096; /* DMA source address */ - desc->dtadr = dma_dst_phys_addr + 4096; /* DMA target address */ - desc->ddadr = (next << 24) + 256; /* size: 256*16 bytes = 4096 bytes */ - - desc++; - next = (dma_desc_phys_addr + 3*(sizeof(jz_dma_desc))) >> 4; - - desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK; - desc->dsadr = dma_src_phys_addr + 8192; /* DMA source address */ - desc->dtadr = dma_dst_phys_addr + 8192; /* DMA target address */ - desc->ddadr = (next << 24) + 256; /* size: 256*16 bytes = 4096 bytes */ + jz4740_dma_complete_callback_t complete_cb; - desc++; - next = (dma_desc_phys_addr + 4*(sizeof(jz_dma_desc))) >> 4; + unsigned used:1; +}; - desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE; - desc->dsadr = dma_src_phys_addr + 12*1024; /* DMA source address */ - desc->dtadr = dma_dst_phys_addr + 12*1024; /* DMA target address */ - desc->ddadr = (next << 24) + 1024; /* size: 1024*4 bytes = 4096 bytes */ +#define JZ4740_DMA_CHANNEL(_id) { .id = _id } - dma_cache_wback((unsigned long)dma_desc, 4*(sizeof(jz_dma_desc))); +struct jz4740_dma_chan jz4740_dma_channels[] = { + JZ4740_DMA_CHANNEL(0), + JZ4740_DMA_CHANNEL(1), + JZ4740_DMA_CHANNEL(2), + JZ4740_DMA_CHANNEL(3), + JZ4740_DMA_CHANNEL(4), + JZ4740_DMA_CHANNEL(5), +}; - /* Setup DMA descriptor address */ - REG_DMAC_DDA(dma_chan) = dma_desc_phys_addr; +struct jz4740_dma_chan* jz4740_dma_request(void *dev, const char *name) +{ + unsigned int i; + struct jz4740_dma_chan *dma = NULL; - /* Setup request source */ - REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; + spin_lock(&jz4740_dma_lock); - /* Setup DMA channel control/status register */ - REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; /* descriptor transfer, clear status, start channel */ + for (i = 0; i < ARRAY_SIZE(jz4740_dma_channels); ++i) { + if (!jz4740_dma_channels[i].used) { + dma = &jz4740_dma_channels[i]; + dma->used = 1; + break; + } + } - /* Enable DMA */ - REG_DMAC_DMACR = DMAC_DMACR_DMAE; + spin_unlock(&jz4740_dma_lock); - /* DMA doorbell set -- start DMA now ... */ - REG_DMAC_DMADBSR = 1 << dma_chan; + if (!dma) + return NULL; - printk("DMA started. IMR=%08x\n", REG_INTC_IMR); + dma->dev = dev; + dma->name = name; + + return dma; } -#endif - -static void jz_dma_irq_demux_handler(unsigned int irq, struct irq_desc *desc) +void jz4740_dma_configure(struct jz4740_dma_chan *dma, + const struct jz4740_dma_config *config) { - int i; - uint32_t pending; + uint32_t cmd; + uint32_t ctrl; - pending = jz_dma_read(JZ_REG_DMA_IRQ); + switch(config->transfer_size) { + case JZ4740_DMA_TRANSFER_SIZE_2BYTE: + dma->transfer_shift = 1; + break; + case JZ4740_DMA_TRANSFER_SIZE_4BYTE: + dma->transfer_shift = 2; + break; + case JZ4740_DMA_TRANSFER_SIZE_16BYTE: + dma->transfer_shift = 4; + break; + case JZ4740_DMA_TRANSFER_SIZE_32BYTE: + dma->transfer_shift = 5; + break; + default: + dma->transfer_shift = 0; + break; + } + + cmd = config->flags << JZ_DMA_CMD_FLAGS_OFFSET; + cmd |= config->src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET; + cmd |= config->dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET; + cmd |= config->transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET; + cmd |= config->mode << JZ_DMA_CMD_MODE_OFFSET; + cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE; + + ctrl = JZ_DMA_STATUS_CTRL_NO_DESC; + ctrl |= JZ_DMA_STATUS_CTRL_HALT; + + jz4740_dma_write(JZ_REG_DMA_CMD(dma->id), cmd); + jz4740_dma_write(JZ_REG_DMA_STATUS_CTRL(dma->id), ctrl); + jz4740_dma_write(JZ_REG_DMA_REQ_TYPE(dma->id), config->request_type); +} +EXPORT_SYMBOL_GPL(jz4740_dma_configure); + +void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src) +{ + jz4740_dma_write(JZ_REG_DMA_SRC_ADDR(dma->id), src); +} +EXPORT_SYMBOL_GPL(jz4740_dma_set_src_addr); + +void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst) +{ + jz4740_dma_write(JZ_REG_DMA_DST_ADDR(dma->id), dst); +} +EXPORT_SYMBOL_GPL(jz4740_dma_set_dst_addr); + +void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count) +{ + count >>= dma->transfer_shift; + jz4740_dma_write(JZ_REG_DMA_TRANSFER_COUNT(dma->id), count); +} +EXPORT_SYMBOL_GPL(jz4740_dma_set_transfer_count); + +void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma, + jz4740_dma_complete_callback_t cb) +{ + dma->complete_cb = cb; +} +EXPORT_SYMBOL_GPL(jz4740_dma_set_complete_cb); + +void jz4740_dma_free(struct jz4740_dma_chan *dma) +{ + dma->dev = NULL; + dma->complete_cb = NULL; + dma->used = 0; +} +EXPORT_SYMBOL_GPL(jz4740_dma_free); + +void jz4740_dma_enable(struct jz4740_dma_chan *dma) +{ + jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), + JZ_DMA_STATUS_CTRL_ENABLE, + JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_HALT); + + jz4740_dma_write_mask(JZ_REG_DMA_CTRL, + JZ_DMA_CTRL_ENABLE, + JZ_DMA_CTRL_ENABLE | JZ_DMA_CTRL_HALT); +} +EXPORT_SYMBOL_GPL(jz4740_dma_enable); + +void jz4740_dma_disable(struct jz4740_dma_chan *dma) +{ + jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0, + JZ_DMA_STATUS_CTRL_ENABLE); +} +EXPORT_SYMBOL_GPL(jz4740_dma_disable); + +uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma) +{ + uint32_t residue; + residue = jz4740_dma_read(JZ_REG_DMA_TRANSFER_COUNT(dma->id)); + return residue << dma->transfer_shift; +} +EXPORT_SYMBOL_GPL(jz4740_dma_get_residue); + +static void jz4740_dma_chan_irq(struct jz4740_dma_chan *dma) +{ + uint32_t status; + + status = jz4740_dma_read(JZ_REG_DMA_STATUS_CTRL(dma->id)); + + jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0, + JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_TRANSFER_DONE); + + if (dma->complete_cb) + dma->complete_cb(dma, 0, dma->dev); +} + +static irqreturn_t jz4740_dma_irq(int irq, void *dev_id) +{ + uint32_t irq_status; + unsigned int i; + + irq_status = readl(jz4740_dma_base + JZ_REG_DMA_IRQ); for (i = 0; i < 6; ++i) { - if (pending & BIT(i)) - generic_handle_irq(JZ_IRQ_DMA(i)); + if (irq_status & (1 << i)) + jz4740_dma_chan_irq(&jz4740_dma_channels[i]); } + + return IRQ_HANDLED; } -#define IRQ_TO_DMA(irq) ((irq) - JZ_IRQ_DMA(0)) - -static void dma_irq_unmask(unsigned int irq) -{ - unsigned long flags; - uint32_t mask; - unsigned int chan; - - chan = IRQ_TO_DMA(irq); - - spin_lock_irqsave(&jz_dma_lock, flags); - - mask = jz_dma_read(JZ_REG_DMA_CMD(chan)); - mask |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE; - jz_dma_write(JZ_REG_DMA_CMD(chan), mask); - - spin_unlock_irqrestore(&jz_dma_lock, flags); -} - -static void dma_irq_mask(unsigned int irq) -{ - unsigned long flags; - uint32_t mask; - unsigned int chan; - - chan = IRQ_TO_DMA(irq); - - spin_lock_irqsave(&jz_dma_lock, flags); - - mask = jz_dma_read(JZ_REG_DMA_CMD(chan)); - mask &= ~JZ_DMA_CMD_TRANSFER_IRQ_ENABLE; - jz_dma_write(JZ_REG_DMA_CMD(chan), mask); - - spin_unlock_irqrestore(&jz_dma_lock, flags); -} - -static void dma_irq_ack(unsigned int irq) -{ - unsigned long flags; - uint32_t pending; - - spin_lock_irqsave(&jz_dma_lock, flags); - - pending = jz_dma_read(JZ_REG_DMA_IRQ); - pending &= ~BIT(irq); - jz_dma_write(JZ_REG_DMA_IRQ, pending); - - spin_unlock_irqrestore(&jz_dma_lock, flags); -} - -static void dma_irq_end(unsigned int irq) -{ - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { - dma_irq_unmask(irq); - } -} - -static struct irq_chip dma_irq_type = { - .name = "DMA", - .unmask = dma_irq_unmask, - .mask = dma_irq_mask, - .ack = dma_irq_ack, - .end = dma_irq_end, +static struct jz4740_dma_config dma_test_config = { + .src_width = JZ4740_DMA_WIDTH_32BIT, + .dst_width = JZ4740_DMA_WIDTH_32BIT, + .transfer_size = JZ4740_DMA_TRANSFER_SIZE_4BYTE, + .request_type = JZ4740_DMA_TYPE_AUTO_REQUEST, + .flags = JZ4740_DMA_SRC_AUTOINC | JZ4740_DMA_DST_AUTOINC, + .mode = JZ4740_DMA_MODE_BLOCK, }; -static int jz_dma_init(void) +#if 0 +static void jz4740_dma_test(void) { + uint32_t *buf1, *buf2; + dma_addr_t addr1, addr2; + struct jz4740_dma_chan *dma = jz4740_dma_request(NULL, "dma test"); int i; - jz_dma_base = ioremap(CPHYSADDR(DMAC_BASE), 0x400); + printk("STARTING DMA TEST\n"); - if (!jz_dma_base) - return -EBUSY; + buf1 = dma_alloc_coherent(NULL, + 0x1000, + &addr1, GFP_KERNEL); + buf2 = dma_alloc_coherent(NULL, + 0x1000, + &addr2, GFP_KERNEL); - spin_lock_init(&jz_dma_lock); + for (i = 0; i < 0x400; ++i) + buf1[i] = i; - set_irq_chained_handler(JZ_IRQ_DMAC, jz_dma_irq_demux_handler); - for (i = 0; i < NUM_DMA; i++) { - dma_irq_mask(JZ_IRQ_DMA(i)); - set_irq_chip_and_handler(JZ_IRQ_DMA(i), &dma_irq_type, handle_level_irq); + jz4740_dma_configure(dma, &dma_test_config); + jz4740_dma_set_src_addr(dma, addr1); + jz4740_dma_set_dst_addr(dma, addr2); + jz4740_dma_set_transfer_count(dma, 0x1000); + + jz4740_dma_enable(dma); + mdelay(2000); + + for (i = 0; i < 0x400; ++i) { + if (buf2[i] != i) + printk("OH MY GOD: %x %x\n", i, buf2[i]); } - return 0; + printk("DMA TEST DONE\n"); } -arch_initcall(jz_dma_init); +#endif -//EXPORT_SYMBOL_NOVERS(jz_dma_table); -EXPORT_SYMBOL(jz_dma_table); -EXPORT_SYMBOL(jz_request_dma); -EXPORT_SYMBOL(jz_free_dma); -EXPORT_SYMBOL(jz_set_dma_src_width); -EXPORT_SYMBOL(jz_set_dma_dest_width); -EXPORT_SYMBOL(jz_set_dma_block_size); -EXPORT_SYMBOL(jz_set_dma_mode); -EXPORT_SYMBOL(set_dma_mode); -EXPORT_SYMBOL(jz_set_alsa_dma); -EXPORT_SYMBOL(set_dma_addr); -EXPORT_SYMBOL(set_dma_count); -EXPORT_SYMBOL(get_dma_residue); -EXPORT_SYMBOL(enable_dma); -EXPORT_SYMBOL(disable_dma); -EXPORT_SYMBOL(dump_jz_dma_channel); +static int jz4740_dma_init(void) +{ + unsigned int ret; + + jz4740_dma_base = ioremap(CPHYSADDR(DMAC_BASE), 0x400); + + if (!jz4740_dma_base) + return -EBUSY; + + spin_lock_init(&jz4740_dma_lock); + + ret = request_irq(JZ_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL); + + if (ret) + printk("JZ4740 DMA: Failed to request irq: %d\n", ret); + + return ret; +} +arch_initcall(jz4740_dma_init); diff --git a/target/linux/xburst/files-2.6.31/sound/soc/jz4740/jz4740-i2s.c b/target/linux/xburst/files-2.6.31/sound/soc/jz4740/jz4740-i2s.c index 978f7e604..773230bd3 100644 --- a/target/linux/xburst/files-2.6.31/sound/soc/jz4740/jz4740-i2s.c +++ b/target/linux/xburst/files-2.6.31/sound/soc/jz4740/jz4740-i2s.c @@ -24,28 +24,6 @@ #include "jz4740-pcm.h" #include "jz4740-i2s.h" -static struct jz4740_dma_client jz4740_dma_client_out = { - .name = "I2S PCM Stereo out" -}; - -static struct jz4740_dma_client jz4740_dma_client_in = { - .name = "I2S PCM Stereo in" -}; - -static struct jz4740_pcm_dma_params jz4740_i2s_pcm_stereo_out = { - .client = &jz4740_dma_client_out, - .channel = DMA_ID_AIC_TX, - .dma_addr = AIC_DR, - .dma_size = 2, -}; - -static struct jz4740_pcm_dma_params jz4740_i2s_pcm_stereo_in = { - .client = &jz4740_dma_client_in, - .channel = DMA_ID_AIC_RX, - .dma_addr = AIC_DR, - .dma_size = 2, -}; - static int jz4740_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { /*struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -136,14 +114,6 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, jz4740_snd_rx_ctrl(0); jz4740_snd_rx_ctrl(0); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - cpu_dai->dma_data = &jz4740_i2s_pcm_stereo_out; - /*if (channels == 1) - __aic_enable_mono2stereo(); - else - __aic_disable_mono2stereo();*/ - } else - cpu_dai->dma_data = &jz4740_i2s_pcm_stereo_in; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: diff --git a/target/linux/xburst/files-2.6.31/sound/soc/jz4740/jz4740-pcm.c b/target/linux/xburst/files-2.6.31/sound/soc/jz4740/jz4740-pcm.c index 6ed4b291b..bd754891c 100644 --- a/target/linux/xburst/files-2.6.31/sound/soc/jz4740/jz4740-pcm.c +++ b/target/linux/xburst/files-2.6.31/sound/soc/jz4740/jz4740-pcm.c @@ -1,15 +1,21 @@ /* + * Copyright (C) 2010, Lars-Peter Clausen + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ +#include #include -#include #include -#include -#include +#include #include #include @@ -17,398 +23,173 @@ #include #include -#include +#include +#include #include "jz4740-pcm.h" -static long sum_bytes = 0; -static int first_transfer = 0; -static int printk_flag = 0; -static int tran_bit = 0; -#ifdef CONFIG_SND_OSSEMUL -static int hw_params_cnt = 0; -#endif - -static struct jz4740_dma_client jz4740_dma_client_out = { - .name = "I2S PCM Stereo out" -}; - -static struct jz4740_dma_client jz4740_dma_client_in = { - .name = "I2S PCM Stereo in" -}; - -static struct jz4740_pcm_dma_params jz4740_i2s_pcm_stereo_out = { - .client = &jz4740_dma_client_out, - .channel = DMA_ID_AIC_TX, - .dma_addr = AIC_DR, - .dma_size = 2, -}; - -static struct jz4740_pcm_dma_params jz4740_i2s_pcm_stereo_in = { - .client = &jz4740_dma_client_in, - .channel = DMA_ID_AIC_RX, - .dma_addr = AIC_DR, - .dma_size = 2, -}; - - -struct jz4740_dma_buf_aic { - struct jz4740_dma_buf_aic *next; - int size; /* buffer size in bytes */ - dma_addr_t data; /* start of DMA data */ - dma_addr_t ptr; /* where the DMA got to [1] */ - void *id; /* client's id */ -}; - struct jz4740_runtime_data { - spinlock_t lock; - int state; - int aic_dma_flag; /* start dma transfer or not */ - 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; - struct jz4740_pcm_dma_params *params; + struct jz4740_dma_chan *dma; +}; - dma_addr_t user_cur_addr; /* user current write buffer start address */ - unsigned int user_cur_len; /* user current write buffer length */ - - /* buffer list and information */ - struct jz4740_dma_buf_aic *curr; /* current dma buffer */ - struct jz4740_dma_buf_aic *next; /* next buffer to load */ - struct jz4740_dma_buf_aic *end; /* end of queue */ +static struct jz4740_dma_config jz4740_pcm_dma_playback_config = { + .src_width = JZ4740_DMA_WIDTH_16BIT, + .dst_width = JZ4740_DMA_WIDTH_32BIT, + .transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE, + .request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT, + .flags = JZ4740_DMA_SRC_AUTOINC, + .mode = JZ4740_DMA_MODE_SINGLE, +}; +static struct jz4740_dma_config jz4740_pcm_dma_capture_config = { + .src_width = JZ4740_DMA_WIDTH_32BIT, + .dst_width = JZ4740_DMA_WIDTH_16BIT, + .transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE, + .request_type = JZ4740_DMA_TYPE_AIC_RECEIVE, + .flags = JZ4740_DMA_DST_AUTOINC, + .mode = JZ4740_DMA_MODE_SINGLE, }; /* identify hardware playback capabilities */ static const struct snd_pcm_hardware jz4740_pcm_hardware = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S8, - .rates = SNDRV_PCM_RATE_8000_48000/*0x3fe*/, - .rate_min = 8000, - .rate_min = 48000, - .channels_min = 2, + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8, + .rates = SNDRV_PCM_RATE_8000_48000, + .channels_min = 1, .channels_max = 2, - .buffer_bytes_max = 128 * 1024,//16 * 1024 - .period_bytes_min = PAGE_SIZE, - .period_bytes_max = PAGE_SIZE * 2, + .period_bytes_min = 32, + .period_bytes_max = 2 * PAGE_SIZE, .periods_min = 2, - .periods_max = 128,//16, - .fifo_size = 32, + .periods_max = 128, + .buffer_bytes_max = 128 * 2 * PAGE_SIZE, + .fifo_size = 32, }; -/* jz4740__dma_buf_enqueue - * - * queue an given buffer for dma transfer. - * - * data the physical address of the buffer data - * size the size of the buffer in bytes - * -*/ -static int jz4740_dma_buf_enqueue(struct jz4740_runtime_data *prtd, dma_addr_t data, int size) -{ - struct jz4740_dma_buf_aic *aic_buf; +static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, int stream) +{ + unsigned int count; - aic_buf = kzalloc(sizeof(struct jz4740_dma_buf_aic), GFP_KERNEL); - if (aic_buf == NULL) { - printk("aic buffer allocate failed,no memory!\n"); - return -ENOMEM; - } - aic_buf->next = NULL; - aic_buf->data = aic_buf->ptr = data; - aic_buf->size = size; - if( prtd->curr == NULL) { - prtd->curr = aic_buf; - prtd->end = aic_buf; - prtd->next = NULL; + if (prtd->dma_pos + prtd->dma_period > prtd->dma_end) + count = prtd->dma_end - prtd->dma_pos; + else + count = prtd->dma_period; + + jz4740_dma_disable(prtd->dma); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos); + jz4740_dma_set_dst_addr(prtd->dma, CPHYSADDR(AIC_DR)); } else { - if (prtd->end == NULL) - printk("prtd->end is NULL\n"); - prtd->end->next = aic_buf; - prtd->end = aic_buf; + jz4740_dma_set_src_addr(prtd->dma, CPHYSADDR(AIC_DR)); + jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos); } - /* if necessary, update the next buffer field */ - if (prtd->next == NULL) - prtd->next = aic_buf; + jz4740_dma_set_transfer_count(prtd->dma, count); - return 0; + jz4740_dma_enable(prtd->dma); + + prtd->dma_pos += prtd->dma_period; + if (prtd->dma_pos >= prtd->dma_end) + prtd->dma_pos = prtd->dma_start; } - -void audio_start_dma(struct jz4740_runtime_data *prtd, int mode) -{ - unsigned long flags; - struct jz4740_dma_buf_aic *aic_buf; - int channel; - - switch (mode) { - case DMA_MODE_WRITE: - /* free cur aic_buf */ - if (first_transfer == 1) { - first_transfer = 0; - } else { - aic_buf = prtd->curr; - if (aic_buf != NULL) { - prtd->curr = aic_buf->next; - prtd->next = aic_buf->next; - aic_buf->next = NULL; - kfree(aic_buf); - aic_buf = NULL; - } - } - - aic_buf = prtd->next; - channel = prtd->params->channel; - if (aic_buf) { - disable_dma(channel); - jz_set_alsa_dma(channel, mode, tran_bit); - set_dma_addr(channel, aic_buf->data); - set_dma_count(channel, aic_buf->size); - enable_dma(channel); - prtd->aic_dma_flag |= AIC_START_DMA; - } else { - printk("next buffer is NULL for playback\n"); - prtd->aic_dma_flag &= ~AIC_START_DMA; - return; - } - break; - case DMA_MODE_READ: - /* free cur aic_buf */ - if (first_transfer == 1) { - first_transfer = 0; - } else { - aic_buf = prtd->curr; - if (aic_buf != NULL) { - prtd->curr = aic_buf->next; - prtd->next = aic_buf->next; - aic_buf->next = NULL; - kfree(aic_buf); - aic_buf = NULL; - } - } - - aic_buf = prtd->next; - channel = prtd->params->channel; - - if (aic_buf) { - disable_dma(channel); - jz_set_alsa_dma(channel, mode, tran_bit); - set_dma_addr(channel, aic_buf->data); - set_dma_count(channel, aic_buf->size); - enable_dma(channel); - prtd->aic_dma_flag |= AIC_START_DMA; - } else { - printk("next buffer is NULL for capture\n"); - prtd->aic_dma_flag &= ~AIC_START_DMA; - return; - } - break; - } - /* dump_jz_dma_channel(channel); */ -} - -/* - * place a dma buffer onto the queue for the dma system to handle. -*/ -static void jz4740_pcm_enqueue(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct jz4740_runtime_data *prtd = runtime->private_data; - /*struct snd_dma_buffer *buf = &substream->dma_buffer;*/ - dma_addr_t pos = prtd->dma_pos; - int ret; - - while (prtd->dma_loaded < prtd->dma_limit) { - unsigned long len = prtd->dma_period; - - if ((pos + len) > prtd->dma_end) { - len = prtd->dma_end - pos; - } - ret = jz4740_dma_buf_enqueue(prtd, pos, len); - if (ret == 0) { - prtd->dma_loaded++; - pos += prtd->dma_period; - if (pos >= prtd->dma_end) - pos = prtd->dma_start; - } else - break; - } - - prtd->dma_pos = pos; -} - -/* - * call the function:jz4740_pcm_dma_irq() after DMA has transfered the current buffer - */ -static irqreturn_t jz4740_pcm_dma_irq(int dma_ch, void *dev_id) +static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err, + void *dev_id) { struct snd_pcm_substream *substream = dev_id; struct snd_pcm_runtime *runtime = substream->runtime; struct jz4740_runtime_data *prtd = runtime->private_data; - /*struct jz4740_dma_buf_aic *aic_buf = prtd->curr;*/ - int channel = prtd->params->channel; - unsigned long flags; - disable_dma(channel); - prtd->aic_dma_flag &= ~AIC_START_DMA; - /* must clear TT bit in DCCSR to avoid interrupt again */ - if (__dmac_channel_transmit_end_detected(channel)) { - __dmac_channel_clear_transmit_end(channel); - } - if (__dmac_channel_transmit_halt_detected(channel)) { - __dmac_channel_clear_transmit_halt(channel); - } + snd_pcm_period_elapsed(substream); - if (__dmac_channel_address_error_detected(channel)) { - __dmac_channel_clear_address_error(channel); - } - - if (substream) - snd_pcm_period_elapsed(substream); - - spin_lock(&prtd->lock); - prtd->dma_loaded--; - if (prtd->state & ST_RUNNING) { - jz4740_pcm_enqueue(substream); - } - spin_unlock(&prtd->lock); - - local_irq_save(flags); - if (prtd->state & ST_RUNNING) { - if (prtd->dma_loaded) { - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - audio_start_dma(prtd, DMA_MODE_WRITE); - else - audio_start_dma(prtd, DMA_MODE_READ); - } - } - local_irq_restore(flags); - return IRQ_HANDLED; + jz4740_pcm_start_transfer(prtd, substream->stream); } -/* some parameter about DMA operation */ static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; struct jz4740_runtime_data *prtd = runtime->private_data; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct jz4740_pcm_dma_params *dma = &jz4740_i2s_pcm_stereo_out; - size_t totbytes = params_buffer_bytes(params); - int ret; - - if (!dma) - return 0; + unsigned int size = params_buffer_bytes(params); + struct jz4740_dma_config *dma_config; + enum jz4740_dma_width width; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: - tran_bit = 8; + width = JZ4740_DMA_WIDTH_8BIT; break; case SNDRV_PCM_FORMAT_S16_LE: - tran_bit = 16; + width = JZ4740_DMA_WIDTH_16BIT; + break; + default: + BUG(); break; } - /* prepare DMA */ - prtd->params = dma; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ret = jz_request_dma(DMA_ID_AIC_TX, prtd->params->client->name, - jz4740_pcm_dma_irq, IRQF_DISABLED, substream); - if (ret < 0) - return ret; - prtd->params->channel = ret; + dma_config = &jz4740_pcm_dma_playback_config; + dma_config->src_width = width; + + prtd->dma = jz4740_dma_request(substream, "PCM Playback"); } else { - ret = jz_request_dma(DMA_ID_AIC_RX, prtd->params->client->name, - jz4740_pcm_dma_irq, IRQF_DISABLED, substream); - if (ret < 0) - return ret; - prtd->params->channel = ret; + dma_config = &jz4740_pcm_dma_capture_config; + dma_config->dst_width = width; + + prtd->dma = jz4740_dma_request(substream, "PCM Capture"); } - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - runtime->dma_bytes = totbytes; + if (!prtd->dma) + return -EBUSY; - spin_lock_irq(&prtd->lock); - prtd->dma_loaded = 0; - prtd->aic_dma_flag = 0; - prtd->dma_limit = runtime->hw.periods_min; - prtd->dma_period = params_period_bytes(params); + jz4740_dma_configure(prtd->dma, dma_config); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + jz4740_dma_set_dst_addr(prtd->dma, CPHYSADDR(AIC_DR)); + else + jz4740_dma_set_src_addr(prtd->dma, CPHYSADDR(AIC_DR)); + + jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = size; + + prtd->dma_period = params_period_bytes(params); prtd->dma_start = runtime->dma_addr; prtd->dma_pos = prtd->dma_start; - prtd->dma_end = prtd->dma_start + totbytes; - prtd->curr = NULL; - prtd->next = NULL; - prtd->end = NULL; - sum_bytes = 0; - first_transfer = 1; - printk_flag = 0; + prtd->dma_end = prtd->dma_start + size; - __dmac_disable_descriptor(prtd->params->channel); - __dmac_channel_disable_irq(prtd->params->channel); - spin_unlock_irq(&prtd->lock); - return ret; + return 0; } static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream) { struct jz4740_runtime_data *prtd = substream->runtime->private_data; - + snd_pcm_set_runtime_buffer(substream, NULL); - if (prtd->params) { - jz_free_dma(prtd->params->channel); - prtd->params = NULL; - } + if (prtd->dma) + jz4740_dma_free(prtd->dma); return 0; } -/* set some dma para for playback/capture */ -static int jz4740_dma_ctrl(int channel) -{ - - disable_dma(channel); - - /* must clear TT bit in DCCSR to avoid interrupt again */ - if (__dmac_channel_transmit_end_detected(channel)) { - __dmac_channel_clear_transmit_end(channel); - } - if (__dmac_channel_transmit_halt_detected(channel)) { - __dmac_channel_clear_transmit_halt(channel); - } - - if (__dmac_channel_address_error_detected(channel)) { - __dmac_channel_clear_address_error(channel); - } - - return 0; - -} - static int jz4740_pcm_prepare(struct snd_pcm_substream *substream) { struct jz4740_runtime_data *prtd = substream->runtime->private_data; int ret = 0; - - /* return if this is a bufferless transfer e.g */ - if (!prtd->params) + + if (!prtd->dma) return 0; - /* flush the DMA channel and DMA channel bit check */ - jz4740_dma_ctrl(prtd->params->channel); - prtd->dma_loaded = 0; prtd->dma_pos = prtd->dma_start; - - /* enqueue dma buffers */ - jz4740_pcm_enqueue(substream); return ret; - } static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd) @@ -417,31 +198,18 @@ static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct jz4740_runtime_data *prtd = runtime->private_data; int ret = 0; - + switch (cmd) { case SNDRV_PCM_TRIGGER_START: - prtd->state |= ST_RUNNING; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - audio_start_dma(prtd, DMA_MODE_WRITE); - } else { - audio_start_dma(prtd, DMA_MODE_READ); - } - + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + jz4740_pcm_start_transfer(prtd, substream->stream); break; - case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - prtd->state &= ~ST_RUNNING; + jz4740_dma_disable(prtd->dma); break; - - case SNDRV_PCM_TRIGGER_RESUME: - printk(" RESUME \n"); - break; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - printk(" RESTART \n"); - break; - default: ret = -EINVAL; } @@ -449,88 +217,39 @@ static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } -static snd_pcm_uframes_t -jz4740_pcm_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct jz4740_runtime_data *prtd = runtime->private_data; - struct jz4740_dma_buf_aic *aic_buf = prtd->curr; - long count,res; + unsigned long count, pos; + snd_pcm_uframes_t offset; + struct jz4740_dma_chan *dma = prtd->dma; - dma_addr_t ptr; - snd_pcm_uframes_t x; - int channel = prtd->params->channel; - - spin_lock(&prtd->lock); -#if 1 + count = jz4740_dma_get_residue(dma); + if (prtd->dma_pos == prtd->dma_start) + pos = prtd->dma_end - prtd->dma_start - count; + else + pos = prtd->dma_pos - prtd->dma_start - count; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - count = get_dma_residue(channel); - count = aic_buf->size - count; - ptr = aic_buf->data + count; - res = ptr - prtd->dma_start; - } else { - count = get_dma_residue(channel); - count = aic_buf->size - count; - ptr = aic_buf->data + count; - res = ptr - prtd->dma_start; - } + offset = bytes_to_frames(runtime, pos); + if (offset >= runtime->buffer_size) + offset = 0; -# else - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if ((prtd->aic_dma_flag & AIC_START_DMA) == 0) { - count = get_dma_residue(channel); - count = aic_buf->size - count; - ptr = aic_buf->data + count; - REG_DMAC_DSAR(channel) = ptr; - res = ptr - prtd->dma_start; - } else { - ptr = REG_DMAC_DSAR(channel); - if (ptr == 0x0) - printk("\ndma address is 00000000 in running!\n"); - res = ptr - prtd->dma_start; - } - } else { - if ((prtd->aic_dma_flag & AIC_START_DMA) == 0) { - count = get_dma_residue(channel); - count = aic_buf->size - count; - ptr = aic_buf->data + count; - REG_DMAC_DTAR(channel) = ptr; - res = ptr - prtd->dma_start; - } else { - ptr = REG_DMAC_DTAR(channel); - if (ptr == 0x0) - printk("\ndma address is 00000000 in running!\n"); - res = ptr - prtd->dma_start; - } - } -#endif - spin_unlock(&prtd->lock); - x = bytes_to_frames(runtime, res); - if (x == runtime->buffer_size) - x = 0; - - return x; + return offset; } static int jz4740_pcm_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct jz4740_runtime_data *prtd; - -#ifdef CONFIG_SND_OSSEMUL - hw_params_cnt = 0; -#endif + snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware); prtd = kzalloc(sizeof(struct jz4740_runtime_data), GFP_KERNEL); + if (prtd == NULL) return -ENOMEM; - spin_lock_init(&prtd->lock); - runtime->private_data = prtd; - REG_AIC_I2SCR = 0x10; return 0; } @@ -538,30 +257,8 @@ static int jz4740_pcm_close(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct jz4740_runtime_data *prtd = runtime->private_data; - struct jz4740_dma_buf_aic *aic_buf = NULL; - -#ifdef CONFIG_SND_OSSEMUL - hw_params_cnt = 0; -#endif - if (prtd) - aic_buf = prtd->curr; - - while (aic_buf != NULL) { - prtd->curr = aic_buf->next; - prtd->next = aic_buf->next; - aic_buf->next = NULL; - kfree(aic_buf); - aic_buf = NULL; - aic_buf = prtd->curr; - } - - if (prtd) { - prtd->curr = NULL; - prtd->next = NULL; - prtd->end = NULL; - kfree(prtd); - } + kfree(prtd); return 0; } @@ -569,8 +266,6 @@ static int jz4740_pcm_close(struct snd_pcm_substream *substream) static int jz4740_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) { - struct snd_pcm_runtime *runtime = substream->runtime; - return remap_pfn_range(vma, vma->vm_start, substream->dma_buffer.addr >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot); @@ -593,17 +288,18 @@ static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; size_t size = jz4740_pcm_hardware.buffer_bytes_max; + buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; buf->private_data = NULL; - /*buf->area = dma_alloc_coherent(pcm->card->dev, size, - &buf->addr, GFP_KERNEL);*/ buf->area = dma_alloc_noncoherent(pcm->card->dev, size, &buf->addr, GFP_KERNEL); if (!buf->area) return -ENOMEM; + buf->bytes = size; + return 0; } @@ -635,10 +331,9 @@ int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, { int ret = 0; - printk("pcm new\n"); - if (!card->dev->dma_mask) card->dev->dma_mask = &jz4740_pcm_dmamask; + if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = DMA_BIT_MASK(32); @@ -646,27 +341,26 @@ int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, ret = jz4740_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); if (ret) - goto out; + goto err; } if (dai->capture.channels_min) { ret = jz4740_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret) - goto out; + goto err; } - out: +err: return ret; } struct snd_soc_platform jz4740_soc_platform = { - .name = "jz4740-audio", + .name = "jz4740-pcm", .pcm_ops = &jz4740_pcm_ops, .pcm_new = jz4740_pcm_new, .pcm_free = jz4740_pcm_free_dma_buffers, }; - EXPORT_SYMBOL_GPL(jz4740_soc_platform); static int __init jz4740_soc_platform_init(void) @@ -681,6 +375,6 @@ static void __exit jz4740_soc_platform_exit(void) } module_exit(jz4740_soc_platform_exit); -MODULE_AUTHOR("Richard"); -MODULE_DESCRIPTION("Ingenic Jz4740 PCM DMA module"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver"); MODULE_LICENSE("GPL"); diff --git a/target/linux/xburst/files-2.6.31/sound/soc/jz4740/jz4740-pcm.h b/target/linux/xburst/files-2.6.31/sound/soc/jz4740/jz4740-pcm.h index 54cadabcb..7f5f6f094 100644 --- a/target/linux/xburst/files-2.6.31/sound/soc/jz4740/jz4740-pcm.h +++ b/target/linux/xburst/files-2.6.31/sound/soc/jz4740/jz4740-pcm.h @@ -8,25 +8,6 @@ #ifndef _JZ4740_PCM_H #define _JZ4740_PCM_H -#include - -#define ST_RUNNING (1<<0) -#define ST_OPENED (1<<1) - -#define AIC_START_DMA (1<<0) -#define AIC_END_DMA (1<<1) - -struct jz4740_dma_client { - char *name; -}; - -struct jz4740_pcm_dma_params { - struct jz4740_dma_client *client; /* stream identifier */ - int channel; /* Channel ID */ - dma_addr_t dma_addr; - int dma_size; /* Size of the DMA transfer */ -}; - /* platform data */ extern struct snd_soc_platform jz4740_soc_platform;