xref: /linux/sound/soc/amd/acp-es8336.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Machine driver for AMD Stoney platform using ES8336 Codec
4  *
5  * Copyright 2022 Advanced Micro Devices, Inc.
6  */
7 
8 #include <sound/core.h>
9 #include <sound/soc.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/device.h>
15 #include <linux/dmi.h>
16 #include <linux/gpio/consumer.h>
17 #include <linux/gpio/machine.h>
18 #include <linux/i2c.h>
19 #include <linux/input.h>
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/acpi.h>
23 
24 #include "acp.h"
25 
26 #define DUAL_CHANNEL	2
27 #define DRV_NAME "acp2x_mach"
28 #define ST_JADEITE	1
29 #define ES8336_PLL_FREQ (48000 * 256)
30 
31 static unsigned long acp2x_machine_id;
32 static struct snd_soc_jack st_jack;
33 static struct device *codec_dev;
34 static struct gpio_desc *gpio_pa;
35 
36 static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
37 					  struct snd_kcontrol *kcontrol, int event)
38 {
39 	if (SND_SOC_DAPM_EVENT_ON(event))
40 		gpiod_set_value_cansleep(gpio_pa, true);
41 	else
42 		gpiod_set_value_cansleep(gpio_pa, false);
43 
44 	return 0;
45 }
46 
47 static struct snd_soc_jack_pin st_es8316_jack_pins[] = {
48 	{
49 		.pin	= "Headphone",
50 		.mask	= SND_JACK_HEADPHONE,
51 	},
52 	{
53 		.pin	= "Headset Mic",
54 		.mask	= SND_JACK_MICROPHONE,
55 	},
56 };
57 
58 static int st_es8336_init(struct snd_soc_pcm_runtime *rtd)
59 {
60 	int ret;
61 	struct snd_soc_card *card;
62 	struct snd_soc_component *codec;
63 
64 	codec = snd_soc_rtd_to_codec(rtd, 0)->component;
65 	card = rtd->card;
66 
67 	ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0,
68 					 &st_jack, st_es8316_jack_pins,
69 					 ARRAY_SIZE(st_es8316_jack_pins));
70 	if (ret) {
71 		dev_err(card->dev, "HP jack creation failed %d\n", ret);
72 		return ret;
73 	}
74 	snd_jack_set_key(st_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
75 	ret = snd_soc_component_set_jack(codec, &st_jack, NULL);
76 	if (ret) {
77 		dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
78 		return ret;
79 	}
80 	return 0;
81 }
82 
83 static const unsigned int st_channels[] = {
84 	DUAL_CHANNEL,
85 };
86 
87 static const unsigned int st_rates[] = {
88 	48000,
89 };
90 
91 static const struct snd_pcm_hw_constraint_list st_constraints_rates = {
92 	.count = ARRAY_SIZE(st_rates),
93 	.list  = st_rates,
94 	.mask = 0,
95 };
96 
97 static const struct snd_pcm_hw_constraint_list st_constraints_channels = {
98 	.count = ARRAY_SIZE(st_channels),
99 	.list = st_channels,
100 	.mask = 0,
101 };
102 
103 static int st_es8336_codec_startup(struct snd_pcm_substream *substream)
104 {
105 	struct snd_pcm_runtime *runtime;
106 	struct snd_soc_pcm_runtime *rtd;
107 	struct snd_soc_card *card;
108 	struct acp_platform_info *machine;
109 	struct snd_soc_dai *codec_dai;
110 	int ret;
111 
112 	runtime = substream->runtime;
113 	rtd = snd_soc_substream_to_rtd(substream);
114 	card = rtd->card;
115 	machine = snd_soc_card_get_drvdata(card);
116 	codec_dai = snd_soc_rtd_to_codec(rtd, 0);
117 	ret = snd_soc_dai_set_sysclk(codec_dai, 0, ES8336_PLL_FREQ, SND_SOC_CLOCK_IN);
118 	if (ret < 0) {
119 		dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
120 		return ret;
121 	}
122 	runtime->hw.channels_max = DUAL_CHANNEL;
123 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
124 				   &st_constraints_channels);
125 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
126 				   &st_constraints_rates);
127 
128 	machine->play_i2s_instance = I2S_MICSP_INSTANCE;
129 	machine->cap_i2s_instance = I2S_MICSP_INSTANCE;
130 	machine->capture_channel = CAP_CHANNEL0;
131 	return 0;
132 }
133 
134 static const struct snd_soc_ops st_es8336_ops = {
135 	.startup = st_es8336_codec_startup,
136 };
137 
138 SND_SOC_DAILINK_DEF(designware1,
139 		    DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1")));
140 SND_SOC_DAILINK_DEF(codec,
141 		    DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
142 SND_SOC_DAILINK_DEF(platform,
143 		    DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0")));
144 
145 static struct snd_soc_dai_link st_dai_es8336[] = {
146 	{
147 		.name = "amdes8336",
148 		.stream_name = "ES8336 HiFi Play",
149 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
150 				| SND_SOC_DAIFMT_CBP_CFP,
151 		.trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
152 		.init = st_es8336_init,
153 		.ops = &st_es8336_ops,
154 		SND_SOC_DAILINK_REG(designware1, codec, platform),
155 	},
156 };
157 
158 static const struct snd_soc_dapm_widget st_widgets[] = {
159 	SND_SOC_DAPM_SPK("Speaker", NULL),
160 	SND_SOC_DAPM_HP("Headphone", NULL),
161 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
162 	SND_SOC_DAPM_MIC("Internal Mic", NULL),
163 
164 	SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
165 			    sof_es8316_speaker_power_event,
166 			    SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
167 };
168 
169 static const struct snd_soc_dapm_route st_audio_route[] = {
170 	{"Speaker", NULL, "HPOL"},
171 	{"Speaker", NULL, "HPOR"},
172 	{"Headphone", NULL, "HPOL"},
173 	{"Headphone", NULL, "HPOR"},
174 	{"MIC1", NULL, "Headset Mic"},
175 	{"MIC2", NULL, "Internal Mic"},
176 	{"Speaker", NULL, "Speaker Power"},
177 };
178 
179 static const struct snd_kcontrol_new st_mc_controls[] = {
180 	SOC_DAPM_PIN_SWITCH("Speaker"),
181 	SOC_DAPM_PIN_SWITCH("Headphone"),
182 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
183 	SOC_DAPM_PIN_SWITCH("Internal Mic"),
184 };
185 
186 static const struct acpi_gpio_params pa_enable_gpio = { 0, 0, false };
187 static const struct acpi_gpio_mapping acpi_es8336_gpios[] = {
188 	{ "pa-enable-gpios", &pa_enable_gpio, 1 },
189 	{ }
190 };
191 
192 static int st_es8336_late_probe(struct snd_soc_card *card)
193 {
194 	struct acpi_device *adev;
195 	int ret;
196 
197 	adev = acpi_dev_get_first_match_dev("ESSX8336", NULL, -1);
198 	if (!adev)
199 		return -ENODEV;
200 
201 	codec_dev = acpi_get_first_physical_node(adev);
202 	acpi_dev_put(adev);
203 	if (!codec_dev) {
204 		dev_err(card->dev, "can not find codec dev\n");
205 		return -ENODEV;
206 	}
207 
208 	ret = devm_acpi_dev_add_driver_gpios(codec_dev, acpi_es8336_gpios);
209 	if (ret)
210 		dev_warn(card->dev, "Failed to add driver gpios\n");
211 
212 	gpio_pa = gpiod_get_optional(codec_dev, "pa-enable", GPIOD_OUT_LOW);
213 	if (IS_ERR(gpio_pa)) {
214 		ret = dev_err_probe(card->dev, PTR_ERR(gpio_pa),
215 				    "could not get pa-enable GPIO\n");
216 		put_device(codec_dev);
217 		return ret;
218 	}
219 	return 0;
220 }
221 
222 static struct snd_soc_card st_card = {
223 	.name = "acpes8336",
224 	.owner = THIS_MODULE,
225 	.dai_link = st_dai_es8336,
226 	.num_links = ARRAY_SIZE(st_dai_es8336),
227 	.dapm_widgets = st_widgets,
228 	.num_dapm_widgets = ARRAY_SIZE(st_widgets),
229 	.dapm_routes = st_audio_route,
230 	.num_dapm_routes = ARRAY_SIZE(st_audio_route),
231 	.controls = st_mc_controls,
232 	.num_controls = ARRAY_SIZE(st_mc_controls),
233 	.late_probe = st_es8336_late_probe,
234 };
235 
236 static int st_es8336_quirk_cb(const struct dmi_system_id *id)
237 {
238 	acp2x_machine_id = ST_JADEITE;
239 	return 1;
240 }
241 
242 static const struct dmi_system_id st_es8336_quirk_table[] = {
243 	{
244 		.callback = st_es8336_quirk_cb,
245 		.matches = {
246 			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMD"),
247 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jadeite"),
248 		},
249 	},
250 	{
251 		.callback = st_es8336_quirk_cb,
252 		.matches = {
253 			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "IP3 Technology CO.,Ltd."),
254 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ASN1D"),
255 		},
256 	},
257 	{
258 		.callback = st_es8336_quirk_cb,
259 		.matches = {
260 			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Standard"),
261 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ASN10"),
262 		},
263 	},
264 	{}
265 };
266 
267 static int st_es8336_probe(struct platform_device *pdev)
268 {
269 	int ret;
270 	struct snd_soc_card *card;
271 	struct acp_platform_info *machine;
272 
273 	machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info), GFP_KERNEL);
274 	if (!machine)
275 		return -ENOMEM;
276 
277 	dmi_check_system(st_es8336_quirk_table);
278 	switch (acp2x_machine_id) {
279 	case ST_JADEITE:
280 		card = &st_card;
281 		st_card.dev = &pdev->dev;
282 		break;
283 	default:
284 		return -ENODEV;
285 	}
286 
287 	platform_set_drvdata(pdev, card);
288 	snd_soc_card_set_drvdata(card, machine);
289 	ret = devm_snd_soc_register_card(&pdev->dev, &st_card);
290 	if (ret) {
291 		return dev_err_probe(&pdev->dev, ret,
292 				     "devm_snd_soc_register_card(%s) failed\n",
293 				     card->name);
294 	}
295 	return 0;
296 }
297 
298 #ifdef CONFIG_ACPI
299 static const struct acpi_device_id st_audio_acpi_match[] = {
300 	{"AMDI8336", 0},
301 	{},
302 };
303 MODULE_DEVICE_TABLE(acpi, st_audio_acpi_match);
304 #endif
305 
306 static struct platform_driver st_mach_driver = {
307 	.driver = {
308 		.name = "st-es8316",
309 		.acpi_match_table = ACPI_PTR(st_audio_acpi_match),
310 		.pm = &snd_soc_pm_ops,
311 	},
312 	.probe = st_es8336_probe,
313 };
314 
315 module_platform_driver(st_mach_driver);
316 
317 MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
318 MODULE_DESCRIPTION("st-es8316 audio support");
319 MODULE_LICENSE("GPL v2");
320