brintos

brintos / linux-shallow public Read only

0
0
Text · 15.2 KiB · bc030ce Raw
581 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// Copyright (c) 2020, The Linux Foundation. All rights reserved.4//5// sc7180.c -- ALSA SoC Machine driver for SC71806 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		1920000025#define MI2S_BCLK_RATE			153600026#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_dapm(kcontrol);291	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(dapm->card);292 293	ucontrol->value.integer.value[0] = data->dmic_switch;294	return 0;295}296 297static int dmic_set(struct snd_kcontrol *kcontrol,298		    struct snd_ctl_elem_value *ucontrol)299{300	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);301	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(dapm->card);302 303	data->dmic_switch = ucontrol->value.integer.value[0];304	gpiod_set_value(data->dmic_sel, data->dmic_switch);305	return 0;306}307 308static void sc7180_snd_shutdown(struct snd_pcm_substream *substream)309{310	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);311	struct snd_soc_card *card = rtd->card;312	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);313	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);314 315	switch (cpu_dai->id) {316	case MI2S_PRIMARY:317		if (--data->pri_mi2s_clk_count == 0) {318			snd_soc_dai_set_sysclk(cpu_dai,319					       LPASS_MCLK0,320					       0,321					       SNDRV_PCM_STREAM_PLAYBACK);322		}323		break;324	case MI2S_SECONDARY:325		break;326	case LPASS_DP_RX:327		break;328	default:329		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,330			cpu_dai->id);331		break;332	}333}334 335static void sc7180_qdsp_snd_shutdown(struct snd_pcm_substream *substream)336{337	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);338	struct snd_soc_card *card = rtd->card;339	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);340	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);341 342	switch (cpu_dai->id) {343	case PRIMARY_MI2S_RX:344	case PRIMARY_MI2S_TX:345		if (--data->pri_mi2s_clk_count == 0) {346			snd_soc_dai_set_sysclk(cpu_dai,347					       Q6AFE_LPASS_CLK_ID_MCLK_1,348					       0,349					       SNDRV_PCM_STREAM_PLAYBACK);350			snd_soc_dai_set_sysclk(cpu_dai,351					       Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,352					       0,353					       SNDRV_PCM_STREAM_PLAYBACK);354		}355		break;356	case TERTIARY_MI2S_RX:357		snd_soc_dai_set_sysclk(cpu_dai,358				       Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,359				       0,360				       SNDRV_PCM_STREAM_PLAYBACK);361		break;362	case DISPLAY_PORT_RX:363		break;364	default:365		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,366			cpu_dai->id);367		break;368	}369}370 371static int sc7180_adau7002_init(struct snd_soc_pcm_runtime *rtd)372{373	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);374 375	switch (cpu_dai->id) {376	case MI2S_PRIMARY:377		return 0;378	case MI2S_SECONDARY:379		return 0;380	case LPASS_DP_RX:381		return sc7180_hdmi_init(rtd);382	default:383		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,384			cpu_dai->id);385		return -EINVAL;386	}387	return 0;388}389 390static int sc7180_adau7002_snd_startup(struct snd_pcm_substream *substream)391{392	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);393	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);394	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);395	struct snd_pcm_runtime *runtime = substream->runtime;396 397	switch (cpu_dai->id) {398	case MI2S_PRIMARY:399		snd_soc_dai_set_fmt(codec_dai,400				    SND_SOC_DAIFMT_CBS_CFS |401				    SND_SOC_DAIFMT_NB_NF |402				    SND_SOC_DAIFMT_I2S);403		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;404		snd_pcm_hw_constraint_msbits(runtime, 0, 32, 32);405 406		break;407	case MI2S_SECONDARY:408		break;409	case LPASS_DP_RX:410		break;411	default:412		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,413			cpu_dai->id);414		return -EINVAL;415	}416	return 0;417}418 419static int sc7180_qdsp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,420				     struct snd_pcm_hw_params *params)421{422	struct snd_interval *rate = hw_param_interval(params,423					SNDRV_PCM_HW_PARAM_RATE);424	struct snd_interval *channels = hw_param_interval(params,425					SNDRV_PCM_HW_PARAM_CHANNELS);426 427	rate->min = rate->max = 48000;428	channels->min = channels->max = 2;429 430	return 0;431}432 433static const struct snd_soc_ops sc7180_ops = {434	.startup = sc7180_snd_startup,435	.shutdown = sc7180_snd_shutdown,436};437 438static const struct snd_soc_ops sc7180_qdsp_ops = {439	.startup = sc7180_qdsp_snd_startup,440	.shutdown = sc7180_qdsp_snd_shutdown,441};442 443static const struct snd_soc_ops sc7180_adau7002_ops = {444	.startup = sc7180_adau7002_snd_startup,445};446 447static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = {448	SND_SOC_DAPM_HP("Headphone Jack", NULL),449	SND_SOC_DAPM_MIC("Headset Mic", NULL),450};451 452static const struct snd_kcontrol_new sc7180_snd_controls[] = {453	SOC_DAPM_PIN_SWITCH("Headphone Jack"),454	SOC_DAPM_PIN_SWITCH("Headset Mic"),455};456 457static const struct snd_soc_dapm_widget sc7180_adau7002_snd_widgets[] = {458	SND_SOC_DAPM_MIC("DMIC", NULL),459};460 461static const char * const dmic_mux_text[] = {462	"Front Mic",463	"Rear Mic",464};465 466static SOC_ENUM_SINGLE_DECL(sc7180_dmic_enum,467			    SND_SOC_NOPM, 0, dmic_mux_text);468 469static const struct snd_kcontrol_new sc7180_dmic_mux_control =470	SOC_DAPM_ENUM_EXT("DMIC Select Mux", sc7180_dmic_enum,471			  dmic_get, dmic_set);472 473static const struct snd_soc_dapm_widget sc7180_snd_dual_mic_widgets[] = {474	SND_SOC_DAPM_HP("Headphone Jack", NULL),475	SND_SOC_DAPM_MIC("Headset Mic", NULL),476	SND_SOC_DAPM_MIC("DMIC", NULL),477	SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0, &sc7180_dmic_mux_control),478};479 480static const struct snd_kcontrol_new sc7180_snd_dual_mic_controls[] = {481	SOC_DAPM_PIN_SWITCH("Headphone Jack"),482	SOC_DAPM_PIN_SWITCH("Headset Mic"),483};484 485static const struct snd_soc_dapm_route sc7180_snd_dual_mic_audio_route[] = {486	{"Dmic Mux", "Front Mic", "DMIC"},487	{"Dmic Mux", "Rear Mic", "DMIC"},488};489 490static int sc7180_snd_platform_probe(struct platform_device *pdev)491{492	struct snd_soc_card *card;493	struct sc7180_snd_data *data;494	struct device *dev = &pdev->dev;495	struct snd_soc_dai_link *link;496	int ret;497	int i;498	bool qdsp = false, no_headphone = false;499 500	/* Allocate the private data */501	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);502	if (!data)503		return -ENOMEM;504 505	card = &data->card;506	snd_soc_card_set_drvdata(card, data);507 508	card->owner = THIS_MODULE;509	card->driver_name = DRIVER_NAME;510	card->dev = dev;511	card->dapm_widgets = sc7180_snd_widgets;512	card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets);513	card->controls = sc7180_snd_controls;514	card->num_controls = ARRAY_SIZE(sc7180_snd_controls);515 516	if (of_property_read_bool(dev->of_node, "dmic-gpios")) {517		card->dapm_widgets = sc7180_snd_dual_mic_widgets,518		card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets),519		card->controls = sc7180_snd_dual_mic_controls,520		card->num_controls = ARRAY_SIZE(sc7180_snd_dual_mic_controls),521		card->dapm_routes = sc7180_snd_dual_mic_audio_route,522		card->num_dapm_routes = ARRAY_SIZE(sc7180_snd_dual_mic_audio_route),523		data->dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW);524		if (IS_ERR(data->dmic_sel)) {525			dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n", PTR_ERR(data->dmic_sel));526			return PTR_ERR(data->dmic_sel);527		}528	}529 530	if (of_device_is_compatible(dev->of_node, "google,sc7180-coachz")) {531		no_headphone = true;532		card->dapm_widgets = sc7180_adau7002_snd_widgets;533		card->num_dapm_widgets = ARRAY_SIZE(sc7180_adau7002_snd_widgets);534	} else if (of_device_is_compatible(dev->of_node, "qcom,sc7180-qdsp6-sndcard")) {535		qdsp = true;536	}537 538	ret = qcom_snd_parse_of(card);539	if (ret)540		return ret;541 542	for_each_card_prelinks(card, i, link) {543		if (no_headphone) {544			link->ops = &sc7180_adau7002_ops;545			link->init = sc7180_adau7002_init;546		} else if (qdsp) {547			if (link->no_pcm == 1) {548				link->ops = &sc7180_qdsp_ops;549				link->be_hw_params_fixup = sc7180_qdsp_be_hw_params_fixup;550				link->init = sc7180_qdsp_init;551			}552		} else {553			link->ops = &sc7180_ops;554			link->init = sc7180_init;555		}556	}557 558	return devm_snd_soc_register_card(dev, card);559}560 561static const struct of_device_id sc7180_snd_device_id[]  = {562	{.compatible = "google,sc7180-trogdor"},563	{.compatible = "google,sc7180-coachz"},564	{.compatible = "qcom,sc7180-qdsp6-sndcard"},565	{},566};567MODULE_DEVICE_TABLE(of, sc7180_snd_device_id);568 569static struct platform_driver sc7180_snd_driver = {570	.probe = sc7180_snd_platform_probe,571	.driver = {572		.name = "msm-snd-sc7180",573		.of_match_table = sc7180_snd_device_id,574		.pm = &snd_soc_pm_ops,575	},576};577module_platform_driver(sc7180_snd_driver);578 579MODULE_DESCRIPTION("sc7180 ASoC Machine Driver");580MODULE_LICENSE("GPL");581