1// SPDX-License-Identifier: GPL-2.0-only
2//
3// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
4//
5// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
6//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
7//
8
9#include <linux/acpi.h>
10#include <linux/module.h>
11#include <linux/dmi.h>
12#include <linux/pci.h>
13#include <linux/platform_device.h>
14#include <sound/hda_codec.h>
15#include <sound/hda_register.h>
16#include <sound/intel-nhlt.h>
17#include <sound/soc-acpi.h>
18#include <sound/soc-component.h>
19#include "avs.h"
20
21static bool i2s_test;
22module_param(i2s_test, bool, 0444);
23MODULE_PARM_DESC(i2s_test, "Probe I2S test-board and skip all other I2S boards");
24
25static const struct dmi_system_id kbl_dmi_table[] = {
26	{
27		.matches = {
28			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
29			DMI_MATCH(DMI_BOARD_NAME, "Skylake Y LPDDR3 RVP3"),
30		},
31	},
32	{
33		.matches = {
34			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
35			DMI_MATCH(DMI_BOARD_NAME, "AmberLake Y"),
36		},
37	},
38	{}
39};
40
41static const struct dmi_system_id kblr_dmi_table[] = {
42	{
43		.matches = {
44			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
45			DMI_MATCH(DMI_BOARD_NAME, "Kabylake R DDR4 RVP"),
46		},
47	},
48	{}
49};
50
51static struct snd_soc_acpi_mach *dmi_match_quirk(void *arg)
52{
53	struct snd_soc_acpi_mach *mach = arg;
54	const struct dmi_system_id *dmi_id;
55	struct dmi_system_id *dmi_table;
56
57	if (mach->quirk_data == NULL)
58		return mach;
59
60	dmi_table = (struct dmi_system_id *)mach->quirk_data;
61
62	dmi_id = dmi_first_match(dmi_table);
63	if (!dmi_id)
64		return NULL;
65
66	return mach;
67}
68
69#define AVS_SSP(x)		(BIT(x))
70#define AVS_SSP_RANGE(a, b)	(GENMASK(b, a))
71
72/* supported I2S board codec configurations */
73static struct snd_soc_acpi_mach avs_skl_i2s_machines[] = {
74	{
75		.id = "INT343A",
76		.drv_name = "avs_rt286",
77		.mach_params = {
78			.i2s_link_mask = AVS_SSP(0),
79		},
80		.tplg_filename = "rt286-tplg.bin",
81	},
82	{
83		.id = "10508825",
84		.drv_name = "avs_nau8825",
85		.mach_params = {
86			.i2s_link_mask = AVS_SSP(1),
87		},
88		.tplg_filename = "nau8825-tplg.bin",
89	},
90	{
91		.id = "INT343B",
92		.drv_name = "avs_ssm4567",
93		.mach_params = {
94			.i2s_link_mask = AVS_SSP(0),
95		},
96		.tplg_filename = "ssm4567-tplg.bin",
97	},
98	{
99		.id = "MX98357A",
100		.drv_name = "avs_max98357a",
101		.mach_params = {
102			.i2s_link_mask = AVS_SSP(0),
103		},
104		.tplg_filename = "max98357a-tplg.bin",
105	},
106	{},
107};
108
109static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
110	{
111		.id = "INT343A",
112		.drv_name = "avs_rt286",
113		.mach_params = {
114			.i2s_link_mask = AVS_SSP(0),
115		},
116		.quirk_data = &kbl_dmi_table,
117		.machine_quirk = dmi_match_quirk,
118		.tplg_filename = "rt286-tplg.bin",
119	},
120	{
121		.id = "INT343A",
122		.drv_name = "avs_rt298",
123		.mach_params = {
124			.i2s_link_mask = AVS_SSP(0),
125		},
126		.quirk_data = &kblr_dmi_table,
127		.machine_quirk = dmi_match_quirk,
128		.tplg_filename = "rt298-tplg.bin",
129	},
130	{
131		.id = "MX98927",
132		.drv_name = "avs_max98927",
133		.mach_params = {
134			.i2s_link_mask = AVS_SSP(0),
135		},
136		.tplg_filename = "max98927-tplg.bin",
137	},
138	{
139		.id = "10EC5663",
140		.drv_name = "avs_rt5663",
141		.mach_params = {
142			.i2s_link_mask = AVS_SSP(1),
143		},
144		.tplg_filename = "rt5663-tplg.bin",
145	},
146	{
147		.id = "MX98373",
148		.drv_name = "avs_max98373",
149		.mach_params = {
150			.i2s_link_mask = AVS_SSP(0),
151		},
152		.tplg_filename = "max98373-tplg.bin",
153	},
154	{
155		.id = "MX98357A",
156		.drv_name = "avs_max98357a",
157		.mach_params = {
158			.i2s_link_mask = AVS_SSP(0),
159		},
160		.tplg_filename = "max98357a-tplg.bin",
161	},
162	{
163		.id = "DLGS7219",
164		.drv_name = "avs_da7219",
165		.mach_params = {
166			.i2s_link_mask = AVS_SSP(1),
167		},
168		.tplg_filename = "da7219-tplg.bin",
169	},
170	{
171		.id = "ESSX8336",
172		.drv_name = "avs_es8336",
173		.mach_params = {
174			.i2s_link_mask = AVS_SSP(0),
175		},
176		.tplg_filename = "es8336-tplg.bin",
177	},
178	{},
179};
180
181static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
182	{
183		.id = "INT343A",
184		.drv_name = "avs_rt298",
185		.mach_params = {
186			.i2s_link_mask = AVS_SSP(5),
187		},
188		.tplg_filename = "rt298-tplg.bin",
189	},
190	{
191		.id = "INT34C3",
192		.drv_name = "avs_tdf8532",
193		.mach_params = {
194			.i2s_link_mask = AVS_SSP_RANGE(0, 5),
195		},
196		.pdata = (unsigned long[]){ 0, 0, 0x14, 0, 0, 0 }, /* SSP2 TDMs */
197		.tplg_filename = "tdf8532-tplg.bin",
198	},
199	{
200		.id = "MX98357A",
201		.drv_name = "avs_max98357a",
202		.mach_params = {
203			.i2s_link_mask = AVS_SSP(5),
204		},
205		.tplg_filename = "max98357a-tplg.bin",
206	},
207	{
208		.id = "DLGS7219",
209		.drv_name = "avs_da7219",
210		.mach_params = {
211			.i2s_link_mask = AVS_SSP(1),
212		},
213		.tplg_filename = "da7219-tplg.bin",
214	},
215	{},
216};
217
218static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
219	{
220		.id = "INT343A",
221		.drv_name = "avs_rt298",
222		.mach_params = {
223			.i2s_link_mask = AVS_SSP(2),
224		},
225		.tplg_filename = "rt298-tplg.bin",
226	},
227	{},
228};
229
230static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
231	{
232		.drv_name = "avs_i2s_test",
233		.mach_params = {
234			.i2s_link_mask = AVS_SSP(0),
235		},
236		.tplg_filename = "i2s-test-tplg.bin",
237	},
238	{
239		.drv_name = "avs_i2s_test",
240		.mach_params = {
241			.i2s_link_mask = AVS_SSP(1),
242		},
243		.tplg_filename = "i2s-test-tplg.bin",
244	},
245	{
246		.drv_name = "avs_i2s_test",
247		.mach_params = {
248			.i2s_link_mask = AVS_SSP(2),
249		},
250		.tplg_filename = "i2s-test-tplg.bin",
251	},
252	{
253		.drv_name = "avs_i2s_test",
254		.mach_params = {
255			.i2s_link_mask = AVS_SSP(3),
256		},
257		.tplg_filename = "i2s-test-tplg.bin",
258	},
259	{
260		.drv_name = "avs_i2s_test",
261		.mach_params = {
262			.i2s_link_mask = AVS_SSP(4),
263		},
264		.tplg_filename = "i2s-test-tplg.bin",
265	},
266	{
267		.drv_name = "avs_i2s_test",
268		.mach_params = {
269			.i2s_link_mask = AVS_SSP(5),
270		},
271		.tplg_filename = "i2s-test-tplg.bin",
272	},
273	/* no NULL terminator, as we depend on ARRAY SIZE due to .id == NULL */
274};
275
276struct avs_acpi_boards {
277	int id;
278	struct snd_soc_acpi_mach *machs;
279};
280
281#define AVS_MACH_ENTRY(_id, _mach) \
282	{ .id = PCI_DEVICE_ID_INTEL_##_id, .machs = (_mach), }
283
284/* supported I2S boards per platform */
285static const struct avs_acpi_boards i2s_boards[] = {
286	AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines),
287	AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines),
288	AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines),
289	AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines),
290	{},
291};
292
293static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
294{
295	int id, i;
296
297	id = adev->base.pci->device;
298	for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
299		if (i2s_boards[i].id == id)
300			return &i2s_boards[i];
301	return NULL;
302}
303
304/* platform devices owned by AVS audio are removed with this hook */
305static void board_pdev_unregister(void *data)
306{
307	platform_device_unregister(data);
308}
309
310static int __maybe_unused avs_register_probe_board(struct avs_dev *adev)
311{
312	struct platform_device *board;
313	struct snd_soc_acpi_mach mach = {{0}};
314	int ret;
315
316	ret = avs_probe_platform_register(adev, "probe-platform");
317	if (ret < 0)
318		return ret;
319
320	mach.mach_params.platform = "probe-platform";
321
322	board = platform_device_register_data(NULL, "avs_probe_mb", PLATFORM_DEVID_NONE,
323					      (const void *)&mach, sizeof(mach));
324	if (IS_ERR(board)) {
325		dev_err(adev->dev, "probe board register failed\n");
326		return PTR_ERR(board);
327	}
328
329	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
330	if (ret < 0) {
331		platform_device_unregister(board);
332		return ret;
333	}
334	return 0;
335}
336
337static int avs_register_dmic_board(struct avs_dev *adev)
338{
339	struct platform_device *codec, *board;
340	struct snd_soc_acpi_mach mach = {{0}};
341	int ret;
342
343	if (!adev->nhlt ||
344	    !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_DMIC)) {
345		dev_dbg(adev->dev, "no DMIC endpoints present\n");
346		return 0;
347	}
348
349	codec = platform_device_register_simple("dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
350	if (IS_ERR(codec)) {
351		dev_err(adev->dev, "dmic codec register failed\n");
352		return PTR_ERR(codec);
353	}
354
355	ret = devm_add_action(adev->dev, board_pdev_unregister, codec);
356	if (ret < 0) {
357		platform_device_unregister(codec);
358		return ret;
359	}
360
361	ret = avs_dmic_platform_register(adev, "dmic-platform");
362	if (ret < 0)
363		return ret;
364
365	mach.tplg_filename = "dmic-tplg.bin";
366	mach.mach_params.platform = "dmic-platform";
367
368	board = platform_device_register_data(NULL, "avs_dmic", PLATFORM_DEVID_NONE,
369					(const void *)&mach, sizeof(mach));
370	if (IS_ERR(board)) {
371		dev_err(adev->dev, "dmic board register failed\n");
372		return PTR_ERR(board);
373	}
374
375	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
376	if (ret < 0) {
377		platform_device_unregister(board);
378		return ret;
379	}
380
381	return 0;
382}
383
384static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
385{
386	struct platform_device *board;
387	int num_ssps;
388	char *name;
389	int ret;
390
391	num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
392	if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
393		dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
394			num_ssps, mach->drv_name,
395			(unsigned long)__fls(mach->mach_params.i2s_link_mask));
396		return -ENODEV;
397	}
398
399	name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name,
400			      mach->mach_params.i2s_link_mask);
401	if (!name)
402		return -ENOMEM;
403
404	ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, mach->pdata);
405	if (ret < 0)
406		return ret;
407
408	mach->mach_params.platform = name;
409
410	board = platform_device_register_data(NULL, mach->drv_name, mach->mach_params.i2s_link_mask,
411					      (const void *)mach, sizeof(*mach));
412	if (IS_ERR(board)) {
413		dev_err(adev->dev, "ssp board register failed\n");
414		return PTR_ERR(board);
415	}
416
417	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
418	if (ret < 0) {
419		platform_device_unregister(board);
420		return ret;
421	}
422
423	return 0;
424}
425
426static int avs_register_i2s_boards(struct avs_dev *adev)
427{
428	const struct avs_acpi_boards *boards;
429	struct snd_soc_acpi_mach *mach;
430	int ret;
431
432	if (!adev->nhlt || !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_SSP)) {
433		dev_dbg(adev->dev, "no I2S endpoints present\n");
434		return 0;
435	}
436
437	if (i2s_test) {
438		int i, num_ssps;
439
440		num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
441		/* constrain just in case FW says there can be more SSPs than possible */
442		num_ssps = min_t(int, ARRAY_SIZE(avs_test_i2s_machines), num_ssps);
443
444		mach = avs_test_i2s_machines;
445
446		for (i = 0; i < num_ssps; i++) {
447			ret = avs_register_i2s_board(adev, &mach[i]);
448			if (ret < 0)
449				dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name,
450					 ret);
451		}
452		return 0;
453	}
454
455	boards = avs_get_i2s_boards(adev);
456	if (!boards) {
457		dev_dbg(adev->dev, "no I2S endpoints supported\n");
458		return 0;
459	}
460
461	for (mach = boards->machs; mach->id[0]; mach++) {
462		if (!acpi_dev_present(mach->id, mach->uid, -1))
463			continue;
464
465		if (mach->machine_quirk)
466			if (!mach->machine_quirk(mach))
467				continue;
468
469		ret = avs_register_i2s_board(adev, mach);
470		if (ret < 0)
471			dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret);
472	}
473
474	return 0;
475}
476
477static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
478{
479	struct snd_soc_acpi_mach mach = {{0}};
480	struct platform_device *board;
481	struct hdac_device *hdev = &codec->core;
482	char *pname;
483	int ret, id;
484
485	pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
486	if (!pname)
487		return -ENOMEM;
488
489	ret = avs_hda_platform_register(adev, pname);
490	if (ret < 0)
491		return ret;
492
493	mach.pdata = codec;
494	mach.mach_params.platform = pname;
495	mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
496					    hdev->vendor_id);
497	if (!mach.tplg_filename)
498		return -ENOMEM;
499
500	id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
501	board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
502					      sizeof(mach));
503	if (IS_ERR(board)) {
504		dev_err(adev->dev, "hda board register failed\n");
505		return PTR_ERR(board);
506	}
507
508	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
509	if (ret < 0) {
510		platform_device_unregister(board);
511		return ret;
512	}
513
514	return 0;
515}
516
517static int avs_register_hda_boards(struct avs_dev *adev)
518{
519	struct hdac_bus *bus = &adev->base.core;
520	struct hdac_device *hdev;
521	int ret;
522
523	if (!bus->num_codecs) {
524		dev_dbg(adev->dev, "no HDA endpoints present\n");
525		return 0;
526	}
527
528	list_for_each_entry(hdev, &bus->codec_list, list) {
529		struct hda_codec *codec;
530
531		codec = dev_to_hda_codec(&hdev->dev);
532
533		ret = avs_register_hda_board(adev, codec);
534		if (ret < 0)
535			dev_warn(adev->dev, "register hda-%08x failed: %d\n",
536				 codec->core.vendor_id, ret);
537	}
538
539	return 0;
540}
541
542int avs_register_all_boards(struct avs_dev *adev)
543{
544	int ret;
545
546#ifdef CONFIG_DEBUG_FS
547	ret = avs_register_probe_board(adev);
548	if (ret < 0)
549		dev_warn(adev->dev, "enumerate PROBE endpoints failed: %d\n", ret);
550#endif
551
552	ret = avs_register_dmic_board(adev);
553	if (ret < 0)
554		dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
555			 ret);
556
557	ret = avs_register_i2s_boards(adev);
558	if (ret < 0)
559		dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
560			 ret);
561
562	ret = avs_register_hda_boards(adev);
563	if (ret < 0)
564		dev_warn(adev->dev, "enumerate HDA endpoints failed: %d\n",
565			 ret);
566
567	return 0;
568}
569
570void avs_unregister_all_boards(struct avs_dev *adev)
571{
572	snd_soc_unregister_component(adev->dev);
573}
574