ehl_rt5660.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. // Copyright (c) 2020 Intel Corporation
  3. /*
  4. * ehl_rt5660 - ASOC Machine driver for Elkhart Lake platforms
  5. * with rt5660 codec
  6. */
  7. #include <linux/acpi.h>
  8. #include <sound/core.h>
  9. #include <linux/device.h>
  10. #include <linux/errno.h>
  11. #include <linux/gfp.h>
  12. #include <sound/jack.h>
  13. #include <linux/kernel.h>
  14. #include <linux/list.h>
  15. #include <linux/module.h>
  16. #include <sound/pcm.h>
  17. #include <sound/pcm_params.h>
  18. #include <sound/soc.h>
  19. #include <sound/soc-acpi.h>
  20. #include "hda_dsp_common.h"
  21. #include "../../codecs/rt5660.h"
  22. #define DUAL_CHANNEL 2
  23. #define HDMI_LINK_START 3
  24. #define HDMI_LINE_END 6
  25. #define NAME_SIZE 32
  26. #define IDISP_CODEC_MASK 0x4
  27. struct sof_card_private {
  28. struct list_head hdmi_pcm_list;
  29. bool idisp_codec;
  30. };
  31. static const struct snd_kcontrol_new rt5660_controls[] = {
  32. SOC_DAPM_PIN_SWITCH("Speaker"),
  33. /* There are two MICBIAS in rt5660, each for one MIC */
  34. SOC_DAPM_PIN_SWITCH("Headset Mic"),
  35. SOC_DAPM_PIN_SWITCH("Headset Mic2"),
  36. SOC_DAPM_PIN_SWITCH("Line Out"),
  37. };
  38. static const struct snd_soc_dapm_widget rt5660_widgets[] = {
  39. SND_SOC_DAPM_SPK("Speaker", NULL),
  40. SND_SOC_DAPM_MIC("Headset Mic", NULL),
  41. SND_SOC_DAPM_MIC("Headset Mic2", NULL),
  42. SND_SOC_DAPM_MIC("SoC DMIC", NULL),
  43. SND_SOC_DAPM_LINE("Line Out", NULL),
  44. };
  45. static const struct snd_soc_dapm_route rt5660_map[] = {
  46. {"Speaker", NULL, "SPO"},
  47. {"Headset Mic", NULL, "MICBIAS1"},
  48. {"Headset Mic2", NULL, "MICBIAS2"},
  49. {"IN1P", NULL, "Headset Mic"},
  50. {"IN2P", NULL, "Headset Mic2"},
  51. {"Line Out", NULL, "LOUTL"},
  52. {"Line Out", NULL, "LOUTR"},
  53. {"DMic", NULL, "SoC DMIC"},
  54. };
  55. struct sof_hdmi_pcm {
  56. struct list_head head;
  57. struct snd_soc_dai *codec_dai;
  58. int device;
  59. };
  60. static int hdmi_init(struct snd_soc_pcm_runtime *rtd)
  61. {
  62. struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
  63. struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
  64. struct sof_hdmi_pcm *pcm;
  65. pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
  66. if (!pcm)
  67. return -ENOMEM;
  68. /* dai_link id is 1:1 mapped to the PCM device */
  69. pcm->device = rtd->dai_link->id;
  70. pcm->codec_dai = dai;
  71. list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
  72. return 0;
  73. }
  74. static int card_late_probe(struct snd_soc_card *card)
  75. {
  76. struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
  77. struct sof_hdmi_pcm *pcm;
  78. if (list_empty(&ctx->hdmi_pcm_list))
  79. return -ENOENT;
  80. if (!ctx->idisp_codec)
  81. return 0;
  82. pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head);
  83. return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component);
  84. }
  85. static int rt5660_hw_params(struct snd_pcm_substream *substream,
  86. struct snd_pcm_hw_params *params)
  87. {
  88. struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  89. struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
  90. int ret;
  91. ret = snd_soc_dai_set_sysclk(codec_dai,
  92. RT5660_SCLK_S_PLL1,
  93. params_rate(params) * 512,
  94. SND_SOC_CLOCK_IN);
  95. if (ret < 0) {
  96. dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
  97. return ret;
  98. }
  99. ret = snd_soc_dai_set_pll(codec_dai, 0,
  100. RT5660_PLL1_S_BCLK,
  101. params_rate(params) * 50,
  102. params_rate(params) * 512);
  103. if (ret < 0)
  104. dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret);
  105. return ret;
  106. }
  107. static struct snd_soc_ops rt5660_ops = {
  108. .hw_params = rt5660_hw_params,
  109. };
  110. SND_SOC_DAILINK_DEF(ssp0_pin,
  111. DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin")));
  112. SND_SOC_DAILINK_DEF(rt5660_codec,
  113. DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5660:00", "rt5660-aif1")));
  114. SND_SOC_DAILINK_DEF(platform,
  115. DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3")));
  116. SND_SOC_DAILINK_DEF(dmic_pin,
  117. DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin")));
  118. SND_SOC_DAILINK_DEF(dmic_codec,
  119. DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
  120. SND_SOC_DAILINK_DEF(dmic16k,
  121. DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin")));
  122. SND_SOC_DAILINK_DEF(idisp1_pin,
  123. DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin")));
  124. SND_SOC_DAILINK_DEF(idisp1_codec,
  125. DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1")));
  126. SND_SOC_DAILINK_DEF(idisp2_pin,
  127. DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin")));
  128. SND_SOC_DAILINK_DEF(idisp2_codec,
  129. DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2")));
  130. SND_SOC_DAILINK_DEF(idisp3_pin,
  131. DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin")));
  132. SND_SOC_DAILINK_DEF(idisp3_codec,
  133. DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3")));
  134. SND_SOC_DAILINK_DEF(idisp4_pin,
  135. DAILINK_COMP_ARRAY(COMP_CPU("iDisp4 Pin")));
  136. SND_SOC_DAILINK_DEF(idisp4_codec,
  137. DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi4")));
  138. static struct snd_soc_dai_link ehl_rt5660_dailink[] = {
  139. /* back ends */
  140. {
  141. .name = "SSP0-Codec",
  142. .id = 0,
  143. .no_pcm = 1,
  144. .dpcm_playback = 1,
  145. .dpcm_capture = 1,
  146. .ops = &rt5660_ops,
  147. SND_SOC_DAILINK_REG(ssp0_pin, rt5660_codec, platform),
  148. },
  149. {
  150. .name = "dmic48k",
  151. .id = 1,
  152. .ignore_suspend = 1,
  153. .dpcm_capture = 1,
  154. .no_pcm = 1,
  155. SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform),
  156. },
  157. {
  158. .name = "dmic16k",
  159. .id = 2,
  160. .ignore_suspend = 1,
  161. .dpcm_capture = 1,
  162. .no_pcm = 1,
  163. SND_SOC_DAILINK_REG(dmic16k, dmic_codec, platform),
  164. },
  165. {
  166. .name = "iDisp1",
  167. .id = 5,
  168. .init = hdmi_init,
  169. .dpcm_playback = 1,
  170. .no_pcm = 1,
  171. SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform),
  172. },
  173. {
  174. .name = "iDisp2",
  175. .id = 6,
  176. .init = hdmi_init,
  177. .dpcm_playback = 1,
  178. .no_pcm = 1,
  179. SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform),
  180. },
  181. {
  182. .name = "iDisp3",
  183. .id = 7,
  184. .init = hdmi_init,
  185. .dpcm_playback = 1,
  186. .no_pcm = 1,
  187. SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform),
  188. },
  189. {
  190. .name = "iDisp4",
  191. .id = 8,
  192. .init = hdmi_init,
  193. .dpcm_playback = 1,
  194. .no_pcm = 1,
  195. SND_SOC_DAILINK_REG(idisp4_pin, idisp4_codec, platform),
  196. },
  197. };
  198. /* SoC card */
  199. static struct snd_soc_card snd_soc_card_ehl_rt5660 = {
  200. .name = "ehl-rt5660",
  201. .owner = THIS_MODULE,
  202. .dai_link = ehl_rt5660_dailink,
  203. .num_links = ARRAY_SIZE(ehl_rt5660_dailink),
  204. .dapm_widgets = rt5660_widgets,
  205. .num_dapm_widgets = ARRAY_SIZE(rt5660_widgets),
  206. .dapm_routes = rt5660_map,
  207. .num_dapm_routes = ARRAY_SIZE(rt5660_map),
  208. .controls = rt5660_controls,
  209. .num_controls = ARRAY_SIZE(rt5660_controls),
  210. .fully_routed = true,
  211. .late_probe = card_late_probe,
  212. };
  213. /* If hdmi codec is not supported, switch to use dummy codec */
  214. static void hdmi_link_init(struct snd_soc_card *card,
  215. struct sof_card_private *ctx,
  216. struct snd_soc_acpi_mach *mach)
  217. {
  218. struct snd_soc_dai_link *link;
  219. int i;
  220. if (mach->mach_params.common_hdmi_codec_drv &&
  221. (mach->mach_params.codec_mask & IDISP_CODEC_MASK)) {
  222. ctx->idisp_codec = true;
  223. return;
  224. }
  225. /*
  226. * if HDMI is not enabled in kernel config, or
  227. * hdmi codec is not supported
  228. */
  229. for (i = HDMI_LINK_START; i <= HDMI_LINE_END; i++) {
  230. link = &card->dai_link[i];
  231. link->codecs[0].name = "snd-soc-dummy";
  232. link->codecs[0].dai_name = "snd-soc-dummy-dai";
  233. }
  234. }
  235. static int snd_ehl_rt5660_probe(struct platform_device *pdev)
  236. {
  237. struct snd_soc_acpi_mach *mach;
  238. struct snd_soc_card *card = &snd_soc_card_ehl_rt5660;
  239. struct sof_card_private *ctx;
  240. int ret;
  241. card->dev = &pdev->dev;
  242. ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
  243. if (!ctx)
  244. return -ENOMEM;
  245. INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
  246. snd_soc_card_set_drvdata(card, ctx);
  247. mach = pdev->dev.platform_data;
  248. ret = snd_soc_fixup_dai_links_platform_name(card,
  249. mach->mach_params.platform);
  250. if (ret)
  251. return ret;
  252. hdmi_link_init(card, ctx, mach);
  253. return devm_snd_soc_register_card(&pdev->dev, card);
  254. }
  255. static const struct platform_device_id ehl_board_ids[] = {
  256. { .name = "ehl_rt5660" },
  257. { }
  258. };
  259. MODULE_DEVICE_TABLE(platform, ehl_board_ids);
  260. static struct platform_driver snd_ehl_rt5660_driver = {
  261. .driver = {
  262. .name = "ehl_rt5660",
  263. .pm = &snd_soc_pm_ops,
  264. },
  265. .probe = snd_ehl_rt5660_probe,
  266. .id_table = ehl_board_ids,
  267. };
  268. module_platform_driver(snd_ehl_rt5660_driver);
  269. MODULE_DESCRIPTION("ASoC Intel(R) Elkhartlake + rt5660 Machine driver");
  270. MODULE_AUTHOR("[email protected]");
  271. MODULE_LICENSE("GPL v2");
  272. MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);