brintos

brintos / linux-shallow public Read only

0
0
Text · 12.6 KiB · 306854f Raw
505 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// Copyright(c) 2024 Advanced Micro Devices, Inc.3 4/*5 *  acp-sdw-sof-mach - ASoC Machine driver for AMD SoundWire platforms6 */7 8#include <linux/bitmap.h>9#include <linux/device.h>10#include <linux/dmi.h>11#include <linux/module.h>12#include <linux/soundwire/sdw.h>13#include <linux/soundwire/sdw_type.h>14#include <sound/soc.h>15#include <sound/soc-acpi.h>16#include "soc_amd_sdw_common.h"17#include "../../codecs/rt711.h"18 19static unsigned long sof_sdw_quirk = RT711_JD1;20static int quirk_override = -1;21module_param_named(quirk, quirk_override, int, 0444);22MODULE_PARM_DESC(quirk, "Board-specific quirk override");23 24static void log_quirks(struct device *dev)25{26	if (SOC_JACK_JDSRC(sof_sdw_quirk))27		dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",28			SOC_JACK_JDSRC(sof_sdw_quirk));29	if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC)30		dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");31}32 33static int sof_sdw_quirk_cb(const struct dmi_system_id *id)34{35	sof_sdw_quirk = (unsigned long)id->driver_data;36	return 1;37}38 39static const struct dmi_system_id sof_sdw_quirk_table[] = {40	{41		.callback = sof_sdw_quirk_cb,42		.matches = {43			DMI_MATCH(DMI_SYS_VENDOR, "AMD"),44			DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"),45		},46		.driver_data = (void *)RT711_JD2,47	},48	{}49};50 51static struct snd_soc_dai_link_component platform_component[] = {52	{53		/* name might be overridden during probe */54		.name = "0000:04:00.5",55	}56};57 58static const struct snd_soc_ops sdw_ops = {59	.startup = asoc_sdw_startup,60	.prepare = asoc_sdw_prepare,61	.trigger = asoc_sdw_trigger,62	.hw_params = asoc_sdw_hw_params,63	.hw_free = asoc_sdw_hw_free,64	.shutdown = asoc_sdw_shutdown,65};66 67static int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev)68{69	switch (sdw_link_id) {70	case AMD_SDW0:71		switch (be_id) {72		case SOC_SDW_JACK_OUT_DAI_ID:73			*cpu_pin_id = ACP63_SW0_AUDIO0_TX;74			break;75		case SOC_SDW_JACK_IN_DAI_ID:76			*cpu_pin_id = ACP63_SW0_AUDIO0_RX;77			break;78		case SOC_SDW_AMP_OUT_DAI_ID:79			*cpu_pin_id = ACP63_SW0_AUDIO1_TX;80			break;81		case SOC_SDW_AMP_IN_DAI_ID:82			*cpu_pin_id = ACP63_SW0_AUDIO1_RX;83			break;84		case SOC_SDW_DMIC_DAI_ID:85			*cpu_pin_id = ACP63_SW0_AUDIO2_RX;86			break;87		default:88			dev_err(dev, "Invalid be id:%d\n", be_id);89			return -EINVAL;90		}91		break;92	case AMD_SDW1:93		switch (be_id) {94		case SOC_SDW_JACK_OUT_DAI_ID:95		case SOC_SDW_AMP_OUT_DAI_ID:96			*cpu_pin_id = ACP63_SW1_AUDIO0_TX;97			break;98		case SOC_SDW_JACK_IN_DAI_ID:99		case SOC_SDW_AMP_IN_DAI_ID:100		case SOC_SDW_DMIC_DAI_ID:101			*cpu_pin_id = ACP63_SW1_AUDIO0_RX;102			break;103		default:104			dev_err(dev, "invalid be_id:%d\n", be_id);105			return -EINVAL;106		}107		break;108	default:109		dev_err(dev, "Invalid link id:%d\n", sdw_link_id);110		return -EINVAL;111	}112	return 0;113}114 115static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};116 117static int create_sdw_dailink(struct snd_soc_card *card,118			      struct asoc_sdw_dailink *sof_dai,119			      struct snd_soc_dai_link **dai_links,120			      int *be_id, struct snd_soc_codec_conf **codec_conf)121{122	struct device *dev = card->dev;123	struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);124	struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;125	struct asoc_sdw_endpoint *sof_end;126	int cpu_pin_id;127	int stream;128	int ret;129 130	list_for_each_entry(sof_end, &sof_dai->endpoints, list) {131		if (sof_end->name_prefix) {132			(*codec_conf)->dlc.name = sof_end->codec_name;133			(*codec_conf)->name_prefix = sof_end->name_prefix;134			(*codec_conf)++;135		}136 137		if (sof_end->include_sidecar) {138			ret = sof_end->codec_info->add_sidecar(card, dai_links, codec_conf);139			if (ret)140				return ret;141		}142	}143 144	for_each_pcm_streams(stream) {145		static const char * const sdw_stream_name[] = {146			"SDW%d-PIN%d-PLAYBACK",147			"SDW%d-PIN%d-CAPTURE",148			"SDW%d-PIN%d-PLAYBACK-%s",149			"SDW%d-PIN%d-CAPTURE-%s",150		};151		struct snd_soc_dai_link_ch_map *codec_maps;152		struct snd_soc_dai_link_component *codecs;153		struct snd_soc_dai_link_component *cpus;154		int num_cpus = hweight32(sof_dai->link_mask[stream]);155		int num_codecs = sof_dai->num_devs[stream];156		int playback, capture;157		int i = 0, j = 0;158		char *name;159 160		if (!sof_dai->num_devs[stream])161			continue;162 163		sof_end = list_first_entry(&sof_dai->endpoints,164					   struct asoc_sdw_endpoint, list);165 166		*be_id = sof_end->dai_info->dailink[stream];167		if (*be_id < 0) {168			dev_err(dev, "Invalid dailink id %d\n", *be_id);169			return -EINVAL;170		}171 172		switch (amd_ctx->acp_rev) {173		case ACP63_PCI_REV:174			ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1),175						   *be_id, &cpu_pin_id, dev);176			if (ret)177				return ret;178			break;179		default:180			return -EINVAL;181		}182		/* create stream name according to first link id */183		if (ctx->append_dai_type) {184			name = devm_kasprintf(dev, GFP_KERNEL,185					      sdw_stream_name[stream + 2],186					      ffs(sof_end->link_mask) - 1,187					      cpu_pin_id,188					      type_strings[sof_end->dai_info->dai_type]);189		} else {190			name = devm_kasprintf(dev, GFP_KERNEL,191					      sdw_stream_name[stream],192					      ffs(sof_end->link_mask) - 1,193					      cpu_pin_id);194		}195		if (!name)196			return -ENOMEM;197 198		cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);199		if (!cpus)200			return -ENOMEM;201 202		codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);203		if (!codecs)204			return -ENOMEM;205 206		codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);207		if (!codec_maps)208			return -ENOMEM;209 210		list_for_each_entry(sof_end, &sof_dai->endpoints, list) {211			if (!sof_end->dai_info->direction[stream])212				continue;213 214			int link_num = ffs(sof_end->link_mask) - 1;215 216			cpus[i].dai_name = devm_kasprintf(dev, GFP_KERNEL,217							  "SDW%d Pin%d",218							  link_num, cpu_pin_id);219			dev_dbg(dev, "cpu[%d].dai_name:%s\n", i, cpus[i].dai_name);220			if (!cpus[i].dai_name)221				return -ENOMEM;222 223			codec_maps[j].cpu = i;224			codec_maps[j].codec = j;225 226			codecs[j].name = sof_end->codec_name;227			codecs[j].dai_name = sof_end->dai_info->dai_name;228			j++;229		}230 231		WARN_ON(j != num_codecs);232 233		playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);234		capture = (stream == SNDRV_PCM_STREAM_CAPTURE);235 236		asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,237				       cpus, num_cpus, platform_component,238				       ARRAY_SIZE(platform_component), codecs, num_codecs,239				       asoc_sdw_rtd_init, &sdw_ops);240 241		/*242		 * SoundWire DAILINKs use 'stream' functions and Bank Switch operations243		 * based on wait_for_completion(), tag them as 'nonatomic'.244		 */245		(*dai_links)->nonatomic = true;246		(*dai_links)->ch_maps = codec_maps;247 248		list_for_each_entry(sof_end, &sof_dai->endpoints, list) {249			if (sof_end->dai_info->init)250				sof_end->dai_info->init(card, *dai_links,251							sof_end->codec_info,252							playback);253		}254 255		(*dai_links)++;256	}257 258	return 0;259}260 261static int create_sdw_dailinks(struct snd_soc_card *card,262			       struct snd_soc_dai_link **dai_links, int *be_id,263			       struct asoc_sdw_dailink *sof_dais,264			       struct snd_soc_codec_conf **codec_conf)265{266	int ret;267 268	/* generate DAI links by each sdw link */269	while (sof_dais->initialised) {270		int current_be_id;271 272		ret = create_sdw_dailink(card, sof_dais, dai_links,273					 &current_be_id, codec_conf);274		if (ret)275			return ret;276 277		/* Update the be_id to match the highest ID used for SDW link */278		if (*be_id < current_be_id)279			*be_id = current_be_id;280 281		sof_dais++;282	}283 284	return 0;285}286 287static int create_dmic_dailinks(struct snd_soc_card *card,288				struct snd_soc_dai_link **dai_links, int *be_id)289{290	struct device *dev = card->dev;291	int ret;292 293	ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",294					    0, 1, // DMIC only supports capture295					    "acp-sof-dmic", platform_component->name,296					    ARRAY_SIZE(platform_component),297					    "dmic-codec", "dmic-hifi",298					    asoc_sdw_dmic_init, NULL);299	if (ret)300		return ret;301 302	(*dai_links)++;303 304	return 0;305}306 307static int sof_card_dai_links_create(struct snd_soc_card *card)308{309	struct device *dev = card->dev;310	struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);311	int sdw_be_num = 0, dmic_num = 0;312	struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);313	struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;314	struct snd_soc_codec_conf *codec_conf;315	struct asoc_sdw_endpoint *sof_ends;316	struct asoc_sdw_dailink *sof_dais;317	struct snd_soc_dai_link *dai_links;318	int num_devs = 0;319	int num_ends = 0;320	int num_links;321	int be_id = 0;322	int ret;323 324	ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends);325	if (ret < 0) {326		dev_err(dev, "failed to count devices/endpoints: %d\n", ret);327		return ret;328	}329 330	/* One per DAI link, worst case is a DAI link for every endpoint */331	sof_dais = kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL);332	if (!sof_dais)333		return -ENOMEM;334 335	/* One per endpoint, ie. each DAI on each codec/amp */336	sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);337	if (!sof_ends) {338		ret = -ENOMEM;339		goto err_dai;340	}341 342	ret = asoc_sdw_parse_sdw_endpoints(card, sof_dais, sof_ends, &num_devs);343	if (ret < 0)344		goto err_end;345 346	sdw_be_num = ret;347 348	/* enable dmic */349	if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)350		dmic_num = 1;351 352	dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);353 354	codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);355	if (!codec_conf) {356		ret = -ENOMEM;357		goto err_end;358	}359 360	/* allocate BE dailinks */361	num_links = sdw_be_num + dmic_num;362	dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);363	if (!dai_links) {364		ret = -ENOMEM;365	goto err_end;366	}367 368	card->codec_conf = codec_conf;369	card->num_configs = num_devs;370	card->dai_link = dai_links;371	card->num_links = num_links;372 373	/* SDW */374	if (sdw_be_num) {375		ret = create_sdw_dailinks(card, &dai_links, &be_id,376					  sof_dais, &codec_conf);377		if (ret)378			goto err_end;379	}380 381	/* dmic */382	if (dmic_num > 0) {383		if (ctx->ignore_internal_dmic) {384			dev_warn(dev, "Ignoring ACP DMIC\n");385		} else {386			ret = create_dmic_dailinks(card, &dai_links, &be_id);387			if (ret)388				goto err_end;389		}390	}391 392	WARN_ON(codec_conf != card->codec_conf + card->num_configs);393	WARN_ON(dai_links != card->dai_link + card->num_links);394 395err_end:396	kfree(sof_ends);397err_dai:398	kfree(sof_dais);399 400	return ret;401}402 403static int mc_probe(struct platform_device *pdev)404{405	struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);406	struct snd_soc_card *card;407	struct amd_mc_ctx *amd_ctx;408	struct asoc_sdw_mc_private *ctx;409	int amp_num = 0, i;410	int ret;411 412	amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);413	if (!amd_ctx)414		return -ENOMEM;415 416	amd_ctx->acp_rev = mach->mach_params.subsystem_rev;417	amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;418	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);419	if (!ctx)420		return -ENOMEM;421	ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();422	ctx->private = amd_ctx;423	card = &ctx->card;424	card->dev = &pdev->dev;425	card->name = "amd-soundwire";426	card->owner = THIS_MODULE;427	card->late_probe = asoc_sdw_card_late_probe;428 429	snd_soc_card_set_drvdata(card, ctx);430 431	dmi_check_system(sof_sdw_quirk_table);432 433	if (quirk_override != -1) {434		dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",435			 sof_sdw_quirk, quirk_override);436		sof_sdw_quirk = quirk_override;437	}438 439	log_quirks(card->dev);440 441	ctx->mc_quirk = sof_sdw_quirk;442	/* reset amp_num to ensure amp_num++ starts from 0 in each probe */443	for (i = 0; i < ctx->codec_info_list_count; i++)444		codec_info_list[i].amp_num = 0;445 446	ret = sof_card_dai_links_create(card);447	if (ret < 0)448		return ret;449 450	/*451	 * the default amp_num is zero for each codec and452	 * amp_num will only be increased for active amp453	 * codecs on used platform454	 */455	for (i = 0; i < ctx->codec_info_list_count; i++)456		amp_num += codec_info_list[i].amp_num;457 458	card->components = devm_kasprintf(card->dev, GFP_KERNEL,459					  " cfg-amp:%d", amp_num);460	if (!card->components)461		return -ENOMEM;462 463	/* Register the card */464	ret = devm_snd_soc_register_card(card->dev, card);465	if (ret) {466		dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);467		asoc_sdw_mc_dailink_exit_loop(card);468		return ret;469	}470 471	platform_set_drvdata(pdev, card);472 473	return ret;474}475 476static void mc_remove(struct platform_device *pdev)477{478	struct snd_soc_card *card = platform_get_drvdata(pdev);479 480	asoc_sdw_mc_dailink_exit_loop(card);481}482 483static const struct platform_device_id mc_id_table[] = {484	{ "amd_sof_sdw", },485	{}486};487MODULE_DEVICE_TABLE(platform, mc_id_table);488 489static struct platform_driver sof_sdw_driver = {490	.driver = {491		.name = "amd_sof_sdw",492		.pm = &snd_soc_pm_ops,493	},494	.probe = mc_probe,495	.remove = mc_remove,496	.id_table = mc_id_table,497};498 499module_platform_driver(sof_sdw_driver);500 501MODULE_DESCRIPTION("ASoC AMD SoundWire Generic Machine driver");502MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com");503MODULE_LICENSE("GPL");504MODULE_IMPORT_NS(SND_SOC_SDW_UTILS);505