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

TOMOYO Linux Cross Reference
Linux/sound/soc/amd/acp/acp-sdw-sof-mach.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 // Copyright(c) 2024 Advanced Micro Devices, Inc.
  3 
  4 /*
  5  *  acp-sdw-sof-mach - ASoC Machine driver for AMD SoundWire platforms
  6  */
  7 
  8 #include <linux/bitmap.h>
  9 #include <linux/device.h>
 10 #include <linux/dmi.h>
 11 #include <linux/module.h>
 12 #include <linux/soundwire/sdw.h>
 13 #include <linux/soundwire/sdw_type.h>
 14 #include <sound/soc.h>
 15 #include <sound/soc-acpi.h>
 16 #include "soc_amd_sdw_common.h"
 17 #include "../../codecs/rt711.h"
 18 
 19 static unsigned long sof_sdw_quirk = RT711_JD1;
 20 static int quirk_override = -1;
 21 module_param_named(quirk, quirk_override, int, 0444);
 22 MODULE_PARM_DESC(quirk, "Board-specific quirk override");
 23 
 24 static void log_quirks(struct device *dev)
 25 {
 26         if (SOC_JACK_JDSRC(sof_sdw_quirk))
 27                 dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
 28                         SOC_JACK_JDSRC(sof_sdw_quirk));
 29         if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC)
 30                 dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");
 31 }
 32 
 33 static int sof_sdw_quirk_cb(const struct dmi_system_id *id)
 34 {
 35         sof_sdw_quirk = (unsigned long)id->driver_data;
 36         return 1;
 37 }
 38 
 39 static const struct dmi_system_id sof_sdw_quirk_table[] = {
 40         {
 41                 .callback = sof_sdw_quirk_cb,
 42                 .matches = {
 43                         DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
 44                         DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"),
 45                 },
 46                 .driver_data = (void *)RT711_JD2,
 47         },
 48         {}
 49 };
 50 
 51 static struct snd_soc_dai_link_component platform_component[] = {
 52         {
 53                 /* name might be overridden during probe */
 54                 .name = "0000:04:00.5",
 55         }
 56 };
 57 
 58 static const struct snd_soc_ops sdw_ops = {
 59         .startup = asoc_sdw_startup,
 60         .prepare = asoc_sdw_prepare,
 61         .trigger = asoc_sdw_trigger,
 62         .hw_params = asoc_sdw_hw_params,
 63         .hw_free = asoc_sdw_hw_free,
 64         .shutdown = asoc_sdw_shutdown,
 65 };
 66 
 67 static int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev)
 68 {
 69         switch (sdw_link_id) {
 70         case AMD_SDW0:
 71                 switch (be_id) {
 72                 case SOC_SDW_JACK_OUT_DAI_ID:
 73                         *cpu_pin_id = ACP63_SW0_AUDIO0_TX;
 74                         break;
 75                 case SOC_SDW_JACK_IN_DAI_ID:
 76                         *cpu_pin_id = ACP63_SW0_AUDIO0_RX;
 77                         break;
 78                 case SOC_SDW_AMP_OUT_DAI_ID:
 79                         *cpu_pin_id = ACP63_SW0_AUDIO1_TX;
 80                         break;
 81                 case SOC_SDW_AMP_IN_DAI_ID:
 82                         *cpu_pin_id = ACP63_SW0_AUDIO1_RX;
 83                         break;
 84                 case SOC_SDW_DMIC_DAI_ID:
 85                         *cpu_pin_id = ACP63_SW0_AUDIO2_RX;
 86                         break;
 87                 default:
 88                         dev_err(dev, "Invalid be id:%d\n", be_id);
 89                         return -EINVAL;
 90                 }
 91                 break;
 92         case AMD_SDW1:
 93                 switch (be_id) {
 94                 case SOC_SDW_JACK_OUT_DAI_ID:
 95                 case SOC_SDW_AMP_OUT_DAI_ID:
 96                         *cpu_pin_id = ACP63_SW1_AUDIO0_TX;
 97                         break;
 98                 case SOC_SDW_JACK_IN_DAI_ID:
 99                 case SOC_SDW_AMP_IN_DAI_ID:
100                 case SOC_SDW_DMIC_DAI_ID:
101                         *cpu_pin_id = ACP63_SW1_AUDIO0_RX;
102                         break;
103                 default:
104                         dev_err(dev, "invalid be_id:%d\n", be_id);
105                         return -EINVAL;
106                 }
107                 break;
108         default:
109                 dev_err(dev, "Invalid link id:%d\n", sdw_link_id);
110                 return -EINVAL;
111         }
112         return 0;
113 }
114 
115 static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
116 
117 static int create_sdw_dailink(struct snd_soc_card *card,
118                               struct asoc_sdw_dailink *sof_dai,
119                               struct snd_soc_dai_link **dai_links,
120                               int *be_id, struct snd_soc_codec_conf **codec_conf)
121 {
122         struct device *dev = card->dev;
123         struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
124         struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
125         struct asoc_sdw_endpoint *sof_end;
126         int cpu_pin_id;
127         int stream;
128         int ret;
129 
130         list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
131                 if (sof_end->name_prefix) {
132                         (*codec_conf)->dlc.name = sof_end->codec_name;
133                         (*codec_conf)->name_prefix = sof_end->name_prefix;
134                         (*codec_conf)++;
135                 }
136 
137                 if (sof_end->include_sidecar) {
138                         ret = sof_end->codec_info->add_sidecar(card, dai_links, codec_conf);
139                         if (ret)
140                                 return ret;
141                 }
142         }
143 
144         for_each_pcm_streams(stream) {
145                 static const char * const sdw_stream_name[] = {
146                         "SDW%d-PIN%d-PLAYBACK",
147                         "SDW%d-PIN%d-CAPTURE",
148                         "SDW%d-PIN%d-PLAYBACK-%s",
149                         "SDW%d-PIN%d-CAPTURE-%s",
150                 };
151                 struct snd_soc_dai_link_ch_map *codec_maps;
152                 struct snd_soc_dai_link_component *codecs;
153                 struct snd_soc_dai_link_component *cpus;
154                 int num_cpus = hweight32(sof_dai->link_mask[stream]);
155                 int num_codecs = sof_dai->num_devs[stream];
156                 int playback, capture;
157                 int i = 0, j = 0;
158                 char *name;
159 
160                 if (!sof_dai->num_devs[stream])
161                         continue;
162 
163                 sof_end = list_first_entry(&sof_dai->endpoints,
164                                            struct asoc_sdw_endpoint, list);
165 
166                 *be_id = sof_end->dai_info->dailink[stream];
167                 if (*be_id < 0) {
168                         dev_err(dev, "Invalid dailink id %d\n", *be_id);
169                         return -EINVAL;
170                 }
171 
172                 switch (amd_ctx->acp_rev) {
173                 case ACP63_PCI_REV:
174                         ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1),
175                                                    *be_id, &cpu_pin_id, dev);
176                         if (ret)
177                                 return ret;
178                         break;
179                 default:
180                         return -EINVAL;
181                 }
182                 /* create stream name according to first link id */
183                 if (ctx->append_dai_type) {
184                         name = devm_kasprintf(dev, GFP_KERNEL,
185                                               sdw_stream_name[stream + 2],
186                                               ffs(sof_end->link_mask) - 1,
187                                               cpu_pin_id,
188                                               type_strings[sof_end->dai_info->dai_type]);
189                 } else {
190                         name = devm_kasprintf(dev, GFP_KERNEL,
191                                               sdw_stream_name[stream],
192                                               ffs(sof_end->link_mask) - 1,
193                                               cpu_pin_id);
194                 }
195                 if (!name)
196                         return -ENOMEM;
197 
198                 cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);
199                 if (!cpus)
200                         return -ENOMEM;
201 
202                 codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);
203                 if (!codecs)
204                         return -ENOMEM;
205 
206                 codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);
207                 if (!codec_maps)
208                         return -ENOMEM;
209 
210                 list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
211                         if (!sof_end->dai_info->direction[stream])
212                                 continue;
213 
214                         int link_num = ffs(sof_end->link_mask) - 1;
215 
216                         cpus[i].dai_name = devm_kasprintf(dev, GFP_KERNEL,
217                                                           "SDW%d Pin%d",
218                                                           link_num, cpu_pin_id);
219                         dev_dbg(dev, "cpu[%d].dai_name:%s\n", i, cpus[i].dai_name);
220                         if (!cpus[i].dai_name)
221                                 return -ENOMEM;
222 
223                         codec_maps[j].cpu = i;
224                         codec_maps[j].codec = j;
225 
226                         codecs[j].name = sof_end->codec_name;
227                         codecs[j].dai_name = sof_end->dai_info->dai_name;
228                         j++;
229                 }
230 
231                 WARN_ON(j != num_codecs);
232 
233                 playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
234                 capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
235 
236                 asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
237                                        cpus, num_cpus, platform_component,
238                                        ARRAY_SIZE(platform_component), codecs, num_codecs,
239                                        asoc_sdw_rtd_init, &sdw_ops);
240 
241                 /*
242                  * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
243                  * based on wait_for_completion(), tag them as 'nonatomic'.
244                  */
245                 (*dai_links)->nonatomic = true;
246                 (*dai_links)->ch_maps = codec_maps;
247 
248                 list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
249                         if (sof_end->dai_info->init)
250                                 sof_end->dai_info->init(card, *dai_links,
251                                                         sof_end->codec_info,
252                                                         playback);
253                 }
254 
255                 (*dai_links)++;
256         }
257 
258         return 0;
259 }
260 
261 static int create_sdw_dailinks(struct snd_soc_card *card,
262                                struct snd_soc_dai_link **dai_links, int *be_id,
263                                struct asoc_sdw_dailink *sof_dais,
264                                struct snd_soc_codec_conf **codec_conf)
265 {
266         int ret;
267 
268         /* generate DAI links by each sdw link */
269         while (sof_dais->initialised) {
270                 int current_be_id;
271 
272                 ret = create_sdw_dailink(card, sof_dais, dai_links,
273                                          &current_be_id, codec_conf);
274                 if (ret)
275                         return ret;
276 
277                 /* Update the be_id to match the highest ID used for SDW link */
278                 if (*be_id < current_be_id)
279                         *be_id = current_be_id;
280 
281                 sof_dais++;
282         }
283 
284         return 0;
285 }
286 
287 static int create_dmic_dailinks(struct snd_soc_card *card,
288                                 struct snd_soc_dai_link **dai_links, int *be_id)
289 {
290         struct device *dev = card->dev;
291         int ret;
292 
293         ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",
294                                             0, 1, // DMIC only supports capture
295                                             "acp-sof-dmic", platform_component->name,
296                                             ARRAY_SIZE(platform_component),
297                                             "dmic-codec", "dmic-hifi",
298                                             asoc_sdw_dmic_init, NULL);
299         if (ret)
300                 return ret;
301 
302         (*dai_links)++;
303 
304         return 0;
305 }
306 
307 static int sof_card_dai_links_create(struct snd_soc_card *card)
308 {
309         struct device *dev = card->dev;
310         struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
311         int sdw_be_num = 0, dmic_num = 0;
312         struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
313         struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
314         struct snd_soc_codec_conf *codec_conf;
315         struct asoc_sdw_endpoint *sof_ends;
316         struct asoc_sdw_dailink *sof_dais;
317         struct snd_soc_dai_link *dai_links;
318         int num_devs = 0;
319         int num_ends = 0;
320         int num_links;
321         int be_id = 0;
322         int ret;
323 
324         ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends);
325         if (ret < 0) {
326                 dev_err(dev, "failed to count devices/endpoints: %d\n", ret);
327                 return ret;
328         }
329 
330         /* One per DAI link, worst case is a DAI link for every endpoint */
331         sof_dais = kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL);
332         if (!sof_dais)
333                 return -ENOMEM;
334 
335         /* One per endpoint, ie. each DAI on each codec/amp */
336         sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);
337         if (!sof_ends) {
338                 ret = -ENOMEM;
339                 goto err_dai;
340         }
341 
342         ret = asoc_sdw_parse_sdw_endpoints(card, sof_dais, sof_ends, &num_devs);
343         if (ret < 0)
344                 goto err_end;
345 
346         sdw_be_num = ret;
347 
348         /* enable dmic */
349         if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)
350                 dmic_num = 1;
351 
352         dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
353 
354         codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);
355         if (!codec_conf) {
356                 ret = -ENOMEM;
357                 goto err_end;
358         }
359 
360         /* allocate BE dailinks */
361         num_links = sdw_be_num + dmic_num;
362         dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
363         if (!dai_links) {
364                 ret = -ENOMEM;
365         goto err_end;
366         }
367 
368         card->codec_conf = codec_conf;
369         card->num_configs = num_devs;
370         card->dai_link = dai_links;
371         card->num_links = num_links;
372 
373         /* SDW */
374         if (sdw_be_num) {
375                 ret = create_sdw_dailinks(card, &dai_links, &be_id,
376                                           sof_dais, &codec_conf);
377                 if (ret)
378                         goto err_end;
379         }
380 
381         /* dmic */
382         if (dmic_num > 0) {
383                 if (ctx->ignore_internal_dmic) {
384                         dev_warn(dev, "Ignoring ACP DMIC\n");
385                 } else {
386                         ret = create_dmic_dailinks(card, &dai_links, &be_id);
387                         if (ret)
388                                 goto err_end;
389                 }
390         }
391 
392         WARN_ON(codec_conf != card->codec_conf + card->num_configs);
393         WARN_ON(dai_links != card->dai_link + card->num_links);
394 
395 err_end:
396         kfree(sof_ends);
397 err_dai:
398         kfree(sof_dais);
399 
400         return ret;
401 }
402 
403 static int mc_probe(struct platform_device *pdev)
404 {
405         struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
406         struct snd_soc_card *card;
407         struct amd_mc_ctx *amd_ctx;
408         struct asoc_sdw_mc_private *ctx;
409         int amp_num = 0, i;
410         int ret;
411 
412         amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);
413         if (!amd_ctx)
414                 return -ENOMEM;
415 
416         amd_ctx->acp_rev = mach->mach_params.subsystem_rev;
417         amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;
418         ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
419         if (!ctx)
420                 return -ENOMEM;
421         ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();
422         ctx->private = amd_ctx;
423         card = &ctx->card;
424         card->dev = &pdev->dev;
425         card->name = "amd-soundwire";
426         card->owner = THIS_MODULE;
427         card->late_probe = asoc_sdw_card_late_probe;
428 
429         snd_soc_card_set_drvdata(card, ctx);
430 
431         dmi_check_system(sof_sdw_quirk_table);
432 
433         if (quirk_override != -1) {
434                 dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",
435                          sof_sdw_quirk, quirk_override);
436                 sof_sdw_quirk = quirk_override;
437         }
438 
439         log_quirks(card->dev);
440 
441         ctx->mc_quirk = sof_sdw_quirk;
442         /* reset amp_num to ensure amp_num++ starts from 0 in each probe */
443         for (i = 0; i < ctx->codec_info_list_count; i++)
444                 codec_info_list[i].amp_num = 0;
445 
446         ret = sof_card_dai_links_create(card);
447         if (ret < 0)
448                 return ret;
449 
450         /*
451          * the default amp_num is zero for each codec and
452          * amp_num will only be increased for active amp
453          * codecs on used platform
454          */
455         for (i = 0; i < ctx->codec_info_list_count; i++)
456                 amp_num += codec_info_list[i].amp_num;
457 
458         card->components = devm_kasprintf(card->dev, GFP_KERNEL,
459                                           " cfg-amp:%d", amp_num);
460         if (!card->components)
461                 return -ENOMEM;
462 
463         /* Register the card */
464         ret = devm_snd_soc_register_card(card->dev, card);
465         if (ret) {
466                 dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
467                 asoc_sdw_mc_dailink_exit_loop(card);
468                 return ret;
469         }
470 
471         platform_set_drvdata(pdev, card);
472 
473         return ret;
474 }
475 
476 static void mc_remove(struct platform_device *pdev)
477 {
478         struct snd_soc_card *card = platform_get_drvdata(pdev);
479 
480         asoc_sdw_mc_dailink_exit_loop(card);
481 }
482 
483 static const struct platform_device_id mc_id_table[] = {
484         { "amd_sof_sdw", },
485         {}
486 };
487 MODULE_DEVICE_TABLE(platform, mc_id_table);
488 
489 static struct platform_driver sof_sdw_driver = {
490         .driver = {
491                 .name = "amd_sof_sdw",
492                 .pm = &snd_soc_pm_ops,
493         },
494         .probe = mc_probe,
495         .remove = mc_remove,
496         .id_table = mc_id_table,
497 };
498 
499 module_platform_driver(sof_sdw_driver);
500 
501 MODULE_DESCRIPTION("ASoC AMD SoundWire Generic Machine driver");
502 MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com");
503 MODULE_LICENSE("GPL");
504 MODULE_IMPORT_NS(SND_SOC_SDW_UTILS);
505 

~ [ 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