Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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 = "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_test_i2s_machines[] = {
240	{
241		.drv_name = "avs_i2s_test",
242		.mach_params = {
243			.i2s_link_mask = AVS_SSP(0),
244		},
245		.tplg_filename = "i2s-test-tplg.bin",
246	},
247	{
248		.drv_name = "avs_i2s_test",
249		.mach_params = {
250			.i2s_link_mask = AVS_SSP(1),
251		},
252		.tplg_filename = "i2s-test-tplg.bin",
253	},
254	{
255		.drv_name = "avs_i2s_test",
256		.mach_params = {
257			.i2s_link_mask = AVS_SSP(2),
258		},
259		.tplg_filename = "i2s-test-tplg.bin",
260	},
261	{
262		.drv_name = "avs_i2s_test",
263		.mach_params = {
264			.i2s_link_mask = AVS_SSP(3),
265		},
266		.tplg_filename = "i2s-test-tplg.bin",
267	},
268	{
269		.drv_name = "avs_i2s_test",
270		.mach_params = {
271			.i2s_link_mask = AVS_SSP(4),
272		},
273		.tplg_filename = "i2s-test-tplg.bin",
274	},
275	{
276		.drv_name = "avs_i2s_test",
277		.mach_params = {
278			.i2s_link_mask = AVS_SSP(5),
279		},
280		.tplg_filename = "i2s-test-tplg.bin",
281	},
282	/* no NULL terminator, as we depend on ARRAY SIZE due to .id == NULL */
283};
284
285struct avs_acpi_boards {
286	int id;
287	struct snd_soc_acpi_mach *machs;
288};
289
290#define AVS_MACH_ENTRY(_id, _mach) \
291	{ .id = PCI_DEVICE_ID_INTEL_##_id, .machs = (_mach), }
292
293/* supported I2S boards per platform */
294static const struct avs_acpi_boards i2s_boards[] = {
295	AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines),
296	AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines),
297	AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines),
298	AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines),
299	{},
300};
301
302static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
303{
304	int id, i;
305
306	id = adev->base.pci->device;
307	for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
308		if (i2s_boards[i].id == id)
309			return &i2s_boards[i];
310	return NULL;
311}
312
313/* platform devices owned by AVS audio are removed with this hook */
314static void board_pdev_unregister(void *data)
315{
316	platform_device_unregister(data);
317}
318
319static int __maybe_unused avs_register_probe_board(struct avs_dev *adev)
320{
321	struct platform_device *board;
322	struct snd_soc_acpi_mach mach = {{0}};
323	int ret;
324
325	ret = avs_probe_platform_register(adev, "probe-platform");
326	if (ret < 0)
327		return ret;
328
329	mach.mach_params.platform = "probe-platform";
330
331	board = platform_device_register_data(NULL, "avs_probe_mb", PLATFORM_DEVID_NONE,
332					      (const void *)&mach, sizeof(mach));
333	if (IS_ERR(board)) {
334		dev_err(adev->dev, "probe board register failed\n");
335		return PTR_ERR(board);
336	}
337
338	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
339	if (ret < 0) {
340		platform_device_unregister(board);
341		return ret;
342	}
343	return 0;
344}
345
346static int avs_register_dmic_board(struct avs_dev *adev)
347{
348	struct platform_device *codec, *board;
349	struct snd_soc_acpi_mach mach = {{0}};
350	int ret;
351
352	if (!adev->nhlt ||
353	    !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_DMIC)) {
354		dev_dbg(adev->dev, "no DMIC endpoints present\n");
355		return 0;
356	}
357
358	codec = platform_device_register_simple("dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
359	if (IS_ERR(codec)) {
360		dev_err(adev->dev, "dmic codec register failed\n");
361		return PTR_ERR(codec);
362	}
363
364	ret = devm_add_action(adev->dev, board_pdev_unregister, codec);
365	if (ret < 0) {
366		platform_device_unregister(codec);
367		return ret;
368	}
369
370	ret = avs_dmic_platform_register(adev, "dmic-platform");
371	if (ret < 0)
372		return ret;
373
374	mach.tplg_filename = "dmic-tplg.bin";
375	mach.mach_params.platform = "dmic-platform";
376
377	board = platform_device_register_data(NULL, "avs_dmic", PLATFORM_DEVID_NONE,
378					(const void *)&mach, sizeof(mach));
379	if (IS_ERR(board)) {
380		dev_err(adev->dev, "dmic board register failed\n");
381		return PTR_ERR(board);
382	}
383
384	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
385	if (ret < 0) {
386		platform_device_unregister(board);
387		return ret;
388	}
389
390	return 0;
391}
392
393static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
394{
395	struct platform_device *board;
396	int num_ssps;
397	char *name;
398	int ret;
399
400	num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
401	if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
402		dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
403			num_ssps, mach->drv_name,
404			(unsigned long)__fls(mach->mach_params.i2s_link_mask));
405		return -ENODEV;
406	}
407
408	name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name,
409			      mach->mach_params.i2s_link_mask);
410	if (!name)
411		return -ENOMEM;
412
413	ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, mach->pdata);
414	if (ret < 0)
415		return ret;
416
417	mach->mach_params.platform = name;
418
419	board = platform_device_register_data(NULL, mach->drv_name, mach->mach_params.i2s_link_mask,
420					      (const void *)mach, sizeof(*mach));
421	if (IS_ERR(board)) {
422		dev_err(adev->dev, "ssp board register failed\n");
423		return PTR_ERR(board);
424	}
425
426	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
427	if (ret < 0) {
428		platform_device_unregister(board);
429		return ret;
430	}
431
432	return 0;
433}
434
435static int avs_register_i2s_boards(struct avs_dev *adev)
436{
437	const struct avs_acpi_boards *boards;
438	struct snd_soc_acpi_mach *mach;
439	int ret;
440
441	if (!adev->nhlt || !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_SSP)) {
442		dev_dbg(adev->dev, "no I2S endpoints present\n");
443		return 0;
444	}
445
446	if (i2s_test) {
447		int i, num_ssps;
448
449		num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
450		/* constrain just in case FW says there can be more SSPs than possible */
451		num_ssps = min_t(int, ARRAY_SIZE(avs_test_i2s_machines), num_ssps);
452
453		mach = avs_test_i2s_machines;
454
455		for (i = 0; i < num_ssps; i++) {
456			ret = avs_register_i2s_board(adev, &mach[i]);
457			if (ret < 0)
458				dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name,
459					 ret);
460		}
461		return 0;
462	}
463
464	boards = avs_get_i2s_boards(adev);
465	if (!boards) {
466		dev_dbg(adev->dev, "no I2S endpoints supported\n");
467		return 0;
468	}
469
470	for (mach = boards->machs; mach->id[0]; mach++) {
471		if (!acpi_dev_present(mach->id, mach->uid, -1))
472			continue;
473
474		if (mach->machine_quirk)
475			if (!mach->machine_quirk(mach))
476				continue;
477
478		ret = avs_register_i2s_board(adev, mach);
479		if (ret < 0)
480			dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret);
481	}
482
483	return 0;
484}
485
486static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
487{
488	struct snd_soc_acpi_mach mach = {{0}};
489	struct platform_device *board;
490	struct hdac_device *hdev = &codec->core;
491	char *pname;
492	int ret, id;
493
494	pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
495	if (!pname)
496		return -ENOMEM;
497
498	ret = avs_hda_platform_register(adev, pname);
499	if (ret < 0)
500		return ret;
501
502	mach.pdata = codec;
503	mach.mach_params.platform = pname;
504	mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
505					    hdev->vendor_id);
506	if (!mach.tplg_filename)
507		return -ENOMEM;
508
509	id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
510	board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
511					      sizeof(mach));
512	if (IS_ERR(board)) {
513		dev_err(adev->dev, "hda board register failed\n");
514		return PTR_ERR(board);
515	}
516
517	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
518	if (ret < 0) {
519		platform_device_unregister(board);
520		return ret;
521	}
522
523	return 0;
524}
525
526static int avs_register_hda_boards(struct avs_dev *adev)
527{
528	struct hdac_bus *bus = &adev->base.core;
529	struct hdac_device *hdev;
530	int ret;
531
532	if (!bus->num_codecs) {
533		dev_dbg(adev->dev, "no HDA endpoints present\n");
534		return 0;
535	}
536
537	list_for_each_entry(hdev, &bus->codec_list, list) {
538		struct hda_codec *codec;
539
540		codec = dev_to_hda_codec(&hdev->dev);
541
542		ret = avs_register_hda_board(adev, codec);
543		if (ret < 0)
544			dev_warn(adev->dev, "register hda-%08x failed: %d\n",
545				 codec->core.vendor_id, ret);
546	}
547
548	return 0;
549}
550
551int avs_register_all_boards(struct avs_dev *adev)
552{
553	int ret;
554
555#ifdef CONFIG_DEBUG_FS
556	ret = avs_register_probe_board(adev);
557	if (ret < 0)
558		dev_warn(adev->dev, "enumerate PROBE endpoints failed: %d\n", ret);
559#endif
560
561	ret = avs_register_dmic_board(adev);
562	if (ret < 0)
563		dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
564			 ret);
565
566	ret = avs_register_i2s_boards(adev);
567	if (ret < 0)
568		dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
569			 ret);
570
571	ret = avs_register_hda_boards(adev);
572	if (ret < 0)
573		dev_warn(adev->dev, "enumerate HDA endpoints failed: %d\n",
574			 ret);
575
576	return 0;
577}
578
579void avs_unregister_all_boards(struct avs_dev *adev)
580{
581	snd_soc_unregister_component(adev->dev);
582}