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 { 31a97e384bSLubomir Rintel void __iomem *tx_base; 32a97e384bSLubomir Rintel void __iomem *rx_base; 33a97e384bSLubomir Rintel 34c9aeda1cSLubomir Rintel struct snd_dmaengine_dai_dma_data playback_dma_data; 35c9aeda1cSLubomir Rintel struct snd_dmaengine_dai_dma_data capture_dma_data; 363c4e89dfSLubomir Rintel struct clk *clk; 37fa375d42SZhangfei Gao struct clk *audio_clk; 38fa375d42SZhangfei Gao struct clk *sysclk; 39a97e384bSLubomir Rintel 40fa375d42SZhangfei Gao int running_cnt; 417d98cc64SLubomir Rintel u32 sp; 427d98cc64SLubomir Rintel u32 ctrl; 43fa375d42SZhangfei Gao }; 44fa375d42SZhangfei Gao 453c4e89dfSLubomir Rintel static void mmp_sspa_tx_enable(struct sspa_priv *sspa) 46fa375d42SZhangfei Gao { 477d98cc64SLubomir Rintel unsigned int sspa_sp = sspa->sp; 48fa375d42SZhangfei Gao 497d98cc64SLubomir Rintel sspa_sp &= ~SSPA_SP_MSL; 50fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_S_EN; 51fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 52a97e384bSLubomir Rintel __raw_writel(sspa_sp, sspa->tx_base + SSPA_SP); 53fa375d42SZhangfei Gao } 54fa375d42SZhangfei Gao 553c4e89dfSLubomir Rintel static void mmp_sspa_tx_disable(struct sspa_priv *sspa) 56fa375d42SZhangfei Gao { 577d98cc64SLubomir Rintel unsigned int sspa_sp = sspa->sp; 58fa375d42SZhangfei Gao 597d98cc64SLubomir Rintel sspa_sp &= ~SSPA_SP_MSL; 60fa375d42SZhangfei Gao sspa_sp &= ~SSPA_SP_S_EN; 61fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 62a97e384bSLubomir Rintel __raw_writel(sspa_sp, sspa->tx_base + SSPA_SP); 63fa375d42SZhangfei Gao } 64fa375d42SZhangfei Gao 653c4e89dfSLubomir Rintel static void mmp_sspa_rx_enable(struct sspa_priv *sspa) 66fa375d42SZhangfei Gao { 677d98cc64SLubomir Rintel unsigned int sspa_sp = sspa->sp; 68fa375d42SZhangfei Gao 69fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_S_EN; 70fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 71a97e384bSLubomir Rintel __raw_writel(sspa_sp, sspa->rx_base + SSPA_SP); 72fa375d42SZhangfei Gao } 73fa375d42SZhangfei Gao 743c4e89dfSLubomir Rintel static void mmp_sspa_rx_disable(struct sspa_priv *sspa) 75fa375d42SZhangfei Gao { 767d98cc64SLubomir Rintel unsigned int sspa_sp = sspa->sp; 77fa375d42SZhangfei Gao 78fa375d42SZhangfei Gao sspa_sp &= ~SSPA_SP_S_EN; 79fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 80a97e384bSLubomir Rintel __raw_writel(sspa_sp, sspa->rx_base + SSPA_SP); 81fa375d42SZhangfei Gao } 82fa375d42SZhangfei Gao 83fa375d42SZhangfei Gao static int mmp_sspa_startup(struct snd_pcm_substream *substream, 84fa375d42SZhangfei Gao struct snd_soc_dai *dai) 85fa375d42SZhangfei Gao { 863c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 87fa375d42SZhangfei Gao 888ecdcac8SLubomir Rintel clk_prepare_enable(sspa->sysclk); 898ecdcac8SLubomir Rintel clk_prepare_enable(sspa->clk); 90fa375d42SZhangfei Gao 91fa375d42SZhangfei Gao return 0; 92fa375d42SZhangfei Gao } 93fa375d42SZhangfei Gao 94fa375d42SZhangfei Gao static void mmp_sspa_shutdown(struct snd_pcm_substream *substream, 95fa375d42SZhangfei Gao struct snd_soc_dai *dai) 96fa375d42SZhangfei Gao { 973c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 98fa375d42SZhangfei Gao 998ecdcac8SLubomir Rintel clk_disable_unprepare(sspa->clk); 1008ecdcac8SLubomir Rintel clk_disable_unprepare(sspa->sysclk); 101fa375d42SZhangfei Gao } 102fa375d42SZhangfei Gao 103fa375d42SZhangfei Gao /* 104fa375d42SZhangfei Gao * Set the SSP ports SYSCLK. 105fa375d42SZhangfei Gao */ 106fa375d42SZhangfei Gao static int mmp_sspa_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 107fa375d42SZhangfei Gao int clk_id, unsigned int freq, int dir) 108fa375d42SZhangfei Gao { 1093c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai); 110a97e384bSLubomir Rintel struct device *dev = cpu_dai->component->dev; 111fa375d42SZhangfei Gao int ret = 0; 112fa375d42SZhangfei Gao 113a97e384bSLubomir Rintel if (dev->of_node) 114a97e384bSLubomir Rintel return -ENOTSUPP; 115a97e384bSLubomir Rintel 116fa375d42SZhangfei Gao switch (clk_id) { 117fa375d42SZhangfei Gao case MMP_SSPA_CLK_AUDIO: 1183c4e89dfSLubomir Rintel ret = clk_set_rate(sspa->audio_clk, freq); 119fa375d42SZhangfei Gao if (ret) 120fa375d42SZhangfei Gao return ret; 121fa375d42SZhangfei Gao break; 122fa375d42SZhangfei Gao case MMP_SSPA_CLK_PLL: 123fa375d42SZhangfei Gao case MMP_SSPA_CLK_VCXO: 124fa375d42SZhangfei Gao /* not support yet */ 125fa375d42SZhangfei Gao return -EINVAL; 126fa375d42SZhangfei Gao default: 127fa375d42SZhangfei Gao return -EINVAL; 128fa375d42SZhangfei Gao } 129fa375d42SZhangfei Gao 130fa375d42SZhangfei Gao return 0; 131fa375d42SZhangfei Gao } 132fa375d42SZhangfei Gao 133fa375d42SZhangfei Gao static int mmp_sspa_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, 134fa375d42SZhangfei Gao int source, unsigned int freq_in, 135fa375d42SZhangfei Gao unsigned int freq_out) 136fa375d42SZhangfei Gao { 1373c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai); 138a97e384bSLubomir Rintel struct device *dev = cpu_dai->component->dev; 139fa375d42SZhangfei Gao int ret = 0; 140fa375d42SZhangfei Gao 141a97e384bSLubomir Rintel if (dev->of_node) 142a97e384bSLubomir Rintel return -ENOTSUPP; 143a97e384bSLubomir Rintel 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 17484c5b47cSCharles Keepax switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 17584c5b47cSCharles Keepax case SND_SOC_DAIFMT_BP_FP: 1767d98cc64SLubomir Rintel sspa->sp |= SSPA_SP_MSL; 177fa375d42SZhangfei Gao break; 17884c5b47cSCharles Keepax case SND_SOC_DAIFMT_BC_FC: 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); 216a97e384bSLubomir Rintel struct device *dev = dai->component->dev; 2177d98cc64SLubomir Rintel u32 sspa_ctrl = sspa->ctrl; 21839ec7e9bSLubomir Rintel int bits; 21939ec7e9bSLubomir Rintel int bitval; 220fa375d42SZhangfei Gao 221fa375d42SZhangfei Gao switch (params_format(params)) { 222fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S8: 22339ec7e9bSLubomir Rintel bits = 8; 22439ec7e9bSLubomir Rintel bitval = SSPA_CTL_8_BITS; 225fa375d42SZhangfei Gao break; 226fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S16_LE: 22739ec7e9bSLubomir Rintel bits = 16; 22839ec7e9bSLubomir Rintel bitval = SSPA_CTL_16_BITS; 229fa375d42SZhangfei Gao break; 230fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S24_3LE: 23139ec7e9bSLubomir Rintel bits = 24; 23239ec7e9bSLubomir Rintel bitval = SSPA_CTL_24_BITS; 233fa375d42SZhangfei Gao break; 234fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S32_LE: 23539ec7e9bSLubomir Rintel bits = 32; 23639ec7e9bSLubomir Rintel bitval = SSPA_CTL_32_BITS; 237fa375d42SZhangfei Gao break; 238fa375d42SZhangfei Gao default: 239fa375d42SZhangfei Gao return -EINVAL; 240fa375d42SZhangfei Gao } 241fa375d42SZhangfei Gao 242b88b31f4SKyle Russell sspa_ctrl &= ~SSPA_CTL_XPH; 243a97e384bSLubomir Rintel if (dev->of_node || params_channels(params) == 2) 24439ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XPH; 24539ec7e9bSLubomir Rintel 24639ec7e9bSLubomir Rintel sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK; 24739ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XWDLEN1(bitval); 24839ec7e9bSLubomir Rintel 24982d1aeb8SKyle Russell sspa_ctrl &= ~SSPA_CTL_XWDLEN2_MASK; 25082d1aeb8SKyle Russell sspa_ctrl |= SSPA_CTL_XWDLEN2(bitval); 25182d1aeb8SKyle Russell 25239ec7e9bSLubomir Rintel sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK; 25339ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XSSZ1(bitval); 25439ec7e9bSLubomir Rintel 25539ec7e9bSLubomir Rintel sspa_ctrl &= ~SSPA_CTL_XSSZ2_MASK; 25639ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XSSZ2(bitval); 25739ec7e9bSLubomir Rintel 25839ec7e9bSLubomir Rintel sspa->sp &= ~SSPA_SP_FWID_MASK; 25939ec7e9bSLubomir Rintel sspa->sp |= SSPA_SP_FWID(bits - 1); 26039ec7e9bSLubomir Rintel 26139ec7e9bSLubomir Rintel sspa->sp &= ~SSPA_TXSP_FPER_MASK; 26239ec7e9bSLubomir Rintel sspa->sp |= SSPA_TXSP_FPER(bits * 2 - 1); 26339ec7e9bSLubomir Rintel 264a97e384bSLubomir Rintel if (dev->of_node) { 265a97e384bSLubomir Rintel clk_set_rate(sspa->clk, params_rate(params) * 266a97e384bSLubomir Rintel params_channels(params) * bits); 267a97e384bSLubomir Rintel } 268a97e384bSLubomir Rintel 269fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 270a97e384bSLubomir Rintel __raw_writel(sspa_ctrl, sspa->tx_base + SSPA_CTL); 271a97e384bSLubomir Rintel __raw_writel(0x1, sspa->tx_base + SSPA_FIFO_UL); 272fa375d42SZhangfei Gao } else { 273a97e384bSLubomir Rintel __raw_writel(sspa_ctrl, sspa->rx_base + SSPA_CTL); 274a97e384bSLubomir Rintel __raw_writel(0x0, sspa->rx_base + SSPA_FIFO_UL); 275fa375d42SZhangfei Gao } 276fa375d42SZhangfei Gao 277fa375d42SZhangfei Gao return 0; 278fa375d42SZhangfei Gao } 279fa375d42SZhangfei Gao 280fa375d42SZhangfei Gao static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd, 281fa375d42SZhangfei Gao struct snd_soc_dai *dai) 282fa375d42SZhangfei Gao { 2833c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 284fa375d42SZhangfei Gao int ret = 0; 285fa375d42SZhangfei Gao 286fa375d42SZhangfei Gao switch (cmd) { 287fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_START: 288fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_RESUME: 289fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 290fa375d42SZhangfei Gao /* 291fa375d42SZhangfei Gao * whatever playback or capture, must enable rx. 292fa375d42SZhangfei Gao * this is a hw issue, so need check if rx has been 293fa375d42SZhangfei Gao * enabled or not; if has been enabled by another 294fa375d42SZhangfei Gao * stream, do not enable again. 295fa375d42SZhangfei Gao */ 2963c4e89dfSLubomir Rintel if (!sspa->running_cnt) 297fa375d42SZhangfei Gao mmp_sspa_rx_enable(sspa); 298fa375d42SZhangfei Gao 299fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 300fa375d42SZhangfei Gao mmp_sspa_tx_enable(sspa); 301fa375d42SZhangfei Gao 3023c4e89dfSLubomir Rintel sspa->running_cnt++; 303fa375d42SZhangfei Gao break; 304fa375d42SZhangfei Gao 305fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_STOP: 306fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_SUSPEND: 307fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3083c4e89dfSLubomir Rintel sspa->running_cnt--; 309fa375d42SZhangfei Gao 310fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 311fa375d42SZhangfei Gao mmp_sspa_tx_disable(sspa); 312fa375d42SZhangfei Gao 313fa375d42SZhangfei Gao /* have no capture stream, disable rx port */ 3143c4e89dfSLubomir Rintel if (!sspa->running_cnt) 315fa375d42SZhangfei Gao mmp_sspa_rx_disable(sspa); 316fa375d42SZhangfei Gao break; 317fa375d42SZhangfei Gao 318fa375d42SZhangfei Gao default: 319fa375d42SZhangfei Gao ret = -EINVAL; 320fa375d42SZhangfei Gao } 321fa375d42SZhangfei Gao 322fa375d42SZhangfei Gao return ret; 323fa375d42SZhangfei Gao } 324fa375d42SZhangfei Gao 325fa375d42SZhangfei Gao static int mmp_sspa_probe(struct snd_soc_dai *dai) 326fa375d42SZhangfei Gao { 3273c4e89dfSLubomir Rintel struct sspa_priv *sspa = dev_get_drvdata(dai->dev); 328fa375d42SZhangfei Gao 329c9aeda1cSLubomir Rintel snd_soc_dai_init_dma_data(dai, 3303c4e89dfSLubomir Rintel &sspa->playback_dma_data, 3313c4e89dfSLubomir Rintel &sspa->capture_dma_data); 332c9aeda1cSLubomir Rintel 333fa375d42SZhangfei Gao return 0; 334fa375d42SZhangfei Gao } 335fa375d42SZhangfei Gao 336fa375d42SZhangfei Gao #define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000 337fa375d42SZhangfei Gao #define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 338fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S16_LE | \ 33900a1aca2SLubomir Rintel SNDRV_PCM_FMTBIT_S24_3LE | \ 340fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S32_LE) 341fa375d42SZhangfei Gao 342cb753443SGustavo A. R. Silva static const struct snd_soc_dai_ops mmp_sspa_dai_ops = { 343fa375d42SZhangfei Gao .startup = mmp_sspa_startup, 344fa375d42SZhangfei Gao .shutdown = mmp_sspa_shutdown, 345fa375d42SZhangfei Gao .trigger = mmp_sspa_trigger, 346fa375d42SZhangfei Gao .hw_params = mmp_sspa_hw_params, 347fa375d42SZhangfei Gao .set_sysclk = mmp_sspa_set_dai_sysclk, 348fa375d42SZhangfei Gao .set_pll = mmp_sspa_set_dai_pll, 3498e2cc2b2SCharles Keepax .set_fmt = mmp_sspa_set_dai_fmt, 350fa375d42SZhangfei Gao }; 351fa375d42SZhangfei Gao 3525d9ff402SLars-Peter Clausen static struct snd_soc_dai_driver mmp_sspa_dai = { 353fa375d42SZhangfei Gao .probe = mmp_sspa_probe, 354fa375d42SZhangfei Gao .playback = { 355fa375d42SZhangfei Gao .channels_min = 1, 356fa375d42SZhangfei Gao .channels_max = 128, 357fa375d42SZhangfei Gao .rates = MMP_SSPA_RATES, 358fa375d42SZhangfei Gao .formats = MMP_SSPA_FORMATS, 359fa375d42SZhangfei Gao }, 360fa375d42SZhangfei Gao .capture = { 361fa375d42SZhangfei Gao .channels_min = 1, 362fa375d42SZhangfei Gao .channels_max = 2, 363fa375d42SZhangfei Gao .rates = MMP_SSPA_RATES, 364fa375d42SZhangfei Gao .formats = MMP_SSPA_FORMATS, 365fa375d42SZhangfei Gao }, 366fa375d42SZhangfei Gao .ops = &mmp_sspa_dai_ops, 367fa375d42SZhangfei Gao }; 368fa375d42SZhangfei Gao 369724da053SLubomir Rintel #define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ 370724da053SLubomir Rintel SNDRV_PCM_INFO_MMAP_VALID | \ 371724da053SLubomir Rintel SNDRV_PCM_INFO_INTERLEAVED | \ 372724da053SLubomir Rintel SNDRV_PCM_INFO_PAUSE | \ 373724da053SLubomir Rintel SNDRV_PCM_INFO_RESUME | \ 374724da053SLubomir Rintel SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) 375724da053SLubomir Rintel 376724da053SLubomir Rintel static const struct snd_pcm_hardware mmp_pcm_hardware[] = { 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 .info = MMP_PCM_INFO, 388724da053SLubomir Rintel .period_bytes_min = 1024, 389724da053SLubomir Rintel .period_bytes_max = 2048, 390724da053SLubomir Rintel .periods_min = 2, 391724da053SLubomir Rintel .periods_max = 32, 392724da053SLubomir Rintel .buffer_bytes_max = 4096, 393724da053SLubomir Rintel .fifo_size = 32, 394724da053SLubomir Rintel }, 395724da053SLubomir Rintel }; 396724da053SLubomir Rintel 397724da053SLubomir Rintel static const struct snd_dmaengine_pcm_config mmp_pcm_config = { 398724da053SLubomir Rintel .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 399724da053SLubomir Rintel .pcm_hardware = mmp_pcm_hardware, 400724da053SLubomir Rintel .prealloc_buffer_size = 4096, 401724da053SLubomir Rintel }; 402724da053SLubomir Rintel 403724da053SLubomir Rintel static int mmp_pcm_mmap(struct snd_soc_component *component, 404724da053SLubomir Rintel struct snd_pcm_substream *substream, 405724da053SLubomir Rintel struct vm_area_struct *vma) 406724da053SLubomir Rintel { 407*1c71222eSSuren Baghdasaryan vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP); 408724da053SLubomir Rintel vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 409724da053SLubomir Rintel return remap_pfn_range(vma, vma->vm_start, 410724da053SLubomir Rintel substream->dma_buffer.addr >> PAGE_SHIFT, 411724da053SLubomir Rintel vma->vm_end - vma->vm_start, vma->vm_page_prot); 412724da053SLubomir Rintel } 413724da053SLubomir Rintel 4147d98cc64SLubomir Rintel static int mmp_sspa_open(struct snd_soc_component *component, 4157d98cc64SLubomir Rintel struct snd_pcm_substream *substream) 4167d98cc64SLubomir Rintel { 4177d98cc64SLubomir Rintel struct sspa_priv *sspa = snd_soc_component_get_drvdata(component); 4187d98cc64SLubomir Rintel 4197d98cc64SLubomir Rintel pm_runtime_get_sync(component->dev); 4207d98cc64SLubomir Rintel 4217d98cc64SLubomir Rintel /* we can only change the settings if the port is not in use */ 422a97e384bSLubomir Rintel if ((__raw_readl(sspa->tx_base + SSPA_SP) & SSPA_SP_S_EN) || 423a97e384bSLubomir Rintel (__raw_readl(sspa->rx_base + SSPA_SP) & SSPA_SP_S_EN)) { 4247d98cc64SLubomir Rintel dev_err(component->dev, 4257d98cc64SLubomir Rintel "can't change hardware dai format: stream is in use\n"); 4267d98cc64SLubomir Rintel return -EBUSY; 4277d98cc64SLubomir Rintel } 4287d98cc64SLubomir Rintel 429a97e384bSLubomir Rintel __raw_writel(sspa->sp, sspa->tx_base + SSPA_SP); 430a97e384bSLubomir Rintel __raw_writel(sspa->sp, sspa->rx_base + SSPA_SP); 4317d98cc64SLubomir Rintel 4327d98cc64SLubomir Rintel sspa->sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH); 433a97e384bSLubomir Rintel __raw_writel(sspa->sp, sspa->tx_base + SSPA_SP); 434a97e384bSLubomir Rintel __raw_writel(sspa->sp, sspa->rx_base + SSPA_SP); 4357d98cc64SLubomir Rintel 4367d98cc64SLubomir Rintel /* 4377d98cc64SLubomir Rintel * FIXME: hw issue, for the tx serial port, 4387d98cc64SLubomir Rintel * can not config the master/slave mode; 4397d98cc64SLubomir Rintel * so must clean this bit. 4407d98cc64SLubomir Rintel * The master/slave mode has been set in the 4417d98cc64SLubomir Rintel * rx port. 4427d98cc64SLubomir Rintel */ 443a97e384bSLubomir Rintel __raw_writel(sspa->sp & ~SSPA_SP_MSL, sspa->tx_base + SSPA_SP); 4447d98cc64SLubomir Rintel 445a97e384bSLubomir Rintel __raw_writel(sspa->ctrl, sspa->tx_base + SSPA_CTL); 446a97e384bSLubomir Rintel __raw_writel(sspa->ctrl, sspa->rx_base + SSPA_CTL); 4477d98cc64SLubomir Rintel 4487d98cc64SLubomir Rintel return 0; 4497d98cc64SLubomir Rintel } 4507d98cc64SLubomir Rintel 4517d98cc64SLubomir Rintel static int mmp_sspa_close(struct snd_soc_component *component, 4527d98cc64SLubomir Rintel struct snd_pcm_substream *substream) 4537d98cc64SLubomir Rintel { 4547d98cc64SLubomir Rintel pm_runtime_put_sync(component->dev); 4557d98cc64SLubomir Rintel return 0; 4567d98cc64SLubomir Rintel } 4577d98cc64SLubomir Rintel 458425f3708SKuninori Morimoto static const struct snd_soc_component_driver mmp_sspa_component = { 459425f3708SKuninori Morimoto .name = "mmp-sspa", 460724da053SLubomir Rintel .mmap = mmp_pcm_mmap, 4617d98cc64SLubomir Rintel .open = mmp_sspa_open, 4627d98cc64SLubomir Rintel .close = mmp_sspa_close, 46305603f15SCharles Keepax .legacy_dai_naming = 1, 464425f3708SKuninori Morimoto }; 465425f3708SKuninori Morimoto 466570f6fe1SBill Pemberton static int asoc_mmp_sspa_probe(struct platform_device *pdev) 467fa375d42SZhangfei Gao { 4683c4e89dfSLubomir Rintel struct sspa_priv *sspa; 4696ea460d5SLubomir Rintel int ret; 470fa375d42SZhangfei Gao 4713c4e89dfSLubomir Rintel sspa = devm_kzalloc(&pdev->dev, 472fa375d42SZhangfei Gao sizeof(struct sspa_priv), GFP_KERNEL); 4733c4e89dfSLubomir Rintel if (!sspa) 474fa375d42SZhangfei Gao return -ENOMEM; 475fa375d42SZhangfei Gao 476a97e384bSLubomir Rintel if (pdev->dev.of_node) { 477a97e384bSLubomir Rintel sspa->rx_base = devm_platform_ioremap_resource(pdev, 0); 478a97e384bSLubomir Rintel if (IS_ERR(sspa->rx_base)) 479a97e384bSLubomir Rintel return PTR_ERR(sspa->rx_base); 480a97e384bSLubomir Rintel 481a97e384bSLubomir Rintel sspa->tx_base = devm_platform_ioremap_resource(pdev, 1); 482a97e384bSLubomir Rintel if (IS_ERR(sspa->tx_base)) 483a97e384bSLubomir Rintel return PTR_ERR(sspa->tx_base); 484a97e384bSLubomir Rintel 485a97e384bSLubomir Rintel sspa->clk = devm_clk_get(&pdev->dev, "bitclk"); 486a97e384bSLubomir Rintel if (IS_ERR(sspa->clk)) 487a97e384bSLubomir Rintel return PTR_ERR(sspa->clk); 488a97e384bSLubomir Rintel 489a97e384bSLubomir Rintel sspa->audio_clk = devm_clk_get(&pdev->dev, "audio"); 490a97e384bSLubomir Rintel if (IS_ERR(sspa->audio_clk)) 491a97e384bSLubomir Rintel return PTR_ERR(sspa->audio_clk); 492a97e384bSLubomir Rintel } else { 493a97e384bSLubomir Rintel struct resource *res; 494a97e384bSLubomir Rintel 495a97e384bSLubomir Rintel res = platform_get_resource(pdev, IORESOURCE_IO, 0); 496a97e384bSLubomir Rintel if (res == NULL) 497a97e384bSLubomir Rintel return -ENODEV; 498a97e384bSLubomir Rintel 499a97e384bSLubomir Rintel sspa->rx_base = devm_ioremap(&pdev->dev, res->start, 0x30); 50018545763SWei Yongjun if (!sspa->rx_base) 50118545763SWei Yongjun return -ENOMEM; 502a97e384bSLubomir Rintel 503a97e384bSLubomir Rintel sspa->tx_base = devm_ioremap(&pdev->dev, 504a97e384bSLubomir Rintel res->start + 0x80, 0x30); 50518545763SWei Yongjun if (!sspa->tx_base) 50618545763SWei Yongjun return -ENOMEM; 507fa375d42SZhangfei Gao 5083c4e89dfSLubomir Rintel sspa->clk = devm_clk_get(&pdev->dev, NULL); 5093c4e89dfSLubomir Rintel if (IS_ERR(sspa->clk)) 5103c4e89dfSLubomir Rintel return PTR_ERR(sspa->clk); 511fa375d42SZhangfei Gao 5123c4e89dfSLubomir Rintel sspa->audio_clk = clk_get(NULL, "mmp-audio"); 5133c4e89dfSLubomir Rintel if (IS_ERR(sspa->audio_clk)) 5143c4e89dfSLubomir Rintel return PTR_ERR(sspa->audio_clk); 515fa375d42SZhangfei Gao 5163c4e89dfSLubomir Rintel sspa->sysclk = clk_get(NULL, "mmp-sysclk"); 5173c4e89dfSLubomir Rintel if (IS_ERR(sspa->sysclk)) { 5183c4e89dfSLubomir Rintel clk_put(sspa->audio_clk); 5193c4e89dfSLubomir Rintel return PTR_ERR(sspa->sysclk); 520fa375d42SZhangfei Gao } 521a97e384bSLubomir Rintel } 5223c4e89dfSLubomir Rintel platform_set_drvdata(pdev, sspa); 523fa375d42SZhangfei Gao 5243c4e89dfSLubomir Rintel sspa->playback_dma_data.maxburst = 4; 5253c4e89dfSLubomir Rintel sspa->capture_dma_data.maxburst = 4; 526c9aeda1cSLubomir Rintel /* You know, these addresses are actually ignored. */ 527a97e384bSLubomir Rintel sspa->capture_dma_data.addr = SSPA_D; 528a97e384bSLubomir Rintel sspa->playback_dma_data.addr = 0x80 + SSPA_D; 529c9aeda1cSLubomir Rintel 530724da053SLubomir Rintel if (pdev->dev.of_node) { 531724da053SLubomir Rintel ret = devm_snd_dmaengine_pcm_register(&pdev->dev, 532724da053SLubomir Rintel &mmp_pcm_config, 0); 533724da053SLubomir Rintel if (ret) 534724da053SLubomir Rintel return ret; 535724da053SLubomir Rintel } 536724da053SLubomir Rintel 5376ea460d5SLubomir Rintel ret = devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component, 538425f3708SKuninori Morimoto &mmp_sspa_dai, 1); 5396ea460d5SLubomir Rintel if (ret) 5406ea460d5SLubomir Rintel return ret; 5416ea460d5SLubomir Rintel 5426ea460d5SLubomir Rintel pm_runtime_enable(&pdev->dev); 5436ea460d5SLubomir Rintel clk_prepare_enable(sspa->audio_clk); 5446ea460d5SLubomir Rintel 5456ea460d5SLubomir Rintel return 0; 546fa375d42SZhangfei Gao } 547fa375d42SZhangfei Gao 548570f6fe1SBill Pemberton static int asoc_mmp_sspa_remove(struct platform_device *pdev) 549fa375d42SZhangfei Gao { 5503c4e89dfSLubomir Rintel struct sspa_priv *sspa = platform_get_drvdata(pdev); 551fa375d42SZhangfei Gao 5528ecdcac8SLubomir Rintel clk_disable_unprepare(sspa->audio_clk); 5537d98cc64SLubomir Rintel pm_runtime_disable(&pdev->dev); 554a97e384bSLubomir Rintel 555a97e384bSLubomir Rintel if (pdev->dev.of_node) 556a97e384bSLubomir Rintel return 0; 557a97e384bSLubomir Rintel 5583c4e89dfSLubomir Rintel clk_put(sspa->audio_clk); 5593c4e89dfSLubomir Rintel clk_put(sspa->sysclk); 560fa375d42SZhangfei Gao return 0; 561fa375d42SZhangfei Gao } 562fa375d42SZhangfei Gao 563a97e384bSLubomir Rintel #ifdef CONFIG_OF 564a97e384bSLubomir Rintel static const struct of_device_id mmp_sspa_of_match[] = { 565a97e384bSLubomir Rintel { .compatible = "marvell,mmp-sspa" }, 566a97e384bSLubomir Rintel {}, 567a97e384bSLubomir Rintel }; 568a97e384bSLubomir Rintel 569a97e384bSLubomir Rintel MODULE_DEVICE_TABLE(of, mmp_sspa_of_match); 570a97e384bSLubomir Rintel #endif 571a97e384bSLubomir Rintel 572fa375d42SZhangfei Gao static struct platform_driver asoc_mmp_sspa_driver = { 573fa375d42SZhangfei Gao .driver = { 574fa375d42SZhangfei Gao .name = "mmp-sspa-dai", 575a97e384bSLubomir Rintel .of_match_table = of_match_ptr(mmp_sspa_of_match), 576fa375d42SZhangfei Gao }, 577fa375d42SZhangfei Gao .probe = asoc_mmp_sspa_probe, 578570f6fe1SBill Pemberton .remove = asoc_mmp_sspa_remove, 579fa375d42SZhangfei Gao }; 580fa375d42SZhangfei Gao 581fa375d42SZhangfei Gao module_platform_driver(asoc_mmp_sspa_driver); 582fa375d42SZhangfei Gao 583fa375d42SZhangfei Gao MODULE_AUTHOR("Leo Yan <leoy@marvell.com>"); 584fa375d42SZhangfei Gao MODULE_DESCRIPTION("MMP SSPA SoC Interface"); 585fa375d42SZhangfei Gao MODULE_LICENSE("GPL"); 586e5b7d71aSAndrea Adami MODULE_ALIAS("platform:mmp-sspa-dai"); 587