blob: 22ccef7fc08c943c138d786c29906bf4c5c9c063 [file] [log] [blame]
/*
* 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);
}