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