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