mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-11-29 07:44:03 +02:00
823 lines
24 KiB
C
823 lines
24 KiB
C
|
/*
|
||
|
* linux/arch/mips/jz4750d/dma.c
|
||
|
*
|
||
|
* Support functions for the JZ4750D 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.
|
||
|
*
|
||
|
* Copyright (C) 2006 - 2008 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.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/errno.h>
|
||
|
#include <linux/sched.h>
|
||
|
#include <linux/spinlock.h>
|
||
|
#include <linux/string.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/soundcard.h>
|
||
|
|
||
|
#include <asm/system.h>
|
||
|
#include <asm/addrspace.h>
|
||
|
#include <asm/jzsoc.h>
|
||
|
|
||
|
/*
|
||
|
* 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:DMA_ID_BCH_ENC,}, /* DMAC0 channel 0, reserved for BCH */
|
||
|
{dev_id:-1,}, /* DMAC0 channel 1 */
|
||
|
{dev_id:-1,}, /* DMAC0 channel 2 */
|
||
|
{dev_id:-1,}, /* DMAC0 channel 3 */
|
||
|
{dev_id:-1,}, /* DMAC1 channel 0 */
|
||
|
{dev_id:-1,}, /* DMAC1 channel 1 */
|
||
|
{dev_id:-1,}, /* DMAC1 channel 2 */
|
||
|
{dev_id:-1,}, /* DMAC1 channel 3 */
|
||
|
};
|
||
|
|
||
|
// 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] = {
|
||
|
{0, DMA_AUTOINIT, DMAC_DRSR_RS_EXT}, /* External request with DREQn */
|
||
|
{0x18000000, DMA_AUTOINIT, DMAC_DRSR_RS_NAND}, /* NAND request */
|
||
|
{CPHYSADDR(BCH_DR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_BCH_ENC},
|
||
|
{CPHYSADDR(BCH_DR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_BCH_DEC},
|
||
|
{0, DMA_AUTOINIT, DMAC_DRSR_RS_AUTO},
|
||
|
// {CPHYSADDR(TSSI_FIFO), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_TSSIIN},
|
||
|
{CPHYSADDR(UART3_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART3OUT},
|
||
|
{CPHYSADDR(UART3_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART3IN},
|
||
|
{CPHYSADDR(UART2_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART2OUT},
|
||
|
{CPHYSADDR(UART2_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART2IN},
|
||
|
{CPHYSADDR(UART1_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART1OUT},
|
||
|
{CPHYSADDR(UART1_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART1IN},
|
||
|
{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(0)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSI0OUT},
|
||
|
{CPHYSADDR(SSI_DR(0)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSI0IN},
|
||
|
{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(0)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSC0OUT},
|
||
|
{CPHYSADDR(MSC_RXFIFO(0)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSC0IN},
|
||
|
{0, DMA_AUTOINIT, DMAC_DRSR_RS_TCU},
|
||
|
{SADC_TSDAT, DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SADC},/* Touch Screen Data Register */
|
||
|
{CPHYSADDR(MSC_TXFIFO(1)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSC1OUT}, /* SSC1 TX */
|
||
|
{CPHYSADDR(MSC_RXFIFO(1)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSC1IN}, /* SSC1 RX */
|
||
|
{CPHYSADDR(SSI_DR(1)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSI1OUT},
|
||
|
{CPHYSADDR(SSI_DR(1)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSI1IN},
|
||
|
{CPHYSADDR(PCM_DP), DMA_16BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_PMOUT},
|
||
|
{CPHYSADDR(PCM_DP), DMA_16BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_PMIN},
|
||
|
{},
|
||
|
};
|
||
|
|
||
|
|
||
|
int jz_dma_read_proc(char *buf, char **start, off_t fpos,
|
||
|
int length, int *eof, void *data)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
void dump_jz_dma_channel(unsigned int dmanr)
|
||
|
{
|
||
|
struct jz_dma_chan *chan;
|
||
|
|
||
|
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(chan->io/HALF_DMA_NUM));
|
||
|
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(chan->io/HALF_DMA_NUM));
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 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 = IRQ_DMA_0 + 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;
|
||
|
|
||
|
if (i < HALF_DMA_NUM)
|
||
|
REG_DMAC_DMACKE(0) = 1 << i;
|
||
|
else
|
||
|
REG_DMAC_DMACKE(1) = 1 << (i - HALF_DMA_NUM);
|
||
|
|
||
|
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");
|
||
|
}
|
||
|
REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK;
|
||
|
REG_DMAC_DRSR(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) {
|
||
|
REG_DMAC_DSAR(chan->io) = chan->fifo_addr;
|
||
|
REG_DMAC_DTAR(chan->io) = phyaddr;
|
||
|
} else if (mode == DMA_MODE_WRITE) {
|
||
|
REG_DMAC_DSAR(chan->io) = phyaddr;
|
||
|
REG_DMAC_DTAR(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;
|
||
|
REG_DMAC_DTCR(chan->io) = bytecnt / dma_ds[ds]; // transfer count
|
||
|
}
|
||
|
|
||
|
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 = REG_DMAC_DTCR(chan->io);
|
||
|
count = count * dma_ds[ds];
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
void jz_set_oss_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 AFMT_U8:
|
||
|
/* burst mode : 32BIT */
|
||
|
break;
|
||
|
case AFMT_S16_LE:
|
||
|
/* burst mode : 16BYTE */
|
||
|
if (mode == DMA_MODE_READ) {
|
||
|
chan->mode = DMA_AIC_32_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_32_16BYTE_TX_CMD | 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("oss_dma_burst_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n");
|
||
|
|
||
|
REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK;
|
||
|
REG_DMAC_DRSR(chan->io) = chan->source;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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");
|
||
|
|
||
|
REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK;
|
||
|
REG_DMAC_DRSR(chan->io) = chan->source;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//#define JZ4750D_DMAC_TEST_ENABLE
|
||
|
#undef JZ4750D_DMAC_TEST_ENABLE
|
||
|
|
||
|
#ifdef JZ4750D_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 irqreturn_t jz4750d_dma_irq(int irq, void *dev_id)
|
||
|
{
|
||
|
printk("jz4750d_dma_irq %d\n", irq);
|
||
|
|
||
|
|
||
|
if (__dmac_channel_transmit_halt_detected(dma_chan)) {
|
||
|
printk("DMA HALT\n");
|
||
|
REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */
|
||
|
__dmac_channel_clear_transmit_halt(dma_chan);
|
||
|
}
|
||
|
|
||
|
if (__dmac_channel_address_error_detected(dma_chan)) {
|
||
|
printk("DMA ADDR ERROR\n");
|
||
|
REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */
|
||
|
REG_DMAC_DSAR(dma_chan) = 0; /* clear source address register */
|
||
|
REG_DMAC_DTAR(dma_chan) = 0; /* clear target address register */
|
||
|
__dmac_channel_clear_address_error(dma_chan);
|
||
|
}
|
||
|
|
||
|
if (__dmac_channel_descriptor_invalid_detected(dma_chan)) {
|
||
|
REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */
|
||
|
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)) {
|
||
|
REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
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", jz4750d_dma_irq,
|
||
|
IRQF_DISABLED, 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(dma_chan/HALF_DMA_NUM) = 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(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE; /* global DMA enable bit */
|
||
|
|
||
|
printk("DMA started. IMR=%08x\n", REG_INTC_IMR);
|
||
|
|
||
|
/* wait a long time, ensure transfer end */
|
||
|
printk("wait 3s...\n");
|
||
|
mdelay(3000); /* wait 3s */
|
||
|
|
||
|
REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */
|
||
|
/* 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_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", jz4750d_dma_irq,
|
||
|
IRQF_DISABLED, 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_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 */
|
||
|
|
||
|
desc++;
|
||
|
next = (dma_desc_phys_addr + 4*(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_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 */
|
||
|
|
||
|
dma_cache_wback((unsigned long)dma_desc, 4*(sizeof(jz_dma_desc)));
|
||
|
|
||
|
/* Setup DMA descriptor address */
|
||
|
REG_DMAC_DDA(dma_chan) = dma_desc_phys_addr;
|
||
|
|
||
|
/* Setup request source */
|
||
|
REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO;
|
||
|
|
||
|
/* Setup DMA channel control/status register */
|
||
|
REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; /* descriptor transfer, clear status, start channel */
|
||
|
|
||
|
/* Enable DMA */
|
||
|
REG_DMAC_DMACR(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE;
|
||
|
|
||
|
/* DMA doorbell set -- start DMA now ... */
|
||
|
REG_DMAC_DMADBSR(dma_chan/HALF_DMA_NUM) = 1 << dma_chan;
|
||
|
|
||
|
printk("DMA started. IMR=%08x\n", REG_INTC_IMR);
|
||
|
/* wait a long time, ensure transfer end */
|
||
|
printk("wait 3s...\n");
|
||
|
mdelay(3000); /* wait 3s */
|
||
|
|
||
|
REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */
|
||
|
/* 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);
|
||
|
}
|
||
|
|
||
|
#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_oss_dma);
|
||
|
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);
|