11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2fa375d42SZhangfei Gao /* 3fa375d42SZhangfei Gao * linux/sound/soc/pxa/mmp-sspa.c 4fa375d42SZhangfei Gao * Base on pxa2xx-ssp.c 5fa375d42SZhangfei Gao * 6fa375d42SZhangfei Gao * Copyright (C) 2011 Marvell International Ltd. 7fa375d42SZhangfei Gao */ 8fa375d42SZhangfei Gao #include <linux/init.h> 9fa375d42SZhangfei Gao #include <linux/module.h> 10fa375d42SZhangfei Gao #include <linux/platform_device.h> 11fa375d42SZhangfei Gao #include <linux/delay.h> 12fa375d42SZhangfei Gao #include <linux/clk.h> 13fa375d42SZhangfei Gao #include <linux/slab.h> 14fa375d42SZhangfei Gao #include <linux/io.h> 15d65a1458SDaniel Mack #include <linux/dmaengine.h> 16d65a1458SDaniel Mack 17fa375d42SZhangfei Gao #include <sound/core.h> 18fa375d42SZhangfei Gao #include <sound/pcm.h> 19fa375d42SZhangfei Gao #include <sound/initval.h> 20fa375d42SZhangfei Gao #include <sound/pcm_params.h> 21fa375d42SZhangfei Gao #include <sound/soc.h> 22fa375d42SZhangfei Gao #include <sound/pxa2xx-lib.h> 23d65a1458SDaniel Mack #include <sound/dmaengine_pcm.h> 24fa375d42SZhangfei Gao #include "mmp-sspa.h" 25fa375d42SZhangfei Gao 26fa375d42SZhangfei Gao /* 27fa375d42SZhangfei Gao * SSPA audio private data 28fa375d42SZhangfei Gao */ 29fa375d42SZhangfei Gao struct sspa_priv { 303c4e89dfSLubomir Rintel void __iomem *mmio_base; 31c9aeda1cSLubomir Rintel struct snd_dmaengine_dai_dma_data playback_dma_data; 32c9aeda1cSLubomir Rintel struct snd_dmaengine_dai_dma_data capture_dma_data; 333c4e89dfSLubomir Rintel struct clk *clk; 34fa375d42SZhangfei Gao struct clk *audio_clk; 35fa375d42SZhangfei Gao struct clk *sysclk; 36fa375d42SZhangfei Gao int dai_fmt; 37fa375d42SZhangfei Gao int running_cnt; 38fa375d42SZhangfei Gao }; 39fa375d42SZhangfei Gao 403c4e89dfSLubomir Rintel static void mmp_sspa_write_reg(struct sspa_priv *sspa, u32 reg, u32 val) 41fa375d42SZhangfei Gao { 42fa375d42SZhangfei Gao __raw_writel(val, sspa->mmio_base + reg); 43fa375d42SZhangfei Gao } 44fa375d42SZhangfei Gao 453c4e89dfSLubomir Rintel static u32 mmp_sspa_read_reg(struct sspa_priv *sspa, u32 reg) 46fa375d42SZhangfei Gao { 47fa375d42SZhangfei Gao return __raw_readl(sspa->mmio_base + reg); 48fa375d42SZhangfei Gao } 49fa375d42SZhangfei Gao 503c4e89dfSLubomir Rintel static void mmp_sspa_tx_enable(struct sspa_priv *sspa) 51fa375d42SZhangfei Gao { 52fa375d42SZhangfei Gao unsigned int sspa_sp; 53fa375d42SZhangfei Gao 54fa375d42SZhangfei Gao sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP); 55fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_S_EN; 56fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 57fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); 58fa375d42SZhangfei Gao } 59fa375d42SZhangfei Gao 603c4e89dfSLubomir Rintel static void mmp_sspa_tx_disable(struct sspa_priv *sspa) 61fa375d42SZhangfei Gao { 62fa375d42SZhangfei Gao unsigned int sspa_sp; 63fa375d42SZhangfei Gao 64fa375d42SZhangfei Gao sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP); 65fa375d42SZhangfei Gao sspa_sp &= ~SSPA_SP_S_EN; 66fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 67fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); 68fa375d42SZhangfei Gao } 69fa375d42SZhangfei Gao 703c4e89dfSLubomir Rintel static void mmp_sspa_rx_enable(struct sspa_priv *sspa) 71fa375d42SZhangfei Gao { 72fa375d42SZhangfei Gao unsigned int sspa_sp; 73fa375d42SZhangfei Gao 74fa375d42SZhangfei Gao sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP); 75fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_S_EN; 76fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 77fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); 78fa375d42SZhangfei Gao } 79fa375d42SZhangfei Gao 803c4e89dfSLubomir Rintel static void mmp_sspa_rx_disable(struct sspa_priv *sspa) 81fa375d42SZhangfei Gao { 82fa375d42SZhangfei Gao unsigned int sspa_sp; 83fa375d42SZhangfei Gao 84fa375d42SZhangfei Gao sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP); 85fa375d42SZhangfei Gao sspa_sp &= ~SSPA_SP_S_EN; 86fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 87fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); 88fa375d42SZhangfei Gao } 89fa375d42SZhangfei Gao 90fa375d42SZhangfei Gao static int mmp_sspa_startup(struct snd_pcm_substream *substream, 91fa375d42SZhangfei Gao struct snd_soc_dai *dai) 92fa375d42SZhangfei Gao { 933c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 94fa375d42SZhangfei Gao 95*8ecdcac8SLubomir Rintel clk_prepare_enable(sspa->sysclk); 96*8ecdcac8SLubomir Rintel clk_prepare_enable(sspa->clk); 97fa375d42SZhangfei Gao 98fa375d42SZhangfei Gao return 0; 99fa375d42SZhangfei Gao } 100fa375d42SZhangfei Gao 101fa375d42SZhangfei Gao static void mmp_sspa_shutdown(struct snd_pcm_substream *substream, 102fa375d42SZhangfei Gao struct snd_soc_dai *dai) 103fa375d42SZhangfei Gao { 1043c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 105fa375d42SZhangfei Gao 106*8ecdcac8SLubomir Rintel clk_disable_unprepare(sspa->clk); 107*8ecdcac8SLubomir Rintel clk_disable_unprepare(sspa->sysclk); 108fa375d42SZhangfei Gao 109fa375d42SZhangfei Gao } 110fa375d42SZhangfei Gao 111fa375d42SZhangfei Gao /* 112fa375d42SZhangfei Gao * Set the SSP ports SYSCLK. 113fa375d42SZhangfei Gao */ 114fa375d42SZhangfei Gao static int mmp_sspa_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 115fa375d42SZhangfei Gao int clk_id, unsigned int freq, int dir) 116fa375d42SZhangfei Gao { 1173c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai); 118fa375d42SZhangfei Gao int ret = 0; 119fa375d42SZhangfei Gao 120fa375d42SZhangfei Gao switch (clk_id) { 121fa375d42SZhangfei Gao case MMP_SSPA_CLK_AUDIO: 1223c4e89dfSLubomir Rintel ret = clk_set_rate(sspa->audio_clk, freq); 123fa375d42SZhangfei Gao if (ret) 124fa375d42SZhangfei Gao return ret; 125fa375d42SZhangfei Gao break; 126fa375d42SZhangfei Gao case MMP_SSPA_CLK_PLL: 127fa375d42SZhangfei Gao case MMP_SSPA_CLK_VCXO: 128fa375d42SZhangfei Gao /* not support yet */ 129fa375d42SZhangfei Gao return -EINVAL; 130fa375d42SZhangfei Gao default: 131fa375d42SZhangfei Gao return -EINVAL; 132fa375d42SZhangfei Gao } 133fa375d42SZhangfei Gao 134fa375d42SZhangfei Gao return 0; 135fa375d42SZhangfei Gao } 136fa375d42SZhangfei Gao 137fa375d42SZhangfei Gao static int mmp_sspa_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, 138fa375d42SZhangfei Gao int source, unsigned int freq_in, 139fa375d42SZhangfei Gao unsigned int freq_out) 140fa375d42SZhangfei Gao { 1413c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai); 142fa375d42SZhangfei Gao int ret = 0; 143fa375d42SZhangfei Gao 144fa375d42SZhangfei Gao switch (pll_id) { 145fa375d42SZhangfei Gao case MMP_SYSCLK: 1463c4e89dfSLubomir Rintel ret = clk_set_rate(sspa->sysclk, freq_out); 147fa375d42SZhangfei Gao if (ret) 148fa375d42SZhangfei Gao return ret; 149fa375d42SZhangfei Gao break; 150fa375d42SZhangfei Gao case MMP_SSPA_CLK: 1513c4e89dfSLubomir Rintel ret = clk_set_rate(sspa->clk, freq_out); 152fa375d42SZhangfei Gao if (ret) 153fa375d42SZhangfei Gao return ret; 154fa375d42SZhangfei Gao break; 155fa375d42SZhangfei Gao default: 156fa375d42SZhangfei Gao return -ENODEV; 157fa375d42SZhangfei Gao } 158fa375d42SZhangfei Gao 159fa375d42SZhangfei Gao return 0; 160fa375d42SZhangfei Gao } 161fa375d42SZhangfei Gao 162fa375d42SZhangfei Gao /* 163fa375d42SZhangfei Gao * Set up the sspa dai format. The sspa port must be inactive 164fa375d42SZhangfei Gao * before calling this function as the physical 165fa375d42SZhangfei Gao * interface format is changed. 166fa375d42SZhangfei Gao */ 167fa375d42SZhangfei Gao static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai, 168fa375d42SZhangfei Gao unsigned int fmt) 169fa375d42SZhangfei Gao { 1703c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai); 171fa375d42SZhangfei Gao u32 sspa_sp, sspa_ctrl; 172fa375d42SZhangfei Gao 173fa375d42SZhangfei Gao /* check if we need to change anything at all */ 1743c4e89dfSLubomir Rintel if (sspa->dai_fmt == fmt) 175fa375d42SZhangfei Gao return 0; 176fa375d42SZhangfei Gao 177fa375d42SZhangfei Gao /* we can only change the settings if the port is not in use */ 178fa375d42SZhangfei Gao if ((mmp_sspa_read_reg(sspa, SSPA_TXSP) & SSPA_SP_S_EN) || 179fa375d42SZhangfei Gao (mmp_sspa_read_reg(sspa, SSPA_RXSP) & SSPA_SP_S_EN)) { 1803c4e89dfSLubomir Rintel dev_err(cpu_dai->dev, 181fa375d42SZhangfei Gao "can't change hardware dai format: stream is in use\n"); 182fa375d42SZhangfei Gao return -EINVAL; 183fa375d42SZhangfei Gao } 184fa375d42SZhangfei Gao 185fa375d42SZhangfei Gao /* reset port settings */ 186fa375d42SZhangfei Gao sspa_sp = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH; 187fa375d42SZhangfei Gao sspa_ctrl = 0; 188fa375d42SZhangfei Gao 189fa375d42SZhangfei Gao switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 190fa375d42SZhangfei Gao case SND_SOC_DAIFMT_CBS_CFS: 191fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_MSL; 192fa375d42SZhangfei Gao break; 193fa375d42SZhangfei Gao case SND_SOC_DAIFMT_CBM_CFM: 194fa375d42SZhangfei Gao break; 195fa375d42SZhangfei Gao default: 196fa375d42SZhangfei Gao return -EINVAL; 197fa375d42SZhangfei Gao } 198fa375d42SZhangfei Gao 199fa375d42SZhangfei Gao switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 200fa375d42SZhangfei Gao case SND_SOC_DAIFMT_NB_NF: 201fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_FSP; 202fa375d42SZhangfei Gao break; 203fa375d42SZhangfei Gao default: 204fa375d42SZhangfei Gao return -EINVAL; 205fa375d42SZhangfei Gao } 206fa375d42SZhangfei Gao 207fa375d42SZhangfei Gao switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 208fa375d42SZhangfei Gao case SND_SOC_DAIFMT_I2S: 209fa375d42SZhangfei Gao sspa_sp |= SSPA_TXSP_FPER(63); 210fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_FWID(31); 211fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XDATDLY(1); 212fa375d42SZhangfei Gao break; 213fa375d42SZhangfei Gao default: 214fa375d42SZhangfei Gao return -EINVAL; 215fa375d42SZhangfei Gao } 216fa375d42SZhangfei Gao 217fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); 218fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); 219fa375d42SZhangfei Gao 220fa375d42SZhangfei Gao sspa_sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH); 221fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); 222fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); 223fa375d42SZhangfei Gao 224fa375d42SZhangfei Gao /* 225fa375d42SZhangfei Gao * FIXME: hw issue, for the tx serial port, 226fa375d42SZhangfei Gao * can not config the master/slave mode; 227fa375d42SZhangfei Gao * so must clean this bit. 228fa375d42SZhangfei Gao * The master/slave mode has been set in the 229fa375d42SZhangfei Gao * rx port. 230fa375d42SZhangfei Gao */ 231fa375d42SZhangfei Gao sspa_sp &= ~SSPA_SP_MSL; 232fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); 233fa375d42SZhangfei Gao 234fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl); 235fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl); 236fa375d42SZhangfei Gao 237fa375d42SZhangfei Gao /* Since we are configuring the timings for the format by hand 238fa375d42SZhangfei Gao * we have to defer some things until hw_params() where we 239fa375d42SZhangfei Gao * know parameters like the sample size. 240fa375d42SZhangfei Gao */ 2413c4e89dfSLubomir Rintel sspa->dai_fmt = fmt; 242fa375d42SZhangfei Gao return 0; 243fa375d42SZhangfei Gao } 244fa375d42SZhangfei Gao 245fa375d42SZhangfei Gao /* 246fa375d42SZhangfei Gao * Set the SSPA audio DMA parameters and sample size. 247fa375d42SZhangfei Gao * Can be called multiple times by oss emulation. 248fa375d42SZhangfei Gao */ 249fa375d42SZhangfei Gao static int mmp_sspa_hw_params(struct snd_pcm_substream *substream, 250fa375d42SZhangfei Gao struct snd_pcm_hw_params *params, 251fa375d42SZhangfei Gao struct snd_soc_dai *dai) 252fa375d42SZhangfei Gao { 2533c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 254fa375d42SZhangfei Gao u32 sspa_ctrl; 255fa375d42SZhangfei Gao 256fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 257fa375d42SZhangfei Gao sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_TXCTL); 258fa375d42SZhangfei Gao else 259fa375d42SZhangfei Gao sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_RXCTL); 260fa375d42SZhangfei Gao 261fa375d42SZhangfei Gao sspa_ctrl &= ~SSPA_CTL_XFRLEN1_MASK; 262fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XFRLEN1(params_channels(params) - 1); 263fa375d42SZhangfei Gao sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK; 264fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XWDLEN1(SSPA_CTL_32_BITS); 265fa375d42SZhangfei Gao sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK; 266fa375d42SZhangfei Gao 267fa375d42SZhangfei Gao switch (params_format(params)) { 268fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S8: 269fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_8_BITS); 270fa375d42SZhangfei Gao break; 271fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S16_LE: 272fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_16_BITS); 273fa375d42SZhangfei Gao break; 274fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S24_3LE: 275fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_24_BITS); 276fa375d42SZhangfei Gao break; 277fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S32_LE: 278fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_32_BITS); 279fa375d42SZhangfei Gao break; 280fa375d42SZhangfei Gao default: 281fa375d42SZhangfei Gao return -EINVAL; 282fa375d42SZhangfei Gao } 283fa375d42SZhangfei Gao 284fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 285fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl); 286fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXFIFO_LL, 0x1); 287fa375d42SZhangfei Gao } else { 288fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl); 289fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXFIFO_UL, 0x0); 290fa375d42SZhangfei Gao } 291fa375d42SZhangfei Gao 292fa375d42SZhangfei Gao return 0; 293fa375d42SZhangfei Gao } 294fa375d42SZhangfei Gao 295fa375d42SZhangfei Gao static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd, 296fa375d42SZhangfei Gao struct snd_soc_dai *dai) 297fa375d42SZhangfei Gao { 2983c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 299fa375d42SZhangfei Gao int ret = 0; 300fa375d42SZhangfei Gao 301fa375d42SZhangfei Gao switch (cmd) { 302fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_START: 303fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_RESUME: 304fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 305fa375d42SZhangfei Gao /* 306fa375d42SZhangfei Gao * whatever playback or capture, must enable rx. 307fa375d42SZhangfei Gao * this is a hw issue, so need check if rx has been 308fa375d42SZhangfei Gao * enabled or not; if has been enabled by another 309fa375d42SZhangfei Gao * stream, do not enable again. 310fa375d42SZhangfei Gao */ 3113c4e89dfSLubomir Rintel if (!sspa->running_cnt) 312fa375d42SZhangfei Gao mmp_sspa_rx_enable(sspa); 313fa375d42SZhangfei Gao 314fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 315fa375d42SZhangfei Gao mmp_sspa_tx_enable(sspa); 316fa375d42SZhangfei Gao 3173c4e89dfSLubomir Rintel sspa->running_cnt++; 318fa375d42SZhangfei Gao break; 319fa375d42SZhangfei Gao 320fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_STOP: 321fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_SUSPEND: 322fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3233c4e89dfSLubomir Rintel sspa->running_cnt--; 324fa375d42SZhangfei Gao 325fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 326fa375d42SZhangfei Gao mmp_sspa_tx_disable(sspa); 327fa375d42SZhangfei Gao 328fa375d42SZhangfei Gao /* have no capture stream, disable rx port */ 3293c4e89dfSLubomir Rintel if (!sspa->running_cnt) 330fa375d42SZhangfei Gao mmp_sspa_rx_disable(sspa); 331fa375d42SZhangfei Gao break; 332fa375d42SZhangfei Gao 333fa375d42SZhangfei Gao default: 334fa375d42SZhangfei Gao ret = -EINVAL; 335fa375d42SZhangfei Gao } 336fa375d42SZhangfei Gao 337fa375d42SZhangfei Gao return ret; 338fa375d42SZhangfei Gao } 339fa375d42SZhangfei Gao 340fa375d42SZhangfei Gao static int mmp_sspa_probe(struct snd_soc_dai *dai) 341fa375d42SZhangfei Gao { 3423c4e89dfSLubomir Rintel struct sspa_priv *sspa = dev_get_drvdata(dai->dev); 343fa375d42SZhangfei Gao 344c9aeda1cSLubomir Rintel snd_soc_dai_init_dma_data(dai, 3453c4e89dfSLubomir Rintel &sspa->playback_dma_data, 3463c4e89dfSLubomir Rintel &sspa->capture_dma_data); 347c9aeda1cSLubomir Rintel 3483c4e89dfSLubomir Rintel snd_soc_dai_set_drvdata(dai, sspa); 349fa375d42SZhangfei Gao return 0; 350fa375d42SZhangfei Gao } 351fa375d42SZhangfei Gao 352fa375d42SZhangfei Gao #define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000 353fa375d42SZhangfei Gao #define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 354fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S16_LE | \ 35500a1aca2SLubomir Rintel SNDRV_PCM_FMTBIT_S24_3LE | \ 356fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S32_LE) 357fa375d42SZhangfei Gao 358cb753443SGustavo A. R. Silva static const struct snd_soc_dai_ops mmp_sspa_dai_ops = { 359fa375d42SZhangfei Gao .startup = mmp_sspa_startup, 360fa375d42SZhangfei Gao .shutdown = mmp_sspa_shutdown, 361fa375d42SZhangfei Gao .trigger = mmp_sspa_trigger, 362fa375d42SZhangfei Gao .hw_params = mmp_sspa_hw_params, 363fa375d42SZhangfei Gao .set_sysclk = mmp_sspa_set_dai_sysclk, 364fa375d42SZhangfei Gao .set_pll = mmp_sspa_set_dai_pll, 365fa375d42SZhangfei Gao .set_fmt = mmp_sspa_set_dai_fmt, 366fa375d42SZhangfei Gao }; 367fa375d42SZhangfei Gao 3685d9ff402SLars-Peter Clausen static struct snd_soc_dai_driver mmp_sspa_dai = { 369fa375d42SZhangfei Gao .probe = mmp_sspa_probe, 370fa375d42SZhangfei Gao .playback = { 371fa375d42SZhangfei Gao .channels_min = 1, 372fa375d42SZhangfei Gao .channels_max = 128, 373fa375d42SZhangfei Gao .rates = MMP_SSPA_RATES, 374fa375d42SZhangfei Gao .formats = MMP_SSPA_FORMATS, 375fa375d42SZhangfei Gao }, 376fa375d42SZhangfei Gao .capture = { 377fa375d42SZhangfei Gao .channels_min = 1, 378fa375d42SZhangfei Gao .channels_max = 2, 379fa375d42SZhangfei Gao .rates = MMP_SSPA_RATES, 380fa375d42SZhangfei Gao .formats = MMP_SSPA_FORMATS, 381fa375d42SZhangfei Gao }, 382fa375d42SZhangfei Gao .ops = &mmp_sspa_dai_ops, 383fa375d42SZhangfei Gao }; 384fa375d42SZhangfei Gao 385724da053SLubomir Rintel #define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ 386724da053SLubomir Rintel SNDRV_PCM_INFO_MMAP_VALID | \ 387724da053SLubomir Rintel SNDRV_PCM_INFO_INTERLEAVED | \ 388724da053SLubomir Rintel SNDRV_PCM_INFO_PAUSE | \ 389724da053SLubomir Rintel SNDRV_PCM_INFO_RESUME | \ 390724da053SLubomir Rintel SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) 391724da053SLubomir Rintel 392724da053SLubomir Rintel static const struct snd_pcm_hardware mmp_pcm_hardware[] = { 393724da053SLubomir Rintel { 394724da053SLubomir Rintel .info = MMP_PCM_INFO, 395724da053SLubomir Rintel .period_bytes_min = 1024, 396724da053SLubomir Rintel .period_bytes_max = 2048, 397724da053SLubomir Rintel .periods_min = 2, 398724da053SLubomir Rintel .periods_max = 32, 399724da053SLubomir Rintel .buffer_bytes_max = 4096, 400724da053SLubomir Rintel .fifo_size = 32, 401724da053SLubomir Rintel }, 402724da053SLubomir Rintel { 403724da053SLubomir Rintel .info = MMP_PCM_INFO, 404724da053SLubomir Rintel .period_bytes_min = 1024, 405724da053SLubomir Rintel .period_bytes_max = 2048, 406724da053SLubomir Rintel .periods_min = 2, 407724da053SLubomir Rintel .periods_max = 32, 408724da053SLubomir Rintel .buffer_bytes_max = 4096, 409724da053SLubomir Rintel .fifo_size = 32, 410724da053SLubomir Rintel }, 411724da053SLubomir Rintel }; 412724da053SLubomir Rintel 413724da053SLubomir Rintel static const struct snd_dmaengine_pcm_config mmp_pcm_config = { 414724da053SLubomir Rintel .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 415724da053SLubomir Rintel .pcm_hardware = mmp_pcm_hardware, 416724da053SLubomir Rintel .prealloc_buffer_size = 4096, 417724da053SLubomir Rintel }; 418724da053SLubomir Rintel 419724da053SLubomir Rintel static int mmp_pcm_mmap(struct snd_soc_component *component, 420724da053SLubomir Rintel struct snd_pcm_substream *substream, 421724da053SLubomir Rintel struct vm_area_struct *vma) 422724da053SLubomir Rintel { 423724da053SLubomir Rintel vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; 424724da053SLubomir Rintel vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 425724da053SLubomir Rintel return remap_pfn_range(vma, vma->vm_start, 426724da053SLubomir Rintel substream->dma_buffer.addr >> PAGE_SHIFT, 427724da053SLubomir Rintel vma->vm_end - vma->vm_start, vma->vm_page_prot); 428724da053SLubomir Rintel } 429724da053SLubomir Rintel 430425f3708SKuninori Morimoto static const struct snd_soc_component_driver mmp_sspa_component = { 431425f3708SKuninori Morimoto .name = "mmp-sspa", 432724da053SLubomir Rintel .mmap = mmp_pcm_mmap, 433425f3708SKuninori Morimoto }; 434425f3708SKuninori Morimoto 435570f6fe1SBill Pemberton static int asoc_mmp_sspa_probe(struct platform_device *pdev) 436fa375d42SZhangfei Gao { 4373c4e89dfSLubomir Rintel struct sspa_priv *sspa; 438fa375d42SZhangfei Gao 4393c4e89dfSLubomir Rintel sspa = devm_kzalloc(&pdev->dev, 440fa375d42SZhangfei Gao sizeof(struct sspa_priv), GFP_KERNEL); 4413c4e89dfSLubomir Rintel if (!sspa) 442fa375d42SZhangfei Gao return -ENOMEM; 443fa375d42SZhangfei Gao 4443c4e89dfSLubomir Rintel sspa->mmio_base = devm_platform_ioremap_resource(pdev, 0); 4453c4e89dfSLubomir Rintel if (IS_ERR(sspa->mmio_base)) 4463c4e89dfSLubomir Rintel return PTR_ERR(sspa->mmio_base); 447fa375d42SZhangfei Gao 4483c4e89dfSLubomir Rintel sspa->clk = devm_clk_get(&pdev->dev, NULL); 4493c4e89dfSLubomir Rintel if (IS_ERR(sspa->clk)) 4503c4e89dfSLubomir Rintel return PTR_ERR(sspa->clk); 451fa375d42SZhangfei Gao 4523c4e89dfSLubomir Rintel sspa->audio_clk = clk_get(NULL, "mmp-audio"); 4533c4e89dfSLubomir Rintel if (IS_ERR(sspa->audio_clk)) 4543c4e89dfSLubomir Rintel return PTR_ERR(sspa->audio_clk); 455fa375d42SZhangfei Gao 4563c4e89dfSLubomir Rintel sspa->sysclk = clk_get(NULL, "mmp-sysclk"); 4573c4e89dfSLubomir Rintel if (IS_ERR(sspa->sysclk)) { 4583c4e89dfSLubomir Rintel clk_put(sspa->audio_clk); 4593c4e89dfSLubomir Rintel return PTR_ERR(sspa->sysclk); 460fa375d42SZhangfei Gao } 461*8ecdcac8SLubomir Rintel clk_prepare_enable(sspa->audio_clk); 4623c4e89dfSLubomir Rintel sspa->dai_fmt = (unsigned int) -1; 4633c4e89dfSLubomir Rintel platform_set_drvdata(pdev, sspa); 464fa375d42SZhangfei Gao 4653c4e89dfSLubomir Rintel sspa->playback_dma_data.maxburst = 4; 4663c4e89dfSLubomir Rintel sspa->capture_dma_data.maxburst = 4; 467c9aeda1cSLubomir Rintel /* You know, these addresses are actually ignored. */ 4683c4e89dfSLubomir Rintel sspa->playback_dma_data.addr = SSPA_TXD; 4693c4e89dfSLubomir Rintel sspa->capture_dma_data.addr = SSPA_RXD; 470c9aeda1cSLubomir Rintel 471724da053SLubomir Rintel if (pdev->dev.of_node) { 472724da053SLubomir Rintel int ret; 473724da053SLubomir Rintel 474724da053SLubomir Rintel ret = devm_snd_dmaengine_pcm_register(&pdev->dev, 475724da053SLubomir Rintel &mmp_pcm_config, 0); 476724da053SLubomir Rintel if (ret) 477724da053SLubomir Rintel return ret; 478724da053SLubomir Rintel } 479724da053SLubomir Rintel 4809ff50721SSachin Kamat return devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component, 481425f3708SKuninori Morimoto &mmp_sspa_dai, 1); 482fa375d42SZhangfei Gao } 483fa375d42SZhangfei Gao 484570f6fe1SBill Pemberton static int asoc_mmp_sspa_remove(struct platform_device *pdev) 485fa375d42SZhangfei Gao { 4863c4e89dfSLubomir Rintel struct sspa_priv *sspa = platform_get_drvdata(pdev); 487fa375d42SZhangfei Gao 488*8ecdcac8SLubomir Rintel clk_disable_unprepare(sspa->audio_clk); 4893c4e89dfSLubomir Rintel clk_put(sspa->audio_clk); 4903c4e89dfSLubomir Rintel clk_put(sspa->sysclk); 491fa375d42SZhangfei Gao return 0; 492fa375d42SZhangfei Gao } 493fa375d42SZhangfei Gao 494fa375d42SZhangfei Gao static struct platform_driver asoc_mmp_sspa_driver = { 495fa375d42SZhangfei Gao .driver = { 496fa375d42SZhangfei Gao .name = "mmp-sspa-dai", 497fa375d42SZhangfei Gao }, 498fa375d42SZhangfei Gao .probe = asoc_mmp_sspa_probe, 499570f6fe1SBill Pemberton .remove = asoc_mmp_sspa_remove, 500fa375d42SZhangfei Gao }; 501fa375d42SZhangfei Gao 502fa375d42SZhangfei Gao module_platform_driver(asoc_mmp_sspa_driver); 503fa375d42SZhangfei Gao 504fa375d42SZhangfei Gao MODULE_AUTHOR("Leo Yan <leoy@marvell.com>"); 505fa375d42SZhangfei Gao MODULE_DESCRIPTION("MMP SSPA SoC Interface"); 506fa375d42SZhangfei Gao MODULE_LICENSE("GPL"); 507e5b7d71aSAndrea Adami MODULE_ALIAS("platform:mmp-sspa-dai"); 508