1 // SPDX-License-Identifier: GPL-2.0-only 2 // 3 // Copyright(c) 2020 Intel Corporation. All rights reserved. 4 #include <linux/module.h> 5 #include <linux/string.h> 6 #include <sound/pcm.h> 7 #include <sound/soc.h> 8 #include <sound/soc-acpi.h> 9 #include <sound/soc-dai.h> 10 #include <sound/soc-dapm.h> 11 #include <uapi/sound/asound.h> 12 #include "sof_maxim_common.h" 13 14 #define MAX_98373_PIN_NAME 16 15 16 const struct snd_soc_dapm_route max_98373_dapm_routes[] = { 17 /* speaker */ 18 { "Left Spk", NULL, "Left BE_OUT" }, 19 { "Right Spk", NULL, "Right BE_OUT" }, 20 }; 21 EXPORT_SYMBOL_NS(max_98373_dapm_routes, SND_SOC_INTEL_SOF_MAXIM_COMMON); 22 23 static struct snd_soc_codec_conf max_98373_codec_conf[] = { 24 { 25 .dlc = COMP_CODEC_CONF(MAX_98373_DEV0_NAME), 26 .name_prefix = "Right", 27 }, 28 { 29 .dlc = COMP_CODEC_CONF(MAX_98373_DEV1_NAME), 30 .name_prefix = "Left", 31 }, 32 }; 33 34 struct snd_soc_dai_link_component max_98373_components[] = { 35 { /* For Right */ 36 .name = MAX_98373_DEV0_NAME, 37 .dai_name = MAX_98373_CODEC_DAI, 38 }, 39 { /* For Left */ 40 .name = MAX_98373_DEV1_NAME, 41 .dai_name = MAX_98373_CODEC_DAI, 42 }, 43 }; 44 EXPORT_SYMBOL_NS(max_98373_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); 45 46 static int max_98373_hw_params(struct snd_pcm_substream *substream, 47 struct snd_pcm_hw_params *params) 48 { 49 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 50 struct snd_soc_dai *codec_dai; 51 int j; 52 53 for_each_rtd_codec_dais(rtd, j, codec_dai) { 54 if (!strcmp(codec_dai->component->name, MAX_98373_DEV0_NAME)) { 55 /* DEV0 tdm slot configuration */ 56 snd_soc_dai_set_tdm_slot(codec_dai, 0x03, 3, 8, 32); 57 } 58 if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) { 59 /* DEV1 tdm slot configuration */ 60 snd_soc_dai_set_tdm_slot(codec_dai, 0x0C, 3, 8, 32); 61 } 62 } 63 return 0; 64 } 65 66 int max_98373_trigger(struct snd_pcm_substream *substream, int cmd) 67 { 68 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 69 struct snd_soc_dai *codec_dai; 70 struct snd_soc_dai *cpu_dai; 71 int j; 72 int ret = 0; 73 74 /* set spk pin by playback only */ 75 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 76 return 0; 77 78 cpu_dai = asoc_rtd_to_cpu(rtd, 0); 79 for_each_rtd_codec_dais(rtd, j, codec_dai) { 80 struct snd_soc_dapm_context *dapm = 81 snd_soc_component_get_dapm(cpu_dai->component); 82 char pin_name[MAX_98373_PIN_NAME]; 83 84 snprintf(pin_name, ARRAY_SIZE(pin_name), "%s Spk", 85 codec_dai->component->name_prefix); 86 87 switch (cmd) { 88 case SNDRV_PCM_TRIGGER_START: 89 case SNDRV_PCM_TRIGGER_RESUME: 90 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 91 ret = snd_soc_dapm_enable_pin(dapm, pin_name); 92 if (!ret) 93 snd_soc_dapm_sync(dapm); 94 break; 95 case SNDRV_PCM_TRIGGER_STOP: 96 case SNDRV_PCM_TRIGGER_SUSPEND: 97 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 98 ret = snd_soc_dapm_disable_pin(dapm, pin_name); 99 if (!ret) 100 snd_soc_dapm_sync(dapm); 101 break; 102 default: 103 break; 104 } 105 } 106 107 return ret; 108 } 109 EXPORT_SYMBOL_NS(max_98373_trigger, SND_SOC_INTEL_SOF_MAXIM_COMMON); 110 111 struct snd_soc_ops max_98373_ops = { 112 .hw_params = max_98373_hw_params, 113 .trigger = max_98373_trigger, 114 }; 115 EXPORT_SYMBOL_NS(max_98373_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON); 116 117 int max_98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd) 118 { 119 struct snd_soc_card *card = rtd->card; 120 int ret; 121 122 ret = snd_soc_dapm_add_routes(&card->dapm, max_98373_dapm_routes, 123 ARRAY_SIZE(max_98373_dapm_routes)); 124 if (ret) 125 dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret); 126 return ret; 127 } 128 EXPORT_SYMBOL_NS(max_98373_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON); 129 130 void max_98373_set_codec_conf(struct snd_soc_card *card) 131 { 132 card->codec_conf = max_98373_codec_conf; 133 card->num_configs = ARRAY_SIZE(max_98373_codec_conf); 134 } 135 EXPORT_SYMBOL_NS(max_98373_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON); 136 137 /* 138 * Maxim MAX98390 139 */ 140 static const struct snd_soc_dapm_route max_98390_dapm_routes[] = { 141 /* speaker */ 142 { "Left Spk", NULL, "Left BE_OUT" }, 143 { "Right Spk", NULL, "Right BE_OUT" }, 144 }; 145 146 static const struct snd_kcontrol_new max_98390_tt_kcontrols[] = { 147 SOC_DAPM_PIN_SWITCH("TL Spk"), 148 SOC_DAPM_PIN_SWITCH("TR Spk"), 149 }; 150 151 static const struct snd_soc_dapm_widget max_98390_tt_dapm_widgets[] = { 152 SND_SOC_DAPM_SPK("TL Spk", NULL), 153 SND_SOC_DAPM_SPK("TR Spk", NULL), 154 }; 155 156 static const struct snd_soc_dapm_route max_98390_tt_dapm_routes[] = { 157 /* Tweeter speaker */ 158 { "TL Spk", NULL, "Tweeter Left BE_OUT" }, 159 { "TR Spk", NULL, "Tweeter Right BE_OUT" }, 160 }; 161 162 static struct snd_soc_codec_conf max_98390_codec_conf[] = { 163 { 164 .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME), 165 .name_prefix = "Right", 166 }, 167 { 168 .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME), 169 .name_prefix = "Left", 170 }, 171 }; 172 173 static struct snd_soc_codec_conf max_98390_4spk_codec_conf[] = { 174 { 175 .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME), 176 .name_prefix = "Right", 177 }, 178 { 179 .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME), 180 .name_prefix = "Left", 181 }, 182 { 183 .dlc = COMP_CODEC_CONF(MAX_98390_DEV2_NAME), 184 .name_prefix = "Tweeter Right", 185 }, 186 { 187 .dlc = COMP_CODEC_CONF(MAX_98390_DEV3_NAME), 188 .name_prefix = "Tweeter Left", 189 }, 190 }; 191 192 struct snd_soc_dai_link_component max_98390_components[] = { 193 { 194 .name = MAX_98390_DEV0_NAME, 195 .dai_name = MAX_98390_CODEC_DAI, 196 }, 197 { 198 .name = MAX_98390_DEV1_NAME, 199 .dai_name = MAX_98390_CODEC_DAI, 200 }, 201 }; 202 EXPORT_SYMBOL_NS(max_98390_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); 203 204 struct snd_soc_dai_link_component max_98390_4spk_components[] = { 205 { 206 .name = MAX_98390_DEV0_NAME, 207 .dai_name = MAX_98390_CODEC_DAI, 208 }, 209 { 210 .name = MAX_98390_DEV1_NAME, 211 .dai_name = MAX_98390_CODEC_DAI, 212 }, 213 { 214 .name = MAX_98390_DEV2_NAME, 215 .dai_name = MAX_98390_CODEC_DAI, 216 }, 217 { 218 .name = MAX_98390_DEV3_NAME, 219 .dai_name = MAX_98390_CODEC_DAI, 220 }, 221 }; 222 EXPORT_SYMBOL_NS(max_98390_4spk_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); 223 224 static int max_98390_hw_params(struct snd_pcm_substream *substream, 225 struct snd_pcm_hw_params *params) 226 { 227 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 228 struct snd_soc_dai *codec_dai; 229 int i; 230 231 for_each_rtd_codec_dais(rtd, i, codec_dai) { 232 if (i >= ARRAY_SIZE(max_98390_4spk_components)) { 233 dev_err(codec_dai->dev, "invalid codec index %d\n", i); 234 return -ENODEV; 235 } 236 237 if (!strcmp(codec_dai->component->name, MAX_98390_DEV0_NAME)) { 238 /* DEV0 tdm slot configuration Right */ 239 snd_soc_dai_set_tdm_slot(codec_dai, 0x01, 3, 4, 32); 240 } 241 if (!strcmp(codec_dai->component->name, MAX_98390_DEV1_NAME)) { 242 /* DEV1 tdm slot configuration Left */ 243 snd_soc_dai_set_tdm_slot(codec_dai, 0x02, 3, 4, 32); 244 } 245 246 if (!strcmp(codec_dai->component->name, MAX_98390_DEV2_NAME)) { 247 /* DEVi2 tdm slot configuration Tweeter Right */ 248 snd_soc_dai_set_tdm_slot(codec_dai, 0x04, 3, 4, 32); 249 } 250 if (!strcmp(codec_dai->component->name, MAX_98390_DEV3_NAME)) { 251 /* DEV3 tdm slot configuration Tweeter Left */ 252 snd_soc_dai_set_tdm_slot(codec_dai, 0x08, 3, 4, 32); 253 } 254 } 255 return 0; 256 } 257 258 int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd) 259 { 260 struct snd_soc_card *card = rtd->card; 261 int ret; 262 263 /* add regular speakers dapm route */ 264 ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_dapm_routes, 265 ARRAY_SIZE(max_98390_dapm_routes)); 266 if (ret) { 267 dev_err(rtd->dev, "unable to add Left/Right Speaker dapm, ret %d\n", ret); 268 return ret; 269 } 270 271 /* add widgets/controls/dapm for tweeter speakers */ 272 if (acpi_dev_present("MX98390", "3", -1)) { 273 ret = snd_soc_dapm_new_controls(&card->dapm, max_98390_tt_dapm_widgets, 274 ARRAY_SIZE(max_98390_tt_dapm_widgets)); 275 276 if (ret) { 277 dev_err(rtd->dev, "unable to add tweeter dapm controls, ret %d\n", ret); 278 /* Don't need to add routes if widget addition failed */ 279 return ret; 280 } 281 282 ret = snd_soc_add_card_controls(card, max_98390_tt_kcontrols, 283 ARRAY_SIZE(max_98390_tt_kcontrols)); 284 if (ret) { 285 dev_err(rtd->dev, "unable to add tweeter card controls, ret %d\n", ret); 286 return ret; 287 } 288 289 ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_tt_dapm_routes, 290 ARRAY_SIZE(max_98390_tt_dapm_routes)); 291 if (ret) 292 dev_err(rtd->dev, 293 "unable to add Tweeter Left/Right Speaker dapm, ret %d\n", ret); 294 } 295 return ret; 296 } 297 EXPORT_SYMBOL_NS(max_98390_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON); 298 299 const struct snd_soc_ops max_98390_ops = { 300 .hw_params = max_98390_hw_params, 301 }; 302 EXPORT_SYMBOL_NS(max_98390_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON); 303 304 void max_98390_set_codec_conf(struct snd_soc_card *card, int ch) 305 { 306 if (ch == ARRAY_SIZE(max_98390_4spk_codec_conf)) { 307 card->codec_conf = max_98390_4spk_codec_conf; 308 card->num_configs = ARRAY_SIZE(max_98390_4spk_codec_conf); 309 } else { 310 card->codec_conf = max_98390_codec_conf; 311 card->num_configs = ARRAY_SIZE(max_98390_codec_conf); 312 } 313 } 314 EXPORT_SYMBOL_NS(max_98390_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON); 315 316 /* 317 * Maxim MAX98357A/MAX98360A 318 */ 319 static const struct snd_kcontrol_new max_98357a_kcontrols[] = { 320 SOC_DAPM_PIN_SWITCH("Spk"), 321 }; 322 323 static const struct snd_soc_dapm_widget max_98357a_dapm_widgets[] = { 324 SND_SOC_DAPM_SPK("Spk", NULL), 325 }; 326 327 static const struct snd_soc_dapm_route max_98357a_dapm_routes[] = { 328 /* speaker */ 329 {"Spk", NULL, "Speaker"}, 330 }; 331 332 static struct snd_soc_dai_link_component max_98357a_components[] = { 333 { 334 .name = MAX_98357A_DEV0_NAME, 335 .dai_name = MAX_98357A_CODEC_DAI, 336 } 337 }; 338 339 static struct snd_soc_dai_link_component max_98360a_components[] = { 340 { 341 .name = MAX_98360A_DEV0_NAME, 342 .dai_name = MAX_98357A_CODEC_DAI, 343 } 344 }; 345 346 static int max_98357a_init(struct snd_soc_pcm_runtime *rtd) 347 { 348 struct snd_soc_card *card = rtd->card; 349 int ret; 350 351 ret = snd_soc_dapm_new_controls(&card->dapm, max_98357a_dapm_widgets, 352 ARRAY_SIZE(max_98357a_dapm_widgets)); 353 if (ret) { 354 dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret); 355 /* Don't need to add routes if widget addition failed */ 356 return ret; 357 } 358 359 ret = snd_soc_add_card_controls(card, max_98357a_kcontrols, 360 ARRAY_SIZE(max_98357a_kcontrols)); 361 if (ret) { 362 dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); 363 return ret; 364 } 365 366 ret = snd_soc_dapm_add_routes(&card->dapm, max_98357a_dapm_routes, 367 ARRAY_SIZE(max_98357a_dapm_routes)); 368 369 if (ret) 370 dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret); 371 372 return ret; 373 } 374 375 void max_98357a_dai_link(struct snd_soc_dai_link *link) 376 { 377 link->codecs = max_98357a_components; 378 link->num_codecs = ARRAY_SIZE(max_98357a_components); 379 link->init = max_98357a_init; 380 } 381 EXPORT_SYMBOL_NS(max_98357a_dai_link, SND_SOC_INTEL_SOF_MAXIM_COMMON); 382 383 void max_98360a_dai_link(struct snd_soc_dai_link *link) 384 { 385 link->codecs = max_98360a_components; 386 link->num_codecs = ARRAY_SIZE(max_98360a_components); 387 link->init = max_98357a_init; 388 } 389 EXPORT_SYMBOL_NS(max_98360a_dai_link, SND_SOC_INTEL_SOF_MAXIM_COMMON); 390 391 MODULE_DESCRIPTION("ASoC Intel SOF Maxim helpers"); 392 MODULE_LICENSE("GPL"); 393