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