1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // MediaTek ALSA SoC Audio DAI HW Gain Control 4 // 5 // Copyright (c) 2022 MediaTek Inc. 6 // Author: Jiaxin Yu <jiaxin.yu@mediatek.com> 7 8 #include <linux/regmap.h> 9 #include "mt8186-afe-common.h" 10 #include "mt8186-interconnection.h" 11 12 #define HW_GAIN_1_EN_W_NAME "HW GAIN 1 Enable" 13 #define HW_GAIN_2_EN_W_NAME "HW GAIN 2 Enable" 14 15 /* dai component */ 16 static const struct snd_kcontrol_new mtk_hw_gain1_in_ch1_mix[] = { 17 SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH1 Switch", AFE_CONN13_1, 18 I_CONNSYS_I2S_CH1, 1, 0), 19 }; 20 21 static const struct snd_kcontrol_new mtk_hw_gain1_in_ch2_mix[] = { 22 SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH2 Switch", AFE_CONN14_1, 23 I_CONNSYS_I2S_CH2, 1, 0), 24 }; 25 26 static const struct snd_kcontrol_new mtk_hw_gain2_in_ch1_mix[] = { 27 SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN15, 28 I_ADDA_UL_CH1, 1, 0), 29 }; 30 31 static const struct snd_kcontrol_new mtk_hw_gain2_in_ch2_mix[] = { 32 SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN16, 33 I_ADDA_UL_CH2, 1, 0), 34 }; 35 36 static int mtk_hw_gain_event(struct snd_soc_dapm_widget *w, 37 struct snd_kcontrol *kcontrol, 38 int event) 39 { 40 struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); 41 struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); 42 unsigned int gain_cur; 43 unsigned int gain_con1; 44 45 dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n", 46 __func__, w->name, event); 47 48 switch (event) { 49 case SND_SOC_DAPM_PRE_PMU: 50 if (snd_soc_dapm_widget_name_cmp(w, HW_GAIN_1_EN_W_NAME) == 0) { 51 gain_cur = AFE_GAIN1_CUR; 52 gain_con1 = AFE_GAIN1_CON1; 53 } else { 54 gain_cur = AFE_GAIN2_CUR; 55 gain_con1 = AFE_GAIN2_CON1; 56 } 57 58 /* let hw gain ramp up, set cur gain to 0 */ 59 regmap_update_bits(afe->regmap, gain_cur, AFE_GAIN1_CUR_MASK_SFT, 0); 60 61 /* set target gain to 0 */ 62 regmap_update_bits(afe->regmap, gain_con1, GAIN1_TARGET_MASK_SFT, 0); 63 break; 64 default: 65 break; 66 } 67 68 return 0; 69 } 70 71 static const struct snd_soc_dapm_widget mtk_dai_hw_gain_widgets[] = { 72 /* inter-connections */ 73 SND_SOC_DAPM_MIXER("HW_GAIN1_IN_CH1", SND_SOC_NOPM, 0, 0, 74 mtk_hw_gain1_in_ch1_mix, 75 ARRAY_SIZE(mtk_hw_gain1_in_ch1_mix)), 76 SND_SOC_DAPM_MIXER("HW_GAIN1_IN_CH2", SND_SOC_NOPM, 0, 0, 77 mtk_hw_gain1_in_ch2_mix, 78 ARRAY_SIZE(mtk_hw_gain1_in_ch2_mix)), 79 SND_SOC_DAPM_MIXER("HW_GAIN2_IN_CH1", SND_SOC_NOPM, 0, 0, 80 mtk_hw_gain2_in_ch1_mix, 81 ARRAY_SIZE(mtk_hw_gain2_in_ch1_mix)), 82 SND_SOC_DAPM_MIXER("HW_GAIN2_IN_CH2", SND_SOC_NOPM, 0, 0, 83 mtk_hw_gain2_in_ch2_mix, 84 ARRAY_SIZE(mtk_hw_gain2_in_ch2_mix)), 85 86 SND_SOC_DAPM_SUPPLY(HW_GAIN_1_EN_W_NAME, 87 AFE_GAIN1_CON0, GAIN1_ON_SFT, 0, 88 mtk_hw_gain_event, 89 SND_SOC_DAPM_PRE_PMU), 90 91 SND_SOC_DAPM_SUPPLY(HW_GAIN_2_EN_W_NAME, 92 AFE_GAIN2_CON0, GAIN2_ON_SFT, 0, 93 mtk_hw_gain_event, 94 SND_SOC_DAPM_PRE_PMU), 95 96 SND_SOC_DAPM_INPUT("HW Gain 1 Out Endpoint"), 97 SND_SOC_DAPM_INPUT("HW Gain 2 Out Endpoint"), 98 SND_SOC_DAPM_OUTPUT("HW Gain 1 In Endpoint"), 99 }; 100 101 static const struct snd_soc_dapm_route mtk_dai_hw_gain_routes[] = { 102 {"HW Gain 1 In", NULL, "HW_GAIN1_IN_CH1"}, 103 {"HW Gain 1 In", NULL, "HW_GAIN1_IN_CH2"}, 104 {"HW Gain 2 In", NULL, "HW_GAIN2_IN_CH1"}, 105 {"HW Gain 2 In", NULL, "HW_GAIN2_IN_CH2"}, 106 107 {"HW Gain 1 In", NULL, HW_GAIN_1_EN_W_NAME}, 108 {"HW Gain 1 Out", NULL, HW_GAIN_1_EN_W_NAME}, 109 {"HW Gain 2 In", NULL, HW_GAIN_2_EN_W_NAME}, 110 {"HW Gain 2 Out", NULL, HW_GAIN_2_EN_W_NAME}, 111 112 {"HW Gain 1 In Endpoint", NULL, "HW Gain 1 In"}, 113 {"HW Gain 1 Out", NULL, "HW Gain 1 Out Endpoint"}, 114 {"HW Gain 2 Out", NULL, "HW Gain 2 Out Endpoint"}, 115 }; 116 117 static const struct snd_kcontrol_new mtk_hw_gain_controls[] = { 118 SOC_SINGLE("HW Gain 1 Volume", AFE_GAIN1_CON1, 119 GAIN1_TARGET_SFT, GAIN1_TARGET_MASK, 0), 120 SOC_SINGLE("HW Gain 2 Volume", AFE_GAIN2_CON1, 121 GAIN2_TARGET_SFT, GAIN2_TARGET_MASK, 0), 122 }; 123 124 /* dai ops */ 125 static int mtk_dai_gain_hw_params(struct snd_pcm_substream *substream, 126 struct snd_pcm_hw_params *params, 127 struct snd_soc_dai *dai) 128 { 129 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); 130 unsigned int rate = params_rate(params); 131 unsigned int rate_reg = mt8186_rate_transform(afe->dev, rate, dai->id); 132 133 dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n", 134 __func__, dai->id, substream->stream, rate); 135 136 /* rate */ 137 regmap_update_bits(afe->regmap, 138 dai->id == MT8186_DAI_HW_GAIN_1 ? 139 AFE_GAIN1_CON0 : AFE_GAIN2_CON0, 140 GAIN1_MODE_MASK_SFT, 141 rate_reg << GAIN1_MODE_SFT); 142 143 /* sample per step */ 144 regmap_update_bits(afe->regmap, 145 dai->id == MT8186_DAI_HW_GAIN_1 ? 146 AFE_GAIN1_CON0 : AFE_GAIN2_CON0, 147 GAIN1_SAMPLE_PER_STEP_MASK_SFT, 148 (dai->id == MT8186_DAI_HW_GAIN_1 ? 0x40 : 0x0) << 149 GAIN1_SAMPLE_PER_STEP_SFT); 150 151 return 0; 152 } 153 154 static const struct snd_soc_dai_ops mtk_dai_gain_ops = { 155 .hw_params = mtk_dai_gain_hw_params, 156 }; 157 158 /* dai driver */ 159 #define MTK_HW_GAIN_RATES (SNDRV_PCM_RATE_8000_48000 |\ 160 SNDRV_PCM_RATE_88200 |\ 161 SNDRV_PCM_RATE_96000 |\ 162 SNDRV_PCM_RATE_176400 |\ 163 SNDRV_PCM_RATE_192000) 164 165 #define MTK_HW_GAIN_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ 166 SNDRV_PCM_FMTBIT_S24_LE |\ 167 SNDRV_PCM_FMTBIT_S32_LE) 168 169 static struct snd_soc_dai_driver mtk_dai_gain_driver[] = { 170 { 171 .name = "HW Gain 1", 172 .id = MT8186_DAI_HW_GAIN_1, 173 .playback = { 174 .stream_name = "HW Gain 1 In", 175 .channels_min = 1, 176 .channels_max = 2, 177 .rates = MTK_HW_GAIN_RATES, 178 .formats = MTK_HW_GAIN_FORMATS, 179 }, 180 .capture = { 181 .stream_name = "HW Gain 1 Out", 182 .channels_min = 1, 183 .channels_max = 2, 184 .rates = MTK_HW_GAIN_RATES, 185 .formats = MTK_HW_GAIN_FORMATS, 186 }, 187 .ops = &mtk_dai_gain_ops, 188 .symmetric_rate = 1, 189 .symmetric_channels = 1, 190 .symmetric_sample_bits = 1, 191 }, 192 { 193 .name = "HW Gain 2", 194 .id = MT8186_DAI_HW_GAIN_2, 195 .playback = { 196 .stream_name = "HW Gain 2 In", 197 .channels_min = 1, 198 .channels_max = 2, 199 .rates = MTK_HW_GAIN_RATES, 200 .formats = MTK_HW_GAIN_FORMATS, 201 }, 202 .capture = { 203 .stream_name = "HW Gain 2 Out", 204 .channels_min = 1, 205 .channels_max = 2, 206 .rates = MTK_HW_GAIN_RATES, 207 .formats = MTK_HW_GAIN_FORMATS, 208 }, 209 .ops = &mtk_dai_gain_ops, 210 .symmetric_rate = 1, 211 .symmetric_channels = 1, 212 .symmetric_sample_bits = 1, 213 }, 214 }; 215 216 int mt8186_dai_hw_gain_register(struct mtk_base_afe *afe) 217 { 218 struct mtk_base_afe_dai *dai; 219 220 dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); 221 if (!dai) 222 return -ENOMEM; 223 224 list_add(&dai->list, &afe->sub_dais); 225 226 dai->dai_drivers = mtk_dai_gain_driver; 227 dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_gain_driver); 228 229 dai->controls = mtk_hw_gain_controls; 230 dai->num_controls = ARRAY_SIZE(mtk_hw_gain_controls); 231 dai->dapm_widgets = mtk_dai_hw_gain_widgets; 232 dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_hw_gain_widgets); 233 dai->dapm_routes = mtk_dai_hw_gain_routes; 234 dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_hw_gain_routes); 235 return 0; 236 } 237