162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (c) 2020, The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// sc7180.c -- ALSA SoC Machine driver for SC7180 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <dt-bindings/sound/sc7180-lpass.h> 862306a36Sopenharmony_ci#include <linux/gpio.h> 962306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/of_device.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <sound/core.h> 1462306a36Sopenharmony_ci#include <sound/jack.h> 1562306a36Sopenharmony_ci#include <sound/pcm.h> 1662306a36Sopenharmony_ci#include <sound/soc.h> 1762306a36Sopenharmony_ci#include <uapi/linux/input-event-codes.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "../codecs/rt5682.h" 2062306a36Sopenharmony_ci#include "../codecs/rt5682s.h" 2162306a36Sopenharmony_ci#include "common.h" 2262306a36Sopenharmony_ci#include "lpass.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define DEFAULT_MCLK_RATE 19200000 2562306a36Sopenharmony_ci#define RT5682_PLL1_FREQ (48000 * 512) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define DRIVER_NAME "SC7180" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct sc7180_snd_data { 3062306a36Sopenharmony_ci struct snd_soc_card card; 3162306a36Sopenharmony_ci u32 pri_mi2s_clk_count; 3262306a36Sopenharmony_ci struct snd_soc_jack hs_jack; 3362306a36Sopenharmony_ci struct snd_soc_jack hdmi_jack; 3462306a36Sopenharmony_ci struct gpio_desc *dmic_sel; 3562306a36Sopenharmony_ci int dmic_switch; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void sc7180_jack_free(struct snd_jack *jack) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct snd_soc_component *component = jack->private_data; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci snd_soc_component_set_jack(component, NULL, NULL); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic struct snd_soc_jack_pin sc7180_jack_pins[] = { 4662306a36Sopenharmony_ci { 4762306a36Sopenharmony_ci .pin = "Headphone Jack", 4862306a36Sopenharmony_ci .mask = SND_JACK_HEADPHONE, 4962306a36Sopenharmony_ci }, 5062306a36Sopenharmony_ci { 5162306a36Sopenharmony_ci .pin = "Headset Mic", 5262306a36Sopenharmony_ci .mask = SND_JACK_MICROPHONE, 5362306a36Sopenharmony_ci }, 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct snd_soc_card *card = rtd->card; 5962306a36Sopenharmony_ci struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card); 6062306a36Sopenharmony_ci struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 6162306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 6262306a36Sopenharmony_ci struct snd_jack *jack; 6362306a36Sopenharmony_ci int rval; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci rval = snd_soc_card_jack_new_pins(card, "Headset Jack", 6662306a36Sopenharmony_ci SND_JACK_HEADSET | 6762306a36Sopenharmony_ci SND_JACK_HEADPHONE | 6862306a36Sopenharmony_ci SND_JACK_BTN_0 | SND_JACK_BTN_1 | 6962306a36Sopenharmony_ci SND_JACK_BTN_2 | SND_JACK_BTN_3, 7062306a36Sopenharmony_ci &pdata->hs_jack, 7162306a36Sopenharmony_ci sc7180_jack_pins, 7262306a36Sopenharmony_ci ARRAY_SIZE(sc7180_jack_pins)); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (rval < 0) { 7562306a36Sopenharmony_ci dev_err(card->dev, "Unable to add Headset Jack\n"); 7662306a36Sopenharmony_ci return rval; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci jack = pdata->hs_jack.jack; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); 8262306a36Sopenharmony_ci snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); 8362306a36Sopenharmony_ci snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP); 8462306a36Sopenharmony_ci snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci jack->private_data = component; 8762306a36Sopenharmony_ci jack->private_free = sc7180_jack_free; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return snd_soc_component_set_jack(component, &pdata->hs_jack, NULL); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int sc7180_hdmi_init(struct snd_soc_pcm_runtime *rtd) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct snd_soc_card *card = rtd->card; 9562306a36Sopenharmony_ci struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card); 9662306a36Sopenharmony_ci struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 9762306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 9862306a36Sopenharmony_ci struct snd_jack *jack; 9962306a36Sopenharmony_ci int rval; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci rval = snd_soc_card_jack_new( 10262306a36Sopenharmony_ci card, "HDMI Jack", 10362306a36Sopenharmony_ci SND_JACK_LINEOUT, 10462306a36Sopenharmony_ci &pdata->hdmi_jack); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (rval < 0) { 10762306a36Sopenharmony_ci dev_err(card->dev, "Unable to add HDMI Jack\n"); 10862306a36Sopenharmony_ci return rval; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci jack = pdata->hdmi_jack.jack; 11262306a36Sopenharmony_ci jack->private_data = component; 11362306a36Sopenharmony_ci jack->private_free = sc7180_jack_free; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int sc7180_init(struct snd_soc_pcm_runtime *rtd) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci switch (cpu_dai->id) { 12362306a36Sopenharmony_ci case MI2S_PRIMARY: 12462306a36Sopenharmony_ci return sc7180_headset_init(rtd); 12562306a36Sopenharmony_ci case MI2S_SECONDARY: 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci case LPASS_DP_RX: 12862306a36Sopenharmony_ci return sc7180_hdmi_init(rtd); 12962306a36Sopenharmony_ci default: 13062306a36Sopenharmony_ci dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, 13162306a36Sopenharmony_ci cpu_dai->id); 13262306a36Sopenharmony_ci return -EINVAL; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int sc7180_snd_startup(struct snd_pcm_substream *substream) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = substream->private_data; 14062306a36Sopenharmony_ci struct snd_soc_card *card = rtd->card; 14162306a36Sopenharmony_ci struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); 14262306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 14362306a36Sopenharmony_ci struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 14462306a36Sopenharmony_ci int pll_id, pll_source, pll_in, pll_out, clk_id, ret; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!strcmp(codec_dai->name, "rt5682-aif1")) { 14762306a36Sopenharmony_ci pll_source = RT5682_PLL1_S_MCLK; 14862306a36Sopenharmony_ci pll_id = 0; 14962306a36Sopenharmony_ci clk_id = RT5682_SCLK_S_PLL1; 15062306a36Sopenharmony_ci pll_out = RT5682_PLL1_FREQ; 15162306a36Sopenharmony_ci pll_in = DEFAULT_MCLK_RATE; 15262306a36Sopenharmony_ci } else if (!strcmp(codec_dai->name, "rt5682s-aif1")) { 15362306a36Sopenharmony_ci pll_source = RT5682S_PLL_S_MCLK; 15462306a36Sopenharmony_ci pll_id = RT5682S_PLL2; 15562306a36Sopenharmony_ci clk_id = RT5682S_SCLK_S_PLL2; 15662306a36Sopenharmony_ci pll_out = RT5682_PLL1_FREQ; 15762306a36Sopenharmony_ci pll_in = DEFAULT_MCLK_RATE; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci switch (cpu_dai->id) { 16162306a36Sopenharmony_ci case MI2S_PRIMARY: 16262306a36Sopenharmony_ci if (++data->pri_mi2s_clk_count == 1) { 16362306a36Sopenharmony_ci snd_soc_dai_set_sysclk(cpu_dai, 16462306a36Sopenharmony_ci LPASS_MCLK0, 16562306a36Sopenharmony_ci DEFAULT_MCLK_RATE, 16662306a36Sopenharmony_ci SNDRV_PCM_STREAM_PLAYBACK); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci snd_soc_dai_set_fmt(codec_dai, 17062306a36Sopenharmony_ci SND_SOC_DAIFMT_BC_FC | 17162306a36Sopenharmony_ci SND_SOC_DAIFMT_NB_NF | 17262306a36Sopenharmony_ci SND_SOC_DAIFMT_I2S); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Configure PLL1 for codec */ 17562306a36Sopenharmony_ci ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source, 17662306a36Sopenharmony_ci pll_in, pll_out); 17762306a36Sopenharmony_ci if (ret) { 17862306a36Sopenharmony_ci dev_err(rtd->dev, "can't set codec pll: %d\n", ret); 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* Configure sysclk for codec */ 18362306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, pll_out, 18462306a36Sopenharmony_ci SND_SOC_CLOCK_IN); 18562306a36Sopenharmony_ci if (ret) 18662306a36Sopenharmony_ci dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", 18762306a36Sopenharmony_ci ret); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci case MI2S_SECONDARY: 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci case LPASS_DP_RX: 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci default: 19562306a36Sopenharmony_ci dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, 19662306a36Sopenharmony_ci cpu_dai->id); 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int dmic_get(struct snd_kcontrol *kcontrol, 20362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 20662306a36Sopenharmony_ci struct sc7180_snd_data *data = snd_soc_card_get_drvdata(dapm->card); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci ucontrol->value.integer.value[0] = data->dmic_switch; 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int dmic_set(struct snd_kcontrol *kcontrol, 21362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 21662306a36Sopenharmony_ci struct sc7180_snd_data *data = snd_soc_card_get_drvdata(dapm->card); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci data->dmic_switch = ucontrol->value.integer.value[0]; 21962306a36Sopenharmony_ci gpiod_set_value(data->dmic_sel, data->dmic_switch); 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void sc7180_snd_shutdown(struct snd_pcm_substream *substream) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = substream->private_data; 22662306a36Sopenharmony_ci struct snd_soc_card *card = rtd->card; 22762306a36Sopenharmony_ci struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); 22862306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci switch (cpu_dai->id) { 23162306a36Sopenharmony_ci case MI2S_PRIMARY: 23262306a36Sopenharmony_ci if (--data->pri_mi2s_clk_count == 0) { 23362306a36Sopenharmony_ci snd_soc_dai_set_sysclk(cpu_dai, 23462306a36Sopenharmony_ci LPASS_MCLK0, 23562306a36Sopenharmony_ci 0, 23662306a36Sopenharmony_ci SNDRV_PCM_STREAM_PLAYBACK); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci case MI2S_SECONDARY: 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci case LPASS_DP_RX: 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci default: 24462306a36Sopenharmony_ci dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, 24562306a36Sopenharmony_ci cpu_dai->id); 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int sc7180_adau7002_init(struct snd_soc_pcm_runtime *rtd) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci switch (cpu_dai->id) { 25562306a36Sopenharmony_ci case MI2S_PRIMARY: 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci case MI2S_SECONDARY: 25862306a36Sopenharmony_ci return 0; 25962306a36Sopenharmony_ci case LPASS_DP_RX: 26062306a36Sopenharmony_ci return sc7180_hdmi_init(rtd); 26162306a36Sopenharmony_ci default: 26262306a36Sopenharmony_ci dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, 26362306a36Sopenharmony_ci cpu_dai->id); 26462306a36Sopenharmony_ci return -EINVAL; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int sc7180_adau7002_snd_startup(struct snd_pcm_substream *substream) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = substream->private_data; 27262306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 27362306a36Sopenharmony_ci struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 27462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci switch (cpu_dai->id) { 27762306a36Sopenharmony_ci case MI2S_PRIMARY: 27862306a36Sopenharmony_ci snd_soc_dai_set_fmt(codec_dai, 27962306a36Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS | 28062306a36Sopenharmony_ci SND_SOC_DAIFMT_NB_NF | 28162306a36Sopenharmony_ci SND_SOC_DAIFMT_I2S); 28262306a36Sopenharmony_ci runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; 28362306a36Sopenharmony_ci snd_pcm_hw_constraint_msbits(runtime, 0, 32, 32); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci case MI2S_SECONDARY: 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci case LPASS_DP_RX: 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci default: 29162306a36Sopenharmony_ci dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, 29262306a36Sopenharmony_ci cpu_dai->id); 29362306a36Sopenharmony_ci return -EINVAL; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic const struct snd_soc_ops sc7180_ops = { 29962306a36Sopenharmony_ci .startup = sc7180_snd_startup, 30062306a36Sopenharmony_ci .shutdown = sc7180_snd_shutdown, 30162306a36Sopenharmony_ci}; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic const struct snd_soc_ops sc7180_adau7002_ops = { 30462306a36Sopenharmony_ci .startup = sc7180_adau7002_snd_startup, 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget sc7180_snd_widgets[] = { 30862306a36Sopenharmony_ci SND_SOC_DAPM_HP("Headphone Jack", NULL), 30962306a36Sopenharmony_ci SND_SOC_DAPM_MIC("Headset Mic", NULL), 31062306a36Sopenharmony_ci}; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic const struct snd_kcontrol_new sc7180_snd_controls[] = { 31362306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headphone Jack"), 31462306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headset Mic"), 31562306a36Sopenharmony_ci}; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget sc7180_adau7002_snd_widgets[] = { 31862306a36Sopenharmony_ci SND_SOC_DAPM_MIC("DMIC", NULL), 31962306a36Sopenharmony_ci}; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic const char * const dmic_mux_text[] = { 32262306a36Sopenharmony_ci "Front Mic", 32362306a36Sopenharmony_ci "Rear Mic", 32462306a36Sopenharmony_ci}; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(sc7180_dmic_enum, 32762306a36Sopenharmony_ci SND_SOC_NOPM, 0, dmic_mux_text); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic const struct snd_kcontrol_new sc7180_dmic_mux_control = 33062306a36Sopenharmony_ci SOC_DAPM_ENUM_EXT("DMIC Select Mux", sc7180_dmic_enum, 33162306a36Sopenharmony_ci dmic_get, dmic_set); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget sc7180_snd_dual_mic_widgets[] = { 33462306a36Sopenharmony_ci SND_SOC_DAPM_HP("Headphone Jack", NULL), 33562306a36Sopenharmony_ci SND_SOC_DAPM_MIC("Headset Mic", NULL), 33662306a36Sopenharmony_ci SND_SOC_DAPM_MIC("DMIC", NULL), 33762306a36Sopenharmony_ci SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0, &sc7180_dmic_mux_control), 33862306a36Sopenharmony_ci}; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic const struct snd_kcontrol_new sc7180_snd_dual_mic_controls[] = { 34162306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headphone Jack"), 34262306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headset Mic"), 34362306a36Sopenharmony_ci}; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic const struct snd_soc_dapm_route sc7180_snd_dual_mic_audio_route[] = { 34662306a36Sopenharmony_ci {"Dmic Mux", "Front Mic", "DMIC"}, 34762306a36Sopenharmony_ci {"Dmic Mux", "Rear Mic", "DMIC"}, 34862306a36Sopenharmony_ci}; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int sc7180_snd_platform_probe(struct platform_device *pdev) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct snd_soc_card *card; 35362306a36Sopenharmony_ci struct sc7180_snd_data *data; 35462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 35562306a36Sopenharmony_ci struct snd_soc_dai_link *link; 35662306a36Sopenharmony_ci int ret; 35762306a36Sopenharmony_ci int i; 35862306a36Sopenharmony_ci bool no_headphone = false; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* Allocate the private data */ 36162306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 36262306a36Sopenharmony_ci if (!data) 36362306a36Sopenharmony_ci return -ENOMEM; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci card = &data->card; 36662306a36Sopenharmony_ci snd_soc_card_set_drvdata(card, data); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci card->owner = THIS_MODULE; 36962306a36Sopenharmony_ci card->driver_name = DRIVER_NAME; 37062306a36Sopenharmony_ci card->dev = dev; 37162306a36Sopenharmony_ci card->dapm_widgets = sc7180_snd_widgets; 37262306a36Sopenharmony_ci card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets); 37362306a36Sopenharmony_ci card->controls = sc7180_snd_controls; 37462306a36Sopenharmony_ci card->num_controls = ARRAY_SIZE(sc7180_snd_controls); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (of_property_read_bool(dev->of_node, "dmic-gpios")) { 37762306a36Sopenharmony_ci card->dapm_widgets = sc7180_snd_dual_mic_widgets, 37862306a36Sopenharmony_ci card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets), 37962306a36Sopenharmony_ci card->controls = sc7180_snd_dual_mic_controls, 38062306a36Sopenharmony_ci card->num_controls = ARRAY_SIZE(sc7180_snd_dual_mic_controls), 38162306a36Sopenharmony_ci card->dapm_routes = sc7180_snd_dual_mic_audio_route, 38262306a36Sopenharmony_ci card->num_dapm_routes = ARRAY_SIZE(sc7180_snd_dual_mic_audio_route), 38362306a36Sopenharmony_ci data->dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW); 38462306a36Sopenharmony_ci if (IS_ERR(data->dmic_sel)) { 38562306a36Sopenharmony_ci dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n", PTR_ERR(data->dmic_sel)); 38662306a36Sopenharmony_ci return PTR_ERR(data->dmic_sel); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (of_device_is_compatible(dev->of_node, "google,sc7180-coachz")) { 39162306a36Sopenharmony_ci no_headphone = true; 39262306a36Sopenharmony_ci card->dapm_widgets = sc7180_adau7002_snd_widgets; 39362306a36Sopenharmony_ci card->num_dapm_widgets = ARRAY_SIZE(sc7180_adau7002_snd_widgets); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ret = qcom_snd_parse_of(card); 39762306a36Sopenharmony_ci if (ret) 39862306a36Sopenharmony_ci return ret; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci for_each_card_prelinks(card, i, link) { 40162306a36Sopenharmony_ci if (no_headphone) { 40262306a36Sopenharmony_ci link->ops = &sc7180_adau7002_ops; 40362306a36Sopenharmony_ci link->init = sc7180_adau7002_init; 40462306a36Sopenharmony_ci } else { 40562306a36Sopenharmony_ci link->ops = &sc7180_ops; 40662306a36Sopenharmony_ci link->init = sc7180_init; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci return devm_snd_soc_register_card(dev, card); 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic const struct of_device_id sc7180_snd_device_id[] = { 41462306a36Sopenharmony_ci {.compatible = "google,sc7180-trogdor"}, 41562306a36Sopenharmony_ci {.compatible = "google,sc7180-coachz"}, 41662306a36Sopenharmony_ci {}, 41762306a36Sopenharmony_ci}; 41862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sc7180_snd_device_id); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic struct platform_driver sc7180_snd_driver = { 42162306a36Sopenharmony_ci .probe = sc7180_snd_platform_probe, 42262306a36Sopenharmony_ci .driver = { 42362306a36Sopenharmony_ci .name = "msm-snd-sc7180", 42462306a36Sopenharmony_ci .of_match_table = sc7180_snd_device_id, 42562306a36Sopenharmony_ci .pm = &snd_soc_pm_ops, 42662306a36Sopenharmony_ci }, 42762306a36Sopenharmony_ci}; 42862306a36Sopenharmony_cimodule_platform_driver(sc7180_snd_driver); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ciMODULE_DESCRIPTION("sc7180 ASoC Machine Driver"); 43162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 432