1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * MediaTek 8365 ALSA SoC Audio DAI PCM 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 16 struct mt8365_pcm_intf_data { 17 bool slave_mode; 18 bool lrck_inv; 19 bool bck_inv; 20 unsigned int format; 21 }; 22 23 /* DAI Drivers */ 24 25 static void mt8365_dai_enable_pcm1(struct mtk_base_afe *afe) 26 { 27 regmap_update_bits(afe->regmap, PCM_INTF_CON1, 28 PCM_INTF_CON1_EN, PCM_INTF_CON1_EN); 29 } 30 31 static void mt8365_dai_disable_pcm1(struct mtk_base_afe *afe) 32 { 33 regmap_update_bits(afe->regmap, PCM_INTF_CON1, 34 PCM_INTF_CON1_EN, 0x0); 35 } 36 37 static int mt8365_dai_configure_pcm1(struct snd_pcm_substream *substream, 38 struct snd_soc_dai *dai) 39 { 40 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); 41 struct mt8365_afe_private *afe_priv = afe->platform_priv; 42 struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1]; 43 bool slave_mode = pcm_priv->slave_mode; 44 bool lrck_inv = pcm_priv->lrck_inv; 45 bool bck_inv = pcm_priv->bck_inv; 46 unsigned int fmt = pcm_priv->format; 47 unsigned int bit_width = dai->sample_bits; 48 unsigned int val = 0; 49 50 if (!slave_mode) { 51 val |= PCM_INTF_CON1_MASTER_MODE | 52 PCM_INTF_CON1_BYPASS_ASRC; 53 54 if (lrck_inv) 55 val |= PCM_INTF_CON1_SYNC_OUT_INV; 56 if (bck_inv) 57 val |= PCM_INTF_CON1_BCLK_OUT_INV; 58 } else { 59 val |= PCM_INTF_CON1_SLAVE_MODE; 60 61 if (lrck_inv) 62 val |= PCM_INTF_CON1_SYNC_IN_INV; 63 if (bck_inv) 64 val |= PCM_INTF_CON1_BCLK_IN_INV; 65 66 /* TODO: add asrc setting */ 67 } 68 69 val |= FIELD_PREP(PCM_INTF_CON1_FORMAT_MASK, fmt); 70 71 if (fmt == MT8365_PCM_FORMAT_PCMA || 72 fmt == MT8365_PCM_FORMAT_PCMB) 73 val |= PCM_INTF_CON1_SYNC_LEN(1); 74 else 75 val |= PCM_INTF_CON1_SYNC_LEN(bit_width); 76 77 switch (substream->runtime->rate) { 78 case 48000: 79 val |= PCM_INTF_CON1_FS_48K; 80 break; 81 case 32000: 82 val |= PCM_INTF_CON1_FS_32K; 83 break; 84 case 16000: 85 val |= PCM_INTF_CON1_FS_16K; 86 break; 87 case 8000: 88 val |= PCM_INTF_CON1_FS_8K; 89 break; 90 default: 91 return -EINVAL; 92 } 93 94 if (bit_width > 16) 95 val |= PCM_INTF_CON1_24BIT | PCM_INTF_CON1_64BCK; 96 else 97 val |= PCM_INTF_CON1_16BIT | PCM_INTF_CON1_32BCK; 98 99 val |= PCM_INTF_CON1_EXT_MODEM; 100 101 regmap_update_bits(afe->regmap, PCM_INTF_CON1, 102 PCM_INTF_CON1_CONFIG_MASK, val); 103 104 return 0; 105 } 106 107 static int mt8365_dai_pcm1_startup(struct snd_pcm_substream *substream, 108 struct snd_soc_dai *dai) 109 { 110 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); 111 112 if (snd_soc_dai_active(dai)) 113 return 0; 114 115 mt8365_afe_enable_main_clk(afe); 116 117 return 0; 118 } 119 120 static void mt8365_dai_pcm1_shutdown(struct snd_pcm_substream *substream, 121 struct snd_soc_dai *dai) 122 { 123 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); 124 125 if (snd_soc_dai_active(dai)) 126 return; 127 128 mt8365_dai_disable_pcm1(afe); 129 mt8365_afe_disable_main_clk(afe); 130 } 131 132 static int mt8365_dai_pcm1_prepare(struct snd_pcm_substream *substream, 133 struct snd_soc_dai *dai) 134 { 135 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); 136 int ret; 137 138 if ((snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK) + 139 snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) > 1) { 140 dev_info(afe->dev, "%s '%s' active(%u-%u) already\n", 141 __func__, snd_pcm_stream_str(substream), 142 snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK), 143 snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)); 144 return 0; 145 } 146 147 ret = mt8365_dai_configure_pcm1(substream, dai); 148 if (ret) 149 return ret; 150 151 mt8365_dai_enable_pcm1(afe); 152 153 return 0; 154 } 155 156 static int mt8365_dai_pcm1_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 157 { 158 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); 159 struct mt8365_afe_private *afe_priv = afe->platform_priv; 160 struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1]; 161 162 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 163 case SND_SOC_DAIFMT_I2S: 164 pcm_priv->format = MT8365_PCM_FORMAT_I2S; 165 break; 166 default: 167 return -EINVAL; 168 } 169 170 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 171 case SND_SOC_DAIFMT_NB_NF: 172 pcm_priv->bck_inv = false; 173 pcm_priv->lrck_inv = false; 174 break; 175 case SND_SOC_DAIFMT_NB_IF: 176 pcm_priv->bck_inv = false; 177 pcm_priv->lrck_inv = true; 178 break; 179 case SND_SOC_DAIFMT_IB_NF: 180 pcm_priv->bck_inv = true; 181 pcm_priv->lrck_inv = false; 182 break; 183 case SND_SOC_DAIFMT_IB_IF: 184 pcm_priv->bck_inv = true; 185 pcm_priv->lrck_inv = true; 186 break; 187 default: 188 return -EINVAL; 189 } 190 191 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 192 case SND_SOC_DAIFMT_CBM_CFM: 193 pcm_priv->slave_mode = true; 194 break; 195 case SND_SOC_DAIFMT_CBS_CFS: 196 pcm_priv->slave_mode = false; 197 break; 198 default: 199 return -EINVAL; 200 } 201 202 return 0; 203 } 204 205 static const struct snd_soc_dai_ops mt8365_dai_pcm1_ops = { 206 .startup = mt8365_dai_pcm1_startup, 207 .shutdown = mt8365_dai_pcm1_shutdown, 208 .prepare = mt8365_dai_pcm1_prepare, 209 .set_fmt = mt8365_dai_pcm1_set_fmt, 210 }; 211 212 static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = { 213 { 214 .name = "PCM1", 215 .id = MT8365_AFE_IO_PCM1, 216 .playback = { 217 .stream_name = "PCM1 Playback", 218 .channels_min = 1, 219 .channels_max = 2, 220 .rates = SNDRV_PCM_RATE_8000 | 221 SNDRV_PCM_RATE_16000 | 222 SNDRV_PCM_RATE_32000 | 223 SNDRV_PCM_RATE_48000, 224 .formats = SNDRV_PCM_FMTBIT_S16_LE | 225 SNDRV_PCM_FMTBIT_S32_LE, 226 }, 227 .capture = { 228 .stream_name = "PCM1 Capture", 229 .channels_min = 1, 230 .channels_max = 2, 231 .rates = SNDRV_PCM_RATE_8000 | 232 SNDRV_PCM_RATE_16000 | 233 SNDRV_PCM_RATE_32000 | 234 SNDRV_PCM_RATE_48000, 235 .formats = SNDRV_PCM_FMTBIT_S16_LE | 236 SNDRV_PCM_FMTBIT_S32_LE, 237 }, 238 .ops = &mt8365_dai_pcm1_ops, 239 .symmetric_rate = 1, 240 .symmetric_sample_bits = 1, 241 } 242 }; 243 244 /* DAI widget */ 245 246 static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = { 247 SND_SOC_DAPM_OUTPUT("PCM1 Out"), 248 SND_SOC_DAPM_INPUT("PCM1 In"), 249 }; 250 251 /* DAI route */ 252 253 static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = { 254 {"PCM1 Playback", NULL, "O07"}, 255 {"PCM1 Playback", NULL, "O08"}, 256 {"PCM1 Out", NULL, "PCM1 Playback"}, 257 258 {"I09", NULL, "PCM1 Capture"}, 259 {"I22", NULL, "PCM1 Capture"}, 260 {"PCM1 Capture", NULL, "PCM1 In"}, 261 }; 262 263 static int init_pcmif_priv_data(struct mtk_base_afe *afe) 264 { 265 struct mt8365_afe_private *afe_priv = afe->platform_priv; 266 struct mt8365_pcm_intf_data *pcmif_priv; 267 268 pcmif_priv = devm_kzalloc(afe->dev, sizeof(struct mt8365_pcm_intf_data), 269 GFP_KERNEL); 270 if (!pcmif_priv) 271 return -ENOMEM; 272 273 afe_priv->dai_priv[MT8365_AFE_IO_PCM1] = pcmif_priv; 274 return 0; 275 } 276 277 int mt8365_dai_pcm_register(struct mtk_base_afe *afe) 278 { 279 struct mtk_base_afe_dai *dai; 280 281 dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); 282 if (!dai) 283 return -ENOMEM; 284 285 list_add(&dai->list, &afe->sub_dais); 286 dai->dai_drivers = mtk_dai_pcm_driver; 287 dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver); 288 dai->dapm_widgets = mtk_dai_pcm_widgets; 289 dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets); 290 dai->dapm_routes = mtk_dai_pcm_routes; 291 dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes); 292 return init_pcmif_priv_data(afe); 293 } 294
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.