1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * MediaTek 8365 ALSA SoC Audio DAI ADDA Control 4 * 5 * Copyright (c) 2024 MediaTek Inc. 6 * Authors: Jia Zeng <jia.zeng@mediatek.com> 7 * Alexandre Mergnat <amergnat@baylibre.com> 8 */ 9 10 #include <linux/bitops.h> 11 #include <linux/regmap.h> 12 #include <sound/pcm_params.h> 13 #include "mt8365-afe-clk.h" 14 #include "mt8365-afe-common.h" 15 #include "../common/mtk-dai-adda-common.h" 16 17 static int adda_afe_on_ref_cnt; 18 19 /* DAI Drivers */ 20 21 static int mt8365_dai_set_adda_out(struct mtk_base_afe *afe, unsigned int rate) 22 { 23 unsigned int val; 24 25 if (rate == 8000 || rate == 16000) 26 val = AFE_ADDA_DL_VOICE_DATA; 27 else 28 val = 0; 29 30 val |= FIELD_PREP(AFE_ADDA_DL_SAMPLING_RATE, 31 mtk_adda_dl_rate_transform(afe, rate)); 32 val |= AFE_ADDA_DL_8X_UPSAMPLE | 33 AFE_ADDA_DL_MUTE_OFF_CH1 | 34 AFE_ADDA_DL_MUTE_OFF_CH2 | 35 AFE_ADDA_DL_DEGRADE_GAIN; 36 37 regmap_update_bits(afe->regmap, AFE_ADDA_PREDIS_CON0, 0xffffffff, 0); 38 regmap_update_bits(afe->regmap, AFE_ADDA_PREDIS_CON1, 0xffffffff, 0); 39 regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 0xffffffff, val); 40 /* SA suggest apply -0.3db to audio/speech path */ 41 regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON1, 42 0xffffffff, 0xf74f0000); 43 /* SA suggest use default value for sdm */ 44 regmap_update_bits(afe->regmap, AFE_ADDA_DL_SDM_DCCOMP_CON, 45 0xffffffff, 0x0700701e); 46 47 return 0; 48 } 49 50 static int mt8365_dai_set_adda_in(struct mtk_base_afe *afe, unsigned int rate) 51 { 52 unsigned int val; 53 54 val = FIELD_PREP(AFE_ADDA_UL_SAMPLING_RATE, 55 mtk_adda_ul_rate_transform(afe, rate)); 56 regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 57 AFE_ADDA_UL_SAMPLING_RATE, val); 58 /* Using Internal ADC */ 59 regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x0); 60 61 return 0; 62 } 63 64 int mt8365_dai_enable_adda_on(struct mtk_base_afe *afe) 65 { 66 unsigned long flags; 67 struct mt8365_afe_private *afe_priv = afe->platform_priv; 68 69 spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags); 70 71 adda_afe_on_ref_cnt++; 72 if (adda_afe_on_ref_cnt == 1) 73 regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0, 74 AFE_ADDA_UL_DL_ADDA_AFE_ON, 75 AFE_ADDA_UL_DL_ADDA_AFE_ON); 76 77 spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags); 78 79 return 0; 80 } 81 82 int mt8365_dai_disable_adda_on(struct mtk_base_afe *afe) 83 { 84 unsigned long flags; 85 struct mt8365_afe_private *afe_priv = afe->platform_priv; 86 87 spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags); 88 89 adda_afe_on_ref_cnt--; 90 if (adda_afe_on_ref_cnt == 0) 91 regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0, 92 AFE_ADDA_UL_DL_ADDA_AFE_ON, 93 ~AFE_ADDA_UL_DL_ADDA_AFE_ON); 94 else if (adda_afe_on_ref_cnt < 0) { 95 adda_afe_on_ref_cnt = 0; 96 dev_warn(afe->dev, "Abnormal adda_on ref count. Force it to 0\n"); 97 } 98 99 spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags); 100 101 return 0; 102 } 103 104 static void mt8365_dai_set_adda_out_enable(struct mtk_base_afe *afe, 105 bool enable) 106 { 107 regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 0x1, enable); 108 109 if (enable) 110 mt8365_dai_enable_adda_on(afe); 111 else 112 mt8365_dai_disable_adda_on(afe); 113 } 114 115 static void mt8365_dai_set_adda_in_enable(struct mtk_base_afe *afe, bool enable) 116 { 117 if (enable) { 118 regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 0x1, 0x1); 119 mt8365_dai_enable_adda_on(afe); 120 /* enable aud_pad_top fifo */ 121 regmap_update_bits(afe->regmap, AFE_AUD_PAD_TOP, 122 0xffffffff, 0x31); 123 } else { 124 /* disable aud_pad_top fifo */ 125 regmap_update_bits(afe->regmap, AFE_AUD_PAD_TOP, 126 0xffffffff, 0x30); 127 regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 0x1, 0x0); 128 /* de suggest disable ADDA_UL_SRC at least wait 125us */ 129 usleep_range(150, 300); 130 mt8365_dai_disable_adda_on(afe); 131 } 132 } 133 134 static int mt8365_dai_int_adda_startup(struct snd_pcm_substream *substream, 135 struct snd_soc_dai *dai) 136 { 137 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); 138 unsigned int stream = substream->stream; 139 140 mt8365_afe_enable_main_clk(afe); 141 142 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 143 mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DAC); 144 mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DAC_PREDIS); 145 } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { 146 mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_ADC); 147 } 148 149 return 0; 150 } 151 152 static void mt8365_dai_int_adda_shutdown(struct snd_pcm_substream *substream, 153 struct snd_soc_dai *dai) 154 { 155 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); 156 struct mt8365_afe_private *afe_priv = afe->platform_priv; 157 struct mt8365_be_dai_data *be = 158 &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE]; 159 unsigned int stream = substream->stream; 160 161 if (be->prepared[stream]) { 162 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 163 mt8365_dai_set_adda_out_enable(afe, false); 164 mt8365_afe_set_i2s_out_enable(afe, false); 165 } else { 166 mt8365_dai_set_adda_in_enable(afe, false); 167 } 168 be->prepared[stream] = false; 169 } 170 171 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 172 mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DAC_PREDIS); 173 mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DAC); 174 } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { 175 mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_ADC); 176 } 177 178 mt8365_afe_disable_main_clk(afe); 179 } 180 181 static int mt8365_dai_int_adda_prepare(struct snd_pcm_substream *substream, 182 struct snd_soc_dai *dai) 183 { 184 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); 185 struct mt8365_afe_private *afe_priv = afe->platform_priv; 186 struct mt8365_be_dai_data *be = 187 &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE]; 188 unsigned int rate = substream->runtime->rate; 189 int bit_width = snd_pcm_format_width(substream->runtime->format); 190 int ret; 191 192 dev_info(afe->dev, "%s '%s' rate = %u\n", __func__, 193 snd_pcm_stream_str(substream), rate); 194 195 if (be->prepared[substream->stream]) { 196 dev_info(afe->dev, "%s '%s' prepared already\n", 197 __func__, snd_pcm_stream_str(substream)); 198 return 0; 199 } 200 201 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 202 ret = mt8365_dai_set_adda_out(afe, rate); 203 if (ret) 204 return ret; 205 206 ret = mt8365_afe_set_i2s_out(afe, rate, bit_width); 207 if (ret) 208 return ret; 209 210 mt8365_dai_set_adda_out_enable(afe, true); 211 mt8365_afe_set_i2s_out_enable(afe, true); 212 } else { 213 ret = mt8365_dai_set_adda_in(afe, rate); 214 if (ret) 215 return ret; 216 217 mt8365_dai_set_adda_in_enable(afe, true); 218 } 219 be->prepared[substream->stream] = true; 220 return 0; 221 } 222 223 static const struct snd_soc_dai_ops mt8365_afe_int_adda_ops = { 224 .startup = mt8365_dai_int_adda_startup, 225 .shutdown = mt8365_dai_int_adda_shutdown, 226 .prepare = mt8365_dai_int_adda_prepare, 227 }; 228 229 static struct snd_soc_dai_driver mtk_dai_adda_driver[] = { 230 { 231 .name = "INT ADDA", 232 .id = MT8365_AFE_IO_INT_ADDA, 233 .playback = { 234 .stream_name = "INT ADDA Playback", 235 .channels_min = 1, 236 .channels_max = 2, 237 .rates = SNDRV_PCM_RATE_8000_48000, 238 .formats = SNDRV_PCM_FMTBIT_S16_LE, 239 }, 240 .capture = { 241 .stream_name = "INT ADDA Capture", 242 .channels_min = 1, 243 .channels_max = 2, 244 .rates = SNDRV_PCM_RATE_16000 | 245 SNDRV_PCM_RATE_32000 | 246 SNDRV_PCM_RATE_48000, 247 .formats = SNDRV_PCM_FMTBIT_S16_LE | 248 SNDRV_PCM_FMTBIT_S32_LE, 249 }, 250 .ops = &mt8365_afe_int_adda_ops, 251 } 252 }; 253 254 /* DAI Controls */ 255 256 static const struct snd_kcontrol_new mtk_adda_dl_ch1_mix[] = { 257 SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1 Switch", AFE_CONN3, 258 10, 1, 0), 259 }; 260 261 static const struct snd_kcontrol_new mtk_adda_dl_ch2_mix[] = { 262 SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2 Switch", AFE_CONN4, 263 11, 1, 0), 264 }; 265 266 static const struct snd_kcontrol_new int_adda_o03_o04_enable_ctl = 267 SOC_DAPM_SINGLE_VIRT("Switch", 1); 268 269 /* DAI widget */ 270 271 static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = { 272 SND_SOC_DAPM_SWITCH("INT ADDA O03_O04", SND_SOC_NOPM, 0, 0, 273 &int_adda_o03_o04_enable_ctl), 274 /* inter-connections */ 275 SND_SOC_DAPM_MIXER("ADDA_DL_CH1", SND_SOC_NOPM, 0, 0, 276 mtk_adda_dl_ch1_mix, 277 ARRAY_SIZE(mtk_adda_dl_ch1_mix)), 278 SND_SOC_DAPM_MIXER("ADDA_DL_CH2", SND_SOC_NOPM, 0, 0, 279 mtk_adda_dl_ch2_mix, 280 ARRAY_SIZE(mtk_adda_dl_ch2_mix)), 281 }; 282 283 /* DAI route */ 284 285 static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = { 286 {"INT ADDA O03_O04", "Switch", "O03"}, 287 {"INT ADDA O03_O04", "Switch", "O04"}, 288 {"INT ADDA Playback", NULL, "INT ADDA O03_O04"}, 289 {"INT ADDA Playback", NULL, "ADDA_DL_CH1"}, 290 {"INT ADDA Playback", NULL, "ADDA_DL_CH2"}, 291 {"AIN Mux", "INT ADC", "INT ADDA Capture"}, 292 {"ADDA_DL_CH1", "GAIN1_OUT_CH1", "Hostless FM DL"}, 293 {"ADDA_DL_CH2", "GAIN1_OUT_CH2", "Hostless FM DL"}, 294 }; 295 296 int mt8365_dai_adda_register(struct mtk_base_afe *afe) 297 { 298 struct mtk_base_afe_dai *dai; 299 300 dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); 301 if (!dai) 302 return -ENOMEM; 303 list_add(&dai->list, &afe->sub_dais); 304 dai->dai_drivers = mtk_dai_adda_driver; 305 dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver); 306 dai->dapm_widgets = mtk_dai_adda_widgets; 307 dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets); 308 dai->dapm_routes = mtk_dai_adda_routes; 309 dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes); 310 return 0; 311 } 312
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.