sof_cirrus_common.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * This file defines data structures and functions used in Machine
  4. * Driver for Intel platforms with Cirrus Logic Codecs.
  5. *
  6. * Copyright 2022 Intel Corporation.
  7. */
  8. #include <linux/module.h>
  9. #include <sound/sof.h>
  10. #include "../../codecs/cs35l41.h"
  11. #include "sof_cirrus_common.h"
  12. #define CS35L41_HID "CSC3541"
  13. #define CS35L41_MAX_AMPS 4
  14. /*
  15. * Cirrus Logic CS35L41/CS35L53
  16. */
  17. static const struct snd_kcontrol_new cs35l41_kcontrols[] = {
  18. SOC_DAPM_PIN_SWITCH("WL Spk"),
  19. SOC_DAPM_PIN_SWITCH("WR Spk"),
  20. SOC_DAPM_PIN_SWITCH("TL Spk"),
  21. SOC_DAPM_PIN_SWITCH("TR Spk"),
  22. };
  23. static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = {
  24. SND_SOC_DAPM_SPK("WL Spk", NULL),
  25. SND_SOC_DAPM_SPK("WR Spk", NULL),
  26. SND_SOC_DAPM_SPK("TL Spk", NULL),
  27. SND_SOC_DAPM_SPK("TR Spk", NULL),
  28. };
  29. static const struct snd_soc_dapm_route cs35l41_dapm_routes[] = {
  30. /* speaker */
  31. {"WL Spk", NULL, "WL SPK"},
  32. {"WR Spk", NULL, "WR SPK"},
  33. {"TL Spk", NULL, "TL SPK"},
  34. {"TR Spk", NULL, "TR SPK"},
  35. };
  36. static struct snd_soc_dai_link_component cs35l41_components[CS35L41_MAX_AMPS];
  37. /*
  38. * Mapping between ACPI instance id and speaker position.
  39. */
  40. static struct snd_soc_codec_conf cs35l41_codec_conf[CS35L41_MAX_AMPS];
  41. static int cs35l41_init(struct snd_soc_pcm_runtime *rtd)
  42. {
  43. struct snd_soc_card *card = rtd->card;
  44. int ret;
  45. ret = snd_soc_dapm_new_controls(&card->dapm, cs35l41_dapm_widgets,
  46. ARRAY_SIZE(cs35l41_dapm_widgets));
  47. if (ret) {
  48. dev_err(rtd->dev, "fail to add dapm controls, ret %d\n", ret);
  49. return ret;
  50. }
  51. ret = snd_soc_add_card_controls(card, cs35l41_kcontrols,
  52. ARRAY_SIZE(cs35l41_kcontrols));
  53. if (ret) {
  54. dev_err(rtd->dev, "fail to add card controls, ret %d\n", ret);
  55. return ret;
  56. }
  57. ret = snd_soc_dapm_add_routes(&card->dapm, cs35l41_dapm_routes,
  58. ARRAY_SIZE(cs35l41_dapm_routes));
  59. if (ret)
  60. dev_err(rtd->dev, "fail to add dapm routes, ret %d\n", ret);
  61. return ret;
  62. }
  63. /*
  64. * Channel map:
  65. *
  66. * TL/WL: ASPRX1 on slot 0, ASPRX2 on slot 1 (default)
  67. * TR/WR: ASPRX1 on slot 1, ASPRX2 on slot 0
  68. */
  69. static const struct {
  70. unsigned int rx[2];
  71. } cs35l41_channel_map[] = {
  72. {.rx = {0, 1}}, /* WL */
  73. {.rx = {1, 0}}, /* WR */
  74. {.rx = {0, 1}}, /* TL */
  75. {.rx = {1, 0}}, /* TR */
  76. };
  77. static int cs35l41_hw_params(struct snd_pcm_substream *substream,
  78. struct snd_pcm_hw_params *params)
  79. {
  80. struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
  81. struct snd_soc_dai *codec_dai;
  82. int clk_freq, i, ret;
  83. clk_freq = sof_dai_get_bclk(rtd); /* BCLK freq */
  84. if (clk_freq <= 0) {
  85. dev_err(rtd->dev, "fail to get bclk freq, ret %d\n", clk_freq);
  86. return -EINVAL;
  87. }
  88. for_each_rtd_codec_dais(rtd, i, codec_dai) {
  89. /* call dai driver's set_sysclk() callback */
  90. ret = snd_soc_dai_set_sysclk(codec_dai, CS35L41_CLKID_SCLK,
  91. clk_freq, SND_SOC_CLOCK_IN);
  92. if (ret < 0) {
  93. dev_err(codec_dai->dev, "fail to set sysclk, ret %d\n",
  94. ret);
  95. return ret;
  96. }
  97. /* call component driver's set_sysclk() callback */
  98. ret = snd_soc_component_set_sysclk(codec_dai->component,
  99. CS35L41_CLKID_SCLK, 0,
  100. clk_freq, SND_SOC_CLOCK_IN);
  101. if (ret < 0) {
  102. dev_err(codec_dai->dev, "fail to set component sysclk, ret %d\n",
  103. ret);
  104. return ret;
  105. }
  106. /* setup channel map */
  107. ret = snd_soc_dai_set_channel_map(codec_dai, 0, NULL,
  108. ARRAY_SIZE(cs35l41_channel_map[i].rx),
  109. (unsigned int *)cs35l41_channel_map[i].rx);
  110. if (ret < 0) {
  111. dev_err(codec_dai->dev, "fail to set channel map, ret %d\n",
  112. ret);
  113. return ret;
  114. }
  115. }
  116. return 0;
  117. }
  118. static const struct snd_soc_ops cs35l41_ops = {
  119. .hw_params = cs35l41_hw_params,
  120. };
  121. static const char * const cs35l41_name_prefixes[] = { "WL", "WR", "TL", "TR" };
  122. /*
  123. * Expected UIDs are integers (stored as strings).
  124. * UID Mapping is fixed:
  125. * UID 0x0 -> WL
  126. * UID 0x1 -> WR
  127. * UID 0x2 -> TL
  128. * UID 0x3 -> TR
  129. * Note: If there are less than 4 Amps, UIDs still map to WL/WR/TL/TR. Dynamic code will only create
  130. * dai links for UIDs which exist, and ignore non-existant ones. Only 2 or 4 amps are expected.
  131. * Return number of codecs found.
  132. */
  133. static int cs35l41_compute_codec_conf(void)
  134. {
  135. static const char * const uid_strings[] = { "0", "1", "2", "3" };
  136. unsigned int uid, sz = 0;
  137. struct acpi_device *adev;
  138. struct device *physdev;
  139. for (uid = 0; uid < CS35L41_MAX_AMPS; uid++) {
  140. adev = acpi_dev_get_first_match_dev(CS35L41_HID, uid_strings[uid], -1);
  141. if (!adev) {
  142. pr_devel("Cannot find match for HID %s UID %u (%s)\n", CS35L41_HID, uid,
  143. cs35l41_name_prefixes[uid]);
  144. continue;
  145. }
  146. physdev = get_device(acpi_get_first_physical_node(adev));
  147. acpi_dev_put(adev);
  148. if (!physdev) {
  149. pr_devel("Cannot find physical node for HID %s UID %u (%s)\n", CS35L41_HID,
  150. uid, cs35l41_name_prefixes[uid]);
  151. return 0;
  152. }
  153. cs35l41_components[sz].name = dev_name(physdev);
  154. cs35l41_components[sz].dai_name = CS35L41_CODEC_DAI;
  155. cs35l41_codec_conf[sz].dlc.name = dev_name(physdev);
  156. cs35l41_codec_conf[sz].name_prefix = cs35l41_name_prefixes[uid];
  157. sz++;
  158. }
  159. if (sz != 2 && sz != 4)
  160. pr_warn("Invalid number of cs35l41 amps found: %d, expected 2 or 4\n", sz);
  161. return sz;
  162. }
  163. void cs35l41_set_dai_link(struct snd_soc_dai_link *link)
  164. {
  165. link->num_codecs = cs35l41_compute_codec_conf();
  166. link->codecs = cs35l41_components;
  167. link->init = cs35l41_init;
  168. link->ops = &cs35l41_ops;
  169. }
  170. EXPORT_SYMBOL_NS(cs35l41_set_dai_link, SND_SOC_INTEL_SOF_CIRRUS_COMMON);
  171. void cs35l41_set_codec_conf(struct snd_soc_card *card)
  172. {
  173. card->codec_conf = cs35l41_codec_conf;
  174. card->num_configs = ARRAY_SIZE(cs35l41_codec_conf);
  175. }
  176. EXPORT_SYMBOL_NS(cs35l41_set_codec_conf, SND_SOC_INTEL_SOF_CIRRUS_COMMON);
  177. MODULE_DESCRIPTION("ASoC Intel SOF Cirrus Logic helpers");
  178. MODULE_LICENSE("GPL");