blob: 96fbcd29e3edaa36bb1ba2e671f065cff4e81cbf [file] [log] [blame]
Akshu Agrawal6b8e4e72020-02-17 10:35:01 +05301// SPDX-License-Identifier: GPL-2.0+
2//
3// Machine driver for AMD ACP Audio engine using DA7219 & MAX98357 codec.
4//
5//Copyright 2016 Advanced Micro Devices, Inc.
6
7#include <sound/core.h>
8#include <sound/soc.h>
9#include <sound/pcm.h>
10#include <sound/pcm_params.h>
11#include <sound/soc-dapm.h>
12#include <sound/jack.h>
13#include <linux/clk.h>
14#include <linux/gpio.h>
15#include <linux/module.h>
16#include <linux/i2c.h>
17#include <linux/input.h>
18#include <linux/acpi.h>
19
20#include "raven/acp3x.h"
21#include "../codecs/rt5682.h"
22
23#define PCO_PLAT_CLK 48000000
24#define RT5682_PLL_FREQ (48000 * 512)
25#define DUAL_CHANNEL 2
26
27static struct snd_soc_jack pco_jack;
28static struct clk *rt5682_dai_wclk;
29static struct clk *rt5682_dai_bclk;
30
31static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd)
32{
33 int ret;
34 struct snd_soc_card *card = rtd->card;
35 struct snd_soc_dai *codec_dai = rtd->codec_dai;
36 struct snd_soc_component *component = codec_dai->component;
37
38 dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
39
40 /* set rt5682 dai fmt */
41 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
42 | SND_SOC_DAIFMT_NB_NF
43 | SND_SOC_DAIFMT_CBM_CFM);
44 if (ret < 0) {
45 dev_err(rtd->card->dev,
46 "Failed to set rt5682 dai fmt: %d\n", ret);
47 return ret;
48 }
49
50 /* set codec PLL */
51 ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK,
52 PCO_PLAT_CLK, RT5682_PLL_FREQ);
53 if (ret < 0) {
54 dev_err(rtd->dev, "can't set rt5682 PLL: %d\n", ret);
55 return ret;
56 }
57
58 /* Set codec sysclk */
59 ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2,
60 RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
61 if (ret < 0) {
62 dev_err(rtd->dev,
63 "Failed to set rt5682 SYSCLK: %d\n", ret);
64 return ret;
65 }
66
67 /* Set tdm/i2s1 master bclk ratio */
68 ret = snd_soc_dai_set_bclk_ratio(codec_dai, 64);
69 if (ret < 0) {
70 dev_err(rtd->dev,
71 "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
72 return ret;
73 }
74
75 rt5682_dai_wclk = clk_get(component->dev, "rt5682-dai-wclk");
76 rt5682_dai_bclk = clk_get(component->dev, "rt5682-dai-bclk");
77
78 ret = snd_soc_card_jack_new(card, "Headset Jack",
79 SND_JACK_HEADSET | SND_JACK_LINEOUT |
80 SND_JACK_BTN_0 | SND_JACK_BTN_1 |
81 SND_JACK_BTN_2 | SND_JACK_BTN_3,
82 &pco_jack, NULL, 0);
83 if (ret) {
84 dev_err(card->dev, "HP jack creation failed %d\n", ret);
85 return ret;
86 }
87
88 snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
89 snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
90 snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
91 snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
92
93 ret = snd_soc_component_set_jack(component, &pco_jack, NULL);
94 if (ret) {
95 dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
96 return ret;
97 }
98
99 return ret;
100}
101
102static int rt5682_clk_enable(struct snd_pcm_substream *substream)
103{
104 int ret = 0;
105 struct snd_soc_pcm_runtime *rtd = substream->private_data;
106
107 /* RT5682 will support only 48K output with 48M mclk */
108 clk_set_rate(rt5682_dai_wclk, 48000);
109 clk_set_rate(rt5682_dai_bclk, 48000 * 64);
110 ret = clk_prepare_enable(rt5682_dai_wclk);
111 if (ret < 0) {
112 dev_err(rtd->dev, "can't enable wclk %d\n", ret);
113 return ret;
114 }
115
116 return ret;
117}
118
119static void rt5682_clk_disable(void)
120{
121 clk_disable_unprepare(rt5682_dai_wclk);
122}
123
124static const unsigned int channels[] = {
125 DUAL_CHANNEL,
126};
127
128static const unsigned int rates[] = {
129 48000,
130};
131
132static const struct snd_pcm_hw_constraint_list constraints_rates = {
133 .count = ARRAY_SIZE(rates),
134 .list = rates,
135 .mask = 0,
136};
137
138static const struct snd_pcm_hw_constraint_list constraints_channels = {
139 .count = ARRAY_SIZE(channels),
140 .list = channels,
141 .mask = 0,
142};
143
144static int acp3x_5682_startup(struct snd_pcm_substream *substream)
145{
146 struct snd_pcm_runtime *runtime = substream->runtime;
147 struct snd_soc_pcm_runtime *rtd = substream->private_data;
148 struct snd_soc_card *card = rtd->card;
149 struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
150
151 machine->play_i2s_instance = I2S_SP_INSTANCE;
152 machine->cap_i2s_instance = I2S_SP_INSTANCE;
153
154 runtime->hw.channels_max = DUAL_CHANNEL;
155 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
156 &constraints_channels);
157 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
158 &constraints_rates);
159 return rt5682_clk_enable(substream);
160}
161
162static int acp3x_max_startup(struct snd_pcm_substream *substream)
163{
164 struct snd_pcm_runtime *runtime = substream->runtime;
165 struct snd_soc_pcm_runtime *rtd = substream->private_data;
166 struct snd_soc_card *card = rtd->card;
167 struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
168
169 machine->play_i2s_instance = I2S_BT_INSTANCE;
170
171 runtime->hw.channels_max = DUAL_CHANNEL;
172 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
173 &constraints_channels);
174 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
175 &constraints_rates);
176 return rt5682_clk_enable(substream);
177}
178
179static int acp3x_ec_startup(struct snd_pcm_substream *substream)
180{
181 struct snd_soc_pcm_runtime *rtd = substream->private_data;
182 struct snd_soc_card *card = rtd->card;
183 struct snd_soc_dai *codec_dai = rtd->codec_dai;
184 struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
185
186 machine->cap_i2s_instance = I2S_BT_INSTANCE;
187 snd_soc_dai_set_bclk_ratio(codec_dai, 64);
188
189 return rt5682_clk_enable(substream);
190}
191
192static void rt5682_shutdown(struct snd_pcm_substream *substream)
193{
194 rt5682_clk_disable();
195}
196
197static const struct snd_soc_ops acp3x_5682_ops = {
198 .startup = acp3x_5682_startup,
199 .shutdown = rt5682_shutdown,
200};
201
202static const struct snd_soc_ops acp3x_max_play_ops = {
203 .startup = acp3x_max_startup,
204 .shutdown = rt5682_shutdown,
205};
206
207static const struct snd_soc_ops acp3x_ec_cap_ops = {
208 .startup = acp3x_ec_startup,
209 .shutdown = rt5682_shutdown,
210};
211
212SND_SOC_DAILINK_DEF(acp3x_i2s,
213 DAILINK_COMP_ARRAY(COMP_CPU("acp3x_i2s_playcap.0")));
214SND_SOC_DAILINK_DEF(acp3x_bt,
215 DAILINK_COMP_ARRAY(COMP_CPU("acp3x_i2s_playcap.2")));
216
217SND_SOC_DAILINK_DEF(rt5682,
218 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1")));
219SND_SOC_DAILINK_DEF(max,
220 DAILINK_COMP_ARRAY(COMP_CODEC("MX98357A:00", "HiFi")));
221SND_SOC_DAILINK_DEF(cros_ec,
222 DAILINK_COMP_ARRAY(COMP_CODEC("GOOG0013:00", "EC Codec I2S RX")));
223
224SND_SOC_DAILINK_DEF(platform,
225 DAILINK_COMP_ARRAY(COMP_PLATFORM("acp3x_rv_i2s_dma.0")));
226
227static struct snd_soc_dai_link acp3x_dai_5682_98357[] = {
228 {
229 .name = "acp3x-5682-play",
230 .stream_name = "Playback",
231 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
232 | SND_SOC_DAIFMT_CBM_CFM,
233 .init = acp3x_5682_init,
234 .dpcm_playback = 1,
235 .dpcm_capture = 1,
236 .ops = &acp3x_5682_ops,
237 SND_SOC_DAILINK_REG(acp3x_i2s, rt5682, platform),
238 },
239 {
240 .name = "acp3x-max98357-play",
241 .stream_name = "HiFi Playback",
242 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
243 | SND_SOC_DAIFMT_CBM_CFM,
244 .dpcm_playback = 1,
245 .ops = &acp3x_max_play_ops,
246 SND_SOC_DAILINK_REG(acp3x_bt, max, platform),
247 },
248 {
249 .name = "acp3x-ec-capture",
250 .stream_name = "Capture",
251 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
252 | SND_SOC_DAIFMT_CBS_CFS,
253 .dpcm_capture = 1,
254 .ops = &acp3x_ec_cap_ops,
255 SND_SOC_DAILINK_REG(acp3x_bt, cros_ec, platform),
256 },
257};
258
259static const struct snd_soc_dapm_widget acp3x_widgets[] = {
260 SND_SOC_DAPM_HP("Headphone Jack", NULL),
261 SND_SOC_DAPM_SPK("Spk", NULL),
262 SND_SOC_DAPM_MIC("Headset Mic", NULL),
263};
264
265static const struct snd_soc_dapm_route acp3x_audio_route[] = {
266 {"Headphone Jack", NULL, "HPOL"},
267 {"Headphone Jack", NULL, "HPOR"},
268 {"IN1P", NULL, "Headset Mic"},
269 {"Spk", NULL, "Speaker"},
270};
271
272static const struct snd_kcontrol_new acp3x_mc_controls[] = {
273 SOC_DAPM_PIN_SWITCH("Headphone Jack"),
274 SOC_DAPM_PIN_SWITCH("Spk"),
275 SOC_DAPM_PIN_SWITCH("Headset Mic"),
276};
277
278static struct snd_soc_card acp3x_card = {
279 .name = "acp3xalc5682m98357",
280 .owner = THIS_MODULE,
281 .dai_link = acp3x_dai_5682_98357,
282 .num_links = ARRAY_SIZE(acp3x_dai_5682_98357),
283 .dapm_widgets = acp3x_widgets,
284 .num_dapm_widgets = ARRAY_SIZE(acp3x_widgets),
285 .dapm_routes = acp3x_audio_route,
286 .num_dapm_routes = ARRAY_SIZE(acp3x_audio_route),
287 .controls = acp3x_mc_controls,
288 .num_controls = ARRAY_SIZE(acp3x_mc_controls),
289};
290
291static int acp3x_probe(struct platform_device *pdev)
292{
293 int ret;
294 struct snd_soc_card *card;
295 struct acp3x_platform_info *machine;
296
297 machine = devm_kzalloc(&pdev->dev, sizeof(*machine), GFP_KERNEL);
298 if (!machine)
299 return -ENOMEM;
300
301 card = &acp3x_card;
302 acp3x_card.dev = &pdev->dev;
303 platform_set_drvdata(pdev, card);
304 snd_soc_card_set_drvdata(card, machine);
305 ret = devm_snd_soc_register_card(&pdev->dev, &acp3x_card);
306 if (ret) {
307 dev_err(&pdev->dev,
308 "devm_snd_soc_register_card(%s) failed: %d\n",
309 acp3x_card.name, ret);
310 return ret;
311 }
312 return 0;
313}
314
315static const struct acpi_device_id acp3x_audio_acpi_match[] = {
316 { "AMDI5682", 0 },
317 {},
318};
319MODULE_DEVICE_TABLE(acpi, acp3x_audio_acpi_match);
320
321static struct platform_driver acp3x_audio = {
322 .driver = {
323 .name = "acp3x-alc5682-max98357",
324 .acpi_match_table = ACPI_PTR(acp3x_audio_acpi_match),
325 .pm = &snd_soc_pm_ops,
326 },
327 .probe = acp3x_probe,
328};
329
330module_platform_driver(acp3x_audio);
331
332MODULE_AUTHOR("akshu.agrawal@amd.com");
333MODULE_DESCRIPTION("ALC5682 & MAX98357 audio support");
334MODULE_LICENSE("GPL v2");