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