1/* sound/soc/samsung/smartq_wm8987.c
2 *
3 * Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com>
4 *
5 * Based on smdk6410_wm8987.c
6 *     Copyright 2007 Wolfson Microelectronics PLC. - linux@wolfsonmicro.com
7 *     Graeme Gregory - graeme.gregory@wolfsonmicro.com
8 *
9 *  This program is free software; you can redistribute  it and/or modify it
10 *  under  the terms of  the GNU General  Public License as published by the
11 *  Free Software Foundation;  either version 2 of the  License, or (at your
12 *  option) any later version.
13 *
14 */
15
16#include <linux/gpio.h>
17#include <linux/module.h>
18
19#include <sound/soc.h>
20#include <sound/jack.h>
21
22#include <mach/gpio-samsung.h>
23#include <asm/mach-types.h>
24
25#include "i2s.h"
26#include "../codecs/wm8750.h"
27
28/*
29 * WM8987 is register compatible with WM8750, so using that as base driver.
30 */
31
32static struct snd_soc_card snd_soc_smartq;
33
34static int smartq_hifi_hw_params(struct snd_pcm_substream *substream,
35	struct snd_pcm_hw_params *params)
36{
37	struct snd_soc_pcm_runtime *rtd = substream->private_data;
38	struct snd_soc_dai *codec_dai = rtd->codec_dai;
39	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
40	unsigned int clk = 0;
41	int ret;
42
43	switch (params_rate(params)) {
44	case 8000:
45	case 16000:
46	case 32000:
47	case 48000:
48	case 96000:
49		clk = 12288000;
50		break;
51	case 11025:
52	case 22050:
53	case 44100:
54	case 88200:
55		clk = 11289600;
56		break;
57	}
58
59	/* Use PCLK for I2S signal generation */
60	ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
61					0, SND_SOC_CLOCK_IN);
62	if (ret < 0)
63		return ret;
64
65	/* Gate the RCLK output on PAD */
66	ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
67					0, SND_SOC_CLOCK_IN);
68	if (ret < 0)
69		return ret;
70
71	/* set the codec system clock for DAC and ADC */
72	ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
73				     SND_SOC_CLOCK_IN);
74	if (ret < 0)
75		return ret;
76
77	return 0;
78}
79
80/*
81 * SmartQ WM8987 HiFi DAI operations.
82 */
83static struct snd_soc_ops smartq_hifi_ops = {
84	.hw_params = smartq_hifi_hw_params,
85};
86
87static struct snd_soc_jack smartq_jack;
88
89static struct snd_soc_jack_pin smartq_jack_pins[] = {
90	/* Disable speaker when headphone is plugged in */
91	{
92		.pin	= "Internal Speaker",
93		.mask	= SND_JACK_HEADPHONE,
94	},
95};
96
97static struct snd_soc_jack_gpio smartq_jack_gpios[] = {
98	{
99		.gpio		= S3C64XX_GPL(12),
100		.name		= "headphone detect",
101		.report		= SND_JACK_HEADPHONE,
102		.debounce_time	= 200,
103	},
104};
105
106static const struct snd_kcontrol_new wm8987_smartq_controls[] = {
107	SOC_DAPM_PIN_SWITCH("Internal Speaker"),
108	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
109	SOC_DAPM_PIN_SWITCH("Internal Mic"),
110};
111
112static int smartq_speaker_event(struct snd_soc_dapm_widget *w,
113				struct snd_kcontrol *k,
114				int event)
115{
116	gpio_set_value(S3C64XX_GPK(12), SND_SOC_DAPM_EVENT_OFF(event));
117
118	return 0;
119}
120
121static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = {
122	SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event),
123	SND_SOC_DAPM_HP("Headphone Jack", NULL),
124	SND_SOC_DAPM_MIC("Internal Mic", NULL),
125};
126
127static const struct snd_soc_dapm_route audio_map[] = {
128	{"Headphone Jack", NULL, "LOUT2"},
129	{"Headphone Jack", NULL, "ROUT2"},
130
131	{"Internal Speaker", NULL, "LOUT2"},
132	{"Internal Speaker", NULL, "ROUT2"},
133
134	{"Mic Bias", NULL, "Internal Mic"},
135	{"LINPUT2", NULL, "Mic Bias"},
136};
137
138static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
139{
140	struct snd_soc_codec *codec = rtd->codec;
141	struct snd_soc_dapm_context *dapm = &codec->dapm;
142	int err = 0;
143
144	/* set endpoints to not connected */
145	snd_soc_dapm_nc_pin(dapm, "LINPUT1");
146	snd_soc_dapm_nc_pin(dapm, "RINPUT1");
147	snd_soc_dapm_nc_pin(dapm, "OUT3");
148	snd_soc_dapm_nc_pin(dapm, "ROUT1");
149
150	/* set endpoints to default off mode */
151	snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
152
153	/* Headphone jack detection */
154	err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
155				    SND_JACK_HEADPHONE, &smartq_jack,
156				    smartq_jack_pins,
157				    ARRAY_SIZE(smartq_jack_pins));
158	if (err)
159		return err;
160
161	err = snd_soc_jack_add_gpios(&smartq_jack,
162				     ARRAY_SIZE(smartq_jack_gpios),
163				     smartq_jack_gpios);
164
165	return err;
166}
167
168static int smartq_wm8987_card_remove(struct snd_soc_card *card)
169{
170	snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios),
171				smartq_jack_gpios);
172
173	return 0;
174}
175
176static struct snd_soc_dai_link smartq_dai[] = {
177	{
178		.name		= "wm8987",
179		.stream_name	= "SmartQ Hi-Fi",
180		.cpu_dai_name	= "samsung-i2s.0",
181		.codec_dai_name	= "wm8750-hifi",
182		.platform_name	= "samsung-i2s.0",
183		.codec_name	= "wm8750.0-0x1a",
184		.init		= smartq_wm8987_init,
185		.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
186				  SND_SOC_DAIFMT_CBS_CFS,
187		.ops		= &smartq_hifi_ops,
188	},
189};
190
191static struct snd_soc_card snd_soc_smartq = {
192	.name = "SmartQ",
193	.owner = THIS_MODULE,
194	.remove = smartq_wm8987_card_remove,
195	.dai_link = smartq_dai,
196	.num_links = ARRAY_SIZE(smartq_dai),
197
198	.dapm_widgets = wm8987_dapm_widgets,
199	.num_dapm_widgets = ARRAY_SIZE(wm8987_dapm_widgets),
200	.dapm_routes = audio_map,
201	.num_dapm_routes = ARRAY_SIZE(audio_map),
202	.controls = wm8987_smartq_controls,
203	.num_controls = ARRAY_SIZE(wm8987_smartq_controls),
204};
205
206static struct platform_device *smartq_snd_device;
207
208static int __init smartq_init(void)
209{
210	int ret;
211
212	if (!machine_is_smartq7() && !machine_is_smartq5()) {
213		pr_info("Only SmartQ is supported by this ASoC driver\n");
214		return -ENODEV;
215	}
216
217	smartq_snd_device = platform_device_alloc("soc-audio", -1);
218	if (!smartq_snd_device)
219		return -ENOMEM;
220
221	platform_set_drvdata(smartq_snd_device, &snd_soc_smartq);
222
223	ret = platform_device_add(smartq_snd_device);
224	if (ret) {
225		platform_device_put(smartq_snd_device);
226		return ret;
227	}
228
229	/* Initialise GPIOs used by amplifiers */
230	ret = gpio_request(S3C64XX_GPK(12), "amplifiers shutdown");
231	if (ret) {
232		dev_err(&smartq_snd_device->dev, "Failed to register GPK12\n");
233		goto err_unregister_device;
234	}
235
236	/* Disable amplifiers */
237	ret = gpio_direction_output(S3C64XX_GPK(12), 1);
238	if (ret) {
239		dev_err(&smartq_snd_device->dev, "Failed to configure GPK12\n");
240		goto err_free_gpio_amp_shut;
241	}
242
243	return 0;
244
245err_free_gpio_amp_shut:
246	gpio_free(S3C64XX_GPK(12));
247err_unregister_device:
248	platform_device_unregister(smartq_snd_device);
249
250	return ret;
251}
252
253static void __exit smartq_exit(void)
254{
255	gpio_free(S3C64XX_GPK(12));
256
257	platform_device_unregister(smartq_snd_device);
258}
259
260module_init(smartq_init);
261module_exit(smartq_exit);
262
263/* Module information */
264MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>");
265MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987");
266MODULE_LICENSE("GPL");
267