1fa375d42SZhangfei Gao /* 2fa375d42SZhangfei Gao * linux/sound/soc/pxa/mmp-sspa.c 3fa375d42SZhangfei Gao * Base on pxa2xx-ssp.c 4fa375d42SZhangfei Gao * 5fa375d42SZhangfei Gao * Copyright (C) 2011 Marvell International Ltd. 6fa375d42SZhangfei Gao * 7fa375d42SZhangfei Gao * This program is free software; you can redistribute it and/or modify 8fa375d42SZhangfei Gao * it under the terms of the GNU General Public License as published by 9fa375d42SZhangfei Gao * the Free Software Foundation; either version 2 of the License, or 10fa375d42SZhangfei Gao * (at your option) any later version. 11fa375d42SZhangfei Gao * 12fa375d42SZhangfei Gao * This program is distributed in the hope that it will be useful, 13fa375d42SZhangfei Gao * but WITHOUT ANY WARRANTY; without even the implied warranty of 14fa375d42SZhangfei Gao * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15fa375d42SZhangfei Gao * GNU General Public License for more details. 16fa375d42SZhangfei Gao * 17fa375d42SZhangfei Gao * You should have received a copy of the GNU General Public License 18fa375d42SZhangfei Gao * along with this program; if not, write to the Free Software 19fa375d42SZhangfei Gao * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20fa375d42SZhangfei Gao * 21fa375d42SZhangfei Gao */ 22fa375d42SZhangfei Gao #include <linux/init.h> 23fa375d42SZhangfei Gao #include <linux/module.h> 24fa375d42SZhangfei Gao #include <linux/platform_device.h> 25fa375d42SZhangfei Gao #include <linux/delay.h> 26fa375d42SZhangfei Gao #include <linux/clk.h> 27fa375d42SZhangfei Gao #include <linux/slab.h> 28fa375d42SZhangfei Gao #include <linux/pxa2xx_ssp.h> 29fa375d42SZhangfei Gao #include <linux/io.h> 30d65a1458SDaniel Mack #include <linux/dmaengine.h> 31d65a1458SDaniel Mack 32fa375d42SZhangfei Gao #include <sound/core.h> 33fa375d42SZhangfei Gao #include <sound/pcm.h> 34fa375d42SZhangfei Gao #include <sound/initval.h> 35fa375d42SZhangfei Gao #include <sound/pcm_params.h> 36fa375d42SZhangfei Gao #include <sound/soc.h> 37fa375d42SZhangfei Gao #include <sound/pxa2xx-lib.h> 38d65a1458SDaniel Mack #include <sound/dmaengine_pcm.h> 39fa375d42SZhangfei Gao #include "mmp-sspa.h" 40fa375d42SZhangfei Gao 41fa375d42SZhangfei Gao /* 42fa375d42SZhangfei Gao * SSPA audio private data 43fa375d42SZhangfei Gao */ 44fa375d42SZhangfei Gao struct sspa_priv { 45fa375d42SZhangfei Gao struct ssp_device *sspa; 46d65a1458SDaniel Mack struct snd_dmaengine_dai_dma_data *dma_params; 47fa375d42SZhangfei Gao struct clk *audio_clk; 48fa375d42SZhangfei Gao struct clk *sysclk; 49fa375d42SZhangfei Gao int dai_fmt; 50fa375d42SZhangfei Gao int running_cnt; 51fa375d42SZhangfei Gao }; 52fa375d42SZhangfei Gao 53fa375d42SZhangfei Gao static void mmp_sspa_write_reg(struct ssp_device *sspa, u32 reg, u32 val) 54fa375d42SZhangfei Gao { 55fa375d42SZhangfei Gao __raw_writel(val, sspa->mmio_base + reg); 56fa375d42SZhangfei Gao } 57fa375d42SZhangfei Gao 58fa375d42SZhangfei Gao static u32 mmp_sspa_read_reg(struct ssp_device *sspa, u32 reg) 59fa375d42SZhangfei Gao { 60fa375d42SZhangfei Gao return __raw_readl(sspa->mmio_base + reg); 61fa375d42SZhangfei Gao } 62fa375d42SZhangfei Gao 63fa375d42SZhangfei Gao static void mmp_sspa_tx_enable(struct ssp_device *sspa) 64fa375d42SZhangfei Gao { 65fa375d42SZhangfei Gao unsigned int sspa_sp; 66fa375d42SZhangfei Gao 67fa375d42SZhangfei Gao sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP); 68fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_S_EN; 69fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 70fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); 71fa375d42SZhangfei Gao } 72fa375d42SZhangfei Gao 73fa375d42SZhangfei Gao static void mmp_sspa_tx_disable(struct ssp_device *sspa) 74fa375d42SZhangfei Gao { 75fa375d42SZhangfei Gao unsigned int sspa_sp; 76fa375d42SZhangfei Gao 77fa375d42SZhangfei Gao sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP); 78fa375d42SZhangfei Gao sspa_sp &= ~SSPA_SP_S_EN; 79fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 80fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); 81fa375d42SZhangfei Gao } 82fa375d42SZhangfei Gao 83fa375d42SZhangfei Gao static void mmp_sspa_rx_enable(struct ssp_device *sspa) 84fa375d42SZhangfei Gao { 85fa375d42SZhangfei Gao unsigned int sspa_sp; 86fa375d42SZhangfei Gao 87fa375d42SZhangfei Gao sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP); 88fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_S_EN; 89fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 90fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); 91fa375d42SZhangfei Gao } 92fa375d42SZhangfei Gao 93fa375d42SZhangfei Gao static void mmp_sspa_rx_disable(struct ssp_device *sspa) 94fa375d42SZhangfei Gao { 95fa375d42SZhangfei Gao unsigned int sspa_sp; 96fa375d42SZhangfei Gao 97fa375d42SZhangfei Gao sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP); 98fa375d42SZhangfei Gao sspa_sp &= ~SSPA_SP_S_EN; 99fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_WEN; 100fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); 101fa375d42SZhangfei Gao } 102fa375d42SZhangfei Gao 103fa375d42SZhangfei Gao static int mmp_sspa_startup(struct snd_pcm_substream *substream, 104fa375d42SZhangfei Gao struct snd_soc_dai *dai) 105fa375d42SZhangfei Gao { 106fa375d42SZhangfei Gao struct sspa_priv *priv = snd_soc_dai_get_drvdata(dai); 107fa375d42SZhangfei Gao 108fa375d42SZhangfei Gao clk_enable(priv->sysclk); 109fa375d42SZhangfei Gao clk_enable(priv->sspa->clk); 110fa375d42SZhangfei Gao 111fa375d42SZhangfei Gao return 0; 112fa375d42SZhangfei Gao } 113fa375d42SZhangfei Gao 114fa375d42SZhangfei Gao static void mmp_sspa_shutdown(struct snd_pcm_substream *substream, 115fa375d42SZhangfei Gao struct snd_soc_dai *dai) 116fa375d42SZhangfei Gao { 117fa375d42SZhangfei Gao struct sspa_priv *priv = snd_soc_dai_get_drvdata(dai); 118fa375d42SZhangfei Gao 119fa375d42SZhangfei Gao clk_disable(priv->sspa->clk); 120fa375d42SZhangfei Gao clk_disable(priv->sysclk); 121fa375d42SZhangfei Gao 122fa375d42SZhangfei Gao return; 123fa375d42SZhangfei Gao } 124fa375d42SZhangfei Gao 125fa375d42SZhangfei Gao /* 126fa375d42SZhangfei Gao * Set the SSP ports SYSCLK. 127fa375d42SZhangfei Gao */ 128fa375d42SZhangfei Gao static int mmp_sspa_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 129fa375d42SZhangfei Gao int clk_id, unsigned int freq, int dir) 130fa375d42SZhangfei Gao { 131fa375d42SZhangfei Gao struct sspa_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 132fa375d42SZhangfei Gao int ret = 0; 133fa375d42SZhangfei Gao 134fa375d42SZhangfei Gao switch (clk_id) { 135fa375d42SZhangfei Gao case MMP_SSPA_CLK_AUDIO: 136fa375d42SZhangfei Gao ret = clk_set_rate(priv->audio_clk, freq); 137fa375d42SZhangfei Gao if (ret) 138fa375d42SZhangfei Gao return ret; 139fa375d42SZhangfei Gao break; 140fa375d42SZhangfei Gao case MMP_SSPA_CLK_PLL: 141fa375d42SZhangfei Gao case MMP_SSPA_CLK_VCXO: 142fa375d42SZhangfei Gao /* not support yet */ 143fa375d42SZhangfei Gao return -EINVAL; 144fa375d42SZhangfei Gao default: 145fa375d42SZhangfei Gao return -EINVAL; 146fa375d42SZhangfei Gao } 147fa375d42SZhangfei Gao 148fa375d42SZhangfei Gao return 0; 149fa375d42SZhangfei Gao } 150fa375d42SZhangfei Gao 151fa375d42SZhangfei Gao static int mmp_sspa_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, 152fa375d42SZhangfei Gao int source, unsigned int freq_in, 153fa375d42SZhangfei Gao unsigned int freq_out) 154fa375d42SZhangfei Gao { 155fa375d42SZhangfei Gao struct sspa_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 156fa375d42SZhangfei Gao int ret = 0; 157fa375d42SZhangfei Gao 158fa375d42SZhangfei Gao switch (pll_id) { 159fa375d42SZhangfei Gao case MMP_SYSCLK: 160fa375d42SZhangfei Gao ret = clk_set_rate(priv->sysclk, freq_out); 161fa375d42SZhangfei Gao if (ret) 162fa375d42SZhangfei Gao return ret; 163fa375d42SZhangfei Gao break; 164fa375d42SZhangfei Gao case MMP_SSPA_CLK: 165fa375d42SZhangfei Gao ret = clk_set_rate(priv->sspa->clk, freq_out); 166fa375d42SZhangfei Gao if (ret) 167fa375d42SZhangfei Gao return ret; 168fa375d42SZhangfei Gao break; 169fa375d42SZhangfei Gao default: 170fa375d42SZhangfei Gao return -ENODEV; 171fa375d42SZhangfei Gao } 172fa375d42SZhangfei Gao 173fa375d42SZhangfei Gao return 0; 174fa375d42SZhangfei Gao } 175fa375d42SZhangfei Gao 176fa375d42SZhangfei Gao /* 177fa375d42SZhangfei Gao * Set up the sspa dai format. The sspa port must be inactive 178fa375d42SZhangfei Gao * before calling this function as the physical 179fa375d42SZhangfei Gao * interface format is changed. 180fa375d42SZhangfei Gao */ 181fa375d42SZhangfei Gao static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai, 182fa375d42SZhangfei Gao unsigned int fmt) 183fa375d42SZhangfei Gao { 184fa375d42SZhangfei Gao struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(cpu_dai); 185fa375d42SZhangfei Gao struct ssp_device *sspa = sspa_priv->sspa; 186fa375d42SZhangfei Gao u32 sspa_sp, sspa_ctrl; 187fa375d42SZhangfei Gao 188fa375d42SZhangfei Gao /* check if we need to change anything at all */ 189fa375d42SZhangfei Gao if (sspa_priv->dai_fmt == fmt) 190fa375d42SZhangfei Gao return 0; 191fa375d42SZhangfei Gao 192fa375d42SZhangfei Gao /* we can only change the settings if the port is not in use */ 193fa375d42SZhangfei Gao if ((mmp_sspa_read_reg(sspa, SSPA_TXSP) & SSPA_SP_S_EN) || 194fa375d42SZhangfei Gao (mmp_sspa_read_reg(sspa, SSPA_RXSP) & SSPA_SP_S_EN)) { 195fa375d42SZhangfei Gao dev_err(&sspa->pdev->dev, 196fa375d42SZhangfei Gao "can't change hardware dai format: stream is in use\n"); 197fa375d42SZhangfei Gao return -EINVAL; 198fa375d42SZhangfei Gao } 199fa375d42SZhangfei Gao 200fa375d42SZhangfei Gao /* reset port settings */ 201fa375d42SZhangfei Gao sspa_sp = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH; 202fa375d42SZhangfei Gao sspa_ctrl = 0; 203fa375d42SZhangfei Gao 204fa375d42SZhangfei Gao switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 205fa375d42SZhangfei Gao case SND_SOC_DAIFMT_CBS_CFS: 206fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_MSL; 207fa375d42SZhangfei Gao break; 208fa375d42SZhangfei Gao case SND_SOC_DAIFMT_CBM_CFM: 209fa375d42SZhangfei Gao break; 210fa375d42SZhangfei Gao default: 211fa375d42SZhangfei Gao return -EINVAL; 212fa375d42SZhangfei Gao } 213fa375d42SZhangfei Gao 214fa375d42SZhangfei Gao switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 215fa375d42SZhangfei Gao case SND_SOC_DAIFMT_NB_NF: 216fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_FSP; 217fa375d42SZhangfei Gao break; 218fa375d42SZhangfei Gao default: 219fa375d42SZhangfei Gao return -EINVAL; 220fa375d42SZhangfei Gao } 221fa375d42SZhangfei Gao 222fa375d42SZhangfei Gao switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 223fa375d42SZhangfei Gao case SND_SOC_DAIFMT_I2S: 224fa375d42SZhangfei Gao sspa_sp |= SSPA_TXSP_FPER(63); 225fa375d42SZhangfei Gao sspa_sp |= SSPA_SP_FWID(31); 226fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XDATDLY(1); 227fa375d42SZhangfei Gao break; 228fa375d42SZhangfei Gao default: 229fa375d42SZhangfei Gao return -EINVAL; 230fa375d42SZhangfei Gao } 231fa375d42SZhangfei Gao 232fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); 233fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); 234fa375d42SZhangfei Gao 235fa375d42SZhangfei Gao sspa_sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH); 236fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); 237fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); 238fa375d42SZhangfei Gao 239fa375d42SZhangfei Gao /* 240fa375d42SZhangfei Gao * FIXME: hw issue, for the tx serial port, 241fa375d42SZhangfei Gao * can not config the master/slave mode; 242fa375d42SZhangfei Gao * so must clean this bit. 243fa375d42SZhangfei Gao * The master/slave mode has been set in the 244fa375d42SZhangfei Gao * rx port. 245fa375d42SZhangfei Gao */ 246fa375d42SZhangfei Gao sspa_sp &= ~SSPA_SP_MSL; 247fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); 248fa375d42SZhangfei Gao 249fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl); 250fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl); 251fa375d42SZhangfei Gao 252fa375d42SZhangfei Gao /* Since we are configuring the timings for the format by hand 253fa375d42SZhangfei Gao * we have to defer some things until hw_params() where we 254fa375d42SZhangfei Gao * know parameters like the sample size. 255fa375d42SZhangfei Gao */ 256fa375d42SZhangfei Gao sspa_priv->dai_fmt = fmt; 257fa375d42SZhangfei Gao return 0; 258fa375d42SZhangfei Gao } 259fa375d42SZhangfei Gao 260fa375d42SZhangfei Gao /* 261fa375d42SZhangfei Gao * Set the SSPA audio DMA parameters and sample size. 262fa375d42SZhangfei Gao * Can be called multiple times by oss emulation. 263fa375d42SZhangfei Gao */ 264fa375d42SZhangfei Gao static int mmp_sspa_hw_params(struct snd_pcm_substream *substream, 265fa375d42SZhangfei Gao struct snd_pcm_hw_params *params, 266fa375d42SZhangfei Gao struct snd_soc_dai *dai) 267fa375d42SZhangfei Gao { 268fa375d42SZhangfei Gao struct snd_soc_pcm_runtime *rtd = substream->private_data; 269fa375d42SZhangfei Gao struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 270fa375d42SZhangfei Gao struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai); 271fa375d42SZhangfei Gao struct ssp_device *sspa = sspa_priv->sspa; 272d65a1458SDaniel Mack struct snd_dmaengine_dai_dma_data *dma_params; 273fa375d42SZhangfei Gao u32 sspa_ctrl; 274fa375d42SZhangfei Gao 275fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 276fa375d42SZhangfei Gao sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_TXCTL); 277fa375d42SZhangfei Gao else 278fa375d42SZhangfei Gao sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_RXCTL); 279fa375d42SZhangfei Gao 280fa375d42SZhangfei Gao sspa_ctrl &= ~SSPA_CTL_XFRLEN1_MASK; 281fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XFRLEN1(params_channels(params) - 1); 282fa375d42SZhangfei Gao sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK; 283fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XWDLEN1(SSPA_CTL_32_BITS); 284fa375d42SZhangfei Gao sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK; 285fa375d42SZhangfei Gao 286fa375d42SZhangfei Gao switch (params_format(params)) { 287fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S8: 288fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_8_BITS); 289fa375d42SZhangfei Gao break; 290fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S16_LE: 291fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_16_BITS); 292fa375d42SZhangfei Gao break; 293fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S20_3LE: 294fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_20_BITS); 295fa375d42SZhangfei Gao break; 296fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S24_3LE: 297fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_24_BITS); 298fa375d42SZhangfei Gao break; 299fa375d42SZhangfei Gao case SNDRV_PCM_FORMAT_S32_LE: 300fa375d42SZhangfei Gao sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_32_BITS); 301fa375d42SZhangfei Gao break; 302fa375d42SZhangfei Gao default: 303fa375d42SZhangfei Gao return -EINVAL; 304fa375d42SZhangfei Gao } 305fa375d42SZhangfei Gao 306fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 307fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl); 308fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_TXFIFO_LL, 0x1); 309fa375d42SZhangfei Gao } else { 310fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl); 311fa375d42SZhangfei Gao mmp_sspa_write_reg(sspa, SSPA_RXFIFO_UL, 0x0); 312fa375d42SZhangfei Gao } 313fa375d42SZhangfei Gao 314fa375d42SZhangfei Gao dma_params = &sspa_priv->dma_params[substream->stream]; 315d65a1458SDaniel Mack dma_params->addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 316fa375d42SZhangfei Gao (sspa->phys_base + SSPA_TXD) : 317fa375d42SZhangfei Gao (sspa->phys_base + SSPA_RXD); 318fa375d42SZhangfei Gao snd_soc_dai_set_dma_data(cpu_dai, substream, dma_params); 319fa375d42SZhangfei Gao return 0; 320fa375d42SZhangfei Gao } 321fa375d42SZhangfei Gao 322fa375d42SZhangfei Gao static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd, 323fa375d42SZhangfei Gao struct snd_soc_dai *dai) 324fa375d42SZhangfei Gao { 325fa375d42SZhangfei Gao struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai); 326fa375d42SZhangfei Gao struct ssp_device *sspa = sspa_priv->sspa; 327fa375d42SZhangfei Gao int ret = 0; 328fa375d42SZhangfei Gao 329fa375d42SZhangfei Gao switch (cmd) { 330fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_START: 331fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_RESUME: 332fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 333fa375d42SZhangfei Gao /* 334fa375d42SZhangfei Gao * whatever playback or capture, must enable rx. 335fa375d42SZhangfei Gao * this is a hw issue, so need check if rx has been 336fa375d42SZhangfei Gao * enabled or not; if has been enabled by another 337fa375d42SZhangfei Gao * stream, do not enable again. 338fa375d42SZhangfei Gao */ 339fa375d42SZhangfei Gao if (!sspa_priv->running_cnt) 340fa375d42SZhangfei Gao mmp_sspa_rx_enable(sspa); 341fa375d42SZhangfei Gao 342fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 343fa375d42SZhangfei Gao mmp_sspa_tx_enable(sspa); 344fa375d42SZhangfei Gao 345fa375d42SZhangfei Gao sspa_priv->running_cnt++; 346fa375d42SZhangfei Gao break; 347fa375d42SZhangfei Gao 348fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_STOP: 349fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_SUSPEND: 350fa375d42SZhangfei Gao case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 351fa375d42SZhangfei Gao sspa_priv->running_cnt--; 352fa375d42SZhangfei Gao 353fa375d42SZhangfei Gao if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 354fa375d42SZhangfei Gao mmp_sspa_tx_disable(sspa); 355fa375d42SZhangfei Gao 356fa375d42SZhangfei Gao /* have no capture stream, disable rx port */ 357fa375d42SZhangfei Gao if (!sspa_priv->running_cnt) 358fa375d42SZhangfei Gao mmp_sspa_rx_disable(sspa); 359fa375d42SZhangfei Gao break; 360fa375d42SZhangfei Gao 361fa375d42SZhangfei Gao default: 362fa375d42SZhangfei Gao ret = -EINVAL; 363fa375d42SZhangfei Gao } 364fa375d42SZhangfei Gao 365fa375d42SZhangfei Gao return ret; 366fa375d42SZhangfei Gao } 367fa375d42SZhangfei Gao 368fa375d42SZhangfei Gao static int mmp_sspa_probe(struct snd_soc_dai *dai) 369fa375d42SZhangfei Gao { 370fa375d42SZhangfei Gao struct sspa_priv *priv = dev_get_drvdata(dai->dev); 371fa375d42SZhangfei Gao 372fa375d42SZhangfei Gao snd_soc_dai_set_drvdata(dai, priv); 373fa375d42SZhangfei Gao return 0; 374fa375d42SZhangfei Gao 375fa375d42SZhangfei Gao } 376fa375d42SZhangfei Gao 377fa375d42SZhangfei Gao #define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000 378fa375d42SZhangfei Gao #define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 379fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S16_LE | \ 380fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S24_LE | \ 381fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S24_LE | \ 382fa375d42SZhangfei Gao SNDRV_PCM_FMTBIT_S32_LE) 383fa375d42SZhangfei Gao 384fa375d42SZhangfei Gao static struct snd_soc_dai_ops mmp_sspa_dai_ops = { 385fa375d42SZhangfei Gao .startup = mmp_sspa_startup, 386fa375d42SZhangfei Gao .shutdown = mmp_sspa_shutdown, 387fa375d42SZhangfei Gao .trigger = mmp_sspa_trigger, 388fa375d42SZhangfei Gao .hw_params = mmp_sspa_hw_params, 389fa375d42SZhangfei Gao .set_sysclk = mmp_sspa_set_dai_sysclk, 390fa375d42SZhangfei Gao .set_pll = mmp_sspa_set_dai_pll, 391fa375d42SZhangfei Gao .set_fmt = mmp_sspa_set_dai_fmt, 392fa375d42SZhangfei Gao }; 393fa375d42SZhangfei Gao 3945d9ff402SLars-Peter Clausen static struct snd_soc_dai_driver mmp_sspa_dai = { 395fa375d42SZhangfei Gao .probe = mmp_sspa_probe, 396fa375d42SZhangfei Gao .playback = { 397fa375d42SZhangfei Gao .channels_min = 1, 398fa375d42SZhangfei Gao .channels_max = 128, 399fa375d42SZhangfei Gao .rates = MMP_SSPA_RATES, 400fa375d42SZhangfei Gao .formats = MMP_SSPA_FORMATS, 401fa375d42SZhangfei Gao }, 402fa375d42SZhangfei Gao .capture = { 403fa375d42SZhangfei Gao .channels_min = 1, 404fa375d42SZhangfei Gao .channels_max = 2, 405fa375d42SZhangfei Gao .rates = MMP_SSPA_RATES, 406fa375d42SZhangfei Gao .formats = MMP_SSPA_FORMATS, 407fa375d42SZhangfei Gao }, 408fa375d42SZhangfei Gao .ops = &mmp_sspa_dai_ops, 409fa375d42SZhangfei Gao }; 410fa375d42SZhangfei Gao 411425f3708SKuninori Morimoto static const struct snd_soc_component_driver mmp_sspa_component = { 412425f3708SKuninori Morimoto .name = "mmp-sspa", 413425f3708SKuninori Morimoto }; 414425f3708SKuninori Morimoto 415570f6fe1SBill Pemberton static int asoc_mmp_sspa_probe(struct platform_device *pdev) 416fa375d42SZhangfei Gao { 417fa375d42SZhangfei Gao struct sspa_priv *priv; 418fa375d42SZhangfei Gao struct resource *res; 419fa375d42SZhangfei Gao 420fa375d42SZhangfei Gao priv = devm_kzalloc(&pdev->dev, 421fa375d42SZhangfei Gao sizeof(struct sspa_priv), GFP_KERNEL); 422fa375d42SZhangfei Gao if (!priv) 423fa375d42SZhangfei Gao return -ENOMEM; 424fa375d42SZhangfei Gao 425fa375d42SZhangfei Gao priv->sspa = devm_kzalloc(&pdev->dev, 426fa375d42SZhangfei Gao sizeof(struct ssp_device), GFP_KERNEL); 427fa375d42SZhangfei Gao if (priv->sspa == NULL) 428fa375d42SZhangfei Gao return -ENOMEM; 429fa375d42SZhangfei Gao 430fa375d42SZhangfei Gao priv->dma_params = devm_kzalloc(&pdev->dev, 431d65a1458SDaniel Mack 2 * sizeof(struct snd_dmaengine_dai_dma_data), 432d65a1458SDaniel Mack GFP_KERNEL); 433fa375d42SZhangfei Gao if (priv->dma_params == NULL) 434fa375d42SZhangfei Gao return -ENOMEM; 435fa375d42SZhangfei Gao 436fa375d42SZhangfei Gao res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 437b25b5aa0SThierry Reding priv->sspa->mmio_base = devm_ioremap_resource(&pdev->dev, res); 438b25b5aa0SThierry Reding if (IS_ERR(priv->sspa->mmio_base)) 439b25b5aa0SThierry Reding return PTR_ERR(priv->sspa->mmio_base); 440fa375d42SZhangfei Gao 441fa375d42SZhangfei Gao priv->sspa->clk = devm_clk_get(&pdev->dev, NULL); 442fa375d42SZhangfei Gao if (IS_ERR(priv->sspa->clk)) 443fa375d42SZhangfei Gao return PTR_ERR(priv->sspa->clk); 444fa375d42SZhangfei Gao 445fa375d42SZhangfei Gao priv->audio_clk = clk_get(NULL, "mmp-audio"); 446fa375d42SZhangfei Gao if (IS_ERR(priv->audio_clk)) 447fa375d42SZhangfei Gao return PTR_ERR(priv->audio_clk); 448fa375d42SZhangfei Gao 449fa375d42SZhangfei Gao priv->sysclk = clk_get(NULL, "mmp-sysclk"); 450fa375d42SZhangfei Gao if (IS_ERR(priv->sysclk)) { 451fa375d42SZhangfei Gao clk_put(priv->audio_clk); 452fa375d42SZhangfei Gao return PTR_ERR(priv->sysclk); 453fa375d42SZhangfei Gao } 454fa375d42SZhangfei Gao clk_enable(priv->audio_clk); 455fa375d42SZhangfei Gao priv->dai_fmt = (unsigned int) -1; 456fa375d42SZhangfei Gao platform_set_drvdata(pdev, priv); 457fa375d42SZhangfei Gao 4589ff50721SSachin Kamat return devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component, 459425f3708SKuninori Morimoto &mmp_sspa_dai, 1); 460fa375d42SZhangfei Gao } 461fa375d42SZhangfei Gao 462570f6fe1SBill Pemberton static int asoc_mmp_sspa_remove(struct platform_device *pdev) 463fa375d42SZhangfei Gao { 464fa375d42SZhangfei Gao struct sspa_priv *priv = platform_get_drvdata(pdev); 465fa375d42SZhangfei Gao 466fa375d42SZhangfei Gao clk_disable(priv->audio_clk); 467fa375d42SZhangfei Gao clk_put(priv->audio_clk); 468fa375d42SZhangfei Gao clk_put(priv->sysclk); 469fa375d42SZhangfei Gao return 0; 470fa375d42SZhangfei Gao } 471fa375d42SZhangfei Gao 472fa375d42SZhangfei Gao static struct platform_driver asoc_mmp_sspa_driver = { 473fa375d42SZhangfei Gao .driver = { 474fa375d42SZhangfei Gao .name = "mmp-sspa-dai", 475fa375d42SZhangfei Gao }, 476fa375d42SZhangfei Gao .probe = asoc_mmp_sspa_probe, 477570f6fe1SBill Pemberton .remove = asoc_mmp_sspa_remove, 478fa375d42SZhangfei Gao }; 479fa375d42SZhangfei Gao 480fa375d42SZhangfei Gao module_platform_driver(asoc_mmp_sspa_driver); 481fa375d42SZhangfei Gao 482fa375d42SZhangfei Gao MODULE_AUTHOR("Leo Yan <leoy@marvell.com>"); 483fa375d42SZhangfei Gao MODULE_DESCRIPTION("MMP SSPA SoC Interface"); 484fa375d42SZhangfei Gao MODULE_LICENSE("GPL"); 485*e5b7d71aSAndrea Adami MODULE_ALIAS("platform:mmp-sspa-dai"); 486