sof_da7219_max98373.c (12095B)
1// SPDX-License-Identifier: GPL-2.0-only 2// Copyright(c) 2019 Intel Corporation. 3 4/* 5 * Intel SOF Machine driver for DA7219 + MAX98373/MAX98360A codec 6 */ 7 8#include <linux/input.h> 9#include <linux/module.h> 10#include <sound/pcm.h> 11#include <sound/pcm_params.h> 12#include <linux/platform_device.h> 13#include <sound/soc.h> 14#include <sound/soc-acpi.h> 15#include "../../codecs/da7219.h" 16#include "../../codecs/da7219-aad.h" 17#include "hda_dsp_common.h" 18 19#define DIALOG_CODEC_DAI "da7219-hifi" 20#define MAX98373_CODEC_DAI "max98373-aif1" 21#define MAXIM_DEV0_NAME "i2c-MX98373:00" 22#define MAXIM_DEV1_NAME "i2c-MX98373:01" 23 24struct hdmi_pcm { 25 struct list_head head; 26 struct snd_soc_dai *codec_dai; 27 int device; 28}; 29 30struct card_private { 31 struct snd_soc_jack headset; 32 struct list_head hdmi_pcm_list; 33 struct snd_soc_jack hdmi[3]; 34}; 35 36static int platform_clock_control(struct snd_soc_dapm_widget *w, 37 struct snd_kcontrol *k, int event) 38{ 39 struct snd_soc_dapm_context *dapm = w->dapm; 40 struct snd_soc_card *card = dapm->card; 41 struct snd_soc_dai *codec_dai; 42 int ret = 0; 43 44 codec_dai = snd_soc_card_get_codec_dai(card, DIALOG_CODEC_DAI); 45 if (!codec_dai) { 46 dev_err(card->dev, "Codec dai not found; Unable to set/unset codec pll\n"); 47 return -EIO; 48 } 49 50 if (SND_SOC_DAPM_EVENT_OFF(event)) { 51 ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 52 0, 0); 53 if (ret) 54 dev_err(card->dev, "failed to stop PLL: %d\n", ret); 55 } else if (SND_SOC_DAPM_EVENT_ON(event)) { 56 ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL_SRM, 57 0, DA7219_PLL_FREQ_OUT_98304); 58 if (ret) 59 dev_err(card->dev, "failed to start PLL: %d\n", ret); 60 } 61 62 return ret; 63} 64 65static const struct snd_kcontrol_new controls[] = { 66 SOC_DAPM_PIN_SWITCH("Headphone Jack"), 67 SOC_DAPM_PIN_SWITCH("Headset Mic"), 68 SOC_DAPM_PIN_SWITCH("Left Spk"), 69 SOC_DAPM_PIN_SWITCH("Right Spk"), 70}; 71 72static const struct snd_kcontrol_new m98360a_controls[] = { 73 SOC_DAPM_PIN_SWITCH("Headphone Jack"), 74 SOC_DAPM_PIN_SWITCH("Headset Mic"), 75 SOC_DAPM_PIN_SWITCH("Spk"), 76}; 77 78/* For MAX98373 amp */ 79static const struct snd_soc_dapm_widget widgets[] = { 80 SND_SOC_DAPM_HP("Headphone Jack", NULL), 81 SND_SOC_DAPM_MIC("Headset Mic", NULL), 82 83 SND_SOC_DAPM_SPK("Left Spk", NULL), 84 SND_SOC_DAPM_SPK("Right Spk", NULL), 85 86 SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, 87 platform_clock_control, SND_SOC_DAPM_POST_PMD | 88 SND_SOC_DAPM_PRE_PMU), 89 90 SND_SOC_DAPM_MIC("SoC DMIC", NULL), 91}; 92 93static const struct snd_soc_dapm_route audio_map[] = { 94 { "Headphone Jack", NULL, "HPL" }, 95 { "Headphone Jack", NULL, "HPR" }, 96 97 { "MIC", NULL, "Headset Mic" }, 98 99 { "Headphone Jack", NULL, "Platform Clock" }, 100 { "Headset Mic", NULL, "Platform Clock" }, 101 102 { "Left Spk", NULL, "Left BE_OUT" }, 103 { "Right Spk", NULL, "Right BE_OUT" }, 104 105 /* digital mics */ 106 {"DMic", NULL, "SoC DMIC"}, 107}; 108 109/* For MAX98360A amp */ 110static const struct snd_soc_dapm_widget max98360a_widgets[] = { 111 SND_SOC_DAPM_HP("Headphone Jack", NULL), 112 SND_SOC_DAPM_MIC("Headset Mic", NULL), 113 114 SND_SOC_DAPM_SPK("Spk", NULL), 115 116 SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, 117 platform_clock_control, SND_SOC_DAPM_POST_PMD | 118 SND_SOC_DAPM_PRE_PMU), 119 120 SND_SOC_DAPM_MIC("SoC DMIC", NULL), 121}; 122 123static const struct snd_soc_dapm_route max98360a_map[] = { 124 { "Headphone Jack", NULL, "HPL" }, 125 { "Headphone Jack", NULL, "HPR" }, 126 127 { "MIC", NULL, "Headset Mic" }, 128 129 { "Headphone Jack", NULL, "Platform Clock" }, 130 { "Headset Mic", NULL, "Platform Clock" }, 131 132 {"Spk", NULL, "Speaker"}, 133 134 /* digital mics */ 135 {"DMic", NULL, "SoC DMIC"}, 136}; 137 138static struct snd_soc_jack headset; 139 140static int da7219_codec_init(struct snd_soc_pcm_runtime *rtd) 141{ 142 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 143 struct snd_soc_component *component = codec_dai->component; 144 struct snd_soc_jack *jack; 145 int ret; 146 147 /* Configure sysclk for codec */ 148 ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, 24000000, 149 SND_SOC_CLOCK_IN); 150 if (ret) { 151 dev_err(rtd->dev, "can't set codec sysclk configuration\n"); 152 return ret; 153 } 154 155 /* 156 * Headset buttons map to the google Reference headset. 157 * These can be configured by userspace. 158 */ 159 ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", 160 SND_JACK_HEADSET | SND_JACK_BTN_0 | 161 SND_JACK_BTN_1 | SND_JACK_BTN_2 | 162 SND_JACK_BTN_3 | SND_JACK_LINEOUT, 163 &headset); 164 if (ret) { 165 dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); 166 return ret; 167 } 168 169 jack = &headset; 170 snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); 171 snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP); 172 snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); 173 snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); 174 da7219_aad_jack_det(component, jack); 175 176 return ret; 177} 178 179static int ssp1_hw_params(struct snd_pcm_substream *substream, 180 struct snd_pcm_hw_params *params) 181{ 182 struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream); 183 int ret, j; 184 185 for (j = 0; j < runtime->num_codecs; j++) { 186 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, j); 187 188 if (!strcmp(codec_dai->component->name, MAXIM_DEV0_NAME)) { 189 /* vmon_slot_no = 0 imon_slot_no = 1 for TX slots */ 190 ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 3, 4, 16); 191 if (ret < 0) { 192 dev_err(runtime->dev, "DEV0 TDM slot err:%d\n", ret); 193 return ret; 194 } 195 } 196 if (!strcmp(codec_dai->component->name, MAXIM_DEV1_NAME)) { 197 /* vmon_slot_no = 2 imon_slot_no = 3 for TX slots */ 198 ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC, 3, 4, 16); 199 if (ret < 0) { 200 dev_err(runtime->dev, "DEV1 TDM slot err:%d\n", ret); 201 return ret; 202 } 203 } 204 } 205 206 return 0; 207} 208 209static struct snd_soc_ops ssp1_ops = { 210 .hw_params = ssp1_hw_params, 211}; 212 213static struct snd_soc_codec_conf max98373_codec_conf[] = { 214 { 215 .dlc = COMP_CODEC_CONF(MAXIM_DEV0_NAME), 216 .name_prefix = "Right", 217 }, 218 { 219 .dlc = COMP_CODEC_CONF(MAXIM_DEV1_NAME), 220 .name_prefix = "Left", 221 }, 222}; 223 224static int hdmi_init(struct snd_soc_pcm_runtime *rtd) 225{ 226 struct card_private *ctx = snd_soc_card_get_drvdata(rtd->card); 227 struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); 228 struct hdmi_pcm *pcm; 229 230 pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); 231 if (!pcm) 232 return -ENOMEM; 233 234 pcm->device = dai->id; 235 pcm->codec_dai = dai; 236 237 list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); 238 239 return 0; 240} 241 242static int card_late_probe(struct snd_soc_card *card) 243{ 244 struct card_private *ctx = snd_soc_card_get_drvdata(card); 245 struct snd_soc_acpi_mach *mach = (card->dev)->platform_data; 246 struct hdmi_pcm *pcm; 247 248 if (mach->mach_params.common_hdmi_codec_drv) { 249 pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm, 250 head); 251 return hda_dsp_hdmi_build_controls(card, 252 pcm->codec_dai->component); 253 } 254 255 return -EINVAL; 256} 257 258SND_SOC_DAILINK_DEF(ssp0_pin, 259 DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin"))); 260SND_SOC_DAILINK_DEF(ssp0_codec, 261 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7219:00", DIALOG_CODEC_DAI))); 262 263SND_SOC_DAILINK_DEF(ssp1_pin, 264 DAILINK_COMP_ARRAY(COMP_CPU("SSP1 Pin"))); 265SND_SOC_DAILINK_DEF(ssp1_amps, 266 DAILINK_COMP_ARRAY( 267 /* Left */ COMP_CODEC(MAXIM_DEV0_NAME, MAX98373_CODEC_DAI), 268 /* Right */ COMP_CODEC(MAXIM_DEV1_NAME, MAX98373_CODEC_DAI))); 269 270SND_SOC_DAILINK_DEF(ssp1_m98360a, 271 DAILINK_COMP_ARRAY(COMP_CODEC("MX98360A:00", "HiFi"))); 272 273SND_SOC_DAILINK_DEF(dmic_pin, 274 DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin"))); 275SND_SOC_DAILINK_DEF(dmic_codec, 276 DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi"))); 277 278SND_SOC_DAILINK_DEF(dmic16k_pin, 279 DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin"))); 280 281SND_SOC_DAILINK_DEF(idisp1_pin, 282 DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin"))); 283SND_SOC_DAILINK_DEF(idisp1_codec, 284 DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1"))); 285 286SND_SOC_DAILINK_DEF(idisp2_pin, 287 DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin"))); 288SND_SOC_DAILINK_DEF(idisp2_codec, 289 DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2"))); 290 291SND_SOC_DAILINK_DEF(idisp3_pin, 292 DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin"))); 293SND_SOC_DAILINK_DEF(idisp3_codec, 294 DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3"))); 295 296SND_SOC_DAILINK_DEF(platform, /* subject to be overridden during probe */ 297 DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3"))); 298 299static struct snd_soc_dai_link dais[] = { 300 /* Back End DAI links */ 301 { 302 .name = "SSP1-Codec", 303 .id = 0, 304 .ignore_pmdown_time = 1, 305 .no_pcm = 1, 306 .dpcm_playback = 1, 307 .dpcm_capture = 1, /* IV feedback */ 308 .ops = &ssp1_ops, 309 SND_SOC_DAILINK_REG(ssp1_pin, ssp1_amps, platform), 310 }, 311 { 312 .name = "SSP0-Codec", 313 .id = 1, 314 .no_pcm = 1, 315 .init = da7219_codec_init, 316 .ignore_pmdown_time = 1, 317 .dpcm_playback = 1, 318 .dpcm_capture = 1, 319 SND_SOC_DAILINK_REG(ssp0_pin, ssp0_codec, platform), 320 }, 321 { 322 .name = "dmic01", 323 .id = 2, 324 .ignore_suspend = 1, 325 .dpcm_capture = 1, 326 .no_pcm = 1, 327 SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform), 328 }, 329 { 330 .name = "iDisp1", 331 .id = 3, 332 .init = hdmi_init, 333 .dpcm_playback = 1, 334 .no_pcm = 1, 335 SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform), 336 }, 337 { 338 .name = "iDisp2", 339 .id = 4, 340 .init = hdmi_init, 341 .dpcm_playback = 1, 342 .no_pcm = 1, 343 SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform), 344 }, 345 { 346 .name = "iDisp3", 347 .id = 5, 348 .init = hdmi_init, 349 .dpcm_playback = 1, 350 .no_pcm = 1, 351 SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform), 352 }, 353 { 354 .name = "dmic16k", 355 .id = 6, 356 .ignore_suspend = 1, 357 .dpcm_capture = 1, 358 .no_pcm = 1, 359 SND_SOC_DAILINK_REG(dmic16k_pin, dmic_codec, platform), 360 } 361}; 362 363static struct snd_soc_card card_da7219_m98373 = { 364 .name = "da7219max", 365 .owner = THIS_MODULE, 366 .dai_link = dais, 367 .num_links = ARRAY_SIZE(dais), 368 .controls = controls, 369 .num_controls = ARRAY_SIZE(controls), 370 .dapm_widgets = widgets, 371 .num_dapm_widgets = ARRAY_SIZE(widgets), 372 .dapm_routes = audio_map, 373 .num_dapm_routes = ARRAY_SIZE(audio_map), 374 .codec_conf = max98373_codec_conf, 375 .num_configs = ARRAY_SIZE(max98373_codec_conf), 376 .fully_routed = true, 377 .late_probe = card_late_probe, 378}; 379 380static struct snd_soc_card card_da7219_m98360a = { 381 .name = "da7219max98360a", 382 .owner = THIS_MODULE, 383 .dai_link = dais, 384 .num_links = ARRAY_SIZE(dais), 385 .controls = m98360a_controls, 386 .num_controls = ARRAY_SIZE(m98360a_controls), 387 .dapm_widgets = max98360a_widgets, 388 .num_dapm_widgets = ARRAY_SIZE(max98360a_widgets), 389 .dapm_routes = max98360a_map, 390 .num_dapm_routes = ARRAY_SIZE(max98360a_map), 391 .fully_routed = true, 392 .late_probe = card_late_probe, 393}; 394 395static int audio_probe(struct platform_device *pdev) 396{ 397 static struct snd_soc_card *card; 398 struct snd_soc_acpi_mach *mach; 399 struct card_private *ctx; 400 int ret; 401 402 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); 403 if (!ctx) 404 return -ENOMEM; 405 406 /* By default dais[0] is configured for max98373 */ 407 if (!strcmp(pdev->name, "sof_da7219_mx98360a")) { 408 dais[0] = (struct snd_soc_dai_link) { 409 .name = "SSP1-Codec", 410 .id = 0, 411 .no_pcm = 1, 412 .dpcm_playback = 1, 413 .ignore_pmdown_time = 1, 414 SND_SOC_DAILINK_REG(ssp1_pin, ssp1_m98360a, platform) }; 415 } 416 417 INIT_LIST_HEAD(&ctx->hdmi_pcm_list); 418 card = (struct snd_soc_card *)pdev->id_entry->driver_data; 419 card->dev = &pdev->dev; 420 421 mach = pdev->dev.platform_data; 422 ret = snd_soc_fixup_dai_links_platform_name(card, 423 mach->mach_params.platform); 424 if (ret) 425 return ret; 426 427 snd_soc_card_set_drvdata(card, ctx); 428 429 return devm_snd_soc_register_card(&pdev->dev, card); 430} 431 432static const struct platform_device_id board_ids[] = { 433 { 434 .name = "sof_da7219_mx98373", 435 .driver_data = (kernel_ulong_t)&card_da7219_m98373, 436 }, 437 { 438 .name = "sof_da7219_mx98360a", 439 .driver_data = (kernel_ulong_t)&card_da7219_m98360a, 440 }, 441 { } 442}; 443MODULE_DEVICE_TABLE(platform, board_ids); 444 445static struct platform_driver audio = { 446 .probe = audio_probe, 447 .driver = { 448 .name = "sof_da7219_max98_360a_373", 449 .pm = &snd_soc_pm_ops, 450 }, 451 .id_table = board_ids, 452}; 453module_platform_driver(audio) 454 455/* Module information */ 456MODULE_DESCRIPTION("ASoC Intel(R) SOF Machine driver"); 457MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>"); 458MODULE_LICENSE("GPL v2"); 459MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);