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