File Index
Files and directories
Sign in
FILE

sc7180.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/qcom/sc7180.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2//
  3// Copyright (c) 2020, The Linux Foundation. All rights reserved.
  4//
  5// sc7180.c -- ALSA SoC Machine driver for SC7180
  6
  7#include <dt-bindings/sound/sc7180-lpass.h>
  8#include <dt-bindings/sound/qcom,q6afe.h>
  9#include <linux/gpio/consumer.h>
 10#include <linux/module.h>
 11#include <linux/of.h>
 12#include <linux/platform_device.h>
 13#include <sound/core.h>
 14#include <sound/jack.h>
 15#include <sound/pcm.h>
 16#include <sound/soc.h>
 17#include <uapi/linux/input-event-codes.h>
 18
 19#include "../codecs/rt5682.h"
 20#include "../codecs/rt5682s.h"
 21#include "common.h"
 22#include "qdsp6/q6afe.h"
 23
 24#define DEFAULT_MCLK_RATE		19200000
 25#define MI2S_BCLK_RATE			1536000
 26#define RT5682_PLL1_FREQ (48000 * 512)
 27
 28#define DRIVER_NAME "SC7180"
 29
 30struct sc7180_snd_data {
 31	struct snd_soc_card card;
 32	u32 pri_mi2s_clk_count;
 33	struct snd_soc_jack hs_jack;
 34	struct snd_soc_jack hdmi_jack;
 35	struct gpio_desc *dmic_sel;
 36	int dmic_switch;
 37};
 38
 39static void sc7180_jack_free(struct snd_jack *jack)
 40{
 41	struct snd_soc_component *component = jack->private_data;
 42
 43	snd_soc_component_set_jack(component, NULL, NULL);
 44}
 45
 46static struct snd_soc_jack_pin sc7180_jack_pins[] = {
 47	{
 48		.pin = "Headphone Jack",
 49		.mask = SND_JACK_HEADPHONE,
 50	},
 51	{
 52		.pin = "Headset Mic",
 53		.mask = SND_JACK_MICROPHONE,
 54	},
 55};
 56
 57static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd)
 58{
 59	struct snd_soc_card *card = rtd->card;
 60	struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
 61	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
 62	struct snd_soc_component *component = codec_dai->component;
 63	struct snd_jack *jack;
 64	int rval;
 65
 66	rval = snd_soc_card_jack_new_pins(card, "Headset Jack",
 67					  SND_JACK_HEADSET |
 68					  SND_JACK_HEADPHONE |
 69					  SND_JACK_BTN_0 | SND_JACK_BTN_1 |
 70					  SND_JACK_BTN_2 | SND_JACK_BTN_3,
 71					  &pdata->hs_jack,
 72					  sc7180_jack_pins,
 73					  ARRAY_SIZE(sc7180_jack_pins));
 74
 75	if (rval < 0) {
 76		dev_err(card->dev, "Unable to add Headset Jack\n");
 77		return rval;
 78	}
 79
 80	jack = pdata->hs_jack.jack;
 81
 82	snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
 83	snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
 84	snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
 85	snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
 86
 87	jack->private_data = component;
 88	jack->private_free = sc7180_jack_free;
 89
 90	return snd_soc_component_set_jack(component, &pdata->hs_jack, NULL);
 91}
 92
 93static int sc7180_hdmi_init(struct snd_soc_pcm_runtime *rtd)
 94{
 95	struct snd_soc_card *card = rtd->card;
 96	struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
 97	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
 98	struct snd_soc_component *component = codec_dai->component;
 99	struct snd_jack *jack;
100	int rval;
101
102	rval = snd_soc_card_jack_new(
103			card, "HDMI Jack",
104			SND_JACK_LINEOUT,
105			&pdata->hdmi_jack);
106
107	if (rval < 0) {
108		dev_err(card->dev, "Unable to add HDMI Jack\n");
109		return rval;
110	}
111
112	jack = pdata->hdmi_jack.jack;
113	jack->private_data = component;
114	jack->private_free = sc7180_jack_free;
115
116	return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL);
117}
118
119static int sc7180_init(struct snd_soc_pcm_runtime *rtd)
120{
121	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
122
123	switch (cpu_dai->id) {
124	case MI2S_PRIMARY:
125		return sc7180_headset_init(rtd);
126	case MI2S_SECONDARY:
127		return 0;
128	case LPASS_DP_RX:
129		return sc7180_hdmi_init(rtd);
130	default:
131		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
132			cpu_dai->id);
133		return -EINVAL;
134	}
135	return 0;
136}
137
138static int sc7180_qdsp_init(struct snd_soc_pcm_runtime *rtd)
139{
140	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
141
142	switch (cpu_dai->id) {
143	case PRIMARY_MI2S_RX:
144		return sc7180_headset_init(rtd);
145	case PRIMARY_MI2S_TX:
146	case TERTIARY_MI2S_RX:
147		return 0;
148	case DISPLAY_PORT_RX:
149		return sc7180_hdmi_init(rtd);
150	default:
151		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
152			cpu_dai->id);
153		return -EINVAL;
154	}
155	return 0;
156}
157
158static int sc7180_startup_realtek_codec(struct snd_soc_pcm_runtime *rtd)
159{
160	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
161	int pll_id, pll_source, pll_in, pll_out, clk_id, ret;
162
163	if (!strcmp(codec_dai->name, "rt5682-aif1")) {
164		pll_source = RT5682_PLL1_S_MCLK;
165		pll_id = 0;
166		clk_id = RT5682_SCLK_S_PLL1;
167		pll_out = RT5682_PLL1_FREQ;
168		pll_in = DEFAULT_MCLK_RATE;
169	} else if (!strcmp(codec_dai->name, "rt5682s-aif1")) {
170		pll_source = RT5682S_PLL_S_MCLK;
171		pll_id = RT5682S_PLL2;
172		clk_id = RT5682S_SCLK_S_PLL2;
173		pll_out = RT5682_PLL1_FREQ;
174		pll_in = DEFAULT_MCLK_RATE;
175	} else {
176		return 0;
177	}
178	snd_soc_dai_set_fmt(codec_dai,
179			    SND_SOC_DAIFMT_BC_FC |
180			    SND_SOC_DAIFMT_NB_NF |
181			    SND_SOC_DAIFMT_I2S);
182
183	/* Configure PLL1 for codec */
184	ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source,
185				  pll_in, pll_out);
186	if (ret) {
187		dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
188		return ret;
189	}
190
191	/* Configure sysclk for codec */
192	ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, pll_out,
193				     SND_SOC_CLOCK_IN);
194	if (ret)
195		dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n",
196			ret);
197
198	return ret;
199}
200
201static int sc7180_snd_startup(struct snd_pcm_substream *substream)
202{
203	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
204	struct snd_soc_card *card = rtd->card;
205	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
206	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
207	int ret;
208
209	switch (cpu_dai->id) {
210	case MI2S_PRIMARY:
211		if (++data->pri_mi2s_clk_count == 1) {
212			snd_soc_dai_set_sysclk(cpu_dai,
213					       LPASS_MCLK0,
214					       DEFAULT_MCLK_RATE,
215					       SNDRV_PCM_STREAM_PLAYBACK);
216		}
217
218		ret = sc7180_startup_realtek_codec(rtd);
219		if (ret)
220			return ret;
221
222		break;
223	case MI2S_SECONDARY:
224		break;
225	case LPASS_DP_RX:
226		break;
227	default:
228		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
229			cpu_dai->id);
230		return -EINVAL;
231	}
232	return 0;
233}
234
235static int sc7180_qdsp_snd_startup(struct snd_pcm_substream *substream)
236{
237	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
238	struct snd_soc_card *card = rtd->card;
239	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
240	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
241	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
242	int ret;
243
244	switch (cpu_dai->id) {
245	case PRIMARY_MI2S_RX:
246	case PRIMARY_MI2S_TX:
247		if (++data->pri_mi2s_clk_count == 1) {
248			snd_soc_dai_set_sysclk(cpu_dai,
249					       Q6AFE_LPASS_CLK_ID_MCLK_1,
250					       DEFAULT_MCLK_RATE,
251					       SNDRV_PCM_STREAM_PLAYBACK);
252			snd_soc_dai_set_sysclk(cpu_dai,
253					       Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
254					       MI2S_BCLK_RATE,
255					       SNDRV_PCM_STREAM_PLAYBACK);
256		}
257
258		snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
259
260		ret = sc7180_startup_realtek_codec(rtd);
261		if (ret)
262			return ret;
263
264		break;
265	case TERTIARY_MI2S_RX:
266		snd_soc_dai_set_sysclk(cpu_dai,
267				       Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
268				       MI2S_BCLK_RATE,
269				       SNDRV_PCM_STREAM_PLAYBACK);
270
271		snd_soc_dai_set_fmt(codec_dai,
272				SND_SOC_DAIFMT_BC_FC |
273				SND_SOC_DAIFMT_NB_NF |
274				SND_SOC_DAIFMT_I2S);
275		snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
276		break;
277	case DISPLAY_PORT_RX:
278		break;
279	default:
280		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
281			cpu_dai->id);
282		return -EINVAL;
283	}
284	return 0;
285}
286
287static int dmic_get(struct snd_kcontrol *kcontrol,
288		    struct snd_ctl_elem_value *ucontrol)
289{
290	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
291	struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
292	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
293
294	ucontrol->value.integer.value[0] = data->dmic_switch;
295	return 0;
296}
297
298static int dmic_set(struct snd_kcontrol *kcontrol,
299		    struct snd_ctl_elem_value *ucontrol)
300{
301	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
302	struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
303	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
304
305	data->dmic_switch = ucontrol->value.integer.value[0];
306	gpiod_set_value(data->dmic_sel, data->dmic_switch);
307	return 0;
308}
309
310static void sc7180_snd_shutdown(struct snd_pcm_substream *substream)
311{
312	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
313	struct snd_soc_card *card = rtd->card;
314	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
315	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
316
317	switch (cpu_dai->id) {
318	case MI2S_PRIMARY:
319		if (--data->pri_mi2s_clk_count == 0) {
320			snd_soc_dai_set_sysclk(cpu_dai,
321					       LPASS_MCLK0,
322					       0,
323					       SNDRV_PCM_STREAM_PLAYBACK);
324		}
325		break;
326	case MI2S_SECONDARY:
327		break;
328	case LPASS_DP_RX:
329		break;
330	default:
331		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
332			cpu_dai->id);
333		break;
334	}
335}
336
337static void sc7180_qdsp_snd_shutdown(struct snd_pcm_substream *substream)
338{
339	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
340	struct snd_soc_card *card = rtd->card;
341	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
342	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
343
344	switch (cpu_dai->id) {
345	case PRIMARY_MI2S_RX:
346	case PRIMARY_MI2S_TX:
347		if (--data->pri_mi2s_clk_count == 0) {
348			snd_soc_dai_set_sysclk(cpu_dai,
349					       Q6AFE_LPASS_CLK_ID_MCLK_1,
350					       0,
351					       SNDRV_PCM_STREAM_PLAYBACK);
352			snd_soc_dai_set_sysclk(cpu_dai,
353					       Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
354					       0,
355					       SNDRV_PCM_STREAM_PLAYBACK);
356		}
357		break;
358	case TERTIARY_MI2S_RX:
359		snd_soc_dai_set_sysclk(cpu_dai,
360				       Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
361				       0,
362				       SNDRV_PCM_STREAM_PLAYBACK);
363		break;
364	case DISPLAY_PORT_RX:
365		break;
366	default:
367		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
368			cpu_dai->id);
369		break;
370	}
371}
372
373static int sc7180_adau7002_init(struct snd_soc_pcm_runtime *rtd)
374{
375	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
376
377	switch (cpu_dai->id) {
378	case MI2S_PRIMARY:
379		return 0;
380	case MI2S_SECONDARY:
381		return 0;
382	case LPASS_DP_RX:
383		return sc7180_hdmi_init(rtd);
384	default:
385		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
386			cpu_dai->id);
387		return -EINVAL;
388	}
389	return 0;
390}
391
392static int sc7180_adau7002_snd_startup(struct snd_pcm_substream *substream)
393{
394	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
395	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
396	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
397	struct snd_pcm_runtime *runtime = substream->runtime;
398
399	switch (cpu_dai->id) {
400	case MI2S_PRIMARY:
401		snd_soc_dai_set_fmt(codec_dai,
402				    SND_SOC_DAIFMT_CBC_CFC |
403				    SND_SOC_DAIFMT_NB_NF |
404				    SND_SOC_DAIFMT_I2S);
405		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
406		snd_pcm_hw_constraint_msbits(runtime, 0, 32, 32);
407
408		break;
409	case MI2S_SECONDARY:
410		break;
411	case LPASS_DP_RX:
412		break;
413	default:
414		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
415			cpu_dai->id);
416		return -EINVAL;
417	}
418	return 0;
419}
420
421static int sc7180_qdsp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
422				     struct snd_pcm_hw_params *params)
423{
424	struct snd_interval *rate = hw_param_interval(params,
425					SNDRV_PCM_HW_PARAM_RATE);
426	struct snd_interval *channels = hw_param_interval(params,
427					SNDRV_PCM_HW_PARAM_CHANNELS);
428
429	rate->min = rate->max = 48000;
430	channels->min = channels->max = 2;
431
432	return 0;
433}
434
435static const struct snd_soc_ops sc7180_ops = {
436	.startup = sc7180_snd_startup,
437	.shutdown = sc7180_snd_shutdown,
438};
439
440static const struct snd_soc_ops sc7180_qdsp_ops = {
441	.startup = sc7180_qdsp_snd_startup,
442	.shutdown = sc7180_qdsp_snd_shutdown,
443};
444
445static const struct snd_soc_ops sc7180_adau7002_ops = {
446	.startup = sc7180_adau7002_snd_startup,
447};
448
449static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = {
450	SND_SOC_DAPM_HP("Headphone Jack", NULL),
451	SND_SOC_DAPM_MIC("Headset Mic", NULL),
452};
453
454static const struct snd_kcontrol_new sc7180_snd_controls[] = {
455	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
456	SOC_DAPM_PIN_SWITCH("Headset Mic"),
457};
458
459static const struct snd_soc_dapm_widget sc7180_adau7002_snd_widgets[] = {
460	SND_SOC_DAPM_MIC("DMIC", NULL),
461};
462
463static const char * const dmic_mux_text[] = {
464	"Front Mic",
465	"Rear Mic",
466};
467
468static SOC_ENUM_SINGLE_DECL(sc7180_dmic_enum,
469			    SND_SOC_NOPM, 0, dmic_mux_text);
470
471static const struct snd_kcontrol_new sc7180_dmic_mux_control =
472	SOC_DAPM_ENUM_EXT("DMIC Select Mux", sc7180_dmic_enum,
473			  dmic_get, dmic_set);
474
475static const struct snd_soc_dapm_widget sc7180_snd_dual_mic_widgets[] = {
476	SND_SOC_DAPM_HP("Headphone Jack", NULL),
477	SND_SOC_DAPM_MIC("Headset Mic", NULL),
478	SND_SOC_DAPM_MIC("DMIC", NULL),
479	SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0, &sc7180_dmic_mux_control),
480};
481
482static const struct snd_kcontrol_new sc7180_snd_dual_mic_controls[] = {
483	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
484	SOC_DAPM_PIN_SWITCH("Headset Mic"),
485};
486
487static const struct snd_soc_dapm_route sc7180_snd_dual_mic_audio_route[] = {
488	{"Dmic Mux", "Front Mic", "DMIC"},
489	{"Dmic Mux", "Rear Mic", "DMIC"},
490};
491
492static int sc7180_snd_platform_probe(struct platform_device *pdev)
493{
494	struct snd_soc_card *card;
495	struct sc7180_snd_data *data;
496	struct device *dev = &pdev->dev;
497	struct snd_soc_dai_link *link;
498	int ret;
499	int i;
500	bool qdsp = false, no_headphone = false;
501
502	/* Allocate the private data */
503	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
504	if (!data)
505		return -ENOMEM;
506
507	card = &data->card;
508	snd_soc_card_set_drvdata(card, data);
509
510	card->owner = THIS_MODULE;
511	card->driver_name = DRIVER_NAME;
512	card->dev = dev;
513	card->dapm_widgets = sc7180_snd_widgets;
514	card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets);
515	card->controls = sc7180_snd_controls;
516	card->num_controls = ARRAY_SIZE(sc7180_snd_controls);
517
518	if (of_property_present(dev->of_node, "dmic-gpios")) {
519		card->dapm_widgets = sc7180_snd_dual_mic_widgets,
520		card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets),
521		card->controls = sc7180_snd_dual_mic_controls,
522		card->num_controls = ARRAY_SIZE(sc7180_snd_dual_mic_controls),
523		card->dapm_routes = sc7180_snd_dual_mic_audio_route,
524		card->num_dapm_routes = ARRAY_SIZE(sc7180_snd_dual_mic_audio_route),
525		data->dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW);
526		if (IS_ERR(data->dmic_sel)) {
527			dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n", PTR_ERR(data->dmic_sel));
528			return PTR_ERR(data->dmic_sel);
529		}
530	}
531
532	if (of_device_is_compatible(dev->of_node, "google,sc7180-coachz")) {
533		no_headphone = true;
534		card->dapm_widgets = sc7180_adau7002_snd_widgets;
535		card->num_dapm_widgets = ARRAY_SIZE(sc7180_adau7002_snd_widgets);
536	} else if (of_device_is_compatible(dev->of_node, "qcom,sc7180-qdsp6-sndcard")) {
537		qdsp = true;
538	}
539
540	ret = qcom_snd_parse_of(card);
541	if (ret)
542		return ret;
543
544	for_each_card_prelinks(card, i, link) {
545		if (no_headphone) {
546			link->ops = &sc7180_adau7002_ops;
547			link->init = sc7180_adau7002_init;
548		} else if (qdsp) {
549			if (link->no_pcm == 1) {
550				link->ops = &sc7180_qdsp_ops;
551				link->be_hw_params_fixup = sc7180_qdsp_be_hw_params_fixup;
552				link->init = sc7180_qdsp_init;
553			}
554		} else {
555			link->ops = &sc7180_ops;
556			link->init = sc7180_init;
557		}
558	}
559
560	return devm_snd_soc_register_card(dev, card);
561}
562
563static const struct of_device_id sc7180_snd_device_id[]  = {
564	{.compatible = "google,sc7180-trogdor"},
565	{.compatible = "google,sc7180-coachz"},
566	{.compatible = "qcom,sc7180-qdsp6-sndcard"},
567	{},
568};
569MODULE_DEVICE_TABLE(of, sc7180_snd_device_id);
570
571static struct platform_driver sc7180_snd_driver = {
572	.probe = sc7180_snd_platform_probe,
573	.driver = {
574		.name = "msm-snd-sc7180",
575		.of_match_table = sc7180_snd_device_id,
576		.pm = &snd_soc_pm_ops,
577	},
578};
579module_platform_driver(sc7180_snd_driver);
580
581MODULE_DESCRIPTION("sc7180 ASoC Machine Driver");
582MODULE_LICENSE("GPL");
~/ Read-only file preview.