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> 167d98cc64SLubomir Rintel #include <linux/pm_runtime.h> 17d65a1458SDaniel Mack 18fa375d42SZhangfei Gao #include <sound/core.h> 19fa375d42SZhangfei Gao #include <sound/pcm.h> 20fa375d42SZhangfei Gao #include <sound/initval.h> 21fa375d42SZhangfei Gao #include <sound/pcm_params.h> 22fa375d42SZhangfei Gao #include <sound/soc.h> 23fa375d42SZhangfei Gao #include <sound/pxa2xx-lib.h> 24d65a1458SDaniel Mack #include <sound/dmaengine_pcm.h> 25fa375d42SZhangfei Gao #include "mmp-sspa.h" 26fa375d42SZhangfei Gao 27fa375d42SZhangfei Gao /* 28fa375d42SZhangfei Gao * SSPA audio private data 29fa375d42SZhangfei Gao */ 30fa375d42SZhangfei Gao struct sspa_priv { 313c4e89dfSLubomir Rintel void __iomem *mmio_base; 32c9aeda1cSLubomir Rintel struct snd_dmaengine_dai_dma_data playback_dma_data; 33c9aeda1cSLubomir Rintel struct snd_dmaengine_dai_dma_data capture_dma_data; 343c4e89dfSLubomir Rintel struct clk *clk; 35fa375d42SZhangfei Gao struct clk *audio_clk; 36fa375d42SZhangfei Gao struct clk *sysclk; 37fa375d42SZhangfei Gao int running_cnt; 387d98cc64SLubomir Rintel u32 sp; 397d98cc64SLubomir Rintel u32 ctrl; 40fa375d42SZhangfei Gao }; 41fa375d42SZhangfei Gao 423c4e89dfSLubomir Rintel static void mmp_sspa_write_reg(struct sspa_priv *sspa, u32 reg, u32 val) 43fa375d42SZhangfei Gao { 44fa375d42SZhangfei Gao __raw_writel(val, sspa->mmio_base + reg); 45fa375d42SZhangfei Gao } 46fa375d42SZhangfei Gao 473c4e89dfSLubomir Rintel static u32 mmp_sspa_read_reg(struct sspa_priv *sspa, u32 reg) 48fa375d42SZhangfei Gao { 49fa375d42SZhangfei Gao return __raw_readl(sspa->mmio_base + reg); 50fa375d42SZhangfei Gao } 51fa375d42SZhangfei Gao 523c4e89dfSLubomir Rintel static void mmp_sspa_tx_enable(struct sspa_priv *sspa) 53fa375d42SZhangfei Gao { 547d98cc64SLubomir Rintel unsigned int sspa_sp = sspa->sp; 55fa375d42SZhangfei Gao 567d98cc64SLubomir Rintel sspa_sp &= ~SSPA_SP_MSL; 57fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_S_EN; 58fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 59fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); 60fa375d42SZhangfei Gao } 61fa375d42SZhangfei Gao 623c4e89dfSLubomir Rintel static void mmp_sspa_tx_disable(struct sspa_priv *sspa) 63fa375d42SZhangfei Gao { 647d98cc64SLubomir Rintel unsigned int sspa_sp = sspa->sp; 65fa375d42SZhangfei Gao 667d98cc64SLubomir Rintel sspa_sp &= ~SSPA_SP_MSL; 67fa375d42SZhangfei Gao sspa_sp &= ~SSPA_SP_S_EN; 68fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 69fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); 70fa375d42SZhangfei Gao } 71fa375d42SZhangfei Gao 723c4e89dfSLubomir Rintel static void mmp_sspa_rx_enable(struct sspa_priv *sspa) 73fa375d42SZhangfei Gao { 747d98cc64SLubomir Rintel unsigned int sspa_sp = sspa->sp; 75fa375d42SZhangfei Gao 76fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_S_EN; 77fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 78fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); 79fa375d42SZhangfei Gao } 80fa375d42SZhangfei Gao 813c4e89dfSLubomir Rintel static void mmp_sspa_rx_disable(struct sspa_priv *sspa) 82fa375d42SZhangfei Gao { 837d98cc64SLubomir Rintel unsigned int sspa_sp = sspa->sp; 84fa375d42SZhangfei Gao 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 958ecdcac8SLubomir Rintel clk_prepare_enable(sspa->sysclk); 968ecdcac8SLubomir 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 1068ecdcac8SLubomir Rintel clk_disable_unprepare(sspa->clk); 1078ecdcac8SLubomir 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 /* 1637d98cc64SLubomir Rintel * Set up the sspa dai format. 164fa375d42SZhangfei Gao */ 165fa375d42SZhangfei Gao static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai, 166fa375d42SZhangfei Gao unsigned int fmt) 167fa375d42SZhangfei Gao { 1683c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai); 169fa375d42SZhangfei Gao 170fa375d42SZhangfei Gao /* reset port settings */ 1717d98cc64SLubomir Rintel sspa->sp = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH; 1727d98cc64SLubomir Rintel sspa->ctrl = 0; 173fa375d42SZhangfei Gao 174fa375d42SZhangfei Gao switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 175fa375d42SZhangfei Gao case SND_SOC_DAIFMT_CBS_CFS: 1767d98cc64SLubomir Rintel sspa->sp |= SSPA_SP_MSL; 177fa375d42SZhangfei Gao break; 178fa375d42SZhangfei Gao case SND_SOC_DAIFMT_CBM_CFM: 179fa375d42SZhangfei Gao break; 180fa375d42SZhangfei Gao default: 181fa375d42SZhangfei Gao return -EINVAL; 182fa375d42SZhangfei Gao } 183fa375d42SZhangfei Gao 184fa375d42SZhangfei Gao switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 185fa375d42SZhangfei Gao case SND_SOC_DAIFMT_NB_NF: 1867d98cc64SLubomir Rintel sspa->sp |= SSPA_SP_FSP; 187fa375d42SZhangfei Gao break; 188fa375d42SZhangfei Gao default: 189fa375d42SZhangfei Gao return -EINVAL; 190fa375d42SZhangfei Gao } 191fa375d42SZhangfei Gao 192fa375d42SZhangfei Gao switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 193fa375d42SZhangfei Gao case SND_SOC_DAIFMT_I2S: 1947d98cc64SLubomir Rintel sspa->ctrl |= SSPA_CTL_XDATDLY(1); 195fa375d42SZhangfei Gao break; 196fa375d42SZhangfei Gao default: 197fa375d42SZhangfei Gao return -EINVAL; 198fa375d42SZhangfei Gao } 199fa375d42SZhangfei Gao 200fa375d42SZhangfei Gao /* Since we are configuring the timings for the format by hand 201fa375d42SZhangfei Gao * we have to defer some things until hw_params() where we 202fa375d42SZhangfei Gao * know parameters like the sample size. 203fa375d42SZhangfei Gao */ 204fa375d42SZhangfei Gao return 0; 205fa375d42SZhangfei Gao } 206fa375d42SZhangfei Gao 207fa375d42SZhangfei Gao /* 208fa375d42SZhangfei Gao * Set the SSPA audio DMA parameters and sample size. 209fa375d42SZhangfei Gao * Can be called multiple times by oss emulation. 210fa375d42SZhangfei Gao */ 211fa375d42SZhangfei Gao static int mmp_sspa_hw_params(struct snd_pcm_substream *substream, 212fa375d42SZhangfei Gao struct snd_pcm_hw_params *params, 213fa375d42SZhangfei Gao struct snd_soc_dai *dai) 214fa375d42SZhangfei Gao { 2153c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 2167d98cc64SLubomir Rintel u32 sspa_ctrl = sspa->ctrl; 217*39ec7e9bSLubomir Rintel int bits; 218*39ec7e9bSLubomir Rintel int bitval; 219fa375d42SZhangfei Gao 220fa375d42SZhangfei Gao switch (params_format(params)) { 221fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S8: 222*39ec7e9bSLubomir Rintel bits = 8; 223*39ec7e9bSLubomir Rintel bitval = SSPA_CTL_8_BITS; 224fa375d42SZhangfei Gao break; 225fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S16_LE: 226*39ec7e9bSLubomir Rintel bits = 16; 227*39ec7e9bSLubomir Rintel bitval = SSPA_CTL_16_BITS; 228fa375d42SZhangfei Gao break; 229fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S24_3LE: 230*39ec7e9bSLubomir Rintel bits = 24; 231*39ec7e9bSLubomir Rintel bitval = SSPA_CTL_24_BITS; 232fa375d42SZhangfei Gao break; 233fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S32_LE: 234*39ec7e9bSLubomir Rintel bits = 32; 235*39ec7e9bSLubomir Rintel bitval = SSPA_CTL_32_BITS; 236fa375d42SZhangfei Gao break; 237fa375d42SZhangfei Gao default: 238fa375d42SZhangfei Gao return -EINVAL; 239fa375d42SZhangfei Gao } 240fa375d42SZhangfei Gao 241*39ec7e9bSLubomir Rintel if (params_channels(params) == 2) 242*39ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XPH; 243*39ec7e9bSLubomir Rintel 244*39ec7e9bSLubomir Rintel sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK; 245*39ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XWDLEN1(bitval); 246*39ec7e9bSLubomir Rintel 247*39ec7e9bSLubomir Rintel sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK; 248*39ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XSSZ1(bitval); 249*39ec7e9bSLubomir Rintel 250*39ec7e9bSLubomir Rintel sspa_ctrl &= ~SSPA_CTL_XSSZ2_MASK; 251*39ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XSSZ2(bitval); 252*39ec7e9bSLubomir Rintel 253*39ec7e9bSLubomir Rintel sspa->sp &= ~SSPA_SP_FWID_MASK; 254*39ec7e9bSLubomir Rintel sspa->sp |= SSPA_SP_FWID(bits - 1); 255*39ec7e9bSLubomir Rintel 256*39ec7e9bSLubomir Rintel sspa->sp &= ~SSPA_TXSP_FPER_MASK; 257*39ec7e9bSLubomir Rintel sspa->sp |= SSPA_TXSP_FPER(bits * 2 - 1); 258*39ec7e9bSLubomir Rintel 259fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 260fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl); 261fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXFIFO_LL, 0x1); 262fa375d42SZhangfei Gao } else { 263fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl); 264fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXFIFO_UL, 0x0); 265fa375d42SZhangfei Gao } 266fa375d42SZhangfei Gao 267fa375d42SZhangfei Gao return 0; 268fa375d42SZhangfei Gao } 269fa375d42SZhangfei Gao 270fa375d42SZhangfei Gao static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd, 271fa375d42SZhangfei Gao struct snd_soc_dai *dai) 272fa375d42SZhangfei Gao { 2733c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 274fa375d42SZhangfei Gao int ret = 0; 275fa375d42SZhangfei Gao 276fa375d42SZhangfei Gao switch (cmd) { 277fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_START: 278fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_RESUME: 279fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 280fa375d42SZhangfei Gao /* 281fa375d42SZhangfei Gao * whatever playback or capture, must enable rx. 282fa375d42SZhangfei Gao * this is a hw issue, so need check if rx has been 283fa375d42SZhangfei Gao * enabled or not; if has been enabled by another 284fa375d42SZhangfei Gao * stream, do not enable again. 285fa375d42SZhangfei Gao */ 2863c4e89dfSLubomir Rintel if (!sspa->running_cnt) 287fa375d42SZhangfei Gao mmp_sspa_rx_enable(sspa); 288fa375d42SZhangfei Gao 289fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 290fa375d42SZhangfei Gao mmp_sspa_tx_enable(sspa); 291fa375d42SZhangfei Gao 2923c4e89dfSLubomir Rintel sspa->running_cnt++; 293fa375d42SZhangfei Gao break; 294fa375d42SZhangfei Gao 295fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_STOP: 296fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_SUSPEND: 297fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2983c4e89dfSLubomir Rintel sspa->running_cnt--; 299fa375d42SZhangfei Gao 300fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 301fa375d42SZhangfei Gao mmp_sspa_tx_disable(sspa); 302fa375d42SZhangfei Gao 303fa375d42SZhangfei Gao /* have no capture stream, disable rx port */ 3043c4e89dfSLubomir Rintel if (!sspa->running_cnt) 305fa375d42SZhangfei Gao mmp_sspa_rx_disable(sspa); 306fa375d42SZhangfei Gao break; 307fa375d42SZhangfei Gao 308fa375d42SZhangfei Gao default: 309fa375d42SZhangfei Gao ret = -EINVAL; 310fa375d42SZhangfei Gao } 311fa375d42SZhangfei Gao 312fa375d42SZhangfei Gao return ret; 313fa375d42SZhangfei Gao } 314fa375d42SZhangfei Gao 315fa375d42SZhangfei Gao static int mmp_sspa_probe(struct snd_soc_dai *dai) 316fa375d42SZhangfei Gao { 3173c4e89dfSLubomir Rintel struct sspa_priv *sspa = dev_get_drvdata(dai->dev); 318fa375d42SZhangfei Gao 319c9aeda1cSLubomir Rintel snd_soc_dai_init_dma_data(dai, 3203c4e89dfSLubomir Rintel &sspa->playback_dma_data, 3213c4e89dfSLubomir Rintel &sspa->capture_dma_data); 322c9aeda1cSLubomir Rintel 3233c4e89dfSLubomir Rintel snd_soc_dai_set_drvdata(dai, sspa); 324fa375d42SZhangfei Gao return 0; 325fa375d42SZhangfei Gao } 326fa375d42SZhangfei Gao 327fa375d42SZhangfei Gao #define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000 328fa375d42SZhangfei Gao #define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 329fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S16_LE | \ 33000a1aca2SLubomir Rintel SNDRV_PCM_FMTBIT_S24_3LE | \ 331fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S32_LE) 332fa375d42SZhangfei Gao 333cb753443SGustavo A. R. Silva static const struct snd_soc_dai_ops mmp_sspa_dai_ops = { 334fa375d42SZhangfei Gao .startup = mmp_sspa_startup, 335fa375d42SZhangfei Gao .shutdown = mmp_sspa_shutdown, 336fa375d42SZhangfei Gao .trigger = mmp_sspa_trigger, 337fa375d42SZhangfei Gao .hw_params = mmp_sspa_hw_params, 338fa375d42SZhangfei Gao .set_sysclk = mmp_sspa_set_dai_sysclk, 339fa375d42SZhangfei Gao .set_pll = mmp_sspa_set_dai_pll, 340fa375d42SZhangfei Gao .set_fmt = mmp_sspa_set_dai_fmt, 341fa375d42SZhangfei Gao }; 342fa375d42SZhangfei Gao 3435d9ff402SLars-Peter Clausen static struct snd_soc_dai_driver mmp_sspa_dai = { 344fa375d42SZhangfei Gao .probe = mmp_sspa_probe, 345fa375d42SZhangfei Gao .playback = { 346fa375d42SZhangfei Gao .channels_min = 1, 347fa375d42SZhangfei Gao .channels_max = 128, 348fa375d42SZhangfei Gao .rates = MMP_SSPA_RATES, 349fa375d42SZhangfei Gao .formats = MMP_SSPA_FORMATS, 350fa375d42SZhangfei Gao }, 351fa375d42SZhangfei Gao .capture = { 352fa375d42SZhangfei Gao .channels_min = 1, 353fa375d42SZhangfei Gao .channels_max = 2, 354fa375d42SZhangfei Gao .rates = MMP_SSPA_RATES, 355fa375d42SZhangfei Gao .formats = MMP_SSPA_FORMATS, 356fa375d42SZhangfei Gao }, 357fa375d42SZhangfei Gao .ops = &mmp_sspa_dai_ops, 358fa375d42SZhangfei Gao }; 359fa375d42SZhangfei Gao 360724da053SLubomir Rintel #define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ 361724da053SLubomir Rintel SNDRV_PCM_INFO_MMAP_VALID | \ 362724da053SLubomir Rintel SNDRV_PCM_INFO_INTERLEAVED | \ 363724da053SLubomir Rintel SNDRV_PCM_INFO_PAUSE | \ 364724da053SLubomir Rintel SNDRV_PCM_INFO_RESUME | \ 365724da053SLubomir Rintel SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) 366724da053SLubomir Rintel 367724da053SLubomir Rintel static const struct snd_pcm_hardware mmp_pcm_hardware[] = { 368724da053SLubomir Rintel { 369724da053SLubomir Rintel .info = MMP_PCM_INFO, 370724da053SLubomir Rintel .period_bytes_min = 1024, 371724da053SLubomir Rintel .period_bytes_max = 2048, 372724da053SLubomir Rintel .periods_min = 2, 373724da053SLubomir Rintel .periods_max = 32, 374724da053SLubomir Rintel .buffer_bytes_max = 4096, 375724da053SLubomir Rintel .fifo_size = 32, 376724da053SLubomir Rintel }, 377724da053SLubomir Rintel { 378724da053SLubomir Rintel .info = MMP_PCM_INFO, 379724da053SLubomir Rintel .period_bytes_min = 1024, 380724da053SLubomir Rintel .period_bytes_max = 2048, 381724da053SLubomir Rintel .periods_min = 2, 382724da053SLubomir Rintel .periods_max = 32, 383724da053SLubomir Rintel .buffer_bytes_max = 4096, 384724da053SLubomir Rintel .fifo_size = 32, 385724da053SLubomir Rintel }, 386724da053SLubomir Rintel }; 387724da053SLubomir Rintel 388724da053SLubomir Rintel static const struct snd_dmaengine_pcm_config mmp_pcm_config = { 389724da053SLubomir Rintel .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 390724da053SLubomir Rintel .pcm_hardware = mmp_pcm_hardware, 391724da053SLubomir Rintel .prealloc_buffer_size = 4096, 392724da053SLubomir Rintel }; 393724da053SLubomir Rintel 394724da053SLubomir Rintel static int mmp_pcm_mmap(struct snd_soc_component *component, 395724da053SLubomir Rintel struct snd_pcm_substream *substream, 396724da053SLubomir Rintel struct vm_area_struct *vma) 397724da053SLubomir Rintel { 398724da053SLubomir Rintel vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; 399724da053SLubomir Rintel vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 400724da053SLubomir Rintel return remap_pfn_range(vma, vma->vm_start, 401724da053SLubomir Rintel substream->dma_buffer.addr >> PAGE_SHIFT, 402724da053SLubomir Rintel vma->vm_end - vma->vm_start, vma->vm_page_prot); 403724da053SLubomir Rintel } 404724da053SLubomir Rintel 4057d98cc64SLubomir Rintel static int mmp_sspa_open(struct snd_soc_component *component, 4067d98cc64SLubomir Rintel struct snd_pcm_substream *substream) 4077d98cc64SLubomir Rintel { 4087d98cc64SLubomir Rintel struct sspa_priv *sspa = snd_soc_component_get_drvdata(component); 4097d98cc64SLubomir Rintel 4107d98cc64SLubomir Rintel pm_runtime_get_sync(component->dev); 4117d98cc64SLubomir Rintel 4127d98cc64SLubomir Rintel /* we can only change the settings if the port is not in use */ 4137d98cc64SLubomir Rintel if ((mmp_sspa_read_reg(sspa, SSPA_TXSP) & SSPA_SP_S_EN) || 4147d98cc64SLubomir Rintel (mmp_sspa_read_reg(sspa, SSPA_RXSP) & SSPA_SP_S_EN)) { 4157d98cc64SLubomir Rintel dev_err(component->dev, 4167d98cc64SLubomir Rintel "can't change hardware dai format: stream is in use\n"); 4177d98cc64SLubomir Rintel return -EBUSY; 4187d98cc64SLubomir Rintel } 4197d98cc64SLubomir Rintel 4207d98cc64SLubomir Rintel mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa->sp); 4217d98cc64SLubomir Rintel mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa->sp); 4227d98cc64SLubomir Rintel 4237d98cc64SLubomir Rintel sspa->sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH); 4247d98cc64SLubomir Rintel mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa->sp); 4257d98cc64SLubomir Rintel mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa->sp); 4267d98cc64SLubomir Rintel 4277d98cc64SLubomir Rintel /* 4287d98cc64SLubomir Rintel * FIXME: hw issue, for the tx serial port, 4297d98cc64SLubomir Rintel * can not config the master/slave mode; 4307d98cc64SLubomir Rintel * so must clean this bit. 4317d98cc64SLubomir Rintel * The master/slave mode has been set in the 4327d98cc64SLubomir Rintel * rx port. 4337d98cc64SLubomir Rintel */ 4347d98cc64SLubomir Rintel mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa->sp & ~SSPA_SP_MSL); 4357d98cc64SLubomir Rintel 4367d98cc64SLubomir Rintel mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa->ctrl); 4377d98cc64SLubomir Rintel mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa->ctrl); 4387d98cc64SLubomir Rintel 4397d98cc64SLubomir Rintel return 0; 4407d98cc64SLubomir Rintel } 4417d98cc64SLubomir Rintel 4427d98cc64SLubomir Rintel static int mmp_sspa_close(struct snd_soc_component *component, 4437d98cc64SLubomir Rintel struct snd_pcm_substream *substream) 4447d98cc64SLubomir Rintel { 4457d98cc64SLubomir Rintel pm_runtime_put_sync(component->dev); 4467d98cc64SLubomir Rintel return 0; 4477d98cc64SLubomir Rintel } 4487d98cc64SLubomir Rintel 449425f3708SKuninori Morimoto static const struct snd_soc_component_driver mmp_sspa_component = { 450425f3708SKuninori Morimoto .name = "mmp-sspa", 451724da053SLubomir Rintel .mmap = mmp_pcm_mmap, 4527d98cc64SLubomir Rintel .open = mmp_sspa_open, 4537d98cc64SLubomir Rintel .close = mmp_sspa_close, 454425f3708SKuninori Morimoto }; 455425f3708SKuninori Morimoto 456570f6fe1SBill Pemberton static int asoc_mmp_sspa_probe(struct platform_device *pdev) 457fa375d42SZhangfei Gao { 4583c4e89dfSLubomir Rintel struct sspa_priv *sspa; 459fa375d42SZhangfei Gao 4603c4e89dfSLubomir Rintel sspa = devm_kzalloc(&pdev->dev, 461fa375d42SZhangfei Gao sizeof(struct sspa_priv), GFP_KERNEL); 4623c4e89dfSLubomir Rintel if (!sspa) 463fa375d42SZhangfei Gao return -ENOMEM; 464fa375d42SZhangfei Gao 4653c4e89dfSLubomir Rintel sspa->mmio_base = devm_platform_ioremap_resource(pdev, 0); 4663c4e89dfSLubomir Rintel if (IS_ERR(sspa->mmio_base)) 4673c4e89dfSLubomir Rintel return PTR_ERR(sspa->mmio_base); 468fa375d42SZhangfei Gao 4693c4e89dfSLubomir Rintel sspa->clk = devm_clk_get(&pdev->dev, NULL); 4703c4e89dfSLubomir Rintel if (IS_ERR(sspa->clk)) 4713c4e89dfSLubomir Rintel return PTR_ERR(sspa->clk); 472fa375d42SZhangfei Gao 4733c4e89dfSLubomir Rintel sspa->audio_clk = clk_get(NULL, "mmp-audio"); 4743c4e89dfSLubomir Rintel if (IS_ERR(sspa->audio_clk)) 4753c4e89dfSLubomir Rintel return PTR_ERR(sspa->audio_clk); 476fa375d42SZhangfei Gao 4773c4e89dfSLubomir Rintel sspa->sysclk = clk_get(NULL, "mmp-sysclk"); 4783c4e89dfSLubomir Rintel if (IS_ERR(sspa->sysclk)) { 4793c4e89dfSLubomir Rintel clk_put(sspa->audio_clk); 4803c4e89dfSLubomir Rintel return PTR_ERR(sspa->sysclk); 481fa375d42SZhangfei Gao } 4827d98cc64SLubomir Rintel pm_runtime_enable(&pdev->dev); 4838ecdcac8SLubomir Rintel clk_prepare_enable(sspa->audio_clk); 4843c4e89dfSLubomir Rintel platform_set_drvdata(pdev, sspa); 485fa375d42SZhangfei Gao 4863c4e89dfSLubomir Rintel sspa->playback_dma_data.maxburst = 4; 4873c4e89dfSLubomir Rintel sspa->capture_dma_data.maxburst = 4; 488c9aeda1cSLubomir Rintel /* You know, these addresses are actually ignored. */ 4893c4e89dfSLubomir Rintel sspa->playback_dma_data.addr = SSPA_TXD; 4903c4e89dfSLubomir Rintel sspa->capture_dma_data.addr = SSPA_RXD; 491c9aeda1cSLubomir Rintel 492724da053SLubomir Rintel if (pdev->dev.of_node) { 493724da053SLubomir Rintel int ret; 494724da053SLubomir Rintel 495724da053SLubomir Rintel ret = devm_snd_dmaengine_pcm_register(&pdev->dev, 496724da053SLubomir Rintel &mmp_pcm_config, 0); 497724da053SLubomir Rintel if (ret) 498724da053SLubomir Rintel return ret; 499724da053SLubomir Rintel } 500724da053SLubomir Rintel 5019ff50721SSachin Kamat return devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component, 502425f3708SKuninori Morimoto &mmp_sspa_dai, 1); 503fa375d42SZhangfei Gao } 504fa375d42SZhangfei Gao 505570f6fe1SBill Pemberton static int asoc_mmp_sspa_remove(struct platform_device *pdev) 506fa375d42SZhangfei Gao { 5073c4e89dfSLubomir Rintel struct sspa_priv *sspa = platform_get_drvdata(pdev); 508fa375d42SZhangfei Gao 5098ecdcac8SLubomir Rintel clk_disable_unprepare(sspa->audio_clk); 5107d98cc64SLubomir Rintel pm_runtime_disable(&pdev->dev); 5113c4e89dfSLubomir Rintel clk_put(sspa->audio_clk); 5123c4e89dfSLubomir Rintel clk_put(sspa->sysclk); 513fa375d42SZhangfei Gao return 0; 514fa375d42SZhangfei Gao } 515fa375d42SZhangfei Gao 516fa375d42SZhangfei Gao static struct platform_driver asoc_mmp_sspa_driver = { 517fa375d42SZhangfei Gao .driver = { 518fa375d42SZhangfei Gao .name = "mmp-sspa-dai", 519fa375d42SZhangfei Gao }, 520fa375d42SZhangfei Gao .probe = asoc_mmp_sspa_probe, 521570f6fe1SBill Pemberton .remove = asoc_mmp_sspa_remove, 522fa375d42SZhangfei Gao }; 523fa375d42SZhangfei Gao 524fa375d42SZhangfei Gao module_platform_driver(asoc_mmp_sspa_driver); 525fa375d42SZhangfei Gao 526fa375d42SZhangfei Gao MODULE_AUTHOR("Leo Yan <leoy@marvell.com>"); 527fa375d42SZhangfei Gao MODULE_DESCRIPTION("MMP SSPA SoC Interface"); 528fa375d42SZhangfei Gao MODULE_LICENSE("GPL"); 529e5b7d71aSAndrea Adami MODULE_ALIAS("platform:mmp-sspa-dai"); 530