/* * fsl_dma_workaround.c - DMA workaround bits * * Copyright (C) 2017 NXP * * Author: Daniel Baluta * * 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 #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); }