diff options
Diffstat (limited to 'sound/hda')
| -rw-r--r-- | sound/hda/codecs/cirrus/cs420x.c | 1 | ||||
| -rw-r--r-- | sound/hda/codecs/hdmi/intelhdmi.c | 1 | ||||
| -rw-r--r-- | sound/hda/codecs/hdmi/nvhdmi-mcp.c | 4 | ||||
| -rw-r--r-- | sound/hda/codecs/realtek/alc269.c | 124 | ||||
| -rw-r--r-- | sound/hda/codecs/senarytech.c | 9 | ||||
| -rw-r--r-- | sound/hda/codecs/side-codecs/Kconfig | 15 | ||||
| -rw-r--r-- | sound/hda/codecs/side-codecs/cs35l41_hda.c | 4 | ||||
| -rw-r--r-- | sound/hda/codecs/side-codecs/cs35l56_hda.c | 116 | ||||
| -rw-r--r-- | sound/hda/codecs/side-codecs/cs35l56_hda.h | 6 | ||||
| -rw-r--r-- | sound/hda/codecs/side-codecs/hda_component.c | 4 | ||||
| -rw-r--r-- | sound/hda/codecs/side-codecs/tas2781_hda_i2c.c | 45 | ||||
| -rw-r--r-- | sound/hda/controllers/Kconfig | 14 | ||||
| -rw-r--r-- | sound/hda/controllers/Makefile | 2 | ||||
| -rw-r--r-- | sound/hda/controllers/cix-ipbloq.c | 436 | ||||
| -rw-r--r-- | sound/hda/controllers/intel.c | 3 | ||||
| -rw-r--r-- | sound/hda/core/bus.c | 1 | ||||
| -rw-r--r-- | sound/hda/core/controller.c | 12 | ||||
| -rw-r--r-- | sound/hda/core/intel-dsp-config.c | 11 | ||||
| -rw-r--r-- | sound/hda/core/stream.c | 10 |
19 files changed, 775 insertions, 43 deletions
diff --git a/sound/hda/codecs/cirrus/cs420x.c b/sound/hda/codecs/cirrus/cs420x.c index 823220d5cada..13f5f1711fa4 100644 --- a/sound/hda/codecs/cirrus/cs420x.c +++ b/sound/hda/codecs/cirrus/cs420x.c @@ -585,6 +585,7 @@ static const struct hda_quirk cs4208_mac_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x6c00, "MacMini 7,1", CS4208_MACMINI), SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6), SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6), + SND_PCI_QUIRK(0x106b, 0x7800, "MacPro 6,1", CS4208_MACMINI), SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11), {} /* terminator */ }; 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/hdmi/nvhdmi-mcp.c b/sound/hda/codecs/hdmi/nvhdmi-mcp.c index 8fd8d76fa72f..1c5fdfe872f2 100644 --- a/sound/hda/codecs/hdmi/nvhdmi-mcp.c +++ b/sound/hda/codecs/hdmi/nvhdmi-mcp.c @@ -350,8 +350,8 @@ static int nvhdmi_mcp_probe(struct hda_codec *codec, static const struct hda_codec_ops nvhdmi_mcp_codec_ops = { .probe = nvhdmi_mcp_probe, .remove = snd_hda_hdmi_simple_remove, - .build_controls = nvhdmi_mcp_build_pcms, - .build_pcms = nvhdmi_mcp_build_controls, + .build_pcms = nvhdmi_mcp_build_pcms, + .build_controls = nvhdmi_mcp_build_controls, .init = nvhdmi_mcp_init, .unsol_event = snd_hda_hdmi_simple_unsol_event, }; diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 214eb9df6ef8..171a71457ec3 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, @@ -3736,6 +3786,11 @@ enum { ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1, 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; @@ -5269,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, @@ -6172,7 +6233,29 @@ static const struct hda_fixup alc269_fixups[] = { { 0x1e, 0x90170150 }, /* Internal Speaker */ { } }, - } + }, + [ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x22a190a0 }, /* dock mic */ + { } + }, + .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[] = { @@ -6397,6 +6480,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x854a, "HP EliteBook 830 G6", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), + SND_PCI_QUIRK(0x103c, 0x8603, "HP Omen 17-cb0xxx", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x860c, "HP ZBook 17 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), @@ -6512,6 +6597,9 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4), SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a75, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a76, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a77, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED), @@ -6559,6 +6647,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8bd4, "HP Victus 16-s0xxx (MB 8BD4)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bd6, "HP Pavilion Aero Laptop 13z-be200", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8bdd, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bde, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bdf, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), @@ -6576,6 +6665,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX), SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8c21, "HP Pavilion Plus Laptop 14-ey0XXX", ALC245_FIXUP_HP_X360_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x8c2d, "HP Victus 15-fa1xxx (MB 8C2D)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), @@ -6680,6 +6770,32 @@ 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, 0x8e8a, "HP NexusX", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8e9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e9e, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8eb6, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8eb7, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8eb8, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ec1, "HP 200 G2i", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ec4, "HP Bantie I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ec5, "HP Bantie I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ece, "HP Abe I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ecf, "HP Abe I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ed2, "HP Abe I6U", 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, 0x8ee4, "HP Bantie A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8ee5, "HP Bantie A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + 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), @@ -6711,6 +6827,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), @@ -6727,6 +6845,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A), SND_PCI_QUIRK(0x1043, 0x1533, "ASUS GV302XA/XJ/XQ/XU/XV/XI", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1573, "ASUS GZ301VV/VQ/VU/VJ/VA/VC/VE/VVC/VQC/VUC/VJC/VEC/VCC", ALC285_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1584, "ASUS UM3406GA ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1652, "ASUS ROG Zephyrus Do 15 SE", ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK), SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK), SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC), @@ -6858,6 +6977,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), @@ -6957,6 +7077,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x971d, "Clevo N970T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa500, "Clevo NL5[03]RU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa554, "VAIO VJFH52", ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xa559, "VAIO RPL", ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa600, "Clevo NL50NU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa650, "Clevo NP[567]0SN[CD]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa671, "Clevo NP70SN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), @@ -7078,6 +7199,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38b4, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2), + HDA_CODEC_QUIRK(0x17aa, 0x391c, "Lenovo Yoga 7 2-in-1 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x38b5, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x38b6, "Legion Slim 7 16APH8", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x38b7, "Legion Slim 7 16APH8", ALC287_FIXUP_CS35L41_I2C_2), 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/Kconfig b/sound/hda/codecs/side-codecs/Kconfig index cbf1847896bc..f674e9a9c7d7 100644 --- a/sound/hda/codecs/side-codecs/Kconfig +++ b/sound/hda/codecs/side-codecs/Kconfig @@ -88,6 +88,21 @@ config SND_HDA_SCODEC_CS35L56_SPI Say Y or M here to include CS35L56 amplifier support with SPI control. +menu "CS35L56 driver options" + depends on SND_HDA_SCODEC_CS35L56 + +config SND_HDA_SCODEC_CS35L56_CAL_DEBUGFS + bool "CS35L56 create debugfs for factory calibration" + default N + depends on DEBUG_FS + select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON + help + Create debugfs entries used during factory-line manufacture + for factory calibration. + + If unsure select "N". +endmenu + config SND_HDA_SCODEC_TAS2781 tristate select SND_HDA_GENERIC diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index c04208e685a0..21e00055c0c4 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -1410,6 +1410,8 @@ static int cs35l41_get_acpi_mute_state(struct cs35l41_hda *cs35l41, acpi_handle if (cs35l41_dsm_supported(handle, CS35L41_DSM_GET_MUTE)) { ret = acpi_evaluate_dsm(handle, &guid, 0, CS35L41_DSM_GET_MUTE, NULL); + if (!ret) + return -EINVAL; mute = *ret->buffer.pointer; dev_dbg(cs35l41->dev, "CS35L41_DSM_GET_MUTE: %d\n", mute); } @@ -1899,6 +1901,8 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i cs35l41->dacpi = adev; physdev = get_device(acpi_get_first_physical_node(adev)); + if (!physdev) + return -ENODEV; sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); if (IS_ERR(sub)) diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c index 5bb1c4ebeaf3..f7ba92e11957 100644 --- a/sound/hda/codecs/side-codecs/cs35l56_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c @@ -548,20 +548,24 @@ static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmw kfree(coeff_filename); } -static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56) +static int cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56) { int ret; if (!cs35l56->base.cal_data_valid || cs35l56->base.secured) - return; + return -EACCES; ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp, &cs35l56_calibration_controls, &cs35l56->base.cal_data); - if (ret < 0) + if (ret < 0) { dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret); - else - dev_info(cs35l56->base.dev, "Calibration applied\n"); + return ret; + } + + dev_info(cs35l56->base.dev, "Calibration applied\n"); + + return 0; } static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) @@ -669,7 +673,9 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) if (ret) dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret); + /* Don't need to check return code, it's not fatal if this fails */ cs35l56_hda_apply_calibration(cs35l56); + ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); if (ret) cs_dsp_stop(&cs35l56->cs_dsp); @@ -695,6 +701,100 @@ static void cs35l56_hda_dsp_work(struct work_struct *work) cs35l56_hda_fw_load(cs35l56); } +static ssize_t cs35l56_hda_debugfs_calibrate_write(struct file *file, + const char __user *from, + size_t count, loff_t *ppos) +{ + struct cs35l56_base *cs35l56_base = file->private_data; + ssize_t ret; + + ret = pm_runtime_resume_and_get(cs35l56_base->dev); + if (ret) + return ret; + + ret = cs35l56_calibrate_debugfs_write(cs35l56_base, from, count, ppos); + pm_runtime_autosuspend(cs35l56_base->dev); + + return ret; +} + +static ssize_t cs35l56_hda_debugfs_cal_temperature_write(struct file *file, + const char __user *from, + size_t count, loff_t *ppos) +{ + struct cs35l56_base *cs35l56_base = file->private_data; + ssize_t ret; + + ret = pm_runtime_resume_and_get(cs35l56_base->dev); + if (ret) + return ret; + + ret = cs35l56_cal_ambient_debugfs_write(cs35l56_base, from, count, ppos); + pm_runtime_autosuspend(cs35l56_base->dev); + + return ret; +} + +static ssize_t cs35l56_hda_debugfs_cal_data_read(struct file *file, + char __user *to, + size_t count, loff_t *ppos) +{ + struct cs35l56_base *cs35l56_base = file->private_data; + ssize_t ret; + + ret = pm_runtime_resume_and_get(cs35l56_base->dev); + if (ret) + return ret; + + ret = cs35l56_cal_data_debugfs_read(cs35l56_base, to, count, ppos); + pm_runtime_autosuspend(cs35l56_base->dev); + + return ret; +} + +static ssize_t cs35l56_hda_debugfs_cal_data_write(struct file *file, + const char __user *from, + size_t count, loff_t *ppos) +{ + struct cs35l56_base *cs35l56_base = file->private_data; + struct cs35l56_hda *cs35l56 = cs35l56_hda_from_base(cs35l56_base); + ssize_t ret; + + ret = cs35l56_cal_data_debugfs_write(cs35l56_base, from, count, ppos); + if (ret == -ENODATA) + return count; /* Ignore writes of empty cal blobs */ + + if (ret < 0) + return ret; + + ret = pm_runtime_resume_and_get(cs35l56_base->dev); + if (ret) + return ret; + + ret = cs35l56_hda_apply_calibration(cs35l56); + if (ret == 0) + cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_AUDIO_REINIT); + else + count = -EIO; + + pm_runtime_autosuspend(cs35l56_base->dev); + + return count; +} + +static const struct cs35l56_cal_debugfs_fops cs35l56_hda_cal_debugfs_fops = { + .calibrate = { + .write = cs35l56_hda_debugfs_calibrate_write, + }, + .cal_temperature = { + .write = cs35l56_hda_debugfs_cal_temperature_write, + }, + .cal_data = { + .read = cs35l56_hda_debugfs_cal_data_read, + .write = cs35l56_hda_debugfs_cal_data_write, + }, +}; + static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data) { struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); @@ -722,6 +822,9 @@ static int cs35l56_hda_bind(struct device *dev, struct device *master, void *mas cs_dsp_init_debugfs(&cs35l56->cs_dsp, cs35l56->debugfs_root); #endif + if (IS_ENABLED(CONFIG_SND_HDA_SCODEC_CS35L56_CAL_DEBUGFS)) + cs35l56_create_cal_debugfs(&cs35l56->base, &cs35l56_hda_cal_debugfs_fops); + dev_dbg(cs35l56->base.dev, "Bound\n"); return 0; @@ -735,6 +838,7 @@ static void cs35l56_hda_unbind(struct device *dev, struct device *master, void * cancel_work_sync(&cs35l56->dsp_work); + cs35l56_remove_cal_debugfs(&cs35l56->base); cs35l56_hda_remove_controls(cs35l56); #if IS_ENABLED(CONFIG_SND_DEBUG) @@ -1050,7 +1154,7 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id) } cs35l56->base.type = hid & 0xff; - cs35l56->base.cal_index = -1; + cs35l56->base.cal_index = cs35l56->index; cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp); cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops; diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.h b/sound/hda/codecs/side-codecs/cs35l56_hda.h index 38d94fb213a5..cb4b5e7356a3 100644 --- a/sound/hda/codecs/side-codecs/cs35l56_hda.h +++ b/sound/hda/codecs/side-codecs/cs35l56_hda.h @@ -9,6 +9,7 @@ #ifndef __CS35L56_HDA_H__ #define __CS35L56_HDA_H__ +#include <linux/container_of.h> #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/firmware/cirrus/cs_dsp.h> @@ -42,6 +43,11 @@ struct cs35l56_hda { #endif }; +static inline struct cs35l56_hda *cs35l56_hda_from_base(struct cs35l56_base *cs35l56_base) +{ + return container_of(cs35l56_base, struct cs35l56_hda, base); +} + extern const struct dev_pm_ops cs35l56_hda_pm_ops; int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id); diff --git a/sound/hda/codecs/side-codecs/hda_component.c b/sound/hda/codecs/side-codecs/hda_component.c index bcf47a301697..8a2a200600a7 100644 --- a/sound/hda/codecs/side-codecs/hda_component.c +++ b/sound/hda/codecs/side-codecs/hda_component.c @@ -174,6 +174,10 @@ int hda_component_manager_init(struct hda_codec *cdc, sm->match_str = match_str; sm->index = i; component_match_add(dev, &match, hda_comp_match_dev_name, sm); + if (IS_ERR(match)) { + codec_err(cdc, "Fail to add component %ld\n", PTR_ERR(match)); + return PTR_ERR(match); + } } ret = component_master_add_with_match(dev, ops, match); diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index a126f04c3ed7..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), @@ -669,6 +675,7 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt) */ device_name = "TXNW5825"; hda_priv->hda_chip_id = HDA_TAS5825; + tas_hda->priv->chip_id = TAS5825; } else { return -ENODEV; } diff --git a/sound/hda/controllers/Kconfig b/sound/hda/controllers/Kconfig index 34721f50b055..72855f2df451 100644 --- a/sound/hda/controllers/Kconfig +++ b/sound/hda/controllers/Kconfig @@ -30,6 +30,20 @@ config SND_HDA_TEGRA To compile this driver as a module, choose M here: the module will be called snd-hda-tegra. +config SND_HDA_CIX_IPBLOQ + tristate "CIX IPBLOQ HD Audio" + depends on ARCH_CIX || COMPILE_TEST + select SND_HDA + select SND_HDA_ALIGNED_MMIO + help + Say Y here to support the HDA controller present in CIX SoCs + + This options enables support for the HD Audio controller + present in some CIX SoCs. + + To compile this driver as a module, choose M here: the module + will be called snd-hda-cix-ipbloq. + config SND_HDA_ACPI tristate "HD Audio ACPI" depends on ACPI diff --git a/sound/hda/controllers/Makefile b/sound/hda/controllers/Makefile index a4bcd055e9ae..8967b6771d90 100644 --- a/sound/hda/controllers/Makefile +++ b/sound/hda/controllers/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 snd-hda-intel-y := intel.o snd-hda-tegra-y := tegra.o +snd-hda-cix-ipbloq-y := cix-ipbloq.o snd-hda-acpi-y := acpi.o subdir-ccflags-y += -I$(src)/../common @@ -10,4 +11,5 @@ CFLAGS_intel.o := -I$(src) obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o +obj-$(CONFIG_SND_HDA_CIX_IPBLOQ) += snd-hda-cix-ipbloq.o obj-$(CONFIG_SND_HDA_ACPI) += snd-hda-acpi.o diff --git a/sound/hda/controllers/cix-ipbloq.c b/sound/hda/controllers/cix-ipbloq.c new file mode 100644 index 000000000000..99f9f48e91d4 --- /dev/null +++ b/sound/hda/controllers/cix-ipbloq.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright 2025 Cix Technology Group Co., Ltd. + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/string.h> + +#include <sound/hda_codec.h> +#include "hda_controller.h" + +#define CIX_IPBLOQ_JACKPOLL_DEFAULT_TIME_MS 1000 +#define CIX_IPBLOQ_POWER_SAVE_DEFAULT_TIME_MS 100 + +#define CIX_IPBLOQ_SKY1_ADDR_HOST_TO_HDAC_OFFSET (-0x90000000ULL) + +struct cix_ipbloq_hda { + struct azx chip; + struct device *dev; + void __iomem *regs; + + struct reset_control *reset; + struct clk_bulk_data clocks[2]; + unsigned int nclocks; +}; + +static const struct hda_controller_ops cix_ipbloq_hda_ops; + +static int cix_ipbloq_hda_dev_disconnect(struct snd_device *device) +{ + struct azx *chip = device->device_data; + + chip->bus.shutdown = 1; + + return 0; +} + +static int cix_ipbloq_hda_dev_free(struct snd_device *device) +{ + struct azx *chip = device->device_data; + + if (azx_bus(chip)->chip_init) { + azx_stop_all_streams(chip); + azx_stop_chip(chip); + } + + azx_free_stream_pages(chip); + azx_free_streams(chip); + snd_hdac_bus_exit(azx_bus(chip)); + + return 0; +} + +static int cix_ipbloq_hda_probe_codec(struct cix_ipbloq_hda *hda) +{ + struct azx *chip = &hda->chip; + struct hdac_bus *bus = azx_bus(chip); + int err; + + to_hda_bus(bus)->bus_probing = 1; + + /* create codec instances */ + err = azx_probe_codecs(chip, 8); + if (err < 0) { + dev_err(hda->dev, "probe codecs failed: %d\n", err); + return err; + } + + err = azx_codec_configure(chip); + if (err < 0) { + dev_err(hda->dev, "codec configure failed: %d\n", err); + return err; + } + + err = snd_card_register(chip->card); + if (err < 0) { + dev_err(hda->dev, "card register failed: %d\n", err); + return err; + } + + chip->running = 1; + + to_hda_bus(bus)->bus_probing = 0; + + snd_hda_set_power_save(&chip->bus, CIX_IPBLOQ_POWER_SAVE_DEFAULT_TIME_MS); + + return 0; +} + +static int cix_ipbloq_hda_init(struct cix_ipbloq_hda *hda, + struct azx *chip, + struct platform_device *pdev) +{ + const char *sname = NULL, *drv_name = "cix-ipbloq-hda"; + struct hdac_bus *bus = azx_bus(chip); + struct snd_card *card = chip->card; + struct resource *res; + unsigned short gcap; + int irq_id, err; + + hda->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(hda->regs)) { + dev_err(hda->dev, "failed to get and ioremap resource\n"); + return PTR_ERR(hda->regs); + } + bus->remap_addr = hda->regs; + bus->addr = res->start; + + irq_id = platform_get_irq(pdev, 0); + if (irq_id < 0) { + dev_err(hda->dev, "failed to get the irq, err = %d\n", irq_id); + return irq_id; + } + + err = devm_request_irq(hda->dev, irq_id, azx_interrupt, + 0, KBUILD_MODNAME, chip); + if (err < 0) + return dev_err_probe(hda->dev, err, + "unable to request IRQ %d : err = %d\n", irq_id, err); + bus->irq = irq_id; + card->sync_irq = bus->irq; + + gcap = azx_readw(chip, GCAP); + chip->capture_streams = (gcap >> 8) & 0x0f; + chip->playback_streams = (gcap >> 12) & 0x0f; + chip->capture_index_offset = 0; + chip->playback_index_offset = chip->capture_streams; + chip->num_streams = chip->playback_streams + chip->capture_streams; + + /* initialize streams */ + err = azx_init_streams(chip); + if (err < 0) { + dev_err(hda->dev, "failed to initialize streams: %d\n", err); + return err; + } + + err = azx_alloc_stream_pages(chip); + if (err < 0) { + dev_err(hda->dev, "failed to allocate stream pages: %d\n", err); + return err; + } + + /* initialize chip */ + azx_init_chip(chip, 1); + + /* codec detection */ + if (!bus->codec_mask) { + dev_err(hda->dev, "no codecs found\n"); + return -ENODEV; + } + dev_dbg(card->dev, "codec detection mask = 0x%lx\n", bus->codec_mask); + + /* driver name */ + strscpy(card->driver, drv_name, sizeof(card->driver)); + + /* shortname for card */ + sname = of_get_property(pdev->dev.of_node, "model", NULL); + if (!sname) + sname = drv_name; + if (strlen(sname) > sizeof(card->shortname)) + dev_dbg(card->dev, "truncating shortname for card\n"); + strscpy(card->shortname, sname, sizeof(card->shortname)); + + /* longname for card */ + snprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx irq %i", + card->shortname, bus->addr, bus->irq); + + return 0; +} + +static int cix_ipbloq_hda_create(struct cix_ipbloq_hda *hda, + struct snd_card *card, + unsigned int driver_caps) +{ + static const struct snd_device_ops ops = { + .dev_disconnect = cix_ipbloq_hda_dev_disconnect, + .dev_free = cix_ipbloq_hda_dev_free, + }; + struct azx *chip; + int err; + + chip = &hda->chip; + chip->card = card; + chip->ops = &cix_ipbloq_hda_ops; + chip->driver_caps = driver_caps; + chip->driver_type = driver_caps & 0xff; + chip->dev_index = 0; + chip->single_cmd = 0; + chip->codec_probe_mask = -1; + chip->align_buffer_size = 1; + chip->jackpoll_interval = msecs_to_jiffies(CIX_IPBLOQ_JACKPOLL_DEFAULT_TIME_MS); + mutex_init(&chip->open_mutex); + INIT_LIST_HEAD(&chip->pcm_list); + + /* + * HD-audio controllers appear pretty inaccurate about the update-IRQ timing. + * The IRQ is issued before actually the data is processed. So use stream + * link position by default instead of dma position buffer. + */ + chip->get_position[0] = chip->get_position[1] = azx_get_pos_lpib; + + err = azx_bus_init(chip, NULL); + if (err < 0) { + dev_err(hda->dev, "failed to init bus, err = %d\n", err); + return err; + } + + /* RIRBSTS.RINTFL cannot be cleared, cause interrupt storm */ + chip->bus.core.polling_mode = 1; + chip->bus.core.not_use_interrupts = 1; + + chip->bus.core.aligned_mmio = 1; + chip->bus.core.dma_stop_delay = 100; + chip->bus.core.addr_offset = (dma_addr_t)CIX_IPBLOQ_SKY1_ADDR_HOST_TO_HDAC_OFFSET; + + chip->bus.jackpoll_in_suspend = 1; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + dev_err(card->dev, "failed to create device, err = %d\n", err); + return err; + } + + return 0; +} + +static int cix_ipbloq_hda_probe(struct platform_device *pdev) +{ + const unsigned int driver_flags = AZX_DCAPS_PM_RUNTIME; + struct cix_ipbloq_hda *hda; + struct snd_card *card; + struct azx *chip; + int err; + + hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); + if (!hda) + return -ENOMEM; + hda->dev = &pdev->dev; + + hda->reset = devm_reset_control_get(hda->dev, NULL); + if (IS_ERR(hda->reset)) + return dev_err_probe(hda->dev, PTR_ERR(hda->reset), + "failed to get reset, err = %ld\n", PTR_ERR(hda->reset)); + + hda->clocks[hda->nclocks++].id = "ipg"; + hda->clocks[hda->nclocks++].id = "per"; + err = devm_clk_bulk_get(hda->dev, hda->nclocks, hda->clocks); + if (err < 0) + return dev_err_probe(hda->dev, err, "failed to get clk, err = %d\n", err); + + dma_set_mask_and_coherent(hda->dev, DMA_BIT_MASK(32)); + + err = of_reserved_mem_device_init(hda->dev); + if (err < 0 && err != -ENODEV) { + dev_err(hda->dev, + "failed to init reserved mem for DMA, err = %d\n", err); + return err; + } + + err = snd_card_new(hda->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (err < 0) + return dev_err_probe(hda->dev, err, "failed to crate card, err = %d\n", err); + + err = cix_ipbloq_hda_create(hda, card, driver_flags); + if (err < 0) + goto out_free_card; + + chip = &hda->chip; + card->private_data = chip; + dev_set_drvdata(hda->dev, card); + + pm_runtime_enable(hda->dev); + if (!azx_has_pm_runtime(chip)) + pm_runtime_forbid(hda->dev); + + err = pm_runtime_resume_and_get(hda->dev); + if (err < 0) { + dev_err(hda->dev, "runtime resume and get failed, err = %d\n", err); + goto out_free_device; + } + + err = cix_ipbloq_hda_init(hda, chip, pdev); + if (err < 0) + goto out_free_device; + + err = cix_ipbloq_hda_probe_codec(hda); + if (err < 0) + goto out_free_device; + + pm_runtime_put(hda->dev); + + return 0; + +out_free_device: + snd_device_free(card, chip); +out_free_card: + snd_card_free(card); + + return err; +} + +static void cix_ipbloq_hda_remove(struct platform_device *pdev) +{ + struct snd_card *card = dev_get_drvdata(&pdev->dev); + struct azx *chip = card->private_data; + + snd_device_free(card, chip); + snd_card_free(card); + + pm_runtime_disable(&pdev->dev); +} + +static void cix_ipbloq_hda_shutdown(struct platform_device *pdev) +{ + struct snd_card *card = dev_get_drvdata(&pdev->dev); + struct azx *chip; + + if (!card) + return; + + chip = card->private_data; + if (chip && chip->running) + azx_stop_chip(chip); +} + +static int cix_ipbloq_hda_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + int rc; + + rc = pm_runtime_force_suspend(dev); + if (rc < 0) + return rc; + snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); + + return 0; +} + +static int cix_ipbloq_hda_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + int rc; + + rc = pm_runtime_force_resume(dev); + if (rc < 0) + return rc; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + + return 0; +} + +static int cix_ipbloq_hda_runtime_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct cix_ipbloq_hda *hda = container_of(chip, struct cix_ipbloq_hda, chip); + + if (chip && chip->running) { + azx_stop_chip(chip); + azx_enter_link_reset(chip); + } + + clk_bulk_disable_unprepare(hda->nclocks, hda->clocks); + + return 0; +} + +static int cix_ipbloq_hda_runtime_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct cix_ipbloq_hda *hda = container_of(chip, struct cix_ipbloq_hda, chip); + int rc; + + rc = clk_bulk_prepare_enable(hda->nclocks, hda->clocks); + if (rc) { + dev_err(dev, "failed to enable clk bulk, rc: %d\n", rc); + return rc; + } + + rc = reset_control_assert(hda->reset); + if (rc) { + dev_err(dev, "failed to assert reset, rc: %d\n", rc); + return rc; + } + + rc = reset_control_deassert(hda->reset); + if (rc) { + dev_err(dev, "failed to deassert reset, rc: %d\n", rc); + return rc; + } + + if (chip && chip->running) + azx_init_chip(chip, 1); + + return 0; +} + +static const struct dev_pm_ops cix_ipbloq_hda_pm = { + SYSTEM_SLEEP_PM_OPS(cix_ipbloq_hda_suspend, + cix_ipbloq_hda_resume) + RUNTIME_PM_OPS(cix_ipbloq_hda_runtime_suspend, + cix_ipbloq_hda_runtime_resume, NULL) +}; + +static const struct of_device_id cix_ipbloq_hda_match[] = { + { .compatible = "cix,sky1-ipbloq-hda" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, cix_ipbloq_hda_match); + +static struct platform_driver cix_ipbloq_hda_driver = { + .driver = { + .name = "cix-ipbloq-hda", + .pm = pm_ptr(&cix_ipbloq_hda_pm), + .of_match_table = cix_ipbloq_hda_match, + }, + .probe = cix_ipbloq_hda_probe, + .remove = cix_ipbloq_hda_remove, + .shutdown = cix_ipbloq_hda_shutdown, +}; +module_platform_driver(cix_ipbloq_hda_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CIX IPBLOQ HDA bus driver"); +MODULE_AUTHOR("Joakim Zhang <joakim.zhang@cixtech.com>"); diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 48c52a207024..1e8e3d61291a 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2075,6 +2075,7 @@ static const struct pci_device_id driver_denylist[] = { { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1043, 0x874f) }, /* ASUS ROG Zenith II / Strix */ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb59) }, /* MSI TRX40 Creator */ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb60) }, /* MSI TRX40 */ + { PCI_DEVICE_SUB(0x1022, 0x15e3, 0x1462, 0xee59) }, /* MSI X870E Tomahawk WiFi */ {} }; @@ -2549,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/bus.c b/sound/hda/core/bus.c index 9b196c915f37..81498f1e413e 100644 --- a/sound/hda/core/bus.c +++ b/sound/hda/core/bus.c @@ -47,6 +47,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, INIT_LIST_HEAD(&bus->hlink_list); init_waitqueue_head(&bus->rirb_wq); bus->irq = -1; + bus->addr_offset = 0; /* * Default value of '8' is as per the HD audio specification (Rev 1.0a). diff --git a/sound/hda/core/controller.c b/sound/hda/core/controller.c index a7c00ad80117..69e11d62bbfa 100644 --- a/sound/hda/core/controller.c +++ b/sound/hda/core/controller.c @@ -48,8 +48,8 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) /* CORB set up */ bus->corb.addr = bus->rb.addr; bus->corb.buf = (__le32 *)bus->rb.area; - snd_hdac_chip_writel(bus, CORBLBASE, (u32)bus->corb.addr); - snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr)); + snd_hdac_chip_writel(bus, CORBLBASE, (u32)(bus->corb.addr + bus->addr_offset)); + snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr + bus->addr_offset)); /* set the corb size to 256 entries (ULI requires explicitly) */ snd_hdac_chip_writeb(bus, CORBSIZE, 0x02); @@ -70,8 +70,8 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) bus->rirb.buf = (__le32 *)(bus->rb.area + 2048); bus->rirb.wp = bus->rirb.rp = 0; memset(bus->rirb.cmds, 0, sizeof(bus->rirb.cmds)); - snd_hdac_chip_writel(bus, RIRBLBASE, (u32)bus->rirb.addr); - snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr)); + snd_hdac_chip_writel(bus, RIRBLBASE, (u32)(bus->rirb.addr + bus->addr_offset)); + snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr + bus->addr_offset)); /* set the rirb size to 256 entries (ULI requires explicitly) */ snd_hdac_chip_writeb(bus, RIRBSIZE, 0x02); @@ -625,8 +625,8 @@ bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) /* program the position buffer */ if (bus->use_posbuf && bus->posbuf.addr) { - snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr); - snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr)); + snd_hdac_chip_writel(bus, DPLBASE, (u32)(bus->posbuf.addr + bus->addr_offset)); + snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr + bus->addr_offset)); } bus->chip_init = true; diff --git a/sound/hda/core/intel-dsp-config.c b/sound/hda/core/intel-dsp-config.c index 2a9e35cddcf7..0c25e87408de 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 @@ -710,7 +718,8 @@ int snd_intel_dsp_driver_probe(struct pci_dev *pci) /* find the configuration for the specific device */ cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table)); if (!cfg) - return SND_INTEL_DSP_DRIVER_ANY; + return IS_ENABLED(CONFIG_SND_HDA_INTEL) ? + SND_INTEL_DSP_DRIVER_LEGACY : SND_INTEL_DSP_DRIVER_ANY; if (cfg->flags & FLAG_SOF) { if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE && diff --git a/sound/hda/core/stream.c b/sound/hda/core/stream.c index 579ec544ef4a..b471a038b314 100644 --- a/sound/hda/core/stream.c +++ b/sound/hda/core/stream.c @@ -288,16 +288,16 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev, bool code_loading) /* program the BDL address */ /* lower BDL address */ - snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr); + snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)(azx_dev->bdl.addr + bus->addr_offset)); /* upper BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPU, - upper_32_bits(azx_dev->bdl.addr)); + upper_32_bits(azx_dev->bdl.addr + bus->addr_offset)); /* enable the position buffer */ if (bus->use_posbuf && bus->posbuf.addr) { if (!(snd_hdac_chip_readl(bus, DPLBASE) & AZX_DPLBASE_ENABLE)) snd_hdac_chip_writel(bus, DPLBASE, - (u32)bus->posbuf.addr | AZX_DPLBASE_ENABLE); + (u32)(bus->posbuf.addr + bus->addr_offset) | AZX_DPLBASE_ENABLE); } /* set the interrupt enable bits in the descriptor control register */ @@ -464,8 +464,8 @@ static int setup_bdle(struct hdac_bus *bus, addr = snd_sgbuf_get_addr(dmab, ofs); /* program the address field of the BDL entry */ - bdl[0] = cpu_to_le32((u32)addr); - bdl[1] = cpu_to_le32(upper_32_bits(addr)); + bdl[0] = cpu_to_le32((u32)(addr + bus->addr_offset)); + bdl[1] = cpu_to_le32(upper_32_bits(addr + bus->addr_offset)); /* program the size field of the BDL entry */ chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size); /* one BDLE cannot cross 4K boundary on CTHDA chips */ |
