Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ASoC: mediatek: mt8189: support PCM in platform driver

Add mt8189 PCM DAI driver support.

Signed-off-by: Cyril Chao <Cyril.Chao@mediatek.com>
Link: https://patch.msgid.link/20251031073216.8662-7-Cyril.Chao@mediatek.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Cyril Chao and committed by
Mark Brown
402ff043 9f202872

+332
+332
sound/soc/mediatek/mt8189/mt8189-dai-pcm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * MediaTek ALSA SoC Audio DAI I2S Control 4 + * 5 + * Copyright (c) 2025 MediaTek Inc. 6 + * Author: Darren Ye <darren.ye@mediatek.com> 7 + */ 8 + 9 + #include <linux/regmap.h> 10 + 11 + #include <sound/pcm_params.h> 12 + 13 + #include "mt8189-afe-common.h" 14 + #include "mt8189-interconnection.h" 15 + #include "mt8189-afe-clk.h" 16 + 17 + enum AUD_TX_LCH_RPT { 18 + AUD_TX_LCH_RPT_NO_REPEAT, 19 + AUD_TX_LCH_RPT_REPEAT 20 + }; 21 + 22 + enum AUD_VBT_16K_MODE { 23 + AUD_VBT_16K_MODE_DISABLE, 24 + AUD_VBT_16K_MODE_ENABLE 25 + }; 26 + 27 + enum AUD_EXT_MODEM { 28 + AUD_EXT_MODEM_SELECT_INTERNAL, 29 + AUD_EXT_MODEM_SELECT_EXTERNAL 30 + }; 31 + 32 + enum AUD_PCM_SYNC_TYPE { 33 + /* bck sync length = 1 */ 34 + AUD_PCM_ONE_BCK_CYCLE_SYNC, 35 + /* bck sync length = PCM_INTF_CON1[9:13] */ 36 + AUD_PCM_EXTENDED_BCK_CYCLE_SYNC 37 + }; 38 + 39 + enum AUD_BT_MODE { 40 + AUD_BT_MODE_DUAL_MIC_ON_TX, 41 + AUD_BT_MODE_SINGLE_MIC_ON_TX 42 + }; 43 + 44 + enum AUD_PCM_AFIFO_SRC { 45 + /* slave mode & external modem uses different crystal */ 46 + AUD_PCM_AFIFO_ASRC, 47 + /* slave mode & external modem uses the same crystal */ 48 + AUD_PCM_AFIFO_AFIFO 49 + }; 50 + 51 + enum AUD_PCM_CLOCK_SOURCE { 52 + AUD_PCM_CLOCK_MASTER_MODE, 53 + AUD_PCM_CLOCK_SLAVE_MODE 54 + }; 55 + 56 + enum AUD_PCM_WLEN { 57 + AUD_PCM_WLEN_PCM_32_BCK_CYCLES, 58 + AUD_PCM_WLEN_PCM_64_BCK_CYCLES 59 + }; 60 + 61 + enum AUD_PCM_MODE { 62 + AUD_PCM_MODE_PCM_MODE_8K, 63 + AUD_PCM_MODE_PCM_MODE_16K, 64 + AUD_PCM_MODE_PCM_MODE_32K, 65 + AUD_PCM_MODE_PCM_MODE_48K 66 + }; 67 + 68 + enum AUD_PCM_FMT { 69 + AUD_PCM_FMT_I2S, 70 + AUD_PCM_FMT_EIAJ, 71 + AUD_PCM_FMT_PCM_MODE_A, 72 + AUD_PCM_FMT_PCM_MODE_B 73 + }; 74 + 75 + enum AUD_BCLK_OUT_INV { 76 + AUD_BCLK_OUT_INV_NO_INVERSE, 77 + AUD_BCLK_OUT_INV_INVERSE 78 + }; 79 + 80 + enum AUD_PCM_EN { 81 + AUD_PCM_EN_DISABLE, 82 + AUD_PCM_EN_ENABLE 83 + }; 84 + 85 + enum AUD_PCM1_1X_EN_DOMAIN { 86 + HOPPING_26M, 87 + APLL, 88 + SLAVE = 6 89 + }; 90 + 91 + enum AUD_PCM1_1X_EN_SLAVE_MODE { 92 + PCM0_SLAVE_1X_EN, 93 + PCM1_SLAVE_1X_EN 94 + }; 95 + 96 + enum { 97 + PCM_8K, 98 + PCM_16K = 4, 99 + PCM_32K = 8, 100 + PCM_48K = 10 101 + }; 102 + 103 + static unsigned int pcm_1x_rate_transform(struct device *dev, 104 + unsigned int rate) 105 + { 106 + switch (rate) { 107 + case 8000: 108 + return PCM_8K; 109 + case 16000: 110 + return PCM_16K; 111 + case 32000: 112 + return PCM_32K; 113 + case 48000: 114 + return PCM_48K; 115 + default: 116 + dev_warn(dev, "rate %u invalid, use %d!!!\n", 117 + rate, PCM_48K); 118 + return PCM_48K; 119 + } 120 + } 121 + 122 + static unsigned int pcm_rate_transform(struct device *dev, 123 + unsigned int rate) 124 + { 125 + switch (rate) { 126 + case 8000: 127 + return MTK_AFE_PCM_RATE_8K; 128 + case 16000: 129 + return MTK_AFE_PCM_RATE_16K; 130 + case 32000: 131 + return MTK_AFE_PCM_RATE_32K; 132 + case 48000: 133 + return MTK_AFE_PCM_RATE_48K; 134 + default: 135 + dev_warn(dev, "rate %u invalid, use %d\n", 136 + rate, MTK_AFE_PCM_RATE_48K); 137 + return MTK_AFE_PCM_RATE_48K; 138 + } 139 + } 140 + 141 + /* dai component */ 142 + static const struct snd_kcontrol_new mtk_pcm_0_playback_ch1_mix[] = { 143 + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN096_0, 144 + I_ADDA_UL_CH1, 1, 0), 145 + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN096_1, 146 + I_DL2_CH1, 1, 0), 147 + SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN096_1, 148 + I_DL_24CH_CH1, 1, 0), 149 + }; 150 + 151 + static const struct snd_kcontrol_new mtk_pcm_0_playback_ch2_mix[] = { 152 + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN097_0, 153 + I_ADDA_UL_CH2, 1, 0), 154 + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN097_1, 155 + I_DL2_CH2, 1, 0), 156 + SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH2", AFE_CONN097_1, 157 + I_DL_24CH_CH2, 1, 0), 158 + }; 159 + 160 + static const struct snd_kcontrol_new mtk_pcm_0_playback_ch4_mix[] = { 161 + SOC_DAPM_SINGLE_AUTODISABLE("I2SIN1_CH1", AFE_CONN099_4, 162 + I_I2SIN1_CH1, 1, 0), 163 + SOC_DAPM_SINGLE_AUTODISABLE("I2SIN1_CH2", AFE_CONN099_4, 164 + I_I2SIN1_CH2, 1, 0), 165 + SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN099_1, 166 + I_DL0_CH1, 1, 0), 167 + SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN099_1, 168 + I_DL_24CH_CH1, 1, 0), 169 + }; 170 + 171 + static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = { 172 + /* inter-connections */ 173 + SND_SOC_DAPM_MIXER("PCM_0_PB_CH1", SND_SOC_NOPM, 0, 0, 174 + mtk_pcm_0_playback_ch1_mix, 175 + ARRAY_SIZE(mtk_pcm_0_playback_ch1_mix)), 176 + SND_SOC_DAPM_MIXER("PCM_0_PB_CH2", SND_SOC_NOPM, 0, 0, 177 + mtk_pcm_0_playback_ch2_mix, 178 + ARRAY_SIZE(mtk_pcm_0_playback_ch2_mix)), 179 + SND_SOC_DAPM_MIXER("PCM_0_PB_CH4", SND_SOC_NOPM, 0, 0, 180 + mtk_pcm_0_playback_ch4_mix, 181 + ARRAY_SIZE(mtk_pcm_0_playback_ch4_mix)), 182 + 183 + SND_SOC_DAPM_SUPPLY("PCM_0_EN", 184 + AFE_PCM0_INTF_CON0, PCM0_EN_SFT, 0, 185 + NULL, 0), 186 + 187 + SND_SOC_DAPM_SUPPLY("PCM0_CG", AUDIO_TOP_CON0, PDN_PCM0_SFT, 1, 188 + NULL, 0), 189 + 190 + SND_SOC_DAPM_INPUT("AFE_PCM_INPUT"), 191 + SND_SOC_DAPM_OUTPUT("AFE_PCM_OUTPUT"), 192 + }; 193 + 194 + static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = { 195 + {"PCM 0 Playback", NULL, "PCM_0_PB_CH1"}, 196 + {"PCM 0 Playback", NULL, "PCM_0_PB_CH2"}, 197 + {"PCM 0 Playback", NULL, "PCM_0_PB_CH4"}, 198 + 199 + {"PCM 0 Playback", NULL, "PCM_0_EN"}, 200 + {"PCM 0 Capture", NULL, "PCM_0_EN"}, 201 + {"PCM 0 Playback", NULL, "PCM0_CG"}, 202 + {"PCM 0 Capture", NULL, "PCM0_CG"}, 203 + 204 + {"AFE_PCM_OUTPUT", NULL, "PCM 0 Playback"}, 205 + {"PCM 0 Capture", NULL, "AFE_PCM_INPUT"}, 206 + 207 + {"PCM_0_PB_CH1", "DL2_CH1", "DL2"}, 208 + {"PCM_0_PB_CH2", "DL2_CH2", "DL2"}, 209 + {"PCM_0_PB_CH4", "DL0_CH1", "DL0"}, 210 + 211 + {"PCM_0_PB_CH1", "DL_24CH_CH1", "DL_24CH"}, 212 + {"PCM_0_PB_CH2", "DL_24CH_CH2", "DL_24CH"}, 213 + {"PCM_0_PB_CH4", "DL_24CH_CH1", "DL_24CH"}, 214 + }; 215 + 216 + /* dai ops */ 217 + static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, 218 + struct snd_pcm_hw_params *params, 219 + struct snd_soc_dai *dai) 220 + { 221 + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); 222 + unsigned int rate = params_rate(params); 223 + unsigned int rate_reg = pcm_rate_transform(afe->dev, rate); 224 + unsigned int x_rate_reg = pcm_1x_rate_transform(afe->dev, rate); 225 + unsigned int pcm_con0; 226 + unsigned int pcm_con1; 227 + unsigned int playback_active = 0; 228 + unsigned int capture_active = 0; 229 + struct snd_soc_dapm_widget *playback_widget = 230 + snd_soc_dai_get_widget(dai, SNDRV_PCM_STREAM_PLAYBACK); 231 + struct snd_soc_dapm_widget *capture_widget = 232 + snd_soc_dai_get_widget(dai, SNDRV_PCM_STREAM_CAPTURE); 233 + 234 + if (playback_widget) 235 + playback_active = playback_widget->active; 236 + if (capture_widget) 237 + capture_active = capture_widget->active; 238 + dev_dbg(afe->dev, 239 + "id %d, stream %d, rate %d, rate_reg %d, active p %d, c %d\n", 240 + dai->id, substream->stream, rate, rate_reg, 241 + playback_active, capture_active); 242 + 243 + if (playback_active || capture_active) 244 + return 0; 245 + switch (dai->id) { 246 + case MT8189_DAI_PCM_0: 247 + pcm_con0 = AUD_BCLK_OUT_INV_NO_INVERSE << PCM0_BCLK_OUT_INV_SFT; 248 + pcm_con0 |= AUD_TX_LCH_RPT_NO_REPEAT << PCM0_TX_LCH_RPT_SFT; 249 + pcm_con0 |= AUD_VBT_16K_MODE_DISABLE << PCM0_VBT_16K_MODE_SFT; 250 + pcm_con0 |= 0 << PCM0_SYNC_LENGTH_SFT; 251 + pcm_con0 |= AUD_PCM_ONE_BCK_CYCLE_SYNC << PCM0_SYNC_TYPE_SFT; 252 + pcm_con0 |= AUD_PCM_AFIFO_AFIFO << PCM0_BYP_ASRC_SFT; 253 + pcm_con0 |= AUD_PCM_CLOCK_MASTER_MODE << PCM0_SLAVE_SFT; 254 + pcm_con0 |= rate_reg << PCM0_MODE_SFT; 255 + pcm_con0 |= AUD_PCM_FMT_I2S << PCM0_FMT_SFT; 256 + 257 + pcm_con1 = AUD_EXT_MODEM_SELECT_INTERNAL << PCM0_EXT_MODEM_SFT; 258 + pcm_con1 |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM0_BT_MODE_SFT; 259 + pcm_con1 |= HOPPING_26M << PCM0_1X_EN_DOMAIN_SFT; 260 + pcm_con1 |= x_rate_reg << PCM0_1X_EN_MODE_SFT; 261 + 262 + regmap_update_bits(afe->regmap, AFE_PCM0_INTF_CON0, 263 + ~(unsigned int)PCM0_EN_MASK_SFT, pcm_con0); 264 + regmap_update_bits(afe->regmap, AFE_PCM0_INTF_CON1, 265 + AFE_PCM0_INTF_CON1_MASK_MON_MASK_SFT, 266 + pcm_con1); 267 + break; 268 + default: 269 + dev_err(afe->dev, "%s(), id %d not support\n", 270 + __func__, dai->id); 271 + return -EINVAL; 272 + } 273 + return 0; 274 + } 275 + 276 + static const struct snd_soc_dai_ops mtk_dai_pcm_ops = { 277 + .hw_params = mtk_dai_pcm_hw_params, 278 + }; 279 + 280 + /* dai driver */ 281 + #define MTK_PCM_RATES (SNDRV_PCM_RATE_8000 |\ 282 + SNDRV_PCM_RATE_16000 |\ 283 + SNDRV_PCM_RATE_32000 |\ 284 + SNDRV_PCM_RATE_48000) 285 + 286 + #define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ 287 + SNDRV_PCM_FMTBIT_S24_LE |\ 288 + SNDRV_PCM_FMTBIT_S32_LE) 289 + 290 + static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = { 291 + { 292 + .name = "PCM 0", 293 + .id = MT8189_DAI_PCM_0, 294 + .playback = { 295 + .stream_name = "PCM 0 Playback", 296 + .channels_min = 1, 297 + .channels_max = 2, 298 + .rates = MTK_PCM_RATES, 299 + .formats = MTK_PCM_FORMATS, 300 + }, 301 + .capture = { 302 + .stream_name = "PCM 0 Capture", 303 + .channels_min = 1, 304 + .channels_max = 2, 305 + .rates = MTK_PCM_RATES, 306 + .formats = MTK_PCM_FORMATS, 307 + }, 308 + .ops = &mtk_dai_pcm_ops, 309 + .symmetric_rate = 1, 310 + .symmetric_sample_bits = 1, 311 + }, 312 + }; 313 + 314 + int mt8189_dai_pcm_register(struct mtk_base_afe *afe) 315 + { 316 + struct mtk_base_afe_dai *dai; 317 + 318 + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); 319 + if (!dai) 320 + return -ENOMEM; 321 + 322 + dai->dai_drivers = mtk_dai_pcm_driver; 323 + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver); 324 + dai->dapm_widgets = mtk_dai_pcm_widgets; 325 + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets); 326 + dai->dapm_routes = mtk_dai_pcm_routes; 327 + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes); 328 + 329 + list_add(&dai->list, &afe->sub_dais); 330 + 331 + return 0; 332 + }