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