• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/sound/soc/pxa/
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
38static struct i2c_client *max9486_client;
39static 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
48static void set_max9485_clk(char clk)
49{
50	i2c_master_send(max9486_client, &clk, 1);
51}
52
53static 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 */
71static 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
80static 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
89static 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
152static 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
158static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state)
159{
160	raumfeld_enable_audio(false);
161	return 0;
162}
163
164static int raumfeld_line_resume(struct platform_device *pdev)
165{
166	raumfeld_enable_audio(true);
167	return 0;
168}
169
170static 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
178static 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
190static 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
246static struct snd_soc_ops raumfeld_ak4104_ops = {
247	.hw_params = raumfeld_ak4104_hw_params,
248};
249
250static 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
258static 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 */
266static struct snd_soc_device raumfeld_line_devdata = {
267	.card = &snd_soc_line_raumfeld,
268	.codec_dev = &soc_codec_device_cs4270,
269};
270
271static struct snd_soc_device raumfeld_spdif_devdata = {
272	.card = &snd_soc_spdif_raumfeld,
273	.codec_dev = &soc_codec_device_ak4104,
274};
275
276static struct platform_device *raumfeld_audio_line_device;
277static struct platform_device *raumfeld_audio_spdif_device;
278
279static 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
332static 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
348module_init(raumfeld_audio_init);
349module_exit(raumfeld_audio_exit);
350
351/* Module information */
352MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
353MODULE_DESCRIPTION("Raumfeld audio SoC");
354MODULE_LICENSE("GPL");
355