1 /* 2 * Machine driver for AMD ACP Audio engine using DA7219 & MAX98357 codec 3 * 4 * Copyright 2017 Advanced Micro Devices, Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 */ 25 26 #include <sound/core.h> 27 #include <sound/soc.h> 28 #include <sound/pcm.h> 29 #include <sound/pcm_params.h> 30 #include <sound/soc-dapm.h> 31 #include <sound/jack.h> 32 #include <linux/clk.h> 33 #include <linux/gpio.h> 34 #include <linux/module.h> 35 #include <linux/regulator/machine.h> 36 #include <linux/regulator/driver.h> 37 #include <linux/i2c.h> 38 #include <linux/input.h> 39 #include <linux/acpi.h> 40 41 #include "acp.h" 42 #include "../codecs/da7219.h" 43 #include "../codecs/da7219-aad.h" 44 45 #define CZ_PLAT_CLK 25000000 46 #define DUAL_CHANNEL 2 47 48 static struct snd_soc_jack cz_jack; 49 static struct clk *da7219_dai_clk; 50 extern int bt_uart_enable; 51 52 static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) 53 { 54 int ret; 55 struct snd_soc_card *card = rtd->card; 56 struct snd_soc_dai *codec_dai = rtd->codec_dai; 57 struct snd_soc_component *component = codec_dai->component; 58 59 dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name); 60 61 ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, 62 CZ_PLAT_CLK, SND_SOC_CLOCK_IN); 63 if (ret < 0) { 64 dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); 65 return ret; 66 } 67 68 ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL, 69 CZ_PLAT_CLK, DA7219_PLL_FREQ_OUT_98304); 70 if (ret < 0) { 71 dev_err(rtd->dev, "can't set codec pll: %d\n", ret); 72 return ret; 73 } 74 75 da7219_dai_clk = clk_get(component->dev, "da7219-dai-clks"); 76 77 ret = snd_soc_card_jack_new(card, "Headset Jack", 78 SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | 79 SND_JACK_BTN_0 | SND_JACK_BTN_1 | 80 SND_JACK_BTN_2 | SND_JACK_BTN_3, 81 &cz_jack, NULL, 0); 82 if (ret) { 83 dev_err(card->dev, "HP jack creation failed %d\n", ret); 84 return ret; 85 } 86 87 snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); 88 snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP); 89 snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); 90 snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); 91 92 da7219_aad_jack_det(component, &cz_jack); 93 94 return 0; 95 } 96 97 static int da7219_clk_enable(struct snd_pcm_substream *substream) 98 { 99 int ret = 0; 100 struct snd_soc_pcm_runtime *rtd = substream->private_data; 101 102 ret = clk_prepare_enable(da7219_dai_clk); 103 if (ret < 0) { 104 dev_err(rtd->dev, "can't enable master clock %d\n", ret); 105 return ret; 106 } 107 108 return ret; 109 } 110 111 static void da7219_clk_disable(void) 112 { 113 clk_disable_unprepare(da7219_dai_clk); 114 } 115 116 static const unsigned int channels[] = { 117 DUAL_CHANNEL, 118 }; 119 120 static const unsigned int rates[] = { 121 48000, 122 }; 123 124 static const struct snd_pcm_hw_constraint_list constraints_rates = { 125 .count = ARRAY_SIZE(rates), 126 .list = rates, 127 .mask = 0, 128 }; 129 130 static const struct snd_pcm_hw_constraint_list constraints_channels = { 131 .count = ARRAY_SIZE(channels), 132 .list = channels, 133 .mask = 0, 134 }; 135 136 static int cz_da7219_startup(struct snd_pcm_substream *substream) 137 { 138 struct snd_pcm_runtime *runtime = substream->runtime; 139 struct snd_soc_pcm_runtime *rtd = substream->private_data; 140 struct snd_soc_card *card = rtd->card; 141 struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); 142 143 /* 144 * On this platform for PCM device we support stereo 145 */ 146 147 runtime->hw.channels_max = DUAL_CHANNEL; 148 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 149 &constraints_channels); 150 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 151 &constraints_rates); 152 153 machine->i2s_instance = I2S_SP_INSTANCE; 154 machine->capture_channel = CAP_CHANNEL1; 155 return da7219_clk_enable(substream); 156 } 157 158 static void cz_da7219_shutdown(struct snd_pcm_substream *substream) 159 { 160 da7219_clk_disable(); 161 } 162 163 static int cz_max_startup(struct snd_pcm_substream *substream) 164 { 165 struct snd_soc_pcm_runtime *rtd = substream->private_data; 166 struct snd_soc_card *card = rtd->card; 167 struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); 168 169 machine->i2s_instance = I2S_BT_INSTANCE; 170 return da7219_clk_enable(substream); 171 } 172 173 static void cz_max_shutdown(struct snd_pcm_substream *substream) 174 { 175 da7219_clk_disable(); 176 } 177 178 static int cz_dmic0_startup(struct snd_pcm_substream *substream) 179 { 180 struct snd_soc_pcm_runtime *rtd = substream->private_data; 181 struct snd_soc_card *card = rtd->card; 182 struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); 183 184 machine->i2s_instance = I2S_BT_INSTANCE; 185 return da7219_clk_enable(substream); 186 } 187 188 static int cz_dmic1_startup(struct snd_pcm_substream *substream) 189 { 190 struct snd_soc_pcm_runtime *rtd = substream->private_data; 191 struct snd_soc_card *card = rtd->card; 192 struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); 193 194 machine->i2s_instance = I2S_SP_INSTANCE; 195 machine->capture_channel = CAP_CHANNEL0; 196 return da7219_clk_enable(substream); 197 } 198 199 static void cz_dmic_shutdown(struct snd_pcm_substream *substream) 200 { 201 da7219_clk_disable(); 202 } 203 204 static const struct snd_soc_ops cz_da7219_cap_ops = { 205 .startup = cz_da7219_startup, 206 .shutdown = cz_da7219_shutdown, 207 }; 208 209 static const struct snd_soc_ops cz_max_play_ops = { 210 .startup = cz_max_startup, 211 .shutdown = cz_max_shutdown, 212 }; 213 214 static const struct snd_soc_ops cz_dmic0_cap_ops = { 215 .startup = cz_dmic0_startup, 216 .shutdown = cz_dmic_shutdown, 217 }; 218 219 static const struct snd_soc_ops cz_dmic1_cap_ops = { 220 .startup = cz_dmic1_startup, 221 .shutdown = cz_dmic_shutdown, 222 }; 223 224 static struct snd_soc_dai_link cz_dai_7219_98357[] = { 225 { 226 .name = "amd-da7219-play", 227 .stream_name = "Playback", 228 .platform_name = "acp_audio_dma.0.auto", 229 .cpu_dai_name = "designware-i2s.1.auto", 230 .codec_dai_name = "da7219-hifi", 231 .codec_name = "i2c-DLGS7219:00", 232 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 233 | SND_SOC_DAIFMT_CBM_CFM, 234 .init = cz_da7219_init, 235 .dpcm_playback = 1, 236 .ops = &cz_da7219_cap_ops, 237 }, 238 { 239 .name = "amd-da7219-cap", 240 .stream_name = "Capture", 241 .platform_name = "acp_audio_dma.0.auto", 242 .cpu_dai_name = "designware-i2s.2.auto", 243 .codec_dai_name = "da7219-hifi", 244 .codec_name = "i2c-DLGS7219:00", 245 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 246 | SND_SOC_DAIFMT_CBM_CFM, 247 .dpcm_capture = 1, 248 .ops = &cz_da7219_cap_ops, 249 }, 250 { 251 .name = "amd-max98357-play", 252 .stream_name = "HiFi Playback", 253 .platform_name = "acp_audio_dma.0.auto", 254 .cpu_dai_name = "designware-i2s.3.auto", 255 .codec_dai_name = "HiFi", 256 .codec_name = "MX98357A:00", 257 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 258 | SND_SOC_DAIFMT_CBM_CFM, 259 .dpcm_playback = 1, 260 .ops = &cz_max_play_ops, 261 }, 262 { 263 /* C panel DMIC */ 264 .name = "dmic0", 265 .stream_name = "DMIC0 Capture", 266 .platform_name = "acp_audio_dma.0.auto", 267 .cpu_dai_name = "designware-i2s.3.auto", 268 .codec_dai_name = "adau7002-hifi", 269 .codec_name = "ADAU7002:00", 270 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 271 | SND_SOC_DAIFMT_CBM_CFM, 272 .dpcm_capture = 1, 273 .ops = &cz_dmic0_cap_ops, 274 }, 275 { 276 /* A/B panel DMIC */ 277 .name = "dmic1", 278 .stream_name = "DMIC1 Capture", 279 .platform_name = "acp_audio_dma.0.auto", 280 .cpu_dai_name = "designware-i2s.2.auto", 281 .codec_dai_name = "adau7002-hifi", 282 .codec_name = "ADAU7002:00", 283 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 284 | SND_SOC_DAIFMT_CBM_CFM, 285 .dpcm_capture = 1, 286 .ops = &cz_dmic1_cap_ops, 287 }, 288 }; 289 290 static const struct snd_soc_dapm_widget cz_widgets[] = { 291 SND_SOC_DAPM_HP("Headphones", NULL), 292 SND_SOC_DAPM_SPK("Speakers", NULL), 293 SND_SOC_DAPM_MIC("Headset Mic", NULL), 294 SND_SOC_DAPM_MIC("Int Mic", NULL), 295 }; 296 297 static const struct snd_soc_dapm_route cz_audio_route[] = { 298 {"Headphones", NULL, "HPL"}, 299 {"Headphones", NULL, "HPR"}, 300 {"MIC", NULL, "Headset Mic"}, 301 {"Speakers", NULL, "Speaker"}, 302 {"PDM_DAT", NULL, "Int Mic"}, 303 }; 304 305 static const struct snd_kcontrol_new cz_mc_controls[] = { 306 SOC_DAPM_PIN_SWITCH("Headphones"), 307 SOC_DAPM_PIN_SWITCH("Speakers"), 308 SOC_DAPM_PIN_SWITCH("Headset Mic"), 309 SOC_DAPM_PIN_SWITCH("Int Mic"), 310 }; 311 312 static struct snd_soc_card cz_card = { 313 .name = "acpd7219m98357", 314 .owner = THIS_MODULE, 315 .dai_link = cz_dai_7219_98357, 316 .num_links = ARRAY_SIZE(cz_dai_7219_98357), 317 .dapm_widgets = cz_widgets, 318 .num_dapm_widgets = ARRAY_SIZE(cz_widgets), 319 .dapm_routes = cz_audio_route, 320 .num_dapm_routes = ARRAY_SIZE(cz_audio_route), 321 .controls = cz_mc_controls, 322 .num_controls = ARRAY_SIZE(cz_mc_controls), 323 }; 324 325 static struct regulator_consumer_supply acp_da7219_supplies[] = { 326 REGULATOR_SUPPLY("VDD", "i2c-DLGS7219:00"), 327 REGULATOR_SUPPLY("VDDMIC", "i2c-DLGS7219:00"), 328 REGULATOR_SUPPLY("VDDIO", "i2c-DLGS7219:00"), 329 REGULATOR_SUPPLY("IOVDD", "ADAU7002:00"), 330 }; 331 332 static struct regulator_init_data acp_da7219_data = { 333 .constraints = { 334 .always_on = 1, 335 }, 336 .num_consumer_supplies = ARRAY_SIZE(acp_da7219_supplies), 337 .consumer_supplies = acp_da7219_supplies, 338 }; 339 340 static struct regulator_config acp_da7219_cfg = { 341 .init_data = &acp_da7219_data, 342 }; 343 344 static struct regulator_ops acp_da7219_ops = { 345 }; 346 347 static struct regulator_desc acp_da7219_desc = { 348 .name = "reg-fixed-1.8V", 349 .type = REGULATOR_VOLTAGE, 350 .owner = THIS_MODULE, 351 .ops = &acp_da7219_ops, 352 .fixed_uV = 1800000, /* 1.8V */ 353 .n_voltages = 1, 354 }; 355 356 static int cz_probe(struct platform_device *pdev) 357 { 358 int ret; 359 struct snd_soc_card *card; 360 struct acp_platform_info *machine; 361 struct regulator_dev *rdev; 362 363 acp_da7219_cfg.dev = &pdev->dev; 364 rdev = devm_regulator_register(&pdev->dev, &acp_da7219_desc, 365 &acp_da7219_cfg); 366 if (IS_ERR(rdev)) { 367 dev_err(&pdev->dev, "Failed to register regulator: %d\n", 368 (int)PTR_ERR(rdev)); 369 return -EINVAL; 370 } 371 372 machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info), 373 GFP_KERNEL); 374 if (!machine) 375 return -ENOMEM; 376 card = &cz_card; 377 cz_card.dev = &pdev->dev; 378 platform_set_drvdata(pdev, card); 379 snd_soc_card_set_drvdata(card, machine); 380 ret = devm_snd_soc_register_card(&pdev->dev, &cz_card); 381 if (ret) { 382 dev_err(&pdev->dev, 383 "devm_snd_soc_register_card(%s) failed: %d\n", 384 cz_card.name, ret); 385 return ret; 386 } 387 bt_uart_enable = !device_property_read_bool(&pdev->dev, 388 "bt-pad-enable"); 389 return 0; 390 } 391 392 static const struct acpi_device_id cz_audio_acpi_match[] = { 393 { "AMD7219", 0 }, 394 {}, 395 }; 396 MODULE_DEVICE_TABLE(acpi, cz_audio_acpi_match); 397 398 static struct platform_driver cz_pcm_driver = { 399 .driver = { 400 .name = "cz-da7219-max98357a", 401 .acpi_match_table = ACPI_PTR(cz_audio_acpi_match), 402 .pm = &snd_soc_pm_ops, 403 }, 404 .probe = cz_probe, 405 }; 406 407 module_platform_driver(cz_pcm_driver); 408 409 MODULE_AUTHOR("akshu.agrawal@amd.com"); 410 MODULE_DESCRIPTION("DA7219 & MAX98357A audio support"); 411 MODULE_LICENSE("GPL v2"); 412