1/*
2 * neo1973_wm8753.c  --  SoC audio for Openmoko Neo1973 and Freerunner devices
3 *
4 * Copyright 2007 Openmoko Inc
5 * Author: Graeme Gregory <graeme@openmoko.org>
6 * Copyright 2007 Wolfson Microelectronics PLC.
7 * Author: Graeme Gregory
8 *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
9 * Copyright 2009 Wolfson Microelectronics
10 *
11 *  This program is free software; you can redistribute  it and/or modify it
12 *  under  the terms of  the GNU General  Public License as published by the
13 *  Free Software Foundation;  either version 2 of the  License, or (at your
14 *  option) any later version.
15 */
16
17#include <linux/module.h>
18#include <linux/platform_device.h>
19#include <linux/gpio.h>
20
21#include <sound/soc.h>
22
23#include <mach/gpio-samsung.h>
24#include <asm/mach-types.h>
25#include "regs-iis.h"
26
27#include "../codecs/wm8753.h"
28#include "s3c24xx-i2s.h"
29
30static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
31	struct snd_pcm_hw_params *params)
32{
33	struct snd_soc_pcm_runtime *rtd = substream->private_data;
34	struct snd_soc_dai *codec_dai = rtd->codec_dai;
35	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
36	unsigned int pll_out = 0, bclk = 0;
37	int ret = 0;
38	unsigned long iis_clkrate;
39
40	iis_clkrate = s3c24xx_i2s_get_clockrate();
41
42	switch (params_rate(params)) {
43	case 8000:
44	case 16000:
45		pll_out = 12288000;
46		break;
47	case 48000:
48		bclk = WM8753_BCLK_DIV_4;
49		pll_out = 12288000;
50		break;
51	case 96000:
52		bclk = WM8753_BCLK_DIV_2;
53		pll_out = 12288000;
54		break;
55	case 11025:
56		bclk = WM8753_BCLK_DIV_16;
57		pll_out = 11289600;
58		break;
59	case 22050:
60		bclk = WM8753_BCLK_DIV_8;
61		pll_out = 11289600;
62		break;
63	case 44100:
64		bclk = WM8753_BCLK_DIV_4;
65		pll_out = 11289600;
66		break;
67	case 88200:
68		bclk = WM8753_BCLK_DIV_2;
69		pll_out = 11289600;
70		break;
71	}
72
73	/* set the codec system clock for DAC and ADC */
74	ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
75		SND_SOC_CLOCK_IN);
76	if (ret < 0)
77		return ret;
78
79	/* set MCLK division for sample rate */
80	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
81		S3C2410_IISMOD_32FS);
82	if (ret < 0)
83		return ret;
84
85	/* set codec BCLK division for sample rate */
86	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
87	if (ret < 0)
88		return ret;
89
90	/* set prescaler division for sample rate */
91	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
92		S3C24XX_PRESCALE(4, 4));
93	if (ret < 0)
94		return ret;
95
96	/* codec PLL input is PCLK/4 */
97	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
98		iis_clkrate / 4, pll_out);
99	if (ret < 0)
100		return ret;
101
102	return 0;
103}
104
105static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
106{
107	struct snd_soc_pcm_runtime *rtd = substream->private_data;
108	struct snd_soc_dai *codec_dai = rtd->codec_dai;
109
110	/* disable the PLL */
111	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
112}
113
114/*
115 * Neo1973 WM8753 HiFi DAI opserations.
116 */
117static struct snd_soc_ops neo1973_hifi_ops = {
118	.hw_params = neo1973_hifi_hw_params,
119	.hw_free = neo1973_hifi_hw_free,
120};
121
122static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
123	struct snd_pcm_hw_params *params)
124{
125	struct snd_soc_pcm_runtime *rtd = substream->private_data;
126	struct snd_soc_dai *codec_dai = rtd->codec_dai;
127	unsigned int pcmdiv = 0;
128	int ret = 0;
129	unsigned long iis_clkrate;
130
131	iis_clkrate = s3c24xx_i2s_get_clockrate();
132
133	if (params_rate(params) != 8000)
134		return -EINVAL;
135	if (params_channels(params) != 1)
136		return -EINVAL;
137
138	pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
139
140	/* set the codec system clock for DAC and ADC */
141	ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
142		SND_SOC_CLOCK_IN);
143	if (ret < 0)
144		return ret;
145
146	/* set codec PCM division for sample rate */
147	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
148	if (ret < 0)
149		return ret;
150
151	/* configure and enable PLL for 12.288MHz output */
152	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
153		iis_clkrate / 4, 12288000);
154	if (ret < 0)
155		return ret;
156
157	return 0;
158}
159
160static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
161{
162	struct snd_soc_pcm_runtime *rtd = substream->private_data;
163	struct snd_soc_dai *codec_dai = rtd->codec_dai;
164
165	/* disable the PLL */
166	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
167}
168
169static struct snd_soc_ops neo1973_voice_ops = {
170	.hw_params = neo1973_voice_hw_params,
171	.hw_free = neo1973_voice_hw_free,
172};
173
174static int gta02_speaker_enabled;
175
176static int lm4853_set_spk(struct snd_kcontrol *kcontrol,
177	struct snd_ctl_elem_value *ucontrol)
178{
179	gta02_speaker_enabled = ucontrol->value.integer.value[0];
180
181	gpio_set_value(S3C2410_GPJ(2), !gta02_speaker_enabled);
182
183	return 0;
184}
185
186static int lm4853_get_spk(struct snd_kcontrol *kcontrol,
187	struct snd_ctl_elem_value *ucontrol)
188{
189	ucontrol->value.integer.value[0] = gta02_speaker_enabled;
190	return 0;
191}
192
193static int lm4853_event(struct snd_soc_dapm_widget *w,
194			struct snd_kcontrol *k, int event)
195{
196	gpio_set_value(S3C2410_GPJ(1), SND_SOC_DAPM_EVENT_OFF(event));
197
198	return 0;
199}
200
201static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = {
202	SND_SOC_DAPM_LINE("GSM Line Out", NULL),
203	SND_SOC_DAPM_LINE("GSM Line In", NULL),
204	SND_SOC_DAPM_MIC("Headset Mic", NULL),
205	SND_SOC_DAPM_MIC("Handset Mic", NULL),
206	SND_SOC_DAPM_SPK("Handset Spk", NULL),
207	SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),
208};
209
210static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = {
211	/* Connections to the GSM Module */
212	{"GSM Line Out", NULL, "MONO1"},
213	{"GSM Line Out", NULL, "MONO2"},
214	{"RXP", NULL, "GSM Line In"},
215	{"RXN", NULL, "GSM Line In"},
216
217	/* Connections to Headset */
218	{"MIC1", NULL, "Mic Bias"},
219	{"Mic Bias", NULL, "Headset Mic"},
220
221	/* Call Mic */
222	{"MIC2", NULL, "Mic Bias"},
223	{"MIC2N", NULL, "Mic Bias"},
224	{"Mic Bias", NULL, "Handset Mic"},
225
226	/* Connect the ALC pins */
227	{"ACIN", NULL, "ACOP"},
228
229	/* Connections to the amp */
230	{"Stereo Out", NULL, "LOUT1"},
231	{"Stereo Out", NULL, "ROUT1"},
232
233	/* Call Speaker */
234	{"Handset Spk", NULL, "LOUT2"},
235	{"Handset Spk", NULL, "ROUT2"},
236};
237
238static const struct snd_kcontrol_new neo1973_wm8753_controls[] = {
239	SOC_DAPM_PIN_SWITCH("GSM Line Out"),
240	SOC_DAPM_PIN_SWITCH("GSM Line In"),
241	SOC_DAPM_PIN_SWITCH("Headset Mic"),
242	SOC_DAPM_PIN_SWITCH("Handset Mic"),
243	SOC_DAPM_PIN_SWITCH("Handset Spk"),
244	SOC_DAPM_PIN_SWITCH("Stereo Out"),
245
246	SOC_SINGLE_BOOL_EXT("Amp Spk Switch", 0,
247		lm4853_get_spk,
248		lm4853_set_spk),
249};
250
251static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd)
252{
253	struct snd_soc_card *card = rtd->card;
254
255	/* set endpoints to default off mode */
256	snd_soc_dapm_disable_pin(&card->dapm, "GSM Line Out");
257	snd_soc_dapm_disable_pin(&card->dapm, "GSM Line In");
258	snd_soc_dapm_disable_pin(&card->dapm, "Headset Mic");
259	snd_soc_dapm_disable_pin(&card->dapm, "Handset Mic");
260	snd_soc_dapm_disable_pin(&card->dapm, "Stereo Out");
261	snd_soc_dapm_disable_pin(&card->dapm, "Handset Spk");
262
263	/* allow audio paths from the GSM modem to run during suspend */
264	snd_soc_dapm_ignore_suspend(&card->dapm, "GSM Line Out");
265	snd_soc_dapm_ignore_suspend(&card->dapm, "GSM Line In");
266	snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
267	snd_soc_dapm_ignore_suspend(&card->dapm, "Handset Mic");
268	snd_soc_dapm_ignore_suspend(&card->dapm, "Stereo Out");
269	snd_soc_dapm_ignore_suspend(&card->dapm, "Handset Spk");
270
271	return 0;
272}
273
274static struct snd_soc_dai_link neo1973_dai[] = {
275{ /* Hifi Playback - for similatious use with voice below */
276	.name = "WM8753",
277	.stream_name = "WM8753 HiFi",
278	.platform_name = "s3c24xx-iis",
279	.cpu_dai_name = "s3c24xx-iis",
280	.codec_dai_name = "wm8753-hifi",
281	.codec_name = "wm8753.0-001a",
282	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
283		   SND_SOC_DAIFMT_CBM_CFM,
284	.init = neo1973_wm8753_init,
285	.ops = &neo1973_hifi_ops,
286},
287{ /* Voice via BT */
288	.name = "Bluetooth",
289	.stream_name = "Voice",
290	.cpu_dai_name = "bt-sco-pcm",
291	.codec_dai_name = "wm8753-voice",
292	.codec_name = "wm8753.0-001a",
293	.dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
294		   SND_SOC_DAIFMT_CBS_CFS,
295	.ops = &neo1973_voice_ops,
296},
297};
298
299static struct snd_soc_aux_dev neo1973_aux_devs[] = {
300	{
301		.name = "dfbmcs320",
302		.codec_name = "dfbmcs320.0",
303	},
304};
305
306static struct snd_soc_codec_conf neo1973_codec_conf[] = {
307	{
308		.dev_name = "lm4857.0-007c",
309		.name_prefix = "Amp",
310	},
311};
312
313static const struct gpio neo1973_gta02_gpios[] = {
314	{ S3C2410_GPJ(2), GPIOF_OUT_INIT_HIGH, "GTA02_HP_IN" },
315	{ S3C2410_GPJ(1), GPIOF_OUT_INIT_HIGH, "GTA02_AMP_SHUT" },
316};
317
318static struct snd_soc_card neo1973 = {
319	.name = "neo1973",
320	.owner = THIS_MODULE,
321	.dai_link = neo1973_dai,
322	.num_links = ARRAY_SIZE(neo1973_dai),
323	.aux_dev = neo1973_aux_devs,
324	.num_aux_devs = ARRAY_SIZE(neo1973_aux_devs),
325	.codec_conf = neo1973_codec_conf,
326	.num_configs = ARRAY_SIZE(neo1973_codec_conf),
327
328	.controls = neo1973_wm8753_controls,
329	.num_controls = ARRAY_SIZE(neo1973_wm8753_controls),
330	.dapm_widgets = neo1973_wm8753_dapm_widgets,
331	.num_dapm_widgets = ARRAY_SIZE(neo1973_wm8753_dapm_widgets),
332	.dapm_routes = neo1973_wm8753_routes,
333	.num_dapm_routes = ARRAY_SIZE(neo1973_wm8753_routes),
334	.fully_routed = true,
335};
336
337static struct platform_device *neo1973_snd_device;
338
339static int __init neo1973_init(void)
340{
341	int ret;
342
343	if (!machine_is_neo1973_gta02())
344		return -ENODEV;
345
346	if (machine_is_neo1973_gta02()) {
347		neo1973.name = "neo1973gta02";
348		neo1973.num_aux_devs = 1;
349
350		ret = gpio_request_array(neo1973_gta02_gpios,
351				ARRAY_SIZE(neo1973_gta02_gpios));
352		if (ret)
353			return ret;
354	}
355
356	neo1973_snd_device = platform_device_alloc("soc-audio", -1);
357	if (!neo1973_snd_device) {
358		ret = -ENOMEM;
359		goto err_gpio_free;
360	}
361
362	platform_set_drvdata(neo1973_snd_device, &neo1973);
363	ret = platform_device_add(neo1973_snd_device);
364
365	if (ret)
366		goto err_put_device;
367
368	return 0;
369
370err_put_device:
371	platform_device_put(neo1973_snd_device);
372err_gpio_free:
373	if (machine_is_neo1973_gta02()) {
374		gpio_free_array(neo1973_gta02_gpios,
375				ARRAY_SIZE(neo1973_gta02_gpios));
376	}
377	return ret;
378}
379module_init(neo1973_init);
380
381static void __exit neo1973_exit(void)
382{
383	platform_device_unregister(neo1973_snd_device);
384
385	if (machine_is_neo1973_gta02()) {
386		gpio_free_array(neo1973_gta02_gpios,
387				ARRAY_SIZE(neo1973_gta02_gpios));
388	}
389}
390module_exit(neo1973_exit);
391
392/* Module information */
393MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org");
394MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 and Frerunner");
395MODULE_LICENSE("GPL");
396