add dma irq mode

This commit is contained in:
倪振宇
2010-05-31 10:15:57 +00:00
committed by 黄涛
parent eec7849793
commit 5dc070d0fd
7 changed files with 171 additions and 77 deletions

View File

@@ -51,6 +51,13 @@
#define DMA_AUTOINIT 0x10
/*
* The DMA irq modes
*/
#define DMA_IRQ_DELAY_MODE 0x00 /*performan in software irq handleer*/
#define DMA_IRQ_RIGHTNOW_MODE 0x01 /*performan in dwdma irq handleer*/
/* Request a DMA channel
*
* Some architectures may need to do allocate an interrupt
@@ -96,10 +103,8 @@ extern void set_dma_sg(unsigned int chan, struct scatterlist *sg, int nr_sg);
* DMA address immediately, but defer it to the enable_dma().
*/
extern void __set_dma_addr(unsigned int chan, void *addr);
#define set_dma_addr(chan, addr) \
__set_dma_addr(chan, addr)
//__set_dma_addr(chan, bus_to_virt(addr))
#define set_dma_addr(chan, addr) __set_dma_addr(chan, addr)//__set_dma_addr(chan, bus_to_virt(addr))
/* Set the DMA byte count for this channel
*
@@ -132,7 +137,12 @@ extern int get_dma_residue(unsigned int chan);
#endif
/* Set dam irq callback that perform when dma transfer has completed
*/
extern void set_dma_handler (unsigned int chan, void (*irq_handler) (int, void *), void *data);
extern void set_dma_handler (unsigned int chan, void (*irq_handler) (int, void *), void *data, unsigned int irq_mode);
/*
* get dma transfer position
*/
extern void get_dma_position(unsigned int chan, dma_addr_t *src_pos, dma_addr_t *dst_pos);
extern int dma_getposition(unsigned int channel, dma_addr_t *src, dma_addr_t *dst);
#endif /* __ASM_ARM_DMA_H */

View File

@@ -19,6 +19,7 @@ struct dma_ops {
int (*free)(unsigned int, dma_t *); /* optional */
int (*enable)(unsigned int, dma_t *); /* mandatory */
int (*disable)(unsigned int, dma_t *); /* mandatory */
void (*position)(unsigned int, dma_t *);
#if 0
int (*residue)(unsigned int, dma_t *); /* optional */
int (*setspeed)(unsigned int, dma_t *, int); /* optional */
@@ -42,8 +43,12 @@ struct dma_struct {
unsigned int lock; /* Device is allocated */
const char *device_id; /* Device name */
int (*irqHandle)(int irq, void *dev_id); /*irq callback*/
void (*irqHandle)(int irq, void *dev_id); /*irq callback*/
void *data;
unsigned int irq_mode;
dma_addr_t src_pos;
dma_addr_t dst_pos;
const struct dma_ops *d_ops;
};

View File

@@ -126,7 +126,7 @@ EXPORT_SYMBOL(free_dma);
*
* Copy irq handler to the structure
*/
void set_dma_handler (unsigned int chan, void (*irq_handler) (int, void *), void *data)
void set_dma_handler (unsigned int chan, void (*irq_handler) (int, void *), void *data, unsigned int irq_mode)
{
dma_t *dma = dma_channel(chan);
@@ -136,6 +136,7 @@ EXPORT_SYMBOL(free_dma);
dma->irqHandle = irq_handler;
dma->data = data;
dma->irq_mode = irq_mode;
}
EXPORT_SYMBOL(set_dma_handler);
@@ -264,6 +265,26 @@ int dma_channel_active(unsigned int chan)
dma_t *dma = dma_channel(chan);
return dma->active;
}
/*
* get dma transfer position
*/
void get_dma_position(unsigned int chan, dma_addr_t *src_pos, dma_addr_t *dst_pos)
{
dma_t *dma = dma_channel(chan);
if (dma->d_ops->position)
dma->d_ops->position(chan, dma);
if (src_pos) {
*src_pos = dma->src_pos;
}
if (dst_pos) {
*dst_pos = dma->dst_pos;
}
}
EXPORT_SYMBOL(dma_channel_active);
#if 0
void set_dma_page(unsigned int chan, char pagenr)

View File

@@ -130,7 +130,7 @@ const static struct rk28_dma_dev rk28_dev_info[] = {
* rk28_dma_ctl_for_write - set dma control register for writing mode
*
*/
static inline unsigned int rk28_dma_ctl_for_write(unsigned int dma_ch, struct rk28_dma_dev *dev_info, dma_t *dma_t)
static inline unsigned int rk28_dma_ctl_for_write(unsigned int dma_ch, const struct rk28_dma_dev *dev_info, dma_t *dma_t)
{
#ifdef test_dma
unsigned int dev_mode = B_CTLL_MEM2MEM_DMAC;
@@ -155,7 +155,7 @@ static inline unsigned int rk28_dma_ctl_for_write(unsigned int dma_ch, struct rk
* rk28_dma_ctl_for_read - set dma control register for reading mode
*
*/
static inline unsigned int rk28_dma_ctl_for_read(unsigned int dma_ch, struct rk28_dma_dev *dev_info, dma_t *dma_t)
static inline unsigned int rk28_dma_ctl_for_read(unsigned int dma_ch, const struct rk28_dma_dev *dev_info, dma_t *dma_t)
{
#ifdef test_dma
unsigned int dev_mode = B_CTLL_MEM2MEM_DMAC;
@@ -246,7 +246,7 @@ static void rk28_dma_write_to_sg(unsigned int dma_ch, dma_t *dma_t)
struct rk28_dma_llp * rk28llp_phy;
struct rk28_dma_llp rk28dma_reg;
struct scatterlist *sg;
unsigned int wid_off;
unsigned int wid_off, bk_count, bk_res, sgcount_tmp, bk_length;
rk28dma = &rk2818_dma[dma_ch];
@@ -258,16 +258,36 @@ static void rk28_dma_write_to_sg(unsigned int dma_ch, dma_t *dma_t)
rk28llp_vir = rk28dma->dma_llp_vir;
rk28llp_phy = (struct rk28_dma_llp *)rk28dma->dma_llp_phy;
sg = dma_t->sg;
#if 1
bk_length = RK28_DMA_CH0A1_MAX_LEN << wid_off;
for (sgcount_tmp = 0; sgcount_tmp < dma_t->sgcount; sgcount_tmp++, sg++) {
bk_count = (sg->length >> wid_off) / RK28_DMA_CH0A1_MAX_LEN;
bk_res = (sg->length >> wid_off) % RK28_DMA_CH0A1_MAX_LEN;
for (i = 0; i < bk_count; i++) {
rk28_dma_set_llp(dev_addr_w,
sg->dma_address + i * bk_length,
rk28llp_vir++,
++rk28llp_phy,
ctll_r,
RK28_DMA_CH0A1_MAX_LEN);
}
if (bk_res > 0) {
rk28_dma_set_llp(dev_addr_w,
sg->dma_address + bk_count * bk_length,
rk28llp_vir++,
++rk28llp_phy,
ctll_r,
bk_res);
}
}
#else
for (i = 0; i < dma_t->sgcount; i++, sg++) {
rk28_dma_set_llp(dev_addr_w, sg->dma_address, rk28llp_vir++, ++rk28llp_phy, ctll_r, (sg->length >> wid_off));
}
}
#endif
rk28_dma_end_of_llp(rk28llp_vir - 1);
rk28dma_reg.sar = 0;//dev_addr_w;
rk28dma_reg.dar = 0;//dma_t->sg->dma_address;
rk28dma_reg.ctll = ctll_r;
rk28dma_reg.llp = (struct rk28_dma_llp *)rk28dma->dma_llp_phy;
rk28dma_reg.size = 0;//dma_t->sg->length;
} else { /*single transfer*/
if (dma_t->buf.length > RK28_DMA_CH2_MAX_LEN) {
rk28dma->length = RK28_DMA_CH2_MAX_LEN;
@@ -276,12 +296,12 @@ static void rk28_dma_write_to_sg(unsigned int dma_ch, dma_t *dma_t)
rk28dma->length = dma_t->buf.length;
rk28dma->residue = 0;
}
rk28dma_reg.sar = dev_addr_w;
rk28dma_reg.dar = dma_t->buf.dma_address;
rk28dma_reg.ctll = ctll_r;
rk28dma_reg.llp = NULL;
rk28dma_reg.size = rk28dma->length;
}
rk28dma_reg.sar = dev_addr_w;
rk28dma_reg.dar = dma_t->buf.dma_address;
rk28dma_reg.ctll = ctll_r;
rk28dma_reg.size = rk28dma->length;
rk28_dma_set_reg(dma_ch, &rk28dma_reg, rk28dma->dev_info->hd_if_r);
/*
printk(KERN_INFO "dma_write_to_sg: ch = %d, sar = 0x%x, dar = 0x%x, ctll = 0x%x, llp = 0x%x, size = %d, \n",
@@ -316,10 +336,10 @@ static void rk28_dma_read_from_sg(unsigned int dma_ch, dma_t *dma_t)
struct rk28_dma_llp * rk28llp_phy;
struct rk28_dma_llp rk28dma_reg;
struct scatterlist *sg;
unsigned int wid_off;
unsigned int wid_off, bk_count, bk_res, sgcount_tmp, bk_length;
rk28dma = &rk2818_dma[dma_ch];
/*setup linked list table end*/
dev_addr_r = rk28dma->dev_info->dev_addr_r;
ctll_w = rk28_dma_ctl_for_write(dma_ch, rk28dma->dev_info, dma_t);
@@ -329,16 +349,36 @@ static void rk28_dma_read_from_sg(unsigned int dma_ch, dma_t *dma_t)
rk28llp_vir = rk28dma->dma_llp_vir;
rk28llp_phy = (struct rk28_dma_llp *)rk28dma->dma_llp_phy;
sg = dma_t->sg;
#if 1
bk_length = RK28_DMA_CH0A1_MAX_LEN << wid_off;
for (sgcount_tmp = 0; sgcount_tmp < dma_t->sgcount; sgcount_tmp++, sg++) {
bk_count = (sg->length >> wid_off) / RK28_DMA_CH0A1_MAX_LEN;
bk_res = (sg->length >> wid_off) % RK28_DMA_CH0A1_MAX_LEN;
for (i = 0; i < bk_count; i++) {
rk28_dma_set_llp(sg->dma_address + i * bk_length,
dev_addr_r,
rk28llp_vir++,
++rk28llp_phy,
ctll_w,
RK28_DMA_CH0A1_MAX_LEN);
}
if (bk_res > 0) {
rk28_dma_set_llp(sg->dma_address + bk_count * bk_length,
dev_addr_r,
rk28llp_vir++,
++rk28llp_phy,
ctll_w,
bk_res);
}
}
#else
for (i = 0; i < dma_t->sgcount; i++, sg++) {
rk28_dma_set_llp(sg->dma_address, dev_addr_r, rk28llp_vir++, ++rk28llp_phy, ctll_w, (sg->length >> wid_off));
}
#endif
rk28_dma_end_of_llp(rk28llp_vir - 1);
rk28dma_reg.sar = 0;//dma_t->sg->dma_address;
rk28dma_reg.dar = 0;//dev_addr_r;
rk28dma_reg.ctll = ctll_w;
rk28dma_reg.llp = (struct rk28_dma_llp *)rk28dma->dma_llp_phy;
rk28dma_reg.size = 0;//dma_t->sg->length;
} else { /*single transfer*/
if (dma_t->buf.length > RK28_DMA_CH2_MAX_LEN) {
rk28dma->length = RK28_DMA_CH2_MAX_LEN;
@@ -347,12 +387,12 @@ static void rk28_dma_read_from_sg(unsigned int dma_ch, dma_t *dma_t)
rk28dma->length = dma_t->buf.length;
rk28dma->residue = 0;
}
rk28dma_reg.sar = dma_t->buf.dma_address;
rk28dma_reg.dar = dev_addr_r;
rk28dma_reg.ctll = ctll_w;
rk28dma_reg.llp = NULL;
rk28dma_reg.size = rk28dma->length;
}
rk28dma_reg.sar = dma_t->buf.dma_address;
rk28dma_reg.dar = dev_addr_r;
rk28dma_reg.ctll = ctll_w;
rk28dma_reg.size = rk28dma->length;
rk28_dma_set_reg(dma_ch, &rk28dma_reg, rk28dma->dev_info->hd_if_w);
//printk(KERN_INFO "read_from_sg: ch = %d, sar = 0x%x, dar = 0x%x, ctll = 0x%x, llp = 0x%x, size = %d, \n",
@@ -430,6 +470,8 @@ static int rk28_dma_disable(unsigned int dma_ch, dma_t *dma_t)
while (GET_DWDMA_STATUS(dma_ch))
cpu_relax();
rk28dma->tasklet_flag = 0;
spin_unlock(&rk28dma->lock);
return 0;
@@ -471,13 +513,15 @@ static int rk28_dma_request(unsigned int dma_ch, dma_t *dma_t)
}
} else {
rk28dma->dma_llp_vir = NULL;
rk28dma->dma_llp_phy = NULL;
rk28dma->dma_llp_phy = 0;
}
rk28dma->dev_info = &rk28_dev_info[i];
/* clear interrupt */
CLR_DWDMA_INTR(dma_ch);
UN_MASK_DWDMA_TRF_INTR(dma_ch);
spin_unlock(&rk28dma->lock);
@@ -500,6 +544,8 @@ static int rk28_dma_free(unsigned int dma_ch, dma_t *dma_t)
/* clear interrupt */
CLR_DWDMA_INTR(dma_ch);
MASK_DWDMA_TRF_INTR(dma_ch);
if (dma_ch < RK28_DMA_CH2) {
if (!rk28dma->dma_llp_vir) {
@@ -515,7 +561,7 @@ static int rk28_dma_free(unsigned int dma_ch, dma_t *dma_t)
rk28dma->dma_t.addr = NULL;
rk28dma->dma_t.count = 0;
rk28dma->dma_llp_vir = NULL;
rk28dma->dma_llp_phy = NULL;
rk28dma->dma_llp_phy = 0;
rk28dma->residue = 0;
rk28dma->length = 0;
@@ -580,6 +626,27 @@ static int rk28_dma_next(unsigned int dma_ch)
*
*/
static void rk28_dma_tasklet(unsigned long data)
{
int i;
struct rk2818_dma *rk28dma;
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
rk28dma = &rk2818_dma[i];
if ((rk28dma->tasklet_flag) && (rk28dma->dma_t.irq_mode == DMA_IRQ_DELAY_MODE)) {
rk28dma->dma_t.active = 0;
rk28dma->tasklet_flag = 0;
if (rk28dma->dma_t.irqHandle)
rk28dma->dma_t.irqHandle(i, rk28dma->dma_t.data);
UN_MASK_DWDMA_TRF_INTR(i);
}
}
}
/**
* rk28_dma_irq_handler - irq callback function
*
*/
static irqreturn_t rk28_dma_irq_handler(int irq, void *dev_id)
{
int i, raw_status;
struct rk2818_dma *rk28dma;
@@ -596,58 +663,42 @@ static void rk28_dma_tasklet(unsigned long data)
//printk(KERN_WARNING "dma_irq: don't finish for channel %d\n", i);
continue;
}
/* already complete transfer */
rk28dma->dma_t.active = 0;
if (rk28dma->dma_t.irqHandle) {
void *data = rk28dma->dma_t.data;
rk28dma->dma_t.irqHandle(i, data);
if (rk28dma->dma_t.irq_mode != DMA_IRQ_DELAY_MODE) {
/* already have completed transfer */
rk28dma->dma_t.active = 0;
rk28dma->dma_t.irqHandle(i, rk28dma->dma_t.data);
} else {
MASK_DWDMA_TRF_INTR(i);
rk28dma->tasklet_flag = 1;
tasklet_schedule(&rk2818_dma_tasklet);
//printk(KERN_WARNING "dma_irq: no IRQ handler for DMA channel %d\n", i);
}
} else {
printk(KERN_WARNING "dma_irq: no IRQ handler for DMA channel %d\n", i);
}
/* already have completed transfer */
rk28dma->dma_t.active = 0;
}
}
}
/*
* Re-enable interrupts
*/
UN_MASK_DWDMA_ALL_TRF_INTR;
}
/**
* rk28_dma_irq_handler - irq callback function
*
*/
static irqreturn_t rk28_dma_irq_handler(int irq, void *dev_id)
{
/*
* Just disable the interrupts. We'll turn them back on in the
* softirq handler.
*/
MASK_DWDMA_ALL_TRF_INTR;
tasklet_schedule(&rk2818_dma_tasklet);
//tasklet_schedule(&rk2818_dma_tasklet);
return IRQ_HANDLED;
}
int dma_getposition(unsigned int channel, dma_addr_t *src, dma_addr_t *dst)
static void rk28_dma_position(unsigned int dma_ch, dma_t *dma_t)
{
u32 phy_ch = channel;
if (src != NULL)
*src = read_dma_reg(DWDMA_SAR(phy_ch));
if (dst != NULL)
*dst = read_dma_reg(DWDMA_DAR(phy_ch));
return 0;
dma_t->src_pos = read_dma_reg(DWDMA_SAR(dma_ch));
dma_t->dst_pos = read_dma_reg(DWDMA_DAR(dma_ch));
}
EXPORT_SYMBOL(dma_getposition);
static struct dma_ops rk2818_dma_ops = {
.request = rk28_dma_request,
.free = rk28_dma_free,
.enable = rk28_dma_enable,
.disable = rk28_dma_disable,
.position = rk28_dma_position,
};
/**
@@ -671,9 +722,10 @@ static int __init rk28_dma_init(void)
dma_add(i, &rk2818_dma[i].dma_t);
rk2818_dma[i].dma_llp_vir = NULL;
rk2818_dma[i].dma_llp_phy = NULL;
rk2818_dma[i].dma_llp_phy = 0;
rk2818_dma[i].residue = 0;
rk2818_dma[i].length = 0;
rk2818_dma[i].tasklet_flag = 0;
}
/* clear all interrupts */
@@ -690,7 +742,8 @@ static int __init rk28_dma_init(void)
write_dma_reg(DWDMA_MaskErr, 0x3f00);
/*unmask transfer completion interrupt*/
UN_MASK_DWDMA_ALL_TRF_INTR;
//UN_MASK_DWDMA_ALL_TRF_INTR;
MASK_DWDMA_ALL_TRF_INTR;
ret = request_irq(RK28_DMA_IRQ_NUM, rk28_dma_irq_handler, 0, "DMA", NULL);
if (ret < 0) {
@@ -698,7 +751,7 @@ static int __init rk28_dma_init(void)
return ret;
}
tasklet_init(&rk2818_dma_tasklet, rk28_dma_tasklet, NULL);
tasklet_init(&rk2818_dma_tasklet, rk28_dma_tasklet, 0);
/* enable DMA module */
write_dma_reg(DWDMA_DmaCfgReg, 0x01);

View File

@@ -151,9 +151,11 @@
/* Unmask interrupt */
#define UN_MASK_DWDMA_ALL_TRF_INTR write_dma_reg(DWDMA_MaskTfr, 0x3f3f)//mask_dma_reg(DWDMA_MaskTfr, 0x101<<dma_ch, 0x101<<dma_ch)
#define UN_MASK_DWDMA_TRF_INTR(dma_ch) mask_dma_reg(DWDMA_MaskTfr, 0x101<<dma_ch, 0x101<<dma_ch)
/* Mask interrupt */
#define MASK_DWDMA_ALL_TRF_INTR write_dma_reg(DWDMA_MaskTfr, 0x3f00)//mask_dma_reg(DWDMA_MaskTfr, 0x101<<dma_ch, 0x100<<dma_ch)
#define MASK_DWDMA_TRF_INTR(dma_ch) mask_dma_reg(DWDMA_MaskTfr, 0x101<<dma_ch, 0x100<<dma_ch)
/* Enable channel */
#define ENABLE_DWDMA(dma_ch) mask_dma_reg(DWDMA_ChEnReg, 0x101<<dma_ch, 0x101<<dma_ch)
@@ -197,11 +199,12 @@ struct rk28_dma_dev {
struct rk2818_dma {
dma_t dma_t;
struct rk28_dma_dev *dev_info;/* basic address of sg in memory */
const struct rk28_dma_dev *dev_info;/* basic address of sg in memory */
struct rk28_dma_llp *dma_llp_vir; /* virtual cpu addrress of linked list */
unsigned int dma_llp_phy; /* physical bus address of linked list */
unsigned int length; /* current transfer block */
unsigned int residue; /* residue block of current dma transfer */
unsigned int tasklet_flag;
spinlock_t lock;
};

View File

@@ -402,7 +402,7 @@ static int rk2818_sdmmc_submit_data_dma(struct rk2818_sdmmc_host *host, struct m
set_dma_sg(host->dma_chn, data->sg, data->sg_len);
set_dma_mode(host->dma_chn,
(data->flags & MMC_DATA_READ)? DMA_MODE_READ : DMA_MODE_WRITE);
set_dma_handler(host->dma_chn, rk2818_sdmmc_dma_complete, (void *)host);
set_dma_handler(host->dma_chn, rk2818_sdmmc_dma_complete, (void *)host, DMA_IRQ_DELAY_MODE);
writel(readl(host->regs + SDMMC_CTRL) | SDMMC_CTRL_DMA_ENABLE,
host->regs +SDMMC_CTRL);
enable_dma(host->dma_chn);

View File

@@ -155,7 +155,7 @@ void audio_start_dma(struct snd_pcm_substream *substream, int mode)
disable_dma(prtd->params->channel);
//set_dma_sg(prtd->params->channel, &(sg_buf->sg), 1);
set_dma_mode(prtd->params->channel, DMA_MODE_WRITE);
set_dma_handler(prtd->params->channel, rockchip_pcm_dma_irq, substream);
set_dma_handler(prtd->params->channel, rockchip_pcm_dma_irq, substream, DMA_IRQ_RIGHTNOW_MODE);
__set_dma_addr(prtd->params->channel, (void *)(sg_buf->sg.dma_address));
set_dma_count(prtd->params->channel, sg_buf->sg.length);
enable_dma(prtd->params->channel);
@@ -185,7 +185,7 @@ void audio_start_dma(struct snd_pcm_substream *substream, int mode)
disable_dma(prtd->params->channel);
//set_dma_sg(prtd->params->channel, &(sg_buf->sg), 1);
set_dma_mode(prtd->params->channel, DMA_MODE_READ);
set_dma_handler(prtd->params->channel, rockchip_pcm_dma_irq, substream);
set_dma_handler(prtd->params->channel, rockchip_pcm_dma_irq, substream, DMA_IRQ_RIGHTNOW_MODE);
__set_dma_addr(prtd->params->channel, (void *)(sg_buf->sg.dma_address));
set_dma_count(prtd->params->channel, sg_buf->sg.length);
enable_dma(prtd->params->channel);
@@ -418,7 +418,9 @@ rockchip_pcm_pointer(struct snd_pcm_substream *substream)
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
spin_lock(&prtd->lock);
dma_getposition(prtd->params->channel, &src, &dst);
get_dma_position(prtd->params->channel, &src, &dst);
//dma_getposition(prtd->params->channel, &src, &dst);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
res = dst - prtd->dma_start;