1e149ca29SPierre-Louis Bossart // SPDX-License-Identifier: GPL-2.0-only 27c33b5f1SRakesh Ughreja // Copyright(c) 2015-18 Intel Corporation. 37c33b5f1SRakesh Ughreja 47c33b5f1SRakesh Ughreja /* 57c33b5f1SRakesh Ughreja * Machine Driver for SKL+ platforms with DSP and iDisp, HDA Codecs 67c33b5f1SRakesh Ughreja */ 77c33b5f1SRakesh Ughreja 87c33b5f1SRakesh Ughreja #include <linux/module.h> 97c33b5f1SRakesh Ughreja #include <linux/platform_device.h> 107c33b5f1SRakesh Ughreja #include <sound/core.h> 117c33b5f1SRakesh Ughreja #include <sound/jack.h> 127c33b5f1SRakesh Ughreja #include <sound/pcm.h> 137c33b5f1SRakesh Ughreja #include <sound/pcm_params.h> 147c33b5f1SRakesh Ughreja #include <sound/soc.h> 15842bb513SPierre-Louis Bossart #include <sound/soc-acpi.h> 167c33b5f1SRakesh Ughreja #include "../../codecs/hdac_hdmi.h" 177c33b5f1SRakesh Ughreja #include "skl_hda_dsp_common.h" 187c33b5f1SRakesh Ughreja 196bae5ea9SRakesh Ughreja static const struct snd_soc_dapm_widget skl_hda_widgets[] = { 206bae5ea9SRakesh Ughreja SND_SOC_DAPM_HP("Analog Out", NULL), 216bae5ea9SRakesh Ughreja SND_SOC_DAPM_MIC("Analog In", NULL), 226bae5ea9SRakesh Ughreja SND_SOC_DAPM_HP("Alt Analog Out", NULL), 236bae5ea9SRakesh Ughreja SND_SOC_DAPM_MIC("Alt Analog In", NULL), 246bae5ea9SRakesh Ughreja SND_SOC_DAPM_SPK("Digital Out", NULL), 256bae5ea9SRakesh Ughreja SND_SOC_DAPM_MIC("Digital In", NULL), 2679631210SKeyon Jie SND_SOC_DAPM_MIC("SoC DMIC", NULL), 276bae5ea9SRakesh Ughreja }; 286bae5ea9SRakesh Ughreja 297c33b5f1SRakesh Ughreja static const struct snd_soc_dapm_route skl_hda_map[] = { 307c33b5f1SRakesh Ughreja { "hifi3", NULL, "iDisp3 Tx"}, 317c33b5f1SRakesh Ughreja { "iDisp3 Tx", NULL, "iDisp3_out"}, 327c33b5f1SRakesh Ughreja { "hifi2", NULL, "iDisp2 Tx"}, 337c33b5f1SRakesh Ughreja { "iDisp2 Tx", NULL, "iDisp2_out"}, 347c33b5f1SRakesh Ughreja { "hifi1", NULL, "iDisp1 Tx"}, 357c33b5f1SRakesh Ughreja { "iDisp1 Tx", NULL, "iDisp1_out"}, 366bae5ea9SRakesh Ughreja 376bae5ea9SRakesh Ughreja { "Analog Out", NULL, "Codec Output Pin1" }, 386bae5ea9SRakesh Ughreja { "Digital Out", NULL, "Codec Output Pin2" }, 396bae5ea9SRakesh Ughreja { "Alt Analog Out", NULL, "Codec Output Pin3" }, 406bae5ea9SRakesh Ughreja 416bae5ea9SRakesh Ughreja { "Codec Input Pin1", NULL, "Analog In" }, 426bae5ea9SRakesh Ughreja { "Codec Input Pin2", NULL, "Digital In" }, 436bae5ea9SRakesh Ughreja { "Codec Input Pin3", NULL, "Alt Analog In" }, 446bae5ea9SRakesh Ughreja 4579631210SKeyon Jie /* digital mics */ 4679631210SKeyon Jie {"DMic", NULL, "SoC DMIC"}, 4779631210SKeyon Jie 486bae5ea9SRakesh Ughreja /* CODEC BE connections */ 496bae5ea9SRakesh Ughreja { "Analog Codec Playback", NULL, "Analog CPU Playback" }, 506bae5ea9SRakesh Ughreja { "Analog CPU Playback", NULL, "codec0_out" }, 516bae5ea9SRakesh Ughreja { "Digital Codec Playback", NULL, "Digital CPU Playback" }, 526bae5ea9SRakesh Ughreja { "Digital CPU Playback", NULL, "codec1_out" }, 536bae5ea9SRakesh Ughreja { "Alt Analog Codec Playback", NULL, "Alt Analog CPU Playback" }, 546bae5ea9SRakesh Ughreja { "Alt Analog CPU Playback", NULL, "codec2_out" }, 556bae5ea9SRakesh Ughreja 566bae5ea9SRakesh Ughreja { "codec0_in", NULL, "Analog CPU Capture" }, 576bae5ea9SRakesh Ughreja { "Analog CPU Capture", NULL, "Analog Codec Capture" }, 586bae5ea9SRakesh Ughreja { "codec1_in", NULL, "Digital CPU Capture" }, 596bae5ea9SRakesh Ughreja { "Digital CPU Capture", NULL, "Digital Codec Capture" }, 606bae5ea9SRakesh Ughreja { "codec2_in", NULL, "Alt Analog CPU Capture" }, 616bae5ea9SRakesh Ughreja { "Alt Analog CPU Capture", NULL, "Alt Analog Codec Capture" }, 627c33b5f1SRakesh Ughreja }; 637c33b5f1SRakesh Ughreja 64d2ad9d6cSKai Vehmanen SND_SOC_DAILINK_DEF(dummy_codec, 65d2ad9d6cSKai Vehmanen DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai"))); 66d2ad9d6cSKai Vehmanen 677c33b5f1SRakesh Ughreja static int skl_hda_card_late_probe(struct snd_soc_card *card) 687c33b5f1SRakesh Ughreja { 697c33b5f1SRakesh Ughreja return skl_hda_hdmi_jack_init(card); 707c33b5f1SRakesh Ughreja } 717c33b5f1SRakesh Ughreja 727c33b5f1SRakesh Ughreja static int 737c33b5f1SRakesh Ughreja skl_hda_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) 747c33b5f1SRakesh Ughreja { 757c33b5f1SRakesh Ughreja struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); 767c33b5f1SRakesh Ughreja int ret = 0; 777c33b5f1SRakesh Ughreja 7846bc6bc3SPierre-Louis Bossart dev_dbg(card->dev, "dai link name - %s\n", link->name); 79a78959f4SKuninori Morimoto link->platforms->name = ctx->platform_name; 807c33b5f1SRakesh Ughreja link->nonatomic = 1; 817c33b5f1SRakesh Ughreja 82ffc6d45dSKai Vehmanen if (!ctx->idisp_codec) 83ffc6d45dSKai Vehmanen return 0; 84ffc6d45dSKai Vehmanen 857c33b5f1SRakesh Ughreja if (strstr(link->name, "HDMI")) { 867c33b5f1SRakesh Ughreja ret = skl_hda_hdmi_add_pcm(card, ctx->pcm_count); 877c33b5f1SRakesh Ughreja 887c33b5f1SRakesh Ughreja if (ret < 0) 897c33b5f1SRakesh Ughreja return ret; 907c33b5f1SRakesh Ughreja 917c33b5f1SRakesh Ughreja ctx->dai_index++; 927c33b5f1SRakesh Ughreja } 937c33b5f1SRakesh Ughreja 947c33b5f1SRakesh Ughreja ctx->pcm_count++; 957c33b5f1SRakesh Ughreja return ret; 967c33b5f1SRakesh Ughreja } 977c33b5f1SRakesh Ughreja 987c33b5f1SRakesh Ughreja static struct snd_soc_card hda_soc_card = { 99d745cc1aSJaroslav Kysela .name = "hda-dsp", 1007c33b5f1SRakesh Ughreja .owner = THIS_MODULE, 1017c33b5f1SRakesh Ughreja .dai_link = skl_hda_be_dai_links, 1026bae5ea9SRakesh Ughreja .dapm_widgets = skl_hda_widgets, 1037c33b5f1SRakesh Ughreja .dapm_routes = skl_hda_map, 1047c33b5f1SRakesh Ughreja .add_dai_link = skl_hda_add_dai_link, 1057c33b5f1SRakesh Ughreja .fully_routed = true, 1067c33b5f1SRakesh Ughreja .late_probe = skl_hda_card_late_probe, 1077c33b5f1SRakesh Ughreja }; 1087c33b5f1SRakesh Ughreja 1098cd9956fSJaroslav Kysela static char hda_soc_components[30]; 1108cd9956fSJaroslav Kysela 1117c33b5f1SRakesh Ughreja #define IDISP_DAI_COUNT 3 1122b131b5aSRander Wang #define HDAC_DAI_COUNT 2 1132b131b5aSRander Wang #define DMIC_DAI_COUNT 2 1142b131b5aSRander Wang 1157c33b5f1SRakesh Ughreja /* there are two routes per iDisp output */ 1167c33b5f1SRakesh Ughreja #define IDISP_ROUTE_COUNT (IDISP_DAI_COUNT * 2) 1177c33b5f1SRakesh Ughreja #define IDISP_CODEC_MASK 0x4 1187c33b5f1SRakesh Ughreja 1193a24f135SHui Wang #define HDA_CODEC_AUTOSUSPEND_DELAY_MS 1000 1203a24f135SHui Wang 121842bb513SPierre-Louis Bossart static int skl_hda_fill_card_info(struct snd_soc_acpi_mach_params *mach_params) 1227c33b5f1SRakesh Ughreja { 1237c33b5f1SRakesh Ughreja struct snd_soc_card *card = &hda_soc_card; 124ffc6d45dSKai Vehmanen struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); 1257fe072b4SKuninori Morimoto struct snd_soc_dai_link *dai_link; 126ffc6d45dSKai Vehmanen u32 codec_count, codec_mask; 1277c33b5f1SRakesh Ughreja int i, num_links, num_route; 1287c33b5f1SRakesh Ughreja 129842bb513SPierre-Louis Bossart codec_mask = mach_params->codec_mask; 1307c33b5f1SRakesh Ughreja codec_count = hweight_long(codec_mask); 131ffc6d45dSKai Vehmanen ctx->idisp_codec = !!(codec_mask & IDISP_CODEC_MASK); 1327c33b5f1SRakesh Ughreja 133d2ad9d6cSKai Vehmanen if (!codec_count || codec_count > 2 || 134ffc6d45dSKai Vehmanen (codec_count == 2 && !ctx->idisp_codec)) 135d2ad9d6cSKai Vehmanen return -EINVAL; 136d2ad9d6cSKai Vehmanen 137ffc6d45dSKai Vehmanen if (codec_mask == IDISP_CODEC_MASK) { 138d2ad9d6cSKai Vehmanen /* topology with iDisp as the only HDA codec */ 1392b131b5aSRander Wang num_links = IDISP_DAI_COUNT + DMIC_DAI_COUNT; 1407c33b5f1SRakesh Ughreja num_route = IDISP_ROUTE_COUNT; 1412b131b5aSRander Wang 1422b131b5aSRander Wang /* 1432b131b5aSRander Wang * rearrange the dai link array and make the 1442b131b5aSRander Wang * dmic dai links follow idsp dai links for only 1452b131b5aSRander Wang * num_links of dai links need to be registered 1462b131b5aSRander Wang * to ASoC. 1472b131b5aSRander Wang */ 1482b131b5aSRander Wang for (i = 0; i < DMIC_DAI_COUNT; i++) { 1492b131b5aSRander Wang skl_hda_be_dai_links[IDISP_DAI_COUNT + i] = 1502b131b5aSRander Wang skl_hda_be_dai_links[IDISP_DAI_COUNT + 1512b131b5aSRander Wang HDAC_DAI_COUNT + i]; 1522b131b5aSRander Wang } 153d2ad9d6cSKai Vehmanen } else { 154d2ad9d6cSKai Vehmanen /* topology with external and iDisp HDA codecs */ 1556bae5ea9SRakesh Ughreja num_links = ARRAY_SIZE(skl_hda_be_dai_links); 1566dd5055fSRander Wang num_route = ARRAY_SIZE(skl_hda_map); 1576bae5ea9SRakesh Ughreja card->dapm_widgets = skl_hda_widgets; 1586bae5ea9SRakesh Ughreja card->num_dapm_widgets = ARRAY_SIZE(skl_hda_widgets); 159ffc6d45dSKai Vehmanen if (!ctx->idisp_codec) { 160d2ad9d6cSKai Vehmanen for (i = 0; i < IDISP_DAI_COUNT; i++) { 161d2ad9d6cSKai Vehmanen skl_hda_be_dai_links[i].codecs = dummy_codec; 162d2ad9d6cSKai Vehmanen skl_hda_be_dai_links[i].num_codecs = 163d2ad9d6cSKai Vehmanen ARRAY_SIZE(dummy_codec); 164d2ad9d6cSKai Vehmanen } 165d2ad9d6cSKai Vehmanen } 1667c33b5f1SRakesh Ughreja } 1677c33b5f1SRakesh Ughreja 1687c33b5f1SRakesh Ughreja card->num_links = num_links; 1697c33b5f1SRakesh Ughreja card->num_dapm_routes = num_route; 1707c33b5f1SRakesh Ughreja 1717fe072b4SKuninori Morimoto for_each_card_prelinks(card, i, dai_link) 172a78959f4SKuninori Morimoto dai_link->platforms->name = mach_params->platform; 1737c33b5f1SRakesh Ughreja 1747c33b5f1SRakesh Ughreja return 0; 1757c33b5f1SRakesh Ughreja } 1767c33b5f1SRakesh Ughreja 1773a24f135SHui Wang static void skl_set_hda_codec_autosuspend_delay(struct snd_soc_card *card) 1783a24f135SHui Wang { 1795bf73b1bSKai Vehmanen struct snd_soc_pcm_runtime *rtd; 1803a24f135SHui Wang struct hdac_hda_priv *hda_pvt; 1815bf73b1bSKai Vehmanen struct snd_soc_dai *dai; 1823a24f135SHui Wang 1835bf73b1bSKai Vehmanen for_each_card_rtds(card, rtd) { 1845610921aSMateusz Gorski if (!strstr(rtd->dai_link->codecs->name, "ehdaudio0D0")) 1855bf73b1bSKai Vehmanen continue; 1865bf73b1bSKai Vehmanen dai = asoc_rtd_to_codec(rtd, 0); 1875bf73b1bSKai Vehmanen hda_pvt = snd_soc_component_get_drvdata(dai->component); 1885bf73b1bSKai Vehmanen if (hda_pvt) { 1893a24f135SHui Wang /* 1903a24f135SHui Wang * all codecs are on the same bus, so it's sufficient 1915bf73b1bSKai Vehmanen * to look up only the first one 1923a24f135SHui Wang */ 193*3fd63658SCezary Rojewski snd_hda_set_power_save(hda_pvt->codec->bus, 1943a24f135SHui Wang HDA_CODEC_AUTOSUSPEND_DELAY_MS); 1955bf73b1bSKai Vehmanen break; 1965bf73b1bSKai Vehmanen } 1975bf73b1bSKai Vehmanen } 1983a24f135SHui Wang } 1993a24f135SHui Wang 2007c33b5f1SRakesh Ughreja static int skl_hda_audio_probe(struct platform_device *pdev) 2017c33b5f1SRakesh Ughreja { 202842bb513SPierre-Louis Bossart struct snd_soc_acpi_mach *mach; 2037c33b5f1SRakesh Ughreja struct skl_hda_private *ctx; 2047c33b5f1SRakesh Ughreja int ret; 2057c33b5f1SRakesh Ughreja 20646bc6bc3SPierre-Louis Bossart dev_dbg(&pdev->dev, "entry\n"); 2077c33b5f1SRakesh Ughreja 208972b0d45SPierre-Louis Bossart ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); 2097c33b5f1SRakesh Ughreja if (!ctx) 2107c33b5f1SRakesh Ughreja return -ENOMEM; 2117c33b5f1SRakesh Ughreja 2127c33b5f1SRakesh Ughreja INIT_LIST_HEAD(&ctx->hdmi_pcm_list); 2137c33b5f1SRakesh Ughreja 21442432196SGuennadi Liakhovetski mach = pdev->dev.platform_data; 215842bb513SPierre-Louis Bossart if (!mach) 2167c33b5f1SRakesh Ughreja return -EINVAL; 2177c33b5f1SRakesh Ughreja 218ffc6d45dSKai Vehmanen snd_soc_card_set_drvdata(&hda_soc_card, ctx); 219ffc6d45dSKai Vehmanen 220842bb513SPierre-Louis Bossart ret = skl_hda_fill_card_info(&mach->mach_params); 2217c33b5f1SRakesh Ughreja if (ret < 0) { 2227c33b5f1SRakesh Ughreja dev_err(&pdev->dev, "Unsupported HDAudio/iDisp configuration found\n"); 2237c33b5f1SRakesh Ughreja return ret; 2247c33b5f1SRakesh Ughreja } 2257c33b5f1SRakesh Ughreja 2267c33b5f1SRakesh Ughreja ctx->pcm_count = hda_soc_card.num_links; 2277c33b5f1SRakesh Ughreja ctx->dai_index = 1; /* hdmi codec dai name starts from index 1 */ 228842bb513SPierre-Louis Bossart ctx->platform_name = mach->mach_params.platform; 2297de9a47cSKai Vehmanen ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; 2307c33b5f1SRakesh Ughreja 2317c33b5f1SRakesh Ughreja hda_soc_card.dev = &pdev->dev; 2327c33b5f1SRakesh Ughreja 2338cd9956fSJaroslav Kysela if (mach->mach_params.dmic_num > 0) { 2348cd9956fSJaroslav Kysela snprintf(hda_soc_components, sizeof(hda_soc_components), 2358cd9956fSJaroslav Kysela "cfg-dmics:%d", mach->mach_params.dmic_num); 2368cd9956fSJaroslav Kysela hda_soc_card.components = hda_soc_components; 2378cd9956fSJaroslav Kysela } 2388cd9956fSJaroslav Kysela 2393a24f135SHui Wang ret = devm_snd_soc_register_card(&pdev->dev, &hda_soc_card); 2403a24f135SHui Wang if (!ret) 2413a24f135SHui Wang skl_set_hda_codec_autosuspend_delay(&hda_soc_card); 2423a24f135SHui Wang 2433a24f135SHui Wang return ret; 2447c33b5f1SRakesh Ughreja } 2457c33b5f1SRakesh Ughreja 2467c33b5f1SRakesh Ughreja static struct platform_driver skl_hda_audio = { 2477c33b5f1SRakesh Ughreja .probe = skl_hda_audio_probe, 2487c33b5f1SRakesh Ughreja .driver = { 2497c33b5f1SRakesh Ughreja .name = "skl_hda_dsp_generic", 2507c33b5f1SRakesh Ughreja .pm = &snd_soc_pm_ops, 2517c33b5f1SRakesh Ughreja }, 2527c33b5f1SRakesh Ughreja }; 2537c33b5f1SRakesh Ughreja 2547c33b5f1SRakesh Ughreja module_platform_driver(skl_hda_audio) 2557c33b5f1SRakesh Ughreja 2567c33b5f1SRakesh Ughreja /* Module information */ 2577c33b5f1SRakesh Ughreja MODULE_DESCRIPTION("SKL/KBL/BXT/APL HDA Generic Machine driver"); 2587c33b5f1SRakesh Ughreja MODULE_AUTHOR("Rakesh Ughreja <rakesh.a.ughreja@intel.com>"); 2597c33b5f1SRakesh Ughreja MODULE_LICENSE("GPL v2"); 2607c33b5f1SRakesh Ughreja MODULE_ALIAS("platform:skl_hda_dsp_generic"); 261f6081af6SPierre-Louis Bossart MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); 262