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