File Index
Files and directories
Sign in
FILE

board_selection.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/intel/avs/board_selection.c C
  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#include "debug.h"
 21#include "pcm.h"
 22#include "utils.h"
 23
 24static char *i2s_test;
 25module_param(i2s_test, charp, 0444);
 26MODULE_PARM_DESC(i2s_test, "Use I2S test-board instead of ACPI, i2s_test=ssp0tdm,ssp1tdm,... 0 to ignore port");
 27
 28bool obsolete_card_names = IS_ENABLED(CONFIG_SND_SOC_INTEL_AVS_CARDNAME_OBSOLETE);
 29module_param_named(obsolete_card_names, obsolete_card_names, bool, 0444);
 30MODULE_PARM_DESC(obsolete_card_names, "Use obsolete card names 0=no, 1=yes");
 31
 32static const struct dmi_system_id kbl_dmi_table[] = {
 33	{
 34		.matches = {
 35			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
 36			DMI_MATCH(DMI_BOARD_NAME, "Skylake Y LPDDR3 RVP3"),
 37		},
 38	},
 39	{
 40		.matches = {
 41			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
 42			DMI_MATCH(DMI_BOARD_NAME, "AmberLake Y"),
 43		},
 44	},
 45	{}
 46};
 47
 48static const struct dmi_system_id kblr_dmi_table[] = {
 49	{
 50		.matches = {
 51			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
 52			DMI_MATCH(DMI_BOARD_NAME, "Kabylake R DDR4 RVP"),
 53		},
 54	},
 55	{}
 56};
 57
 58static struct snd_soc_acpi_mach *dmi_match_quirk(void *arg)
 59{
 60	struct snd_soc_acpi_mach *mach = arg;
 61	struct dmi_system_id *dmi_table;
 62
 63	dmi_table = (struct dmi_system_id *)mach->quirk_data;
 64
 65	if (!dmi_table || dmi_first_match(dmi_table))
 66		return mach;
 67	return NULL;
 68}
 69
 70#define AVS_SSP(x)		(BIT(x))
 71#define AVS_SSP_RANGE(a, b)	(GENMASK(b, a))
 72
 73/* supported I2S board codec configurations */
 74static struct snd_soc_acpi_mach avs_skl_i2s_machines[] = {
 75	{
 76		.id = "INT343A",
 77		.drv_name = "avs_rt286",
 78		.mach_params = {
 79			.i2s_link_mask = AVS_SSP(0),
 80		},
 81		.tplg_filename = "rt286-tplg.bin",
 82	},
 83	{
 84		.id = "10508825",
 85		.drv_name = "avs_nau8825",
 86		.mach_params = {
 87			.i2s_link_mask = AVS_SSP(1),
 88		},
 89		.tplg_filename = "nau8825-tplg.bin",
 90	},
 91	{
 92		.id = "INT343B",
 93		.drv_name = "avs_ssm4567",
 94		.mach_params = {
 95			.i2s_link_mask = AVS_SSP(0),
 96		},
 97		.tplg_filename = "ssm4567-tplg.bin",
 98	},
 99	{
100		.id = "MX98357A",
101		.drv_name = "avs_max98357a",
102		.mach_params = {
103			.i2s_link_mask = AVS_SSP(0),
104		},
105		.tplg_filename = "max98357a-tplg.bin",
106	},
107	{},
108};
109
110static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
111	{
112		.id = "INT343A",
113		.drv_name = "avs_rt286",
114		.mach_params = {
115			.i2s_link_mask = AVS_SSP(0),
116		},
117		.quirk_data = &kbl_dmi_table,
118		.machine_quirk = dmi_match_quirk,
119		.tplg_filename = "rt286-tplg.bin",
120	},
121	{
122		.id = "INT343A",
123		.drv_name = "avs_rt298",
124		.mach_params = {
125			.i2s_link_mask = AVS_SSP(0),
126		},
127		.quirk_data = &kblr_dmi_table,
128		.machine_quirk = dmi_match_quirk,
129		.tplg_filename = "rt298-tplg.bin",
130	},
131	{
132		.id = "MX98927",
133		.drv_name = "avs_max98927",
134		.mach_params = {
135			.i2s_link_mask = AVS_SSP(0),
136		},
137		.tplg_filename = "max98927-tplg.bin",
138	},
139	{
140		.id = "10EC5514",
141		.drv_name = "avs_rt5514",
142		.mach_params = {
143			.i2s_link_mask = AVS_SSP(0),
144		},
145		.pdata = (struct avs_mach_pdata[]){ { .tdms = (unsigned long[]){ 0x2 } } },
146		.tplg_filename = "rt5514-tplg.bin",
147	},
148	{
149		.id = "10EC5663",
150		.drv_name = "avs_rt5663",
151		.mach_params = {
152			.i2s_link_mask = AVS_SSP(1),
153		},
154		.tplg_filename = "rt5663-tplg.bin",
155	},
156	{
157		.id = "MX98373",
158		.drv_name = "avs_max98373",
159		.mach_params = {
160			.i2s_link_mask = AVS_SSP(0),
161		},
162		.tplg_filename = "max98373-tplg.bin",
163	},
164	{
165		.id = "MX98357A",
166		.drv_name = "avs_max98357a",
167		.mach_params = {
168			.i2s_link_mask = AVS_SSP(0),
169		},
170		.tplg_filename = "max98357a-tplg.bin",
171	},
172	{
173		.id = "DLGS7219",
174		.drv_name = "avs_da7219",
175		.mach_params = {
176			.i2s_link_mask = AVS_SSP(1),
177		},
178		.tplg_filename = "da7219-tplg.bin",
179	},
180	{
181		.id = "ESSX8336",
182		.drv_name = "avs_es8336",
183		.mach_params = {
184			.i2s_link_mask = AVS_SSP(0),
185		},
186		.tplg_filename = "es8336-tplg.bin",
187	},
188	{},
189};
190
191static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
192	{
193		.id = "INT343A",
194		.drv_name = "avs_rt298",
195		.mach_params = {
196			.i2s_link_mask = AVS_SSP(5),
197		},
198		.tplg_filename = "rt298-tplg.bin",
199	},
200	{
201		.id = "INT34C3",
202		.drv_name = "avs_tdf8532",
203		.mach_params = {
204			.i2s_link_mask = AVS_SSP_RANGE(0, 5),
205		},
206		.pdata = (struct avs_mach_pdata[]){ {
207			.tdms = (unsigned long[]){ 0x1, 0x1, 0x14, 0x1, 0x1, 0x1 }
208		} },
209		.tplg_filename = "tdf8532-tplg.bin",
210	},
211	{
212		.id = "MX98357A",
213		.drv_name = "avs_max98357a",
214		.mach_params = {
215			.i2s_link_mask = AVS_SSP(5),
216		},
217		.tplg_filename = "max98357a-tplg.bin",
218	},
219	{
220		.id = "DLGS7219",
221		.drv_name = "avs_da7219",
222		.mach_params = {
223			.i2s_link_mask = AVS_SSP(1),
224		},
225		.tplg_filename = "da7219-tplg.bin",
226	},
227	{},
228};
229
230static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
231	{
232		.id = "INT343A",
233		.drv_name = "avs_rt298",
234		.mach_params = {
235			.i2s_link_mask = AVS_SSP(2),
236		},
237		.tplg_filename = "rt298-tplg.bin",
238	},
239	{},
240};
241
242static struct snd_soc_acpi_mach avs_cnl_i2s_machines[] = {
243	{
244		.id = "INT34C2",
245		.drv_name = "avs_rt274",
246		.mach_params = {
247			.i2s_link_mask = AVS_SSP(0),
248		},
249		.tplg_filename = "rt274-tplg.bin",
250	},
251	{
252		.id = "10EC5682",
253		.drv_name = "avs_rt5682",
254		.mach_params = {
255			.i2s_link_mask = AVS_SSP(1),
256		},
257		.tplg_filename = "rt5682-tplg.bin",
258	},
259	{},
260};
261
262static struct snd_soc_acpi_mach avs_icl_i2s_machines[] = {
263	{
264		.id = "INT343A",
265		.drv_name = "avs_rt298",
266		.mach_params = {
267			.i2s_link_mask = AVS_SSP(0),
268		},
269		.tplg_filename = "rt298-tplg.bin",
270	},
271	{
272		.id = "INT34C2",
273		.drv_name = "avs_rt274",
274		.mach_params = {
275			.i2s_link_mask = AVS_SSP(0),
276		},
277		.tplg_filename = "rt274-tplg.bin",
278	},
279	{},
280};
281
282static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = {
283	{
284		.id = "INT34C2",
285		.drv_name = "avs_rt274",
286		.mach_params = {
287			.i2s_link_mask = AVS_SSP(0),
288		},
289		.tplg_filename = "rt274-tplg.bin",
290	},
291	{
292		.id = "10EC0298",
293		.drv_name = "avs_rt298",
294		.mach_params = {
295			.i2s_link_mask = AVS_SSP(0),
296		},
297		.tplg_filename = "rt298-tplg.bin",
298	},
299	{
300		.id = "10EC1308",
301		.drv_name = "avs_rt1308",
302		.mach_params = {
303			.i2s_link_mask = AVS_SSP(1),
304		},
305		.tplg_filename = "rt1308-tplg.bin",
306	},
307	{
308		.id = "10EC5640",
309		.uid = "1",
310		.drv_name = "avs_rt5640",
311		.mach_params = {
312			.i2s_link_mask = AVS_SSP(0),
313		},
314		.tplg_filename = "rt5640-tplg.bin",
315	},
316	{
317		.id = "10EC5640",
318		.uid = "3",
319		.drv_name = "avs_rt5640",
320		.mach_params = {
321			.i2s_link_mask = AVS_SSP(1),
322		},
323		.tplg_filename = "rt5640-tplg.bin",
324	},
325	{
326		.id = "10EC5640",
327		.uid = "2",
328		.drv_name = "avs_rt5640",
329		.mach_params = {
330			.i2s_link_mask = AVS_SSP(2),
331		},
332		.tplg_filename = "rt5640-tplg.bin",
333	},
334	{
335		.id = "ESSX8336",
336		.drv_name = "avs_es8336",
337		.mach_params = {
338			.i2s_link_mask = AVS_SSP(0),
339		},
340		.tplg_filename = "es8336-tplg.bin",
341	},
342	{},
343};
344
345static struct snd_soc_acpi_mach avs_mbl_i2s_machines[] = {
346	{
347		.id = "PCM3168A",
348		.drv_name = "avs_pcm3168a",
349		.mach_params = {
350			.i2s_link_mask = AVS_SSP(0) | AVS_SSP(2),
351		},
352		.tplg_filename = "pcm3168a-tplg.bin",
353	},
354	{}
355};
356
357struct avs_acpi_boards {
358	int id;
359	struct snd_soc_acpi_mach *machs;
360};
361
362#define AVS_MACH_ENTRY(_id, _mach) \
363	{ .id = PCI_DEVICE_ID_INTEL_##_id, .machs = (_mach), }
364
365/* supported I2S boards per platform */
366static const struct avs_acpi_boards i2s_boards[] = {
367	AVS_MACH_ENTRY(HDA_SKL_LP,	avs_skl_i2s_machines),
368	AVS_MACH_ENTRY(HDA_KBL_LP,	avs_kbl_i2s_machines),
369	AVS_MACH_ENTRY(HDA_APL,		avs_apl_i2s_machines),
370	AVS_MACH_ENTRY(HDA_GML,		avs_gml_i2s_machines),
371	AVS_MACH_ENTRY(HDA_CNL_LP,	avs_cnl_i2s_machines),
372	AVS_MACH_ENTRY(HDA_CNL_H,	avs_cnl_i2s_machines),
373	AVS_MACH_ENTRY(HDA_CML_LP,	avs_cnl_i2s_machines),
374	AVS_MACH_ENTRY(HDA_ICL_LP,	avs_icl_i2s_machines),
375	AVS_MACH_ENTRY(HDA_TGL_LP,	avs_tgl_i2s_machines),
376	AVS_MACH_ENTRY(HDA_EHL_0,	avs_tgl_i2s_machines),
377	AVS_MACH_ENTRY(HDA_ADL_N,	avs_mbl_i2s_machines),
378	AVS_MACH_ENTRY(HDA_ADL_P,	avs_tgl_i2s_machines),
379	AVS_MACH_ENTRY(HDA_RPL_P_0,	avs_tgl_i2s_machines),
380	AVS_MACH_ENTRY(HDA_RPL_M,	avs_mbl_i2s_machines),
381	AVS_MACH_ENTRY(HDA_FCL,		avs_tgl_i2s_machines),
382	{ },
383};
384
385static struct snd_soc_acpi_mach *avs_get_i2s_machines(struct avs_dev *adev)
386{
387	int id, i;
388
389	id = adev->base.pci->device;
390	for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
391		if (i2s_boards[i].id == id)
392			return i2s_boards[i].machs;
393	return NULL;
394}
395
396/* Platform devices spawned by AVS driver are removed with this hook. */
397static void avs_unregister_board(void *pdev)
398{
399	platform_device_unregister(pdev);
400}
401
402static struct platform_device *avs_register_board(struct avs_dev *adev, const char *name,
403						  const void *data, size_t size)
404{
405	struct platform_device *pdev;
406	int ret;
407
408	pdev = platform_device_register_data(NULL, name, PLATFORM_DEVID_AUTO, data, size);
409	if (IS_ERR(pdev))
410		return pdev;
411
412	ret = devm_add_action_or_reset(adev->dev, avs_unregister_board, pdev);
413	if (ret)
414		return ERR_PTR(ret);
415
416	return pdev;
417}
418
419static struct platform_device *avs_register_board_pdata(struct avs_dev *adev, const char *name,
420							struct snd_soc_acpi_mach *mach,
421							struct hda_codec *codec,
422							unsigned long *tdms, char *codec_name)
423{
424	struct avs_mach_pdata *pdata;
425
426	pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
427	if (!pdata)
428		return ERR_PTR(-ENOMEM);
429
430	pdata->codec = codec;
431	pdata->tdms = tdms;
432	pdata->codec_name = codec_name;
433	pdata->obsolete_card_names = obsolete_card_names;
434	mach->pdata = pdata;
435
436	return avs_register_board(adev, name, mach, sizeof(*mach));
437}
438
439static int __maybe_unused avs_register_probe_board(struct avs_dev *adev)
440{
441	struct platform_device *pdev;
442
443	pdev = avs_register_board(adev, "avs_probe_mb", NULL, 0);
444	if (IS_ERR(pdev))
445		return PTR_ERR(pdev);
446
447	return avs_register_probe_component(adev, dev_name(&pdev->dev));
448}
449
450static int avs_register_dmic_board(struct avs_dev *adev)
451{
452	static struct snd_soc_acpi_mach mach = {
453		.tplg_filename = "dmic-tplg.bin",
454	};
455	struct platform_device *pdev;
456	char *codec_name;
457
458	if (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, -1, -1)) {
459		dev_dbg(adev->dev, "no DMIC endpoints present\n");
460		return 0;
461	}
462
463	/* DMIC present in Intel PCH is enumerated statically. */
464	pdev = avs_register_board(adev, "dmic-codec", NULL, 0);
465	if (IS_ERR(pdev))
466		return PTR_ERR(pdev);
467
468	codec_name = devm_kstrdup(adev->dev, dev_name(&pdev->dev), GFP_KERNEL);
469	if (!codec_name)
470		return -ENOMEM;
471
472	pdev = avs_register_board_pdata(adev, "avs_dmic", &mach, NULL, NULL, codec_name);
473	if (IS_ERR(pdev))
474		return PTR_ERR(pdev);
475
476	return avs_register_dmic_component(adev, dev_name(&pdev->dev));
477}
478
479static int avs_register_i2s_test_board(struct avs_dev *adev, int ssp_port, int tdm_slot)
480{
481	struct snd_soc_acpi_mach mach = {{0}};
482	struct platform_device *pdev;
483	unsigned long *tdms;
484
485	tdms = devm_kcalloc(adev->dev, ssp_port + 1, sizeof(*tdms), GFP_KERNEL);
486	mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL,
487					    AVS_STRING_FMT("i2s", "-test-tplg.bin",
488							   ssp_port, tdm_slot));
489	if (!tdms || !mach.tplg_filename)
490		return -ENOMEM;
491
492	tdms[ssp_port] = BIT(tdm_slot);
493	mach.drv_name = "avs_i2s_test";
494	mach.mach_params.i2s_link_mask = AVS_SSP(ssp_port);
495
496	pdev = avs_register_board_pdata(adev, mach.drv_name, &mach, NULL, tdms, NULL);
497	if (IS_ERR(pdev))
498		return PTR_ERR(pdev);
499
500	return avs_register_i2s_component(adev, dev_name(&pdev->dev), AVS_SSP(ssp_port), tdms);
501}
502
503static int avs_register_i2s_test_boards(struct avs_dev *adev)
504{
505	int max_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
506	int ssp_port, tdm_slot, ret;
507	unsigned long tdm_slots;
508	u32 *array, num_elems;
509
510	if (!i2s_test)
511		return 0;
512
513	ret = parse_int_array(i2s_test, strlen(i2s_test), (int **)&array);
514	if (ret) {
515		dev_err(adev->dev, "failed to parse i2s_test parameter\n");
516		return ret;
517	}
518
519	num_elems = *array;
520	if (num_elems > max_ssps) {
521		dev_err(adev->dev, "board supports only %d SSP, %d specified\n",
522			max_ssps, num_elems);
523		ret = -EINVAL;
524		goto exit;
525	}
526
527	for (ssp_port = 0; ssp_port < num_elems; ssp_port++) {
528		tdm_slots = array[1 + ssp_port];
529		for_each_set_bit(tdm_slot, &tdm_slots, 16) {
530			ret = avs_register_i2s_test_board(adev, ssp_port, tdm_slot);
531			if (ret)
532				goto exit;
533		}
534	}
535
536exit:
537	kfree(array);
538	return ret;
539}
540
541static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
542{
543	u32 i2s_mask = mach->mach_params.i2s_link_mask;
544	struct platform_device *pdev;
545	unsigned long *tdms = NULL;
546
547	if (mach->pdata)
548		tdms = ((struct avs_mach_pdata *)mach->pdata)->tdms;
549
550	pdev = avs_register_board_pdata(adev, mach->drv_name, mach, NULL, tdms, NULL);
551	if (IS_ERR(pdev))
552		return PTR_ERR(pdev);
553
554	return avs_register_i2s_component(adev, dev_name(&pdev->dev), i2s_mask, tdms);
555}
556
557static int avs_register_i2s_boards(struct avs_dev *adev)
558{
559	int num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
560	struct snd_soc_acpi_mach *machs;
561	struct snd_soc_acpi_mach *mach;
562	int ret;
563
564	if (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_SSP, -1, -1, -1)) {
565		dev_dbg(adev->dev, "no I2S endpoints present\n");
566		return 0;
567	}
568
569	machs = avs_get_i2s_machines(adev);
570	if (!machs) {
571		dev_dbg(adev->dev, "no I2S endpoints supported\n");
572		return 0;
573	}
574
575	for (mach = machs; mach->id[0]; mach++) {
576		if (!acpi_dev_present(mach->id, mach->uid, -1))
577			continue;
578
579		if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
580			dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
581				num_ssps, mach->drv_name,
582				(unsigned long)__fls(mach->mach_params.i2s_link_mask));
583			continue;
584		}
585		if (mach->machine_quirk)
586			if (!mach->machine_quirk(mach))
587				continue;
588
589		ret = avs_register_i2s_board(adev, mach);
590		if (ret < 0)
591			dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret);
592	}
593
594	return 0;
595}
596
597static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
598{
599	struct hdac_device *hdev = &codec->core;
600	struct snd_soc_acpi_mach mach = {{0}};
601	struct platform_device *pdev;
602
603	mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
604					    hdev->vendor_id);
605	if (!mach.tplg_filename)
606		return -ENOMEM;
607
608	pdev = avs_register_board_pdata(adev, "avs_hdaudio", &mach, codec, NULL, NULL);
609	if (IS_ERR(pdev))
610		return PTR_ERR(pdev);
611
612	return avs_register_hda_component(adev, dev_name(&pdev->dev));
613}
614
615static int avs_register_hda_boards(struct avs_dev *adev)
616{
617	struct hdac_bus *bus = &adev->base.core;
618	struct hdac_device *hdev;
619	int ret;
620
621	if (!bus->num_codecs) {
622		dev_dbg(adev->dev, "no HDA endpoints present\n");
623		return 0;
624	}
625
626	list_for_each_entry(hdev, &bus->codec_list, list) {
627		struct hda_codec *codec;
628
629		codec = dev_to_hda_codec(&hdev->dev);
630
631		ret = avs_register_hda_board(adev, codec);
632		if (ret < 0)
633			dev_warn(adev->dev, "register hda-%08x failed: %d\n",
634				 codec->core.vendor_id, ret);
635	}
636
637	return 0;
638}
639
640int avs_register_all_boards(struct avs_dev *adev)
641{
642	int ret;
643
644#ifdef CONFIG_DEBUG_FS
645	ret = avs_register_probe_board(adev);
646	if (ret < 0)
647		dev_warn(adev->dev, "enumerate PROBE endpoints failed: %d\n", ret);
648#endif
649
650	ret = avs_register_dmic_board(adev);
651	if (ret < 0)
652		dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
653			 ret);
654
655	ret = avs_register_i2s_test_boards(adev);
656	if (ret)
657		dev_dbg(adev->dev, "enumerate I2S TEST endpoints failed: %d\n", ret);
658
659	ret = avs_register_i2s_boards(adev);
660	if (ret < 0)
661		dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
662			 ret);
663
664	ret = avs_register_hda_boards(adev);
665	if (ret < 0)
666		dev_warn(adev->dev, "enumerate HDA endpoints failed: %d\n",
667			 ret);
668
669	return 0;
670}
671
672void avs_unregister_all_boards(struct avs_dev *adev)
673{
674	snd_soc_unregister_component(adev->dev);
675}
~/ Read-only file preview.