diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index b6d301946244e6..c1552bc6e31cd4 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -73,6 +73,7 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) * @subsystem_rev: optional PCI SSID revision value * @subsystem_id_set: true if a value has been written to * subsystem_vendor and subsystem_device. + * @bt_link_mask: BT offload link enabled on the board */ struct snd_soc_acpi_mach_params { u32 acpi_ipc_irq_index; @@ -89,6 +90,7 @@ struct snd_soc_acpi_mach_params { unsigned short subsystem_device; unsigned short subsystem_rev; bool subsystem_id_set; + u32 bt_link_mask; }; /** diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.c b/sound/soc/intel/boards/skl_hda_dsp_common.c index e9cefa4ae56dd7..5eb63f4df24103 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.c +++ b/sound/soc/intel/boards/skl_hda_dsp_common.c @@ -75,6 +75,11 @@ SND_SOC_DAILINK_DEF(dmic_codec, SND_SOC_DAILINK_DEF(dmic16k, DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin"))); +SND_SOC_DAILINK_DEF(bt_offload_pin, + DAILINK_COMP_ARRAY(COMP_CPU(""))); /* initialized in driver probe function */ +SND_SOC_DAILINK_DEF(dummy, + DAILINK_COMP_ARRAY(COMP_DUMMY())); + SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3"))); @@ -132,6 +137,14 @@ struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS] = { .no_pcm = 1, SND_SOC_DAILINK_REG(dmic16k, dmic_codec, platform), }, + { + .name = NULL, /* initialized in driver probe function */ + .id = 8, + .dpcm_playback = 1, + .dpcm_capture = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(bt_offload_pin, dummy, platform), + }, }; int skl_hda_hdmi_jack_init(struct snd_soc_card *card) diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.h b/sound/soc/intel/boards/skl_hda_dsp_common.h index 19b814dee4ad6d..9d714f747dcad4 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.h +++ b/sound/soc/intel/boards/skl_hda_dsp_common.h @@ -18,7 +18,7 @@ #include "../../codecs/hdac_hda.h" #include "hda_dsp_common.h" -#define HDA_DSP_MAX_BE_DAI_LINKS 7 +#define HDA_DSP_MAX_BE_DAI_LINKS 8 struct skl_hda_hdmi_pcm { struct list_head head; @@ -35,6 +35,8 @@ struct skl_hda_private { const char *platform_name; bool common_hdmi_codec_drv; bool idisp_codec; + bool bt_offload_present; + int ssp_bt; }; extern struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS]; diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 88d91c0280bbb8..927c73bef06514 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -95,6 +95,7 @@ skl_hda_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) #define IDISP_DAI_COUNT 3 #define HDAC_DAI_COUNT 2 #define DMIC_DAI_COUNT 2 +#define BT_DAI_COUNT 1 /* there are two routes per iDisp output */ #define IDISP_ROUTE_COUNT (IDISP_DAI_COUNT * 2) @@ -102,11 +103,12 @@ skl_hda_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) #define HDA_CODEC_AUTOSUSPEND_DELAY_MS 1000 -static int skl_hda_fill_card_info(struct snd_soc_card *card, +static int skl_hda_fill_card_info(struct device *dev, struct snd_soc_card *card, struct snd_soc_acpi_mach_params *mach_params) { struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_dai_link *dai_link; + struct snd_soc_dai_link *bt_link; u32 codec_count, codec_mask; int i, num_links, num_route; @@ -120,7 +122,7 @@ static int skl_hda_fill_card_info(struct snd_soc_card *card, if (codec_mask == IDISP_CODEC_MASK) { /* topology with iDisp as the only HDA codec */ - num_links = IDISP_DAI_COUNT + DMIC_DAI_COUNT; + num_links = IDISP_DAI_COUNT + DMIC_DAI_COUNT + BT_DAI_COUNT; num_route = IDISP_ROUTE_COUNT; /* @@ -129,7 +131,7 @@ static int skl_hda_fill_card_info(struct snd_soc_card *card, * num_links of dai links need to be registered * to ASoC. */ - for (i = 0; i < DMIC_DAI_COUNT; i++) { + for (i = 0; i < (DMIC_DAI_COUNT + BT_DAI_COUNT); i++) { skl_hda_be_dai_links[IDISP_DAI_COUNT + i] = skl_hda_be_dai_links[IDISP_DAI_COUNT + HDAC_DAI_COUNT + i]; @@ -150,6 +152,28 @@ static int skl_hda_fill_card_info(struct snd_soc_card *card, } } + if (!ctx->bt_offload_present) { + /* remove last link since bt audio offload is not supported */ + num_links -= BT_DAI_COUNT; + } else { + if (codec_mask == IDISP_CODEC_MASK) + bt_link = &skl_hda_be_dai_links[IDISP_DAI_COUNT + DMIC_DAI_COUNT]; + else + bt_link = &skl_hda_be_dai_links[IDISP_DAI_COUNT + HDAC_DAI_COUNT + DMIC_DAI_COUNT]; + + /* complete the link name and dai name with SSP port number */ + bt_link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", + ctx->ssp_bt); + if (!bt_link->name) + return -ENOMEM; + + bt_link->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d Pin", + ctx->ssp_bt); + if (!bt_link->cpus->dai_name) + return -ENOMEM; + } + card->num_links = num_links; card->num_dapm_routes = num_route; @@ -213,7 +237,12 @@ static int skl_hda_audio_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, ctx); - ret = skl_hda_fill_card_info(card, &mach->mach_params); + if (hweight_long(mach->mach_params.bt_link_mask) == 1) { + ctx->bt_offload_present = true; + ctx->ssp_bt = fls(mach->mach_params.bt_link_mask) - 1; + } + + ret = skl_hda_fill_card_info(&pdev->dev, card, &mach->mach_params); if (ret < 0) { dev_err(&pdev->dev, "Unsupported HDAudio/iDisp configuration found\n"); return ret; diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index b98b8c41dc0802..bffb2d2d2c82f5 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -444,6 +444,10 @@ static int mclk_id_override = -1; module_param_named(mclk_id, mclk_id_override, int, 0444); MODULE_PARM_DESC(mclk_id, "SOF SSP mclk_id"); +static int bt_link_mask_override; +module_param_named(bt_link_mask, bt_link_mask_override, int, 0444); +MODULE_PARM_DESC(bt_link_mask, "SOF BT offload link mask"); + static int hda_init(struct snd_sof_dev *sdev) { struct hda_bus *hbus; @@ -529,7 +533,7 @@ static int check_dmic_num(struct snd_sof_dev *sdev) return dmic_num; } -static int check_nhlt_ssp_mask(struct snd_sof_dev *sdev) +static int check_nhlt_ssp_mask(struct snd_sof_dev *sdev, u8 device_type) { struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct nhlt_acpi_table *nhlt; @@ -540,9 +544,11 @@ static int check_nhlt_ssp_mask(struct snd_sof_dev *sdev) return ssp_mask; if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_SSP)) { - ssp_mask = intel_nhlt_ssp_endpoint_mask(nhlt, NHLT_DEVICE_I2S); + ssp_mask = intel_nhlt_ssp_endpoint_mask(nhlt, device_type); if (ssp_mask) - dev_info(sdev->dev, "NHLT_DEVICE_I2S detected, ssp_mask %#x\n", ssp_mask); + dev_info(sdev->dev, "NHLT device %s(%d) detected, ssp_mask %#x\n", + device_type == NHLT_DEVICE_BT ? "BT" : "I2S", + device_type, ssp_mask); } return ssp_mask; @@ -1233,8 +1239,29 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) * Otherwise, set certain mach params. */ hda_generic_machine_select(sdev, &mach); - if (!mach) + if (!mach) { dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); + return NULL; + } + + /* report BT offload link mask to machine driver */ + mach->mach_params.bt_link_mask = check_nhlt_ssp_mask(sdev, NHLT_DEVICE_BT); + + dev_info(sdev->dev, "BT link detected in NHLT tables: %#x\n", + mach->mach_params.bt_link_mask); + + /* allow for module parameter override */ + if (bt_link_mask_override) { + dev_dbg(sdev->dev, "overriding BT link detected in NHLT tables %#x by kernel param %#x\n", + mach->mach_params.bt_link_mask, bt_link_mask_override); + mach->mach_params.bt_link_mask = bt_link_mask_override; + } + + if (hweight_long(mach->mach_params.bt_link_mask) > 1) { + dev_warn(sdev->dev, "invalid BT link mask %#x found, reset the mask\n", + mach->mach_params.bt_link_mask); + mach->mach_params.bt_link_mask = 0; + } /* * Fixup tplg file name by appending dmic num, ssp num, codec/amplifier @@ -1308,7 +1335,7 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) } /* report SSP link mask to machine driver */ - mach->mach_params.i2s_link_mask = check_nhlt_ssp_mask(sdev); + mach->mach_params.i2s_link_mask = check_nhlt_ssp_mask(sdev, NHLT_DEVICE_I2S); if (tplg_fixup && mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER &&