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 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); 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 242*b88b31f4SKyle 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 24939ec7e9bSLubomir Rintel sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK; 25039ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XSSZ1(bitval); 25139ec7e9bSLubomir Rintel 25239ec7e9bSLubomir Rintel sspa_ctrl &= ~SSPA_CTL_XSSZ2_MASK; 25339ec7e9bSLubomir Rintel sspa_ctrl |= SSPA_CTL_XSSZ2(bitval); 25439ec7e9bSLubomir Rintel 25539ec7e9bSLubomir Rintel sspa->sp &= ~SSPA_SP_FWID_MASK; 25639ec7e9bSLubomir Rintel sspa->sp |= SSPA_SP_FWID(bits - 1); 25739ec7e9bSLubomir Rintel 25839ec7e9bSLubomir Rintel sspa->sp &= ~SSPA_TXSP_FPER_MASK; 25939ec7e9bSLubomir Rintel sspa->sp |= SSPA_TXSP_FPER(bits * 2 - 1); 26039ec7e9bSLubomir Rintel 261a97e384bSLubomir Rintel if (dev->of_node) { 262a97e384bSLubomir Rintel clk_set_rate(sspa->clk, params_rate(params) * 263a97e384bSLubomir Rintel params_channels(params) * bits); 264a97e384bSLubomir Rintel } 265a97e384bSLubomir Rintel 266fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 267a97e384bSLubomir Rintel __raw_writel(sspa_ctrl, sspa->tx_base + SSPA_CTL); 268a97e384bSLubomir Rintel __raw_writel(0x1, sspa->tx_base + SSPA_FIFO_UL); 269fa375d42SZhangfei Gao } else { 270a97e384bSLubomir Rintel __raw_writel(sspa_ctrl, sspa->rx_base + SSPA_CTL); 271a97e384bSLubomir Rintel __raw_writel(0x0, sspa->rx_base + SSPA_FIFO_UL); 272fa375d42SZhangfei Gao } 273fa375d42SZhangfei Gao 274fa375d42SZhangfei Gao return 0; 275fa375d42SZhangfei Gao } 276fa375d42SZhangfei Gao 277fa375d42SZhangfei Gao static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd, 278fa375d42SZhangfei Gao struct snd_soc_dai *dai) 279fa375d42SZhangfei Gao { 2803c4e89dfSLubomir Rintel struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); 281fa375d42SZhangfei Gao int ret = 0; 282fa375d42SZhangfei Gao 283fa375d42SZhangfei Gao switch (cmd) { 284fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_START: 285fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_RESUME: 286fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 287fa375d42SZhangfei Gao /* 288fa375d42SZhangfei Gao * whatever playback or capture, must enable rx. 289fa375d42SZhangfei Gao * this is a hw issue, so need check if rx has been 290fa375d42SZhangfei Gao * enabled or not; if has been enabled by another 291fa375d42SZhangfei Gao * stream, do not enable again. 292fa375d42SZhangfei Gao */ 2933c4e89dfSLubomir Rintel if (!sspa->running_cnt) 294fa375d42SZhangfei Gao mmp_sspa_rx_enable(sspa); 295fa375d42SZhangfei Gao 296fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 297fa375d42SZhangfei Gao mmp_sspa_tx_enable(sspa); 298fa375d42SZhangfei Gao 2993c4e89dfSLubomir Rintel sspa->running_cnt++; 300fa375d42SZhangfei Gao break; 301fa375d42SZhangfei Gao 302fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_STOP: 303fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_SUSPEND: 304fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3053c4e89dfSLubomir Rintel sspa->running_cnt--; 306fa375d42SZhangfei Gao 307fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 308fa375d42SZhangfei Gao mmp_sspa_tx_disable(sspa); 309fa375d42SZhangfei Gao 310fa375d42SZhangfei Gao /* have no capture stream, disable rx port */ 3113c4e89dfSLubomir Rintel if (!sspa->running_cnt) 312fa375d42SZhangfei Gao mmp_sspa_rx_disable(sspa); 313fa375d42SZhangfei Gao break; 314fa375d42SZhangfei Gao 315fa375d42SZhangfei Gao default: 316fa375d42SZhangfei Gao ret = -EINVAL; 317fa375d42SZhangfei Gao } 318fa375d42SZhangfei Gao 319fa375d42SZhangfei Gao return ret; 320fa375d42SZhangfei Gao } 321fa375d42SZhangfei Gao 322fa375d42SZhangfei Gao static int mmp_sspa_probe(struct snd_soc_dai *dai) 323fa375d42SZhangfei Gao { 3243c4e89dfSLubomir Rintel struct sspa_priv *sspa = dev_get_drvdata(dai->dev); 325fa375d42SZhangfei Gao 326c9aeda1cSLubomir Rintel snd_soc_dai_init_dma_data(dai, 3273c4e89dfSLubomir Rintel &sspa->playback_dma_data, 3283c4e89dfSLubomir Rintel &sspa->capture_dma_data); 329c9aeda1cSLubomir Rintel 3303c4e89dfSLubomir Rintel snd_soc_dai_set_drvdata(dai, sspa); 331fa375d42SZhangfei Gao return 0; 332fa375d42SZhangfei Gao } 333fa375d42SZhangfei Gao 334fa375d42SZhangfei Gao #define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000 335fa375d42SZhangfei Gao #define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 336fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S16_LE | \ 33700a1aca2SLubomir Rintel SNDRV_PCM_FMTBIT_S24_3LE | \ 338fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S32_LE) 339fa375d42SZhangfei Gao 340cb753443SGustavo A. R. Silva static const struct snd_soc_dai_ops mmp_sspa_dai_ops = { 341fa375d42SZhangfei Gao .startup = mmp_sspa_startup, 342fa375d42SZhangfei Gao .shutdown = mmp_sspa_shutdown, 343fa375d42SZhangfei Gao .trigger = mmp_sspa_trigger, 344fa375d42SZhangfei Gao .hw_params = mmp_sspa_hw_params, 345fa375d42SZhangfei Gao .set_sysclk = mmp_sspa_set_dai_sysclk, 346fa375d42SZhangfei Gao .set_pll = mmp_sspa_set_dai_pll, 347fa375d42SZhangfei Gao .set_fmt = mmp_sspa_set_dai_fmt, 348fa375d42SZhangfei Gao }; 349fa375d42SZhangfei Gao 3505d9ff402SLars-Peter Clausen static struct snd_soc_dai_driver mmp_sspa_dai = { 351fa375d42SZhangfei Gao .probe = mmp_sspa_probe, 352fa375d42SZhangfei Gao .playback = { 353fa375d42SZhangfei Gao .channels_min = 1, 354fa375d42SZhangfei Gao .channels_max = 128, 355fa375d42SZhangfei Gao .rates = MMP_SSPA_RATES, 356fa375d42SZhangfei Gao .formats = MMP_SSPA_FORMATS, 357fa375d42SZhangfei Gao }, 358fa375d42SZhangfei Gao .capture = { 359fa375d42SZhangfei Gao .channels_min = 1, 360fa375d42SZhangfei Gao .channels_max = 2, 361fa375d42SZhangfei Gao .rates = MMP_SSPA_RATES, 362fa375d42SZhangfei Gao .formats = MMP_SSPA_FORMATS, 363fa375d42SZhangfei Gao }, 364fa375d42SZhangfei Gao .ops = &mmp_sspa_dai_ops, 365fa375d42SZhangfei Gao }; 366fa375d42SZhangfei Gao 367724da053SLubomir Rintel #define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ 368724da053SLubomir Rintel SNDRV_PCM_INFO_MMAP_VALID | \ 369724da053SLubomir Rintel SNDRV_PCM_INFO_INTERLEAVED | \ 370724da053SLubomir Rintel SNDRV_PCM_INFO_PAUSE | \ 371724da053SLubomir Rintel SNDRV_PCM_INFO_RESUME | \ 372724da053SLubomir Rintel SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) 373724da053SLubomir Rintel 374724da053SLubomir Rintel static const struct snd_pcm_hardware mmp_pcm_hardware[] = { 375724da053SLubomir Rintel { 376724da053SLubomir Rintel .info = MMP_PCM_INFO, 377724da053SLubomir Rintel .period_bytes_min = 1024, 378724da053SLubomir Rintel .period_bytes_max = 2048, 379724da053SLubomir Rintel .periods_min = 2, 380724da053SLubomir Rintel .periods_max = 32, 381724da053SLubomir Rintel .buffer_bytes_max = 4096, 382724da053SLubomir Rintel .fifo_size = 32, 383724da053SLubomir Rintel }, 384724da053SLubomir Rintel { 385724da053SLubomir Rintel .info = MMP_PCM_INFO, 386724da053SLubomir Rintel .period_bytes_min = 1024, 387724da053SLubomir Rintel .period_bytes_max = 2048, 388724da053SLubomir Rintel .periods_min = 2, 389724da053SLubomir Rintel .periods_max = 32, 390724da053SLubomir Rintel .buffer_bytes_max = 4096, 391724da053SLubomir Rintel .fifo_size = 32, 392724da053SLubomir Rintel }, 393724da053SLubomir Rintel }; 394724da053SLubomir Rintel 395724da053SLubomir Rintel static const struct snd_dmaengine_pcm_config mmp_pcm_config = { 396724da053SLubomir Rintel .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 397724da053SLubomir Rintel .pcm_hardware = mmp_pcm_hardware, 398724da053SLubomir Rintel .prealloc_buffer_size = 4096, 399724da053SLubomir Rintel }; 400724da053SLubomir Rintel 401724da053SLubomir Rintel static int mmp_pcm_mmap(struct snd_soc_component *component, 402724da053SLubomir Rintel struct snd_pcm_substream *substream, 403724da053SLubomir Rintel struct vm_area_struct *vma) 404724da053SLubomir Rintel { 405724da053SLubomir Rintel vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; 406724da053SLubomir Rintel vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 407724da053SLubomir Rintel return remap_pfn_range(vma, vma->vm_start, 408724da053SLubomir Rintel substream->dma_buffer.addr >> PAGE_SHIFT, 409724da053SLubomir Rintel vma->vm_end - vma->vm_start, vma->vm_page_prot); 410724da053SLubomir Rintel } 411724da053SLubomir Rintel 4127d98cc64SLubomir Rintel static int mmp_sspa_open(struct snd_soc_component *component, 4137d98cc64SLubomir Rintel struct snd_pcm_substream *substream) 4147d98cc64SLubomir Rintel { 4157d98cc64SLubomir Rintel struct sspa_priv *sspa = snd_soc_component_get_drvdata(component); 4167d98cc64SLubomir Rintel 4177d98cc64SLubomir Rintel pm_runtime_get_sync(component->dev); 4187d98cc64SLubomir Rintel 4197d98cc64SLubomir Rintel /* we can only change the settings if the port is not in use */ 420a97e384bSLubomir Rintel if ((__raw_readl(sspa->tx_base + SSPA_SP) & SSPA_SP_S_EN) || 421a97e384bSLubomir Rintel (__raw_readl(sspa->rx_base + SSPA_SP) & SSPA_SP_S_EN)) { 4227d98cc64SLubomir Rintel dev_err(component->dev, 4237d98cc64SLubomir Rintel "can't change hardware dai format: stream is in use\n"); 4247d98cc64SLubomir Rintel return -EBUSY; 4257d98cc64SLubomir Rintel } 4267d98cc64SLubomir Rintel 427a97e384bSLubomir Rintel __raw_writel(sspa->sp, sspa->tx_base + SSPA_SP); 428a97e384bSLubomir Rintel __raw_writel(sspa->sp, sspa->rx_base + SSPA_SP); 4297d98cc64SLubomir Rintel 4307d98cc64SLubomir Rintel sspa->sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH); 431a97e384bSLubomir Rintel __raw_writel(sspa->sp, sspa->tx_base + SSPA_SP); 432a97e384bSLubomir Rintel __raw_writel(sspa->sp, sspa->rx_base + SSPA_SP); 4337d98cc64SLubomir Rintel 4347d98cc64SLubomir Rintel /* 4357d98cc64SLubomir Rintel * FIXME: hw issue, for the tx serial port, 4367d98cc64SLubomir Rintel * can not config the master/slave mode; 4377d98cc64SLubomir Rintel * so must clean this bit. 4387d98cc64SLubomir Rintel * The master/slave mode has been set in the 4397d98cc64SLubomir Rintel * rx port. 4407d98cc64SLubomir Rintel */ 441a97e384bSLubomir Rintel __raw_writel(sspa->sp & ~SSPA_SP_MSL, sspa->tx_base + SSPA_SP); 4427d98cc64SLubomir Rintel 443a97e384bSLubomir Rintel __raw_writel(sspa->ctrl, sspa->tx_base + SSPA_CTL); 444a97e384bSLubomir Rintel __raw_writel(sspa->ctrl, sspa->rx_base + SSPA_CTL); 4457d98cc64SLubomir Rintel 4467d98cc64SLubomir Rintel return 0; 4477d98cc64SLubomir Rintel } 4487d98cc64SLubomir Rintel 4497d98cc64SLubomir Rintel static int mmp_sspa_close(struct snd_soc_component *component, 4507d98cc64SLubomir Rintel struct snd_pcm_substream *substream) 4517d98cc64SLubomir Rintel { 4527d98cc64SLubomir Rintel pm_runtime_put_sync(component->dev); 4537d98cc64SLubomir Rintel return 0; 4547d98cc64SLubomir Rintel } 4557d98cc64SLubomir Rintel 456425f3708SKuninori Morimoto static const struct snd_soc_component_driver mmp_sspa_component = { 457425f3708SKuninori Morimoto .name = "mmp-sspa", 458724da053SLubomir Rintel .mmap = mmp_pcm_mmap, 4597d98cc64SLubomir Rintel .open = mmp_sspa_open, 4607d98cc64SLubomir Rintel .close = mmp_sspa_close, 461425f3708SKuninori Morimoto }; 462425f3708SKuninori Morimoto 463570f6fe1SBill Pemberton static int asoc_mmp_sspa_probe(struct platform_device *pdev) 464fa375d42SZhangfei Gao { 4653c4e89dfSLubomir Rintel struct sspa_priv *sspa; 4666ea460d5SLubomir Rintel int ret; 467fa375d42SZhangfei Gao 4683c4e89dfSLubomir Rintel sspa = devm_kzalloc(&pdev->dev, 469fa375d42SZhangfei Gao sizeof(struct sspa_priv), GFP_KERNEL); 4703c4e89dfSLubomir Rintel if (!sspa) 471fa375d42SZhangfei Gao return -ENOMEM; 472fa375d42SZhangfei Gao 473a97e384bSLubomir Rintel if (pdev->dev.of_node) { 474a97e384bSLubomir Rintel sspa->rx_base = devm_platform_ioremap_resource(pdev, 0); 475a97e384bSLubomir Rintel if (IS_ERR(sspa->rx_base)) 476a97e384bSLubomir Rintel return PTR_ERR(sspa->rx_base); 477a97e384bSLubomir Rintel 478a97e384bSLubomir Rintel sspa->tx_base = devm_platform_ioremap_resource(pdev, 1); 479a97e384bSLubomir Rintel if (IS_ERR(sspa->tx_base)) 480a97e384bSLubomir Rintel return PTR_ERR(sspa->tx_base); 481a97e384bSLubomir Rintel 482a97e384bSLubomir Rintel sspa->clk = devm_clk_get(&pdev->dev, "bitclk"); 483a97e384bSLubomir Rintel if (IS_ERR(sspa->clk)) 484a97e384bSLubomir Rintel return PTR_ERR(sspa->clk); 485a97e384bSLubomir Rintel 486a97e384bSLubomir Rintel sspa->audio_clk = devm_clk_get(&pdev->dev, "audio"); 487a97e384bSLubomir Rintel if (IS_ERR(sspa->audio_clk)) 488a97e384bSLubomir Rintel return PTR_ERR(sspa->audio_clk); 489a97e384bSLubomir Rintel } else { 490a97e384bSLubomir Rintel struct resource *res; 491a97e384bSLubomir Rintel 492a97e384bSLubomir Rintel res = platform_get_resource(pdev, IORESOURCE_IO, 0); 493a97e384bSLubomir Rintel if (res == NULL) 494a97e384bSLubomir Rintel return -ENODEV; 495a97e384bSLubomir Rintel 496a97e384bSLubomir Rintel sspa->rx_base = devm_ioremap(&pdev->dev, res->start, 0x30); 49718545763SWei Yongjun if (!sspa->rx_base) 49818545763SWei Yongjun return -ENOMEM; 499a97e384bSLubomir Rintel 500a97e384bSLubomir Rintel sspa->tx_base = devm_ioremap(&pdev->dev, 501a97e384bSLubomir Rintel res->start + 0x80, 0x30); 50218545763SWei Yongjun if (!sspa->tx_base) 50318545763SWei Yongjun return -ENOMEM; 504fa375d42SZhangfei Gao 5053c4e89dfSLubomir Rintel sspa->clk = devm_clk_get(&pdev->dev, NULL); 5063c4e89dfSLubomir Rintel if (IS_ERR(sspa->clk)) 5073c4e89dfSLubomir Rintel return PTR_ERR(sspa->clk); 508fa375d42SZhangfei Gao 5093c4e89dfSLubomir Rintel sspa->audio_clk = clk_get(NULL, "mmp-audio"); 5103c4e89dfSLubomir Rintel if (IS_ERR(sspa->audio_clk)) 5113c4e89dfSLubomir Rintel return PTR_ERR(sspa->audio_clk); 512fa375d42SZhangfei Gao 5133c4e89dfSLubomir Rintel sspa->sysclk = clk_get(NULL, "mmp-sysclk"); 5143c4e89dfSLubomir Rintel if (IS_ERR(sspa->sysclk)) { 5153c4e89dfSLubomir Rintel clk_put(sspa->audio_clk); 5163c4e89dfSLubomir Rintel return PTR_ERR(sspa->sysclk); 517fa375d42SZhangfei Gao } 518a97e384bSLubomir Rintel } 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. */ 524a97e384bSLubomir Rintel sspa->capture_dma_data.addr = SSPA_D; 525a97e384bSLubomir Rintel sspa->playback_dma_data.addr = 0x80 + SSPA_D; 526c9aeda1cSLubomir Rintel 527724da053SLubomir Rintel if (pdev->dev.of_node) { 528724da053SLubomir Rintel ret = devm_snd_dmaengine_pcm_register(&pdev->dev, 529724da053SLubomir Rintel &mmp_pcm_config, 0); 530724da053SLubomir Rintel if (ret) 531724da053SLubomir Rintel return ret; 532724da053SLubomir Rintel } 533724da053SLubomir Rintel 5346ea460d5SLubomir Rintel ret = devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component, 535425f3708SKuninori Morimoto &mmp_sspa_dai, 1); 5366ea460d5SLubomir Rintel if (ret) 5376ea460d5SLubomir Rintel return ret; 5386ea460d5SLubomir Rintel 5396ea460d5SLubomir Rintel pm_runtime_enable(&pdev->dev); 5406ea460d5SLubomir Rintel clk_prepare_enable(sspa->audio_clk); 5416ea460d5SLubomir Rintel 5426ea460d5SLubomir Rintel return 0; 543fa375d42SZhangfei Gao } 544fa375d42SZhangfei Gao 545570f6fe1SBill Pemberton static int asoc_mmp_sspa_remove(struct platform_device *pdev) 546fa375d42SZhangfei Gao { 5473c4e89dfSLubomir Rintel struct sspa_priv *sspa = platform_get_drvdata(pdev); 548fa375d42SZhangfei Gao 5498ecdcac8SLubomir Rintel clk_disable_unprepare(sspa->audio_clk); 5507d98cc64SLubomir Rintel pm_runtime_disable(&pdev->dev); 551a97e384bSLubomir Rintel 552a97e384bSLubomir Rintel if (pdev->dev.of_node) 553a97e384bSLubomir Rintel return 0; 554a97e384bSLubomir Rintel 5553c4e89dfSLubomir Rintel clk_put(sspa->audio_clk); 5563c4e89dfSLubomir Rintel clk_put(sspa->sysclk); 557fa375d42SZhangfei Gao return 0; 558fa375d42SZhangfei Gao } 559fa375d42SZhangfei Gao 560a97e384bSLubomir Rintel #ifdef CONFIG_OF 561a97e384bSLubomir Rintel static const struct of_device_id mmp_sspa_of_match[] = { 562a97e384bSLubomir Rintel { .compatible = "marvell,mmp-sspa" }, 563a97e384bSLubomir Rintel {}, 564a97e384bSLubomir Rintel }; 565a97e384bSLubomir Rintel 566a97e384bSLubomir Rintel MODULE_DEVICE_TABLE(of, mmp_sspa_of_match); 567a97e384bSLubomir Rintel #endif 568a97e384bSLubomir Rintel 569fa375d42SZhangfei Gao static struct platform_driver asoc_mmp_sspa_driver = { 570fa375d42SZhangfei Gao .driver = { 571fa375d42SZhangfei Gao .name = "mmp-sspa-dai", 572a97e384bSLubomir Rintel .of_match_table = of_match_ptr(mmp_sspa_of_match), 573fa375d42SZhangfei Gao }, 574fa375d42SZhangfei Gao .probe = asoc_mmp_sspa_probe, 575570f6fe1SBill Pemberton .remove = asoc_mmp_sspa_remove, 576fa375d42SZhangfei Gao }; 577fa375d42SZhangfei Gao 578fa375d42SZhangfei Gao module_platform_driver(asoc_mmp_sspa_driver); 579fa375d42SZhangfei Gao 580fa375d42SZhangfei Gao MODULE_AUTHOR("Leo Yan <leoy@marvell.com>"); 581fa375d42SZhangfei Gao MODULE_DESCRIPTION("MMP SSPA SoC Interface"); 582fa375d42SZhangfei Gao MODULE_LICENSE("GPL"); 583e5b7d71aSAndrea Adami MODULE_ALIAS("platform:mmp-sspa-dai"); 584