162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * n810.c  --  SoC audio for Nokia N810
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/i2c.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <sound/core.h>
1462306a36Sopenharmony_ci#include <sound/pcm.h>
1562306a36Sopenharmony_ci#include <sound/soc.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <asm/mach-types.h>
1862306a36Sopenharmony_ci#include <linux/gpio.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/platform_data/asoc-ti-mcbsp.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "omap-mcbsp.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define N810_HEADSET_AMP_GPIO	10
2562306a36Sopenharmony_ci#define N810_SPEAKER_AMP_GPIO	101
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cienum {
2862306a36Sopenharmony_ci	N810_JACK_DISABLED,
2962306a36Sopenharmony_ci	N810_JACK_HP,
3062306a36Sopenharmony_ci	N810_JACK_HS,
3162306a36Sopenharmony_ci	N810_JACK_MIC,
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic struct clk *sys_clkout2;
3562306a36Sopenharmony_cistatic struct clk *sys_clkout2_src;
3662306a36Sopenharmony_cistatic struct clk *func96m_clk;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int n810_spk_func;
3962306a36Sopenharmony_cistatic int n810_jack_func;
4062306a36Sopenharmony_cistatic int n810_dmic_func;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void n810_ext_control(struct snd_soc_dapm_context *dapm)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	int hp = 0, line1l = 0;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	switch (n810_jack_func) {
4762306a36Sopenharmony_ci	case N810_JACK_HS:
4862306a36Sopenharmony_ci		line1l = 1;
4962306a36Sopenharmony_ci		fallthrough;
5062306a36Sopenharmony_ci	case N810_JACK_HP:
5162306a36Sopenharmony_ci		hp = 1;
5262306a36Sopenharmony_ci		break;
5362306a36Sopenharmony_ci	case N810_JACK_MIC:
5462306a36Sopenharmony_ci		line1l = 1;
5562306a36Sopenharmony_ci		break;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	snd_soc_dapm_mutex_lock(dapm);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (n810_spk_func)
6162306a36Sopenharmony_ci		snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
6262306a36Sopenharmony_ci	else
6362306a36Sopenharmony_ci		snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (hp)
6662306a36Sopenharmony_ci		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
6762306a36Sopenharmony_ci	else
6862306a36Sopenharmony_ci		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
6962306a36Sopenharmony_ci	if (line1l)
7062306a36Sopenharmony_ci		snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic");
7162306a36Sopenharmony_ci	else
7262306a36Sopenharmony_ci		snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic");
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (n810_dmic_func)
7562306a36Sopenharmony_ci		snd_soc_dapm_enable_pin_unlocked(dapm, "DMic");
7662306a36Sopenharmony_ci	else
7762306a36Sopenharmony_ci		snd_soc_dapm_disable_pin_unlocked(dapm, "DMic");
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	snd_soc_dapm_sync_unlocked(dapm);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	snd_soc_dapm_mutex_unlock(dapm);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int n810_startup(struct snd_pcm_substream *substream)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
8762306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	n810_ext_control(&rtd->card->dapm);
9262306a36Sopenharmony_ci	return clk_prepare_enable(sys_clkout2);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic void n810_shutdown(struct snd_pcm_substream *substream)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	clk_disable_unprepare(sys_clkout2);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int n810_hw_params(struct snd_pcm_substream *substream,
10162306a36Sopenharmony_ci	struct snd_pcm_hw_params *params)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
10462306a36Sopenharmony_ci	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
10562306a36Sopenharmony_ci	int err;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Set the codec system clock for DAC and ADC */
10862306a36Sopenharmony_ci	err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000,
10962306a36Sopenharmony_ci					    SND_SOC_CLOCK_IN);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return err;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic const struct snd_soc_ops n810_ops = {
11562306a36Sopenharmony_ci	.startup = n810_startup,
11662306a36Sopenharmony_ci	.hw_params = n810_hw_params,
11762306a36Sopenharmony_ci	.shutdown = n810_shutdown,
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int n810_get_spk(struct snd_kcontrol *kcontrol,
12162306a36Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = n810_spk_func;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return 0;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic int n810_set_spk(struct snd_kcontrol *kcontrol,
12962306a36Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (n810_spk_func == ucontrol->value.enumerated.item[0])
13462306a36Sopenharmony_ci		return 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	n810_spk_func = ucontrol->value.enumerated.item[0];
13762306a36Sopenharmony_ci	n810_ext_control(&card->dapm);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return 1;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int n810_get_jack(struct snd_kcontrol *kcontrol,
14362306a36Sopenharmony_ci			 struct snd_ctl_elem_value *ucontrol)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = n810_jack_func;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int n810_set_jack(struct snd_kcontrol *kcontrol,
15162306a36Sopenharmony_ci			 struct snd_ctl_elem_value *ucontrol)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (n810_jack_func == ucontrol->value.enumerated.item[0])
15662306a36Sopenharmony_ci		return 0;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	n810_jack_func = ucontrol->value.enumerated.item[0];
15962306a36Sopenharmony_ci	n810_ext_control(&card->dapm);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return 1;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic int n810_get_input(struct snd_kcontrol *kcontrol,
16562306a36Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = n810_dmic_func;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return 0;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int n810_set_input(struct snd_kcontrol *kcontrol,
17362306a36Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (n810_dmic_func == ucontrol->value.enumerated.item[0])
17862306a36Sopenharmony_ci		return 0;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	n810_dmic_func = ucontrol->value.enumerated.item[0];
18162306a36Sopenharmony_ci	n810_ext_control(&card->dapm);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return 1;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic int n810_spk_event(struct snd_soc_dapm_widget *w,
18762306a36Sopenharmony_ci			  struct snd_kcontrol *k, int event)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	if (SND_SOC_DAPM_EVENT_ON(event))
19062306a36Sopenharmony_ci		gpio_set_value(N810_SPEAKER_AMP_GPIO, 1);
19162306a36Sopenharmony_ci	else
19262306a36Sopenharmony_ci		gpio_set_value(N810_SPEAKER_AMP_GPIO, 0);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int n810_jack_event(struct snd_soc_dapm_widget *w,
19862306a36Sopenharmony_ci			   struct snd_kcontrol *k, int event)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	if (SND_SOC_DAPM_EVENT_ON(event))
20162306a36Sopenharmony_ci		gpio_set_value(N810_HEADSET_AMP_GPIO, 1);
20262306a36Sopenharmony_ci	else
20362306a36Sopenharmony_ci		gpio_set_value(N810_HEADSET_AMP_GPIO, 0);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return 0;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
20962306a36Sopenharmony_ci	SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
21062306a36Sopenharmony_ci	SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
21162306a36Sopenharmony_ci	SND_SOC_DAPM_MIC("DMic", NULL),
21262306a36Sopenharmony_ci	SND_SOC_DAPM_MIC("HS Mic", NULL),
21362306a36Sopenharmony_ci};
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic const struct snd_soc_dapm_route audio_map[] = {
21662306a36Sopenharmony_ci	{"Headphone Jack", NULL, "HPLOUT"},
21762306a36Sopenharmony_ci	{"Headphone Jack", NULL, "HPROUT"},
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	{"Ext Spk", NULL, "LLOUT"},
22062306a36Sopenharmony_ci	{"Ext Spk", NULL, "RLOUT"},
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	{"DMic Rate 64", NULL, "DMic"},
22362306a36Sopenharmony_ci	{"DMic", NULL, "Mic Bias"},
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/*
22662306a36Sopenharmony_ci	 * Note that the mic bias is coming from Retu/Vilma and we don't have
22762306a36Sopenharmony_ci	 * control over it atm. The analog HS mic is not working. <- TODO
22862306a36Sopenharmony_ci	 */
22962306a36Sopenharmony_ci	{"LINE1L", NULL, "HS Mic"},
23062306a36Sopenharmony_ci};
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic const char *spk_function[] = {"Off", "On"};
23362306a36Sopenharmony_cistatic const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"};
23462306a36Sopenharmony_cistatic const char *input_function[] = {"ADC", "Digital Mic"};
23562306a36Sopenharmony_cistatic const struct soc_enum n810_enum[] = {
23662306a36Sopenharmony_ci	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
23762306a36Sopenharmony_ci	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
23862306a36Sopenharmony_ci	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
23962306a36Sopenharmony_ci};
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic const struct snd_kcontrol_new aic33_n810_controls[] = {
24262306a36Sopenharmony_ci	SOC_ENUM_EXT("Speaker Function", n810_enum[0],
24362306a36Sopenharmony_ci		     n810_get_spk, n810_set_spk),
24462306a36Sopenharmony_ci	SOC_ENUM_EXT("Jack Function", n810_enum[1],
24562306a36Sopenharmony_ci		     n810_get_jack, n810_set_jack),
24662306a36Sopenharmony_ci	SOC_ENUM_EXT("Input Select",  n810_enum[2],
24762306a36Sopenharmony_ci		     n810_get_input, n810_set_input),
24862306a36Sopenharmony_ci};
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/* Digital audio interface glue - connects codec <--> CPU */
25162306a36Sopenharmony_ciSND_SOC_DAILINK_DEFS(aic33,
25262306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CPU("48076000.mcbsp")),
25362306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic3x-codec.1-0018",
25462306a36Sopenharmony_ci				      "tlv320aic3x-hifi")),
25562306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_PLATFORM("48076000.mcbsp")));
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic struct snd_soc_dai_link n810_dai = {
25862306a36Sopenharmony_ci	.name = "TLV320AIC33",
25962306a36Sopenharmony_ci	.stream_name = "AIC33",
26062306a36Sopenharmony_ci	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
26162306a36Sopenharmony_ci		   SND_SOC_DAIFMT_CBM_CFM,
26262306a36Sopenharmony_ci	.ops = &n810_ops,
26362306a36Sopenharmony_ci	SND_SOC_DAILINK_REG(aic33),
26462306a36Sopenharmony_ci};
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/* Audio machine driver */
26762306a36Sopenharmony_cistatic struct snd_soc_card snd_soc_n810 = {
26862306a36Sopenharmony_ci	.name = "N810",
26962306a36Sopenharmony_ci	.owner = THIS_MODULE,
27062306a36Sopenharmony_ci	.dai_link = &n810_dai,
27162306a36Sopenharmony_ci	.num_links = 1,
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	.controls = aic33_n810_controls,
27462306a36Sopenharmony_ci	.num_controls = ARRAY_SIZE(aic33_n810_controls),
27562306a36Sopenharmony_ci	.dapm_widgets = aic33_dapm_widgets,
27662306a36Sopenharmony_ci	.num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets),
27762306a36Sopenharmony_ci	.dapm_routes = audio_map,
27862306a36Sopenharmony_ci	.num_dapm_routes = ARRAY_SIZE(audio_map),
27962306a36Sopenharmony_ci	.fully_routed = true,
28062306a36Sopenharmony_ci};
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic struct platform_device *n810_snd_device;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic int __init n810_soc_init(void)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	int err;
28762306a36Sopenharmony_ci	struct device *dev;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (!of_have_populated_dt() ||
29062306a36Sopenharmony_ci	    (!of_machine_is_compatible("nokia,n810") &&
29162306a36Sopenharmony_ci	     !of_machine_is_compatible("nokia,n810-wimax")))
29262306a36Sopenharmony_ci		return -ENODEV;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	n810_snd_device = platform_device_alloc("soc-audio", -1);
29562306a36Sopenharmony_ci	if (!n810_snd_device)
29662306a36Sopenharmony_ci		return -ENOMEM;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	platform_set_drvdata(n810_snd_device, &snd_soc_n810);
29962306a36Sopenharmony_ci	err = platform_device_add(n810_snd_device);
30062306a36Sopenharmony_ci	if (err)
30162306a36Sopenharmony_ci		goto err1;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	dev = &n810_snd_device->dev;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
30662306a36Sopenharmony_ci	if (IS_ERR(sys_clkout2_src)) {
30762306a36Sopenharmony_ci		dev_err(dev, "Could not get sys_clkout2_src clock\n");
30862306a36Sopenharmony_ci		err = PTR_ERR(sys_clkout2_src);
30962306a36Sopenharmony_ci		goto err2;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci	sys_clkout2 = clk_get(dev, "sys_clkout2");
31262306a36Sopenharmony_ci	if (IS_ERR(sys_clkout2)) {
31362306a36Sopenharmony_ci		dev_err(dev, "Could not get sys_clkout2\n");
31462306a36Sopenharmony_ci		err = PTR_ERR(sys_clkout2);
31562306a36Sopenharmony_ci		goto err3;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci	/*
31862306a36Sopenharmony_ci	 * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
31962306a36Sopenharmony_ci	 * 96 MHz as its parent in order to get 12 MHz
32062306a36Sopenharmony_ci	 */
32162306a36Sopenharmony_ci	func96m_clk = clk_get(dev, "func_96m_ck");
32262306a36Sopenharmony_ci	if (IS_ERR(func96m_clk)) {
32362306a36Sopenharmony_ci		dev_err(dev, "Could not get func 96M clock\n");
32462306a36Sopenharmony_ci		err = PTR_ERR(func96m_clk);
32562306a36Sopenharmony_ci		goto err4;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci	clk_set_parent(sys_clkout2_src, func96m_clk);
32862306a36Sopenharmony_ci	clk_set_rate(sys_clkout2, 12000000);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (WARN_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) ||
33162306a36Sopenharmony_ci		    (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0))) {
33262306a36Sopenharmony_ci		err = -EINVAL;
33362306a36Sopenharmony_ci		goto err4;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
33762306a36Sopenharmony_ci	gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	return 0;
34062306a36Sopenharmony_cierr4:
34162306a36Sopenharmony_ci	clk_put(sys_clkout2);
34262306a36Sopenharmony_cierr3:
34362306a36Sopenharmony_ci	clk_put(sys_clkout2_src);
34462306a36Sopenharmony_cierr2:
34562306a36Sopenharmony_ci	platform_device_del(n810_snd_device);
34662306a36Sopenharmony_cierr1:
34762306a36Sopenharmony_ci	platform_device_put(n810_snd_device);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return err;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic void __exit n810_soc_exit(void)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	gpio_free(N810_SPEAKER_AMP_GPIO);
35562306a36Sopenharmony_ci	gpio_free(N810_HEADSET_AMP_GPIO);
35662306a36Sopenharmony_ci	clk_put(sys_clkout2_src);
35762306a36Sopenharmony_ci	clk_put(sys_clkout2);
35862306a36Sopenharmony_ci	clk_put(func96m_clk);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	platform_device_unregister(n810_snd_device);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cimodule_init(n810_soc_init);
36462306a36Sopenharmony_cimodule_exit(n810_soc_exit);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ciMODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
36762306a36Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC Nokia N810");
36862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
369