diff options
43 files changed, 864 insertions, 126 deletions
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 92ffc4373f6d..a9a089566b7c 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -3075,6 +3075,7 @@ #define PCI_DEVICE_ID_INTEL_5100_22 0x65f6 #define PCI_DEVICE_ID_INTEL_IOAT_SCNB 0x65ff #define PCI_DEVICE_ID_INTEL_HDA_FCL 0x67a8 +#define PCI_DEVICE_ID_INTEL_HDA_NVL_S 0x6e50 #define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 #define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 #define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index daed7123df9d..382029724e85 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -34,6 +34,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_sdw_machines[]; @@ -46,6 +47,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_sdw_machines[]; /* * generic table used for HDA codec-based platforms, possibly with diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index 0fbcdb15c74b..29d15ba65f04 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -197,7 +197,6 @@ struct tasdevice_priv { struct acoustic_data acou_data; #endif struct tasdevice_fw *fmw; - struct gpio_desc *speaker_id; struct gpio_desc *reset; struct mutex codec_lock; struct regmap *regmap; @@ -215,6 +214,7 @@ struct tasdevice_priv { unsigned int magic_num; unsigned int chip_id; unsigned int sysclk; + int speaker_id; int irq; int cur_prog; diff --git a/sound/ac97_bus.c b/sound/ac97_bus.c index 1484fc178fa4..8a44297964f5 100644 --- a/sound/ac97_bus.c +++ b/sound/ac97_bus.c @@ -46,11 +46,14 @@ static bool snd_ac97_check_id(struct snd_ac97 *ac97, unsigned int id, * @id_mask: Mask that is applied to the device ID before comparing to @id * * This function resets the AC'97 device. If @try_warm is true the function - * first performs a warm reset. If the warm reset is successful the function - * returns 1. Otherwise or if @try_warm is false the function issues cold reset - * followed by a warm reset. If this is successful the function returns 0, - * otherwise a negative error code. If @id is 0 any valid device ID will be - * accepted, otherwise only the ID that matches @id and @id_mask is accepted. + * first performs a warm reset. If @try_warm is false the function issues + * cold reset followed by a warm reset. If @id is 0 any valid device ID + * will be accepted, otherwise only the ID that matches @id and @id_mask + * is accepted. + * Returns: + * * %1 - if warm reset is successful + * * %0 - if cold reset and warm reset is successful + * * %-ENODEV - if @id and @id_mask not matching */ int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id, unsigned int id_mask) diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c index 688eefce82fa..cb2eebaac85f 100644 --- a/sound/core/pcm_drm_eld.c +++ b/sound/core/pcm_drm_eld.c @@ -379,7 +379,7 @@ int snd_parse_eld(struct device *dev, struct snd_parsed_hdmi_eld *e, * in console or for audio devices. Assume the highest speakers * configuration, to _not_ prohibit multi-channel audio playback. */ - if (!e->spk_alloc) + if (!e->spk_alloc && e->sad_count) e->spk_alloc = 0xffff; return 0; diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c index 19b3f306c564..b8474631f0b5 100644 --- a/sound/drivers/pcmtest.c +++ b/sound/drivers/pcmtest.c @@ -696,10 +696,10 @@ static int setup_patt_bufs(void) size_t i; for (i = 0; i < ARRAY_SIZE(patt_bufs); i++) { - patt_bufs[i].buf = kzalloc(MAX_PATTERN_LEN, GFP_KERNEL); + patt_bufs[i].buf = kmalloc(MAX_PATTERN_LEN, GFP_KERNEL); if (!patt_bufs[i].buf) break; - strcpy(patt_bufs[i].buf, DEFAULT_PATTERN); + strscpy_pad(patt_bufs[i].buf, DEFAULT_PATTERN, MAX_PATTERN_LEN); patt_bufs[i].len = DEFAULT_PATTERN_LEN; } diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 36e25a3cf3c6..478cd7a08fb5 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -2,5 +2,5 @@ snd-dice-y := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \ dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \ dice-alesis.o dice-extension.o dice-mytek.o dice-presonus.o \ - dice-harman.o dice-focusrite.o dice-weiss.o + dice-harman.o dice-focusrite.o dice-weiss.o dice-teac.o obj-$(CONFIG_SND_DICE) += snd-dice.o diff --git a/sound/firewire/dice/dice-teac.c b/sound/firewire/dice/dice-teac.c new file mode 100644 index 000000000000..29febddfe3a5 --- /dev/null +++ b/sound/firewire/dice/dice-teac.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +// dice-teac.c - a part of driver for DICE based devices +// +// Copyright (c) 2025 Takashi Sakamoto + +#include "dice.h" + +int snd_dice_detect_teac_formats(struct snd_dice *dice) +{ + __be32 reg; + u32 data; + int err; + + err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®, sizeof(reg)); + if (err < 0) + return err; + + dice->tx_pcm_chs[0][SND_DICE_RATE_MODE_LOW] = 16; + dice->tx_pcm_chs[0][SND_DICE_RATE_MODE_MIDDLE] = 16; + dice->tx_midi_ports[0] = 1; + + data = be32_to_cpu(reg); + if (data > 1) { + dice->tx_pcm_chs[1][SND_DICE_RATE_MODE_LOW] = 16; + dice->tx_pcm_chs[1][SND_DICE_RATE_MODE_MIDDLE] = 16; + } + + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®, sizeof(reg)); + if (err < 0) + return err; + + dice->rx_pcm_chs[0][SND_DICE_RATE_MODE_LOW] = 16; + dice->rx_pcm_chs[0][SND_DICE_RATE_MODE_MIDDLE] = 16; + dice->rx_midi_ports[0] = 1; + + data = be32_to_cpu(reg); + if (data > 1) { + dice->rx_pcm_chs[1][SND_DICE_RATE_MODE_LOW] = 16; + dice->rx_pcm_chs[1][SND_DICE_RATE_MODE_MIDDLE] = 16; + } + + return 0; +} diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index bcbe80344328..85d265c7d544 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -22,6 +22,7 @@ MODULE_LICENSE("GPL"); #define OUI_PRESONUS 0x000a92 #define OUI_HARMAN 0x000fd7 #define OUI_AVID 0x00a07e +#define OUI_TEAC 0x00022e #define DICE_CATEGORY_ID 0x04 #define WEISS_CATEGORY_ID 0x00 @@ -458,6 +459,18 @@ static const struct ieee1394_device_id dice_id_table[] = { .match_flags = IEEE1394_MATCH_VERSION, .version = DICE_INTERFACE, }, + // Tascam IF-FW/DM MkII for DM-3200 and DM-4800. + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = OUI_TEAC, + .model_id = OUI_TEAC, + .specifier_id = OUI_TEAC, + .version = 0x800006, + .driver_data = (kernel_ulong_t)snd_dice_detect_teac_formats, + }, { } }; MODULE_DEVICE_TABLE(ieee1394, dice_id_table); diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 4c0ad7335998..7744ea6a0791 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -233,5 +233,6 @@ int snd_dice_detect_presonus_formats(struct snd_dice *dice); int snd_dice_detect_harman_formats(struct snd_dice *dice); int snd_dice_detect_focusrite_pro40_tcd3070_formats(struct snd_dice *dice); int snd_dice_detect_weiss_formats(struct snd_dice *dice); +int snd_dice_detect_teac_formats(struct snd_dice *dice); #endif diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c index 23237d527430..9460c8db39a9 100644 --- a/sound/hda/codecs/hdmi/intelhdmi.c +++ b/sound/hda/codecs/hdmi/intelhdmi.c @@ -793,6 +793,7 @@ static const struct hda_device_id snd_hda_id_intelhdmi[] = { HDA_CODEC_ID_MODEL(0x80862820, "Lunar Lake HDMI", MODEL_ADLP), HDA_CODEC_ID_MODEL(0x80862822, "Panther Lake HDMI", MODEL_ADLP), HDA_CODEC_ID_MODEL(0x80862823, "Wildcat Lake HDMI", MODEL_ADLP), + HDA_CODEC_ID_MODEL(0x80862824, "Nova Lake HDMI", MODEL_ADLP), HDA_CODEC_ID_MODEL(0x80862882, "Valleyview2 HDMI", MODEL_BYT), HDA_CODEC_ID_MODEL(0x80862883, "Braswell HDMI", MODEL_BYT), {} /* terminator */ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index b45fcc9a3785..1a8fb43887b3 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3406,7 +3406,42 @@ static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec, spec->power_hook = alc287_s4_power_gpio3_default; spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook; } +/* GPIO2: mute led GPIO3: micmute led */ +static void alc245_tas2781_spi_hp_fixup_muteled(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const hda_nid_t conn[] = { 0x02 }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.auto_mute_via_amp = 1; + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + break; + } + + tas2781_fixup_spi(codec, fix, action); + alc_fixup_hp_gpio_led(codec, action, 0x04, 0x0); + alc285_fixup_hp_coef_micmute_led(codec, fix, action); +} +/* JD2: mute led GPIO3: micmute led */ +static void alc245_tas2781_i2c_hp_fixup_muteled(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const hda_nid_t conn[] = { 0x02 }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.auto_mute_via_amp = 1; + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + break; + } + tas2781_fixup_txnw_i2c(codec, fix, action); + alc245_fixup_hp_mute_led_coefbit(codec, fix, action); + alc285_fixup_hp_coef_micmute_led(codec, fix, action); +} /* * Clear COEF 0x0d (PCBEEP passthrough) bit 0x40 where BIOS sets it wrongly * at PM resume @@ -3418,6 +3453,21 @@ static void alc283_fixup_dell_hp_resume(struct hda_codec *codec, alc_write_coef_idx(codec, 0xd, 0x2800); } +/* Swap DAC assignments for HP and speaker */ +static void alc288_fixup_surface_swap_dacs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static hda_nid_t preferred_pairs[] = { + 0x21, 0x03, 0x14, 0x02, 0 + }; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + spec->gen.preferred_dacs = preferred_pairs; +} + enum { ALC269_FIXUP_GPIO2, ALC269_FIXUP_SONY_VAIO, @@ -3737,6 +3787,10 @@ enum { ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC, ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK, ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE, + ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED, + ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED, + ALC288_FIXUP_SURFACE_SWAP_DACS, + ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -5270,6 +5324,12 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc236_fixup_hp_mute_led_micmute_vref, }, + [ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc236_fixup_hp_mute_led_coefbit2, + .chained = true, + .chain_id = ALC236_FIXUP_HP_GPIO_LED, + }, [ALC236_FIXUP_LENOVO_INV_DMIC] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic, @@ -6183,7 +6243,19 @@ static const struct hda_fixup alc269_fixups[] = { }, .chained = true, .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST - } + }, + [ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_tas2781_spi_hp_fixup_muteled, + }, + [ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_tas2781_i2c_hp_fixup_muteled, + }, + [ALC288_FIXUP_SURFACE_SWAP_DACS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc288_fixup_surface_swap_dacs, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -6696,15 +6768,19 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8e60, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8ed5, "HP Merino13X", ALC245_FIXUP_TAS2781_SPI_2), - SND_PCI_QUIRK(0x103c, 0x8ed6, "HP Merino13", ALC245_FIXUP_TAS2781_SPI_2), - SND_PCI_QUIRK(0x103c, 0x8ed7, "HP Merino14", ALC245_FIXUP_TAS2781_SPI_2), - SND_PCI_QUIRK(0x103c, 0x8ed8, "HP Merino16", ALC245_FIXUP_TAS2781_SPI_2), - SND_PCI_QUIRK(0x103c, 0x8ed9, "HP Merino14W", ALC245_FIXUP_TAS2781_SPI_2), - SND_PCI_QUIRK(0x103c, 0x8eda, "HP Merino16W", ALC245_FIXUP_TAS2781_SPI_2), - SND_PCI_QUIRK(0x103c, 0x8f40, "HP Lampas14", ALC287_FIXUP_TXNW2781_I2C), - SND_PCI_QUIRK(0x103c, 0x8f41, "HP Lampas16", ALC287_FIXUP_TXNW2781_I2C), - SND_PCI_QUIRK(0x103c, 0x8f42, "HP LampasW14", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x103c, 0x8ec1, "HP 200 G2i", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ed5, "HP EliteBook 8 Flip G2i 13", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8ed6, "HP EliteBook 8 G2i 13", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8ed7, "HP EliteBook 8 G2i 14", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8ed8, "HP EliteBook 8 G2i 16", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8ed9, "HP ZBook Firefly 14W", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8eda, "HP ZBook Firefly 16W", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8f0c, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8f0e, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8f40, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8f41, "HP ZBook 8 G2a 16", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8f42, "HP ZBook 8 G2a 14W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8f62, "HP ZBook 8 G2a 16W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x1032, "ASUS VivoBook X513EA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1034, "ASUS GU605C", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), @@ -6736,6 +6812,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1313, "Asus K42JZ", ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1314, "ASUS GA605K", ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1384, "ASUS RC73XA", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x1394, "ASUS RC73YA", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK), SND_PCI_QUIRK(0x1043, 0x1433, "ASUS GX650PY/PZ/PV/PU/PYV/PZV/PIV/PVV", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC), @@ -6883,6 +6961,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x12f6, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_ASPIRE_HEADSET_MIC), + SND_PCI_QUIRK(0x1414, 0x9c20, "Microsoft Surface Pro 2/3", ALC288_FIXUP_SURFACE_SWAP_DACS), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc176, "Samsung Notebook 9 Pro (NP930MBE-K04US)", ALC298_FIXUP_SAMSUNG_AMP), diff --git a/sound/hda/codecs/senarytech.c b/sound/hda/codecs/senarytech.c index 9aa1e9bcd9ec..63cda57cf786 100644 --- a/sound/hda/codecs/senarytech.c +++ b/sound/hda/codecs/senarytech.c @@ -19,6 +19,9 @@ #include "hda_jack.h" #include "generic.h" +/* GPIO node ID */ +#define SENARY_GPIO_NODE 0x01 + struct senary_spec { struct hda_gen_spec gen; @@ -120,11 +123,11 @@ static void senary_init_gpio_led(struct hda_codec *codec) unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask; if (mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, + snd_hda_codec_write(codec, SENARY_GPIO_NODE, 0, AC_VERB_SET_GPIO_MASK, mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, + snd_hda_codec_write(codec, SENARY_GPIO_NODE, 0, AC_VERB_SET_GPIO_DIRECTION, mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + snd_hda_codec_write(codec, SENARY_GPIO_NODE, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_led); } } diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index 0357401a6023..c8619995b1d7 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -87,6 +87,7 @@ static const struct acpi_gpio_mapping tas2781_speaker_id_gpios[] = { static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) { + struct gpio_desc *speaker_id; struct acpi_device *adev; struct device *physdev; LIST_HEAD(resources); @@ -119,19 +120,31 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) /* Speaker id was needed for ASUS projects. */ ret = kstrtou32(sub, 16, &subid); if (!ret && upper_16_bits(subid) == PCI_VENDOR_ID_ASUSTEK) { - ret = devm_acpi_dev_add_driver_gpios(p->dev, - tas2781_speaker_id_gpios); - if (ret < 0) + ret = acpi_dev_add_driver_gpios(adev, tas2781_speaker_id_gpios); + if (ret < 0) { dev_err(p->dev, "Failed to add driver gpio %d.\n", ret); - p->speaker_id = devm_gpiod_get(p->dev, "speakerid", GPIOD_IN); - if (IS_ERR(p->speaker_id)) { - dev_err(p->dev, "Failed to get Speaker id.\n"); - ret = PTR_ERR(p->speaker_id); - goto err; + p->speaker_id = -1; + goto end_2563; + } + + speaker_id = fwnode_gpiod_get_index(acpi_fwnode_handle(adev), + "speakerid", 0, GPIOD_IN, NULL); + if (!IS_ERR(speaker_id)) { + p->speaker_id = gpiod_get_value_cansleep(speaker_id); + dev_dbg(p->dev, "Got speaker id gpio from ACPI: %d.\n", + p->speaker_id); + gpiod_put(speaker_id); + } else { + p->speaker_id = -1; + ret = PTR_ERR(speaker_id); + dev_err(p->dev, "Get speaker id gpio failed %d.\n", + ret); } + + acpi_dev_remove_driver_gpios(adev); } else { - p->speaker_id = NULL; + p->speaker_id = -1; } end_2563: @@ -432,23 +445,16 @@ static void tasdevice_dspfw_init(void *context) struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv; struct hda_codec *codec = tas_priv->codec; - int ret, spk_id; + int ret; tasdevice_dsp_remove(tas_priv); tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; - if (tas_priv->speaker_id != NULL) { - // Speaker id need to be checked for ASUS only. - spk_id = gpiod_get_value(tas_priv->speaker_id); - if (spk_id < 0) { - // Speaker id is not valid, use default. - dev_dbg(tas_priv->dev, "Wrong spk_id = %d\n", spk_id); - spk_id = 0; - } + if (tas_priv->speaker_id >= 0) { snprintf(tas_priv->coef_binaryname, sizeof(tas_priv->coef_binaryname), "TAS2XXX%04X%d.bin", lower_16_bits(codec->core.subsystem_id), - spk_id); + tas_priv->speaker_id); } else { snprintf(tas_priv->coef_binaryname, sizeof(tas_priv->coef_binaryname), diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index a19258c95886..1e8e3d61291a 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2550,6 +2550,8 @@ static const struct pci_device_id azx_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_PTL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, /* Wildcat Lake */ { PCI_DEVICE_DATA(INTEL, HDA_WCL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, + /* Nova Lake */ + { PCI_DEVICE_DATA(INTEL, HDA_NVL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, /* Apollolake (Broxton-P) */ { PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) }, /* Gemini-Lake */ diff --git a/sound/hda/core/intel-dsp-config.c b/sound/hda/core/intel-dsp-config.c index 2a9e35cddcf7..c401c0658421 100644 --- a/sound/hda/core/intel-dsp-config.c +++ b/sound/hda/core/intel-dsp-config.c @@ -578,6 +578,14 @@ static const struct config_entry config_table[] = { #endif + /* Nova Lake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_NOVALAKE) + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = PCI_DEVICE_ID_INTEL_HDA_NVL_S, + }, +#endif + }; static const struct config_entry *snd_intel_dsp_find_config diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c index 5cbace8369f8..a7a3e764bb77 100644 --- a/sound/isa/gus/gus_reset.c +++ b/sound/isa/gus/gus_reset.c @@ -9,9 +9,6 @@ #include <sound/core.h> #include <sound/gus.h> -extern int snd_gf1_synth_init(struct snd_gus_card * gus); -extern void snd_gf1_synth_done(struct snd_gus_card * gus); - /* * ok.. default interrupt handlers... */ diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c index 1250ecba659a..69d87c4cafae 100644 --- a/sound/isa/wavefront/wavefront_midi.c +++ b/sound/isa/wavefront/wavefront_midi.c @@ -278,6 +278,7 @@ static int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substrea return -EIO; guard(spinlock_irqsave)(&midi->open); + midi->substream_input[mpu] = NULL; midi->mode[mpu] &= ~MPU401_MODE_INPUT; return 0; @@ -300,6 +301,7 @@ static int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substre return -EIO; guard(spinlock_irqsave)(&midi->open); + midi->substream_output[mpu] = NULL; midi->mode[mpu] &= ~MPU401_MODE_OUTPUT; return 0; } diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index cd5c177943aa..0d78533e1cfd 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -950,9 +950,9 @@ wavefront_send_sample (snd_wavefront_t *dev, if (header->size) { dev->freemem = wavefront_freemem (dev); - if (dev->freemem < (int)header->size) { + if (dev->freemem < 0 || dev->freemem < header->size) { dev_err(dev->card->dev, - "insufficient memory to load %d byte sample.\n", + "insufficient memory to load %u byte sample.\n", header->size); return -ENOMEM; } diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c index 71c13100d7ef..81a63b5bb31c 100644 --- a/sound/pci/au88x0/au88x0_eq.c +++ b/sound/pci/au88x0/au88x0_eq.c @@ -568,7 +568,7 @@ static int vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex_t * vortex) eqlzr_t *eq = &(vortex->eq); vortex_EqHw_SetLeftGainsTarget(vortex, eq->this130); - vortex_EqHw_SetRightGainsTarget(vortex, &(eq->this130[eq->this10])); + vortex_EqHw_SetRightGainsTarget(vortex, eq->this130 + eq->this10); return 0; } diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 14779b383d9e..227d8c8490e1 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -25,6 +25,9 @@ #include <sound/control.h> #include <sound/asoundef.h> +#define NUM_ATC_SRCS 6 +#define NUM_ATC_PCM (2 * 4) + #define MONO_SUM_SCALE 0x19a8 /* 2^(-0.5) in 14-bit floating format */ #define MAX_MULTI_CHN 8 @@ -60,6 +63,7 @@ static const struct snd_pci_quirk subsys_20k2_list[] = { SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000, PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "HENDRIX", CTHENDRIX), + SND_PCI_QUIRK(0x160b, 0x0101, "OK0010", CTOK0010), { } /* terminator */ }; @@ -75,6 +79,7 @@ static const char *ct_subsys_name[NUM_CTCARDS] = { [CTHENDRIX] = "Hendrix", [CTSB0880] = "SB0880", [CTSB1270] = "SB1270", + [CTOK0010] = "OK0010", [CT20K2_UNKNOWN] = "Unknown", }; @@ -984,6 +989,24 @@ static struct capabilities atc_capabilities(struct ct_atc *atc) return hw->capabilities(hw); } +static void atc_dedicated_rca_select(struct ct_atc *atc) +{ + struct dao *dao; + struct ct_mixer *mixer = atc->mixer; + struct rsc *rscs[2] = {NULL}; + + dao = container_of(atc->daios[atc->rca_state ? RCA : LINEO1], + struct dao, daio); + dao->ops->clear_left_input(dao); + dao->ops->clear_right_input(dao); + + mixer->get_output_ports(mixer, MIX_WAVE_FRONT, &rscs[0], &rscs[1]); + dao = container_of(atc->daios[atc->rca_state ? LINEO1 : RCA], + struct dao, daio); + dao->ops->set_left_input(dao, rscs[0]); + dao->ops->set_right_input(dao, rscs[1]); +} + static int atc_output_switch_get(struct ct_atc *atc) { struct hw *hw = atc->hw; @@ -1085,6 +1108,11 @@ static int atc_mic_unmute(struct ct_atc *atc, unsigned char state) return atc_daio_unmute(atc, state, MIC); } +static int atc_rca_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, RCA); +} + static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state) { return atc_daio_unmute(atc, state, SPDIFOO); @@ -1161,9 +1189,11 @@ static int atc_release_resources(struct ct_atc *atc) if (atc->daios) { daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; - for (i = 0; i < atc->n_daio; i++) { + for (i = 0; i < NUM_DAIOTYP; i++) { daio = atc->daios[i]; - if (daio->type < LINEIM) { + if (!daio) + continue; + if (daio->output) { dao = container_of(daio, struct dao, daio); dao->ops->clear_left_input(dao); dao->ops->clear_right_input(dao); @@ -1176,8 +1206,9 @@ static int atc_release_resources(struct ct_atc *atc) if (atc->pcm) { sum_mgr = atc->rsc_mgrs[SUM]; - for (i = 0; i < atc->n_pcm; i++) - sum_mgr->put_sum(sum_mgr, atc->pcm[i]); + for (i = 0; i < NUM_ATC_PCM; i++) + if (atc->pcm[i]) + sum_mgr->put_sum(sum_mgr, atc->pcm[i]); kfree(atc->pcm); atc->pcm = NULL; @@ -1185,8 +1216,9 @@ static int atc_release_resources(struct ct_atc *atc) if (atc->srcs) { src_mgr = atc->rsc_mgrs[SRC]; - for (i = 0; i < atc->n_src; i++) - src_mgr->put_src(src_mgr, atc->srcs[i]); + for (i = 0; i < NUM_ATC_SRCS; i++) + if (atc->srcs[i]) + src_mgr->put_src(src_mgr, atc->srcs[i]); kfree(atc->srcs); atc->srcs = NULL; @@ -1194,7 +1226,9 @@ static int atc_release_resources(struct ct_atc *atc) if (atc->srcimps) { srcimp_mgr = atc->rsc_mgrs[SRCIMP]; - for (i = 0; i < atc->n_srcimp; i++) { + for (i = 0; i < NUM_ATC_SRCS; i++) { + if (!atc->srcimps[i]) + continue; srcimp = atc->srcimps[i]; srcimp->ops->unmap(srcimp); srcimp_mgr->put_srcimp(srcimp_mgr, atc->srcimps[i]); @@ -1294,6 +1328,7 @@ static int atc_identify_card(struct ct_atc *atc, unsigned int ssid) dev_info(atc->card->dev, "chip %s model %s (%04x:%04x) is found\n", atc->chip_name, atc->model_name, vendor_id, device_id); + atc->rca_state = 0; return 0; } @@ -1367,32 +1402,35 @@ static int atc_get_resources(struct ct_atc *atc) struct srcimp_mgr *srcimp_mgr; struct sum_desc sum_dsc = {0}; struct sum_mgr *sum_mgr; - int err, i, num_srcs, num_daios; + struct capabilities cap; + int err, i; - num_daios = ((atc->model == CTSB1270) ? 8 : 7); - num_srcs = ((atc->model == CTSB1270) ? 6 : 4); + cap = atc->capabilities(atc); - atc->daios = kcalloc(num_daios, sizeof(void *), GFP_KERNEL); + atc->daios = kcalloc(NUM_DAIOTYP, sizeof(void *), GFP_KERNEL); if (!atc->daios) return -ENOMEM; - atc->srcs = kcalloc(num_srcs, sizeof(void *), GFP_KERNEL); + atc->srcs = kcalloc(NUM_ATC_SRCS, sizeof(void *), GFP_KERNEL); if (!atc->srcs) return -ENOMEM; - atc->srcimps = kcalloc(num_srcs, sizeof(void *), GFP_KERNEL); + atc->srcimps = kcalloc(NUM_ATC_SRCS, sizeof(void *), GFP_KERNEL); if (!atc->srcimps) return -ENOMEM; - atc->pcm = kcalloc(2 * 4, sizeof(void *), GFP_KERNEL); + atc->pcm = kcalloc(NUM_ATC_PCM, sizeof(void *), GFP_KERNEL); if (!atc->pcm) return -ENOMEM; daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; da_desc.msr = atc->msr; - for (i = 0, atc->n_daio = 0; i < num_daios; i++) { + for (i = 0; i < NUM_DAIOTYP; i++) { + if (((i == MIC) && !cap.dedicated_mic) || ((i == RCA) && !cap.dedicated_rca)) + continue; da_desc.type = (atc->model != CTSB073X) ? i : ((i == SPDIFIO) ? SPDIFI1 : i); + da_desc.output = (i < LINEIM) || (i == RCA); err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); if (err) { @@ -1401,42 +1439,39 @@ static int atc_get_resources(struct ct_atc *atc) i); return err; } - atc->n_daio++; } src_mgr = atc->rsc_mgrs[SRC]; src_dsc.multi = 1; src_dsc.msr = atc->msr; src_dsc.mode = ARCRW; - for (i = 0, atc->n_src = 0; i < num_srcs; i++) { + for (i = 0; i < NUM_ATC_SRCS; i++) { + if (((i > 3) && !cap.dedicated_mic)) + continue; err = src_mgr->get_src(src_mgr, &src_dsc, (struct src **)&atc->srcs[i]); if (err) return err; - - atc->n_src++; } srcimp_mgr = atc->rsc_mgrs[SRCIMP]; srcimp_dsc.msr = 8; - for (i = 0, atc->n_srcimp = 0; i < num_srcs; i++) { + for (i = 0; i < NUM_ATC_SRCS; i++) { + if (((i > 3) && !cap.dedicated_mic)) + continue; err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, (struct srcimp **)&atc->srcimps[i]); if (err) return err; - - atc->n_srcimp++; } sum_mgr = atc->rsc_mgrs[SUM]; sum_dsc.msr = atc->msr; - for (i = 0, atc->n_pcm = 0; i < (2*4); i++) { + for (i = 0; i < NUM_ATC_PCM; i++) { err = sum_mgr->get_sum(sum_mgr, &sum_dsc, (struct sum **)&atc->pcm[i]); if (err) return err; - - atc->n_pcm++; } return 0; @@ -1489,9 +1524,11 @@ static void atc_connect_resources(struct ct_atc *atc) struct sum *sum; struct ct_mixer *mixer; struct rsc *rscs[2] = {NULL}; + struct capabilities cap; int i, j; mixer = atc->mixer; + cap = atc->capabilities(atc); for (i = MIX_WAVE_FRONT, j = LINEO1; i <= MIX_SPDIF_OUT; i++, j++) { mixer->get_output_ports(mixer, i, &rscs[0], &rscs[1]); @@ -1500,6 +1537,11 @@ static void atc_connect_resources(struct ct_atc *atc) dao->ops->set_right_input(dao, rscs[1]); } + if (cap.dedicated_rca) { + /* SE-300PCIE has a dedicated DAC for the RCA. */ + atc_dedicated_rca_select(atc); + } + dai = container_of(atc->daios[LINEIM], struct dai, daio); atc_connect_dai(atc->rsc_mgrs[SRC], dai, (struct src **)&atc->srcs[2], @@ -1509,8 +1551,9 @@ static void atc_connect_resources(struct ct_atc *atc) src = atc->srcs[3]; mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc); - if (atc->model == CTSB1270) { + if (cap.dedicated_mic) { /* Titanium HD has a dedicated ADC for the Mic. */ + /* SE-300PCIE has a 4-channel ADC. */ dai = container_of(atc->daios[MIC], struct dai, daio); atc_connect_dai(atc->rsc_mgrs[SRC], dai, (struct src **)&atc->srcs[4], @@ -1632,12 +1675,14 @@ static const struct ct_atc atc_preset = { .line_rear_unmute = atc_line_rear_unmute, .line_in_unmute = atc_line_in_unmute, .mic_unmute = atc_mic_unmute, + .rca_unmute = atc_rca_unmute, .spdif_out_unmute = atc_spdif_out_unmute, .spdif_in_unmute = atc_spdif_in_unmute, .spdif_out_get_status = atc_spdif_out_get_status, .spdif_out_set_status = atc_spdif_out_set_status, .spdif_out_passthru = atc_spdif_out_passthru, .capabilities = atc_capabilities, + .dedicated_rca_select = atc_dedicated_rca_select, .output_switch_get = atc_output_switch_get, .output_switch_put = atc_output_switch_put, .mic_source_switch_get = atc_mic_source_switch_get, diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index 0bc7b71d910b..ca0a9d5b86d8 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -82,6 +82,8 @@ struct ct_atc { const char *chip_name; const char *model_name; + unsigned char rca_state; /* 0 = dedicated RCA, 1 = 7.1ch Front */ + struct ct_vm *vm; /* device virtual memory manager for this card */ int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm); void (*unmap_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm); @@ -113,12 +115,14 @@ struct ct_atc { int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state); int (*line_in_unmute)(struct ct_atc *atc, unsigned char state); int (*mic_unmute)(struct ct_atc *atc, unsigned char state); + int (*rca_unmute)(struct ct_atc *atc, unsigned char state); int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state); int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state); int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status); int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status); int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state); struct capabilities (*capabilities)(struct ct_atc *atc); + void (*dedicated_rca_select)(struct ct_atc *atc); int (*output_switch_get)(struct ct_atc *atc); int (*output_switch_put)(struct ct_atc *atc, int position); int (*mic_source_switch_get)(struct ct_atc *atc); @@ -132,10 +136,6 @@ struct ct_atc { void **pcm; /* SUMs for collecting all pcm stream */ void **srcs; /* Sample Rate Converters for input signal */ void **srcimps; /* input mappers for SRCs */ - unsigned char n_daio; - unsigned char n_src; - unsigned char n_srcimp; - unsigned char n_pcm; struct ct_timer *timer; diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index c0c3f8ab8467..1c8f8efd836c 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -18,8 +18,6 @@ #include <linux/slab.h> #include <linux/kernel.h> -#define DAIO_OUT_MAX SPDIFOO - struct daio_usage { unsigned short data; }; @@ -47,6 +45,7 @@ static const struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { [LINEO4] = {.left = 0x70, .right = 0x71}, [LINEIM] = {.left = 0x45, .right = 0xc5}, [MIC] = {.left = 0x55, .right = 0xd5}, + [RCA] = {.left = 0x30, .right = 0x31}, [SPDIFOO] = {.left = 0x00, .right = 0x01}, [SPDIFIO] = {.left = 0x05, .right = 0x85}, }; @@ -125,6 +124,7 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) case LINEO4: return 6; case LINEIM: return 4; case MIC: return 5; + case RCA: return 3; default: return -EINVAL; } default: @@ -329,7 +329,7 @@ static int daio_rsc_init(struct daio *daio, goto error1; /* Set daio->rscl/r->ops to daio specific ones */ - if (desc->type <= DAIO_OUT_MAX) { + if (desc->output) { daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops; } else { switch (hw->chip_type) { @@ -344,6 +344,7 @@ static int daio_rsc_init(struct daio *daio, } } daio->type = desc->type; + daio->output = desc->output; return 0; @@ -390,7 +391,7 @@ static int dao_rsc_init(struct dao *dao, hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk); conf = (desc->msr & 0x7) | (desc->passthru << 3); - hw->daio_mgr_dao_init(mgr->mgr.ctrl_blk, + hw->daio_mgr_dao_init(hw, mgr->mgr.ctrl_blk, daio_device_index(dao->daio.type, hw), conf); hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, daio_device_index(dao->daio.type, hw)); @@ -433,6 +434,7 @@ static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc) dsc.type = dao->daio.type; dsc.msr = desc->msr; dsc.passthru = desc->passthru; + dsc.output = dao->daio.output; dao_rsc_uninit(dao); return dao_rsc_init(dao, &dsc, mgr); } @@ -518,7 +520,7 @@ static int get_daio_rsc(struct daio_mgr *mgr, err = -ENOMEM; /* Allocate mem for daio resource */ - if (desc->type <= DAIO_OUT_MAX) { + if (desc->output) { struct dao *dao = kzalloc(sizeof(*dao), GFP_KERNEL); if (!dao) goto error; @@ -565,7 +567,7 @@ static int put_daio_rsc(struct daio_mgr *mgr, struct daio *daio) daio_mgr_put_rsc(&mgr->mgr, daio->type); } - if (daio->type <= DAIO_OUT_MAX) { + if (daio->output) { dao_rsc_uninit(container_of(daio, struct dao, daio)); kfree(container_of(daio, struct dao, daio)); } else { @@ -580,7 +582,7 @@ static int daio_mgr_enb_daio(struct daio_mgr *mgr, struct daio *daio) { struct hw *hw = mgr->mgr.hw; - if (DAIO_OUT_MAX >= daio->type) { + if (daio->output) { hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, daio_device_index(daio->type, hw)); } else { @@ -594,7 +596,7 @@ static int daio_mgr_dsb_daio(struct daio_mgr *mgr, struct daio *daio) { struct hw *hw = mgr->mgr.hw; - if (DAIO_OUT_MAX >= daio->type) { + if (daio->output) { hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk, daio_device_index(daio->type, hw)); } else { diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h index 15147fe5f74a..ff77d55539a5 100644 --- a/sound/pci/ctxfi/ctdaio.h +++ b/sound/pci/ctxfi/ctdaio.h @@ -31,6 +31,7 @@ enum DAIOTYP { LINEIM, SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */ MIC, /* Dedicated mic on Titanium HD */ + RCA, /* Dedicated RCA on SE-300PCIE */ SPDIFI1, /* S/PDIF In on internal Drive Bay */ NUM_DAIOTYP }; @@ -43,6 +44,7 @@ struct daio { struct rsc rscl; /* Basic resource info for left TX/RX */ struct rsc rscr; /* Basic resource info for right TX/RX */ enum DAIOTYP type; + unsigned char output; }; struct dao { @@ -91,6 +93,7 @@ struct daio_desc { unsigned int type:4; unsigned int msr:4; unsigned int passthru:1; + unsigned int output:1; }; struct daio_mgr { diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index 2875cec83b8f..a3051fdd31f6 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -38,6 +38,7 @@ enum CTCARDS { CTHENDRIX, CTSB0880, CTSB1270, + CTOK0010, CT20K2_UNKNOWN, NUM_CTCARDS /* This should always be the last */ }; @@ -62,6 +63,7 @@ struct card_conf { struct capabilities { unsigned int digit_io_switch:1; unsigned int dedicated_mic:1; + unsigned int dedicated_rca:1; unsigned int output_switch:1; unsigned int mic_source_switch:1; }; @@ -167,7 +169,7 @@ struct hw { int (*daio_mgr_dsb_dai)(void *blk, unsigned int idx); int (*daio_mgr_enb_dao)(void *blk, unsigned int idx); int (*daio_mgr_dsb_dao)(void *blk, unsigned int idx); - int (*daio_mgr_dao_init)(void *blk, unsigned int idx, + int (*daio_mgr_dao_init)(struct hw *hw, void *blk, unsigned int idx, unsigned int conf); int (*daio_mgr_set_imaparc)(void *blk, unsigned int slot); int (*daio_mgr_set_imapnxt)(void *blk, unsigned int next); diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 42b90c9b2ee9..ea0a928937b6 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1031,7 +1031,7 @@ static int daio_mgr_dsb_dao(void *blk, unsigned int idx) return 0; } -static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf) +static int daio_mgr_dao_init(struct hw *hw __maybe_unused, void *blk, unsigned int idx, unsigned int conf) { struct daio_mgr_ctrl_blk *ctl = blk; @@ -1775,6 +1775,7 @@ static struct capabilities hw_capabilities(struct hw *hw) /* SB073x and Vista compatible cards have no digit IO switch */ cap.digit_io_switch = !(hw->model == CTSB073X || hw->model == CTUAA); cap.dedicated_mic = 0; + cap.dedicated_rca = 0; cap.output_switch = 0; cap.mic_source_switch = 0; diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 55af8ef29838..fac88f5590c9 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -910,7 +910,7 @@ static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk) struct dao_ctrl_blk *ctl = blk; if (ctl->dirty.bf.atxcsl) { - if (idx < 4) { + if ((idx < 4) && ((hw->model != CTOK0010) || (idx < 3))) { /* S/PDIF SPOSx */ hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+0x40*idx, ctl->atxcsl); @@ -985,11 +985,12 @@ static int daio_mgr_dsb_dao(void *blk, unsigned int idx) return 0; } -static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf) +static int daio_mgr_dao_init(struct hw *hw, void *blk, unsigned int idx, unsigned int conf) { struct daio_mgr_ctrl_blk *ctl = blk; - if (idx < 4) { + /* Port 3 is dedicated to RCA on SE-300PCIE */ + if ((idx < 4) && ((hw->model != CTOK0010) || (idx < 3))) { /* S/PDIF output */ switch ((conf & 0xf)) { case 1: @@ -1176,6 +1177,10 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) hw_write_20kx(hw, AUDIO_IO_MCLK, 0x21011111); hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121); hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); + } else if ((4 == info->msr) && (hw->model == CTOK0010)) { + hw_write_20kx(hw, AUDIO_IO_MCLK, 0x21212121); + hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121); + hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); } else { dev_alert(hw->card->dev, "ERROR!!! Invalid sampling rate!!!\n"); @@ -1183,7 +1188,8 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) } for (i = 0; i < 8; i++) { - if (i <= 3) { + /* Port 3 is configured as I2S on SE-300PCIE */ + if ((i < 4) && ((hw->model != CTOK0010) || (i < 3))) { /* This comment looks wrong since loop is over 4 */ /* channels and emu20k2 supports 4 spdif IOs. */ /* 1st 3 channels are SPDIFs (SB0960) */ @@ -1637,6 +1643,13 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) hw_write_20kx(hw, GPIO_DATA, data); hw_dac_start(hw); return 0; + } else if (hw->model == CTOK0010) { + hw_dac_stop(hw); + data = hw_read_20kx(hw, GPIO_DATA); + data |= 0x1000; + hw_write_20kx(hw, GPIO_DATA, data); + hw_dac_start(hw); + return 0; } /* Set DAC reset bit as output */ @@ -1756,9 +1769,11 @@ End: static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) { u32 data; - if (hw->model == CTSB1270) { + if ((hw->model == CTSB1270) || (hw->model == CTOK0010)) { /* Titanium HD has two ADC chips, one for line in and one */ - /* for MIC. We don't need to switch the ADC input. */ + /* for MIC. Also, SE-300PCIE has a single ADC chip that */ + /* simultaneously supports 4-channel input. We don't need */ + /* to switch the ADC input. */ return 1; } data = hw_read_20kx(hw, GPIO_DATA); @@ -1826,6 +1841,32 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) return 0; } +static void hw_adc_stop(struct hw *hw) +{ + u32 data; + /* Reset the ADC (reset is active low). */ + data = hw_read_20kx(hw, GPIO_DATA); + data &= ~(0x1 << 15); + hw_write_20kx(hw, GPIO_DATA, data); + usleep_range(10000, 11000); +} + +static void hw_adc_start(struct hw *hw) +{ + u32 data; + /* Return the ADC to normal operation. */ + data = hw_read_20kx(hw, GPIO_DATA); + data |= (0x1 << 15); + hw_write_20kx(hw, GPIO_DATA, data); + msleep(50); +} + +static void hw_adc_reset(struct hw *hw) +{ + hw_adc_stop(hw); + hw_adc_start(hw); +} + static int hw_adc_init(struct hw *hw, const struct adc_conf *info) { int err; @@ -1836,6 +1877,12 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) data |= (0x1 << 15); hw_write_20kx(hw, GPIO_CTRL, data); + if (hw->model == CTOK0010) { + /* Manual ADC setup for SE-300PCIE is not needed. */ + hw_adc_reset(hw); + return 0; + } + /* Initialize I2C */ err = hw20k2_i2c_init(hw, 0x1A, 1, 1); if (err < 0) { @@ -1843,10 +1890,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) goto error; } - /* Reset the ADC (reset is active low). */ - data = hw_read_20kx(hw, GPIO_DATA); - data &= ~(0x1 << 15); - hw_write_20kx(hw, GPIO_DATA, data); + hw_adc_stop(hw); if (hw->model == CTSB1270) { /* Set up the PCM4220 ADC on Titanium HD */ @@ -1860,11 +1904,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) hw_write_20kx(hw, GPIO_DATA, data); } - usleep_range(10000, 11000); - /* Return the ADC to normal operation. */ - data |= (0x1 << 15); - hw_write_20kx(hw, GPIO_DATA, data); - msleep(50); + hw_adc_start(hw); /* I2C write to register offset 0x0B to set ADC LRCLK polarity */ /* invert bit, interface format to I2S, word length to 24-bit, */ @@ -1910,7 +1950,8 @@ static struct capabilities hw_capabilities(struct hw *hw) struct capabilities cap; cap.digit_io_switch = 0; - cap.dedicated_mic = hw->model == CTSB1270; + cap.dedicated_mic = (hw->model == CTSB1270) || (hw->model == CTOK0010); + cap.dedicated_rca = hw->model == CTOK0010; cap.output_switch = hw->model == CTSB1270; cap.mic_source_switch = hw->model == CTSB1270; @@ -2147,15 +2188,17 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) /* Reset all SRC pending interrupts */ hw_write_20kx(hw, SRC_IP, 0); - if (hw->model != CTSB1270) { + if (hw->model == CTSB1270) { + hw_write_20kx(hw, GPIO_CTRL, 0x9E5F); + } else if (hw->model == CTOK0010) { + hw_write_20kx(hw, GPIO_CTRL, 0x9902); + } else { /* TODO: detect the card ID and configure GPIO accordingly. */ /* Configures GPIO (0xD802 0x98028) */ /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/ /* Configures GPIO (SB0880) */ /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/ hw_write_20kx(hw, GPIO_CTRL, 0xD802); - } else { - hw_write_20kx(hw, GPIO_CTRL, 0x9E5F); } /* Enable audio ring */ hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01); diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 496682613db5..fc9fde284fb3 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -547,8 +547,14 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state) atc->mic_unmute(atc, state); else if (MIXER_SPDIFI_C_S == type) atc->spdif_in_unmute(atc, state); - else if (MIXER_WAVEF_P_S == type) - atc->line_front_unmute(atc, state); + else if (MIXER_WAVEF_P_S == type) { + if (cap.dedicated_rca) { + atc->rca_unmute(atc, atc->rca_state ? 0 : state); + atc->line_front_unmute(atc, atc->rca_state ? state : 0); + } else { + atc->line_front_unmute(atc, state); + } + } else if (MIXER_WAVES_P_S == type) atc->line_surround_unmute(atc, state); else if (MIXER_WAVEC_P_S == type) @@ -612,6 +618,57 @@ static struct snd_kcontrol_new swh_ctl = { .put = ct_alsa_mix_switch_put }; +static int dedicated_rca_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { + "RCA", "Front" + }; + + return snd_ctl_enum_info(info, 1, 2, names); +} + +static int dedicated_rca_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = atc->rca_state; + return 0; +} + +static int dedicated_rca_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + unsigned int rca_state = ucontrol->value.enumerated.item[0]; + unsigned char state; + + if (rca_state > 1) + return -EINVAL; + + if (rca_state == atc->rca_state) + return 0; + + state = get_switch_state(atc->mixer, MIXER_WAVEF_P_S); + do_switch(atc, MIXER_WAVEF_P_S, 0); + + atc->rca_state = rca_state; + atc->dedicated_rca_select(atc); + + do_switch(atc, MIXER_WAVEF_P_S, state); + + return 1; +} + +static struct snd_kcontrol_new rca_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Playback Route", + .info = dedicated_rca_info, + .get = dedicated_rca_get, + .put = dedicated_rca_put, +}; + static int ct_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -784,7 +841,17 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) if (err) return err; } - atc->line_front_unmute(atc, 1); + + if (cap.dedicated_rca) { + err = ct_mixer_kcontrol_new(mixer, &rca_ctl); + if (err) + return err; + + atc->line_front_unmute(atc, 0); + atc->rca_unmute(atc, 1); + } else { + atc->line_front_unmute(atc, 1); + } set_switch_state(mixer, MIXER_WAVEF_P_S, 1); atc->line_surround_unmute(atc, 0); set_switch_state(mixer, MIXER_WAVES_P_S, 0); diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index bddf47a1f263..3353980d5cd8 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2571,9 +2571,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, if (IS_ENABLED(CONFIG_PM_SLEEP)) { chip->suspend_mem = - vmalloc(array_size(sizeof(u16), - REV_B_CODE_MEMORY_LENGTH + - REV_B_DATA_MEMORY_LENGTH)); + vmalloc_array(REV_B_CODE_MEMORY_LENGTH + + REV_B_DATA_MEMORY_LENGTH, + sizeof(u16)); if (!chip->suspend_mem) dev_warn(card->dev, "can't allocate apm buffer\n"); } diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 7822bcae6c69..dbfd9e2ac015 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -11,6 +11,7 @@ snd-soc-acpi-intel-match-y := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-matc soc-acpi-intel-arl-match.o \ soc-acpi-intel-lnl-match.o \ soc-acpi-intel-ptl-match.o \ + soc-acpi-intel-nvl-match.o \ soc-acpi-intel-hda-match.o \ soc-acpi-intel-sdw-mockup-match.o sof-function-topology-lib.o diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c new file mode 100644 index 000000000000..2768dd10aaa0 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * soc-acpi-intel-nvl-match.c - tables and support for NVL ACPI enumeration. + * + * Copyright (c) 2025, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include "soc-acpi-intel-sdw-mockup-match.h" + +struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_nvl_machines); + +/* + * Multi-function codecs with three endpoints created for + * headset, amp and dmic functions. + */ +static const struct snd_soc_acpi_endpoint rt_mf_endpoints[] = { + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_adr_device rt722_3_single_adr[] = { + { + .adr = 0x000330025d072201ull, + .num_endpoints = ARRAY_SIZE(rt_mf_endpoints), + .endpoints = rt_mf_endpoints, + .name_prefix = "rt722" + } +}; + +static const struct snd_soc_acpi_link_adr nvl_rt722_l3[] = { + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt722_3_single_adr), + .adr_d = rt722_3_single_adr, + }, + {} +}; + +/* this table is used when there is no I2S codec present */ +struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_sdw_machines[] = { + /* mockup tests need to be first */ + { + .link_mask = GENMASK(3, 0), + .links = sdw_mockup_headset_2amps_mic, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-nvl-rt711-rt1308-rt715.tplg", + }, + { + .link_mask = BIT(0) | BIT(1) | BIT(3), + .links = sdw_mockup_headset_1amp_mic, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-nvl-rt711-rt1308-mono-rt715.tplg", + }, + { + .link_mask = GENMASK(2, 0), + .links = sdw_mockup_mic_headset_1amp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-nvl-rt715-rt711-rt1308-mono.tplg", + }, + { + .link_mask = BIT(3), + .links = nvl_rt722_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-nvl-rt722.tplg", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_nvl_sdw_machines); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 4f27f8c8debf..b14e7ca60e91 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -300,6 +300,23 @@ config SND_SOC_SOF_PANTHERLAKE Say Y if you have such a device. If unsure select "N". +config SND_SOC_SOF_INTEL_NVL + tristate + select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE + select SND_SOC_SOF_IPC4 + select SND_SOC_SOF_INTEL_PTL + +config SND_SOC_SOF_NOVALAKE + tristate "SOF support for Novalake" + default SND_SOC_SOF_PCI + select SND_SOC_SOF_INTEL_NVL + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Novalake processors. + Say Y if you have such a device. + If unsure select "N". + config SND_SOC_SOF_HDA_COMMON tristate diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index aab803a495b1..cc9783e933f8 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -39,6 +39,7 @@ snd-sof-pci-intel-tgl-y := pci-tgl.o tgl.o snd-sof-pci-intel-mtl-y := pci-mtl.o mtl.o snd-sof-pci-intel-lnl-y := pci-lnl.o lnl.o snd-sof-pci-intel-ptl-y := pci-ptl.o ptl.o +snd-sof-pci-intel-nvl-y := pci-nvl.o nvl.o obj-$(CONFIG_SND_SOC_SOF_MERRIFIELD) += snd-sof-pci-intel-tng.o obj-$(CONFIG_SND_SOC_SOF_INTEL_SKL) += snd-sof-pci-intel-skl.o @@ -49,3 +50,4 @@ obj-$(CONFIG_SND_SOC_SOF_INTEL_TGL) += snd-sof-pci-intel-tgl.o obj-$(CONFIG_SND_SOC_SOF_INTEL_MTL) += snd-sof-pci-intel-mtl.o obj-$(CONFIG_SND_SOC_SOF_INTEL_LNL) += snd-sof-pci-intel-lnl.o obj-$(CONFIG_SND_SOC_SOF_INTEL_PTL) += snd-sof-pci-intel-ptl.o +obj-$(CONFIG_SND_SOC_SOF_INTEL_NVL) += snd-sof-pci-intel-nvl.o diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 3ab6d5ce6329..e9f092f082a1 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -70,6 +70,7 @@ static void hda_get_interfaces(struct snd_sof_dev *sdev, u32 *interface_mask) break; case SOF_INTEL_ACE_2_0: case SOF_INTEL_ACE_3_0: + case SOF_INTEL_ACE_4_0: interface_mask[SOF_DAI_DSP_ACCESS] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 28daf0a3b984..562fe8be79c1 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -936,6 +936,7 @@ extern const struct sof_intel_dsp_desc arl_s_chip_info; extern const struct sof_intel_dsp_desc lnl_chip_info; extern const struct sof_intel_dsp_desc ptl_chip_info; extern const struct sof_intel_dsp_desc wcl_chip_info; +extern const struct sof_intel_dsp_desc nvl_s_chip_info; /* Probes support */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) diff --git a/sound/soc/sof/intel/nvl.c b/sound/soc/sof/intel/nvl.c new file mode 100644 index 000000000000..ff215151af2a --- /dev/null +++ b/sound/soc/sof/intel/nvl.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2025 Intel Corporation + +/* + * Hardware interface for audio DSP on NovaLake. + */ + +#include <sound/hda_register.h> +#include <sound/hda-mlink.h> +#include <sound/sof/ipc4/header.h> +#include "../ipc4-priv.h" +#include "../ops.h" +#include "hda.h" +#include "hda-ipc.h" +#include "../sof-audio.h" +#include "mtl.h" +#include "lnl.h" +#include "ptl.h" +#include "nvl.h" + +int sof_nvl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops) +{ + /* Use PTL ops for NVL */ + return sof_ptl_set_ops(sdev, dsp_ops); +}; +EXPORT_SYMBOL_NS(sof_nvl_set_ops, "SND_SOC_SOF_INTEL_NVL"); + +const struct sof_intel_dsp_desc nvl_s_chip_info = { + .cores_num = 2, + .init_core_mask = BIT(0), + .host_managed_cores_mask = BIT(0), + .ipc_req = MTL_DSP_REG_HFIPCXIDR, + .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY, + .ipc_ack = MTL_DSP_REG_HFIPCXIDA, + .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE, + .ipc_ctl = MTL_DSP_REG_HFIPCXCTL, + .rom_status_reg = LNL_DSP_REG_HFDSC, + .rom_init_timeout = 300, + .ssp_count = MTL_SSP_COUNT, + .d0i3_offset = MTL_HDA_VS_D0I3C, + .read_sdw_lcount = hda_sdw_check_lcount_ext, + .check_sdw_irq = lnl_dsp_check_sdw_irq, + .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq, + .sdw_process_wakeen = hda_sdw_process_wakeen_common, + .check_ipc_irq = mtl_dsp_check_ipc_irq, + .cl_init = mtl_dsp_cl_init, + .power_down_dsp = mtl_power_down_dsp, + .disable_interrupts = lnl_dsp_disable_interrupts, + .hw_ip_version = SOF_INTEL_ACE_4_0, +}; + +MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL"); +MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_LNL"); +MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_PTL"); diff --git a/sound/soc/sof/intel/nvl.h b/sound/soc/sof/intel/nvl.h new file mode 100644 index 000000000000..0be3fdfbbd48 --- /dev/null +++ b/sound/soc/sof/intel/nvl.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2025 Intel Corporation + */ + +#ifndef __SOF_INTEL_NVL_H +#define __SOF_INTEL_NVL_H + +int sof_nvl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops); + +#endif /* __SOF_INTEL_NVL_H */ diff --git a/sound/soc/sof/intel/pci-nvl.c b/sound/soc/sof/intel/pci-nvl.c new file mode 100644 index 000000000000..c499c14b93d5 --- /dev/null +++ b/sound/soc/sof/intel/pci-nvl.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2025 Intel Corporation. +// + +#include <linux/module.h> +#include <linux/pci.h> +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include <sound/sof.h> +#include "../ops.h" +#include "../sof-pci-dev.h" + +/* platform specific devices */ +#include "hda.h" +#include "nvl.h" + +/* PantherLake ops */ +static struct snd_sof_dsp_ops sof_nvl_ops; + +static int sof_nvl_ops_init(struct snd_sof_dev *sdev) +{ + return sof_nvl_set_ops(sdev, &sof_nvl_ops); +} + +static const struct sof_dev_desc nvl_s_desc = { + .use_acpi_target_states = true, + .machines = snd_soc_acpi_intel_nvl_machines, + .alt_machines = snd_soc_acpi_intel_nvl_sdw_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .chip_info = &nvl_s_chip_info, + .ipc_supported_mask = BIT(SOF_IPC_TYPE_4), + .ipc_default = SOF_IPC_TYPE_4, + .dspless_mode_supported = true, + .default_fw_path = { + [SOF_IPC_TYPE_4] = "intel/sof-ipc4/nvl-s", + }, + .default_lib_path = { + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/nvl-s", + }, + .default_tplg_path = { + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", + }, + .default_fw_filename = { + [SOF_IPC_TYPE_4] = "sof-nvl-s.ri", + }, + .nocodec_tplg_filename = "sof-nvl-nocodec.tplg", + .ops = &sof_nvl_ops, + .ops_init = sof_nvl_ops_init, +}; + +/* PCI IDs */ +static const struct pci_device_id sof_pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, HDA_NVL_S, &nvl_s_desc) }, /* NVL-S */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, sof_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_sof_pci_intel_nvl_driver = { + .name = "sof-audio-pci-intel-nvl", + .id_table = sof_pci_ids, + .probe = hda_pci_intel_probe, + .remove = sof_pci_remove, + .shutdown = sof_pci_shutdown, + .driver = { + .pm = pm_ptr(&sof_pci_pm), + }, +}; +module_pci_driver(snd_sof_pci_intel_nvl_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("SOF support for NovaLake platforms"); +MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC"); +MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON"); +MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h index d4372f0bff7e..8b7ccb1596d5 100644 --- a/sound/soc/sof/intel/shim.h +++ b/sound/soc/sof/intel/shim.h @@ -23,6 +23,7 @@ enum sof_intel_hw_ip_version { SOF_INTEL_ACE_1_0, /* MeteorLake */ SOF_INTEL_ACE_2_0, /* LunarLake */ SOF_INTEL_ACE_3_0, /* PantherLake */ + SOF_INTEL_ACE_4_0, /* NovaLake */ }; /* diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c index 70de08635f54..ea1324c22f46 100644 --- a/sound/usb/line6/podhd.c +++ b/sound/usb/line6/podhd.c @@ -26,7 +26,8 @@ enum { LINE6_PODX3, LINE6_PODX3LIVE, LINE6_PODHD500X, - LINE6_PODHDDESKTOP + LINE6_PODHDDESKTOP, + LINE6_PODHDPROX, }; struct usb_line6_podhd { @@ -440,6 +441,7 @@ static const struct usb_device_id podhd_id_table[] = { { LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE }, { LINE6_IF_NUM(0x4159, 0), .driver_info = LINE6_PODHD500X }, { LINE6_IF_NUM(0x4156, 0), .driver_info = LINE6_PODHDDESKTOP }, + { LINE6_IF_NUM(0x415A, 0), .driver_info = LINE6_PODHDPROX }, {} }; @@ -528,6 +530,18 @@ static const struct line6_properties podhd_properties_table[] = { .ep_audio_r = 0x86, .ep_audio_w = 0x02, }, + [LINE6_PODHDPROX] = { + .id = "PODHDPROX", + .name = "POD HD Pro X", + .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_CONTROL_INFO + | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT, + .altsetting = 1, + .ctrl_if = 1, + .ep_ctrl_r = 0x81, + .ep_ctrl_w = 0x01, + .ep_audio_r = 0x86, + .ep_audio_w = 0x02, + }, }; /* diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 828af3095b86..fe6c2cebc7f0 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -2277,7 +2277,8 @@ static int realtek_resume_jack(struct usb_mixer_elem_list *list) } static int realtek_add_jack(struct usb_mixer_interface *mixer, - char *name, u32 val) + char *name, u32 val, int unitid, + const struct snd_kcontrol_new *kctl_new) { struct usb_mixer_elem_info *cval; struct snd_kcontrol *kctl; @@ -2285,14 +2286,13 @@ static int realtek_add_jack(struct usb_mixer_interface *mixer, cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return -ENOMEM; - snd_usb_mixer_elem_init_std(&cval->head, mixer, - REALTEK_JACK_INTERRUPT_NODE); + snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid); cval->head.resume = realtek_resume_jack; cval->val_type = USB_MIXER_BOOLEAN; cval->channels = 1; cval->min = 0; cval->max = 1; - kctl = snd_ctl_new1(&realtek_connector_ctl_ro, cval); + kctl = snd_ctl_new1(kctl_new, cval); if (!kctl) { kfree(cval); return -ENOMEM; @@ -2322,14 +2322,20 @@ static int dell_dock_mixer_create(struct usb_mixer_interface *mixer) USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, NULL, 0); - err = realtek_add_jack(mixer, "Line Out Jack", REALTEK_LINE1); + err = realtek_add_jack(mixer, "Line Out Jack", REALTEK_LINE1, + REALTEK_JACK_INTERRUPT_NODE, + &realtek_connector_ctl_ro); if (err < 0) return err; - err = realtek_add_jack(mixer, "Headphone Jack", REALTEK_HP_OUT); + err = realtek_add_jack(mixer, "Headphone Jack", REALTEK_HP_OUT, + REALTEK_JACK_INTERRUPT_NODE, + &realtek_connector_ctl_ro); if (err < 0) return err; err = realtek_add_jack(mixer, "Headset Mic Jack", - REALTEK_HP_OUT | REALTEK_MIC_FLAG); + REALTEK_HP_OUT | REALTEK_MIC_FLAG, + REALTEK_JACK_INTERRUPT_NODE, + &realtek_connector_ctl_ro); if (err < 0) return err; return 0; @@ -2357,6 +2363,105 @@ static int dell_dock_mixer_init(struct usb_mixer_interface *mixer) return 0; } +/* + * HP Thunderbolt Dock G2 jack detection + * + * Similar to the Dell WD15/WD19, but with different commands. + */ + +#define HP_DOCK_JACK_INTERRUPT_NODE 7 + +#define HP_DOCK_GET 37 + +#define HP_DOCK_JACK_PRESENCE 0xffb8 +#define HP_DOCK_JACK_PRESENCE_BIT BIT(2) + +#define HP_DOCK_MIC_SENSE 0xf753 +#define HP_DOCK_MIC_SENSE_COMPLETE_BIT BIT(4) + +#define HP_DOCK_MIC_SENSE_MASK (BIT(2) | BIT(1) | BIT(0)) +/* #define HP_DOCK_MIC_SENSE_PRESENT 0x2 */ +#define HP_DOCK_MIC_SENSE_NOT_PRESENT 0x4 + +static int hp_dock_ctl_connector_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol); + struct snd_usb_audio *chip = cval->head.mixer->chip; + u32 pv = kcontrol->private_value; + bool presence; + int err; + u8 buf; + + CLASS(snd_usb_lock, pm)(chip); + if (pm.err < 0) + return pm.err; + + err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), + HP_DOCK_GET, + USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_IN, + 0, HP_DOCK_JACK_PRESENCE, &buf, sizeof(buf)); + if (err < 0) + return err; + + presence = !(buf & HP_DOCK_JACK_PRESENCE_BIT); + + if (pv && presence) { + for (int i = 0; i < 20; i++) { + err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), + HP_DOCK_GET, + USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_IN, + 0, HP_DOCK_MIC_SENSE, &buf, sizeof(buf)); + if (err < 0) + return err; + + /* Mic sense is complete, we have a result. */ + if (buf & HP_DOCK_MIC_SENSE_COMPLETE_BIT) + break; + + msleep(100); + } + + /* + * If we reach the retry limit without mic sense having + * completed, buf will contain HP_DOCK_MIC_SENSE_PRESENT, + * thus presence remains true even when detection fails. + */ + if ((buf & HP_DOCK_MIC_SENSE_MASK) == HP_DOCK_MIC_SENSE_NOT_PRESENT) + presence = false; + } + ucontrol->value.integer.value[0] = presence; + return 0; +} + +static const struct snd_kcontrol_new hp_dock_connector_ctl_ro = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "", /* will be filled later manually */ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_ctl_boolean_mono_info, + .get = hp_dock_ctl_connector_get, +}; + +static int hp_dock_mixer_create(struct usb_mixer_interface *mixer) +{ + int err; + + err = realtek_add_jack(mixer, "Headsets Playback Jack", 0, + HP_DOCK_JACK_INTERRUPT_NODE, + &hp_dock_connector_ctl_ro); + if (err < 0) + return err; + + err = realtek_add_jack(mixer, "Headset Capture Jack", 1, + HP_DOCK_JACK_INTERRUPT_NODE, + &hp_dock_connector_ctl_ro); + if (err < 0) + return err; + + return 0; +} + + /* RME Class Compliant device quirks */ #define SND_RME_GET_STATUS1 23 @@ -4408,6 +4513,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x2b73, 0x0034): /* Pioneer DJ DJM-V10 */ err = snd_djm_controls_create(mixer, SND_DJM_V10_IDX); break; + case USB_ID(0x03f0, 0x0269): /* HP TB Dock G2 */ + err = hp_dock_mixer_create(mixer); + break; } return err; diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 5c235a5ba7e1..074a61215de6 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -684,6 +684,37 @@ snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface, return NULL; } +static unsigned int +snd_usb_max_bytes_per_interval(struct snd_usb_audio *chip, + struct usb_host_interface *alts) +{ + struct usb_host_endpoint *ep = &alts->endpoint[0]; + unsigned int max_bytes = usb_endpoint_maxp(&ep->desc); + + /* SuperSpeed isoc endpoints have up to 16 bursts of up to 3 packets each */ + if (snd_usb_get_speed(chip->dev) >= USB_SPEED_SUPER) { + int burst = 1 + ep->ss_ep_comp.bMaxBurst; + int mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes); + max_bytes *= burst; + max_bytes *= mult; + } + + if (snd_usb_get_speed(chip->dev) == USB_SPEED_SUPER_PLUS && + USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes)) { + max_bytes = le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval); + } + + /* High speed, 1-3 packets/uframe, max 6 for eUSB2 double bw */ + if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH) { + if (usb_endpoint_is_hs_isoc_double(chip->dev, ep)) + max_bytes = le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval); + else + max_bytes *= usb_endpoint_maxp_mult(&ep->desc); + } + + return max_bytes; +} + static struct audioformat * audio_format_alloc_init(struct snd_usb_audio *chip, struct usb_host_interface *alts, @@ -703,11 +734,8 @@ audio_format_alloc_init(struct snd_usb_audio *chip, fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; fp->datainterval = snd_usb_parse_datainterval(chip, alts); fp->protocol = protocol; - fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + fp->maxpacksize = snd_usb_max_bytes_per_interval(chip, alts); fp->channels = num_channels; - if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH) - fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) - * (fp->maxpacksize & 0x7ff); fp->clock = clock; INIT_LIST_HEAD(&fp->list); diff --git a/tools/testing/selftests/alsa/conf.c b/tools/testing/selftests/alsa/conf.c index e2b3a5810f47..dc7f40e68dee 100644 --- a/tools/testing/selftests/alsa/conf.c +++ b/tools/testing/selftests/alsa/conf.c @@ -448,7 +448,7 @@ int conf_get_bool(snd_config_t *root, const char *key1, const char *key2, int de ksft_exit_fail_msg("key '%s'.'%s' search error: %s\n", key1, key2, snd_strerror(ret)); ret = snd_config_get_bool(cfg); if (ret < 0) - ksft_exit_fail_msg("key '%s'.'%s' is not an bool\n", key1, key2); + ksft_exit_fail_msg("key '%s'.'%s' is not a bool\n", key1, key2); return !!ret; } |