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