xref: /kernel/linux/linux-5.10/sound/soc/ti/n810.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * n810.c  --  SoC audio for Nokia N810
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/i2c.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <sound/core.h>
148c2ecf20Sopenharmony_ci#include <sound/pcm.h>
158c2ecf20Sopenharmony_ci#include <sound/soc.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <asm/mach-types.h>
188c2ecf20Sopenharmony_ci#include <linux/gpio.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_data/asoc-ti-mcbsp.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "omap-mcbsp.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define N810_HEADSET_AMP_GPIO	10
258c2ecf20Sopenharmony_ci#define N810_SPEAKER_AMP_GPIO	101
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cienum {
288c2ecf20Sopenharmony_ci	N810_JACK_DISABLED,
298c2ecf20Sopenharmony_ci	N810_JACK_HP,
308c2ecf20Sopenharmony_ci	N810_JACK_HS,
318c2ecf20Sopenharmony_ci	N810_JACK_MIC,
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic struct clk *sys_clkout2;
358c2ecf20Sopenharmony_cistatic struct clk *sys_clkout2_src;
368c2ecf20Sopenharmony_cistatic struct clk *func96m_clk;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int n810_spk_func;
398c2ecf20Sopenharmony_cistatic int n810_jack_func;
408c2ecf20Sopenharmony_cistatic int n810_dmic_func;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void n810_ext_control(struct snd_soc_dapm_context *dapm)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	int hp = 0, line1l = 0;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	switch (n810_jack_func) {
478c2ecf20Sopenharmony_ci	case N810_JACK_HS:
488c2ecf20Sopenharmony_ci		line1l = 1;
498c2ecf20Sopenharmony_ci		fallthrough;
508c2ecf20Sopenharmony_ci	case N810_JACK_HP:
518c2ecf20Sopenharmony_ci		hp = 1;
528c2ecf20Sopenharmony_ci		break;
538c2ecf20Sopenharmony_ci	case N810_JACK_MIC:
548c2ecf20Sopenharmony_ci		line1l = 1;
558c2ecf20Sopenharmony_ci		break;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	snd_soc_dapm_mutex_lock(dapm);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (n810_spk_func)
618c2ecf20Sopenharmony_ci		snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
628c2ecf20Sopenharmony_ci	else
638c2ecf20Sopenharmony_ci		snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (hp)
668c2ecf20Sopenharmony_ci		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
678c2ecf20Sopenharmony_ci	else
688c2ecf20Sopenharmony_ci		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
698c2ecf20Sopenharmony_ci	if (line1l)
708c2ecf20Sopenharmony_ci		snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic");
718c2ecf20Sopenharmony_ci	else
728c2ecf20Sopenharmony_ci		snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic");
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (n810_dmic_func)
758c2ecf20Sopenharmony_ci		snd_soc_dapm_enable_pin_unlocked(dapm, "DMic");
768c2ecf20Sopenharmony_ci	else
778c2ecf20Sopenharmony_ci		snd_soc_dapm_disable_pin_unlocked(dapm, "DMic");
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	snd_soc_dapm_sync_unlocked(dapm);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	snd_soc_dapm_mutex_unlock(dapm);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int n810_startup(struct snd_pcm_substream *substream)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
878c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	n810_ext_control(&rtd->card->dapm);
928c2ecf20Sopenharmony_ci	return clk_prepare_enable(sys_clkout2);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void n810_shutdown(struct snd_pcm_substream *substream)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	clk_disable_unprepare(sys_clkout2);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic int n810_hw_params(struct snd_pcm_substream *substream,
1018c2ecf20Sopenharmony_ci	struct snd_pcm_hw_params *params)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
1048c2ecf20Sopenharmony_ci	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
1058c2ecf20Sopenharmony_ci	int err;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* Set the codec system clock for DAC and ADC */
1088c2ecf20Sopenharmony_ci	err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000,
1098c2ecf20Sopenharmony_ci					    SND_SOC_CLOCK_IN);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	return err;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic const struct snd_soc_ops n810_ops = {
1158c2ecf20Sopenharmony_ci	.startup = n810_startup,
1168c2ecf20Sopenharmony_ci	.hw_params = n810_hw_params,
1178c2ecf20Sopenharmony_ci	.shutdown = n810_shutdown,
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int n810_get_spk(struct snd_kcontrol *kcontrol,
1218c2ecf20Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = n810_spk_func;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int n810_set_spk(struct snd_kcontrol *kcontrol,
1298c2ecf20Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (n810_spk_func == ucontrol->value.enumerated.item[0])
1348c2ecf20Sopenharmony_ci		return 0;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	n810_spk_func = ucontrol->value.enumerated.item[0];
1378c2ecf20Sopenharmony_ci	n810_ext_control(&card->dapm);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return 1;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int n810_get_jack(struct snd_kcontrol *kcontrol,
1438c2ecf20Sopenharmony_ci			 struct snd_ctl_elem_value *ucontrol)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = n810_jack_func;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int n810_set_jack(struct snd_kcontrol *kcontrol,
1518c2ecf20Sopenharmony_ci			 struct snd_ctl_elem_value *ucontrol)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (n810_jack_func == ucontrol->value.enumerated.item[0])
1568c2ecf20Sopenharmony_ci		return 0;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	n810_jack_func = ucontrol->value.enumerated.item[0];
1598c2ecf20Sopenharmony_ci	n810_ext_control(&card->dapm);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return 1;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic int n810_get_input(struct snd_kcontrol *kcontrol,
1658c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = n810_dmic_func;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return 0;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic int n810_set_input(struct snd_kcontrol *kcontrol,
1738c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (n810_dmic_func == ucontrol->value.enumerated.item[0])
1788c2ecf20Sopenharmony_ci		return 0;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	n810_dmic_func = ucontrol->value.enumerated.item[0];
1818c2ecf20Sopenharmony_ci	n810_ext_control(&card->dapm);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return 1;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int n810_spk_event(struct snd_soc_dapm_widget *w,
1878c2ecf20Sopenharmony_ci			  struct snd_kcontrol *k, int event)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	if (SND_SOC_DAPM_EVENT_ON(event))
1908c2ecf20Sopenharmony_ci		gpio_set_value(N810_SPEAKER_AMP_GPIO, 1);
1918c2ecf20Sopenharmony_ci	else
1928c2ecf20Sopenharmony_ci		gpio_set_value(N810_SPEAKER_AMP_GPIO, 0);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int n810_jack_event(struct snd_soc_dapm_widget *w,
1988c2ecf20Sopenharmony_ci			   struct snd_kcontrol *k, int event)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	if (SND_SOC_DAPM_EVENT_ON(event))
2018c2ecf20Sopenharmony_ci		gpio_set_value(N810_HEADSET_AMP_GPIO, 1);
2028c2ecf20Sopenharmony_ci	else
2038c2ecf20Sopenharmony_ci		gpio_set_value(N810_HEADSET_AMP_GPIO, 0);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
2098c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
2108c2ecf20Sopenharmony_ci	SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
2118c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIC("DMic", NULL),
2128c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIC("HS Mic", NULL),
2138c2ecf20Sopenharmony_ci};
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route audio_map[] = {
2168c2ecf20Sopenharmony_ci	{"Headphone Jack", NULL, "HPLOUT"},
2178c2ecf20Sopenharmony_ci	{"Headphone Jack", NULL, "HPROUT"},
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	{"Ext Spk", NULL, "LLOUT"},
2208c2ecf20Sopenharmony_ci	{"Ext Spk", NULL, "RLOUT"},
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	{"DMic Rate 64", NULL, "DMic"},
2238c2ecf20Sopenharmony_ci	{"DMic", NULL, "Mic Bias"},
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/*
2268c2ecf20Sopenharmony_ci	 * Note that the mic bias is coming from Retu/Vilma and we don't have
2278c2ecf20Sopenharmony_ci	 * control over it atm. The analog HS mic is not working. <- TODO
2288c2ecf20Sopenharmony_ci	 */
2298c2ecf20Sopenharmony_ci	{"LINE1L", NULL, "HS Mic"},
2308c2ecf20Sopenharmony_ci};
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic const char *spk_function[] = {"Off", "On"};
2338c2ecf20Sopenharmony_cistatic const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"};
2348c2ecf20Sopenharmony_cistatic const char *input_function[] = {"ADC", "Digital Mic"};
2358c2ecf20Sopenharmony_cistatic const struct soc_enum n810_enum[] = {
2368c2ecf20Sopenharmony_ci	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
2378c2ecf20Sopenharmony_ci	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
2388c2ecf20Sopenharmony_ci	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
2398c2ecf20Sopenharmony_ci};
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new aic33_n810_controls[] = {
2428c2ecf20Sopenharmony_ci	SOC_ENUM_EXT("Speaker Function", n810_enum[0],
2438c2ecf20Sopenharmony_ci		     n810_get_spk, n810_set_spk),
2448c2ecf20Sopenharmony_ci	SOC_ENUM_EXT("Jack Function", n810_enum[1],
2458c2ecf20Sopenharmony_ci		     n810_get_jack, n810_set_jack),
2468c2ecf20Sopenharmony_ci	SOC_ENUM_EXT("Input Select",  n810_enum[2],
2478c2ecf20Sopenharmony_ci		     n810_get_input, n810_set_input),
2488c2ecf20Sopenharmony_ci};
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/* Digital audio interface glue - connects codec <--> CPU */
2518c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(aic33,
2528c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CPU("48076000.mcbsp")),
2538c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic3x-codec.1-0018",
2548c2ecf20Sopenharmony_ci				      "tlv320aic3x-hifi")),
2558c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_PLATFORM("48076000.mcbsp")));
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link n810_dai = {
2588c2ecf20Sopenharmony_ci	.name = "TLV320AIC33",
2598c2ecf20Sopenharmony_ci	.stream_name = "AIC33",
2608c2ecf20Sopenharmony_ci	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
2618c2ecf20Sopenharmony_ci		   SND_SOC_DAIFMT_CBM_CFM,
2628c2ecf20Sopenharmony_ci	.ops = &n810_ops,
2638c2ecf20Sopenharmony_ci	SND_SOC_DAILINK_REG(aic33),
2648c2ecf20Sopenharmony_ci};
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci/* Audio machine driver */
2678c2ecf20Sopenharmony_cistatic struct snd_soc_card snd_soc_n810 = {
2688c2ecf20Sopenharmony_ci	.name = "N810",
2698c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
2708c2ecf20Sopenharmony_ci	.dai_link = &n810_dai,
2718c2ecf20Sopenharmony_ci	.num_links = 1,
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	.controls = aic33_n810_controls,
2748c2ecf20Sopenharmony_ci	.num_controls = ARRAY_SIZE(aic33_n810_controls),
2758c2ecf20Sopenharmony_ci	.dapm_widgets = aic33_dapm_widgets,
2768c2ecf20Sopenharmony_ci	.num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets),
2778c2ecf20Sopenharmony_ci	.dapm_routes = audio_map,
2788c2ecf20Sopenharmony_ci	.num_dapm_routes = ARRAY_SIZE(audio_map),
2798c2ecf20Sopenharmony_ci	.fully_routed = true,
2808c2ecf20Sopenharmony_ci};
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic struct platform_device *n810_snd_device;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic int __init n810_soc_init(void)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	int err;
2878c2ecf20Sopenharmony_ci	struct device *dev;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (!of_have_populated_dt() ||
2908c2ecf20Sopenharmony_ci	    (!of_machine_is_compatible("nokia,n810") &&
2918c2ecf20Sopenharmony_ci	     !of_machine_is_compatible("nokia,n810-wimax")))
2928c2ecf20Sopenharmony_ci		return -ENODEV;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	n810_snd_device = platform_device_alloc("soc-audio", -1);
2958c2ecf20Sopenharmony_ci	if (!n810_snd_device)
2968c2ecf20Sopenharmony_ci		return -ENOMEM;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	platform_set_drvdata(n810_snd_device, &snd_soc_n810);
2998c2ecf20Sopenharmony_ci	err = platform_device_add(n810_snd_device);
3008c2ecf20Sopenharmony_ci	if (err)
3018c2ecf20Sopenharmony_ci		goto err1;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	dev = &n810_snd_device->dev;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
3068c2ecf20Sopenharmony_ci	if (IS_ERR(sys_clkout2_src)) {
3078c2ecf20Sopenharmony_ci		dev_err(dev, "Could not get sys_clkout2_src clock\n");
3088c2ecf20Sopenharmony_ci		err = PTR_ERR(sys_clkout2_src);
3098c2ecf20Sopenharmony_ci		goto err2;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci	sys_clkout2 = clk_get(dev, "sys_clkout2");
3128c2ecf20Sopenharmony_ci	if (IS_ERR(sys_clkout2)) {
3138c2ecf20Sopenharmony_ci		dev_err(dev, "Could not get sys_clkout2\n");
3148c2ecf20Sopenharmony_ci		err = PTR_ERR(sys_clkout2);
3158c2ecf20Sopenharmony_ci		goto err3;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci	/*
3188c2ecf20Sopenharmony_ci	 * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
3198c2ecf20Sopenharmony_ci	 * 96 MHz as its parent in order to get 12 MHz
3208c2ecf20Sopenharmony_ci	 */
3218c2ecf20Sopenharmony_ci	func96m_clk = clk_get(dev, "func_96m_ck");
3228c2ecf20Sopenharmony_ci	if (IS_ERR(func96m_clk)) {
3238c2ecf20Sopenharmony_ci		dev_err(dev, "Could not get func 96M clock\n");
3248c2ecf20Sopenharmony_ci		err = PTR_ERR(func96m_clk);
3258c2ecf20Sopenharmony_ci		goto err4;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci	clk_set_parent(sys_clkout2_src, func96m_clk);
3288c2ecf20Sopenharmony_ci	clk_set_rate(sys_clkout2, 12000000);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (WARN_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) ||
3318c2ecf20Sopenharmony_ci		    (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0))) {
3328c2ecf20Sopenharmony_ci		err = -EINVAL;
3338c2ecf20Sopenharmony_ci		goto err4;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
3378c2ecf20Sopenharmony_ci	gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return 0;
3408c2ecf20Sopenharmony_cierr4:
3418c2ecf20Sopenharmony_ci	clk_put(sys_clkout2);
3428c2ecf20Sopenharmony_cierr3:
3438c2ecf20Sopenharmony_ci	clk_put(sys_clkout2_src);
3448c2ecf20Sopenharmony_cierr2:
3458c2ecf20Sopenharmony_ci	platform_device_del(n810_snd_device);
3468c2ecf20Sopenharmony_cierr1:
3478c2ecf20Sopenharmony_ci	platform_device_put(n810_snd_device);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return err;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic void __exit n810_soc_exit(void)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	gpio_free(N810_SPEAKER_AMP_GPIO);
3558c2ecf20Sopenharmony_ci	gpio_free(N810_HEADSET_AMP_GPIO);
3568c2ecf20Sopenharmony_ci	clk_put(sys_clkout2_src);
3578c2ecf20Sopenharmony_ci	clk_put(sys_clkout2);
3588c2ecf20Sopenharmony_ci	clk_put(func96m_clk);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	platform_device_unregister(n810_snd_device);
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cimodule_init(n810_soc_init);
3648c2ecf20Sopenharmony_cimodule_exit(n810_soc_exit);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
3678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC Nokia N810");
3688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
369