1 // SPDX-License-Identifier: GPL-2.0+
3 * Machine driver for AMD Stoney platform using ES8336 Codec
5 * Copyright 2022 Advanced Micro Devices, Inc.
8 #include <sound/core.h>
10 #include <sound/pcm.h>
11 #include <sound/pcm_params.h>
12 #include <sound/soc-dapm.h>
13 #include <sound/jack.h>
14 #include <linux/gpio.h>
15 #include <linux/device.h>
16 #include <linux/dmi.h>
17 #include <linux/gpio/consumer.h>
18 #include <linux/gpio/machine.h>
19 #include <linux/i2c.h>
20 #include <linux/input.h>
21 #include <linux/module.h>
22 #include <linux/platform_device.h>
23 #include <linux/acpi.h>
27 #define DUAL_CHANNEL 2
28 #define DRV_NAME "acp2x_mach"
30 #define ES8336_PLL_FREQ (48000 * 256)
32 static unsigned long acp2x_machine_id
;
33 static struct snd_soc_jack st_jack
;
34 static struct device
*codec_dev
;
35 static struct gpio_desc
*gpio_pa
;
37 static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget
*w
,
38 struct snd_kcontrol
*kcontrol
, int event
)
40 if (SND_SOC_DAPM_EVENT_ON(event
))
41 gpiod_set_value_cansleep(gpio_pa
, true);
43 gpiod_set_value_cansleep(gpio_pa
, false);
48 static struct snd_soc_jack_pin st_es8316_jack_pins
[] = {
51 .mask
= SND_JACK_HEADPHONE
,
55 .mask
= SND_JACK_MICROPHONE
,
59 static int st_es8336_init(struct snd_soc_pcm_runtime
*rtd
)
62 struct snd_soc_card
*card
;
63 struct snd_soc_component
*codec
;
65 codec
= snd_soc_rtd_to_codec(rtd
, 0)->component
;
68 ret
= snd_soc_card_jack_new_pins(card
, "Headset", SND_JACK_HEADSET
| SND_JACK_BTN_0
,
69 &st_jack
, st_es8316_jack_pins
,
70 ARRAY_SIZE(st_es8316_jack_pins
));
72 dev_err(card
->dev
, "HP jack creation failed %d\n", ret
);
75 snd_jack_set_key(st_jack
.jack
, SND_JACK_BTN_0
, KEY_PLAYPAUSE
);
76 ret
= snd_soc_component_set_jack(codec
, &st_jack
, NULL
);
78 dev_err(rtd
->dev
, "Headset Jack call-back failed: %d\n", ret
);
84 static const unsigned int st_channels
[] = {
88 static const unsigned int st_rates
[] = {
92 static const struct snd_pcm_hw_constraint_list st_constraints_rates
= {
93 .count
= ARRAY_SIZE(st_rates
),
98 static const struct snd_pcm_hw_constraint_list st_constraints_channels
= {
99 .count
= ARRAY_SIZE(st_channels
),
104 static int st_es8336_codec_startup(struct snd_pcm_substream
*substream
)
106 struct snd_pcm_runtime
*runtime
;
107 struct snd_soc_pcm_runtime
*rtd
;
108 struct snd_soc_card
*card
;
109 struct acp_platform_info
*machine
;
110 struct snd_soc_dai
*codec_dai
;
113 runtime
= substream
->runtime
;
114 rtd
= snd_soc_substream_to_rtd(substream
);
116 machine
= snd_soc_card_get_drvdata(card
);
117 codec_dai
= snd_soc_rtd_to_codec(rtd
, 0);
118 ret
= snd_soc_dai_set_sysclk(codec_dai
, 0, ES8336_PLL_FREQ
, SND_SOC_CLOCK_IN
);
120 dev_err(rtd
->dev
, "can't set codec sysclk: %d\n", ret
);
123 runtime
->hw
.channels_max
= DUAL_CHANNEL
;
124 snd_pcm_hw_constraint_list(runtime
, 0, SNDRV_PCM_HW_PARAM_CHANNELS
,
125 &st_constraints_channels
);
126 snd_pcm_hw_constraint_list(runtime
, 0, SNDRV_PCM_HW_PARAM_RATE
,
127 &st_constraints_rates
);
129 machine
->play_i2s_instance
= I2S_MICSP_INSTANCE
;
130 machine
->cap_i2s_instance
= I2S_MICSP_INSTANCE
;
131 machine
->capture_channel
= CAP_CHANNEL0
;
135 static const struct snd_soc_ops st_es8336_ops
= {
136 .startup
= st_es8336_codec_startup
,
139 SND_SOC_DAILINK_DEF(designware1
,
140 DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2.auto")));
141 SND_SOC_DAILINK_DEF(codec
,
142 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
143 SND_SOC_DAILINK_DEF(platform
,
144 DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.1.auto")));
146 static struct snd_soc_dai_link st_dai_es8336
[] = {
149 .stream_name
= "ES8336 HiFi Play",
150 .dai_fmt
= SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
151 | SND_SOC_DAIFMT_CBP_CFP
,
152 .trigger_stop
= SND_SOC_TRIGGER_ORDER_LDC
,
153 .init
= st_es8336_init
,
154 .ops
= &st_es8336_ops
,
155 SND_SOC_DAILINK_REG(designware1
, codec
, platform
),
159 static const struct snd_soc_dapm_widget st_widgets
[] = {
160 SND_SOC_DAPM_SPK("Speaker", NULL
),
161 SND_SOC_DAPM_HP("Headphone", NULL
),
162 SND_SOC_DAPM_MIC("Headset Mic", NULL
),
163 SND_SOC_DAPM_MIC("Internal Mic", NULL
),
165 SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM
, 0, 0,
166 sof_es8316_speaker_power_event
,
167 SND_SOC_DAPM_PRE_PMD
| SND_SOC_DAPM_POST_PMU
),
170 static const struct snd_soc_dapm_route st_audio_route
[] = {
171 {"Speaker", NULL
, "HPOL"},
172 {"Speaker", NULL
, "HPOR"},
173 {"Headphone", NULL
, "HPOL"},
174 {"Headphone", NULL
, "HPOR"},
175 {"MIC1", NULL
, "Headset Mic"},
176 {"MIC2", NULL
, "Internal Mic"},
177 {"Speaker", NULL
, "Speaker Power"},
180 static const struct snd_kcontrol_new st_mc_controls
[] = {
181 SOC_DAPM_PIN_SWITCH("Speaker"),
182 SOC_DAPM_PIN_SWITCH("Headphone"),
183 SOC_DAPM_PIN_SWITCH("Headset Mic"),
184 SOC_DAPM_PIN_SWITCH("Internal Mic"),
187 static const struct acpi_gpio_params pa_enable_gpio
= { 0, 0, false };
188 static const struct acpi_gpio_mapping acpi_es8336_gpios
[] = {
189 { "pa-enable-gpios", &pa_enable_gpio
, 1 },
193 static int st_es8336_late_probe(struct snd_soc_card
*card
)
195 struct acpi_device
*adev
;
198 adev
= acpi_dev_get_first_match_dev("ESSX8336", NULL
, -1);
202 codec_dev
= acpi_get_first_physical_node(adev
);
205 dev_err(card
->dev
, "can not find codec dev\n");
209 ret
= devm_acpi_dev_add_driver_gpios(codec_dev
, acpi_es8336_gpios
);
211 dev_warn(card
->dev
, "Failed to add driver gpios\n");
213 gpio_pa
= gpiod_get_optional(codec_dev
, "pa-enable", GPIOD_OUT_LOW
);
214 if (IS_ERR(gpio_pa
)) {
215 ret
= dev_err_probe(card
->dev
, PTR_ERR(gpio_pa
),
216 "could not get pa-enable GPIO\n");
217 put_device(codec_dev
);
223 static struct snd_soc_card st_card
= {
225 .owner
= THIS_MODULE
,
226 .dai_link
= st_dai_es8336
,
227 .num_links
= ARRAY_SIZE(st_dai_es8336
),
228 .dapm_widgets
= st_widgets
,
229 .num_dapm_widgets
= ARRAY_SIZE(st_widgets
),
230 .dapm_routes
= st_audio_route
,
231 .num_dapm_routes
= ARRAY_SIZE(st_audio_route
),
232 .controls
= st_mc_controls
,
233 .num_controls
= ARRAY_SIZE(st_mc_controls
),
234 .late_probe
= st_es8336_late_probe
,
237 static int st_es8336_quirk_cb(const struct dmi_system_id
*id
)
239 acp2x_machine_id
= ST_JADEITE
;
243 static const struct dmi_system_id st_es8336_quirk_table
[] = {
245 .callback
= st_es8336_quirk_cb
,
247 DMI_EXACT_MATCH(DMI_BOARD_VENDOR
, "AMD"),
248 DMI_EXACT_MATCH(DMI_PRODUCT_NAME
, "Jadeite"),
252 .callback
= st_es8336_quirk_cb
,
254 DMI_EXACT_MATCH(DMI_BOARD_VENDOR
, "IP3 Technology CO.,Ltd."),
255 DMI_EXACT_MATCH(DMI_PRODUCT_NAME
, "ASN1D"),
259 .callback
= st_es8336_quirk_cb
,
261 DMI_EXACT_MATCH(DMI_BOARD_VENDOR
, "Standard"),
262 DMI_EXACT_MATCH(DMI_PRODUCT_NAME
, "ASN10"),
268 static int st_es8336_probe(struct platform_device
*pdev
)
271 struct snd_soc_card
*card
;
272 struct acp_platform_info
*machine
;
274 machine
= devm_kzalloc(&pdev
->dev
, sizeof(struct acp_platform_info
), GFP_KERNEL
);
278 dmi_check_system(st_es8336_quirk_table
);
279 switch (acp2x_machine_id
) {
282 st_card
.dev
= &pdev
->dev
;
288 platform_set_drvdata(pdev
, card
);
289 snd_soc_card_set_drvdata(card
, machine
);
290 ret
= devm_snd_soc_register_card(&pdev
->dev
, &st_card
);
292 return dev_err_probe(&pdev
->dev
, ret
,
293 "devm_snd_soc_register_card(%s) failed\n",
300 static const struct acpi_device_id st_audio_acpi_match
[] = {
304 MODULE_DEVICE_TABLE(acpi
, st_audio_acpi_match
);
307 static struct platform_driver st_mach_driver
= {
310 .acpi_match_table
= ACPI_PTR(st_audio_acpi_match
),
311 .pm
= &snd_soc_pm_ops
,
313 .probe
= st_es8336_probe
,
316 module_platform_driver(st_mach_driver
);
318 MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
319 MODULE_DESCRIPTION("st-es8316 audio support");
320 MODULE_LICENSE("GPL v2");