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