1// SPDX-License-Identifier: GPL-2.0+
2//
3// Machine driver for AMD ACP Audio engine using ES8336 codec.
4//
5// Copyright 2023 Marian Postevca <posteuca@mutex.one>
6#include <sound/core.h>
7#include <sound/soc.h>
8#include <sound/pcm.h>
9#include <sound/pcm_params.h>
10#include <sound/soc-dapm.h>
11#include <sound/jack.h>
12#include <sound/soc-acpi.h>
13#include <linux/clk.h>
14#include <linux/gpio.h>
15#include <linux/gpio/consumer.h>
16#include <linux/module.h>
17#include <linux/i2c.h>
18#include <linux/input.h>
19#include <linux/io.h>
20#include <linux/acpi.h>
21#include <linux/dmi.h>
22#include <linux/string_choices.h>
23#include "../acp-mach.h"
24#include "acp3x-es83xx.h"
25
26#define get_mach_priv(card) ((struct acp3x_es83xx_private *)((acp_get_drvdata(card))->mach_priv))
27
28#define DUAL_CHANNEL 2
29
30#define ES83XX_ENABLE_DMIC BIT(4)
31#define ES83XX_48_MHZ_MCLK BIT(5)
32
33struct acp3x_es83xx_private {
34 bool speaker_on;
35 bool headphone_on;
36 unsigned long quirk;
37 struct snd_soc_component *codec;
38 struct device *codec_dev;
39 struct gpio_desc *gpio_speakers, *gpio_headphone;
40 struct acpi_gpio_params enable_spk_gpio, enable_hp_gpio;
41 struct acpi_gpio_mapping gpio_mapping[3];
42 struct snd_soc_dapm_route mic_map[2];
43};
44
45static const unsigned int channels[] = {
46 DUAL_CHANNEL,
47};
48
49static const struct snd_pcm_hw_constraint_list constraints_channels = {
50 .count = ARRAY_SIZE(channels),
51 .list = channels,
52 .mask = 0,
53};
54
55#define ES83xx_12288_KHZ_MCLK_FREQ (48000 * 256)
56#define ES83xx_48_MHZ_MCLK_FREQ (48000 * 1000)
57
58static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w,
59 struct snd_kcontrol *kcontrol, int event);
60static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w,
61 struct snd_kcontrol *kcontrol, int event);
62
63static int acp3x_es83xx_codec_startup(struct snd_pcm_substream *substream)
64{
65 struct snd_pcm_runtime *runtime;
66 struct snd_soc_pcm_runtime *rtd;
67 struct snd_soc_dai *codec_dai;
68 struct acp3x_es83xx_private *priv;
69 unsigned int freq;
70 int ret;
71
72 runtime = substream->runtime;
73 rtd = snd_soc_substream_to_rtd(substream);
74 codec_dai = snd_soc_rtd_to_codec(rtd, 0);
75 priv = get_mach_priv(rtd->card);
76
77 if (priv->quirk & ES83XX_48_MHZ_MCLK) {
78 dev_dbg(priv->codec_dev, "using a 48Mhz MCLK\n");
79 freq = ES83xx_48_MHZ_MCLK_FREQ;
80 } else {
81 dev_dbg(priv->codec_dev, "using a 12.288Mhz MCLK\n");
82 freq = ES83xx_12288_KHZ_MCLK_FREQ;
83 }
84
85 ret = snd_soc_dai_set_sysclk(dai: codec_dai, clk_id: 0, freq, SND_SOC_CLOCK_OUT);
86 if (ret < 0) {
87 dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
88 return ret;
89 }
90
91 runtime->hw.channels_max = DUAL_CHANNEL;
92 snd_pcm_hw_constraint_list(runtime, cond: 0, SNDRV_PCM_HW_PARAM_CHANNELS,
93 l: &constraints_channels);
94
95 return 0;
96}
97
98static struct snd_soc_jack es83xx_jack;
99
100static struct snd_soc_jack_pin es83xx_jack_pins[] = {
101 {
102 .pin = "Headphone",
103 .mask = SND_JACK_HEADPHONE,
104 },
105 {
106 .pin = "Headset Mic",
107 .mask = SND_JACK_MICROPHONE,
108 },
109};
110
111static const struct snd_soc_dapm_widget acp3x_es83xx_widgets[] = {
112 SND_SOC_DAPM_SPK("Speaker", NULL),
113 SND_SOC_DAPM_HP("Headphone", NULL),
114 SND_SOC_DAPM_MIC("Headset Mic", NULL),
115 SND_SOC_DAPM_MIC("Internal Mic", NULL),
116
117 SND_SOC_DAPM_SUPPLY("Headphone Power", SND_SOC_NOPM, 0, 0,
118 acp3x_es83xx_headphone_power_event,
119 SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
120 SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
121 acp3x_es83xx_speaker_power_event,
122 SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
123};
124
125static const struct snd_soc_dapm_route acp3x_es83xx_audio_map[] = {
126 {"Headphone", NULL, "HPOL"},
127 {"Headphone", NULL, "HPOR"},
128 {"Headphone", NULL, "Headphone Power"},
129
130 /*
131 * There is no separate speaker output instead the speakers are muxed to
132 * the HP outputs. The mux is controlled Speaker and/or headphone switch.
133 */
134 {"Speaker", NULL, "HPOL"},
135 {"Speaker", NULL, "HPOR"},
136 {"Speaker", NULL, "Speaker Power"},
137};
138
139
140static const struct snd_kcontrol_new acp3x_es83xx_controls[] = {
141 SOC_DAPM_PIN_SWITCH("Speaker"),
142 SOC_DAPM_PIN_SWITCH("Headphone"),
143 SOC_DAPM_PIN_SWITCH("Headset Mic"),
144 SOC_DAPM_PIN_SWITCH("Internal Mic"),
145};
146
147static int acp3x_es83xx_configure_widgets(struct snd_soc_card *card)
148{
149 card->dapm_widgets = acp3x_es83xx_widgets;
150 card->num_dapm_widgets = ARRAY_SIZE(acp3x_es83xx_widgets);
151 card->controls = acp3x_es83xx_controls;
152 card->num_controls = ARRAY_SIZE(acp3x_es83xx_controls);
153 card->dapm_routes = acp3x_es83xx_audio_map;
154 card->num_dapm_routes = ARRAY_SIZE(acp3x_es83xx_audio_map);
155
156 return 0;
157}
158
159static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w,
160 struct snd_kcontrol *kcontrol, int event)
161{
162 struct snd_soc_card *card = snd_soc_dapm_to_card(dapm: w->dapm);
163 struct acp3x_es83xx_private *priv = get_mach_priv(card);
164
165 dev_dbg(priv->codec_dev, "headphone power event = %d\n", event);
166 if (SND_SOC_DAPM_EVENT_ON(event))
167 priv->headphone_on = true;
168 else
169 priv->headphone_on = false;
170
171 gpiod_set_value_cansleep(desc: priv->gpio_speakers, value: priv->speaker_on);
172 gpiod_set_value_cansleep(desc: priv->gpio_headphone, value: priv->headphone_on);
173
174 return 0;
175}
176
177static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w,
178 struct snd_kcontrol *kcontrol, int event)
179{
180 struct snd_soc_card *card = snd_soc_dapm_to_card(dapm: w->dapm);
181 struct acp3x_es83xx_private *priv = get_mach_priv(card);
182
183 dev_dbg(priv->codec_dev, "speaker power event: %d\n", event);
184 if (SND_SOC_DAPM_EVENT_ON(event))
185 priv->speaker_on = true;
186 else
187 priv->speaker_on = false;
188
189 gpiod_set_value_cansleep(desc: priv->gpio_speakers, value: priv->speaker_on);
190 gpiod_set_value_cansleep(desc: priv->gpio_headphone, value: priv->headphone_on);
191
192 return 0;
193}
194
195static int acp3x_es83xx_suspend_pre(struct snd_soc_card *card)
196{
197 struct acp3x_es83xx_private *priv = get_mach_priv(card);
198
199 /* We need to disable the jack in the machine driver suspend
200 * callback so that the CODEC suspend callback actually gets
201 * called. Without doing it, the CODEC suspend/resume
202 * callbacks do not get called if headphones are plugged in.
203 * This is because plugging in headphones keeps some supplies
204 * active, this in turn means that the lowest bias level
205 * that the CODEC can go to is SND_SOC_BIAS_STANDBY.
206 * If components do not set idle_bias_on to true then
207 * their suspend/resume callbacks do not get called.
208 */
209 dev_dbg(priv->codec_dev, "card suspend\n");
210 snd_soc_component_set_jack(component: priv->codec, NULL, NULL);
211 return 0;
212}
213
214static int acp3x_es83xx_resume_post(struct snd_soc_card *card)
215{
216 struct acp3x_es83xx_private *priv = get_mach_priv(card);
217
218 /* We disabled jack detection in suspend callback,
219 * enable it back.
220 */
221 dev_dbg(priv->codec_dev, "card resume\n");
222 snd_soc_component_set_jack(component: priv->codec, jack: &es83xx_jack, NULL);
223 return 0;
224}
225
226static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv)
227{
228
229 priv->enable_spk_gpio.crs_entry_index = 0;
230 priv->enable_hp_gpio.crs_entry_index = 1;
231
232 priv->enable_spk_gpio.active_low = false;
233 priv->enable_hp_gpio.active_low = false;
234
235 priv->gpio_mapping[0].name = "speakers-enable-gpios";
236 priv->gpio_mapping[0].data = &priv->enable_spk_gpio;
237 priv->gpio_mapping[0].size = 1;
238 priv->gpio_mapping[0].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO;
239
240 priv->gpio_mapping[1].name = "headphone-enable-gpios";
241 priv->gpio_mapping[1].data = &priv->enable_hp_gpio;
242 priv->gpio_mapping[1].size = 1;
243 priv->gpio_mapping[1].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO;
244
245 dev_info(priv->codec_dev, "speaker gpio %d active %s, headphone gpio %d active %s\n",
246 priv->enable_spk_gpio.crs_entry_index,
247 str_low_high(priv->enable_spk_gpio.active_low),
248 priv->enable_hp_gpio.crs_entry_index,
249 str_low_high(priv->enable_hp_gpio.active_low));
250 return 0;
251}
252
253static int acp3x_es83xx_configure_mics(struct acp3x_es83xx_private *priv)
254{
255 int num_routes = 0;
256 int i;
257
258 if (!(priv->quirk & ES83XX_ENABLE_DMIC)) {
259 priv->mic_map[num_routes].sink = "MIC1";
260 priv->mic_map[num_routes].source = "Internal Mic";
261 num_routes++;
262 }
263
264 priv->mic_map[num_routes].sink = "MIC2";
265 priv->mic_map[num_routes].source = "Headset Mic";
266 num_routes++;
267
268 for (i = 0; i < num_routes; i++)
269 dev_info(priv->codec_dev, "%s is %s\n",
270 priv->mic_map[i].source, priv->mic_map[i].sink);
271
272 return num_routes;
273}
274
275static int acp3x_es83xx_init(struct snd_soc_pcm_runtime *runtime)
276{
277 struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component;
278 struct snd_soc_card *card = runtime->card;
279 struct acp3x_es83xx_private *priv = get_mach_priv(card);
280 int ret = 0;
281 int num_routes;
282
283 ret = snd_soc_card_jack_new_pins(card, id: "Headset",
284 type: SND_JACK_HEADSET | SND_JACK_BTN_0,
285 jack: &es83xx_jack, pins: es83xx_jack_pins,
286 ARRAY_SIZE(es83xx_jack_pins));
287 if (ret) {
288 dev_err(card->dev, "jack creation failed %d\n", ret);
289 return ret;
290 }
291
292 snd_jack_set_key(jack: es83xx_jack.jack, type: SND_JACK_BTN_0, KEY_PLAYPAUSE);
293
294 snd_soc_component_set_jack(component: codec, jack: &es83xx_jack, NULL);
295
296 priv->codec = codec;
297 acp3x_es83xx_configure_gpios(priv);
298
299 ret = devm_acpi_dev_add_driver_gpios(dev: priv->codec_dev, gpios: priv->gpio_mapping);
300 if (ret)
301 dev_warn(priv->codec_dev, "failed to add speaker gpio\n");
302
303 priv->gpio_speakers = gpiod_get_optional(dev: priv->codec_dev, con_id: "speakers-enable",
304 flags: priv->enable_spk_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
305 if (IS_ERR(ptr: priv->gpio_speakers)) {
306 dev_err(priv->codec_dev, "could not get speakers-enable GPIO\n");
307 return PTR_ERR(ptr: priv->gpio_speakers);
308 }
309
310 priv->gpio_headphone = gpiod_get_optional(dev: priv->codec_dev, con_id: "headphone-enable",
311 flags: priv->enable_hp_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
312 if (IS_ERR(ptr: priv->gpio_headphone)) {
313 dev_err(priv->codec_dev, "could not get headphone-enable GPIO\n");
314 return PTR_ERR(ptr: priv->gpio_headphone);
315 }
316
317 num_routes = acp3x_es83xx_configure_mics(priv);
318 if (num_routes > 0) {
319 struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
320
321 ret = snd_soc_dapm_add_routes(dapm, route: priv->mic_map, num: num_routes);
322 if (ret != 0)
323 device_remove_software_node(dev: priv->codec_dev);
324 }
325
326 return ret;
327}
328
329static const struct snd_soc_ops acp3x_es83xx_ops = {
330 .startup = acp3x_es83xx_codec_startup,
331};
332
333
334SND_SOC_DAILINK_DEF(codec,
335 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
336
337static const struct dmi_system_id acp3x_es83xx_dmi_table[] = {
338 {
339 .matches = {
340 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
341 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"),
342 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
343 },
344 .driver_data = (void *)(ES83XX_ENABLE_DMIC),
345 },
346 {
347 .matches = {
348 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
349 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"),
350 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
351 },
352 .driver_data = (void *)(ES83XX_ENABLE_DMIC),
353 },
354 {
355 .matches = {
356 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
357 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"),
358 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
359 },
360 .driver_data = (void *)(ES83XX_ENABLE_DMIC|ES83XX_48_MHZ_MCLK),
361 },
362 {
363 .matches = {
364 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
365 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
366 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
367 },
368 .driver_data = (void *)(ES83XX_ENABLE_DMIC),
369 },
370 {
371 .matches = {
372 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
373 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
374 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"),
375 },
376 .driver_data = (void *)(ES83XX_ENABLE_DMIC),
377 },
378 {
379 .matches = {
380 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
381 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
382 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"),
383 },
384 .driver_data = (void *)(ES83XX_ENABLE_DMIC),
385 },
386 {}
387};
388
389static int acp3x_es83xx_configure_link(struct snd_soc_card *card, struct snd_soc_dai_link *link)
390{
391 link->codecs = codec;
392 link->num_codecs = ARRAY_SIZE(codec);
393 link->init = acp3x_es83xx_init;
394 link->ops = &acp3x_es83xx_ops;
395 link->dai_fmt = SND_SOC_DAIFMT_I2S
396 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
397
398 return 0;
399}
400
401static int acp3x_es83xx_probe(struct snd_soc_card *card)
402{
403 int ret = 0;
404 struct device *dev = card->dev;
405 const struct dmi_system_id *dmi_id;
406
407 dmi_id = dmi_first_match(list: acp3x_es83xx_dmi_table);
408 if (dmi_id && dmi_id->driver_data) {
409 struct acp3x_es83xx_private *priv;
410 struct acp_card_drvdata *acp_drvdata;
411 struct acpi_device *adev;
412 struct device *codec_dev;
413
414 acp_drvdata = (struct acp_card_drvdata *)card->drvdata;
415
416 dev_info(dev, "matched DMI table with this system, trying to register sound card\n");
417
418 adev = acpi_dev_get_first_match_dev(hid: acp_drvdata->acpi_mach->id, NULL, hrv: -1);
419 if (!adev) {
420 dev_err(dev, "Error cannot find '%s' dev\n", acp_drvdata->acpi_mach->id);
421 return -ENXIO;
422 }
423
424 codec_dev = acpi_get_first_physical_node(adev);
425 acpi_dev_put(adev);
426 if (!codec_dev) {
427 dev_warn(dev, "Error cannot find codec device, will defer probe\n");
428 return -EPROBE_DEFER;
429 }
430
431 priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL);
432 if (!priv) {
433 put_device(dev: codec_dev);
434 return -ENOMEM;
435 }
436
437 priv->codec_dev = codec_dev;
438 priv->quirk = (unsigned long)dmi_id->driver_data;
439 acp_drvdata->mach_priv = priv;
440 dev_info(dev, "successfully probed the sound card\n");
441 } else {
442 ret = -ENODEV;
443 dev_warn(dev, "this system has a ES83xx codec defined in ACPI, but the driver doesn't have this system registered in DMI table\n");
444 }
445 return ret;
446}
447
448
449void acp3x_es83xx_init_ops(struct acp_mach_ops *ops)
450{
451 ops->probe = acp3x_es83xx_probe;
452 ops->configure_widgets = acp3x_es83xx_configure_widgets;
453 ops->configure_link = acp3x_es83xx_configure_link;
454 ops->suspend_pre = acp3x_es83xx_suspend_pre;
455 ops->resume_post = acp3x_es83xx_resume_post;
456}
457

source code of linux/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c