diff options
Diffstat (limited to 'sound/soc/pxa/raumfeld.c')
-rw-r--r-- | sound/soc/pxa/raumfeld.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c new file mode 100644 index 000000000000..7e3f41696c41 --- /dev/null +++ b/sound/soc/pxa/raumfeld.c | |||
@@ -0,0 +1,354 @@ | |||
1 | /* | ||
2 | * raumfeld_audio.c -- SoC audio for Raumfeld audio devices | ||
3 | * | ||
4 | * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> | ||
5 | * | ||
6 | * based on code from: | ||
7 | * | ||
8 | * Wolfson Microelectronics PLC. | ||
9 | * Openedhand Ltd. | ||
10 | * Liam Girdwood <lrg@slimlogic.co.uk> | ||
11 | * Richard Purdie <richard@openedhand.com> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify it | ||
14 | * under the terms of the GNU General Public License as published by the | ||
15 | * Free Software Foundation; either version 2 of the License, or (at your | ||
16 | * option) any later version. | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/i2c.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/gpio.h> | ||
23 | #include <sound/pcm.h> | ||
24 | #include <sound/soc.h> | ||
25 | #include <sound/soc-dapm.h> | ||
26 | |||
27 | #include <asm/mach-types.h> | ||
28 | |||
29 | #include "../codecs/cs4270.h" | ||
30 | #include "../codecs/ak4104.h" | ||
31 | #include "pxa2xx-pcm.h" | ||
32 | #include "pxa-ssp.h" | ||
33 | |||
34 | #define GPIO_SPDIF_RESET (38) | ||
35 | #define GPIO_MCLK_RESET (111) | ||
36 | #define GPIO_CODEC_RESET (120) | ||
37 | |||
38 | static struct i2c_client *max9486_client; | ||
39 | static struct i2c_board_info max9486_hwmon_info = { | ||
40 | I2C_BOARD_INFO("max9485", 0x63), | ||
41 | }; | ||
42 | |||
43 | #define MAX9485_MCLK_FREQ_112896 0x22 | ||
44 | #define MAX9485_MCLK_FREQ_122880 0x23 | ||
45 | #define MAX9485_MCLK_FREQ_225792 0x32 | ||
46 | #define MAX9485_MCLK_FREQ_245760 0x33 | ||
47 | |||
48 | static void set_max9485_clk(char clk) | ||
49 | { | ||
50 | i2c_master_send(max9486_client, &clk, 1); | ||
51 | } | ||
52 | |||
53 | static void raumfeld_enable_audio(bool en) | ||
54 | { | ||
55 | if (en) { | ||
56 | gpio_set_value(GPIO_MCLK_RESET, 1); | ||
57 | |||
58 | /* wait some time to let the clocks become stable */ | ||
59 | msleep(100); | ||
60 | |||
61 | gpio_set_value(GPIO_SPDIF_RESET, 1); | ||
62 | gpio_set_value(GPIO_CODEC_RESET, 1); | ||
63 | } else { | ||
64 | gpio_set_value(GPIO_MCLK_RESET, 0); | ||
65 | gpio_set_value(GPIO_SPDIF_RESET, 0); | ||
66 | gpio_set_value(GPIO_CODEC_RESET, 0); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | /* CS4270 */ | ||
71 | static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream) | ||
72 | { | ||
73 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
74 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
75 | |||
76 | /* set freq to 0 to enable all possible codec sample rates */ | ||
77 | return snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); | ||
78 | } | ||
79 | |||
80 | static void raumfeld_cs4270_shutdown(struct snd_pcm_substream *substream) | ||
81 | { | ||
82 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
83 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
84 | |||
85 | /* set freq to 0 to enable all possible codec sample rates */ | ||
86 | snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); | ||
87 | } | ||
88 | |||
89 | static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, | ||
90 | struct snd_pcm_hw_params *params) | ||
91 | { | ||
92 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
93 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
94 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
95 | unsigned int fmt, clk = 0; | ||
96 | int ret = 0; | ||
97 | |||
98 | switch (params_rate(params)) { | ||
99 | case 44100: | ||
100 | set_max9485_clk(MAX9485_MCLK_FREQ_112896); | ||
101 | clk = 11289600; | ||
102 | break; | ||
103 | case 48000: | ||
104 | set_max9485_clk(MAX9485_MCLK_FREQ_122880); | ||
105 | clk = 12288000; | ||
106 | break; | ||
107 | case 88200: | ||
108 | set_max9485_clk(MAX9485_MCLK_FREQ_225792); | ||
109 | clk = 22579200; | ||
110 | break; | ||
111 | case 96000: | ||
112 | set_max9485_clk(MAX9485_MCLK_FREQ_245760); | ||
113 | clk = 24576000; | ||
114 | break; | ||
115 | default: | ||
116 | return -EINVAL; | ||
117 | } | ||
118 | |||
119 | fmt = SND_SOC_DAIFMT_I2S | | ||
120 | SND_SOC_DAIFMT_NB_NF | | ||
121 | SND_SOC_DAIFMT_CBS_CFS; | ||
122 | |||
123 | /* setup the CODEC DAI */ | ||
124 | ret = snd_soc_dai_set_fmt(codec_dai, fmt); | ||
125 | if (ret < 0) | ||
126 | return ret; | ||
127 | |||
128 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0); | ||
129 | if (ret < 0) | ||
130 | return ret; | ||
131 | |||
132 | /* setup the CPU DAI */ | ||
133 | ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); | ||
134 | if (ret < 0) | ||
135 | return ret; | ||
136 | |||
137 | ret = snd_soc_dai_set_fmt(cpu_dai, fmt); | ||
138 | if (ret < 0) | ||
139 | return ret; | ||
140 | |||
141 | ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); | ||
142 | if (ret < 0) | ||
143 | return ret; | ||
144 | |||
145 | ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); | ||
146 | if (ret < 0) | ||
147 | return ret; | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | static struct snd_soc_ops raumfeld_cs4270_ops = { | ||
153 | .startup = raumfeld_cs4270_startup, | ||
154 | .shutdown = raumfeld_cs4270_shutdown, | ||
155 | .hw_params = raumfeld_cs4270_hw_params, | ||
156 | }; | ||
157 | |||
158 | static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state) | ||
159 | { | ||
160 | raumfeld_enable_audio(false); | ||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int raumfeld_line_resume(struct platform_device *pdev) | ||
165 | { | ||
166 | raumfeld_enable_audio(true); | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | static struct snd_soc_dai_link raumfeld_line_dai = { | ||
171 | .name = "CS4270", | ||
172 | .stream_name = "CS4270", | ||
173 | .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP1], | ||
174 | .codec_dai = &cs4270_dai, | ||
175 | .ops = &raumfeld_cs4270_ops, | ||
176 | }; | ||
177 | |||
178 | static struct snd_soc_card snd_soc_line_raumfeld = { | ||
179 | .name = "Raumfeld analog", | ||
180 | .platform = &pxa2xx_soc_platform, | ||
181 | .dai_link = &raumfeld_line_dai, | ||
182 | .suspend_post = raumfeld_line_suspend, | ||
183 | .resume_pre = raumfeld_line_resume, | ||
184 | .num_links = 1, | ||
185 | }; | ||
186 | |||
187 | |||
188 | /* AK4104 */ | ||
189 | |||
190 | static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, | ||
191 | struct snd_pcm_hw_params *params) | ||
192 | { | ||
193 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
194 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
195 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
196 | int fmt, ret = 0, clk = 0; | ||
197 | |||
198 | switch (params_rate(params)) { | ||
199 | case 44100: | ||
200 | set_max9485_clk(MAX9485_MCLK_FREQ_112896); | ||
201 | clk = 11289600; | ||
202 | break; | ||
203 | case 48000: | ||
204 | set_max9485_clk(MAX9485_MCLK_FREQ_122880); | ||
205 | clk = 12288000; | ||
206 | break; | ||
207 | case 88200: | ||
208 | set_max9485_clk(MAX9485_MCLK_FREQ_225792); | ||
209 | clk = 22579200; | ||
210 | break; | ||
211 | case 96000: | ||
212 | set_max9485_clk(MAX9485_MCLK_FREQ_245760); | ||
213 | clk = 24576000; | ||
214 | break; | ||
215 | default: | ||
216 | return -EINVAL; | ||
217 | } | ||
218 | |||
219 | fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF; | ||
220 | |||
221 | /* setup the CODEC DAI */ | ||
222 | ret = snd_soc_dai_set_fmt(codec_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); | ||
223 | if (ret < 0) | ||
224 | return ret; | ||
225 | |||
226 | /* setup the CPU DAI */ | ||
227 | ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); | ||
228 | if (ret < 0) | ||
229 | return ret; | ||
230 | |||
231 | ret = snd_soc_dai_set_fmt(cpu_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); | ||
232 | if (ret < 0) | ||
233 | return ret; | ||
234 | |||
235 | ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); | ||
236 | if (ret < 0) | ||
237 | return ret; | ||
238 | |||
239 | ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); | ||
240 | if (ret < 0) | ||
241 | return ret; | ||
242 | |||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | static struct snd_soc_ops raumfeld_ak4104_ops = { | ||
247 | .hw_params = raumfeld_ak4104_hw_params, | ||
248 | }; | ||
249 | |||
250 | static struct snd_soc_dai_link raumfeld_spdif_dai = { | ||
251 | .name = "ak4104", | ||
252 | .stream_name = "Playback", | ||
253 | .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP2], | ||
254 | .codec_dai = &ak4104_dai, | ||
255 | .ops = &raumfeld_ak4104_ops, | ||
256 | }; | ||
257 | |||
258 | static struct snd_soc_card snd_soc_spdif_raumfeld = { | ||
259 | .name = "Raumfeld S/PDIF", | ||
260 | .platform = &pxa2xx_soc_platform, | ||
261 | .dai_link = &raumfeld_spdif_dai, | ||
262 | .num_links = 1 | ||
263 | }; | ||
264 | |||
265 | /* raumfeld_audio audio subsystem */ | ||
266 | static struct snd_soc_device raumfeld_line_devdata = { | ||
267 | .card = &snd_soc_line_raumfeld, | ||
268 | .codec_dev = &soc_codec_device_cs4270, | ||
269 | }; | ||
270 | |||
271 | static struct snd_soc_device raumfeld_spdif_devdata = { | ||
272 | .card = &snd_soc_spdif_raumfeld, | ||
273 | .codec_dev = &soc_codec_device_ak4104, | ||
274 | }; | ||
275 | |||
276 | static struct platform_device *raumfeld_audio_line_device; | ||
277 | static struct platform_device *raumfeld_audio_spdif_device; | ||
278 | |||
279 | static int __init raumfeld_audio_init(void) | ||
280 | { | ||
281 | int ret; | ||
282 | |||
283 | if (!machine_is_raumfeld_speaker() && | ||
284 | !machine_is_raumfeld_connector()) | ||
285 | return 0; | ||
286 | |||
287 | max9486_client = i2c_new_device(i2c_get_adapter(0), | ||
288 | &max9486_hwmon_info); | ||
289 | |||
290 | if (!max9486_client) | ||
291 | return -ENOMEM; | ||
292 | |||
293 | set_max9485_clk(MAX9485_MCLK_FREQ_122880); | ||
294 | |||
295 | /* LINE */ | ||
296 | raumfeld_audio_line_device = platform_device_alloc("soc-audio", 0); | ||
297 | if (!raumfeld_audio_line_device) | ||
298 | return -ENOMEM; | ||
299 | |||
300 | platform_set_drvdata(raumfeld_audio_line_device, | ||
301 | &raumfeld_line_devdata); | ||
302 | raumfeld_line_devdata.dev = &raumfeld_audio_line_device->dev; | ||
303 | ret = platform_device_add(raumfeld_audio_line_device); | ||
304 | if (ret) | ||
305 | platform_device_put(raumfeld_audio_line_device); | ||
306 | |||
307 | /* no S/PDIF on Speakers */ | ||
308 | if (machine_is_raumfeld_speaker()) | ||
309 | return ret; | ||
310 | |||
311 | /* S/PDIF */ | ||
312 | raumfeld_audio_spdif_device = platform_device_alloc("soc-audio", 1); | ||
313 | if (!raumfeld_audio_spdif_device) { | ||
314 | platform_device_put(raumfeld_audio_line_device); | ||
315 | return -ENOMEM; | ||
316 | } | ||
317 | |||
318 | platform_set_drvdata(raumfeld_audio_spdif_device, | ||
319 | &raumfeld_spdif_devdata); | ||
320 | raumfeld_spdif_devdata.dev = &raumfeld_audio_spdif_device->dev; | ||
321 | ret = platform_device_add(raumfeld_audio_spdif_device); | ||
322 | if (ret) { | ||
323 | platform_device_put(raumfeld_audio_line_device); | ||
324 | platform_device_put(raumfeld_audio_spdif_device); | ||
325 | } | ||
326 | |||
327 | raumfeld_enable_audio(true); | ||
328 | |||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | static void __exit raumfeld_audio_exit(void) | ||
333 | { | ||
334 | raumfeld_enable_audio(false); | ||
335 | |||
336 | platform_device_unregister(raumfeld_audio_line_device); | ||
337 | |||
338 | if (machine_is_raumfeld_connector()) | ||
339 | platform_device_unregister(raumfeld_audio_spdif_device); | ||
340 | |||
341 | i2c_unregister_device(max9486_client); | ||
342 | |||
343 | gpio_free(GPIO_MCLK_RESET); | ||
344 | gpio_free(GPIO_CODEC_RESET); | ||
345 | gpio_free(GPIO_SPDIF_RESET); | ||
346 | } | ||
347 | |||
348 | module_init(raumfeld_audio_init); | ||
349 | module_exit(raumfeld_audio_exit); | ||
350 | |||
351 | /* Module information */ | ||
352 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); | ||
353 | MODULE_DESCRIPTION("Raumfeld audio SoC"); | ||
354 | MODULE_LICENSE("GPL"); | ||