1 // SPDX-License-Identifier: GPL-2.0-only 2 // This file incorporates work covered by the following copyright notice: 3 // Copyright (c) 2022 Intel Corporation 4 // Copyright (c) 2024 Advanced Micro Devices, Inc. 5 6 /* 7 * soc_sdw_rt_amp - Helpers to handle RT1308/RT1316/RT1318 from generic machine driver 8 */ 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 7 24 25 /* choose a larger value to resolve compatibility issues */ 26 #define RT_AMP_MAX_BQ_REG RT1316_MAX_BQ_REG 27 28 struct rt_amp_platform_data { 29 const unsigned char *bq_params; 30 const unsigned int bq_params_cnt; 31 }; 32 33 static 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 38 static 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 43 static 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 106 static 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 dynamically 138 * according to the number of rt1308/rt1316/rt1318 used. The first two 139 * entries will be registered for one codec case, and the last two entries 140 * are also registered if two 1308s/1316s/1318s are used. 141 */ 142 static 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 149 static 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 156 static 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 163 static 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 170 static 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 else 184 return rt1320_map; 185 } 186 187 int 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 } 213 EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_spk_rtd_init, SND_SOC_SDW_UTILS); 214 215 static 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 */ 248 const struct snd_soc_ops soc_sdw_rt1308_i2s_ops = { 249 .hw_params = rt1308_i2s_hw_params, 250 }; 251 EXPORT_SYMBOL_NS(soc_sdw_rt1308_i2s_ops, SND_SOC_SDW_UTILS); 252 253 int 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 } 269 EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_exit, SND_SOC_SDW_UTILS); 270 271 int 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 } 312 EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_init, SND_SOC_SDW_UTILS); 313
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.