162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (C) 2015 - 2016 Samsung Electronics Co., Ltd. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Authors: Inha Song <ideal.song@samsung.com> 662306a36Sopenharmony_ci// Sylwester Nawrocki <s.nawrocki@samsung.com> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/gpio.h> 1062306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <sound/pcm_params.h> 1462306a36Sopenharmony_ci#include <sound/soc.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "i2s.h" 1762306a36Sopenharmony_ci#include "../codecs/wm5110.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * The source clock is XCLKOUT with its mux set to the external fixed rate 2162306a36Sopenharmony_ci * oscillator (XXTI). 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#define MCLK_RATE 24000000U 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define TM2_DAI_AIF1 0 2662306a36Sopenharmony_ci#define TM2_DAI_AIF2 1 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct tm2_machine_priv { 2962306a36Sopenharmony_ci struct snd_soc_component *component; 3062306a36Sopenharmony_ci unsigned int sysclk_rate; 3162306a36Sopenharmony_ci struct gpio_desc *gpio_mic_bias; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int tm2_start_sysclk(struct snd_soc_card *card) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card); 3762306a36Sopenharmony_ci struct snd_soc_component *component = priv->component; 3862306a36Sopenharmony_ci int ret; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci ret = snd_soc_component_set_pll(component, WM5110_FLL1_REFCLK, 4162306a36Sopenharmony_ci ARIZONA_FLL_SRC_MCLK1, 4262306a36Sopenharmony_ci MCLK_RATE, 4362306a36Sopenharmony_ci priv->sysclk_rate); 4462306a36Sopenharmony_ci if (ret < 0) { 4562306a36Sopenharmony_ci dev_err(component->dev, "Failed to set FLL1 source: %d\n", ret); 4662306a36Sopenharmony_ci return ret; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci ret = snd_soc_component_set_pll(component, WM5110_FLL1, 5062306a36Sopenharmony_ci ARIZONA_FLL_SRC_MCLK1, 5162306a36Sopenharmony_ci MCLK_RATE, 5262306a36Sopenharmony_ci priv->sysclk_rate); 5362306a36Sopenharmony_ci if (ret < 0) { 5462306a36Sopenharmony_ci dev_err(component->dev, "Failed to start FLL1: %d\n", ret); 5562306a36Sopenharmony_ci return ret; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK, 5962306a36Sopenharmony_ci ARIZONA_CLK_SRC_FLL1, 6062306a36Sopenharmony_ci priv->sysclk_rate, 6162306a36Sopenharmony_ci SND_SOC_CLOCK_IN); 6262306a36Sopenharmony_ci if (ret < 0) { 6362306a36Sopenharmony_ci dev_err(component->dev, "Failed to set SYSCLK source: %d\n", ret); 6462306a36Sopenharmony_ci return ret; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int tm2_stop_sysclk(struct snd_soc_card *card) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card); 7362306a36Sopenharmony_ci struct snd_soc_component *component = priv->component; 7462306a36Sopenharmony_ci int ret; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci ret = snd_soc_component_set_pll(component, WM5110_FLL1, 0, 0, 0); 7762306a36Sopenharmony_ci if (ret < 0) { 7862306a36Sopenharmony_ci dev_err(component->dev, "Failed to stop FLL1: %d\n", ret); 7962306a36Sopenharmony_ci return ret; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK, 8362306a36Sopenharmony_ci ARIZONA_CLK_SRC_FLL1, 0, 0); 8462306a36Sopenharmony_ci if (ret < 0) { 8562306a36Sopenharmony_ci dev_err(component->dev, "Failed to stop SYSCLK: %d\n", ret); 8662306a36Sopenharmony_ci return ret; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int tm2_aif1_hw_params(struct snd_pcm_substream *substream, 9362306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 9662306a36Sopenharmony_ci struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; 9762306a36Sopenharmony_ci struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(rtd->card); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci switch (params_rate(params)) { 10062306a36Sopenharmony_ci case 4000: 10162306a36Sopenharmony_ci case 8000: 10262306a36Sopenharmony_ci case 12000: 10362306a36Sopenharmony_ci case 16000: 10462306a36Sopenharmony_ci case 24000: 10562306a36Sopenharmony_ci case 32000: 10662306a36Sopenharmony_ci case 48000: 10762306a36Sopenharmony_ci case 96000: 10862306a36Sopenharmony_ci case 192000: 10962306a36Sopenharmony_ci /* Highest possible SYSCLK frequency: 147.456MHz */ 11062306a36Sopenharmony_ci priv->sysclk_rate = 147456000U; 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci case 11025: 11362306a36Sopenharmony_ci case 22050: 11462306a36Sopenharmony_ci case 44100: 11562306a36Sopenharmony_ci case 88200: 11662306a36Sopenharmony_ci case 176400: 11762306a36Sopenharmony_ci /* Highest possible SYSCLK frequency: 135.4752 MHz */ 11862306a36Sopenharmony_ci priv->sysclk_rate = 135475200U; 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci default: 12162306a36Sopenharmony_ci dev_err(component->dev, "Not supported sample rate: %d\n", 12262306a36Sopenharmony_ci params_rate(params)); 12362306a36Sopenharmony_ci return -EINVAL; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return tm2_start_sysclk(rtd->card); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic const struct snd_soc_ops tm2_aif1_ops = { 13062306a36Sopenharmony_ci .hw_params = tm2_aif1_hw_params, 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int tm2_aif2_hw_params(struct snd_pcm_substream *substream, 13462306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 13762306a36Sopenharmony_ci struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; 13862306a36Sopenharmony_ci unsigned int asyncclk_rate; 13962306a36Sopenharmony_ci int ret; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci switch (params_rate(params)) { 14262306a36Sopenharmony_ci case 8000: 14362306a36Sopenharmony_ci case 12000: 14462306a36Sopenharmony_ci case 16000: 14562306a36Sopenharmony_ci /* Highest possible ASYNCCLK frequency: 49.152MHz */ 14662306a36Sopenharmony_ci asyncclk_rate = 49152000U; 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci case 11025: 14962306a36Sopenharmony_ci /* Highest possible ASYNCCLK frequency: 45.1584 MHz */ 15062306a36Sopenharmony_ci asyncclk_rate = 45158400U; 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci default: 15362306a36Sopenharmony_ci dev_err(component->dev, "Not supported sample rate: %d\n", 15462306a36Sopenharmony_ci params_rate(params)); 15562306a36Sopenharmony_ci return -EINVAL; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ret = snd_soc_component_set_pll(component, WM5110_FLL2_REFCLK, 15962306a36Sopenharmony_ci ARIZONA_FLL_SRC_MCLK1, 16062306a36Sopenharmony_ci MCLK_RATE, 16162306a36Sopenharmony_ci asyncclk_rate); 16262306a36Sopenharmony_ci if (ret < 0) { 16362306a36Sopenharmony_ci dev_err(component->dev, "Failed to set FLL2 source: %d\n", ret); 16462306a36Sopenharmony_ci return ret; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ret = snd_soc_component_set_pll(component, WM5110_FLL2, 16862306a36Sopenharmony_ci ARIZONA_FLL_SRC_MCLK1, 16962306a36Sopenharmony_ci MCLK_RATE, 17062306a36Sopenharmony_ci asyncclk_rate); 17162306a36Sopenharmony_ci if (ret < 0) { 17262306a36Sopenharmony_ci dev_err(component->dev, "Failed to start FLL2: %d\n", ret); 17362306a36Sopenharmony_ci return ret; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_ASYNCCLK, 17762306a36Sopenharmony_ci ARIZONA_CLK_SRC_FLL2, 17862306a36Sopenharmony_ci asyncclk_rate, 17962306a36Sopenharmony_ci SND_SOC_CLOCK_IN); 18062306a36Sopenharmony_ci if (ret < 0) { 18162306a36Sopenharmony_ci dev_err(component->dev, "Failed to set ASYNCCLK source: %d\n", ret); 18262306a36Sopenharmony_ci return ret; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int tm2_aif2_hw_free(struct snd_pcm_substream *substream) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 19162306a36Sopenharmony_ci struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; 19262306a36Sopenharmony_ci int ret; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* disable FLL2 */ 19562306a36Sopenharmony_ci ret = snd_soc_component_set_pll(component, WM5110_FLL2, ARIZONA_FLL_SRC_MCLK1, 19662306a36Sopenharmony_ci 0, 0); 19762306a36Sopenharmony_ci if (ret < 0) 19862306a36Sopenharmony_ci dev_err(component->dev, "Failed to stop FLL2: %d\n", ret); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return ret; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic const struct snd_soc_ops tm2_aif2_ops = { 20462306a36Sopenharmony_ci .hw_params = tm2_aif2_hw_params, 20562306a36Sopenharmony_ci .hw_free = tm2_aif2_hw_free, 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int tm2_hdmi_hw_params(struct snd_pcm_substream *substream, 20962306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 21262306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 21362306a36Sopenharmony_ci unsigned int bfs; 21462306a36Sopenharmony_ci int bitwidth, ret; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci bitwidth = snd_pcm_format_width(params_format(params)); 21762306a36Sopenharmony_ci if (bitwidth < 0) { 21862306a36Sopenharmony_ci dev_err(rtd->card->dev, "Invalid bit-width: %d\n", bitwidth); 21962306a36Sopenharmony_ci return bitwidth; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci switch (bitwidth) { 22362306a36Sopenharmony_ci case 48: 22462306a36Sopenharmony_ci bfs = 64; 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci case 16: 22762306a36Sopenharmony_ci bfs = 32; 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci default: 23062306a36Sopenharmony_ci dev_err(rtd->card->dev, "Unsupported bit-width: %d\n", bitwidth); 23162306a36Sopenharmony_ci return -EINVAL; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci switch (params_rate(params)) { 23562306a36Sopenharmony_ci case 48000: 23662306a36Sopenharmony_ci case 96000: 23762306a36Sopenharmony_ci case 192000: 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci default: 24062306a36Sopenharmony_ci dev_err(rtd->card->dev, "Unsupported sample rate: %d\n", 24162306a36Sopenharmony_ci params_rate(params)); 24262306a36Sopenharmony_ci return -EINVAL; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_OPCLK, 24662306a36Sopenharmony_ci 0, SAMSUNG_I2S_OPCLK_PCLK); 24762306a36Sopenharmony_ci if (ret < 0) 24862306a36Sopenharmony_ci return ret; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci ret = snd_soc_dai_set_clkdiv(cpu_dai, SAMSUNG_I2S_DIV_BCLK, bfs); 25162306a36Sopenharmony_ci if (ret < 0) 25262306a36Sopenharmony_ci return ret; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic const struct snd_soc_ops tm2_hdmi_ops = { 25862306a36Sopenharmony_ci .hw_params = tm2_hdmi_hw_params, 25962306a36Sopenharmony_ci}; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int tm2_mic_bias(struct snd_soc_dapm_widget *w, 26262306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct snd_soc_card *card = w->dapm->card; 26562306a36Sopenharmony_ci struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci switch (event) { 26862306a36Sopenharmony_ci case SND_SOC_DAPM_PRE_PMU: 26962306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->gpio_mic_bias, 1); 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci case SND_SOC_DAPM_POST_PMD: 27262306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->gpio_mic_bias, 0); 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int tm2_set_bias_level(struct snd_soc_card *card, 28062306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm, 28162306a36Sopenharmony_ci enum snd_soc_bias_level level) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (dapm->dev != asoc_rtd_to_codec(rtd, 0)->dev) 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci switch (level) { 29162306a36Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 29262306a36Sopenharmony_ci if (card->dapm.bias_level == SND_SOC_BIAS_OFF) 29362306a36Sopenharmony_ci tm2_start_sysclk(card); 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci case SND_SOC_BIAS_OFF: 29662306a36Sopenharmony_ci tm2_stop_sysclk(card); 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci default: 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic struct snd_soc_aux_dev tm2_speaker_amp_dev; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int tm2_late_probe(struct snd_soc_card *card) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card); 31062306a36Sopenharmony_ci unsigned int ch_map[] = { 0, 1 }; 31162306a36Sopenharmony_ci struct snd_soc_dai *amp_pdm_dai; 31262306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 31362306a36Sopenharmony_ci struct snd_soc_dai *aif1_dai; 31462306a36Sopenharmony_ci struct snd_soc_dai *aif2_dai; 31562306a36Sopenharmony_ci int ret; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[TM2_DAI_AIF1]); 31862306a36Sopenharmony_ci aif1_dai = asoc_rtd_to_codec(rtd, 0); 31962306a36Sopenharmony_ci priv->component = asoc_rtd_to_codec(rtd, 0)->component; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0); 32262306a36Sopenharmony_ci if (ret < 0) { 32362306a36Sopenharmony_ci dev_err(aif1_dai->dev, "Failed to set SYSCLK: %d\n", ret); 32462306a36Sopenharmony_ci return ret; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[TM2_DAI_AIF2]); 32862306a36Sopenharmony_ci aif2_dai = asoc_rtd_to_codec(rtd, 0); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0); 33162306a36Sopenharmony_ci if (ret < 0) { 33262306a36Sopenharmony_ci dev_err(aif2_dai->dev, "Failed to set ASYNCCLK: %d\n", ret); 33362306a36Sopenharmony_ci return ret; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci amp_pdm_dai = snd_soc_find_dai(&tm2_speaker_amp_dev.dlc); 33762306a36Sopenharmony_ci if (!amp_pdm_dai) 33862306a36Sopenharmony_ci return -ENODEV; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Set the MAX98504 V/I sense PDM Tx DAI channel mapping */ 34162306a36Sopenharmony_ci ret = snd_soc_dai_set_channel_map(amp_pdm_dai, ARRAY_SIZE(ch_map), 34262306a36Sopenharmony_ci ch_map, 0, NULL); 34362306a36Sopenharmony_ci if (ret < 0) 34462306a36Sopenharmony_ci return ret; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ret = snd_soc_dai_set_tdm_slot(amp_pdm_dai, 0x3, 0x0, 2, 16); 34762306a36Sopenharmony_ci if (ret < 0) 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic const struct snd_kcontrol_new tm2_controls[] = { 35462306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("HP"), 35562306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("SPK"), 35662306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("RCV"), 35762306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("VPS"), 35862306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("HDMI"), 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Main Mic"), 36162306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Sub Mic"), 36262306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Third Mic"), 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headset Mic"), 36562306a36Sopenharmony_ci}; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget tm2_dapm_widgets[] = { 36862306a36Sopenharmony_ci SND_SOC_DAPM_HP("HP", NULL), 36962306a36Sopenharmony_ci SND_SOC_DAPM_SPK("SPK", NULL), 37062306a36Sopenharmony_ci SND_SOC_DAPM_SPK("RCV", NULL), 37162306a36Sopenharmony_ci SND_SOC_DAPM_LINE("VPS", NULL), 37262306a36Sopenharmony_ci SND_SOC_DAPM_LINE("HDMI", NULL), 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci SND_SOC_DAPM_MIC("Main Mic", tm2_mic_bias), 37562306a36Sopenharmony_ci SND_SOC_DAPM_MIC("Sub Mic", NULL), 37662306a36Sopenharmony_ci SND_SOC_DAPM_MIC("Third Mic", NULL), 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci SND_SOC_DAPM_MIC("Headset Mic", NULL), 37962306a36Sopenharmony_ci}; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic const struct snd_soc_component_driver tm2_component = { 38262306a36Sopenharmony_ci .name = "tm2-audio", 38362306a36Sopenharmony_ci}; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic struct snd_soc_dai_driver tm2_ext_dai[] = { 38662306a36Sopenharmony_ci { 38762306a36Sopenharmony_ci .name = "Voice call", 38862306a36Sopenharmony_ci .playback = { 38962306a36Sopenharmony_ci .channels_min = 1, 39062306a36Sopenharmony_ci .channels_max = 4, 39162306a36Sopenharmony_ci .rate_min = 8000, 39262306a36Sopenharmony_ci .rate_max = 48000, 39362306a36Sopenharmony_ci .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | 39462306a36Sopenharmony_ci SNDRV_PCM_RATE_48000), 39562306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 39662306a36Sopenharmony_ci }, 39762306a36Sopenharmony_ci .capture = { 39862306a36Sopenharmony_ci .channels_min = 1, 39962306a36Sopenharmony_ci .channels_max = 4, 40062306a36Sopenharmony_ci .rate_min = 8000, 40162306a36Sopenharmony_ci .rate_max = 48000, 40262306a36Sopenharmony_ci .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | 40362306a36Sopenharmony_ci SNDRV_PCM_RATE_48000), 40462306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 40562306a36Sopenharmony_ci }, 40662306a36Sopenharmony_ci }, 40762306a36Sopenharmony_ci { 40862306a36Sopenharmony_ci .name = "Bluetooth", 40962306a36Sopenharmony_ci .playback = { 41062306a36Sopenharmony_ci .channels_min = 1, 41162306a36Sopenharmony_ci .channels_max = 4, 41262306a36Sopenharmony_ci .rate_min = 8000, 41362306a36Sopenharmony_ci .rate_max = 16000, 41462306a36Sopenharmony_ci .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), 41562306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 41662306a36Sopenharmony_ci }, 41762306a36Sopenharmony_ci .capture = { 41862306a36Sopenharmony_ci .channels_min = 1, 41962306a36Sopenharmony_ci .channels_max = 2, 42062306a36Sopenharmony_ci .rate_min = 8000, 42162306a36Sopenharmony_ci .rate_max = 16000, 42262306a36Sopenharmony_ci .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), 42362306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 42462306a36Sopenharmony_ci }, 42562306a36Sopenharmony_ci }, 42662306a36Sopenharmony_ci}; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ciSND_SOC_DAILINK_DEFS(aif1, 42962306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)), 43062306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm5110-aif1")), 43162306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_EMPTY())); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ciSND_SOC_DAILINK_DEFS(voice, 43462306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)), 43562306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm5110-aif2")), 43662306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_EMPTY())); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ciSND_SOC_DAILINK_DEFS(bt, 43962306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)), 44062306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm5110-aif3")), 44162306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_EMPTY())); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ciSND_SOC_DAILINK_DEFS(hdmi, 44462306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_EMPTY()), 44562306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_EMPTY()), 44662306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_EMPTY())); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic struct snd_soc_dai_link tm2_dai_links[] = { 44962306a36Sopenharmony_ci { 45062306a36Sopenharmony_ci .name = "WM5110 AIF1", 45162306a36Sopenharmony_ci .stream_name = "HiFi Primary", 45262306a36Sopenharmony_ci .ops = &tm2_aif1_ops, 45362306a36Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 45462306a36Sopenharmony_ci SND_SOC_DAIFMT_CBM_CFM, 45562306a36Sopenharmony_ci SND_SOC_DAILINK_REG(aif1), 45662306a36Sopenharmony_ci }, { 45762306a36Sopenharmony_ci .name = "WM5110 Voice", 45862306a36Sopenharmony_ci .stream_name = "Voice call", 45962306a36Sopenharmony_ci .ops = &tm2_aif2_ops, 46062306a36Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 46162306a36Sopenharmony_ci SND_SOC_DAIFMT_CBM_CFM, 46262306a36Sopenharmony_ci .ignore_suspend = 1, 46362306a36Sopenharmony_ci SND_SOC_DAILINK_REG(voice), 46462306a36Sopenharmony_ci }, { 46562306a36Sopenharmony_ci .name = "WM5110 BT", 46662306a36Sopenharmony_ci .stream_name = "Bluetooth", 46762306a36Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 46862306a36Sopenharmony_ci SND_SOC_DAIFMT_CBM_CFM, 46962306a36Sopenharmony_ci .ignore_suspend = 1, 47062306a36Sopenharmony_ci SND_SOC_DAILINK_REG(bt), 47162306a36Sopenharmony_ci }, { 47262306a36Sopenharmony_ci .name = "HDMI", 47362306a36Sopenharmony_ci .stream_name = "i2s1", 47462306a36Sopenharmony_ci .ops = &tm2_hdmi_ops, 47562306a36Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 47662306a36Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS, 47762306a36Sopenharmony_ci SND_SOC_DAILINK_REG(hdmi), 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci}; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic struct snd_soc_card tm2_card = { 48262306a36Sopenharmony_ci .owner = THIS_MODULE, 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci .dai_link = tm2_dai_links, 48562306a36Sopenharmony_ci .controls = tm2_controls, 48662306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(tm2_controls), 48762306a36Sopenharmony_ci .dapm_widgets = tm2_dapm_widgets, 48862306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(tm2_dapm_widgets), 48962306a36Sopenharmony_ci .aux_dev = &tm2_speaker_amp_dev, 49062306a36Sopenharmony_ci .num_aux_devs = 1, 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci .late_probe = tm2_late_probe, 49362306a36Sopenharmony_ci .set_bias_level = tm2_set_bias_level, 49462306a36Sopenharmony_ci}; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic int tm2_probe(struct platform_device *pdev) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct device_node *cpu_dai_node[2] = {}; 49962306a36Sopenharmony_ci struct device_node *codec_dai_node[2] = {}; 50062306a36Sopenharmony_ci const char *cells_name = NULL; 50162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 50262306a36Sopenharmony_ci struct snd_soc_card *card = &tm2_card; 50362306a36Sopenharmony_ci struct tm2_machine_priv *priv; 50462306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link; 50562306a36Sopenharmony_ci int num_codecs, ret, i; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 50862306a36Sopenharmony_ci if (!priv) 50962306a36Sopenharmony_ci return -ENOMEM; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci snd_soc_card_set_drvdata(card, priv); 51262306a36Sopenharmony_ci card->dev = dev; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci priv->gpio_mic_bias = devm_gpiod_get(dev, "mic-bias", GPIOD_OUT_HIGH); 51562306a36Sopenharmony_ci if (IS_ERR(priv->gpio_mic_bias)) { 51662306a36Sopenharmony_ci dev_err(dev, "Failed to get mic bias gpio\n"); 51762306a36Sopenharmony_ci return PTR_ERR(priv->gpio_mic_bias); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci ret = snd_soc_of_parse_card_name(card, "model"); 52162306a36Sopenharmony_ci if (ret < 0) { 52262306a36Sopenharmony_ci dev_err(dev, "Card name is not specified\n"); 52362306a36Sopenharmony_ci return ret; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); 52762306a36Sopenharmony_ci if (ret < 0) { 52862306a36Sopenharmony_ci /* Backwards compatible way */ 52962306a36Sopenharmony_ci ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); 53062306a36Sopenharmony_ci if (ret < 0) { 53162306a36Sopenharmony_ci dev_err(dev, "Audio routing is not specified or invalid\n"); 53262306a36Sopenharmony_ci return ret; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci card->aux_dev[0].dlc.of_node = of_parse_phandle(dev->of_node, 53762306a36Sopenharmony_ci "audio-amplifier", 0); 53862306a36Sopenharmony_ci if (!card->aux_dev[0].dlc.of_node) { 53962306a36Sopenharmony_ci dev_err(dev, "audio-amplifier property invalid or missing\n"); 54062306a36Sopenharmony_ci return -EINVAL; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci num_codecs = of_count_phandle_with_args(dev->of_node, "audio-codec", 54462306a36Sopenharmony_ci NULL); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* Skip the HDMI link if not specified in DT */ 54762306a36Sopenharmony_ci if (num_codecs > 1) { 54862306a36Sopenharmony_ci card->num_links = ARRAY_SIZE(tm2_dai_links); 54962306a36Sopenharmony_ci cells_name = "#sound-dai-cells"; 55062306a36Sopenharmony_ci } else { 55162306a36Sopenharmony_ci card->num_links = ARRAY_SIZE(tm2_dai_links) - 1; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci for (i = 0; i < num_codecs; i++) { 55562306a36Sopenharmony_ci struct of_phandle_args args; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci ret = of_parse_phandle_with_args(dev->of_node, "i2s-controller", 55862306a36Sopenharmony_ci cells_name, i, &args); 55962306a36Sopenharmony_ci if (ret) { 56062306a36Sopenharmony_ci dev_err(dev, "i2s-controller property parse error: %d\n", i); 56162306a36Sopenharmony_ci ret = -EINVAL; 56262306a36Sopenharmony_ci goto dai_node_put; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci cpu_dai_node[i] = args.np; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci codec_dai_node[i] = of_parse_phandle(dev->of_node, 56762306a36Sopenharmony_ci "audio-codec", i); 56862306a36Sopenharmony_ci if (!codec_dai_node[i]) { 56962306a36Sopenharmony_ci dev_err(dev, "audio-codec property parse error\n"); 57062306a36Sopenharmony_ci ret = -EINVAL; 57162306a36Sopenharmony_ci goto dai_node_put; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* Initialize WM5110 - I2S and HDMI - I2S1 DAI links */ 57662306a36Sopenharmony_ci for_each_card_prelinks(card, i, dai_link) { 57762306a36Sopenharmony_ci unsigned int dai_index = 0; /* WM5110 */ 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci dai_link->cpus->name = NULL; 58062306a36Sopenharmony_ci dai_link->platforms->name = NULL; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (num_codecs > 1 && i == card->num_links - 1) 58362306a36Sopenharmony_ci dai_index = 1; /* HDMI */ 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci dai_link->codecs->of_node = codec_dai_node[dai_index]; 58662306a36Sopenharmony_ci dai_link->cpus->of_node = cpu_dai_node[dai_index]; 58762306a36Sopenharmony_ci dai_link->platforms->of_node = cpu_dai_node[dai_index]; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (num_codecs > 1) { 59162306a36Sopenharmony_ci struct of_phandle_args args; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* HDMI DAI link (I2S1) */ 59462306a36Sopenharmony_ci i = card->num_links - 1; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci ret = of_parse_phandle_with_fixed_args(dev->of_node, 59762306a36Sopenharmony_ci "audio-codec", 0, 1, &args); 59862306a36Sopenharmony_ci if (ret) { 59962306a36Sopenharmony_ci dev_err(dev, "audio-codec property parse error\n"); 60062306a36Sopenharmony_ci goto dai_node_put; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci ret = snd_soc_get_dai_name(&args, &card->dai_link[i].codecs->dai_name); 60462306a36Sopenharmony_ci if (ret) { 60562306a36Sopenharmony_ci dev_err(dev, "Unable to get codec_dai_name\n"); 60662306a36Sopenharmony_ci goto dai_node_put; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci ret = devm_snd_soc_register_component(dev, &tm2_component, 61162306a36Sopenharmony_ci tm2_ext_dai, ARRAY_SIZE(tm2_ext_dai)); 61262306a36Sopenharmony_ci if (ret < 0) { 61362306a36Sopenharmony_ci dev_err(dev, "Failed to register component: %d\n", ret); 61462306a36Sopenharmony_ci goto dai_node_put; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci ret = devm_snd_soc_register_card(dev, card); 61862306a36Sopenharmony_ci if (ret < 0) { 61962306a36Sopenharmony_ci dev_err_probe(dev, ret, "Failed to register card\n"); 62062306a36Sopenharmony_ci goto dai_node_put; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cidai_node_put: 62462306a36Sopenharmony_ci for (i = 0; i < num_codecs; i++) { 62562306a36Sopenharmony_ci of_node_put(codec_dai_node[i]); 62662306a36Sopenharmony_ci of_node_put(cpu_dai_node[i]); 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci of_node_put(card->aux_dev[0].dlc.of_node); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci return ret; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic int tm2_pm_prepare(struct device *dev) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci struct snd_soc_card *card = dev_get_drvdata(dev); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return tm2_stop_sysclk(card); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic void tm2_pm_complete(struct device *dev) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct snd_soc_card *card = dev_get_drvdata(dev); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci tm2_start_sysclk(card); 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic const struct dev_pm_ops tm2_pm_ops = { 64962306a36Sopenharmony_ci .prepare = tm2_pm_prepare, 65062306a36Sopenharmony_ci .suspend = snd_soc_suspend, 65162306a36Sopenharmony_ci .resume = snd_soc_resume, 65262306a36Sopenharmony_ci .complete = tm2_pm_complete, 65362306a36Sopenharmony_ci .freeze = snd_soc_suspend, 65462306a36Sopenharmony_ci .thaw = snd_soc_resume, 65562306a36Sopenharmony_ci .poweroff = snd_soc_poweroff, 65662306a36Sopenharmony_ci .restore = snd_soc_resume, 65762306a36Sopenharmony_ci}; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic const struct of_device_id tm2_of_match[] = { 66062306a36Sopenharmony_ci { .compatible = "samsung,tm2-audio" }, 66162306a36Sopenharmony_ci { }, 66262306a36Sopenharmony_ci}; 66362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tm2_of_match); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic struct platform_driver tm2_driver = { 66662306a36Sopenharmony_ci .driver = { 66762306a36Sopenharmony_ci .name = "tm2-audio", 66862306a36Sopenharmony_ci .pm = &tm2_pm_ops, 66962306a36Sopenharmony_ci .of_match_table = tm2_of_match, 67062306a36Sopenharmony_ci }, 67162306a36Sopenharmony_ci .probe = tm2_probe, 67262306a36Sopenharmony_ci}; 67362306a36Sopenharmony_cimodule_platform_driver(tm2_driver); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ciMODULE_AUTHOR("Inha Song <ideal.song@samsung.com>"); 67662306a36Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC Exynos TM2 Audio Support"); 67762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 678