~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/sdw_utils/soc_sdw_rt_amp.c

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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(&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 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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php