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