xref: /openbmc/linux/sound/soc/pxa/pxa2xx-i2s.c (revision 5a2cc50f)
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 <linux/clk.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/pxa2xx-gpio.h>
30 #include <asm/arch/audio.h>
31 
32 #include "pxa2xx-pcm.h"
33 #include "pxa2xx-i2s.h"
34 
35 struct pxa_i2s_port {
36 	u32 sadiv;
37 	u32 sacr0;
38 	u32 sacr1;
39 	u32 saimr;
40 	int master;
41 	u32 fmt;
42 };
43 static struct pxa_i2s_port pxa_i2s;
44 static struct clk *clk_i2s;
45 
46 static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {
47 	.name			= "I2S PCM Stereo out",
48 	.dev_addr		= __PREG(SADR),
49 	.drcmr			= &DRCMRTXSADR,
50 	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
51 				  DCMD_BURST32 | DCMD_WIDTH4,
52 };
53 
54 static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = {
55 	.name			= "I2S PCM Stereo in",
56 	.dev_addr		= __PREG(SADR),
57 	.drcmr			= &DRCMRRXSADR,
58 	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
59 				  DCMD_BURST32 | DCMD_WIDTH4,
60 };
61 
62 static struct pxa2xx_gpio gpio_bus[] = {
63 	{ /* I2S SoC Slave */
64 		.rx = GPIO29_SDATA_IN_I2S_MD,
65 		.tx = GPIO30_SDATA_OUT_I2S_MD,
66 		.clk = GPIO28_BITCLK_IN_I2S_MD,
67 		.frm = GPIO31_SYNC_I2S_MD,
68 	},
69 	{ /* I2S SoC Master */
70 #ifdef CONFIG_PXA27x
71 		.sys = GPIO113_I2S_SYSCLK_MD,
72 #else
73 		.sys = GPIO32_SYSCLK_I2S_MD,
74 #endif
75 		.rx = GPIO29_SDATA_IN_I2S_MD,
76 		.tx = GPIO30_SDATA_OUT_I2S_MD,
77 		.clk = GPIO28_BITCLK_OUT_I2S_MD,
78 		.frm = GPIO31_SYNC_I2S_MD,
79 	},
80 };
81 
82 static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
83 {
84 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
85 	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
86 
87 	clk_i2s = clk_get(NULL, "I2SCLK");
88 	if (IS_ERR(clk_i2s))
89 		return PTR_ERR(clk_i2s);
90 
91 	if (!cpu_dai->active) {
92 		SACR0 |= SACR0_RST;
93 		SACR0 = 0;
94 	}
95 
96 	return 0;
97 }
98 
99 /* wait for I2S controller to be ready */
100 static int pxa_i2s_wait(void)
101 {
102 	int i;
103 
104 	/* flush the Rx FIFO */
105 	for(i = 0; i < 16; i++)
106 		SADR;
107 	return 0;
108 }
109 
110 static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
111 		unsigned int fmt)
112 {
113 	/* interface format */
114 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
115 	case SND_SOC_DAIFMT_I2S:
116 		pxa_i2s.fmt = 0;
117 		break;
118 	case SND_SOC_DAIFMT_LEFT_J:
119 		pxa_i2s.fmt = SACR1_AMSL;
120 		break;
121 	}
122 
123 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
124 	case SND_SOC_DAIFMT_CBS_CFS:
125 		pxa_i2s.master = 1;
126 		break;
127 	case SND_SOC_DAIFMT_CBM_CFS:
128 		pxa_i2s.master = 0;
129 		break;
130 	default:
131 		break;
132 	}
133 	return 0;
134 }
135 
136 static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
137 		int clk_id, unsigned int freq, int dir)
138 {
139 	if (clk_id != PXA2XX_I2S_SYSCLK)
140 		return -ENODEV;
141 
142 	if (pxa_i2s.master && dir == SND_SOC_CLOCK_OUT)
143 		pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys);
144 
145 	return 0;
146 }
147 
148 static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
149 				struct snd_pcm_hw_params *params)
150 {
151 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
152 	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
153 
154 	pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx);
155 	pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx);
156 	pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm);
157 	pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk);
158 	clk_enable(clk_i2s);
159 	pxa_i2s_wait();
160 
161 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
162 		cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out;
163 	else
164 		cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in;
165 
166 	/* is port used by another stream */
167 	if (!(SACR0 & SACR0_ENB)) {
168 
169 		SACR0 = 0;
170 		SACR1 = 0;
171 		if (pxa_i2s.master)
172 			SACR0 |= SACR0_BCKD;
173 
174 		SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
175 		SACR1 |= pxa_i2s.fmt;
176 	}
177 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
178 		SAIMR |= SAIMR_TFS;
179 	else
180 		SAIMR |= SAIMR_RFS;
181 
182 	switch (params_rate(params)) {
183 	case 8000:
184 		SADIV = 0x48;
185 		break;
186 	case 11025:
187 		SADIV = 0x34;
188 		break;
189 	case 16000:
190 		SADIV = 0x24;
191 		break;
192 	case 22050:
193 		SADIV = 0x1a;
194 		break;
195 	case 44100:
196 		SADIV = 0xd;
197 		break;
198 	case 48000:
199 		SADIV = 0xc;
200 		break;
201 	case 96000: /* not in manual and possibly slightly inaccurate */
202 		SADIV = 0x6;
203 		break;
204 	}
205 
206 	return 0;
207 }
208 
209 static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
210 {
211 	int ret = 0;
212 
213 	switch (cmd) {
214 	case SNDRV_PCM_TRIGGER_START:
215 		SACR0 |= SACR0_ENB;
216 		break;
217 	case SNDRV_PCM_TRIGGER_RESUME:
218 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
219 	case SNDRV_PCM_TRIGGER_STOP:
220 	case SNDRV_PCM_TRIGGER_SUSPEND:
221 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
222 		break;
223 	default:
224 		ret = -EINVAL;
225 	}
226 
227 	return ret;
228 }
229 
230 static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
231 {
232 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
233 		SACR1 |= SACR1_DRPL;
234 		SAIMR &= ~SAIMR_TFS;
235 	} else {
236 		SACR1 |= SACR1_DREC;
237 		SAIMR &= ~SAIMR_RFS;
238 	}
239 
240 	if (SACR1 & (SACR1_DREC | SACR1_DRPL)) {
241 		SACR0 &= ~SACR0_ENB;
242 		pxa_i2s_wait();
243 		clk_disable(clk_i2s);
244 	}
245 
246 	clk_put(clk_i2s);
247 }
248 
249 #ifdef CONFIG_PM
250 static int pxa2xx_i2s_suspend(struct platform_device *dev,
251 	struct snd_soc_cpu_dai *dai)
252 {
253 	if (!dai->active)
254 		return 0;
255 
256 	/* store registers */
257 	pxa_i2s.sacr0 = SACR0;
258 	pxa_i2s.sacr1 = SACR1;
259 	pxa_i2s.saimr = SAIMR;
260 	pxa_i2s.sadiv = SADIV;
261 
262 	/* deactivate link */
263 	SACR0 &= ~SACR0_ENB;
264 	pxa_i2s_wait();
265 	return 0;
266 }
267 
268 static int pxa2xx_i2s_resume(struct platform_device *pdev,
269 	struct snd_soc_cpu_dai *dai)
270 {
271 	if (!dai->active)
272 		return 0;
273 
274 	pxa_i2s_wait();
275 
276 	SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB;
277 	SACR1 = pxa_i2s.sacr1;
278 	SAIMR = pxa_i2s.saimr;
279 	SADIV = pxa_i2s.sadiv;
280 	SACR0 |= SACR0_ENB;
281 
282 	return 0;
283 }
284 
285 #else
286 #define pxa2xx_i2s_suspend	NULL
287 #define pxa2xx_i2s_resume	NULL
288 #endif
289 
290 #define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
291 		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
292 		SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
293 
294 struct snd_soc_cpu_dai pxa_i2s_dai = {
295 	.name = "pxa2xx-i2s",
296 	.id = 0,
297 	.type = SND_SOC_DAI_I2S,
298 	.suspend = pxa2xx_i2s_suspend,
299 	.resume = pxa2xx_i2s_resume,
300 	.playback = {
301 		.channels_min = 2,
302 		.channels_max = 2,
303 		.rates = PXA2XX_I2S_RATES,
304 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
305 	.capture = {
306 		.channels_min = 2,
307 		.channels_max = 2,
308 		.rates = PXA2XX_I2S_RATES,
309 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
310 	.ops = {
311 		.startup = pxa2xx_i2s_startup,
312 		.shutdown = pxa2xx_i2s_shutdown,
313 		.trigger = pxa2xx_i2s_trigger,
314 		.hw_params = pxa2xx_i2s_hw_params,},
315 	.dai_ops = {
316 		.set_fmt = pxa2xx_i2s_set_dai_fmt,
317 		.set_sysclk = pxa2xx_i2s_set_dai_sysclk,
318 	},
319 };
320 
321 EXPORT_SYMBOL_GPL(pxa_i2s_dai);
322 
323 /* Module information */
324 MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
325 MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
326 MODULE_LICENSE("GPL");
327