| // SPDX-License-Identifier: GPL-2.0-only |
| // Copyright(c) 2015-18 Intel Corporation. |
| |
| /* |
| * Machine Driver for SKL+ platforms with DSP and iDisp, HDA Codecs |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <sound/core.h> |
| #include <sound/hda_codec.h> |
| #include <sound/jack.h> |
| #include <sound/pcm.h> |
| #include <sound/pcm_params.h> |
| #include <sound/soc.h> |
| #include <sound/soc-acpi.h> |
| #include "../../codecs/hdac_hda.h" |
| #include "../../sof/intel/hda.h" |
| #include "sof_board_helpers.h" |
| |
| static int skl_hda_card_late_probe(struct snd_soc_card *card) |
| { |
| return sof_intel_board_card_late_probe(card); |
| } |
| |
| #define HDA_CODEC_AUTOSUSPEND_DELAY_MS 1000 |
| |
| static void skl_set_hda_codec_autosuspend_delay(struct snd_soc_card *card) |
| { |
| struct snd_soc_pcm_runtime *rtd; |
| struct hdac_hda_priv *hda_pvt; |
| struct snd_soc_dai *dai; |
| |
| for_each_card_rtds(card, rtd) { |
| if (!strstr(rtd->dai_link->codecs->name, "ehdaudio0D0")) |
| continue; |
| dai = snd_soc_rtd_to_codec(rtd, 0); |
| hda_pvt = snd_soc_component_get_drvdata(dai->component); |
| if (hda_pvt) { |
| /* |
| * all codecs are on the same bus, so it's sufficient |
| * to look up only the first one |
| */ |
| snd_hda_set_power_save(hda_pvt->codec->bus, |
| HDA_CODEC_AUTOSUSPEND_DELAY_MS); |
| break; |
| } |
| } |
| } |
| |
| #define IDISP_HDMI_BE_ID 1 |
| #define HDA_BE_ID 4 |
| #define DMIC01_BE_ID 6 |
| #define DMIC16K_BE_ID 7 |
| #define BT_OFFLOAD_BE_ID 8 |
| |
| #define HDA_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_IDISP_HDMI, \ |
| SOF_LINK_HDA, \ |
| SOF_LINK_DMIC01, \ |
| SOF_LINK_DMIC16K, \ |
| SOF_LINK_BT_OFFLOAD, \ |
| SOF_LINK_NONE, \ |
| SOF_LINK_NONE) |
| |
| #define HDA_LINK_IDS SOF_LINK_ORDER(IDISP_HDMI_BE_ID, \ |
| HDA_BE_ID, \ |
| DMIC01_BE_ID, \ |
| DMIC16K_BE_ID, \ |
| BT_OFFLOAD_BE_ID, \ |
| 0, \ |
| 0) |
| |
| static unsigned long |
| skl_hda_get_board_quirk(struct snd_soc_acpi_mach_params *mach_params) |
| { |
| unsigned long board_quirk = 0; |
| int ssp_bt; |
| |
| if (hweight_long(mach_params->bt_link_mask) == 1) { |
| ssp_bt = fls(mach_params->bt_link_mask) - 1; |
| board_quirk |= SOF_SSP_PORT_BT_OFFLOAD(ssp_bt) | |
| SOF_BT_OFFLOAD_PRESENT; |
| } |
| |
| return board_quirk; |
| } |
| |
| static int skl_hda_audio_probe(struct platform_device *pdev) |
| { |
| struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; |
| struct sof_card_private *ctx; |
| struct snd_soc_card *card; |
| unsigned long board_quirk = skl_hda_get_board_quirk(&mach->mach_params); |
| int ret; |
| |
| card = devm_kzalloc(&pdev->dev, sizeof(struct snd_soc_card), GFP_KERNEL); |
| if (!card) |
| return -ENOMEM; |
| |
| card->name = "hda-dsp"; |
| card->owner = THIS_MODULE; |
| card->fully_routed = true; |
| card->late_probe = skl_hda_card_late_probe; |
| |
| dev_dbg(&pdev->dev, "board_quirk = %lx\n", board_quirk); |
| |
| /* initialize ctx with board quirk */ |
| ctx = sof_intel_board_get_ctx(&pdev->dev, board_quirk); |
| if (!ctx) |
| return -ENOMEM; |
| |
| if (HDA_EXT_CODEC(mach->mach_params.codec_mask)) |
| ctx->hda_codec_present = true; |
| |
| if (mach->mach_params.codec_mask & IDISP_CODEC_MASK) |
| ctx->hdmi.idisp_codec = true; |
| |
| ctx->link_order_overwrite = HDA_LINK_ORDER; |
| ctx->link_id_overwrite = HDA_LINK_IDS; |
| |
| /* update dai_link */ |
| ret = sof_intel_board_set_dai_link(&pdev->dev, card, ctx); |
| if (ret) |
| return ret; |
| |
| card->dev = &pdev->dev; |
| if (!snd_soc_acpi_sof_parent(&pdev->dev)) |
| card->disable_route_checks = true; |
| |
| if (mach->mach_params.dmic_num > 0) { |
| card->components = devm_kasprintf(card->dev, GFP_KERNEL, |
| "cfg-dmics:%d", |
| mach->mach_params.dmic_num); |
| if (!card->components) |
| return -ENOMEM; |
| } |
| |
| ret = snd_soc_fixup_dai_links_platform_name(card, |
| mach->mach_params.platform); |
| if (ret) |
| return ret; |
| |
| snd_soc_card_set_drvdata(card, ctx); |
| |
| ret = devm_snd_soc_register_card(&pdev->dev, card); |
| if (!ret) |
| skl_set_hda_codec_autosuspend_delay(card); |
| |
| return ret; |
| } |
| |
| static struct platform_driver skl_hda_audio = { |
| .probe = skl_hda_audio_probe, |
| .driver = { |
| .name = "skl_hda_dsp_generic", |
| .pm = &snd_soc_pm_ops, |
| }, |
| }; |
| |
| module_platform_driver(skl_hda_audio) |
| |
| /* Module information */ |
| MODULE_DESCRIPTION("SKL/KBL/BXT/APL HDA Generic Machine driver"); |
| MODULE_AUTHOR("Rakesh Ughreja <rakesh.a.ughreja@intel.com>"); |
| MODULE_LICENSE("GPL v2"); |
| MODULE_ALIAS("platform:skl_hda_dsp_generic"); |
| MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); |