| /* |
| * mt8167-codec.c -- MT8167 ALSA SoC codec driver |
| * |
| * Copyright (c) 2016 MediaTek Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <linux/clk.h> |
| #include <linux/debugfs.h> |
| #include <linux/delay.h> |
| #include <linux/mfd/syscon.h> |
| #include <linux/module.h> |
| #include <linux/of_platform.h> |
| #include <sound/soc.h> |
| #include <sound/tlv.h> |
| #include "mt8167-codec.h" |
| #include "mt8167-codec-utils.h" |
| #include <linux/of_address.h> |
| |
| #ifdef CONFIG_MTK_SPEAKER |
| #include "mt6392-codec.h" |
| #endif |
| |
| #define DMIC_PHASE_NUM 8 |
| |
| enum regmap_module_id { |
| REGMAP_AFE = 0, |
| // REGMAP_APMIXEDSYS, |
| #ifdef CONFIG_MTK_SPEAKER |
| REGMAP_PWRAP, |
| #endif |
| REGMAP_NUMS, |
| }; |
| |
| enum dmic_wire_mode { |
| DMIC_ONE_WIRE = 0, |
| DMIC_TWO_WIRE, |
| }; |
| |
| enum dmic_rate_mode { |
| DMIC_RATE_D1P625M = 0, |
| DMIC_RATE_D3P25M, |
| }; |
| |
| enum dmic_ch_mode { |
| DMIC_L_CH = 0, |
| DMIC_R_CH, |
| DMIC_CH_NUM, |
| }; |
| |
| enum codec_pga_gain_enum_id { |
| HP_L_PGA_GAIN = 0, |
| HP_R_PGA_GAIN, |
| LOUT_PGA_GAIN, |
| UL_L_PGA_GAIN, |
| UL_R_PGA_GAIN, |
| PGA_GAIN_MAX, |
| }; |
| |
| struct mt8167_codec_priv { |
| #ifdef CONFIG_MTK_SPEAKER |
| struct mt6392_codec_priv mt6392_data; |
| #endif |
| struct snd_soc_component *codec; |
| struct regmap *regmap; |
| struct regmap *regmap_modules[REGMAP_NUMS]; |
| void __iomem *apmixedsys_reg_base; |
| uint32_t lch_dccomp_val; /* L-ch DC compensation value */ |
| uint32_t rch_dccomp_val; /* R-ch DC compensation value */ |
| int32_t lch_dc_offset; /* L-ch DC offset value */ |
| int32_t rch_dc_offset; /* R-ch DC offset value */ |
| bool is_lch_dc_calibrated; |
| bool is_rch_dc_calibrated; |
| uint32_t pga_gain[PGA_GAIN_MAX]; |
| uint32_t dmic_wire_mode; |
| uint32_t dmic_ch_phase[DMIC_CH_NUM]; |
| uint32_t dmic_rate_mode; /* 0:1.625MHz 1:3.25MHz */ |
| uint32_t loopback_type; |
| bool dl_en; |
| struct clk *clk; |
| #ifdef CONFIG_DEBUG_FS |
| struct dentry *debugfs; |
| #endif |
| }; |
| |
| #define MT8167_CODEC_NAME "mt8167-codec" |
| |
| static int mt8167_codec_startup(struct snd_pcm_substream *substream, |
| struct snd_soc_dai *codec_dai) |
| { |
| dev_dbg(codec_dai->component->dev, "%s\n", __func__); |
| return 0; |
| } |
| |
| static void mt8167_codec_shutdown(struct snd_pcm_substream *substream, |
| struct snd_soc_dai *codec_dai) |
| { |
| dev_dbg(codec_dai->component->dev, "%s\n", __func__); |
| } |
| |
| struct mt8167_codec_rate { |
| unsigned int rate; |
| unsigned int regvalue; |
| }; |
| |
| static const struct mt8167_codec_rate mt8167_codec_ul_rates[] = { |
| { .rate = 8000, .regvalue = 0 }, |
| { .rate = 16000, .regvalue = 0 }, |
| { .rate = 32000, .regvalue = 0 }, |
| { .rate = 48000, .regvalue = 1 }, |
| }; |
| |
| static const struct mt8167_codec_rate mt8167_codec_dl_rates[] = { |
| { .rate = 8000, .regvalue = 0 }, |
| { .rate = 11025, .regvalue = 1 }, |
| { .rate = 12000, .regvalue = 2 }, |
| { .rate = 16000, .regvalue = 3 }, |
| { .rate = 22050, .regvalue = 4 }, |
| { .rate = 24000, .regvalue = 5 }, |
| { .rate = 32000, .regvalue = 6 }, |
| { .rate = 44100, .regvalue = 7 }, |
| { .rate = 48000, .regvalue = 8 }, |
| }; |
| |
| static int mt8167_codec_ul_rate_to_val(struct mt8167_codec_priv *codec_data, |
| int rate) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(mt8167_codec_ul_rates); i++) |
| if (mt8167_codec_ul_rates[i].rate == rate) |
| return mt8167_codec_ul_rates[i].regvalue; |
| |
| dev_err(codec_data->codec->dev, "%s unsupported ul rate %d\n", |
| __func__, rate); |
| |
| return -EINVAL; |
| } |
| |
| static int mt8167_codec_dl_rate_to_val(struct mt8167_codec_priv *codec_data, |
| int rate) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(mt8167_codec_dl_rates); i++) |
| if (mt8167_codec_dl_rates[i].rate == rate) |
| return mt8167_codec_dl_rates[i].regvalue; |
| |
| dev_err(codec_data->codec->dev, "%s unsupported dl rate %d\n", |
| __func__, rate); |
| |
| return -EINVAL; |
| } |
| |
| static int mt8167_codec_valid_new_rate(struct mt8167_codec_priv *codec_data) |
| { |
| uint32_t abb_afe_con11_val = 0; |
| |
| abb_afe_con11_val = ABB_AFE_CON11_TOP_CTRL; |
| |
| /* toggle top_ctrl status */ |
| if (snd_soc_component_read32(codec_data->codec, ABB_AFE_CON11) & |
| ABB_AFE_CON11_TOP_CTRL_STATUS) |
| snd_soc_component_update_bits(codec_data->codec, |
| ABB_AFE_CON11, |
| ABB_AFE_CON11_TOP_CTRL, 0x0); |
| else |
| snd_soc_component_update_bits(codec_data->codec, |
| ABB_AFE_CON11, |
| ABB_AFE_CON11_TOP_CTRL, abb_afe_con11_val); |
| |
| return 0; |
| } |
| |
| static int mt8167_codec_setup_ul_rate(struct snd_soc_dai *codec_dai, int rate) |
| { |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(codec_dai->component); |
| uint32_t val = 0; |
| |
| if (mt8167_codec_ul_rate_to_val(codec_data, rate) < 0) { |
| dev_err(codec_dai->component->dev, |
| "%s error to get ul rate\n", __func__); |
| return -EINVAL; |
| } |
| |
| val = mt8167_codec_ul_rate_to_val(codec_data, rate); |
| snd_soc_component_update_bits(codec_data->codec, |
| ABB_AFE_CON1, (0xF << 4), (val << 4)); |
| mt8167_codec_valid_new_rate(codec_data); |
| |
| return 0; |
| } |
| |
| static int mt8167_codec_setup_dl_rate(struct snd_soc_dai *codec_dai, int rate) |
| { |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(codec_dai->component); |
| uint32_t val = 0; |
| |
| if (mt8167_codec_dl_rate_to_val(codec_data, rate) < 0) { |
| dev_err(codec_dai->component->dev, |
| "%s error to get dl rate\n", __func__); |
| return -EINVAL; |
| } |
| |
| val = mt8167_codec_dl_rate_to_val(codec_data, rate); |
| snd_soc_component_update_bits(codec_data->codec, |
| ABB_AFE_CON1, GENMASK(3, 0), val); |
| mt8167_codec_valid_new_rate(codec_data); |
| |
| return 0; |
| } |
| |
| static int mt8167_codec_valid_new_dc_comp( |
| struct mt8167_codec_priv *codec_data) |
| { |
| /* toggle DC status */ |
| if (snd_soc_component_read32(codec_data->codec, ABB_AFE_CON11) & |
| ABB_AFE_CON11_DC_CTRL_STATUS) |
| snd_soc_component_update_bits(codec_data->codec, |
| ABB_AFE_CON11, |
| ABB_AFE_CON11_DC_CTRL, 0); |
| else |
| snd_soc_component_update_bits(codec_data->codec, |
| ABB_AFE_CON11, |
| ABB_AFE_CON11_DC_CTRL, ABB_AFE_CON11_DC_CTRL); |
| |
| return 0; |
| } |
| |
| static void mt8167_codec_setup_dc_comp(struct snd_soc_dai *codec_dai) |
| { |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(codec_dai->component); |
| |
| /* L-ch DC compensation value */ |
| snd_soc_component_update_bits(codec_data->codec, |
| ABB_AFE_CON3, GENMASK(15, 0), |
| codec_data->lch_dccomp_val); |
| /* R-ch DC compensation value */ |
| snd_soc_component_update_bits(codec_data->codec, |
| ABB_AFE_CON4, GENMASK(15, 0), |
| codec_data->rch_dccomp_val); |
| /* DC compensation enable */ |
| snd_soc_component_update_bits(codec_data->codec, ABB_AFE_CON10, 0x1, 0x1); |
| mt8167_codec_valid_new_dc_comp(codec_data); |
| } |
| |
| static int mt8167_codec_hw_params(struct snd_pcm_substream *substream, |
| struct snd_pcm_hw_params *params, |
| struct snd_soc_dai *codec_dai) |
| { |
| int ret = 0; |
| int rate = params_rate(params); |
| |
| if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
| dev_dbg(codec_dai->component->dev, |
| "%s capture rate = %d\n", __func__, rate); |
| ret = mt8167_codec_setup_ul_rate(codec_dai, rate); |
| } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
| dev_dbg(codec_dai->component->dev, |
| "%s playback rate = %d\n", __func__, rate); |
| ret = mt8167_codec_setup_dl_rate(codec_dai, rate); |
| mt8167_codec_setup_dc_comp(codec_dai); |
| } |
| |
| return ret; |
| } |
| |
| static int mt8167_codec_hw_free(struct snd_pcm_substream *substream, |
| struct snd_soc_dai *codec_dai) |
| { |
| dev_dbg(codec_dai->component->dev, "%s\n", __func__); |
| return 0; |
| } |
| |
| static int mt8167_codec_prepare(struct snd_pcm_substream *substream, |
| struct snd_soc_dai *codec_dai) |
| { |
| dev_err(codec_dai->component->dev, "%s\n", __func__); |
| return 0; |
| } |
| |
| static int mt8167_codec_dl_ul_enable(struct snd_pcm_substream *substream, |
| struct snd_soc_dai *codec_dai) |
| { |
| if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) |
| snd_soc_component_update_bits(codec_dai->component, |
| ABB_AFE_CON0, BIT(1), BIT(1)); |
| else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| snd_soc_component_update_bits(codec_dai->component, |
| ABB_AFE_CON0, BIT(0), BIT(0)); |
| |
| return 0; |
| } |
| |
| static int mt8167_codec_dl_ul_disable(struct snd_pcm_substream *substream, |
| struct snd_soc_dai *codec_dai) |
| { |
| if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) |
| snd_soc_component_update_bits(codec_dai->component, |
| ABB_AFE_CON0, BIT(1), 0x0); |
| else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| snd_soc_component_update_bits(codec_dai->component, |
| ABB_AFE_CON0, BIT(0), 0x0); |
| |
| return 0; |
| } |
| |
| static int mt8167_codec_trigger(struct snd_pcm_substream *substream, |
| int command, |
| struct snd_soc_dai *codec_dai) |
| { |
| switch (command) { |
| case SNDRV_PCM_TRIGGER_START: |
| case SNDRV_PCM_TRIGGER_RESUME: |
| mt8167_codec_dl_ul_enable(substream, codec_dai); |
| break; |
| case SNDRV_PCM_TRIGGER_STOP: |
| case SNDRV_PCM_TRIGGER_SUSPEND: |
| mt8167_codec_dl_ul_disable(substream, codec_dai); |
| break; |
| } |
| dev_err(codec_dai->component->dev, "%s command = %d\n ", |
| __func__, command); |
| return 0; |
| } |
| |
| static const struct snd_soc_dai_ops mt8167_codec_aif_dai_ops = { |
| .startup = mt8167_codec_startup, |
| .shutdown = mt8167_codec_shutdown, |
| .hw_params = mt8167_codec_hw_params, |
| .hw_free = mt8167_codec_hw_free, |
| .prepare = mt8167_codec_prepare, |
| .trigger = mt8167_codec_trigger, |
| }; |
| |
| #define MT8167_CODEC_DL_RATES SNDRV_PCM_RATE_8000_48000 |
| #define MT8167_CODEC_UL_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ |
| SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) |
| |
| static struct snd_soc_dai_driver mt8167_codec_dai = { |
| .name = "mt8167-codec-dai", |
| .ops = &mt8167_codec_aif_dai_ops, |
| .playback = { |
| .stream_name = "Playback", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = MT8167_CODEC_DL_RATES, |
| .formats = SNDRV_PCM_FMTBIT_S16_LE, |
| }, |
| .capture = { |
| .stream_name = "Capture", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = MT8167_CODEC_UL_RATES, |
| .formats = SNDRV_PCM_FMTBIT_S16_LE, |
| }, |
| }; |
| |
| static int mt8167_codec_dmic_event(struct snd_soc_dapm_widget *w, |
| struct snd_kcontrol *kcontrol, int event) |
| { |
| struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); |
| struct mt8167_codec_priv *codec_data = snd_soc_component_get_drvdata(codec); |
| uint32_t abb_afe_con9_val = 0; |
| uint32_t audio_codec_con03_val = 0; |
| |
| if (codec_data->dmic_wire_mode == DMIC_TWO_WIRE) |
| abb_afe_con9_val |= |
| ABB_AFE_CON9_TWO_WIRE_EN; |
| |
| if (codec_data->dmic_rate_mode == DMIC_RATE_D3P25M) |
| abb_afe_con9_val |= |
| ABB_AFE_CON9_D3P25M_SEL; |
| |
| abb_afe_con9_val |= |
| (codec_data->dmic_ch_phase[DMIC_L_CH] << 13); |
| abb_afe_con9_val |= |
| (codec_data->dmic_ch_phase[DMIC_R_CH] << 10); |
| |
| abb_afe_con9_val |= |
| ABB_AFE_CON9_DIG_MIC_EN; |
| |
| audio_codec_con03_val = |
| AUDIO_CODEC_CON03_SLEW_RATE_10 | |
| AUDIO_CODEC_CON03_DIG_MIC_EN; |
| |
| dev_dbg(codec->dev, "%s, event %d\n", __func__, event); |
| |
| switch (event) { |
| case SND_SOC_DAPM_POST_PMU: |
| snd_soc_component_update_bits(codec, ABB_AFE_CON9, |
| abb_afe_con9_val, abb_afe_con9_val); |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON03, |
| audio_codec_con03_val, audio_codec_con03_val); |
| break; |
| case SND_SOC_DAPM_PRE_PMD: |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON03, |
| AUDIO_CODEC_CON03_DIG_MIC_EN, 0x0); |
| snd_soc_component_update_bits(codec, ABB_AFE_CON9, |
| ABB_AFE_CON9_DIG_MIC_EN, 0x0); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static int mt8167_codec_left_audio_amp_event(struct snd_soc_dapm_widget *w, |
| struct snd_kcontrol *kcontrol, int event) |
| { |
| struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); |
| struct mt8167_codec_priv *codec_data = snd_soc_component_get_drvdata(codec); |
| |
| dev_dbg(codec->dev, "%s, event %d\n", __func__, event); |
| |
| switch (event) { |
| case SND_SOC_DAPM_PRE_PMU: |
| /* store gain */ |
| codec_data->pga_gain[HP_L_PGA_GAIN] = |
| snd_soc_component_read32(codec, AUDIO_CODEC_CON01) & |
| GENMASK(2, 0); |
| /* set to small gain for depop sequence */ |
| snd_soc_component_update_bits(codec, |
| AUDIO_CODEC_CON01, GENMASK(2, 0), 0x0); |
| /* disable input short (left audio amp only) */ |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON02, |
| AUDIO_CODEC_CON02_ABUF_INSHORT, 0x0); |
| break; |
| case SND_SOC_DAPM_POST_PMD: |
| /* enable input short to prevent signal leakage from L-DAC */ |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON02, |
| AUDIO_CODEC_CON02_ABUF_INSHORT, |
| AUDIO_CODEC_CON02_ABUF_INSHORT); |
| /* restore gain */ |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON01, |
| GENMASK(2, 0), |
| codec_data->pga_gain[HP_L_PGA_GAIN]); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static int mt8167_codec_right_audio_amp_event(struct snd_soc_dapm_widget *w, |
| struct snd_kcontrol *kcontrol, int event) |
| { |
| struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); |
| struct mt8167_codec_priv *codec_data = snd_soc_component_get_drvdata(codec); |
| |
| dev_dbg(codec->dev, "%s, event %d\n", __func__, event); |
| |
| switch (event) { |
| case SND_SOC_DAPM_PRE_PMU: |
| /* store gain */ |
| codec_data->pga_gain[HP_R_PGA_GAIN] = |
| (snd_soc_component_read32(codec, AUDIO_CODEC_CON01) & |
| GENMASK(5, 3)) >> 3; |
| /* set to small gain for depop sequence */ |
| snd_soc_component_update_bits(codec, |
| AUDIO_CODEC_CON01, GENMASK(5, 3), 0x0); |
| break; |
| case SND_SOC_DAPM_POST_PMD: |
| /* restore gain */ |
| snd_soc_component_update_bits(codec, |
| AUDIO_CODEC_CON01, GENMASK(5, 3), |
| (codec_data->pga_gain[HP_R_PGA_GAIN]) << 3); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static int mt8167_codec_voice_amp_event(struct snd_soc_dapm_widget *w, |
| struct snd_kcontrol *kcontrol, int event) |
| { |
| struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); |
| struct mt8167_codec_priv *codec_data = snd_soc_component_get_drvdata(codec); |
| |
| dev_dbg(codec->dev, "%s, event %d\n", __func__, event); |
| |
| switch (event) { |
| case SND_SOC_DAPM_PRE_PMU: |
| /* store gain */ |
| codec_data->pga_gain[LOUT_PGA_GAIN] = |
| (snd_soc_component_read32(codec, AUDIO_CODEC_CON02) & |
| GENMASK(12, 9)) >> 9; |
| /* set to small gain for depop sequence */ |
| snd_soc_component_update_bits(codec, |
| AUDIO_CODEC_CON02, GENMASK(12, 9), 0x0); |
| break; |
| case SND_SOC_DAPM_POST_PMU: |
| /* restore gain (fade in ?) */ |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON02, |
| GENMASK(12, 9), |
| (codec_data->pga_gain[LOUT_PGA_GAIN]) << 9); |
| break; |
| case SND_SOC_DAPM_PRE_PMD: |
| /* store gain */ |
| codec_data->pga_gain[LOUT_PGA_GAIN] = |
| (snd_soc_component_read32(codec, AUDIO_CODEC_CON02) & |
| GENMASK(12, 9)) >> 9; |
| /* set to small gain for depop sequence (fade out ?) */ |
| snd_soc_component_update_bits(codec, |
| AUDIO_CODEC_CON02, GENMASK(12, 9), 0x0); |
| break; |
| case SND_SOC_DAPM_POST_PMD: |
| /* restore gain */ |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON02, |
| GENMASK(12, 9), |
| (codec_data->pga_gain[LOUT_PGA_GAIN]) << 9); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* CODEC_CLK */ |
| static int mt8167_codec_clk_event(struct snd_soc_dapm_widget *w, |
| struct snd_kcontrol *kcontrol, int event) |
| { |
| struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); |
| |
| dev_dbg(codec->dev, "%s, event %d\n", __func__, event); |
| |
| switch (event) { |
| case SND_SOC_DAPM_PRE_PMU: |
| /* CLKLDO power on */ |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON01, BIT(20), BIT(20)); |
| /* Audio Codec CLK on */ |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON03, BIT(30), BIT(30)); |
| break; |
| case SND_SOC_DAPM_POST_PMD: |
| /* Audio Codec CLK off */ |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON03, BIT(30), 0x0); |
| /* CLKLDO power off */ |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON01, BIT(20), 0x0); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* 1.4V common mode voltage */ |
| static int mt8167_codec_vcm14_event(struct snd_soc_dapm_widget *w, |
| struct snd_kcontrol *kcontrol, int event) |
| { |
| struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); |
| |
| dev_dbg(codec->dev, "%s, event %d\n", __func__, event); |
| |
| switch (event) { |
| case SND_SOC_DAPM_PRE_PMU: |
| snd_soc_component_update_bits(codec, |
| AUDIO_CODEC_CON00, |
| AUDIO_CODEC_CON00_AUDULL_VCM14_EN, |
| AUDIO_CODEC_CON00_AUDULL_VCM14_EN); |
| snd_soc_component_update_bits(codec, |
| AUDIO_CODEC_CON00, |
| AUDIO_CODEC_CON00_AUDULR_VCM14_EN, |
| AUDIO_CODEC_CON00_AUDULR_VCM14_EN); |
| break; |
| case SND_SOC_DAPM_POST_PMD: |
| snd_soc_component_update_bits(codec, |
| AUDIO_CODEC_CON00, |
| AUDIO_CODEC_CON00_AUDULL_VCM14_EN, 0x0); |
| snd_soc_component_update_bits(codec, |
| AUDIO_CODEC_CON00, |
| AUDIO_CODEC_CON00_AUDULR_VCM14_EN, 0x0); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* Uplink 2.4V differential reference */ |
| static int mt8167_codec_ul_vref24_event(struct snd_soc_dapm_widget *w, |
| struct snd_kcontrol *kcontrol, int event) |
| { |
| struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); |
| |
| dev_dbg(codec->dev, "%s, event %d\n", __func__, event); |
| |
| switch (event) { |
| case SND_SOC_DAPM_PRE_PMU: |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON00, |
| AUDIO_CODEC_CON00_AUDULL_VREF24_EN, |
| AUDIO_CODEC_CON00_AUDULL_VREF24_EN); |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON00, |
| AUDIO_CODEC_CON00_AUDULR_VREF24_EN, |
| AUDIO_CODEC_CON00_AUDULR_VREF24_EN); |
| break; |
| case SND_SOC_DAPM_POST_PMD: |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON00, |
| AUDIO_CODEC_CON00_AUDULL_VREF24_EN, 0x0); |
| snd_soc_component_update_bits(codec, AUDIO_CODEC_CON00, |
| AUDIO_CODEC_CON00_AUDULR_VREF24_EN, 0x0); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static void mt8167_codec_hp_depop_setup( |
| struct mt8167_codec_priv *codec_data) |
| { |
| /* Set audio DAC bias current */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON01, (0x1F << 6), 0x0); |
| /* Set the charge option of depop VCM gen. to "charge type" */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON02, BIT(18), BIT(18)); |
| /* Set the 22uA current step of depop VCM gen. to charge 22uF cap. */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON02, GENMASK(20, 19), (0x3 << 19));//47uf |
| /* Set the depop VCM voltage of depop VCM gen. to 1.35V. */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON02, BIT(21), 0x0); |
| /* Enable the depop VCM generator. */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON02, BIT(22), BIT(22)); |
| /* Set the series resistor of depop mux of HP drivers to 62.5 Ohm. */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON02, (0x3 << 23), (0x3 << 23)); |
| /* Disable audio DAC clock. */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON02, BIT(27), 0x0); |
| /* Enable the depop mux of HP drivers. */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON02, BIT(25), BIT(25)); |
| } |
| |
| static void mt8167_codec_hp_depop_cleanup( |
| struct mt8167_codec_priv *codec_data) |
| { |
| /* Set the charge option of depop VCM gen. to "dis-charge type" */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON02, BIT(18), 0x0); |
| /* Set the 33uF cap current step of depop VCM gen to dis-charge */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON02, (0x2 << 19), (0x2 << 19)); |
| /* Disable the depop VCM generator */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON02, BIT(22), 0x0); |
| } |
| |
| /* PRE_PMD */ |
| static void mt8167_codec_hp_depop_enable( |
| struct mt8167_codec_priv *codec_data) |
| { |
| /* store gain */ |
| codec_data->pga_gain[HP_L_PGA_GAIN] = |
| snd_soc_component_read32(codec_data->codec, AUDIO_CODEC_CON01) & |
| GENMASK(2, 0); |
| codec_data->pga_gain[HP_R_PGA_GAIN] = |
| (snd_soc_component_read32(codec_data->codec, AUDIO_CODEC_CON01) & |
| GENMASK(5, 3)) >> 3; |
| /* set to small gain for depop sequence */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON01, GENMASK(2, 0), 0x0); |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON01, GENMASK(5, 3), 0x0); |
| /* Reset HP Pre-charge function */ |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON02, |
| BIT(28), 0x0); |
| /* Enable the depop mux of HP drivers */ |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON02, |
| BIT(25), BIT(25)); |
| /* Enable depop VCM gen */ |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON02, |
| BIT(22), BIT(22)); |
| } |
| |
| /* POST_PMU */ |
| static void mt8167_codec_hp_depop_disable( |
| struct mt8167_codec_priv *codec_data) |
| { |
| /* HP Pre-charge function release */ |
| usleep_range(10000, 11000); |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON02, |
| BIT(28), BIT(28)); |
| /* Disable the depop mux of HP drivers */ |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON02, |
| BIT(25), 0x0); |
| /* Disable the depop VCM gen */ |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON02, |
| BIT(22), 0x0); |
| /* restore gain */ |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON01, |
| GENMASK(2, 0), |
| codec_data->pga_gain[HP_L_PGA_GAIN]); |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON01, |
| GENMASK(5, 3), |
| (codec_data->pga_gain[HP_R_PGA_GAIN]) << 3); |
| } |
| |
| static int mt8167_codec_depop_vcm_event(struct snd_soc_dapm_widget *w, |
| struct snd_kcontrol *kcontrol, int event) |
| { |
| struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); |
| struct mt8167_codec_priv *codec_data = snd_soc_component_get_drvdata(codec); |
| |
| dev_dbg(codec->dev, "%s, event %d\n", __func__, event); |
| |
| switch (event) { |
| case SND_SOC_DAPM_POST_PMU: |
| mt8167_codec_hp_depop_disable(codec_data); |
| break; |
| case SND_SOC_DAPM_PRE_PMD: |
| mt8167_codec_hp_depop_enable(codec_data); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* AIF DL_UL loopback Switch */ |
| static int mt8167_codec_aif_dl_ul_lpbk_event(struct snd_soc_dapm_widget *w, |
| struct snd_kcontrol *kcontrol, int event) |
| { |
| struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); |
| |
| dev_dbg(codec->dev, "%s, event %d\n", __func__, event); |
| |
| switch (event) { |
| case SND_SOC_DAPM_POST_PMU: |
| /* Enable downlink data loopback to uplink */ |
| snd_soc_component_update_bits(codec, ABB_AFE_CON2, BIT(3), BIT(3)); |
| break; |
| case SND_SOC_DAPM_PRE_PMD: |
| /* Disable downlink data loopback to uplink */ |
| snd_soc_component_update_bits(codec, ABB_AFE_CON2, BIT(3), 0x0); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* DMIC Data Gen Switch */ |
| static int mt8167_codec_dmic_data_gen_event(struct snd_soc_dapm_widget *w, |
| struct snd_kcontrol *kcontrol, int event) |
| { |
| struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); |
| |
| dev_dbg(codec->dev, "%s, event %d\n", __func__, event); |
| |
| switch (event) { |
| case SND_SOC_DAPM_POST_PMU: |
| /* ul_dmic_debug_ch1/2 data 1 */ |
| snd_soc_component_update_bits(codec, ABB_AFE_CON1, |
| GENMASK(13, 12), (0x3 << 12)); |
| /* ul_dmic_debug_en (enable) */ |
| snd_soc_component_update_bits(codec, ABB_AFE_CON1, |
| BIT(8), BIT(8)); |
| break; |
| case SND_SOC_DAPM_PRE_PMD: |
| /* ul_dmic_debug_ch1/2 data 0 */ |
| snd_soc_component_update_bits(codec, ABB_AFE_CON1, |
| GENMASK(13, 12), 0x0); |
| /* ul_dmic_debug_en (disable) */ |
| snd_soc_component_update_bits(codec, ABB_AFE_CON1, |
| BIT(8), 0x0); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* AMIC Data Gen Switch */ |
| static int mt8167_codec_amic_data_gen_event(struct snd_soc_dapm_widget *w, |
| struct snd_kcontrol *kcontrol, int event) |
| { |
| struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); |
| |
| dev_dbg(codec->dev, "%s, event %d\n", __func__, event); |
| |
| switch (event) { |
| case SND_SOC_DAPM_POST_PMU: |
| /* ul_amic_debug_ch1/2 data 1 */ |
| snd_soc_component_update_bits(codec, ABB_AFE_CON1, |
| GENMASK(15, 14), (0x3 << 14)); |
| /* ul_amic_debug_en (enable) */ |
| snd_soc_component_update_bits(codec, ABB_AFE_CON1, |
| BIT(9), BIT(9)); |
| break; |
| case SND_SOC_DAPM_PRE_PMD: |
| /* ul_amic_debug_ch1/2 data 0 */ |
| snd_soc_component_update_bits(codec, ABB_AFE_CON1, |
| GENMASK(15, 14), 0x0); |
| /* ul_amic_debug_en (disable) */ |
| snd_soc_component_update_bits(codec, ABB_AFE_CON1, |
| BIT(9), 0x0); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* SDM Tone Gen Switch */ |
| static int mt8167_codec_sdm_tone_gen_event(struct snd_soc_dapm_widget *w, |
| struct snd_kcontrol *kcontrol, int event) |
| { |
| struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); |
| |
| dev_dbg(codec->dev, "%s, event %d\n", __func__, event); |
| |
| switch (event) { |
| case SND_SOC_DAPM_POST_PMU: |
| /* tri_amp_div */ |
| snd_soc_component_update_bits(codec, ABB_AFE_SDM_TEST, |
| GENMASK(14, 12), (7 << 12)); |
| /* tri_freq_div */ |
| snd_soc_component_update_bits(codec, ABB_AFE_SDM_TEST, |
| GENMASK(9, 4), (1 << 4)); |
| /* abb_sdm_src_sel_ctl (Triangular tone) */ |
| snd_soc_component_update_bits(codec, ABB_AFE_SDM_TEST, |
| BIT(2), BIT(2)); |
| /* tri_mute_sw (Unmute trigen) */ |
| snd_soc_component_update_bits(codec, ABB_AFE_SDM_TEST, |
| BIT(1), 0x0); |
| /* tri_dac_en (Enable trigen) */ |
| snd_soc_component_update_bits(codec, ABB_AFE_SDM_TEST, |
| BIT(0), BIT(0)); |
| break; |
| case SND_SOC_DAPM_PRE_PMD: |
| /* tri_mute_sw (Mute trigen) */ |
| snd_soc_component_update_bits(codec, ABB_AFE_SDM_TEST, |
| BIT(1), BIT(1)); |
| /* tri_dac_en (Disable trigen) */ |
| snd_soc_component_update_bits(codec, ABB_AFE_SDM_TEST, |
| BIT(0), 0x0); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* Audio Amp Playback Volume |
| * {-2, 0, +2, +4, +6, +8, +10, +12} dB |
| */ |
| static const unsigned int dl_audio_amp_gain_tlv[] = { |
| TLV_DB_RANGE_HEAD(1), |
| 0, 7, TLV_DB_SCALE_ITEM(-200, 200, 0), |
| }; |
| |
| /* Voice Amp Playback Volume |
| * {-18, -16, -14, -12, -10, ..., +12} dB |
| */ |
| static const unsigned int dl_voice_amp_gain_tlv[] = { |
| TLV_DB_RANGE_HEAD(1), |
| 0, 15, TLV_DB_SCALE_ITEM(-1800, 200, 0), |
| }; |
| |
| /* PGA Capture Volume |
| * {-6, 0, +6, +12, +18, +24} dB |
| */ |
| static const unsigned int ul_pga_gain_tlv[] = { |
| TLV_DB_RANGE_HEAD(1), |
| 0, 5, TLV_DB_SCALE_ITEM(-600, 600, 0), |
| }; |
| |
| /* Headset_PGAL_GAIN |
| * Headset_PGAR_GAIN |
| * {-2, 0, +2, +4, +6, +8, +10, +12} dB |
| */ |
| static const char *const headset_pga_gain_text[] = { |
| "-2dB", "+0dB", "+2dB", "+4dB", |
| "+6dB", "+8dB", "+10dB", "+12dB", |
| }; |
| |
| /* Lineout_PGA_GAIN |
| * {-18, -16, -14, -12, -10, ..., +12} dB |
| */ |
| static const char *const lineout_pga_gain_text[] = { |
| "-18dB", "-16dB", "-14dB", "-12dB", |
| "-10dB", "-8dB", "-6dB", "-4dB", |
| "-2dB", "+0dB", "+2dB", "+4dB", |
| "+6dB", "+8dB", "+10dB", "+12dB", |
| }; |
| |
| /* Audio_PGA1_Setting |
| * Audio_PGA2_Setting |
| * {-6, 0, +6, +12, +18, +24} dB |
| */ |
| static const char *const ul_pga_gain_text[] = { |
| "-6dB", "+0dB", "+6dB", "+12dB", "+18dB", "+24dB" |
| }; |
| |
| static const struct soc_enum mt8167_codec_pga_gain_enums[] = { |
| SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(headset_pga_gain_text), |
| headset_pga_gain_text), |
| SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(headset_pga_gain_text), |
| headset_pga_gain_text), |
| SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lineout_pga_gain_text), |
| lineout_pga_gain_text), |
| SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ul_pga_gain_text), |
| ul_pga_gain_text), |
| SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ul_pga_gain_text), |
| ul_pga_gain_text), |
| }; |
| |
| static int mt8167_codec_get_gain_enum_id(const char *name) |
| { |
| if (!strcmp(name, "Headset_PGAL_GAIN")) |
| return HP_L_PGA_GAIN; |
| if (!strcmp(name, "Headset_PGAR_GAIN")) |
| return HP_R_PGA_GAIN; |
| if (!strcmp(name, "Lineout_PGA_GAIN")) |
| return LOUT_PGA_GAIN; |
| if (!strcmp(name, "Audio_PGA1_Setting")) |
| return UL_L_PGA_GAIN; |
| if (!strcmp(name, "Audio_PGA2_Setting")) |
| return UL_R_PGA_GAIN; |
| return -EINVAL; |
| } |
| |
| static int mt8167_codec_pga_gain_get(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| int id = mt8167_codec_get_gain_enum_id(kcontrol->id.name); |
| uint32_t value = 0; |
| |
| switch (id) { |
| case HP_L_PGA_GAIN: |
| case HP_R_PGA_GAIN: |
| case LOUT_PGA_GAIN: |
| case UL_L_PGA_GAIN: |
| case UL_R_PGA_GAIN: |
| value = codec_data->pga_gain[id]; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| ucontrol->value.integer.value[0] = value; |
| |
| return 0; |
| } |
| |
| static int mt8167_codec_pga_gain_put(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
| int id = mt8167_codec_get_gain_enum_id(kcontrol->id.name); |
| uint32_t value = ucontrol->value.integer.value[0]; |
| |
| if (value >= e->items) |
| return -EINVAL; |
| |
| dev_dbg(codec_data->codec->dev, |
| "%s id %d, value %u\n", __func__, id, value); |
| |
| switch (id) { |
| case HP_L_PGA_GAIN: |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON01, |
| GENMASK(2, 0), value); |
| break; |
| case HP_R_PGA_GAIN: |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON01, |
| GENMASK(5, 3), value << 3); |
| break; |
| case LOUT_PGA_GAIN: |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON02, |
| GENMASK(12, 9), value << 9); |
| break; |
| case UL_L_PGA_GAIN: |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON00, |
| GENMASK(27, 25), value << 25); |
| break; |
| case UL_R_PGA_GAIN: |
| snd_soc_component_update_bits(codec_data->codec, AUDIO_CODEC_CON00, |
| GENMASK(9, 7), value << 7); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| codec_data->pga_gain[id] = value; |
| |
| return 0; |
| } |
| |
| /* HPL Calibration */ |
| static int mt8167_codec_hpl_dc_comp_get(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| |
| dev_dbg(codec_data->codec->dev, "%s\n", __func__); |
| |
| if (!codec_data->is_lch_dc_calibrated) { |
| mt8167_codec_get_hpl_cali_val(codec_data->codec, |
| &codec_data->lch_dccomp_val, |
| &codec_data->lch_dc_offset); |
| codec_data->is_lch_dc_calibrated = true; |
| } |
| ucontrol->value.integer.value[0] = codec_data->lch_dc_offset; |
| return 0; |
| } |
| |
| static int mt8167_codec_hpl_dc_comp_put(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| |
| dev_dbg(codec_data->codec->dev, "%s\n", __func__); |
| |
| codec_data->lch_dc_offset = ucontrol->value.integer.value[0]; |
| codec_data->lch_dccomp_val = |
| mt8167_codec_conv_dc_offset_to_comp_val( |
| codec_data->lch_dc_offset); |
| return 0; |
| } |
| |
| /* HPR Calibration */ |
| static int mt8167_codec_hpr_dc_comp_get(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| |
| dev_dbg(codec_data->codec->dev, "%s\n", __func__); |
| if (!codec_data->is_rch_dc_calibrated) { |
| mt8167_codec_get_hpr_cali_val(codec_data->codec, |
| &codec_data->rch_dccomp_val, |
| &codec_data->rch_dc_offset); |
| codec_data->is_rch_dc_calibrated = true; |
| } |
| ucontrol->value.integer.value[0] = codec_data->rch_dc_offset; |
| return 0; |
| } |
| |
| static int mt8167_codec_hpr_dc_comp_put(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| |
| dev_dbg(codec_data->codec->dev, "%s\n", __func__); |
| |
| codec_data->rch_dc_offset = ucontrol->value.integer.value[0]; |
| codec_data->rch_dccomp_val = |
| mt8167_codec_conv_dc_offset_to_comp_val( |
| codec_data->rch_dc_offset); |
| |
| return 0; |
| } |
| |
| /* UL to DL loopback (Codec_Loopback_Select) */ |
| #define ENUM_TO_STR(enum) #enum |
| static const char * const mt8167_codec_loopback_text[] = { |
| ENUM_TO_STR(CODEC_LOOPBACK_NONE), |
| ENUM_TO_STR(CODEC_LOOPBACK_AMIC_TO_SPK), |
| ENUM_TO_STR(CODEC_LOOPBACK_AMIC_TO_HP), |
| ENUM_TO_STR(CODEC_LOOPBACK_DMIC_TO_SPK), |
| ENUM_TO_STR(CODEC_LOOPBACK_DMIC_TO_HP), |
| ENUM_TO_STR(CODEC_LOOPBACK_HEADSET_MIC_TO_SPK), |
| ENUM_TO_STR(CODEC_LOOPBACK_HEADSET_MIC_TO_HP), |
| }; |
| |
| static const struct soc_enum mt8167_codec_loopback_enum = |
| SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8167_codec_loopback_text), |
| mt8167_codec_loopback_text); |
| |
| static int mt8167_codec_loopback_get(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| |
| dev_dbg(codec_data->codec->dev, "%s\n", __func__); |
| ucontrol->value.integer.value[0] = codec_data->loopback_type; |
| return 0; |
| } |
| |
| static int mt8167_codec_loopback_put(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| uint32_t prev_lpbk_type = codec_data->loopback_type; |
| uint32_t next_lpbk_type = ucontrol->value.integer.value[0]; |
| |
| dev_dbg(codec_data->codec->dev, "%s\n", __func__); |
| |
| if (next_lpbk_type == prev_lpbk_type) { |
| dev_dbg(codec_data->codec->dev, "%s dummy action\n", __func__); |
| return 0; |
| } |
| |
| if (prev_lpbk_type != CODEC_LOOPBACK_NONE) |
| mt8167_codec_turn_off_lpbk_path( |
| codec_data->codec, prev_lpbk_type); |
| if (next_lpbk_type != CODEC_LOOPBACK_NONE) |
| mt8167_codec_turn_on_lpbk_path( |
| codec_data->codec, next_lpbk_type); |
| |
| codec_data->loopback_type = ucontrol->value.integer.value[0]; |
| return 0; |
| } |
| |
| /* Codec_DL_Switch */ |
| static const char * const mt8167_codec_dl_switch_text[] = { "Off", "On" }; |
| |
| static const struct soc_enum mt8167_codec_dl_switch_enum = |
| SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8167_codec_dl_switch_text), |
| mt8167_codec_dl_switch_text); |
| |
| static int mt8167_codec_dl_switch_get(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| |
| dev_dbg(codec_data->codec->dev, "%s\n", __func__); |
| ucontrol->value.integer.value[0] = codec_data->dl_en; |
| return 0; |
| } |
| |
| static int mt8167_codec_dl_switch_put(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| uint32_t prev_dl_en = codec_data->dl_en; |
| uint32_t next_dl_en = ucontrol->value.integer.value[0]; |
| |
| dev_dbg(codec_data->codec->dev, "%s\n", __func__); |
| |
| if (next_dl_en == prev_dl_en) { |
| dev_dbg(codec_data->codec->dev, "%s dummy action\n", __func__); |
| return 0; |
| } |
| |
| if (prev_dl_en) |
| /* turn off */ |
| snd_soc_component_update_bits(codec_data->codec, |
| ABB_AFE_CON0, BIT(0), 0x0); |
| else |
| /* turn on */ |
| snd_soc_component_update_bits(codec_data->codec, |
| ABB_AFE_CON0, BIT(0), BIT(0)); |
| |
| codec_data->dl_en = next_dl_en; |
| return 0; |
| } |
| |
| static int mt8167_codec_dmic_ch_phase_get(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| |
| ucontrol->value.integer.value[0] = codec_data->dmic_ch_phase[DMIC_L_CH]; |
| ucontrol->value.integer.value[1] = codec_data->dmic_ch_phase[DMIC_R_CH]; |
| return 0; |
| } |
| |
| static int mt8167_codec_dmic_ch_phase_put(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| |
| codec_data->dmic_ch_phase[DMIC_L_CH] = ucontrol->value.integer.value[0]; |
| codec_data->dmic_ch_phase[DMIC_R_CH] = ucontrol->value.integer.value[1]; |
| return 0; |
| } |
| |
| static int mt8167_codec_dmic_rate_mode_get(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| |
| ucontrol->value.integer.value[0] = codec_data->dmic_rate_mode; |
| return 0; |
| } |
| |
| static int mt8167_codec_dmic_rate_mode_put(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); |
| struct mt8167_codec_priv *codec_data = |
| snd_soc_component_get_drvdata(component); |
| |
| codec_data->dmic_rate_mode = ucontrol->value.integer.value[0]; |
| return 0; |
| } |
| |
| static const struct snd_kcontrol_new mt8167_codec_controls[] = { |
| /* DL Audio amplifier gain adjustment */ |
| SOC_DOUBLE_TLV("Audio Amp Playback Volume", |
| AUDIO_CODEC_CON01, 0, 3, 7, 0, |
| dl_audio_amp_gain_tlv), |
| /* DL Voice amplifier gain adjustment */ |
| SOC_SINGLE_TLV("Voice Amp Playback Volume", |
| AUDIO_CODEC_CON02, 9, 15, 0, |
| dl_voice_amp_gain_tlv), |
| /* UL PGA gain adjustment */ |
| SOC_DOUBLE_TLV("PGA Capture Volume", |
| AUDIO_CODEC_CON00, 25, 7, 5, 0, |
| ul_pga_gain_tlv), |
| /* Headset_PGAL_GAIN */ |
| SOC_ENUM_EXT("Headset_PGAL_GAIN", |
| mt8167_codec_pga_gain_enums[HP_L_PGA_GAIN], |
| mt8167_codec_pga_gain_get, |
| mt8167_codec_pga_gain_put), |
| /* Headset_PGAR_GAIN */ |
| SOC_ENUM_EXT("Headset_PGAR_GAIN", |
| mt8167_codec_pga_gain_enums[HP_R_PGA_GAIN], |
| mt8167_codec_pga_gain_get, |
| mt8167_codec_pga_gain_put), |
| /* Lineout_PGA_GAIN */ |
| SOC_ENUM_EXT("Lineout_PGA_GAIN", |
| mt8167_codec_pga_gain_enums[LOUT_PGA_GAIN], |
| mt8167_codec_pga_gain_get, |
| mt8167_codec_pga_gain_put), |
| /* Audio_PGA1_Setting */ |
| SOC_ENUM_EXT("Audio_PGA1_Setting", |
| mt8167_codec_pga_gain_enums[UL_L_PGA_GAIN], |
| mt8167_codec_pga_gain_get, |
| mt8167_codec_pga_gain_put), |
| /* Audio_PGA2_Setting */ |
| SOC_ENUM_EXT("Audio_PGA2_Setting", |
| mt8167_codec_pga_gain_enums[UL_R_PGA_GAIN], |
| mt8167_codec_pga_gain_get, |
| mt8167_codec_pga_gain_put), |
| /* HP calibration */ |
| SOC_SINGLE_EXT("Audio HPL Offset", |
| SND_SOC_NOPM, 0, 0x8000, 0, |
| mt8167_codec_hpl_dc_comp_get, |
| mt8167_codec_hpl_dc_comp_put), |
| SOC_SINGLE_EXT("Audio HPR Offset", |
| SND_SOC_NOPM, 0, 0x8000, 0, |
| mt8167_codec_hpr_dc_comp_get, |
| mt8167_codec_hpr_dc_comp_put), |
| /* UL to DL loopback */ |
| SOC_ENUM_EXT("Codec_Loopback_Select", |
| mt8167_codec_loopback_enum, |
| mt8167_codec_loopback_get, |
| mt8167_codec_loopback_put), |
| /* for factory usage */ |
| SOC_ENUM_EXT("Codec_DL_Switch", |
| mt8167_codec_dl_switch_enum, |
| mt8167_codec_dl_switch_get, |
| mt8167_codec_dl_switch_put), |
| /* for dmic phase debug */ |
| SOC_DOUBLE_EXT("Dmic Ch Phase", |
| SND_SOC_NOPM, 0, 1, 7, 0, |
| mt8167_codec_dmic_ch_phase_get, |
| mt8167_codec_dmic_ch_phase_put), |
| /* for dmic rate mode debug */ |
| SOC_SINGLE_EXT("Dmic Rate Mode", |
| SND_SOC_NOPM, 0, 1, 0, |
| mt8167_codec_dmic_rate_mode_get, |
| mt8167_codec_dmic_rate_mode_put), |
| }; |
| |
| /* Left PGA Mux/Right PGA Mux */ |
| static const char * const pga_mux_text[] = { |
| "CH0", "CH1", "OPEN", |
| }; |
| |
| static SOC_ENUM_SINGLE_DECL(mt8167_codec_left_pga_mux_enum, |
| AUDIO_CODEC_CON00, 28, pga_mux_text); |
| |
| static SOC_ENUM_SINGLE_DECL(mt8167_codec_right_pga_mux_enum, |
| AUDIO_CODEC_CON00, 10, pga_mux_text); |
| |
| static const struct snd_kcontrol_new mt8167_codec_left_pga_mux = |
| SOC_DAPM_ENUM("Left PGA Mux", mt8167_codec_left_pga_mux_enum); |
| |
| static const struct snd_kcontrol_new mt8167_codec_right_pga_mux = |
| SOC_DAPM_ENUM("Right PGA Mux", mt8167_codec_right_pga_mux_enum); |
| |
| /* AIF TX Mux */ |
| static const char * const aif_tx_mux_text[] = { |
| "Analog MIC", "Digital MIC", "Aif Rx" |
| }; |
| |
| static SOC_ENUM_SINGLE_DECL(mt8167_codec_aif_tx_mux_enum, |
| SND_SOC_NOPM, 0, aif_tx_mux_text); |
| |
| static const struct snd_kcontrol_new mt8167_codec_aif_tx_mux = |
| SOC_DAPM_ENUM("AIF TX Mux", mt8167_codec_aif_tx_mux_enum); |
| |
| /* HPOUT Mux */ |
| static const char * const hp_out_mux_text[] = { |
| "OPEN", "AUDIO_AMP", |
| }; |
| |
| static SOC_ENUM_SINGLE_DECL(mt8167_codec_hp_out_mux_enum, |
| SND_SOC_NOPM, 0, hp_out_mux_text); |
| |
| static const struct snd_kcontrol_new mt8167_codec_hp_out_mux = |
| SOC_DAPM_ENUM("HPOUT Mux", |
| mt8167_codec_hp_out_mux_enum); |
| |
| /* LINEOUT Mux */ |
| static const char * const line_out_mux_text[] = { |
| "OPEN", "VOICE_AMP", |
| }; |
| |
| static SOC_ENUM_SINGLE_DECL(mt8167_codec_line_out_mux_enum, |
| SND_SOC_NOPM, 0, line_out_mux_text); |
| |
| static const struct snd_kcontrol_new mt8167_codec_line_out_mux = |
| SOC_DAPM_ENUM("LINEOUT Mux", |
| mt8167_codec_line_out_mux_enum); |
| |
| /* AIF DL_UL loopback Switch */ |
| static const struct snd_kcontrol_new mt8167_codec_aif_dl_ul_lpbk_ctrl = |
| SOC_DAPM_SINGLE_VIRT("Switch", 1); |
| |
| /* DMIC Data Gen Switch */ |
| static const struct snd_kcontrol_new mt8167_codec_dmic_data_gen_ctrl = |
| SOC_DAPM_SINGLE_VIRT("Switch", 1); |
| |
| /* AMIC Data Gen Switch */ |
| static const struct snd_kcontrol_new mt8167_codec_amic_data_gen_ctrl = |
| SOC_DAPM_SINGLE_VIRT("Switch", 1); |
| |
| /* SDM Tone Gen Switch */ |
| static const struct snd_kcontrol_new mt8167_codec_sdm_tone_gen_ctrl = |
| SOC_DAPM_SINGLE_VIRT("Switch", 1); |
| |
| static const struct snd_soc_dapm_widget mt8167_codec_dapm_widgets[] = { |
| /* stream domain */ |
| SND_SOC_DAPM_AIF_OUT("AIF TX", "Capture", 0, SND_SOC_NOPM, 0, 0), |
| SND_SOC_DAPM_AIF_IN("AIF RX", "Playback", 0, SND_SOC_NOPM, 0, 0), |
| SND_SOC_DAPM_ADC("Left ADC", NULL, AUDIO_CODEC_CON00, 23, 0), |
| SND_SOC_DAPM_ADC("Right ADC", NULL, AUDIO_CODEC_CON00, 5, 0), |
| SND_SOC_DAPM_DAC("Left DAC", NULL, AUDIO_CODEC_CON01, 15, 0), |
| SND_SOC_DAPM_DAC("Right DAC", NULL, AUDIO_CODEC_CON01, 14, 0), |
| |
| /* path domain */ |
| SND_SOC_DAPM_MUX("Left PGA Mux", SND_SOC_NOPM, 0, 0, |
| &mt8167_codec_left_pga_mux), |
| SND_SOC_DAPM_MUX("Right PGA Mux", SND_SOC_NOPM, 0, 0, |
| &mt8167_codec_right_pga_mux), |
| SND_SOC_DAPM_MUX("AIF TX Mux", SND_SOC_NOPM, 0, 0, |
| &mt8167_codec_aif_tx_mux), |
| SND_SOC_DAPM_PGA("Left PGA", AUDIO_CODEC_CON00, 24, 0, NULL, 0), |
| SND_SOC_DAPM_PGA("Right PGA", AUDIO_CODEC_CON00, 6, 0, NULL, 0), |
| SND_SOC_DAPM_PGA_S("Left DMIC", 1, SND_SOC_NOPM, 0, 0, NULL, 0), |
| SND_SOC_DAPM_PGA_S("Right DMIC", 1, SND_SOC_NOPM, 0, 0, NULL, 0), |
| SND_SOC_DAPM_PGA_S("DMIC", 2, SND_SOC_NOPM, 0, 0, |
| mt8167_codec_dmic_event, |
| SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
| SND_SOC_DAPM_PGA_S("Left Audio Amp", 1, |
| AUDIO_CODEC_CON01, 12, 0, |
| mt8167_codec_left_audio_amp_event, |
| SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
| SND_SOC_DAPM_PGA_S("Right Audio Amp", 1, |
| AUDIO_CODEC_CON01, 11, 0, |
| mt8167_codec_right_audio_amp_event, |
| SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
| SND_SOC_DAPM_PGA_S("HP Depop VCM", 2, SND_SOC_NOPM, 0, 0, |
| mt8167_codec_depop_vcm_event, |
| SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
| SND_SOC_DAPM_MUX("HPOUT Mux", SND_SOC_NOPM, 0, 0, |
| &mt8167_codec_hp_out_mux), |
| SND_SOC_DAPM_PGA_E("Voice Amp", AUDIO_CODEC_CON02, 8, 0, NULL, 0, |
| mt8167_codec_voice_amp_event, |
| SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | |
| SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), |
| SND_SOC_DAPM_MUX("LINEOUT Mux", SND_SOC_NOPM, 0, 0, |
| &mt8167_codec_line_out_mux), |
| SND_SOC_DAPM_SWITCH_E("AIF DL_UL loopback", SND_SOC_NOPM, 0, 0, |
| &mt8167_codec_aif_dl_ul_lpbk_ctrl, |
| mt8167_codec_aif_dl_ul_lpbk_event, |
| SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
| SND_SOC_DAPM_SWITCH_E("DMIC Data Gen", SND_SOC_NOPM, 0, 0, |
| &mt8167_codec_dmic_data_gen_ctrl, |
| mt8167_codec_dmic_data_gen_event, |
| SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
| SND_SOC_DAPM_SWITCH_E("AMIC Data Gen", SND_SOC_NOPM, 0, 0, |
| &mt8167_codec_amic_data_gen_ctrl, |
| mt8167_codec_amic_data_gen_event, |
| SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
| SND_SOC_DAPM_SWITCH_E("SDM Tone Gen", SND_SOC_NOPM, 0, 0, |
| &mt8167_codec_sdm_tone_gen_ctrl, |
| mt8167_codec_sdm_tone_gen_event, |
| SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
| /* generic widgets */ |
| SND_SOC_DAPM_SUPPLY_S("CODEC_CLK", 1, SND_SOC_NOPM, 0, 0, |
| mt8167_codec_clk_event, |
| SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
| SND_SOC_DAPM_SUPPLY_S("UL_CLK", 2, AUDIO_CODEC_CON03, 21, 0, NULL, 0), |
| SND_SOC_DAPM_SUPPLY_S("DL_CLK", 2, AUDIO_CODEC_CON04, 15, 0, NULL, 0), |
| SND_SOC_DAPM_SUPPLY_S("Vcm14", 3, SND_SOC_NOPM, 0, 0, |
| mt8167_codec_vcm14_event, |
| SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
| SND_SOC_DAPM_SUPPLY_S("UL_Vref24", 4, SND_SOC_NOPM, 0, 0, |
| mt8167_codec_ul_vref24_event, |
| SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
| SND_SOC_DAPM_SUPPLY_S("DL_Vref24", 4, |
| AUDIO_CODEC_CON02, 16, 0, NULL, 0), |
| SND_SOC_DAPM_SUPPLY_S("DAC_CLK", 4, AUDIO_CODEC_CON02, 27, 0, NULL, 0), |
| SND_SOC_DAPM_SUPPLY_S("DL_VCM1", 4, AUDIO_CODEC_CON01, 13, 0, NULL, 0), |
| SND_SOC_DAPM_SUPPLY_S("DL_VCM2", 4, AUDIO_CODEC_CON02, 17, 0, NULL, 0), |
| SND_SOC_DAPM_SUPPLY_S("AU_MICBIAS0", 5, |
| AUDIO_CODEC_CON03, 17, 0, NULL, 0), |
| SND_SOC_DAPM_SUPPLY_S("AU_MICBIAS1", 5, |
| AUDIO_CODEC_CON01, 21, 0, NULL, 0), |
| |
| /* platform domain */ |
| SND_SOC_DAPM_INPUT("AU_VIN0"), |
| SND_SOC_DAPM_INPUT("AU_VIN1"), |
| SND_SOC_DAPM_INPUT("AU_VIN2"), |
| |
| SND_SOC_DAPM_OUTPUT("AU_HPL"), |
| SND_SOC_DAPM_OUTPUT("AU_HPR"), |
| |
| SND_SOC_DAPM_OUTPUT("AU_LOL"), |
| |
| SND_SOC_DAPM_SIGGEN("SDM Tone"), |
| SND_SOC_DAPM_SIGGEN("DMIC Data"), |
| SND_SOC_DAPM_SIGGEN("AMIC Data"), |
| }; |
| |
| static const struct snd_soc_dapm_route mt8167_codec_dapm_routes[] = { |
| /* Power */ |
| {"AIF TX", NULL, "CODEC_CLK"}, |
| {"AIF RX", NULL, "CODEC_CLK"}, |
| |
| {"AIF TX", NULL, "UL_CLK"}, |
| {"AIF RX", NULL, "DL_CLK"}, |
| |
| /* DL to UL loopback */ |
| {"AIF DL_UL loopback", "Switch", "AIF RX"}, |
| {"AIF TX Mux", "Aif Rx", "AIF DL_UL loopback"}, |
| |
| /* UL */ |
| {"AIF TX", NULL, "AIF TX Mux"}, |
| |
| {"AU_VIN0", NULL, "AU_MICBIAS0"}, |
| {"AU_VIN2", NULL, "AU_MICBIAS0"}, |
| |
| {"AU_VIN1", NULL, "AU_MICBIAS1"}, |
| |
| /* UL - Analog MIC Path */ |
| {"AIF TX Mux", "Analog MIC", "Left ADC"}, |
| {"AIF TX Mux", "Analog MIC", "Right ADC"}, |
| |
| {"Left ADC", NULL, "Left PGA"}, |
| {"Right ADC", NULL, "Right PGA"}, |
| |
| {"Left PGA", NULL, "Left PGA Mux"}, |
| {"Left PGA", NULL, "Left PGA Mux"}, |
| |
| {"Right PGA", NULL, "Right PGA Mux"}, |
| {"Right PGA", NULL, "Right PGA Mux"}, |
| |
| {"Left PGA Mux", "CH0", "AU_VIN0"}, |
| {"Left PGA Mux", "CH1", "AU_VIN1"}, |
| |
| {"Right PGA Mux", "CH1", "AU_VIN1"}, |
| {"Right PGA Mux", "CH0", "AU_VIN2"}, |
| |
| {"Left PGA", NULL, "Vcm14"}, |
| {"Right PGA", NULL, "Vcm14"}, |
| |
| {"Left ADC", NULL, "UL_Vref24"}, |
| {"Right ADC", NULL, "UL_Vref24"}, |
| |
| /* UL - Digital MIC Path */ |
| {"AIF TX Mux", "Digital MIC", "DMIC"}, |
| |
| {"DMIC", NULL, "Left DMIC"}, |
| {"DMIC", NULL, "Right DMIC"}, |
| |
| {"Left DMIC", NULL, "AU_VIN0"}, |
| {"Right DMIC", NULL, "AU_VIN2"}, |
| |
| {"Left DMIC", NULL, "Vcm14"}, |
| {"Right DMIC", NULL, "Vcm14"}, |
| |
| /* UL - Debug Path (DMIC Data Generator) */ |
| {"DMIC Data Gen", "Switch", "DMIC Data"}, |
| {"AIF TX Mux", "Digital MIC", "DMIC Data Gen"}, |
| |
| /* UL - Debug Path (AMIC Data Generator) */ |
| {"AMIC Data Gen", "Switch", "AMIC Data"}, |
| {"AIF TX Mux", "Analog MIC", "AMIC Data Gen"}, |
| |
| /* DL */ |
| {"AIF RX", NULL, "Vcm14"}, |
| |
| {"Left DAC", NULL, "AIF RX"}, |
| {"Right DAC", NULL, "AIF RX"}, |
| |
| {"Left DAC", NULL, "DL_Vref24"}, |
| {"Right DAC", NULL, "DL_Vref24"}, |
| |
| {"Left DAC", NULL, "DAC_CLK"}, |
| {"Right DAC", NULL, "DAC_CLK"}, |
| |
| {"Left DAC", NULL, "DL_VCM1"}, |
| {"Right DAC", NULL, "DL_VCM1"}, |
| |
| /* DL - Audio Amp Path */ |
| {"Left Audio Amp", NULL, "Left DAC"}, |
| {"Right Audio Amp", NULL, "Right DAC"}, |
| |
| {"Left Audio Amp", NULL, "DL_VCM2"}, |
| {"Right Audio Amp", NULL, "DL_VCM2"}, |
| |
| {"HP Depop VCM", NULL, "Left Audio Amp"}, |
| {"HP Depop VCM", NULL, "Right Audio Amp"}, |
| |
| {"HPOUT Mux", "AUDIO_AMP", "HP Depop VCM"}, |
| |
| {"AU_HPL", NULL, "HPOUT Mux"}, |
| {"AU_HPR", NULL, "HPOUT Mux"}, |
| |
| /* DL - Voice Amp Path */ |
| {"Voice Amp", NULL, "Left DAC"}, |
| |
| {"Voice Amp", NULL, "DL_VCM2"}, |
| {"Voice Amp", NULL, "DL_VCM2"}, |
| |
| {"LINEOUT Mux", "VOICE_AMP", "Voice Amp"}, |
| |
| {"AU_LOL", NULL, "LINEOUT Mux"}, |
| |
| /* DL - Debug Path (Triangular Tone Generator) */ |
| {"SDM Tone Gen", "Switch", "SDM Tone"}, |
| {"AIF RX", NULL, "SDM Tone Gen"}, |
| }; |
| |
| static int afe_reg_read(void *context, unsigned int reg, unsigned int *val) |
| { |
| struct mt8167_codec_priv *codec_data = |
| (struct mt8167_codec_priv *) context; |
| int ret = 0; |
| |
| if (!(codec_data && codec_data->regmap_modules[REGMAP_AFE])) |
| return -1; |
| |
| dev_dbg(codec_data->codec->dev, "%s reg 0x%x\n", |
| __func__, reg); |
| |
| ret = regmap_read(codec_data->regmap_modules[REGMAP_AFE], |
| (reg & (~AFE_OFFSET)), val); |
| return ret; |
| } |
| |
| static int afe_reg_write(void *context, unsigned int reg, unsigned int val) |
| { |
| struct mt8167_codec_priv *codec_data = |
| (struct mt8167_codec_priv *) context; |
| int ret = 0; |
| |
| if (!(codec_data && codec_data->regmap_modules[REGMAP_AFE])) |
| return -1; |
| |
| dev_dbg(codec_data->codec->dev, "%s reg 0x%x, val 0x%x\n", |
| __func__, reg, val); |
| |
| ret = regmap_write(codec_data->regmap_modules[REGMAP_AFE], |
| (reg & (~AFE_OFFSET)), val); |
| return ret; |
| } |
| |
| static bool reg_is_in_afe(unsigned int reg) |
| { |
| if (reg & AFE_OFFSET) |
| return true; |
| else |
| return false; |
| } |
| |
| static int apmixedsys_reg_read(void *context, |
| unsigned int reg, unsigned int *val) |
| { |
| struct mt8167_codec_priv *codec_data = |
| (struct mt8167_codec_priv *) context; |
| int ret = 0; |
| |
| *val = readl(codec_data->apmixedsys_reg_base + (reg & (~APMIXED_OFFSET))); |
| // printk(KERN_ERR "XXX: APMIXED[%x] = %x\n", reg & 0xffff, *val); |
| #if 0 |
| if (!(codec_data && codec_data->regmap_modules[REGMAP_APMIXEDSYS])) |
| return -1; |
| ret = regmap_read(codec_data->regmap_modules[REGMAP_APMIXEDSYS], |
| (reg & (~APMIXED_OFFSET)), val); |
| #endif |
| return ret; |
| } |
| |
| static int apmixedsys_reg_write(void *context, |
| unsigned int reg, unsigned int val) |
| { |
| struct mt8167_codec_priv *codec_data = |
| (struct mt8167_codec_priv *) context; |
| int ret = 0; |
| |
| writel(val, codec_data->apmixedsys_reg_base + (reg & (~APMIXED_OFFSET))); |
| // printk(KERN_ERR "XXX: APMIXED[%x] = %x\n", reg & 0xffff, val); |
| #if 0 |
| if (!(codec_data && codec_data->regmap_modules[REGMAP_APMIXEDSYS])) |
| return -1; |
| ret = regmap_write(codec_data->regmap_modules[REGMAP_APMIXEDSYS], |
| (reg & (~APMIXED_OFFSET)), val); |
| #endif |
| return ret; |
| } |
| |
| static bool reg_is_in_apmixedsys(unsigned int reg) |
| { |
| if (reg & APMIXED_OFFSET) |
| return true; |
| else |
| return false; |
| } |
| |
| #ifdef CONFIG_MTK_SPEAKER |
| static int pwrap_reg_read(void *context, |
| unsigned int reg, unsigned int *val) |
| { |
| struct mt8167_codec_priv *codec_data = |
| (struct mt8167_codec_priv *) context; |
| int ret = 0; |
| |
| if (!(codec_data && codec_data->regmap_modules[REGMAP_PWRAP])) |
| return -1; |
| |
| dev_dbg(codec_data->codec->dev, "%s reg 0x%x\n", |
| __func__, reg); |
| |
| ret = regmap_read(codec_data->regmap_modules[REGMAP_PWRAP], |
| (reg & (~PMIC_OFFSET)), val); |
| return ret; |
| } |
| |
| static int pwrap_reg_write(void *context, |
| unsigned int reg, unsigned int val) |
| { |
| struct mt8167_codec_priv *codec_data = |
| (struct mt8167_codec_priv *) context; |
| int ret = 0; |
| |
| if (!(codec_data && codec_data->regmap_modules[REGMAP_PWRAP])) |
| return -1; |
| |
| dev_dbg(codec_data->codec->dev, "%s reg 0x%x, val 0x%x\n", |
| __func__, reg, val); |
| |
| ret = regmap_write(codec_data->regmap_modules[REGMAP_PWRAP], |
| (reg & (~PMIC_OFFSET)), val); |
| return ret; |
| } |
| |
| static bool reg_is_in_pmic(unsigned int reg) |
| { |
| if (reg & PMIC_OFFSET) |
| return true; |
| else |
| return false; |
| } |
| #endif |
| |
| /* regmap functions */ |
| static int codec_reg_read(void *context, |
| unsigned int reg, unsigned int *val) |
| { |
| int ret = 0; |
| |
| if (reg_is_in_afe(reg)) |
| ret = afe_reg_read(context, reg, val); |
| else if (reg_is_in_apmixedsys(reg)) |
| ret = apmixedsys_reg_read(context, reg, val); |
| #ifdef CONFIG_MTK_SPEAKER |
| else if (reg_is_in_pmic(reg)) |
| ret = pwrap_reg_read(context, reg, val); |
| #endif |
| else |
| ret = -1; |
| return ret; |
| } |
| |
| static int codec_reg_write(void *context, |
| unsigned int reg, unsigned int val) |
| { |
| int ret = 0; |
| |
| if (reg_is_in_afe(reg)) |
| ret = afe_reg_write(context, reg, val); |
| else if (reg_is_in_apmixedsys(reg)) |
| ret = apmixedsys_reg_write(context, reg, val); |
| #ifdef CONFIG_MTK_SPEAKER |
| else if (reg_is_in_pmic(reg)) |
| ret = pwrap_reg_write(context, reg, val); |
| #endif |
| else |
| ret = -1; |
| return ret; |
| } |
| |
| static void codec_regmap_lock(void *lock_arg) |
| { |
| } |
| |
| static void codec_regmap_unlock(void *lock_arg) |
| { |
| } |
| |
| static struct regmap_config mt8167_codec_regmap_config = { |
| .reg_bits = 32, |
| .val_bits = 32, |
| .reg_read = codec_reg_read, |
| .reg_write = codec_reg_write, |
| .lock = codec_regmap_lock, |
| .unlock = codec_regmap_unlock, |
| .cache_type = REGCACHE_NONE, |
| }; |
| |
| #ifdef CONFIG_DEBUG_FS |
| struct mt8167_codec_reg_attr { |
| uint32_t offset; |
| char *name; |
| }; |
| |
| #define DUMP_REG_ENTRY(reg) {reg, #reg} |
| |
| static const struct mt8167_codec_reg_attr mt8167_codec_dump_reg_list[] = { |
| /* audio_top_sys */ |
| DUMP_REG_ENTRY(ABB_AFE_CON0), |
| DUMP_REG_ENTRY(ABB_AFE_CON1), |
| DUMP_REG_ENTRY(ABB_AFE_CON2), |
| DUMP_REG_ENTRY(ABB_AFE_CON3), |
| DUMP_REG_ENTRY(ABB_AFE_CON4), |
| DUMP_REG_ENTRY(ABB_AFE_CON5), |
| DUMP_REG_ENTRY(ABB_AFE_CON6), |
| DUMP_REG_ENTRY(ABB_AFE_CON7), |
| DUMP_REG_ENTRY(ABB_AFE_CON8), |
| DUMP_REG_ENTRY(ABB_AFE_CON9), |
| DUMP_REG_ENTRY(ABB_AFE_CON10), |
| DUMP_REG_ENTRY(ABB_AFE_CON11), |
| DUMP_REG_ENTRY(ABB_AFE_STA0), |
| DUMP_REG_ENTRY(ABB_AFE_STA1), |
| DUMP_REG_ENTRY(ABB_AFE_STA2), |
| DUMP_REG_ENTRY(AFE_MON_DEBUG0), |
| DUMP_REG_ENTRY(AFE_MON_DEBUG1), |
| DUMP_REG_ENTRY(ABB_AFE_SDM_TEST), |
| |
| /* apmixedsys */ |
| DUMP_REG_ENTRY(AUDIO_CODEC_CON00), |
| DUMP_REG_ENTRY(AUDIO_CODEC_CON01), |
| DUMP_REG_ENTRY(AUDIO_CODEC_CON02), |
| DUMP_REG_ENTRY(AUDIO_CODEC_CON03), |
| DUMP_REG_ENTRY(AUDIO_CODEC_CON04), |
| }; |
| |
| static ssize_t mt8167_codec_debug_read(struct file *file, |
| char __user *user_buf, |
| size_t count, loff_t *pos) |
| { |
| struct mt8167_codec_priv *codec_data = file->private_data; |
| ssize_t ret, i; |
| char *buf; |
| int n = 0; |
| |
| if (*pos < 0 || !count) |
| return -EINVAL; |
| |
| buf = kmalloc(count, GFP_KERNEL); |
| if (!buf) |
| return -ENOMEM; |
| |
| for (i = 0; i < ARRAY_SIZE(mt8167_codec_dump_reg_list); i++) { |
| n += scnprintf(buf + n, count - n, "%s = 0x%x\n", |
| mt8167_codec_dump_reg_list[i].name, |
| snd_soc_component_read32(codec_data->codec, |
| mt8167_codec_dump_reg_list[i].offset)); |
| } |
| |
| ret = simple_read_from_buffer(user_buf, count, pos, buf, n); |
| |
| kfree(buf); |
| |
| return ret; |
| } |
| |
| static const struct file_operations mt8167_codec_debug_ops = { |
| .open = simple_open, |
| .read = mt8167_codec_debug_read, |
| .llseek = default_llseek, |
| }; |
| #endif |
| |
| static void mt8167_codec_init_regs(struct mt8167_codec_priv *codec_data) |
| { |
| dev_dbg(codec_data->codec->dev, "%s\n", __func__); |
| |
| /* disable chopper of uplink */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON00, BIT(17), 0x0); |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON01, BIT(31), 0x0); |
| |
| /* Audio buffer quasi-current */ |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON02, GENMASK(31, 30), 0x0); |
| |
| /* setup default gain */ |
| /* +4dB for voice buf gain */ |
| codec_data->pga_gain[LOUT_PGA_GAIN] = 0xB; |
| snd_soc_component_update_bits(codec_data->codec, |
| AUDIO_CODEC_CON02, GENMASK(12, 9), |
| (codec_data->pga_gain[LOUT_PGA_GAIN]) << 9); |
| |
| mt8167_codec_hp_depop_setup(codec_data); |
| } |
| |
| static struct regmap *mt8167_codec_get_regmap_from_dt(const char *phandle_name, |
| struct mt8167_codec_priv *codec_data) |
| { |
| struct device_node *self_node = NULL, *node = NULL; |
| struct platform_device *platdev = NULL; |
| struct device *dev = codec_data->codec->dev; |
| struct regmap *regmap = NULL; |
| |
| self_node = of_find_compatible_node(NULL, NULL, |
| "mediatek," MT8167_CODEC_NAME); |
| if (!self_node) { |
| dev_err(dev, "%s failed to find %s node\n", |
| __func__, MT8167_CODEC_NAME); |
| return NULL; |
| } |
| dev_err(dev, "%s found %s node\n", __func__, MT8167_CODEC_NAME); |
| |
| node = of_parse_phandle(self_node, phandle_name, 0); |
| if (!node) { |
| dev_err(dev, "%s failed to find %s node\n", |
| __func__, phandle_name); |
| return NULL; |
| } |
| dev_err(dev, "%s found %s\n", __func__, phandle_name); |
| |
| platdev = of_find_device_by_node(node); |
| if (!platdev) { |
| dev_err(dev, "%s failed to get platform device of %s\n", |
| __func__, phandle_name); |
| return NULL; |
| } |
| dev_err(dev, "%s found platform device of %s\n", |
| __func__, phandle_name); |
| |
| regmap = dev_get_regmap(&platdev->dev, NULL); |
| if (regmap) { |
| dev_err(dev, "%s found regmap of %s\n", __func__, phandle_name); |
| return regmap; |
| } |
| |
| regmap = syscon_regmap_lookup_by_phandle(dev->of_node, phandle_name); |
| if (!IS_ERR(regmap)) { |
| dev_err(dev, "%s found regmap of syscon node %s\n", |
| __func__, phandle_name); |
| return regmap; |
| } |
| dev_err(dev, "%s failed to get regmap of syscon node %s\n", |
| __func__, phandle_name); |
| |
| return NULL; |
| } |
| |
| static const char * const modules_dt_regmap_str[REGMAP_NUMS] = { |
| "mediatek,afe-regmap", |
| // "mediatek,apmixedsys-regmap", |
| #ifdef CONFIG_MTK_SPEAKER |
| "mediatek,pwrap-regmap", |
| #endif |
| }; |
| |
| static int mt8167_codec_parse_dt(struct mt8167_codec_priv *codec_data) |
| { |
| struct device *dev = codec_data->codec->dev; |
| int ret = 0; |
| int i; |
| struct device_node *np; |
| |
| for (i = 0 ; i < REGMAP_NUMS ; i++) { |
| codec_data->regmap_modules[i] = mt8167_codec_get_regmap_from_dt( |
| modules_dt_regmap_str[i], |
| codec_data); |
| if (!codec_data->regmap_modules[i]) { |
| dev_err(dev, "%s failed to get %s\n", |
| __func__, modules_dt_regmap_str[i]); |
| // devm_kfree(dev, codec_data); |
| return -EPROBE_DEFER; |
| break; |
| } |
| } |
| |
| np = of_find_compatible_node(NULL, NULL, "mediatek,mt8516-apmixedsys"); |
| if (!np) |
| np = of_find_compatible_node(NULL, NULL, "mediatek,mt8167-apmixedsys"); |
| codec_data->apmixedsys_reg_base = of_iomap(np, 0); |
| |
| ret = of_property_read_u32(dev->of_node, "mediatek,dmic-wire-mode", |
| &codec_data->dmic_wire_mode); |
| if (ret) { |
| dev_warn(dev, "%s fail to read dmic-wire-mode in node %s\n", |
| __func__, dev->of_node->full_name); |
| codec_data->dmic_wire_mode = DMIC_ONE_WIRE; |
| } else if ((codec_data->dmic_wire_mode != DMIC_ONE_WIRE) && |
| codec_data->dmic_wire_mode != DMIC_TWO_WIRE) { |
| codec_data->dmic_wire_mode = DMIC_ONE_WIRE; |
| } |
| |
| if (of_property_read_u32_array(dev->of_node, "mediatek,dmic-ch-phase", |
| codec_data->dmic_ch_phase, ARRAY_SIZE(codec_data->dmic_ch_phase))) { |
| for (i = 0; i < ARRAY_SIZE(codec_data->dmic_ch_phase); i++) |
| codec_data->dmic_ch_phase[i] = 0; |
| } |
| for (i = 0; i < ARRAY_SIZE(codec_data->dmic_ch_phase); i++) { |
| if (codec_data->dmic_ch_phase[i] >= DMIC_PHASE_NUM) |
| codec_data->dmic_ch_phase[i] = 0; |
| } |
| |
| if (of_property_read_u32(dev->of_node, "mediatek,dmic-rate-mode", |
| &codec_data->dmic_rate_mode)) |
| codec_data->dmic_rate_mode = DMIC_RATE_D1P625M; |
| else if ((codec_data->dmic_rate_mode != DMIC_RATE_D1P625M) && |
| (codec_data->dmic_rate_mode != DMIC_RATE_D3P25M)) |
| codec_data->dmic_rate_mode = DMIC_RATE_D1P625M; |
| return ret; |
| } |
| |
| static int mt8167_codec_probe(struct snd_soc_component *codec) |
| { |
| struct mt8167_codec_priv *codec_data = snd_soc_component_get_drvdata(codec); |
| int ret = 0; |
| |
| dev_dbg(codec->dev, "%s\n", __func__); |
| |
| codec_data->codec = codec; |
| |
| ret = mt8167_codec_parse_dt(codec_data); |
| if (ret < 0) |
| return ret; |
| |
| codec_data->clk = devm_clk_get(codec->dev, "bus"); |
| if (IS_ERR(codec_data->clk)) { |
| dev_err(codec->dev, "%s devm_clk_get %s fail\n", |
| __func__, "bus"); |
| return PTR_ERR(codec_data->clk); |
| } |
| |
| ret = clk_prepare_enable(codec_data->clk); |
| if (ret) |
| return ret; |
| |
| mt8167_codec_init_regs(codec_data); |
| #ifdef CONFIG_DEBUG_FS |
| codec_data->debugfs = debugfs_create_file("mt8167_codec_regs", |
| S_IFREG | S_IRUGO, |
| NULL, codec_data, &mt8167_codec_debug_ops); |
| #endif |
| #ifdef CONFIG_MTK_SPEAKER |
| ret = mt6392_codec_probe(codec); |
| if (ret < 0) |
| clk_disable_unprepare(codec_data->clk); |
| #endif |
| return ret; |
| } |
| |
| static void mt8167_codec_remove(struct snd_soc_component *codec) |
| { |
| struct mt8167_codec_priv *codec_data = snd_soc_component_get_drvdata(codec); |
| |
| clk_disable_unprepare(codec_data->clk); |
| #ifdef CONFIG_DEBUG_FS |
| debugfs_remove(codec_data->debugfs); |
| #endif |
| #ifdef CONFIG_MTK_SPEAKER |
| mt6392_codec_remove(codec); |
| #endif |
| } |
| |
| static int mt8167_codec_suspend(struct snd_soc_component *codec) |
| { |
| struct mt8167_codec_priv *codec_data = snd_soc_component_get_drvdata(codec); |
| |
| clk_disable_unprepare(codec_data->clk); |
| return 0; |
| } |
| |
| static int mt8167_codec_resume(struct snd_soc_component *codec) |
| { |
| struct mt8167_codec_priv *codec_data = snd_soc_component_get_drvdata(codec); |
| |
| return clk_prepare_enable(codec_data->clk); |
| } |
| |
| static int mt8167_codec_set_bias_level(struct snd_soc_component *codec, |
| enum snd_soc_bias_level level) |
| { |
| dev_dbg(codec->dev, "%s curr bias_level %d, set bias_level: %d\n", |
| __func__, snd_soc_component_get_bias_level(codec), level); |
| |
| switch (snd_soc_component_get_bias_level(codec)) { |
| case SND_SOC_BIAS_OFF: |
| break; |
| case SND_SOC_BIAS_STANDBY: |
| break; |
| case SND_SOC_BIAS_PREPARE: |
| break; |
| case SND_SOC_BIAS_ON: |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static struct snd_soc_component_driver mt8167_codec_driver = { |
| .probe = mt8167_codec_probe, |
| .remove = mt8167_codec_remove, |
| .suspend = mt8167_codec_suspend, |
| .resume = mt8167_codec_resume, |
| .set_bias_level = mt8167_codec_set_bias_level, |
| .controls = mt8167_codec_controls, |
| .num_controls = ARRAY_SIZE(mt8167_codec_controls), |
| .dapm_widgets = mt8167_codec_dapm_widgets, |
| .num_dapm_widgets = ARRAY_SIZE(mt8167_codec_dapm_widgets), |
| .dapm_routes = mt8167_codec_dapm_routes, |
| .num_dapm_routes = ARRAY_SIZE(mt8167_codec_dapm_routes), |
| }; |
| |
| static int mt8167_codec_dev_probe(struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| struct mt8167_codec_priv *codec_data = NULL; |
| |
| dev_dbg(dev, "%s dev name %s\n", __func__, dev_name(dev)); |
| |
| if (dev->of_node) { |
| dev_set_name(dev, "%s", MT8167_CODEC_NAME); |
| dev_dbg(dev, "%s set dev name %s\n", __func__, dev_name(dev)); |
| } |
| |
| codec_data = devm_kzalloc(dev, |
| sizeof(struct mt8167_codec_priv), GFP_KERNEL); |
| if (!codec_data) |
| return -ENOMEM; |
| |
| dev_set_drvdata(dev, codec_data); |
| |
| /* get regmap of codec */ |
| codec_data->regmap = devm_regmap_init(dev, NULL, codec_data, |
| &mt8167_codec_regmap_config); |
| if (IS_ERR(codec_data->regmap)) { |
| dev_err(dev, "%s failed to get regmap of codec\n", __func__); |
| // devm_kfree(dev, codec_data); |
| codec_data->regmap = NULL; |
| return -EINVAL; |
| } |
| |
| return devm_snd_soc_register_component(dev, |
| &mt8167_codec_driver, &mt8167_codec_dai, 1); |
| } |
| |
| static int mt8167_codec_dev_remove(struct platform_device *pdev) |
| { |
| struct mt8167_codec_priv *codec_data = dev_get_drvdata(&pdev->dev); |
| |
| mt8167_codec_hp_depop_cleanup(codec_data); |
| |
| return 0; |
| } |
| |
| static const struct of_device_id mt8167_codec_dt_match[] = { |
| {.compatible = "mediatek," MT8167_CODEC_NAME,}, |
| {} |
| }; |
| |
| MODULE_DEVICE_TABLE(of, mt8167_codec_dt_match); |
| |
| static struct platform_driver mt8167_codec_device_driver = { |
| .driver = { |
| .name = MT8167_CODEC_NAME, |
| .owner = THIS_MODULE, |
| .of_match_table = mt8167_codec_dt_match, |
| }, |
| .probe = mt8167_codec_dev_probe, |
| .remove = mt8167_codec_dev_remove, |
| }; |
| |
| module_platform_driver(mt8167_codec_device_driver); |
| |
| /* Module information */ |
| MODULE_DESCRIPTION("ASoC MT8167 driver"); |
| MODULE_LICENSE("GPL v2"); |