summaryrefslogtreecommitdiff
path: root/sound/soc/fsl/fsl_dma_workaround.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/fsl/fsl_dma_workaround.c')
-rw-r--r--sound/soc/fsl/fsl_dma_workaround.c254
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);
+}