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 { 31*a97e384bSLubomir Rintel void __iomem *tx_base; 32*a97e384bSLubomir Rintel void __iomem *rx_base; 33*a97e384bSLubomir 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; 39*a97e384bSLubomir 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; 52*a97e384bSLubomir 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; 62*a97e384bSLubomir 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; 71*a97e384bSLubomir 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; 80*a97e384bSLubomir 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); 110*a97e384bSLubomir Rintel struct device *dev = cpu_dai->component->dev; 111fa375d42SZhangfei Gao int ret = 0; 112fa375d42SZhangfei Gao 113*a97e384bSLubomir Rintel if (dev->of_node) 114*a97e384bSLubomir Rintel return -ENOTSUPP; 115*a97e384bSLubomir 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); 138*a97e384bSLubomir Rintel struct device *dev = cpu_dai->component->dev; 139fa375d42SZhangfei Gao int ret = 0; 140fa375d42SZhangfei Gao 141*a97e384bSLubomir Rintel if (dev->of_node) 142*a97e384bSLubomir Rintel return -ENOTSUPP; 143*a97e384bSLubomir 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 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); 216*a97e384bSLubomir 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 242*a97e384bSLubomir Rintel if (dev->of_node || params_channels(params) == 2) 24339ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XPH; 24439ec7e9bSLubomir Rintel 24539ec7e9bSLubomir Rintel sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK; 24639ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XWDLEN1(bitval); 24739ec7e9bSLubomir Rintel 24839ec7e9bSLubomir Rintel sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK; 24939ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XSSZ1(bitval); 25039ec7e9bSLubomir Rintel 25139ec7e9bSLubomir Rintel sspa_ctrl &= ~SSPA_CTL_XSSZ2_MASK; 25239ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XSSZ2(bitval); 25339ec7e9bSLubomir Rintel 25439ec7e9bSLubomir Rintel sspa->sp &= ~SSPA_SP_FWID_MASK; 25539ec7e9bSLubomir Rintel sspa->sp |= SSPA_SP_FWID(bits - 1); 25639ec7e9bSLubomir Rintel 25739ec7e9bSLubomir Rintel sspa->sp &= ~SSPA_TXSP_FPER_MASK; 25839ec7e9bSLubomir Rintel sspa->sp |= SSPA_TXSP_FPER(bits * 2 - 1); 25939ec7e9bSLubomir Rintel 260*a97e384bSLubomir Rintel if (dev->of_node) { 261*a97e384bSLubomir Rintel clk_set_rate(sspa->clk, params_rate(params) * 262*a97e384bSLubomir Rintel params_channels(params) * bits); 263*a97e384bSLubomir Rintel } 264*a97e384bSLubomir Rintel 265fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 266*a97e384bSLubomir Rintel __raw_writel(sspa_ctrl, sspa->tx_base + SSPA_CTL); 267*a97e384bSLubomir Rintel __raw_writel(0x1, sspa->tx_base + SSPA_FIFO_UL); 268fa375d42SZhangfei Gao } else { 269*a97e384bSLubomir Rintel __raw_writel(sspa_ctrl, sspa->rx_base + SSPA_CTL); 270*a97e384bSLubomir Rintel __raw_writel(0x0, sspa->rx_base + SSPA_FIFO_UL); 271fa375d42SZhangfei Gao } 272fa375d42SZhangfei Gao 273fa375d42SZhangfei Gao return 0; 274fa375d42SZhangfei Gao } 275fa375d42SZhangfei Gao 276fa375d42SZhangfei Gao static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd, 277fa375d42SZhangfei Gao struct snd_soc_dai *dai) 278fa375d42SZhangfei Gao { 2793c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 280fa375d42SZhangfei Gao int ret = 0; 281fa375d42SZhangfei Gao 282fa375d42SZhangfei Gao switch (cmd) { 283fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_START: 284fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_RESUME: 285fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 286fa375d42SZhangfei Gao /* 287fa375d42SZhangfei Gao * whatever playback or capture, must enable rx. 288fa375d42SZhangfei Gao * this is a hw issue, so need check if rx has been 289fa375d42SZhangfei Gao * enabled or not; if has been enabled by another 290fa375d42SZhangfei Gao * stream, do not enable again. 291fa375d42SZhangfei Gao */ 2923c4e89dfSLubomir Rintel if (!sspa->running_cnt) 293fa375d42SZhangfei Gao mmp_sspa_rx_enable(sspa); 294fa375d42SZhangfei Gao 295fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 296fa375d42SZhangfei Gao mmp_sspa_tx_enable(sspa); 297fa375d42SZhangfei Gao 2983c4e89dfSLubomir Rintel sspa->running_cnt++; 299fa375d42SZhangfei Gao break; 300fa375d42SZhangfei Gao 301fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_STOP: 302fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_SUSPEND: 303fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3043c4e89dfSLubomir Rintel sspa->running_cnt--; 305fa375d42SZhangfei Gao 306fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 307fa375d42SZhangfei Gao mmp_sspa_tx_disable(sspa); 308fa375d42SZhangfei Gao 309fa375d42SZhangfei Gao /* have no capture stream, disable rx port */ 3103c4e89dfSLubomir Rintel if (!sspa->running_cnt) 311fa375d42SZhangfei Gao mmp_sspa_rx_disable(sspa); 312fa375d42SZhangfei Gao break; 313fa375d42SZhangfei Gao 314fa375d42SZhangfei Gao default: 315fa375d42SZhangfei Gao ret = -EINVAL; 316fa375d42SZhangfei Gao } 317fa375d42SZhangfei Gao 318fa375d42SZhangfei Gao return ret; 319fa375d42SZhangfei Gao } 320fa375d42SZhangfei Gao 321fa375d42SZhangfei Gao static int mmp_sspa_probe(struct snd_soc_dai *dai) 322fa375d42SZhangfei Gao { 3233c4e89dfSLubomir Rintel struct sspa_priv *sspa = dev_get_drvdata(dai->dev); 324fa375d42SZhangfei Gao 325c9aeda1cSLubomir Rintel snd_soc_dai_init_dma_data(dai, 3263c4e89dfSLubomir Rintel &sspa->playback_dma_data, 3273c4e89dfSLubomir Rintel &sspa->capture_dma_data); 328c9aeda1cSLubomir Rintel 3293c4e89dfSLubomir Rintel snd_soc_dai_set_drvdata(dai, sspa); 330fa375d42SZhangfei Gao return 0; 331fa375d42SZhangfei Gao } 332fa375d42SZhangfei Gao 333fa375d42SZhangfei Gao #define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000 334fa375d42SZhangfei Gao #define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 335fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S16_LE | \ 33600a1aca2SLubomir Rintel SNDRV_PCM_FMTBIT_S24_3LE | \ 337fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S32_LE) 338fa375d42SZhangfei Gao 339cb753443SGustavo A. R. Silva static const struct snd_soc_dai_ops mmp_sspa_dai_ops = { 340fa375d42SZhangfei Gao .startup = mmp_sspa_startup, 341fa375d42SZhangfei Gao .shutdown = mmp_sspa_shutdown, 342fa375d42SZhangfei Gao .trigger = mmp_sspa_trigger, 343fa375d42SZhangfei Gao .hw_params = mmp_sspa_hw_params, 344fa375d42SZhangfei Gao .set_sysclk = mmp_sspa_set_dai_sysclk, 345fa375d42SZhangfei Gao .set_pll = mmp_sspa_set_dai_pll, 346fa375d42SZhangfei Gao .set_fmt = mmp_sspa_set_dai_fmt, 347fa375d42SZhangfei Gao }; 348fa375d42SZhangfei Gao 3495d9ff402SLars-Peter Clausen static struct snd_soc_dai_driver mmp_sspa_dai = { 350fa375d42SZhangfei Gao .probe = mmp_sspa_probe, 351fa375d42SZhangfei Gao .playback = { 352fa375d42SZhangfei Gao .channels_min = 1, 353fa375d42SZhangfei Gao .channels_max = 128, 354fa375d42SZhangfei Gao .rates = MMP_SSPA_RATES, 355fa375d42SZhangfei Gao .formats = MMP_SSPA_FORMATS, 356fa375d42SZhangfei Gao }, 357fa375d42SZhangfei Gao .capture = { 358fa375d42SZhangfei Gao .channels_min = 1, 359fa375d42SZhangfei Gao .channels_max = 2, 360fa375d42SZhangfei Gao .rates = MMP_SSPA_RATES, 361fa375d42SZhangfei Gao .formats = MMP_SSPA_FORMATS, 362fa375d42SZhangfei Gao }, 363fa375d42SZhangfei Gao .ops = &mmp_sspa_dai_ops, 364fa375d42SZhangfei Gao }; 365fa375d42SZhangfei Gao 366724da053SLubomir Rintel #define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ 367724da053SLubomir Rintel SNDRV_PCM_INFO_MMAP_VALID | \ 368724da053SLubomir Rintel SNDRV_PCM_INFO_INTERLEAVED | \ 369724da053SLubomir Rintel SNDRV_PCM_INFO_PAUSE | \ 370724da053SLubomir Rintel SNDRV_PCM_INFO_RESUME | \ 371724da053SLubomir Rintel SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) 372724da053SLubomir Rintel 373724da053SLubomir Rintel static const struct snd_pcm_hardware mmp_pcm_hardware[] = { 374724da053SLubomir Rintel { 375724da053SLubomir Rintel .info = MMP_PCM_INFO, 376724da053SLubomir Rintel .period_bytes_min = 1024, 377724da053SLubomir Rintel .period_bytes_max = 2048, 378724da053SLubomir Rintel .periods_min = 2, 379724da053SLubomir Rintel .periods_max = 32, 380724da053SLubomir Rintel .buffer_bytes_max = 4096, 381724da053SLubomir Rintel .fifo_size = 32, 382724da053SLubomir Rintel }, 383724da053SLubomir Rintel { 384724da053SLubomir Rintel .info = MMP_PCM_INFO, 385724da053SLubomir Rintel .period_bytes_min = 1024, 386724da053SLubomir Rintel .period_bytes_max = 2048, 387724da053SLubomir Rintel .periods_min = 2, 388724da053SLubomir Rintel .periods_max = 32, 389724da053SLubomir Rintel .buffer_bytes_max = 4096, 390724da053SLubomir Rintel .fifo_size = 32, 391724da053SLubomir Rintel }, 392724da053SLubomir Rintel }; 393724da053SLubomir Rintel 394724da053SLubomir Rintel static const struct snd_dmaengine_pcm_config mmp_pcm_config = { 395724da053SLubomir Rintel .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 396724da053SLubomir Rintel .pcm_hardware = mmp_pcm_hardware, 397724da053SLubomir Rintel .prealloc_buffer_size = 4096, 398724da053SLubomir Rintel }; 399724da053SLubomir Rintel 400724da053SLubomir Rintel static int mmp_pcm_mmap(struct snd_soc_component *component, 401724da053SLubomir Rintel struct snd_pcm_substream *substream, 402724da053SLubomir Rintel struct vm_area_struct *vma) 403724da053SLubomir Rintel { 404724da053SLubomir Rintel vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; 405724da053SLubomir Rintel vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 406724da053SLubomir Rintel return remap_pfn_range(vma, vma->vm_start, 407724da053SLubomir Rintel substream->dma_buffer.addr >> PAGE_SHIFT, 408724da053SLubomir Rintel vma->vm_end - vma->vm_start, vma->vm_page_prot); 409724da053SLubomir Rintel } 410724da053SLubomir Rintel 4117d98cc64SLubomir Rintel static int mmp_sspa_open(struct snd_soc_component *component, 4127d98cc64SLubomir Rintel struct snd_pcm_substream *substream) 4137d98cc64SLubomir Rintel { 4147d98cc64SLubomir Rintel struct sspa_priv *sspa = snd_soc_component_get_drvdata(component); 4157d98cc64SLubomir Rintel 4167d98cc64SLubomir Rintel pm_runtime_get_sync(component->dev); 4177d98cc64SLubomir Rintel 4187d98cc64SLubomir Rintel /* we can only change the settings if the port is not in use */ 419*a97e384bSLubomir Rintel if ((__raw_readl(sspa->tx_base + SSPA_SP) & SSPA_SP_S_EN) || 420*a97e384bSLubomir Rintel (__raw_readl(sspa->rx_base + SSPA_SP) & SSPA_SP_S_EN)) { 4217d98cc64SLubomir Rintel dev_err(component->dev, 4227d98cc64SLubomir Rintel "can't change hardware dai format: stream is in use\n"); 4237d98cc64SLubomir Rintel return -EBUSY; 4247d98cc64SLubomir Rintel } 4257d98cc64SLubomir Rintel 426*a97e384bSLubomir Rintel __raw_writel(sspa->sp, sspa->tx_base + SSPA_SP); 427*a97e384bSLubomir Rintel __raw_writel(sspa->sp, sspa->rx_base + SSPA_SP); 4287d98cc64SLubomir Rintel 4297d98cc64SLubomir Rintel sspa->sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH); 430*a97e384bSLubomir Rintel __raw_writel(sspa->sp, sspa->tx_base + SSPA_SP); 431*a97e384bSLubomir Rintel __raw_writel(sspa->sp, sspa->rx_base + SSPA_SP); 4327d98cc64SLubomir Rintel 4337d98cc64SLubomir Rintel /* 4347d98cc64SLubomir Rintel * FIXME: hw issue, for the tx serial port, 4357d98cc64SLubomir Rintel * can not config the master/slave mode; 4367d98cc64SLubomir Rintel * so must clean this bit. 4377d98cc64SLubomir Rintel * The master/slave mode has been set in the 4387d98cc64SLubomir Rintel * rx port. 4397d98cc64SLubomir Rintel */ 440*a97e384bSLubomir Rintel __raw_writel(sspa->sp & ~SSPA_SP_MSL, sspa->tx_base + SSPA_SP); 4417d98cc64SLubomir Rintel 442*a97e384bSLubomir Rintel __raw_writel(sspa->ctrl, sspa->tx_base + SSPA_CTL); 443*a97e384bSLubomir Rintel __raw_writel(sspa->ctrl, sspa->rx_base + SSPA_CTL); 4447d98cc64SLubomir Rintel 4457d98cc64SLubomir Rintel return 0; 4467d98cc64SLubomir Rintel } 4477d98cc64SLubomir Rintel 4487d98cc64SLubomir Rintel static int mmp_sspa_close(struct snd_soc_component *component, 4497d98cc64SLubomir Rintel struct snd_pcm_substream *substream) 4507d98cc64SLubomir Rintel { 4517d98cc64SLubomir Rintel pm_runtime_put_sync(component->dev); 4527d98cc64SLubomir Rintel return 0; 4537d98cc64SLubomir Rintel } 4547d98cc64SLubomir Rintel 455425f3708SKuninori Morimoto static const struct snd_soc_component_driver mmp_sspa_component = { 456425f3708SKuninori Morimoto .name = "mmp-sspa", 457724da053SLubomir Rintel .mmap = mmp_pcm_mmap, 4587d98cc64SLubomir Rintel .open = mmp_sspa_open, 4597d98cc64SLubomir Rintel .close = mmp_sspa_close, 460425f3708SKuninori Morimoto }; 461425f3708SKuninori Morimoto 462570f6fe1SBill Pemberton static int asoc_mmp_sspa_probe(struct platform_device *pdev) 463fa375d42SZhangfei Gao { 4643c4e89dfSLubomir Rintel struct sspa_priv *sspa; 465fa375d42SZhangfei Gao 4663c4e89dfSLubomir Rintel sspa = devm_kzalloc(&pdev->dev, 467fa375d42SZhangfei Gao sizeof(struct sspa_priv), GFP_KERNEL); 4683c4e89dfSLubomir Rintel if (!sspa) 469fa375d42SZhangfei Gao return -ENOMEM; 470fa375d42SZhangfei Gao 471*a97e384bSLubomir Rintel if (pdev->dev.of_node) { 472*a97e384bSLubomir Rintel sspa->rx_base = devm_platform_ioremap_resource(pdev, 0); 473*a97e384bSLubomir Rintel if (IS_ERR(sspa->rx_base)) 474*a97e384bSLubomir Rintel return PTR_ERR(sspa->rx_base); 475*a97e384bSLubomir Rintel 476*a97e384bSLubomir Rintel sspa->tx_base = devm_platform_ioremap_resource(pdev, 1); 477*a97e384bSLubomir Rintel if (IS_ERR(sspa->tx_base)) 478*a97e384bSLubomir Rintel return PTR_ERR(sspa->tx_base); 479*a97e384bSLubomir Rintel 480*a97e384bSLubomir Rintel sspa->clk = devm_clk_get(&pdev->dev, "bitclk"); 481*a97e384bSLubomir Rintel if (IS_ERR(sspa->clk)) 482*a97e384bSLubomir Rintel return PTR_ERR(sspa->clk); 483*a97e384bSLubomir Rintel 484*a97e384bSLubomir Rintel sspa->audio_clk = devm_clk_get(&pdev->dev, "audio"); 485*a97e384bSLubomir Rintel if (IS_ERR(sspa->audio_clk)) 486*a97e384bSLubomir Rintel return PTR_ERR(sspa->audio_clk); 487*a97e384bSLubomir Rintel } else { 488*a97e384bSLubomir Rintel struct resource *res; 489*a97e384bSLubomir Rintel 490*a97e384bSLubomir Rintel res = platform_get_resource(pdev, IORESOURCE_IO, 0); 491*a97e384bSLubomir Rintel if (res == NULL) 492*a97e384bSLubomir Rintel return -ENODEV; 493*a97e384bSLubomir Rintel 494*a97e384bSLubomir Rintel sspa->rx_base = devm_ioremap(&pdev->dev, res->start, 0x30); 495*a97e384bSLubomir Rintel if (IS_ERR(sspa->rx_base)) 496*a97e384bSLubomir Rintel return PTR_ERR(sspa->rx_base); 497*a97e384bSLubomir Rintel 498*a97e384bSLubomir Rintel sspa->tx_base = devm_ioremap(&pdev->dev, 499*a97e384bSLubomir Rintel res->start + 0x80, 0x30); 500*a97e384bSLubomir Rintel if (IS_ERR(sspa->tx_base)) 501*a97e384bSLubomir Rintel return PTR_ERR(sspa->tx_base); 502fa375d42SZhangfei Gao 5033c4e89dfSLubomir Rintel sspa->clk = devm_clk_get(&pdev->dev, NULL); 5043c4e89dfSLubomir Rintel if (IS_ERR(sspa->clk)) 5053c4e89dfSLubomir Rintel return PTR_ERR(sspa->clk); 506fa375d42SZhangfei Gao 5073c4e89dfSLubomir Rintel sspa->audio_clk = clk_get(NULL, "mmp-audio"); 5083c4e89dfSLubomir Rintel if (IS_ERR(sspa->audio_clk)) 5093c4e89dfSLubomir Rintel return PTR_ERR(sspa->audio_clk); 510fa375d42SZhangfei Gao 5113c4e89dfSLubomir Rintel sspa->sysclk = clk_get(NULL, "mmp-sysclk"); 5123c4e89dfSLubomir Rintel if (IS_ERR(sspa->sysclk)) { 5133c4e89dfSLubomir Rintel clk_put(sspa->audio_clk); 5143c4e89dfSLubomir Rintel return PTR_ERR(sspa->sysclk); 515fa375d42SZhangfei Gao } 516*a97e384bSLubomir Rintel } 5177d98cc64SLubomir Rintel pm_runtime_enable(&pdev->dev); 5188ecdcac8SLubomir Rintel clk_prepare_enable(sspa->audio_clk); 5193c4e89dfSLubomir Rintel platform_set_drvdata(pdev, sspa); 520fa375d42SZhangfei Gao 5213c4e89dfSLubomir Rintel sspa->playback_dma_data.maxburst = 4; 5223c4e89dfSLubomir Rintel sspa->capture_dma_data.maxburst = 4; 523c9aeda1cSLubomir Rintel /* You know, these addresses are actually ignored. */ 524*a97e384bSLubomir Rintel sspa->capture_dma_data.addr = SSPA_D; 525*a97e384bSLubomir Rintel sspa->playback_dma_data.addr = 0x80 + SSPA_D; 526c9aeda1cSLubomir Rintel 527724da053SLubomir Rintel if (pdev->dev.of_node) { 528724da053SLubomir Rintel int ret; 529724da053SLubomir Rintel 530724da053SLubomir Rintel ret = devm_snd_dmaengine_pcm_register(&pdev->dev, 531724da053SLubomir Rintel &mmp_pcm_config, 0); 532724da053SLubomir Rintel if (ret) 533724da053SLubomir Rintel return ret; 534724da053SLubomir Rintel } 535724da053SLubomir Rintel 5369ff50721SSachin Kamat return devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component, 537425f3708SKuninori Morimoto &mmp_sspa_dai, 1); 538fa375d42SZhangfei Gao } 539fa375d42SZhangfei Gao 540570f6fe1SBill Pemberton static int asoc_mmp_sspa_remove(struct platform_device *pdev) 541fa375d42SZhangfei Gao { 5423c4e89dfSLubomir Rintel struct sspa_priv *sspa = platform_get_drvdata(pdev); 543fa375d42SZhangfei Gao 5448ecdcac8SLubomir Rintel clk_disable_unprepare(sspa->audio_clk); 5457d98cc64SLubomir Rintel pm_runtime_disable(&pdev->dev); 546*a97e384bSLubomir Rintel 547*a97e384bSLubomir Rintel if (pdev->dev.of_node) 548*a97e384bSLubomir Rintel return 0; 549*a97e384bSLubomir Rintel 5503c4e89dfSLubomir Rintel clk_put(sspa->audio_clk); 5513c4e89dfSLubomir Rintel clk_put(sspa->sysclk); 552fa375d42SZhangfei Gao return 0; 553fa375d42SZhangfei Gao } 554fa375d42SZhangfei Gao 555*a97e384bSLubomir Rintel #ifdef CONFIG_OF 556*a97e384bSLubomir Rintel static const struct of_device_id mmp_sspa_of_match[] = { 557*a97e384bSLubomir Rintel { .compatible = "marvell,mmp-sspa" }, 558*a97e384bSLubomir Rintel {}, 559*a97e384bSLubomir Rintel }; 560*a97e384bSLubomir Rintel 561*a97e384bSLubomir Rintel MODULE_DEVICE_TABLE(of, mmp_sspa_of_match); 562*a97e384bSLubomir Rintel #endif 563*a97e384bSLubomir Rintel 564fa375d42SZhangfei Gao static struct platform_driver asoc_mmp_sspa_driver = { 565fa375d42SZhangfei Gao .driver = { 566fa375d42SZhangfei Gao .name = "mmp-sspa-dai", 567*a97e384bSLubomir Rintel .of_match_table = of_match_ptr(mmp_sspa_of_match), 568fa375d42SZhangfei Gao }, 569fa375d42SZhangfei Gao .probe = asoc_mmp_sspa_probe, 570570f6fe1SBill Pemberton .remove = asoc_mmp_sspa_remove, 571fa375d42SZhangfei Gao }; 572fa375d42SZhangfei Gao 573fa375d42SZhangfei Gao module_platform_driver(asoc_mmp_sspa_driver); 574fa375d42SZhangfei Gao 575fa375d42SZhangfei Gao MODULE_AUTHOR("Leo Yan <leoy@marvell.com>"); 576fa375d42SZhangfei Gao MODULE_DESCRIPTION("MMP SSPA SoC Interface"); 577fa375d42SZhangfei Gao MODULE_LICENSE("GPL"); 578e5b7d71aSAndrea Adami MODULE_ALIAS("platform:mmp-sspa-dai"); 579