xref: /linux/sound/soc/ti/n810.c (revision f2055e145f2975a75dace8e386fad9364828cdb4)
1*f2055e14SPeter Ujfalusi /*
2*f2055e14SPeter Ujfalusi  * n810.c  --  SoC audio for Nokia N810
3*f2055e14SPeter Ujfalusi  *
4*f2055e14SPeter Ujfalusi  * Copyright (C) 2008 Nokia Corporation
5*f2055e14SPeter Ujfalusi  *
6*f2055e14SPeter Ujfalusi  * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
7*f2055e14SPeter Ujfalusi  *
8*f2055e14SPeter Ujfalusi  * This program is free software; you can redistribute it and/or
9*f2055e14SPeter Ujfalusi  * modify it under the terms of the GNU General Public License
10*f2055e14SPeter Ujfalusi  * version 2 as published by the Free Software Foundation.
11*f2055e14SPeter Ujfalusi  *
12*f2055e14SPeter Ujfalusi  * This program is distributed in the hope that it will be useful, but
13*f2055e14SPeter Ujfalusi  * WITHOUT ANY WARRANTY; without even the implied warranty of
14*f2055e14SPeter Ujfalusi  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15*f2055e14SPeter Ujfalusi  * General Public License for more details.
16*f2055e14SPeter Ujfalusi  *
17*f2055e14SPeter Ujfalusi  * You should have received a copy of the GNU General Public License
18*f2055e14SPeter Ujfalusi  * along with this program; if not, write to the Free Software
19*f2055e14SPeter Ujfalusi  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20*f2055e14SPeter Ujfalusi  * 02110-1301 USA
21*f2055e14SPeter Ujfalusi  *
22*f2055e14SPeter Ujfalusi  */
23*f2055e14SPeter Ujfalusi 
24*f2055e14SPeter Ujfalusi #include <linux/clk.h>
25*f2055e14SPeter Ujfalusi #include <linux/i2c.h>
26*f2055e14SPeter Ujfalusi #include <linux/platform_device.h>
27*f2055e14SPeter Ujfalusi #include <sound/core.h>
28*f2055e14SPeter Ujfalusi #include <sound/pcm.h>
29*f2055e14SPeter Ujfalusi #include <sound/soc.h>
30*f2055e14SPeter Ujfalusi 
31*f2055e14SPeter Ujfalusi #include <asm/mach-types.h>
32*f2055e14SPeter Ujfalusi #include <linux/gpio.h>
33*f2055e14SPeter Ujfalusi #include <linux/module.h>
34*f2055e14SPeter Ujfalusi #include <linux/platform_data/asoc-ti-mcbsp.h>
35*f2055e14SPeter Ujfalusi 
36*f2055e14SPeter Ujfalusi #include "omap-mcbsp.h"
37*f2055e14SPeter Ujfalusi 
38*f2055e14SPeter Ujfalusi #define N810_HEADSET_AMP_GPIO	10
39*f2055e14SPeter Ujfalusi #define N810_SPEAKER_AMP_GPIO	101
40*f2055e14SPeter Ujfalusi 
41*f2055e14SPeter Ujfalusi enum {
42*f2055e14SPeter Ujfalusi 	N810_JACK_DISABLED,
43*f2055e14SPeter Ujfalusi 	N810_JACK_HP,
44*f2055e14SPeter Ujfalusi 	N810_JACK_HS,
45*f2055e14SPeter Ujfalusi 	N810_JACK_MIC,
46*f2055e14SPeter Ujfalusi };
47*f2055e14SPeter Ujfalusi 
48*f2055e14SPeter Ujfalusi static struct clk *sys_clkout2;
49*f2055e14SPeter Ujfalusi static struct clk *sys_clkout2_src;
50*f2055e14SPeter Ujfalusi static struct clk *func96m_clk;
51*f2055e14SPeter Ujfalusi 
52*f2055e14SPeter Ujfalusi static int n810_spk_func;
53*f2055e14SPeter Ujfalusi static int n810_jack_func;
54*f2055e14SPeter Ujfalusi static int n810_dmic_func;
55*f2055e14SPeter Ujfalusi 
56*f2055e14SPeter Ujfalusi static void n810_ext_control(struct snd_soc_dapm_context *dapm)
57*f2055e14SPeter Ujfalusi {
58*f2055e14SPeter Ujfalusi 	int hp = 0, line1l = 0;
59*f2055e14SPeter Ujfalusi 
60*f2055e14SPeter Ujfalusi 	switch (n810_jack_func) {
61*f2055e14SPeter Ujfalusi 	case N810_JACK_HS:
62*f2055e14SPeter Ujfalusi 		line1l = 1;
63*f2055e14SPeter Ujfalusi 	case N810_JACK_HP:
64*f2055e14SPeter Ujfalusi 		hp = 1;
65*f2055e14SPeter Ujfalusi 		break;
66*f2055e14SPeter Ujfalusi 	case N810_JACK_MIC:
67*f2055e14SPeter Ujfalusi 		line1l = 1;
68*f2055e14SPeter Ujfalusi 		break;
69*f2055e14SPeter Ujfalusi 	}
70*f2055e14SPeter Ujfalusi 
71*f2055e14SPeter Ujfalusi 	snd_soc_dapm_mutex_lock(dapm);
72*f2055e14SPeter Ujfalusi 
73*f2055e14SPeter Ujfalusi 	if (n810_spk_func)
74*f2055e14SPeter Ujfalusi 		snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
75*f2055e14SPeter Ujfalusi 	else
76*f2055e14SPeter Ujfalusi 		snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
77*f2055e14SPeter Ujfalusi 
78*f2055e14SPeter Ujfalusi 	if (hp)
79*f2055e14SPeter Ujfalusi 		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
80*f2055e14SPeter Ujfalusi 	else
81*f2055e14SPeter Ujfalusi 		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
82*f2055e14SPeter Ujfalusi 	if (line1l)
83*f2055e14SPeter Ujfalusi 		snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic");
84*f2055e14SPeter Ujfalusi 	else
85*f2055e14SPeter Ujfalusi 		snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic");
86*f2055e14SPeter Ujfalusi 
87*f2055e14SPeter Ujfalusi 	if (n810_dmic_func)
88*f2055e14SPeter Ujfalusi 		snd_soc_dapm_enable_pin_unlocked(dapm, "DMic");
89*f2055e14SPeter Ujfalusi 	else
90*f2055e14SPeter Ujfalusi 		snd_soc_dapm_disable_pin_unlocked(dapm, "DMic");
91*f2055e14SPeter Ujfalusi 
92*f2055e14SPeter Ujfalusi 	snd_soc_dapm_sync_unlocked(dapm);
93*f2055e14SPeter Ujfalusi 
94*f2055e14SPeter Ujfalusi 	snd_soc_dapm_mutex_unlock(dapm);
95*f2055e14SPeter Ujfalusi }
96*f2055e14SPeter Ujfalusi 
97*f2055e14SPeter Ujfalusi static int n810_startup(struct snd_pcm_substream *substream)
98*f2055e14SPeter Ujfalusi {
99*f2055e14SPeter Ujfalusi 	struct snd_pcm_runtime *runtime = substream->runtime;
100*f2055e14SPeter Ujfalusi 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
101*f2055e14SPeter Ujfalusi 
102*f2055e14SPeter Ujfalusi 	snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
103*f2055e14SPeter Ujfalusi 
104*f2055e14SPeter Ujfalusi 	n810_ext_control(&rtd->card->dapm);
105*f2055e14SPeter Ujfalusi 	return clk_prepare_enable(sys_clkout2);
106*f2055e14SPeter Ujfalusi }
107*f2055e14SPeter Ujfalusi 
108*f2055e14SPeter Ujfalusi static void n810_shutdown(struct snd_pcm_substream *substream)
109*f2055e14SPeter Ujfalusi {
110*f2055e14SPeter Ujfalusi 	clk_disable_unprepare(sys_clkout2);
111*f2055e14SPeter Ujfalusi }
112*f2055e14SPeter Ujfalusi 
113*f2055e14SPeter Ujfalusi static int n810_hw_params(struct snd_pcm_substream *substream,
114*f2055e14SPeter Ujfalusi 	struct snd_pcm_hw_params *params)
115*f2055e14SPeter Ujfalusi {
116*f2055e14SPeter Ujfalusi 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
117*f2055e14SPeter Ujfalusi 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
118*f2055e14SPeter Ujfalusi 	int err;
119*f2055e14SPeter Ujfalusi 
120*f2055e14SPeter Ujfalusi 	/* Set the codec system clock for DAC and ADC */
121*f2055e14SPeter Ujfalusi 	err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000,
122*f2055e14SPeter Ujfalusi 					    SND_SOC_CLOCK_IN);
123*f2055e14SPeter Ujfalusi 
124*f2055e14SPeter Ujfalusi 	return err;
125*f2055e14SPeter Ujfalusi }
126*f2055e14SPeter Ujfalusi 
127*f2055e14SPeter Ujfalusi static const struct snd_soc_ops n810_ops = {
128*f2055e14SPeter Ujfalusi 	.startup = n810_startup,
129*f2055e14SPeter Ujfalusi 	.hw_params = n810_hw_params,
130*f2055e14SPeter Ujfalusi 	.shutdown = n810_shutdown,
131*f2055e14SPeter Ujfalusi };
132*f2055e14SPeter Ujfalusi 
133*f2055e14SPeter Ujfalusi static int n810_get_spk(struct snd_kcontrol *kcontrol,
134*f2055e14SPeter Ujfalusi 			struct snd_ctl_elem_value *ucontrol)
135*f2055e14SPeter Ujfalusi {
136*f2055e14SPeter Ujfalusi 	ucontrol->value.enumerated.item[0] = n810_spk_func;
137*f2055e14SPeter Ujfalusi 
138*f2055e14SPeter Ujfalusi 	return 0;
139*f2055e14SPeter Ujfalusi }
140*f2055e14SPeter Ujfalusi 
141*f2055e14SPeter Ujfalusi static int n810_set_spk(struct snd_kcontrol *kcontrol,
142*f2055e14SPeter Ujfalusi 			struct snd_ctl_elem_value *ucontrol)
143*f2055e14SPeter Ujfalusi {
144*f2055e14SPeter Ujfalusi 	struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
145*f2055e14SPeter Ujfalusi 
146*f2055e14SPeter Ujfalusi 	if (n810_spk_func == ucontrol->value.enumerated.item[0])
147*f2055e14SPeter Ujfalusi 		return 0;
148*f2055e14SPeter Ujfalusi 
149*f2055e14SPeter Ujfalusi 	n810_spk_func = ucontrol->value.enumerated.item[0];
150*f2055e14SPeter Ujfalusi 	n810_ext_control(&card->dapm);
151*f2055e14SPeter Ujfalusi 
152*f2055e14SPeter Ujfalusi 	return 1;
153*f2055e14SPeter Ujfalusi }
154*f2055e14SPeter Ujfalusi 
155*f2055e14SPeter Ujfalusi static int n810_get_jack(struct snd_kcontrol *kcontrol,
156*f2055e14SPeter Ujfalusi 			 struct snd_ctl_elem_value *ucontrol)
157*f2055e14SPeter Ujfalusi {
158*f2055e14SPeter Ujfalusi 	ucontrol->value.enumerated.item[0] = n810_jack_func;
159*f2055e14SPeter Ujfalusi 
160*f2055e14SPeter Ujfalusi 	return 0;
161*f2055e14SPeter Ujfalusi }
162*f2055e14SPeter Ujfalusi 
163*f2055e14SPeter Ujfalusi static int n810_set_jack(struct snd_kcontrol *kcontrol,
164*f2055e14SPeter Ujfalusi 			 struct snd_ctl_elem_value *ucontrol)
165*f2055e14SPeter Ujfalusi {
166*f2055e14SPeter Ujfalusi 	struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
167*f2055e14SPeter Ujfalusi 
168*f2055e14SPeter Ujfalusi 	if (n810_jack_func == ucontrol->value.enumerated.item[0])
169*f2055e14SPeter Ujfalusi 		return 0;
170*f2055e14SPeter Ujfalusi 
171*f2055e14SPeter Ujfalusi 	n810_jack_func = ucontrol->value.enumerated.item[0];
172*f2055e14SPeter Ujfalusi 	n810_ext_control(&card->dapm);
173*f2055e14SPeter Ujfalusi 
174*f2055e14SPeter Ujfalusi 	return 1;
175*f2055e14SPeter Ujfalusi }
176*f2055e14SPeter Ujfalusi 
177*f2055e14SPeter Ujfalusi static int n810_get_input(struct snd_kcontrol *kcontrol,
178*f2055e14SPeter Ujfalusi 			  struct snd_ctl_elem_value *ucontrol)
179*f2055e14SPeter Ujfalusi {
180*f2055e14SPeter Ujfalusi 	ucontrol->value.enumerated.item[0] = n810_dmic_func;
181*f2055e14SPeter Ujfalusi 
182*f2055e14SPeter Ujfalusi 	return 0;
183*f2055e14SPeter Ujfalusi }
184*f2055e14SPeter Ujfalusi 
185*f2055e14SPeter Ujfalusi static int n810_set_input(struct snd_kcontrol *kcontrol,
186*f2055e14SPeter Ujfalusi 			  struct snd_ctl_elem_value *ucontrol)
187*f2055e14SPeter Ujfalusi {
188*f2055e14SPeter Ujfalusi 	struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
189*f2055e14SPeter Ujfalusi 
190*f2055e14SPeter Ujfalusi 	if (n810_dmic_func == ucontrol->value.enumerated.item[0])
191*f2055e14SPeter Ujfalusi 		return 0;
192*f2055e14SPeter Ujfalusi 
193*f2055e14SPeter Ujfalusi 	n810_dmic_func = ucontrol->value.enumerated.item[0];
194*f2055e14SPeter Ujfalusi 	n810_ext_control(&card->dapm);
195*f2055e14SPeter Ujfalusi 
196*f2055e14SPeter Ujfalusi 	return 1;
197*f2055e14SPeter Ujfalusi }
198*f2055e14SPeter Ujfalusi 
199*f2055e14SPeter Ujfalusi static int n810_spk_event(struct snd_soc_dapm_widget *w,
200*f2055e14SPeter Ujfalusi 			  struct snd_kcontrol *k, int event)
201*f2055e14SPeter Ujfalusi {
202*f2055e14SPeter Ujfalusi 	if (SND_SOC_DAPM_EVENT_ON(event))
203*f2055e14SPeter Ujfalusi 		gpio_set_value(N810_SPEAKER_AMP_GPIO, 1);
204*f2055e14SPeter Ujfalusi 	else
205*f2055e14SPeter Ujfalusi 		gpio_set_value(N810_SPEAKER_AMP_GPIO, 0);
206*f2055e14SPeter Ujfalusi 
207*f2055e14SPeter Ujfalusi 	return 0;
208*f2055e14SPeter Ujfalusi }
209*f2055e14SPeter Ujfalusi 
210*f2055e14SPeter Ujfalusi static int n810_jack_event(struct snd_soc_dapm_widget *w,
211*f2055e14SPeter Ujfalusi 			   struct snd_kcontrol *k, int event)
212*f2055e14SPeter Ujfalusi {
213*f2055e14SPeter Ujfalusi 	if (SND_SOC_DAPM_EVENT_ON(event))
214*f2055e14SPeter Ujfalusi 		gpio_set_value(N810_HEADSET_AMP_GPIO, 1);
215*f2055e14SPeter Ujfalusi 	else
216*f2055e14SPeter Ujfalusi 		gpio_set_value(N810_HEADSET_AMP_GPIO, 0);
217*f2055e14SPeter Ujfalusi 
218*f2055e14SPeter Ujfalusi 	return 0;
219*f2055e14SPeter Ujfalusi }
220*f2055e14SPeter Ujfalusi 
221*f2055e14SPeter Ujfalusi static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
222*f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
223*f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
224*f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_MIC("DMic", NULL),
225*f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_MIC("HS Mic", NULL),
226*f2055e14SPeter Ujfalusi };
227*f2055e14SPeter Ujfalusi 
228*f2055e14SPeter Ujfalusi static const struct snd_soc_dapm_route audio_map[] = {
229*f2055e14SPeter Ujfalusi 	{"Headphone Jack", NULL, "HPLOUT"},
230*f2055e14SPeter Ujfalusi 	{"Headphone Jack", NULL, "HPROUT"},
231*f2055e14SPeter Ujfalusi 
232*f2055e14SPeter Ujfalusi 	{"Ext Spk", NULL, "LLOUT"},
233*f2055e14SPeter Ujfalusi 	{"Ext Spk", NULL, "RLOUT"},
234*f2055e14SPeter Ujfalusi 
235*f2055e14SPeter Ujfalusi 	{"DMic Rate 64", NULL, "DMic"},
236*f2055e14SPeter Ujfalusi 	{"DMic", NULL, "Mic Bias"},
237*f2055e14SPeter Ujfalusi 
238*f2055e14SPeter Ujfalusi 	/*
239*f2055e14SPeter Ujfalusi 	 * Note that the mic bias is coming from Retu/Vilma and we don't have
240*f2055e14SPeter Ujfalusi 	 * control over it atm. The analog HS mic is not working. <- TODO
241*f2055e14SPeter Ujfalusi 	 */
242*f2055e14SPeter Ujfalusi 	{"LINE1L", NULL, "HS Mic"},
243*f2055e14SPeter Ujfalusi };
244*f2055e14SPeter Ujfalusi 
245*f2055e14SPeter Ujfalusi static const char *spk_function[] = {"Off", "On"};
246*f2055e14SPeter Ujfalusi static const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"};
247*f2055e14SPeter Ujfalusi static const char *input_function[] = {"ADC", "Digital Mic"};
248*f2055e14SPeter Ujfalusi static const struct soc_enum n810_enum[] = {
249*f2055e14SPeter Ujfalusi 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
250*f2055e14SPeter Ujfalusi 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
251*f2055e14SPeter Ujfalusi 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
252*f2055e14SPeter Ujfalusi };
253*f2055e14SPeter Ujfalusi 
254*f2055e14SPeter Ujfalusi static const struct snd_kcontrol_new aic33_n810_controls[] = {
255*f2055e14SPeter Ujfalusi 	SOC_ENUM_EXT("Speaker Function", n810_enum[0],
256*f2055e14SPeter Ujfalusi 		     n810_get_spk, n810_set_spk),
257*f2055e14SPeter Ujfalusi 	SOC_ENUM_EXT("Jack Function", n810_enum[1],
258*f2055e14SPeter Ujfalusi 		     n810_get_jack, n810_set_jack),
259*f2055e14SPeter Ujfalusi 	SOC_ENUM_EXT("Input Select",  n810_enum[2],
260*f2055e14SPeter Ujfalusi 		     n810_get_input, n810_set_input),
261*f2055e14SPeter Ujfalusi };
262*f2055e14SPeter Ujfalusi 
263*f2055e14SPeter Ujfalusi /* Digital audio interface glue - connects codec <--> CPU */
264*f2055e14SPeter Ujfalusi static struct snd_soc_dai_link n810_dai = {
265*f2055e14SPeter Ujfalusi 	.name = "TLV320AIC33",
266*f2055e14SPeter Ujfalusi 	.stream_name = "AIC33",
267*f2055e14SPeter Ujfalusi 	.cpu_dai_name = "48076000.mcbsp",
268*f2055e14SPeter Ujfalusi 	.platform_name = "48076000.mcbsp",
269*f2055e14SPeter Ujfalusi 	.codec_name = "tlv320aic3x-codec.1-0018",
270*f2055e14SPeter Ujfalusi 	.codec_dai_name = "tlv320aic3x-hifi",
271*f2055e14SPeter Ujfalusi 	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
272*f2055e14SPeter Ujfalusi 		   SND_SOC_DAIFMT_CBM_CFM,
273*f2055e14SPeter Ujfalusi 	.ops = &n810_ops,
274*f2055e14SPeter Ujfalusi };
275*f2055e14SPeter Ujfalusi 
276*f2055e14SPeter Ujfalusi /* Audio machine driver */
277*f2055e14SPeter Ujfalusi static struct snd_soc_card snd_soc_n810 = {
278*f2055e14SPeter Ujfalusi 	.name = "N810",
279*f2055e14SPeter Ujfalusi 	.owner = THIS_MODULE,
280*f2055e14SPeter Ujfalusi 	.dai_link = &n810_dai,
281*f2055e14SPeter Ujfalusi 	.num_links = 1,
282*f2055e14SPeter Ujfalusi 
283*f2055e14SPeter Ujfalusi 	.controls = aic33_n810_controls,
284*f2055e14SPeter Ujfalusi 	.num_controls = ARRAY_SIZE(aic33_n810_controls),
285*f2055e14SPeter Ujfalusi 	.dapm_widgets = aic33_dapm_widgets,
286*f2055e14SPeter Ujfalusi 	.num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets),
287*f2055e14SPeter Ujfalusi 	.dapm_routes = audio_map,
288*f2055e14SPeter Ujfalusi 	.num_dapm_routes = ARRAY_SIZE(audio_map),
289*f2055e14SPeter Ujfalusi 	.fully_routed = true,
290*f2055e14SPeter Ujfalusi };
291*f2055e14SPeter Ujfalusi 
292*f2055e14SPeter Ujfalusi static struct platform_device *n810_snd_device;
293*f2055e14SPeter Ujfalusi 
294*f2055e14SPeter Ujfalusi static int __init n810_soc_init(void)
295*f2055e14SPeter Ujfalusi {
296*f2055e14SPeter Ujfalusi 	int err;
297*f2055e14SPeter Ujfalusi 	struct device *dev;
298*f2055e14SPeter Ujfalusi 
299*f2055e14SPeter Ujfalusi 	if (!of_have_populated_dt() ||
300*f2055e14SPeter Ujfalusi 	    (!of_machine_is_compatible("nokia,n810") &&
301*f2055e14SPeter Ujfalusi 	     !of_machine_is_compatible("nokia,n810-wimax")))
302*f2055e14SPeter Ujfalusi 		return -ENODEV;
303*f2055e14SPeter Ujfalusi 
304*f2055e14SPeter Ujfalusi 	n810_snd_device = platform_device_alloc("soc-audio", -1);
305*f2055e14SPeter Ujfalusi 	if (!n810_snd_device)
306*f2055e14SPeter Ujfalusi 		return -ENOMEM;
307*f2055e14SPeter Ujfalusi 
308*f2055e14SPeter Ujfalusi 	platform_set_drvdata(n810_snd_device, &snd_soc_n810);
309*f2055e14SPeter Ujfalusi 	err = platform_device_add(n810_snd_device);
310*f2055e14SPeter Ujfalusi 	if (err)
311*f2055e14SPeter Ujfalusi 		goto err1;
312*f2055e14SPeter Ujfalusi 
313*f2055e14SPeter Ujfalusi 	dev = &n810_snd_device->dev;
314*f2055e14SPeter Ujfalusi 
315*f2055e14SPeter Ujfalusi 	sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
316*f2055e14SPeter Ujfalusi 	if (IS_ERR(sys_clkout2_src)) {
317*f2055e14SPeter Ujfalusi 		dev_err(dev, "Could not get sys_clkout2_src clock\n");
318*f2055e14SPeter Ujfalusi 		err = PTR_ERR(sys_clkout2_src);
319*f2055e14SPeter Ujfalusi 		goto err2;
320*f2055e14SPeter Ujfalusi 	}
321*f2055e14SPeter Ujfalusi 	sys_clkout2 = clk_get(dev, "sys_clkout2");
322*f2055e14SPeter Ujfalusi 	if (IS_ERR(sys_clkout2)) {
323*f2055e14SPeter Ujfalusi 		dev_err(dev, "Could not get sys_clkout2\n");
324*f2055e14SPeter Ujfalusi 		err = PTR_ERR(sys_clkout2);
325*f2055e14SPeter Ujfalusi 		goto err3;
326*f2055e14SPeter Ujfalusi 	}
327*f2055e14SPeter Ujfalusi 	/*
328*f2055e14SPeter Ujfalusi 	 * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
329*f2055e14SPeter Ujfalusi 	 * 96 MHz as its parent in order to get 12 MHz
330*f2055e14SPeter Ujfalusi 	 */
331*f2055e14SPeter Ujfalusi 	func96m_clk = clk_get(dev, "func_96m_ck");
332*f2055e14SPeter Ujfalusi 	if (IS_ERR(func96m_clk)) {
333*f2055e14SPeter Ujfalusi 		dev_err(dev, "Could not get func 96M clock\n");
334*f2055e14SPeter Ujfalusi 		err = PTR_ERR(func96m_clk);
335*f2055e14SPeter Ujfalusi 		goto err4;
336*f2055e14SPeter Ujfalusi 	}
337*f2055e14SPeter Ujfalusi 	clk_set_parent(sys_clkout2_src, func96m_clk);
338*f2055e14SPeter Ujfalusi 	clk_set_rate(sys_clkout2, 12000000);
339*f2055e14SPeter Ujfalusi 
340*f2055e14SPeter Ujfalusi 	if (WARN_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) ||
341*f2055e14SPeter Ujfalusi 		    (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0))) {
342*f2055e14SPeter Ujfalusi 		err = -EINVAL;
343*f2055e14SPeter Ujfalusi 		goto err4;
344*f2055e14SPeter Ujfalusi 	}
345*f2055e14SPeter Ujfalusi 
346*f2055e14SPeter Ujfalusi 	gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
347*f2055e14SPeter Ujfalusi 	gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
348*f2055e14SPeter Ujfalusi 
349*f2055e14SPeter Ujfalusi 	return 0;
350*f2055e14SPeter Ujfalusi err4:
351*f2055e14SPeter Ujfalusi 	clk_put(sys_clkout2);
352*f2055e14SPeter Ujfalusi err3:
353*f2055e14SPeter Ujfalusi 	clk_put(sys_clkout2_src);
354*f2055e14SPeter Ujfalusi err2:
355*f2055e14SPeter Ujfalusi 	platform_device_del(n810_snd_device);
356*f2055e14SPeter Ujfalusi err1:
357*f2055e14SPeter Ujfalusi 	platform_device_put(n810_snd_device);
358*f2055e14SPeter Ujfalusi 
359*f2055e14SPeter Ujfalusi 	return err;
360*f2055e14SPeter Ujfalusi }
361*f2055e14SPeter Ujfalusi 
362*f2055e14SPeter Ujfalusi static void __exit n810_soc_exit(void)
363*f2055e14SPeter Ujfalusi {
364*f2055e14SPeter Ujfalusi 	gpio_free(N810_SPEAKER_AMP_GPIO);
365*f2055e14SPeter Ujfalusi 	gpio_free(N810_HEADSET_AMP_GPIO);
366*f2055e14SPeter Ujfalusi 	clk_put(sys_clkout2_src);
367*f2055e14SPeter Ujfalusi 	clk_put(sys_clkout2);
368*f2055e14SPeter Ujfalusi 	clk_put(func96m_clk);
369*f2055e14SPeter Ujfalusi 
370*f2055e14SPeter Ujfalusi 	platform_device_unregister(n810_snd_device);
371*f2055e14SPeter Ujfalusi }
372*f2055e14SPeter Ujfalusi 
373*f2055e14SPeter Ujfalusi module_init(n810_soc_init);
374*f2055e14SPeter Ujfalusi module_exit(n810_soc_exit);
375*f2055e14SPeter Ujfalusi 
376*f2055e14SPeter Ujfalusi MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
377*f2055e14SPeter Ujfalusi MODULE_DESCRIPTION("ALSA SoC Nokia N810");
378*f2055e14SPeter Ujfalusi MODULE_LICENSE("GPL");
379