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 *get_codec_name_and_route(struct snd_soc_dai *dai, 164 char *codec_name) 165 { 166 /* get the codec name */ 167 snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai->name); 168 169 /* choose the right codec's map */ 170 if (strcmp(codec_name, "rt1308") == 0) 171 return rt1308_map; 172 else if (strcmp(codec_name, "rt1316") == 0) 173 return rt1316_map; 174 else 175 return rt1318_map; 176 } 177 178 int asoc_sdw_rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) 179 { 180 struct snd_soc_card *card = rtd->card; 181 const struct snd_soc_dapm_route *rt_amp_map; 182 char codec_name[CODEC_NAME_SIZE]; 183 struct snd_soc_dai *codec_dai; 184 int ret; 185 int i; 186 187 rt_amp_map = get_codec_name_and_route(dai, codec_name); 188 189 card->components = devm_kasprintf(card->dev, GFP_KERNEL, 190 "%s spk:%s", 191 card->components, codec_name); 192 if (!card->components) 193 return -ENOMEM; 194 195 for_each_rtd_codec_dais(rtd, i, codec_dai) { 196 if (strstr(codec_dai->component->name_prefix, "-1")) 197 ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2); 198 else if (strstr(codec_dai->component->name_prefix, "-2")) 199 ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2); 200 } 201 202 return ret; 203 } 204 EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_spk_rtd_init, SND_SOC_SDW_UTILS); 205 206 static int rt1308_i2s_hw_params(struct snd_pcm_substream *substream, 207 struct snd_pcm_hw_params *params) 208 { 209 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 210 struct snd_soc_card *card = rtd->card; 211 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 212 int clk_id, clk_freq, pll_out; 213 int err; 214 215 clk_id = RT1308_PLL_S_MCLK; 216 clk_freq = 38400000; 217 218 pll_out = params_rate(params) * 512; 219 220 /* Set rt1308 pll */ 221 err = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out); 222 if (err < 0) { 223 dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", err); 224 return err; 225 } 226 227 /* Set rt1308 sysclk */ 228 err = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out, 229 SND_SOC_CLOCK_IN); 230 if (err < 0) { 231 dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", err); 232 return err; 233 } 234 235 return 0; 236 } 237 238 /* machine stream operations */ 239 const struct snd_soc_ops soc_sdw_rt1308_i2s_ops = { 240 .hw_params = rt1308_i2s_hw_params, 241 }; 242 EXPORT_SYMBOL_NS(soc_sdw_rt1308_i2s_ops, SND_SOC_SDW_UTILS); 243 244 int asoc_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) 245 { 246 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); 247 248 if (ctx->amp_dev1) { 249 device_remove_software_node(ctx->amp_dev1); 250 put_device(ctx->amp_dev1); 251 } 252 253 if (ctx->amp_dev2) { 254 device_remove_software_node(ctx->amp_dev2); 255 put_device(ctx->amp_dev2); 256 } 257 258 return 0; 259 } 260 EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_exit, SND_SOC_SDW_UTILS); 261 262 int asoc_sdw_rt_amp_init(struct snd_soc_card *card, 263 struct snd_soc_dai_link *dai_links, 264 struct asoc_sdw_codec_info *info, 265 bool playback) 266 { 267 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); 268 struct device *sdw_dev1, *sdw_dev2; 269 int ret; 270 271 /* Count amp number and do init on playback link only. */ 272 if (!playback) 273 return 0; 274 275 info->amp_num++; 276 277 if (info->amp_num == 2) { 278 sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name); 279 if (!sdw_dev1) 280 return -EPROBE_DEFER; 281 282 ret = rt_amp_add_device_props(sdw_dev1); 283 if (ret < 0) { 284 put_device(sdw_dev1); 285 return ret; 286 } 287 ctx->amp_dev1 = sdw_dev1; 288 289 sdw_dev2 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[1].name); 290 if (!sdw_dev2) 291 return -EPROBE_DEFER; 292 293 ret = rt_amp_add_device_props(sdw_dev2); 294 if (ret < 0) { 295 put_device(sdw_dev2); 296 return ret; 297 } 298 ctx->amp_dev2 = sdw_dev2; 299 } 300 301 return 0; 302 } 303 EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_init, SND_SOC_SDW_UTILS); 304