1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // MediaTek ALSA SoC Audio DAI I2S Control 4 // 5 // Copyright (c) 2022 MediaTek Inc. 6 // Author: Jiaxin Yu <jiaxin.yu@mediatek.com> 7 8 #include <linux/regmap.h> 9 #include <sound/pcm_params.h> 10 #include "mt8186-afe-common.h" 11 #include "mt8186-afe-gpio.h" 12 #include "mt8186-interconnection.h" 13 14 struct mtk_afe_pcm_priv { 15 unsigned int id; 16 unsigned int fmt; 17 unsigned int bck_invert; 18 unsigned int lck_invert; 19 }; 20 21 enum aud_tx_lch_rpt { 22 AUD_TX_LCH_RPT_NO_REPEAT = 0, 23 AUD_TX_LCH_RPT_REPEAT = 1 24 }; 25 26 enum aud_vbt_16k_mode { 27 AUD_VBT_16K_MODE_DISABLE = 0, 28 AUD_VBT_16K_MODE_ENABLE = 1 29 }; 30 31 enum aud_ext_modem { 32 AUD_EXT_MODEM_SELECT_INTERNAL = 0, 33 AUD_EXT_MODEM_SELECT_EXTERNAL = 1 34 }; 35 36 enum aud_pcm_sync_type { 37 /* bck sync length = 1 */ 38 AUD_PCM_ONE_BCK_CYCLE_SYNC = 0, 39 /* bck sync length = PCM_INTF_CON1[9:13] */ 40 AUD_PCM_EXTENDED_BCK_CYCLE_SYNC = 1 41 }; 42 43 enum aud_bt_mode { 44 AUD_BT_MODE_DUAL_MIC_ON_TX = 0, 45 AUD_BT_MODE_SINGLE_MIC_ON_TX = 1 46 }; 47 48 enum aud_pcm_afifo_src { 49 /* slave mode & external modem uses different crystal */ 50 AUD_PCM_AFIFO_ASRC = 0, 51 /* slave mode & external modem uses the same crystal */ 52 AUD_PCM_AFIFO_AFIFO = 1 53 }; 54 55 enum aud_pcm_clock_source { 56 AUD_PCM_CLOCK_MASTER_MODE = 0, 57 AUD_PCM_CLOCK_SLAVE_MODE = 1 58 }; 59 60 enum aud_pcm_wlen { 61 AUD_PCM_WLEN_PCM_32_BCK_CYCLES = 0, 62 AUD_PCM_WLEN_PCM_64_BCK_CYCLES = 1 63 }; 64 65 enum aud_pcm_24bit { 66 AUD_PCM_24BIT_PCM_16_BITS = 0, 67 AUD_PCM_24BIT_PCM_24_BITS = 1 68 }; 69 70 enum aud_pcm_mode { 71 AUD_PCM_MODE_PCM_MODE_8K = 0, 72 AUD_PCM_MODE_PCM_MODE_16K = 1, 73 AUD_PCM_MODE_PCM_MODE_32K = 2, 74 AUD_PCM_MODE_PCM_MODE_48K = 3, 75 }; 76 77 enum aud_pcm_fmt { 78 AUD_PCM_FMT_I2S = 0, 79 AUD_PCM_FMT_EIAJ = 1, 80 AUD_PCM_FMT_PCM_MODE_A = 2, 81 AUD_PCM_FMT_PCM_MODE_B = 3 82 }; 83 84 enum aud_bclk_out_inv { 85 AUD_BCLK_OUT_INV_NO_INVERSE = 0, 86 AUD_BCLK_OUT_INV_INVERSE = 1 87 }; 88 89 enum aud_lrclk_out_inv { 90 AUD_LRCLK_OUT_INV_NO_INVERSE = 0, 91 AUD_LRCLK_OUT_INV_INVERSE = 1 92 }; 93 94 enum aud_pcm_en { 95 AUD_PCM_EN_DISABLE = 0, 96 AUD_PCM_EN_ENABLE = 1 97 }; 98 99 /* dai component */ 100 static const struct snd_kcontrol_new mtk_pcm_1_playback_ch1_mix[] = { 101 SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN7, 102 I_ADDA_UL_CH1, 1, 0), 103 SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN7, 104 I_DL2_CH1, 1, 0), 105 SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN7_1, 106 I_DL4_CH1, 1, 0), 107 }; 108 109 static const struct snd_kcontrol_new mtk_pcm_1_playback_ch2_mix[] = { 110 SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN8, 111 I_ADDA_UL_CH2, 1, 0), 112 SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN8, 113 I_DL2_CH2, 1, 0), 114 SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN8_1, 115 I_DL4_CH2, 1, 0), 116 }; 117 118 static int mtk_pcm_en_event(struct snd_soc_dapm_widget *w, 119 struct snd_kcontrol *kcontrol, 120 int event) 121 { 122 struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); 123 struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); 124 125 dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n", 126 __func__, w->name, event); 127 128 switch (event) { 129 case SND_SOC_DAPM_PRE_PMU: 130 mt8186_afe_gpio_request(afe->dev, true, MT8186_DAI_PCM, 0); 131 break; 132 case SND_SOC_DAPM_POST_PMD: 133 mt8186_afe_gpio_request(afe->dev, false, MT8186_DAI_PCM, 0); 134 break; 135 } 136 137 return 0; 138 } 139 140 /* pcm in/out lpbk */ 141 static const char * const pcm_lpbk_mux_map[] = { 142 "Normal", "Lpbk", 143 }; 144 145 static int pcm_lpbk_mux_map_value[] = { 146 0, 1, 147 }; 148 149 static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(pcm_in_lpbk_mux_map_enum, 150 PCM_INTF_CON1, 151 PCM_I2S_PCM_LOOPBACK_SFT, 152 1, 153 pcm_lpbk_mux_map, 154 pcm_lpbk_mux_map_value); 155 156 static const struct snd_kcontrol_new pcm_in_lpbk_mux_control = 157 SOC_DAPM_ENUM("PCM In Lpbk Select", pcm_in_lpbk_mux_map_enum); 158 159 static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(pcm_out_lpbk_mux_map_enum, 160 PCM_INTF_CON1, 161 PCM_I2S_PCM_LOOPBACK_SFT, 162 1, 163 pcm_lpbk_mux_map, 164 pcm_lpbk_mux_map_value); 165 166 static const struct snd_kcontrol_new pcm_out_lpbk_mux_control = 167 SOC_DAPM_ENUM("PCM Out Lpbk Select", pcm_out_lpbk_mux_map_enum); 168 169 static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = { 170 /* inter-connections */ 171 SND_SOC_DAPM_MIXER("PCM_1_PB_CH1", SND_SOC_NOPM, 0, 0, 172 mtk_pcm_1_playback_ch1_mix, 173 ARRAY_SIZE(mtk_pcm_1_playback_ch1_mix)), 174 SND_SOC_DAPM_MIXER("PCM_1_PB_CH2", SND_SOC_NOPM, 0, 0, 175 mtk_pcm_1_playback_ch2_mix, 176 ARRAY_SIZE(mtk_pcm_1_playback_ch2_mix)), 177 178 SND_SOC_DAPM_SUPPLY("PCM_1_EN", 179 PCM_INTF_CON1, PCM_EN_SFT, 0, 180 mtk_pcm_en_event, 181 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), 182 183 /* pcm in lpbk */ 184 SND_SOC_DAPM_MUX("PCM_In_Lpbk_Mux", 185 SND_SOC_NOPM, 0, 0, &pcm_in_lpbk_mux_control), 186 187 /* pcm out lpbk */ 188 SND_SOC_DAPM_MUX("PCM_Out_Lpbk_Mux", 189 SND_SOC_NOPM, 0, 0, &pcm_out_lpbk_mux_control), 190 }; 191 192 static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = { 193 {"PCM 1 Playback", NULL, "PCM_1_PB_CH1"}, 194 {"PCM 1 Playback", NULL, "PCM_1_PB_CH2"}, 195 196 {"PCM 1 Playback", NULL, "PCM_1_EN"}, 197 {"PCM 1 Capture", NULL, "PCM_1_EN"}, 198 199 {"PCM_1_PB_CH1", "DL2_CH1 Switch", "DL2"}, 200 {"PCM_1_PB_CH2", "DL2_CH2 Switch", "DL2"}, 201 202 {"PCM_1_PB_CH1", "DL4_CH1 Switch", "DL4"}, 203 {"PCM_1_PB_CH2", "DL4_CH2 Switch", "DL4"}, 204 205 /* pcm out lpbk */ 206 {"PCM_Out_Lpbk_Mux", "Lpbk", "PCM 1 Playback"}, 207 {"I2S0", NULL, "PCM_Out_Lpbk_Mux"}, 208 209 /* pcm in lpbk */ 210 {"PCM_In_Lpbk_Mux", "Lpbk", "PCM 1 Capture"}, 211 {"I2S3", NULL, "PCM_In_Lpbk_Mux"}, 212 }; 213 214 /* dai ops */ 215 static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, 216 struct snd_pcm_hw_params *params, 217 struct snd_soc_dai *dai) 218 { 219 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); 220 struct mt8186_afe_private *afe_priv = afe->platform_priv; 221 int pcm_id = dai->id; 222 struct mtk_afe_pcm_priv *pcm_priv = afe_priv->dai_priv[pcm_id]; 223 unsigned int rate = params_rate(params); 224 unsigned int rate_reg = mt8186_rate_transform(afe->dev, rate, dai->id); 225 snd_pcm_format_t format = params_format(params); 226 unsigned int data_width = 227 snd_pcm_format_width(format); 228 unsigned int wlen_width = 229 snd_pcm_format_physical_width(format); 230 unsigned int pcm_con = 0; 231 232 dev_dbg(afe->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n", 233 __func__, dai->id, substream->stream, dai->playback_widget->active, 234 dai->capture_widget->active); 235 dev_dbg(afe->dev, "%s(), rate %d, rate_reg %d, data_width %d, wlen_width %d\n", 236 __func__, rate, rate_reg, data_width, wlen_width); 237 238 if (dai->playback_widget->active || dai->capture_widget->active) 239 return 0; 240 241 switch (dai->id) { 242 case MT8186_DAI_PCM: 243 pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM_TX_LCH_RPT_SFT; 244 pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM_VBT_16K_MODE_SFT; 245 pcm_con |= AUD_EXT_MODEM_SELECT_EXTERNAL << PCM_EXT_MODEM_SFT; 246 pcm_con |= AUD_PCM_ONE_BCK_CYCLE_SYNC << PCM_SYNC_TYPE_SFT; 247 pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM_BT_MODE_SFT; 248 pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM_BYP_ASRC_SFT; 249 pcm_con |= AUD_PCM_CLOCK_MASTER_MODE << PCM_SLAVE_SFT; 250 pcm_con |= 0 << PCM_SYNC_LENGTH_SFT; 251 252 /* sampling rate */ 253 pcm_con |= rate_reg << PCM_MODE_SFT; 254 255 /* format */ 256 pcm_con |= pcm_priv->fmt << PCM_FMT_SFT; 257 258 /* 24bit data width */ 259 if (data_width > 16) 260 pcm_con |= AUD_PCM_24BIT_PCM_24_BITS << PCM_24BIT_SFT; 261 else 262 pcm_con |= AUD_PCM_24BIT_PCM_16_BITS << PCM_24BIT_SFT; 263 264 /* wlen width*/ 265 if (wlen_width > 16) 266 pcm_con |= AUD_PCM_WLEN_PCM_64_BCK_CYCLES << PCM_WLEN_SFT; 267 else 268 pcm_con |= AUD_PCM_WLEN_PCM_32_BCK_CYCLES << PCM_WLEN_SFT; 269 270 /* clock invert */ 271 pcm_con |= pcm_priv->lck_invert << PCM_SYNC_OUT_INV_SFT; 272 pcm_con |= pcm_priv->bck_invert << PCM_BCLK_OUT_INV_SFT; 273 274 regmap_update_bits(afe->regmap, PCM_INTF_CON1, 0xfffffffe, pcm_con); 275 break; 276 default: 277 dev_err(afe->dev, "%s(), id %d not support\n", __func__, dai->id); 278 return -EINVAL; 279 } 280 281 return 0; 282 } 283 284 static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 285 { 286 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); 287 struct mt8186_afe_private *afe_priv = afe->platform_priv; 288 struct mtk_afe_pcm_priv *pcm_priv = afe_priv->dai_priv[dai->id]; 289 290 if (!pcm_priv) { 291 dev_err(afe->dev, "%s(), tdm_priv == NULL", __func__); 292 return -EINVAL; 293 } 294 295 /* DAI mode*/ 296 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 297 case SND_SOC_DAIFMT_I2S: 298 pcm_priv->fmt = AUD_PCM_FMT_I2S; 299 break; 300 case SND_SOC_DAIFMT_LEFT_J: 301 pcm_priv->fmt = AUD_PCM_FMT_EIAJ; 302 break; 303 case SND_SOC_DAIFMT_DSP_A: 304 pcm_priv->fmt = AUD_PCM_FMT_PCM_MODE_A; 305 break; 306 case SND_SOC_DAIFMT_DSP_B: 307 pcm_priv->fmt = AUD_PCM_FMT_PCM_MODE_B; 308 break; 309 default: 310 pcm_priv->fmt = AUD_PCM_FMT_I2S; 311 } 312 313 /* DAI clock inversion*/ 314 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 315 case SND_SOC_DAIFMT_NB_NF: 316 pcm_priv->bck_invert = AUD_BCLK_OUT_INV_NO_INVERSE; 317 pcm_priv->lck_invert = AUD_LRCLK_OUT_INV_NO_INVERSE; 318 break; 319 case SND_SOC_DAIFMT_NB_IF: 320 pcm_priv->bck_invert = AUD_BCLK_OUT_INV_NO_INVERSE; 321 pcm_priv->lck_invert = AUD_BCLK_OUT_INV_INVERSE; 322 break; 323 case SND_SOC_DAIFMT_IB_NF: 324 pcm_priv->bck_invert = AUD_BCLK_OUT_INV_INVERSE; 325 pcm_priv->lck_invert = AUD_LRCLK_OUT_INV_NO_INVERSE; 326 break; 327 case SND_SOC_DAIFMT_IB_IF: 328 pcm_priv->bck_invert = AUD_BCLK_OUT_INV_INVERSE; 329 pcm_priv->lck_invert = AUD_BCLK_OUT_INV_INVERSE; 330 break; 331 default: 332 pcm_priv->bck_invert = AUD_BCLK_OUT_INV_NO_INVERSE; 333 pcm_priv->lck_invert = AUD_LRCLK_OUT_INV_NO_INVERSE; 334 break; 335 } 336 337 return 0; 338 } 339 340 static const struct snd_soc_dai_ops mtk_dai_pcm_ops = { 341 .hw_params = mtk_dai_pcm_hw_params, 342 .set_fmt = mtk_dai_pcm_set_fmt, 343 }; 344 345 /* dai driver */ 346 #define MTK_PCM_RATES (SNDRV_PCM_RATE_8000 |\ 347 SNDRV_PCM_RATE_16000 |\ 348 SNDRV_PCM_RATE_32000 |\ 349 SNDRV_PCM_RATE_48000) 350 351 #define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ 352 SNDRV_PCM_FMTBIT_S24_LE |\ 353 SNDRV_PCM_FMTBIT_S32_LE) 354 355 static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = { 356 { 357 .name = "PCM 1", 358 .id = MT8186_DAI_PCM, 359 .playback = { 360 .stream_name = "PCM 1 Playback", 361 .channels_min = 1, 362 .channels_max = 2, 363 .rates = MTK_PCM_RATES, 364 .formats = MTK_PCM_FORMATS, 365 }, 366 .capture = { 367 .stream_name = "PCM 1 Capture", 368 .channels_min = 1, 369 .channels_max = 2, 370 .rates = MTK_PCM_RATES, 371 .formats = MTK_PCM_FORMATS, 372 }, 373 .ops = &mtk_dai_pcm_ops, 374 .symmetric_rate = 1, 375 .symmetric_sample_bits = 1, 376 }, 377 }; 378 379 static struct mtk_afe_pcm_priv *init_pcm_priv_data(struct mtk_base_afe *afe) 380 { 381 struct mtk_afe_pcm_priv *pcm_priv; 382 383 pcm_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_afe_pcm_priv), 384 GFP_KERNEL); 385 if (!pcm_priv) 386 return NULL; 387 388 pcm_priv->id = MT8186_DAI_PCM; 389 pcm_priv->fmt = AUD_PCM_FMT_I2S; 390 pcm_priv->bck_invert = AUD_BCLK_OUT_INV_NO_INVERSE; 391 pcm_priv->lck_invert = AUD_LRCLK_OUT_INV_NO_INVERSE; 392 393 return pcm_priv; 394 } 395 396 int mt8186_dai_pcm_register(struct mtk_base_afe *afe) 397 { 398 struct mt8186_afe_private *afe_priv = afe->platform_priv; 399 struct mtk_afe_pcm_priv *pcm_priv; 400 struct mtk_base_afe_dai *dai; 401 402 dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); 403 if (!dai) 404 return -ENOMEM; 405 406 list_add(&dai->list, &afe->sub_dais); 407 408 dai->dai_drivers = mtk_dai_pcm_driver; 409 dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver); 410 411 dai->dapm_widgets = mtk_dai_pcm_widgets; 412 dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets); 413 dai->dapm_routes = mtk_dai_pcm_routes; 414 dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes); 415 416 pcm_priv = init_pcm_priv_data(afe); 417 if (!pcm_priv) 418 return -ENOMEM; 419 420 afe_priv->dai_priv[MT8186_DAI_PCM] = pcm_priv; 421 422 return 0; 423 } 424