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/i2c.h> 36 #include <linux/input.h> 37 #include <linux/acpi.h> 38 39 #include "acp.h" 40 #include "../codecs/da7219.h" 41 #include "../codecs/da7219-aad.h" 42 43 #define CZ_PLAT_CLK 25000000 44 #define DUAL_CHANNEL 2 45 46 static struct snd_soc_jack cz_jack; 47 static struct clk *da7219_dai_clk; 48 extern int bt_uart_enable; 49 50 static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) 51 { 52 int ret; 53 struct snd_soc_card *card = rtd->card; 54 struct snd_soc_dai *codec_dai = rtd->codec_dai; 55 struct snd_soc_component *component = codec_dai->component; 56 57 dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name); 58 59 ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, 60 CZ_PLAT_CLK, SND_SOC_CLOCK_IN); 61 if (ret < 0) { 62 dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); 63 return ret; 64 } 65 66 ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL, 67 CZ_PLAT_CLK, DA7219_PLL_FREQ_OUT_98304); 68 if (ret < 0) { 69 dev_err(rtd->dev, "can't set codec pll: %d\n", ret); 70 return ret; 71 } 72 73 da7219_dai_clk = clk_get(component->dev, "da7219-dai-clks"); 74 75 ret = snd_soc_card_jack_new(card, "Headset Jack", 76 SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | 77 SND_JACK_BTN_0 | SND_JACK_BTN_1 | 78 SND_JACK_BTN_2 | SND_JACK_BTN_3, 79 &cz_jack, NULL, 0); 80 if (ret) { 81 dev_err(card->dev, "HP jack creation failed %d\n", ret); 82 return ret; 83 } 84 85 snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); 86 snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP); 87 snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); 88 snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); 89 90 da7219_aad_jack_det(component, &cz_jack); 91 92 return 0; 93 } 94 95 static int da7219_clk_enable(struct snd_pcm_substream *substream) 96 { 97 int ret = 0; 98 struct snd_soc_pcm_runtime *rtd = substream->private_data; 99 100 ret = clk_prepare_enable(da7219_dai_clk); 101 if (ret < 0) { 102 dev_err(rtd->dev, "can't enable master clock %d\n", ret); 103 return ret; 104 } 105 106 return ret; 107 } 108 109 static void da7219_clk_disable(void) 110 { 111 clk_disable_unprepare(da7219_dai_clk); 112 } 113 114 static const unsigned int channels[] = { 115 DUAL_CHANNEL, 116 }; 117 118 static const unsigned int rates[] = { 119 48000, 120 }; 121 122 static const struct snd_pcm_hw_constraint_list constraints_rates = { 123 .count = ARRAY_SIZE(rates), 124 .list = rates, 125 .mask = 0, 126 }; 127 128 static const struct snd_pcm_hw_constraint_list constraints_channels = { 129 .count = ARRAY_SIZE(channels), 130 .list = channels, 131 .mask = 0, 132 }; 133 134 static int cz_da7219_startup(struct snd_pcm_substream *substream) 135 { 136 struct snd_pcm_runtime *runtime = substream->runtime; 137 struct snd_soc_pcm_runtime *rtd = substream->private_data; 138 struct snd_soc_card *card = rtd->card; 139 struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); 140 141 /* 142 * On this platform for PCM device we support stereo 143 */ 144 145 runtime->hw.channels_max = DUAL_CHANNEL; 146 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 147 &constraints_channels); 148 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 149 &constraints_rates); 150 151 machine->i2s_instance = I2S_BT_INSTANCE; 152 return da7219_clk_enable(substream); 153 } 154 155 static void cz_da7219_shutdown(struct snd_pcm_substream *substream) 156 { 157 da7219_clk_disable(); 158 } 159 160 static int cz_max_startup(struct snd_pcm_substream *substream) 161 { 162 struct snd_soc_pcm_runtime *rtd = substream->private_data; 163 struct snd_soc_card *card = rtd->card; 164 struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); 165 166 machine->i2s_instance = I2S_SP_INSTANCE; 167 return da7219_clk_enable(substream); 168 } 169 170 static void cz_max_shutdown(struct snd_pcm_substream *substream) 171 { 172 da7219_clk_disable(); 173 } 174 175 static int cz_dmic_startup(struct snd_pcm_substream *substream) 176 { 177 struct snd_soc_pcm_runtime *rtd = substream->private_data; 178 struct snd_soc_card *card = rtd->card; 179 struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); 180 181 machine->i2s_instance = I2S_SP_INSTANCE; 182 return da7219_clk_enable(substream); 183 } 184 185 static void cz_dmic_shutdown(struct snd_pcm_substream *substream) 186 { 187 da7219_clk_disable(); 188 } 189 190 static const struct snd_soc_ops cz_da7219_cap_ops = { 191 .startup = cz_da7219_startup, 192 .shutdown = cz_da7219_shutdown, 193 }; 194 195 static const struct snd_soc_ops cz_max_play_ops = { 196 .startup = cz_max_startup, 197 .shutdown = cz_max_shutdown, 198 }; 199 200 static const struct snd_soc_ops cz_dmic_cap_ops = { 201 .startup = cz_dmic_startup, 202 .shutdown = cz_dmic_shutdown, 203 }; 204 205 static struct snd_soc_dai_link cz_dai_7219_98357[] = { 206 { 207 .name = "amd-da7219-play-cap", 208 .stream_name = "Playback and Capture", 209 .platform_name = "acp_audio_dma.0.auto", 210 .cpu_dai_name = "designware-i2s.3.auto", 211 .codec_dai_name = "da7219-hifi", 212 .codec_name = "i2c-DLGS7219:00", 213 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 214 | SND_SOC_DAIFMT_CBM_CFM, 215 .init = cz_da7219_init, 216 .dpcm_playback = 1, 217 .dpcm_capture = 1, 218 .ops = &cz_da7219_cap_ops, 219 }, 220 { 221 .name = "amd-max98357-play", 222 .stream_name = "HiFi Playback", 223 .platform_name = "acp_audio_dma.0.auto", 224 .cpu_dai_name = "designware-i2s.1.auto", 225 .codec_dai_name = "HiFi", 226 .codec_name = "MX98357A:00", 227 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 228 | SND_SOC_DAIFMT_CBM_CFM, 229 .dpcm_playback = 1, 230 .ops = &cz_max_play_ops, 231 }, 232 { 233 .name = "dmic", 234 .stream_name = "DMIC Capture", 235 .platform_name = "acp_audio_dma.0.auto", 236 .cpu_dai_name = "designware-i2s.2.auto", 237 .codec_dai_name = "adau7002-hifi", 238 .codec_name = "ADAU7002:00", 239 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 240 | SND_SOC_DAIFMT_CBM_CFM, 241 .dpcm_capture = 1, 242 .ops = &cz_dmic_cap_ops, 243 }, 244 }; 245 246 static const struct snd_soc_dapm_widget cz_widgets[] = { 247 SND_SOC_DAPM_HP("Headphones", NULL), 248 SND_SOC_DAPM_SPK("Speakers", NULL), 249 SND_SOC_DAPM_MIC("Headset Mic", NULL), 250 SND_SOC_DAPM_MIC("Int Mic", NULL), 251 }; 252 253 static const struct snd_soc_dapm_route cz_audio_route[] = { 254 {"Headphones", NULL, "HPL"}, 255 {"Headphones", NULL, "HPR"}, 256 {"MIC", NULL, "Headset Mic"}, 257 {"Speakers", NULL, "Speaker"}, 258 {"PDM_DAT", NULL, "Int Mic"}, 259 }; 260 261 static const struct snd_kcontrol_new cz_mc_controls[] = { 262 SOC_DAPM_PIN_SWITCH("Headphones"), 263 SOC_DAPM_PIN_SWITCH("Speakers"), 264 SOC_DAPM_PIN_SWITCH("Headset Mic"), 265 SOC_DAPM_PIN_SWITCH("Int Mic"), 266 }; 267 268 static struct snd_soc_card cz_card = { 269 .name = "acpd7219m98357", 270 .owner = THIS_MODULE, 271 .dai_link = cz_dai_7219_98357, 272 .num_links = ARRAY_SIZE(cz_dai_7219_98357), 273 .dapm_widgets = cz_widgets, 274 .num_dapm_widgets = ARRAY_SIZE(cz_widgets), 275 .dapm_routes = cz_audio_route, 276 .num_dapm_routes = ARRAY_SIZE(cz_audio_route), 277 .controls = cz_mc_controls, 278 .num_controls = ARRAY_SIZE(cz_mc_controls), 279 }; 280 281 static int cz_probe(struct platform_device *pdev) 282 { 283 int ret; 284 struct snd_soc_card *card; 285 struct acp_platform_info *machine; 286 287 machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info), 288 GFP_KERNEL); 289 if (!machine) 290 return -ENOMEM; 291 card = &cz_card; 292 cz_card.dev = &pdev->dev; 293 platform_set_drvdata(pdev, card); 294 snd_soc_card_set_drvdata(card, machine); 295 ret = devm_snd_soc_register_card(&pdev->dev, &cz_card); 296 if (ret) { 297 dev_err(&pdev->dev, 298 "devm_snd_soc_register_card(%s) failed: %d\n", 299 cz_card.name, ret); 300 return ret; 301 } 302 bt_uart_enable = !device_property_read_bool(&pdev->dev, 303 "bt-pad-enable"); 304 return 0; 305 } 306 307 static const struct acpi_device_id cz_audio_acpi_match[] = { 308 { "AMD7219", 0 }, 309 {}, 310 }; 311 MODULE_DEVICE_TABLE(acpi, cz_audio_acpi_match); 312 313 static struct platform_driver cz_pcm_driver = { 314 .driver = { 315 .name = "cz-da7219-max98357a", 316 .acpi_match_table = ACPI_PTR(cz_audio_acpi_match), 317 .pm = &snd_soc_pm_ops, 318 }, 319 .probe = cz_probe, 320 }; 321 322 module_platform_driver(cz_pcm_driver); 323 324 MODULE_AUTHOR("akshu.agrawal@amd.com"); 325 MODULE_DESCRIPTION("DA7219 & MAX98357A audio support"); 326 MODULE_LICENSE("GPL v2"); 327