Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
   1// SPDX-License-Identifier: GPL-2.0-only
   2// This file incorporates work covered by the following copyright notice:
   3// Copyright (c) 2020 Intel Corporation
   4// Copyright(c) 2024 Advanced Micro Devices, Inc.
   5/*
   6 *  soc-sdw-utils.c - common SoundWire machine driver helper functions
   7 */
   8
   9#include <linux/device.h>
  10#include <linux/module.h>
  11#include <linux/soundwire/sdw.h>
  12#include <linux/soundwire/sdw_type.h>
  13#include <sound/soc_sdw_utils.h>
  14
  15static const struct snd_soc_dapm_widget generic_dmic_widgets[] = {
  16	SND_SOC_DAPM_MIC("DMIC", NULL),
  17};
  18
  19static const struct snd_soc_dapm_widget generic_jack_widgets[] = {
  20	SND_SOC_DAPM_HP("Headphone", NULL),
  21	SND_SOC_DAPM_MIC("Headset Mic", NULL),
  22};
  23
  24static const struct snd_kcontrol_new generic_jack_controls[] = {
  25	SOC_DAPM_PIN_SWITCH("Headphone"),
  26	SOC_DAPM_PIN_SWITCH("Headset Mic"),
  27};
  28
  29static const struct snd_soc_dapm_widget generic_spk_widgets[] = {
  30	SND_SOC_DAPM_SPK("Speaker", NULL),
  31};
  32
  33static const struct snd_kcontrol_new generic_spk_controls[] = {
  34	SOC_DAPM_PIN_SWITCH("Speaker"),
  35};
  36
  37static const struct snd_soc_dapm_widget maxim_widgets[] = {
  38	SND_SOC_DAPM_SPK("Left Spk", NULL),
  39	SND_SOC_DAPM_SPK("Right Spk", NULL),
  40};
  41
  42static const struct snd_kcontrol_new maxim_controls[] = {
  43	SOC_DAPM_PIN_SWITCH("Left Spk"),
  44	SOC_DAPM_PIN_SWITCH("Right Spk"),
  45};
  46
  47static const struct snd_soc_dapm_widget rt700_widgets[] = {
  48	SND_SOC_DAPM_HP("Headphones", NULL),
  49	SND_SOC_DAPM_MIC("AMIC", NULL),
  50	SND_SOC_DAPM_SPK("Speaker", NULL),
  51};
  52
  53static const struct snd_kcontrol_new rt700_controls[] = {
  54	SOC_DAPM_PIN_SWITCH("Headphones"),
  55	SOC_DAPM_PIN_SWITCH("AMIC"),
  56	SOC_DAPM_PIN_SWITCH("Speaker"),
  57};
  58
  59struct asoc_sdw_codec_info codec_info_list[] = {
  60	{
  61		.part_id = 0x700,
  62		.dais = {
  63			{
  64				.direction = {true, true},
  65				.dai_name = "rt700-aif1",
  66				.dai_type = SOC_SDW_DAI_TYPE_JACK,
  67				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  68				.rtd_init = asoc_sdw_rt700_rtd_init,
  69				.controls = rt700_controls,
  70				.num_controls = ARRAY_SIZE(rt700_controls),
  71				.widgets = rt700_widgets,
  72				.num_widgets = ARRAY_SIZE(rt700_widgets),
  73			},
  74		},
  75		.dai_num = 1,
  76	},
  77	{
  78		.part_id = 0x711,
  79		.version_id = 3,
  80		.dais = {
  81			{
  82				.direction = {true, true},
  83				.dai_name = "rt711-sdca-aif1",
  84				.dai_type = SOC_SDW_DAI_TYPE_JACK,
  85				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  86				.init = asoc_sdw_rt_sdca_jack_init,
  87				.exit = asoc_sdw_rt_sdca_jack_exit,
  88				.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
  89				.controls = generic_jack_controls,
  90				.num_controls = ARRAY_SIZE(generic_jack_controls),
  91				.widgets = generic_jack_widgets,
  92				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
  93			},
  94		},
  95		.dai_num = 1,
  96	},
  97	{
  98		.part_id = 0x711,
  99		.version_id = 2,
 100		.dais = {
 101			{
 102				.direction = {true, true},
 103				.dai_name = "rt711-aif1",
 104				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 105				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 106				.init = asoc_sdw_rt711_init,
 107				.exit = asoc_sdw_rt711_exit,
 108				.rtd_init = asoc_sdw_rt711_rtd_init,
 109				.controls = generic_jack_controls,
 110				.num_controls = ARRAY_SIZE(generic_jack_controls),
 111				.widgets = generic_jack_widgets,
 112				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 113			},
 114		},
 115		.dai_num = 1,
 116	},
 117	{
 118		.part_id = 0x712,
 119		.version_id = 3,
 120		.dais =	{
 121			{
 122				.direction = {true, true},
 123				.dai_name = "rt712-sdca-aif1",
 124				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 125				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 126				.init = asoc_sdw_rt_sdca_jack_init,
 127				.exit = asoc_sdw_rt_sdca_jack_exit,
 128				.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
 129				.controls = generic_jack_controls,
 130				.num_controls = ARRAY_SIZE(generic_jack_controls),
 131				.widgets = generic_jack_widgets,
 132				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 133			},
 134			{
 135				.direction = {true, false},
 136				.dai_name = "rt712-sdca-aif2",
 137				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 138				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 139				.init = asoc_sdw_rt_amp_init,
 140				.exit = asoc_sdw_rt_amp_exit,
 141				.rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init,
 142				.controls = generic_spk_controls,
 143				.num_controls = ARRAY_SIZE(generic_spk_controls),
 144				.widgets = generic_spk_widgets,
 145				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 146			},
 147			{
 148				.direction = {false, true},
 149				.dai_name = "rt712-sdca-aif3",
 150				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 151				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 152				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 153			},
 154		},
 155		.dai_num = 3,
 156	},
 157	{
 158		.part_id = 0x1712,
 159		.version_id = 3,
 160		.dais =	{
 161			{
 162				.direction = {false, true},
 163				.dai_name = "rt712-sdca-dmic-aif1",
 164				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 165				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 166				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 167			},
 168		},
 169		.dai_num = 1,
 170	},
 171	{
 172		.part_id = 0x713,
 173		.version_id = 3,
 174		.dais =	{
 175			{
 176				.direction = {true, true},
 177				.dai_name = "rt712-sdca-aif1",
 178				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 179				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 180				.init = asoc_sdw_rt_sdca_jack_init,
 181				.exit = asoc_sdw_rt_sdca_jack_exit,
 182				.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
 183				.controls = generic_jack_controls,
 184				.num_controls = ARRAY_SIZE(generic_jack_controls),
 185				.widgets = generic_jack_widgets,
 186				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 187			},
 188			{
 189				.direction = {false, true},
 190				.dai_name = "rt712-sdca-aif3",
 191				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 192				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 193				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 194			},
 195		},
 196		.dai_num = 2,
 197	},
 198	{
 199		.part_id = 0x1713,
 200		.version_id = 3,
 201		.dais =	{
 202			{
 203				.direction = {false, true},
 204				.dai_name = "rt712-sdca-dmic-aif1",
 205				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 206				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 207				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 208			},
 209		},
 210		.dai_num = 1,
 211	},
 212	{
 213		.part_id = 0x1308,
 214		.acpi_id = "10EC1308",
 215		.dais = {
 216			{
 217				.direction = {true, false},
 218				.dai_name = "rt1308-aif",
 219				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 220				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 221				.init = asoc_sdw_rt_amp_init,
 222				.exit = asoc_sdw_rt_amp_exit,
 223				.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
 224				.controls = generic_spk_controls,
 225				.num_controls = ARRAY_SIZE(generic_spk_controls),
 226				.widgets = generic_spk_widgets,
 227				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 228			},
 229		},
 230		.dai_num = 1,
 231		.ops = &soc_sdw_rt1308_i2s_ops,
 232	},
 233	{
 234		.part_id = 0x1316,
 235		.dais = {
 236			{
 237				.direction = {true, true},
 238				.dai_name = "rt1316-aif",
 239				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 240				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
 241				.init = asoc_sdw_rt_amp_init,
 242				.exit = asoc_sdw_rt_amp_exit,
 243				.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
 244				.controls = generic_spk_controls,
 245				.num_controls = ARRAY_SIZE(generic_spk_controls),
 246				.widgets = generic_spk_widgets,
 247				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 248			},
 249		},
 250		.dai_num = 1,
 251	},
 252	{
 253		.part_id = 0x1318,
 254		.dais = {
 255			{
 256				.direction = {true, true},
 257				.dai_name = "rt1318-aif",
 258				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 259				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
 260				.init = asoc_sdw_rt_amp_init,
 261				.exit = asoc_sdw_rt_amp_exit,
 262				.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
 263				.controls = generic_spk_controls,
 264				.num_controls = ARRAY_SIZE(generic_spk_controls),
 265				.widgets = generic_spk_widgets,
 266				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 267			},
 268		},
 269		.dai_num = 1,
 270	},
 271	{
 272		.part_id = 0x1320,
 273		.dais = {
 274			{
 275				.direction = {true, false},
 276				.dai_name = "rt1320-aif1",
 277				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 278				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 279				.init = asoc_sdw_rt_amp_init,
 280				.exit = asoc_sdw_rt_amp_exit,
 281				.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
 282				.controls = generic_spk_controls,
 283				.num_controls = ARRAY_SIZE(generic_spk_controls),
 284				.widgets = generic_spk_widgets,
 285				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 286			},
 287		},
 288		.dai_num = 1,
 289	},
 290	{
 291		.part_id = 0x714,
 292		.version_id = 3,
 293		.ignore_internal_dmic = true,
 294		.dais = {
 295			{
 296				.direction = {false, true},
 297				.dai_name = "rt715-sdca-aif2",
 298				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 299				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 300				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 301			},
 302		},
 303		.dai_num = 1,
 304	},
 305	{
 306		.part_id = 0x715,
 307		.version_id = 3,
 308		.ignore_internal_dmic = true,
 309		.dais = {
 310			{
 311				.direction = {false, true},
 312				.dai_name = "rt715-sdca-aif2",
 313				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 314				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 315				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 316			},
 317		},
 318		.dai_num = 1,
 319	},
 320	{
 321		.part_id = 0x714,
 322		.version_id = 2,
 323		.ignore_internal_dmic = true,
 324		.dais = {
 325			{
 326				.direction = {false, true},
 327				.dai_name = "rt715-aif2",
 328				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 329				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 330				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 331			},
 332		},
 333		.dai_num = 1,
 334	},
 335	{
 336		.part_id = 0x715,
 337		.version_id = 2,
 338		.ignore_internal_dmic = true,
 339		.dais = {
 340			{
 341				.direction = {false, true},
 342				.dai_name = "rt715-aif2",
 343				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 344				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 345				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 346			},
 347		},
 348		.dai_num = 1,
 349	},
 350	{
 351		.part_id = 0x721,
 352		.version_id = 3,
 353		.dais = {
 354			{
 355				.direction = {true, true},
 356				.dai_name = "rt721-sdca-aif1",
 357				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 358				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 359				.init = asoc_sdw_rt_sdca_jack_init,
 360				.exit = asoc_sdw_rt_sdca_jack_exit,
 361				.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
 362				.controls = generic_jack_controls,
 363				.num_controls = ARRAY_SIZE(generic_jack_controls),
 364				.widgets = generic_jack_widgets,
 365				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 366			},
 367			{
 368				.direction = {true, false},
 369				.dai_name = "rt721-sdca-aif2",
 370				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 371				/* No feedback capability is provided by rt721-sdca codec driver*/
 372				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 373				.init = asoc_sdw_rt_amp_init,
 374				.exit = asoc_sdw_rt_amp_exit,
 375				.rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init,
 376				.controls = generic_spk_controls,
 377				.num_controls = ARRAY_SIZE(generic_spk_controls),
 378				.widgets = generic_spk_widgets,
 379				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 380			},
 381			{
 382				.direction = {false, true},
 383				.dai_name = "rt721-sdca-aif3",
 384				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 385				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 386				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 387			},
 388		},
 389		.dai_num = 3,
 390	},
 391	{
 392		.part_id = 0x722,
 393		.version_id = 3,
 394		.dais = {
 395			{
 396				.direction = {true, true},
 397				.dai_name = "rt722-sdca-aif1",
 398				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 399				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 400				.init = asoc_sdw_rt_sdca_jack_init,
 401				.exit = asoc_sdw_rt_sdca_jack_exit,
 402				.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
 403				.controls = generic_jack_controls,
 404				.num_controls = ARRAY_SIZE(generic_jack_controls),
 405				.widgets = generic_jack_widgets,
 406				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 407			},
 408			{
 409				.direction = {true, false},
 410				.dai_name = "rt722-sdca-aif2",
 411				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 412				/* No feedback capability is provided by rt722-sdca codec driver*/
 413				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 414				.init = asoc_sdw_rt_amp_init,
 415				.exit = asoc_sdw_rt_amp_exit,
 416				.rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init,
 417				.controls = generic_spk_controls,
 418				.num_controls = ARRAY_SIZE(generic_spk_controls),
 419				.widgets = generic_spk_widgets,
 420				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 421				.quirk = SOC_SDW_CODEC_SPKR,
 422				.quirk_exclude = true,
 423			},
 424			{
 425				.direction = {false, true},
 426				.dai_name = "rt722-sdca-aif3",
 427				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 428				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 429				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 430			},
 431		},
 432		.dai_num = 3,
 433	},
 434	{
 435		.part_id = 0x8373,
 436		.dais = {
 437			{
 438				.direction = {true, true},
 439				.dai_name = "max98373-aif1",
 440				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 441				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
 442				.init = asoc_sdw_maxim_init,
 443				.rtd_init = asoc_sdw_maxim_spk_rtd_init,
 444				.controls = maxim_controls,
 445				.num_controls = ARRAY_SIZE(maxim_controls),
 446				.widgets = maxim_widgets,
 447				.num_widgets = ARRAY_SIZE(maxim_widgets),
 448			},
 449		},
 450		.dai_num = 1,
 451	},
 452	{
 453		.part_id = 0x8363,
 454		.dais = {
 455			{
 456				.direction = {true, false},
 457				.dai_name = "max98363-aif1",
 458				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 459				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 460				.init = asoc_sdw_maxim_init,
 461				.rtd_init = asoc_sdw_maxim_spk_rtd_init,
 462				.controls = maxim_controls,
 463				.num_controls = ARRAY_SIZE(maxim_controls),
 464				.widgets = maxim_widgets,
 465				.num_widgets = ARRAY_SIZE(maxim_widgets),
 466			},
 467		},
 468		.dai_num = 1,
 469	},
 470	{
 471		.part_id = 0x5682,
 472		.dais = {
 473			{
 474				.direction = {true, true},
 475				.dai_name = "rt5682-sdw",
 476				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 477				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 478				.rtd_init = asoc_sdw_rt5682_rtd_init,
 479				.controls = generic_jack_controls,
 480				.num_controls = ARRAY_SIZE(generic_jack_controls),
 481				.widgets = generic_jack_widgets,
 482				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 483			},
 484		},
 485		.dai_num = 1,
 486	},
 487	{
 488		.part_id = 0x3556,
 489		.dais = {
 490			{
 491				.direction = {true, true},
 492				.dai_name = "cs35l56-sdw1",
 493				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 494				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
 495				.init = asoc_sdw_cs_amp_init,
 496				.rtd_init = asoc_sdw_cs_spk_rtd_init,
 497				.controls = generic_spk_controls,
 498				.num_controls = ARRAY_SIZE(generic_spk_controls),
 499				.widgets = generic_spk_widgets,
 500				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 501			},
 502		},
 503		.dai_num = 1,
 504	},
 505	{
 506		.part_id = 0x4242,
 507		.dais = {
 508			{
 509				.direction = {true, true},
 510				.dai_name = "cs42l42-sdw",
 511				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 512				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 513				.rtd_init = asoc_sdw_cs42l42_rtd_init,
 514				.controls = generic_jack_controls,
 515				.num_controls = ARRAY_SIZE(generic_jack_controls),
 516				.widgets = generic_jack_widgets,
 517				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 518			},
 519		},
 520		.dai_num = 1,
 521	},
 522	{
 523		.part_id = 0x4243,
 524		.codec_name = "cs42l43-codec",
 525		.count_sidecar = asoc_sdw_bridge_cs35l56_count_sidecar,
 526		.add_sidecar = asoc_sdw_bridge_cs35l56_add_sidecar,
 527		.dais = {
 528			{
 529				.direction = {true, false},
 530				.dai_name = "cs42l43-dp5",
 531				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 532				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 533				.rtd_init = asoc_sdw_cs42l43_hs_rtd_init,
 534				.controls = generic_jack_controls,
 535				.num_controls = ARRAY_SIZE(generic_jack_controls),
 536				.widgets = generic_jack_widgets,
 537				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 538			},
 539			{
 540				.direction = {false, true},
 541				.dai_name = "cs42l43-dp1",
 542				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 543				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 544				.rtd_init = asoc_sdw_cs42l43_dmic_rtd_init,
 545				.widgets = generic_dmic_widgets,
 546				.num_widgets = ARRAY_SIZE(generic_dmic_widgets),
 547				.quirk = SOC_SDW_CODEC_MIC,
 548				.quirk_exclude = true,
 549			},
 550			{
 551				.direction = {false, true},
 552				.dai_name = "cs42l43-dp2",
 553				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 554				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 555			},
 556			{
 557				.direction = {true, false},
 558				.dai_name = "cs42l43-dp6",
 559				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 560				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 561				.init = asoc_sdw_cs42l43_spk_init,
 562				.rtd_init = asoc_sdw_cs42l43_spk_rtd_init,
 563				.controls = generic_spk_controls,
 564				.num_controls = ARRAY_SIZE(generic_spk_controls),
 565				.widgets = generic_spk_widgets,
 566				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 567				.quirk = SOC_SDW_CODEC_SPKR | SOC_SDW_SIDECAR_AMPS,
 568			},
 569		},
 570		.dai_num = 4,
 571	},
 572	{
 573		.part_id = 0xaaaa, /* generic codec mockup */
 574		.version_id = 0,
 575		.dais = {
 576			{
 577				.direction = {true, true},
 578				.dai_name = "sdw-mockup-aif1",
 579				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 580				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 581			},
 582		},
 583		.dai_num = 1,
 584	},
 585	{
 586		.part_id = 0xaa55, /* headset codec mockup */
 587		.version_id = 0,
 588		.dais = {
 589			{
 590				.direction = {true, true},
 591				.dai_name = "sdw-mockup-aif1",
 592				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 593				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 594			},
 595		},
 596		.dai_num = 1,
 597	},
 598	{
 599		.part_id = 0x55aa, /* amplifier mockup */
 600		.version_id = 0,
 601		.dais = {
 602			{
 603				.direction = {true, true},
 604				.dai_name = "sdw-mockup-aif1",
 605				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 606				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
 607			},
 608		},
 609		.dai_num = 1,
 610	},
 611	{
 612		.part_id = 0x5555,
 613		.version_id = 0,
 614		.dais = {
 615			{
 616				.dai_name = "sdw-mockup-aif1",
 617				.direction = {false, true},
 618				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 619				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 620			},
 621		},
 622		.dai_num = 1,
 623	},
 624};
 625EXPORT_SYMBOL_NS(codec_info_list, "SND_SOC_SDW_UTILS");
 626
 627int asoc_sdw_get_codec_info_list_count(void)
 628{
 629	return ARRAY_SIZE(codec_info_list);
 630};
 631EXPORT_SYMBOL_NS(asoc_sdw_get_codec_info_list_count, "SND_SOC_SDW_UTILS");
 632
 633struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr)
 634{
 635	unsigned int part_id, sdw_version;
 636	int i;
 637
 638	part_id = SDW_PART_ID(adr);
 639	sdw_version = SDW_VERSION(adr);
 640	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
 641		/*
 642		 * A codec info is for all sdw version with the part id if
 643		 * version_id is not specified in the codec info.
 644		 */
 645		if (part_id == codec_info_list[i].part_id &&
 646		    (!codec_info_list[i].version_id ||
 647		     sdw_version == codec_info_list[i].version_id))
 648			return &codec_info_list[i];
 649
 650	return NULL;
 651}
 652EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_part, "SND_SOC_SDW_UTILS");
 653
 654struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_acpi(const u8 *acpi_id)
 655{
 656	int i;
 657
 658	if (!acpi_id[0])
 659		return NULL;
 660
 661	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
 662		if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN))
 663			return &codec_info_list[i];
 664
 665	return NULL;
 666}
 667EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_acpi, "SND_SOC_SDW_UTILS");
 668
 669struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_dai(const char *dai_name, int *dai_index)
 670{
 671	int i, j;
 672
 673	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
 674		for (j = 0; j < codec_info_list[i].dai_num; j++) {
 675			if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) {
 676				*dai_index = j;
 677				return &codec_info_list[i];
 678			}
 679		}
 680	}
 681
 682	return NULL;
 683}
 684EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_dai, "SND_SOC_SDW_UTILS");
 685
 686int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd)
 687{
 688	struct snd_soc_card *card = rtd->card;
 689	struct asoc_sdw_codec_info *codec_info;
 690	struct snd_soc_dai *dai;
 691	int dai_index;
 692	int ret;
 693	int i;
 694
 695	for_each_rtd_codec_dais(rtd, i, dai) {
 696		codec_info = asoc_sdw_find_codec_info_dai(dai->name, &dai_index);
 697		if (!codec_info)
 698			return -EINVAL;
 699
 700		/*
 701		 * A codec dai can be connected to different dai links for capture and playback,
 702		 * but we only need to call the rtd_init function once.
 703		 * The rtd_init for each codec dai is independent. So, the order of rtd_init
 704		 * doesn't matter.
 705		 */
 706		if (codec_info->dais[dai_index].rtd_init_done)
 707			continue;
 708
 709		/*
 710		 * Add card controls and dapm widgets for the first codec dai.
 711		 * The controls and widgets will be used for all codec dais.
 712		 */
 713
 714		if (i > 0)
 715			goto skip_add_controls_widgets;
 716
 717		if (codec_info->dais[dai_index].controls) {
 718			ret = snd_soc_add_card_controls(card, codec_info->dais[dai_index].controls,
 719							codec_info->dais[dai_index].num_controls);
 720			if (ret) {
 721				dev_err(card->dev, "%#x controls addition failed: %d\n",
 722					codec_info->part_id, ret);
 723				return ret;
 724			}
 725		}
 726		if (codec_info->dais[dai_index].widgets) {
 727			ret = snd_soc_dapm_new_controls(&card->dapm,
 728							codec_info->dais[dai_index].widgets,
 729							codec_info->dais[dai_index].num_widgets);
 730			if (ret) {
 731				dev_err(card->dev, "%#x widgets addition failed: %d\n",
 732					codec_info->part_id, ret);
 733				return ret;
 734			}
 735		}
 736
 737skip_add_controls_widgets:
 738		if (codec_info->dais[dai_index].rtd_init) {
 739			ret = codec_info->dais[dai_index].rtd_init(rtd, dai);
 740			if (ret)
 741				return ret;
 742		}
 743		codec_info->dais[dai_index].rtd_init_done = true;
 744	}
 745
 746	return 0;
 747}
 748EXPORT_SYMBOL_NS(asoc_sdw_rtd_init, "SND_SOC_SDW_UTILS");
 749
 750/* these wrappers are only needed to avoid typecast compilation errors */
 751int asoc_sdw_startup(struct snd_pcm_substream *substream)
 752{
 753	return sdw_startup_stream(substream);
 754}
 755EXPORT_SYMBOL_NS(asoc_sdw_startup, "SND_SOC_SDW_UTILS");
 756
 757int asoc_sdw_prepare(struct snd_pcm_substream *substream)
 758{
 759	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 760	struct sdw_stream_runtime *sdw_stream;
 761	struct snd_soc_dai *dai;
 762
 763	/* Find stream from first CPU DAI */
 764	dai = snd_soc_rtd_to_cpu(rtd, 0);
 765
 766	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
 767	if (IS_ERR(sdw_stream)) {
 768		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
 769		return PTR_ERR(sdw_stream);
 770	}
 771
 772	return sdw_prepare_stream(sdw_stream);
 773}
 774EXPORT_SYMBOL_NS(asoc_sdw_prepare, "SND_SOC_SDW_UTILS");
 775
 776int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd)
 777{
 778	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 779	struct sdw_stream_runtime *sdw_stream;
 780	struct snd_soc_dai *dai;
 781	int ret;
 782
 783	/* Find stream from first CPU DAI */
 784	dai = snd_soc_rtd_to_cpu(rtd, 0);
 785
 786	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
 787	if (IS_ERR(sdw_stream)) {
 788		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
 789		return PTR_ERR(sdw_stream);
 790	}
 791
 792	switch (cmd) {
 793	case SNDRV_PCM_TRIGGER_START:
 794	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 795	case SNDRV_PCM_TRIGGER_RESUME:
 796		ret = sdw_enable_stream(sdw_stream);
 797		break;
 798
 799	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 800	case SNDRV_PCM_TRIGGER_SUSPEND:
 801	case SNDRV_PCM_TRIGGER_STOP:
 802		ret = sdw_disable_stream(sdw_stream);
 803		break;
 804	default:
 805		ret = -EINVAL;
 806		break;
 807	}
 808
 809	if (ret)
 810		dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret);
 811
 812	return ret;
 813}
 814EXPORT_SYMBOL_NS(asoc_sdw_trigger, "SND_SOC_SDW_UTILS");
 815
 816int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
 817		       struct snd_pcm_hw_params *params)
 818{
 819	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 820	struct snd_soc_dai_link_ch_map *ch_maps;
 821	int ch = params_channels(params);
 822	unsigned int ch_mask;
 823	int num_codecs;
 824	int step;
 825	int i;
 826
 827	if (!rtd->dai_link->ch_maps)
 828		return 0;
 829
 830	/* Identical data will be sent to all codecs in playback */
 831	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 832		ch_mask = GENMASK(ch - 1, 0);
 833		step = 0;
 834	} else {
 835		num_codecs = rtd->dai_link->num_codecs;
 836
 837		if (ch < num_codecs || ch % num_codecs != 0) {
 838			dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n",
 839				ch, num_codecs);
 840			return -EINVAL;
 841		}
 842
 843		ch_mask = GENMASK(ch / num_codecs - 1, 0);
 844		step = hweight_long(ch_mask);
 845	}
 846
 847	/*
 848	 * The captured data will be combined from each cpu DAI if the dai
 849	 * link has more than one codec DAIs. Set codec channel mask and
 850	 * ASoC will set the corresponding channel numbers for each cpu dai.
 851	 */
 852	for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
 853		ch_maps->ch_mask = ch_mask << (i * step);
 854
 855	return 0;
 856}
 857EXPORT_SYMBOL_NS(asoc_sdw_hw_params, "SND_SOC_SDW_UTILS");
 858
 859int asoc_sdw_hw_free(struct snd_pcm_substream *substream)
 860{
 861	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 862	struct sdw_stream_runtime *sdw_stream;
 863	struct snd_soc_dai *dai;
 864
 865	/* Find stream from first CPU DAI */
 866	dai = snd_soc_rtd_to_cpu(rtd, 0);
 867
 868	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
 869	if (IS_ERR(sdw_stream)) {
 870		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
 871		return PTR_ERR(sdw_stream);
 872	}
 873
 874	return sdw_deprepare_stream(sdw_stream);
 875}
 876EXPORT_SYMBOL_NS(asoc_sdw_hw_free, "SND_SOC_SDW_UTILS");
 877
 878void asoc_sdw_shutdown(struct snd_pcm_substream *substream)
 879{
 880	sdw_shutdown_stream(substream);
 881}
 882EXPORT_SYMBOL_NS(asoc_sdw_shutdown, "SND_SOC_SDW_UTILS");
 883
 884static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_link,
 885				      unsigned int sdw_version,
 886				      unsigned int mfg_id,
 887				      unsigned int part_id,
 888				      unsigned int class_id,
 889				      int index_in_link)
 890{
 891	int i;
 892
 893	for (i = 0; i < adr_link->num_adr; i++) {
 894		unsigned int sdw1_version, mfg1_id, part1_id, class1_id;
 895		u64 adr;
 896
 897		/* skip itself */
 898		if (i == index_in_link)
 899			continue;
 900
 901		adr = adr_link->adr_d[i].adr;
 902
 903		sdw1_version = SDW_VERSION(adr);
 904		mfg1_id = SDW_MFG_ID(adr);
 905		part1_id = SDW_PART_ID(adr);
 906		class1_id = SDW_CLASS_ID(adr);
 907
 908		if (sdw_version == sdw1_version &&
 909		    mfg_id == mfg1_id &&
 910		    part_id == part1_id &&
 911		    class_id == class1_id)
 912			return false;
 913	}
 914
 915	return true;
 916}
 917
 918const char *asoc_sdw_get_codec_name(struct device *dev,
 919				    const struct asoc_sdw_codec_info *codec_info,
 920				    const struct snd_soc_acpi_link_adr *adr_link,
 921				    int adr_index)
 922{
 923	u64 adr = adr_link->adr_d[adr_index].adr;
 924	unsigned int sdw_version = SDW_VERSION(adr);
 925	unsigned int link_id = SDW_DISCO_LINK_ID(adr);
 926	unsigned int unique_id = SDW_UNIQUE_ID(adr);
 927	unsigned int mfg_id = SDW_MFG_ID(adr);
 928	unsigned int part_id = SDW_PART_ID(adr);
 929	unsigned int class_id = SDW_CLASS_ID(adr);
 930
 931	if (codec_info->codec_name)
 932		return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL);
 933	else if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id,
 934					   class_id, adr_index))
 935		return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x",
 936				      link_id, mfg_id, part_id, class_id);
 937	else
 938		return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x",
 939				      link_id, mfg_id, part_id, class_id, unique_id);
 940
 941	return NULL;
 942}
 943EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, "SND_SOC_SDW_UTILS");
 944
 945/* helper to get the link that the codec DAI is used */
 946struct snd_soc_dai_link *asoc_sdw_mc_find_codec_dai_used(struct snd_soc_card *card,
 947							 const char *dai_name)
 948{
 949	struct snd_soc_dai_link *dai_link;
 950	int i;
 951	int j;
 952
 953	for_each_card_prelinks(card, i, dai_link) {
 954		for (j = 0; j < dai_link->num_codecs; j++) {
 955			/* Check each codec in a link */
 956			if (!strcmp(dai_link->codecs[j].dai_name, dai_name))
 957				return dai_link;
 958		}
 959	}
 960	return NULL;
 961}
 962EXPORT_SYMBOL_NS(asoc_sdw_mc_find_codec_dai_used, "SND_SOC_SDW_UTILS");
 963
 964void asoc_sdw_mc_dailink_exit_loop(struct snd_soc_card *card)
 965{
 966	struct snd_soc_dai_link *dai_link;
 967	struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
 968	int ret;
 969	int i, j;
 970
 971	for (i = 0; i < ctx->codec_info_list_count; i++) {
 972		for (j = 0; j < codec_info_list[i].dai_num; j++) {
 973			codec_info_list[i].dais[j].rtd_init_done = false;
 974			/* Check each dai in codec_info_lis to see if it is used in the link */
 975			if (!codec_info_list[i].dais[j].exit)
 976				continue;
 977			/*
 978			 * We don't need to call .exit function if there is no matched
 979			 * dai link found.
 980			 */
 981			dai_link = asoc_sdw_mc_find_codec_dai_used(card,
 982							  codec_info_list[i].dais[j].dai_name);
 983			if (dai_link) {
 984				/* Do the .exit function if the codec dai is used in the link */
 985				ret = codec_info_list[i].dais[j].exit(card, dai_link);
 986				if (ret)
 987					dev_warn(card->dev,
 988						 "codec exit failed %d\n",
 989						 ret);
 990				break;
 991			}
 992		}
 993	}
 994}
 995EXPORT_SYMBOL_NS(asoc_sdw_mc_dailink_exit_loop, "SND_SOC_SDW_UTILS");
 996
 997int asoc_sdw_card_late_probe(struct snd_soc_card *card)
 998{
 999	int ret = 0;
1000	int i;
1001
1002	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
1003		if (codec_info_list[i].codec_card_late_probe) {
1004			ret = codec_info_list[i].codec_card_late_probe(card);
1005			if (ret < 0)
1006				return ret;
1007		}
1008	}
1009	return ret;
1010}
1011EXPORT_SYMBOL_NS(asoc_sdw_card_late_probe, "SND_SOC_SDW_UTILS");
1012
1013void asoc_sdw_init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
1014			    int *be_id, char *name, int playback, int capture,
1015			    struct snd_soc_dai_link_component *cpus, int cpus_num,
1016			    struct snd_soc_dai_link_component *platform_component,
1017			    int num_platforms, struct snd_soc_dai_link_component *codecs,
1018			    int codecs_num, int no_pcm,
1019			    int (*init)(struct snd_soc_pcm_runtime *rtd),
1020			    const struct snd_soc_ops *ops)
1021{
1022	dev_dbg(dev, "create dai link %s, id %d\n", name, *be_id);
1023	dai_links->id = (*be_id)++;
1024	dai_links->name = name;
1025	dai_links->stream_name = name;
1026	dai_links->platforms = platform_component;
1027	dai_links->num_platforms = num_platforms;
1028	dai_links->no_pcm = no_pcm;
1029	dai_links->cpus = cpus;
1030	dai_links->num_cpus = cpus_num;
1031	dai_links->codecs = codecs;
1032	dai_links->num_codecs = codecs_num;
1033	dai_links->playback_only =  playback && !capture;
1034	dai_links->capture_only  = !playback &&  capture;
1035	dai_links->init = init;
1036	dai_links->ops = ops;
1037}
1038EXPORT_SYMBOL_NS(asoc_sdw_init_dai_link, "SND_SOC_SDW_UTILS");
1039
1040int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
1041				  int *be_id, char *name, int playback, int capture,
1042				  const char *cpu_dai_name, const char *platform_comp_name,
1043				  int num_platforms, const char *codec_name,
1044				  const char *codec_dai_name, int no_pcm,
1045				  int (*init)(struct snd_soc_pcm_runtime *rtd),
1046				  const struct snd_soc_ops *ops)
1047{
1048	struct snd_soc_dai_link_component *dlc;
1049
1050	/* Allocate three DLCs one for the CPU, one for platform and one for the CODEC */
1051	dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL);
1052	if (!dlc || !name || !cpu_dai_name || !platform_comp_name || !codec_name || !codec_dai_name)
1053		return -ENOMEM;
1054
1055	dlc[0].dai_name = cpu_dai_name;
1056	dlc[1].name = platform_comp_name;
1057
1058	dlc[2].name = codec_name;
1059	dlc[2].dai_name = codec_dai_name;
1060
1061	asoc_sdw_init_dai_link(dev, dai_links, be_id, name, playback, capture,
1062			       &dlc[0], 1, &dlc[1], num_platforms,
1063			       &dlc[2], 1, no_pcm, init, ops);
1064
1065	return 0;
1066}
1067EXPORT_SYMBOL_NS(asoc_sdw_init_simple_dai_link, "SND_SOC_SDW_UTILS");
1068
1069int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, int *num_devs, int *num_ends)
1070{
1071	struct device *dev = card->dev;
1072	struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
1073	struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
1074	const struct snd_soc_acpi_link_adr *adr_link;
1075	int i;
1076
1077	for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
1078		*num_devs += adr_link->num_adr;
1079
1080		for (i = 0; i < adr_link->num_adr; i++)
1081			*num_ends += adr_link->adr_d[i].num_endpoints;
1082	}
1083
1084	dev_dbg(dev, "Found %d devices with %d endpoints\n", *num_devs, *num_ends);
1085
1086	return 0;
1087}
1088EXPORT_SYMBOL_NS(asoc_sdw_count_sdw_endpoints, "SND_SOC_SDW_UTILS");
1089
1090struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks,
1091					       const struct snd_soc_acpi_endpoint *new)
1092{
1093	while (dailinks->initialised) {
1094		if (new->aggregated && dailinks->group_id == new->group_id)
1095			return dailinks;
1096
1097		dailinks++;
1098	}
1099
1100	INIT_LIST_HEAD(&dailinks->endpoints);
1101	dailinks->group_id = new->group_id;
1102	dailinks->initialised = true;
1103
1104	return dailinks;
1105}
1106EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, "SND_SOC_SDW_UTILS");
1107
1108int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
1109				 struct asoc_sdw_dailink *soc_dais,
1110				 struct asoc_sdw_endpoint *soc_ends,
1111				 int *num_devs)
1112{
1113	struct device *dev = card->dev;
1114	struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
1115	struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
1116	struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
1117	const struct snd_soc_acpi_link_adr *adr_link;
1118	struct asoc_sdw_endpoint *soc_end = soc_ends;
1119	int num_dais = 0;
1120	int i, j;
1121	int ret;
1122
1123	for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
1124		int num_link_dailinks = 0;
1125
1126		if (!is_power_of_2(adr_link->mask)) {
1127			dev_err(dev, "link with multiple mask bits: 0x%x\n",
1128				adr_link->mask);
1129			return -EINVAL;
1130		}
1131
1132		for (i = 0; i < adr_link->num_adr; i++) {
1133			const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i];
1134			struct asoc_sdw_codec_info *codec_info;
1135			const char *codec_name;
1136
1137			if (!adr_dev->name_prefix) {
1138				dev_err(dev, "codec 0x%llx does not have a name prefix\n",
1139					adr_dev->adr);
1140				return -EINVAL;
1141			}
1142
1143			codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr);
1144			if (!codec_info)
1145				return -EINVAL;
1146
1147			ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic;
1148
1149			codec_name = asoc_sdw_get_codec_name(dev, codec_info, adr_link, i);
1150			if (!codec_name)
1151				return -ENOMEM;
1152
1153			dev_dbg(dev, "Adding prefix %s for %s\n",
1154				adr_dev->name_prefix, codec_name);
1155
1156			soc_end->name_prefix = adr_dev->name_prefix;
1157
1158			if (codec_info->count_sidecar && codec_info->add_sidecar) {
1159				ret = codec_info->count_sidecar(card, &num_dais, num_devs);
1160				if (ret)
1161					return ret;
1162
1163				soc_end->include_sidecar = true;
1164			}
1165
1166			for (j = 0; j < adr_dev->num_endpoints; j++) {
1167				const struct snd_soc_acpi_endpoint *adr_end;
1168				const struct asoc_sdw_dai_info *dai_info;
1169				struct asoc_sdw_dailink *soc_dai;
1170				int stream;
1171
1172				adr_end = &adr_dev->endpoints[j];
1173				dai_info = &codec_info->dais[adr_end->num];
1174				soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end);
1175
1176				if (dai_info->quirk &&
1177				    !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk)))
1178					continue;
1179
1180				dev_dbg(dev,
1181					"Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n",
1182					ffs(adr_link->mask) - 1, adr_dev->adr,
1183					adr_end->num, dai_info->dai_type,
1184					dai_info->direction[SNDRV_PCM_STREAM_PLAYBACK] ? 'P' : '-',
1185					dai_info->direction[SNDRV_PCM_STREAM_CAPTURE] ? 'C' : '-',
1186					adr_end->aggregated ? "group" : "solo",
1187					adr_end->group_id);
1188
1189				if (adr_end->num >= codec_info->dai_num) {
1190					dev_err(dev,
1191						"%d is too many endpoints for codec: 0x%x\n",
1192						adr_end->num, codec_info->part_id);
1193					return -EINVAL;
1194				}
1195
1196				for_each_pcm_streams(stream) {
1197					if (dai_info->direction[stream] &&
1198					    dai_info->dailink[stream] < 0) {
1199						dev_err(dev,
1200							"Invalid dailink id %d for codec: 0x%x\n",
1201							dai_info->dailink[stream],
1202							codec_info->part_id);
1203						return -EINVAL;
1204					}
1205
1206					if (dai_info->direction[stream]) {
1207						num_dais += !soc_dai->num_devs[stream];
1208						soc_dai->num_devs[stream]++;
1209						soc_dai->link_mask[stream] |= adr_link->mask;
1210					}
1211				}
1212
1213				num_link_dailinks += !!list_empty(&soc_dai->endpoints);
1214				list_add_tail(&soc_end->list, &soc_dai->endpoints);
1215
1216				soc_end->link_mask = adr_link->mask;
1217				soc_end->codec_name = codec_name;
1218				soc_end->codec_info = codec_info;
1219				soc_end->dai_info = dai_info;
1220				soc_end++;
1221			}
1222		}
1223
1224		ctx->append_dai_type |= (num_link_dailinks > 1);
1225	}
1226
1227	return num_dais;
1228}
1229EXPORT_SYMBOL_NS(asoc_sdw_parse_sdw_endpoints, "SND_SOC_SDW_UTILS");
1230
1231MODULE_LICENSE("GPL");
1232MODULE_DESCRIPTION("SoundWire ASoC helpers");