File Index
Files and directories
Sign in
FILE

n810.c

File preview with syntax highlighting.

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