File Index
Files and directories
Sign in
FILE

soc_sdw_utils.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/sdw_utils/soc_sdw_utils.c C
   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/sdca_function.h>
  14#include <sound/soc_sdw_utils.h>
  15
  16static const struct snd_soc_dapm_widget generic_dmic_widgets[] = {
  17	SND_SOC_DAPM_MIC("DMIC", NULL),
  18};
  19
  20static const struct snd_soc_dapm_widget generic_jack_widgets[] = {
  21	SND_SOC_DAPM_HP("Headphone", NULL),
  22	SND_SOC_DAPM_MIC("Headset Mic", NULL),
  23};
  24
  25static const struct snd_kcontrol_new generic_jack_controls[] = {
  26	SOC_DAPM_PIN_SWITCH("Headphone"),
  27	SOC_DAPM_PIN_SWITCH("Headset Mic"),
  28};
  29
  30static const struct snd_soc_dapm_widget generic_spk_widgets[] = {
  31	SND_SOC_DAPM_SPK("Speaker", NULL),
  32};
  33
  34static const struct snd_kcontrol_new generic_spk_controls[] = {
  35	SOC_DAPM_PIN_SWITCH("Speaker"),
  36};
  37
  38static const struct snd_soc_dapm_widget lr_spk_widgets[] = {
  39	SND_SOC_DAPM_SPK("Left Spk", NULL),
  40	SND_SOC_DAPM_SPK("Right Spk", NULL),
  41};
  42
  43static const struct snd_kcontrol_new lr_spk_controls[] = {
  44	SOC_DAPM_PIN_SWITCH("Left Spk"),
  45	SOC_DAPM_PIN_SWITCH("Right Spk"),
  46};
  47
  48static const struct snd_soc_dapm_widget rt700_widgets[] = {
  49	SND_SOC_DAPM_HP("Headphones", NULL),
  50	SND_SOC_DAPM_MIC("AMIC", NULL),
  51	SND_SOC_DAPM_SPK("Speaker", NULL),
  52};
  53
  54static const struct snd_kcontrol_new rt700_controls[] = {
  55	SOC_DAPM_PIN_SWITCH("Headphones"),
  56	SOC_DAPM_PIN_SWITCH("AMIC"),
  57	SOC_DAPM_PIN_SWITCH("Speaker"),
  58};
  59
  60struct asoc_sdw_codec_info codec_info_list[] = {
  61	{
  62		.part_id = 0x0000, /* TAS2783A */
  63		.name_prefix = "tas2783",
  64		.dais = {
  65			{
  66				.direction = {true, true},
  67				.dai_name = "tas2783-codec",
  68				.dai_type = SOC_SDW_DAI_TYPE_AMP,
  69				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
  70				.init = asoc_sdw_ti_amp_init,
  71				.rtd_init = asoc_sdw_ti_spk_rtd_init,
  72				.controls = lr_spk_controls,
  73				.num_controls = ARRAY_SIZE(lr_spk_controls),
  74				.widgets = lr_spk_widgets,
  75				.num_widgets = ARRAY_SIZE(lr_spk_widgets),
  76			},
  77		},
  78		.dai_num = 1,
  79	},
  80	{
  81		.part_id = 0x700,
  82		.name_prefix = "rt700",
  83		.dais = {
  84			{
  85				.direction = {true, true},
  86				.dai_name = "rt700-aif1",
  87				.dai_type = SOC_SDW_DAI_TYPE_JACK,
  88				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  89				.rtd_init = asoc_sdw_rt700_rtd_init,
  90				.controls = rt700_controls,
  91				.num_controls = ARRAY_SIZE(rt700_controls),
  92				.widgets = rt700_widgets,
  93				.num_widgets = ARRAY_SIZE(rt700_widgets),
  94			},
  95		},
  96		.dai_num = 1,
  97	},
  98	{
  99		.part_id = 0x711,
 100		.name_prefix = "rt711",
 101		.version_id = 3,
 102		.dais = {
 103			{
 104				.direction = {true, true},
 105				.dai_name = "rt711-sdca-aif1",
 106				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 107				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 108				.init = asoc_sdw_rt_sdca_jack_init,
 109				.exit = asoc_sdw_rt_sdca_jack_exit,
 110				.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
 111				.controls = generic_jack_controls,
 112				.num_controls = ARRAY_SIZE(generic_jack_controls),
 113				.widgets = generic_jack_widgets,
 114				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 115			},
 116		},
 117		.dai_num = 1,
 118	},
 119	{
 120		.part_id = 0x711,
 121		.name_prefix = "rt711",
 122		.version_id = 2,
 123		.dais = {
 124			{
 125				.direction = {true, true},
 126				.dai_name = "rt711-aif1",
 127				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 128				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 129				.init = asoc_sdw_rt711_init,
 130				.exit = asoc_sdw_rt711_exit,
 131				.rtd_init = asoc_sdw_rt711_rtd_init,
 132				.controls = generic_jack_controls,
 133				.num_controls = ARRAY_SIZE(generic_jack_controls),
 134				.widgets = generic_jack_widgets,
 135				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 136			},
 137		},
 138		.dai_num = 1,
 139	},
 140	{
 141		.part_id = 0x712,
 142		.name_prefix = "rt712",
 143		.version_id = 3,
 144		.dais =	{
 145			{
 146				.direction = {true, true},
 147				.dai_name = "rt712-sdca-aif1",
 148				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 149				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 150				.init = asoc_sdw_rt_sdca_jack_init,
 151				.exit = asoc_sdw_rt_sdca_jack_exit,
 152				.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
 153				.controls = generic_jack_controls,
 154				.num_controls = ARRAY_SIZE(generic_jack_controls),
 155				.widgets = generic_jack_widgets,
 156				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 157			},
 158			{
 159				.direction = {true, false},
 160				.dai_name = "rt712-sdca-aif2",
 161				.component_name = "rt712",
 162				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 163				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 164				.init = asoc_sdw_rt_amp_init,
 165				.exit = asoc_sdw_rt_amp_exit,
 166				.rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init,
 167				.controls = generic_spk_controls,
 168				.num_controls = ARRAY_SIZE(generic_spk_controls),
 169				.widgets = generic_spk_widgets,
 170				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 171			},
 172			{
 173				.direction = {false, true},
 174				.dai_name = "rt712-sdca-aif3",
 175				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 176				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 177				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 178			},
 179		},
 180		.dai_num = 3,
 181	},
 182	{
 183		.part_id = 0x1712,
 184		.name_prefix = "rt712-dmic",
 185		.version_id = 3,
 186		.dais =	{
 187			{
 188				.direction = {false, true},
 189				.dai_name = "rt712-sdca-dmic-aif1",
 190				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 191				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 192				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 193			},
 194		},
 195		.dai_num = 1,
 196	},
 197	{
 198		.part_id = 0x713,
 199		.name_prefix = "rt713",
 200		.version_id = 3,
 201		.dais =	{
 202			{
 203				.direction = {true, true},
 204				.dai_name = "rt712-sdca-aif1",
 205				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 206				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 207				.init = asoc_sdw_rt_sdca_jack_init,
 208				.exit = asoc_sdw_rt_sdca_jack_exit,
 209				.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
 210				.controls = generic_jack_controls,
 211				.num_controls = ARRAY_SIZE(generic_jack_controls),
 212				.widgets = generic_jack_widgets,
 213				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 214			},
 215			{
 216				.direction = {false, true},
 217				.dai_name = "rt712-sdca-aif3",
 218				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 219				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 220				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 221			},
 222		},
 223		.dai_num = 2,
 224	},
 225	{
 226		.part_id = 0x1713,
 227		.name_prefix = "rt713-dmic",
 228		.version_id = 3,
 229		.dais =	{
 230			{
 231				.direction = {false, true},
 232				.dai_name = "rt712-sdca-dmic-aif1",
 233				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 234				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 235				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 236			},
 237		},
 238		.dai_num = 1,
 239	},
 240	{
 241		.part_id = 0x1308,
 242		.name_prefix = "rt1308",
 243		.acpi_id = "10EC1308",
 244		.dais = {
 245			{
 246				.direction = {true, false},
 247				.dai_name = "rt1308-aif",
 248				.component_name = "rt1308",
 249				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 250				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 251				.init = asoc_sdw_rt_amp_init,
 252				.exit = asoc_sdw_rt_amp_exit,
 253				.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
 254				.controls = generic_spk_controls,
 255				.num_controls = ARRAY_SIZE(generic_spk_controls),
 256				.widgets = generic_spk_widgets,
 257				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 258			},
 259		},
 260		.dai_num = 1,
 261		.ops = &soc_sdw_rt1308_i2s_ops,
 262	},
 263	{
 264		.part_id = 0x1316,
 265		.name_prefix = "rt1316",
 266		.dais = {
 267			{
 268				.direction = {true, true},
 269				.dai_name = "rt1316-aif",
 270				.component_name = "rt1316",
 271				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 272				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
 273				.init = asoc_sdw_rt_amp_init,
 274				.exit = asoc_sdw_rt_amp_exit,
 275				.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
 276				.controls = generic_spk_controls,
 277				.num_controls = ARRAY_SIZE(generic_spk_controls),
 278				.widgets = generic_spk_widgets,
 279				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 280			},
 281		},
 282		.dai_num = 1,
 283	},
 284	{
 285		.part_id = 0x1318,
 286		.name_prefix = "rt1318",
 287		.dais = {
 288			{
 289				.direction = {true, true},
 290				.dai_name = "rt1318-aif",
 291				.component_name = "rt1318",
 292				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 293				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
 294				.init = asoc_sdw_rt_amp_init,
 295				.exit = asoc_sdw_rt_amp_exit,
 296				.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
 297				.controls = generic_spk_controls,
 298				.num_controls = ARRAY_SIZE(generic_spk_controls),
 299				.widgets = generic_spk_widgets,
 300				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 301			},
 302		},
 303		.dai_num = 1,
 304	},
 305	{
 306		.part_id = 0x1320,
 307		.name_prefix = "rt1320",
 308		.dais = {
 309			{
 310				.direction = {true, false},
 311				.dai_name = "rt1320-aif1",
 312				.component_name = "rt1320",
 313				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 314				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 315				.init = asoc_sdw_rt_amp_init,
 316				.exit = asoc_sdw_rt_amp_exit,
 317				.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
 318				.controls = generic_spk_controls,
 319				.num_controls = ARRAY_SIZE(generic_spk_controls),
 320				.widgets = generic_spk_widgets,
 321				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 322			},
 323		},
 324		.dai_num = 1,
 325	},
 326	{
 327		.part_id = 0x1321,
 328		.name_prefix = "rt1320",
 329		.dais = {
 330			{
 331				.direction = {true, false},
 332				.dai_name = "rt1320-aif1",
 333				.component_name = "rt1320",
 334				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 335				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 336				.init = asoc_sdw_rt_amp_init,
 337				.exit = asoc_sdw_rt_amp_exit,
 338				.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
 339				.controls = generic_spk_controls,
 340				.num_controls = ARRAY_SIZE(generic_spk_controls),
 341				.widgets = generic_spk_widgets,
 342				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 343			},
 344		},
 345		.dai_num = 1,
 346	},
 347	{
 348		.part_id = 0x714,
 349		.name_prefix = "rt714",
 350		.version_id = 3,
 351		.ignore_internal_dmic = true,
 352		.dais = {
 353			{
 354				.direction = {false, true},
 355				.dai_name = "rt715-sdca-aif2",
 356				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 357				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 358				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 359			},
 360		},
 361		.dai_num = 1,
 362	},
 363	{
 364		.part_id = 0x715,
 365		.name_prefix = "rt715",
 366		.version_id = 3,
 367		.ignore_internal_dmic = true,
 368		.dais = {
 369			{
 370				.direction = {false, true},
 371				.dai_name = "rt715-sdca-aif2",
 372				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 373				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 374				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 375			},
 376		},
 377		.dai_num = 1,
 378	},
 379	{
 380		.part_id = 0x714,
 381		.name_prefix = "rt714",
 382		.version_id = 2,
 383		.ignore_internal_dmic = true,
 384		.dais = {
 385			{
 386				.direction = {false, true},
 387				.dai_name = "rt715-aif2",
 388				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 389				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 390				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 391			},
 392		},
 393		.dai_num = 1,
 394	},
 395	{
 396		.part_id = 0x715,
 397		.name_prefix = "rt715",
 398		.version_id = 2,
 399		.ignore_internal_dmic = true,
 400		.dais = {
 401			{
 402				.direction = {false, true},
 403				.dai_name = "rt715-aif2",
 404				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 405				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 406				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 407			},
 408		},
 409		.dai_num = 1,
 410	},
 411	{
 412		.part_id = 0x721,
 413		.name_prefix = "rt721",
 414		.version_id = 3,
 415		.dais = {
 416			{
 417				.direction = {true, true},
 418				.dai_name = "rt721-sdca-aif1",
 419				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 420				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 421				.init = asoc_sdw_rt_sdca_jack_init,
 422				.exit = asoc_sdw_rt_sdca_jack_exit,
 423				.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
 424				.controls = generic_jack_controls,
 425				.num_controls = ARRAY_SIZE(generic_jack_controls),
 426				.widgets = generic_jack_widgets,
 427				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 428			},
 429			{
 430				.direction = {true, false},
 431				.dai_name = "rt721-sdca-aif2",
 432				.component_name = "rt721",
 433				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 434				/* No feedback capability is provided by rt721-sdca codec driver*/
 435				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 436				.init = asoc_sdw_rt_amp_init,
 437				.exit = asoc_sdw_rt_amp_exit,
 438				.rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init,
 439				.controls = generic_spk_controls,
 440				.num_controls = ARRAY_SIZE(generic_spk_controls),
 441				.widgets = generic_spk_widgets,
 442				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 443			},
 444			{
 445				.direction = {false, true},
 446				.dai_name = "rt721-sdca-aif3",
 447				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 448				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 449				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 450			},
 451		},
 452		.dai_num = 3,
 453	},
 454	{
 455		.part_id = 0x722,
 456		.name_prefix = "rt722",
 457		.version_id = 3,
 458		.dais = {
 459			{
 460				.direction = {true, true},
 461				.dai_name = "rt722-sdca-aif1",
 462				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 463				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 464				.init = asoc_sdw_rt_sdca_jack_init,
 465				.exit = asoc_sdw_rt_sdca_jack_exit,
 466				.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
 467				.controls = generic_jack_controls,
 468				.num_controls = ARRAY_SIZE(generic_jack_controls),
 469				.widgets = generic_jack_widgets,
 470				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 471			},
 472			{
 473				.direction = {true, false},
 474				.dai_name = "rt722-sdca-aif2",
 475				.component_name = "rt722",
 476				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 477				/* No feedback capability is provided by rt722-sdca codec driver*/
 478				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 479				.init = asoc_sdw_rt_amp_init,
 480				.exit = asoc_sdw_rt_amp_exit,
 481				.rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init,
 482				.controls = generic_spk_controls,
 483				.num_controls = ARRAY_SIZE(generic_spk_controls),
 484				.widgets = generic_spk_widgets,
 485				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 486				.quirk = SOC_SDW_CODEC_SPKR,
 487				.quirk_exclude = true,
 488			},
 489			{
 490				.direction = {false, true},
 491				.dai_name = "rt722-sdca-aif3",
 492				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 493				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 494				.rtd_init = asoc_sdw_rt_dmic_rtd_init,
 495			},
 496		},
 497		.dai_num = 3,
 498	},
 499	{
 500		.part_id = 0x8373,
 501		.name_prefix = "Left",
 502		.dais = {
 503			{
 504				.direction = {true, true},
 505				.dai_name = "max98373-aif1",
 506				.component_name = "mx8373",
 507				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 508				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
 509				.init = asoc_sdw_maxim_init,
 510				.rtd_init = asoc_sdw_maxim_spk_rtd_init,
 511				.controls = lr_spk_controls,
 512				.num_controls = ARRAY_SIZE(lr_spk_controls),
 513				.widgets = lr_spk_widgets,
 514				.num_widgets = ARRAY_SIZE(lr_spk_widgets),
 515			},
 516		},
 517		.dai_num = 1,
 518	},
 519	{
 520		.part_id = 0x8363,
 521		.name_prefix = "Left",
 522		.dais = {
 523			{
 524				.direction = {true, false},
 525				.dai_name = "max98363-aif1",
 526				.component_name = "mx8363",
 527				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 528				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 529				.init = asoc_sdw_maxim_init,
 530				.rtd_init = asoc_sdw_maxim_spk_rtd_init,
 531				.controls = lr_spk_controls,
 532				.num_controls = ARRAY_SIZE(lr_spk_controls),
 533				.widgets = lr_spk_widgets,
 534				.num_widgets = ARRAY_SIZE(lr_spk_widgets),
 535			},
 536		},
 537		.dai_num = 1,
 538	},
 539	{
 540		.part_id = 0x5682,
 541		.name_prefix = "rt5682",
 542		.dais = {
 543			{
 544				.direction = {true, true},
 545				.dai_name = "rt5682-sdw",
 546				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 547				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 548				.rtd_init = asoc_sdw_rt5682_rtd_init,
 549				.controls = generic_jack_controls,
 550				.num_controls = ARRAY_SIZE(generic_jack_controls),
 551				.widgets = generic_jack_widgets,
 552				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 553			},
 554		},
 555		.dai_num = 1,
 556	},
 557	{
 558		.part_id = 0x3556,
 559		.name_prefix = "AMP",
 560		.dais = {
 561			{
 562				.direction = {true, false},
 563				.dai_name = "cs35l56-sdw1",
 564				.component_name = "cs35l56",
 565				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 566				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 567				.init = asoc_sdw_cs_amp_init,
 568				.rtd_init = asoc_sdw_cs_spk_rtd_init,
 569				.controls = generic_spk_controls,
 570				.num_controls = ARRAY_SIZE(generic_spk_controls),
 571				.widgets = generic_spk_widgets,
 572				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 573			},
 574			{
 575				.direction = {false, true},
 576				.dai_name = "cs35l56-sdw1c",
 577				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 578				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
 579				.rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
 580			},
 581		},
 582		.dai_num = 2,
 583	},
 584	{
 585		.part_id = 0x3557,
 586		.name_prefix = "AMP",
 587		.dais = {
 588			{
 589				.direction = {true, false},
 590				.dai_name = "cs35l56-sdw1",
 591				.component_name = "cs35l56",
 592				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 593				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 594				.init = asoc_sdw_cs_amp_init,
 595				.rtd_init = asoc_sdw_cs_spk_rtd_init,
 596				.controls = generic_spk_controls,
 597				.num_controls = ARRAY_SIZE(generic_spk_controls),
 598				.widgets = generic_spk_widgets,
 599				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 600			},
 601			{
 602				.direction = {false, true},
 603				.dai_name = "cs35l56-sdw1c",
 604				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 605				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
 606				.rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
 607			},
 608		},
 609		.dai_num = 2,
 610	},
 611	{
 612		.part_id = 0x3563,
 613		.name_prefix = "AMP",
 614		.dais = {
 615			{
 616				.direction = {true, false},
 617				.dai_name = "cs35l56-sdw1",
 618				.component_name = "cs35l56",
 619				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 620				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 621				.init = asoc_sdw_cs_amp_init,
 622				.rtd_init = asoc_sdw_cs_spk_rtd_init,
 623				.controls = generic_spk_controls,
 624				.num_controls = ARRAY_SIZE(generic_spk_controls),
 625				.widgets = generic_spk_widgets,
 626				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 627			},
 628			{
 629				.direction = {false, true},
 630				.dai_name = "cs35l56-sdw1c",
 631				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 632				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
 633				.rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
 634			},
 635		},
 636		.dai_num = 2,
 637	},
 638	{
 639		.part_id = 0x4242,
 640		.name_prefix = "cs42l42",
 641		.dais = {
 642			{
 643				.direction = {true, true},
 644				.dai_name = "cs42l42-sdw",
 645				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 646				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 647				.rtd_init = asoc_sdw_cs42l42_rtd_init,
 648				.controls = generic_jack_controls,
 649				.num_controls = ARRAY_SIZE(generic_jack_controls),
 650				.widgets = generic_jack_widgets,
 651				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 652			},
 653		},
 654		.dai_num = 1,
 655	},
 656	{
 657		.part_id = 0x4243,
 658		.name_prefix = "cs42l43",
 659		.count_sidecar = asoc_sdw_bridge_cs35l56_count_sidecar,
 660		.add_sidecar = asoc_sdw_bridge_cs35l56_add_sidecar,
 661		.dais = {
 662			{
 663				.direction = {true, false},
 664				.codec_name = "cs42l43-codec",
 665				.dai_name = "cs42l43-dp5",
 666				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 667				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 668				.rtd_init = asoc_sdw_cs42l43_hs_rtd_init,
 669				.controls = generic_jack_controls,
 670				.num_controls = ARRAY_SIZE(generic_jack_controls),
 671				.widgets = generic_jack_widgets,
 672				.num_widgets = ARRAY_SIZE(generic_jack_widgets),
 673			},
 674			{
 675				.direction = {false, true},
 676				.codec_name = "cs42l43-codec",
 677				.dai_name = "cs42l43-dp1",
 678				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 679				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 680				.rtd_init = asoc_sdw_cs42l43_dmic_rtd_init,
 681				.widgets = generic_dmic_widgets,
 682				.num_widgets = ARRAY_SIZE(generic_dmic_widgets),
 683				.quirk = SOC_SDW_CODEC_MIC,
 684				.quirk_exclude = true,
 685			},
 686			{
 687				.direction = {false, true},
 688				.codec_name = "cs42l43-codec",
 689				.dai_name = "cs42l43-dp2",
 690				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 691				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 692			},
 693			{
 694				.direction = {true, false},
 695				.codec_name = "cs42l43-codec",
 696				.dai_name = "cs42l43-dp6",
 697				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 698				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 699				.init = asoc_sdw_cs42l43_spk_init,
 700				.rtd_init = asoc_sdw_cs42l43_spk_rtd_init,
 701				.controls = generic_spk_controls,
 702				.num_controls = ARRAY_SIZE(generic_spk_controls),
 703				.widgets = generic_spk_widgets,
 704				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
 705				.quirk = SOC_SDW_CODEC_SPKR | SOC_SDW_SIDECAR_AMPS,
 706			},
 707		},
 708		.dai_num = 4,
 709	},
 710	{
 711		.part_id = 0x4245,
 712		.name_prefix = "cs42l45",
 713		.dais = {
 714			{
 715				.direction = {true, false},
 716				.codec_name = "snd_soc_sdca.UAJ.1",
 717				.dai_name = "IT 41",
 718				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 719				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 720				.rtd_init = asoc_sdw_cs42l45_hs_rtd_init,
 721			},
 722			{
 723				.direction = {false, true},
 724				.codec_name = "snd_soc_sdca.SmartMic.0",
 725				.dai_name = "OT 113",
 726				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 727				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 728				.rtd_init = asoc_sdw_cs42l45_dmic_rtd_init,
 729			},
 730			{
 731				.direction = {false, true},
 732				.codec_name = "snd_soc_sdca.UAJ.1",
 733				.dai_name = "OT 36",
 734				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 735				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 736			},
 737		},
 738		.dai_num = 3,
 739		.auxs = {
 740			{
 741				.codec_name = "snd_soc_sdca.HID.2",
 742			},
 743		},
 744		.aux_num = 1,
 745	},
 746	{
 747		.part_id = 0xaaaa, /* generic codec mockup */
 748		.name_prefix = "sdw_mockup_mmulti-function",
 749		.version_id = 0,
 750		.dais = {
 751			{
 752				.direction = {true, true},
 753				.dai_name = "sdw-mockup-aif1",
 754				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 755				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 756			},
 757			{
 758				.direction = {true, false},
 759				.dai_name = "sdw-mockup-aif1",
 760				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 761				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
 762			},
 763			{
 764				.direction = {false, true},
 765				.dai_name = "sdw-mockup-aif1",
 766				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 767				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 768			},
 769		},
 770		.dai_num = 3,
 771	},
 772	{
 773		.part_id = 0xaa55, /* headset codec mockup */
 774		.name_prefix = "sdw_mockup_headset0",
 775		.version_id = 0,
 776		.dais = {
 777			{
 778				.direction = {true, true},
 779				.dai_name = "sdw-mockup-aif1",
 780				.dai_type = SOC_SDW_DAI_TYPE_JACK,
 781				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
 782			},
 783		},
 784		.dai_num = 1,
 785	},
 786	{
 787		.part_id = 0x55aa, /* amplifier mockup */
 788		.name_prefix = "sdw_mockup_amp1",
 789		.version_id = 0,
 790		.dais = {
 791			{
 792				.direction = {true, true},
 793				.dai_name = "sdw-mockup-aif1",
 794				.dai_type = SOC_SDW_DAI_TYPE_AMP,
 795				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
 796			},
 797		},
 798		.dai_num = 1,
 799	},
 800	{
 801		.part_id = 0x5555,
 802		.name_prefix = "sdw_mockup_mic0",
 803		.version_id = 0,
 804		.dais = {
 805			{
 806				.dai_name = "sdw-mockup-aif1",
 807				.direction = {false, true},
 808				.dai_type = SOC_SDW_DAI_TYPE_MIC,
 809				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
 810			},
 811		},
 812		.dai_num = 1,
 813	},
 814};
 815EXPORT_SYMBOL_NS(codec_info_list, "SND_SOC_SDW_UTILS");
 816
 817int asoc_sdw_get_codec_info_list_count(void)
 818{
 819	return ARRAY_SIZE(codec_info_list);
 820};
 821EXPORT_SYMBOL_NS(asoc_sdw_get_codec_info_list_count, "SND_SOC_SDW_UTILS");
 822
 823struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr)
 824{
 825	unsigned int part_id, sdw_version;
 826	int i;
 827
 828	part_id = SDW_PART_ID(adr);
 829	sdw_version = SDW_VERSION(adr);
 830	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
 831		/*
 832		 * A codec info is for all sdw version with the part id if
 833		 * version_id is not specified in the codec info.
 834		 */
 835		if (part_id == codec_info_list[i].part_id &&
 836		    (!codec_info_list[i].version_id ||
 837		     sdw_version == codec_info_list[i].version_id))
 838			return &codec_info_list[i];
 839
 840	return NULL;
 841}
 842EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_part, "SND_SOC_SDW_UTILS");
 843
 844static struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_sdw_id(const struct sdw_slave_id *id)
 845{
 846	int i;
 847
 848	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
 849		if (id->part_id == codec_info_list[i].part_id &&
 850		    (!codec_info_list[i].version_id ||
 851		     id->sdw_version == codec_info_list[i].version_id))
 852			return &codec_info_list[i];
 853
 854	return NULL;
 855}
 856
 857struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_acpi(const u8 *acpi_id)
 858{
 859	int i;
 860
 861	if (!acpi_id[0])
 862		return NULL;
 863
 864	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
 865		if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN))
 866			return &codec_info_list[i];
 867
 868	return NULL;
 869}
 870EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_acpi, "SND_SOC_SDW_UTILS");
 871
 872struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_dai(const char *dai_name, int *dai_index)
 873{
 874	int i, j;
 875
 876	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
 877		for (j = 0; j < codec_info_list[i].dai_num; j++) {
 878			if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) {
 879				*dai_index = j;
 880				return &codec_info_list[i];
 881			}
 882		}
 883	}
 884
 885	return NULL;
 886}
 887EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_dai, "SND_SOC_SDW_UTILS");
 888
 889static int asoc_sdw_find_codec_info_dai_index(const struct asoc_sdw_codec_info *codec_info,
 890					      const char *dai_name)
 891{
 892	int i;
 893
 894	for (i = 0; i < codec_info->dai_num; i++) {
 895		if (!strcmp(codec_info->dais[i].dai_name, dai_name))
 896			return i;
 897	}
 898
 899	return -ENOENT;
 900}
 901
 902int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd)
 903{
 904	struct snd_soc_card *card = rtd->card;
 905	struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
 906	struct asoc_sdw_codec_info *codec_info;
 907	struct snd_soc_dai *dai;
 908	struct sdw_slave *sdw_peripheral;
 909	const char *spk_components="";
 910	int dai_index;
 911	int ret;
 912	int i;
 913
 914	for_each_rtd_codec_dais(rtd, i, dai) {
 915		if (is_sdw_slave(dai->component->dev))
 916			sdw_peripheral = dev_to_sdw_dev(dai->component->dev);
 917		else if (dai->component->dev->parent && is_sdw_slave(dai->component->dev->parent))
 918			sdw_peripheral = dev_to_sdw_dev(dai->component->dev->parent);
 919		else
 920			continue;
 921
 922		codec_info = asoc_sdw_find_codec_info_sdw_id(&sdw_peripheral->id);
 923		if (!codec_info)
 924			return -EINVAL;
 925
 926		dai_index = asoc_sdw_find_codec_info_dai_index(codec_info, dai->name);
 927		WARN_ON(dai_index < 0);
 928
 929		/*
 930		 * A codec dai can be connected to different dai links for capture and playback,
 931		 * but we only need to call the rtd_init function once.
 932		 * The rtd_init for each codec dai is independent. So, the order of rtd_init
 933		 * doesn't matter.
 934		 */
 935		if (codec_info->dais[dai_index].rtd_init_done)
 936			continue;
 937
 938		dev_dbg(card->dev, "%#x/%s initializing for %s/%s\n",
 939			codec_info->part_id, codec_info->dais[dai_index].dai_name,
 940			dai->component->name, dai->name);
 941
 942		/*
 943		 * Add card controls and dapm widgets for the first codec dai.
 944		 * The controls and widgets will be used for all codec dais.
 945		 */
 946
 947		if (i > 0)
 948			goto skip_add_controls_widgets;
 949
 950		if (codec_info->dais[dai_index].controls) {
 951			ret = snd_soc_add_card_controls(card, codec_info->dais[dai_index].controls,
 952							codec_info->dais[dai_index].num_controls);
 953			if (ret) {
 954				dev_err(card->dev, "%#x controls addition failed: %d\n",
 955					codec_info->part_id, ret);
 956				return ret;
 957			}
 958		}
 959		if (codec_info->dais[dai_index].widgets) {
 960			ret = snd_soc_dapm_new_controls(dapm,
 961							codec_info->dais[dai_index].widgets,
 962							codec_info->dais[dai_index].num_widgets);
 963			if (ret) {
 964				dev_err(card->dev, "%#x widgets addition failed: %d\n",
 965					codec_info->part_id, ret);
 966				return ret;
 967			}
 968		}
 969
 970skip_add_controls_widgets:
 971		if (codec_info->dais[dai_index].rtd_init) {
 972			ret = codec_info->dais[dai_index].rtd_init(rtd, dai);
 973			if (ret)
 974				return ret;
 975		}
 976
 977		/* Generate the spk component string for card->components string */
 978		if (codec_info->dais[dai_index].dai_type == SOC_SDW_DAI_TYPE_AMP &&
 979		    codec_info->dais[dai_index].component_name) {
 980			if (strlen (spk_components) == 0)
 981				spk_components =
 982					devm_kasprintf(card->dev, GFP_KERNEL, "%s",
 983						       codec_info->dais[dai_index].component_name);
 984			else
 985				/* Append component name to spk_components */
 986				spk_components =
 987					devm_kasprintf(card->dev, GFP_KERNEL,
 988						       "%s+%s", spk_components,
 989						       codec_info->dais[dai_index].component_name);
 990		}
 991
 992		codec_info->dais[dai_index].rtd_init_done = true;
 993
 994	}
 995
 996	if (strlen (spk_components) > 0) {
 997		/* Update card components for speaker components */
 998		card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s spk:%s",
 999						  card->components, spk_components);
1000		if (!card->components)
1001			return -ENOMEM;
1002	}
1003
1004	return 0;
1005}
1006EXPORT_SYMBOL_NS(asoc_sdw_rtd_init, "SND_SOC_SDW_UTILS");
1007
1008/* these wrappers are only needed to avoid typecast compilation errors */
1009int asoc_sdw_startup(struct snd_pcm_substream *substream)
1010{
1011	return sdw_startup_stream(substream);
1012}
1013EXPORT_SYMBOL_NS(asoc_sdw_startup, "SND_SOC_SDW_UTILS");
1014
1015int asoc_sdw_prepare(struct snd_pcm_substream *substream)
1016{
1017	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
1018	struct sdw_stream_runtime *sdw_stream;
1019	struct snd_soc_dai *dai;
1020
1021	/* Find stream from first CPU DAI */
1022	dai = snd_soc_rtd_to_cpu(rtd, 0);
1023
1024	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
1025	if (IS_ERR(sdw_stream)) {
1026		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
1027		return PTR_ERR(sdw_stream);
1028	}
1029
1030	return sdw_prepare_stream(sdw_stream);
1031}
1032EXPORT_SYMBOL_NS(asoc_sdw_prepare, "SND_SOC_SDW_UTILS");
1033
1034int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd)
1035{
1036	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
1037	struct sdw_stream_runtime *sdw_stream;
1038	struct snd_soc_dai *dai;
1039	int ret;
1040
1041	/* Find stream from first CPU DAI */
1042	dai = snd_soc_rtd_to_cpu(rtd, 0);
1043
1044	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
1045	if (IS_ERR(sdw_stream)) {
1046		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
1047		return PTR_ERR(sdw_stream);
1048	}
1049
1050	switch (cmd) {
1051	case SNDRV_PCM_TRIGGER_START:
1052	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
1053	case SNDRV_PCM_TRIGGER_RESUME:
1054		ret = sdw_enable_stream(sdw_stream);
1055		break;
1056
1057	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
1058	case SNDRV_PCM_TRIGGER_SUSPEND:
1059	case SNDRV_PCM_TRIGGER_STOP:
1060		ret = sdw_disable_stream(sdw_stream);
1061		break;
1062	default:
1063		ret = -EINVAL;
1064		break;
1065	}
1066
1067	if (ret)
1068		dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret);
1069
1070	return ret;
1071}
1072EXPORT_SYMBOL_NS(asoc_sdw_trigger, "SND_SOC_SDW_UTILS");
1073
1074int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
1075		       struct snd_pcm_hw_params *params)
1076{
1077	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
1078	struct snd_soc_dai_link_ch_map *ch_maps;
1079	int ch = params_channels(params);
1080	unsigned int ch_mask;
1081	int num_codecs;
1082	int step;
1083	int i;
1084
1085	if (!rtd->dai_link->ch_maps)
1086		return 0;
1087
1088	/* Identical data will be sent to all codecs in playback */
1089	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
1090		ch_mask = GENMASK(ch - 1, 0);
1091		step = 0;
1092	} else {
1093		num_codecs = rtd->dai_link->num_codecs;
1094
1095		if (ch < num_codecs || ch % num_codecs != 0) {
1096			dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n",
1097				ch, num_codecs);
1098			return -EINVAL;
1099		}
1100
1101		ch_mask = GENMASK(ch / num_codecs - 1, 0);
1102		step = hweight_long(ch_mask);
1103	}
1104
1105	/*
1106	 * The captured data will be combined from each cpu DAI if the dai
1107	 * link has more than one codec DAIs. Set codec channel mask and
1108	 * ASoC will set the corresponding channel numbers for each cpu dai.
1109	 */
1110	for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
1111		ch_maps->ch_mask = ch_mask << (i * step);
1112
1113	return 0;
1114}
1115EXPORT_SYMBOL_NS(asoc_sdw_hw_params, "SND_SOC_SDW_UTILS");
1116
1117int asoc_sdw_hw_free(struct snd_pcm_substream *substream)
1118{
1119	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
1120	struct sdw_stream_runtime *sdw_stream;
1121	struct snd_soc_dai *dai;
1122
1123	/* Find stream from first CPU DAI */
1124	dai = snd_soc_rtd_to_cpu(rtd, 0);
1125
1126	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
1127	if (IS_ERR(sdw_stream)) {
1128		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
1129		return PTR_ERR(sdw_stream);
1130	}
1131
1132	return sdw_deprepare_stream(sdw_stream);
1133}
1134EXPORT_SYMBOL_NS(asoc_sdw_hw_free, "SND_SOC_SDW_UTILS");
1135
1136void asoc_sdw_shutdown(struct snd_pcm_substream *substream)
1137{
1138	sdw_shutdown_stream(substream);
1139}
1140EXPORT_SYMBOL_NS(asoc_sdw_shutdown, "SND_SOC_SDW_UTILS");
1141
1142static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_link,
1143				      unsigned int sdw_version,
1144				      unsigned int mfg_id,
1145				      unsigned int part_id,
1146				      unsigned int class_id,
1147				      int index_in_link)
1148{
1149	int i;
1150
1151	for (i = 0; i < adr_link->num_adr; i++) {
1152		unsigned int sdw1_version, mfg1_id, part1_id, class1_id;
1153		u64 adr;
1154
1155		/* skip itself */
1156		if (i == index_in_link)
1157			continue;
1158
1159		adr = adr_link->adr_d[i].adr;
1160
1161		sdw1_version = SDW_VERSION(adr);
1162		mfg1_id = SDW_MFG_ID(adr);
1163		part1_id = SDW_PART_ID(adr);
1164		class1_id = SDW_CLASS_ID(adr);
1165
1166		if (sdw_version == sdw1_version &&
1167		    mfg_id == mfg1_id &&
1168		    part_id == part1_id &&
1169		    class_id == class1_id)
1170			return false;
1171	}
1172
1173	return true;
1174}
1175
1176static const char *_asoc_sdw_get_codec_name(struct device *dev,
1177					    const struct snd_soc_acpi_link_adr *adr_link,
1178					    int adr_index)
1179{
1180	u64 adr = adr_link->adr_d[adr_index].adr;
1181	unsigned int sdw_version = SDW_VERSION(adr);
1182	unsigned int link_id = SDW_DISCO_LINK_ID(adr);
1183	unsigned int unique_id = SDW_UNIQUE_ID(adr);
1184	unsigned int mfg_id = SDW_MFG_ID(adr);
1185	unsigned int part_id = SDW_PART_ID(adr);
1186	unsigned int class_id = SDW_CLASS_ID(adr);
1187
1188	if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id,
1189				      class_id, adr_index))
1190		return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x",
1191				      link_id, mfg_id, part_id, class_id);
1192
1193	return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x",
1194			      link_id, mfg_id, part_id, class_id, unique_id);
1195}
1196
1197const char *asoc_sdw_get_codec_name(struct device *dev,
1198				    const struct asoc_sdw_dai_info *dai_info,
1199				    const struct snd_soc_acpi_link_adr *adr_link,
1200				    int adr_index)
1201{
1202	if (dai_info->codec_name)
1203		return devm_kstrdup(dev, dai_info->codec_name, GFP_KERNEL);
1204
1205	return _asoc_sdw_get_codec_name(dev, adr_link, adr_index);
1206}
1207EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, "SND_SOC_SDW_UTILS");
1208
1209/* helper to get the link that the codec DAI is used */
1210struct snd_soc_dai_link *asoc_sdw_mc_find_codec_dai_used(struct snd_soc_card *card,
1211							 const char *dai_name)
1212{
1213	struct snd_soc_dai_link *dai_link;
1214	int i;
1215	int j;
1216
1217	for_each_card_prelinks(card, i, dai_link) {
1218		for (j = 0; j < dai_link->num_codecs; j++) {
1219			/* Check each codec in a link */
1220			if (!strcmp(dai_link->codecs[j].dai_name, dai_name))
1221				return dai_link;
1222		}
1223	}
1224	return NULL;
1225}
1226EXPORT_SYMBOL_NS(asoc_sdw_mc_find_codec_dai_used, "SND_SOC_SDW_UTILS");
1227
1228void asoc_sdw_mc_dailink_exit_loop(struct snd_soc_card *card)
1229{
1230	struct snd_soc_dai_link *dai_link;
1231	struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
1232	int ret;
1233	int i, j;
1234
1235	for (i = 0; i < ctx->codec_info_list_count; i++) {
1236		for (j = 0; j < codec_info_list[i].dai_num; j++) {
1237			codec_info_list[i].dais[j].rtd_init_done = false;
1238			/* Check each dai in codec_info_lis to see if it is used in the link */
1239			if (!codec_info_list[i].dais[j].exit)
1240				continue;
1241			/*
1242			 * We don't need to call .exit function if there is no matched
1243			 * dai link found.
1244			 */
1245			dai_link = asoc_sdw_mc_find_codec_dai_used(card,
1246							  codec_info_list[i].dais[j].dai_name);
1247			if (dai_link) {
1248				/* Do the .exit function if the codec dai is used in the link */
1249				ret = codec_info_list[i].dais[j].exit(card, dai_link);
1250				if (ret)
1251					dev_warn(card->dev,
1252						 "codec exit failed %d\n",
1253						 ret);
1254				break;
1255			}
1256		}
1257	}
1258}
1259EXPORT_SYMBOL_NS(asoc_sdw_mc_dailink_exit_loop, "SND_SOC_SDW_UTILS");
1260
1261int asoc_sdw_card_late_probe(struct snd_soc_card *card)
1262{
1263	int ret = 0;
1264	int i;
1265
1266	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
1267		if (codec_info_list[i].codec_card_late_probe) {
1268			ret = codec_info_list[i].codec_card_late_probe(card);
1269			if (ret < 0)
1270				return ret;
1271		}
1272	}
1273	return ret;
1274}
1275EXPORT_SYMBOL_NS(asoc_sdw_card_late_probe, "SND_SOC_SDW_UTILS");
1276
1277void asoc_sdw_init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
1278			    int *be_id, char *name, int playback, int capture,
1279			    struct snd_soc_dai_link_component *cpus, int cpus_num,
1280			    struct snd_soc_dai_link_component *platform_component,
1281			    int num_platforms, struct snd_soc_dai_link_component *codecs,
1282			    int codecs_num, int no_pcm,
1283			    int (*init)(struct snd_soc_pcm_runtime *rtd),
1284			    const struct snd_soc_ops *ops)
1285{
1286	dev_dbg(dev, "create dai link %s, id %d\n", name, *be_id);
1287	dai_links->id = (*be_id)++;
1288	dai_links->name = name;
1289	dai_links->stream_name = name;
1290	dai_links->platforms = platform_component;
1291	dai_links->num_platforms = num_platforms;
1292	dai_links->no_pcm = no_pcm;
1293	dai_links->cpus = cpus;
1294	dai_links->num_cpus = cpus_num;
1295	dai_links->codecs = codecs;
1296	dai_links->num_codecs = codecs_num;
1297	dai_links->playback_only =  playback && !capture;
1298	dai_links->capture_only  = !playback &&  capture;
1299	dai_links->init = init;
1300	dai_links->ops = ops;
1301}
1302EXPORT_SYMBOL_NS(asoc_sdw_init_dai_link, "SND_SOC_SDW_UTILS");
1303
1304int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
1305				  int *be_id, char *name, int playback, int capture,
1306				  const char *cpu_dai_name, const char *platform_comp_name,
1307				  const char *codec_name, const char *codec_dai_name,
1308				  int no_pcm, int (*init)(struct snd_soc_pcm_runtime *rtd),
1309				  const struct snd_soc_ops *ops)
1310{
1311	struct snd_soc_dai_link_component *dlc;
1312
1313	/* Allocate three DLCs one for the CPU, one for platform and one for the CODEC */
1314	dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL);
1315	if (!dlc || !name || !cpu_dai_name || !platform_comp_name || !codec_name || !codec_dai_name)
1316		return -ENOMEM;
1317
1318	dlc[0].dai_name = cpu_dai_name;
1319	dlc[1].name = platform_comp_name;
1320
1321	dlc[2].name = codec_name;
1322	dlc[2].dai_name = codec_dai_name;
1323
1324	asoc_sdw_init_dai_link(dev, dai_links, be_id, name, playback, capture,
1325			       &dlc[0], 1, &dlc[1], 1, &dlc[2], 1,
1326			       no_pcm, init, ops);
1327
1328	return 0;
1329}
1330EXPORT_SYMBOL_NS(asoc_sdw_init_simple_dai_link, "SND_SOC_SDW_UTILS");
1331
1332int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card,
1333				 int *num_devs, int *num_ends, int *num_aux)
1334{
1335	struct device *dev = card->dev;
1336	struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
1337	struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
1338	const struct snd_soc_acpi_link_adr *adr_link;
1339	int i;
1340
1341	for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
1342		*num_devs += adr_link->num_adr;
1343
1344		for (i = 0; i < adr_link->num_adr; i++) {
1345			const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i];
1346			struct asoc_sdw_codec_info *codec_info;
1347
1348			*num_ends += adr_dev->num_endpoints;
1349
1350			codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr);
1351			if (!codec_info)
1352				return -EINVAL;
1353
1354			*num_aux += codec_info->aux_num;
1355		}
1356	}
1357
1358	dev_dbg(dev, "Found %d devices with %d endpoints\n", *num_devs, *num_ends);
1359
1360	return 0;
1361}
1362EXPORT_SYMBOL_NS(asoc_sdw_count_sdw_endpoints, "SND_SOC_SDW_UTILS");
1363
1364struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks,
1365					       const struct snd_soc_acpi_endpoint *new)
1366{
1367	while (dailinks->initialised) {
1368		if (new->aggregated && dailinks->group_id == new->group_id)
1369			return dailinks;
1370
1371		dailinks++;
1372	}
1373
1374	INIT_LIST_HEAD(&dailinks->endpoints);
1375	dailinks->group_id = new->group_id;
1376	dailinks->initialised = true;
1377
1378	return dailinks;
1379}
1380EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, "SND_SOC_SDW_UTILS");
1381
1382int asoc_sdw_get_dai_type(u32 type)
1383{
1384	switch (type) {
1385	case SDCA_FUNCTION_TYPE_SMART_AMP:
1386	case SDCA_FUNCTION_TYPE_SIMPLE_AMP:
1387		return SOC_SDW_DAI_TYPE_AMP;
1388	case SDCA_FUNCTION_TYPE_SMART_MIC:
1389	case SDCA_FUNCTION_TYPE_SIMPLE_MIC:
1390	case SDCA_FUNCTION_TYPE_SPEAKER_MIC:
1391		return SOC_SDW_DAI_TYPE_MIC;
1392	case SDCA_FUNCTION_TYPE_UAJ:
1393	case SDCA_FUNCTION_TYPE_RJ:
1394	case SDCA_FUNCTION_TYPE_SIMPLE_JACK:
1395		return SOC_SDW_DAI_TYPE_JACK;
1396	default:
1397		return -EINVAL;
1398	}
1399}
1400EXPORT_SYMBOL_NS(asoc_sdw_get_dai_type, "SND_SOC_SDW_UTILS");
1401
1402/*
1403 * Check if the SDCA endpoint is present by the SDW peripheral
1404 *
1405 * @dev: Device pointer
1406 * @codec_info: Codec info pointer
1407 * @adr_link: ACPI link address
1408 * @adr_index: Index of the ACPI link address
1409 * @end_index: Index of the endpoint
1410 *
1411 * Return: 1 if the endpoint is present,
1412 *	   0 if the endpoint is not present,
1413 *	   negative error code.
1414 */
1415
1416static int is_sdca_endpoint_present(struct device *dev,
1417				    struct asoc_sdw_codec_info *codec_info,
1418				    const struct snd_soc_acpi_link_adr *adr_link,
1419				    int adr_index, int end_index)
1420{
1421	const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index];
1422	const struct snd_soc_acpi_endpoint *adr_end;
1423	const struct asoc_sdw_dai_info *dai_info;
1424	struct sdw_slave *slave;
1425	struct device *sdw_dev;
1426	const char *sdw_codec_name;
1427	int ret, i;
1428
1429	adr_end = &adr_dev->endpoints[end_index];
1430	dai_info = &codec_info->dais[adr_end->num];
1431
1432	sdw_codec_name = _asoc_sdw_get_codec_name(dev, adr_link, adr_index);
1433	if (!sdw_codec_name)
1434		return -ENOMEM;
1435
1436	sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name);
1437	if (!sdw_dev) {
1438		dev_err(dev, "codec %s not found\n", sdw_codec_name);
1439		return -EINVAL;
1440	}
1441
1442	slave = dev_to_sdw_dev(sdw_dev);
1443
1444	/* Make sure BIOS provides SDCA properties */
1445	if (!slave->sdca_data.interface_revision) {
1446		dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n");
1447		ret = 1;
1448		goto put_device;
1449	}
1450
1451	for (i = 0; i < slave->sdca_data.num_functions; i++) {
1452		int dai_type = asoc_sdw_get_dai_type(slave->sdca_data.function[i].type);
1453
1454		if (dai_type == dai_info->dai_type) {
1455			dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n",
1456				dai_type, slave->sdca_data.function[i].name);
1457			ret = 1;
1458			goto put_device;
1459		}
1460	}
1461
1462	dev_dbg(&slave->dev,
1463		"SDCA device function for DAI type %d not supported, skip endpoint\n",
1464		dai_info->dai_type);
1465
1466	ret = 0;
1467
1468put_device:
1469	put_device(sdw_dev);
1470	return ret;
1471}
1472
1473int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
1474				 struct snd_soc_aux_dev *soc_aux,
1475				 struct asoc_sdw_dailink *soc_dais,
1476				 struct asoc_sdw_endpoint *soc_ends,
1477				 int *num_devs)
1478{
1479	struct device *dev = card->dev;
1480	struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
1481	struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
1482	struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
1483	const struct snd_soc_acpi_link_adr *adr_link;
1484	struct asoc_sdw_endpoint *soc_end = soc_ends;
1485	int num_dais = 0;
1486	int i, j;
1487	int ret;
1488
1489	for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
1490		int num_link_dailinks = 0;
1491
1492		if (!is_power_of_2(adr_link->mask)) {
1493			dev_err(dev, "link with multiple mask bits: 0x%x\n",
1494				adr_link->mask);
1495			return -EINVAL;
1496		}
1497
1498		for (i = 0; i < adr_link->num_adr; i++) {
1499			const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i];
1500			struct asoc_sdw_codec_info *codec_info;
1501			const char *codec_name;
1502			bool check_sdca = false;
1503
1504			if (!adr_dev->name_prefix) {
1505				dev_err(dev, "codec 0x%llx does not have a name prefix\n",
1506					adr_dev->adr);
1507				return -EINVAL;
1508			}
1509
1510			codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr);
1511			if (!codec_info)
1512				return -EINVAL;
1513
1514			for (j = 0; j < codec_info->aux_num; j++) {
1515				soc_aux->dlc.name = codec_info->auxs[j].codec_name;
1516				soc_aux++;
1517			}
1518
1519			ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic;
1520
1521			if (codec_info->count_sidecar && codec_info->add_sidecar) {
1522				ret = codec_info->count_sidecar(card, &num_dais, num_devs);
1523				if (ret)
1524					return ret;
1525
1526				soc_end->include_sidecar = true;
1527			}
1528
1529			if (SDW_CLASS_ID(adr_dev->adr) && adr_dev->num_endpoints > 1)
1530				check_sdca = true;
1531
1532			for (j = 0; j < adr_dev->num_endpoints; j++) {
1533				const struct snd_soc_acpi_endpoint *adr_end;
1534				const struct asoc_sdw_dai_info *dai_info;
1535				struct asoc_sdw_dailink *soc_dai;
1536				int stream;
1537
1538				adr_end = &adr_dev->endpoints[j];
1539				dai_info = &codec_info->dais[adr_end->num];
1540				soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end);
1541
1542				/*
1543				 * quirk should have higher priority than the sdca properties
1544				 * in the BIOS. We can't always check the DAI quirk because we
1545				 * will set the mc_quirk when the BIOS doesn't provide the right
1546				 * information. The endpoint will be skipped if the dai_info->
1547				 * quirk_exclude and mc_quirk are both not set if we always skip
1548				 * the endpoint according to the quirk information. We need to
1549				 * keep the endpoint if it is present in the BIOS. So, only
1550				 * check the DAI quirk when the mc_quirk is set or SDCA endpoint
1551				 * present check is not needed.
1552				 */
1553				if (dai_info->quirk & ctx->mc_quirk || !check_sdca) {
1554					/*
1555					 * Check the endpoint if a matching quirk is set or SDCA
1556					 * endpoint check is not necessary
1557					 */
1558					if (dai_info->quirk &&
1559					    !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk))) {
1560						(*num_devs)--;
1561						continue;
1562					}
1563				} else {
1564					/* Check SDCA codec endpoint if there is no matching quirk */
1565					ret = is_sdca_endpoint_present(dev, codec_info, adr_link, i, j);
1566					if (ret < 0)
1567						return ret;
1568
1569					/* The endpoint is not present, skip */
1570					if (!ret) {
1571						(*num_devs)--;
1572						continue;
1573					}
1574				}
1575
1576				dev_dbg(dev,
1577					"Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n",
1578					ffs(adr_link->mask) - 1, adr_dev->adr,
1579					adr_end->num, dai_info->dai_type,
1580					dai_info->direction[SNDRV_PCM_STREAM_PLAYBACK] ? 'P' : '-',
1581					dai_info->direction[SNDRV_PCM_STREAM_CAPTURE] ? 'C' : '-',
1582					adr_end->aggregated ? "group" : "solo",
1583					adr_end->group_id);
1584
1585				if (adr_end->num >= codec_info->dai_num) {
1586					dev_err(dev,
1587						"%d is too many endpoints for codec: 0x%x\n",
1588						adr_end->num, codec_info->part_id);
1589					return -EINVAL;
1590				}
1591
1592				for_each_pcm_streams(stream) {
1593					if (dai_info->direction[stream] &&
1594					    dai_info->dailink[stream] < 0) {
1595						dev_err(dev,
1596							"Invalid dailink id %d for codec: 0x%x\n",
1597							dai_info->dailink[stream],
1598							codec_info->part_id);
1599						return -EINVAL;
1600					}
1601
1602					if (dai_info->direction[stream]) {
1603						num_dais += !soc_dai->num_devs[stream];
1604						soc_dai->num_devs[stream]++;
1605						soc_dai->link_mask[stream] |= adr_link->mask;
1606					}
1607				}
1608
1609				num_link_dailinks += !!list_empty(&soc_dai->endpoints);
1610				list_add_tail(&soc_end->list, &soc_dai->endpoints);
1611
1612				codec_name = asoc_sdw_get_codec_name(dev, dai_info,
1613								     adr_link, i);
1614				if (!codec_name)
1615					return -ENOMEM;
1616
1617				dev_dbg(dev, "Adding prefix %s for %s\n",
1618					adr_dev->name_prefix, codec_name);
1619
1620				soc_end->name_prefix = adr_dev->name_prefix;
1621
1622				soc_end->link_mask = adr_link->mask;
1623				soc_end->codec_name = codec_name;
1624				soc_end->codec_info = codec_info;
1625				soc_end->dai_info = dai_info;
1626				soc_end++;
1627			}
1628		}
1629
1630		ctx->append_dai_type |= (num_link_dailinks > 1);
1631	}
1632
1633	return num_dais;
1634}
1635EXPORT_SYMBOL_NS(asoc_sdw_parse_sdw_endpoints, "SND_SOC_SDW_UTILS");
1636
1637MODULE_LICENSE("GPL");
1638MODULE_DESCRIPTION("SoundWire ASoC helpers");
~/ Read-only file preview.