From: 倪振宇 Date: Mon, 31 May 2010 10:15:57 +0000 (+0000) Subject: add dma irq mode X-Git-Tag: firefly_0821_release~11437 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=5dc070d0fdd17c64f3e819cc0367ba116d685c85;p=firefly-linux-kernel-4.4.55.git add dma irq mode --- diff --git a/arch/arm/include/asm/dma.h b/arch/arm/include/asm/dma.h index 008b67fb4297..fcaa8e2b6b98 100644 --- a/arch/arm/include/asm/dma.h +++ b/arch/arm/include/asm/dma.h @@ -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 */ diff --git a/arch/arm/include/asm/mach/dma.h b/arch/arm/include/asm/mach/dma.h index c46ca4c5aa91..22ab6f207291 100644 --- a/arch/arm/include/asm/mach/dma.h +++ b/arch/arm/include/asm/mach/dma.h @@ -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; }; diff --git a/arch/arm/kernel/dma.c b/arch/arm/kernel/dma.c index 478498105a8b..90c627011f82 100644 --- a/arch/arm/kernel/dma.c +++ b/arch/arm/kernel/dma.c @@ -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) diff --git a/arch/arm/mach-rk2818/dma.c b/arch/arm/mach-rk2818/dma.c index fb0b3ab014a9..d9cdde936a0b 100644 --- a/arch/arm/mach-rk2818/dma.c +++ b/arch/arm/mach-rk2818/dma.c @@ -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); diff --git a/arch/arm/mach-rk2818/include/mach/dma.h b/arch/arm/mach-rk2818/include/mach/dma.h index 0ac2ea8c6f06..f0cdfb2666c1 100644 --- a/arch/arm/mach-rk2818/include/mach/dma.h +++ b/arch/arm/mach-rk2818/include/mach/dma.h @@ -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_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); diff --git a/sound/soc/rk2818/rk2818_pcm.c b/sound/soc/rk2818/rk2818_pcm.c index bcdd924d1fbf..5d2519cdf030 100755 --- a/sound/soc/rk2818/rk2818_pcm.c +++ b/sound/soc/rk2818/rk2818_pcm.c @@ -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;