Linux Audio

Check our new training course

Open-source upstreaming

Need help get the support for your hardware in upstream Linux?
Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-only
  2//
  3// Copyright(c) 2021-2022 Intel Corporation
  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 <acpi/nhlt.h>
 14#include <linux/platform_device.h>
 15#include <sound/hda_codec.h>
 16#include <sound/hda_register.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 = "10EC5514",
140		.drv_name = "avs_rt5514",
141		.mach_params = {
142			.i2s_link_mask = AVS_SSP(0),
143		},
144		.pdata = (unsigned long[]){ 0x2, 0, 0, 0, 0, 0 }, /* SSP0 TDMs */
145		.tplg_filename = "rt5514-tplg.bin",
146	},
147	{
148		.id = "10EC5663",
149		.drv_name = "avs_rt5663",
150		.mach_params = {
151			.i2s_link_mask = AVS_SSP(1),
152		},
153		.tplg_filename = "rt5663-tplg.bin",
154	},
155	{
156		.id = "MX98373",
157		.drv_name = "avs_max98373",
158		.mach_params = {
159			.i2s_link_mask = AVS_SSP(0),
160		},
161		.tplg_filename = "max98373-tplg.bin",
162	},
163	{
164		.id = "MX98357A",
165		.drv_name = "avs_max98357a",
166		.mach_params = {
167			.i2s_link_mask = AVS_SSP(0),
168		},
169		.tplg_filename = "max98357a-tplg.bin",
170	},
171	{
172		.id = "DLGS7219",
173		.drv_name = "avs_da7219",
174		.mach_params = {
175			.i2s_link_mask = AVS_SSP(1),
176		},
177		.tplg_filename = "da7219-tplg.bin",
178	},
179	{
180		.id = "ESSX8336",
181		.drv_name = "avs_es8336",
182		.mach_params = {
183			.i2s_link_mask = AVS_SSP(0),
184		},
185		.tplg_filename = "es8336-tplg.bin",
186	},
187	{},
188};
189
190static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
191	{
192		.id = "INT343A",
193		.drv_name = "avs_rt298",
194		.mach_params = {
195			.i2s_link_mask = AVS_SSP(5),
196		},
197		.tplg_filename = "rt298-tplg.bin",
198	},
199	{
200		.id = "INT34C3",
201		.drv_name = "avs_tdf8532",
202		.mach_params = {
203			.i2s_link_mask = AVS_SSP_RANGE(0, 5),
204		},
205		.pdata = (unsigned long[]){ 0x1, 0x1, 0x14, 0x1, 0x1, 0x1 }, /* SSP2 TDMs */
206		.tplg_filename = "tdf8532-tplg.bin",
207	},
208	{
209		.id = "MX98357A",
210		.drv_name = "avs_max98357a",
211		.mach_params = {
212			.i2s_link_mask = AVS_SSP(5),
213		},
214		.tplg_filename = "max98357a-tplg.bin",
215	},
216	{
217		.id = "DLGS7219",
218		.drv_name = "avs_da7219",
219		.mach_params = {
220			.i2s_link_mask = AVS_SSP(1),
221		},
222		.tplg_filename = "da7219-tplg.bin",
223	},
224	{},
225};
226
227static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
228	{
229		.id = "INT343A",
230		.drv_name = "avs_rt298",
231		.mach_params = {
232			.i2s_link_mask = AVS_SSP(2),
233		},
234		.tplg_filename = "rt298-tplg.bin",
235	},
236	{},
237};
238
239static struct snd_soc_acpi_mach avs_cnl_i2s_machines[] = {
240	{
241		.id = "INT34C2",
242		.drv_name = "avs_rt274",
243		.mach_params = {
244			.i2s_link_mask = AVS_SSP(0),
245		},
246		.tplg_filename = "rt274-tplg.bin",
247	},
248	{
249		.id = "10EC5682",
250		.drv_name = "avs_rt5682",
251		.mach_params = {
252			.i2s_link_mask = AVS_SSP(1),
253		},
254		.tplg_filename = "rt5682-tplg.bin",
255	},
256	{},
257};
258
259static struct snd_soc_acpi_mach avs_icl_i2s_machines[] = {
260	{
261		.id = "INT343A",
262		.drv_name = "avs_rt298",
263		.mach_params = {
264			.i2s_link_mask = AVS_SSP(0),
265		},
266		.tplg_filename = "rt298-tplg.bin",
267	},
268	{
269		.id = "INT34C2",
270		.drv_name = "avs_rt274",
271		.mach_params = {
272			.i2s_link_mask = AVS_SSP(0),
273		},
274		.tplg_filename = "rt274-tplg.bin",
275	},
276	{},
277};
278
279static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = {
280	{
281		.id = "INT34C2",
282		.drv_name = "avs_rt274",
283		.mach_params = {
284			.i2s_link_mask = AVS_SSP(0),
285		},
286		.tplg_filename = "rt274-tplg.bin",
287	},
288	{
289		.id = "10EC0298",
290		.drv_name = "avs_rt298",
291		.mach_params = {
292			.i2s_link_mask = AVS_SSP(0),
293		},
294		.tplg_filename = "rt298-tplg.bin",
295	},
296	{
297		.id = "10EC1308",
298		.drv_name = "avs_rt1308",
299		.mach_params = {
300			.i2s_link_mask = AVS_SSP(1),
301		},
302		.tplg_filename = "rt1308-tplg.bin",
303	},
304	{
305		.id = "ESSX8336",
306		.drv_name = "avs_es8336",
307		.mach_params = {
308			.i2s_link_mask = AVS_SSP(0),
309		},
310		.tplg_filename = "es8336-tplg.bin",
311	},
312	{},
313};
314
315static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
316	{
317		.drv_name = "avs_i2s_test",
318		.mach_params = {
319			.i2s_link_mask = AVS_SSP(0),
320		},
321		.tplg_filename = "i2s-test-tplg.bin",
322	},
323	{
324		.drv_name = "avs_i2s_test",
325		.mach_params = {
326			.i2s_link_mask = AVS_SSP(1),
327		},
328		.tplg_filename = "i2s-test-tplg.bin",
329	},
330	{
331		.drv_name = "avs_i2s_test",
332		.mach_params = {
333			.i2s_link_mask = AVS_SSP(2),
334		},
335		.tplg_filename = "i2s-test-tplg.bin",
336	},
337	{
338		.drv_name = "avs_i2s_test",
339		.mach_params = {
340			.i2s_link_mask = AVS_SSP(3),
341		},
342		.tplg_filename = "i2s-test-tplg.bin",
343	},
344	{
345		.drv_name = "avs_i2s_test",
346		.mach_params = {
347			.i2s_link_mask = AVS_SSP(4),
348		},
349		.tplg_filename = "i2s-test-tplg.bin",
350	},
351	{
352		.drv_name = "avs_i2s_test",
353		.mach_params = {
354			.i2s_link_mask = AVS_SSP(5),
355		},
356		.tplg_filename = "i2s-test-tplg.bin",
357	},
358	/* no NULL terminator, as we depend on ARRAY SIZE due to .id == NULL */
359};
360
361struct avs_acpi_boards {
362	int id;
363	struct snd_soc_acpi_mach *machs;
364};
365
366#define AVS_MACH_ENTRY(_id, _mach) \
367	{ .id = PCI_DEVICE_ID_INTEL_##_id, .machs = (_mach), }
368
369/* supported I2S boards per platform */
370static const struct avs_acpi_boards i2s_boards[] = {
371	AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines),
372	AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines),
373	AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines),
374	AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines),
375	AVS_MACH_ENTRY(HDA_CNL_LP,	avs_cnl_i2s_machines),
376	AVS_MACH_ENTRY(HDA_CNL_H,	avs_cnl_i2s_machines),
377	AVS_MACH_ENTRY(HDA_CML_LP,	avs_cnl_i2s_machines),
378	AVS_MACH_ENTRY(HDA_ICL_LP,	avs_icl_i2s_machines),
379	AVS_MACH_ENTRY(HDA_TGL_LP,	avs_tgl_i2s_machines),
380	AVS_MACH_ENTRY(HDA_EHL_0,	avs_tgl_i2s_machines),
381	AVS_MACH_ENTRY(HDA_ADL_P,	avs_tgl_i2s_machines),
382	AVS_MACH_ENTRY(HDA_RPL_P_0,	avs_tgl_i2s_machines),
383	AVS_MACH_ENTRY(HDA_RPL_M,	avs_tgl_i2s_machines),
384	{},
385};
386
387static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
388{
389	int id, i;
390
391	id = adev->base.pci->device;
392	for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
393		if (i2s_boards[i].id == id)
394			return &i2s_boards[i];
395	return NULL;
396}
397
398/* platform devices owned by AVS audio are removed with this hook */
399static void board_pdev_unregister(void *data)
400{
401	platform_device_unregister(data);
402}
403
404static int __maybe_unused avs_register_probe_board(struct avs_dev *adev)
405{
406	struct platform_device *board;
407	struct snd_soc_acpi_mach mach = {{0}};
408	int ret;
409
410	ret = avs_probe_platform_register(adev, "probe-platform");
411	if (ret < 0)
412		return ret;
413
414	mach.mach_params.platform = "probe-platform";
415
416	board = platform_device_register_data(NULL, "avs_probe_mb", PLATFORM_DEVID_NONE,
417					      (const void *)&mach, sizeof(mach));
418	if (IS_ERR(board)) {
419		dev_err(adev->dev, "probe board register failed\n");
420		return PTR_ERR(board);
421	}
422
423	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
424	if (ret < 0) {
425		platform_device_unregister(board);
426		return ret;
427	}
428	return 0;
429}
430
431static int avs_register_dmic_board(struct avs_dev *adev)
432{
433	struct platform_device *codec, *board;
434	struct snd_soc_acpi_mach mach = {{0}};
435	int ret;
436
437	if (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, -1, -1)) {
 
438		dev_dbg(adev->dev, "no DMIC endpoints present\n");
439		return 0;
440	}
441
442	codec = platform_device_register_simple("dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
443	if (IS_ERR(codec)) {
444		dev_err(adev->dev, "dmic codec register failed\n");
445		return PTR_ERR(codec);
446	}
447
448	ret = devm_add_action(adev->dev, board_pdev_unregister, codec);
449	if (ret < 0) {
450		platform_device_unregister(codec);
451		return ret;
452	}
453
454	ret = avs_dmic_platform_register(adev, "dmic-platform");
455	if (ret < 0)
456		return ret;
457
458	mach.tplg_filename = "dmic-tplg.bin";
459	mach.mach_params.platform = "dmic-platform";
460
461	board = platform_device_register_data(NULL, "avs_dmic", PLATFORM_DEVID_NONE,
462					(const void *)&mach, sizeof(mach));
463	if (IS_ERR(board)) {
464		dev_err(adev->dev, "dmic board register failed\n");
465		return PTR_ERR(board);
466	}
467
468	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
469	if (ret < 0) {
470		platform_device_unregister(board);
471		return ret;
472	}
473
474	return 0;
475}
476
477static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
478{
479	struct platform_device *board;
480	int num_ssps;
481	char *name;
482	int ret;
483
484	num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
485	if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
486		dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
487			num_ssps, mach->drv_name,
488			(unsigned long)__fls(mach->mach_params.i2s_link_mask));
489		return -ENODEV;
490	}
491
492	name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name,
493			      mach->mach_params.i2s_link_mask);
494	if (!name)
495		return -ENOMEM;
496
497	ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, mach->pdata);
498	if (ret < 0)
499		return ret;
500
501	mach->mach_params.platform = name;
502
503	board = platform_device_register_data(NULL, mach->drv_name, mach->mach_params.i2s_link_mask,
504					      (const void *)mach, sizeof(*mach));
505	if (IS_ERR(board)) {
506		dev_err(adev->dev, "ssp board register failed\n");
507		return PTR_ERR(board);
508	}
509
510	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
511	if (ret < 0) {
512		platform_device_unregister(board);
513		return ret;
514	}
515
516	return 0;
517}
518
519static int avs_register_i2s_boards(struct avs_dev *adev)
520{
521	const struct avs_acpi_boards *boards;
522	struct snd_soc_acpi_mach *mach;
523	int ret;
524
525	if (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_SSP, -1, -1, -1)) {
526		dev_dbg(adev->dev, "no I2S endpoints present\n");
527		return 0;
528	}
529
530	if (i2s_test) {
531		int i, num_ssps;
532
533		num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
534		/* constrain just in case FW says there can be more SSPs than possible */
535		num_ssps = min_t(int, ARRAY_SIZE(avs_test_i2s_machines), num_ssps);
536
537		mach = avs_test_i2s_machines;
538
539		for (i = 0; i < num_ssps; i++) {
540			ret = avs_register_i2s_board(adev, &mach[i]);
541			if (ret < 0)
542				dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name,
543					 ret);
544		}
545		return 0;
546	}
547
548	boards = avs_get_i2s_boards(adev);
549	if (!boards) {
550		dev_dbg(adev->dev, "no I2S endpoints supported\n");
551		return 0;
552	}
553
554	for (mach = boards->machs; mach->id[0]; mach++) {
555		if (!acpi_dev_present(mach->id, mach->uid, -1))
556			continue;
557
558		if (mach->machine_quirk)
559			if (!mach->machine_quirk(mach))
560				continue;
561
562		ret = avs_register_i2s_board(adev, mach);
563		if (ret < 0)
564			dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret);
565	}
566
567	return 0;
568}
569
570static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
571{
572	struct snd_soc_acpi_mach mach = {{0}};
573	struct platform_device *board;
574	struct hdac_device *hdev = &codec->core;
575	char *pname;
576	int ret, id;
577
578	pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
579	if (!pname)
580		return -ENOMEM;
581
582	ret = avs_hda_platform_register(adev, pname);
583	if (ret < 0)
584		return ret;
585
586	mach.pdata = codec;
587	mach.mach_params.platform = pname;
588	mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
589					    hdev->vendor_id);
590	if (!mach.tplg_filename)
591		return -ENOMEM;
592
593	id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
594	board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
595					      sizeof(mach));
596	if (IS_ERR(board)) {
597		dev_err(adev->dev, "hda board register failed\n");
598		return PTR_ERR(board);
599	}
600
601	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
602	if (ret < 0) {
603		platform_device_unregister(board);
604		return ret;
605	}
606
607	return 0;
608}
609
610static int avs_register_hda_boards(struct avs_dev *adev)
611{
612	struct hdac_bus *bus = &adev->base.core;
613	struct hdac_device *hdev;
614	int ret;
615
616	if (!bus->num_codecs) {
617		dev_dbg(adev->dev, "no HDA endpoints present\n");
618		return 0;
619	}
620
621	list_for_each_entry(hdev, &bus->codec_list, list) {
622		struct hda_codec *codec;
623
624		codec = dev_to_hda_codec(&hdev->dev);
625
626		ret = avs_register_hda_board(adev, codec);
627		if (ret < 0)
628			dev_warn(adev->dev, "register hda-%08x failed: %d\n",
629				 codec->core.vendor_id, ret);
630	}
631
632	return 0;
633}
634
635int avs_register_all_boards(struct avs_dev *adev)
636{
637	int ret;
638
639#ifdef CONFIG_DEBUG_FS
640	ret = avs_register_probe_board(adev);
641	if (ret < 0)
642		dev_warn(adev->dev, "enumerate PROBE endpoints failed: %d\n", ret);
643#endif
644
645	ret = avs_register_dmic_board(adev);
646	if (ret < 0)
647		dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
648			 ret);
649
650	ret = avs_register_i2s_boards(adev);
651	if (ret < 0)
652		dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
653			 ret);
654
655	ret = avs_register_hda_boards(adev);
656	if (ret < 0)
657		dev_warn(adev->dev, "enumerate HDA endpoints failed: %d\n",
658			 ret);
659
660	return 0;
661}
662
663void avs_unregister_all_boards(struct avs_dev *adev)
664{
665	snd_soc_unregister_component(adev->dev);
666}
v6.2
  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 = "MX98373",
140		.drv_name = "avs_max98373",
141		.mach_params = {
142			.i2s_link_mask = AVS_SSP(0),
143		},
144		.tplg_filename = "max98373-tplg.bin",
145	},
146	{
147		.id = "MX98357A",
148		.drv_name = "avs_max98357a",
149		.mach_params = {
150			.i2s_link_mask = AVS_SSP(0),
151		},
152		.tplg_filename = "max98357a-tplg.bin",
153	},
154	{
155		.id = "DLGS7219",
156		.drv_name = "avs_da7219",
157		.mach_params = {
158			.i2s_link_mask = AVS_SSP(1),
159		},
160		.tplg_filename = "da7219-tplg.bin",
161	},
 
 
 
 
 
 
 
 
162	{},
163};
164
165static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
166	{
167		.id = "INT343A",
168		.drv_name = "avs_rt298",
169		.mach_params = {
170			.i2s_link_mask = AVS_SSP(5),
171		},
172		.tplg_filename = "rt298-tplg.bin",
173	},
174	{
175		.id = "INT34C3",
176		.drv_name = "avs_tdf8532",
177		.mach_params = {
178			.i2s_link_mask = AVS_SSP_RANGE(0, 5),
179		},
180		.pdata = (unsigned long[]){ 0, 0, 0x14, 0, 0, 0 }, /* SSP2 TDMs */
181		.tplg_filename = "tdf8532-tplg.bin",
182	},
183	{
184		.id = "MX98357A",
185		.drv_name = "avs_max98357a",
186		.mach_params = {
187			.i2s_link_mask = AVS_SSP(5),
188		},
189		.tplg_filename = "max98357a-tplg.bin",
190	},
191	{
192		.id = "DLGS7219",
193		.drv_name = "avs_da7219",
194		.mach_params = {
195			.i2s_link_mask = AVS_SSP(1),
196		},
197		.tplg_filename = "da7219-tplg.bin",
198	},
199	{},
200};
201
202static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
203	{
204		.id = "INT343A",
205		.drv_name = "avs_rt298",
206		.mach_params = {
207			.i2s_link_mask = AVS_SSP(2),
208		},
209		.tplg_filename = "rt298-tplg.bin",
210	},
211	{},
212};
213
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
215	{
216		.drv_name = "avs_i2s_test",
217		.mach_params = {
218			.i2s_link_mask = AVS_SSP(0),
219		},
220		.tplg_filename = "i2s-test-tplg.bin",
221	},
222	{
223		.drv_name = "avs_i2s_test",
224		.mach_params = {
225			.i2s_link_mask = AVS_SSP(1),
226		},
227		.tplg_filename = "i2s-test-tplg.bin",
228	},
229	{
230		.drv_name = "avs_i2s_test",
231		.mach_params = {
232			.i2s_link_mask = AVS_SSP(2),
233		},
234		.tplg_filename = "i2s-test-tplg.bin",
235	},
236	{
237		.drv_name = "avs_i2s_test",
238		.mach_params = {
239			.i2s_link_mask = AVS_SSP(3),
240		},
241		.tplg_filename = "i2s-test-tplg.bin",
242	},
243	{
244		.drv_name = "avs_i2s_test",
245		.mach_params = {
246			.i2s_link_mask = AVS_SSP(4),
247		},
248		.tplg_filename = "i2s-test-tplg.bin",
249	},
250	{
251		.drv_name = "avs_i2s_test",
252		.mach_params = {
253			.i2s_link_mask = AVS_SSP(5),
254		},
255		.tplg_filename = "i2s-test-tplg.bin",
256	},
257	/* no NULL terminator, as we depend on ARRAY SIZE due to .id == NULL */
258};
259
260struct avs_acpi_boards {
261	int id;
262	struct snd_soc_acpi_mach *machs;
263};
264
265#define AVS_MACH_ENTRY(_id, _mach) \
266	{ .id = (_id), .machs = (_mach), }
267
268/* supported I2S boards per platform */
269static const struct avs_acpi_boards i2s_boards[] = {
270	AVS_MACH_ENTRY(0x9d70, avs_skl_i2s_machines), /* SKL */
271	AVS_MACH_ENTRY(0x9d71, avs_kbl_i2s_machines), /* KBL */
272	AVS_MACH_ENTRY(0x5a98, avs_apl_i2s_machines), /* APL */
273	AVS_MACH_ENTRY(0x3198, avs_gml_i2s_machines), /* GML */
 
 
 
 
 
 
 
 
 
274	{},
275};
276
277static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
278{
279	int id, i;
280
281	id = adev->base.pci->device;
282	for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
283		if (i2s_boards[i].id == id)
284			return &i2s_boards[i];
285	return NULL;
286}
287
288/* platform devices owned by AVS audio are removed with this hook */
289static void board_pdev_unregister(void *data)
290{
291	platform_device_unregister(data);
292}
293
294static int __maybe_unused avs_register_probe_board(struct avs_dev *adev)
295{
296	struct platform_device *board;
297	struct snd_soc_acpi_mach mach = {{0}};
298	int ret;
299
300	ret = avs_probe_platform_register(adev, "probe-platform");
301	if (ret < 0)
302		return ret;
303
304	mach.mach_params.platform = "probe-platform";
305
306	board = platform_device_register_data(NULL, "avs_probe_mb", PLATFORM_DEVID_NONE,
307					      (const void *)&mach, sizeof(mach));
308	if (IS_ERR(board)) {
309		dev_err(adev->dev, "probe board register failed\n");
310		return PTR_ERR(board);
311	}
312
313	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
314	if (ret < 0) {
315		platform_device_unregister(board);
316		return ret;
317	}
318	return 0;
319}
320
321static int avs_register_dmic_board(struct avs_dev *adev)
322{
323	struct platform_device *codec, *board;
324	struct snd_soc_acpi_mach mach = {{0}};
325	int ret;
326
327	if (!adev->nhlt ||
328	    !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_DMIC)) {
329		dev_dbg(adev->dev, "no DMIC endpoints present\n");
330		return 0;
331	}
332
333	codec = platform_device_register_simple("dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
334	if (IS_ERR(codec)) {
335		dev_err(adev->dev, "dmic codec register failed\n");
336		return PTR_ERR(codec);
337	}
338
339	ret = devm_add_action(adev->dev, board_pdev_unregister, codec);
340	if (ret < 0) {
341		platform_device_unregister(codec);
342		return ret;
343	}
344
345	ret = avs_dmic_platform_register(adev, "dmic-platform");
346	if (ret < 0)
347		return ret;
348
349	mach.tplg_filename = "dmic-tplg.bin";
350	mach.mach_params.platform = "dmic-platform";
351
352	board = platform_device_register_data(NULL, "avs_dmic", PLATFORM_DEVID_NONE,
353					(const void *)&mach, sizeof(mach));
354	if (IS_ERR(board)) {
355		dev_err(adev->dev, "dmic board register failed\n");
356		return PTR_ERR(board);
357	}
358
359	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
360	if (ret < 0) {
361		platform_device_unregister(board);
362		return ret;
363	}
364
365	return 0;
366}
367
368static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
369{
370	struct platform_device *board;
371	int num_ssps;
372	char *name;
373	int ret;
374
375	num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
376	if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
377		dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
378			num_ssps, mach->drv_name,
379			(unsigned long)__fls(mach->mach_params.i2s_link_mask));
380		return -ENODEV;
381	}
382
383	name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name,
384			      mach->mach_params.i2s_link_mask);
385	if (!name)
386		return -ENOMEM;
387
388	ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, mach->pdata);
389	if (ret < 0)
390		return ret;
391
392	mach->mach_params.platform = name;
393
394	board = platform_device_register_data(NULL, mach->drv_name, mach->mach_params.i2s_link_mask,
395					      (const void *)mach, sizeof(*mach));
396	if (IS_ERR(board)) {
397		dev_err(adev->dev, "ssp board register failed\n");
398		return PTR_ERR(board);
399	}
400
401	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
402	if (ret < 0) {
403		platform_device_unregister(board);
404		return ret;
405	}
406
407	return 0;
408}
409
410static int avs_register_i2s_boards(struct avs_dev *adev)
411{
412	const struct avs_acpi_boards *boards;
413	struct snd_soc_acpi_mach *mach;
414	int ret;
415
416	if (!adev->nhlt || !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_SSP)) {
417		dev_dbg(adev->dev, "no I2S endpoints present\n");
418		return 0;
419	}
420
421	if (i2s_test) {
422		int i, num_ssps;
423
424		num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
425		/* constrain just in case FW says there can be more SSPs than possible */
426		num_ssps = min_t(int, ARRAY_SIZE(avs_test_i2s_machines), num_ssps);
427
428		mach = avs_test_i2s_machines;
429
430		for (i = 0; i < num_ssps; i++) {
431			ret = avs_register_i2s_board(adev, &mach[i]);
432			if (ret < 0)
433				dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name,
434					 ret);
435		}
436		return 0;
437	}
438
439	boards = avs_get_i2s_boards(adev);
440	if (!boards) {
441		dev_dbg(adev->dev, "no I2S endpoints supported\n");
442		return 0;
443	}
444
445	for (mach = boards->machs; mach->id[0]; mach++) {
446		if (!acpi_dev_present(mach->id, NULL, -1))
447			continue;
448
449		if (mach->machine_quirk)
450			if (!mach->machine_quirk(mach))
451				continue;
452
453		ret = avs_register_i2s_board(adev, mach);
454		if (ret < 0)
455			dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret);
456	}
457
458	return 0;
459}
460
461static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
462{
463	struct snd_soc_acpi_mach mach = {{0}};
464	struct platform_device *board;
465	struct hdac_device *hdev = &codec->core;
466	char *pname;
467	int ret, id;
468
469	pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
470	if (!pname)
471		return -ENOMEM;
472
473	ret = avs_hda_platform_register(adev, pname);
474	if (ret < 0)
475		return ret;
476
477	mach.pdata = codec;
478	mach.mach_params.platform = pname;
479	mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
480					    hdev->vendor_id);
481	if (!mach.tplg_filename)
482		return -ENOMEM;
483
484	id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
485	board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
486					      sizeof(mach));
487	if (IS_ERR(board)) {
488		dev_err(adev->dev, "hda board register failed\n");
489		return PTR_ERR(board);
490	}
491
492	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
493	if (ret < 0) {
494		platform_device_unregister(board);
495		return ret;
496	}
497
498	return 0;
499}
500
501static int avs_register_hda_boards(struct avs_dev *adev)
502{
503	struct hdac_bus *bus = &adev->base.core;
504	struct hdac_device *hdev;
505	int ret;
506
507	if (!bus->num_codecs) {
508		dev_dbg(adev->dev, "no HDA endpoints present\n");
509		return 0;
510	}
511
512	list_for_each_entry(hdev, &bus->codec_list, list) {
513		struct hda_codec *codec;
514
515		codec = dev_to_hda_codec(&hdev->dev);
516
517		ret = avs_register_hda_board(adev, codec);
518		if (ret < 0)
519			dev_warn(adev->dev, "register hda-%08x failed: %d\n",
520				 codec->core.vendor_id, ret);
521	}
522
523	return 0;
524}
525
526int avs_register_all_boards(struct avs_dev *adev)
527{
528	int ret;
529
530#ifdef CONFIG_DEBUG_FS
531	ret = avs_register_probe_board(adev);
532	if (ret < 0)
533		dev_warn(adev->dev, "enumerate PROBE endpoints failed: %d\n", ret);
534#endif
535
536	ret = avs_register_dmic_board(adev);
537	if (ret < 0)
538		dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
539			 ret);
540
541	ret = avs_register_i2s_boards(adev);
542	if (ret < 0)
543		dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
544			 ret);
545
546	ret = avs_register_hda_boards(adev);
547	if (ret < 0)
548		dev_warn(adev->dev, "enumerate HDA endpoints failed: %d\n",
549			 ret);
550
551	return 0;
552}
553
554void avs_unregister_all_boards(struct avs_dev *adev)
555{
556	snd_soc_unregister_component(adev->dev);
557}