xref: /openbmc/linux/sound/soc/pxa/pxa2xx-i2s.c (revision a71a468a)
1 /*
2  * pxa2xx-i2s.c  --  ALSA Soc Audio Layer
3  *
4  * Copyright 2005 Wolfson Microelectronics PLC.
5  * Author: Liam Girdwood
6  *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
7  *
8  *  This program is free software; you can redistribute  it and/or modify it
9  *  under  the terms of  the GNU General  Public License as published by the
10  *  Free Software Foundation;  either version 2 of the  License, or (at your
11  *  option) any later version.
12  *
13  *  Revision history
14  *    12th Aug 2005   Initial version.
15  */
16 
17 #include <linux/init.h>
18 #include <linux/module.h>
19 #include <linux/device.h>
20 #include <linux/delay.h>
21 #include <sound/driver.h>
22 #include <sound/core.h>
23 #include <sound/pcm.h>
24 #include <sound/initval.h>
25 #include <sound/soc.h>
26 
27 #include <asm/hardware.h>
28 #include <asm/arch/pxa-regs.h>
29 #include <asm/arch/audio.h>
30 
31 #include "pxa2xx-pcm.h"
32 
33 /* used to disable sysclk if external crystal is used */
34 static int extclk;
35 module_param(extclk, int, 0);
36 MODULE_PARM_DESC(extclk, "set to 1 to disable pxa2xx i2s sysclk");
37 
38 struct pxa_i2s_port {
39 	u32 sadiv;
40 	u32 sacr0;
41 	u32 sacr1;
42 	u32 saimr;
43 	int master;
44 };
45 static struct pxa_i2s_port pxa_i2s;
46 
47 #define PXA_I2S_DAIFMT \
48 	(SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF)
49 
50 #define PXA_I2S_DIR \
51 	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
52 
53 #define PXA_I2S_RATES \
54 	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
55 	SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
56 	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
57 
58 /* priv is divider */
59 static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = {
60 	/* pxa2xx I2S frame and clock master modes */
61 	{
62 		.fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
63 		.pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
64 		.pcmrate = SNDRV_PCM_RATE_8000,
65 		.pcmdir = PXA_I2S_DIR,
66 		.flags = SND_SOC_DAI_BFS_DIV,
67 		.fs = 256,
68 		.bfs = SND_SOC_FSBD(4),
69 		.priv = 0x48,
70 	},
71 	{
72 		.fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
73 		.pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
74 		.pcmrate = SNDRV_PCM_RATE_11025,
75 		.pcmdir = PXA_I2S_DIR,
76 		.flags = SND_SOC_DAI_BFS_DIV,
77 		.fs = 256,
78 		.bfs = SND_SOC_FSBD(4),
79 		.priv = 0x34,
80 	},
81 	{
82 		.fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
83 		.pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
84 		.pcmrate = SNDRV_PCM_RATE_16000,
85 		.pcmdir = PXA_I2S_DIR,
86 		.flags = SND_SOC_DAI_BFS_DIV,
87 		.fs = 256,
88 		.bfs = SND_SOC_FSBD(4),
89 		.priv = 0x24,
90 	},
91 	{
92 		.fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
93 		.pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
94 		.pcmrate = SNDRV_PCM_RATE_22050,
95 		.pcmdir = PXA_I2S_DIR,
96 		.flags = SND_SOC_DAI_BFS_DIV,
97 		.fs = 256,
98 		.bfs = SND_SOC_FSBD(4),
99 		.priv = 0x1a,
100 	},
101 	{
102 		.fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
103 		.pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
104 		.pcmrate = SNDRV_PCM_RATE_44100,
105 		.pcmdir = PXA_I2S_DIR,
106 		.flags = SND_SOC_DAI_BFS_DIV,
107 		.fs = 256,
108 		.bfs = SND_SOC_FSBD(4),
109 		.priv = 0xd,
110 	},
111 	{
112 		.fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
113 		.pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
114 		.pcmrate = SNDRV_PCM_RATE_48000,
115 		.pcmdir = PXA_I2S_DIR,
116 		.flags = SND_SOC_DAI_BFS_DIV,
117 		.fs = 256,
118 		.bfs = SND_SOC_FSBD(4),
119 		.priv = 0xc,
120 	},
121 
122 	/* pxa2xx I2S frame master and clock slave mode */
123 	{
124 		.fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS,
125 		.pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
126 		.pcmrate = PXA_I2S_RATES,
127 		.pcmdir = PXA_I2S_DIR,
128 		.fs = SND_SOC_FS_ALL,
129 		.flags = SND_SOC_DAI_BFS_RATE,
130 		.bfs = 64,
131 		.priv = 0x48,
132 	},
133 };
134 
135 static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {
136 	.name			= "I2S PCM Stereo out",
137 	.dev_addr		= __PREG(SADR),
138 	.drcmr			= &DRCMRTXSADR,
139 	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
140 				  DCMD_BURST32 | DCMD_WIDTH4,
141 };
142 
143 static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = {
144 	.name			= "I2S PCM Stereo in",
145 	.dev_addr		= __PREG(SADR),
146 	.drcmr			= &DRCMRRXSADR,
147 	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
148 				  DCMD_BURST32 | DCMD_WIDTH4,
149 };
150 
151 static struct pxa2xx_gpio gpio_bus[] = {
152 	{ /* I2S SoC Slave */
153 		.rx = GPIO29_SDATA_IN_I2S_MD,
154 		.tx = GPIO30_SDATA_OUT_I2S_MD,
155 		.clk = GPIO28_BITCLK_IN_I2S_MD,
156 		.frm = GPIO31_SYNC_I2S_MD,
157 	},
158 	{ /* I2S SoC Master */
159 #ifdef CONFIG_PXA27x
160 		.sys = GPIO113_I2S_SYSCLK_MD,
161 #else
162 		.sys = GPIO32_SYSCLK_I2S_MD,
163 #endif
164 		.rx = GPIO29_SDATA_IN_I2S_MD,
165 		.tx = GPIO30_SDATA_OUT_I2S_MD,
166 		.clk = GPIO28_BITCLK_OUT_I2S_MD,
167 		.frm = GPIO31_SYNC_I2S_MD,
168 	},
169 };
170 
171 static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
172 {
173 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
174 
175 	if (!rtd->cpu_dai->active) {
176 		SACR0 |= SACR0_RST;
177 		SACR0 = 0;
178 	}
179 
180 	return 0;
181 }
182 
183 /* wait for I2S controller to be ready */
184 static int pxa_i2s_wait(void)
185 {
186 	int i;
187 
188 	/* flush the Rx FIFO */
189 	for(i = 0; i < 16; i++)
190 		SADR;
191 	return 0;
192 }
193 
194 static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
195 				struct snd_pcm_hw_params *params)
196 {
197 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
198 
199 	pxa_i2s.master = 0;
200 	if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CBS_CFS)
201 		pxa_i2s.master = 1;
202 
203 	if (pxa_i2s.master && !extclk)
204 		pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys);
205 
206 	pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx);
207 	pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx);
208 	pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm);
209 	pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk);
210 	pxa_set_cken(CKEN8_I2S, 1);
211 	pxa_i2s_wait();
212 
213 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
214 		rtd->cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out;
215 	else
216 		rtd->cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in;
217 
218 	/* is port used by another stream */
219 	if (!(SACR0 & SACR0_ENB)) {
220 
221 		SACR0 = 0;
222 		SACR1 = 0;
223 		if (pxa_i2s.master)
224 			SACR0 |= SACR0_BCKD;
225 
226 		SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
227 
228 		if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_LEFT_J)
229 			SACR1 |= SACR1_AMSL;
230 	}
231 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
232 		SAIMR |= SAIMR_TFS;
233 	else
234 		SAIMR |= SAIMR_RFS;
235 
236 	SADIV = rtd->cpu_dai->dai_runtime.priv;
237 	return 0;
238 }
239 
240 static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
241 {
242 	int ret = 0;
243 
244 	switch (cmd) {
245 	case SNDRV_PCM_TRIGGER_START:
246 		SACR0 |= SACR0_ENB;
247 		break;
248 	case SNDRV_PCM_TRIGGER_RESUME:
249 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
250 	case SNDRV_PCM_TRIGGER_STOP:
251 	case SNDRV_PCM_TRIGGER_SUSPEND:
252 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
253 		break;
254 	default:
255 		ret = -EINVAL;
256 	}
257 
258 	return ret;
259 }
260 
261 static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
262 {
263 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
264 		SACR1 |= SACR1_DRPL;
265 		SAIMR &= ~SAIMR_TFS;
266 	} else {
267 		SACR1 |= SACR1_DREC;
268 		SAIMR &= ~SAIMR_RFS;
269 	}
270 
271 	if (SACR1 & (SACR1_DREC | SACR1_DRPL)) {
272 		SACR0 &= ~SACR0_ENB;
273 		pxa_i2s_wait();
274 		pxa_set_cken(CKEN8_I2S, 0);
275 	}
276 }
277 
278 #ifdef CONFIG_PM
279 static int pxa2xx_i2s_suspend(struct platform_device *dev,
280 	struct snd_soc_cpu_dai *dai)
281 {
282 	if (!dai->active)
283 		return 0;
284 
285 	/* store registers */
286 	pxa_i2s.sacr0 = SACR0;
287 	pxa_i2s.sacr1 = SACR1;
288 	pxa_i2s.saimr = SAIMR;
289 	pxa_i2s.sadiv = SADIV;
290 
291 	/* deactivate link */
292 	SACR0 &= ~SACR0_ENB;
293 	pxa_i2s_wait();
294 	return 0;
295 }
296 
297 static int pxa2xx_i2s_resume(struct platform_device *pdev,
298 	struct snd_soc_cpu_dai *dai)
299 {
300 	if (!dai->active)
301 		return 0;
302 
303 	pxa_i2s_wait();
304 
305 	SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB;
306 	SACR1 = pxa_i2s.sacr1;
307 	SAIMR = pxa_i2s.saimr;
308 	SADIV = pxa_i2s.sadiv;
309 	SACR0 |= SACR0_ENB;
310 
311 	return 0;
312 }
313 
314 #else
315 #define pxa2xx_i2s_suspend	NULL
316 #define pxa2xx_i2s_resume	NULL
317 #endif
318 
319 /* pxa2xx I2S sysclock is always 256 FS */
320 static unsigned int pxa_i2s_config_sysclk(struct snd_soc_cpu_dai *iface,
321 	struct snd_soc_clock_info *info, unsigned int clk)
322 {
323 	return info->rate << 8;
324 }
325 
326 struct snd_soc_cpu_dai pxa_i2s_dai = {
327 	.name = "pxa2xx-i2s",
328 	.id = 0,
329 	.type = SND_SOC_DAI_I2S,
330 	.suspend = pxa2xx_i2s_suspend,
331 	.resume = pxa2xx_i2s_resume,
332 	.config_sysclk = pxa_i2s_config_sysclk,
333 	.playback = {
334 		.channels_min = 2,
335 		.channels_max = 2,},
336 	.capture = {
337 		.channels_min = 2,
338 		.channels_max = 2,},
339 	.ops = {
340 		.startup = pxa2xx_i2s_startup,
341 		.shutdown = pxa2xx_i2s_shutdown,
342 		.trigger = pxa2xx_i2s_trigger,
343 		.hw_params = pxa2xx_i2s_hw_params,},
344 	.caps = {
345 		.num_modes = ARRAY_SIZE(pxa2xx_i2s_modes),
346 		.mode = pxa2xx_i2s_modes,},
347 };
348 
349 EXPORT_SYMBOL_GPL(pxa_i2s_dai);
350 
351 /* Module information */
352 MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
353 MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
354 MODULE_LICENSE("GPL");
355