1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * MediaTek MT8365 Sound Card driver 4 * 5 * Copyright (c) 2024 MediaTek Inc. 6 * Authors: Nicolas Belin <nbelin@baylibre.com> 7 */ 8 9 #include <linux/module.h> 10 #include <linux/of_gpio.h> 11 #include <sound/soc.h> 12 #include <sound/pcm_params.h> 13 #include "mt8365-afe-common.h" 14 #include <linux/pinctrl/consumer.h> 15 #include "../common/mtk-soc-card.h" 16 #include "../common/mtk-soundcard-driver.h" 17 18 enum pinctrl_pin_state { 19 PIN_STATE_DEFAULT, 20 PIN_STATE_DMIC, 21 PIN_STATE_MISO_OFF, 22 PIN_STATE_MISO_ON, 23 PIN_STATE_MOSI_OFF, 24 PIN_STATE_MOSI_ON, 25 PIN_STATE_MAX 26 }; 27 28 static const char * const mt8365_mt6357_pin_str[PIN_STATE_MAX] = { 29 "default", 30 "dmic", 31 "miso_off", 32 "miso_on", 33 "mosi_off", 34 "mosi_on", 35 }; 36 37 struct mt8365_mt6357_priv { 38 struct pinctrl *pinctrl; 39 struct pinctrl_state *pin_states[PIN_STATE_MAX]; 40 }; 41 42 enum { 43 /* FE */ 44 DAI_LINK_DL1_PLAYBACK = 0, 45 DAI_LINK_DL2_PLAYBACK, 46 DAI_LINK_AWB_CAPTURE, 47 DAI_LINK_VUL_CAPTURE, 48 /* BE */ 49 DAI_LINK_2ND_I2S_INTF, 50 DAI_LINK_DMIC, 51 DAI_LINK_INT_ADDA, 52 DAI_LINK_NUM 53 }; 54 55 static const struct snd_soc_dapm_widget mt8365_mt6357_widgets[] = { 56 SND_SOC_DAPM_OUTPUT("HDMI Out"), 57 }; 58 59 static const struct snd_soc_dapm_route mt8365_mt6357_routes[] = { 60 {"HDMI Out", NULL, "2ND I2S Playback"}, 61 {"DMIC In", NULL, "MICBIAS0"}, 62 }; 63 64 static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) 65 { 66 struct snd_soc_pcm_runtime *rtd = substream->private_data; 67 struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); 68 int ret = 0; 69 70 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 71 if (IS_ERR(priv->pin_states[PIN_STATE_MOSI_ON])) 72 return ret; 73 74 ret = pinctrl_select_state(priv->pinctrl, 75 priv->pin_states[PIN_STATE_MOSI_ON]); 76 if (ret) 77 dev_err(rtd->card->dev, "%s failed to select state %d\n", 78 __func__, ret); 79 } 80 81 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 82 if (IS_ERR(priv->pin_states[PIN_STATE_MISO_ON])) 83 return ret; 84 85 ret = pinctrl_select_state(priv->pinctrl, 86 priv->pin_states[PIN_STATE_MISO_ON]); 87 if (ret) 88 dev_err(rtd->card->dev, "%s failed to select state %d\n", 89 __func__, ret); 90 } 91 92 return 0; 93 } 94 95 static void mt8365_mt6357_int_adda_shutdown(struct snd_pcm_substream *substream) 96 { 97 struct snd_soc_pcm_runtime *rtd = substream->private_data; 98 struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); 99 int ret = 0; 100 101 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 102 if (IS_ERR(priv->pin_states[PIN_STATE_MOSI_OFF])) 103 return; 104 105 ret = pinctrl_select_state(priv->pinctrl, 106 priv->pin_states[PIN_STATE_MOSI_OFF]); 107 if (ret) 108 dev_err(rtd->card->dev, "%s failed to select state %d\n", 109 __func__, ret); 110 } 111 112 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 113 if (IS_ERR(priv->pin_states[PIN_STATE_MISO_OFF])) 114 return; 115 116 ret = pinctrl_select_state(priv->pinctrl, 117 priv->pin_states[PIN_STATE_MISO_OFF]); 118 if (ret) 119 dev_err(rtd->card->dev, "%s failed to select state %d\n", 120 __func__, ret); 121 } 122 } 123 124 static const struct snd_soc_ops mt8365_mt6357_int_adda_ops = { 125 .startup = mt8365_mt6357_int_adda_startup, 126 .shutdown = mt8365_mt6357_int_adda_shutdown, 127 }; 128 129 SND_SOC_DAILINK_DEFS(playback1, 130 DAILINK_COMP_ARRAY(COMP_CPU("DL1")), 131 DAILINK_COMP_ARRAY(COMP_DUMMY()), 132 DAILINK_COMP_ARRAY(COMP_EMPTY())); 133 SND_SOC_DAILINK_DEFS(playback2, 134 DAILINK_COMP_ARRAY(COMP_CPU("DL2")), 135 DAILINK_COMP_ARRAY(COMP_DUMMY()), 136 DAILINK_COMP_ARRAY(COMP_EMPTY())); 137 SND_SOC_DAILINK_DEFS(awb_capture, 138 DAILINK_COMP_ARRAY(COMP_CPU("AWB")), 139 DAILINK_COMP_ARRAY(COMP_DUMMY()), 140 DAILINK_COMP_ARRAY(COMP_EMPTY())); 141 SND_SOC_DAILINK_DEFS(vul, 142 DAILINK_COMP_ARRAY(COMP_CPU("VUL")), 143 DAILINK_COMP_ARRAY(COMP_DUMMY()), 144 DAILINK_COMP_ARRAY(COMP_EMPTY())); 145 146 SND_SOC_DAILINK_DEFS(i2s3, 147 DAILINK_COMP_ARRAY(COMP_CPU("2ND I2S")), 148 DAILINK_COMP_ARRAY(COMP_DUMMY()), 149 DAILINK_COMP_ARRAY(COMP_EMPTY())); 150 SND_SOC_DAILINK_DEFS(dmic, 151 DAILINK_COMP_ARRAY(COMP_CPU("DMIC")), 152 DAILINK_COMP_ARRAY(COMP_DUMMY()), 153 DAILINK_COMP_ARRAY(COMP_EMPTY())); 154 SND_SOC_DAILINK_DEFS(primary_codec, 155 DAILINK_COMP_ARRAY(COMP_CPU("INT ADDA")), 156 DAILINK_COMP_ARRAY(COMP_CODEC("mt6357-sound", "mt6357-snd-codec-aif1")), 157 DAILINK_COMP_ARRAY(COMP_EMPTY())); 158 159 /* Digital audio interface glue - connects codec <---> CPU */ 160 static struct snd_soc_dai_link mt8365_mt6357_dais[] = { 161 /* Front End DAI links */ 162 [DAI_LINK_DL1_PLAYBACK] = { 163 .name = "DL1_FE", 164 .stream_name = "MultiMedia1_PLayback", 165 .id = DAI_LINK_DL1_PLAYBACK, 166 .trigger = { 167 SND_SOC_DPCM_TRIGGER_POST, 168 SND_SOC_DPCM_TRIGGER_POST 169 }, 170 .dynamic = 1, 171 .dpcm_playback = 1, 172 .dpcm_merged_rate = 1, 173 SND_SOC_DAILINK_REG(playback1), 174 }, 175 [DAI_LINK_DL2_PLAYBACK] = { 176 .name = "DL2_FE", 177 .stream_name = "MultiMedia2_PLayback", 178 .id = DAI_LINK_DL2_PLAYBACK, 179 .trigger = { 180 SND_SOC_DPCM_TRIGGER_POST, 181 SND_SOC_DPCM_TRIGGER_POST 182 }, 183 .dynamic = 1, 184 .dpcm_playback = 1, 185 .dpcm_merged_rate = 1, 186 SND_SOC_DAILINK_REG(playback2), 187 }, 188 [DAI_LINK_AWB_CAPTURE] = { 189 .name = "AWB_FE", 190 .stream_name = "DL1_AWB_Record", 191 .id = DAI_LINK_AWB_CAPTURE, 192 .trigger = { 193 SND_SOC_DPCM_TRIGGER_POST, 194 SND_SOC_DPCM_TRIGGER_POST 195 }, 196 .dynamic = 1, 197 .dpcm_capture = 1, 198 .dpcm_merged_rate = 1, 199 SND_SOC_DAILINK_REG(awb_capture), 200 }, 201 [DAI_LINK_VUL_CAPTURE] = { 202 .name = "VUL_FE", 203 .stream_name = "MultiMedia1_Capture", 204 .id = DAI_LINK_VUL_CAPTURE, 205 .trigger = { 206 SND_SOC_DPCM_TRIGGER_POST, 207 SND_SOC_DPCM_TRIGGER_POST 208 }, 209 .dynamic = 1, 210 .dpcm_capture = 1, 211 .dpcm_merged_rate = 1, 212 SND_SOC_DAILINK_REG(vul), 213 }, 214 /* Back End DAI links */ 215 [DAI_LINK_2ND_I2S_INTF] = { 216 .name = "I2S_OUT_BE", 217 .no_pcm = 1, 218 .id = DAI_LINK_2ND_I2S_INTF, 219 .dai_fmt = SND_SOC_DAIFMT_I2S | 220 SND_SOC_DAIFMT_NB_NF | 221 SND_SOC_DAIFMT_CBS_CFS, 222 .dpcm_playback = 1, 223 .dpcm_capture = 1, 224 SND_SOC_DAILINK_REG(i2s3), 225 }, 226 [DAI_LINK_DMIC] = { 227 .name = "DMIC_BE", 228 .no_pcm = 1, 229 .id = DAI_LINK_DMIC, 230 .dpcm_capture = 1, 231 SND_SOC_DAILINK_REG(dmic), 232 }, 233 [DAI_LINK_INT_ADDA] = { 234 .name = "MTK_Codec", 235 .no_pcm = 1, 236 .id = DAI_LINK_INT_ADDA, 237 .dpcm_playback = 1, 238 .dpcm_capture = 1, 239 .ops = &mt8365_mt6357_int_adda_ops, 240 SND_SOC_DAILINK_REG(primary_codec), 241 }, 242 }; 243 244 static int mt8365_mt6357_gpio_probe(struct snd_soc_card *card) 245 { 246 struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(card); 247 int ret, i; 248 249 priv->pinctrl = devm_pinctrl_get(card->dev); 250 if (IS_ERR(priv->pinctrl)) { 251 ret = PTR_ERR(priv->pinctrl); 252 return dev_err_probe(card->dev, ret, 253 "Failed to get pinctrl\n"); 254 } 255 256 for (i = PIN_STATE_DEFAULT ; i < PIN_STATE_MAX ; i++) { 257 priv->pin_states[i] = pinctrl_lookup_state(priv->pinctrl, 258 mt8365_mt6357_pin_str[i]); 259 if (IS_ERR(priv->pin_states[i])) { 260 dev_info(card->dev, "No pin state for %s\n", 261 mt8365_mt6357_pin_str[i]); 262 } else { 263 ret = pinctrl_select_state(priv->pinctrl, 264 priv->pin_states[i]); 265 if (ret) { 266 dev_err_probe(card->dev, ret, 267 "Failed to select pin state %s\n", 268 mt8365_mt6357_pin_str[i]); 269 return ret; 270 } 271 } 272 } 273 return 0; 274 } 275 276 static struct snd_soc_card mt8365_mt6357_soc_card = { 277 .name = "mt8365-evk", 278 .owner = THIS_MODULE, 279 .dai_link = mt8365_mt6357_dais, 280 .num_links = ARRAY_SIZE(mt8365_mt6357_dais), 281 .dapm_widgets = mt8365_mt6357_widgets, 282 .num_dapm_widgets = ARRAY_SIZE(mt8365_mt6357_widgets), 283 .dapm_routes = mt8365_mt6357_routes, 284 .num_dapm_routes = ARRAY_SIZE(mt8365_mt6357_routes), 285 }; 286 287 static int mt8365_mt6357_dev_probe(struct mtk_soc_card_data *soc_card_data, bool legacy) 288 { 289 struct mtk_platform_card_data *card_data = soc_card_data->card_data; 290 struct snd_soc_card *card = card_data->card; 291 struct device *dev = card->dev; 292 struct mt8365_mt6357_priv *mach_priv; 293 int ret; 294 295 card->dev = dev; 296 ret = parse_dai_link_info(card); 297 if (ret) 298 goto err; 299 300 mach_priv = devm_kzalloc(dev, sizeof(*mach_priv), 301 GFP_KERNEL); 302 if (!mach_priv) 303 return -ENOMEM; 304 soc_card_data->mach_priv = mach_priv; 305 snd_soc_card_set_drvdata(card, soc_card_data); 306 mt8365_mt6357_gpio_probe(card); 307 return 0; 308 309 err: 310 clean_card_reference(card); 311 return ret; 312 } 313 314 static const struct mtk_soundcard_pdata mt8365_mt6357_card = { 315 .card_name = "mt8365-mt6357", 316 .card_data = &(struct mtk_platform_card_data) { 317 .card = &mt8365_mt6357_soc_card, 318 }, 319 .soc_probe = mt8365_mt6357_dev_probe 320 }; 321 322 static const struct of_device_id mt8365_mt6357_dt_match[] = { 323 { .compatible = "mediatek,mt8365-mt6357", .data = &mt8365_mt6357_card }, 324 { /* sentinel */ } 325 }; 326 MODULE_DEVICE_TABLE(of, mt8365_mt6357_dt_match); 327 328 static struct platform_driver mt8365_mt6357_driver = { 329 .driver = { 330 .name = "mt8365_mt6357", 331 .of_match_table = mt8365_mt6357_dt_match, 332 .pm = &snd_soc_pm_ops, 333 }, 334 .probe = mtk_soundcard_common_probe, 335 }; 336 337 module_platform_driver(mt8365_mt6357_driver); 338 339 /* Module information */ 340 MODULE_DESCRIPTION("MT8365 EVK SoC machine driver"); 341 MODULE_AUTHOR("Nicolas Belin <nbelin@baylibre.com>"); 342 MODULE_LICENSE("GPL"); 343 MODULE_ALIAS("platform: mt8365_mt6357"); 344
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.