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(¶ms, 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