brintos

brintos / linux-shallow public Read only

0
0
Text · 8.2 KiB · 6951dfb Raw
313 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// This file incorporates work covered by the following copyright notice:3// Copyright (c) 2022 Intel Corporation4// Copyright (c) 2024 Advanced Micro Devices, Inc.5 6/*7 *  soc_sdw_rt_amp - Helpers to handle RT1308/RT1316/RT1318 from generic machine driver8 */9 10#include <linux/device.h>11#include <linux/errno.h>12#include <sound/control.h>13#include <sound/soc.h>14#include <sound/soc-acpi.h>15#include <sound/soc-dapm.h>16#include <linux/soundwire/sdw.h>17#include <linux/soundwire/sdw_type.h>18#include <linux/dmi.h>19#include <sound/soc_sdw_utils.h>20#include "soc_sdw_rt_amp_coeff_tables.h"21#include "../codecs/rt1308.h"22 23#define CODEC_NAME_SIZE	724 25/* choose a larger value to resolve compatibility issues */26#define RT_AMP_MAX_BQ_REG RT1316_MAX_BQ_REG27 28struct rt_amp_platform_data {29	const unsigned char *bq_params;30	const unsigned int bq_params_cnt;31};32 33static const struct rt_amp_platform_data dell_0a5d_platform_data = {34	.bq_params = dell_0a5d_bq_params,35	.bq_params_cnt = ARRAY_SIZE(dell_0a5d_bq_params),36};37 38static const struct rt_amp_platform_data dell_0b00_platform_data = {39	.bq_params = dell_0b00_bq_params,40	.bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params),41};42 43static const struct dmi_system_id dmi_platform_data[] = {44	/* CometLake devices */45	{46		.matches = {47			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),48			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990")49		},50		.driver_data = (void *)&dell_0a5d_platform_data,51	},52	{53		.matches = {54			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),55			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F")56		},57		.driver_data = (void *)&dell_0a5d_platform_data,58	},59	/* TigerLake devices */60	{61		.matches = {62			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),63			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5D")64		},65		.driver_data = (void *)&dell_0a5d_platform_data,66	},67	{68		.matches = {69			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),70			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E")71		},72		.driver_data = (void *)&dell_0a5d_platform_data,73	},74	/* AlderLake devices */75	{76		.matches = {77			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),78			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")79		},80		.driver_data = (void *)&dell_0b00_platform_data,81	},82	{83		.matches = {84			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),85			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")86		},87		.driver_data = (void *)&dell_0b00_platform_data,88	},89	{90		.matches = {91			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),92			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF")93		},94		.driver_data = (void *)&dell_0b00_platform_data,95	},96	{97		.matches = {98			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),99			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE")100		},101		.driver_data = (void *)&dell_0b00_platform_data,102	},103	{},104};105 106static int rt_amp_add_device_props(struct device *sdw_dev)107{108	struct property_entry props[3] = {};109	struct fwnode_handle *fwnode;110	const struct dmi_system_id *dmi_data;111	const struct rt_amp_platform_data *pdata;112	unsigned char params[RT_AMP_MAX_BQ_REG];113	int ret;114 115	dmi_data = dmi_first_match(dmi_platform_data);116	if (!dmi_data)117		return 0;118 119	pdata = dmi_data->driver_data;120	memcpy(&params, pdata->bq_params, sizeof(unsigned char) * pdata->bq_params_cnt);121 122	props[0] = PROPERTY_ENTRY_U8_ARRAY("realtek,bq-params", params);123	props[1] = PROPERTY_ENTRY_U32("realtek,bq-params-cnt", pdata->bq_params_cnt);124 125	fwnode = fwnode_create_software_node(props, NULL);126	if (IS_ERR(fwnode))127		return PTR_ERR(fwnode);128 129	ret = device_add_software_node(sdw_dev, to_software_node(fwnode));130 131	fwnode_handle_put(fwnode);132 133	return ret;134}135 136/*137 * dapm routes for rt1308/rt1316/rt1318 will be registered dynamically138 * according to the number of rt1308/rt1316/rt1318 used. The first two139 * entries will be registered for one codec case, and the last two entries140 * are also registered if two 1308s/1316s/1318s are used.141 */142static const struct snd_soc_dapm_route rt1308_map[] = {143	{ "Speaker", NULL, "rt1308-1 SPOL" },144	{ "Speaker", NULL, "rt1308-1 SPOR" },145	{ "Speaker", NULL, "rt1308-2 SPOL" },146	{ "Speaker", NULL, "rt1308-2 SPOR" },147};148 149static const struct snd_soc_dapm_route rt1316_map[] = {150	{ "Speaker", NULL, "rt1316-1 SPOL" },151	{ "Speaker", NULL, "rt1316-1 SPOR" },152	{ "Speaker", NULL, "rt1316-2 SPOL" },153	{ "Speaker", NULL, "rt1316-2 SPOR" },154};155 156static const struct snd_soc_dapm_route rt1318_map[] = {157	{ "Speaker", NULL, "rt1318-1 SPOL" },158	{ "Speaker", NULL, "rt1318-1 SPOR" },159	{ "Speaker", NULL, "rt1318-2 SPOL" },160	{ "Speaker", NULL, "rt1318-2 SPOR" },161};162 163static const struct snd_soc_dapm_route rt1320_map[] = {164	{ "Speaker", NULL, "rt1320-1 SPOL" },165	{ "Speaker", NULL, "rt1320-1 SPOR" },166	{ "Speaker", NULL, "rt1320-2 SPOL" },167	{ "Speaker", NULL, "rt1320-2 SPOR" },168};169 170static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_dai *dai,171								 char *codec_name)172{173	/* get the codec name */174	snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai->name);175 176	/* choose the right codec's map  */177	if (strcmp(codec_name, "rt1308") == 0)178		return rt1308_map;179	else if (strcmp(codec_name, "rt1316") == 0)180		return rt1316_map;181	else if (strcmp(codec_name, "rt1318") == 0)182		return rt1318_map;183	else184		return rt1320_map;185}186 187int asoc_sdw_rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)188{189	struct snd_soc_card *card = rtd->card;190	const struct snd_soc_dapm_route *rt_amp_map;191	char codec_name[CODEC_NAME_SIZE];192	struct snd_soc_dai *codec_dai;193	int ret;194	int i;195 196	rt_amp_map = get_codec_name_and_route(dai, codec_name);197 198	card->components = devm_kasprintf(card->dev, GFP_KERNEL,199					  "%s spk:%s",200					  card->components, codec_name);201	if (!card->components)202		return -ENOMEM;203 204	for_each_rtd_codec_dais(rtd, i, codec_dai) {205		if (strstr(codec_dai->component->name_prefix, "-1"))206			ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2);207		else if (strstr(codec_dai->component->name_prefix, "-2"))208			ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2);209	}210 211	return ret;212}213EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_spk_rtd_init, SND_SOC_SDW_UTILS);214 215static int rt1308_i2s_hw_params(struct snd_pcm_substream *substream,216				struct snd_pcm_hw_params *params)217{218	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);219	struct snd_soc_card *card = rtd->card;220	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);221	int clk_id, clk_freq, pll_out;222	int err;223 224	clk_id = RT1308_PLL_S_MCLK;225	clk_freq = 38400000;226 227	pll_out = params_rate(params) * 512;228 229	/* Set rt1308 pll */230	err = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out);231	if (err < 0) {232		dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", err);233		return err;234	}235 236	/* Set rt1308 sysclk */237	err = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out,238				     SND_SOC_CLOCK_IN);239	if (err < 0) {240		dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", err);241		return err;242	}243 244	return 0;245}246 247/* machine stream operations */248const struct snd_soc_ops soc_sdw_rt1308_i2s_ops = {249	.hw_params = rt1308_i2s_hw_params,250};251EXPORT_SYMBOL_NS(soc_sdw_rt1308_i2s_ops, SND_SOC_SDW_UTILS);252 253int asoc_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)254{255	struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);256 257	if (ctx->amp_dev1) {258		device_remove_software_node(ctx->amp_dev1);259		put_device(ctx->amp_dev1);260	}261 262	if (ctx->amp_dev2) {263		device_remove_software_node(ctx->amp_dev2);264		put_device(ctx->amp_dev2);265	}266 267	return 0;268}269EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_exit, SND_SOC_SDW_UTILS);270 271int asoc_sdw_rt_amp_init(struct snd_soc_card *card,272			 struct snd_soc_dai_link *dai_links,273			 struct asoc_sdw_codec_info *info,274			 bool playback)275{276	struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);277	struct device *sdw_dev1, *sdw_dev2;278	int ret;279 280	/* Count amp number and do init on playback link only. */281	if (!playback)282		return 0;283 284	info->amp_num++;285 286	if (info->amp_num == 2) {287		sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);288		if (!sdw_dev1)289			return -EPROBE_DEFER;290 291		ret = rt_amp_add_device_props(sdw_dev1);292		if (ret < 0) {293			put_device(sdw_dev1);294			return ret;295		}296		ctx->amp_dev1 = sdw_dev1;297 298		sdw_dev2 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[1].name);299		if (!sdw_dev2)300			return -EPROBE_DEFER;301 302		ret = rt_amp_add_device_props(sdw_dev2);303		if (ret < 0) {304			put_device(sdw_dev2);305			return ret;306		}307		ctx->amp_dev2 = sdw_dev2;308	}309 310	return 0;311}312EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_init, SND_SOC_SDW_UTILS);313