brintos

brintos / linux-shallow public Read only

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