1 /* 2 * Intel Broxton-P I2S Machine Driver 3 * 4 * Copyright (C) 2016, Intel Corporation. All rights reserved. 5 * 6 * Modified from: 7 * Intel Skylake I2S Machine driver 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License version 11 * 2 as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 */ 18 19 #include <linux/module.h> 20 #include <linux/platform_device.h> 21 #include <sound/core.h> 22 #include <sound/jack.h> 23 #include <sound/pcm.h> 24 #include <sound/pcm_params.h> 25 #include <sound/soc.h> 26 #include "../../codecs/hdac_hdmi.h" 27 #include "../../codecs/da7219.h" 28 #include "../../codecs/da7219-aad.h" 29 30 #define BXT_DIALOG_CODEC_DAI "da7219-hifi" 31 #define BXT_MAXIM_CODEC_DAI "HiFi" 32 #define DUAL_CHANNEL 2 33 34 static struct snd_soc_jack broxton_headset; 35 36 enum { 37 BXT_DPCM_AUDIO_PB = 0, 38 BXT_DPCM_AUDIO_CP, 39 BXT_DPCM_AUDIO_REF_CP, 40 BXT_DPCM_AUDIO_HDMI1_PB, 41 BXT_DPCM_AUDIO_HDMI2_PB, 42 BXT_DPCM_AUDIO_HDMI3_PB, 43 }; 44 45 static const struct snd_kcontrol_new broxton_controls[] = { 46 SOC_DAPM_PIN_SWITCH("Headphone Jack"), 47 SOC_DAPM_PIN_SWITCH("Headset Mic"), 48 SOC_DAPM_PIN_SWITCH("Spk"), 49 }; 50 51 static const struct snd_soc_dapm_widget broxton_widgets[] = { 52 SND_SOC_DAPM_HP("Headphone Jack", NULL), 53 SND_SOC_DAPM_MIC("Headset Mic", NULL), 54 SND_SOC_DAPM_SPK("Spk", NULL), 55 SND_SOC_DAPM_MIC("SoC DMIC", NULL), 56 SND_SOC_DAPM_SPK("HDMI1", NULL), 57 SND_SOC_DAPM_SPK("HDMI2", NULL), 58 SND_SOC_DAPM_SPK("HDMI3", NULL), 59 }; 60 61 static const struct snd_soc_dapm_route broxton_map[] = { 62 /* HP jack connectors - unknown if we have jack detection */ 63 {"Headphone Jack", NULL, "HPL"}, 64 {"Headphone Jack", NULL, "HPR"}, 65 66 /* speaker */ 67 {"Spk", NULL, "Speaker"}, 68 69 /* other jacks */ 70 {"MIC", NULL, "Headset Mic"}, 71 72 /* digital mics */ 73 {"DMic", NULL, "SoC DMIC"}, 74 75 /* CODEC BE connections */ 76 {"HiFi Playback", NULL, "ssp5 Tx"}, 77 {"ssp5 Tx", NULL, "codec0_out"}, 78 79 {"Playback", NULL, "ssp1 Tx"}, 80 {"ssp1 Tx", NULL, "codec1_out"}, 81 82 {"codec0_in", NULL, "ssp1 Rx"}, 83 {"ssp1 Rx", NULL, "Capture"}, 84 85 {"HDMI1", NULL, "hif5 Output"}, 86 {"HDMI2", NULL, "hif6 Output"}, 87 {"HDMI3", NULL, "hif7 Output"}, 88 89 {"hifi3", NULL, "iDisp3 Tx"}, 90 {"iDisp3 Tx", NULL, "iDisp3_out"}, 91 {"hifi2", NULL, "iDisp2 Tx"}, 92 {"iDisp2 Tx", NULL, "iDisp2_out"}, 93 {"hifi1", NULL, "iDisp1 Tx"}, 94 {"iDisp1 Tx", NULL, "iDisp1_out"}, 95 96 /* DMIC */ 97 {"dmic01_hifi", NULL, "DMIC01 Rx"}, 98 {"DMIC01 Rx", NULL, "DMIC AIF"}, 99 }; 100 101 static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd, 102 struct snd_pcm_hw_params *params) 103 { 104 struct snd_interval *rate = hw_param_interval(params, 105 SNDRV_PCM_HW_PARAM_RATE); 106 struct snd_interval *channels = hw_param_interval(params, 107 SNDRV_PCM_HW_PARAM_CHANNELS); 108 struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); 109 110 /* The ADSP will convert the FE rate to 48k, stereo */ 111 rate->min = rate->max = 48000; 112 channels->min = channels->max = DUAL_CHANNEL; 113 114 /* set SSP to 24 bit */ 115 snd_mask_none(fmt); 116 snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); 117 118 return 0; 119 } 120 121 static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) 122 { 123 int ret; 124 struct snd_soc_codec *codec = rtd->codec; 125 126 /* 127 * Headset buttons map to the google Reference headset. 128 * These can be configured by userspace. 129 */ 130 ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", 131 SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | 132 SND_JACK_BTN_2 | SND_JACK_BTN_3, &broxton_headset, 133 NULL, 0); 134 if (ret) { 135 dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); 136 return ret; 137 } 138 139 da7219_aad_jack_det(codec, &broxton_headset); 140 141 snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); 142 143 return ret; 144 } 145 146 static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd) 147 { 148 struct snd_soc_dai *dai = rtd->codec_dai; 149 150 return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id); 151 } 152 153 static int broxton_da7219_fe_init(struct snd_soc_pcm_runtime *rtd) 154 { 155 struct snd_soc_dapm_context *dapm; 156 struct snd_soc_component *component = rtd->cpu_dai->component; 157 158 dapm = snd_soc_component_get_dapm(component); 159 snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); 160 161 return 0; 162 } 163 164 static unsigned int rates[] = { 165 48000, 166 }; 167 168 static struct snd_pcm_hw_constraint_list constraints_rates = { 169 .count = ARRAY_SIZE(rates), 170 .list = rates, 171 .mask = 0, 172 }; 173 174 static unsigned int channels[] = { 175 DUAL_CHANNEL, 176 }; 177 178 static struct snd_pcm_hw_constraint_list constraints_channels = { 179 .count = ARRAY_SIZE(channels), 180 .list = channels, 181 .mask = 0, 182 }; 183 184 static int bxt_fe_startup(struct snd_pcm_substream *substream) 185 { 186 struct snd_pcm_runtime *runtime = substream->runtime; 187 188 /* 189 * On this platform for PCM device we support, 190 * 48Khz 191 * stereo 192 * 16 bit audio 193 */ 194 195 runtime->hw.channels_max = DUAL_CHANNEL; 196 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 197 &constraints_channels); 198 199 runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; 200 snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); 201 202 snd_pcm_hw_constraint_list(runtime, 0, 203 SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); 204 205 return 0; 206 } 207 208 static const struct snd_soc_ops broxton_da7219_fe_ops = { 209 .startup = bxt_fe_startup, 210 }; 211 212 static int broxton_da7219_hw_params(struct snd_pcm_substream *substream, 213 struct snd_pcm_hw_params *params) 214 { 215 struct snd_soc_pcm_runtime *rtd = substream->private_data; 216 struct snd_soc_dai *codec_dai = rtd->codec_dai; 217 int ret; 218 219 ret = snd_soc_dai_set_sysclk(codec_dai, 220 DA7219_CLKSRC_MCLK, 19200000, SND_SOC_CLOCK_IN); 221 if (ret < 0) 222 dev_err(codec_dai->dev, "can't set codec sysclk configuration\n"); 223 224 ret = snd_soc_dai_set_pll(codec_dai, 0, 225 DA7219_SYSCLK_PLL_SRM, 0, DA7219_PLL_FREQ_OUT_98304); 226 if (ret < 0) { 227 dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret); 228 return -EIO; 229 } 230 231 return ret; 232 } 233 234 static int broxton_da7219_hw_free(struct snd_pcm_substream *substream) 235 { 236 struct snd_soc_pcm_runtime *rtd = substream->private_data; 237 struct snd_soc_dai *codec_dai = rtd->codec_dai; 238 int ret; 239 240 ret = snd_soc_dai_set_pll(codec_dai, 0, 241 DA7219_SYSCLK_MCLK, 0, 0); 242 if (ret < 0) { 243 dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret); 244 return -EIO; 245 } 246 247 return ret; 248 } 249 250 static struct snd_soc_ops broxton_da7219_ops = { 251 .hw_params = broxton_da7219_hw_params, 252 .hw_free = broxton_da7219_hw_free, 253 }; 254 255 /* broxton digital audio interface glue - connects codec <--> CPU */ 256 static struct snd_soc_dai_link broxton_dais[] = { 257 /* Front End DAI links */ 258 [BXT_DPCM_AUDIO_PB] 259 { 260 .name = "Bxt Audio Port", 261 .stream_name = "Audio", 262 .cpu_dai_name = "System Pin", 263 .platform_name = "0000:00:0e.0", 264 .dynamic = 1, 265 .codec_name = "snd-soc-dummy", 266 .codec_dai_name = "snd-soc-dummy-dai", 267 .nonatomic = 1, 268 .init = broxton_da7219_fe_init, 269 .trigger = { 270 SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, 271 .dpcm_playback = 1, 272 .ops = &broxton_da7219_fe_ops, 273 }, 274 [BXT_DPCM_AUDIO_CP] 275 { 276 .name = "Bxt Audio Capture Port", 277 .stream_name = "Audio Record", 278 .cpu_dai_name = "System Pin", 279 .platform_name = "0000:00:0e.0", 280 .dynamic = 1, 281 .codec_name = "snd-soc-dummy", 282 .codec_dai_name = "snd-soc-dummy-dai", 283 .nonatomic = 1, 284 .trigger = { 285 SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, 286 .dpcm_capture = 1, 287 .ops = &broxton_da7219_fe_ops, 288 }, 289 [BXT_DPCM_AUDIO_REF_CP] 290 { 291 .name = "Bxt Audio Reference cap", 292 .stream_name = "Refcap", 293 .cpu_dai_name = "Reference Pin", 294 .codec_name = "snd-soc-dummy", 295 .codec_dai_name = "snd-soc-dummy-dai", 296 .platform_name = "0000:00:0e.0", 297 .init = NULL, 298 .dpcm_capture = 1, 299 .ignore_suspend = 1, 300 .nonatomic = 1, 301 .dynamic = 1, 302 }, 303 [BXT_DPCM_AUDIO_HDMI1_PB] 304 { 305 .name = "Bxt HDMI Port1", 306 .stream_name = "Hdmi1", 307 .cpu_dai_name = "HDMI1 Pin", 308 .codec_name = "snd-soc-dummy", 309 .codec_dai_name = "snd-soc-dummy-dai", 310 .platform_name = "0000:00:0e.0", 311 .dpcm_playback = 1, 312 .init = NULL, 313 .nonatomic = 1, 314 .dynamic = 1, 315 }, 316 [BXT_DPCM_AUDIO_HDMI2_PB] 317 { 318 .name = "Bxt HDMI Port2", 319 .stream_name = "Hdmi2", 320 .cpu_dai_name = "HDMI2 Pin", 321 .codec_name = "snd-soc-dummy", 322 .codec_dai_name = "snd-soc-dummy-dai", 323 .platform_name = "0000:00:0e.0", 324 .dpcm_playback = 1, 325 .init = NULL, 326 .nonatomic = 1, 327 .dynamic = 1, 328 }, 329 [BXT_DPCM_AUDIO_HDMI3_PB] 330 { 331 .name = "Bxt HDMI Port3", 332 .stream_name = "Hdmi3", 333 .cpu_dai_name = "HDMI3 Pin", 334 .codec_name = "snd-soc-dummy", 335 .codec_dai_name = "snd-soc-dummy-dai", 336 .platform_name = "0000:00:0e.0", 337 .dpcm_playback = 1, 338 .init = NULL, 339 .nonatomic = 1, 340 .dynamic = 1, 341 }, 342 /* Back End DAI links */ 343 { 344 /* SSP5 - Codec */ 345 .name = "SSP5-Codec", 346 .id = 0, 347 .cpu_dai_name = "SSP5 Pin", 348 .platform_name = "0000:00:0e.0", 349 .no_pcm = 1, 350 .codec_name = "MX98357A:00", 351 .codec_dai_name = BXT_MAXIM_CODEC_DAI, 352 .dai_fmt = SND_SOC_DAIFMT_I2S | 353 SND_SOC_DAIFMT_NB_NF | 354 SND_SOC_DAIFMT_CBS_CFS, 355 .ignore_pmdown_time = 1, 356 .be_hw_params_fixup = broxton_ssp_fixup, 357 .dpcm_playback = 1, 358 }, 359 { 360 /* SSP1 - Codec */ 361 .name = "SSP1-Codec", 362 .id = 1, 363 .cpu_dai_name = "SSP1 Pin", 364 .platform_name = "0000:00:0e.0", 365 .no_pcm = 1, 366 .codec_name = "i2c-DLGS7219:00", 367 .codec_dai_name = BXT_DIALOG_CODEC_DAI, 368 .init = broxton_da7219_codec_init, 369 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 370 SND_SOC_DAIFMT_CBS_CFS, 371 .ignore_pmdown_time = 1, 372 .be_hw_params_fixup = broxton_ssp_fixup, 373 .ops = &broxton_da7219_ops, 374 .dpcm_playback = 1, 375 .dpcm_capture = 1, 376 }, 377 { 378 .name = "dmic01", 379 .id = 2, 380 .cpu_dai_name = "DMIC01 Pin", 381 .codec_name = "dmic-codec", 382 .codec_dai_name = "dmic-hifi", 383 .platform_name = "0000:00:0e.0", 384 .ignore_suspend = 1, 385 .dpcm_capture = 1, 386 .no_pcm = 1, 387 }, 388 { 389 .name = "iDisp1", 390 .id = 3, 391 .cpu_dai_name = "iDisp1 Pin", 392 .codec_name = "ehdaudio0D2", 393 .codec_dai_name = "intel-hdmi-hifi1", 394 .platform_name = "0000:00:0e.0", 395 .init = broxton_hdmi_init, 396 .dpcm_playback = 1, 397 .no_pcm = 1, 398 }, 399 { 400 .name = "iDisp2", 401 .id = 4, 402 .cpu_dai_name = "iDisp2 Pin", 403 .codec_name = "ehdaudio0D2", 404 .codec_dai_name = "intel-hdmi-hifi2", 405 .platform_name = "0000:00:0e.0", 406 .init = broxton_hdmi_init, 407 .dpcm_playback = 1, 408 .no_pcm = 1, 409 }, 410 { 411 .name = "iDisp3", 412 .id = 5, 413 .cpu_dai_name = "iDisp3 Pin", 414 .codec_name = "ehdaudio0D2", 415 .codec_dai_name = "intel-hdmi-hifi3", 416 .platform_name = "0000:00:0e.0", 417 .init = broxton_hdmi_init, 418 .dpcm_playback = 1, 419 .no_pcm = 1, 420 }, 421 }; 422 423 /* broxton audio machine driver for SPT + da7219 */ 424 static struct snd_soc_card broxton_audio_card = { 425 .name = "bxtda7219max", 426 .owner = THIS_MODULE, 427 .dai_link = broxton_dais, 428 .num_links = ARRAY_SIZE(broxton_dais), 429 .controls = broxton_controls, 430 .num_controls = ARRAY_SIZE(broxton_controls), 431 .dapm_widgets = broxton_widgets, 432 .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), 433 .dapm_routes = broxton_map, 434 .num_dapm_routes = ARRAY_SIZE(broxton_map), 435 .fully_routed = true, 436 }; 437 438 static int broxton_audio_probe(struct platform_device *pdev) 439 { 440 broxton_audio_card.dev = &pdev->dev; 441 return devm_snd_soc_register_card(&pdev->dev, &broxton_audio_card); 442 } 443 444 static struct platform_driver broxton_audio = { 445 .probe = broxton_audio_probe, 446 .driver = { 447 .name = "bxt_da7219_max98357a_i2s", 448 .pm = &snd_soc_pm_ops, 449 }, 450 }; 451 module_platform_driver(broxton_audio) 452 453 /* Module information */ 454 MODULE_DESCRIPTION("Audio Machine driver-DA7219 & MAX98357A in I2S mode"); 455 MODULE_AUTHOR("Sathyanarayana Nujella <sathyanarayana.nujella@intel.com>"); 456 MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com>"); 457 MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>"); 458 MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>"); 459 MODULE_LICENSE("GPL v2"); 460 MODULE_ALIAS("platform:bxt_da7219_max98357a_i2s"); 461