18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/sound/soc/pxa/mmp-sspa.c 48c2ecf20Sopenharmony_ci * Base on pxa2xx-ssp.c 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2011 Marvell International Ltd. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <sound/core.h> 198c2ecf20Sopenharmony_ci#include <sound/pcm.h> 208c2ecf20Sopenharmony_ci#include <sound/initval.h> 218c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 228c2ecf20Sopenharmony_ci#include <sound/soc.h> 238c2ecf20Sopenharmony_ci#include <sound/pxa2xx-lib.h> 248c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 258c2ecf20Sopenharmony_ci#include "mmp-sspa.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * SSPA audio private data 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_cistruct sspa_priv { 318c2ecf20Sopenharmony_ci void __iomem *tx_base; 328c2ecf20Sopenharmony_ci void __iomem *rx_base; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data playback_dma_data; 358c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data capture_dma_data; 368c2ecf20Sopenharmony_ci struct clk *clk; 378c2ecf20Sopenharmony_ci struct clk *audio_clk; 388c2ecf20Sopenharmony_ci struct clk *sysclk; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci int running_cnt; 418c2ecf20Sopenharmony_ci u32 sp; 428c2ecf20Sopenharmony_ci u32 ctrl; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void mmp_sspa_tx_enable(struct sspa_priv *sspa) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci unsigned int sspa_sp = sspa->sp; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci sspa_sp &= ~SSPA_SP_MSL; 508c2ecf20Sopenharmony_ci sspa_sp |= SSPA_SP_S_EN; 518c2ecf20Sopenharmony_ci sspa_sp |= SSPA_SP_WEN; 528c2ecf20Sopenharmony_ci __raw_writel(sspa_sp, sspa->tx_base + SSPA_SP); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void mmp_sspa_tx_disable(struct sspa_priv *sspa) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci unsigned int sspa_sp = sspa->sp; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci sspa_sp &= ~SSPA_SP_MSL; 608c2ecf20Sopenharmony_ci sspa_sp &= ~SSPA_SP_S_EN; 618c2ecf20Sopenharmony_ci sspa_sp |= SSPA_SP_WEN; 628c2ecf20Sopenharmony_ci __raw_writel(sspa_sp, sspa->tx_base + SSPA_SP); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void mmp_sspa_rx_enable(struct sspa_priv *sspa) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci unsigned int sspa_sp = sspa->sp; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci sspa_sp |= SSPA_SP_S_EN; 708c2ecf20Sopenharmony_ci sspa_sp |= SSPA_SP_WEN; 718c2ecf20Sopenharmony_ci __raw_writel(sspa_sp, sspa->rx_base + SSPA_SP); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void mmp_sspa_rx_disable(struct sspa_priv *sspa) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci unsigned int sspa_sp = sspa->sp; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci sspa_sp &= ~SSPA_SP_S_EN; 798c2ecf20Sopenharmony_ci sspa_sp |= SSPA_SP_WEN; 808c2ecf20Sopenharmony_ci __raw_writel(sspa_sp, sspa->rx_base + SSPA_SP); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int mmp_sspa_startup(struct snd_pcm_substream *substream, 848c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci clk_prepare_enable(sspa->sysclk); 898c2ecf20Sopenharmony_ci clk_prepare_enable(sspa->clk); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void mmp_sspa_shutdown(struct snd_pcm_substream *substream, 958c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci clk_disable_unprepare(sspa->clk); 1008c2ecf20Sopenharmony_ci clk_disable_unprepare(sspa->sysclk); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* 1048c2ecf20Sopenharmony_ci * Set the SSP ports SYSCLK. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_cistatic int mmp_sspa_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 1078c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, int dir) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai); 1108c2ecf20Sopenharmony_ci struct device *dev = cpu_dai->component->dev; 1118c2ecf20Sopenharmony_ci int ret = 0; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (dev->of_node) 1148c2ecf20Sopenharmony_ci return -ENOTSUPP; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci switch (clk_id) { 1178c2ecf20Sopenharmony_ci case MMP_SSPA_CLK_AUDIO: 1188c2ecf20Sopenharmony_ci ret = clk_set_rate(sspa->audio_clk, freq); 1198c2ecf20Sopenharmony_ci if (ret) 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci case MMP_SSPA_CLK_PLL: 1238c2ecf20Sopenharmony_ci case MMP_SSPA_CLK_VCXO: 1248c2ecf20Sopenharmony_ci /* not support yet */ 1258c2ecf20Sopenharmony_ci return -EINVAL; 1268c2ecf20Sopenharmony_ci default: 1278c2ecf20Sopenharmony_ci return -EINVAL; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int mmp_sspa_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, 1348c2ecf20Sopenharmony_ci int source, unsigned int freq_in, 1358c2ecf20Sopenharmony_ci unsigned int freq_out) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai); 1388c2ecf20Sopenharmony_ci struct device *dev = cpu_dai->component->dev; 1398c2ecf20Sopenharmony_ci int ret = 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (dev->of_node) 1428c2ecf20Sopenharmony_ci return -ENOTSUPP; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci switch (pll_id) { 1458c2ecf20Sopenharmony_ci case MMP_SYSCLK: 1468c2ecf20Sopenharmony_ci ret = clk_set_rate(sspa->sysclk, freq_out); 1478c2ecf20Sopenharmony_ci if (ret) 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci case MMP_SSPA_CLK: 1518c2ecf20Sopenharmony_ci ret = clk_set_rate(sspa->clk, freq_out); 1528c2ecf20Sopenharmony_ci if (ret) 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci default: 1568c2ecf20Sopenharmony_ci return -ENODEV; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* 1638c2ecf20Sopenharmony_ci * Set up the sspa dai format. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_cistatic int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai, 1668c2ecf20Sopenharmony_ci unsigned int fmt) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* reset port settings */ 1718c2ecf20Sopenharmony_ci sspa->sp = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH; 1728c2ecf20Sopenharmony_ci sspa->ctrl = 0; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 1758c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 1768c2ecf20Sopenharmony_ci sspa->sp |= SSPA_SP_MSL; 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci default: 1818c2ecf20Sopenharmony_ci return -EINVAL; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 1858c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 1868c2ecf20Sopenharmony_ci sspa->sp |= SSPA_SP_FSP; 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci default: 1898c2ecf20Sopenharmony_ci return -EINVAL; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 1938c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 1948c2ecf20Sopenharmony_ci sspa->ctrl |= SSPA_CTL_XDATDLY(1); 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci default: 1978c2ecf20Sopenharmony_ci return -EINVAL; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Since we are configuring the timings for the format by hand 2018c2ecf20Sopenharmony_ci * we have to defer some things until hw_params() where we 2028c2ecf20Sopenharmony_ci * know parameters like the sample size. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* 2088c2ecf20Sopenharmony_ci * Set the SSPA audio DMA parameters and sample size. 2098c2ecf20Sopenharmony_ci * Can be called multiple times by oss emulation. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_cistatic int mmp_sspa_hw_params(struct snd_pcm_substream *substream, 2128c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 2138c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 2168c2ecf20Sopenharmony_ci struct device *dev = dai->component->dev; 2178c2ecf20Sopenharmony_ci u32 sspa_ctrl = sspa->ctrl; 2188c2ecf20Sopenharmony_ci int bits; 2198c2ecf20Sopenharmony_ci int bitval; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci switch (params_format(params)) { 2228c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S8: 2238c2ecf20Sopenharmony_ci bits = 8; 2248c2ecf20Sopenharmony_ci bitval = SSPA_CTL_8_BITS; 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 2278c2ecf20Sopenharmony_ci bits = 16; 2288c2ecf20Sopenharmony_ci bitval = SSPA_CTL_16_BITS; 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_3LE: 2318c2ecf20Sopenharmony_ci bits = 24; 2328c2ecf20Sopenharmony_ci bitval = SSPA_CTL_24_BITS; 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 2358c2ecf20Sopenharmony_ci bits = 32; 2368c2ecf20Sopenharmony_ci bitval = SSPA_CTL_32_BITS; 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci default: 2398c2ecf20Sopenharmony_ci return -EINVAL; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (dev->of_node || params_channels(params) == 2) 2438c2ecf20Sopenharmony_ci sspa_ctrl |= SSPA_CTL_XPH; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK; 2468c2ecf20Sopenharmony_ci sspa_ctrl |= SSPA_CTL_XWDLEN1(bitval); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK; 2498c2ecf20Sopenharmony_ci sspa_ctrl |= SSPA_CTL_XSSZ1(bitval); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci sspa_ctrl &= ~SSPA_CTL_XSSZ2_MASK; 2528c2ecf20Sopenharmony_ci sspa_ctrl |= SSPA_CTL_XSSZ2(bitval); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci sspa->sp &= ~SSPA_SP_FWID_MASK; 2558c2ecf20Sopenharmony_ci sspa->sp |= SSPA_SP_FWID(bits - 1); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci sspa->sp &= ~SSPA_TXSP_FPER_MASK; 2588c2ecf20Sopenharmony_ci sspa->sp |= SSPA_TXSP_FPER(bits * 2 - 1); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (dev->of_node) { 2618c2ecf20Sopenharmony_ci clk_set_rate(sspa->clk, params_rate(params) * 2628c2ecf20Sopenharmony_ci params_channels(params) * bits); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 2668c2ecf20Sopenharmony_ci __raw_writel(sspa_ctrl, sspa->tx_base + SSPA_CTL); 2678c2ecf20Sopenharmony_ci __raw_writel(0x1, sspa->tx_base + SSPA_FIFO_UL); 2688c2ecf20Sopenharmony_ci } else { 2698c2ecf20Sopenharmony_ci __raw_writel(sspa_ctrl, sspa->rx_base + SSPA_CTL); 2708c2ecf20Sopenharmony_ci __raw_writel(0x0, sspa->rx_base + SSPA_FIFO_UL); 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd, 2778c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 2808c2ecf20Sopenharmony_ci int ret = 0; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci switch (cmd) { 2838c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2848c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 2858c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2868c2ecf20Sopenharmony_ci /* 2878c2ecf20Sopenharmony_ci * whatever playback or capture, must enable rx. 2888c2ecf20Sopenharmony_ci * this is a hw issue, so need check if rx has been 2898c2ecf20Sopenharmony_ci * enabled or not; if has been enabled by another 2908c2ecf20Sopenharmony_ci * stream, do not enable again. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci if (!sspa->running_cnt) 2938c2ecf20Sopenharmony_ci mmp_sspa_rx_enable(sspa); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 2968c2ecf20Sopenharmony_ci mmp_sspa_tx_enable(sspa); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci sspa->running_cnt++; 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3028c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 3038c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3048c2ecf20Sopenharmony_ci sspa->running_cnt--; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 3078c2ecf20Sopenharmony_ci mmp_sspa_tx_disable(sspa); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* have no capture stream, disable rx port */ 3108c2ecf20Sopenharmony_ci if (!sspa->running_cnt) 3118c2ecf20Sopenharmony_ci mmp_sspa_rx_disable(sspa); 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci default: 3158c2ecf20Sopenharmony_ci ret = -EINVAL; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return ret; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int mmp_sspa_probe(struct snd_soc_dai *dai) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct sspa_priv *sspa = dev_get_drvdata(dai->dev); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(dai, 3268c2ecf20Sopenharmony_ci &sspa->playback_dma_data, 3278c2ecf20Sopenharmony_ci &sspa->capture_dma_data); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci snd_soc_dai_set_drvdata(dai, sspa); 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci#define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000 3348c2ecf20Sopenharmony_ci#define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 3358c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | \ 3368c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | \ 3378c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE) 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops mmp_sspa_dai_ops = { 3408c2ecf20Sopenharmony_ci .startup = mmp_sspa_startup, 3418c2ecf20Sopenharmony_ci .shutdown = mmp_sspa_shutdown, 3428c2ecf20Sopenharmony_ci .trigger = mmp_sspa_trigger, 3438c2ecf20Sopenharmony_ci .hw_params = mmp_sspa_hw_params, 3448c2ecf20Sopenharmony_ci .set_sysclk = mmp_sspa_set_dai_sysclk, 3458c2ecf20Sopenharmony_ci .set_pll = mmp_sspa_set_dai_pll, 3468c2ecf20Sopenharmony_ci .set_fmt = mmp_sspa_set_dai_fmt, 3478c2ecf20Sopenharmony_ci}; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver mmp_sspa_dai = { 3508c2ecf20Sopenharmony_ci .probe = mmp_sspa_probe, 3518c2ecf20Sopenharmony_ci .playback = { 3528c2ecf20Sopenharmony_ci .channels_min = 1, 3538c2ecf20Sopenharmony_ci .channels_max = 128, 3548c2ecf20Sopenharmony_ci .rates = MMP_SSPA_RATES, 3558c2ecf20Sopenharmony_ci .formats = MMP_SSPA_FORMATS, 3568c2ecf20Sopenharmony_ci }, 3578c2ecf20Sopenharmony_ci .capture = { 3588c2ecf20Sopenharmony_ci .channels_min = 1, 3598c2ecf20Sopenharmony_ci .channels_max = 2, 3608c2ecf20Sopenharmony_ci .rates = MMP_SSPA_RATES, 3618c2ecf20Sopenharmony_ci .formats = MMP_SSPA_FORMATS, 3628c2ecf20Sopenharmony_ci }, 3638c2ecf20Sopenharmony_ci .ops = &mmp_sspa_dai_ops, 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ 3678c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | \ 3688c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | \ 3698c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | \ 3708c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | \ 3718c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware mmp_pcm_hardware[] = { 3748c2ecf20Sopenharmony_ci { 3758c2ecf20Sopenharmony_ci .info = MMP_PCM_INFO, 3768c2ecf20Sopenharmony_ci .period_bytes_min = 1024, 3778c2ecf20Sopenharmony_ci .period_bytes_max = 2048, 3788c2ecf20Sopenharmony_ci .periods_min = 2, 3798c2ecf20Sopenharmony_ci .periods_max = 32, 3808c2ecf20Sopenharmony_ci .buffer_bytes_max = 4096, 3818c2ecf20Sopenharmony_ci .fifo_size = 32, 3828c2ecf20Sopenharmony_ci }, 3838c2ecf20Sopenharmony_ci { 3848c2ecf20Sopenharmony_ci .info = MMP_PCM_INFO, 3858c2ecf20Sopenharmony_ci .period_bytes_min = 1024, 3868c2ecf20Sopenharmony_ci .period_bytes_max = 2048, 3878c2ecf20Sopenharmony_ci .periods_min = 2, 3888c2ecf20Sopenharmony_ci .periods_max = 32, 3898c2ecf20Sopenharmony_ci .buffer_bytes_max = 4096, 3908c2ecf20Sopenharmony_ci .fifo_size = 32, 3918c2ecf20Sopenharmony_ci }, 3928c2ecf20Sopenharmony_ci}; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic const struct snd_dmaengine_pcm_config mmp_pcm_config = { 3958c2ecf20Sopenharmony_ci .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 3968c2ecf20Sopenharmony_ci .pcm_hardware = mmp_pcm_hardware, 3978c2ecf20Sopenharmony_ci .prealloc_buffer_size = 4096, 3988c2ecf20Sopenharmony_ci}; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int mmp_pcm_mmap(struct snd_soc_component *component, 4018c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 4028c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; 4058c2ecf20Sopenharmony_ci vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 4068c2ecf20Sopenharmony_ci return remap_pfn_range(vma, vma->vm_start, 4078c2ecf20Sopenharmony_ci substream->dma_buffer.addr >> PAGE_SHIFT, 4088c2ecf20Sopenharmony_ci vma->vm_end - vma->vm_start, vma->vm_page_prot); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int mmp_sspa_open(struct snd_soc_component *component, 4128c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct sspa_priv *sspa = snd_soc_component_get_drvdata(component); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci pm_runtime_get_sync(component->dev); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* we can only change the settings if the port is not in use */ 4198c2ecf20Sopenharmony_ci if ((__raw_readl(sspa->tx_base + SSPA_SP) & SSPA_SP_S_EN) || 4208c2ecf20Sopenharmony_ci (__raw_readl(sspa->rx_base + SSPA_SP) & SSPA_SP_S_EN)) { 4218c2ecf20Sopenharmony_ci dev_err(component->dev, 4228c2ecf20Sopenharmony_ci "can't change hardware dai format: stream is in use\n"); 4238c2ecf20Sopenharmony_ci return -EBUSY; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci __raw_writel(sspa->sp, sspa->tx_base + SSPA_SP); 4278c2ecf20Sopenharmony_ci __raw_writel(sspa->sp, sspa->rx_base + SSPA_SP); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci sspa->sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH); 4308c2ecf20Sopenharmony_ci __raw_writel(sspa->sp, sspa->tx_base + SSPA_SP); 4318c2ecf20Sopenharmony_ci __raw_writel(sspa->sp, sspa->rx_base + SSPA_SP); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* 4348c2ecf20Sopenharmony_ci * FIXME: hw issue, for the tx serial port, 4358c2ecf20Sopenharmony_ci * can not config the master/slave mode; 4368c2ecf20Sopenharmony_ci * so must clean this bit. 4378c2ecf20Sopenharmony_ci * The master/slave mode has been set in the 4388c2ecf20Sopenharmony_ci * rx port. 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_ci __raw_writel(sspa->sp & ~SSPA_SP_MSL, sspa->tx_base + SSPA_SP); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci __raw_writel(sspa->ctrl, sspa->tx_base + SSPA_CTL); 4438c2ecf20Sopenharmony_ci __raw_writel(sspa->ctrl, sspa->rx_base + SSPA_CTL); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci return 0; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int mmp_sspa_close(struct snd_soc_component *component, 4498c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci pm_runtime_put_sync(component->dev); 4528c2ecf20Sopenharmony_ci return 0; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver mmp_sspa_component = { 4568c2ecf20Sopenharmony_ci .name = "mmp-sspa", 4578c2ecf20Sopenharmony_ci .mmap = mmp_pcm_mmap, 4588c2ecf20Sopenharmony_ci .open = mmp_sspa_open, 4598c2ecf20Sopenharmony_ci .close = mmp_sspa_close, 4608c2ecf20Sopenharmony_ci}; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic int asoc_mmp_sspa_probe(struct platform_device *pdev) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci struct sspa_priv *sspa; 4658c2ecf20Sopenharmony_ci int ret; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci sspa = devm_kzalloc(&pdev->dev, 4688c2ecf20Sopenharmony_ci sizeof(struct sspa_priv), GFP_KERNEL); 4698c2ecf20Sopenharmony_ci if (!sspa) 4708c2ecf20Sopenharmony_ci return -ENOMEM; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (pdev->dev.of_node) { 4738c2ecf20Sopenharmony_ci sspa->rx_base = devm_platform_ioremap_resource(pdev, 0); 4748c2ecf20Sopenharmony_ci if (IS_ERR(sspa->rx_base)) 4758c2ecf20Sopenharmony_ci return PTR_ERR(sspa->rx_base); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci sspa->tx_base = devm_platform_ioremap_resource(pdev, 1); 4788c2ecf20Sopenharmony_ci if (IS_ERR(sspa->tx_base)) 4798c2ecf20Sopenharmony_ci return PTR_ERR(sspa->tx_base); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci sspa->clk = devm_clk_get(&pdev->dev, "bitclk"); 4828c2ecf20Sopenharmony_ci if (IS_ERR(sspa->clk)) 4838c2ecf20Sopenharmony_ci return PTR_ERR(sspa->clk); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci sspa->audio_clk = devm_clk_get(&pdev->dev, "audio"); 4868c2ecf20Sopenharmony_ci if (IS_ERR(sspa->audio_clk)) 4878c2ecf20Sopenharmony_ci return PTR_ERR(sspa->audio_clk); 4888c2ecf20Sopenharmony_ci } else { 4898c2ecf20Sopenharmony_ci struct resource *res; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IO, 0); 4928c2ecf20Sopenharmony_ci if (res == NULL) 4938c2ecf20Sopenharmony_ci return -ENODEV; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci sspa->rx_base = devm_ioremap(&pdev->dev, res->start, 0x30); 4968c2ecf20Sopenharmony_ci if (!sspa->rx_base) 4978c2ecf20Sopenharmony_ci return -ENOMEM; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci sspa->tx_base = devm_ioremap(&pdev->dev, 5008c2ecf20Sopenharmony_ci res->start + 0x80, 0x30); 5018c2ecf20Sopenharmony_ci if (!sspa->tx_base) 5028c2ecf20Sopenharmony_ci return -ENOMEM; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci sspa->clk = devm_clk_get(&pdev->dev, NULL); 5058c2ecf20Sopenharmony_ci if (IS_ERR(sspa->clk)) 5068c2ecf20Sopenharmony_ci return PTR_ERR(sspa->clk); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci sspa->audio_clk = clk_get(NULL, "mmp-audio"); 5098c2ecf20Sopenharmony_ci if (IS_ERR(sspa->audio_clk)) 5108c2ecf20Sopenharmony_ci return PTR_ERR(sspa->audio_clk); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci sspa->sysclk = clk_get(NULL, "mmp-sysclk"); 5138c2ecf20Sopenharmony_ci if (IS_ERR(sspa->sysclk)) { 5148c2ecf20Sopenharmony_ci clk_put(sspa->audio_clk); 5158c2ecf20Sopenharmony_ci return PTR_ERR(sspa->sysclk); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, sspa); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci sspa->playback_dma_data.maxburst = 4; 5218c2ecf20Sopenharmony_ci sspa->capture_dma_data.maxburst = 4; 5228c2ecf20Sopenharmony_ci /* You know, these addresses are actually ignored. */ 5238c2ecf20Sopenharmony_ci sspa->capture_dma_data.addr = SSPA_D; 5248c2ecf20Sopenharmony_ci sspa->playback_dma_data.addr = 0x80 + SSPA_D; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (pdev->dev.of_node) { 5278c2ecf20Sopenharmony_ci ret = devm_snd_dmaengine_pcm_register(&pdev->dev, 5288c2ecf20Sopenharmony_ci &mmp_pcm_config, 0); 5298c2ecf20Sopenharmony_ci if (ret) 5308c2ecf20Sopenharmony_ci return ret; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component, 5348c2ecf20Sopenharmony_ci &mmp_sspa_dai, 1); 5358c2ecf20Sopenharmony_ci if (ret) 5368c2ecf20Sopenharmony_ci return ret; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 5398c2ecf20Sopenharmony_ci clk_prepare_enable(sspa->audio_clk); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return 0; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic int asoc_mmp_sspa_remove(struct platform_device *pdev) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct sspa_priv *sspa = platform_get_drvdata(pdev); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci clk_disable_unprepare(sspa->audio_clk); 5498c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (pdev->dev.of_node) 5528c2ecf20Sopenharmony_ci return 0; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci clk_put(sspa->audio_clk); 5558c2ecf20Sopenharmony_ci clk_put(sspa->sysclk); 5568c2ecf20Sopenharmony_ci return 0; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 5608c2ecf20Sopenharmony_cistatic const struct of_device_id mmp_sspa_of_match[] = { 5618c2ecf20Sopenharmony_ci { .compatible = "marvell,mmp-sspa" }, 5628c2ecf20Sopenharmony_ci {}, 5638c2ecf20Sopenharmony_ci}; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mmp_sspa_of_match); 5668c2ecf20Sopenharmony_ci#endif 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic struct platform_driver asoc_mmp_sspa_driver = { 5698c2ecf20Sopenharmony_ci .driver = { 5708c2ecf20Sopenharmony_ci .name = "mmp-sspa-dai", 5718c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(mmp_sspa_of_match), 5728c2ecf20Sopenharmony_ci }, 5738c2ecf20Sopenharmony_ci .probe = asoc_mmp_sspa_probe, 5748c2ecf20Sopenharmony_ci .remove = asoc_mmp_sspa_remove, 5758c2ecf20Sopenharmony_ci}; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cimodule_platform_driver(asoc_mmp_sspa_driver); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Leo Yan <leoy@marvell.com>"); 5808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MMP SSPA SoC Interface"); 5818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5828c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:mmp-sspa-dai"); 583