diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml | 27 | ||||
-rw-r--r-- | sound/soc/amd/Kconfig | 1 | ||||
-rw-r--r-- | sound/soc/mediatek/Kconfig | 4 | ||||
-rw-r--r-- | sound/soc/mediatek/common/mtk-soundcard-driver.c | 53 | ||||
-rw-r--r-- | sound/soc/mediatek/mt8188/mt8188-afe-common.h | 3 | ||||
-rw-r--r-- | sound/soc/mediatek/mt8188/mt8188-dai-adda.c | 73 | ||||
-rw-r--r-- | sound/soc/mediatek/mt8188/mt8188-mt6359.c | 386 | ||||
-rw-r--r-- | sound/soc/soc-dapm.c | 16 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-dai-ops.c | 58 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-dai.c | 64 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda.c | 25 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda.h | 19 | ||||
-rw-r--r-- | sound/soc/sof/intel/mtl.c | 12 | ||||
-rw-r--r-- | sound/soc/sof/intel/mtl.h | 7 | ||||
-rw-r--r-- | sound/soc/sof/ipc4-topology.c | 44 | ||||
-rw-r--r-- | sound/soc/sof/ipc4-topology.h | 74 |
16 files changed, 746 insertions, 120 deletions
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml index 6640272b3f4f..05e532b5d50a 100644 --- a/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml @@ -11,7 +11,9 @@ maintainers: properties: compatible: - const: mediatek,mt8188-mt6359-evb + enum: + - mediatek,mt8188-mt6359-evb + - mediatek,mt8188-nau8825 model: $ref: /schemas/types.yaml#/definitions/string @@ -42,7 +44,6 @@ patternProperties: we are going to update parameters in this node. items: enum: - - ADDA_BE - DPTX_BE - ETDM1_IN_BE - ETDM2_IN_BE @@ -62,11 +63,28 @@ patternProperties: required: - sound-dai + dai-format: + description: audio format. + items: + enum: + - i2s + - right_j + - left_j + - dsp_a + - dsp_b + + mediatek,clk-provider: + $ref: /schemas/types.yaml#/definitions/string + description: Indicates dai-link clock master. + items: + enum: + - cpu + - codec + additionalProperties: false required: - link-name - - codec additionalProperties: false @@ -87,7 +105,8 @@ examples: "AIN1", "Headset Mic"; dai-link-0 { link-name = "ETDM3_OUT_BE"; - + dai-format = "i2s"; + mediatek,clk-provider = "cpu"; codec { sound-dai = <&hdmi0>; }; diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index e724cb3c70b7..57d5e342a8eb 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -71,6 +71,7 @@ config SND_SOC_AMD_RENOIR_MACH config SND_SOC_AMD_ACP5x tristate "AMD Audio Coprocessor-v5.x I2S support" depends on X86 && PCI + select SND_AMD_ACP_CONFIG help This option enables ACP v5.x support on AMD platform diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 4baac72677d9..4ea012342b52 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -225,6 +225,10 @@ config SND_SOC_MT8188_MT6359 depends on SND_SOC_MT8188 && MTK_PMIC_WRAP select SND_SOC_MT6359 select SND_SOC_HDMI_CODEC + select SND_SOC_DMIC + select SND_SOC_MAX98390 + select SND_SOC_NAU8315 + select SND_SOC_NAU8825 help This adds support for ASoC machine driver for MediaTek MT8188 boards with the MT6359 and other I2S audio codecs. diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.c b/sound/soc/mediatek/common/mtk-soundcard-driver.c index 738093451ccb..a58e1e3674de 100644 --- a/sound/soc/mediatek/common/mtk-soundcard-driver.c +++ b/sound/soc/mediatek/common/mtk-soundcard-driver.c @@ -21,8 +21,10 @@ static int set_card_codec_info(struct snd_soc_card *card, int ret; codec_node = of_get_child_by_name(sub_node, "codec"); - if (!codec_node) - return -EINVAL; + if (!codec_node) { + dev_dbg(dev, "%s no specified codec\n", dai_link->name); + return 0; + } /* set card codec info */ ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link); @@ -36,6 +38,47 @@ static int set_card_codec_info(struct snd_soc_card *card, return 0; } +static int set_dailink_daifmt(struct snd_soc_card *card, + struct device_node *sub_node, + struct snd_soc_dai_link *dai_link) +{ + unsigned int daifmt; + const char *str; + int ret; + struct { + char *name; + unsigned int val; + } of_clk_table[] = { + { "cpu", SND_SOC_DAIFMT_CBC_CFC }, + { "codec", SND_SOC_DAIFMT_CBP_CFP }, + }; + + daifmt = snd_soc_daifmt_parse_format(sub_node, NULL); + if (daifmt) { + dai_link->dai_fmt &= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; + dai_link->dai_fmt |= daifmt; + } + + /* + * check "mediatek,clk-provider = xxx" + * SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK area + */ + ret = of_property_read_string(sub_node, "mediatek,clk-provider", &str); + if (ret == 0) { + int i; + + for (i = 0; i < ARRAY_SIZE(of_clk_table); i++) { + if (strcmp(str, of_clk_table[i].name) == 0) { + dai_link->dai_fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; + dai_link->dai_fmt |= of_clk_table[i].val; + break; + } + } + } + + return 0; +} + int parse_dai_link_info(struct snd_soc_card *card) { struct device *dev = card->dev; @@ -67,6 +110,12 @@ int parse_dai_link_info(struct snd_soc_card *card) of_node_put(sub_node); return ret; } + + ret = set_dailink_daifmt(card, sub_node, dai_link); + if (ret < 0) { + of_node_put(sub_node); + return ret; + } } return 0; diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-common.h b/sound/soc/mediatek/mt8188/mt8188-afe-common.h index eb7e57c239bd..1304d685a306 100644 --- a/sound/soc/mediatek/mt8188/mt8188-afe-common.h +++ b/sound/soc/mediatek/mt8188/mt8188-afe-common.h @@ -39,7 +39,7 @@ enum { MT8188_AFE_MEMIF_END, MT8188_AFE_MEMIF_NUM = (MT8188_AFE_MEMIF_END - MT8188_AFE_MEMIF_START), MT8188_AFE_IO_START = MT8188_AFE_MEMIF_END, - MT8188_AFE_IO_ADDA = MT8188_AFE_IO_START, + MT8188_AFE_IO_DL_SRC = MT8188_AFE_IO_START, MT8188_AFE_IO_DMIC_IN, MT8188_AFE_IO_DPTX, MT8188_AFE_IO_ETDM_START, @@ -52,6 +52,7 @@ enum { MT8188_AFE_IO_ETDM_NUM = (MT8188_AFE_IO_ETDM_END - MT8188_AFE_IO_ETDM_START), MT8188_AFE_IO_PCM = MT8188_AFE_IO_ETDM_END, + MT8188_AFE_IO_UL_SRC, MT8188_AFE_IO_END, MT8188_AFE_IO_NUM = (MT8188_AFE_IO_END - MT8188_AFE_IO_START), MT8188_DAI_END = MT8188_AFE_IO_END, diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-adda.c b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c index fed9f927e623..7dc029f2b428 100644 --- a/sound/soc/mediatek/mt8188/mt8188-dai-adda.c +++ b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c @@ -53,8 +53,7 @@ enum { }; struct mtk_dai_adda_priv { - unsigned int dl_rate; - unsigned int ul_rate; + bool hires_required; }; static unsigned int afe_adda_dl_rate_transform(struct mtk_base_afe *afe, @@ -241,42 +240,35 @@ static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w, return 0; } -static int mtk_afe_adc_hires_connect(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink) +static struct mtk_dai_adda_priv *get_adda_priv_by_name(struct mtk_base_afe *afe, + const char *name) { - struct snd_soc_dapm_widget *w = source; - struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); struct mt8188_afe_private *afe_priv = afe->platform_priv; - struct mtk_dai_adda_priv *adda_priv; - - adda_priv = afe_priv->dai_priv[MT8188_AFE_IO_ADDA]; - if (!adda_priv) { - dev_err(afe->dev, "%s adda_priv == NULL", __func__); - return 0; - } - - return !!(adda_priv->ul_rate > ADDA_HIRES_THRES); + if (strstr(name, "aud_adc_hires")) + return afe_priv->dai_priv[MT8188_AFE_IO_UL_SRC]; + else if (strstr(name, "aud_dac_hires")) + return afe_priv->dai_priv[MT8188_AFE_IO_DL_SRC]; + else + return NULL; } -static int mtk_afe_dac_hires_connect(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink) +static int mtk_afe_adda_hires_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) { struct snd_soc_dapm_widget *w = source; struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); - struct mt8188_afe_private *afe_priv = afe->platform_priv; struct mtk_dai_adda_priv *adda_priv; - adda_priv = afe_priv->dai_priv[MT8188_AFE_IO_ADDA]; + adda_priv = get_adda_priv_by_name(afe, w->name); if (!adda_priv) { - dev_err(afe->dev, "%s adda_priv == NULL", __func__); + dev_dbg(afe->dev, "adda_priv == NULL"); return 0; } - return !!(adda_priv->dl_rate > ADDA_HIRES_THRES); + return (adda_priv->hires_required) ? 1 : 0; } static const struct snd_kcontrol_new mtk_dai_adda_o176_mix[] = { @@ -361,7 +353,7 @@ static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = { {"ADDA Capture", NULL, "ADDA Capture Enable"}, {"ADDA Capture", NULL, "ADDA_MTKAIF_CFG"}, {"ADDA Capture", NULL, "aud_adc"}, - {"ADDA Capture", NULL, "aud_adc_hires", mtk_afe_adc_hires_connect}, + {"ADDA Capture", NULL, "aud_adc_hires", mtk_afe_adda_hires_connect}, {"I168", NULL, "ADDA Capture"}, {"I169", NULL, "ADDA Capture"}, @@ -369,7 +361,7 @@ static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = { {"ADDA Playback", NULL, "ADDA Enable"}, {"ADDA Playback", NULL, "ADDA Playback Enable"}, {"ADDA Playback", NULL, "aud_dac"}, - {"ADDA Playback", NULL, "aud_dac_hires", mtk_afe_dac_hires_connect}, + {"ADDA Playback", NULL, "aud_dac_hires", mtk_afe_adda_hires_connect}, {"DL_GAIN", NULL, "O176"}, {"DL_GAIN", NULL, "O177"}, @@ -503,13 +495,12 @@ static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream, dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %u\n", __func__, id, substream->stream, rate); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - adda_priv->dl_rate = rate; + adda_priv->hires_required = (rate > ADDA_HIRES_THRES); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ret = mtk_dai_da_configure(afe, rate, id); - } else { - adda_priv->ul_rate = rate; + else ret = mtk_dai_ad_configure(afe, rate, id); - } return ret; } @@ -536,8 +527,8 @@ static const struct snd_soc_dai_ops mtk_dai_adda_ops = { static struct snd_soc_dai_driver mtk_dai_adda_driver[] = { { - .name = "ADDA", - .id = MT8188_AFE_IO_ADDA, + .name = "DL_SRC", + .id = MT8188_AFE_IO_DL_SRC, .playback = { .stream_name = "ADDA Playback", .channels_min = 1, @@ -545,6 +536,11 @@ static struct snd_soc_dai_driver mtk_dai_adda_driver[] = { .rates = MTK_ADDA_PLAYBACK_RATES, .formats = MTK_ADDA_FORMATS, }, + .ops = &mtk_dai_adda_ops, + }, + { + .name = "UL_SRC", + .id = MT8188_AFE_IO_UL_SRC, .capture = { .stream_name = "ADDA Capture", .channels_min = 1, @@ -560,13 +556,18 @@ static int init_adda_priv_data(struct mtk_base_afe *afe) { struct mt8188_afe_private *afe_priv = afe->platform_priv; struct mtk_dai_adda_priv *adda_priv; + int adda_dai_list[] = {MT8188_AFE_IO_DL_SRC, MT8188_AFE_IO_UL_SRC}; + int i; - adda_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_adda_priv), - GFP_KERNEL); - if (!adda_priv) - return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(adda_dai_list); i++) { + adda_priv = devm_kzalloc(afe->dev, + sizeof(struct mtk_dai_adda_priv), + GFP_KERNEL); + if (!adda_priv) + return -ENOMEM; - afe_priv->dai_priv[MT8188_AFE_IO_ADDA] = adda_priv; + afe_priv->dai_priv[adda_dai_list[i]] = adda_priv; + } return 0; } diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c index 919d74ea1934..bc4b74970a46 100644 --- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -6,6 +6,7 @@ * Author: Trevor Wu <[email protected]> */ +#include <linux/input.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/pm_runtime.h> @@ -13,10 +14,27 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include "mt8188-afe-common.h" +#include "../../codecs/nau8825.h" #include "../../codecs/mt6359.h" #include "../common/mtk-afe-platform-driver.h" #include "../common/mtk-soundcard-driver.h" +#define NAU8825_HS_PRESENT BIT(0) + +/* + * Maxim MAX98390 + */ +#define MAX98390_CODEC_DAI "max98390-aif1" +#define MAX98390_DEV0_NAME "max98390.0-0038" /* rear right */ +#define MAX98390_DEV1_NAME "max98390.0-0039" /* rear left */ +#define MAX98390_DEV2_NAME "max98390.0-003a" /* front right */ +#define MAX98390_DEV3_NAME "max98390.0-003b" /* front left */ + +/* + * Nau88l25 + */ +#define NAU8825_CODEC_DAI "nau8825-hifi" + /* FE */ SND_SOC_DAILINK_DEFS(playback2, DAILINK_COMP_ARRAY(COMP_CPU("DL2")), @@ -99,8 +117,8 @@ SND_SOC_DAILINK_DEFS(capture10, DAILINK_COMP_ARRAY(COMP_EMPTY())); /* BE */ -SND_SOC_DAILINK_DEFS(adda, - DAILINK_COMP_ARRAY(COMP_CPU("ADDA")), +SND_SOC_DAILINK_DEFS(dl_src, + DAILINK_COMP_ARRAY(COMP_CPU("DL_SRC")), DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound", "mt6359-snd-codec-aif1")), DAILINK_COMP_ARRAY(COMP_EMPTY())); @@ -140,9 +158,44 @@ SND_SOC_DAILINK_DEFS(pcm1, DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); +SND_SOC_DAILINK_DEFS(ul_src, + DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC")), + DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound", + "mt6359-snd-codec-aif1"), + COMP_CODEC("dmic-codec", + "dmic-hifi")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + struct mt8188_mt6359_priv { struct snd_soc_jack dp_jack; struct snd_soc_jack hdmi_jack; + struct snd_soc_jack headset_jack; + void *private_data; +}; + +static struct snd_soc_jack_pin mt8188_hdmi_jack_pins[] = { + { + .pin = "HDMI", + .mask = SND_JACK_LINEOUT, + }, +}; + +static struct snd_soc_jack_pin mt8188_dp_jack_pins[] = { + { + .pin = "DP", + .mask = SND_JACK_LINEOUT, + }, +}; + +static struct snd_soc_jack_pin nau8825_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, }; struct mt8188_card_data { @@ -150,9 +203,39 @@ struct mt8188_card_data { unsigned long quirk; }; +static const struct snd_kcontrol_new mt8188_dumb_spk_controls[] = { + SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; + +static const struct snd_soc_dapm_widget mt8188_dumb_spk_widgets[] = { + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static const struct snd_kcontrol_new mt8188_dual_spk_controls[] = { + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; + +static const struct snd_soc_dapm_widget mt8188_dual_spk_widgets[] = { + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), +}; + +static const struct snd_kcontrol_new mt8188_rear_spk_controls[] = { + SOC_DAPM_PIN_SWITCH("Rear Left Spk"), + SOC_DAPM_PIN_SWITCH("Rear Right Spk"), +}; + +static const struct snd_soc_dapm_widget mt8188_rear_spk_widgets[] = { + SND_SOC_DAPM_SPK("Rear Left Spk", NULL), + SND_SOC_DAPM_SPK("Rear Right Spk", NULL), +}; + static const struct snd_soc_dapm_widget mt8188_mt6359_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SINK("HDMI"), + SND_SOC_DAPM_SINK("DP"), }; static const struct snd_kcontrol_new mt8188_mt6359_controls[] = { @@ -160,6 +243,14 @@ static const struct snd_kcontrol_new mt8188_mt6359_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), }; +static const struct snd_soc_dapm_widget mt8188_nau8825_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), +}; + +static const struct snd_kcontrol_new mt8188_nau8825_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), +}; + #define CKSYS_AUD_TOP_CFG 0x032c #define CKSYS_AUD_TOP_MON 0x0330 @@ -345,7 +436,7 @@ enum { DAI_LINK_UL8_FE, DAI_LINK_UL9_FE, DAI_LINK_UL10_FE, - DAI_LINK_ADDA_BE, + DAI_LINK_DL_SRC_BE, DAI_LINK_DPTX_BE, DAI_LINK_ETDM1_IN_BE, DAI_LINK_ETDM2_IN_BE, @@ -353,6 +444,7 @@ enum { DAI_LINK_ETDM2_OUT_BE, DAI_LINK_ETDM3_OUT_BE, DAI_LINK_PCM1_BE, + DAI_LINK_UL_SRC_BE, }; static int mt8188_dptx_hw_params(struct snd_pcm_substream *substream, @@ -389,8 +481,10 @@ static int mt8188_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret = 0; - ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, - &priv->hdmi_jack); + ret = snd_soc_card_jack_new_pins(rtd->card, "HDMI Jack", + SND_JACK_LINEOUT, &priv->hdmi_jack, + mt8188_hdmi_jack_pins, + ARRAY_SIZE(mt8188_hdmi_jack_pins)); if (ret) { dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret); return ret; @@ -410,8 +504,9 @@ static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret = 0; - ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT, - &priv->dp_jack); + ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_LINEOUT, + &priv->dp_jack, mt8188_dp_jack_pins, + ARRAY_SIZE(mt8188_dp_jack_pins)); if (ret) { dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret); return ret; @@ -425,6 +520,189 @@ static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd) return ret; } +static int mt8188_dumb_amp_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret = 0; + + ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_dumb_spk_widgets, + ARRAY_SIZE(mt8188_dumb_spk_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add Dumb Speaker dapm, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, mt8188_dumb_spk_controls, + ARRAY_SIZE(mt8188_dumb_spk_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add Dumb card controls, ret %d\n", ret); + return ret; + } + + return ret; +} + +static int mt8188_max98390_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int bit_width = params_width(params); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai; + int i; + + snd_soc_dai_set_tdm_slot(cpu_dai, 0xf, 0xf, 4, bit_width); + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + if (!strcmp(codec_dai->component->name, MAX98390_DEV0_NAME)) + snd_soc_dai_set_tdm_slot(codec_dai, 0x8, 0x3, 4, bit_width); + + if (!strcmp(codec_dai->component->name, MAX98390_DEV1_NAME)) + snd_soc_dai_set_tdm_slot(codec_dai, 0x4, 0x3, 4, bit_width); + + if (!strcmp(codec_dai->component->name, MAX98390_DEV2_NAME)) + snd_soc_dai_set_tdm_slot(codec_dai, 0x2, 0x3, 4, bit_width); + + if (!strcmp(codec_dai->component->name, MAX98390_DEV3_NAME)) + snd_soc_dai_set_tdm_slot(codec_dai, 0x1, 0x3, 4, bit_width); + } + return 0; +} + +static const struct snd_soc_ops mt8188_max98390_ops = { + .hw_params = mt8188_max98390_hw_params, +}; + +static int mt8188_max98390_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + /* add regular speakers dapm route */ + ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_dual_spk_widgets, + ARRAY_SIZE(mt8188_dual_spk_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add Left/Right Speaker widget, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, mt8188_dual_spk_controls, + ARRAY_SIZE(mt8188_dual_spk_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add Left/Right card controls, ret %d\n", ret); + return ret; + } + + if (rtd->dai_link->num_codecs <= 2) + return ret; + + /* add widgets/controls/dapm for rear speakers */ + ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_rear_spk_widgets, + ARRAY_SIZE(mt8188_rear_spk_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add Rear Speaker widget, ret %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_add_card_controls(card, mt8188_rear_spk_controls, + ARRAY_SIZE(mt8188_rear_spk_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add Rear card controls, ret %d\n", ret); + return ret; + } + + return ret; +} + +static int mt8188_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + struct snd_soc_jack *jack = &priv->headset_jack; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_nau8825_widgets, + ARRAY_SIZE(mt8188_nau8825_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add nau8825 card widget, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, mt8188_nau8825_controls, + ARRAY_SIZE(mt8188_nau8825_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add nau8825 card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + jack, + nau8825_jack_pins, + ARRAY_SIZE(nau8825_jack_pins)); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) { + dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); + return ret; + } + + return ret; +}; + +static void mt8188_nau8825_codec_exit(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + + snd_soc_component_set_jack(component, NULL, NULL); +} + +static int mt8188_nau8825_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + unsigned int rate = params_rate(params); + unsigned int bit_width = params_width(params); + int clk_freq, ret; + + clk_freq = rate * 2 * bit_width; + + /* Configure clock for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_BLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "can't set BCLK clock %d\n", ret); + return ret; + } + + /* Configure pll for codec */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_freq, + params_rate(params) * 256); + if (ret < 0) { + dev_err(codec_dai->dev, "can't set BCLK: %d\n", ret); + return ret; + } + + return ret; +} + +static const struct snd_soc_ops mt8188_nau8825_ops = { + .hw_params = mt8188_nau8825_hw_params, +}; static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { /* FE */ [DAI_LINK_DL2_FE] = { @@ -604,13 +882,11 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { SND_SOC_DAILINK_REG(capture10), }, /* BE */ - [DAI_LINK_ADDA_BE] = { - .name = "ADDA_BE", + [DAI_LINK_DL_SRC_BE] = { + .name = "DL_SRC_BE", .no_pcm = 1, .dpcm_playback = 1, - .dpcm_capture = 1, - .init = mt8188_mt6359_init, - SND_SOC_DAILINK_REG(adda), + SND_SOC_DAILINK_REG(dl_src), }, [DAI_LINK_DPTX_BE] = { .name = "DPTX_BE", @@ -676,8 +952,48 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { .dpcm_capture = 1, SND_SOC_DAILINK_REG(pcm1), }, + [DAI_LINK_UL_SRC_BE] = { + .name = "UL_SRC_BE", + .no_pcm = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(ul_src), + }, }; +static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name) +{ + struct snd_ctl_elem_id sid; + + memset(&sid, 0, sizeof(sid)); + strcpy(sid.name, name); + sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + return snd_ctl_find_id(card, &sid); +} + +static void mt8188_fixup_controls(struct snd_soc_card *card) +{ + struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(card); + struct mt8188_card_data *card_data = (struct mt8188_card_data *)priv->private_data; + struct snd_kcontrol *kctl; + + if (card_data->quirk & NAU8825_HS_PRESENT) { + struct snd_soc_dapm_widget *w, *next_w; + + for_each_card_widgets_safe(card, w, next_w) { + if (strcmp(w->name, "Headphone")) + continue; + + snd_soc_dapm_free_widget(w); + } + + kctl = ctl_find(card->snd_card, "Headphone Switch"); + if (kctl) + snd_ctl_remove(card->snd_card, kctl); + else + dev_warn(card->dev, "Cannot find ctl : Headphone Switch\n"); + } +} + static struct snd_soc_card mt8188_mt6359_soc_card = { .owner = THIS_MODULE, .dai_link = mt8188_mt6359_dai_links, @@ -686,6 +1002,7 @@ static struct snd_soc_card mt8188_mt6359_soc_card = { .num_dapm_widgets = ARRAY_SIZE(mt8188_mt6359_widgets), .controls = mt8188_mt6359_controls, .num_controls = ARRAY_SIZE(mt8188_mt6359_controls), + .fixup_controls = mt8188_fixup_controls, }; static int mt8188_mt6359_dev_probe(struct platform_device *pdev) @@ -695,6 +1012,10 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev) struct mt8188_mt6359_priv *priv; struct mt8188_card_data *card_data; struct snd_soc_dai_link *dai_link; + bool init_mt6359 = false; + bool init_nau8825 = false; + bool init_max98390 = false; + bool init_dumb = false; int ret, i; card_data = (struct mt8188_card_data *)of_device_get_match_data(&pdev->dev); @@ -739,9 +1060,41 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev) } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) dai_link->init = mt8188_hdmi_codec_init; + } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 || + strcmp(dai_link->name, "UL_SRC_BE") == 0) { + if (!init_mt6359) { + dai_link->init = mt8188_mt6359_init; + init_mt6359 = true; + } + } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 || + strcmp(dai_link->name, "ETDM2_OUT_BE") == 0 || + strcmp(dai_link->name, "ETDM1_IN_BE") == 0 || + strcmp(dai_link->name, "ETDM2_IN_BE") == 0) { + if (!strcmp(dai_link->codecs->dai_name, MAX98390_CODEC_DAI)) { + dai_link->ops = &mt8188_max98390_ops; + if (!init_max98390) { + dai_link->init = mt8188_max98390_codec_init; + init_max98390 = true; + } + } else if (!strcmp(dai_link->codecs->dai_name, NAU8825_CODEC_DAI)) { + dai_link->ops = &mt8188_nau8825_ops; + if (!init_nau8825) { + dai_link->init = mt8188_nau8825_codec_init; + dai_link->exit = mt8188_nau8825_codec_exit; + init_nau8825 = true; + } + } else { + if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) { + if (!init_dumb) { + dai_link->init = mt8188_dumb_amp_init; + init_dumb = true; + } + } + } } } + priv->private_data = card_data; snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); @@ -758,11 +1111,20 @@ static struct mt8188_card_data mt8188_evb_card = { .name = "mt8188_mt6359", }; +static struct mt8188_card_data mt8188_nau8825_card = { + .name = "mt8188_nau8825", + .quirk = NAU8825_HS_PRESENT, +}; + static const struct of_device_id mt8188_mt6359_dt_match[] = { { .compatible = "mediatek,mt8188-mt6359-evb", .data = &mt8188_evb_card, }, + { + .compatible = "mediatek,mt8188-nau8825", + .data = &mt8188_nau8825_card, + }, {}, }; MODULE_DEVICE_TABLE(of, mt8188_mt6359_dt_match); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f2f04ce693a1..c65cc374bb3f 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2216,6 +2216,16 @@ static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) &dapm_widget_power_fops); } +static void dapm_debugfs_free_widget(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + + if (!dapm->debugfs_dapm || !w->name) + return; + + debugfs_lookup_and_remove(w->name, dapm->debugfs_dapm); +} + static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) { debugfs_remove_recursive(dapm->debugfs_dapm); @@ -2232,6 +2242,10 @@ static inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) { } +static inline void dapm_debugfs_free_widget(struct snd_soc_dapm_widget *w) +{ +} + static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) { } @@ -2495,6 +2509,8 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) dapm_free_path(p); } + dapm_debugfs_free_widget(w); + kfree(w->kcontrols); kfree_const(w->name); kfree_const(w->sname); diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 1e58256c8003..f3513796c189 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -175,6 +175,50 @@ static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stre snd_hdac_ext_stream_reset(hext_stream); } +static void hda_codec_dai_set_stream(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct hdac_stream *hstream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + + /* set the hdac_stream in the codec dai */ + snd_soc_dai_set_stream(codec_dai, hstream, substream->stream); +} + +static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + unsigned int link_bps; + unsigned int format_val; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + link_bps = codec_dai->driver->playback.sig_bits; + else + link_bps = codec_dai->driver->capture.sig_bits; + + format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), + params_format(params), link_bps, 0); + + dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, + params_rate(params), params_channels(params), params_format(params)); + + return format_val; +} + +static struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct hdac_bus *bus = sof_to_bus(sdev); + + return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); +} + static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { @@ -307,7 +351,10 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { .reset_hext_stream = hda_reset_hext_stream, .pre_trigger = hda_ipc4_pre_trigger, .trigger = hda_trigger, - .post_trigger = hda_ipc4_post_trigger + .post_trigger = hda_ipc4_post_trigger, + .codec_dai_set_stream = hda_codec_dai_set_stream, + .calc_stream_format = hda_calc_stream_format, + .get_hlink = hda_get_hlink, }; static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { @@ -317,6 +364,9 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { .setup_hext_stream = hda_setup_hext_stream, .reset_hext_stream = hda_reset_hext_stream, .trigger = hda_trigger, + .codec_dai_set_stream = hda_codec_dai_set_stream, + .calc_stream_format = hda_calc_stream_format, + .get_hlink = hda_get_hlink, }; static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, @@ -350,6 +400,9 @@ static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = { .reset_hext_stream = hda_reset_hext_stream, .trigger = hda_trigger, .post_trigger = hda_ipc3_post_trigger, + .codec_dai_set_stream = hda_codec_dai_set_stream, + .calc_stream_format = hda_calc_stream_format, + .get_hlink = hda_get_hlink, }; static struct hdac_ext_stream * @@ -376,6 +429,9 @@ static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev, static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = { .get_hext_stream = hda_dspless_get_hext_stream, .setup_hext_stream = hda_dspless_setup_hext_stream, + .codec_dai_set_stream = hda_codec_dai_set_stream, + .calc_stream_format = hda_calc_stream_format, + .get_hlink = hda_get_hlink, }; #endif diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 09d8ee98581d..3297dea493aa 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -109,20 +109,22 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai static int hda_link_dma_cleanup(struct snd_pcm_substream *substream, struct hdac_ext_stream *hext_stream, - struct snd_soc_dai *cpu_dai, - struct snd_soc_dai *codec_dai) + struct snd_soc_dai *cpu_dai) { const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); - struct hdac_stream *hstream = &hext_stream->hstream; - struct hdac_bus *bus = hstream->bus; struct sof_intel_hda_stream *hda_stream; struct hdac_ext_link *hlink; struct snd_sof_dev *sdev; int stream_tag; + if (!ops) { + dev_err(cpu_dai->dev, "DAI widget ops not set\n"); + return -EINVAL; + } + sdev = dai_to_sdev(substream, cpu_dai); - hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); + hlink = ops->get_hlink(sdev, substream); if (!hlink) return -EINVAL; @@ -147,21 +149,20 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct hdac_ext_stream *hext_stream; struct hdac_stream *hstream; struct hdac_ext_link *hlink; struct snd_sof_dev *sdev; - struct hdac_bus *bus; - unsigned int format_val; - unsigned int link_bps; int stream_tag; + if (!ops) { + dev_err(cpu_dai->dev, "DAI widget ops not set\n"); + return -EINVAL; + } + sdev = dai_to_sdev(substream, cpu_dai); - bus = sof_to_bus(sdev); - hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); + hlink = ops->get_hlink(sdev, substream); if (!hlink) return -EINVAL; @@ -182,24 +183,17 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag); /* set the hdac_stream in the codec dai */ - snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - link_bps = codec_dai->driver->playback.sig_bits; - else - link_bps = codec_dai->driver->capture.sig_bits; + if (ops->codec_dai_set_stream) + ops->codec_dai_set_stream(sdev, substream, hstream); if (ops->reset_hext_stream) ops->reset_hext_stream(sdev, hext_stream); - format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), - params_format(params), link_bps, 0); + if (ops->calc_stream_format && ops->setup_hext_stream) { + unsigned int format_val = ops->calc_stream_format(sdev, substream, params); - dev_dbg(bus->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, - params_rate(params), params_channels(params), params_format(params)); - - if (ops->setup_hext_stream) ops->setup_hext_stream(sdev, hext_stream, format_val); + } hext_stream->link_prepared = 1; @@ -210,13 +204,11 @@ static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct hdac_ext_stream *hext_stream; struct snd_sof_dev *sdev = dai_to_sdev(substream, cpu_dai); if (!ops) { - dev_err(sdev->dev, "DAI widget ops not set\n"); + dev_err(cpu_dai->dev, "DAI widget ops not set\n"); return -EINVAL; } @@ -224,7 +216,7 @@ static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream, if (!hext_stream) return 0; - return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai); + return hda_link_dma_cleanup(substream, hext_stream, cpu_dai); } static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream, @@ -269,11 +261,14 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i { const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); struct hdac_ext_stream *hext_stream; - struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai *codec_dai; struct snd_sof_dev *sdev; int ret; + if (!ops) { + dev_err(dai->dev, "DAI widget ops not set\n"); + return -EINVAL; + } + dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd, dai->name, substream->stream); @@ -283,9 +278,6 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i if (!hext_stream) return -EINVAL; - rtd = asoc_substream_to_rtd(substream); - codec_dai = asoc_rtd_to_codec(rtd, 0); - if (ops->pre_trigger) { ret = ops->pre_trigger(sdev, dai, substream, cmd); if (ret < 0) @@ -306,7 +298,7 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i switch (cmd) { case SNDRV_PCM_TRIGGER_SUSPEND: - ret = hda_link_dma_cleanup(substream, hext_stream, dai, codec_dai); + ret = hda_link_dma_cleanup(substream, hext_stream, dai); if (ret < 0) { dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__); return ret; @@ -360,14 +352,12 @@ static int hda_dai_suspend(struct hdac_bus *bus) const struct hda_dai_widget_dma_ops *ops; struct snd_sof_widget *swidget; struct snd_soc_dapm_widget *w; - struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; struct snd_sof_dev *sdev; struct snd_sof_dai *sdai; rtd = asoc_substream_to_rtd(hext_stream->link_substream); cpu_dai = asoc_rtd_to_cpu(rtd, 0); - codec_dai = asoc_rtd_to_codec(rtd, 0); w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction); swidget = w->dobj.private; sdev = widget_to_sdev(w); @@ -376,7 +366,7 @@ static int hda_dai_suspend(struct hdac_bus *bus) ret = hda_link_dma_cleanup(hext_stream->link_substream, hext_stream, - cpu_dai, codec_dai); + cpu_dai); if (ret < 0) return ret; diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 835c2568dd60..066f27e3402f 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -222,6 +222,31 @@ int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev) return 0; } +int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + struct sdw_intel_ctx *ctx; + struct hdac_bus *bus; + u32 slcount; + + bus = sof_to_bus(sdev); + + hdev = sdev->pdata->hw_pdata; + ctx = hdev->sdw; + + slcount = hdac_bus_eml_get_count(bus, true, AZX_REG_ML_LEPTR_ID_SDW); + + /* Check HW supported vs property value */ + if (slcount < ctx->count) { + dev_err(sdev->dev, + "%s: BIOS master count %d is larger than hardware capabilities %d\n", + __func__, ctx->count, slcount); + return -EINVAL; + } + + return 0; +} + static int hda_sdw_check_lcount(struct snd_sof_dev *sdev) { const struct sof_intel_dsp_desc *chip; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 5b3dad2dadf4..3f7c6fb05e5d 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -781,6 +781,7 @@ int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev); +int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev); int hda_sdw_startup(struct snd_sof_dev *sdev); void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable); void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable); @@ -794,6 +795,11 @@ static inline int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev) return 0; } +static inline int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev) +{ + return 0; +} + static inline int hda_sdw_startup(struct snd_sof_dev *sdev) { return 0; @@ -919,6 +925,11 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, * @pre_trigger: Function pointer for DAI DMA pre-trigger actions * @trigger: Function pointer for DAI DMA trigger actions * @post_trigger: Function pointer for DAI DMA post-trigger actions + * @codec_dai_set_stream: Function pointer to set codec-side stream information + * @calc_stream_format: Function pointer to determine stream format from hw_params and + * for HDaudio codec DAI from the .sig bits + * @get_hlink: Mandatory function pointer to retrieve hlink, mainly to program LOSIDV + * for legacy HDaudio links or program HDaudio Extended Link registers. */ struct hda_dai_widget_dma_ops { struct hdac_ext_stream *(*get_hext_stream)(struct snd_sof_dev *sdev, @@ -938,6 +949,14 @@ struct hda_dai_widget_dma_ops { struct snd_pcm_substream *substream, int cmd); int (*post_trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd); + void (*codec_dai_set_stream)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct hdac_stream *hstream); + unsigned int (*calc_stream_format)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); + struct hdac_ext_link * (*get_hlink)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); }; const struct hda_dai_widget_dma_ops * diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 93dc2c9d8448..8ae331faca4e 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -55,7 +55,7 @@ static void mtl_ipc_dsp_done(struct snd_sof_dev *sdev) } /* Check if an IPC IRQ occurred */ -static bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev) +bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev) { u32 irq_status; u32 hfintipptr; @@ -118,7 +118,7 @@ static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *ms return 0; } -static void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev) +void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; @@ -132,7 +132,7 @@ static void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev) MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE); } -static void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev) +void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; @@ -173,7 +173,7 @@ static void mtl_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable) enable ? "enable" : "disable"); } -static int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) +int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) { u32 hfintipptr; u32 irqinten; @@ -394,7 +394,7 @@ static int mtl_dsp_core_power_down(struct snd_sof_dev *sdev, int core) return ret; } -static int mtl_power_down_dsp(struct snd_sof_dev *sdev) +int mtl_power_down_dsp(struct snd_sof_dev *sdev) { u32 dsphfdsscs, cpa; int ret; @@ -421,7 +421,7 @@ static int mtl_power_down_dsp(struct snd_sof_dev *sdev) HDA_DSP_RESET_TIMEOUT_US); } -static int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot) +int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h index 26418fb08807..2794fe6e8139 100644 --- a/sound/soc/sof/intel/mtl.h +++ b/sound/soc/sof/intel/mtl.h @@ -82,3 +82,10 @@ #define MTL_DSP_REG_HfIMRIS1 0x162088 #define MTL_DSP_REG_HfIMRIS1_IU_MASK BIT(0) +void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev); +void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev); +bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev); + +int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable); +int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); +int mtl_power_down_dsp(struct snd_sof_dev *sdev); diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index db64e0cb8663..a4e1a70b607d 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -559,7 +559,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) strcmp(w->widget->sname, swidget->widget->sname)) continue; - blob->alh_cfg.count++; + blob->alh_cfg.device_count++; } ipc4_copier->copier_config = (uint32_t *)blob; @@ -1225,7 +1225,7 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) unsigned int group_id; blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config; - if (blob->alh_cfg.count > 1) { + if (blob->alh_cfg.device_count > 1) { group_id = SOF_IPC4_NODE_INDEX(ipc4_copier->data.gtw_cfg.node_id) - ALH_MULTI_GTW_BASE; ida_free(&alh_group_ida, group_id); @@ -1383,6 +1383,8 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_sof_dai *dai; struct snd_mask *fmt; int out_sample_valid_bits; + u32 gtw_cfg_config_length; + u32 dma_config_tlv_size = 0; void **ipc_config_data; int *ipc_config_size; u32 **data; @@ -1609,7 +1611,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ch_map >>= 4; } - step = ch_count / blob->alh_cfg.count; + step = ch_count / blob->alh_cfg.device_count; mask = GENMASK(step - 1, 0); /* * Set each gtw_cfg.node_id to blob->alh_cfg.mapping[] @@ -1624,7 +1626,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, dai = w->private; alh_copier = (struct sof_ipc4_copier *)dai->private; alh_data = &alh_copier->data; - blob->alh_cfg.mapping[i].alh_id = alh_data->gtw_cfg.node_id; + blob->alh_cfg.mapping[i].device = alh_data->gtw_cfg.node_id; /* * Set the same channel mask for playback as the audio data is * duplicated for all speakers. For capture, split the channels @@ -1643,7 +1645,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, i++; } - if (blob->alh_cfg.count > 1) { + if (blob->alh_cfg.device_count > 1) { int group_id; group_id = ida_alloc_max(&alh_group_ida, ALH_MULTI_GTW_COUNT - 1, @@ -1699,7 +1701,27 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ipc_config_data = &ipc4_copier->ipc_config_data; /* config_length is DWORD based */ - ipc_size = sizeof(*copier_data) + copier_data->gtw_cfg.config_length * 4; + gtw_cfg_config_length = copier_data->gtw_cfg.config_length * 4; + ipc_size = sizeof(*copier_data) + gtw_cfg_config_length; + + if (ipc4_copier->dma_config_tlv.type == SOF_IPC4_GTW_DMA_CONFIG_ID && + ipc4_copier->dma_config_tlv.length) { + dma_config_tlv_size = sizeof(ipc4_copier->dma_config_tlv) + + ipc4_copier->dma_config_tlv.dma_config.dma_priv_config_size; + + /* paranoia check on TLV size/length */ + if (dma_config_tlv_size != ipc4_copier->dma_config_tlv.length + + sizeof(uint32_t) * 2) { + dev_err(sdev->dev, "Invalid configuration, TLV size %d length %d\n", + dma_config_tlv_size, ipc4_copier->dma_config_tlv.length); + return -EINVAL; + } + + ipc_size += dma_config_tlv_size; + + /* we also need to increase the size at the gtw level */ + copier_data->gtw_cfg.config_length += dma_config_tlv_size / 4; + } dev_dbg(sdev->dev, "copier %s, IPC size is %d", swidget->widget->name, ipc_size); @@ -1711,9 +1733,15 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, /* copy IPC data */ memcpy(*ipc_config_data, (void *)copier_data, sizeof(*copier_data)); - if (copier_data->gtw_cfg.config_length) + if (gtw_cfg_config_length) memcpy(*ipc_config_data + sizeof(*copier_data), - *data, copier_data->gtw_cfg.config_length * 4); + *data, gtw_cfg_config_length); + + /* add DMA Config TLV, if configured */ + if (dma_config_tlv_size) + memcpy(*ipc_config_data + sizeof(*copier_data) + + gtw_cfg_config_length, + &ipc4_copier->dma_config_tlv, dma_config_tlv_size); /* update pipeline memory usage */ sof_ipc4_update_resource_usage(sdev, swidget, &copier_data->base_config); diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index cf007282867b..6dcf14886e85 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -55,7 +55,7 @@ #define SOF_IPC4_GAIN_ALL_CHANNELS_MASK 0xffffffff #define SOF_IPC4_VOL_ZERO_DB 0x7fffffff -#define ALH_MAX_NUMBER_OF_GTW 16 +#define SOF_IPC4_DMA_DEVICE_MAX_COUNT 16 #define SOF_IPC4_INVALID_NODE_ID 0xffffffff @@ -220,18 +220,64 @@ struct sof_ipc4_gtw_attributes { uint32_t rsvd : 30; }; -/** struct sof_ipc4_alh_multi_gtw_cfg: ALH gateway cfg data - * @count: Number of streams (valid items in mapping array) - * @alh_id: ALH stream id of a single ALH stream aggregated - * @channel_mask: Channel mask - * @mapping: ALH streams +/** + * struct sof_ipc4_dma_device_stream_ch_map: abstract representation of + * channel mapping to DMAs + * @device: representation of hardware device address or FIFO + * @channel_mask: channels handled by @device. Channels are expected to be + * contiguous + */ +struct sof_ipc4_dma_device_stream_ch_map { + uint32_t device; + uint32_t channel_mask; +}; + +/** + * struct sof_ipc4_dma_stream_ch_map: DMA configuration data + * @device_count: Number valid items in mapping array + * @mapping: device address and channel mask + */ +struct sof_ipc4_dma_stream_ch_map { + uint32_t device_count; + struct sof_ipc4_dma_device_stream_ch_map mapping[SOF_IPC4_DMA_DEVICE_MAX_COUNT]; +} __packed; + +#define SOF_IPC4_DMA_METHOD_HDA 1 +#define SOF_IPC4_DMA_METHOD_GPDMA 2 /* defined for consistency but not used */ + +/** + * struct sof_ipc4_dma_config: DMA configuration + * @dma_method: HDAudio or GPDMA + * @pre_allocated_by_host: 1 if host driver allocates DMA channels, 0 otherwise + * @dma_channel_id: for HDaudio defined as @stream_id - 1 + * @stream_id: HDaudio stream tag + * @dma_stream_channel_map: array of device/channel mappings + * @dma_priv_config_size: currently not used + * @dma_priv_config: currently not used + */ +struct sof_ipc4_dma_config { + uint8_t dma_method; + uint8_t pre_allocated_by_host; + uint16_t rsvd; + uint32_t dma_channel_id; + uint32_t stream_id; + struct sof_ipc4_dma_stream_ch_map dma_stream_channel_map; + uint32_t dma_priv_config_size; + uint8_t dma_priv_config[]; +} __packed; + +#define SOF_IPC4_GTW_DMA_CONFIG_ID 0x1000 + +/** + * struct sof_ipc4_dma_config: DMA configuration + * @type: set to SOF_IPC4_GTW_DMA_CONFIG_ID + * @length: sizeof(struct sof_ipc4_dma_config) + dma_config.dma_priv_config_size + * @dma_config: actual DMA configuration */ -struct sof_ipc4_alh_multi_gtw_cfg { - uint32_t count; - struct { - uint32_t alh_id; - uint32_t channel_mask; - } mapping[ALH_MAX_NUMBER_OF_GTW]; +struct sof_ipc4_dma_config_tlv { + uint32_t type; + uint32_t length; + struct sof_ipc4_dma_config dma_config; } __packed; /** struct sof_ipc4_alh_configuration_blob: ALH blob @@ -240,7 +286,7 @@ struct sof_ipc4_alh_multi_gtw_cfg { */ struct sof_ipc4_alh_configuration_blob { struct sof_ipc4_gtw_attributes gw_attr; - struct sof_ipc4_alh_multi_gtw_cfg alh_cfg; + struct sof_ipc4_dma_stream_ch_map alh_cfg; }; /** @@ -254,6 +300,7 @@ struct sof_ipc4_alh_configuration_blob { * @gtw_attr: Gateway attributes for copier blob * @dai_type: DAI type * @dai_index: DAI index + * @dma_config_tlv: DMA configuration */ struct sof_ipc4_copier { struct sof_ipc4_copier_data data; @@ -266,6 +313,7 @@ struct sof_ipc4_copier { struct sof_ipc4_gtw_attributes *gtw_attr; u32 dai_type; int dai_index; + struct sof_ipc4_dma_config_tlv dma_config_tlv; }; /** |