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