diff options
Diffstat (limited to 'sound/soc/fsl/fsl_dma_workaround.c')
-rw-r--r-- | sound/soc/fsl/fsl_dma_workaround.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/sound/soc/fsl/fsl_dma_workaround.c b/sound/soc/fsl/fsl_dma_workaround.c new file mode 100644 index 000000000000..22ccef7fc08c --- /dev/null +++ b/sound/soc/fsl/fsl_dma_workaround.c @@ -0,0 +1,254 @@ +/* + * fsl_dma_workaround.c - DMA workaround bits + * + * Copyright (C) 2017 NXP + * + * Author: Daniel Baluta <daniel.baluta@nxp.com> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ +#include <linux/slab.h> +#include "fsl_acm.h" +#include "fsl_dma_workaround.h" + +int gpt_events[2][2] = { + {ESAI0_IPD_ESAI_TX_B, ESAI0_IPD_ESAI_RX_B}, + {SPDIF0_DRQ1_SPDIF_B, SPDIF0_DRQ0_SPDIF_B}, +}; + +/* + * configure_gpt_dma - configures GPT DMA for a given audio interface + * @substream: PCM substream + * @info: DMA workaround specific info + * + */ +int configure_gpt_dma(struct snd_pcm_substream *substream, + struct fsl_dma_workaround_info *info) +{ + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + if (tx) { + writel_relaxed(gpt_events[info->iface][0], + info->base_acm + GPT0_CAPIN1_SEL_OFF); + writel_relaxed(gpt_events[info->iface][0], + info->base_acm + GPT1_CAPIN1_SEL_OFF); + + writel(le32_to_cpu(info->tcd_sw[0].vtcd->saddr), + info->base_edma_gpt1 + EDMA_TCD_SADDR); + writel(le32_to_cpu(info->tcd_sw[0].vtcd->daddr), + info->base_edma_gpt1 + EDMA_TCD_DADDR); + writew(le16_to_cpu(info->tcd_sw[0].vtcd->attr), + info->base_edma_gpt1 + EDMA_TCD_ATTR); + writew(le16_to_cpu(info->tcd_sw[0].vtcd->soff), + info->base_edma_gpt1 + EDMA_TCD_SOFF); + writel(le32_to_cpu(info->tcd_sw[0].vtcd->nbytes), + info->base_edma_gpt1 + EDMA_TCD_NBYTES); + writel(le32_to_cpu(info->tcd_sw[0].vtcd->slast), + info->base_edma_gpt1 + EDMA_TCD_SLAST); + writew(le16_to_cpu(info->tcd_sw[0].vtcd->citer), + info->base_edma_gpt1 + EDMA_TCD_CITER); + writew(le16_to_cpu(info->tcd_sw[0].vtcd->biter), + info->base_edma_gpt1 + EDMA_TCD_BITER); + writew(le16_to_cpu(info->tcd_sw[0].vtcd->doff), + info->base_edma_gpt1 + EDMA_TCD_DOFF); + writel(le32_to_cpu(info->tcd_sw[0].vtcd->dlast_sga), + info->base_edma_gpt1 + EDMA_TCD_DLAST_SGA); + writew(le16_to_cpu(info->tcd_sw[0].vtcd->csr), + info->base_edma_gpt1 + EDMA_TCD_CSR); + + writel(0x0, info->base_edma_gpt1 + EDMA_CH_SBR); + writel(0x1, info->base_edma_gpt1 + EDMA_CH_CSR); + + /* configure this gpt for dma tx */ + writel_relaxed(0x8, info->base_gpt0 + GPT_IR); + writel_relaxed(0x7<<12, info->base_gpt0 + GPT_PR); + writel_relaxed(0x20441, info->base_gpt0 + GPT_CR); + + /* configure this gpt for dma tx request clear */ + writel_relaxed(0x8, info->base_gpt1 + GPT_IR); + writel_relaxed(0x7<<12, info->base_gpt1 + GPT_PR); + writel_relaxed(0x10441, info->base_gpt1 + GPT_CR); + + } else { + writel_relaxed(gpt_events[info->iface][1], + info->base_acm + GPT2_CAPIN1_SEL_OFF); + writel_relaxed(gpt_events[info->iface][1], + info->base_acm + GPT3_CAPIN1_SEL_OFF); + + writel(le32_to_cpu(info->tcd_sw[2].vtcd->saddr), + info->base_edma_gpt3 + EDMA_TCD_SADDR); + writel(le32_to_cpu(info->tcd_sw[2].vtcd->daddr), + info->base_edma_gpt3 + EDMA_TCD_DADDR); + writew(le16_to_cpu(info->tcd_sw[2].vtcd->attr), + info->base_edma_gpt3 + EDMA_TCD_ATTR); + writew(le16_to_cpu(info->tcd_sw[2].vtcd->soff), + info->base_edma_gpt3 + EDMA_TCD_SOFF); + writel(le32_to_cpu(info->tcd_sw[2].vtcd->nbytes), + info->base_edma_gpt3 + EDMA_TCD_NBYTES); + writel(le32_to_cpu(info->tcd_sw[2].vtcd->slast), + info->base_edma_gpt3 + EDMA_TCD_SLAST); + writew(le16_to_cpu(info->tcd_sw[2].vtcd->citer), + info->base_edma_gpt3 + EDMA_TCD_CITER); + writew(le16_to_cpu(info->tcd_sw[2].vtcd->biter), + info->base_edma_gpt3 + EDMA_TCD_BITER); + writew(le16_to_cpu(info->tcd_sw[2].vtcd->doff), + info->base_edma_gpt3 + EDMA_TCD_DOFF); + writel(le32_to_cpu(info->tcd_sw[2].vtcd->dlast_sga), + info->base_edma_gpt3 + EDMA_TCD_DLAST_SGA); + writew(le16_to_cpu(info->tcd_sw[2].vtcd->csr), + info->base_edma_gpt3 + EDMA_TCD_CSR); + + writel(0x0, info->base_edma_gpt3 + EDMA_CH_SBR); + writel(0x1, info->base_edma_gpt3 + EDMA_CH_CSR); + + /* configure this gpt for dma rx */ + writel_relaxed(0x8, info->base_gpt2 + GPT_IR); + writel_relaxed(0x7<<12, info->base_gpt2 + GPT_PR); + writel_relaxed(0x20441, info->base_gpt2 + GPT_CR); + + /* configure this gpt for dma rx request clear*/ + writel_relaxed(0x8, info->base_gpt3 + GPT_IR); + writel_relaxed(0x7<<12, info->base_gpt3 + GPT_PR); + writel_relaxed(0x10441, info->base_gpt3 + GPT_CR); + + } + + return 0; +} + + +int clear_gpt_dma(struct snd_pcm_substream *substream, + struct fsl_dma_workaround_info *info) +{ + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u32 val; + + if (tx) { + val = readl(info->base_edma_gpt1 + EDMA_CH_CSR); + val &= ~0x1; + writel(val, info->base_edma_gpt1 + EDMA_CH_CSR); + + /* disable gpt */ + writel_relaxed(0, info->base_gpt0 + GPT_IR); + writel_relaxed(0, info->base_gpt0 + GPT_PR); + writel_relaxed(0, info->base_gpt0 + GPT_CR); + + writel_relaxed(0, info->base_gpt1 + GPT_IR); + writel_relaxed(0, info->base_gpt1 + GPT_PR); + writel_relaxed(0, info->base_gpt1 + GPT_CR); + + } else { + val = readl(info->base_edma_gpt3 + EDMA_CH_CSR); + val &= ~0x1; + writel(val, info->base_edma_gpt3 + EDMA_CH_CSR); + + /* disable gpt */ + writel_relaxed(0, info->base_gpt2 + GPT_IR); + writel_relaxed(0, info->base_gpt2 + GPT_PR); + writel_relaxed(0, info->base_gpt2 + GPT_CR); + + writel_relaxed(0, info->base_gpt3 + GPT_IR); + writel_relaxed(0, info->base_gpt3 + GPT_PR); + writel_relaxed(0, info->base_gpt3 + GPT_CR); + } + + return 0; +} + +struct fsl_dma_workaround_info * +fsl_dma_workaround_alloc_info(const char *pool_name, struct device *dma_dev, + const char *base_acm_compat, int iface) +{ + struct fsl_dma_workaround_info *info; + int *buffer; + int i; + + info = devm_kzalloc(dma_dev, sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + info->tcd_pool = dma_pool_create(pool_name, dma_dev, + sizeof(struct fsl_edma3_hw_tcd), + 32, 0); + + info->buf.area = dma_alloc_writecombine(dma_dev, 0x1000, + &info->buf.addr, GFP_KERNEL); + + buffer = (int *)info->buf.area; + buffer[0] = 0x8; + + info->tcd_sw[0].vtcd = dma_pool_alloc(info->tcd_pool, + GFP_ATOMIC, &info->tcd_sw[0].ptcd); + info->tcd_sw[1].vtcd = dma_pool_alloc(info->tcd_pool, + GFP_ATOMIC, &info->tcd_sw[1].ptcd); + info->tcd_sw[2].vtcd = dma_pool_alloc(info->tcd_pool, + GFP_ATOMIC, &info->tcd_sw[2].ptcd); + info->tcd_sw[3].vtcd = dma_pool_alloc(info->tcd_pool, + GFP_ATOMIC, &info->tcd_sw[3].ptcd); + + for (i = 0; i < 4; i++) { + info->tcd_sw[i].vtcd->saddr = info->buf.addr; + info->tcd_sw[i].vtcd->attr = 0x0202; + info->tcd_sw[i].vtcd->soff = 0x0; + info->tcd_sw[i].vtcd->nbytes = 0x4; + info->tcd_sw[i].vtcd->slast = 0x0; + info->tcd_sw[i].vtcd->citer = 0x1; + info->tcd_sw[i].vtcd->biter = 0x1; + info->tcd_sw[i].vtcd->doff = 0x0; + info->tcd_sw[i].vtcd->csr = 0x10; + } + + info->tcd_sw[0].vtcd->daddr = GPT5_ADDR + GPT_SR; + info->tcd_sw[1].vtcd->daddr = GPT6_ADDR + GPT_SR; + info->tcd_sw[2].vtcd->daddr = GPT7_ADDR + GPT_SR; + info->tcd_sw[3].vtcd->daddr = GPT8_ADDR + GPT_SR; + + info->tcd_sw[0].vtcd->dlast_sga = + info->tcd_sw[1].ptcd; + info->tcd_sw[1].vtcd->dlast_sga = + info->tcd_sw[0].ptcd; + info->tcd_sw[2].vtcd->dlast_sga = + info->tcd_sw[3].ptcd; + info->tcd_sw[3].vtcd->dlast_sga = + info->tcd_sw[2].ptcd; + + info->base_gpt0 = ioremap(GPT5_ADDR, SZ_64K); + info->base_gpt1 = ioremap(GPT6_ADDR, SZ_64K); + info->base_gpt2 = ioremap(GPT7_ADDR, SZ_64K); + info->base_gpt3 = ioremap(GPT8_ADDR, SZ_64K); + + info->base_edma_gpt1 = ioremap(EDMA_GPT6_ADDR, SZ_64K); + info->base_edma_gpt3 = ioremap(EDMA_GPT8_ADDR, SZ_64K); + + info->base_acm = of_iomap(of_find_compatible_node( + NULL, NULL, base_acm_compat), 0); + + info->iface = iface; + return info; +} + +void fsl_dma_workaround_free_info(struct fsl_dma_workaround_info *info, + struct device *dma_dev) +{ + dma_free_writecombine(dma_dev, + 0x1000, + info->buf.area, + info->buf.addr); + + dma_pool_free(info->tcd_pool, + info->tcd_sw[0].vtcd, + info->tcd_sw[0].ptcd); + dma_pool_free(info->tcd_pool, + info->tcd_sw[1].vtcd, + info->tcd_sw[1].ptcd); + dma_pool_free(info->tcd_pool, + info->tcd_sw[2].vtcd, + info->tcd_sw[2].ptcd); + dma_pool_free(info->tcd_pool, + info->tcd_sw[3].vtcd, + info->tcd_sw[3].ptcd); + + dma_pool_destroy(info->tcd_pool); +} |